oh-my-openagent 4.11.0 → 4.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/.agents/skills/codex-qa/scripts/lib/app-server-client.mjs +132 -82
  2. package/.agents/skills/codex-qa/scripts/lib/app-server-client.test.js +48 -0
  3. package/.agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh +361 -84
  4. package/.agents/skills/work-with-pr/SKILL.md +19 -5
  5. package/.opencode/skills/work-with-pr/SKILL.md +19 -5
  6. package/dist/agents/atlas/agent.d.ts +3 -2
  7. package/dist/agents/sisyphus/glm-5-2.d.ts +4 -0
  8. package/dist/agents/sisyphus/index.d.ts +1 -0
  9. package/dist/agents/sisyphus-agent-config.d.ts +1 -0
  10. package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
  11. package/dist/agents/sisyphus-junior/glm-5-2.d.ts +1 -0
  12. package/dist/agents/sisyphus-junior/index.d.ts +1 -0
  13. package/dist/agents/types.d.ts +7 -6
  14. package/dist/cli/index.js +449 -318
  15. package/dist/cli-node/index.js +449 -318
  16. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +1 -0
  17. package/dist/features/background-agent/parent-wake-history-state.d.ts +1 -0
  18. package/dist/features/background-agent/parent-wake-session-history.d.ts +1 -47
  19. package/dist/features/background-agent/parent-wake-session-message.d.ts +47 -0
  20. package/dist/hooks/keyword-detector/ultrawork/glm.d.ts +2 -0
  21. package/dist/hooks/keyword-detector/ultrawork/index.d.ts +4 -2
  22. package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts +5 -4
  23. package/dist/index.js +1185 -87
  24. package/dist/plugin/tool-execute-before.d.ts +2 -0
  25. package/dist/plugin-handlers/plugin-components-loader.d.ts +1 -0
  26. package/dist/skills/init-deep/SKILL.md +21 -26
  27. package/dist/skills/programming/SKILL.md +25 -121
  28. package/dist/skills/programming/references/code-smells.md +390 -0
  29. package/dist/skills/ulw-plan/SKILL.md +2 -1
  30. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  31. package/dist/skills/ulw-plan/references/intent-clear.md +2 -2
  32. package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -2
  33. package/dist/tools/delegate-task/sync-session-turns.d.ts +3 -0
  34. package/dist/tui.js +16 -7
  35. package/package.json +13 -13
  36. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +22 -2
  37. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +51 -21
  38. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +18 -7
  41. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +9 -2
  42. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +8 -7
  44. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +7 -2
  45. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +2 -1
  46. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +20 -19
  47. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +94 -0
  48. package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +33 -0
  49. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/comment-checker/test/package-smoke.test.ts +2 -71
  52. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  53. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  54. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  55. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  56. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  57. package/packages/omo-codex/plugin/components/lsp/package.json +2 -2
  58. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +26 -104
  59. package/packages/omo-codex/plugin/components/rules/dist/cli.js +2 -2
  60. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  61. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  62. package/packages/omo-codex/plugin/components/rules/src/sparkshell-awareness.ts +2 -2
  63. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +12 -92
  64. package/packages/omo-codex/plugin/components/rules/test/sparkshell-awareness.test.ts +3 -0
  65. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +157 -234
  66. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  67. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -2
  68. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +227 -15
  69. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +62 -2
  70. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +34 -3
  71. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +139 -0
  72. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +111 -0
  73. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +250 -0
  74. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +220 -0
  75. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  76. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  77. package/packages/omo-codex/plugin/components/test-support/package-smoke-fixture.ts +158 -0
  78. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  79. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  80. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -1
  81. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/agents/openai.yaml +1 -1
  82. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  83. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -2
  84. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +76 -49
  85. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  86. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  87. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/agents/openai.yaml +1 -1
  88. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +1 -1
  89. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +17 -0
  90. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +17 -0
  91. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +17 -0
  92. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +17 -0
  93. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +17 -0
  94. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +17 -0
  95. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +17 -0
  96. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +17 -0
  97. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +17 -0
  98. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +17 -0
  99. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +16 -0
  100. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +16 -0
  101. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +16 -0
  102. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +16 -0
  103. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +16 -0
  104. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +17 -0
  105. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +16 -0
  106. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +16 -0
  107. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +16 -0
  108. package/packages/omo-codex/plugin/package-lock.json +12 -22
  109. package/packages/omo-codex/plugin/package.json +1 -1
  110. package/packages/omo-codex/plugin/scripts/auto-update.mjs +1 -1
  111. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +14 -11
  112. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +15 -4
  113. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +37 -0
  114. package/packages/omo-codex/plugin/skills/ast-grep/agents/openai.yaml +2 -0
  115. package/packages/omo-codex/plugin/skills/comment-checker/agents/openai.yaml +2 -0
  116. package/packages/omo-codex/plugin/skills/debugging/agents/openai.yaml +2 -0
  117. package/packages/omo-codex/plugin/skills/frontend/agents/openai.yaml +2 -0
  118. package/packages/omo-codex/plugin/skills/git-master/agents/openai.yaml +1 -1
  119. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +21 -26
  120. package/packages/omo-codex/plugin/skills/init-deep/agents/openai.yaml +2 -0
  121. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  122. package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
  123. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  124. package/packages/omo-codex/plugin/skills/lsp/agents/openai.yaml +2 -0
  125. package/packages/omo-codex/plugin/skills/lsp-setup/agents/openai.yaml +2 -0
  126. package/packages/omo-codex/plugin/skills/programming/SKILL.md +25 -121
  127. package/packages/omo-codex/plugin/skills/programming/agents/openai.yaml +2 -0
  128. package/packages/omo-codex/plugin/skills/programming/references/code-smells.md +390 -0
  129. package/packages/omo-codex/plugin/skills/refactor/agents/openai.yaml +2 -0
  130. package/packages/omo-codex/plugin/skills/remove-ai-slops/agents/openai.yaml +2 -0
  131. package/packages/omo-codex/plugin/skills/review-work/agents/openai.yaml +2 -0
  132. package/packages/omo-codex/plugin/skills/rules/agents/openai.yaml +2 -0
  133. package/packages/omo-codex/plugin/skills/start-work/agents/openai.yaml +2 -0
  134. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +139 -0
  135. package/packages/omo-codex/plugin/skills/teammode/agents/openai.yaml +2 -0
  136. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +111 -0
  137. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +250 -0
  138. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +220 -0
  139. package/packages/omo-codex/plugin/skills/ultraresearch/agents/openai.yaml +2 -0
  140. package/packages/omo-codex/plugin/skills/ulw-loop/agents/openai.yaml +1 -1
  141. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -1
  142. package/packages/omo-codex/plugin/skills/ulw-plan/agents/openai.yaml +1 -1
  143. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  144. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -2
  145. package/packages/omo-codex/plugin/skills/visual-qa/agents/openai.yaml +2 -0
  146. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +34 -33
  147. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +5 -2
  148. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +13 -0
  149. package/packages/omo-codex/plugin/test/auto-update.test.mjs +1 -1
  150. package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +1 -1
  151. package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +14 -6
  152. package/packages/omo-codex/plugin/test/bootstrap-ps-guard.test.mjs +7 -3
  153. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +31 -0
  154. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +3 -3
  155. package/packages/omo-codex/plugin/test/display-metadata.test.mjs +83 -0
  156. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +30 -19
  157. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +3 -3
  158. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
  159. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
  160. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +9 -4
  161. package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +88 -0
  162. package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +240 -0
  163. package/packages/omo-codex/scripts/install-dist/install-local.mjs +412 -216
  164. package/packages/omo-codex/scripts/install-git-bash-mcp-env.test.mjs +24 -0
  165. package/packages/omo-codex/scripts/install-hook-targets.test.mjs +93 -0
  166. package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
  167. package/packages/omo-codex/scripts/install-local-bun-global-update.test.mjs +63 -0
  168. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +109 -1
  169. package/packages/omo-codex/scripts/install-local.mjs +1 -0
  170. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +36 -0
  171. package/packages/shared-skills/skills/init-deep/SKILL.md +21 -26
  172. package/packages/shared-skills/skills/programming/SKILL.md +25 -121
  173. package/packages/shared-skills/skills/programming/references/code-smells.md +390 -0
  174. package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -1
  175. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  176. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -2
  177. package/packages/omo-codex/plugin/hooks/hooks.json +0 -214
