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
|
@@ -55,5 +55,12 @@ describe('CLI win32 platform warning (#923)', () => {
|
|
|
55
55
|
continued = true;
|
|
56
56
|
expect(continued).toBe(true);
|
|
57
57
|
});
|
|
58
|
+
it('should suppress the warning when --json is present in argv, even without tmux', async () => {
|
|
59
|
+
Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
|
|
60
|
+
vi.mocked(spawnSync).mockReturnValue({ status: 1 });
|
|
61
|
+
const { warnIfWin32 } = await import('../cli/win32-warning.js');
|
|
62
|
+
warnIfWin32(['node', 'bridge/cli.cjs', 'smoke', 'copilot', '--tier', '0', '--json']);
|
|
63
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
58
65
|
});
|
|
59
66
|
//# sourceMappingURL=cli-win32-warning.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-win32-warning.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-win32-warning.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-win32-warning.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-win32-warning.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-cjs-copilot-worker-routing.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/run-cjs-copilot-worker-routing.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { execFileSync, spawnSync } from 'child_process';
|
|
3
|
+
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from 'fs';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import { basename, join, relative } from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Fork (oh-my-copilot): under Copilot (OMC_HOOK_EVENT set) scripts/run.cjs runs
|
|
9
|
+
* the audited generic hooks in an in-process Worker instead of the win32
|
|
10
|
+
* `--generic-child-supervisor` child chain. Claude Code (no OMC_HOOK_EVENT)
|
|
11
|
+
* and OMC_COPILOT_HOOK_WORKER=0 keep the generic child path.
|
|
12
|
+
*/
|
|
13
|
+
const REPO_ROOT = join(__dirname, '..', '..');
|
|
14
|
+
const RUN_CJS_PATH = join(REPO_ROOT, 'scripts', 'run.cjs');
|
|
15
|
+
const NODE = process.execPath;
|
|
16
|
+
const runCjs = createRequire(__filename)(RUN_CJS_PATH);
|
|
17
|
+
/**
|
|
18
|
+
* A tiny --require preload that reports, via fd 3, whether it ever loaded
|
|
19
|
+
* inside a Worker thread, without touching stdout/stderr. It is passed on
|
|
20
|
+
* the *outer* `node` invocation, so it always loads once in the main thread
|
|
21
|
+
* regardless of which path run.cjs takes; a Worker thread inherits execArgv
|
|
22
|
+
* (and so --require) from its creator, so it loads a second time inside the
|
|
23
|
+
* Worker only when run.cjs actually spawned one. Writing only on the
|
|
24
|
+
* non-main-thread load keeps the signal unambiguous: a 'worker' write means
|
|
25
|
+
* a Worker ran, and no write at all means the child path ran.
|
|
26
|
+
*/
|
|
27
|
+
function writeRuntimeMarker(dir) {
|
|
28
|
+
const markerPath = join(dir, 'runtime-marker.cjs');
|
|
29
|
+
writeFileSync(markerPath, `
|
|
30
|
+
const { isMainThread } = require('node:worker_threads');
|
|
31
|
+
if (!isMainThread) { try { require('fs').writeSync(3, 'worker'); } catch {} }
|
|
32
|
+
`);
|
|
33
|
+
return markerPath;
|
|
34
|
+
}
|
|
35
|
+
const PROBE = `
|
|
36
|
+
import { isMainThread } from 'node:worker_threads';
|
|
37
|
+
let input = '';
|
|
38
|
+
process.stdin.setEncoding('utf8');
|
|
39
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
40
|
+
process.stdin.on('end', () => {
|
|
41
|
+
const payload = JSON.parse(input || '{}');
|
|
42
|
+
console.log(JSON.stringify({ isMainThread, argv: process.argv.slice(2), owner: process.env.OMC_SESSION_OWNER_PID ?? null, payload }));
|
|
43
|
+
process.stderr.write('probe-stderr\\n');
|
|
44
|
+
if (payload.hang) setTimeout(() => {}, 60_000);
|
|
45
|
+
else process.exitCode = payload.exitCode ?? 0;
|
|
46
|
+
});
|
|
47
|
+
`;
|
|
48
|
+
function hostEnv(extra) {
|
|
49
|
+
const env = { ...process.env, ...extra };
|
|
50
|
+
for (const key of ['OMC_HOOK_EVENT', 'OMC_HOOK_FAIL_CLOSED', 'OMC_COPILOT_HOOK_WORKER', 'OMC_SESSION_OWNER_PID', 'NODE_ENV']) {
|
|
51
|
+
if (!(key in extra))
|
|
52
|
+
delete env[key];
|
|
53
|
+
}
|
|
54
|
+
return env;
|
|
55
|
+
}
|
|
56
|
+
describe('run.cjs Copilot generic-hook Worker routing (fork)', () => {
|
|
57
|
+
const tempDirs = [];
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
for (const dir of tempDirs.splice(0))
|
|
60
|
+
rmSync(dir, { recursive: true, force: true, maxRetries: 40, retryDelay: 25 });
|
|
61
|
+
});
|
|
62
|
+
function tempDir(prefix) {
|
|
63
|
+
const dir = mkdtempSync(join(tmpdir(), prefix));
|
|
64
|
+
tempDirs.push(dir);
|
|
65
|
+
return dir;
|
|
66
|
+
}
|
|
67
|
+
/** A plugin whose audited generic script names (budget-guard, subagent-tracker) run the probe. */
|
|
68
|
+
function probePlugin() {
|
|
69
|
+
const root = tempDir('omc-run-cjs-copilot-worker-');
|
|
70
|
+
mkdirSync(join(root, 'scripts'), { recursive: true });
|
|
71
|
+
mkdirSync(join(root, 'hooks'), { recursive: true });
|
|
72
|
+
writeFileSync(join(root, 'scripts', 'run.cjs'), '// plugin-root marker');
|
|
73
|
+
writeFileSync(join(root, 'scripts', 'budget-guard.mjs'), PROBE);
|
|
74
|
+
writeFileSync(join(root, 'scripts', 'subagent-tracker.mjs'), PROBE);
|
|
75
|
+
writeFileSync(join(root, 'scripts', 'not-audited.mjs'), PROBE);
|
|
76
|
+
const command = (script) => `node "$CLAUDE_PLUGIN_ROOT"/scripts/run.cjs "$CLAUDE_PLUGIN_ROOT"/scripts/${script}`;
|
|
77
|
+
writeFileSync(join(root, 'hooks', 'hooks.json'), JSON.stringify({
|
|
78
|
+
hooks: {
|
|
79
|
+
Stop: [{ hooks: [
|
|
80
|
+
// Above COPILOT_WORKER_MIN_TIMEOUT_MS: these fixtures exercise the
|
|
81
|
+
// Worker path itself, not the <=3s child-path carve-out (covered
|
|
82
|
+
// against the real manifest below).
|
|
83
|
+
{ type: 'command', command: command('budget-guard.mjs'), timeout: 5 },
|
|
84
|
+
{ type: 'command', command: command('not-audited.mjs'), timeout: 5 },
|
|
85
|
+
] }],
|
|
86
|
+
SubagentStart: [{ hooks: [{ type: 'command', command: `${command('subagent-tracker.mjs')} start`, timeout: 5 }] }],
|
|
87
|
+
},
|
|
88
|
+
}));
|
|
89
|
+
return root;
|
|
90
|
+
}
|
|
91
|
+
function run(root, script, args, extra, payload = {}) {
|
|
92
|
+
const result = spawnSync(NODE, [RUN_CJS_PATH, join(root, 'scripts', script), ...args], {
|
|
93
|
+
encoding: 'utf-8',
|
|
94
|
+
env: hostEnv({ CLAUDE_PLUGIN_ROOT: root, ...extra }),
|
|
95
|
+
input: JSON.stringify(payload),
|
|
96
|
+
timeout: 30_000,
|
|
97
|
+
windowsHide: true,
|
|
98
|
+
});
|
|
99
|
+
const line = (result.stdout || '').trim().split('\n').find((candidate) => candidate.startsWith('{'));
|
|
100
|
+
return { status: result.status, stdout: result.stdout || '', stderr: result.stderr || '', probe: line ? JSON.parse(line) : null };
|
|
101
|
+
}
|
|
102
|
+
it('runs an audited generic hook in a Worker under Copilot, with stdin, owner pid and exit code', () => {
|
|
103
|
+
const root = probePlugin();
|
|
104
|
+
const result = run(root, 'budget-guard.mjs', [], { OMC_HOOK_EVENT: 'Stop' }, { exitCode: 3, marker: 'stdin-ok' });
|
|
105
|
+
expect(result.status, result.stderr).toBe(3);
|
|
106
|
+
expect(result.probe).toMatchObject({ isMainThread: false, argv: [], payload: { marker: 'stdin-ok' } });
|
|
107
|
+
expect(result.probe.owner).toMatch(/^\d+$/);
|
|
108
|
+
expect(result.stderr).toContain('probe-stderr');
|
|
109
|
+
});
|
|
110
|
+
it('passes extra script arguments (subagent-tracker start) into the Worker argv', () => {
|
|
111
|
+
const root = probePlugin();
|
|
112
|
+
const result = run(root, 'subagent-tracker.mjs', ['start'], { OMC_HOOK_EVENT: 'SubagentStart' });
|
|
113
|
+
expect(result.status, result.stderr).toBe(0);
|
|
114
|
+
expect(result.probe).toMatchObject({ isMainThread: false, argv: ['start'] });
|
|
115
|
+
});
|
|
116
|
+
it('keeps an explicit OMC_SESSION_OWNER_PID', () => {
|
|
117
|
+
const root = probePlugin();
|
|
118
|
+
const result = run(root, 'budget-guard.mjs', [], { OMC_HOOK_EVENT: 'Stop', OMC_SESSION_OWNER_PID: '4242' });
|
|
119
|
+
expect(result.probe).toMatchObject({ isMainThread: false, owner: '4242' });
|
|
120
|
+
});
|
|
121
|
+
it('times out fail-closed (124) in the Worker path, fail-open (0) otherwise', () => {
|
|
122
|
+
const root = probePlugin();
|
|
123
|
+
const closed = run(root, 'budget-guard.mjs', [], { OMC_HOOK_EVENT: 'Stop', OMC_HOOK_FAIL_CLOSED: '1' }, { hang: true });
|
|
124
|
+
expect(closed.status, closed.stderr).toBe(124);
|
|
125
|
+
expect(closed.stderr).toContain('exiting fail-closed (124).');
|
|
126
|
+
const open = run(root, 'budget-guard.mjs', [], { OMC_HOOK_EVENT: 'Stop' }, { hang: true });
|
|
127
|
+
expect(open.status, open.stderr).toBe(0);
|
|
128
|
+
expect(open.stderr).toContain('exiting fail-open.');
|
|
129
|
+
}, 30_000);
|
|
130
|
+
it.each([
|
|
131
|
+
['Claude Code (no OMC_HOOK_EVENT)', 'budget-guard.mjs', [], {}],
|
|
132
|
+
['the OMC_COPILOT_HOOK_WORKER=0 kill switch', 'budget-guard.mjs', [], { OMC_HOOK_EVENT: 'Stop', OMC_COPILOT_HOOK_WORKER: '0' }],
|
|
133
|
+
['an event that does not match the manifest', 'budget-guard.mjs', [], { OMC_HOOK_EVENT: 'PreToolUse' }],
|
|
134
|
+
['a script outside the audited set', 'not-audited.mjs', [], { OMC_HOOK_EVENT: 'Stop' }],
|
|
135
|
+
['extra args that do not match the manifest', 'subagent-tracker.mjs', ['stop'], { OMC_HOOK_EVENT: 'SubagentStart' }],
|
|
136
|
+
])('keeps the generic child path for %s', (_label, script, args, extra) => {
|
|
137
|
+
const root = probePlugin();
|
|
138
|
+
const result = run(root, script, [...args], extra, { exitCode: 0 });
|
|
139
|
+
expect(result.status, result.stderr).toBe(0);
|
|
140
|
+
expect(result.probe).toMatchObject({ isMainThread: true });
|
|
141
|
+
});
|
|
142
|
+
it('keeps the generic child path for a target outside the trusted plugin root', () => {
|
|
143
|
+
const root = probePlugin();
|
|
144
|
+
const other = probePlugin();
|
|
145
|
+
const result = spawnSync(NODE, [RUN_CJS_PATH, join(other, 'scripts', 'budget-guard.mjs')], {
|
|
146
|
+
encoding: 'utf-8', env: hostEnv({ CLAUDE_PLUGIN_ROOT: root, OMC_HOOK_EVENT: 'Stop' }), input: '{}', timeout: 30_000, windowsHide: true,
|
|
147
|
+
});
|
|
148
|
+
expect(result.status, result.stderr).toBe(0);
|
|
149
|
+
expect(JSON.parse(result.stdout.trim()).isMainThread).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
it('the audited set covers exactly the generic entries of copilot/hooks.json', () => {
|
|
152
|
+
const manifest = JSON.parse(readFileSync(join(REPO_ROOT, 'copilot', 'hooks.json'), 'utf8'));
|
|
153
|
+
const generic = new Set();
|
|
154
|
+
for (const entries of Object.values(manifest.hooks)) {
|
|
155
|
+
for (const entry of entries) {
|
|
156
|
+
const script = basename(entry.args[3]);
|
|
157
|
+
if (!runCjs.TRUSTED_WORKER_HOOKS.has(script) && !/^(wiki-)?session-end\.mjs$/.test(script))
|
|
158
|
+
generic.add(script);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// A new upstream hook must be audited (no process.chdir, signal handlers,
|
|
162
|
+
// isMainThread gating, or stdio-inheriting children) before it joins the set.
|
|
163
|
+
expect([...runCjs.COPILOT_WORKER_HOOKS].sort()).toEqual([...generic].sort());
|
|
164
|
+
});
|
|
165
|
+
it('every shipped generic entry resolves to the Worker path under its own event, except the <=3s-budget ones that stay on the child path', () => {
|
|
166
|
+
const manifest = JSON.parse(readFileSync(join(REPO_ROOT, 'copilot', 'hooks.json'), 'utf8'));
|
|
167
|
+
const root = realpathSync(REPO_ROOT);
|
|
168
|
+
let checked = 0;
|
|
169
|
+
for (const entries of Object.values(manifest.hooks)) {
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
const script = basename(entry.args[3]);
|
|
172
|
+
if (!runCjs.COPILOT_WORKER_HOOKS.has(script))
|
|
173
|
+
continue;
|
|
174
|
+
const resolution = { targetPath: realpathSync(join(root, 'scripts', script)), trustedPluginRoot: root };
|
|
175
|
+
const hook = runCjs.resolveCopilotWorkerTarget(resolution, entry.args.slice(4), entry.env);
|
|
176
|
+
const label = `${script} ${entry.env.OMC_HOOK_EVENT} (timeoutSec=${entry.timeoutSec})`;
|
|
177
|
+
if (entry.timeoutSec * 1000 <= runCjs.COPILOT_WORKER_MIN_TIMEOUT_MS) {
|
|
178
|
+
// A Worker cannot preempt a blocked sync call; a tight manifest
|
|
179
|
+
// budget stays on the supervised child path instead.
|
|
180
|
+
expect(hook, label).toBeNull();
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
expect(hook, label).toMatchObject({ event: entry.env.OMC_HOOK_EVENT });
|
|
184
|
+
}
|
|
185
|
+
expect(runCjs.resolveCopilotWorkerTarget(resolution, entry.args.slice(4), {})).toBeNull();
|
|
186
|
+
checked++;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
expect(checked).toBe(runCjs.COPILOT_WORKER_HOOKS.size + 1); // subagent-tracker start + stop
|
|
190
|
+
});
|
|
191
|
+
// Every generic shipped hook, once through each path with the same fixture
|
|
192
|
+
// payload in fresh identical projects: exit code, normalised stdout, and the
|
|
193
|
+
// set of files written must match.
|
|
194
|
+
it('on an idle project, every hook\'s stdout/stderr and written files match the child path, and the worker run actually used the Worker path where expected', () => {
|
|
195
|
+
const manifest = JSON.parse(readFileSync(join(REPO_ROOT, 'copilot', 'hooks.json'), 'utf8'));
|
|
196
|
+
const sessionId = '11111111-2222-4333-8444-555555555555';
|
|
197
|
+
const runAll = (tag, extra, markerPath) => {
|
|
198
|
+
const base = tempDir(`omc-copilot-worker-equiv-${tag}-`);
|
|
199
|
+
const project = join(base, 'p');
|
|
200
|
+
const home = join(base, 'h');
|
|
201
|
+
mkdirSync(project);
|
|
202
|
+
mkdirSync(home);
|
|
203
|
+
execFileSync('git', ['init', '-q'], { cwd: project, windowsHide: true });
|
|
204
|
+
writeFileSync(join(project, 'README.md'), 'x\n');
|
|
205
|
+
const normalise = (text) => text
|
|
206
|
+
.split(project).join('<P>').split(project.replace(/\\/g, '\\\\')).join('<P>')
|
|
207
|
+
.split(home).join('<H>').split(home.replace(/\\/g, '\\\\')).join('<H>')
|
|
208
|
+
.replace(/\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?Z/g, '<TS>');
|
|
209
|
+
const results = [];
|
|
210
|
+
const runtimeByName = {};
|
|
211
|
+
for (const [event, entries] of Object.entries(manifest.hooks)) {
|
|
212
|
+
for (const entry of entries) {
|
|
213
|
+
const args = entry.args.map((arg) => arg.replaceAll('${CLAUDE_PLUGIN_ROOT}', REPO_ROOT)).slice(2);
|
|
214
|
+
if (!runCjs.COPILOT_WORKER_HOOKS.has(basename(args[1])))
|
|
215
|
+
continue;
|
|
216
|
+
const payload = {
|
|
217
|
+
session_id: sessionId, cwd: project, hook_event_name: event, transcript_path: join(home, 'events.jsonl'),
|
|
218
|
+
};
|
|
219
|
+
if (/ToolUse|Permission/.test(event))
|
|
220
|
+
Object.assign(payload, { tool_name: 'Bash', tool_input: { command: 'ls' }, tool_response: { output: 'README.md' }, error: 'boom' });
|
|
221
|
+
if (event === 'SessionStart')
|
|
222
|
+
payload.source = 'startup';
|
|
223
|
+
if (/Subagent/.test(event))
|
|
224
|
+
Object.assign(payload, { agent_id: 'a1', agent_type: 'executor' });
|
|
225
|
+
if (/Stop/.test(event))
|
|
226
|
+
payload.stop_hook_active = false;
|
|
227
|
+
if (event === 'PreCompact')
|
|
228
|
+
payload.trigger = 'auto';
|
|
229
|
+
// fd 3 carries the --require marker's worker/child report so it
|
|
230
|
+
// never touches the stdout/stderr the hook itself produces.
|
|
231
|
+
const result = spawnSync(NODE, ['--require', markerPath, ...args], {
|
|
232
|
+
cwd: project,
|
|
233
|
+
env: hostEnv({
|
|
234
|
+
HOME: home, USERPROFILE: home, COPILOT_HOME: home, CLAUDE_CONFIG_DIR: home, CLAUDE_PLUGIN_ROOT: REPO_ROOT,
|
|
235
|
+
OMC_STATE_DIR: '', ...entry.env, ...extra,
|
|
236
|
+
}),
|
|
237
|
+
input: JSON.stringify(payload),
|
|
238
|
+
encoding: 'utf8',
|
|
239
|
+
stdio: ['pipe', 'pipe', 'pipe', 'pipe'],
|
|
240
|
+
timeout: 30_000,
|
|
241
|
+
windowsHide: true,
|
|
242
|
+
});
|
|
243
|
+
const name = `${event} ${args.slice(1).map((arg) => basename(arg)).join(' ')}`;
|
|
244
|
+
results.push({
|
|
245
|
+
name,
|
|
246
|
+
status: result.status,
|
|
247
|
+
stdout: normalise(result.stdout || ''),
|
|
248
|
+
stderr: normalise(result.stderr || ''),
|
|
249
|
+
});
|
|
250
|
+
const stayedOnChildPath = entry.timeoutSec * 1000 <= runCjs.COPILOT_WORKER_MIN_TIMEOUT_MS;
|
|
251
|
+
const expectedWorker = extra.OMC_COPILOT_HOOK_WORKER !== '0' && !stayedOnChildPath;
|
|
252
|
+
// `encoding: 'utf8'` above decodes every stdio slot in `output`,
|
|
253
|
+
// including fd 3, to a string rather than a Buffer.
|
|
254
|
+
const marker = result.output?.[3];
|
|
255
|
+
const actualWorker = typeof marker === 'string' && marker.trim() === 'worker';
|
|
256
|
+
runtimeByName[name] = { actualWorker, expectedWorker };
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const files = [];
|
|
260
|
+
const walk = (dir) => {
|
|
261
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
262
|
+
const path = join(dir, entry.name);
|
|
263
|
+
if (entry.isDirectory()) {
|
|
264
|
+
if (entry.name !== '.git')
|
|
265
|
+
walk(path);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// pre-compact names checkpoints by time: checkpoint-2026-10-06T04-05-11-863Z.json
|
|
269
|
+
files.push(relative(base, path).replace(/\\/g, '/').replace(/\d{4}-\d\d-\d\dT[\d-]+Z/g, '<TS>'));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
walk(base);
|
|
274
|
+
return { results, files: files.sort(), runtimeByName };
|
|
275
|
+
};
|
|
276
|
+
const markerPath = writeRuntimeMarker(tempDir('omc-copilot-worker-marker-'));
|
|
277
|
+
const child = runAll('child', { OMC_COPILOT_HOOK_WORKER: '0' }, markerPath);
|
|
278
|
+
const worker = runAll('worker', {}, markerPath);
|
|
279
|
+
expect(worker.results.length).toBe(runCjs.COPILOT_WORKER_HOOKS.size + 1); // subagent-tracker start + stop
|
|
280
|
+
expect(worker.results).toEqual(child.results);
|
|
281
|
+
expect(worker.files).toEqual(child.files);
|
|
282
|
+
// Every child-path run actually ran in a plain child process (no Worker
|
|
283
|
+
// spawned at all), and every worker-path run actually used a Worker
|
|
284
|
+
// thread exactly where resolveCopilotWorkerTarget says it should (and
|
|
285
|
+
// correctly fell through to the child path for the <=3s budgets).
|
|
286
|
+
for (const [name, { actualWorker }] of Object.entries(child.runtimeByName)) {
|
|
287
|
+
expect(actualWorker, `child path, ${name}`).toBe(false);
|
|
288
|
+
}
|
|
289
|
+
for (const [name, { actualWorker, expectedWorker }] of Object.entries(worker.runtimeByName)) {
|
|
290
|
+
expect(actualWorker, `worker run, ${name}`).toBe(expectedWorker);
|
|
291
|
+
}
|
|
292
|
+
}, 120_000);
|
|
293
|
+
});
|
|
294
|
+
//# sourceMappingURL=run-cjs-copilot-worker-routing.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-cjs-copilot-worker-routing.test.js","sourceRoot":"","sources":["../../src/__tests__/run-cjs-copilot-worker-routing.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-cjs-fail-closed-timeout.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/run-cjs-fail-closed-timeout.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { spawnSync } from 'child_process';
|
|
6
|
+
/**
|
|
7
|
+
* The fork's fail-closed contract in scripts/run.cjs: with OMC_HOOK_FAIL_CLOSED=1
|
|
8
|
+
* a hook timeout exits 124 (generic child, trusted Worker, and the SessionEnd
|
|
9
|
+
* foreground budget); without it the runner stays fail-open (exit 0).
|
|
10
|
+
*/
|
|
11
|
+
const RUN_CJS_PATH = join(__dirname, '..', '..', 'scripts', 'run.cjs');
|
|
12
|
+
const NODE = process.execPath;
|
|
13
|
+
const HANG = 'setTimeout(() => process.exit(0), 20000);';
|
|
14
|
+
describe('run.cjs — OMC_HOOK_FAIL_CLOSED hook timeouts', () => {
|
|
15
|
+
let tmpDir;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
tmpDir = mkdtempSync(join(tmpdir(), 'omc-run-cjs-fail-closed-'));
|
|
18
|
+
});
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
function plugin(name, script, event, timeoutSec) {
|
|
23
|
+
const root = join(tmpDir, `${name}-root`);
|
|
24
|
+
mkdirSync(join(root, 'scripts'), { recursive: true });
|
|
25
|
+
mkdirSync(join(root, 'hooks'), { recursive: true });
|
|
26
|
+
writeFileSync(join(root, 'scripts', 'run.cjs'), '// plugin-root marker');
|
|
27
|
+
writeFileSync(join(root, 'scripts', name), script);
|
|
28
|
+
writeFileSync(join(root, 'hooks', 'hooks.json'), JSON.stringify({
|
|
29
|
+
hooks: {
|
|
30
|
+
[event]: [{ matcher: '', hooks: [{
|
|
31
|
+
type: 'command',
|
|
32
|
+
command: `node "$CLAUDE_PLUGIN_ROOT"/scripts/run.cjs "$CLAUDE_PLUGIN_ROOT"/scripts/${name}`,
|
|
33
|
+
timeout: timeoutSec,
|
|
34
|
+
}] }],
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
return join(root, 'scripts', name);
|
|
38
|
+
}
|
|
39
|
+
function run(target, failClosed, extraEnv = {}) {
|
|
40
|
+
const env = { ...process.env, CLAUDE_PLUGIN_ROOT: join(target, '..', '..'), ...extraEnv };
|
|
41
|
+
delete env.OMC_HOOK_FAIL_CLOSED;
|
|
42
|
+
if (failClosed)
|
|
43
|
+
env.OMC_HOOK_FAIL_CLOSED = '1';
|
|
44
|
+
const result = spawnSync(NODE, [RUN_CJS_PATH, target], { encoding: 'utf-8', env, input: '{}', timeout: 30_000 });
|
|
45
|
+
return { status: result.status, stderr: result.stderr || '' };
|
|
46
|
+
}
|
|
47
|
+
it.each([
|
|
48
|
+
['generic child', 'slow-stop.cjs', HANG, 'Stop'],
|
|
49
|
+
['trusted Worker', 'pre-tool-enforcer.mjs', HANG, 'PreToolUse'],
|
|
50
|
+
])('%s: timeout exits 124 when fail-closed, 0 otherwise', (_label, name, script, event) => {
|
|
51
|
+
const target = plugin(name, script, event, 2);
|
|
52
|
+
const closed = run(target, true);
|
|
53
|
+
expect(closed.status, closed.stderr).toBe(124);
|
|
54
|
+
expect(closed.stderr).toContain('exiting fail-closed (124).');
|
|
55
|
+
const open = run(target, false);
|
|
56
|
+
expect(open.status, open.stderr).toBe(0);
|
|
57
|
+
expect(open.stderr).toContain('exiting fail-open.');
|
|
58
|
+
});
|
|
59
|
+
it('SessionEnd foreground budget: timeout exits 124 when fail-closed, 0 otherwise', () => {
|
|
60
|
+
const target = plugin('session-end.mjs', 'await new Promise((r) => setTimeout(r, 20000));', 'SessionEnd', 5);
|
|
61
|
+
const env = { NODE_ENV: 'test', OMC_SESSION_END_TEST_FOREGROUND_TIMEOUT_MS: '500' };
|
|
62
|
+
const closed = run(target, true, env);
|
|
63
|
+
expect(closed.status, closed.stderr).toBe(124);
|
|
64
|
+
const open = run(target, false, env);
|
|
65
|
+
expect(open.status, open.stderr).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=run-cjs-fail-closed-timeout.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-cjs-fail-closed-timeout.test.js","sourceRoot":"","sources":["../../src/__tests__/run-cjs-fail-closed-timeout.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-cjs-session-end-budget.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/run-cjs-session-end-budget.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, onTestFinished } from 'vitest';
|
|
2
|
+
import { spawn, spawnSync } from 'child_process';
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Fork (oh-my-copilot) SessionEnd foreground budget in scripts/run.cjs:
|
|
9
|
+
* 300ms on Claude Code (upstream, unchanged), 1500ms when the Copilot
|
|
10
|
+
* projection sets OMC_HOOK_EVENT=SessionEnd, OMC_SESSION_END_BUDGET_MS
|
|
11
|
+
* overriding both. The 124 fail-closed timeout contract is unchanged.
|
|
12
|
+
*/
|
|
13
|
+
const REPO_ROOT = join(__dirname, '..', '..');
|
|
14
|
+
const RUN_CJS_PATH = join(REPO_ROOT, 'scripts', 'run.cjs');
|
|
15
|
+
const NODE = process.execPath;
|
|
16
|
+
const HAS_GENERATED_DIST = existsSync(join(REPO_ROOT, 'dist', 'hooks', 'session-end', 'foreground-bootstrap.js'));
|
|
17
|
+
const { resolveSessionEndBudgetMs } = createRequire(__filename)(RUN_CJS_PATH);
|
|
18
|
+
/** A clean host env: no inherited budget knobs, no vitest NODE_ENV=test widening. */
|
|
19
|
+
function hostEnv(extra = {}) {
|
|
20
|
+
const env = { ...process.env, ...extra };
|
|
21
|
+
for (const key of ['NODE_ENV', 'OMC_SESSION_END_TEST_FOREGROUND_TIMEOUT_MS', 'OMC_SESSION_END_BUDGET_MS', 'OMC_HOOK_EVENT', 'OMC_HOOK_FAIL_CLOSED']) {
|
|
22
|
+
if (!(key in extra))
|
|
23
|
+
delete env[key];
|
|
24
|
+
}
|
|
25
|
+
return env;
|
|
26
|
+
}
|
|
27
|
+
describe('run.cjs SessionEnd foreground budget (fork)', () => {
|
|
28
|
+
const tempDirs = [];
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
for (const dir of tempDirs.splice(0))
|
|
31
|
+
rmSync(dir, { recursive: true, force: true, maxRetries: 40, retryDelay: 25 });
|
|
32
|
+
});
|
|
33
|
+
describe('resolveSessionEndBudgetMs', () => {
|
|
34
|
+
it('keeps 300ms without OMC_HOOK_EVENT (Claude Code) and uses 1500ms under Copilot SessionEnd', () => {
|
|
35
|
+
expect(resolveSessionEndBudgetMs({})).toBe(300);
|
|
36
|
+
expect(resolveSessionEndBudgetMs({ OMC_HOOK_EVENT: 'Stop' })).toBe(300);
|
|
37
|
+
expect(resolveSessionEndBudgetMs({ OMC_HOOK_EVENT: 'SessionEnd' })).toBe(1500);
|
|
38
|
+
});
|
|
39
|
+
it('honours a valid OMC_SESSION_END_BUDGET_MS on both hosts', () => {
|
|
40
|
+
expect(resolveSessionEndBudgetMs({ OMC_SESSION_END_BUDGET_MS: '800' })).toBe(800);
|
|
41
|
+
expect(resolveSessionEndBudgetMs({ OMC_SESSION_END_BUDGET_MS: ' 2500 ', OMC_HOOK_EVENT: 'SessionEnd' })).toBe(2500);
|
|
42
|
+
expect(resolveSessionEndBudgetMs({ OMC_SESSION_END_BUDGET_MS: '100', OMC_HOOK_EVENT: 'SessionEnd' })).toBe(100);
|
|
43
|
+
});
|
|
44
|
+
it.each(['', 'abc', '0', '-5', '1.5', '1e3', '60001'])('ignores invalid override %j', (value) => {
|
|
45
|
+
expect(resolveSessionEndBudgetMs({ OMC_SESSION_END_BUDGET_MS: value })).toBe(300);
|
|
46
|
+
expect(resolveSessionEndBudgetMs({ OMC_SESSION_END_BUDGET_MS: value, OMC_HOOK_EVENT: 'SessionEnd' })).toBe(1500);
|
|
47
|
+
});
|
|
48
|
+
it('keeps the test-only widening first and only under NODE_ENV=test', () => {
|
|
49
|
+
const widen = { OMC_SESSION_END_TEST_FOREGROUND_TIMEOUT_MS: '4000', OMC_SESSION_END_BUDGET_MS: '800', OMC_HOOK_EVENT: 'SessionEnd' };
|
|
50
|
+
expect(resolveSessionEndBudgetMs({ ...widen, NODE_ENV: 'test' })).toBe(4000);
|
|
51
|
+
expect(resolveSessionEndBudgetMs(widen)).toBe(800);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('runner exit behaviour', () => {
|
|
55
|
+
// A SessionEnd hook whose foreground takes ~700ms: over the Claude budget,
|
|
56
|
+
// inside the Copilot one.
|
|
57
|
+
function slowSessionEndPlugin() {
|
|
58
|
+
const root = mkdtempSync(join(tmpdir(), 'omc-run-cjs-se-budget-'));
|
|
59
|
+
tempDirs.push(root);
|
|
60
|
+
mkdirSync(join(root, 'scripts'), { recursive: true });
|
|
61
|
+
mkdirSync(join(root, 'hooks'), { recursive: true });
|
|
62
|
+
writeFileSync(join(root, 'scripts', 'run.cjs'), '// plugin-root marker');
|
|
63
|
+
writeFileSync(join(root, 'scripts', 'session-end.mjs'), 'await new Promise((r) => setTimeout(r, 700));\nconsole.log(JSON.stringify({ continue: true }));\n');
|
|
64
|
+
// resolveTrustedSessionEndTarget realpaths both canonical scripts; without
|
|
65
|
+
// this file the runner silently takes the generic child path instead.
|
|
66
|
+
writeFileSync(join(root, 'scripts', 'wiki-session-end.mjs'), '');
|
|
67
|
+
writeFileSync(join(root, 'hooks', 'hooks.json'), JSON.stringify({
|
|
68
|
+
hooks: { SessionEnd: [{ hooks: [{ type: 'command', command: 'node "$CLAUDE_PLUGIN_ROOT"/scripts/run.cjs "$CLAUDE_PLUGIN_ROOT"/scripts/session-end.mjs', timeout: 30 }] }] },
|
|
69
|
+
}));
|
|
70
|
+
return root;
|
|
71
|
+
}
|
|
72
|
+
function run(root, extra) {
|
|
73
|
+
const result = spawnSync(NODE, [RUN_CJS_PATH, join(root, 'scripts', 'session-end.mjs')], {
|
|
74
|
+
encoding: 'utf-8',
|
|
75
|
+
env: hostEnv({ CLAUDE_PLUGIN_ROOT: root, OMC_HOOK_FAIL_CLOSED: '1', ...extra }),
|
|
76
|
+
input: '{}',
|
|
77
|
+
timeout: 20_000,
|
|
78
|
+
windowsHide: true,
|
|
79
|
+
});
|
|
80
|
+
return { status: result.status, stdout: result.stdout || '', stderr: result.stderr || '' };
|
|
81
|
+
}
|
|
82
|
+
it('Claude Code (no OMC_HOOK_EVENT): the 300ms budget times out fail-closed (124)', () => {
|
|
83
|
+
const result = run(slowSessionEndPlugin(), {});
|
|
84
|
+
expect(result.status, result.stderr).toBe(124);
|
|
85
|
+
expect(result.stderr).toContain('timed out after 300ms; exiting fail-closed (124).');
|
|
86
|
+
});
|
|
87
|
+
it('Copilot (OMC_HOOK_EVENT=SessionEnd): the 1500ms budget lets the hook finish', () => {
|
|
88
|
+
const result = run(slowSessionEndPlugin(), { OMC_HOOK_EVENT: 'SessionEnd' });
|
|
89
|
+
expect(result.status, result.stderr).toBe(0);
|
|
90
|
+
expect(result.stderr).not.toContain('timed out');
|
|
91
|
+
expect(JSON.parse(result.stdout.trim())).toEqual({ continue: true });
|
|
92
|
+
});
|
|
93
|
+
it('OMC_SESSION_END_BUDGET_MS widens Claude Code and narrows Copilot', () => {
|
|
94
|
+
const root = slowSessionEndPlugin();
|
|
95
|
+
const widened = run(root, { OMC_SESSION_END_BUDGET_MS: '2000' });
|
|
96
|
+
expect(widened.status, widened.stderr).toBe(0);
|
|
97
|
+
const narrowed = run(root, { OMC_HOOK_EVENT: 'SessionEnd', OMC_SESSION_END_BUDGET_MS: '100' });
|
|
98
|
+
expect(narrowed.status, narrowed.stderr).toBe(124);
|
|
99
|
+
expect(narrowed.stderr).toContain('timed out after 100ms');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// Regression for the red Tier 2 gate: under 4 parallel SessionEnd loads the
|
|
103
|
+
// 300ms budget failed 39/64 runs (exit 124). With the Copilot budget the real
|
|
104
|
+
// shipped hooks must pass every run fail-closed.
|
|
105
|
+
//
|
|
106
|
+
// Each run.cjs/session-end.mjs foreground process hands off cleanup to a
|
|
107
|
+
// detached background worker (src/hooks/session-end/worker.ts,
|
|
108
|
+
// spawnSessionEndWorker). This test only awaits the foreground process, not
|
|
109
|
+
// that worker, by design: awaiting it would defeat the fast-foreground
|
|
110
|
+
// point of the budget this test is regression-testing. The worker is
|
|
111
|
+
// bounded by its own MAX_WORKER_MS (10s) deadline and self-terminates, so
|
|
112
|
+
// nothing it leaves running is unbounded; the directory removal below is
|
|
113
|
+
// best-effort only because Windows can hold a handle open for that window,
|
|
114
|
+
// not because a worker could run forever.
|
|
115
|
+
async function runSessionEndLoadTest(lanes, rounds) {
|
|
116
|
+
const project = mkdtempSync(join(tmpdir(), 'omc-run-cjs-se-load-'));
|
|
117
|
+
onTestFinished(() => {
|
|
118
|
+
try {
|
|
119
|
+
rmSync(project, { recursive: true, force: true, maxRetries: 20, retryDelay: 100 });
|
|
120
|
+
}
|
|
121
|
+
catch { /* workers still running, bounded by MAX_WORKER_MS */ }
|
|
122
|
+
});
|
|
123
|
+
const home = join(project, 'home');
|
|
124
|
+
mkdirSync(home);
|
|
125
|
+
expect(spawnSync('git', ['init', '-q'], { cwd: project, windowsHide: true }).status).toBe(0);
|
|
126
|
+
const env = hostEnv({
|
|
127
|
+
HOME: home,
|
|
128
|
+
USERPROFILE: home,
|
|
129
|
+
COPILOT_HOME: home,
|
|
130
|
+
CLAUDE_CONFIG_DIR: home,
|
|
131
|
+
CLAUDE_PLUGIN_ROOT: REPO_ROOT,
|
|
132
|
+
OMC_STATE_DIR: '',
|
|
133
|
+
OMC_HOOK_EVENT: 'SessionEnd',
|
|
134
|
+
OMC_HOOK_FAIL_CLOSED: '1',
|
|
135
|
+
});
|
|
136
|
+
const runHook = (script, sessionId) => new Promise((resolve) => {
|
|
137
|
+
const child = spawn(NODE, [RUN_CJS_PATH, join(REPO_ROOT, 'scripts', script)], { cwd: project, env, windowsHide: true });
|
|
138
|
+
let stderr = '';
|
|
139
|
+
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
140
|
+
child.stdout.resume();
|
|
141
|
+
child.once('close', (code) => resolve({ code, stderr }));
|
|
142
|
+
child.stdin.end(JSON.stringify({
|
|
143
|
+
session_id: sessionId, transcript_path: join(project, 'transcript.jsonl'), cwd: project,
|
|
144
|
+
permission_mode: 'default', hook_event_name: 'SessionEnd', reason: 'other',
|
|
145
|
+
}));
|
|
146
|
+
});
|
|
147
|
+
const failures = [];
|
|
148
|
+
await Promise.all(Array.from({ length: lanes }, async (_unused, lane) => {
|
|
149
|
+
for (let round = 0; round < rounds; round++) {
|
|
150
|
+
for (const script of ['session-end.mjs', 'wiki-session-end.mjs']) {
|
|
151
|
+
const result = await runHook(script, `se-load-${lane}-${round}`);
|
|
152
|
+
if (result.code !== 0)
|
|
153
|
+
failures.push(`${script} lane ${lane} round ${round}: exit ${result.code} ${result.stderr.trim()}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}));
|
|
157
|
+
return failures;
|
|
158
|
+
}
|
|
159
|
+
// Always-on, light: catches a gross regression on every run without the
|
|
160
|
+
// full 40-call cost below.
|
|
161
|
+
it.skipIf(!HAS_GENERATED_DIST)('real session-end + wiki-session-end pass fail-closed under 2 parallel lanes x 2 rounds', async () => {
|
|
162
|
+
expect(await runSessionEndLoadTest(2, 2)).toEqual([]);
|
|
163
|
+
}, 60_000);
|
|
164
|
+
// The full regression load (4 lanes x 5 rounds = 40 hook calls): opt-in via
|
|
165
|
+
// OMC_LOAD_TESTS=1, since it is slow and its 40 detached background workers
|
|
166
|
+
// are unnecessary load on every CI run once the light case above passes.
|
|
167
|
+
it.skipIf(!HAS_GENERATED_DIST || !process.env.OMC_LOAD_TESTS)('real session-end + wiki-session-end pass 5/5 rounds fail-closed under 4 parallel lanes (OMC_LOAD_TESTS=1)', async () => {
|
|
168
|
+
expect(await runSessionEndLoadTest(4, 5)).toEqual([]);
|
|
169
|
+
}, 120_000);
|
|
170
|
+
});
|
|
171
|
+
//# sourceMappingURL=run-cjs-session-end-budget.test.js.map
|