vigiles 2.6.0 → 3.0.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 (207) hide show
  1. package/.claude-plugin/plugin.json +9 -0
  2. package/README.md +74 -129
  3. package/action.yml +143 -7
  4. package/dist/action-gate.d.ts +1 -1
  5. package/dist/action-gate.js +1 -1
  6. package/dist/adapter-conformance.d.ts +30 -0
  7. package/dist/adapter-conformance.js +153 -0
  8. package/dist/adapter-registry.d.ts +42 -0
  9. package/dist/adapter-registry.js +55 -0
  10. package/dist/adapter.d.ts +26 -0
  11. package/dist/adapter.js +16 -0
  12. package/dist/adapters/claude-code/adapter.d.ts +3 -0
  13. package/dist/adapters/claude-code/adapter.js +46 -0
  14. package/dist/{agent-result.d.ts → adapters/claude-code/agent-result.d.ts} +1 -1
  15. package/dist/adapters/claude-code/dialect.d.ts +13 -0
  16. package/dist/adapters/claude-code/dialect.js +51 -0
  17. package/dist/adapters/claude-code/egress-entry.d.ts +2 -0
  18. package/dist/adapters/claude-code/egress-entry.js +115 -0
  19. package/dist/adapters/claude-code/egress.d.ts +114 -0
  20. package/dist/adapters/claude-code/egress.js +276 -0
  21. package/dist/{eval-cache.d.ts → adapters/claude-code/eval-cache.d.ts} +1 -1
  22. package/dist/{eval-cache.js → adapters/claude-code/eval-cache.js} +1 -1
  23. package/dist/{eval.d.ts → adapters/claude-code/eval.d.ts} +28 -4
  24. package/dist/{eval.js → adapters/claude-code/eval.js} +61 -25
  25. package/dist/{harness-test.d.ts → adapters/claude-code/harness-test.d.ts} +33 -37
  26. package/dist/{harness-test.js → adapters/claude-code/harness-test.js} +124 -43
  27. package/dist/adapters/claude-code/hook-protocol.d.ts +10 -0
  28. package/dist/adapters/claude-code/hook-protocol.js +10 -0
  29. package/dist/adapters/claude-code/layout.d.ts +8 -0
  30. package/dist/adapters/claude-code/layout.js +18 -0
  31. package/dist/{mock-model.d.ts → adapters/claude-code/mock-model.d.ts} +2 -24
  32. package/dist/adapters/claude-code/model-mock.d.ts +11 -0
  33. package/dist/adapters/claude-code/model-mock.js +10 -0
  34. package/dist/adapters/claude-code/plugin-loader.d.ts +25 -0
  35. package/dist/adapters/claude-code/plugin-loader.js +19 -0
  36. package/dist/{run-hook.d.ts → adapters/claude-code/run-hook.d.ts} +50 -3
  37. package/dist/{run-hook.js → adapters/claude-code/run-hook.js} +178 -18
  38. package/dist/adapters/claude-code/run-scripts.d.ts +52 -0
  39. package/dist/adapters/claude-code/run-scripts.js +150 -0
  40. package/dist/adapters/claude-code/runtime.d.ts +16 -0
  41. package/dist/adapters/claude-code/runtime.js +39 -0
  42. package/dist/{sandbox.d.ts → adapters/claude-code/sandbox.d.ts} +10 -0
  43. package/dist/{sandbox.js → adapters/claude-code/sandbox.js} +5 -3
  44. package/dist/{skill-driver.d.ts → adapters/claude-code/skill-driver.d.ts} +1 -1
  45. package/dist/adapters/codex/adapter.d.ts +3 -0
  46. package/dist/adapters/codex/adapter.js +49 -0
  47. package/dist/adapters/codex/dialect.d.ts +10 -0
  48. package/dist/adapters/codex/dialect.js +30 -0
  49. package/dist/adapters/codex/driver.d.ts +20 -0
  50. package/dist/adapters/codex/driver.js +89 -0
  51. package/dist/adapters/codex/hook-protocol.d.ts +10 -0
  52. package/dist/adapters/codex/hook-protocol.js +18 -0
  53. package/dist/adapters/codex/layout.d.ts +16 -0
  54. package/dist/adapters/codex/layout.js +18 -0
  55. package/dist/adapters/codex/mock-model.d.ts +52 -0
  56. package/dist/adapters/codex/mock-model.js +210 -0
  57. package/dist/adapters/codex/model-mock.d.ts +11 -0
  58. package/dist/adapters/codex/model-mock.js +10 -0
  59. package/dist/adapters/codex/runtime.d.ts +38 -0
  60. package/dist/adapters/codex/runtime.js +62 -0
  61. package/dist/adapters/opencode/adapter.d.ts +3 -0
  62. package/dist/adapters/opencode/adapter.js +48 -0
  63. package/dist/adapters/opencode/dialect.d.ts +10 -0
  64. package/dist/adapters/opencode/dialect.js +36 -0
  65. package/dist/adapters/opencode/layout.d.ts +9 -0
  66. package/dist/adapters/opencode/layout.js +25 -0
  67. package/dist/adapters/opencode/model-mock.d.ts +10 -0
  68. package/dist/adapters/opencode/model-mock.js +10 -0
  69. package/dist/adapters/opencode/runtime.d.ts +9 -0
  70. package/dist/adapters/opencode/runtime.js +21 -0
  71. package/dist/claude-code.d.ts +8 -2
  72. package/dist/claude-code.js +8 -2
  73. package/dist/cli-flags.d.ts +22 -0
  74. package/dist/cli-flags.js +38 -0
  75. package/dist/cli.js +406 -206
  76. package/dist/codex.d.ts +20 -0
  77. package/dist/codex.js +36 -0
  78. package/dist/community-skills.d.ts +1 -1
  79. package/dist/community-skills.js +2 -2
  80. package/dist/core/adapter.d.ts +88 -0
  81. package/dist/core/adapter.js +3 -0
  82. package/dist/{compile.d.ts → core/compile.d.ts} +11 -2
  83. package/dist/{compile.js → core/compile.js} +68 -53
  84. package/dist/core/compose.d.ts +79 -0
  85. package/dist/core/compose.js +145 -0
  86. package/dist/core/dialect.d.ts +51 -0
  87. package/dist/core/dialect.js +3 -0
  88. package/dist/core/harness-driver.d.ts +134 -0
  89. package/dist/core/harness-driver.js +3 -0
  90. package/dist/core/hook-protocol.d.ts +28 -0
  91. package/dist/core/hook-protocol.js +3 -0
  92. package/dist/core/layout.d.ts +43 -0
  93. package/dist/core/layout.js +3 -0
  94. package/dist/core/model-mock.d.ts +27 -0
  95. package/dist/core/model-mock.js +3 -0
  96. package/dist/core/refs.d.ts +62 -0
  97. package/dist/{refs.js → core/refs.js} +49 -23
  98. package/dist/core/runtime.d.ts +38 -0
  99. package/dist/core/runtime.js +3 -0
  100. package/dist/{types.d.ts → core/types.d.ts} +29 -0
  101. package/dist/{validate.d.ts → core/validate.d.ts} +1 -1
  102. package/dist/{validate.js → core/validate.js} +41 -4
  103. package/dist/e2e.d.ts +19 -0
  104. package/dist/e2e.js +39 -0
  105. package/dist/harness-assert.d.ts +29 -14
  106. package/dist/harness-assert.js +37 -10
  107. package/dist/integration.d.ts +16 -0
  108. package/dist/integration.js +32 -0
  109. package/dist/leaderboard.d.ts +33 -0
  110. package/dist/leaderboard.js +107 -0
  111. package/dist/linting.d.ts +2 -2
  112. package/dist/linting.js +2 -2
  113. package/dist/plugin-loader.d.ts +6 -3
  114. package/dist/plugin-loader.js +101 -49
  115. package/dist/scan.d.ts +49 -0
  116. package/dist/scan.js +177 -0
  117. package/dist/setup-plan.d.ts +51 -0
  118. package/dist/setup-plan.js +85 -0
  119. package/dist/skill-test.d.ts +1 -1
  120. package/dist/skill-test.js +1 -1
  121. package/dist/test-coverage.d.ts +71 -0
  122. package/dist/test-coverage.js +228 -0
  123. package/dist/testing.d.ts +3 -3
  124. package/dist/testing.js +3 -3
  125. package/dist/unit.d.ts +17 -0
  126. package/dist/unit.js +36 -0
  127. package/hooks/refs-nudge.sh +24 -0
  128. package/package.json +30 -15
  129. package/skills/audit-feedback-loop/SKILL.md +76 -0
  130. package/skills/edit-spec/SKILL.md +131 -0
  131. package/skills/enforce-rules-format/SKILL.md +71 -0
  132. package/skills/generate-logo/SKILL.md +103 -0
  133. package/skills/generate-rule/SKILL.md +64 -0
  134. package/skills/linter-docs/clippy.md +241 -0
  135. package/skills/linter-docs/eslint.md +384 -0
  136. package/skills/linter-docs/pylint.md +288 -0
  137. package/skills/linter-docs/rubocop.md +277 -0
  138. package/skills/linter-docs/ruff.md +187 -0
  139. package/skills/linter-docs/stylelint.md +247 -0
  140. package/skills/migrate-to-spec/SKILL.md +126 -0
  141. package/skills/pr-to-lint-rule/SKILL.md +97 -0
  142. package/skills/strengthen/SKILL.md +168 -0
  143. package/skills/test-harness/SKILL.md +157 -0
  144. package/dist/action.d.ts +0 -7
  145. package/dist/action.js +0 -180
  146. package/dist/refs.d.ts +0 -44
  147. package/dist/run-scripts.d.ts +0 -20
  148. package/dist/run-scripts.js +0 -70
  149. /package/dist/{agent-result.js → adapters/claude-code/agent-result.js} +0 -0
  150. /package/dist/{agent-runtime.d.ts → adapters/claude-code/agent-runtime.d.ts} +0 -0
  151. /package/dist/{agent-runtime.js → adapters/claude-code/agent-runtime.js} +0 -0
  152. /package/dist/{egress-proxy.d.ts → adapters/claude-code/egress-proxy.d.ts} +0 -0
  153. /package/dist/{egress-proxy.js → adapters/claude-code/egress-proxy.js} +0 -0
  154. /package/dist/{eval-baseline.d.ts → adapters/claude-code/eval-baseline.d.ts} +0 -0
  155. /package/dist/{eval-baseline.js → adapters/claude-code/eval-baseline.js} +0 -0
  156. /package/dist/{judge.d.ts → adapters/claude-code/judge.d.ts} +0 -0
  157. /package/dist/{judge.js → adapters/claude-code/judge.js} +0 -0
  158. /package/dist/{mock-entry.d.ts → adapters/claude-code/mock-entry.d.ts} +0 -0
  159. /package/dist/{mock-entry.js → adapters/claude-code/mock-entry.js} +0 -0
  160. /package/dist/{mock-model.js → adapters/claude-code/mock-model.js} +0 -0
  161. /package/dist/{skill-driver.js → adapters/claude-code/skill-driver.js} +0 -0
  162. /package/dist/{skill-runtime.d.ts → adapters/claude-code/skill-runtime.d.ts} +0 -0
  163. /package/dist/{skill-runtime.js → adapters/claude-code/skill-runtime.js} +0 -0
  164. /package/dist/{stats.d.ts → adapters/claude-code/stats.d.ts} +0 -0
  165. /package/dist/{stats.js → adapters/claude-code/stats.js} +0 -0
  166. /package/dist/{compile-generator.d.ts → core/compile-generator.d.ts} +0 -0
  167. /package/dist/{compile-generator.js → core/compile-generator.js} +0 -0
  168. /package/dist/{coverage.d.ts → core/coverage.d.ts} +0 -0
  169. /package/dist/{coverage.js → core/coverage.js} +0 -0
  170. /package/dist/{doc-refs.d.ts → core/doc-refs.d.ts} +0 -0
  171. /package/dist/{doc-refs.js → core/doc-refs.js} +0 -0
  172. /package/dist/{evolve.d.ts → core/evolve.d.ts} +0 -0
  173. /package/dist/{evolve.js → core/evolve.js} +0 -0
  174. /package/dist/{frontmatter.d.ts → core/frontmatter.d.ts} +0 -0
  175. /package/dist/{frontmatter.js → core/frontmatter.js} +0 -0
  176. /package/dist/{generate-schema.d.ts → core/generate-schema.d.ts} +0 -0
  177. /package/dist/{generate-schema.js → core/generate-schema.js} +0 -0
  178. /package/dist/{generate-types.d.ts → core/generate-types.d.ts} +0 -0
  179. /package/dist/{generate-types.js → core/generate-types.js} +0 -0
  180. /package/dist/{hash.d.ts → core/hash.d.ts} +0 -0
  181. /package/dist/{hash.js → core/hash.js} +0 -0
  182. /package/dist/{inline.d.ts → core/inline.d.ts} +0 -0
  183. /package/dist/{inline.js → core/inline.js} +0 -0
  184. /package/dist/{integrity.d.ts → core/integrity.d.ts} +0 -0
  185. /package/dist/{integrity.js → core/integrity.js} +0 -0
  186. /package/dist/{linters.d.ts → core/linters.d.ts} +0 -0
  187. /package/dist/{linters.js → core/linters.js} +0 -0
  188. /package/dist/{mcp.d.ts → core/mcp.d.ts} +0 -0
  189. /package/dist/{mcp.js → core/mcp.js} +0 -0
  190. /package/dist/{orphans.d.ts → core/orphans.d.ts} +0 -0
  191. /package/dist/{orphans.js → core/orphans.js} +0 -0
  192. /package/dist/{proofs.d.ts → core/proofs.d.ts} +0 -0
  193. /package/dist/{proofs.js → core/proofs.js} +0 -0
  194. /package/dist/{session.d.ts → core/session.d.ts} +0 -0
  195. /package/dist/{session.js → core/session.js} +0 -0
  196. /package/dist/{sidecar.d.ts → core/sidecar.d.ts} +0 -0
  197. /package/dist/{sidecar.js → core/sidecar.js} +0 -0
  198. /package/dist/{spec.d.ts → core/spec.d.ts} +0 -0
  199. /package/dist/{spec.js → core/spec.js} +0 -0
  200. /package/dist/{symbols.d.ts → core/symbols.d.ts} +0 -0
  201. /package/dist/{symbols.js → core/symbols.js} +0 -0
  202. /package/dist/{test-utils.d.ts → core/test-utils.d.ts} +0 -0
  203. /package/dist/{test-utils.js → core/test-utils.js} +0 -0
  204. /package/dist/{types.js → core/types.js} +0 -0
  205. /package/{.claude-plugin/hooks → hooks}/post-edit.sh +0 -0
  206. /package/{.claude-plugin/hooks → hooks}/pre-edit.sh +0 -0
  207. /package/{.claude-plugin/hooks → hooks}/session-start.sh +0 -0
