feat(sdk): Copilot CLI tool compatibility (Layer A) + SubagentControl - #60
Open
simurg79 wants to merge 3 commits into
Open
feat(sdk): Copilot CLI tool compatibility (Layer A) + SubagentControl#60simurg79 wants to merge 3 commits into
simurg79 wants to merge 3 commits into
Conversation
An app's `default.agent.md` is force-merged into every session -- both its prompt and its `tools:`. The only escape hatch was the internal `pilotswarm-system-agent` prompt-layer kind, which app agents cannot reach, so authoring one agent that did not want the app-wide instructions meant weakening the default for every other agent in the deployment. Agents can now declare `inheritAppDefaults: false`. Such an agent is composed from the framework base plus its own prompt and tools only, and it is dropped from the prompt layer manifest. Omitting the field inherits, so every existing agent is unchanged. The flag is resolved from the bound agent definition at prompt-composition time rather than persisted on the session row, matching how agent redefinition is expected to take effect on the next turn -- so no migration is needed and no session can carry a stale copy of the decision. A single `inheritsAppDefaults()` resolver gates all four consumption points (session prompt, subagent prompt, app-default tool merge, layer manifest) so they cannot drift apart. This is a composition choice, not a security boundary: invariants that must hold for every session still belong in the framework base layer, which cannot be opted out of. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PLUGIN_DIRS is documented and parsed as a comma-separated list, and the SDK worker loads every entry (worker.ts Tier 4 iterates config.pluginDirs). The TUI/portal bootstrap did not: resolvePluginDir() returned dirs[0] and then overwrote process.env.PLUGIN_DIRS with that single path, so every dir after the first was discarded before the worker ever saw it. The failure is silent and the blast radius is bad: an overlay dir holding default.agent.md would simply not load, dropping the app default prompt and its force-merged tools with no warning. resolvePluginDirs() now returns the full list, --plugin accepts a comma-separated value, and system.md is resolved across all dirs (first match wins). Branding still reads only the first dir, so single-dir configurations resolve to exactly the same values as before. The same truncation in web/bin/serve.js --plugin handling is fixed too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
An agent file written for Copilot CLI names tools like `execute` and `read`. Those names mean nothing to a host such as Waldemort, so the same file could not run in both places without being rewritten for each. Authors kept two divergent copies. Introduce an opt-in compatibility profile. When enabled, an agent may declare Copilot tool names; the host registers adapters, and aliases resolve at load. Resolution happens once at load rather than per invocation so a bad tool name surfaces at startup instead of mid-turn. Every declared name is validated against what the host actually registered, and an unresolvable name is fatal: silently dropping it would let an agent run believing it has a tool it does not, and fail unpredictably much later. The profile is opt-in, so existing deployments see no behavior change. Add a SubagentControl capability. Callers previously had to scrape spawn and status results out of human-readable prose, which broke whenever the wording changed. Expose structured records instead, discriminated on an explicit `ok` field so a caller cannot mistake failure for success by testing truthiness. `renderSpawnAgentReport` moves to module scope and is exported, keeping the existing prose byte-identical for human-facing paths while the structured path bypasses it entirely. The capability is attached per turn and is absent for service and read-only tuner sessions, so its absence is meaningful rather than ambiguous. Each operation re-checks the turn boundary to prevent use after the turn ends. packages/sdk test:unit: 148 passed, 0 failed. tsc --noEmit clean; npm run build succeeds.
Collaborator
Author
|
Companion PR (Layer B, Waldemort adapters): microsoft/waldemort#37 — https://github.com/microsoft/waldemort/pull/37%0A%0AThat PR consumes the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Lets a single
.agent.mdfile run both locally in Copilot CLI and remotely through ahost such as Waldemort, with minimal changes. Previously an agent file naming Copilot
tools (
execute,read,edit, ...) meant nothing to a host, so authors maintainedtwo divergent copies of the same agent.
This is Layer A of a two-layer feature — the SDK half. Layer B is the Waldemort
adapter implementation, in a companion PR (cross-linked below).
Layer A contents
change. A parity fixture asserts the disabled path is byte-identical to today.
rather than per invocation, means a bad tool name surfaces at startup.
Dropping it silently would let an agent run believing it holds a tool it does not,
then fail unpredictably much later.
SubagentControlcapability. Structured spawn/status/wait records replaceprose-scraping. All unions discriminate on an explicit
okfield, so a caller cannotmistake failure for success by testing truthiness.
renderSpawnAgentReportis liftedto module scope and exported; human-facing prose stays byte-identical, and a
dedicated prose test pins that.
SubagentControlis attached per turn and is absent for service and read-onlytuner sessions, so absence is meaningful rather than ambiguous. Each operation
re-checks the turn boundary to prevent use after the turn ends.
Tests
packages/sdknpm run test:unit: 148 passed, 0 failed.tsc --noEmitclean;npm run buildsucceeds.New suites:
test/unit/subagent-control-hook.test.mjs,test/unit/spawn-agent-report-prose.test.mjs,test/unit/copilot-compat.test.mjs.Dependencies and merge order
This PR is stacked on PR #59 (app default overlay / PLUGIN_DIRS). Because that
branch lives on a fork, GitHub will not accept it as a base, so this PR targets
mainand its diff currently also contains #59's two commits. Review only the compat
commit (
5dd60be); once #59 merges, this diff collapses to compat-only on its own.The compat changes in
agent-loader.tsandworker.tsinterleave with #59's changesinside the same functions, so the two could not be separated without hand-editing
already-reviewed code. Merge #59 first.
This PR must merge and be released before the Waldemort side can ship. Waldemort
consumes
SubagentControland cannot bump its production pin (currently v0.5.26) untilthat release lands.
Not yet shippable
Merging this is necessary but not sufficient. The sequence is: this PR merges → the SDK
publishes a release → Waldemort bumps its manifest, lockfile, and override pin.