oh-my-codex 0.18.0 → 0.18.1
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 +43 -19
- package/crates/omx-api/src/lib.rs +66 -9
- package/crates/omx-sparkshell/src/exec.rs +125 -3
- package/crates/omx-sparkshell/src/main.rs +126 -36
- package/crates/omx-sparkshell/tests/execution.rs +225 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +15 -7
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +76 -3
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +49 -1
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/install-docs-contract.test.d.ts +2 -0
- package/dist/cli/__tests__/install-docs-contract.test.d.ts.map +1 -0
- package/dist/cli/__tests__/install-docs-contract.test.js +55 -0
- package/dist/cli/__tests__/install-docs-contract.test.js.map +1 -0
- package/dist/cli/__tests__/launch-fallback.test.js +115 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +27 -41
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +94 -35
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +20 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-packaging.test.js +1 -0
- package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +227 -4
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +72 -1
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/codex-feature-probe.d.ts +5 -0
- package/dist/cli/codex-feature-probe.d.ts.map +1 -1
- package/dist/cli/codex-feature-probe.js +13 -7
- package/dist/cli/codex-feature-probe.js.map +1 -1
- package/dist/cli/doctor.d.ts +7 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +119 -10
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +3 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +345 -90
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +2 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +15 -1
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +71 -11
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts +7 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +13 -3
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/ultragoal.d.ts +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +184 -10
- package/dist/cli/ultragoal.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 +14 -3
- package/dist/cli/update.js.map +1 -1
- package/dist/compat/__tests__/doctor-contract.test.js +3 -0
- package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
- package/dist/config/__tests__/codex-feature-flags.test.js +11 -1
- package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +19 -8
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/commit-lore-guard.test.d.ts +2 -0
- package/dist/config/__tests__/commit-lore-guard.test.d.ts.map +1 -0
- package/dist/config/__tests__/commit-lore-guard.test.js +20 -0
- package/dist/config/__tests__/commit-lore-guard.test.js.map +1 -0
- package/dist/config/codex-feature-flags.d.ts +4 -0
- package/dist/config/codex-feature-flags.d.ts.map +1 -1
- package/dist/config/codex-feature-flags.js +4 -0
- package/dist/config/codex-feature-flags.js.map +1 -1
- package/dist/config/codex-hooks.js +6 -6
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/commit-lore-guard.d.ts +1 -0
- package/dist/config/commit-lore-guard.d.ts.map +1 -1
- package/dist/config/commit-lore-guard.js +29 -3
- package/dist/config/commit-lore-guard.js.map +1 -1
- package/dist/config/generator.d.ts +3 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +24 -10
- package/dist/config/generator.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +1 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +5 -1
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +10 -6
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +1 -1
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -11
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +4 -3
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +4 -3
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +33 -0
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +26 -3
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +29 -14
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +8 -3
- 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 +3 -2
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +14 -8
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +2 -2
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/resource-leak-watch.test.d.ts +2 -0
- package/dist/hud/__tests__/resource-leak-watch.test.d.ts.map +1 -0
- package/dist/hud/__tests__/resource-leak-watch.test.js +28 -0
- package/dist/hud/__tests__/resource-leak-watch.test.js.map +1 -0
- package/dist/hud/index.d.ts +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +10 -4
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/tmux.js +2 -2
- package/dist/hud/tmux.js.map +1 -1
- package/dist/notifications/__tests__/http-client-resource.test.d.ts +2 -0
- package/dist/notifications/__tests__/http-client-resource.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/http-client-resource.test.js +41 -0
- package/dist/notifications/__tests__/http-client-resource.test.js.map +1 -0
- package/dist/notifications/__tests__/verbosity.test.js +20 -0
- package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
- package/dist/notifications/config.d.ts.map +1 -1
- package/dist/notifications/config.js +6 -3
- package/dist/notifications/config.js.map +1 -1
- package/dist/notifications/http-client.d.ts.map +1 -1
- package/dist/notifications/http-client.js +78 -27
- package/dist/notifications/http-client.js.map +1 -1
- package/dist/notifications/types.d.ts +2 -0
- package/dist/notifications/types.d.ts.map +1 -1
- package/dist/openclaw/__tests__/dispatcher.test.js +49 -1
- package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
- package/dist/openclaw/dispatcher.d.ts +7 -4
- package/dist/openclaw/dispatcher.d.ts.map +1 -1
- package/dist/openclaw/dispatcher.js +32 -69
- package/dist/openclaw/dispatcher.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +65 -3
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +50 -5
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/index.d.ts +8 -2
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +5 -2
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts +5 -4
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +56 -15
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/code-review.d.ts +2 -2
- package/dist/pipeline/stages/code-review.d.ts.map +1 -1
- package/dist/pipeline/stages/code-review.js +5 -3
- package/dist/pipeline/stages/code-review.js.map +1 -1
- package/dist/pipeline/stages/deep-interview.d.ts +15 -0
- package/dist/pipeline/stages/deep-interview.d.ts.map +1 -0
- package/dist/pipeline/stages/deep-interview.js +32 -0
- package/dist/pipeline/stages/deep-interview.js.map +1 -0
- package/dist/pipeline/stages/ralph-verify.d.ts +5 -5
- package/dist/pipeline/stages/ralph-verify.d.ts.map +1 -1
- package/dist/pipeline/stages/ralph-verify.js +2 -2
- package/dist/pipeline/stages/ralph-verify.js.map +1 -1
- package/dist/pipeline/stages/ultragoal.d.ts +19 -0
- package/dist/pipeline/stages/ultragoal.d.ts.map +1 -0
- package/dist/pipeline/stages/ultragoal.js +38 -0
- package/dist/pipeline/stages/ultragoal.js.map +1 -0
- package/dist/pipeline/stages/ultraqa.d.ts +30 -0
- package/dist/pipeline/stages/ultraqa.d.ts.map +1 -0
- package/dist/pipeline/stages/ultraqa.js +46 -0
- package/dist/pipeline/stages/ultraqa.js.map +1 -0
- package/dist/pipeline/types.d.ts +8 -6
- package/dist/pipeline/types.d.ts.map +1 -1
- package/dist/pipeline/types.js +2 -2
- package/dist/scripts/__tests__/codex-native-hook.test.js +705 -45
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +23 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +16 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/cleanup-explore-harness.js +1 -0
- package/dist/scripts/cleanup-explore-harness.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +158 -10
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +9 -1
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
- package/dist/scripts/notify-hook/process-runner.js +39 -17
- package/dist/scripts/notify-hook/process-runner.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +9 -5
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +7 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +3 -0
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +99 -1
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +2 -2
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-agents.js +2 -2
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts +2 -0
- package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/resource-leak-watch.test.js +38 -0
- package/dist/sidecar/__tests__/resource-leak-watch.test.js.map +1 -0
- package/dist/sidecar/index.d.ts +1 -1
- package/dist/sidecar/index.d.ts.map +1 -1
- package/dist/sidecar/index.js +29 -12
- package/dist/sidecar/index.js.map +1 -1
- package/dist/state/__tests__/operations-ralph-phase.test.js +88 -1
- package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +11 -0
- package/dist/state/operations.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +111 -3
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +39 -18
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +714 -10
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +57 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/__tests__/steering-fixtures.d.ts +68 -0
- package/dist/ultragoal/__tests__/steering-fixtures.d.ts.map +1 -0
- package/dist/ultragoal/__tests__/steering-fixtures.js +259 -0
- package/dist/ultragoal/__tests__/steering-fixtures.js.map +1 -0
- package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts +2 -0
- package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts.map +1 -0
- package/dist/ultragoal/__tests__/steering-fixtures.test.js +65 -0
- package/dist/ultragoal/__tests__/steering-fixtures.test.js.map +1 -0
- package/dist/ultragoal/artifacts.d.ts +97 -2
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +811 -256
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/sleep-resource.test.d.ts +2 -0
- package/dist/utils/__tests__/sleep-resource.test.d.ts.map +1 -0
- package/dist/utils/__tests__/sleep-resource.test.js +39 -0
- package/dist/utils/__tests__/sleep-resource.test.js.map +1 -0
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js +17 -6
- package/dist/utils/sleep.js.map +1 -1
- package/package.json +2 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +4 -3
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +56 -0
- package/plugins/oh-my-codex/hooks/hooks.json +77 -0
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +77 -47
- package/plugins/oh-my-codex/skills/cancel/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +8 -8
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/pipeline/SKILL.md +22 -11
- package/plugins/oh-my-codex/skills/plan/SKILL.md +8 -8
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +7 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +4 -4
- package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +38 -4
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +1 -1
- package/prompts/planner.md +1 -1
- package/skills/autopilot/SKILL.md +77 -47
- package/skills/cancel/SKILL.md +2 -2
- package/skills/deep-interview/SKILL.md +8 -8
- package/skills/omx-setup/SKILL.md +1 -1
- package/skills/pipeline/SKILL.md +22 -11
- package/skills/plan/SKILL.md +8 -8
- package/skills/ralph/SKILL.md +7 -0
- package/skills/ralplan/SKILL.md +4 -4
- package/skills/team/SKILL.md +1 -1
- package/skills/ultragoal/SKILL.md +38 -4
- package/skills/ultrawork/SKILL.md +1 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +867 -81
- package/src/scripts/__tests__/smoke-packed-install.test.ts +31 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +21 -1
- package/src/scripts/cleanup-explore-harness.ts +1 -0
- package/src/scripts/codex-native-hook.ts +156 -10
- package/src/scripts/codex-native-pre-post.ts +16 -1
- package/src/scripts/notify-hook/process-runner.ts +40 -16
- package/src/scripts/notify-hook/team-dispatch.ts +9 -5
- package/src/scripts/notify-hook/team-tmux-guard.ts +7 -0
- package/src/scripts/smoke-packed-install.ts +105 -0
- package/src/scripts/sync-plugin-mirror.ts +3 -3
- package/src/scripts/verify-native-agents.ts +2 -2
|
@@ -7,10 +7,13 @@ import { test } from 'node:test';
|
|
|
7
7
|
import {
|
|
8
8
|
ensureRepoDependencies,
|
|
9
9
|
hasUsableNodeModules,
|
|
10
|
+
buildNativeHookSmokePayload,
|
|
11
|
+
PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS,
|
|
10
12
|
PACKED_INSTALL_SMOKE_CORE_COMMANDS,
|
|
11
13
|
parseNpmPackJsonOutput,
|
|
12
14
|
resolveGitCommonDir,
|
|
13
15
|
resolveReusableNodeModulesSource,
|
|
16
|
+
validateHookStdout,
|
|
14
17
|
} from '../smoke-packed-install.js';
|
|
15
18
|
|
|
16
19
|
test('packed install smoke stays limited to boot + core commands', () => {
|
|
@@ -30,6 +33,34 @@ test('packed install smoke stays limited to boot + core commands', () => {
|
|
|
30
33
|
);
|
|
31
34
|
});
|
|
32
35
|
|
|
36
|
+
test('packed install smoke covers every installed native hook event with minimal payloads', () => {
|
|
37
|
+
assert.deepEqual(PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS, [
|
|
38
|
+
'SessionStart',
|
|
39
|
+
'PreToolUse',
|
|
40
|
+
'PostToolUse',
|
|
41
|
+
'UserPromptSubmit',
|
|
42
|
+
'PreCompact',
|
|
43
|
+
'PostCompact',
|
|
44
|
+
'Stop',
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
for (const eventName of PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS) {
|
|
48
|
+
const payload = buildNativeHookSmokePayload(eventName, '/tmp/omx-packed-hook-smoke');
|
|
49
|
+
assert.equal(payload.hook_event_name, eventName);
|
|
50
|
+
assert.equal(typeof payload.session_id, 'string');
|
|
51
|
+
assert.equal(payload.cwd, '/tmp/omx-packed-hook-smoke');
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('packed install native hook stdout validation allows empty or JSON output only', () => {
|
|
56
|
+
assert.doesNotThrow(() => validateHookStdout('PostCompact', ''));
|
|
57
|
+
assert.doesNotThrow(() => validateHookStdout('Stop', '{}\n'));
|
|
58
|
+
assert.throws(
|
|
59
|
+
() => validateHookStdout('UserPromptSubmit', '{not json'),
|
|
60
|
+
/native hook UserPromptSubmit emitted invalid JSON stdout/,
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
33
64
|
test('parseNpmPackJsonOutput ignores prepack logs before npm pack JSON', () => {
|
|
34
65
|
const parsed = parseNpmPackJsonOutput([
|
|
35
66
|
'[sync-plugin-mirror] synced 29 canonical skill directories and plugin metadata',
|
|
@@ -53,7 +53,7 @@ describe("verify-native-agents", () => {
|
|
|
53
53
|
]),
|
|
54
54
|
definitions: { executor: definition, "style-reviewer": { ...definition, name: "style-reviewer" } },
|
|
55
55
|
promptNames: new Set(["executor", "style-reviewer", "explore-harness"]),
|
|
56
|
-
pluginManifest: { skills: "./skills/" },
|
|
56
|
+
pluginManifest: { skills: "./skills/", hooks: "./hooks/hooks.json" },
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
assert.deepEqual(result.installableAgentNames, ["executor"]);
|
|
@@ -183,6 +183,26 @@ describe("verify-native-agents", () => {
|
|
|
183
183
|
pluginManifest: { agents: "./agents/" },
|
|
184
184
|
}),
|
|
185
185
|
);
|
|
186
|
+
|
|
187
|
+
await rejectsWithCode("native_agent_plugin_boundary_violation", () =>
|
|
188
|
+
verifyNativeAgents({
|
|
189
|
+
manifest: manifest([{ name: "executor", category: "build", status: "active" }]),
|
|
190
|
+
definitions: { executor: definition },
|
|
191
|
+
promptNames: new Set(["executor"]),
|
|
192
|
+
pluginManifest: { prompts: "./prompts/" },
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("allows plugin-scoped lifecycle hooks without treating them as native agents", async () => {
|
|
198
|
+
const result = await verifyNativeAgents({
|
|
199
|
+
manifest: manifest([{ name: "executor", category: "build", status: "active" }]),
|
|
200
|
+
definitions: { executor: definition },
|
|
201
|
+
promptNames: new Set(["executor"]),
|
|
202
|
+
pluginManifest: { hooks: "./hooks/hooks.json" },
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
assert.deepEqual(result.installableAgentNames, ["executor"]);
|
|
186
206
|
});
|
|
187
207
|
|
|
188
208
|
it("keeps merged prompt-backed agents out of the installable set", () => {
|
|
@@ -12,6 +12,7 @@ for (const path of [
|
|
|
12
12
|
join(root, 'bin', 'omx-explore-harness.exe'),
|
|
13
13
|
join(root, 'bin', 'omx-explore-harness.meta.json'),
|
|
14
14
|
join(root, 'bin', 'native'),
|
|
15
|
+
join(root, 'crates', 'omx-sparkshell', '.omx'),
|
|
15
16
|
]) {
|
|
16
17
|
await rm(path, { recursive: true, force: true });
|
|
17
18
|
}
|
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
} from "../hooks/extensibility/events.js";
|
|
70
70
|
import type { HookEventEnvelope } from "../hooks/extensibility/types.js";
|
|
71
71
|
import { dispatchHookEventRuntime } from "../hooks/extensibility/runtime.js";
|
|
72
|
+
import { getNotificationConfig, getVerbosity } from "../notifications/config.js";
|
|
72
73
|
import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
|
|
73
74
|
import {
|
|
74
75
|
onPreCompact as buildWikiPreCompactContext,
|
|
@@ -78,6 +79,11 @@ import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDec
|
|
|
78
79
|
import { readRunState } from "../runtime/run-state.js";
|
|
79
80
|
import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
|
|
80
81
|
import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
|
|
82
|
+
import {
|
|
83
|
+
parseUltragoalSteeringDirective,
|
|
84
|
+
steerUltragoal,
|
|
85
|
+
type UltragoalSteeringProposal,
|
|
86
|
+
} from "../ultragoal/artifacts.js";
|
|
81
87
|
import { triagePrompt } from "../hooks/triage-heuristic.js";
|
|
82
88
|
import { readTriageConfig } from "../hooks/triage-config.js";
|
|
83
89
|
import {
|
|
@@ -305,6 +311,13 @@ async function isNativeSubagentHook(
|
|
|
305
311
|
return candidateIds.some((id) => summary.allSubagentThreadIds.includes(id));
|
|
306
312
|
}
|
|
307
313
|
|
|
314
|
+
function shouldSuppressSubagentLifecycleHookDispatch(): boolean {
|
|
315
|
+
const config = getNotificationConfig();
|
|
316
|
+
if (config?.includeChildAgents === true) return false;
|
|
317
|
+
const verbosity = getVerbosity(config);
|
|
318
|
+
return verbosity !== "agent" && verbosity !== "verbose";
|
|
319
|
+
}
|
|
320
|
+
|
|
308
321
|
async function recordIgnoredNativeSubagentSessionStart(
|
|
309
322
|
cwd: string,
|
|
310
323
|
canonicalSessionId: string,
|
|
@@ -419,6 +432,104 @@ function readPromptText(payload: CodexHookPayload): string {
|
|
|
419
432
|
return "";
|
|
420
433
|
}
|
|
421
434
|
|
|
435
|
+
|
|
436
|
+
function extractBalancedJsonObject(text: string, startIndex: number): string | null {
|
|
437
|
+
let depth = 0;
|
|
438
|
+
let inString = false;
|
|
439
|
+
let escaped = false;
|
|
440
|
+
for (let index = startIndex; index < text.length; index++) {
|
|
441
|
+
const char = text[index];
|
|
442
|
+
if (inString) {
|
|
443
|
+
if (escaped) escaped = false;
|
|
444
|
+
else if (char === "\\") escaped = true;
|
|
445
|
+
else if (char === '"') inString = false;
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (char === '"') {
|
|
449
|
+
inString = true;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (char === "{") depth += 1;
|
|
453
|
+
else if (char === "}") {
|
|
454
|
+
depth -= 1;
|
|
455
|
+
if (depth === 0) return text.slice(startIndex, index + 1);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function normalizePromptSteeringProposal(raw: unknown, prompt: string): UltragoalSteeringProposal | null {
|
|
462
|
+
const candidate = safeObject(raw);
|
|
463
|
+
const nested = candidate.omx_ultragoal_steer ?? candidate.ultragoal_steer ?? candidate.steering ?? candidate;
|
|
464
|
+
const proposal = parseUltragoalSteeringDirective(JSON.stringify(nested));
|
|
465
|
+
if (!proposal) return null;
|
|
466
|
+
if (proposal.source !== "user_prompt_submit") return null;
|
|
467
|
+
const normalized = prompt.trim().toLowerCase();
|
|
468
|
+
return {
|
|
469
|
+
...proposal,
|
|
470
|
+
directiveText: proposal.directiveText ?? safeContextSnippet(prompt, 600),
|
|
471
|
+
promptSignature: proposal.promptSignature ?? promptSignature(normalized),
|
|
472
|
+
idempotencyKey: proposal.idempotencyKey ?? `user_prompt_submit:${promptSignature(normalized)}`,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function parseUserPromptUltragoalSteeringDirective(prompt: string): UltragoalSteeringProposal | null {
|
|
477
|
+
const trimmed = prompt.trim();
|
|
478
|
+
if (!trimmed) return null;
|
|
479
|
+
const fenced = trimmed.match(/```(?:omx-ultragoal-steer|ultragoal-steer)\s*([\s\S]*?)```/i);
|
|
480
|
+
if (fenced?.[1]) {
|
|
481
|
+
try {
|
|
482
|
+
return normalizePromptSteeringProposal(JSON.parse(fenced[1]), prompt);
|
|
483
|
+
} catch {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const label = trimmed.match(/(?:^|\n)\s*(?:OMX_ULTRAGOAL_STEER|omx\.ultragoal\.steer|omx ultragoal steer)\s*:\s*{/i);
|
|
489
|
+
if (label?.index !== undefined) {
|
|
490
|
+
const brace = trimmed.indexOf("{", label.index);
|
|
491
|
+
const json = brace >= 0 ? extractBalancedJsonObject(trimmed, brace) : null;
|
|
492
|
+
if (json) {
|
|
493
|
+
try {
|
|
494
|
+
return normalizePromptSteeringProposal(JSON.parse(json), prompt);
|
|
495
|
+
} catch {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (trimmed.startsWith("{")) {
|
|
502
|
+
try {
|
|
503
|
+
const parsed = JSON.parse(trimmed);
|
|
504
|
+
const object = safeObject(parsed);
|
|
505
|
+
if ("omx_ultragoal_steer" in object || "ultragoal_steer" in object) {
|
|
506
|
+
return normalizePromptSteeringProposal(parsed, prompt);
|
|
507
|
+
}
|
|
508
|
+
} catch {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async function applyUserPromptUltragoalSteering(cwd: string, prompt: string): Promise<string | null> {
|
|
516
|
+
const proposal = parseUserPromptUltragoalSteeringDirective(prompt);
|
|
517
|
+
if (!proposal) return null;
|
|
518
|
+
try {
|
|
519
|
+
const result = await steerUltragoal(cwd, proposal);
|
|
520
|
+
const status = result.deduped ? "deduped" : result.accepted ? "accepted" : "rejected";
|
|
521
|
+
const reasons = result.rejectedReasons.length > 0 ? ` rejectedReasons=${result.rejectedReasons.join("; ")}` : "";
|
|
522
|
+
return [
|
|
523
|
+
`OMX native UserPromptSubmit applied bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${status}.`,
|
|
524
|
+
`mutation=${result.audit.kind}; source=${result.audit.source}; targets=${result.audit.targetGoalIds.join(",") || "none"}; idempotencyKey=${result.audit.idempotencyKey ?? "none"}.${reasons}`,
|
|
525
|
+
"Only explicit structured steering directives are parsed; normal prose is ignored and cannot mutate .omx/ultragoal.",
|
|
526
|
+
].join(" ");
|
|
527
|
+
} catch (error) {
|
|
528
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
529
|
+
return `OMX native UserPromptSubmit rejected bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${message}`;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
422
533
|
function sanitizePayloadForHookContext(
|
|
423
534
|
payload: CodexHookPayload,
|
|
424
535
|
hookEventName: CodexHookEventName,
|
|
@@ -793,13 +904,12 @@ async function reopenRalphCompletionAuditBlock(block: RalphCompletionAuditBlockS
|
|
|
793
904
|
const nowIso = new Date().toISOString();
|
|
794
905
|
const next: Record<string, unknown> = {
|
|
795
906
|
...block.state,
|
|
796
|
-
active:
|
|
797
|
-
current_phase: "
|
|
907
|
+
active: false,
|
|
908
|
+
current_phase: "complete",
|
|
798
909
|
completion_audit_gate: "blocked",
|
|
799
910
|
completion_audit_missing_reason: block.reason,
|
|
800
911
|
completion_audit_blocked_at: nowIso,
|
|
801
912
|
};
|
|
802
|
-
delete next.completed_at;
|
|
803
913
|
await writeFile(block.path, JSON.stringify(next, null, 2));
|
|
804
914
|
}
|
|
805
915
|
|
|
@@ -1604,7 +1714,7 @@ function buildAdditionalContextMessage(
|
|
|
1604
1714
|
? "Ultrawork protocol: ground the task before editing, define pass/fail acceptance criteria, keep shared-file work local, and use direct-tool plus background evidence lanes only for truly independent work. Direct ultrawork provides lightweight verification only; Ralph owns persistence and the full verified-completion promise."
|
|
1605
1715
|
: null;
|
|
1606
1716
|
const ultragoalPromptActivationNote = match.skill === "ultragoal"
|
|
1607
|
-
? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal."
|
|
1717
|
+
? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal. Ultragoal does not call `/goal clear`; for multiple sequential ultragoal runs in one Codex session/thread, manually clear the completed Codex goal in the UI before creating the next aggregate goal."
|
|
1608
1718
|
: null;
|
|
1609
1719
|
const combinedTransitionMessage = (() => {
|
|
1610
1720
|
if (!skillState?.transition_message) return null;
|
|
@@ -1867,6 +1977,9 @@ async function buildModeBasedStopOutput(
|
|
|
1867
1977
|
cwd: string,
|
|
1868
1978
|
sessionId?: string,
|
|
1869
1979
|
): Promise<Record<string, unknown> | null> {
|
|
1980
|
+
if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, mode)) {
|
|
1981
|
+
return null;
|
|
1982
|
+
}
|
|
1870
1983
|
const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
|
|
1871
1984
|
if (!state || !shouldContinueRun(state)) return null;
|
|
1872
1985
|
const phase = formatPhase(state.current_phase);
|
|
@@ -1895,6 +2008,22 @@ function reportsAutoresearchGoalObjectiveMismatch(text: string): boolean {
|
|
|
1895
2008
|
&& /objective mismatch/i.test(text);
|
|
1896
2009
|
}
|
|
1897
2010
|
|
|
2011
|
+
function reportsBlockedPerformanceGoalObjectiveMismatch(state: unknown): boolean {
|
|
2012
|
+
const performanceState = safeObject(state);
|
|
2013
|
+
const lastValidation = safeObject(performanceState.lastValidation);
|
|
2014
|
+
if (safeString(performanceState.workflow) !== "performance-goal") return false;
|
|
2015
|
+
if (safeString(performanceState.status) !== "blocked") return false;
|
|
2016
|
+
if (safeString(lastValidation.status) !== "blocked") return false;
|
|
2017
|
+
|
|
2018
|
+
const evidence = [
|
|
2019
|
+
safeString(lastValidation.evidence),
|
|
2020
|
+
safeString(lastValidation.message),
|
|
2021
|
+
safeString(performanceState.evidence),
|
|
2022
|
+
safeString(performanceState.message),
|
|
2023
|
+
].join(" ");
|
|
2024
|
+
return /objective mismatch/i.test(evidence);
|
|
2025
|
+
}
|
|
2026
|
+
|
|
1898
2027
|
async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string; remediation?: string } | null> {
|
|
1899
2028
|
const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
|
|
1900
2029
|
const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
|
|
@@ -1912,7 +2041,7 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
|
|
|
1912
2041
|
`If get_goal returns a completed task-scoped objective for the same aggregate ultragoal plan, checkpoint ${goalId} with evidence naming ${goalId} plus .omx/ultragoal/goals.json or ledger.jsonl and pass final quality-gate JSON; OMX will reconcile the completed planned scope without mutating Codex goal state.`,
|
|
1913
2042
|
`If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
|
|
1914
2043
|
`Record the non-terminal blocker with: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<different completed get_goal JSON or path>' --evidence '<completed legacy Codex goal blocks create_goal in this thread>'.`,
|
|
1915
|
-
"Then continue
|
|
2044
|
+
"Then continue only from a Codex goal context with no active/completed conflicting goal in the same repo/worktree and create the intended goal there.",
|
|
1916
2045
|
].join(" "),
|
|
1917
2046
|
};
|
|
1918
2047
|
}
|
|
@@ -1922,6 +2051,9 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
|
|
|
1922
2051
|
if (!entry.isDirectory()) continue;
|
|
1923
2052
|
const state = await readJsonIfExists(join(performanceRoot, entry.name, "state.json"));
|
|
1924
2053
|
const status = safeString(state?.status);
|
|
2054
|
+
if (reportsBlockedPerformanceGoalObjectiveMismatch(state)) {
|
|
2055
|
+
continue;
|
|
2056
|
+
}
|
|
1925
2057
|
if (state?.workflow === "performance-goal" && status && status !== "complete") {
|
|
1926
2058
|
return {
|
|
1927
2059
|
workflow: "performance-goal",
|
|
@@ -1948,8 +2080,8 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
|
|
|
1948
2080
|
workflow: "autoresearch-goal",
|
|
1949
2081
|
command: `omx autoresearch-goal complete --slug ${safeString(mission.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>'`,
|
|
1950
2082
|
remediation: [
|
|
1951
|
-
"If that command fails with a Codex goal objective mismatch after a
|
|
1952
|
-
"Either retry with a correct
|
|
2083
|
+
"If that command fails with a Codex goal objective mismatch after a refreshed get_goal snapshot, do not repeat the same complete command blindly in this thread.",
|
|
2084
|
+
"Either retry with a correct refreshed snapshot or record an explicit blocked verdict for this autoresearch-goal and continue from the explicit blocker path.",
|
|
1953
2085
|
].join(" "),
|
|
1954
2086
|
};
|
|
1955
2087
|
}
|
|
@@ -3040,7 +3172,7 @@ async function buildStopHookOutput(
|
|
|
3040
3172
|
`OMX Ralph completion audit is missing required evidence (${ralphCompletionAuditBlock.reason}; state: ${blockingPath}).`,
|
|
3041
3173
|
"Continue verification and do not report complete yet.",
|
|
3042
3174
|
"Record machine-readable completion evidence before stopping:",
|
|
3043
|
-
|
|
3175
|
+
'- either set "completion_audit" on the Ralph state object, for example: omx state write --input \'{"mode":"ralph","active":false,"current_phase":"complete","completion_audit":{"passed":true,"prompt_to_artifact_checklist":["..."],"verification_evidence":["..."]}}\' --json',
|
|
3044
3176
|
"- or set completion_audit_path / completion_audit_evidence_path to a repo-relative JSON file with those same fields.",
|
|
3045
3177
|
"Markdown artifacts and flat top-level checklist/evidence fields are not accepted by the Ralph Stop gate.",
|
|
3046
3178
|
].join(" ");
|
|
@@ -3337,6 +3469,7 @@ export async function dispatchCodexNativeHook(
|
|
|
3337
3469
|
let skillState: SkillActiveState | null = null;
|
|
3338
3470
|
let triageAdditionalContext: string | null = null;
|
|
3339
3471
|
let goalWorkflowAdditionalContext: string | null = null;
|
|
3472
|
+
let ultragoalSteeringAdditionalContext: string | null = null;
|
|
3340
3473
|
|
|
3341
3474
|
const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
|
|
3342
3475
|
const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
|
|
@@ -3345,11 +3478,13 @@ export async function dispatchCodexNativeHook(
|
|
|
3345
3478
|
let canonicalSessionId = safeString(currentSessionState?.session_id).trim();
|
|
3346
3479
|
let resolvedNativeSessionId = nativeSessionId;
|
|
3347
3480
|
let skipCanonicalSessionStartContext = false;
|
|
3481
|
+
let isSubagentSessionStart = false;
|
|
3348
3482
|
|
|
3349
3483
|
if (hookEventName === "SessionStart" && nativeSessionId) {
|
|
3350
3484
|
const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim();
|
|
3351
3485
|
const subagentSessionStart = readNativeSubagentSessionStartMetadata(transcriptPath);
|
|
3352
3486
|
if (subagentSessionStart && canonicalSessionId) {
|
|
3487
|
+
isSubagentSessionStart = true;
|
|
3353
3488
|
const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(
|
|
3354
3489
|
cwd,
|
|
3355
3490
|
canonicalSessionId,
|
|
@@ -3418,10 +3553,16 @@ export async function dispatchCodexNativeHook(
|
|
|
3418
3553
|
.map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)),
|
|
3419
3554
|
)).some(Boolean)
|
|
3420
3555
|
: false;
|
|
3556
|
+
const suppressNoisySubagentLifecycleDispatch =
|
|
3557
|
+
(isSubagentSessionStart || isSubagentStop)
|
|
3558
|
+
&& shouldSuppressSubagentLifecycleHookDispatch();
|
|
3421
3559
|
|
|
3422
3560
|
if (hookEventName === "UserPromptSubmit") {
|
|
3423
3561
|
const prompt = readPromptText(payload);
|
|
3424
3562
|
goalWorkflowAdditionalContext = await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
|
|
3563
|
+
ultragoalSteeringAdditionalContext = prompt && !isSubagentPromptSubmit
|
|
3564
|
+
? await applyUserPromptUltragoalSteering(cwd, prompt).catch((error) => `OMX native UserPromptSubmit rejected bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${error instanceof Error ? error.message : String(error)}`)
|
|
3565
|
+
: null;
|
|
3425
3566
|
if (prompt && !isSubagentPromptSubmit) {
|
|
3426
3567
|
skillState = buildNativeOutsideTmuxTeamPromptBlockState(
|
|
3427
3568
|
prompt,
|
|
@@ -3513,7 +3654,7 @@ export async function dispatchCodexNativeHook(
|
|
|
3513
3654
|
await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => {});
|
|
3514
3655
|
}
|
|
3515
3656
|
|
|
3516
|
-
if (omxEventName && !skipCanonicalSessionStartContext) {
|
|
3657
|
+
if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
|
|
3517
3658
|
const baseContext = buildBaseContext(cwd, payload, hookEventName!, canonicalSessionId);
|
|
3518
3659
|
if (resolvedNativeSessionId) {
|
|
3519
3660
|
baseContext.native_session_id = resolvedNativeSessionId;
|
|
@@ -3554,7 +3695,12 @@ export async function dispatchCodexNativeHook(
|
|
|
3554
3695
|
})
|
|
3555
3696
|
: isSubagentPromptSubmit
|
|
3556
3697
|
? null
|
|
3557
|
-
:
|
|
3698
|
+
: [
|
|
3699
|
+
buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload),
|
|
3700
|
+
ultragoalSteeringAdditionalContext,
|
|
3701
|
+
goalWorkflowAdditionalContext,
|
|
3702
|
+
triageAdditionalContext,
|
|
3703
|
+
].filter((entry): entry is string => Boolean(entry)).join("\n\n") || null;
|
|
3558
3704
|
if (additionalContext) {
|
|
3559
3705
|
outputJson = {
|
|
3560
3706
|
hookSpecificOutput: {
|
|
@@ -2,7 +2,11 @@ import {
|
|
|
2
2
|
buildDocumentRefreshAdvisoryOutput,
|
|
3
3
|
evaluateStagedDocumentRefresh,
|
|
4
4
|
} from "../document-refresh/enforcer.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
OMX_LORE_COMMIT_GUARD_ENV,
|
|
7
|
+
isLoreCommitGuardEnabled,
|
|
8
|
+
readConfiguredLoreCommitGuardValue,
|
|
9
|
+
} from "../config/commit-lore-guard.js";
|
|
6
10
|
import { resolveCodexExecutionSurface } from "./codex-execution-surface.js";
|
|
7
11
|
|
|
8
12
|
type CodexHookPayload = Record<string, unknown>;
|
|
@@ -747,6 +751,17 @@ function buildEffectiveLoreCommitGuardEnv(parsed: GitCommitCommandParseResult):
|
|
|
747
751
|
for (const [name, value] of Object.entries(parsed.inlineEnvironment)) {
|
|
748
752
|
if (typeof value === "string") effectiveEnvironment[name] = value;
|
|
749
753
|
}
|
|
754
|
+
|
|
755
|
+
if (
|
|
756
|
+
!parsed.environmentStartsClean
|
|
757
|
+
&& !parsed.unsetEnvironmentNames.includes(OMX_LORE_COMMIT_GUARD_ENV)
|
|
758
|
+
&& typeof effectiveEnvironment[OMX_LORE_COMMIT_GUARD_ENV] !== "string"
|
|
759
|
+
) {
|
|
760
|
+
const configuredValue = readConfiguredLoreCommitGuardValue(effectiveEnvironment);
|
|
761
|
+
if (typeof configuredValue === "string") {
|
|
762
|
+
effectiveEnvironment[OMX_LORE_COMMIT_GUARD_ENV] = configuredValue;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
750
765
|
return effectiveEnvironment;
|
|
751
766
|
}
|
|
752
767
|
|
|
@@ -17,35 +17,59 @@ export function runProcess(command: string, args: string[], timeoutMs = 3000): P
|
|
|
17
17
|
let stdout = '';
|
|
18
18
|
let stderr = '';
|
|
19
19
|
let finished = false;
|
|
20
|
+
let sigkillTimer: ReturnType<typeof setTimeout> | undefined;
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const cleanup = (clearPendingSigkill = true) => {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
if (clearPendingSigkill && sigkillTimer) {
|
|
25
|
+
clearTimeout(sigkillTimer);
|
|
26
|
+
sigkillTimer = undefined;
|
|
27
|
+
}
|
|
28
|
+
child.stdout.off('data', onStdout);
|
|
29
|
+
child.stderr.off('data', onStderr);
|
|
30
|
+
child.off('error', onError);
|
|
31
|
+
child.off('close', onClose);
|
|
32
|
+
};
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
const onStdout = (chunk: Buffer) => {
|
|
29
35
|
stdout += chunk.toString();
|
|
30
|
-
}
|
|
31
|
-
|
|
36
|
+
};
|
|
37
|
+
const onStderr = (chunk: Buffer) => {
|
|
32
38
|
stderr += chunk.toString();
|
|
33
|
-
}
|
|
34
|
-
|
|
39
|
+
};
|
|
40
|
+
const onError = (err: Error) => {
|
|
35
41
|
if (finished) return;
|
|
36
42
|
finished = true;
|
|
37
|
-
|
|
43
|
+
cleanup();
|
|
38
44
|
reject(err);
|
|
39
|
-
}
|
|
40
|
-
|
|
45
|
+
};
|
|
46
|
+
const onClose = (code: number | null) => {
|
|
41
47
|
if (finished) return;
|
|
42
48
|
finished = true;
|
|
43
|
-
|
|
49
|
+
cleanup();
|
|
44
50
|
if (code === 0) {
|
|
45
51
|
resolve({ stdout, stderr, code });
|
|
46
52
|
} else {
|
|
47
53
|
reject(new Error(stderr.trim() || `${command} exited ${code}`));
|
|
48
54
|
}
|
|
49
|
-
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const timer = setTimeout(() => {
|
|
58
|
+
if (finished) return;
|
|
59
|
+
finished = true;
|
|
60
|
+
child.kill('SIGTERM');
|
|
61
|
+
sigkillTimer = setTimeout(() => {
|
|
62
|
+
sigkillTimer = undefined;
|
|
63
|
+
child.kill('SIGKILL');
|
|
64
|
+
}, 250);
|
|
65
|
+
sigkillTimer.unref?.();
|
|
66
|
+
cleanup(false);
|
|
67
|
+
reject(new Error(`timeout after ${effectiveTimeoutMs}ms`));
|
|
68
|
+
}, effectiveTimeoutMs);
|
|
69
|
+
|
|
70
|
+
child.stdout.on('data', onStdout);
|
|
71
|
+
child.stderr.on('data', onStderr);
|
|
72
|
+
child.on('error', onError);
|
|
73
|
+
child.on('close', onClose);
|
|
50
74
|
});
|
|
51
75
|
}
|
|
@@ -822,6 +822,7 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
|
|
|
822
822
|
prompt: request.trigger_message,
|
|
823
823
|
submitKeyPresses,
|
|
824
824
|
typePrompt: shouldTypePrompt,
|
|
825
|
+
queueFirstSubmit: leaderTargeted,
|
|
825
826
|
});
|
|
826
827
|
if (!sendResult.ok) {
|
|
827
828
|
return {
|
|
@@ -876,11 +877,13 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
|
|
|
876
877
|
tmux_injection_attempted: true,
|
|
877
878
|
};
|
|
878
879
|
}
|
|
879
|
-
// Do not declare success while a
|
|
880
|
-
//
|
|
881
|
-
//
|
|
882
|
-
//
|
|
883
|
-
|
|
880
|
+
// Do not declare success while a pane is not input-ready. Otherwise a
|
|
881
|
+
// pre-ready send can be marked "confirmed" and later appear as a stuck
|
|
882
|
+
// unsent draft once the UI finishes loading. This includes leader-fixed:
|
|
883
|
+
// its Codex UI can show "tab to queue message" while busy, and marking
|
|
884
|
+
// delivered before queue/consumption confirmation loses the orchestration
|
|
885
|
+
// nudge until a human presses Tab manually.
|
|
886
|
+
if (!paneLooksReady(wideCap.stdout)) {
|
|
884
887
|
continue;
|
|
885
888
|
}
|
|
886
889
|
if (!triggerInNarrow && !triggerNearTail) {
|
|
@@ -904,6 +907,7 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
|
|
|
904
907
|
prompt: request.trigger_message,
|
|
905
908
|
submitKeyPresses,
|
|
906
909
|
typePrompt: false,
|
|
910
|
+
queueFirstSubmit: leaderTargeted,
|
|
907
911
|
}).catch(() => {});
|
|
908
912
|
}
|
|
909
913
|
|
|
@@ -134,6 +134,7 @@ export async function sendPaneInput({
|
|
|
134
134
|
submitKeyPresses = 2,
|
|
135
135
|
submitDelayMs = 0,
|
|
136
136
|
typePrompt = true,
|
|
137
|
+
queueFirstSubmit = false,
|
|
137
138
|
}: any): Promise<any> {
|
|
138
139
|
const target = safeString(paneTarget).trim();
|
|
139
140
|
if (!target) {
|
|
@@ -163,6 +164,12 @@ export async function sendPaneInput({
|
|
|
163
164
|
if (typePrompt) {
|
|
164
165
|
await runProcess('tmux', argv.typeArgv, 3000);
|
|
165
166
|
}
|
|
167
|
+
if (queueFirstSubmit && argv.submitArgv.length > 0) {
|
|
168
|
+
await runProcess('tmux', ['send-keys', '-t', target, 'Tab'], 3000);
|
|
169
|
+
if (submitDelayMs > 0) {
|
|
170
|
+
await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
166
173
|
for (const submit of argv.submitArgv) {
|
|
167
174
|
if (submitDelayMs > 0) {
|
|
168
175
|
await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
|