oh-my-codex 0.18.7 → 0.18.8
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/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +5 -5
- package/crates/omx-sparkshell/tests/execution.rs +1 -1
- package/dist/agents/__tests__/native-config.test.js +42 -1
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +8 -0
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +1 -0
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +5 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +17 -2
- package/dist/agents/native-config.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +61 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +14 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +65 -0
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +21 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +2 -2
- package/dist/cli/__tests__/update.test.js +110 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +8 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +11 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +108 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +14 -2
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +62 -15
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +3 -1
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +2 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +4 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +166 -27
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +8 -1
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +16 -0
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/update.d.ts +2 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +47 -3
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +1 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +2 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +2 -2
- package/dist/config/generator.js.map +1 -1
- package/dist/config/team-mode.d.ts +12 -0
- package/dist/config/team-mode.d.ts.map +1 -0
- package/dist/config/team-mode.js +91 -0
- package/dist/config/team-mode.js.map +1 -0
- package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +423 -3
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +36 -50
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +17 -21
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +258 -12
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +6 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +1 -0
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/authority.test.js +435 -32
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +42 -0
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +521 -15
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +61 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +132 -4
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +180 -21
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/authority.d.ts +5 -0
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +324 -28
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/index.d.ts +3 -2
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +42 -19
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts +3 -3
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +128 -19
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +35 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +61 -62
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +24 -6
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +136 -38
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +11 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +71 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +32 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +113 -17
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/scripts/__tests__/codex-native-hook.test.js +593 -11
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js +74 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +88 -31
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +3 -1
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +62 -38
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +24 -18
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +75 -11
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-test-files.js +193 -22
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +61 -3
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-agents.d.ts.map +1 -1
- package/dist/scripts/verify-native-agents.js +58 -1
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +113 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +3 -16
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +25 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +57 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +7 -39
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +10 -14
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1 -1
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +9 -4
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +195 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +3 -2
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +142 -12
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +8 -8
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
- package/plugins/oh-my-codex/hooks/hooks.json +1 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +3 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
- package/skills/autopilot/SKILL.md +3 -1
- package/skills/code-review/SKILL.md +7 -7
- package/skills/ralph/SKILL.md +22 -22
- package/skills/ultraqa/SKILL.md +9 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +686 -13
- package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
- package/src/scripts/__tests__/run-test-files.test.ts +102 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
- package/src/scripts/codex-native-hook.ts +105 -28
- package/src/scripts/demo-team-e2e.sh +10 -7
- package/src/scripts/eval/eval-parity-smoke.ts +1 -1
- package/src/scripts/notify-hook/auto-nudge.ts +3 -1
- package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
- package/src/scripts/notify-hook/state-io.ts +75 -37
- package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
- package/src/scripts/notify-hook/tmux-injection.ts +35 -19
- package/src/scripts/notify-hook.ts +91 -4
- package/src/scripts/run-test-files.ts +192 -22
- package/src/scripts/sync-plugin-mirror.ts +98 -9
- package/src/scripts/verify-native-agents.ts +65 -1
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { basename } from 'path';
|
|
2
|
+
import { readFileSync, writeSync } from 'fs';
|
|
2
3
|
import { pathToFileURL } from 'url';
|
|
3
4
|
import { createHookPluginSdk } from './sdk.js';
|
|
4
5
|
const RESULT_PREFIX = '__OMX_PLUGIN_RESULT__ ';
|
|
5
6
|
async function readStdin() {
|
|
6
|
-
|
|
7
|
-
for await (const chunk of process.stdin) {
|
|
8
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
9
|
-
}
|
|
10
|
-
return Buffer.concat(chunks).toString('utf-8').trim();
|
|
7
|
+
return readFileSync(process.stdin.fd, 'utf-8').trim();
|
|
11
8
|
}
|
|
12
9
|
function emitResult(result) {
|
|
13
|
-
process.stdout.
|
|
10
|
+
writeSync(process.stdout.fd, `${RESULT_PREFIX}${JSON.stringify(result)}\n`);
|
|
11
|
+
}
|
|
12
|
+
function finish(result, exitCode) {
|
|
13
|
+
process.exitCode = exitCode;
|
|
14
|
+
emitResult(result);
|
|
15
|
+
process.exit(exitCode);
|
|
14
16
|
}
|
|
15
17
|
async function main() {
|
|
16
18
|
const raw = await readStdin();
|
|
17
19
|
if (!raw) {
|
|
18
|
-
|
|
19
|
-
process.exit(1);
|
|
20
|
+
finish({ ok: false, plugin: 'unknown', reason: 'empty_request' }, 1);
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
23
|
let request;
|
|
@@ -24,8 +25,7 @@ async function main() {
|
|
|
24
25
|
request = JSON.parse(raw);
|
|
25
26
|
}
|
|
26
27
|
catch {
|
|
27
|
-
|
|
28
|
-
process.exit(1);
|
|
28
|
+
finish({ ok: false, plugin: 'unknown', reason: 'invalid_json' }, 1);
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
const pluginId = (request.pluginId || basename(request.pluginPath || 'unknown')).trim() || 'unknown';
|
|
@@ -33,8 +33,7 @@ async function main() {
|
|
|
33
33
|
const moduleUrl = `${pathToFileURL(request.pluginPath).href}?t=${Date.now()}`;
|
|
34
34
|
const loaded = await import(moduleUrl);
|
|
35
35
|
if (typeof loaded.onHookEvent !== 'function') {
|
|
36
|
-
|
|
37
|
-
process.exit(1);
|
|
36
|
+
finish({ ok: false, plugin: pluginId, reason: 'invalid_export' }, 1);
|
|
38
37
|
return;
|
|
39
38
|
}
|
|
40
39
|
const sdk = createHookPluginSdk({
|
|
@@ -44,26 +43,23 @@ async function main() {
|
|
|
44
43
|
sideEffectsEnabled: request.sideEffectsEnabled !== false,
|
|
45
44
|
});
|
|
46
45
|
await Promise.resolve(loaded.onHookEvent(request.event, sdk));
|
|
47
|
-
|
|
48
|
-
process.exit(0);
|
|
46
|
+
finish({ ok: true, plugin: pluginId, reason: 'ok' }, 0);
|
|
49
47
|
}
|
|
50
48
|
catch (error) {
|
|
51
|
-
|
|
49
|
+
finish({
|
|
52
50
|
ok: false,
|
|
53
51
|
plugin: pluginId,
|
|
54
52
|
reason: 'runner_error',
|
|
55
53
|
error: error instanceof Error ? error.message : String(error),
|
|
56
|
-
});
|
|
57
|
-
process.exit(1);
|
|
54
|
+
}, 1);
|
|
58
55
|
}
|
|
59
56
|
}
|
|
60
|
-
main().catch((error) => {
|
|
61
|
-
|
|
57
|
+
await main().catch((error) => {
|
|
58
|
+
finish({
|
|
62
59
|
ok: false,
|
|
63
60
|
plugin: 'unknown',
|
|
64
61
|
reason: 'runner_error',
|
|
65
62
|
error: error instanceof Error ? error.message : String(error),
|
|
66
|
-
});
|
|
67
|
-
process.exit(1);
|
|
63
|
+
}, 1);
|
|
68
64
|
});
|
|
69
65
|
//# sourceMappingURL=plugin-runner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-runner.js","sourceRoot":"","sources":["../../../src/hooks/extensibility/plugin-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAkB/C,MAAM,aAAa,GAAG,wBAAwB,CAAC;AAE/C,KAAK,UAAU,SAAS;IACtB,
|
|
1
|
+
{"version":3,"file":"plugin-runner.js","sourceRoot":"","sources":["../../../src/hooks/extensibility/plugin-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyword-detector.d.ts","sourceRoot":"","sources":["../../src/hooks/keyword-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"keyword-detector.d.ts","sourceRoot":"","sources":["../../src/hooks/keyword-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
* skill/workflow state. AGENTS.md now carries the behavioral fallback contract
|
|
10
10
|
* rather than the full keyword/state table.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import { constants as fsConstants } from 'node:fs';
|
|
13
|
+
import { access, lstat, mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
13
14
|
import { withModeRuntimeContext } from '../state/mode-state-context.js';
|
|
14
|
-
import { dirname, join } from 'node:path';
|
|
15
|
+
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
15
16
|
import { classifyTaskSize, isHeavyMode } from './task-size-detector.js';
|
|
16
17
|
import { isApprovedExecutionFollowupShortcut } from '../team/followup-planner.js';
|
|
17
18
|
import { isPlanningComplete, readPlanningArtifacts } from '../planning/artifacts.js';
|
|
18
19
|
import { hasDurableRalplanConsensusEvidenceForCwd } from '../ralplan/consensus-gate.js';
|
|
19
20
|
import { KEYWORD_TRIGGER_DEFINITIONS, compareKeywordMatches } from './keyword-registry.js';
|
|
21
|
+
import { readTeamModeConfig } from '../config/team-mode.js';
|
|
20
22
|
import { SKILL_ACTIVE_STATE_FILE, listActiveSkills, writeSkillActiveStateCopiesForStateDir, } from '../state/skill-active.js';
|
|
21
23
|
import { buildWorkflowTransitionError, evaluateWorkflowTransition, isTrackedWorkflowMode, } from '../state/workflow-transition.js';
|
|
22
24
|
import { reconcileWorkflowTransition } from '../state/workflow-transition-reconcile.js';
|
|
@@ -58,6 +60,198 @@ const STATEFUL_SKILL_SEED_CONFIG = {
|
|
|
58
60
|
ultrawork: { mode: 'ultrawork', initialPhase: 'planning' },
|
|
59
61
|
ultraqa: { mode: 'ultraqa', initialPhase: 'planning' },
|
|
60
62
|
};
|
|
63
|
+
function slugifyAutopilotTask(text) {
|
|
64
|
+
const slug = text
|
|
65
|
+
.replace(/(?:^|\s)\$?(?:oh-my-codex:)?autopilot\b/gi, ' ')
|
|
66
|
+
.replace(/[^A-Za-z0-9]+/g, '-')
|
|
67
|
+
.replace(/^-+|-+$/g, '')
|
|
68
|
+
.toLowerCase()
|
|
69
|
+
.slice(0, 48)
|
|
70
|
+
.replace(/-+$/g, '');
|
|
71
|
+
return slug || 'autopilot-task';
|
|
72
|
+
}
|
|
73
|
+
function utcCompactTimestamp(nowIso) {
|
|
74
|
+
const parsed = new Date(nowIso);
|
|
75
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
76
|
+
throw new Error(`Invalid Autopilot context timestamp: ${nowIso}`);
|
|
77
|
+
}
|
|
78
|
+
return parsed.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
|
|
79
|
+
}
|
|
80
|
+
function isSafeAutopilotContextSnapshotPath(value) {
|
|
81
|
+
const path = safeString(value).trim();
|
|
82
|
+
const contextPrefix = '.omx/context/';
|
|
83
|
+
const snapshotName = path.startsWith(contextPrefix) ? path.slice(contextPrefix.length) : '';
|
|
84
|
+
return path.startsWith('.omx/context/')
|
|
85
|
+
&& path.endsWith('.md')
|
|
86
|
+
&& !isAbsolute(path)
|
|
87
|
+
&& !path.split('/').includes('..')
|
|
88
|
+
&& !path.includes('\\')
|
|
89
|
+
&& snapshotName !== ''
|
|
90
|
+
&& !snapshotName.includes('/');
|
|
91
|
+
}
|
|
92
|
+
function isAutopilotRecoverySnapshotPath(path) {
|
|
93
|
+
return path.startsWith('.omx/context/autopilot-recovery-');
|
|
94
|
+
}
|
|
95
|
+
const MAX_REUSABLE_AUTOPILOT_CONTEXT_SNAPSHOT_BYTES = 1024 * 1024;
|
|
96
|
+
async function isReadableAutopilotContextSnapshotPath(sourceCwd, value) {
|
|
97
|
+
if (!isSafeAutopilotContextSnapshotPath(value))
|
|
98
|
+
return false;
|
|
99
|
+
const contextDir = await ensureSafeAutopilotContextDir(sourceCwd);
|
|
100
|
+
const absolutePath = join(sourceCwd, value);
|
|
101
|
+
try {
|
|
102
|
+
const snapshotStat = await lstat(absolutePath);
|
|
103
|
+
if (!snapshotStat.isFile() || snapshotStat.isSymbolicLink())
|
|
104
|
+
return false;
|
|
105
|
+
if (snapshotStat.size > MAX_REUSABLE_AUTOPILOT_CONTEXT_SNAPSHOT_BYTES)
|
|
106
|
+
return false;
|
|
107
|
+
const contextRealPath = await realpath(contextDir);
|
|
108
|
+
const snapshotRealPath = await realpath(absolutePath);
|
|
109
|
+
const relativeToContext = relative(contextRealPath, snapshotRealPath);
|
|
110
|
+
if (relativeToContext === '' || relativeToContext.startsWith('..') || isAbsolute(relativeToContext))
|
|
111
|
+
return false;
|
|
112
|
+
await access(absolutePath, fsConstants.R_OK);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function isRecord(value) {
|
|
120
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
121
|
+
}
|
|
122
|
+
function normalizeAutopilotContextSnapshotCandidate(candidate) {
|
|
123
|
+
if (isRecord(candidate.value)) {
|
|
124
|
+
const path = safeString(candidate.value.path).trim();
|
|
125
|
+
const kind = safeString(candidate.value.kind).trim();
|
|
126
|
+
if (!path || !['canonical', 'legacy', 'recovery'].includes(kind))
|
|
127
|
+
return null;
|
|
128
|
+
if (kind === 'recovery' || isAutopilotRecoverySnapshotPath(path))
|
|
129
|
+
return null;
|
|
130
|
+
return { path, kind };
|
|
131
|
+
}
|
|
132
|
+
const path = safeString(candidate.value).trim();
|
|
133
|
+
if (!path)
|
|
134
|
+
return null;
|
|
135
|
+
if (isAutopilotRecoverySnapshotPath(path))
|
|
136
|
+
return null;
|
|
137
|
+
return { path, kind: candidate.kind ?? 'legacy' };
|
|
138
|
+
}
|
|
139
|
+
async function findReusableAutopilotContextSnapshotPath(sourceCwd, candidates) {
|
|
140
|
+
for (const candidate of candidates) {
|
|
141
|
+
const normalized = normalizeAutopilotContextSnapshotCandidate(candidate);
|
|
142
|
+
if (!normalized || isAutopilotRecoverySnapshotPath(normalized.path) || !isSafeAutopilotContextSnapshotPath(normalized.path))
|
|
143
|
+
continue;
|
|
144
|
+
if (await isReadableAutopilotContextSnapshotPath(sourceCwd, normalized.path))
|
|
145
|
+
return normalized;
|
|
146
|
+
}
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const AUTOPILOT_CONTEXT_RECOVERY_REASON_MESSAGES = {
|
|
150
|
+
'missing-or-unsafe-legacy-context-snapshot': 'no safe legacy Autopilot context snapshot path was available during continuation.',
|
|
151
|
+
'missing-autopilot-mode-state': 'active Autopilot skill state existed but no matching Autopilot mode state was available during continuation.',
|
|
152
|
+
'malformed-autopilot-mode-state': 'active Autopilot mode state could not be parsed during continuation.',
|
|
153
|
+
'nonpreservable-autopilot-mode-state-missing-current-phase': 'active Autopilot mode state was missing current_phase during continuation.',
|
|
154
|
+
};
|
|
155
|
+
async function ensureSafeAutopilotContextDir(sourceCwd) {
|
|
156
|
+
const rootRealPath = await realpath(sourceCwd);
|
|
157
|
+
const omxDir = join(sourceCwd, '.omx');
|
|
158
|
+
await mkdir(omxDir, { recursive: true });
|
|
159
|
+
if ((await lstat(omxDir)).isSymbolicLink()) {
|
|
160
|
+
throw new Error('Unsafe Autopilot context directory: .omx is a symbolic link');
|
|
161
|
+
}
|
|
162
|
+
const contextDir = join(omxDir, 'context');
|
|
163
|
+
await mkdir(contextDir, { recursive: true });
|
|
164
|
+
if ((await lstat(contextDir)).isSymbolicLink()) {
|
|
165
|
+
throw new Error('Unsafe Autopilot context directory: .omx/context is a symbolic link');
|
|
166
|
+
}
|
|
167
|
+
const contextRealPath = await realpath(contextDir);
|
|
168
|
+
const relativeToRoot = relative(rootRealPath, contextRealPath);
|
|
169
|
+
if (relativeToRoot === '' || relativeToRoot.startsWith('..') || isAbsolute(relativeToRoot)) {
|
|
170
|
+
throw new Error('Unsafe Autopilot context directory: resolved path escapes repository root');
|
|
171
|
+
}
|
|
172
|
+
return contextDir;
|
|
173
|
+
}
|
|
174
|
+
async function writeUniqueAutopilotContextSnapshot(sourceCwd, slug, nowIso, body) {
|
|
175
|
+
const contextDir = await ensureSafeAutopilotContextDir(sourceCwd);
|
|
176
|
+
const timestamp = utcCompactTimestamp(nowIso);
|
|
177
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
178
|
+
const suffix = attempt === 0 ? '' : `-${attempt + 1}`;
|
|
179
|
+
const filename = `${slug}-${timestamp}${suffix}.md`;
|
|
180
|
+
const relativePath = `.omx/context/${filename}`;
|
|
181
|
+
const absolutePath = resolve(contextDir, filename);
|
|
182
|
+
try {
|
|
183
|
+
await writeFile(absolutePath, body, { encoding: 'utf-8', flag: 'wx' });
|
|
184
|
+
return relativePath;
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (error.code === 'EEXIST')
|
|
188
|
+
continue;
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
throw new Error(`Unable to allocate unique Autopilot context snapshot for ${slug}`);
|
|
193
|
+
}
|
|
194
|
+
async function ensureAutopilotContextSnapshot(sourceCwd, nowIso, activationText, existingSnapshot, options = {}) {
|
|
195
|
+
if (existingSnapshot) {
|
|
196
|
+
if (isSafeAutopilotContextSnapshotPath(existingSnapshot.path)) {
|
|
197
|
+
return {
|
|
198
|
+
path: existingSnapshot.path,
|
|
199
|
+
kind: existingSnapshot.kind,
|
|
200
|
+
original_task_status: existingSnapshot.kind === 'legacy' ? 'legacy-unverified' : 'activation-prompt',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
throw new Error(`Unsafe Autopilot context snapshot path: ${existingSnapshot.path}`);
|
|
204
|
+
}
|
|
205
|
+
if (options.allowTaskSnapshotCreation === false) {
|
|
206
|
+
const slug = 'autopilot-recovery';
|
|
207
|
+
const continuationInput = activationText.trim() || '<empty>';
|
|
208
|
+
const reason = options.recoveryReason ?? 'missing-or-unsafe-legacy-context-snapshot';
|
|
209
|
+
const body = [
|
|
210
|
+
'# Autopilot context recovery',
|
|
211
|
+
'',
|
|
212
|
+
'- recovery status: degraded',
|
|
213
|
+
`- recovery reason: ${reason}`,
|
|
214
|
+
`- reason detail: ${AUTOPILOT_CONTEXT_RECOVERY_REASON_MESSAGES[reason]}`,
|
|
215
|
+
`- continuation input: ${continuationInput}`,
|
|
216
|
+
'- original task status: unavailable',
|
|
217
|
+
'- original task seed: unavailable; do not treat the continuation input as the task seed.',
|
|
218
|
+
'- required follow-up: re-establish or confirm the intended task context before downstream handoff.',
|
|
219
|
+
'',
|
|
220
|
+
].join('\n');
|
|
221
|
+
const path = await writeUniqueAutopilotContextSnapshot(sourceCwd, slug, nowIso, body);
|
|
222
|
+
return {
|
|
223
|
+
path,
|
|
224
|
+
kind: 'recovery',
|
|
225
|
+
original_task_status: 'unavailable',
|
|
226
|
+
recovery: {
|
|
227
|
+
status: 'degraded',
|
|
228
|
+
reason,
|
|
229
|
+
recovered_at: nowIso,
|
|
230
|
+
source: 'keyword-detector',
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const slug = slugifyAutopilotTask(activationText);
|
|
235
|
+
const taskSeed = activationText.trim() || '$autopilot';
|
|
236
|
+
const body = [
|
|
237
|
+
`# Autopilot task seed: ${slug}`,
|
|
238
|
+
'',
|
|
239
|
+
`- activation prompt / task seed: ${taskSeed}`,
|
|
240
|
+
'- original task status: activation-prompt',
|
|
241
|
+
'- scope note: this seed captures the Autopilot activation prompt and is not guaranteed to include prior conversation context.',
|
|
242
|
+
'- desired outcome: complete the requested Autopilot workflow correctly with durable gate evidence.',
|
|
243
|
+
'- known facts/evidence: Autopilot was activated from a UserPromptSubmit keyword.',
|
|
244
|
+
'- constraints: follow deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa; do not skip gates without persisted evidence.',
|
|
245
|
+
'- unknowns/open questions: to be resolved by the deep-interview gate.',
|
|
246
|
+
'- likely codebase touchpoints: to be discovered during pre-context intake and planning.',
|
|
247
|
+
'',
|
|
248
|
+
].join('\n');
|
|
249
|
+
return {
|
|
250
|
+
path: await writeUniqueAutopilotContextSnapshot(sourceCwd, slug, nowIso, body),
|
|
251
|
+
kind: 'canonical',
|
|
252
|
+
original_task_status: 'activation-prompt',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
61
255
|
function createDeepInterviewInputLock(nowIso, previous) {
|
|
62
256
|
return {
|
|
63
257
|
active: true,
|
|
@@ -118,12 +312,21 @@ async function readExistingDeepInterviewState(statePath) {
|
|
|
118
312
|
}
|
|
119
313
|
}
|
|
120
314
|
async function readJsonStateIfExists(path) {
|
|
315
|
+
return (await readJsonStateWithStatus(path)).state;
|
|
316
|
+
}
|
|
317
|
+
async function readJsonStateWithStatus(path) {
|
|
121
318
|
try {
|
|
122
319
|
const raw = await readFile(path, 'utf-8');
|
|
123
|
-
|
|
320
|
+
const parsed = JSON.parse(raw);
|
|
321
|
+
if (!isRecord(parsed))
|
|
322
|
+
return { state: null, status: 'malformed' };
|
|
323
|
+
return { state: parsed, status: 'ok' };
|
|
124
324
|
}
|
|
125
|
-
catch {
|
|
126
|
-
|
|
325
|
+
catch (error) {
|
|
326
|
+
if (error.code === 'ENOENT') {
|
|
327
|
+
return { state: null, status: 'missing' };
|
|
328
|
+
}
|
|
329
|
+
return { state: null, status: 'malformed' };
|
|
127
330
|
}
|
|
128
331
|
}
|
|
129
332
|
export async function persistDeepInterviewModeState(stateDir, nextSkill, nowIso, previousSkill, input) {
|
|
@@ -208,12 +411,13 @@ function isResettableTerminalModeState(state, expectedMode) {
|
|
|
208
411
|
|| lifecycleOutcome === 'userinterlude'
|
|
209
412
|
|| (expectedMode === 'ralph' && lifecycleOutcome === 'blocked');
|
|
210
413
|
}
|
|
211
|
-
async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previousSkill) {
|
|
414
|
+
async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previousSkill, activationText, sourceCwd, options = {}) {
|
|
212
415
|
const config = STATEFUL_SKILL_SEED_CONFIG[nextSkill.skill];
|
|
213
416
|
if (!config)
|
|
214
417
|
return nextSkill;
|
|
215
418
|
const { absolutePath, relativePath } = resolveSeedStateFilePath(stateDir, config.mode, nextSkill.session_id, config.scope);
|
|
216
|
-
const
|
|
419
|
+
const existingModeStateResult = await readJsonStateWithStatus(absolutePath);
|
|
420
|
+
const existingModeState = existingModeStateResult.state;
|
|
217
421
|
const sameActiveSkill = previousSkill?.skill === nextSkill.skill && previousSkill.active;
|
|
218
422
|
const existingModeMatches = safeString(existingModeState?.mode).trim() === config.mode;
|
|
219
423
|
const existingPhase = safeString(existingModeState?.current_phase).trim();
|
|
@@ -256,13 +460,38 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
|
|
|
256
460
|
}
|
|
257
461
|
if (config.mode === 'autopilot') {
|
|
258
462
|
const reusableModeState = preserveExistingModeState ? existingModeState : null;
|
|
259
|
-
const
|
|
463
|
+
const existingStateRaw = (reusableModeState?.state && typeof reusableModeState.state === 'object')
|
|
260
464
|
? reusableModeState.state
|
|
261
465
|
: {};
|
|
466
|
+
const { context_snapshot_path: legacyStateContextSnapshotPath, context_snapshot_recovery: _legacyContextSnapshotRecovery, ...existingState } = existingStateRaw;
|
|
262
467
|
const existingHandoffs = (existingState.handoff_artifacts && typeof existingState.handoff_artifacts === 'object')
|
|
263
468
|
? existingState.handoff_artifacts
|
|
264
469
|
: {};
|
|
470
|
+
let recoveryReason = 'missing-or-unsafe-legacy-context-snapshot';
|
|
471
|
+
if (options.activeContinuation === true && !preserveExistingModeState) {
|
|
472
|
+
if (existingModeStateResult.status === 'missing') {
|
|
473
|
+
recoveryReason = 'missing-autopilot-mode-state';
|
|
474
|
+
}
|
|
475
|
+
else if (existingModeStateResult.status === 'malformed') {
|
|
476
|
+
recoveryReason = 'malformed-autopilot-mode-state';
|
|
477
|
+
}
|
|
478
|
+
else if (existingModeMatches && existingPhase === '') {
|
|
479
|
+
recoveryReason = 'nonpreservable-autopilot-mode-state-missing-current-phase';
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const existingContextSnapshotPath = await findReusableAutopilotContextSnapshotPath(sourceCwd, [
|
|
483
|
+
{ value: existingHandoffs.context_snapshot },
|
|
484
|
+
{ value: existingHandoffs.context_snapshot_path, kind: 'legacy' },
|
|
485
|
+
{ value: legacyStateContextSnapshotPath, kind: 'legacy' },
|
|
486
|
+
{ value: reusableModeState?.context_snapshot_path, kind: 'legacy' },
|
|
487
|
+
]);
|
|
488
|
+
const contextSnapshot = await ensureAutopilotContextSnapshot(sourceCwd, nowIso, activationText || safeString(nextSkill.keyword) || '$autopilot', existingContextSnapshotPath, {
|
|
489
|
+
allowTaskSnapshotCreation: !(preserveExistingModeState || options.activeContinuation === true),
|
|
490
|
+
recoveryReason,
|
|
491
|
+
});
|
|
492
|
+
const contextSnapshotPath = contextSnapshot.path;
|
|
265
493
|
baseState.review_cycle = typeof reusableModeState?.review_cycle === 'number' ? reusableModeState.review_cycle : 0;
|
|
494
|
+
delete baseState.context_snapshot_path;
|
|
266
495
|
baseState.state = {
|
|
267
496
|
...existingState,
|
|
268
497
|
phase_cycle: Array.isArray(existingState.phase_cycle) ? existingState.phase_cycle : ['deep-interview', 'ralplan', 'ultragoal', 'code-review', 'ultraqa'],
|
|
@@ -282,6 +511,13 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
|
|
|
282
511
|
code_review: null,
|
|
283
512
|
ultraqa: null,
|
|
284
513
|
...existingHandoffs,
|
|
514
|
+
context_snapshot_path: contextSnapshotPath,
|
|
515
|
+
context_snapshot: {
|
|
516
|
+
path: contextSnapshotPath,
|
|
517
|
+
kind: contextSnapshot.kind,
|
|
518
|
+
...(contextSnapshot.original_task_status ? { original_task_status: contextSnapshot.original_task_status } : {}),
|
|
519
|
+
...(contextSnapshot.recovery ? { recovery: contextSnapshot.recovery } : {}),
|
|
520
|
+
},
|
|
285
521
|
},
|
|
286
522
|
review_verdict: Object.prototype.hasOwnProperty.call(existingState, 'review_verdict')
|
|
287
523
|
? existingState.review_verdict
|
|
@@ -292,6 +528,7 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
|
|
|
292
528
|
return_to_ralplan_reason: Object.prototype.hasOwnProperty.call(existingState, 'return_to_ralplan_reason')
|
|
293
529
|
? existingState.return_to_ralplan_reason
|
|
294
530
|
: null,
|
|
531
|
+
...(contextSnapshot.recovery ? { context_snapshot_recovery: contextSnapshot.recovery } : {}),
|
|
295
532
|
deep_interview_gate: (existingState.deep_interview_gate && typeof existingState.deep_interview_gate === 'object')
|
|
296
533
|
? existingState.deep_interview_gate
|
|
297
534
|
: {
|
|
@@ -525,6 +762,13 @@ export function detectPrimaryKeyword(text) {
|
|
|
525
762
|
const matches = detectKeywords(text);
|
|
526
763
|
return matches.length > 0 ? matches[0] : null;
|
|
527
764
|
}
|
|
765
|
+
function filterMatchesForTeamMode(matches, teamEnabled) {
|
|
766
|
+
return teamEnabled ? matches : matches.filter((entry) => entry.skill !== 'team');
|
|
767
|
+
}
|
|
768
|
+
function detectPrimaryKeywordForTeamMode(text, teamEnabled) {
|
|
769
|
+
const matches = filterMatchesForTeamMode(detectKeywords(text), teamEnabled);
|
|
770
|
+
return matches[0] ?? null;
|
|
771
|
+
}
|
|
528
772
|
function isActiveSkillContinuationPrompt(text) {
|
|
529
773
|
const normalized = text.trim();
|
|
530
774
|
if (!normalized)
|
|
@@ -657,12 +901,13 @@ export async function recordSkillActivation(input) {
|
|
|
657
901
|
const previousRoot = await readExistingSkillState(rootStatePath);
|
|
658
902
|
const previousSession = sessionStatePath ? await readExistingSkillState(sessionStatePath) : null;
|
|
659
903
|
const previous = input.sessionId ? previousSession : previousRoot;
|
|
660
|
-
const
|
|
904
|
+
const teamMode = readTeamModeConfig(sourceCwd);
|
|
905
|
+
const match = resolveContinuationKeywordMatch(input.text, previous, detectPrimaryKeywordForTeamMode(input.text, teamMode.enabled));
|
|
661
906
|
if (!match)
|
|
662
907
|
return null;
|
|
663
908
|
const nowIso = input.nowIso ?? new Date().toISOString();
|
|
664
909
|
const hadDeepInterviewLock = previous?.skill === 'deep-interview' && previous?.input_lock?.active === true;
|
|
665
|
-
const matches = detectKeywords(input.text);
|
|
910
|
+
const matches = filterMatchesForTeamMode(detectKeywords(input.text), teamMode.enabled);
|
|
666
911
|
const hasCancelIntent = matches.some((entry) => entry.skill === 'cancel');
|
|
667
912
|
if (hasCancelIntent && hadDeepInterviewLock) {
|
|
668
913
|
const state = {
|
|
@@ -716,6 +961,7 @@ export async function recordSkillActivation(input) {
|
|
|
716
961
|
const workflowMatches = isTrackedWorkflowMatch && !markedQuestionAnswerContinuation
|
|
717
962
|
? parseExplicitSkillInvocations(normalizedInputText).matches
|
|
718
963
|
.map((entry) => entry.skill)
|
|
964
|
+
.filter((skill) => teamMode.enabled || skill !== 'team')
|
|
719
965
|
.filter(isTrackedWorkflowMode)
|
|
720
966
|
: [];
|
|
721
967
|
const resolvedWorkflowRequest = isTrackedWorkflowMatch
|
|
@@ -901,7 +1147,7 @@ export async function recordSkillActivation(input) {
|
|
|
901
1147
|
activated_at: requestedEntry.activated_at || workflowState.activated_at,
|
|
902
1148
|
updated_at: requestedEntry.updated_at || workflowState.updated_at,
|
|
903
1149
|
...(requestedEntry.skill === 'deep-interview' && deepInterviewConfig ? { deep_interview_config: deepInterviewConfig } : {}),
|
|
904
|
-
}, nowIso, previous);
|
|
1150
|
+
}, nowIso, previous, input.text, sourceCwd, { activeContinuation: requestedEntry.skill === 'autopilot' && sameSkillContinuation });
|
|
905
1151
|
if (requestedEntry.skill === workflowState.skill) {
|
|
906
1152
|
nextState = {
|
|
907
1153
|
...workflowState,
|
|
@@ -946,7 +1192,7 @@ export async function recordSkillActivation(input) {
|
|
|
946
1192
|
...(match.skill === 'deep-interview' && deepInterviewConfig ? { deep_interview_config: deepInterviewConfig } : {}),
|
|
947
1193
|
};
|
|
948
1194
|
try {
|
|
949
|
-
const nextState = await persistStatefulSkillSeedState(input.stateDir, state, nowIso, previous);
|
|
1195
|
+
const nextState = await persistStatefulSkillSeedState(input.stateDir, state, nowIso, previous, input.text, sourceCwd, { activeContinuation: match.skill === 'autopilot' && sameSkillContinuation });
|
|
950
1196
|
nextState.active_skills = buildActiveSkills(nextState);
|
|
951
1197
|
await writeSkillActiveStateCopiesForStateDir(input.stateDir, nextState, input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId));
|
|
952
1198
|
await persistDeepInterviewModeState(input.stateDir, nextState, nowIso, previous, input);
|