@@ -0,0 +1,20 @@
1
+ import type { HarnessTestDriver, HarnessDriverContext, ParsedRun } from "../../core/harness-driver.js";
2
+ /**
3
+ * The non-interactive `codex exec` argv. The mock-wiring `-c` flags
4
+ * (`ctx.mockArgs`, from `runtime.wireMock`) MUST follow `exec` — placed before
5
+ * `exec` they're parsed as global flags and ignored (codex falls back to the
6
+ * real OpenAI provider). `--ephemeral` +
7
+ * `--dangerously-bypass-approvals-and-sandbox` make it run unattended;
8
+ * `--skip-git-repo-check` lets it run in a bare temp dir; `--ignore-user-config`
9
+ * keeps the host's `~/.codex` out. The prompt is the trailing positional.
10
+ * Pure, so the arg shape is unit-tested.
11
+ */
12
+ export declare function buildCodexArgs(ctx: HarnessDriverContext): string[];
13
+ /**
14
+ * Extract the final assistant text from codex stdout. Codex `exec` prints the
15
+ * assistant message as plain text (no `--json` here); we return the trimmed
16
+ * stdout as the output. Pure, so it's unit-tested without spawning codex.
17
+ */
18
+ export declare function parseCodexRun(stdout: string): ParsedRun;
19
+ export declare const codexDriver: HarnessTestDriver;
20
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.codexDriver = void 0;
4
+ exports.buildCodexArgs = buildCodexArgs;
5
+ exports.parseCodexRun = parseCodexRun;
6
+ /**
7
+ * codexDriver — the Codex `HarnessTestDriver`. Lets the adapter-driven
8
+ * `runHarnessTest` drive real `codex exec` against the proven OpenAI-Responses
9
+ * mock (`startCodexMock`), keylessly, via the `wireMock` flag recipe in
10
+ * `runtime.ts`. The Codex-side analogue of `claudeCodeDriver`.
11
+ *
12
+ * Scope: pillar-2 deterministic harness testing of the *output* turn — codex's
13
+ * non-interactive `exec` runs the scripted mock to completion and the final
14
+ * assistant text lands in `parseRun().output`. Tool-call / hook stream parsing
15
+ * is left empty (the JSONL schema isn't worth fully parsing for the output
16
+ * check); a test that needs those uses Claude Code today.
17
+ */
18
+ const node_child_process_1 = require("node:child_process");
19
+ const runtime_js_1 = require("./runtime.js");
20
+ const mock_model_js_1 = require("./mock-model.js");
21
+ /**
22
+ * The non-interactive `codex exec` argv. The mock-wiring `-c` flags
23
+ * (`ctx.mockArgs`, from `runtime.wireMock`) MUST follow `exec` — placed before
24
+ * `exec` they're parsed as global flags and ignored (codex falls back to the
25
+ * real OpenAI provider). `--ephemeral` +
26
+ * `--dangerously-bypass-approvals-and-sandbox` make it run unattended;
27
+ * `--skip-git-repo-check` lets it run in a bare temp dir; `--ignore-user-config`
28
+ * keeps the host's `~/.codex` out. The prompt is the trailing positional.
29
+ * Pure, so the arg shape is unit-tested.
30
+ */
31
+ function buildCodexArgs(ctx) {
32
+ return [
33
+ "exec",
34
+ "--ignore-user-config",
35
+ "--skip-git-repo-check",
36
+ "--ephemeral",
37
+ "--dangerously-bypass-approvals-and-sandbox",
38
+ ...ctx.mockArgs,
39
+ "-c",
40
+ 'model="gpt-5-codex"',
41
+ ctx.prompt,
42
+ ];
43
+ }
44
+ /**
45
+ * Extract the final assistant text from codex stdout. Codex `exec` prints the
46
+ * assistant message as plain text (no `--json` here); we return the trimmed
47
+ * stdout as the output. Pure, so it's unit-tested without spawning codex.
48
+ */
49
+ function parseCodexRun(stdout) {
50
+ return { toolCalls: [], hooks: [], output: stdout.trim() };
51
+ }
52
+ /* v8 ignore start -- spawns the real codex CLI; exercised by the gated
53
+ codex-backed suite, the pure helpers above carry the testable logic. */
54
+ function codexAvailable() {
55
+ try {
56
+ return ((0, node_child_process_1.spawnSync)(runtime_js_1.codexRuntime.agentBinary, ["--version"], {
57
+ stdio: "ignore",
58
+ }).status === 0);
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ async function startCodexMockHandle(script) {
65
+ const mock = await (0, mock_model_js_1.startCodexMock)(script.map((t) => ({ text: t.text ?? "" })));
66
+ return {
67
+ url: mock.url,
68
+ get requests() {
69
+ // Codex requests carry only the flattened prompt; adapt to ModelRequest.
70
+ return mock.requests.map((r) => ({
71
+ system: "",
72
+ messages: [{ role: "user", text: r.prompt }],
73
+ }));
74
+ },
75
+ get count() {
76
+ return mock.requests.length;
77
+ },
78
+ close: () => mock.close(),
79
+ };
80
+ }
81
+ exports.codexDriver = {
82
+ runtime: runtime_js_1.codexRuntime,
83
+ buildArgs: buildCodexArgs,
84
+ startMock: startCodexMockHandle,
85
+ parseRun: parseCodexRun,
86
+ available: codexAvailable,
87
+ };
88
+ /* v8 ignore stop */
89
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * codexHookProtocol — EXPERIMENTAL, internal-only. Codex's hook wire protocol.
3
+ * Finding: it is essentially IDENTICAL to Claude Code's (exit 2 / `decision:block`
4
+ * / `permissionDecision:deny`) — the thin `HookProtocol` port was the right call.
5
+ * The genuine deltas are the env vars a hook receives + the TOML config format
6
+ * (the latter lives in PluginLayout.settingsFormat, not here).
7
+ */
8
+ import type { HookProtocol } from "../../core/hook-protocol.js";
9
+ export declare const codexHookProtocol: HookProtocol;
10
+ //# sourceMappingURL=hook-protocol.d.ts.map
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.codexHookProtocol = void 0;
4
+ exports.codexHookProtocol = {
5
+ name: "codex",
6
+ blockExitCode: 2,
7
+ denyDecisionValues: ["block", "deny"],
8
+ eventEnvVars: [
9
+ "session_id",
10
+ "cwd",
11
+ "hook_event_name",
12
+ "model",
13
+ "turn_id",
14
+ "permission_mode",
15
+ "PLUGIN_ROOT",
16
+ ],
17
+ };
18
+ //# sourceMappingURL=hook-protocol.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * codexLayout — EXPERIMENTAL, internal-only. A prototype `PluginLayout` for
3
+ * Codex: `.codex/` conventions, `config.toml` (TOML) hooks, `AGENTS.md`,
4
+ * `${PLUGIN_ROOT}`. Validates the layout port + the settings-format axis against
5
+ * real Codex shapes. NOT exported / NOT registered.
6
+ *
7
+ * Findings surfaced by writing it (see research/codex-prototype-findings.md):
8
+ * - Codex has no separate JSON *manifest* — `config.toml` carries everything; we
9
+ * point `manifestPath` at it (its JSON parse simply fails → the loader falls
10
+ * through to the TOML `settingsPath`). The manifest field is CC-JSON-shaped.
11
+ * - MCP detection (`mcpConfigFile`/`mcpManifestKey`) is JSON-shaped, so it won't
12
+ * see Codex's `[mcp_servers]` TOML table — a known layout-port gap.
13
+ */
14
+ import type { PluginLayout } from "../../core/layout.js";
15
+ export declare const codexLayout: PluginLayout;
16
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.codexLayout = void 0;
4
+ exports.codexLayout = {
5
+ name: "codex",
6
+ manifestPath: ".codex/config.toml",
7
+ hooksConventionPath: ".codex/hooks.json",
8
+ settingsPath: ".codex/config.toml",
9
+ settingsFormat: "toml",
10
+ instructionFile: "AGENTS.md",
11
+ surfaceDirs: ["skills", "prompts"],
12
+ materializeRoot: ".codex",
13
+ pluginRootToken: "${PLUGIN_ROOT}",
14
+ mcpConfigFile: ".mcp.json",
15
+ mcpManifestKey: "mcp_servers",
16
+ intraRefDirs: ["skills", "prompts", "hooks"],
17
+ };
18
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1,52 @@
1
+ /** One scripted assistant turn: the final text answer codex should emit. */
2
+ export interface CodexTurn {
3
+ /** Final text answer for this turn. */
4
+ readonly text: string;
5
+ }
6
+ /**
7
+ * One `/v1/responses` request the mock received, parsed for assertions: the
8
+ * user's prompt (last `input` item with `role:"user"`), the requested model,
9
+ * and the declared function-tool names.
10
+ */
11
+ export interface CodexRequest {
12
+ readonly prompt: string;
13
+ readonly model: string;
14
+ readonly toolNames: readonly string[];
15
+ }
16
+ export interface CodexMockHandle {
17
+ readonly url: string;
18
+ readonly port: number;
19
+ /** Every `/v1/responses` request the mock received, in order. */
20
+ readonly requests: readonly CodexRequest[];
21
+ close(): Promise<void>;
22
+ }
23
+ /**
24
+ * Render the PROVEN OpenAI-Responses SSE event sequence for a single assistant
25
+ * text message — `response.created` through `response.completed`. This exact
26
+ * sequence is confirmed to make real `codex` complete a turn. Pure and exported
27
+ * so the renderer is testable without the HTTP server.
28
+ */
29
+ export declare function renderResponsesSSE(text: string, opts?: {
30
+ model?: string;
31
+ }): string;
32
+ /**
33
+ * Parse a Responses request body — the user's prompt (the LAST `input` item
34
+ * with `role:"user"`, joining its `content[].text`), the requested `model`, and
35
+ * the declared function-tool names. Tolerates malformed JSON (returns an empty
36
+ * prompt). Pure and exported so request capture is testable without the server.
37
+ */
38
+ export declare function parseResponsesRequest(body: string): {
39
+ prompt: string;
40
+ model: string;
41
+ toolNames: string[];
42
+ };
43
+ /**
44
+ * Start the scripted Codex Responses mock on a free port. Each
45
+ * `POST /v1/responses` consumes the next scripted turn (the last turn repeats if
46
+ * codex asks for more); HEAD and any other path get `{}`. Resolves to a handle
47
+ * with the base `url`, the `port`, the recorded `requests`, and `close()`.
48
+ */
49
+ export declare function startCodexMock(script: readonly CodexTurn[], opts?: {
50
+ onRequest?: (req: CodexRequest) => void;
51
+ }): Promise<CodexMockHandle>;
52
+ //# sourceMappingURL=mock-model.d.ts.map
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderResponsesSSE = renderResponsesSSE;
7
+ exports.parseResponsesRequest = parseResponsesRequest;
8
+ exports.startCodexMock = startCodexMock;
9
+ /**
10
+ * codex mock-model — EXPERIMENTAL, internal-only. A scripted, deterministic
11
+ * OpenAI **Responses API** mock that real `codex` completes a turn against.
12
+ *
13
+ * Codex speaks the Responses API: a turn-consuming request hits
14
+ * `POST /v1/responses` with `Accept: text/event-stream`, and the mock answers
15
+ * with the proven `response.created → … → response.completed` SSE sequence that
16
+ * makes codex emit one assistant text message. There is no count-tokens
17
+ * endpoint. This is the Codex-side analogue of the Anthropic Messages mock in
18
+ * `src/adapters/claude-code/mock-model.ts` (`startMock`).
19
+ *
20
+ * The wire format here is NOT guessed — it is PROVEN end-to-end against real
21
+ * `codex` (codex-cli 0.139.0) via the `-c model_providers.mock.*` recipe in
22
+ * `src/adapters/codex/runtime.ts` (`codexMockArgs`/`codexMockEnv`).
23
+ */
24
+ const node_http_1 = __importDefault(require("node:http"));
25
+ const MSG_ID = "msg_mock_1";
26
+ const RESP_ID = "resp_mock_1";
27
+ /**
28
+ * Render the PROVEN OpenAI-Responses SSE event sequence for a single assistant
29
+ * text message — `response.created` through `response.completed`. This exact
30
+ * sequence is confirmed to make real `codex` complete a turn. Pure and exported
31
+ * so the renderer is testable without the HTTP server.
32
+ */
33
+ function renderResponsesSSE(text, opts = {}) {
34
+ const model = opts.model ?? "gpt-5-codex";
35
+ const response = (status, output) => ({
36
+ id: RESP_ID,
37
+ object: "response",
38
+ status,
39
+ model,
40
+ output,
41
+ usage: null,
42
+ });
43
+ const completedItem = {
44
+ type: "message",
45
+ id: MSG_ID,
46
+ status: "completed",
47
+ role: "assistant",
48
+ content: [{ type: "output_text", text }],
49
+ };
50
+ const events = [
51
+ {
52
+ type: "response.created",
53
+ data: { response: response("in_progress", []) },
54
+ },
55
+ {
56
+ type: "response.in_progress",
57
+ data: { response: response("in_progress", []) },
58
+ },
59
+ {
60
+ type: "response.output_item.added",
61
+ data: {
62
+ output_index: 0,
63
+ item: {
64
+ type: "message",
65
+ id: MSG_ID,
66
+ status: "in_progress",
67
+ role: "assistant",
68
+ content: [],
69
+ },
70
+ },
71
+ },
72
+ {
73
+ type: "response.content_part.added",
74
+ data: {
75
+ item_id: MSG_ID,
76
+ output_index: 0,
77
+ content_index: 0,
78
+ part: { type: "output_text", text: "" },
79
+ },
80
+ },
81
+ {
82
+ type: "response.output_text.delta",
83
+ data: { item_id: MSG_ID, output_index: 0, content_index: 0, delta: text },
84
+ },
85
+ {
86
+ type: "response.output_text.done",
87
+ data: { item_id: MSG_ID, output_index: 0, content_index: 0, text },
88
+ },
89
+ {
90
+ type: "response.content_part.done",
91
+ data: {
92
+ item_id: MSG_ID,
93
+ output_index: 0,
94
+ content_index: 0,
95
+ part: { type: "output_text", text },
96
+ },
97
+ },
98
+ {
99
+ type: "response.output_item.done",
100
+ data: { output_index: 0, item: completedItem },
101
+ },
102
+ {
103
+ type: "response.completed",
104
+ data: { response: response("completed", [completedItem]) },
105
+ },
106
+ ];
107
+ return events
108
+ .map((e) => `event: ${e.type}\n` +
109
+ `data: ${JSON.stringify({ type: e.type, ...e.data })}\n\n`)
110
+ .join("");
111
+ }
112
+ function joinInputText(content) {
113
+ if (!Array.isArray(content))
114
+ return "";
115
+ return content
116
+ .map((c) => {
117
+ const t = c.text;
118
+ return typeof t === "string" ? t : "";
119
+ })
120
+ .join("");
121
+ }
122
+ /**
123
+ * Parse a Responses request body — the user's prompt (the LAST `input` item
124
+ * with `role:"user"`, joining its `content[].text`), the requested `model`, and
125
+ * the declared function-tool names. Tolerates malformed JSON (returns an empty
126
+ * prompt). Pure and exported so request capture is testable without the server.
127
+ */
128
+ function parseResponsesRequest(body) {
129
+ let parsed = {};
130
+ try {
131
+ parsed = JSON.parse(body);
132
+ }
133
+ catch {
134
+ return { prompt: "", model: "", toolNames: [] };
135
+ }
136
+ const input = Array.isArray(parsed.input)
137
+ ? parsed.input
138
+ : [];
139
+ const lastUser = [...input]
140
+ .reverse()
141
+ .find((item) => item.role === "user" && item.type === "message");
142
+ const prompt = lastUser ? joinInputText(lastUser.content) : "";
143
+ const model = typeof parsed.model === "string" ? parsed.model : "";
144
+ const toolNames = Array.isArray(parsed.tools)
145
+ ? parsed.tools
146
+ .map((t) => {
147
+ const tool = t;
148
+ if (typeof tool.name === "string")
149
+ return tool.name;
150
+ const fnName = tool.function?.name;
151
+ return typeof fnName === "string" ? fnName : "";
152
+ })
153
+ .filter((n) => n.length > 0)
154
+ : [];
155
+ return { prompt, model, toolNames };
156
+ }
157
+ /**
158
+ * Start the scripted Codex Responses mock on a free port. Each
159
+ * `POST /v1/responses` consumes the next scripted turn (the last turn repeats if
160
+ * codex asks for more); HEAD and any other path get `{}`. Resolves to a handle
161
+ * with the base `url`, the `port`, the recorded `requests`, and `close()`.
162
+ */
163
+ function startCodexMock(script, opts = {}) {
164
+ let i = 0;
165
+ const requests = [];
166
+ const server = node_http_1.default.createServer((req, res) => {
167
+ let body = "";
168
+ req.on("data", (c) => (body += c));
169
+ req.on("end", () => {
170
+ const url = req.url ?? "";
171
+ const isResponses = req.method === "POST" && url.includes("/v1/responses");
172
+ if (!isResponses) {
173
+ res.writeHead(200, { "content-type": "application/json" });
174
+ res.end("{}");
175
+ return;
176
+ }
177
+ const parsed = parseResponsesRequest(body);
178
+ requests.push(parsed);
179
+ opts.onRequest?.(parsed);
180
+ const turn = script[Math.min(i, script.length - 1)] ?? { text: "" };
181
+ i++;
182
+ const model = parsed.model || "gpt-5-codex";
183
+ res.writeHead(200, {
184
+ "content-type": "text/event-stream",
185
+ "cache-control": "no-cache",
186
+ connection: "keep-alive",
187
+ });
188
+ res.write(renderResponsesSSE(turn.text, { model }));
189
+ res.end();
190
+ });
191
+ });
192
+ return new Promise((resolve) => {
193
+ server.listen(0, "127.0.0.1", () => {
194
+ const { port } = server.address();
195
+ resolve({
196
+ url: `http://127.0.0.1:${String(port)}`,
197
+ port,
198
+ get requests() {
199
+ return requests;
200
+ },
201
+ close: () => new Promise((r) => {
202
+ server.close(() => {
203
+ r();
204
+ });
205
+ }),
206
+ });
207
+ });
208
+ });
209
+ }
210
+ //# sourceMappingURL=mock-model.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * codexModelMock — EXPERIMENTAL, internal-only. Codex speaks the OpenAI
3
+ * **Responses** API: a turn-consuming request hits `/v1/responses`, and there is
4
+ * no count-tokens endpoint. Finding: the `ModelMock` *descriptor* captures the
5
+ * wire facts cleanly; the SSE *renderer* (the `response.created → … →
6
+ * response.completed` event sequence + a Responses request-parser) is the
7
+ * deferred piece — built with the Codex transport tier, not declared here.
8
+ */
9
+ import type { ModelMock } from "../../core/model-mock.js";
10
+ export declare const codexModelMock: ModelMock;
11
+ //# sourceMappingURL=model-mock.d.ts.map
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.codexModelMock = void 0;
4
+ exports.codexModelMock = {
5
+ name: "codex",
6
+ wireApi: "openai-responses",
7
+ modelEndpoint: "/v1/responses",
8
+ // No count-tokens endpoint (countTokensEndpoint omitted).
9
+ };
10
+ //# sourceMappingURL=model-mock.js.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * codexRuntime — EXPERIMENTAL, internal-only. A prototype `HarnessRuntime` for
3
+ * Codex: spawn `codex`, and point it at a no-key mock.
4
+ *
5
+ * PROVEN wiring (validated against real codex-cli 0.139.0): the clean keyless
6
+ * route is NOT the built-in `OPENAI_BASE_URL` override the prototype guessed
7
+ * (the docs called that "messy") — it is a `[model_providers.mock]` provider
8
+ * defined inline via `-c` flags on `codex exec`. `codexMockArgs(baseUrl)`
9
+ * returns that flag array (the `wireMock` recipe as data) and `codexMockEnv()`
10
+ * returns the dummy-key env codex reads through it. So "point at the mock" is a
11
+ * flag-based *operation* here, not a single env var.
12
+ *
13
+ * The `HarnessRuntime` port still requires `modelBaseUrlEnv`/`modelApiKeyEnv`
14
+ * (the env-var transport axis), so we keep valid values for conformance — but
15
+ * codex's real wiring is the flag-based recipe below, not those env vars. The
16
+ * port's `wireMock(baseUrl)` surfaces that recipe as `{ args, env }` so the
17
+ * generalized `runHarnessTest` runner drives codex the same way it drives
18
+ * Claude Code; `codexMockArgs`/`codexMockEnv` remain exported (used by
19
+ * `wireMock` and by `mock-model.test.ts`).
20
+ */
21
+ import type { HarnessRuntime } from "../../core/runtime.js";
22
+ export declare const codexRuntime: HarnessRuntime;
23
+ /**
24
+ * The PROVEN `-c` flag array that points `codex exec` at a mock served at
25
+ * `baseUrl` (e.g. `http://127.0.0.1:PORT`) over the Responses wire API, keyless.
26
+ * Defines a `[model_providers.mock]` provider inline and selects it as the
27
+ * active model provider. `baseUrl` is suffixed with `/v1` (codex appends
28
+ * `/responses`). Combine with `codexMockEnv()` and the `codex exec` flags
29
+ * `--ignore-user-config --skip-git-repo-check --ephemeral
30
+ * --dangerously-bypass-approvals-and-sandbox -c model="gpt-5-codex"`.
31
+ */
32
+ export declare function codexMockArgs(baseUrl: string): string[];
33
+ /**
34
+ * The dummy-key env codex reads `model_providers.mock.env_key` through. Codex
35
+ * sends `Authorization: Bearer dummy-key`; the mock ignores it.
36
+ */
37
+ export declare function codexMockEnv(): Record<string, string>;
38
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.codexRuntime = void 0;
4
+ exports.codexMockArgs = codexMockArgs;
5
+ exports.codexMockEnv = codexMockEnv;
6
+ exports.codexRuntime = {
7
+ name: "codex",
8
+ agentBinary: "codex",
9
+ // Kept non-empty for HarnessRuntime conformance (the env-var transport axis).
10
+ // Codex's PROVEN keyless wiring is the flag-based codexMockArgs recipe, not
11
+ // this built-in-provider override.
12
+ modelBaseUrlEnv: "OPENAI_BASE_URL",
13
+ modelApiKeyEnv: "MOCK_KEY",
14
+ mockApiKey: "dummy-key",
15
+ /**
16
+ * Codex reaches the mock through the PROVEN keyless flag recipe (the
17
+ * `-c model_providers.mock.*` array), NOT a single base-URL env var. So
18
+ * `wireMock` carries args (the flags) AND env (the dummy key codex reads
19
+ * `model_providers.mock.env_key` through). The `wireMock` method unifies both
20
+ * transport axes (CC's env-only, Codex's flags) behind one call.
21
+ */
22
+ wireMock(baseUrl) {
23
+ return { args: codexMockArgs(baseUrl), env: codexMockEnv() };
24
+ },
25
+ };
26
+ /**
27
+ * The PROVEN `-c` flag array that points `codex exec` at a mock served at
28
+ * `baseUrl` (e.g. `http://127.0.0.1:PORT`) over the Responses wire API, keyless.
29
+ * Defines a `[model_providers.mock]` provider inline and selects it as the
30
+ * active model provider. `baseUrl` is suffixed with `/v1` (codex appends
31
+ * `/responses`). Combine with `codexMockEnv()` and the `codex exec` flags
32
+ * `--ignore-user-config --skip-git-repo-check --ephemeral
33
+ * --dangerously-bypass-approvals-and-sandbox -c model="gpt-5-codex"`.
34
+ */
35
+ function codexMockArgs(baseUrl) {
36
+ return [
37
+ "-c",
38
+ "model_provider=mock",
39
+ "-c",
40
+ 'model_providers.mock.name="mock"',
41
+ "-c",
42
+ `model_providers.mock.base_url="${baseUrl}/v1"`,
43
+ "-c",
44
+ 'model_providers.mock.wire_api="responses"',
45
+ "-c",
46
+ 'model_providers.mock.env_key="MOCK_KEY"',
47
+ "-c",
48
+ "model_providers.mock.requires_openai_auth=false",
49
+ "-c",
50
+ "model_providers.mock.request_max_retries=0",
51
+ "-c",
52
+ "model_providers.mock.stream_max_retries=0",
53
+ ];
54
+ }
55
+ /**
56
+ * The dummy-key env codex reads `model_providers.mock.env_key` through. Codex
57
+ * sends `Authorization: Bearer dummy-key`; the mock ignores it.
58
+ */
59
+ function codexMockEnv() {
60
+ return { MOCK_KEY: "dummy-key" };
61
+ }
62
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1,3 @@
1
+ import type { HarnessAdapter } from "../../core/adapter.js";
2
+ export declare const opencodeAdapter: HarnessAdapter;
3
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.opencodeAdapter = void 0;
4
+ /**
5
+ * opencodeAdapter — EXPERIMENTAL, internal-only prototype `HarnessAdapter`. It
6
+ * exists to PROVE the kit generalizes to the optional-transport-port shape: a
7
+ * harness that does pillar 1 AND is mockable (openai-compatible) BUT whose hooks
8
+ * are in-process JS/TS plugin modules, not shell processes. So it declares
9
+ * `shellHooks: false`, ships NO `hookProtocol`, and the conformance kit must
10
+ * accept it without demanding a fake one. This is the pillar-1-+-mockable-but-
11
+ * no-shell-hooks shape the capability gating was built for.
12
+ *
13
+ * It is deliberately NOT registered in `src/adapter-registry.ts` and NOT exported
14
+ * from any `vigiles/*` subpath, so the CLI never auto-detects OpenCode and
15
+ * consumers can't import it. Promote it (register + `vigiles/opencode` export +
16
+ * the deferred transport renderers) only when OpenCode support actually ships.
17
+ */
18
+ const node_fs_1 = require("node:fs");
19
+ const node_path_1 = require("node:path");
20
+ const dialect_js_1 = require("./dialect.js");
21
+ const layout_js_1 = require("./layout.js");
22
+ const runtime_js_1 = require("./runtime.js");
23
+ const model_mock_js_1 = require("./model-mock.js");
24
+ exports.opencodeAdapter = {
25
+ name: "opencode",
26
+ // Pillar 1 + mockable (openai-chat SSE), but hooks are in-process JS/TS plugin
27
+ // modules — no shell-hook tier, hence shellHooks:false and NO hookProtocol.
28
+ capabilities: {
29
+ referenceVerification: true,
30
+ harnessTesting: true,
31
+ shellHooks: false,
32
+ },
33
+ dialect: dialect_js_1.opencodeDialect,
34
+ layout: layout_js_1.opencodeLayout,
35
+ runtime: runtime_js_1.opencodeRuntime,
36
+ modelMock: model_mock_js_1.opencodeModelMock,
37
+ // No hookProtocol: OpenCode hooks are code modules, not shell processes.
38
+ detect(root) {
39
+ // An `opencode.json` is a strong signal; a bare AGENTS.md is weak (many
40
+ // harnesses read it). (Unused while unregistered — kept for symmetry.)
41
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, "opencode.json")))
42
+ return 3;
43
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, layout_js_1.opencodeLayout.instructionFile)))
44
+ return 1;
45
+ return 0;
46
+ },
47
+ };
48
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * opencodeDialect — EXPERIMENTAL, internal-only. A prototype `HarnessDialect` for
3
+ * OpenCode, built to validate that the format axis generalizes to a harness whose
4
+ * hooks are in-process JS/TS plugin modules rather than shell processes. NOT
5
+ * exported from `vigiles/*` and NOT registered in the adapter registry — it exists
6
+ * to be run through the conformance kit + real OpenCode-shaped fixtures.
7
+ */
8
+ import type { HarnessDialect } from "../../core/dialect.js";
9
+ export declare const opencodeDialect: HarnessDialect;
10
+ //# sourceMappingURL=dialect.d.ts.map
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.opencodeDialect = void 0;
4
+ exports.opencodeDialect = {
5
+ name: "opencode",
6
+ // OpenCode's lowercase built-in tool catalog (file/shell/search + task).
7
+ builtinAgentTools: [
8
+ "bash",
9
+ "edit",
10
+ "read",
11
+ "write",
12
+ "grep",
13
+ "glob",
14
+ "list",
15
+ "webfetch",
16
+ "task",
17
+ ],
18
+ // No documented "never available to a subagent" set — left empty.
19
+ neverAvailableTools: [],
20
+ mcpToolPattern: /^mcp__[a-z0-9_-]+__[a-z0-9_-]+$/i,
21
+ // OpenCode's event-bus names, NOT shell hook events: a plugin module subscribes
22
+ // to these in-process (no exit-code/env wire protocol → shellHooks:false).
23
+ hookEvents: [
24
+ "session.start",
25
+ "tool.execute.before",
26
+ "tool.execute.after",
27
+ "session.idle",
28
+ ],
29
+ // AGENTS.md native + CLAUDE.md fallback.
30
+ instructionTargets: ["AGENTS.md", "CLAUDE.md"],
31
+ pluginRootToken: "${OPENCODE_PLUGIN_ROOT}",
32
+ // OpenCode reads the minimal cross-tool SKILL.md frontmatter (name +
33
+ // description); the CC-only keys are not part of its format.
34
+ skillFrontmatter: "minimal",
35
+ };
36
+ //# sourceMappingURL=dialect.js.map