@kontextmind/kxm 0.6.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 (227) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/.kxm/README.md +14 -0
  3. package/.kxm/assets/README.md +5 -0
  4. package/.kxm/assets/retrospectives/README.md +5 -0
  5. package/.kxm/config/README.md +5 -0
  6. package/.kxm/config/agents.json +43 -0
  7. package/.kxm/config/env.example +56 -0
  8. package/.kxm/config/update.example.yaml +9 -0
  9. package/.kxm/config/workflows/fix.json +160 -0
  10. package/.kxm/config/workflows/jira-development.json +116 -0
  11. package/.kxm/config/workflows/provenance-quorum.json +150 -0
  12. package/.kxm/config/workflows/v04-dogfood.json +72 -0
  13. package/CHANGELOG.md +465 -0
  14. package/LICENSE +21 -0
  15. package/README.md +306 -0
  16. package/SECURITY.md +72 -0
  17. package/docs/README.md +48 -0
  18. package/docs/agent-communication-envelopes-and-gates.md +553 -0
  19. package/docs/architecture.md +242 -0
  20. package/docs/assignment-runner.md +241 -0
  21. package/docs/configuration.md +361 -0
  22. package/docs/continuous-improvement.md +114 -0
  23. package/docs/getting-started.md +253 -0
  24. package/docs/kxm-handbook.md +1090 -0
  25. package/docs/operations.md +205 -0
  26. package/docs/provenance-gates.md +291 -0
  27. package/docs/skills.md +45 -0
  28. package/docs/templates/README.md +95 -0
  29. package/docs/templates/adr.md +88 -0
  30. package/docs/templates/architecture.md +120 -0
  31. package/docs/templates/bug-fix.md +109 -0
  32. package/docs/templates/feature.md +108 -0
  33. package/docs/templates/handoff.md +72 -0
  34. package/docs/templates/postmortem.md +77 -0
  35. package/docs/templates/research.md +100 -0
  36. package/docs/templates/review.md +85 -0
  37. package/docs/templates/runbook.md +73 -0
  38. package/docs/templates/test-plan.md +87 -0
  39. package/docs/templates/test-report.md +72 -0
  40. package/docs/test-matrix.md +121 -0
  41. package/docs/troubleshooting.md +249 -0
  42. package/docs/vnext/README.md +62 -0
  43. package/docs/vnext/architecture.md +185 -0
  44. package/docs/vnext/effects-and-recovery.md +172 -0
  45. package/docs/vnext/lifecycles.md +235 -0
  46. package/docs/vnext/migration.md +220 -0
  47. package/docs/vnext/routing.md +184 -0
  48. package/docs/vnext/synchronization.md +172 -0
  49. package/docs/vnext/terminology.md +240 -0
  50. package/docs/vnext/validation.md +335 -0
  51. package/docs/webhook-workflows.md +240 -0
  52. package/docs/workflow-guide.md +1150 -0
  53. package/examples/README.md +102 -0
  54. package/examples/provenance-workflow.json +40 -0
  55. package/examples/requester.ts +30 -0
  56. package/examples/reviewer-agent.ts +29 -0
  57. package/examples/roundtrip.ts +46 -0
  58. package/examples/vnext/.kxm/agents/coordinator.yaml +16 -0
  59. package/examples/vnext/.kxm/agents/critic-1.yaml +16 -0
  60. package/examples/vnext/.kxm/agents/critic-2.yaml +15 -0
  61. package/examples/vnext/.kxm/agents/critic-3.yaml +15 -0
  62. package/examples/vnext/.kxm/agents/implementer.yaml +15 -0
  63. package/examples/vnext/.kxm/agents/planner.yaml +13 -0
  64. package/examples/vnext/.kxm/agents/reproducer.yaml +15 -0
  65. package/examples/vnext/.kxm/agents/reviewer.yaml +15 -0
  66. package/examples/vnext/.kxm/gates.yaml +8 -0
  67. package/examples/vnext/.kxm/models/critic-claude.yaml +11 -0
  68. package/examples/vnext/.kxm/models/critic-gemini.yaml +11 -0
  69. package/examples/vnext/.kxm/models/critic-grok.yaml +12 -0
  70. package/examples/vnext/.kxm/models/implementation.yaml +14 -0
  71. package/examples/vnext/.kxm/models/primary.yaml +17 -0
  72. package/examples/vnext/.kxm/prices.yaml +111 -0
  73. package/examples/vnext/.kxm/project/env.yaml +7 -0
  74. package/examples/vnext/.kxm/project.yaml +32 -0
  75. package/examples/vnext/.kxm/repo/repo.yaml +8 -0
  76. package/examples/vnext/.kxm/workflows/default.yaml +92 -0
  77. package/examples/vnext/.kxm/workflows/fix.yaml +376 -0
  78. package/examples/vnext/.kxm/workflows/improve.yaml +57 -0
  79. package/examples/vnext/README.md +53 -0
  80. package/examples/vnext/records/assignment-result-recorded.json +63 -0
  81. package/examples/vnext/records/assignment-result.json +46 -0
  82. package/examples/vnext/records/context-candidate.json +42 -0
  83. package/examples/vnext/records/delivery-manifest.json +66 -0
  84. package/examples/vnext/records/effect-uncertainty-resolved-sync.json +67 -0
  85. package/examples/vnext/records/effect-uncertainty-resolved.json +62 -0
  86. package/examples/vnext/records/run-created.json +54 -0
  87. package/examples/vnext/records/sync-event.json +65 -0
  88. package/examples/vnext/repositories/api/.kxm/repo/env.yaml +7 -0
  89. package/examples/vnext/repositories/api/.kxm/repo/repo.yaml +8 -0
  90. package/examples/vnext/repositories/web/.kxm/repo/repo.yaml +8 -0
  91. package/examples/workflow-signal.ts +63 -0
  92. package/package.json +129 -0
  93. package/plugins/kxm/.claude-plugin/plugin.json +73 -0
  94. package/plugins/kxm/.mcp.json +19 -0
  95. package/plugins/kxm/README.md +93 -0
  96. package/plugins/kxm/dist/cli.js +42853 -0
  97. package/plugins/kxm/dist/client.js +416 -0
  98. package/plugins/kxm/dist/core.js +1823 -0
  99. package/plugins/kxm/dist/extension.js +3797 -0
  100. package/plugins/kxm/dist/mcp-server.js +17104 -0
  101. package/plugins/kxm/dist/runtime.js +23361 -0
  102. package/plugins/kxm/dist/server.js +13640 -0
  103. package/plugins/kxm/dist/vnext-runtime-supervisor.js +21109 -0
  104. package/plugins/kxm/package.json +12 -0
  105. package/plugins/kxm/skills/kxm/SKILL.md +97 -0
  106. package/plugins/kxm/skills/kxm/references/protocol.md +103 -0
  107. package/plugins/kxm/skills/kxm-session/SKILL.md +53 -0
  108. package/plugins/kxm/src/arbiter.ts +355 -0
  109. package/plugins/kxm/src/artifacts-exist.ts +62 -0
  110. package/plugins/kxm/src/autocomplete.ts +236 -0
  111. package/plugins/kxm/src/cli.ts +3707 -0
  112. package/plugins/kxm/src/client.ts +614 -0
  113. package/plugins/kxm/src/commands.ts +1063 -0
  114. package/plugins/kxm/src/config.ts +290 -0
  115. package/plugins/kxm/src/context/providers.ts +101 -0
  116. package/plugins/kxm/src/context-packet.ts +332 -0
  117. package/plugins/kxm/src/context.ts +499 -0
  118. package/plugins/kxm/src/core.ts +6 -0
  119. package/plugins/kxm/src/database.ts +563 -0
  120. package/plugins/kxm/src/diagnostics.ts +184 -0
  121. package/plugins/kxm/src/envelope.ts +118 -0
  122. package/plugins/kxm/src/extension.ts +895 -0
  123. package/plugins/kxm/src/external-effects.ts +299 -0
  124. package/plugins/kxm/src/github-watch.ts +255 -0
  125. package/plugins/kxm/src/hub-binding.ts +160 -0
  126. package/plugins/kxm/src/hub.ts +2502 -0
  127. package/plugins/kxm/src/improve.ts +383 -0
  128. package/plugins/kxm/src/inbox.ts +10 -0
  129. package/plugins/kxm/src/kxm-install-kind.ts +113 -0
  130. package/plugins/kxm/src/kxm-update-config.ts +39 -0
  131. package/plugins/kxm/src/kxm-update.ts +238 -0
  132. package/plugins/kxm/src/local-snapshot.ts +406 -0
  133. package/plugins/kxm/src/logger.ts +198 -0
  134. package/plugins/kxm/src/mcp-server.ts +143 -0
  135. package/plugins/kxm/src/memory.ts +385 -0
  136. package/plugins/kxm/src/nous-pi.ts +287 -0
  137. package/plugins/kxm/src/nous-provider.ts +729 -0
  138. package/plugins/kxm/src/price-calc.ts +87 -0
  139. package/plugins/kxm/src/prices.ts +121 -0
  140. package/plugins/kxm/src/protocol.ts +172 -0
  141. package/plugins/kxm/src/recovery.ts +211 -0
  142. package/plugins/kxm/src/redact.ts +26 -0
  143. package/plugins/kxm/src/retrospective.ts +400 -0
  144. package/plugins/kxm/src/routing.ts +830 -0
  145. package/plugins/kxm/src/runtime.ts +9 -0
  146. package/plugins/kxm/src/server.ts +117 -0
  147. package/plugins/kxm/src/session-work.ts +571 -0
  148. package/plugins/kxm/src/session.ts +184 -0
  149. package/plugins/kxm/src/skills.ts +535 -0
  150. package/plugins/kxm/src/state.ts +326 -0
  151. package/plugins/kxm/src/store.ts +637 -0
  152. package/plugins/kxm/src/studio-layout.ts +268 -0
  153. package/plugins/kxm/src/suggest.ts +162 -0
  154. package/plugins/kxm/src/task-manager.ts +244 -0
  155. package/plugins/kxm/src/telemetry.ts +116 -0
  156. package/plugins/kxm/src/tui.ts +1046 -0
  157. package/plugins/kxm/src/vnext-bindings.ts +403 -0
  158. package/plugins/kxm/src/vnext-config.ts +1646 -0
  159. package/plugins/kxm/src/vnext-engine-artifacts.ts +86 -0
  160. package/plugins/kxm/src/vnext-engine-command.ts +533 -0
  161. package/plugins/kxm/src/vnext-engine-compile.ts +722 -0
  162. package/plugins/kxm/src/vnext-engine-evidence.ts +273 -0
  163. package/plugins/kxm/src/vnext-engine-fold.ts +1400 -0
  164. package/plugins/kxm/src/vnext-engine-gate-records.ts +583 -0
  165. package/plugins/kxm/src/vnext-engine-plan.ts +717 -0
  166. package/plugins/kxm/src/vnext-engine.ts +2458 -0
  167. package/plugins/kxm/src/vnext-gate-hash.ts +10 -0
  168. package/plugins/kxm/src/vnext-harness.ts +1142 -0
  169. package/plugins/kxm/src/vnext-init.ts +430 -0
  170. package/plugins/kxm/src/vnext-migrate.ts +1848 -0
  171. package/plugins/kxm/src/vnext-oneshot-producer.ts +424 -0
  172. package/plugins/kxm/src/vnext-permission.ts +936 -0
  173. package/plugins/kxm/src/vnext-pi-producer.ts +628 -0
  174. package/plugins/kxm/src/vnext-repair.ts +1094 -0
  175. package/plugins/kxm/src/vnext-runtime-owner.ts +320 -0
  176. package/plugins/kxm/src/vnext-runtime-store.ts +1560 -0
  177. package/plugins/kxm/src/vnext-runtime-supervisor.ts +586 -0
  178. package/plugins/kxm/src/vnext-runtime.ts +663 -0
  179. package/plugins/kxm/src/vnext-template.ts +247 -0
  180. package/plugins/kxm/src/wiki.ts +313 -0
  181. package/plugins/kxm/src/workflow.ts +1548 -0
  182. package/schemas/vnext/README.md +46 -0
  183. package/schemas/vnext/agent.schema.json +40 -0
  184. package/schemas/vnext/assignment-result.schema.json +66 -0
  185. package/schemas/vnext/backup-manifest.schema.json +89 -0
  186. package/schemas/vnext/candidate.schema.json +109 -0
  187. package/schemas/vnext/common.schema.json +422 -0
  188. package/schemas/vnext/context-candidate.schema.json +76 -0
  189. package/schemas/vnext/context-packet.schema.json +192 -0
  190. package/schemas/vnext/delivery-manifest.schema.json +159 -0
  191. package/schemas/vnext/environment.schema.json +66 -0
  192. package/schemas/vnext/gate-registry.schema.json +109 -0
  193. package/schemas/vnext/handoff-manifest.schema.json +146 -0
  194. package/schemas/vnext/init-operation.schema.json +61 -0
  195. package/schemas/vnext/local-repository-bindings.schema.json +30 -0
  196. package/schemas/vnext/memory-record.schema.json +45 -0
  197. package/schemas/vnext/migration-decision.schema.json +26 -0
  198. package/schemas/vnext/migration-plan.schema.json +123 -0
  199. package/schemas/vnext/migration-receipt.schema.json +52 -0
  200. package/schemas/vnext/model.schema.json +42 -0
  201. package/schemas/vnext/permission-diff.schema.json +57 -0
  202. package/schemas/vnext/prices.schema.json +115 -0
  203. package/schemas/vnext/project.schema.json +85 -0
  204. package/schemas/vnext/repository.schema.json +24 -0
  205. package/schemas/vnext/run-event.schema.json +460 -0
  206. package/schemas/vnext/session-brief.schema.json +153 -0
  207. package/schemas/vnext/sync-event.schema.json +234 -0
  208. package/schemas/vnext/template-provenance.schema.json +38 -0
  209. package/schemas/vnext/workflow.schema.json +248 -0
  210. package/scripts/assignment-run.d.mts +354 -0
  211. package/scripts/assignment-run.mjs +4451 -0
  212. package/scripts/build-runtime.mjs +56 -0
  213. package/scripts/check-generated.mjs +77 -0
  214. package/scripts/check-versions.mjs +34 -0
  215. package/scripts/emit-codex-artifacts.d.mts +9 -0
  216. package/scripts/emit-codex-artifacts.mjs +91 -0
  217. package/scripts/harness-run.d.mts +83 -0
  218. package/scripts/harness-run.mjs +2095 -0
  219. package/scripts/kxm-hub.mjs +105 -0
  220. package/scripts/kxm-publish-npm.mjs +327 -0
  221. package/scripts/kxm-release-github.mjs +472 -0
  222. package/scripts/kxm-runtime-supervisor.mjs +7 -0
  223. package/scripts/kxm-worker.mjs +1127 -0
  224. package/scripts/kxm.mjs +27 -0
  225. package/scripts/roster-policy.d.mts +20 -0
  226. package/scripts/roster-policy.mjs +161 -0
  227. package/scripts/smoke-multi-pi.mjs +479 -0
