Skip to content

feat(agent): let the admin allow stacklet commands for the agent - #233

Merged
arthware-dev merged 2 commits into
mainfrom
feat/agent-allow
Oct 16, 2026
Merged

arthware-dev merged 2 commits into
mainfrom
feat/agent-allow

Conversation

@arthware-dev

Copy link
Copy Markdown
Contributor

Summary

  • The agent reaches the CLI only through the core API, which runs a fixed set of memory and docs show commands. [agent] allow in stack.toml now adds stacklet commands: "kettle" for all of a stacklet's commands, "kettle status" for one. The API reads it on every request.
  • Only stacklet commands qualify. An entry that names no discovered stacklet command is ignored, and stack up agent warns about it, so no entry can allow up, destroy or config.
  • stack up agent writes the allowed commands, each with its HELP line, into the agent's workspace as an always-loaded skill. With nothing allowed there is no skill and the prompt is unchanged.

Test added 2 commits October 16, 2026 12:44
The agent's allowlist needs the same answer the CLI uses to build its
parser: which commands each stacklet has, and each command's HELP line.
Both now live in `stack.plugins`, so the two cannot disagree and nothing
imports private names from `cli.py`.

No behaviour change.
The agent reaches the CLI only through the core API, which ran a fixed
set of `memory` and `docs show` commands. An admin can now list more in
`stack.toml`:

    [agent]
    allow = ["kettle"]            # every command of the kettle stacklet
    allow = ["kettle status"]     # only this one

- The API reads the list on every plaintext request, so a change
  applies without a restart.
- Only stacklet commands qualify: an entry must name a discovered
  stacklet with CLI commands and, optionally, one of its commands. No
  entry can allow a framework command such as `up` or `destroy`.
  `stack up agent` warns about every entry it ignores.
- `stack up agent` writes the allowed commands, each with its own
  `HELP` line, into the agent's workspace as an always-loaded skill, so
  the agent can call one without asking for `--help`. With nothing
  allowed there is no such skill and the prompt is unchanged.

Anything allowed runs, including commands that change something.
@arthware-dev

Copy link
Copy Markdown
Contributor Author

Cache alignment of the always-loaded skill (open point from the design).

Measured with the Qwen3.5 tokenizer from the local model files (the rig's default, Qwen3.6-35B-A3B, is served remotely and is the same family). The example: one stacklet with five commands and HELP lines that name their arguments.

What Tokens
Skill body (what lands in the prompt) 93
Whole SKILL.md including frontmatter (upper bound) 127
zz-cache-pad for comparison 867

The pad tunes the stable prefix to about 8,213 tokens, just past the 8,192 boundary of two 4,096-token blocks. Five commands move it to at most about 8,340. The next boundary is 12,288, so no block is crossed and the pad stays as it is. An instance without [agent] allow gets no skill, so its prompt is byte-identical to today.

Checks run: tests/unit/stacklets/test_agent_allow.py (12 passed; red first with the module missing), make lint clean, make test-unit 3855 passed, 7 skipped. The design's open assumption holds: ./stack kettle status with STACK_DIR set runs an extension plugin without Docker (also checked with DOCKER_HOST pointing nowhere).

@arthware-dev

Copy link
Copy Markdown
Contributor Author

Validated on the test rig (port mode, 0.5 branch plus this PR, stack restart core and stack restart agent), with a throwaway extension kettle in the instance's extension dir (status, and boil <minutes> limited to 1-10; each run appends a line to a log file).

  • Before [agent] allow: stack kettle status from the agent's container is refused, exit 126. The built-in memory commands still run.
  • With allow = ["kettle", "up", "kettle grind"], no restart: kettle status and kettle boil 2 run (both in the kettle's log); up kettle and config --secrets are refused with 126.
  • stack restart agent writes skills/stack-commands/SKILL.md with both kettle commands and their HELP lines, and warns once each for up and kettle grind.
  • In chat, Marge: "is the kettle on? If not, please boil the water for 4 minutes." The agent called stack kettle status, then stack kettle boil 4, with no --help turn, and answered "The kettle was off with 0.8 litres of water. I've started boiling for 4 minutes." Both runs are in the kettle's log.
  • Asked to run stack up kettle and stack config --secrets, it tried both, both were refused, and it told her so.

The first chat attempt failed for an unrelated reason: every stack call from the agent's shell exited 127 because nanobot 0.3.5 runs commands without PATH. Fixed separately in #235; the run above is with that fix applied.

arthware-dev added a commit that referenced this pull request Oct 16, 2026
## Summary
- The agent's shell could not run any `stack` command: nanobot 0.3.5 starts commands without PATH, so the client's `#!/usr/bin/env python3` did not find the image's Python and every call exited 127. Calls only worked when the model asked for a login shell.
- The exec tool now passes PATH through. Found while validating PR #233 on the test rig; with it, the agent ran an allowed extension command from chat.
@arthware-dev
arthware-dev merged commit ea6d566 into main Oct 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant