oh-my-copilot 5.6.2 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -0
- package/bridge/__pycache__/gyoshu_bridge.cpython-312.pyc +0 -0
- package/bridge/claude-md-coordinator.cjs +2 -2
- package/bridge/cli.cjs +81405 -76559
- package/bridge/mcp-server.cjs +40048 -32187
- package/bridge/runtime-cli.cjs +943 -199
- package/bridge/team-mcp.cjs +716 -125
- package/bridge/team.js +1066 -290
- package/dist/__tests__/cli-win32-warning.test.js +7 -0
- package/dist/__tests__/cli-win32-warning.test.js.map +1 -1
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.d.ts +2 -0
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.d.ts.map +1 -0
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.js +294 -0
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.js.map +1 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.d.ts +2 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.d.ts.map +1 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.js +68 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.js.map +1 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.d.ts +2 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.d.ts.map +1 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.js +171 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.js.map +1 -0
- package/dist/__tests__/session-end-process-exit.test.js +70 -2
- package/dist/__tests__/session-end-process-exit.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-conflicts-json.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-conflicts-json.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-conflicts-json.test.js +46 -0
- package/dist/cli/__tests__/doctor-conflicts-json.test.js.map +1 -0
- package/dist/cli/__tests__/team.test.js +67 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/wait-teleport-json.test.d.ts +2 -0
- package/dist/cli/__tests__/wait-teleport-json.test.d.ts.map +1 -0
- package/dist/cli/__tests__/wait-teleport-json.test.js +64 -0
- package/dist/cli/__tests__/wait-teleport-json.test.js.map +1 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.d.ts +2 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.d.ts.map +1 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.js +110 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.js.map +1 -0
- package/dist/cli/commands/__tests__/team-status-json.test.d.ts +2 -0
- package/dist/cli/commands/__tests__/team-status-json.test.d.ts.map +1 -0
- package/dist/cli/commands/__tests__/team-status-json.test.js +141 -0
- package/dist/cli/commands/__tests__/team-status-json.test.js.map +1 -0
- package/dist/cli/commands/factory.d.ts +2 -0
- package/dist/cli/commands/factory.d.ts.map +1 -1
- package/dist/cli/commands/factory.js +8 -1
- package/dist/cli/commands/factory.js.map +1 -1
- package/dist/cli/commands/smoke.d.ts +36 -0
- package/dist/cli/commands/smoke.d.ts.map +1 -0
- package/dist/cli/commands/smoke.js +169 -0
- package/dist/cli/commands/smoke.js.map +1 -0
- package/dist/cli/commands/team.d.ts +2 -0
- package/dist/cli/commands/team.d.ts.map +1 -1
- package/dist/cli/commands/team.js +124 -10
- package/dist/cli/commands/team.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +70 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/win32-warning.d.ts +7 -1
- package/dist/cli/win32-warning.d.ts.map +1 -1
- package/dist/cli/win32-warning.js +9 -1
- package/dist/cli/win32-warning.js.map +1 -1
- package/dist/constants/names.d.ts +1 -0
- package/dist/constants/names.d.ts.map +1 -1
- package/dist/constants/names.js +1 -0
- package/dist/constants/names.js.map +1 -1
- package/dist/factory/__tests__/check-run.test.js +2 -2
- package/dist/factory/__tests__/check-run.test.js.map +1 -1
- package/dist/factory/__tests__/listener.test.js +1 -1
- package/dist/factory/__tests__/listener.test.js.map +1 -1
- package/dist/factory/__tests__/status.test.js +42 -0
- package/dist/factory/__tests__/status.test.js.map +1 -1
- package/dist/factory/listener.d.ts.map +1 -1
- package/dist/factory/listener.js +7 -8
- package/dist/factory/listener.js.map +1 -1
- package/dist/factory/status.d.ts +19 -1
- package/dist/factory/status.d.ts.map +1 -1
- package/dist/factory/status.js +49 -8
- package/dist/factory/status.js.map +1 -1
- package/dist/factory/watchdog.d.ts.map +1 -1
- package/dist/factory/watchdog.js +14 -0
- package/dist/factory/watchdog.js.map +1 -1
- package/dist/factory/watchdog.test.js +45 -0
- package/dist/factory/watchdog.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/action-runner.test.js +5 -0
- package/dist/hooks/session-end/__tests__/action-runner.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/chain-enqueuer.test.js +186 -1
- package/dist/hooks/session-end/__tests__/chain-enqueuer.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.d.ts +2 -0
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.d.ts.map +1 -0
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.js +49 -0
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.js.map +1 -0
- package/dist/hooks/session-end/__tests__/foreground-bootstrap.test.js +30 -1
- package/dist/hooks/session-end/__tests__/foreground-bootstrap.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/spawn-next.test.js +104 -3
- package/dist/hooks/session-end/__tests__/spawn-next.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/worker.test.js +6 -0
- package/dist/hooks/session-end/__tests__/worker.test.js.map +1 -1
- package/dist/hooks/session-end/action-runner.d.ts.map +1 -1
- package/dist/hooks/session-end/action-runner.js +2 -1
- package/dist/hooks/session-end/action-runner.js.map +1 -1
- package/dist/hooks/session-end/chain-enqueuer.d.ts +70 -7
- package/dist/hooks/session-end/chain-enqueuer.d.ts.map +1 -1
- package/dist/hooks/session-end/chain-enqueuer.js +354 -82
- package/dist/hooks/session-end/chain-enqueuer.js.map +1 -1
- package/dist/hooks/session-end/foreground-bootstrap.d.ts.map +1 -1
- package/dist/hooks/session-end/foreground-bootstrap.js +24 -16
- package/dist/hooks/session-end/foreground-bootstrap.js.map +1 -1
- package/dist/hooks/session-end/index.d.ts +2 -3
- package/dist/hooks/session-end/index.d.ts.map +1 -1
- package/dist/hooks/session-end/index.js +11 -13
- package/dist/hooks/session-end/index.js.map +1 -1
- package/dist/hooks/session-end/spawn-next.d.ts +81 -4
- package/dist/hooks/session-end/spawn-next.d.ts.map +1 -1
- package/dist/hooks/session-end/spawn-next.js +141 -14
- package/dist/hooks/session-end/spawn-next.js.map +1 -1
- package/dist/hooks/session-end/wiki-foreground-bootstrap.d.ts +17 -0
- package/dist/hooks/session-end/wiki-foreground-bootstrap.d.ts.map +1 -0
- package/dist/hooks/session-end/wiki-foreground-bootstrap.js +20 -0
- package/dist/hooks/session-end/wiki-foreground-bootstrap.js.map +1 -0
- package/dist/hooks/session-end/worker.d.ts.map +1 -1
- package/dist/hooks/session-end/worker.js +11 -6
- package/dist/hooks/session-end/worker.js.map +1 -1
- package/dist/lib/__tests__/atomic-write.test.js +32 -1
- package/dist/lib/__tests__/atomic-write.test.js.map +1 -1
- package/dist/lib/__tests__/mode-state-lock.test.js +121 -12
- package/dist/lib/__tests__/mode-state-lock.test.js.map +1 -1
- package/dist/lib/atomic-write.d.ts +13 -3
- package/dist/lib/atomic-write.d.ts.map +1 -1
- package/dist/lib/atomic-write.js +29 -15
- package/dist/lib/atomic-write.js.map +1 -1
- package/dist/lib/mode-state-io.d.ts +2 -2
- package/dist/lib/mode-state-io.d.ts.map +1 -1
- package/dist/lib/mode-state-io.js +16 -10
- package/dist/lib/mode-state-io.js.map +1 -1
- package/dist/mcp/__tests__/host-smoke-tool.test.d.ts +2 -0
- package/dist/mcp/__tests__/host-smoke-tool.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/host-smoke-tool.test.js +174 -0
- package/dist/mcp/__tests__/host-smoke-tool.test.js.map +1 -0
- package/dist/mcp/disable-tools.d.ts.map +1 -1
- package/dist/mcp/disable-tools.js +1 -0
- package/dist/mcp/disable-tools.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +2 -0
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/shared/types.d.ts +11 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/smoke/__tests__/copilot-chain-scenario.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-chain-scenario.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-chain-scenario.test.js +154 -0
- package/dist/smoke/__tests__/copilot-chain-scenario.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.js +591 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.js +362 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.js +264 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-smoke.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-smoke.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-smoke.test.js +482 -0
- package/dist/smoke/__tests__/copilot-smoke.test.js.map +1 -0
- package/dist/smoke/copilot-binary.d.ts +21 -0
- package/dist/smoke/copilot-binary.d.ts.map +1 -0
- package/dist/smoke/copilot-binary.js +46 -0
- package/dist/smoke/copilot-binary.js.map +1 -0
- package/dist/smoke/copilot-chain-scenario.d.ts +61 -0
- package/dist/smoke/copilot-chain-scenario.d.ts.map +1 -0
- package/dist/smoke/copilot-chain-scenario.js +253 -0
- package/dist/smoke/copilot-chain-scenario.js.map +1 -0
- package/dist/smoke/copilot-sdk-driver.d.ts +189 -0
- package/dist/smoke/copilot-sdk-driver.d.ts.map +1 -0
- package/dist/smoke/copilot-sdk-driver.js +637 -0
- package/dist/smoke/copilot-sdk-driver.js.map +1 -0
- package/dist/smoke/copilot-sdk-scenarios.d.ts +200 -0
- package/dist/smoke/copilot-sdk-scenarios.d.ts.map +1 -0
- package/dist/smoke/copilot-sdk-scenarios.js +620 -0
- package/dist/smoke/copilot-sdk-scenarios.js.map +1 -0
- package/dist/smoke/copilot-session-env.d.ts +75 -0
- package/dist/smoke/copilot-session-env.d.ts.map +1 -0
- package/dist/smoke/copilot-session-env.js +154 -0
- package/dist/smoke/copilot-session-env.js.map +1 -0
- package/dist/smoke/copilot-session-eval.d.ts +90 -0
- package/dist/smoke/copilot-session-eval.d.ts.map +1 -0
- package/dist/smoke/copilot-session-eval.js +323 -0
- package/dist/smoke/copilot-session-eval.js.map +1 -0
- package/dist/smoke/copilot-smoke.d.ts +166 -0
- package/dist/smoke/copilot-smoke.d.ts.map +1 -0
- package/dist/smoke/copilot-smoke.js +688 -0
- package/dist/smoke/copilot-smoke.js.map +1 -0
- package/dist/smoke/process-utils.d.ts +47 -0
- package/dist/smoke/process-utils.d.ts.map +1 -0
- package/dist/smoke/process-utils.js +99 -0
- package/dist/smoke/process-utils.js.map +1 -0
- package/dist/team/__tests__/runtime-v2.monitor.test.js +35 -0
- package/dist/team/__tests__/runtime-v2.monitor.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +13 -0
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/sdk-transport.runtime.test.d.ts +2 -0
- package/dist/team/__tests__/sdk-transport.runtime.test.d.ts.map +1 -0
- package/dist/team/__tests__/sdk-transport.runtime.test.js +96 -0
- package/dist/team/__tests__/sdk-transport.runtime.test.js.map +1 -0
- package/dist/team/__tests__/sdk-transport.test.d.ts +2 -0
- package/dist/team/__tests__/sdk-transport.test.d.ts.map +1 -0
- package/dist/team/__tests__/sdk-transport.test.js +650 -0
- package/dist/team/__tests__/sdk-transport.test.js.map +1 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.d.ts +2 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.d.ts.map +1 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.js +52 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.js.map +1 -0
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +8 -0
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/runtime-v2.d.ts +33 -0
- package/dist/team/runtime-v2.d.ts.map +1 -1
- package/dist/team/runtime-v2.js +338 -24
- package/dist/team/runtime-v2.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +5 -0
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/sdk-host.d.ts +48 -0
- package/dist/team/sdk-host.d.ts.map +1 -0
- package/dist/team/sdk-host.js +604 -0
- package/dist/team/sdk-host.js.map +1 -0
- package/dist/team/sdk-policy.d.ts +79 -0
- package/dist/team/sdk-policy.d.ts.map +1 -0
- package/dist/team/sdk-policy.js +186 -0
- package/dist/team/sdk-policy.js.map +1 -0
- package/dist/team/sdk-transport.d.ts +164 -0
- package/dist/team/sdk-transport.d.ts.map +1 -0
- package/dist/team/sdk-transport.js +266 -0
- package/dist/team/sdk-transport.js.map +1 -0
- package/dist/team/tmux-session.d.ts +7 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +80 -18
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/types.d.ts +2 -0
- package/dist/team/types.d.ts.map +1 -1
- package/dist/team/types.js.map +1 -1
- package/dist/tools/host-smoke-tool.d.ts +34 -0
- package/dist/tools/host-smoke-tool.d.ts.map +1 -0
- package/dist/tools/host-smoke-tool.js +133 -0
- package/dist/tools/host-smoke-tool.js.map +1 -0
- package/docs/CLAUDE.md +1 -1
- package/docs/DEVELOPERS.md +122 -0
- package/docs/HOOKS.md +34 -2
- package/docs/MIGRATION.md +194 -3
- package/docs/REFERENCE.md +95 -1
- package/docs/TOOLS.md +32 -1
- package/package.json +11 -2
- package/plugin.json +1 -1
- package/scripts/dev/repro-state-lock.mjs +1 -1
- package/scripts/lib/atomic-write.mjs +4 -2
- package/scripts/lib/state-lock.mjs +21 -12
- package/scripts/run.cjs +141 -18
- package/scripts/session-start.mjs +13 -0
- package/scripts/wiki-session-end.mjs +2 -1
- package/templates/hooks/lib/atomic-write.mjs +4 -2
package/docs/MIGRATION.md
CHANGED
|
@@ -8,6 +8,8 @@ This guide covers all migration paths for oh-my-copilot. Find your current versi
|
|
|
8
8
|
|
|
9
9
|
- [Unreleased: Team Instance Ownership](#unreleased-team-instance-ownership)
|
|
10
10
|
- [Unreleased: Cancellation Scope](#unreleased-cancellation-scope)
|
|
11
|
+
- [v5.7.0 → v5.8.0: SDK Team Transport](#v570--v580-sdk-team-transport)
|
|
12
|
+
- [v5.6.2 → v5.7.0: Smoke Harness](#v562--v570-smoke-harness)
|
|
11
13
|
- [v5.6.1 → v5.6.2: Postinstall Hook](#v561--v562-postinstall-hook)
|
|
12
14
|
- [v5.5.0 → v5.6.1: Fork Upgrade Guide](#v550--v561-fork-upgrade-guide)
|
|
13
15
|
- [v5.1.0 → v5.5.0: Fork Upgrade Guide](#v510--v550-fork-upgrade-guide)
|
|
@@ -122,6 +124,194 @@ locations and descendants of system temp/OS roots are never used as roots.
|
|
|
122
124
|
|
|
123
125
|
---
|
|
124
126
|
|
|
127
|
+
## v5.7.0 → v5.8.0: SDK Team Transport
|
|
128
|
+
|
|
129
|
+
Fork **v5.8.0** adds an experimental headless SDK transport for `omg team`,
|
|
130
|
+
factory chains on Copilot, faster Copilot hooks and a Copilot smoke job in CI.
|
|
131
|
+
There is nothing to migrate: no config, command or skill is renamed or
|
|
132
|
+
removed, and every default is unchanged. The team transport stays `pane`, the
|
|
133
|
+
SessionEnd budget under Claude Code stays 300 ms, and the CI smoke job skips
|
|
134
|
+
without its secret.
|
|
135
|
+
|
|
136
|
+
- Update with `npm install -g oh-my-copilot@5.8.0` or
|
|
137
|
+
`copilot plugin update oh-my-copilot@omc`.
|
|
138
|
+
|
|
139
|
+
### Adopting `--transport sdk`
|
|
140
|
+
|
|
141
|
+
The transport is experimental and opt-in. It needs `copilot` on PATH and the
|
|
142
|
+
optional SDK, installed the same way as for smoke tier 2:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm i -g @github/copilot-sdk --omit=optional --ignore-scripts
|
|
146
|
+
omg team 2:copilot --transport sdk "<task>"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
To opt in for every team, set it in `.copilot/omg.jsonc`:
|
|
150
|
+
|
|
151
|
+
```jsonc
|
|
152
|
+
{
|
|
153
|
+
"team": {
|
|
154
|
+
"transport": "sdk",
|
|
155
|
+
"sdk": { "maxCreditsPerWorker": 10 }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- **`team.sdk.maxCreditsPerWorker`** caps each worker (default 10, a
|
|
161
|
+
positive number; env `OMC_TEAM_SDK_MAX_CREDITS`). At the cap the host aborts
|
|
162
|
+
the turn, takes no more prompts, and `omg team status` shows the worker as
|
|
163
|
+
`capped`.
|
|
164
|
+
- **Permissions are a policy, not a sandbox.** There is no allow-all. The
|
|
165
|
+
host allows writes only in the worker's worktree and the team state root,
|
|
166
|
+
and MCP calls only to the plugin server. The shell check is a denylist: it
|
|
167
|
+
blocks team control other than `team api`, `omg smoke`, `tmux`/`psmux`,
|
|
168
|
+
`git push`, and the `shell(<prefix>)` entries of `workerDenyTools`, but a
|
|
169
|
+
command built indirectly, such as a script file or an alias, is not caught.
|
|
170
|
+
- **Excluded tools.** `task`, `run_dynamic_workflow`,
|
|
171
|
+
`dynamic_workflows_manage`, `write_agent`, `read_agent`, `list_agents`,
|
|
172
|
+
`search_code_subagent` and the plugin's `host_smoke` are removed from each
|
|
173
|
+
worker session.
|
|
174
|
+
- **Rejected at start:** `--auto-merge`, non-copilot workers, scaling, and an
|
|
175
|
+
explicitly assigned reviewer-contract role (`critic`, `code-reviewer`,
|
|
176
|
+
`security-reviewer`, `test-engineer`). Keep those teams on `pane`.
|
|
177
|
+
|
|
178
|
+
Details are in
|
|
179
|
+
[Headless SDK workers](./REFERENCE.md#headless-sdk-workers---transport-sdk-experimental).
|
|
180
|
+
|
|
181
|
+
### Factory chains on Copilot
|
|
182
|
+
|
|
183
|
+
Software-factory chains now run past their first link on Copilot CLI. Before,
|
|
184
|
+
a Copilot link got a random session id (Copilot has no `--session-id`), its
|
|
185
|
+
SessionEnd found no ledger, and the chain stopped silently after one link.
|
|
186
|
+
Nothing to configure:
|
|
187
|
+
|
|
188
|
+
- The spawner (factory listener or SessionEnd worker) sets
|
|
189
|
+
`OMC_CHAIN_LINK=<link id>` on each Copilot link. SessionEnd resolves the
|
|
190
|
+
link as `OMC_CHAIN_LINK`, then the host session id, and trusts the variable
|
|
191
|
+
only when it names a `host: "copilot"` ledger bound to the ending session
|
|
192
|
+
by that link's SessionStart, so a nested session cannot end its parent's
|
|
193
|
+
link. A duplicate SessionEnd gets the same chain back. Claude links keep
|
|
194
|
+
`--session-id`.
|
|
195
|
+
- Copilot's SessionEnd reason `complete` now counts as success, so route keys
|
|
196
|
+
such as `success:*` match a finished Copilot link.
|
|
197
|
+
- Link ledgers record `chainLink`, `host`, `createdAt` and `parentLink`, and
|
|
198
|
+
each link's SessionEnd closes its ledger once (`closedAt`, `hostSessionId`,
|
|
199
|
+
`outcome`, `decision`). The watchdog skips a closed ledger only when it
|
|
200
|
+
advanced the chain or left a stop marker, and `omg factory status` lists
|
|
201
|
+
each chain's links.
|
|
202
|
+
- A ledger's `maxStageVisits` now applies to the whole chain, not just the
|
|
203
|
+
first link; values above 99 clamp to 99 (before, they fell back to 2 on
|
|
204
|
+
later links).
|
|
205
|
+
- Under a dev plugin root (`omg --plugin-dir`), Copilot links get
|
|
206
|
+
`--plugin-dir` too, and the SessionEnd worker forwards `OMC_PLUGIN_ROOT`,
|
|
207
|
+
unless the root overlaps the link's working directory.
|
|
208
|
+
- `omg smoke copilot --tier 2 --scenario chain` runs a real two-link chain
|
|
209
|
+
(about 2 premium requests). It is opt-in and not part of `all`.
|
|
210
|
+
|
|
211
|
+
Copilot fires `sessionEnd` after every turn, so a link that a Stop hook
|
|
212
|
+
continues into a second turn hands off after its first turn; the closed
|
|
213
|
+
ledger prevents a second hand-off. Ledgers written by an older version have
|
|
214
|
+
no `host` field: a Copilot link
|
|
215
|
+
spawned before the upgrade still ends its chain after one link. Guardrails
|
|
216
|
+
(serial lock, daily cap), `COPILOT_ALLOW_ALL=false` and the AFK permission
|
|
217
|
+
profile are unchanged. See
|
|
218
|
+
[Factory chains on Copilot](./REFERENCE.md#factory-chains-on-copilot).
|
|
219
|
+
|
|
220
|
+
### Hook environment knobs
|
|
221
|
+
|
|
222
|
+
Under Copilot the SessionEnd foreground budget is now 1500 ms and the audited
|
|
223
|
+
generic hooks run in a Worker thread. Claude Code is unchanged. Two variables
|
|
224
|
+
tune this:
|
|
225
|
+
|
|
226
|
+
| Variable | Effect |
|
|
227
|
+
| --- | --- |
|
|
228
|
+
| `OMC_SESSION_END_BUDGET_MS` | Overrides the SessionEnd foreground budget on both hosts, an integer from 1 to 60000. Default 1500 under Copilot, 300 under Claude Code. |
|
|
229
|
+
| `OMC_COPILOT_HOOK_WORKER=0` | Turns off Worker routing under Copilot, so every generic hook takes the child path again. |
|
|
230
|
+
|
|
231
|
+
See [SessionEnd](./HOOKS.md#sessionend) in the hooks reference.
|
|
232
|
+
|
|
233
|
+
### CI secret for the Copilot smoke job
|
|
234
|
+
|
|
235
|
+
The new `smoke` job in `.github/workflows/ci.yml` needs the repository secret
|
|
236
|
+
`COPILOT_GITHUB_TOKEN`, a fine-grained personal access token of a user with
|
|
237
|
+
an active Copilot subscription and the "Copilot Requests" account
|
|
238
|
+
permission. Without it the job passes with a skip notice. With it, PRs and
|
|
239
|
+
branch pushes run the free static checks, and pushed `v*` tags also run the
|
|
240
|
+
default scenarios, about 2 premium requests billed to that user. Setup is in
|
|
241
|
+
[DEVELOPERS.md](DEVELOPERS.md#tier-2-in-ci).
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## v5.6.2 → v5.7.0: Smoke Harness
|
|
246
|
+
|
|
247
|
+
Fork **v5.7.0** adds a headless smoke harness and ports upstream
|
|
248
|
+
oh-my-claudecode `dev` 486b85bbb..bcaceb136. There is nothing to migrate: no
|
|
249
|
+
config, command or skill is renamed or removed.
|
|
250
|
+
|
|
251
|
+
- Update with `npm install -g oh-my-copilot@5.7.0` or
|
|
252
|
+
`copilot plugin update oh-my-copilot@omc`.
|
|
253
|
+
|
|
254
|
+
### Adopting `omg smoke copilot` and `host_smoke`
|
|
255
|
+
|
|
256
|
+
Build first (`npm run build`); the MCP check spawns
|
|
257
|
+
`dist/mcp/standalone-server.js`.
|
|
258
|
+
|
|
259
|
+
| Tier | Command | Cost |
|
|
260
|
+
| --- | --- | --- |
|
|
261
|
+
| 0 | `omg smoke copilot --tier 0` | Free, no model call |
|
|
262
|
+
| 1 | `omg smoke copilot --tier 1` | 1 premium request |
|
|
263
|
+
| 2 static | `omg smoke copilot --sdk-static` | Free, no model call |
|
|
264
|
+
| 2 | `omg smoke copilot --tier 2 [--scenario all]` | About 1 premium request per scenario; the default `smoke` + `guardrail` costs about 2 |
|
|
265
|
+
|
|
266
|
+
`--max-credits` (default and minimum 30) caps a run. The CLI has no opt-in
|
|
267
|
+
gate. The MCP tool `host_smoke` has two, set as env vars on the MCP server:
|
|
268
|
+
|
|
269
|
+
- **`OMC_SMOKE_ALLOW_LIVE=1`** allows `{ "tier": 1 }` and tier 2 with
|
|
270
|
+
scenarios. `{ "tier": 2, "scenarios": [] }` runs the free static checks
|
|
271
|
+
without it.
|
|
272
|
+
- **`OMC_SMOKE_ALLOW_ANY_ROOT=1`** allows a `pluginRoot` other than the
|
|
273
|
+
server's own package root.
|
|
274
|
+
|
|
275
|
+
`OMC_DISABLE_TOOLS=smoke` hides the tool. From vitest, `npm run test:live`
|
|
276
|
+
runs tier 0 whenever the `copilot` binary resolves. It runs tier 1 only with
|
|
277
|
+
`OMC_LIVE_SMOKE=1` and the tier 2 scenarios only with `OMC_LIVE_SMOKE=2`.
|
|
278
|
+
Details are in
|
|
279
|
+
[DEVELOPERS.md](DEVELOPERS.md#headless-smoke-against-a-local-build).
|
|
280
|
+
|
|
281
|
+
### Optional SDK for tier 2
|
|
282
|
+
|
|
283
|
+
`@github/copilot-sdk` is an optional peer dependency; installing
|
|
284
|
+
oh-my-copilot never pulls it in. Install it only if you want tier 2:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npm i -g @github/copilot-sdk --omit=optional --ignore-scripts
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`--ignore-scripts` avoids a CMake source build on Windows. If a global
|
|
291
|
+
install still adds the SDK's bundled platform runtime (about 128 MB), delete
|
|
292
|
+
it; tier 2 always uses the installed `copilot` binary.
|
|
293
|
+
|
|
294
|
+
### `OMC_HOOK_FAIL_CLOSED=1`
|
|
295
|
+
|
|
296
|
+
The variable is opt-in and unset by default. Without it, the Copilot hook
|
|
297
|
+
adapter turns a hook's non-zero exit into `0` plus one `[omg-hook]` stderr
|
|
298
|
+
line, and a hook that times out in `scripts/run.cjs` exits `0`, so hooks fail
|
|
299
|
+
open. With `OMC_HOOK_FAIL_CLOSED=1` the adapter keeps the hook's exit code,
|
|
300
|
+
and a hook timeout in `scripts/run.cjs` exits `124`. Exit `2` on
|
|
301
|
+
PermissionRequest means deny in both modes. The smoke harness sets the
|
|
302
|
+
variable for its own sessions.
|
|
303
|
+
|
|
304
|
+
### Team env passthrough on POSIX (#4236)
|
|
305
|
+
|
|
306
|
+
`OMC_TEAM_WORKER_ENV_PASSTHROUGH` keeps the same configuration. On POSIX tmux
|
|
307
|
+
teams its values are no longer inlined into the worker pane's command line.
|
|
308
|
+
They are written to a private `0600` file in a `0700` directory, which the
|
|
309
|
+
pane's `/bin/sh` sources and removes before it execs the login shell. Nothing
|
|
310
|
+
changes for you, but the values no longer appear in `ps` output or tmux
|
|
311
|
+
`pane_start_command`.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
125
315
|
## v5.6.1 → v5.6.2: Postinstall Hook
|
|
126
316
|
|
|
127
317
|
Fork **v5.6.2** ports upstream oh-my-claudecode `dev` 4280efb1f..486b85bbb.
|
|
@@ -240,9 +430,10 @@ it there.
|
|
|
240
430
|
|
|
241
431
|
### Known limitations
|
|
242
432
|
|
|
243
|
-
- **Copilot factory chains stop after one link
|
|
244
|
-
|
|
245
|
-
|
|
433
|
+
- **Copilot factory chains stop after one link** (fixed in v5.8.0, see
|
|
434
|
+
[Factory chains on Copilot](#factory-chains-on-copilot)).
|
|
435
|
+
Copilot CLI has no `--session-id`, so the next link cannot be pinned;
|
|
436
|
+
`omg factory status` and the watchdog report the stalled chain.
|
|
246
437
|
- **git-guardrails under Copilot on Windows is not live-verified.** The hook
|
|
247
438
|
is projected and unit-tested, but its PreToolUse payload has not been
|
|
248
439
|
exercised in a live Copilot session on Windows. Plant a violation (for
|
package/docs/REFERENCE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Complete reference for oh-my-copilot. For quick start, see the main [README.md](../README.md).
|
|
4
4
|
|
|
5
|
-
For v5.
|
|
5
|
+
For v5.8.0, the plugin ships 20 agents, 61 skills, 21 command files, and one configured MCP server exposing exactly 56 tools.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -139,6 +139,7 @@ If both configurations exist, **project-scoped takes precedence** over global:
|
|
|
139
139
|
| `OMC_FREE_MEMORY_THRESHOLD` | `256` | Free memory threshold in MB. Gate blocks operations when free memory falls below this. Set to negative value to disable. |
|
|
140
140
|
| `OMC_MAX_SIBLING_SESSIONS` | `8` | Maximum concurrent OMC sessions before gating expensive operations. Set to negative value to disable. |
|
|
141
141
|
| `OMC_HOST_LOAD_GATE_DISABLED` | _(unset)_ | Set to any value to disable the host load gate entirely. |
|
|
142
|
+
| `OMC_CHAIN_LINK` | _(set by the spawner)_ | Internal. The chain-link id of a Copilot factory link, set on the spawned `copilot` process by the factory listener or the SessionEnd worker. Do not set it yourself: SessionEnd only trusts it when it names a `host: "copilot"` ledger, and a finished link is a no-op. See [Factory chains on Copilot](#factory-chains-on-copilot). |
|
|
142
143
|
|
|
143
144
|
#### Host Load Gate
|
|
144
145
|
|
|
@@ -609,12 +610,22 @@ omg ask claude --agent-prompt executor --prompt "create an implementation plan"
|
|
|
609
610
|
```bash
|
|
610
611
|
omg team 2:codex "review auth flow"
|
|
611
612
|
omg team status review-auth-flow
|
|
613
|
+
omg team status review-auth-flow --json
|
|
614
|
+
omg team status --json # auto-resolves the team name when exactly one team is active
|
|
612
615
|
omg team shutdown review-auth-flow --force
|
|
613
616
|
omg team api claim-task --input '{"team_name":"auth-review","task_id":"1","worker":"worker-1"}' --json
|
|
614
617
|
```
|
|
615
618
|
|
|
616
619
|
Supported entrypoints: direct start (`omg team [N:agent] "<task>"`), `status`, `shutdown`, and `api`.
|
|
617
620
|
|
|
621
|
+
`team status [team-name] --json` prints the same status data as the human-readable
|
|
622
|
+
report (team/instance id, phase, workspace/worktree mode, worker list with worktree
|
|
623
|
+
metadata, SDK worker fields — provider liveness, state, turns, credits, premium
|
|
624
|
+
requests — task counts, and leader guidance) as a single JSON document instead of
|
|
625
|
+
text, with `ok: false` when no team state is found. `--json` can appear before or
|
|
626
|
+
after the team name and is never mistaken for one; when the team name is omitted
|
|
627
|
+
and exactly one team is active, it is resolved automatically.
|
|
628
|
+
|
|
618
629
|
Startup reserves the team name for an immutable instance. Shutdown and job cleanup
|
|
619
630
|
require matching instance and worker-launch evidence; `--force` skips graceful
|
|
620
631
|
waits but does not bypass ownership checks. Missing or corrupt evidence preserves
|
|
@@ -676,6 +687,16 @@ Worker panes run unattended, so each provider launches with its own auto-approve
|
|
|
676
687
|
|
|
677
688
|
- `disableExternalLLM` / `OMC_SECURITY=strict` allows only the current host CLI's workers. See [SECURITY.md](../SECURITY.md).
|
|
678
689
|
|
|
690
|
+
#### Headless SDK workers (`--transport sdk`, experimental)
|
|
691
|
+
|
|
692
|
+
`omg team 2:copilot --transport sdk "<task>"` (or `team.transport: "sdk"`) runs each copilot worker as a detached `omg team sdk-host` process that owns one `@github/copilot-sdk` session, with no pane and no multiplexer. It needs `@github/copilot-sdk` installed (`npm i -g @github/copilot-sdk --omit=optional --ignore-scripts`).
|
|
693
|
+
|
|
694
|
+
- **No allow-all.** The host answers every permission request. Writes are allowed only inside the worker's worktree and the team state root. Reads also allow the plugin root. MCP calls are allowed only to the plugin server.
|
|
695
|
+
- **The shell policy is a denylist, not a sandbox.** It rejects team control other than `team api`, `omg smoke`, `tmux`/`psmux` and `git push`, plus `shell(<prefix>)` entries of `workerDenyTools`, including quoted spellings such as `"omg" team shutdown`. A command built indirectly (a script file, an alias, `Invoke-Expression`, an encoded command) is not caught. Treat the write-path check, the credit cap and the per-worker `COPILOT_HOME` as the boundaries.
|
|
696
|
+
- **Sub-agent tools are removed**: `task`, `run_dynamic_workflow`, `dynamic_workflows_manage`, `write_agent`, `read_agent`, `list_agents`, `search_code_subagent` and the plugin's `host_smoke`.
|
|
697
|
+
- **Credit cap.** `team.sdk.maxCreditsPerWorker` (default 10, must be a positive number; env `OMC_TEAM_SDK_MAX_CREDITS`) is enforced by the host: at the cap it aborts the turn and takes no more prompts, and `omg team status` shows the worker as `capped` with a reassign recommendation.
|
|
698
|
+
- **Unsupported under sdk:** `--auto-merge`, non-copilot workers, scaling, dead-worker recovery, and an **explicitly** assigned reviewer role with a verdict contract (`critic`, `code-reviewer`, `security-reviewer`, `test-engineer` — set directly via a task's `role` or via `team.roleRouting`); startup rejects them. A contract role merely *inferred* from task text (with no explicit assignment) is not rejected: the role is dropped and the worker runs as a plain executor instead, since an SDK host never exits between turns and so could never have its verdict file read anyway.
|
|
699
|
+
|
|
679
700
|
### `omg session search`
|
|
680
701
|
|
|
681
702
|
```bash
|
|
@@ -732,6 +753,73 @@ omg lookout scan --strict # exit 1 when review is recommended (for scripts)
|
|
|
732
753
|
- `--json` emits the full report; exit codes: `0` for every successful non-strict scan and for strict scans without a review-recommended verdict, `1` for `--strict` with a review-recommended verdict, `2` for a usage/scan error
|
|
733
754
|
- High-risk verdicts pair with approval gates and checkpoints: `omg graph run --approval-mode remote --checkpoint`, `omg checkpoint create`
|
|
734
755
|
|
|
756
|
+
### `omg factory`
|
|
757
|
+
|
|
758
|
+
`omg factory init` seeds the project route table `.omg/factory-routes.json`, `omg factory listen` runs the HMAC-verified tracker intake daemon, and `omg factory status [--json]` is the read-only chain audit. A chain is a series of headless AFK links: when a link's session ends, the SessionEnd hook routes its `outcome:reason` through the route table and the detached SessionEnd worker spawns the next link. Guardrails apply per link: one active link per intent (serial lock) and a daily cap of 10 (`OMC_DAILY_CHAIN_LIMIT`).
|
|
759
|
+
|
|
760
|
+
#### Factory chains on Copilot
|
|
761
|
+
|
|
762
|
+
Each link has a chain-link id and a ledger `.omg/state/factory/chain-<id>.json`, written by whoever spawns the link (the listener for the first link, the SessionEnd worker for later ones). The SessionEnd hook must find that ledger, so the link id has to reach the ending session:
|
|
763
|
+
|
|
764
|
+
- **Claude Code** pins it with `--session-id <id>`; the host session id is the link id.
|
|
765
|
+
- **Copilot CLI** has no `--session-id`, so the spawner sets `OMC_CHAIN_LINK=<id>` on the `copilot` process. Copilot passes its env to hook processes, and SessionEnd resolves the link as `OMC_CHAIN_LINK` first, then the host session id.
|
|
766
|
+
|
|
767
|
+
The link's own SessionStart binds the link to its host session: the first session that starts with `OMC_CHAIN_LINK` naming an open Copilot ledger records itself as `boundSessionId` (an exclusive `chain-<id>.json.bind` file). `OMC_CHAIN_LINK` is trusted at SessionEnd only when `chain-<id>.json` exists, records `host: "copilot"` and `chainLink: <id>`, is bound to the ending host session, and the ending session is not a Claude session (`CLAUDE_CODE_ENTRYPOINT` unset). A nested session that inherited the variable (a verify command's `copilot -p`, an MCP child, a team worker) therefore cannot end its parent's link. Otherwise SessionEnd falls back to the host session id and records `chain-link-rejected` in `chain-decisions.jsonl`. A link is decided once: a duplicate SessionEnd of the same host session gets the chain it enqueued back (`already-closed`, `replayed: enqueued`), so whichever hook's manifest wins still carries the chain, and the one job per host session spawns it once. A concurrent duplicate waits briefly for the deciding hook (`chain-<id>.json.claim`), then records `duplicate-session-end`. So setting the variable alone can neither inject a chain nor replay a finished link. If the hand-off fails after the decision, the ledger's `decision` is corrected to `manifest-unavailable`, `worker-spawn-failed`, or `enqueued-failed`, and the stored chain is dropped so it is never replayed. The detached SessionEnd worker never forwards `OMC_CHAIN_LINK`: the chain identity rides the durable manifest payload, and every spawned link gets its own value. `COPILOT_ALLOW_ALL` stays forced to `false` on every link, and the AFK permission profile is unchanged.
|
|
768
|
+
|
|
769
|
+
| Ledger field | Meaning |
|
|
770
|
+
|---|---|
|
|
771
|
+
| `chainLink`, `host`, `createdAt`, `parentLink` | Written at spawn: the link id, `copilot` or `claude`, and the link that spawned it. |
|
|
772
|
+
| `boundSessionId`, `boundAt` | Copilot only: the host session that started the link. |
|
|
773
|
+
| `closedAt`, `hostSessionId`, `endReason`, `outcome`, `decision`, `enqueuedChain` | Written once by the link's SessionEnd. The watchdog skips a closed ledger only when its decision advanced the chain or left a stop marker (`enqueued`, `chain-terminal`, `chain-loop-capped`, `human-gate`, `no-route` on failure); a link dropped by a guardrail, an invalid ledger, or an error is still flagged. |
|
|
774
|
+
| `maxStageVisits` | Stage-visit cap, carried from the first ledger to every later link (default 2, values above 99 clamp to 99). |
|
|
775
|
+
|
|
776
|
+
Copilot's SessionEnd reason for a finished `-p` link is `complete` (CLI 1.0.91), which counts as `success` alongside Claude's `other`, `prompt_input_exit`, and `logout`; route keys such as `success:*` match both hosts. When OMC runs from a dev plugin root (`omg --plugin-dir`, which sets `OMC_PLUGIN_ROOT`), Copilot links also get `--plugin-dir <root>`, and the SessionEnd worker forwards `OMC_PLUGIN_ROOT`, so every link loads the same plugin. The root is dropped when it overlaps the link's working directory, because an AFK link may write there and could rewrite the plugin's hooks; a chain over the plugin's own repo needs the plugin installed. On Windows a root containing `%` is dropped as well. Limitation: Copilot fires `sessionEnd` after every turn, also in `-p` mode, so a link that a Stop hook keeps going into a second turn hands off after its first turn. The closed ledger stops later turns from enqueuing again, but the next link may start while this one still runs. A single-prompt link is not affected. `omg factory status` lists each intent's links in spawn order with host, link id, and closeout, for example `环 2 link-2 [copilot] link=<id> 已结束 success→chain-loop-capped session=<host session>`.
|
|
777
|
+
|
|
778
|
+
### `omg smoke copilot`
|
|
779
|
+
|
|
780
|
+
Headless smoke check of a plugin root against the real GitHub Copilot CLI, in a throwaway `COPILOT_HOME`. Use it on a local build before opening a PR or tagging a release.
|
|
781
|
+
|
|
782
|
+
```bash
|
|
783
|
+
omg smoke copilot # tier 0: load check, no model call
|
|
784
|
+
omg smoke copilot --tier 1 # tier 0 + one live session, prompt on stdin (one premium request)
|
|
785
|
+
omg smoke copilot --plugin-root /path/to/oh-my-copilot --json
|
|
786
|
+
omg smoke copilot --tier 1 --keep-home --delegate --model <id> --max-credits 30 --timeout 180000
|
|
787
|
+
omg smoke copilot --sdk-static # tier 2 SDK static checks only, no model call
|
|
788
|
+
omg smoke copilot --tier 2 # tier 2 default scenarios smoke,guardrail (~2 premium requests, 1 per scenario)
|
|
789
|
+
omg smoke copilot --tier 2 --scenario skill,delegate # or --scenario all
|
|
790
|
+
omg smoke copilot --tier 2 --scenario chain # opt-in: a real two-link factory chain (~2 premium requests)
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
- **Tier 0** checks: `plugin.manifest`, `plugin.hooks`, `copilot.binary`, `copilot.plugin_list`, `copilot.skill_list`, `copilot.agents`, `mcp.list_tools`. `mcp.list_tools` spawns `dist/mcp/standalone-server.js`, so run `npm run build` first.
|
|
794
|
+
- **Tier 1** adds `session.exit`, `session.events`, `hooks.*` (including `hooks.adapter_errors`), `plugins.loaded`, `mcp.server_loaded`, `state.written`, and with `--delegate` `subagent.selected`. Hooks run with `OMC_HOOK_FAIL_CLOSED=1`; a `hook.end` without `success: true` fails its check, and `hooks.adapter_errors` fails on any `[omg-hook]` error line.
|
|
795
|
+
- **Tier 2** drives the installed `copilot` binary through the optional peer `@github/copilot-sdk` (install it with `npm i -g @github/copilot-sdk --omit=optional --ignore-scripts`; a nested `@github/copilot-sdk-<platform>` runtime package is unused and can be deleted, see [DEVELOPERS.md](./DEVELOPERS.md#tier-2--sdk-scenarios)). It runs the tier 0 checks, then the SDK static checks `sdk.available`, `sdk.runtime`, `sdk.plugins`, `sdk.skills`, `sdk.agents`, `sdk.mcp`, `sdk.tools_excluded` (our `<server>-host_smoke` tool is absent from the session's tool list), then the chosen scenarios. It does not run the tier 1 `-p` session. `--sdk-static` runs no scenario and makes no model call.
|
|
796
|
+
- **Tier 2 scenarios** yield checks prefixed `scn.<name>.`. Each one also gets `scn.<name>.exit`, `scn.<name>.adapter_errors` (fails on any `[omg-hook]` or `[run.cjs]` hook stderr line, such as a hook timeout), and `scn.<name>.cost`. Each scenario costs about one premium request, so the default run costs about two.
|
|
797
|
+
|
|
798
|
+
| Scenario | Prompt exercises | Key checks |
|
|
799
|
+
|---|---|---|
|
|
800
|
+
| `smoke` (default) | a plain reply, no tools | `scn.smoke.reply`, `scn.smoke.hooks`, `scn.smoke.no_tools` |
|
|
801
|
+
| `guardrail` (default) | `git push --force` with `OMC_GIT_GUARDRAILS=1` against a bare remote | `scn.guardrail.denied`, `scn.guardrail.no_push`, `scn.guardrail.hooks` |
|
|
802
|
+
| `skill` | `/oh-my-copilot:plan` | `scn.skill.invoked` |
|
|
803
|
+
| `delegate` | a hand-off to `oh-my-copilot:architect` | `scn.delegate.selected`, `scn.delegate.hooks`, `scn.delegate.completed` |
|
|
804
|
+
| `chain` (opt-in, never in `all`) | two real `copilot -p` factory links: link 1 replies with one token, link 2 runs the project skill `/chain-ack` | `scn.chain.link1`, `scn.chain.spawned`, `scn.chain.inherited`, `scn.chain.closed`, `scn.chain.premium` |
|
|
805
|
+
|
|
806
|
+
- **`chain` scenario.** It does not use an SDK session. It seeds the sandbox with `omg factory init` plus a `success:*` route to stage `link-2`, pre-writes link 1's ledger, and spawns link 1 like a factory link (AFK profile, `--plugin-dir`, `OMC_CHAIN_LINK`). The chain then runs itself through the SessionEnd hook and worker, and stops at the stage-visit cap after link 2. The checks read the ledgers, decisions, stop marker, and both link sessions' `events.jsonl`: link 1 was resolved by `OMC_CHAIN_LINK`, link 2 was spawned and ended under its own inherited identity, the chain closed out (`loop-capped:link-2`, no open ledger), and both links together cost at most 2 premium requests. The links run on the CLI's default model without `--max-ai-credits`, because a factory link's argv carries no credit cap, so `--max-credits` does not bound them. `--timeout` applies to link 1 and again to the wait for link 2.
|
|
807
|
+
|
|
808
|
+
- **Tier 2 report** adds `sdk` (SDK version, runtime version, protocol version), `cost` (premium requests and credits summed over scenarios), and `artifacts.events` with one events JSONL path per scenario. Human output groups the `sdk.*` checks and each scenario's `scn.<name>.*` checks under a heading.
|
|
809
|
+
- **Model:** `--model` is optional. Copilot auto-selects when it is omitted (1.0.91 picked `mai-code-1.1-flash`); explicit ids may be rejected as unavailable.
|
|
810
|
+
- **Auth:** tier 1 reuses the login identity from your Copilot config, so `copilot /login` is enough. When a login is found the session drops `GH_TOKEN` and `GITHUB_TOKEN`, which would otherwise shadow it. `COPILOT_GITHUB_TOKEN` always passes through, and without a stored login (CI) all three do.
|
|
811
|
+
- `--max-credits` must be at least 30, the Copilot CLI minimum. At tier 1 it is passed as `--max-ai-credits`. At tier 2 it caps the whole run: the runtime also gets `--max-ai-credits`, a scenario whose credits pass what is left is aborted, and the remaining scenarios fail with `skipped: credit cap reached`. `--model` applies to tiers 1 and 2. `--timeout` applies per subprocess at tier 0, to the session at tier 1, and per scenario at tier 2; the MCP check is fixed at 10 s.
|
|
812
|
+
- Under a test runner (`VITEST` or `NODE_ENV=test`), a run that would call a model is refused with one `cli.guard` check before anything is spawned, unless `OMC_LIVE_SMOKE=1` (tier 1) or `OMC_LIVE_SMOKE=2` (tier 2 scenarios) is set.
|
|
813
|
+
- Exit codes: `0` all checks pass, `1` a check failed or an option was invalid, `2` skipped because the `copilot` binary or, at tier 2, `@github/copilot-sdk` did not resolve and nothing else failed. Resolution order is PATH, then `COPILOT_CLI_PATH`, then the WinGet install location on Windows, so a `copilot` on PATH wins over `COPILOT_CLI_PATH`.
|
|
814
|
+
- `--json` always prints a `SmokeReport`, with one `cli.error` check for invalid options. `--keep-home` keeps the temp homes and project and reports them as `artifacts.listHome` (tier 0) and `artifacts.copilotHome` (tier 1).
|
|
815
|
+
- The same check is exposed as the MCP tool `host_smoke` (arguments `tier`, `pluginRoot`, `model`, `maxCredits`, `timeoutMs`, `keepHome`, `delegate`, `scenarios`). It returns the report JSON with `isError` set when any check fails. Tier 1 and tier 2 with scenarios need `OMC_SMOKE_ALLOW_LIVE=1` in the MCP server env; tier 2 with `scenarios: []` is free and needs no opt-in, and a `pluginRoot` other than the server's own package root needs `OMC_SMOKE_ALLOW_ANY_ROOT=1`. To call it from Claude Code against a local build, register the standalone server:
|
|
816
|
+
|
|
817
|
+
```bash
|
|
818
|
+
claude mcp add omg-dev -- node C:/Code/OMC/dist/mcp/standalone-server.js
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
`OMC_DISABLE_TOOLS=smoke` hides `host_smoke`. See [DEVELOPERS.md](./DEVELOPERS.md#headless-smoke-against-a-local-build) for the developer workflow.
|
|
822
|
+
|
|
735
823
|
### Graph approval gates (remote approvals)
|
|
736
824
|
|
|
737
825
|
Graph runtime `human-approval` nodes support two gate styles via `omg graph run`:
|
|
@@ -1438,6 +1526,12 @@ From a shell, `omg doctor conflicts` checks for configuration conflicts. Among i
|
|
|
1438
1526
|
- **Legacy `COPILOT_CONFIG_DIR`**: warns when it is set. OMC reads only `COPILOT_HOME`, Copilot CLI's own variable; rename it.
|
|
1439
1527
|
- **`node` on PATH**: every hook runs `node`, and under Copilot a PreToolUse hook that cannot start denies the tool call, so a missing `node` blocks every tool.
|
|
1440
1528
|
|
|
1529
|
+
`omg doctor conflicts --json` prints the full conflict report (every check above, plus
|
|
1530
|
+
`hasConflicts`) as a single JSON document on stdout instead of the coloured human
|
|
1531
|
+
report; the human report is unchanged when `--json` is omitted. `--json` and
|
|
1532
|
+
`--plugin-dir` work both on `omg doctor` (flag form, e.g. `omg doctor --team-routing
|
|
1533
|
+
--json`) and on `omg doctor conflicts`/`omg doctor team-routing` directly.
|
|
1534
|
+
|
|
1441
1535
|
### Configure HUD Statusline
|
|
1442
1536
|
|
|
1443
1537
|
```bash
|
package/docs/TOOLS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> OMC provides MCP tools for state management, code intelligence, and data analysis.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The configured MCP server exposes exactly 56 tools.
|
|
6
6
|
|
|
7
7
|
Unlike skills that users invoke directly, tools are used internally by agents during task execution.
|
|
8
8
|
|
|
@@ -19,6 +19,7 @@ Unlike skills that users invoke directly, tools are used internally by agents du
|
|
|
19
19
|
- [Shared Memory](#shared-memory) — Cross-agent shared memory for team coordination
|
|
20
20
|
- [Skills](#skills) — Internal skill management tools
|
|
21
21
|
- [Deepinit Manifest](#deepinit-manifest) — Incremental AGENTS.md regeneration manifest
|
|
22
|
+
- [Host Smoke](#host-smoke) — Headless Copilot CLI smoke check of a plugin root
|
|
22
23
|
|
|
23
24
|
---
|
|
24
25
|
|
|
@@ -768,3 +769,33 @@ deepinit_manifest(action="check")
|
|
|
768
769
|
```
|
|
769
770
|
|
|
770
771
|
Used internally by the `deepinit` skill (`/oh-my-copilot:deepinit`) to enable incremental AGENTS.md regeneration instead of full re-scans.
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
## Host Smoke
|
|
776
|
+
|
|
777
|
+
Runs the same check as `omg smoke copilot` against a plugin root and returns the `SmokeReport` JSON. The result has `isError` set when any check fails. `OMC_DISABLE_TOOLS=smoke` hides it.
|
|
778
|
+
|
|
779
|
+
### Tool
|
|
780
|
+
|
|
781
|
+
#### `host_smoke`
|
|
782
|
+
|
|
783
|
+
| Argument | Type | Meaning |
|
|
784
|
+
| ------------ | ------- | ----------------------------------------------------------------------- |
|
|
785
|
+
| `tier` | 0, 1 or 2 | `0` load check without a model call (default); `1` adds one live session, one premium request; `2` adds `@github/copilot-sdk` static checks and SDK scenarios. Tier 1 needs `OMC_SMOKE_ALLOW_LIVE=1` in the server env |
|
|
786
|
+
| `scenarios` | string[] | Tier 2 only: `smoke`, `guardrail`, `skill`, `delegate`, or `all`. Omitted runs `smoke` and `guardrail`, about two premium requests (one per scenario), and needs `OMC_SMOKE_ALLOW_LIVE=1`. `[]` runs the SDK static checks only, with no model call and no opt-in |
|
|
787
|
+
| `pluginRoot` | string | Plugin root passed to `--plugin-dir`; defaults to the package root. Any other root needs `OMC_SMOKE_ALLOW_ANY_ROOT=1` |
|
|
788
|
+
| `model` | string | Tiers 1-2 model id; omitted means Copilot auto-selects at tier 1 and a cheap listed model, else auto, at tier 2 |
|
|
789
|
+
| `maxCredits` | integer | Tiers 1-2 AI credit cap, default and minimum 30. Tier 1 passes `--max-ai-credits`; tier 2 also aborts a scenario and skips the rest once the run passes it |
|
|
790
|
+
| `timeoutMs` | integer | Per subprocess at tier 0 (default 60000), for the session at tier 1 (default 180000), or per scenario at tier 2 (default 120000); the MCP check is fixed at 10 s |
|
|
791
|
+
| `keepHome` | boolean | Keep the temp `COPILOT_HOME` and project; paths are in `artifacts` |
|
|
792
|
+
| `delegate` | boolean | Tier 1: also exercise one delegation to `oh-my-copilot:architect` |
|
|
793
|
+
|
|
794
|
+
```
|
|
795
|
+
host_smoke(tier=0)
|
|
796
|
+
host_smoke(tier=2, scenarios=[]) # SDK static checks, free
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
Tier 2 needs the optional peer `@github/copilot-sdk` next to the package. Without it the `sdk.*` checks fail with an install hint and the report is marked `skipped`.
|
|
800
|
+
|
|
801
|
+
See [DEVELOPERS.md](./DEVELOPERS.md#headless-smoke-against-a-local-build) for the check list and how to register a local build in Claude Code.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-copilot",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "Multi-agent orchestration system for Copilot CLI - Inspired by oh-my-opencode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"test:ui": "vitest --ui --exclude \"tests/perf/**\"",
|
|
75
75
|
"test:run": "vitest run --exclude \"tests/perf/**\"",
|
|
76
76
|
"test:coverage": "vitest run --coverage --exclude \"tests/perf/**\"",
|
|
77
|
+
"test:live": "vitest run --config vitest.live.config.ts",
|
|
77
78
|
"prepack": "npm run build",
|
|
78
79
|
"lint": "eslint src",
|
|
79
80
|
"format": "prettier --write src/**/*.ts",
|
|
@@ -131,6 +132,14 @@
|
|
|
131
132
|
"typescript-eslint": "^8.53.0",
|
|
132
133
|
"vitest": "^4.1.11"
|
|
133
134
|
},
|
|
135
|
+
"peerDependencies": {
|
|
136
|
+
"@github/copilot-sdk": "^1.0.16"
|
|
137
|
+
},
|
|
138
|
+
"peerDependenciesMeta": {
|
|
139
|
+
"@github/copilot-sdk": {
|
|
140
|
+
"optional": true
|
|
141
|
+
}
|
|
142
|
+
},
|
|
134
143
|
"engines": {
|
|
135
144
|
"node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x"
|
|
136
145
|
},
|
|
@@ -159,5 +168,5 @@
|
|
|
159
168
|
"publishConfig": {
|
|
160
169
|
"access": "public"
|
|
161
170
|
},
|
|
162
|
-
"gitHead": "
|
|
171
|
+
"gitHead": "a63b41dee653b2fad61a2fdd840a24dd2350ffc5"
|
|
163
172
|
}
|
package/plugin.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Repro for
|
|
1
|
+
// Repro for issue #4146: mutual exclusion of the owner-file fallback in
|
|
2
2
|
// scripts/lib/state-lock.mjs (owner releases/exits during a reclaimer's liveness
|
|
3
3
|
// probe; the reclaimer must not quarantine a live replacement owner).
|
|
4
4
|
//
|
|
@@ -137,9 +137,11 @@ function readEmergencyJournal(path) {
|
|
|
137
137
|
} catch { return null; }
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// BigInt ids: NTFS file IDs exceed 2^53 once the MFT sequence number reaches
|
|
141
|
+
// 32, and a Number ino rounds distinct files onto the same value.
|
|
140
142
|
function fileIdentity(path) {
|
|
141
143
|
try {
|
|
142
|
-
const stat = statSync(path);
|
|
144
|
+
const stat = statSync(path, { bigint: true });
|
|
143
145
|
return { dev: stat.dev, ino: stat.ino };
|
|
144
146
|
} catch { return null; }
|
|
145
147
|
}
|
|
@@ -157,7 +159,7 @@ function sameFileIdentity(a, b) {
|
|
|
157
159
|
// On Windows, lstat returns dev=0, so skip dev comparison when on Windows
|
|
158
160
|
// unless both are non-zero (indicating a real comparison is possible)
|
|
159
161
|
const isWindows = process.platform === 'win32';
|
|
160
|
-
if (isWindows && (a.dev ===
|
|
162
|
+
if (isWindows && (a.dev === 0n || b.dev === 0n)) {
|
|
161
163
|
return true; // Skip dev comparison on Windows when either is 0
|
|
162
164
|
}
|
|
163
165
|
|
|
@@ -156,33 +156,41 @@ function sameOwner(left, right) {
|
|
|
156
156
|
return Boolean(left && right && left.pid === right.pid && left.processStart === right.processStart && left.nonce === right.nonce);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
// BigInt ids: NTFS file IDs exceed 2^53 once the MFT sequence number reaches
|
|
160
|
+
// 32, and a Number ino rounds distinct files onto the same value.
|
|
159
161
|
function ownerArtifactIdentity(path) {
|
|
160
162
|
try {
|
|
161
|
-
const stats = statSync(path);
|
|
163
|
+
const stats = statSync(path, { bigint: true });
|
|
162
164
|
return stats.isFile() ? { dev: stats.dev, ino: stats.ino } : null;
|
|
163
165
|
} catch {
|
|
164
166
|
return null;
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
/**
|
|
170
|
+
/** Mirrors sameFileIdentity in src/lib/atomic-write.ts: ino always, dev unless win32 reports 0 (#4156). */
|
|
169
171
|
function sameArtifactIdentity(a, b) {
|
|
170
172
|
if (a.ino !== b.ino) return false;
|
|
171
|
-
if (process.platform === 'win32' && (a.dev ===
|
|
173
|
+
if (process.platform === 'win32' && (a.dev === 0n || b.dev === 0n)) return true;
|
|
172
174
|
return a.dev === b.dev;
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
/** Remove only the exact dead publication that was inspected. */
|
|
176
178
|
function reclaimDeadOwner(path, observed, identity) {
|
|
177
|
-
// Fork fix: the liveness verdict can be seconds old (win32 probe), and the owner may have
|
|
178
|
-
// exited and been replaced meanwhile. Renaming a live replacement into quarantine lets a third
|
|
179
|
-
// contender publish before the restore, leaving two holders, so re-verify the exact artifact
|
|
180
|
-
// (owner record AND dev/ino, upstream #4149 checks only the latter) immediately before the rename.
|
|
181
|
-
const current = readOwner(path);
|
|
182
|
-
const currentIdentity = ownerArtifactIdentity(path);
|
|
183
|
-
if (current === 'absent' || !current || !sameOwner(current, observed) || !currentIdentity ||
|
|
184
|
-
!sameArtifactIdentity(currentIdentity, identity)) return 'changed';
|
|
185
179
|
const quarantinePath = `${path}.reclaim.${process.pid}.${randomUUID()}`;
|
|
180
|
+
// The liveness verdict can be seconds old (the win32 probe spawns PowerShell),
|
|
181
|
+
// and the identity alone cannot tell a replacement apart when its inode reuses
|
|
182
|
+
// the old one (or when the identity was captured after the probe). Renaming a
|
|
183
|
+
// live replacement into quarantine opens a window in which a third contender
|
|
184
|
+
// publishes, leaving two holders. Re-verify the exact artifact, owner record
|
|
185
|
+
// AND file identity, immediately before the rename. Bracketing the read with
|
|
186
|
+
// two stats binds the record that was read to the identity that was checked.
|
|
187
|
+
const before = ownerArtifactIdentity(path);
|
|
188
|
+
const current = readOwner(path);
|
|
189
|
+
const after = ownerArtifactIdentity(path);
|
|
190
|
+
if (current === 'absent' || !current || !sameOwner(current, observed) ||
|
|
191
|
+
!before || !after || !sameArtifactIdentity(before, identity) || !sameArtifactIdentity(after, identity)) {
|
|
192
|
+
return 'changed';
|
|
193
|
+
}
|
|
186
194
|
try {
|
|
187
195
|
renameSync(path, quarantinePath);
|
|
188
196
|
} catch (error) {
|
|
@@ -194,7 +202,8 @@ function reclaimDeadOwner(path, observed, identity) {
|
|
|
194
202
|
try {
|
|
195
203
|
moved = readOwner(quarantinePath);
|
|
196
204
|
movedIdentity = ownerArtifactIdentity(quarantinePath);
|
|
197
|
-
if (moved !== 'absent' && moved && movedIdentity &&
|
|
205
|
+
if (moved !== 'absent' && moved && movedIdentity &&
|
|
206
|
+
sameArtifactIdentity(movedIdentity, identity) &&
|
|
198
207
|
sameOwner(moved, observed)) {
|
|
199
208
|
try {
|
|
200
209
|
unlinkSync(quarantinePath);
|