@@ -19,99 +19,149 @@
19
19
  // turn/completed.
20
20
  // CODEX_BIN codex binary (default "codex"; PATH lookup, no shell function).
21
21
  //
22
- // Prints a JSON summary to stdout. Exit 0 iff the turn completed AND every
23
- // EXPECT_HOOK fired with status "completed".
22
+ // Prints a JSON summary to stdout. Exit 0 iff the turn completed, every
23
+ // EXPECT_HOOK fired with status "completed", and no hook completed failed.
24
24
  import { spawn } from "node:child_process";
25
+ import { pathToFileURL } from "node:url";
25
26
 
26
- const CODEX_BIN = process.env.CODEX_BIN || "codex";
27
- const MOCK_PORT = process.env.MOCK_PORT;
28
- const PROMPT = process.env.PROMPT || "say hello";
29
- const CWD = process.env.QA_CWD || process.cwd();
30
- const DEADLINE_MS = Number(process.env.DEADLINE_MS || 60000);
31
- const EXPECT = (process.env.EXPECT_HOOK || "").split(",").map((s) => s.trim()).filter(Boolean);
27
+ export function parseExpectedHooks(value) {
28
+ return (value || "").split(",").map((s) => s.trim()).filter(Boolean);
29
+ }
32
30
 
33
- if (!MOCK_PORT) {
34
- console.error("app-server-client: MOCK_PORT is required (start lib/mock-model.mjs first)");
35
- process.exit(2);
31
+ export function summarizeRun({ turnStatus, assistantText, threadId, turnId, expectHook, hooks, stderr }) {
32
+ const completed = new Set(
33
+ hooks
34
+ .filter((h) => h.method === "hook/completed" && h.status === "completed")
35
+ .map((h) => h.eventName),
36
+ );
37
+ const missingHooks = expectHook.filter((eventName) => !completed.has(eventName));
38
+ const failedHooks = hooks.filter((h) => h.method === "hook/completed" && h.status !== "completed");
39
+ const ok = turnStatus === "completed" && missingHooks.length === 0 && failedHooks.length === 0;
40
+ return {
41
+ ok,
42
+ turnStatus,
43
+ assistantText,
44
+ threadId,
45
+ turnId,
46
+ expectHook,
47
+ missingHooks,
48
+ failedHooks,
49
+ hooks,
50
+ stderrTail: stderr.split("\n").slice(-10).join("\n"),
51
+ };
36
52
  }
37
53
 
38
- // Config overrides force codex onto the local mock provider, never the real one.
39
- const overrides = [
40
- `model="mock-model"`,
41
- `model_provider="mock_provider"`,
42
- `model_providers.mock_provider.name="codex-qa mock"`,
43
- `model_providers.mock_provider.base_url="http://127.0.0.1:${MOCK_PORT}/v1"`,
44
- `model_providers.mock_provider.wire_api="responses"`,
45
- `model_providers.mock_provider.request_max_retries=0`,
46
- `model_providers.mock_provider.stream_max_retries=0`,
47
- `approval_policy="never"`,
48
- `sandbox_mode="read-only"`,
49
- ];
50
- const args = overrides.flatMap((o) => ["-c", o]).concat("app-server");
54
+ function main() {
55
+ const CODEX_BIN = process.env.CODEX_BIN || "codex";
56
+ const MOCK_PORT = process.env.MOCK_PORT;
57
+ const PROMPT = process.env.PROMPT || "say hello";
58
+ const CWD = process.env.QA_CWD || process.cwd();
59
+ const DEADLINE_MS = Number(process.env.DEADLINE_MS || 60000);
60
+ const EXPECT = parseExpectedHooks(process.env.EXPECT_HOOK || "");
51
61
 
52
- const child = spawn(CODEX_BIN, args, { stdio: ["pipe", "pipe", "pipe"], env: process.env });
53
- let stderr = "";
54
- child.stderr.on("data", (c) => (stderr += c));
62
+ if (!MOCK_PORT) {
63
+ console.error("app-server-client: MOCK_PORT is required (start lib/mock-model.mjs first)");
64
+ process.exit(2);
65
+ }
55
66
 
56
- const hooks = [];
57
- let assistantText = null;
58
- let threadId = null;
59
- let turnId = null;
60
- let turnStatus = null;
61
- let buf = "";
62
- let finished = false;
67
+ // Config overrides force codex onto the local mock provider, never the real one.
68
+ const overrides = [
69
+ `model="mock-model"`,
70
+ `model_provider="mock_provider"`,
71
+ `model_providers.mock_provider.name="codex-qa mock"`,
72
+ `model_providers.mock_provider.base_url="http://127.0.0.1:${MOCK_PORT}/v1"`,
73
+ `model_providers.mock_provider.wire_api="responses"`,
74
+ `model_providers.mock_provider.request_max_retries=0`,
75
+ `model_providers.mock_provider.stream_max_retries=0`,
76
+ `approval_policy="never"`,
77
+ `sandbox_mode="read-only"`,
78
+ ];
79
+ const args = overrides.flatMap((o) => ["-c", o]).concat("app-server");
63
80
 
64
- const send = (obj) => child.stdin.write(JSON.stringify(obj) + "\n");
81
+ const child = spawn(CODEX_BIN, args, { stdio: ["pipe", "pipe", "pipe"], env: process.env });
82
+ let stderr = "";
83
+ child.stderr.on("data", (c) => (stderr += c));
65
84
 
66
- function finish() {
67
- if (finished) return;
68
- finished = true;
69
- try { child.kill("SIGTERM"); } catch {}
70
- const completed = new Set(hooks.filter((h) => h.method === "hook/completed" && h.status === "completed").map((h) => h.eventName));
71
- const missing = EXPECT.filter((e) => !completed.has(e));
72
- const ok = turnStatus === "completed" && missing.length === 0;
73
- console.log(JSON.stringify({ ok, turnStatus, assistantText, threadId, turnId, expectHook: EXPECT, missingHooks: missing, hooks, stderrTail: stderr.split("\n").slice(-10).join("\n") }, null, 2));
74
- process.exit(ok ? 0 : 1);
75
- }
85
+ const hooks = [];
86
+ let assistantText = null;
87
+ let threadId = null;
88
+ let turnId = null;
89
+ let turnStatus = null;
90
+ let buf = "";
91
+ let finished = false;
76
92
 
77
- function handle(msg) {
78
- if (msg.id === 1 && msg.result) {
79
- send({ method: "initialized" });
80
- send({ id: 2, method: "thread/start", params: { cwd: CWD } });
81
- } else if (msg.id === 2 && msg.result) {
82
- threadId = msg.result.thread?.id;
83
- send({ id: 3, method: "turn/start", params: { threadId, input: [{ type: "text", text: PROMPT }] } });
84
- } else if (msg.id === 3 && msg.result) {
85
- turnId = msg.result.turn?.id;
86
- } else if (msg.method === "hook/started" || msg.method === "hook/completed") {
87
- const run = msg.params?.run || {};
88
- hooks.push({ method: msg.method, eventName: run.eventName, status: run.status, source: run.source ?? run.pluginId });
89
- } else if (msg.method === "item/completed") {
90
- const item = msg.params?.item;
91
- if (item?.type === "agentMessage" && typeof item.text === "string") assistantText = item.text;
92
- } else if (msg.method === "turn/completed") {
93
- turnStatus = msg.params?.turn?.status;
94
- finish();
93
+ const send = (obj) => child.stdin.write(JSON.stringify(obj) + "\n");
94
+
95
+ function finish() {
96
+ if (finished) return;
97
+ finished = true;
98
+ try {
99
+ child.kill("SIGTERM");
100
+ } catch (error) {
101
+ const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
102
+ stderr += `\n[driver] failed to terminate app-server: ${message}\n`;
103
+ }
104
+ const summary = summarizeRun({ turnStatus, assistantText, threadId, turnId, expectHook: EXPECT, hooks, stderr });
105
+ console.log(JSON.stringify(summary, null, 2));
106
+ process.exit(summary.ok ? 0 : 1);
95
107
  }
96
- }
97
108
 
98
- child.stdout.on("data", (chunk) => {
99
- buf += chunk;
100
- let nl;
101
- while ((nl = buf.indexOf("\n")) >= 0) {
102
- const line = buf.slice(0, nl).trim();
103
- buf = buf.slice(nl + 1);
104
- if (!line) continue;
105
- let msg;
106
- try { msg = JSON.parse(line); } catch { continue; }
107
- handle(msg);
109
+ function handle(msg) {
110
+ if (msg.id === 1 && msg.result) {
111
+ send({ method: "initialized" });
112
+ send({ id: 2, method: "thread/start", params: { cwd: CWD } });
113
+ } else if (msg.id === 2 && msg.result) {
114
+ threadId = msg.result.thread?.id;
115
+ send({ id: 3, method: "turn/start", params: { threadId, input: [{ type: "text", text: PROMPT }] } });
116
+ } else if (msg.id === 3 && msg.result) {
117
+ turnId = msg.result.turn?.id;
118
+ } else if (msg.method === "hook/started" || msg.method === "hook/completed") {
119
+ const run = msg.params?.run || {};
120
+ hooks.push({
121
+ method: msg.method,
122
+ eventName: run.eventName,
123
+ status: run.status,
124
+ source: run.source ?? run.pluginId,
125
+ pluginId: run.pluginId,
126
+ hookName: run.hookName ?? run.name,
127
+ runId: run.id,
128
+ });
129
+ } else if (msg.method === "item/completed") {
130
+ const item = msg.params?.item;
131
+ if (item?.type === "agentMessage" && typeof item.text === "string") assistantText = item.text;
132
+ } else if (msg.method === "turn/completed") {
133
+ turnStatus = msg.params?.turn?.status;
134
+ finish();
135
+ }
108
136
  }
109
- });
110
- child.on("exit", (code) => {
111
- if (finished) return;
112
- console.log(JSON.stringify({ ok: false, exitCode: code, turnStatus, hooks, stderrTail: stderr.split("\n").slice(-15).join("\n") }, null, 2));
113
- process.exit(1);
114
- });
115
137
 
116
- send({ id: 1, method: "initialize", params: { clientInfo: { name: "codex-qa", version: "0.1.0" }, capabilities: { experimentalApi: true, requestAttestation: false } } });
117
- setTimeout(() => { stderr += "\n[driver] deadline reached\n"; finish(); }, DEADLINE_MS);
138
+ child.stdout.on("data", (chunk) => {
139
+ buf += chunk;
140
+ let nl;
141
+ while ((nl = buf.indexOf("\n")) >= 0) {
142
+ const line = buf.slice(0, nl).trim();
143
+ buf = buf.slice(nl + 1);
144
+ if (!line) continue;
145
+ let msg;
146
+ try {
147
+ msg = JSON.parse(line);
148
+ } catch (error) {
149
+ if (error instanceof SyntaxError) continue;
150
+ throw error;
151
+ }
152
+ handle(msg);
153
+ }
154
+ });
155
+ child.on("exit", (code) => {
156
+ if (finished) return;
157
+ console.log(JSON.stringify({ ok: false, exitCode: code, turnStatus, hooks, stderrTail: stderr.split("\n").slice(-15).join("\n") }, null, 2));
158
+ process.exit(1);
159
+ });
160
+
161
+ send({ id: 1, method: "initialize", params: { clientInfo: { name: "codex-qa", version: "0.1.0" }, capabilities: { experimentalApi: true, requestAttestation: false } } });
162
+ setTimeout(() => { stderr += "\n[driver] deadline reached\n"; finish(); }, DEADLINE_MS);
163
+ }
164
+
165
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
166
+ main();
167
+ }
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { parseExpectedHooks, summarizeRun } from "./app-server-client.mjs";
3
+
4
+ describe("app-server-client summary", () => {
5
+ it("#given a completed expected event also has a failed hook run #when summarized #then the QA run fails", () => {
6
+ const summary = summarizeRun({
7
+ turnStatus: "completed",
8
+ assistantText: "ok",
9
+ threadId: "thread",
10
+ turnId: "turn",
11
+ expectHook: ["sessionStart", "userPromptSubmit"],
12
+ hooks: [
13
+ { method: "hook/completed", eventName: "sessionStart", status: "completed", source: "plugin" },
14
+ { method: "hook/completed", eventName: "userPromptSubmit", status: "completed", source: "plugin" },
15
+ { method: "hook/completed", eventName: "userPromptSubmit", status: "failed", source: "plugin" },
16
+ ],
17
+ stderr: "",
18
+ });
19
+
20
+ expect(summary.ok).toBe(false);
21
+ expect(summary.missingHooks).toEqual([]);
22
+ expect(summary.failedHooks).toEqual([
23
+ { method: "hook/completed", eventName: "userPromptSubmit", status: "failed", source: "plugin" },
24
+ ]);
25
+ });
26
+
27
+ it("#given all expected hooks complete #when summarized #then the QA run passes", () => {
28
+ const summary = summarizeRun({
29
+ turnStatus: "completed",
30
+ assistantText: "ok",
31
+ threadId: "thread",
32
+ turnId: "turn",
33
+ expectHook: ["sessionStart", "userPromptSubmit"],
34
+ hooks: [
35
+ { method: "hook/completed", eventName: "sessionStart", status: "completed", source: "plugin" },
36
+ { method: "hook/completed", eventName: "userPromptSubmit", status: "completed", source: "plugin" },
37
+ ],
38
+ stderr: "",
39
+ });
40
+
41
+ expect(summary.ok).toBe(true);
42
+ expect(summary.failedHooks).toEqual([]);
43
+ });
44
+
45
+ it("#given a comma-separated expectation #when parsed #then whitespace and empties are ignored", () => {
46
+ expect(parseExpectedHooks(" sessionStart, ,userPromptSubmit ")).toEqual(["sessionStart", "userPromptSubmit"]);
47
+ });
48
+ });