oh-my-codex 0.19.1 → 0.20.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 +3 -1
- package/crates/omx-api/src/lib.rs +3 -3
- package/crates/omx-api/tests/cli.rs +4 -4
- package/crates/omx-explore/src/main.rs +4 -4
- package/crates/omx-sparkshell/src/codex_bridge.rs +2 -2
- package/dist/agents/__tests__/definitions.test.js +3 -3
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +71 -42
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +1 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +3 -3
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -2
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +7 -5
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/planner-routing.test.js +19 -12
- package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +151 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/planner-routing.d.ts.map +1 -1
- package/dist/autopilot/planner-routing.js +4 -2
- package/dist/autopilot/planner-routing.js.map +1 -1
- package/dist/autopilot/ralplan-gate.d.ts +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +38 -2
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/autoresearch/runtime.d.ts +2 -0
- package/dist/autoresearch/runtime.d.ts.map +1 -1
- package/dist/autoresearch/runtime.js +8 -0
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/capabilities/__tests__/lockfile.test.d.ts +2 -0
- package/dist/capabilities/__tests__/lockfile.test.d.ts.map +1 -0
- package/dist/capabilities/__tests__/lockfile.test.js +115 -0
- package/dist/capabilities/__tests__/lockfile.test.js.map +1 -0
- package/dist/capabilities/lockfile.d.ts +102 -0
- package/dist/capabilities/lockfile.d.ts.map +1 -0
- package/dist/capabilities/lockfile.js +334 -0
- package/dist/capabilities/lockfile.js.map +1 -0
- package/dist/cli/__tests__/agents.test.js +5 -5
- package/dist/cli/__tests__/agents.test.js.map +1 -1
- package/dist/cli/__tests__/capabilities.test.d.ts +2 -0
- package/dist/cli/__tests__/capabilities.test.d.ts.map +1 -0
- package/dist/cli/__tests__/capabilities.test.js +135 -0
- package/dist/cli/__tests__/capabilities.test.js.map +1 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js +264 -6
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.js +83 -78
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +10 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-spark-routing.test.js +22 -6
- package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +87 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +53 -18
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +3 -3
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +95 -6
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +54 -2
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +49 -21
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +3 -3
- package/dist/cli/__tests__/setup-refresh.test.js +166 -12
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +27 -3
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +301 -7
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/agents.js +1 -1
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/capabilities.d.ts +3 -0
- package/dist/cli/capabilities.d.ts.map +1 -0
- package/dist/cli/capabilities.js +118 -0
- package/dist/cli/capabilities.js.map +1 -0
- package/dist/cli/doctor.d.ts +3 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +50 -34
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +7 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +77 -19
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +1 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +31 -0
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +9 -6
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +3 -4
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +210 -114
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +26 -52
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +51 -26
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +20 -11
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +169 -152
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +16 -6
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +19 -8
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +12 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +1 -1
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js +2 -2
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js +2 -2
- package/dist/leader/__tests__/contract.test.js +150 -1
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +26 -0
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +188 -0
- package/dist/leader/contract.js.map +1 -1
- package/dist/modes/base.d.ts +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +27 -1
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts +2 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +1 -0
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/pipeline/stages/ultragoal.d.ts +5 -1
- package/dist/pipeline/stages/ultragoal.d.ts.map +1 -1
- package/dist/pipeline/stages/ultragoal.js +14 -4
- package/dist/pipeline/stages/ultragoal.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +132 -0
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +17 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +97 -1
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +1528 -32
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +377 -31
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +450 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +14 -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 +72 -11
- package/dist/state/operations.js.map +1 -1
- package/dist/state/terminal-normalization.d.ts +8 -0
- package/dist/state/terminal-normalization.d.ts.map +1 -0
- package/dist/state/terminal-normalization.js +127 -0
- package/dist/state/terminal-normalization.js.map +1 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +3 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/delegation-policy.test.js +2 -2
- package/dist/team/__tests__/delegation-policy.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +25 -25
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +28 -28
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +7 -7
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js +5 -5
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +12 -12
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +57 -9
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +3 -3
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/model-contract.d.ts +1 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +11 -0
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +2 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +6 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +68 -1
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +5 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +41 -9
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/agents-md.test.js +16 -0
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +7 -7
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts +2 -0
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts.map +1 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js +118 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js.map +1 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +4 -3
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/worktree-tool-context.d.ts +25 -0
- package/dist/utils/worktree-tool-context.d.ts.map +1 -0
- package/dist/utils/worktree-tool-context.js +116 -0
- package/dist/utils/worktree-tool-context.js.map +1 -0
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +96 -10
- package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +5 -5
- package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +2 -2
- package/prompts/prometheus-strict-metis.md +1 -1
- package/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/skills/code-review/SKILL.md +1 -1
- package/skills/ecomode/references/agent-tiers.md +1 -1
- package/skills/omx-setup/SKILL.md +5 -5
- package/skills/prometheus-strict/SKILL.md +3 -3
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/team/SKILL.md +1 -1
- package/skills/ultrawork/references/agent-tiers.md +2 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +1818 -113
- package/src/scripts/codex-native-hook.ts +414 -27
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { describe, it } from "node:test";
|
|
8
|
+
const testDir = join(fileURLToPath(import.meta.url), "..");
|
|
9
|
+
const repoRoot = join(testDir, "..", "..", "..");
|
|
10
|
+
const omxBin = join(repoRoot, "dist", "cli", "omx.js");
|
|
11
|
+
async function withTempDir(run) {
|
|
12
|
+
const dir = await mkdtemp(join(tmpdir(), "omx-capabilities-cli-"));
|
|
13
|
+
try {
|
|
14
|
+
return await run(dir);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await rm(dir, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function runOmx(cwd, args) {
|
|
21
|
+
return spawnSync(process.execPath, [omxBin, ...args], { cwd, encoding: "utf8" });
|
|
22
|
+
}
|
|
23
|
+
async function writeObservations(dir, observations) {
|
|
24
|
+
const path = join(dir, "observations.json");
|
|
25
|
+
await writeFile(path, JSON.stringify({ version: 1, kind: "omx_capability_observations", observations }, null, 2), "utf8");
|
|
26
|
+
return path;
|
|
27
|
+
}
|
|
28
|
+
const SUCCESS_OBSERVATIONS = [
|
|
29
|
+
{
|
|
30
|
+
fixture_id: "project-memory-write-required-arg",
|
|
31
|
+
tool_calls: [{ name: "project_memory_write", arguments: { memory: { note: "capability preflight fixture" } } }],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
fixture_id: "project-memory-read-known-tool",
|
|
35
|
+
tool_calls: [{ name: "project_memory_read", arguments: {} }],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
fixture_id: "tool-restraint-no-call",
|
|
39
|
+
tool_calls: [],
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
function hasFailure(stdout, code) {
|
|
43
|
+
const parsed = JSON.parse(stdout);
|
|
44
|
+
return parsed.failures?.some((failure) => failure.code === code) ?? false;
|
|
45
|
+
}
|
|
46
|
+
describe("omx capabilities cli", () => {
|
|
47
|
+
it("routes local help", async () => {
|
|
48
|
+
await withTempDir(async (dir) => {
|
|
49
|
+
const result = runOmx(dir, ["capabilities", "--help"]);
|
|
50
|
+
assert.equal(result.status, 0);
|
|
51
|
+
assert.match(result.stdout, /omx capabilities lock/);
|
|
52
|
+
assert.doesNotMatch(result.stdout, /Launch Codex CLI/);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it("locks and checks with JSON output", async () => {
|
|
56
|
+
await withTempDir(async (dir) => {
|
|
57
|
+
const lock = runOmx(dir, ["capabilities", "lock", "--json"]);
|
|
58
|
+
assert.equal(lock.status, 0, lock.stderr);
|
|
59
|
+
const lockJson = JSON.parse(lock.stdout);
|
|
60
|
+
assert.equal(lockJson.ok, true);
|
|
61
|
+
assert.match(lockJson.lockfile, /omx-capabilities\.lock\.json$/);
|
|
62
|
+
const check = runOmx(dir, ["capabilities", "check", "--json"]);
|
|
63
|
+
assert.equal(check.status, 0, check.stderr);
|
|
64
|
+
const checkJson = JSON.parse(check.stdout);
|
|
65
|
+
assert.equal(checkJson.ok, true);
|
|
66
|
+
assert.equal(checkJson.observations_checked, false);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
it("checks successful fixture observations", async () => {
|
|
70
|
+
await withTempDir(async (dir) => {
|
|
71
|
+
const lock = runOmx(dir, ["capabilities", "lock", "--json"]);
|
|
72
|
+
assert.equal(lock.status, 0, lock.stderr);
|
|
73
|
+
const observations = await writeObservations(dir, SUCCESS_OBSERVATIONS);
|
|
74
|
+
const check = runOmx(dir, ["capabilities", "check", "--observations", observations, "--require-observations", "--json"]);
|
|
75
|
+
assert.equal(check.status, 0, check.stderr);
|
|
76
|
+
const checkJson = JSON.parse(check.stdout);
|
|
77
|
+
assert.equal(checkJson.ok, true);
|
|
78
|
+
assert.equal(checkJson.observations_checked, true);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
it("exits non-zero for required missing observations", async () => {
|
|
82
|
+
await withTempDir(async (dir) => {
|
|
83
|
+
const lock = runOmx(dir, ["capabilities", "lock", "--json"]);
|
|
84
|
+
assert.equal(lock.status, 0, lock.stderr);
|
|
85
|
+
const check = runOmx(dir, ["capabilities", "check", "--require-observations", "--json"]);
|
|
86
|
+
assert.notEqual(check.status, 0);
|
|
87
|
+
const checkJson = JSON.parse(check.stdout);
|
|
88
|
+
assert.equal(checkJson.ok, false);
|
|
89
|
+
assert.ok(checkJson.failures.some((failure) => failure.code === "observations_missing"));
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
it("reports missing required tool arguments", async () => {
|
|
93
|
+
await withTempDir(async (dir) => {
|
|
94
|
+
const lock = runOmx(dir, ["capabilities", "lock", "--json"]);
|
|
95
|
+
assert.equal(lock.status, 0, lock.stderr);
|
|
96
|
+
const observations = await writeObservations(dir, [
|
|
97
|
+
{ fixture_id: "project-memory-write-required-arg", tool_calls: [{ name: "project_memory_write", arguments: {} }] },
|
|
98
|
+
SUCCESS_OBSERVATIONS[1],
|
|
99
|
+
SUCCESS_OBSERVATIONS[2],
|
|
100
|
+
]);
|
|
101
|
+
const check = runOmx(dir, ["capabilities", "check", "--observations", observations, "--json"]);
|
|
102
|
+
assert.notEqual(check.status, 0);
|
|
103
|
+
assert.equal(hasFailure(check.stdout, "missing_required_arg"), true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
it("reports hallucinated tools", async () => {
|
|
107
|
+
await withTempDir(async (dir) => {
|
|
108
|
+
const lock = runOmx(dir, ["capabilities", "lock", "--json"]);
|
|
109
|
+
assert.equal(lock.status, 0, lock.stderr);
|
|
110
|
+
const observations = await writeObservations(dir, [
|
|
111
|
+
SUCCESS_OBSERVATIONS[0],
|
|
112
|
+
{ fixture_id: "project-memory-read-known-tool", tool_calls: [{ name: "imaginary_tool", arguments: {} }] },
|
|
113
|
+
SUCCESS_OBSERVATIONS[2],
|
|
114
|
+
]);
|
|
115
|
+
const check = runOmx(dir, ["capabilities", "check", "--observations", observations, "--json"]);
|
|
116
|
+
assert.notEqual(check.status, 0);
|
|
117
|
+
assert.equal(hasFailure(check.stdout, "hallucinated_tool"), true);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
it("reports tool-restraint violations", async () => {
|
|
121
|
+
await withTempDir(async (dir) => {
|
|
122
|
+
const lock = runOmx(dir, ["capabilities", "lock", "--json"]);
|
|
123
|
+
assert.equal(lock.status, 0, lock.stderr);
|
|
124
|
+
const observations = await writeObservations(dir, [
|
|
125
|
+
SUCCESS_OBSERVATIONS[0],
|
|
126
|
+
SUCCESS_OBSERVATIONS[1],
|
|
127
|
+
{ fixture_id: "tool-restraint-no-call", tool_calls: [{ name: "project_memory_read", arguments: {} }] },
|
|
128
|
+
]);
|
|
129
|
+
const check = runOmx(dir, ["capabilities", "check", "--observations", observations, "--json"]);
|
|
130
|
+
assert.notEqual(check.status, 0);
|
|
131
|
+
assert.equal(hasFailure(check.stdout, "unexpected_tool_call"), true);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
//# sourceMappingURL=capabilities.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.test.js","sourceRoot":"","sources":["../../../src/cli/__tests__/capabilities.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -148,6 +148,8 @@ async function assertPluginHookLaunchesPostCompactFromCache() {
|
|
|
148
148
|
OMX_ROOT: join(cacheRoot, '.omx-root'),
|
|
149
149
|
OMX_SESSION_ID: 'omx-plugin-hook-postcompact-smoke',
|
|
150
150
|
OMX_SOURCE_CWD: cacheRoot,
|
|
151
|
+
OMX_ENTRY_PATH: omxBin,
|
|
152
|
+
OMX_CODEX_LAUNCH_ID: 'omx-plugin-hook-postcompact-smoke-launch',
|
|
151
153
|
OMX_STARTUP_CWD: cacheRoot,
|
|
152
154
|
},
|
|
153
155
|
});
|
|
@@ -197,6 +199,8 @@ async function assertPluginHookDelegatesPostCompactToPinnedCommand() {
|
|
|
197
199
|
OMX_ROOT: join(cacheRoot, '.omx-root'),
|
|
198
200
|
OMX_SESSION_ID: 'omx-plugin-hook-postcompact-delegate',
|
|
199
201
|
OMX_SOURCE_CWD: cacheRoot,
|
|
202
|
+
OMX_ENTRY_PATH: omxBin,
|
|
203
|
+
OMX_CODEX_LAUNCH_ID: 'omx-plugin-hook-postcompact-delegate-launch',
|
|
200
204
|
OMX_STARTUP_CWD: cacheRoot,
|
|
201
205
|
},
|
|
202
206
|
});
|
|
@@ -254,10 +258,25 @@ async function withPluginCacheCopy(run) {
|
|
|
254
258
|
}
|
|
255
259
|
function pluginHookEnv(overrides = {}) {
|
|
256
260
|
const env = { ...process.env };
|
|
257
|
-
for (const key of [
|
|
261
|
+
for (const key of [
|
|
262
|
+
'OMX_TEAM_STATE_ROOT',
|
|
263
|
+
'OMX_ROOT',
|
|
264
|
+
'OMX_STATE_ROOT',
|
|
265
|
+
'OMX_SESSION_ID',
|
|
266
|
+
'CODEX_SESSION_ID',
|
|
267
|
+
'OMX_ENTRY_PATH',
|
|
268
|
+
'OMX_CODEX_LAUNCH_ID',
|
|
269
|
+
'OMX_STARTUP_CWD',
|
|
270
|
+
]) {
|
|
258
271
|
delete env[key];
|
|
259
272
|
}
|
|
260
|
-
return {
|
|
273
|
+
return {
|
|
274
|
+
...env,
|
|
275
|
+
OMX_ENTRY_PATH: omxBin,
|
|
276
|
+
OMX_CODEX_LAUNCH_ID: 'omx-plugin-layout-launch',
|
|
277
|
+
OMX_STARTUP_CWD: root,
|
|
278
|
+
...overrides,
|
|
279
|
+
};
|
|
261
280
|
}
|
|
262
281
|
function runPluginNativeHook(cachePluginRoot, input, env = {}) {
|
|
263
282
|
return spawnSync(process.execPath, [join(cachePluginRoot, 'hooks', 'codex-native-hook.mjs')], {
|
|
@@ -336,6 +355,97 @@ describe('official Codex plugin layout', () => {
|
|
|
336
355
|
assert.match(launcher, /return \{ \.\.\.options, shell: true, windowsHide: true \};/);
|
|
337
356
|
assert.doesNotMatch(launcher, /shell:\s*process\.platform === 'win32'/);
|
|
338
357
|
});
|
|
358
|
+
it('no-ops plugin hooks when Codex was not launched through omx', async () => {
|
|
359
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
360
|
+
const calledPath = join(cacheRoot, 'called.txt');
|
|
361
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'record-called.cmd' : 'record-called.sh');
|
|
362
|
+
if (process.platform === 'win32') {
|
|
363
|
+
await writeFile(commandPath, `@echo off\r\necho called > "${calledPath}"\r\necho {}\r\n`, 'utf-8');
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
await writeFile(commandPath, `#!/bin/sh\necho called > "${calledPath}"\nprintf '{}\\n'\n`, 'utf-8');
|
|
367
|
+
await chmod(commandPath, 0o755);
|
|
368
|
+
}
|
|
369
|
+
const userPrompt = runPluginNativeHook(cachePluginRoot, JSON.stringify({ hook_event_name: 'UserPromptSubmit', prompt: '$ralplan smoke' }), {
|
|
370
|
+
OMX_ENTRY_PATH: '',
|
|
371
|
+
OMX_CODEX_LAUNCH_ID: '',
|
|
372
|
+
OMX_NATIVE_HOOK_COMMAND: commandPath,
|
|
373
|
+
});
|
|
374
|
+
assert.equal(userPrompt.status, 0, userPrompt.stderr || userPrompt.stdout);
|
|
375
|
+
assert.equal(userPrompt.stdout, '');
|
|
376
|
+
await assert.rejects(readFile(calledPath, 'utf-8'), { code: 'ENOENT' });
|
|
377
|
+
const stop = runPluginNativeHook(cachePluginRoot, JSON.stringify({ hook_event_name: 'Stop', session_id: 'plain-codex-stop' }), {
|
|
378
|
+
OMX_ENTRY_PATH: '',
|
|
379
|
+
OMX_CODEX_LAUNCH_ID: '',
|
|
380
|
+
OMX_NATIVE_HOOK_COMMAND: commandPath,
|
|
381
|
+
});
|
|
382
|
+
assert.equal(stop.status, 0, stop.stderr || stop.stdout);
|
|
383
|
+
assert.deepEqual(parseSingleJsonStdout(stop.stdout), {});
|
|
384
|
+
await assert.rejects(readFile(calledPath, 'utf-8'), { code: 'ENOENT' });
|
|
385
|
+
const oversizedUserPrompt = runPluginNativeHook(cachePluginRoot, JSON.stringify({
|
|
386
|
+
hook_event_name: 'UserPromptSubmit',
|
|
387
|
+
prompt: 'x'.repeat(2 * 1024 * 1024),
|
|
388
|
+
}), {
|
|
389
|
+
OMX_ENTRY_PATH: '',
|
|
390
|
+
OMX_CODEX_LAUNCH_ID: '',
|
|
391
|
+
OMX_NATIVE_HOOK_COMMAND: commandPath,
|
|
392
|
+
});
|
|
393
|
+
assert.equal(oversizedUserPrompt.error, undefined, oversizedUserPrompt.error?.message ?? '');
|
|
394
|
+
assert.equal(oversizedUserPrompt.status, 0, oversizedUserPrompt.stderr || oversizedUserPrompt.stdout);
|
|
395
|
+
assert.equal(oversizedUserPrompt.stdout, '');
|
|
396
|
+
await assert.rejects(readFile(calledPath, 'utf-8'), { code: 'ENOENT' });
|
|
397
|
+
const oversizedStop = runPluginNativeHook(cachePluginRoot, JSON.stringify({
|
|
398
|
+
hook_event_name: 'Stop',
|
|
399
|
+
session_id: 'plain-codex-oversized-stop',
|
|
400
|
+
padding: 'x'.repeat(2 * 1024 * 1024),
|
|
401
|
+
}), {
|
|
402
|
+
OMX_ENTRY_PATH: '',
|
|
403
|
+
OMX_CODEX_LAUNCH_ID: '',
|
|
404
|
+
OMX_NATIVE_HOOK_COMMAND: commandPath,
|
|
405
|
+
});
|
|
406
|
+
assert.equal(oversizedStop.error, undefined, oversizedStop.error?.message ?? '');
|
|
407
|
+
assert.equal(oversizedStop.status, 0, oversizedStop.stderr || oversizedStop.stdout);
|
|
408
|
+
assert.deepEqual(parseSingleJsonStdout(oversizedStop.stdout), {});
|
|
409
|
+
await assert.rejects(readFile(calledPath, 'utf-8'), { code: 'ENOENT' });
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
it('no-ops plugin hooks for nested plain Codex sessions that inherit omx launch env', async () => {
|
|
413
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
414
|
+
const calledPath = join(cacheRoot, 'called.txt');
|
|
415
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'record-inherited-called.cmd' : 'record-inherited-called.sh');
|
|
416
|
+
if (process.platform === 'win32') {
|
|
417
|
+
await writeFile(commandPath, `@echo off\r\necho %* > "${calledPath}"\r\necho {}\r\n`, 'utf-8');
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
await writeFile(commandPath, `#!/bin/sh\necho "$@" > "${calledPath}"\nprintf '{}\\n'\n`, 'utf-8');
|
|
421
|
+
await chmod(commandPath, 0o755);
|
|
422
|
+
}
|
|
423
|
+
const inheritedEnv = {
|
|
424
|
+
OMX_ROOT: join(cacheRoot, '.omx-root'),
|
|
425
|
+
OMX_ENTRY_PATH: omxBin,
|
|
426
|
+
OMX_CODEX_LAUNCH_ID: 'inherited-launch-token',
|
|
427
|
+
OMX_NATIVE_HOOK_COMMAND: commandPath,
|
|
428
|
+
};
|
|
429
|
+
const owner = runPluginNativeHook(cachePluginRoot, JSON.stringify({
|
|
430
|
+
hook_event_name: 'UserPromptSubmit',
|
|
431
|
+
session_id: 'owner-codex-session',
|
|
432
|
+
session_pid: 111,
|
|
433
|
+
prompt: '$ralplan smoke',
|
|
434
|
+
}), inheritedEnv);
|
|
435
|
+
assert.equal(owner.status, 0, owner.stderr || owner.stdout);
|
|
436
|
+
assert.equal((await readFile(calledPath, 'utf-8')).trim(), 'codex-native-hook');
|
|
437
|
+
await rm(calledPath, { force: true });
|
|
438
|
+
const nestedPlainCodex = runPluginNativeHook(cachePluginRoot, JSON.stringify({
|
|
439
|
+
hook_event_name: 'UserPromptSubmit',
|
|
440
|
+
session_id: 'nested-plain-codex-session',
|
|
441
|
+
session_pid: 222,
|
|
442
|
+
prompt: '$ralplan nested',
|
|
443
|
+
}), inheritedEnv);
|
|
444
|
+
assert.equal(nestedPlainCodex.status, 0, nestedPlainCodex.stderr || nestedPlainCodex.stdout);
|
|
445
|
+
assert.equal(nestedPlainCodex.stdout, '');
|
|
446
|
+
await assert.rejects(readFile(calledPath, 'utf-8'), { code: 'ENOENT' });
|
|
447
|
+
});
|
|
448
|
+
});
|
|
339
449
|
it('emits Stop JSON when the plugin hook pinned launcher is invalid', async () => {
|
|
340
450
|
await withPluginCacheCopy(async (cachePluginRoot) => {
|
|
341
451
|
await writeFile(join(cachePluginRoot, 'hooks', 'omx-command.json'), '{"command":', 'utf-8');
|
|
@@ -428,6 +538,152 @@ exit 7
|
|
|
428
538
|
assert.doesNotMatch(result.stdout, /plugin_stop_hook_launcher/);
|
|
429
539
|
});
|
|
430
540
|
});
|
|
541
|
+
it('forwards the complete valid Stop child decision unchanged', async () => {
|
|
542
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
543
|
+
const childOutput = {
|
|
544
|
+
decision: 'block',
|
|
545
|
+
reason: 'Autopilot workflow is still active.',
|
|
546
|
+
stopReason: 'autopilot_ultragoal',
|
|
547
|
+
systemMessage: 'Autopilot diagnostic: complete the active workflow before stopping.',
|
|
548
|
+
};
|
|
549
|
+
const childJson = JSON.stringify(childOutput);
|
|
550
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'complete-valid-json.cmd' : 'complete-valid-json.sh');
|
|
551
|
+
if (process.platform === 'win32') {
|
|
552
|
+
await writeFile(commandPath, `@echo off\r\necho ${childJson}\r\nexit /b 0\r\n`, 'utf-8');
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
await writeFile(commandPath, `#!/bin/sh
|
|
556
|
+
printf '${childJson}\n'
|
|
557
|
+
`, 'utf-8');
|
|
558
|
+
await chmod(commandPath, 0o755);
|
|
559
|
+
}
|
|
560
|
+
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({ hook_event_name: 'Stop', session_id: 'sess-plugin-complete-valid-json-stop' }), { OMX_NATIVE_HOOK_COMMAND: commandPath });
|
|
561
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
562
|
+
assert.equal(result.stderr, '');
|
|
563
|
+
const output = parseSingleJsonStdout(result.stdout);
|
|
564
|
+
assert.deepEqual(Object.keys(output).sort(), ['decision', 'reason', 'stopReason', 'systemMessage']);
|
|
565
|
+
assert.deepEqual(output, childOutput);
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
it('preserves valid pretty-printed Stop child JSON', async () => {
|
|
569
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
570
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'pretty-valid-json.cmd' : 'pretty-valid-json.sh');
|
|
571
|
+
if (process.platform === 'win32') {
|
|
572
|
+
await writeFile(commandPath, '@echo off\r\necho {\r\necho "decision": "block",\r\necho "stopReason": "pretty_child_json"\r\necho }\r\nexit /b 0\r\n', 'utf-8');
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
await writeFile(commandPath, `#!/bin/sh
|
|
576
|
+
printf '{\n'
|
|
577
|
+
printf ' "decision": "block",\n'
|
|
578
|
+
printf ' "stopReason": "pretty_child_json"\n'
|
|
579
|
+
printf '}\n'
|
|
580
|
+
`, 'utf-8');
|
|
581
|
+
await chmod(commandPath, 0o755);
|
|
582
|
+
}
|
|
583
|
+
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({ hook_event_name: 'Stop', session_id: 'sess-plugin-pretty-valid-json-stop' }), { OMX_NATIVE_HOOK_COMMAND: commandPath });
|
|
584
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
585
|
+
const output = parseSingleJsonStdout(result.stdout);
|
|
586
|
+
assert.equal(output.decision, 'block');
|
|
587
|
+
assert.equal(output.stopReason, 'pretty_child_json');
|
|
588
|
+
assert.doesNotMatch(result.stdout, /plugin_stop_hook_launcher_invalid_stdout/);
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
it('preserves valid Stop child JSON when stdout includes prior launcher noise', async () => {
|
|
592
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
593
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'noisy-valid-json.cmd' : 'noisy-valid-json.sh');
|
|
594
|
+
if (process.platform === 'win32') {
|
|
595
|
+
await writeFile(commandPath, '@echo off\r\necho runtime notice before json\r\necho {"decision":"block","stopReason":"autopilot_ultragoal"}\r\nexit /b 0\r\n', 'utf-8');
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
await writeFile(commandPath, `#!/bin/sh
|
|
599
|
+
printf 'runtime notice before json\n'
|
|
600
|
+
printf '{"decision":"block","stopReason":"autopilot_ultragoal"}\n'
|
|
601
|
+
`, 'utf-8');
|
|
602
|
+
await chmod(commandPath, 0o755);
|
|
603
|
+
}
|
|
604
|
+
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({
|
|
605
|
+
hook_event_name: 'Stop',
|
|
606
|
+
session_id: 'omx-1783508412223-c32f1l',
|
|
607
|
+
cwd: cacheRoot,
|
|
608
|
+
}), { OMX_NATIVE_HOOK_COMMAND: commandPath });
|
|
609
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
610
|
+
assert.doesNotMatch(result.stdout, /runtime notice before json/);
|
|
611
|
+
const output = parseSingleJsonStdout(result.stdout);
|
|
612
|
+
assert.equal(output.decision, 'block');
|
|
613
|
+
assert.equal(output.stopReason, 'autopilot_ultragoal');
|
|
614
|
+
assert.doesNotMatch(result.stdout, /plugin_stop_hook_launcher_invalid_stdout/);
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
it('rejects Stop child JSON when later stdout noise follows it', async () => {
|
|
618
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
619
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'valid-json-trailing-noise.cmd' : 'valid-json-trailing-noise.sh');
|
|
620
|
+
if (process.platform === 'win32') {
|
|
621
|
+
await writeFile(commandPath, '@echo off\r\necho {"decision":"block","stopReason":"child_valid_json"}\r\necho trailing runtime noise\r\nexit /b 0\r\n', 'utf-8');
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
await writeFile(commandPath, `#!/bin/sh
|
|
625
|
+
printf '{"decision":"block","stopReason":"child_valid_json"}\n'
|
|
626
|
+
printf 'trailing runtime noise\n'
|
|
627
|
+
`, 'utf-8');
|
|
628
|
+
await chmod(commandPath, 0o755);
|
|
629
|
+
}
|
|
630
|
+
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({ hook_event_name: 'Stop', session_id: 'sess-plugin-trailing-noise-stop' }), { OMX_NATIVE_HOOK_COMMAND: commandPath });
|
|
631
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
632
|
+
assert.doesNotMatch(result.stdout, /trailing runtime noise/);
|
|
633
|
+
const output = parseSingleJsonStdout(result.stdout);
|
|
634
|
+
assert.equal(output.decision, 'block');
|
|
635
|
+
assert.equal(output.stopReason, 'plugin_stop_hook_launcher_invalid_stdout');
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
it('rejects Stop child JSON when an earlier JSON decision is followed by noise and final JSON', async () => {
|
|
639
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
640
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'json-noise-json.cmd' : 'json-noise-json.sh');
|
|
641
|
+
if (process.platform === 'win32') {
|
|
642
|
+
await writeFile(commandPath, '@echo off\r\necho {"decision":"block","stopReason":"first_json"}\r\necho trailing runtime noise\r\necho {"decision":"block","stopReason":"second_json"}\r\nexit /b 0\r\n', 'utf-8');
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
await writeFile(commandPath, `#!/bin/sh
|
|
646
|
+
printf '{"decision":"block","stopReason":"first_json"}\n'
|
|
647
|
+
printf 'trailing runtime noise\n'
|
|
648
|
+
printf '{"decision":"block","stopReason":"second_json"}\n'
|
|
649
|
+
`, 'utf-8');
|
|
650
|
+
await chmod(commandPath, 0o755);
|
|
651
|
+
}
|
|
652
|
+
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({ hook_event_name: 'Stop', session_id: 'sess-plugin-json-noise-json-stop' }), { OMX_NATIVE_HOOK_COMMAND: commandPath });
|
|
653
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
654
|
+
assert.doesNotMatch(result.stdout, /first_json/);
|
|
655
|
+
assert.doesNotMatch(result.stdout, /second_json/);
|
|
656
|
+
assert.doesNotMatch(result.stdout, /trailing runtime noise/);
|
|
657
|
+
const output = parseSingleJsonStdout(result.stdout);
|
|
658
|
+
assert.equal(output.decision, 'block');
|
|
659
|
+
assert.equal(output.stopReason, 'plugin_stop_hook_launcher_invalid_stdout');
|
|
660
|
+
});
|
|
661
|
+
});
|
|
662
|
+
it('rejects pretty Stop child JSON followed by final JSON log noise', async () => {
|
|
663
|
+
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
664
|
+
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'pretty-json-final-log.cmd' : 'pretty-json-final-log.sh');
|
|
665
|
+
if (process.platform === 'win32') {
|
|
666
|
+
await writeFile(commandPath, '@echo off\r\necho {\r\necho "decision": "block",\r\necho "stopReason": "real_block"\r\necho }\r\necho {"level":"info"}\r\nexit /b 0\r\n', 'utf-8');
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
await writeFile(commandPath, `#!/bin/sh
|
|
670
|
+
printf '{\n'
|
|
671
|
+
printf ' "decision": "block",\n'
|
|
672
|
+
printf ' "stopReason": "real_block"\n'
|
|
673
|
+
printf '}\n'
|
|
674
|
+
printf '{"level":"info"}\n'
|
|
675
|
+
`, 'utf-8');
|
|
676
|
+
await chmod(commandPath, 0o755);
|
|
677
|
+
}
|
|
678
|
+
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({ hook_event_name: 'Stop', session_id: 'sess-plugin-pretty-json-final-log-stop' }), { OMX_NATIVE_HOOK_COMMAND: commandPath });
|
|
679
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
680
|
+
assert.doesNotMatch(result.stdout, /real_block/);
|
|
681
|
+
assert.doesNotMatch(result.stdout, /level/);
|
|
682
|
+
const output = parseSingleJsonStdout(result.stdout);
|
|
683
|
+
assert.equal(output.decision, 'block');
|
|
684
|
+
assert.equal(output.stopReason, 'plugin_stop_hook_launcher_invalid_stdout');
|
|
685
|
+
});
|
|
686
|
+
});
|
|
431
687
|
it('replaces oversized Stop child stdout with fallback Stop JSON', async () => {
|
|
432
688
|
await withPluginCacheCopy(async (cachePluginRoot, cacheRoot) => {
|
|
433
689
|
const commandPath = join(cacheRoot, process.platform === 'win32' ? 'oversized-stop-stdout.cmd' : 'oversized-stop-stdout.sh');
|
|
@@ -451,7 +707,7 @@ head -c 1100000 /dev/zero | tr '\0' x
|
|
|
451
707
|
it('emits Stop JSON for malformed Stop-looking stdin before invalid launcher failure', async () => {
|
|
452
708
|
await withPluginCacheCopy(async (cachePluginRoot) => {
|
|
453
709
|
await writeFile(join(cachePluginRoot, 'hooks', 'omx-command.json'), '{"command":', 'utf-8');
|
|
454
|
-
const result = runPluginNativeHook(cachePluginRoot, '{"hook_event_name":"Stop",');
|
|
710
|
+
const result = runPluginNativeHook(cachePluginRoot, '{"hook_event_name":"Stop","session_id":"sess-plugin-malformed-stop",');
|
|
455
711
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
456
712
|
const output = parseSingleJsonStdout(result.stdout);
|
|
457
713
|
assert.equal(output.decision, 'block');
|
|
@@ -461,7 +717,7 @@ head -c 1100000 /dev/zero | tr '\0' x
|
|
|
461
717
|
it('emits Stop JSON for the core-supported name alias before invalid launcher failure', async () => {
|
|
462
718
|
await withPluginCacheCopy(async (cachePluginRoot) => {
|
|
463
719
|
await writeFile(join(cachePluginRoot, 'hooks', 'omx-command.json'), '{"command":', 'utf-8');
|
|
464
|
-
const result = runPluginNativeHook(cachePluginRoot, '{"name":"Stop",');
|
|
720
|
+
const result = runPluginNativeHook(cachePluginRoot, '{"name":"Stop","session_id":"sess-plugin-malformed-name-stop",');
|
|
465
721
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
466
722
|
const output = parseSingleJsonStdout(result.stdout);
|
|
467
723
|
assert.equal(output.decision, 'block');
|
|
@@ -494,6 +750,7 @@ head -c 1100000 /dev/zero | tr '\0' x
|
|
|
494
750
|
await writeFile(join(cachePluginRoot, 'hooks', 'omx-command.json'), '{"command":', 'utf-8');
|
|
495
751
|
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({
|
|
496
752
|
hook_event_name: 'UserPromptSubmit',
|
|
753
|
+
session_id: 'sess-plugin-invalid-launcher-user-prompt',
|
|
497
754
|
prompt: 'hello',
|
|
498
755
|
}));
|
|
499
756
|
assert.equal(result.status, 1);
|
|
@@ -531,6 +788,7 @@ head -c 1100000 /dev/zero | tr '\0' x
|
|
|
531
788
|
await writeFile(join(cachePluginRoot, 'hooks', 'omx-command.json'), '{"command":', 'utf-8');
|
|
532
789
|
const result = runPluginNativeHook(cachePluginRoot, JSON.stringify({
|
|
533
790
|
hook_event_name: 'PreToolUse',
|
|
791
|
+
session_id: 'sess-plugin-nested-stop-text',
|
|
534
792
|
tool_input: { name: 'Stop' },
|
|
535
793
|
}));
|
|
536
794
|
assert.equal(result.status, 1);
|
|
@@ -541,7 +799,7 @@ head -c 1100000 /dev/zero | tr '\0' x
|
|
|
541
799
|
it('does not classify malformed non-Stop plugin JSON with nested Stop text as Stop', async () => {
|
|
542
800
|
await withPluginCacheCopy(async (cachePluginRoot) => {
|
|
543
801
|
await writeFile(join(cachePluginRoot, 'hooks', 'omx-command.json'), '{"command":', 'utf-8');
|
|
544
|
-
const result = runPluginNativeHook(cachePluginRoot, '{"hook_event_name":"PreToolUse","tool_input":{"name":"Stop"},');
|
|
802
|
+
const result = runPluginNativeHook(cachePluginRoot, '{"hook_event_name":"PreToolUse","session_id":"sess-plugin-malformed-non-stop","tool_input":{"name":"Stop"},');
|
|
545
803
|
assert.equal(result.status, 1);
|
|
546
804
|
assert.equal(result.stdout, '');
|
|
547
805
|
assert.match(result.stderr, /invalid plugin hook launcher/);
|
|
@@ -681,7 +939,7 @@ head -c 1100000 /dev/zero | tr '\0' x
|
|
|
681
939
|
});
|
|
682
940
|
it('fails oversized non-Stop plugin stdin without Stop JSON', async () => {
|
|
683
941
|
await withPluginCacheCopy(async (cachePluginRoot) => {
|
|
684
|
-
const result = runPluginNativeHook(cachePluginRoot, 'x'.repeat(1024 * 1024 + 1));
|
|
942
|
+
const result = runPluginNativeHook(cachePluginRoot, `{"hook_event_name":"UserPromptSubmit","session_id":"sess-plugin-oversized-non-stop","padding":"${'x'.repeat(1024 * 1024 + 1)}`);
|
|
685
943
|
assert.equal(result.status, 1);
|
|
686
944
|
assert.equal(result.stdout, '');
|
|
687
945
|
assert.match(result.stderr, /plugin hook stdin exceeded/);
|