@mindfoldhq/trellis 0.6.0-beta.9 → 0.6.0-rc.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/README.md +49 -49
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +36 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/channel/adapters/claude.d.ts +29 -0
- package/dist/commands/channel/adapters/claude.d.ts.map +1 -0
- package/dist/commands/channel/adapters/claude.js +203 -0
- package/dist/commands/channel/adapters/claude.js.map +1 -0
- package/dist/commands/channel/adapters/codex.d.ts +85 -0
- package/dist/commands/channel/adapters/codex.d.ts.map +1 -0
- package/dist/commands/channel/adapters/codex.js +505 -0
- package/dist/commands/channel/adapters/codex.js.map +1 -0
- package/dist/commands/channel/adapters/index.d.ts +84 -0
- package/dist/commands/channel/adapters/index.d.ts.map +1 -0
- package/dist/commands/channel/adapters/index.js +115 -0
- package/dist/commands/channel/adapters/index.js.map +1 -0
- package/dist/commands/channel/adapters/types.d.ts +33 -0
- package/dist/commands/channel/adapters/types.d.ts.map +1 -0
- package/dist/commands/channel/adapters/types.js +2 -0
- package/dist/commands/channel/adapters/types.js.map +1 -0
- package/dist/commands/channel/agent-loader.d.ts +32 -0
- package/dist/commands/channel/agent-loader.d.ts.map +1 -0
- package/dist/commands/channel/agent-loader.js +154 -0
- package/dist/commands/channel/agent-loader.js.map +1 -0
- package/dist/commands/channel/context-loader.d.ts +26 -0
- package/dist/commands/channel/context-loader.d.ts.map +1 -0
- package/dist/commands/channel/context-loader.js +290 -0
- package/dist/commands/channel/context-loader.js.map +1 -0
- package/dist/commands/channel/context.d.ts +16 -0
- package/dist/commands/channel/context.d.ts.map +1 -0
- package/dist/commands/channel/context.js +83 -0
- package/dist/commands/channel/context.js.map +1 -0
- package/dist/commands/channel/create.d.ts +27 -0
- package/dist/commands/channel/create.d.ts.map +1 -0
- package/dist/commands/channel/create.js +39 -0
- package/dist/commands/channel/create.js.map +1 -0
- package/dist/commands/channel/dev-parse-trace.d.ts +14 -0
- package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -0
- package/dist/commands/channel/dev-parse-trace.js +70 -0
- package/dist/commands/channel/dev-parse-trace.js.map +1 -0
- package/dist/commands/channel/guard.d.ts +150 -0
- package/dist/commands/channel/guard.d.ts.map +1 -0
- package/dist/commands/channel/guard.js +474 -0
- package/dist/commands/channel/guard.js.map +1 -0
- package/dist/commands/channel/index.d.ts +3 -0
- package/dist/commands/channel/index.d.ts.map +1 -0
- package/dist/commands/channel/index.js +531 -0
- package/dist/commands/channel/index.js.map +1 -0
- package/dist/commands/channel/interrupt.d.ts +10 -0
- package/dist/commands/channel/interrupt.d.ts.map +1 -0
- package/dist/commands/channel/interrupt.js +22 -0
- package/dist/commands/channel/interrupt.js.map +1 -0
- package/dist/commands/channel/kill.d.ts +7 -0
- package/dist/commands/channel/kill.d.ts.map +1 -0
- package/dist/commands/channel/kill.js +121 -0
- package/dist/commands/channel/kill.js.map +1 -0
- package/dist/commands/channel/list.d.ts +17 -0
- package/dist/commands/channel/list.d.ts.map +1 -0
- package/dist/commands/channel/list.js +233 -0
- package/dist/commands/channel/list.js.map +1 -0
- package/dist/commands/channel/messages.d.ts +15 -0
- package/dist/commands/channel/messages.d.ts.map +1 -0
- package/dist/commands/channel/messages.js +245 -0
- package/dist/commands/channel/messages.js.map +1 -0
- package/dist/commands/channel/rm.d.ts +27 -0
- package/dist/commands/channel/rm.d.ts.map +1 -0
- package/dist/commands/channel/rm.js +216 -0
- package/dist/commands/channel/rm.js.map +1 -0
- package/dist/commands/channel/run.d.ts +30 -0
- package/dist/commands/channel/run.d.ts.map +1 -0
- package/dist/commands/channel/run.js +130 -0
- package/dist/commands/channel/run.js.map +1 -0
- package/dist/commands/channel/send.d.ts +11 -0
- package/dist/commands/channel/send.d.ts.map +1 -0
- package/dist/commands/channel/send.js +24 -0
- package/dist/commands/channel/send.js.map +1 -0
- package/dist/commands/channel/spawn.d.ts +40 -0
- package/dist/commands/channel/spawn.d.ts.map +1 -0
- package/dist/commands/channel/spawn.js +244 -0
- package/dist/commands/channel/spawn.js.map +1 -0
- package/dist/commands/channel/store/events.d.ts +39 -0
- package/dist/commands/channel/store/events.d.ts.map +1 -0
- package/dist/commands/channel/store/events.js +87 -0
- package/dist/commands/channel/store/events.js.map +1 -0
- package/dist/commands/channel/store/filter.d.ts +3 -0
- package/dist/commands/channel/store/filter.d.ts.map +1 -0
- package/dist/commands/channel/store/filter.js +2 -0
- package/dist/commands/channel/store/filter.js.map +1 -0
- package/dist/commands/channel/store/lock.d.ts +23 -0
- package/dist/commands/channel/store/lock.d.ts.map +1 -0
- package/dist/commands/channel/store/lock.js +99 -0
- package/dist/commands/channel/store/lock.js.map +1 -0
- package/dist/commands/channel/store/paths.d.ts +63 -0
- package/dist/commands/channel/store/paths.d.ts.map +1 -0
- package/dist/commands/channel/store/paths.js +246 -0
- package/dist/commands/channel/store/paths.js.map +1 -0
- package/dist/commands/channel/store/schema.d.ts +27 -0
- package/dist/commands/channel/store/schema.d.ts.map +1 -0
- package/dist/commands/channel/store/schema.js +34 -0
- package/dist/commands/channel/store/schema.js.map +1 -0
- package/dist/commands/channel/store/thread-state.d.ts +5 -0
- package/dist/commands/channel/store/thread-state.d.ts.map +1 -0
- package/dist/commands/channel/store/thread-state.js +16 -0
- package/dist/commands/channel/store/thread-state.js.map +1 -0
- package/dist/commands/channel/store/watch.d.ts +19 -0
- package/dist/commands/channel/store/watch.d.ts.map +1 -0
- package/dist/commands/channel/store/watch.js +146 -0
- package/dist/commands/channel/store/watch.js.map +1 -0
- package/dist/commands/channel/supervisor/idle.d.ts +46 -0
- package/dist/commands/channel/supervisor/idle.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/idle.js +72 -0
- package/dist/commands/channel/supervisor/idle.js.map +1 -0
- package/dist/commands/channel/supervisor/inbox.d.ts +30 -0
- package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/inbox.js +160 -0
- package/dist/commands/channel/supervisor/inbox.js.map +1 -0
- package/dist/commands/channel/supervisor/shutdown.d.ts +68 -0
- package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/shutdown.js +146 -0
- package/dist/commands/channel/supervisor/shutdown.js.map +1 -0
- package/dist/commands/channel/supervisor/stdout.d.ts +51 -0
- package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/stdout.js +121 -0
- package/dist/commands/channel/supervisor/stdout.js.map +1 -0
- package/dist/commands/channel/supervisor/turns.d.ts +31 -0
- package/dist/commands/channel/supervisor/turns.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/turns.js +45 -0
- package/dist/commands/channel/supervisor/turns.js.map +1 -0
- package/dist/commands/channel/supervisor/warning.d.ts +48 -0
- package/dist/commands/channel/supervisor/warning.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/warning.js +77 -0
- package/dist/commands/channel/supervisor/warning.js.map +1 -0
- package/dist/commands/channel/supervisor.d.ts +59 -0
- package/dist/commands/channel/supervisor.d.ts.map +1 -0
- package/dist/commands/channel/supervisor.js +344 -0
- package/dist/commands/channel/supervisor.js.map +1 -0
- package/dist/commands/channel/text-body.d.ts +13 -0
- package/dist/commands/channel/text-body.d.ts.map +1 -0
- package/dist/commands/channel/text-body.js +47 -0
- package/dist/commands/channel/text-body.js.map +1 -0
- package/dist/commands/channel/threads.d.ts +39 -0
- package/dist/commands/channel/threads.d.ts.map +1 -0
- package/dist/commands/channel/threads.js +106 -0
- package/dist/commands/channel/threads.js.map +1 -0
- package/dist/commands/channel/title.d.ts +12 -0
- package/dist/commands/channel/title.d.ts.map +1 -0
- package/dist/commands/channel/title.js +24 -0
- package/dist/commands/channel/title.js.map +1 -0
- package/dist/commands/channel/wait.d.ts +17 -0
- package/dist/commands/channel/wait.d.ts.map +1 -0
- package/dist/commands/channel/wait.js +75 -0
- package/dist/commands/channel/wait.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +162 -43
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mem.d.ts +13 -217
- package/dist/commands/mem.d.ts.map +1 -1
- package/dist/commands/mem.js +142 -1587
- package/dist/commands/mem.js.map +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +28 -2
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +102 -5
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/workflow.d.ts +35 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +232 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/configurators/claude.d.ts.map +1 -1
- package/dist/configurators/claude.js +1 -0
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +5 -0
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/reasonix.d.ts +23 -0
- package/dist/configurators/reasonix.d.ts.map +1 -0
- package/dist/configurators/reasonix.js +60 -0
- package/dist/configurators/reasonix.js.map +1 -0
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +8 -0
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts +8 -0
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +14 -3
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/constants/paths.d.ts +4 -0
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +4 -0
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.5.14.json +9 -0
- package/dist/migrations/manifests/0.5.15.json +9 -0
- package/dist/migrations/manifests/0.5.16.json +9 -0
- package/dist/migrations/manifests/0.5.17.json +9 -0
- package/dist/migrations/manifests/0.5.18.json +9 -0
- package/dist/migrations/manifests/0.5.19.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.11.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.12.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.13.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.15.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.16.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.17.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.18.json +16 -0
- package/dist/migrations/manifests/0.6.0-beta.19.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.20.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.21.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.22.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.23.json +88 -0
- package/dist/migrations/manifests/0.6.0-rc.0.json +9 -0
- package/dist/templates/claude/agents/trellis-check.md +12 -6
- package/dist/templates/claude/agents/trellis-implement.md +1 -1
- package/dist/templates/claude/agents/trellis-research.md +1 -1
- package/dist/templates/codebuddy/agents/trellis-check.md +12 -6
- package/dist/templates/codebuddy/agents/trellis-implement.md +1 -1
- package/dist/templates/codebuddy/agents/trellis-research.md +1 -1
- package/dist/templates/codex/agents/trellis-check.toml +0 -25
- package/dist/templates/codex/agents/trellis-implement.toml +0 -25
- package/dist/templates/codex/config.toml +9 -16
- package/dist/templates/codex/hooks/session-start.py +22 -0
- package/dist/templates/codex/hooks.json +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +27 -0
- package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +81 -0
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +66 -0
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/triggering-patterns.md +93 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/SKILL.md +41 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/mcp-setup.md +90 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/repository-analysis.md +59 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-task-planning.md +61 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-writing.md +70 -0
- package/dist/templates/copilot/hooks/session-start.py +24 -0
- package/dist/templates/cursor/agents/trellis-check.md +12 -6
- package/dist/templates/cursor/agents/trellis-implement.md +1 -1
- package/dist/templates/cursor/agents/trellis-research.md +1 -1
- package/dist/templates/cursor/hooks.json +0 -6
- package/dist/templates/droid/droids/trellis-check.md +12 -6
- package/dist/templates/droid/droids/trellis-implement.md +1 -1
- package/dist/templates/droid/droids/trellis-research.md +1 -1
- package/dist/templates/gemini/agents/trellis-check.md +11 -5
- package/dist/templates/kiro/agents/trellis-check.json +1 -1
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +127 -9
- package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +130 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +38 -0
- package/dist/templates/markdown/spec/guides/index.md.txt +18 -0
- package/dist/templates/opencode/agents/trellis-check.md +11 -5
- package/dist/templates/pi/agents/trellis-check.md +5 -4
- package/dist/templates/pi/agents/trellis-implement.md +5 -4
- package/dist/templates/pi/extensions/trellis/index.ts.txt +1339 -913
- package/dist/templates/pi/settings.json +0 -9
- package/dist/templates/qoder/agents/trellis-check.md +12 -6
- package/dist/templates/qoder/agents/trellis-implement.md +1 -1
- package/dist/templates/qoder/agents/trellis-research.md +1 -1
- package/dist/templates/reasonix/agents/trellis-check.md +36 -0
- package/dist/templates/reasonix/agents/trellis-implement.md +41 -0
- package/dist/templates/reasonix/index.d.ts +13 -0
- package/dist/templates/reasonix/index.d.ts.map +1 -0
- package/dist/templates/reasonix/index.js +16 -0
- package/dist/templates/reasonix/index.js.map +1 -0
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +0 -1
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-workflow-state.py +22 -0
- package/dist/templates/shared-hooks/session-start.py +25 -8
- package/dist/templates/trellis/agents/check.md +70 -0
- package/dist/templates/trellis/agents/implement.md +71 -0
- package/dist/templates/trellis/config.yaml +20 -0
- package/dist/templates/trellis/index.d.ts +13 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +22 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/common/safe_commit.py +49 -19
- package/dist/templates/trellis/scripts/common/task_store.py +94 -16
- package/dist/templates/trellis/workflow.md +21 -0
- package/dist/types/ai-tools.d.ts +4 -4
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +16 -0
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/agent-refs.d.ts +31 -0
- package/dist/utils/agent-refs.d.ts.map +1 -0
- package/dist/utils/agent-refs.js +63 -0
- package/dist/utils/agent-refs.js.map +1 -0
- package/dist/utils/cwd-guard.d.ts +38 -0
- package/dist/utils/cwd-guard.d.ts.map +1 -0
- package/dist/utils/cwd-guard.js +62 -0
- package/dist/utils/cwd-guard.js.map +1 -0
- package/dist/utils/file-writer.d.ts +13 -0
- package/dist/utils/file-writer.d.ts.map +1 -1
- package/dist/utils/file-writer.js +59 -1
- package/dist/utils/file-writer.js.map +1 -1
- package/dist/utils/manifest-prune.d.ts +61 -0
- package/dist/utils/manifest-prune.d.ts.map +1 -0
- package/dist/utils/manifest-prune.js +136 -0
- package/dist/utils/manifest-prune.js.map +1 -0
- package/dist/utils/registry-config.d.ts +7 -0
- package/dist/utils/registry-config.d.ts.map +1 -0
- package/dist/utils/registry-config.js +171 -0
- package/dist/utils/registry-config.js.map +1 -0
- package/dist/utils/task-json.d.ts +9 -42
- package/dist/utils/task-json.d.ts.map +1 -1
- package/dist/utils/task-json.js +8 -45
- package/dist/utils/task-json.js.map +1 -1
- package/dist/utils/template-fetcher.d.ts +11 -0
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +51 -2
- package/dist/utils/template-fetcher.js.map +1 -1
- package/dist/utils/template-hash.d.ts +32 -6
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +53 -31
- package/dist/utils/template-hash.js.map +1 -1
- package/dist/utils/workflow-resolver.d.ts +86 -0
- package/dist/utils/workflow-resolver.d.ts.map +1 -0
- package/dist/utils/workflow-resolver.js +265 -0
- package/dist/utils/workflow-resolver.js.map +1 -0
- package/package.json +9 -8
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supervisor-side idle-timeout timer.
|
|
3
|
+
*
|
|
4
|
+
* Complements the spawn-time guard: each running worker self-terminates
|
|
5
|
+
* after its own idle TTL so a long-lived supervisor can't keep an
|
|
6
|
+
* otherwise-idle worker alive indefinitely.
|
|
7
|
+
*
|
|
8
|
+
* Behavior:
|
|
9
|
+
* - Start an idle timer right after `spawned`.
|
|
10
|
+
* - Reset / restart on `turn_finished` and `interrupted` (worker
|
|
11
|
+
* transitioned back to idle).
|
|
12
|
+
* - Pause on `turn_started` (worker is mid-turn; never kill mid-turn).
|
|
13
|
+
* - On idle timeout, call `shutdown.request("SIGTERM", "idle-timeout")`.
|
|
14
|
+
*
|
|
15
|
+
* Lives outside `createShutdown` so the shutdown funnel only owns the
|
|
16
|
+
* kill ladder + `killed` append. Cancellation is via the returned
|
|
17
|
+
* handle (used on supervisor teardown).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Schedule a self-resetting idle timer. `idleTimeoutMs <= 0` short-
|
|
21
|
+
* circuits to a no-op handle (idle cleanup disabled).
|
|
22
|
+
*/
|
|
23
|
+
export function scheduleSupervisorIdleTimer(args) {
|
|
24
|
+
const { idleTimeoutMs, shutdown, isChildExited, log } = args;
|
|
25
|
+
if (idleTimeoutMs <= 0) {
|
|
26
|
+
return {
|
|
27
|
+
reset: () => undefined,
|
|
28
|
+
pause: () => undefined,
|
|
29
|
+
cancel: () => undefined,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
let timer;
|
|
33
|
+
let cancelled = false;
|
|
34
|
+
const clear = () => {
|
|
35
|
+
if (timer) {
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
timer = undefined;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const fire = () => {
|
|
41
|
+
timer = undefined;
|
|
42
|
+
if (cancelled)
|
|
43
|
+
return;
|
|
44
|
+
if (shutdown.isShuttingDown() ||
|
|
45
|
+
shutdown.hasTerminalEvent() ||
|
|
46
|
+
isChildExited()) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
log.write(`[supervisor] idle timeout ${idleTimeoutMs}ms reached, requesting shutdown\n`);
|
|
50
|
+
void shutdown.request("SIGTERM", "idle-timeout");
|
|
51
|
+
};
|
|
52
|
+
const start = () => {
|
|
53
|
+
if (cancelled)
|
|
54
|
+
return;
|
|
55
|
+
clear();
|
|
56
|
+
timer = setTimeout(fire, idleTimeoutMs);
|
|
57
|
+
// Don't keep the supervisor alive solely for the idle timer; if
|
|
58
|
+
// every other handle has gone away the worker has nothing to do.
|
|
59
|
+
timer.unref?.();
|
|
60
|
+
};
|
|
61
|
+
// Initial schedule: worker just spawned, currently idle.
|
|
62
|
+
start();
|
|
63
|
+
return {
|
|
64
|
+
reset: start,
|
|
65
|
+
pause: clear,
|
|
66
|
+
cancel: () => {
|
|
67
|
+
cancelled = true;
|
|
68
|
+
clear();
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=idle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idle.js","sourceRoot":"","sources":["../../../../src/commands/channel/supervisor/idle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA0BH;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,IAA2B;IAE3B,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7D,IAAI,aAAa,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS;YACtB,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS;YACtB,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS;SACxB,CAAC;IACJ,CAAC;IAED,IAAI,KAAgD,CAAC;IACrD,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,KAAK,EAAE,CAAC;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,GAAG,SAAS,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,KAAK,GAAG,SAAS,CAAC;QAClB,IAAI,SAAS;YAAE,OAAO;QACtB,IACE,QAAQ,CAAC,cAAc,EAAE;YACzB,QAAQ,CAAC,gBAAgB,EAAE;YAC3B,aAAa,EAAE,EACf,CAAC;YACD,OAAO;QACT,CAAC;QACD,GAAG,CAAC,KAAK,CACP,6BAA6B,aAAa,mCAAmC,CAC9E,CAAC;QACF,KAAK,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACnD,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,SAAS;YAAE,OAAO;QACtB,KAAK,EAAE,CAAC;QACR,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACxC,gEAAgE;QAChE,iEAAiE;QACjE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,yDAAyD;IACzD,KAAK,EAAE,CAAC;IAER,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,GAAG,EAAE;YACX,SAAS,GAAG,IAAI,CAAC;YACjB,KAAK,EAAE,CAAC;QACV,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbox watcher: tails events.jsonl for worker-addressed messages and
|
|
3
|
+
* interrupt requests, then forwards accepted input into the worker's stdin.
|
|
4
|
+
* A persisted cursor file keeps respawns from replaying events the previous
|
|
5
|
+
* supervisor already delivered.
|
|
6
|
+
*
|
|
7
|
+
* Step 3 of the supervisor refactor: pulled out of supervisor.ts so the
|
|
8
|
+
* orchestrator only needs to call `runInboxWatcher(...)`. Cursor
|
|
9
|
+
* read/write helpers stay private to this module.
|
|
10
|
+
*/
|
|
11
|
+
import type { ChildProcessByStdio } from "node:child_process";
|
|
12
|
+
import type { Readable, Writable } from "node:stream";
|
|
13
|
+
import { type InboxPolicy } from "@mindfoldhq/trellis-core/channel";
|
|
14
|
+
import type { WorkerAdapter } from "../adapters/index.js";
|
|
15
|
+
import type { TurnTracker } from "./turns.js";
|
|
16
|
+
type Child = ChildProcessByStdio<Writable, Readable, Readable>;
|
|
17
|
+
export interface InboxWatcherArgs {
|
|
18
|
+
channelName: string;
|
|
19
|
+
workerName: string;
|
|
20
|
+
adapter: WorkerAdapter;
|
|
21
|
+
ctx: unknown;
|
|
22
|
+
child: Child;
|
|
23
|
+
signal: AbortSignal;
|
|
24
|
+
/** Inbox delivery policy. Defaults to `explicitOnly` (legacy behavior). */
|
|
25
|
+
inboxPolicy?: InboxPolicy;
|
|
26
|
+
turnTracker?: TurnTracker;
|
|
27
|
+
}
|
|
28
|
+
export declare function runInboxWatcher(args: InboxWatcherArgs): Promise<void>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=inbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../../../../src/commands/channel/supervisor/inbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,KAAK,KAAK,GAAG,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoH3E"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbox watcher: tails events.jsonl for worker-addressed messages and
|
|
3
|
+
* interrupt requests, then forwards accepted input into the worker's stdin.
|
|
4
|
+
* A persisted cursor file keeps respawns from replaying events the previous
|
|
5
|
+
* supervisor already delivered.
|
|
6
|
+
*
|
|
7
|
+
* Step 3 of the supervisor refactor: pulled out of supervisor.ts so the
|
|
8
|
+
* orchestrator only needs to call `runInboxWatcher(...)`. Cursor
|
|
9
|
+
* read/write helpers stay private to this module.
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import { DEFAULT_INBOX_POLICY, matchesInboxPolicy, } from "@mindfoldhq/trellis-core/channel";
|
|
13
|
+
import { appendEvent } from "../store/events.js";
|
|
14
|
+
import { workerFile } from "../store/paths.js";
|
|
15
|
+
import { watchEvents } from "../store/watch.js";
|
|
16
|
+
export async function runInboxWatcher(args) {
|
|
17
|
+
const { channelName, workerName, adapter, ctx, child, signal } = args;
|
|
18
|
+
const inboxPolicy = args.inboxPolicy ?? DEFAULT_INBOX_POLICY;
|
|
19
|
+
// Resume from persisted cursor: first-time spawn → 0 (read full backlog);
|
|
20
|
+
// respawn after kill → last forwarded seq (no replay).
|
|
21
|
+
let cursor = readInboxCursor(channelName, workerName);
|
|
22
|
+
for await (const ev of watchEvents(channelName, {
|
|
23
|
+
self: workerName, // ignore our own events
|
|
24
|
+
kind: ["message", "interrupt_requested"],
|
|
25
|
+
},
|
|
26
|
+
// First run with cursor=0 reads backlog from start; subsequent runs
|
|
27
|
+
// use sinceSeq to skip already-processed events. Both cases tail
|
|
28
|
+
// future events normally.
|
|
29
|
+
{ signal, sinceSeq: cursor, fromStart: cursor === 0 ? true : undefined })) {
|
|
30
|
+
if (signal.aborted)
|
|
31
|
+
return;
|
|
32
|
+
if (ev.kind === "message") {
|
|
33
|
+
// Core decides delivery from the worker's inbox policy: explicitOnly
|
|
34
|
+
// (default) consumes only targeted messages; broadcastAndExplicit
|
|
35
|
+
// also consumes broadcasts.
|
|
36
|
+
if (!matchesInboxPolicy(ev, workerName, inboxPolicy))
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
else if (ev.worker !== workerName) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const text = (ev.text ?? "").trim();
|
|
43
|
+
const interruptText = (ev.message ?? "").trim();
|
|
44
|
+
const isInterrupt = ev.kind === "interrupt_requested";
|
|
45
|
+
if (!text && (!isInterrupt || !interruptText))
|
|
46
|
+
continue;
|
|
47
|
+
// Block until the adapter says it can accept input (e.g. codex
|
|
48
|
+
// thread/start has produced a threadId). Drop the message if we
|
|
49
|
+
// never get ready before being aborted.
|
|
50
|
+
if (!adapter.isReady(ctx)) {
|
|
51
|
+
const deadline = Date.now() + 60_000;
|
|
52
|
+
while (!adapter.isReady(ctx) &&
|
|
53
|
+
Date.now() < deadline &&
|
|
54
|
+
!signal.aborted) {
|
|
55
|
+
await sleep(25);
|
|
56
|
+
}
|
|
57
|
+
if (!adapter.isReady(ctx)) {
|
|
58
|
+
// never became ready; advance the cursor anyway so we don't
|
|
59
|
+
// re-attempt this exact event on next start.
|
|
60
|
+
cursor = ev.seq;
|
|
61
|
+
writeInboxCursor(channelName, workerName, cursor);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!isInterrupt) {
|
|
66
|
+
await waitForActiveTurnToFinish(args.turnTracker, signal);
|
|
67
|
+
if (signal.aborted)
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (isInterrupt) {
|
|
71
|
+
const aborted = args.turnTracker?.abortCurrent();
|
|
72
|
+
if (aborted) {
|
|
73
|
+
await appendEvent(channelName, {
|
|
74
|
+
kind: "turn_finished",
|
|
75
|
+
by: workerName,
|
|
76
|
+
worker: workerName,
|
|
77
|
+
inputSeq: aborted.inputSeq,
|
|
78
|
+
turnId: aborted.turnId,
|
|
79
|
+
outcome: "aborted",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
await appendEvent(channelName, {
|
|
83
|
+
kind: "interrupted",
|
|
84
|
+
by: workerName,
|
|
85
|
+
worker: workerName,
|
|
86
|
+
...(aborted?.turnId ? { turnId: aborted.turnId } : {}),
|
|
87
|
+
reason: "user",
|
|
88
|
+
method: "stdin",
|
|
89
|
+
outcome: aborted ? "interrupted" : "no-active-turn",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
let turn = args.turnTracker?.begin(ev.seq);
|
|
93
|
+
try {
|
|
94
|
+
if (turn) {
|
|
95
|
+
await appendEvent(channelName, {
|
|
96
|
+
kind: "turn_started",
|
|
97
|
+
by: workerName,
|
|
98
|
+
worker: workerName,
|
|
99
|
+
inputSeq: ev.seq,
|
|
100
|
+
turnId: turn.turnId,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
child.stdin.write(isInterrupt
|
|
104
|
+
? adapter.encodeInterruptMessage(interruptText, ctx)
|
|
105
|
+
: adapter.encodeUserMessage(text, ctx));
|
|
106
|
+
cursor = ev.seq;
|
|
107
|
+
writeInboxCursor(channelName, workerName, cursor);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
if (turn) {
|
|
111
|
+
args.turnTracker?.finish();
|
|
112
|
+
await appendEvent(channelName, {
|
|
113
|
+
kind: "turn_finished",
|
|
114
|
+
by: workerName,
|
|
115
|
+
worker: workerName,
|
|
116
|
+
inputSeq: turn.inputSeq,
|
|
117
|
+
turnId: turn.turnId,
|
|
118
|
+
outcome: "aborted",
|
|
119
|
+
}).catch(() => undefined);
|
|
120
|
+
turn = undefined;
|
|
121
|
+
}
|
|
122
|
+
// stdin closed, worker exiting — bail out
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Per-worker inbox consumption cursor. Persisted to
|
|
129
|
+
* `<worker>.inbox-cursor` so a respawn (same worker name) doesn't replay
|
|
130
|
+
* messages that the previous supervisor already forwarded into the worker
|
|
131
|
+
* process. The cursor is the highest seq we've already turned into a
|
|
132
|
+
* worker stdin write.
|
|
133
|
+
*/
|
|
134
|
+
function readInboxCursor(channelName, workerName) {
|
|
135
|
+
try {
|
|
136
|
+
const raw = fs.readFileSync(workerFile(channelName, workerName, "inbox-cursor"), "utf-8");
|
|
137
|
+
const n = Number(raw.trim());
|
|
138
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function writeInboxCursor(channelName, workerName, seq) {
|
|
145
|
+
try {
|
|
146
|
+
fs.writeFileSync(workerFile(channelName, workerName, "inbox-cursor"), String(seq), "utf-8");
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// ignore — cursor is best-effort; worst case we replay a message
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function sleep(ms) {
|
|
153
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
154
|
+
}
|
|
155
|
+
async function waitForActiveTurnToFinish(turnTracker, signal) {
|
|
156
|
+
while (turnTracker?.current() && !signal.aborted) {
|
|
157
|
+
await sleep(25);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=inbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox.js","sourceRoot":"","sources":["../../../../src/commands/channel/supervisor/inbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShutdownController — the single funnel for every "this worker is going
|
|
3
|
+
* away" trigger (explicit kill, timeout, post-spawn crash, signal, child
|
|
4
|
+
* exit). It owns:
|
|
5
|
+
*
|
|
6
|
+
* - the `shutdownReason` flag (idempotent — first call wins)
|
|
7
|
+
* - the SIGTERM → grace → SIGKILL ladder
|
|
8
|
+
* - the trailing `killed` event append
|
|
9
|
+
* - a `terminalEmitted` flag tracking adapter-emitted done/error
|
|
10
|
+
* - `finalizeOnExit` — synthesises a fallback `done`/`error` when the
|
|
11
|
+
* worker exited without the adapter sending one (otherwise
|
|
12
|
+
* `wait --kind done` would hang forever)
|
|
13
|
+
* - `awaitFinalize` — lets `child.on("exit")` block process.exit until
|
|
14
|
+
* any in-progress killed-append from a concurrent shutdown completes
|
|
15
|
+
*
|
|
16
|
+
* Step 2 of the supervisor refactor: this absorbs the 5 reviewer issues
|
|
17
|
+
* (codex #1 crashed-without-done, #2 fire-and-forget shutdown, #3 spawn
|
|
18
|
+
* after shutdown requested, #4 handshake error detail; claude M2 post-
|
|
19
|
+
* spawn error ordering, L1 pre-spawn double-fire guard) into a single
|
|
20
|
+
* state machine. The supervisor.ts orchestrator stays mostly mechanical.
|
|
21
|
+
*/
|
|
22
|
+
import type { ChildProcessByStdio } from "node:child_process";
|
|
23
|
+
import type { Readable, Writable } from "node:stream";
|
|
24
|
+
type Child = ChildProcessByStdio<Writable, Readable, Readable>;
|
|
25
|
+
export type ShutdownReason = "explicit-kill" | "timeout" | "crash" | "idle-timeout";
|
|
26
|
+
export interface ShutdownController {
|
|
27
|
+
/** Idempotent: only the first call wins. Returns the killed-append
|
|
28
|
+
* promise so callers can await ordering if they need to. */
|
|
29
|
+
request(signal: NodeJS.Signals, reason: ShutdownReason): Promise<void>;
|
|
30
|
+
/** Synchronously mark shutdown intent without starting the kill
|
|
31
|
+
* ladder or appending `killed`. Use when other code must see
|
|
32
|
+
* `isShuttingDown=true` BEFORE the caller proceeds to any await
|
|
33
|
+
* (e.g. post-spawn error handler that needs to `await appendEvent`
|
|
34
|
+
* first, but doesn't want the main flow to keep writing `spawned`).
|
|
35
|
+
* Returns true if this call claimed the flag, false if already set. */
|
|
36
|
+
claim(reason: ShutdownReason): boolean;
|
|
37
|
+
isShuttingDown(): boolean;
|
|
38
|
+
reason(): ShutdownReason | null;
|
|
39
|
+
/** Mark that the adapter has produced a `done` or `error` event, so
|
|
40
|
+
* `finalizeOnExit` won't synthesise a fallback. */
|
|
41
|
+
markTerminalEmitted(): void;
|
|
42
|
+
hasTerminalEvent(): boolean;
|
|
43
|
+
/** Call from `child.on("exit")` — synthesises a fallback terminal
|
|
44
|
+
* event if the adapter never produced one, then awaits any pending
|
|
45
|
+
* `request()` so `killed` lands before the supervisor exits. */
|
|
46
|
+
finalizeOnExit(code: number | null, signal: NodeJS.Signals | null): Promise<void>;
|
|
47
|
+
/** Promise that resolves when the current (or last) `request()` has
|
|
48
|
+
* finished writing its `killed` event. No-op when never requested. */
|
|
49
|
+
awaitFinalize(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
export interface CreateShutdownArgs {
|
|
52
|
+
channelName: string;
|
|
53
|
+
workerName: string;
|
|
54
|
+
log: {
|
|
55
|
+
write: (data: string) => void;
|
|
56
|
+
};
|
|
57
|
+
/** Lazy child getter — the controller is created before `spawn()`
|
|
58
|
+
* returns, so we read the child handle at shutdown time. */
|
|
59
|
+
getChild: () => Child;
|
|
60
|
+
graceMs: number;
|
|
61
|
+
/** Recorded on the `killed` event for the timeout reason. */
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
/** Recorded on the `killed` event when reason is `"idle-timeout"`. */
|
|
64
|
+
idleTimeoutMs?: number;
|
|
65
|
+
}
|
|
66
|
+
export declare function createShutdown(args: CreateShutdownArgs): ShutdownController;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=shutdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../../../src/commands/channel/supervisor/shutdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAItD,KAAK,KAAK,GAAG,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAE/D,MAAM,MAAM,cAAc,GACtB,eAAe,GACf,SAAS,GACT,OAAO,GACP,cAAc,CAAC;AAEnB,MAAM,WAAW,kBAAkB;IACjC;iEAC6D;IAC7D,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE;;;;;4EAKwE;IACxE,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC;IACvC,cAAc,IAAI,OAAO,CAAC;IAC1B,MAAM,IAAI,cAAc,GAAG,IAAI,CAAC;IAEhC;wDACoD;IACpD,mBAAmB,IAAI,IAAI,CAAC;IAC5B,gBAAgB,IAAI,OAAO,CAAC;IAE5B;;qEAEiE;IACjE,cAAc,CACZ,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;2EACuE;IACvE,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE;QAAE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACvC;iEAC6D;IAC7D,QAAQ,EAAE,MAAM,KAAK,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,CAkJ3E"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShutdownController — the single funnel for every "this worker is going
|
|
3
|
+
* away" trigger (explicit kill, timeout, post-spawn crash, signal, child
|
|
4
|
+
* exit). It owns:
|
|
5
|
+
*
|
|
6
|
+
* - the `shutdownReason` flag (idempotent — first call wins)
|
|
7
|
+
* - the SIGTERM → grace → SIGKILL ladder
|
|
8
|
+
* - the trailing `killed` event append
|
|
9
|
+
* - a `terminalEmitted` flag tracking adapter-emitted done/error
|
|
10
|
+
* - `finalizeOnExit` — synthesises a fallback `done`/`error` when the
|
|
11
|
+
* worker exited without the adapter sending one (otherwise
|
|
12
|
+
* `wait --kind done` would hang forever)
|
|
13
|
+
* - `awaitFinalize` — lets `child.on("exit")` block process.exit until
|
|
14
|
+
* any in-progress killed-append from a concurrent shutdown completes
|
|
15
|
+
*
|
|
16
|
+
* Step 2 of the supervisor refactor: this absorbs the 5 reviewer issues
|
|
17
|
+
* (codex #1 crashed-without-done, #2 fire-and-forget shutdown, #3 spawn
|
|
18
|
+
* after shutdown requested, #4 handshake error detail; claude M2 post-
|
|
19
|
+
* spawn error ordering, L1 pre-spawn double-fire guard) into a single
|
|
20
|
+
* state machine. The supervisor.ts orchestrator stays mostly mechanical.
|
|
21
|
+
*/
|
|
22
|
+
import { appendEvent } from "../store/events.js";
|
|
23
|
+
export function createShutdown(args) {
|
|
24
|
+
const { channelName, workerName, log, getChild, graceMs, timeoutMs, idleTimeoutMs, } = args;
|
|
25
|
+
let shutdownReason = null;
|
|
26
|
+
let requestSignal = null;
|
|
27
|
+
let terminalEmitted = false;
|
|
28
|
+
let killedPromise = null;
|
|
29
|
+
const childStillRunning = (child) => child.exitCode === null && child.signalCode === null;
|
|
30
|
+
const startKillLadder = (child) => {
|
|
31
|
+
try {
|
|
32
|
+
child.stdin.end();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// already closed
|
|
36
|
+
}
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
if (childStillRunning(child)) {
|
|
39
|
+
log.write(`[supervisor] grace expired, SIGTERM worker\n`);
|
|
40
|
+
try {
|
|
41
|
+
child.kill("SIGTERM");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// already dead
|
|
45
|
+
}
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
if (childStillRunning(child)) {
|
|
48
|
+
log.write(`[supervisor] still alive, SIGKILL worker\n`);
|
|
49
|
+
try {
|
|
50
|
+
child.kill("SIGKILL");
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// already dead
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}, graceMs);
|
|
57
|
+
}
|
|
58
|
+
}, graceMs);
|
|
59
|
+
};
|
|
60
|
+
const writeKilled = async (reason, signal) => {
|
|
61
|
+
await appendEvent(channelName, {
|
|
62
|
+
kind: "killed",
|
|
63
|
+
by: `supervisor:${workerName}`,
|
|
64
|
+
reason,
|
|
65
|
+
signal,
|
|
66
|
+
...(reason === "timeout" && timeoutMs ? { timeout_ms: timeoutMs } : {}),
|
|
67
|
+
...(reason === "idle-timeout" && idleTimeoutMs
|
|
68
|
+
? { idle_timeout_ms: idleTimeoutMs }
|
|
69
|
+
: {}),
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
const claim = (reason) => {
|
|
73
|
+
if (shutdownReason)
|
|
74
|
+
return false;
|
|
75
|
+
shutdownReason = reason;
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
const request = async (signal, reason) => {
|
|
79
|
+
// The kill ladder + killed-append are one-shot; whether we got here
|
|
80
|
+
// via `claim()` + `request()` (post-spawn error) or a single
|
|
81
|
+
// `request()` (signal / timeout), only run them once.
|
|
82
|
+
if (killedPromise) {
|
|
83
|
+
await killedPromise.catch(() => undefined);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
shutdownReason ??= reason;
|
|
87
|
+
requestSignal ??= signal;
|
|
88
|
+
log.write(`[supervisor] shutting down worker (reason=${shutdownReason}, signal=${requestSignal})\n`);
|
|
89
|
+
startKillLadder(getChild());
|
|
90
|
+
killedPromise = writeKilled(shutdownReason, requestSignal);
|
|
91
|
+
await killedPromise;
|
|
92
|
+
};
|
|
93
|
+
const finalizeOnExit = async (code, signal) => {
|
|
94
|
+
log.write(`[supervisor] worker exit code=${code ?? "null"} signal=${signal ?? "null"}\n`);
|
|
95
|
+
// #1 fix: synthesise a terminal event so consumers blocked on
|
|
96
|
+
// `wait --kind done` don't hang when the adapter never produced
|
|
97
|
+
// one. Only applies to COLD exits (no explicit shutdown requested);
|
|
98
|
+
// when shutdown was requested the `killed` event already serves as
|
|
99
|
+
// the terminal signal and a synthesised error would just duplicate.
|
|
100
|
+
//
|
|
101
|
+
// The flag is claimed SYNCHRONOUSLY before the await so two
|
|
102
|
+
// concurrent `finalizeOnExit` calls (or `applyParseResult` racing
|
|
103
|
+
// with `child.on("exit")`) can't both pass the guard and emit
|
|
104
|
+
// duplicate synthetic events. `by` is the worker name (not
|
|
105
|
+
// `supervisor:<name>`) so `wait --from <worker> --kind done` wakes
|
|
106
|
+
// for the synthesised event same as for an adapter-emitted one.
|
|
107
|
+
if (!terminalEmitted && shutdownReason === null) {
|
|
108
|
+
terminalEmitted = true;
|
|
109
|
+
if (code === 0) {
|
|
110
|
+
await appendEvent(channelName, {
|
|
111
|
+
kind: "done",
|
|
112
|
+
by: workerName,
|
|
113
|
+
synthesized: true,
|
|
114
|
+
exit_code: code,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
await appendEvent(channelName, {
|
|
119
|
+
kind: "error",
|
|
120
|
+
by: workerName,
|
|
121
|
+
message: `worker exited without terminal event (code=${code ?? "null"}, signal=${signal ?? "null"})`,
|
|
122
|
+
synthesized: true,
|
|
123
|
+
exit_code: code,
|
|
124
|
+
exit_signal: signal,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// #2 fix: wait for any in-progress `killed` append from a concurrent
|
|
129
|
+
// shutdown request so it lands before the supervisor exits.
|
|
130
|
+
if (killedPromise)
|
|
131
|
+
await killedPromise.catch(() => undefined);
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
request,
|
|
135
|
+
claim,
|
|
136
|
+
isShuttingDown: () => shutdownReason !== null,
|
|
137
|
+
reason: () => shutdownReason,
|
|
138
|
+
markTerminalEmitted: () => {
|
|
139
|
+
terminalEmitted = true;
|
|
140
|
+
},
|
|
141
|
+
hasTerminalEvent: () => terminalEmitted,
|
|
142
|
+
finalizeOnExit,
|
|
143
|
+
awaitFinalize: () => killedPromise ?? Promise.resolve(),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=shutdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../../../src/commands/channel/supervisor/shutdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stdout pipeline: line-buffered reader → adapter.parseLine → append
|
|
3
|
+
* events into events.jsonl + persist session/thread IDs + write any
|
|
4
|
+
* adapter `reply` back to the worker's stdin.
|
|
5
|
+
*
|
|
6
|
+
* Step 3 of the supervisor refactor: pulled out of supervisor.ts so the
|
|
7
|
+
* orchestrator stays thin. The pump itself is pure (no fs / process), so
|
|
8
|
+
* unit testing the line-splitting logic is straightforward once we want
|
|
9
|
+
* to. `applyParseResult` still touches fs for session-id persistence —
|
|
10
|
+
* that's intentional, it's the only place that needs to.
|
|
11
|
+
*/
|
|
12
|
+
import type { ChildProcessByStdio } from "node:child_process";
|
|
13
|
+
import type { Readable, Writable } from "node:stream";
|
|
14
|
+
import type { WorkerAdapter } from "../adapters/index.js";
|
|
15
|
+
import type { ParseResult } from "../adapters/types.js";
|
|
16
|
+
import type { ShutdownController } from "./shutdown.js";
|
|
17
|
+
import type { TurnTracker } from "./turns.js";
|
|
18
|
+
type Child = ChildProcessByStdio<Writable, Readable, Readable>;
|
|
19
|
+
/**
|
|
20
|
+
* Line-buffered stdout pump. Yields each non-empty line to `onLine` as
|
|
21
|
+
* soon as a newline arrives, wraps the handler in a `.catch` so a thrown
|
|
22
|
+
* await doesn't escape as `unhandledRejection`, and reports the failure
|
|
23
|
+
* through `onError` for observability.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pumpStdout(stream: Readable, onLine: (line: string) => Promise<void> | void, onError?: (err: Error) => void): void;
|
|
26
|
+
/**
|
|
27
|
+
* Translate an adapter `ParseResult` into channel events + adapter-level
|
|
28
|
+
* side-effects (session-id persistence, stdin writes). Also tells the
|
|
29
|
+
* shutdown controller when the adapter emits a `done`/`error` so the
|
|
30
|
+
* fallback synthesiser in `finalizeOnExit` doesn't duplicate.
|
|
31
|
+
*/
|
|
32
|
+
export declare function applyParseResult(channelName: string, workerName: string, result: ParseResult, child: Child, shutdown: ShutdownController, turnTracker?: TurnTracker): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Convenience wrapper: wire `pumpStdout` to `applyParseResult` with
|
|
35
|
+
* standard error-event-on-failure handling. The orchestrator just calls
|
|
36
|
+
* this and forgets about line buffering / parse plumbing.
|
|
37
|
+
*/
|
|
38
|
+
export declare function startStdoutPump(args: {
|
|
39
|
+
channelName: string;
|
|
40
|
+
workerName: string;
|
|
41
|
+
child: Child;
|
|
42
|
+
adapter: WorkerAdapter;
|
|
43
|
+
adapterCtx: unknown;
|
|
44
|
+
log: {
|
|
45
|
+
write: (data: string) => void;
|
|
46
|
+
};
|
|
47
|
+
shutdown: ShutdownController;
|
|
48
|
+
turnTracker?: TurnTracker;
|
|
49
|
+
}): void;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=stdout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdout.d.ts","sourceRoot":"","sources":["../../../../src/commands/channel/supervisor/stdout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,YAAY,CAAC;AAE3D,KAAK,KAAK,GAAG,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAC9C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,GAC7B,IAAI,CAuBN;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,kBAAkB,EAC5B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,IAAI,CAAC,CAqDf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE;QAAE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACvC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GAAG,IAAI,CAkCP"}
|