Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ jobs:
pac-cli: ./**/pac-cli/**
txc-cli: ./**/txc-cli/**
power-platform: ./**/power-platform/**
agent:
- src/agent/**
- src/container/scripts/configure-agent-harness.sh
- test/agent/**

test-features:
needs: [detect-changes]
Expand Down
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ real security exposure, and heavy agent workloads wear on a machine over time. R
isolated environments below instead — see GitHub's own take on why:
[About cloud and local sandboxes](https://docs.github.com/en/copilot/concepts/about-cloud-and-local-sandboxes).

## Configuring the agents

Configuration is split along the line between the box and the agent. This repo owns the environment —
[`src/agent/`](src/agent) declares which Skills plugins every box gets, and installing and updating
tooling is its business. [TALXIS/skills](https://github.com/TALXIS/skills) owns process and know-how:
its `harness/` directory says how a harness should behave, colocated with the Skills it belongs to.

Every environment below already runs the script that applies both, so an edit on either side needs no
per-environment change, and nothing is written into a checkout: the config applies whichever
repository is cloned into the box. See [`src/agent/README.md`](src/agent/README.md) for where each
piece lands per harness.

## Choose your environment

### 1. GitHub Codespaces
Expand Down Expand Up @@ -34,8 +46,9 @@ isolated environments below instead — see GitHub's own take on why:
`ghcr.io/talxis/tools-agentbox/image:latest` and add that same `postCreateCommand` line — it's
easy to end up with a working toolchain but no Skills/MCP registration if this step gets skipped,
since nothing it does is baked into the image itself. It registers the
[TALXIS/skills](https://github.com/TALXIS/skills) plugin (Skills + the `txc` MCP server) with
whichever of Claude Code / GitHub Copilot is present — omit it if you don't want that.
[TALXIS/skills](https://github.com/TALXIS/skills) plugin (Skills + the `txc` MCP server) and applies
that repo's agent behaviour config to whichever of Claude Code / GitHub Copilot is present. Omit it if
you don't want that.

Or build your own `devcontainer.json` from individual features listed in
[`devcontainer.features.json`](src/container/templates/power-platform/.devcontainer/devcontainer.features.json).
Expand All @@ -45,6 +58,10 @@ Or build your own `devcontainer.json` from individual features listed in
Copilot's cloud agent environment doesn't use `devcontainer.json` — customize it with
[`.github/workflows/copilot-setup-steps.yml`](.github/workflows/copilot-setup-steps.yml) instead.

That step runs as root via `sudo`, while the agent itself runs as the unprivileged runner user, so the
agent config is written to both homes — see
[`src/agent/README.md`](src/agent/README.md#which-home-directories-get-the-user-level-files).

### 3. Claude Code cloud environment

Create an org-shared [Claude Code cloud environment](https://code.claude.com/docs/en/cloud-environments)
Expand All @@ -63,8 +80,9 @@ with:

This installs the Feature list from
[`devcontainer.features.json`](src/container/templates/power-platform/.devcontainer/devcontainer.features.json)
directly on the VM, and registers the [TALXIS/skills](https://github.com/TALXIS/skills) plugin
(Skills + the `txc` MCP server) with Claude Code. No changes needed in individual repos.
directly on the VM, registers the [TALXIS/skills](https://github.com/TALXIS/skills) plugin (Skills +
the `txc` MCP server), and applies that repo's agent behaviour config to Claude Code. No changes
needed in individual repos.

To keep `txc` and the Dataverse templates current between setup script runs, merge the `hooks` from
[`src/container/claude-code/session-start-hook.json`](src/container/claude-code/session-start-hook.json) into
Expand Down
94 changes: 94 additions & 0 deletions src/agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Agent configuration

Two manifests configure the agents in an AgentBox, split along the line between the box and the agent:

- **This repo owns the environment.** `agent.json` here declares which plugin marketplaces and
plugins every box gets, and where the behaviour config lives. Installing and updating tooling is
also this repo's business.
- **[TALXIS/skills](https://github.com/TALXIS/skills) owns process and know-how.** Its `harness/`
directory holds how a harness should behave: the instructions loaded into every session, and the
briefing injected once at session start — colocated with the Skills they belong to.

[`../container/scripts/configure-agent-harness.sh`](../container/scripts/configure-agent-harness.sh)
reads both and applies them. Every surface already runs it — the Claude Code cloud setup script and
the Copilot cloud sandbox via `install-features.sh`, Codespaces via its own `postCreateCommand` — so an
edit on either side reaches all of them with no per-surface change. Re-running is safe and is how an
edit is picked up: JSON is merged, and the markdown files carry a marked `AGENTBOX` block that is
replaced rather than appended, so anything a developer wrote around it survives.

Each manifest exists in exactly one place, and there is no built-in copy to fall back on: a run that
cannot read either one names the URL that failed and exits non-zero rather than leaving a
half-configured box.

## Where each piece lands

Nothing is project-scoped, so the config applies whichever repository is cloned into the box.

| Piece | Claude Code | GitHub Copilot CLI |
|-------|-------------|--------------------|
| Marketplaces / plugins | `claude plugin marketplace add` + `claude plugin install`, with the plugin cache seeded machine-wide via `CLAUDE_CODE_PLUGIN_CACHE_DIR` | `copilot plugin …`, plus `extraKnownMarketplaces`/`enabledPlugins` in `settings.json` |
| Session instructions | `/etc/claude-code/CLAUDE.md` — managed-policy memory: machine-wide, user-independent, and not excludable via a user's `claudeMdExcludes`. Falls back to `~/.claude/CLAUDE.md` when not running as root | `copilot-instructions.md` — user-level custom instructions, loaded in every repository |
| Session briefing | `SessionStart` hook in `settings.json`, returning `hookSpecificOutput.additionalContext` | `sessionStart` hook in `hooks/agentbox.json`, returning `additionalContext` |

Both harnesses get the briefing from one generated script,
`/usr/local/share/agentbox/session-start.sh`, which prints it in whichever JSON shape the calling
harness expects. Don't edit that generated copy.

### Which home directories get the user-level files

Claude's instructions have a machine-level path; the rest are user-level, and the user who runs the
harness is not always the user that provisioned the box. So each user-level file is written to the
invoking user's home, to `/etc/skel` when running as root so a user created later starts configured,
and to the home of the user behind `sudo` when there is one.

That last case is not hypothetical: the Copilot cloud sandbox provisions with
`sudo -E bash install-features.sh`, where `HOME` resolves to `/root`, and then runs the agent as the
unprivileged user. Without the mirror nothing written under `${HOME}` would ever be read there, and
`/etc/skel` doesn't help because that user already exists.

## Short links

| Short link | Resolves to |
|------------|-------------|
| `https://talxis.com/agentbox-agent` | this repo's `src/agent/agent.json` on `master` |
| `https://talxis.com/agentbox-instructions` | `TALXIS/skills` → `harness/instructions.json` on `master` |

Payload files are fetched relative to the URL the instructions manifest actually resolved to, so no
branch, path or filename is pinned here. Point a short link at a branch and that branch's config is
what a box gets. `AGENTBOX_CONFIG_URL` / `AGENTBOX_INSTRUCTIONS_URL` override either at run time, and
`AGENTBOX_CONFIG_DIR` / `AGENTBOX_INSTRUCTIONS_DIR` point at local directories instead (which is how
the tests stay offline).

## Two things to know before writing instructions

- **This is context, not enforcement.** Both harnesses load these files as instructions the model is
asked to follow, not as constraints on what it can do — and a developer can opt out
(`copilot --no-custom-instructions`). Anything that must hold regardless belongs in a permission
rule or a `PreToolUse`/`preToolUse` hook. Each harness does have a machine-level policy channel a
developer cannot remove (Claude's `/etc/claude-code/managed-settings.json`, Copilot's
`/etc/github-copilot/policy.d/`); AgentBox deliberately doesn't use them, because it configures
boxes rather than policing them.
- **Length costs adherence**, and the instructions live in every session's context window. Keep them
to rules that apply to all work; task-specific guidance belongs in a Skill, which loads on demand.

## Mechanisms deliberately not used

- `COPILOT_CUSTOM_INSTRUCTIONS_DIRS` pointing at a machine-wide directory: it only works where the
environment is under our control, and as `install-features.sh` notes, neither Claude Code nor a
GitHub Actions step sources `/etc/profile.d`.
- An `AGENTS.md` in a directory above the clone: Copilot discovers instruction files in the git root
and the working directory, not above them.

## Verifying

In a session on a freshly provisioned environment:

- Claude Code: `/context` lists the managed `CLAUDE.md` under **Memory files**; `/hooks` shows the
`SessionStart` entry.
- Copilot CLI: `/instructions` lists `copilot-instructions.md` as an active source.

Or run the assertions, which apply a fixture through every path above without touching the network:

```bash
HOME="$(mktemp -d)" bash test/agent/assert.sh
```
13 changes: 13 additions & 0 deletions src/agent/agent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"//": "What an AgentBox provisions into a box: the plugin marketplaces and plugins every harness gets, and where the harness-behaviour config lives. Applied by src/container/scripts/configure-agent-harness.sh — see src/agent/README.md.",

"marketplaces": {
"talxis": "TALXIS/skills"
},

"plugins": [
"implement@talxis"
],

"instructions": "https://talxis.com/agentbox-instructions"
}
Loading
Loading