@@ -0,0 +1,424 @@
1
+ import { spawn } from "node:child_process";
2
+ import {
3
+ BUILTIN_HARNESSES,
4
+ NATIVE_HARNESS_PROVIDERS,
5
+ probeHarnessAssignment,
6
+ type HarnessCatalogEntry,
7
+ type HarnessInventory,
8
+ } from "./vnext-harness.ts";
9
+ import {
10
+ calculateModelCost,
11
+ loadPriceCatalog,
12
+ type PriceCatalog,
13
+ } from "./prices.ts";
14
+ import {
15
+ registerTrustedProducer,
16
+ type VnextProducer,
17
+ type VnextProducerRequest,
18
+ type VnextProducerResult,
19
+ } from "./vnext-engine.ts";
20
+
21
+ export interface VnextOneShotProcessResult {
22
+ stdout: string;
23
+ stderr: string;
24
+ code: number | null;
25
+ error?: Error | undefined;
26
+ }
27
+
28
+ export type VnextOneShotSpawn = (
29
+ command: string,
30
+ args: readonly string[],
31
+ options: {
32
+ cwd?: string | undefined;
33
+ env?: NodeJS.ProcessEnv | undefined;
34
+ input?: string | undefined;
35
+ timeoutMs?: number | undefined;
36
+ signal?: AbortSignal | undefined;
37
+ },
38
+ ) => Promise<VnextOneShotProcessResult>;
39
+
40
+ export interface VnextOneShotProducerOptions {
41
+ projectRoot?: string | undefined;
42
+ defaultHarness?: string | undefined;
43
+ defaultModel?: string | undefined;
44
+ defaultProvider?: string | undefined;
45
+ inventory?: HarnessInventory | undefined;
46
+ catalog?: readonly HarnessCatalogEntry[] | undefined;
47
+ priceCatalog?: PriceCatalog | undefined;
48
+ env?: NodeJS.ProcessEnv | undefined;
49
+ timeoutMs?: number | undefined;
50
+ spawnProcess?: VnextOneShotSpawn | undefined;
51
+ probeHarness?: typeof probeHarnessAssignment | undefined;
52
+ resolveHarness?: ((agentId: string, runId: string) => string | undefined) | undefined;
53
+ resolveModel?: ((agentId: string, runId: string) => {
54
+ harness?: string | undefined;
55
+ provider?: string | undefined;
56
+ model?: string | undefined;
57
+ thinking?: string | undefined;
58
+ } | undefined) | undefined;
59
+ }
60
+
61
+ export interface VnextOneShotProducer extends VnextProducer {
62
+ readonly id: "oneshot";
63
+ produce(request: VnextProducerRequest): Promise<VnextProducerResult>;
64
+ close(): Promise<void>;
65
+ }
66
+
67
+ function determineOutcome(text: string, allowedOutcomes: readonly string[]): string {
68
+ const normalized = text.trim();
69
+ const jsonMatch = /"outcome"\s*:\s*"([^"]+)"/.exec(normalized);
70
+ if (jsonMatch && allowedOutcomes.includes(jsonMatch[1]!)) {
71
+ return jsonMatch[1]!;
72
+ }
73
+ for (const outcome of allowedOutcomes) {
74
+ const regex = new RegExp(`\\b${outcome}\\b`, "i");
75
+ if (regex.test(normalized)) {
76
+ return outcome;
77
+ }
78
+ }
79
+ if (allowedOutcomes.includes("passed")) return "passed";
80
+ return allowedOutcomes[0] ?? "completed";
81
+ }
82
+
83
+ export function defaultSpawn(
84
+ command: string,
85
+ args: readonly string[],
86
+ options: {
87
+ cwd?: string | undefined;
88
+ env?: NodeJS.ProcessEnv | undefined;
89
+ input?: string | undefined;
90
+ timeoutMs?: number | undefined;
91
+ signal?: AbortSignal | undefined;
92
+ },
93
+ ): Promise<VnextOneShotProcessResult> {
94
+ return new Promise((resolve) => {
95
+ let stdout = "";
96
+ let stderr = "";
97
+ let killed = false;
98
+
99
+ const child = spawn(command, [...args], {
100
+ cwd: options.cwd,
101
+ env: options.env ?? process.env,
102
+ stdio: ["pipe", "pipe", "pipe"],
103
+ windowsHide: true,
104
+ });
105
+
106
+ if (options.signal) {
107
+ if (options.signal.aborted) {
108
+ child.kill();
109
+ return resolve({ stdout: "", stderr: "aborted", code: null, error: new Error("process_aborted") });
110
+ }
111
+ const onAbort = () => {
112
+ killed = true;
113
+ child.kill();
114
+ };
115
+ options.signal.addEventListener("abort", onAbort, { once: true });
116
+ child.on("close", () => options.signal?.removeEventListener("abort", onAbort));
117
+ }
118
+
119
+ if (options.timeoutMs && options.timeoutMs > 0) {
120
+ const timer = setTimeout(() => {
121
+ killed = true;
122
+ child.kill();
123
+ }, options.timeoutMs);
124
+ child.on("close", () => clearTimeout(timer));
125
+ }
126
+
127
+ child.stdout.on("data", (chunk: Buffer | string) => {
128
+ stdout += chunk.toString();
129
+ });
130
+ child.stderr.on("data", (chunk: Buffer | string) => {
131
+ stderr += chunk.toString();
132
+ });
133
+
134
+ child.on("error", (err) => {
135
+ resolve({ stdout, stderr, code: null, error: err });
136
+ });
137
+
138
+ child.on("close", (code) => {
139
+ resolve({
140
+ stdout,
141
+ stderr,
142
+ code,
143
+ ...(killed ? { error: new Error("process_aborted") } : {}),
144
+ });
145
+ });
146
+
147
+ if (options.input !== undefined) {
148
+ child.stdin.write(options.input);
149
+ child.stdin.end();
150
+ } else {
151
+ child.stdin.end();
152
+ }
153
+ });
154
+ }
155
+
156
+ export function createVnextOneShotProducer(options: VnextOneShotProducerOptions = {}): VnextOneShotProducer {
157
+ const defaultHarness = options.defaultHarness ?? "claude";
158
+
159
+ function resolveHarnessForRequest(request: VnextProducerRequest): string {
160
+ if (request.harness) return request.harness;
161
+ if (options.resolveHarness) {
162
+ const resolved = options.resolveHarness(request.agentId, request.runId);
163
+ if (resolved) return resolved;
164
+ }
165
+ if (options.resolveModel) {
166
+ const resolved = options.resolveModel(request.agentId, request.runId);
167
+ if (resolved?.harness) return resolved.harness;
168
+ }
169
+ return defaultHarness;
170
+ }
171
+
172
+ function parseModelString(spec: string, harness: string): { provider: string; model: string } {
173
+ const trimmed = spec.trim();
174
+ if (trimmed.includes("/")) {
175
+ const idx = trimmed.indexOf("/");
176
+ return { provider: trimmed.slice(0, idx).toLowerCase(), model: trimmed.slice(idx + 1) };
177
+ }
178
+ const fallbackProvider = NATIVE_HARNESS_PROVIDERS[harness] ?? options.defaultProvider ?? "anthropic";
179
+ return { provider: fallbackProvider, model: trimmed };
180
+ }
181
+
182
+ function resolveModelForRequest(
183
+ request: VnextProducerRequest,
184
+ harness: string,
185
+ ): { provider: string; model: string; thinking?: string | undefined } {
186
+ if (request.model) {
187
+ const parsed = parseModelString(request.model, harness);
188
+ return {
189
+ provider: request.provider?.toLowerCase() ?? parsed.provider,
190
+ model: parsed.model,
191
+ thinking: request.thinking,
192
+ };
193
+ }
194
+ if (options.resolveModel) {
195
+ const resolved = options.resolveModel(request.agentId, request.runId);
196
+ if (resolved && resolved.model) {
197
+ const parsed = parseModelString(resolved.model, harness);
198
+ return {
199
+ provider: resolved.provider?.toLowerCase() ?? parsed.provider,
200
+ model: parsed.model,
201
+ thinking: resolved.thinking,
202
+ };
203
+ }
204
+ }
205
+ const defaultModel = options.defaultModel ?? (harness === "codex" ? "gpt-5.6-sol" : "claude-3-7-sonnet");
206
+ const parsed = parseModelString(defaultModel, harness);
207
+ return { provider: parsed.provider, model: parsed.model, thinking: request.thinking };
208
+ }
209
+
210
+ function checkAuth(harness: string, provider: string, model: string): void {
211
+ if (options.inventory) {
212
+ const entry = options.inventory.harnesses.find((h) => h.id === harness);
213
+ if (!entry || !entry.detected || entry.authenticated !== true) {
214
+ throw new Error(`${harness}_not_authenticated: ${harness} harness not authenticated in inventory`);
215
+ }
216
+ }
217
+
218
+ const probeFn = options.probeHarness ?? probeHarnessAssignment;
219
+ const probe = probeFn({
220
+ harness,
221
+ provider,
222
+ model,
223
+ env: options.env,
224
+ });
225
+
226
+ if (!probe.detected) {
227
+ throw new Error(`${harness}_not_authenticated: ${harness} harness not detected (${probe.issues.join(", ")})`);
228
+ }
229
+ if (probe.authenticated !== true) {
230
+ throw new Error(`${harness}_not_authenticated: ${harness} not authenticated (${probe.issues.join(", ")})`);
231
+ }
232
+ }
233
+
234
+ const producer: VnextOneShotProducer = {
235
+ id: "oneshot",
236
+
237
+ async produce(request: VnextProducerRequest): Promise<VnextProducerResult> {
238
+ const startTime = Date.now();
239
+ const harness = resolveHarnessForRequest(request);
240
+ const resolved = resolveModelForRequest(request, harness);
241
+
242
+ // Preflight fail-closed check
243
+ checkAuth(harness, resolved.provider, resolved.model);
244
+
245
+ const catalogEntry = (options.catalog ?? BUILTIN_HARNESSES).find((h) => h.id === harness);
246
+ if (!catalogEntry || !catalogEntry.oneShot) {
247
+ const hint = harness === "gemini" ? " (Gemini CLI is deprecated; use agy for Google models)" : "";
248
+ throw new Error(`oneshot_harness_unsupported: ${harness}${hint}`);
249
+ }
250
+
251
+ const command = catalogEntry.commands[0] ?? harness;
252
+ const oneShot = catalogEntry.oneShot;
253
+
254
+ let args: string[];
255
+ if (harness === "claude") {
256
+ args = [
257
+ "-p",
258
+ "--model",
259
+ resolved.model,
260
+ ...(resolved.thinking ? ["--effort", resolved.thinking] : []),
261
+ "--output-format",
262
+ "json",
263
+ ];
264
+ } else if (harness === "codex") {
265
+ args = [
266
+ "exec",
267
+ "-m",
268
+ resolved.model,
269
+ ...(resolved.thinking ? ["-c", `model_reasoning_effort="${resolved.thinking}"`] : []),
270
+ "--json",
271
+ "-",
272
+ ];
273
+ } else if (harness === "kimi") {
274
+ args = [
275
+ "--output-format",
276
+ "stream-json",
277
+ "-m",
278
+ resolved.model,
279
+ "-p",
280
+ ];
281
+ } else if (harness === "agy") {
282
+ args = [
283
+ "--output-format",
284
+ "json",
285
+ ...(resolved.thinking ? ["--effort", resolved.thinking] : []),
286
+ "--model",
287
+ resolved.model,
288
+ "-p",
289
+ ];
290
+ } else {
291
+ args = [
292
+ ...oneShot.argv,
293
+ "--model",
294
+ resolved.model,
295
+ ...(resolved.thinking ? ["--thinking", resolved.thinking] : []),
296
+ ];
297
+ }
298
+
299
+ const promptMessage = request.prompt
300
+ ?? `Execute step ${request.stepId} (attempt ${request.stepAttempt}) for agent ${request.agentId}. Allowed outcomes: ${request.allowedOutcomes.join(", ")}.`;
301
+
302
+ let input: string | undefined;
303
+ if (oneShot.promptVia === "stdin") {
304
+ input = promptMessage;
305
+ } else {
306
+ args.push(promptMessage);
307
+ }
308
+
309
+ if (request.signal?.aborted) {
310
+ const outcome = request.allowedOutcomes.includes("cancelled")
311
+ ? "cancelled"
312
+ : (request.allowedOutcomes.includes("failed") ? "failed" : request.allowedOutcomes[0]!);
313
+ return {
314
+ outcome,
315
+ costBasis: "unknown",
316
+ costUsd: null,
317
+ latencyMs: 0,
318
+ harness,
319
+ provider: resolved.provider,
320
+ requestedModel: resolved.model,
321
+ effectiveModel: resolved.model,
322
+ agentRole: request.agentRole ?? request.agentId,
323
+ };
324
+ }
325
+
326
+ const spawnFn = options.spawnProcess ?? defaultSpawn;
327
+ const procResult = await spawnFn(command, args, {
328
+ cwd: options.projectRoot ?? process.cwd(),
329
+ env: options.env,
330
+ input,
331
+ timeoutMs: options.timeoutMs,
332
+ signal: request.signal,
333
+ });
334
+
335
+ if (request.signal?.aborted || procResult.error?.message === "process_aborted") {
336
+ const outcome = request.allowedOutcomes.includes("cancelled")
337
+ ? "cancelled"
338
+ : (request.allowedOutcomes.includes("failed") ? "failed" : request.allowedOutcomes[0]!);
339
+ return {
340
+ outcome,
341
+ costBasis: "unknown",
342
+ costUsd: null,
343
+ latencyMs: Math.max(0, Date.now() - startTime),
344
+ harness,
345
+ provider: resolved.provider,
346
+ requestedModel: resolved.model,
347
+ effectiveModel: resolved.model,
348
+ agentRole: request.agentRole ?? request.agentId,
349
+ };
350
+ }
351
+
352
+ const parsed = oneShot.usageParser(procResult.stdout, procResult.stderr);
353
+ let outcome: string;
354
+ if (parsed.isError) {
355
+ outcome = request.allowedOutcomes.includes("failed") ? "failed" : request.allowedOutcomes[0]!;
356
+ } else {
357
+ outcome = determineOutcome(parsed.text || procResult.stdout, request.allowedOutcomes);
358
+ }
359
+
360
+ const latencyMs = Math.max(0, Date.now() - startTime);
361
+ const usage = parsed.usage;
362
+ const tokensIn = typeof usage?.tokensIn === "number" ? usage.tokensIn : null;
363
+ const tokensOut = typeof usage?.tokensOut === "number" ? usage.tokensOut : null;
364
+ const cacheReadTokens = typeof usage?.cacheReadTokens === "number" ? usage.cacheReadTokens : null;
365
+ const cacheWriteTokens = typeof usage?.cacheWriteTokens === "number" ? usage.cacheWriteTokens : null;
366
+ const contextTokens = typeof usage?.contextTokens === "number" ? usage.contextTokens : tokensIn;
367
+
368
+ // Price calculation: metered when price row exists, else unmetered for subscription with priceRef
369
+ const catalog = options.priceCatalog ?? loadPriceCatalog(options.projectRoot ?? process.cwd());
370
+ let costBasis: "metered" | "unmetered" | "unknown" = "unknown";
371
+ let costUsd: number | null = null;
372
+ let priceRef: string | undefined;
373
+
374
+ if (catalog) {
375
+ const calculated = calculateModelCost(catalog, {
376
+ model: resolved.model,
377
+ provider: resolved.provider,
378
+ tokensIn,
379
+ tokensOut,
380
+ cacheReadTokens,
381
+ cacheWriteTokens,
382
+ contextTokens,
383
+ });
384
+ if (calculated) {
385
+ costBasis = "metered";
386
+ costUsd = calculated.costUsd;
387
+ priceRef = calculated.priceRef;
388
+ }
389
+ }
390
+
391
+ if (costBasis === "unknown" && (harness === "claude" || harness === "codex" || harness === "agy")) {
392
+ costBasis = "unmetered";
393
+ costUsd = null;
394
+ priceRef = `subscription:${harness}`;
395
+ }
396
+
397
+ return {
398
+ outcome,
399
+ costBasis,
400
+ costUsd,
401
+ tokensIn,
402
+ tokensOut,
403
+ cacheReadTokens,
404
+ cacheWriteTokens,
405
+ contextTokens,
406
+ latencyMs,
407
+ harness,
408
+ provider: resolved.provider,
409
+ requestedModel: resolved.model,
410
+ effectiveModel: resolved.model,
411
+ ...(resolved.thinking !== undefined ? { thinking: resolved.thinking } : {}),
412
+ agentRole: request.agentRole ?? request.agentId,
413
+ ...(priceRef !== undefined ? { priceRef } : {}),
414
+ };
415
+ },
416
+
417
+ async close(): Promise<void> {
418
+ // One-shot producer holds no persistent process pools
419
+ },
420
+ };
421
+
422
+ registerTrustedProducer(producer);
423
+ return producer;
424
+ }