@zhivex-ai/harness 1.1.1 → 1.2.0-rc.13

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 (119) hide show
  1. package/CHANGELOG.md +160 -0
  2. package/README.md +14 -7
  3. package/SECURITY.md +1 -1
  4. package/SUPPORT.md +3 -3
  5. package/benchmarks/README.md +17 -0
  6. package/contracts/public-api.json +95 -5
  7. package/contracts/stable-api-signatures.json +38 -38
  8. package/dist/acp-cli.d.ts +2 -0
  9. package/dist/acp-cli.js +15938 -0
  10. package/dist/cli/arguments.d.ts +7 -1
  11. package/dist/cli/cli-credentials.d.ts +6 -1
  12. package/dist/cli/cli-options.d.ts +1 -1
  13. package/dist/cli/cli-profiles.d.ts +15 -0
  14. package/dist/cli/cli-stream.d.ts +3 -3
  15. package/dist/cli/console/console-budget.d.ts +9 -0
  16. package/dist/cli/console/console-commands.d.ts +3 -3
  17. package/dist/cli/console/console-compaction.d.ts +18 -0
  18. package/dist/cli/console/console-input.d.ts +22 -3
  19. package/dist/cli/console/console-navigation.d.ts +3 -0
  20. package/dist/cli/console/console-presentation.d.ts +9 -2
  21. package/dist/cli/console/console-progress.d.ts +6 -0
  22. package/dist/cli/console/console-reasoning.d.ts +3 -0
  23. package/dist/cli/doctor.d.ts +2 -2
  24. package/dist/cli/presentation.d.ts +8 -1
  25. package/dist/cli/qwen-connection.d.ts +23 -0
  26. package/dist/cli/run-document.d.ts +4 -4
  27. package/dist/cli/terminal/activity-history.d.ts +15 -0
  28. package/dist/cli/terminal/session-grants.d.ts +4 -0
  29. package/dist/cli/terminal/terminal-markdown.d.ts +7 -4
  30. package/dist/cli/terminal/terminal-table.d.ts +6 -0
  31. package/dist/cli/terminal/terminal-ui.d.ts +3 -0
  32. package/dist/cli/terminal/tool-activity.d.ts +21 -10
  33. package/dist/cli/tool-execution.d.ts +1 -0
  34. package/dist/cli-3jmy4h5h.js +5798 -0
  35. package/dist/cli-entry-1bx83sw4.js +4 -0
  36. package/dist/cli-entry-427869g9.js +2009 -0
  37. package/dist/cli-entry-64pdp3k5.js +17 -0
  38. package/dist/cli-entry-b1mfvmxe.js +376 -0
  39. package/dist/cli-entry-c2f58zj2.js +13602 -0
  40. package/dist/cli-entry.d.ts +2 -0
  41. package/dist/cli.d.ts +1 -0
  42. package/dist/cli.js +15 -19168
  43. package/dist/client/acp-stdio.d.ts +9 -0
  44. package/dist/client/acp.d.ts +18 -0
  45. package/dist/client/index.d.ts +2 -0
  46. package/dist/client/json-contracts.d.ts +22 -8
  47. package/dist/config-1k9t4jhh.js +59 -0
  48. package/dist/context/adaptive-compaction.d.ts +2 -0
  49. package/dist/context/compaction.d.ts +3 -1
  50. package/dist/context/qwen-reasoning.d.ts +5 -0
  51. package/dist/context/scoped-context.d.ts +61 -0
  52. package/dist/context/semantic-compaction.d.ts +21 -0
  53. package/dist/context/task-memory.d.ts +13 -8
  54. package/dist/execution/execution-environment.d.ts +5 -0
  55. package/dist/execution/oci-observability.d.ts +2 -0
  56. package/dist/execution/patch-diagnostics.d.ts +6 -0
  57. package/dist/hostile-repository-demo.js +4614 -1930
  58. package/dist/index.d.ts +13 -1
  59. package/dist/index.js +16435 -12486
  60. package/dist/integrations/mcp.d.ts +23 -3
  61. package/dist/models/catalog.d.ts +76 -0
  62. package/dist/models/compaction-recommendations.d.ts +28 -0
  63. package/dist/models/sdk-catalog-adapter.d.ts +7 -0
  64. package/dist/persistence/operations.d.ts +10 -6
  65. package/dist/persistence/state-backup-stream.d.ts +4 -0
  66. package/dist/persistence/state-backup.d.ts +52 -0
  67. package/dist/persistence/state-gitignore.d.ts +2 -0
  68. package/dist/persistence/workspace-checkpoints.d.ts +149 -0
  69. package/dist/providers/meta-continuation.d.ts +1 -1
  70. package/dist/providers/meta-errors.d.ts +2 -0
  71. package/dist/providers/meta-replay.d.ts +2 -0
  72. package/dist/providers/qwen-context.d.ts +2 -0
  73. package/dist/providers/qwen-hosted-policy.d.ts +5 -0
  74. package/dist/providers/reasoning.d.ts +18 -0
  75. package/dist/runtime/config.d.ts +12 -3
  76. package/dist/runtime/context-runtime.d.ts +12 -0
  77. package/dist/runtime/error-diagnostics.d.ts +641 -0
  78. package/dist/runtime/harness.d.ts +13 -4
  79. package/dist/runtime/model-edit-references.d.ts +8 -0
  80. package/dist/runtime/oci-delivery.d.ts +17 -0
  81. package/dist/runtime/progress-monitor.d.ts +33 -0
  82. package/dist/runtime/provider-diagnostics.d.ts +35 -0
  83. package/dist/runtime/runtime-checkpoints.d.ts +3 -0
  84. package/dist/runtime/runtime-diagnostics.d.ts +5 -5
  85. package/dist/runtime/runtime-policy.d.ts +14 -6
  86. package/dist/runtime/time-to-safe-fix.d.ts +308 -1
  87. package/dist/runtime/tool-execution.d.ts +11 -0
  88. package/dist/runtime/tool-policy.d.ts +55 -0
  89. package/dist/runtime/tool-scheduling.d.ts +5 -0
  90. package/dist/runtime/usage-ledger.d.ts +1 -0
  91. package/dist/service-cli.js +9422 -6622
  92. package/dist/service-client-cli-yx56n0hc.js +1086 -0
  93. package/dist/tools/dependency-read.d.ts +63 -0
  94. package/dist/tools/execution.d.ts +8 -8
  95. package/dist/tools/workspace.d.ts +31 -15
  96. package/dist/verify-historical-migrations.js +5272 -2437
  97. package/dist/workspace/file-security.d.ts +6 -0
  98. package/dist/zhx.js +15 -19168
  99. package/docs/ACP.md +55 -0
  100. package/docs/CLI.md +292 -29
  101. package/docs/CONTEXT_ENGINEERING.md +184 -20
  102. package/docs/CREDENTIALS.md +38 -6
  103. package/docs/DURABLE_OPERATIONS.md +10 -0
  104. package/docs/EXTENSIBILITY.md +101 -4
  105. package/docs/FIRST_USE.md +7 -5
  106. package/docs/MODEL_CATALOG.md +52 -1
  107. package/docs/README.md +1 -0
  108. package/docs/REPOSITORY_EDITING.md +42 -0
  109. package/docs/STABILITY.md +2 -0
  110. package/docs/SUPPORT_MATRIX_1_1.md +17 -0
  111. package/docs/USAGE.md +9 -0
  112. package/docs/WORKSPACE_CHECKPOINTS.md +27 -0
  113. package/docs/images/cli-conversation.png +0 -0
  114. package/docs/support-matrix-1.1.json +29 -0
  115. package/evaluations/golden-expectations.json +5 -0
  116. package/evaluations/representative-assembly-matrix.json +145 -1
  117. package/evaluations/representative-repositories-v2.jsonl +7 -0
  118. package/examples/README.md +2 -2
  119. package/package.json +19 -13
@@ -0,0 +1,2009 @@
1
+ // src/providers/reasoning.ts
2
+ import { z as z2 } from "zod";
3
+ import { wrapLanguageModel as wrapLanguageModel4 } from "@zhivex-ai/core";
4
+
5
+ // src/providers/qwen-hosted-policy.ts
6
+ import { wrapLanguageModel } from "@zhivex-ai/core";
7
+ function withQwenHostedPolicy(model) {
8
+ if (model.modelId !== "kimi-k3")
9
+ return model;
10
+ const prepare = (input) => {
11
+ const options = input.providerOptions ?? {};
12
+ if (options.apiMode && !["auto", "chat"].includes(String(options.apiMode)))
13
+ throw new Error("Kimi K3 on Model Studio requires Chat Completions.");
14
+ if (input.reasoning?.effort !== undefined || input.reasoning?.budgetTokens !== undefined || options.reasoning_effort !== undefined || options.thinking_budget !== undefined || options.enable_thinking === false) {
15
+ throw new Error("Kimi K3 on Model Studio currently supports default thinking only.");
16
+ }
17
+ input.providerOptions = { ...options, apiMode: "chat", enable_thinking: true };
18
+ };
19
+ const wrapped = wrapLanguageModel(model, [{
20
+ name: "harness-kimi-k3-chat-thinking-v1",
21
+ async wrapGenerate(context, next) {
22
+ prepare(context.input);
23
+ return next();
24
+ },
25
+ async wrapStream(context, next) {
26
+ prepare(context.input);
27
+ return next();
28
+ }
29
+ }]);
30
+ return { ...wrapped, capabilities: {
31
+ ...wrapped.capabilities,
32
+ reasoningEfforts: [],
33
+ webSearch: false,
34
+ ...wrapped.capabilities.agentCapabilities ? { agentCapabilities: { ...wrapped.capabilities.agentCapabilities, hostedWebSearch: false, codeExecution: false, webExtraction: false } } : {}
35
+ } };
36
+ }
37
+
38
+ // src/providers/meta-continuation.ts
39
+ import { setTimeout as delay } from "node:timers/promises";
40
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
41
+ var isReceiptContinuation = (input, init) => {
42
+ if (init?.method !== "POST" || typeof init.body !== "string")
43
+ return false;
44
+ try {
45
+ const url = new URL(typeof input === "string" ? input : input instanceof URL ? input.href : input.url);
46
+ if (!url.pathname.endsWith("/responses"))
47
+ return false;
48
+ const body = JSON.parse(init.body);
49
+ if (!isRecord(body))
50
+ return false;
51
+ return typeof body.previous_response_id === "string" && body.previous_response_id.length > 0 && Array.isArray(body.input) && body.input.length > 0 && body.input.every((item) => isRecord(item) && item.type === "function_call_output" && typeof item.call_id === "string" && typeof item.output === "string") && Array.isArray(body.tools) && body.tools.every((tool) => isRecord(tool) && tool.type === "function");
52
+ } catch {
53
+ return false;
54
+ }
55
+ };
56
+ var isUnavailableResponse = async (response) => {
57
+ const reader = response.clone().body?.getReader();
58
+ if (!reader)
59
+ return false;
60
+ const chunks = [];
61
+ let size = 0;
62
+ try {
63
+ for (;; ) {
64
+ const next = await reader.read();
65
+ if (next.done)
66
+ break;
67
+ size += next.value.byteLength;
68
+ if (size > 8192)
69
+ return false;
70
+ chunks.push(next.value);
71
+ }
72
+ const error = JSON.parse(Buffer.concat(chunks).toString("utf8"))?.error;
73
+ return error?.type === "invalid_request_error" && typeof error.message === "string" && /response/i.test(error.message) && /not found|expired/i.test(error.message) && (error.param == null || error.param === "previous_response_id");
74
+ } catch {
75
+ return false;
76
+ } finally {
77
+ reader.cancel().catch(() => {});
78
+ }
79
+ };
80
+ var createMetaContinuationFetch = (fetcher = fetch, backoff = [1000, 2000]) => {
81
+ const recovered = new WeakSet;
82
+ return Object.assign(async (input, init) => {
83
+ let response = await fetcher(input, init);
84
+ if (response.status !== 400 || !init || recovered.has(init) || !isReceiptContinuation(input, init) || !await isUnavailableResponse(response))
85
+ return response;
86
+ recovered.add(init);
87
+ for (const waitMs of typeof backoff === "number" ? [backoff] : backoff) {
88
+ init.signal?.throwIfAborted();
89
+ await response.body?.cancel();
90
+ await delay(waitMs, undefined, { signal: init.signal ?? undefined });
91
+ init.signal?.throwIfAborted();
92
+ response = await fetcher(input, init);
93
+ if (response.status !== 400 || !await isUnavailableResponse(response))
94
+ break;
95
+ }
96
+ return response;
97
+ }, { preconnect: fetcher.preconnect });
98
+ };
99
+
100
+ // src/providers/meta-replay.ts
101
+ import { wrapLanguageModel as wrapLanguageModel2 } from "@zhivex-ai/core";
102
+ import { createMeta } from "@zhivex-ai/meta";
103
+ var replayPlan = (messages) => {
104
+ let offset = 0;
105
+ const insertions = [];
106
+ for (const message of messages) {
107
+ for (const part of message.parts) {
108
+ if (message.role !== "assistant" || part.type !== "provider-data" || part.provider !== "meta")
109
+ continue;
110
+ const data = part.data;
111
+ if (!data || typeof data !== "object" || Array.isArray(data) || data.type !== "reasoning" || typeof data.encrypted_content !== "string" || !data.encrypted_content)
112
+ continue;
113
+ insertions.push({ offset, item: {
114
+ type: "reasoning",
115
+ summary: [],
116
+ encrypted_content: data.encrypted_content,
117
+ ...typeof data.id === "string" ? { id: data.id } : {}
118
+ } });
119
+ }
120
+ if (message.role === "tool") {
121
+ offset += message.parts.filter((part) => part.type === "tool-result").length;
122
+ } else {
123
+ if (message.role === "assistant")
124
+ offset += message.parts.filter((part) => part.type === "tool-call").length;
125
+ if (message.parts.some((part) => ["text", "image", "audio", "file"].includes(part.type)))
126
+ offset++;
127
+ }
128
+ }
129
+ return { insertions, itemCount: offset };
130
+ };
131
+ var createMetaReplayModel = (options, modelId) => {
132
+ const fetcher = options.fetch ?? fetch;
133
+ const prepare = (input) => {
134
+ const plan = replayPlan(input.messages);
135
+ const messages = input.messages.map((message) => ({ ...message, parts: message.parts.filter((part) => !(part.type === "provider-data" && part.provider === "meta" && part.data && typeof part.data === "object" && !Array.isArray(part.data) && ("responseId" in part.data))) }));
136
+ const { previous_response_id: _previous, ...providerOptions } = input.providerOptions ?? {};
137
+ const include = Array.isArray(providerOptions.include) ? providerOptions.include.filter((v) => typeof v === "string") : [];
138
+ const replayFetch = Object.assign(async (url, init) => {
139
+ if (typeof init?.body !== "string")
140
+ throw new Error("Meta replay requires a serialized SDK request.");
141
+ const body = JSON.parse(init.body);
142
+ const items = body.input ?? [];
143
+ if (!Array.isArray(items) || items.length !== plan.itemCount)
144
+ throw new Error("Meta SDK input layout changed; cannot safely replay reasoning.");
145
+ const replayed = [];
146
+ let insertion = 0;
147
+ for (let offset = 0;offset <= items.length; offset++) {
148
+ while (plan.insertions[insertion]?.offset === offset)
149
+ replayed.push(plan.insertions[insertion++].item);
150
+ if (offset < items.length)
151
+ replayed.push(items[offset]);
152
+ }
153
+ delete body.previous_response_id;
154
+ body.input = replayed;
155
+ return fetcher(url, { ...init, body: JSON.stringify(body) });
156
+ }, { preconnect: fetcher.preconnect });
157
+ return {
158
+ model: createMeta({ ...options, fetch: replayFetch })(modelId),
159
+ input: { ...input, messages, providerOptions: {
160
+ ...providerOptions,
161
+ apiMode: "responses",
162
+ store: false,
163
+ include: [...new Set([...include, "reasoning.encrypted_content"])]
164
+ } }
165
+ };
166
+ };
167
+ return wrapLanguageModel2(createMeta(options)(modelId), [{
168
+ name: "harness-meta-stateless-replay-v1",
169
+ async wrapGenerate({ input }) {
170
+ const prepared = prepare(input);
171
+ return prepared.model.generate(prepared.input);
172
+ },
173
+ async wrapStream({ input }) {
174
+ const prepared = prepare(input);
175
+ return prepared.model.stream(prepared.input);
176
+ }
177
+ }]);
178
+ };
179
+
180
+ // src/providers/meta-errors.ts
181
+ import { ProviderHTTPError } from "@zhivex-ai/core/provider";
182
+ function annotateMetaRequestError(error) {
183
+ if (error instanceof ProviderHTTPError) {
184
+ let reason = "reason unavailable";
185
+ try {
186
+ const detail = JSON.parse(typeof error.responseBody === "string" ? error.responseBody : "").error;
187
+ const message = typeof detail?.message === "string" ? detail.message : "";
188
+ if (detail?.type === "invalid_request_error" && /response/i.test(message) && /not found|expired/i.test(message) && (detail.param == null || detail.param === "previous_response_id")) {
189
+ reason = "previous response unavailable";
190
+ } else if (detail?.code === "context_length_exceeded") {
191
+ reason = "context length exceeded";
192
+ } else if (detail?.param === "tools") {
193
+ reason = "tools rejected";
194
+ } else if (detail?.param === "input") {
195
+ reason = "input rejected";
196
+ } else if (detail?.param === "max_output_tokens") {
197
+ reason = "output token limit rejected";
198
+ } else if (detail?.param === "previous_response_id") {
199
+ reason = "previous response rejected";
200
+ }
201
+ } catch {}
202
+ error.message = `Meta request failed with status ${error.status} (${reason}).`;
203
+ }
204
+ throw error;
205
+ }
206
+
207
+ // src/models/catalog.ts
208
+ import { z } from "zod";
209
+ // src/models/catalog.json
210
+ var catalog_default = {
211
+ schemaVersion: 1,
212
+ revision: "2026-09-26-frontier",
213
+ providers: [
214
+ {
215
+ id: "openai",
216
+ defaultModel: "gpt-6-luna",
217
+ models: [
218
+ {
219
+ id: "gpt-6-astra",
220
+ name: "gpt-6-astra",
221
+ group: "primary",
222
+ order: 0,
223
+ lifecycle: "active",
224
+ validation: "unverified",
225
+ capabilities: [
226
+ "chat",
227
+ "tools"
228
+ ],
229
+ limits: {
230
+ contextWindowTokens: 1050000,
231
+ contextWindowType: "combined",
232
+ maxOutputTokens: 128000,
233
+ evidence: {
234
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-astra",
235
+ checkedAt: "2026-09-24"
236
+ }
237
+ },
238
+ pricing: {
239
+ currency: "USD",
240
+ inputPerMillionTokens: 10,
241
+ outputPerMillionTokens: 50,
242
+ cachedInputPerMillionTokens: 1,
243
+ maxInputTokens: 272000,
244
+ scope: "Standard text tokens; excludes regional, mode, cache-write and tool charges.",
245
+ evidence: {
246
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-astra",
247
+ checkedAt: "2026-09-24"
248
+ }
249
+ },
250
+ compaction: {
251
+ suitability: "candidate",
252
+ reason: "Editorial candidate based on documented text generation and token limits; summary fidelity has not been benchmarked.",
253
+ evidence: {
254
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-astra",
255
+ checkedAt: "2026-09-24"
256
+ }
257
+ }
258
+ },
259
+ {
260
+ id: "gpt-6-sol",
261
+ name: "gpt-6-sol",
262
+ group: "primary",
263
+ order: 1,
264
+ lifecycle: "active",
265
+ validation: "unverified",
266
+ capabilities: [
267
+ "chat",
268
+ "tools",
269
+ "vision"
270
+ ],
271
+ limits: {
272
+ contextWindowTokens: 1050000,
273
+ contextWindowType: "combined",
274
+ maxOutputTokens: 128000,
275
+ evidence: {
276
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-sol",
277
+ checkedAt: "2026-09-24"
278
+ }
279
+ },
280
+ pricing: {
281
+ currency: "USD",
282
+ inputPerMillionTokens: 2,
283
+ outputPerMillionTokens: 10,
284
+ cachedInputPerMillionTokens: 0.2,
285
+ maxInputTokens: 272000,
286
+ scope: "Standard text tokens; excludes regional, mode, cache-write and tool charges.",
287
+ evidence: {
288
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-sol",
289
+ checkedAt: "2026-09-24"
290
+ }
291
+ },
292
+ compaction: {
293
+ suitability: "candidate",
294
+ reason: "Editorial candidate based on documented text generation and token limits; summary fidelity has not been benchmarked.",
295
+ evidence: {
296
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-sol",
297
+ checkedAt: "2026-09-24"
298
+ }
299
+ }
300
+ },
301
+ {
302
+ id: "gpt-6-luna",
303
+ name: "gpt-6-luna",
304
+ group: "primary",
305
+ order: 2,
306
+ lifecycle: "active",
307
+ validation: "unverified",
308
+ capabilities: [
309
+ "chat",
310
+ "tools",
311
+ "vision"
312
+ ],
313
+ limits: {
314
+ contextWindowTokens: 1050000,
315
+ contextWindowType: "combined",
316
+ maxOutputTokens: 128000,
317
+ evidence: {
318
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-luna",
319
+ checkedAt: "2026-09-24"
320
+ }
321
+ },
322
+ pricing: {
323
+ currency: "USD",
324
+ inputPerMillionTokens: 0.1,
325
+ outputPerMillionTokens: 0.5,
326
+ cachedInputPerMillionTokens: 0.01,
327
+ maxInputTokens: 272000,
328
+ scope: "Standard text tokens; excludes regional, mode, cache-write and tool charges.",
329
+ evidence: {
330
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-luna",
331
+ checkedAt: "2026-09-24"
332
+ }
333
+ },
334
+ compaction: {
335
+ suitability: "candidate",
336
+ reason: "Editorial candidate based on documented text generation and token limits; summary fidelity has not been benchmarked.",
337
+ evidence: {
338
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-6-luna",
339
+ checkedAt: "2026-09-24"
340
+ }
341
+ }
342
+ },
343
+ {
344
+ id: "gpt-5.6-sol",
345
+ name: "gpt-5.6-sol",
346
+ group: "other",
347
+ order: 3,
348
+ lifecycle: "unknown",
349
+ validation: "unverified",
350
+ capabilities: [
351
+ "chat",
352
+ "tools"
353
+ ]
354
+ },
355
+ {
356
+ id: "gpt-5.6-terra",
357
+ name: "gpt-5.6-terra",
358
+ group: "other",
359
+ order: 4,
360
+ lifecycle: "unknown",
361
+ validation: "unverified",
362
+ capabilities: [
363
+ "chat",
364
+ "tools"
365
+ ]
366
+ },
367
+ {
368
+ id: "gpt-5.6-luna",
369
+ name: "gpt-5.6-luna",
370
+ group: "other",
371
+ order: 5,
372
+ lifecycle: "unknown",
373
+ validation: "unverified",
374
+ capabilities: [
375
+ "chat",
376
+ "tools"
377
+ ]
378
+ },
379
+ {
380
+ id: "gpt-5.5",
381
+ name: "gpt-5.5",
382
+ group: "other",
383
+ order: 6,
384
+ lifecycle: "unknown",
385
+ validation: "unverified",
386
+ capabilities: [
387
+ "chat",
388
+ "tools"
389
+ ],
390
+ reason: "Earlier generation; GPT-6 and GPT-5.6 are the current primary choices."
391
+ },
392
+ {
393
+ id: "gpt-5.4",
394
+ name: "gpt-5.4",
395
+ group: "other",
396
+ order: 7,
397
+ lifecycle: "unknown",
398
+ validation: "unverified",
399
+ capabilities: [
400
+ "chat",
401
+ "tools"
402
+ ],
403
+ reason: "Earlier generation; GPT-6 and GPT-5.6 are the current primary choices."
404
+ },
405
+ {
406
+ id: "gpt-5.4-mini",
407
+ name: "gpt-5.4-mini",
408
+ group: "other",
409
+ order: 8,
410
+ lifecycle: "unknown",
411
+ validation: "unverified",
412
+ capabilities: [
413
+ "chat",
414
+ "tools"
415
+ ],
416
+ reason: "Earlier generation; GPT-6 and GPT-5.6 are the current primary choices."
417
+ },
418
+ {
419
+ id: "gpt-4o-mini",
420
+ name: "gpt-4o-mini",
421
+ group: "other",
422
+ order: 9,
423
+ lifecycle: "unknown",
424
+ validation: "unverified",
425
+ capabilities: [
426
+ "chat",
427
+ "tools"
428
+ ],
429
+ reason: "Earlier generation; GPT-6 and GPT-5.6 are the current primary choices.",
430
+ limits: {
431
+ contextWindowTokens: 128000,
432
+ contextWindowType: "combined",
433
+ maxOutputTokens: 16384,
434
+ evidence: {
435
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-4o-mini",
436
+ checkedAt: "2026-09-24"
437
+ }
438
+ },
439
+ pricing: {
440
+ currency: "USD",
441
+ inputPerMillionTokens: 0.15,
442
+ outputPerMillionTokens: 0.6,
443
+ cachedInputPerMillionTokens: 0.075,
444
+ scope: "Standard text tokens; excludes regional, mode, cache-write and tool charges.",
445
+ evidence: {
446
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-4o-mini",
447
+ checkedAt: "2026-09-24"
448
+ }
449
+ },
450
+ compaction: {
451
+ suitability: "candidate",
452
+ reason: "Editorial candidate based on documented text generation and token limits; summary fidelity has not been benchmarked.",
453
+ evidence: {
454
+ sourceUrl: "https://developers.openai.com/api/docs/models/gpt-4o-mini",
455
+ checkedAt: "2026-09-24"
456
+ }
457
+ }
458
+ }
459
+ ]
460
+ },
461
+ {
462
+ id: "qwen",
463
+ defaultModel: "qwen3.8-max",
464
+ models: [
465
+ {
466
+ id: "qwen3.8-flash",
467
+ name: "qwen3.8-flash",
468
+ group: "primary",
469
+ order: 0,
470
+ lifecycle: "unknown",
471
+ validation: "unverified",
472
+ capabilities: [
473
+ "chat",
474
+ "tools"
475
+ ],
476
+ limits: {
477
+ contextWindowTokens: 1e6,
478
+ contextWindowType: "combined",
479
+ maxOutputTokens: 131072,
480
+ maxInputTokens: 983616,
481
+ evidence: {
482
+ sourceUrl: "https://www.alibabacloud.com/help/en/model-studio/qwen3-8-flash",
483
+ checkedAt: "2026-09-24"
484
+ }
485
+ },
486
+ compaction: {
487
+ suitability: "candidate",
488
+ reason: "Editorial candidate based on documented text generation. Conservative thinking-mode input limit; summary fidelity has not been benchmarked.",
489
+ evidence: {
490
+ sourceUrl: "https://www.alibabacloud.com/help/en/model-studio/qwen3-8-flash",
491
+ checkedAt: "2026-09-24"
492
+ }
493
+ }
494
+ },
495
+ {
496
+ id: "qwen3.8-max",
497
+ name: "qwen3.8-max",
498
+ group: "primary",
499
+ order: 1,
500
+ lifecycle: "unknown",
501
+ validation: "unverified",
502
+ capabilities: [
503
+ "chat",
504
+ "tools"
505
+ ],
506
+ limits: {
507
+ contextWindowTokens: 1e6,
508
+ contextWindowType: "combined",
509
+ maxOutputTokens: 131072,
510
+ maxInputTokens: 983616,
511
+ evidence: {
512
+ sourceUrl: "https://www.alibabacloud.com/help/en/model-studio/qwen3-8-max",
513
+ checkedAt: "2026-09-24"
514
+ }
515
+ },
516
+ compaction: {
517
+ suitability: "candidate",
518
+ reason: "Editorial candidate based on documented text generation. Conservative thinking-mode input limit; summary fidelity has not been benchmarked.",
519
+ evidence: {
520
+ sourceUrl: "https://www.alibabacloud.com/help/en/model-studio/qwen3-8-max",
521
+ checkedAt: "2026-09-24"
522
+ }
523
+ }
524
+ },
525
+ {
526
+ id: "glm-5.3",
527
+ name: "GLM 5.3",
528
+ group: "primary",
529
+ order: 2,
530
+ lifecycle: "active",
531
+ validation: "unverified",
532
+ capabilities: [
533
+ "chat",
534
+ "tools"
535
+ ],
536
+ reason: "Zhipu model served by Alibaba Model Studio; region and account access vary"
537
+ },
538
+ {
539
+ id: "deepseek-v4.1-flash",
540
+ name: "DeepSeek V4.1 Flash",
541
+ group: "primary",
542
+ order: 3,
543
+ lifecycle: "active",
544
+ validation: "unverified",
545
+ capabilities: [
546
+ "chat",
547
+ "tools",
548
+ "vision"
549
+ ],
550
+ reason: "DeepSeek model served by Alibaba Model Studio; region and account access vary"
551
+ },
552
+ {
553
+ id: "kimi-k3",
554
+ name: "Kimi K3",
555
+ group: "primary",
556
+ order: 4,
557
+ lifecycle: "active",
558
+ validation: "unverified",
559
+ capabilities: [
560
+ "chat",
561
+ "tools",
562
+ "vision"
563
+ ],
564
+ reason: "Moonshot model served by Alibaba Model Studio; region and account access vary"
565
+ },
566
+ {
567
+ id: "qwen3.7-plus",
568
+ name: "qwen3.7-plus",
569
+ group: "primary",
570
+ order: 5,
571
+ lifecycle: "unknown",
572
+ validation: "unverified",
573
+ capabilities: [
574
+ "chat",
575
+ "tools"
576
+ ]
577
+ },
578
+ {
579
+ id: "qwen3.8-omni-flash",
580
+ name: "qwen3.8-omni-flash",
581
+ group: "other",
582
+ order: 6,
583
+ lifecycle: "unknown",
584
+ validation: "unverified",
585
+ capabilities: [
586
+ "chat",
587
+ "tools"
588
+ ]
589
+ },
590
+ {
591
+ id: "qwen3.8-max-0902",
592
+ name: "qwen3.8-max-0902",
593
+ group: "other",
594
+ order: 7,
595
+ lifecycle: "unknown",
596
+ validation: "unverified",
597
+ capabilities: [
598
+ "chat",
599
+ "tools"
600
+ ]
601
+ },
602
+ {
603
+ id: "qwen3.8-max-preview",
604
+ name: "qwen3.8-max-preview",
605
+ group: "other",
606
+ order: 8,
607
+ lifecycle: "unknown",
608
+ validation: "unverified",
609
+ capabilities: [
610
+ "chat",
611
+ "tools"
612
+ ]
613
+ },
614
+ {
615
+ id: "qwen3.7-max",
616
+ name: "qwen3.7-max",
617
+ group: "other",
618
+ order: 9,
619
+ lifecycle: "unknown",
620
+ validation: "unverified",
621
+ capabilities: [
622
+ "chat",
623
+ "tools"
624
+ ]
625
+ },
626
+ {
627
+ id: "qwen3.6-flash",
628
+ name: "qwen3.6-flash",
629
+ group: "other",
630
+ order: 10,
631
+ lifecycle: "unknown",
632
+ validation: "unverified",
633
+ capabilities: [
634
+ "chat",
635
+ "tools"
636
+ ]
637
+ },
638
+ {
639
+ id: "qwen3.5-omni-plus",
640
+ name: "qwen3.5-omni-plus",
641
+ group: "other",
642
+ order: 11,
643
+ lifecycle: "unknown",
644
+ validation: "unverified",
645
+ capabilities: [
646
+ "chat",
647
+ "tools"
648
+ ]
649
+ },
650
+ {
651
+ id: "qwen-plus",
652
+ name: "qwen-plus",
653
+ group: "other",
654
+ order: 12,
655
+ lifecycle: "unknown",
656
+ validation: "unverified",
657
+ capabilities: [
658
+ "chat",
659
+ "tools"
660
+ ]
661
+ }
662
+ ]
663
+ },
664
+ {
665
+ id: "gemini",
666
+ defaultModel: "gemini-3.6-flash",
667
+ models: [
668
+ {
669
+ id: "gemini-3.8-flash",
670
+ name: "gemini-3.8-flash",
671
+ group: "other",
672
+ order: 0,
673
+ lifecycle: "unknown",
674
+ validation: "unverified",
675
+ capabilities: [
676
+ "chat",
677
+ "tools"
678
+ ],
679
+ limits: {
680
+ contextWindowTokens: 1048576,
681
+ contextWindowType: "input",
682
+ maxOutputTokens: 65536,
683
+ evidence: {
684
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash",
685
+ checkedAt: "2026-09-24"
686
+ }
687
+ },
688
+ compaction: {
689
+ suitability: "candidate",
690
+ reason: "Editorial candidate based on documented text generation and token limits; summary fidelity has not been benchmarked.",
691
+ evidence: {
692
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash",
693
+ checkedAt: "2026-09-24"
694
+ }
695
+ }
696
+ },
697
+ {
698
+ id: "gemini-3.7-flash",
699
+ name: "gemini-3.7-flash",
700
+ group: "other",
701
+ order: 1,
702
+ lifecycle: "unknown",
703
+ validation: "unverified",
704
+ capabilities: [
705
+ "chat",
706
+ "tools"
707
+ ],
708
+ limits: {
709
+ contextWindowTokens: 1048576,
710
+ contextWindowType: "input",
711
+ maxOutputTokens: 65536,
712
+ evidence: {
713
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.7-flash",
714
+ checkedAt: "2026-09-24"
715
+ }
716
+ },
717
+ compaction: {
718
+ suitability: "candidate",
719
+ reason: "Editorial candidate based on documented text generation and token limits; summary fidelity has not been benchmarked.",
720
+ evidence: {
721
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.7-flash",
722
+ checkedAt: "2026-09-24"
723
+ }
724
+ }
725
+ },
726
+ {
727
+ id: "gemini-3.6-flash",
728
+ name: "gemini-3.6-flash",
729
+ group: "primary",
730
+ order: 2,
731
+ lifecycle: "unknown",
732
+ validation: "unverified",
733
+ capabilities: [
734
+ "chat",
735
+ "tools"
736
+ ],
737
+ limits: {
738
+ contextWindowTokens: 1048576,
739
+ contextWindowType: "input",
740
+ maxOutputTokens: 65536,
741
+ evidence: {
742
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.6-flash",
743
+ checkedAt: "2026-09-24"
744
+ }
745
+ },
746
+ compaction: {
747
+ suitability: "candidate",
748
+ reason: "Editorial candidate based on documented text generation and token limits; summary fidelity has not been benchmarked.",
749
+ evidence: {
750
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.6-flash",
751
+ checkedAt: "2026-09-24"
752
+ }
753
+ }
754
+ },
755
+ {
756
+ id: "gemini-3.5-flash-lite",
757
+ name: "gemini-3.5-flash-lite",
758
+ group: "other",
759
+ order: 3,
760
+ lifecycle: "unknown",
761
+ validation: "unverified",
762
+ capabilities: [
763
+ "chat",
764
+ "tools"
765
+ ],
766
+ limits: {
767
+ contextWindowTokens: 1048576,
768
+ contextWindowType: "input",
769
+ maxOutputTokens: 65536,
770
+ evidence: {
771
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.5-flash-lite",
772
+ checkedAt: "2026-09-24"
773
+ }
774
+ },
775
+ compaction: {
776
+ suitability: "candidate",
777
+ reason: "Editorial candidate based on documented document parsing and text generation; summary fidelity has not been benchmarked.",
778
+ evidence: {
779
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/models/gemini-3.5-flash-lite",
780
+ checkedAt: "2026-09-24"
781
+ }
782
+ },
783
+ pricing: {
784
+ currency: "USD",
785
+ inputPerMillionTokens: 0.3,
786
+ outputPerMillionTokens: 2.5,
787
+ cachedInputPerMillionTokens: 0.03,
788
+ scope: "Standard paid-tier text tokens; includes output thinking tokens, excludes cache storage and tool charges.",
789
+ evidence: {
790
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing#gemini-3.5-flash-lite",
791
+ checkedAt: "2026-09-24"
792
+ }
793
+ }
794
+ },
795
+ {
796
+ id: "gemini-3.5-flash",
797
+ name: "gemini-3.5-flash",
798
+ group: "other",
799
+ order: 4,
800
+ lifecycle: "unknown",
801
+ validation: "unverified",
802
+ capabilities: [
803
+ "chat",
804
+ "tools"
805
+ ]
806
+ },
807
+ {
808
+ id: "gemini-3.1-pro-preview",
809
+ name: "gemini-3.1-pro-preview",
810
+ group: "other",
811
+ order: 5,
812
+ lifecycle: "unknown",
813
+ validation: "unverified",
814
+ capabilities: [
815
+ "chat",
816
+ "tools"
817
+ ]
818
+ },
819
+ {
820
+ id: "gemini-3.1-pro-preview-customtools",
821
+ name: "gemini-3.1-pro-preview-customtools",
822
+ group: "other",
823
+ order: 6,
824
+ lifecycle: "unknown",
825
+ validation: "unverified",
826
+ capabilities: [
827
+ "chat",
828
+ "tools"
829
+ ]
830
+ },
831
+ {
832
+ id: "gemini-3-flash-preview",
833
+ name: "gemini-3-flash-preview",
834
+ group: "other",
835
+ order: 7,
836
+ lifecycle: "unknown",
837
+ validation: "unverified",
838
+ capabilities: [
839
+ "chat",
840
+ "tools"
841
+ ]
842
+ },
843
+ {
844
+ id: "gemini-3.1-flash-lite",
845
+ name: "gemini-3.1-flash-lite",
846
+ group: "other",
847
+ order: 8,
848
+ lifecycle: "unknown",
849
+ validation: "unverified",
850
+ capabilities: [
851
+ "chat",
852
+ "tools"
853
+ ]
854
+ }
855
+ ]
856
+ },
857
+ {
858
+ id: "meta",
859
+ defaultModel: "muse-spark-1.3",
860
+ models: [
861
+ {
862
+ id: "muse-spark-1.3",
863
+ name: "muse-spark-1.3",
864
+ group: "primary",
865
+ order: 0,
866
+ lifecycle: "active",
867
+ validation: "unverified",
868
+ capabilities: [
869
+ "chat",
870
+ "tools"
871
+ ]
872
+ },
873
+ {
874
+ id: "muse-spark-1.2",
875
+ name: "muse-spark-1.2",
876
+ group: "primary",
877
+ order: 1,
878
+ lifecycle: "unknown",
879
+ validation: "unverified",
880
+ capabilities: [
881
+ "chat",
882
+ "tools"
883
+ ]
884
+ },
885
+ {
886
+ id: "muse-spark-1.2-contributor",
887
+ name: "muse-spark-1.2-contributor",
888
+ group: "other",
889
+ order: 2,
890
+ lifecycle: "unknown",
891
+ validation: "unverified",
892
+ capabilities: [
893
+ "chat",
894
+ "tools"
895
+ ]
896
+ },
897
+ {
898
+ id: "muse-spark-1.1",
899
+ name: "muse-spark-1.1",
900
+ group: "other",
901
+ order: 3,
902
+ lifecycle: "unknown",
903
+ validation: "unverified",
904
+ capabilities: [
905
+ "chat",
906
+ "tools"
907
+ ]
908
+ }
909
+ ]
910
+ }
911
+ ]
912
+ };
913
+
914
+ // src/models/catalog.ts
915
+ var modelId = z.string().min(1).max(160).regex(/^[a-zA-Z0-9][a-zA-Z0-9._:/-]*$/);
916
+ var text = z.string().min(1).max(300).regex(/^[^\x00-\x1f\x7f-\x9f]*$/);
917
+ var evidence = z.object({
918
+ sourceUrl: z.url().max(1000).refine((value) => {
919
+ const url = new URL(value);
920
+ return url.protocol === "https:" && !url.username && !url.password;
921
+ }, "Evidence requires an HTTPS URL without credentials"),
922
+ checkedAt: z.iso.date()
923
+ }).strict();
924
+ var tokenLimit = z.number().int().positive().max(1e9);
925
+ var price = z.number().finite().nonnegative().max(1e6);
926
+ var catalogModelSchema = z.object({
927
+ id: modelId,
928
+ name: text,
929
+ group: z.enum(["primary", "other"]),
930
+ order: z.number().int().min(0),
931
+ lifecycle: z.enum(["active", "deprecated", "retired", "unknown"]),
932
+ validation: z.enum(["verified", "unverified"]),
933
+ capabilities: z.array(z.enum(["chat", "tools", "vision"])).max(3),
934
+ reason: text.optional(),
935
+ replacement: modelId.optional(),
936
+ retirementDate: z.iso.date().optional(),
937
+ limits: z.object({
938
+ contextWindowTokens: tokenLimit,
939
+ contextWindowType: z.enum(["combined", "input"]),
940
+ maxOutputTokens: tokenLimit,
941
+ maxInputTokens: tokenLimit.optional(),
942
+ evidence
943
+ }).strict().optional(),
944
+ pricing: z.object({
945
+ currency: z.literal("USD"),
946
+ inputPerMillionTokens: price,
947
+ outputPerMillionTokens: price,
948
+ cachedInputPerMillionTokens: price.optional(),
949
+ maxInputTokens: tokenLimit.optional(),
950
+ scope: text,
951
+ evidence
952
+ }).strict().optional(),
953
+ compaction: z.object({
954
+ suitability: z.enum(["candidate", "evaluated", "unsuitable", "unknown"]),
955
+ reason: text,
956
+ evidence
957
+ }).strict().optional()
958
+ }).strict().superRefine((model, ctx) => {
959
+ if (!model.limits)
960
+ return;
961
+ if (model.limits.maxInputTokens !== undefined && model.limits.maxInputTokens > model.limits.contextWindowTokens) {
962
+ ctx.addIssue({ code: "custom", message: "maxInputTokens exceeds context window" });
963
+ }
964
+ if (model.limits.contextWindowType === "combined" && model.limits.maxOutputTokens > model.limits.contextWindowTokens) {
965
+ ctx.addIssue({ code: "custom", message: "maxOutputTokens exceeds combined context window" });
966
+ }
967
+ });
968
+ var modelCatalogSchema = z.object({
969
+ schemaVersion: z.literal(1),
970
+ revision: z.string().min(1).max(80).regex(/^[a-zA-Z0-9._-]+$/),
971
+ providers: z.array(z.object({
972
+ id: z.enum(["openai", "qwen", "gemini", "meta"]),
973
+ defaultModel: modelId,
974
+ models: z.array(catalogModelSchema).min(1).max(500)
975
+ }).strict()).length(4)
976
+ }).strict().superRefine((catalog, ctx) => {
977
+ const invalid = (message) => ctx.addIssue({ code: "custom", message });
978
+ if (new Set(catalog.providers.map((p) => p.id)).size !== 4)
979
+ invalid("Duplicate providers");
980
+ for (const provider of catalog.providers) {
981
+ const ids = new Set(provider.models.map((m) => m.id));
982
+ if (ids.size !== provider.models.length)
983
+ invalid(`Duplicate models: ${provider.id}`);
984
+ const preferred = provider.models.find((m) => m.id === provider.defaultModel);
985
+ if (!preferred || preferred.group !== "primary" || preferred.lifecycle === "retired")
986
+ invalid(`Invalid default: ${provider.id}`);
987
+ for (const model of provider.models) {
988
+ if (model.group === "primary" && model.lifecycle === "retired")
989
+ invalid("Retired primary model");
990
+ if (model.replacement && (!ids.has(model.replacement) || model.replacement === model.id))
991
+ invalid("Invalid replacement");
992
+ }
993
+ }
994
+ });
995
+ var bundledModelCatalog = modelCatalogSchema.parse(catalog_default);
996
+ var bundledDefaultModel = (provider) => {
997
+ const entry = bundledModelCatalog.providers.find((p) => p.id === provider);
998
+ if (!entry)
999
+ throw new Error("UNKNOWN_CATALOG_PROVIDER");
1000
+ return entry.defaultModel;
1001
+ };
1002
+ function catalogModels(catalog, provider, current) {
1003
+ const models = [...catalog.providers.find((p) => p.id === provider)?.models ?? []].sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
1004
+ if (current && !models.some((m) => m.id === current))
1005
+ models.unshift({
1006
+ id: current,
1007
+ name: current,
1008
+ group: "other",
1009
+ order: 0,
1010
+ lifecycle: "unknown",
1011
+ validation: "unverified",
1012
+ capabilities: []
1013
+ });
1014
+ return models;
1015
+ }
1016
+ function modelDescription(model) {
1017
+ return [
1018
+ model.lifecycle === "deprecated" ? "In retirement" : model.lifecycle === "retired" ? "Retired" : "",
1019
+ model.validation === "unverified" ? "Unverified" : "Verified in Zhivex",
1020
+ model.limits ? `Context: ${model.limits.contextWindowTokens.toLocaleString("en-US")} tokens (${model.limits.contextWindowType}) · Output: ${model.limits.maxOutputTokens.toLocaleString("en-US")}` : "Context: unknown",
1021
+ model.reason,
1022
+ model.replacement ? `Suggested replacement: ${model.replacement}` : "",
1023
+ model.retirementDate ? `Retirement: ${model.retirementDate}` : ""
1024
+ ].filter(Boolean).join(" · ");
1025
+ }
1026
+
1027
+ // src/providers/providers.ts
1028
+ import { createHash } from "node:crypto";
1029
+ import {
1030
+ wrapLanguageModel as wrapLanguageModel3
1031
+ } from "@zhivex-ai/core";
1032
+ import { createGemini } from "@zhivex-ai/gemini";
1033
+ import { createOpenAI } from "@zhivex-ai/openai";
1034
+ import { createQwen } from "@zhivex-ai/qwen";
1035
+ var PROVIDERS = ["meta", "qwen", "openai", "gemini"];
1036
+ var HARNESS_PROVIDER_CAPABILITIES = [
1037
+ "streaming",
1038
+ "tool-calling",
1039
+ "approval-resume"
1040
+ ];
1041
+ var QWEN_REGIONS = [
1042
+ "singapore",
1043
+ "beijing",
1044
+ "hong-kong",
1045
+ "tokyo",
1046
+ "frankfurt",
1047
+ "virginia"
1048
+ ];
1049
+ var PROVIDER_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
1050
+ var ENVIRONMENT_VARIABLE = /^[A-Z_][A-Z0-9_]{0,127}$/;
1051
+ var DIAGNOSTIC_KEY = /^[a-z][A-Za-z0-9]{0,63}$/;
1052
+ var requiredText = (name, value) => {
1053
+ const normalized = value.trim();
1054
+ if (!normalized || /[\u0000-\u001f\u007f]/.test(normalized)) {
1055
+ throw new Error(`${name} must be a non-empty printable string.`);
1056
+ }
1057
+ return normalized;
1058
+ };
1059
+ var environmentVariable = (name, value) => {
1060
+ if (!ENVIRONMENT_VARIABLE.test(value)) {
1061
+ throw new Error(`${name} must be a valid environment variable name.`);
1062
+ }
1063
+ return value;
1064
+ };
1065
+ var diagnosticKey = (value) => {
1066
+ if (!DIAGNOSTIC_KEY.test(value)) {
1067
+ throw new Error(`Invalid provider diagnostic key: ${value}.`);
1068
+ }
1069
+ return value;
1070
+ };
1071
+ var normalizedDescriptor = (descriptor) => {
1072
+ const id = descriptor.id.trim().toLowerCase();
1073
+ if (descriptor.id !== id || !PROVIDER_ID.test(id)) {
1074
+ throw new Error(`Invalid provider id: ${descriptor.id}. Use 1-64 lowercase letters, digits, dot, underscore, or hyphen.`);
1075
+ }
1076
+ if (!(descriptor.support === "certified" || descriptor.support === "provisional")) {
1077
+ throw new Error(`Invalid support level for provider ${id}.`);
1078
+ }
1079
+ const credentialNames = [...new Set(descriptor.credentialNames.map((name) => environmentVariable(`Credential for provider ${id}`, name)))];
1080
+ const capabilities = [...new Set(descriptor.capabilities)];
1081
+ const invalidCapability = capabilities.find((capability) => !HARNESS_PROVIDER_CAPABILITIES.includes(capability));
1082
+ if (invalidCapability) {
1083
+ throw new Error(`Invalid capability for provider ${id}: ${invalidCapability}.`);
1084
+ }
1085
+ return Object.freeze({
1086
+ id,
1087
+ name: requiredText(`Name for provider ${id}`, descriptor.name),
1088
+ defaultModel: requiredText(`Default model for provider ${id}`, descriptor.defaultModel),
1089
+ credentialNames: Object.freeze(credentialNames),
1090
+ capabilities: Object.freeze(capabilities),
1091
+ support: descriptor.support
1092
+ });
1093
+ };
1094
+ var normalizedDiagnostics = (provider, diagnostics) => {
1095
+ const endpointEnvironmentVariable = diagnostics?.endpointEnvironmentVariable === undefined ? undefined : environmentVariable(`Endpoint diagnostic for provider ${provider}`, diagnostics.endpointEnvironmentVariable);
1096
+ const seenKeys = new Set;
1097
+ const presence = (diagnostics?.presence ?? []).map((entry) => {
1098
+ const key = diagnosticKey(entry.key);
1099
+ if (seenKeys.has(key)) {
1100
+ throw new Error(`Duplicate diagnostic key for provider ${provider}: ${key}.`);
1101
+ }
1102
+ seenKeys.add(key);
1103
+ return Object.freeze({
1104
+ key,
1105
+ environmentVariable: environmentVariable(`Presence diagnostic for provider ${provider}`, entry.environmentVariable)
1106
+ });
1107
+ });
1108
+ const enums = (diagnostics?.enums ?? []).map((entry) => {
1109
+ const key = diagnosticKey(entry.key);
1110
+ if (seenKeys.has(key)) {
1111
+ throw new Error(`Duplicate diagnostic key for provider ${provider}: ${key}.`);
1112
+ }
1113
+ seenKeys.add(key);
1114
+ const allowedValues = [...new Set(entry.allowedValues.map((value) => requiredText(`Allowed diagnostic value for provider ${provider}`, value)))];
1115
+ if (allowedValues.length === 0) {
1116
+ throw new Error(`Enum diagnostic ${key} for provider ${provider} needs allowed values.`);
1117
+ }
1118
+ return Object.freeze({
1119
+ key,
1120
+ environmentVariable: environmentVariable(`Enum diagnostic for provider ${provider}`, entry.environmentVariable),
1121
+ allowedValues: Object.freeze(allowedValues)
1122
+ });
1123
+ });
1124
+ return Object.freeze({
1125
+ ...endpointEnvironmentVariable ? { endpointEnvironmentVariable } : {},
1126
+ ...presence.length > 0 ? { presence: Object.freeze(presence) } : {},
1127
+ ...enums.length > 0 ? { enums: Object.freeze(enums) } : {}
1128
+ });
1129
+ };
1130
+ var endpointConfiguration = (value) => {
1131
+ const configured = Boolean(value?.trim());
1132
+ if (!configured) {
1133
+ return {
1134
+ customEndpoint: false,
1135
+ endpointValid: true,
1136
+ endpointSecure: true
1137
+ };
1138
+ }
1139
+ try {
1140
+ const endpoint = new URL(value.trim());
1141
+ const supportedProtocol = endpoint.protocol === "https:" || endpoint.protocol === "http:";
1142
+ return {
1143
+ customEndpoint: true,
1144
+ endpointValid: supportedProtocol && !endpoint.username && !endpoint.password && !endpoint.hash && !endpoint.search,
1145
+ endpointSecure: endpoint.protocol === "https:"
1146
+ };
1147
+ } catch {
1148
+ return {
1149
+ customEndpoint: true,
1150
+ endpointValid: false,
1151
+ endpointSecure: false
1152
+ };
1153
+ }
1154
+ };
1155
+ var createCredentials = (env, provider, names) => Object.freeze({
1156
+ names,
1157
+ get: (name) => {
1158
+ if (!names.includes(name)) {
1159
+ throw new Error(`Credential ${name} is not declared for provider ${provider}.`);
1160
+ }
1161
+ return env[name]?.trim() || undefined;
1162
+ },
1163
+ first: () => {
1164
+ for (const name of names) {
1165
+ const value = env[name]?.trim();
1166
+ if (value) {
1167
+ return value;
1168
+ }
1169
+ }
1170
+ return;
1171
+ },
1172
+ require: () => {
1173
+ for (const name of names) {
1174
+ const value = env[name]?.trim();
1175
+ if (value) {
1176
+ return value;
1177
+ }
1178
+ }
1179
+ throw new Error(names.length === 0 ? `Provider ${provider} does not declare a credential.` : `Missing credentials for ${provider}. Set ${names.join(" or ")} in the environment.`);
1180
+ }
1181
+ });
1182
+ var normalizeRegistration = (registration) => {
1183
+ if (typeof registration.factory !== "function") {
1184
+ throw new Error("Provider registration factory must be a function.");
1185
+ }
1186
+ const descriptor = normalizedDescriptor(registration.descriptor);
1187
+ return Object.freeze({
1188
+ descriptor,
1189
+ factory: registration.factory,
1190
+ diagnostics: normalizedDiagnostics(descriptor.id, registration.diagnostics)
1191
+ });
1192
+ };
1193
+ var providerConfiguration = (diagnostics, env) => {
1194
+ const configuration = endpointConfiguration(diagnostics.endpointEnvironmentVariable ? env[diagnostics.endpointEnvironmentVariable] : undefined);
1195
+ for (const entry of diagnostics.presence ?? []) {
1196
+ configuration[`${entry.key}Configured`] = Boolean(env[entry.environmentVariable]?.trim());
1197
+ }
1198
+ for (const entry of diagnostics.enums ?? []) {
1199
+ const value = env[entry.environmentVariable]?.trim();
1200
+ configuration[`${entry.key}Configured`] = Boolean(value);
1201
+ configuration[`${entry.key}Valid`] = !value || entry.allowedValues.includes(value);
1202
+ }
1203
+ return Object.freeze(configuration);
1204
+ };
1205
+ var parseWithRegistrations = (value, registrations) => {
1206
+ const defaultProvider = registrations.has("openai") ? "openai" : registrations.keys().next().value;
1207
+ const normalized = (value ?? defaultProvider ?? "").trim().toLowerCase();
1208
+ if (registrations.has(normalized)) {
1209
+ return normalized;
1210
+ }
1211
+ throw new Error(`Unknown provider: ${value}. Use ${[...registrations.keys()].join(", ")}.`);
1212
+ };
1213
+ var createProviderRegistry = (registrations) => {
1214
+ if (registrations.length === 0) {
1215
+ throw new Error("Provider registry must contain at least one provider.");
1216
+ }
1217
+ const entries = new Map;
1218
+ for (const registration of registrations) {
1219
+ const normalized = normalizeRegistration(registration);
1220
+ if (entries.has(normalized.descriptor.id)) {
1221
+ throw new Error(`Duplicate provider registration: ${normalized.descriptor.id}.`);
1222
+ }
1223
+ entries.set(normalized.descriptor.id, normalized);
1224
+ }
1225
+ const frozenRegistrations = Object.freeze([...entries.values()]);
1226
+ const descriptors = Object.freeze(frozenRegistrations.map((entry) => entry.descriptor));
1227
+ const registry = Object.freeze({
1228
+ descriptors,
1229
+ has: (provider) => entries.has(provider.trim().toLowerCase()),
1230
+ parse: (value) => parseWithRegistrations(value, entries),
1231
+ descriptor: (provider) => {
1232
+ const registration = entries.get(provider);
1233
+ if (!registration) {
1234
+ throw new Error(`No descriptor is available for provider ${provider}.`);
1235
+ }
1236
+ return registration.descriptor;
1237
+ },
1238
+ createModel: (config, env = process.env) => {
1239
+ const registration = entries.get(config.provider);
1240
+ if (!registration) {
1241
+ throw new Error(`No factory is available for provider ${config.provider}.`);
1242
+ }
1243
+ return registration.factory(Object.freeze({
1244
+ provider: config.provider,
1245
+ model: config.model,
1246
+ env,
1247
+ credentials: createCredentials(env, config.provider, registration.descriptor.credentialNames)
1248
+ }));
1249
+ },
1250
+ availability: (env = process.env) => Object.freeze(frozenRegistrations.map((registration) => {
1251
+ const credentials = Object.freeze(registration.descriptor.credentialNames.map((name) => Object.freeze({ name, present: Boolean(env[name]?.trim()) })));
1252
+ return Object.freeze({
1253
+ ...registration.descriptor,
1254
+ configured: credentials.length === 0 || credentials.some((credential) => credential.present),
1255
+ credentials,
1256
+ configuration: providerConfiguration(registration.diagnostics, env)
1257
+ });
1258
+ })),
1259
+ transportFingerprint: (env = process.env) => {
1260
+ const transport = frozenRegistrations.map((registration) => {
1261
+ const names = [
1262
+ registration.diagnostics.endpointEnvironmentVariable,
1263
+ ...(registration.diagnostics.presence ?? []).map((entry) => entry.environmentVariable),
1264
+ ...(registration.diagnostics.enums ?? []).map((entry) => entry.environmentVariable)
1265
+ ].filter((name) => Boolean(name));
1266
+ return {
1267
+ provider: registration.descriptor.id,
1268
+ values: names.map((name) => [name, env[name]?.trim() ?? ""])
1269
+ };
1270
+ });
1271
+ return `sha256:${createHash("sha256").update(JSON.stringify(transport)).digest("hex")}`;
1272
+ },
1273
+ extend: (additional) => createProviderRegistry([
1274
+ ...frozenRegistrations,
1275
+ ...additional
1276
+ ])
1277
+ });
1278
+ return registry;
1279
+ };
1280
+ var optionalQwenRegion = (value) => {
1281
+ if (!value) {
1282
+ return;
1283
+ }
1284
+ if (QWEN_REGIONS.includes(value)) {
1285
+ return value;
1286
+ }
1287
+ throw new Error(`Invalid QWEN_REGION: ${value}.`);
1288
+ };
1289
+ var generatedToolCallId = (model, input, index, toolCall) => `harness_call_${createHash("sha256").update(JSON.stringify({
1290
+ provider: model.provider,
1291
+ modelId: model.modelId,
1292
+ messages: input.messages,
1293
+ index,
1294
+ name: toolCall.name,
1295
+ input: toolCall.input
1296
+ })).digest("hex").slice(0, 48)}`;
1297
+ var existingToolCallIds = (messages) => new Set(messages.flatMap((message) => message.parts.flatMap((part) => {
1298
+ if (part.type === "tool-call")
1299
+ return [part.toolCall.id];
1300
+ if (part.type === "tool-result")
1301
+ return [part.toolResult.toolCallId];
1302
+ return [];
1303
+ }).filter((id) => typeof id === "string" && id.length > 0)));
1304
+ var normalizedToolCall = (model, input, index, toolCall, seenIds) => {
1305
+ const providerId = typeof toolCall.id === "string" ? toolCall.id.trim() : "";
1306
+ const id = providerId.length > 0 && !/^\d+$/.test(providerId) ? providerId : generatedToolCallId(model, input, index, toolCall);
1307
+ if (seenIds.has(id)) {
1308
+ throw new QwenToolCallIdError(`Qwen returned a duplicate tool-call id: ${id}.`);
1309
+ }
1310
+ seenIds.add(id);
1311
+ return id === toolCall.id ? toolCall : { ...toolCall, id };
1312
+ };
1313
+
1314
+ class QwenToolCallIdError extends Error {
1315
+ diagnosticCode = "QWEN_DUPLICATE_TOOL_CALL_ID";
1316
+ constructor(message) {
1317
+ super(message);
1318
+ this.name = "QwenToolCallIdError";
1319
+ }
1320
+ }
1321
+ var normalizeMessageToolCallIds = (model, input, messages, seenIds = existingToolCallIds(input.messages)) => {
1322
+ let index = 0;
1323
+ return messages.map((message) => ({
1324
+ ...message,
1325
+ parts: message.parts.map((part) => part.type === "tool-call" ? {
1326
+ ...part,
1327
+ toolCall: normalizedToolCall(model, input, index++, part.toolCall, seenIds)
1328
+ } : part)
1329
+ }));
1330
+ };
1331
+ var normalizeGenerateToolCallIds = (model, input, result) => {
1332
+ const messages = result.messages ? normalizeMessageToolCallIds(model, input, result.messages) : undefined;
1333
+ const message = result.message ? normalizeMessageToolCallIds(model, input, [result.message])[0] : undefined;
1334
+ return {
1335
+ ...result,
1336
+ ...message ? { message } : {},
1337
+ ...messages ? { messages } : {}
1338
+ };
1339
+ };
1340
+ var normalizeStreamToolCallIds = async function* (model, input, events) {
1341
+ let index = 0;
1342
+ const seenIds = existingToolCallIds(input.messages);
1343
+ for await (const event of events) {
1344
+ if (event.type !== "tool-call") {
1345
+ yield event;
1346
+ continue;
1347
+ }
1348
+ yield {
1349
+ ...event,
1350
+ toolCall: normalizedToolCall(model, input, index++, event.toolCall, seenIds)
1351
+ };
1352
+ }
1353
+ };
1354
+ var withQwenDurableToolCallIds = (model) => wrapLanguageModel3(model, [{
1355
+ name: "harness-qwen-durable-tool-call-ids",
1356
+ wrapGenerate: async ({ input }, next) => normalizeGenerateToolCallIds(model, input, await next()),
1357
+ wrapStream: async ({ input }, next) => normalizeStreamToolCallIds(model, input, await next())
1358
+ }]);
1359
+ var withOpenAIToolResultEnvelopes = (model) => wrapLanguageModel3(model, [{
1360
+ name: "harness-openai-tool-result-envelopes-v1",
1361
+ async wrapGenerate({ input }, next) {
1362
+ input.toolResultFormat = "envelope";
1363
+ return next();
1364
+ },
1365
+ async wrapStream({ input }, next) {
1366
+ input.toolResultFormat = "envelope";
1367
+ return next();
1368
+ }
1369
+ }]);
1370
+ var withMetaResponses = (model) => wrapLanguageModel3(model, [{
1371
+ name: "harness-meta-responses-v1",
1372
+ async wrapGenerate({ input }, next) {
1373
+ input.providerOptions = { ...input.providerOptions, apiMode: "responses" };
1374
+ try {
1375
+ return await next();
1376
+ } catch (error) {
1377
+ return annotateMetaRequestError(error);
1378
+ }
1379
+ },
1380
+ async wrapStream({ input }, next) {
1381
+ input.providerOptions = { ...input.providerOptions, apiMode: "responses" };
1382
+ try {
1383
+ return await next();
1384
+ } catch (error) {
1385
+ return annotateMetaRequestError(error);
1386
+ }
1387
+ }
1388
+ }]);
1389
+ var BUILTIN_PROVIDER_REGISTRATIONS = Object.freeze([
1390
+ {
1391
+ descriptor: {
1392
+ id: "meta",
1393
+ name: "Meta Model API",
1394
+ defaultModel: bundledDefaultModel("meta"),
1395
+ credentialNames: ["MODEL_API_KEY"],
1396
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
1397
+ support: "certified"
1398
+ },
1399
+ diagnostics: { endpointEnvironmentVariable: "META_BASE_URL" },
1400
+ factory: ({ model, env, credentials }) => {
1401
+ const baseURL = env.META_BASE_URL?.trim();
1402
+ return withMetaResponses(createMetaReplayModel({
1403
+ apiKey: credentials.require(),
1404
+ fetch: createMetaContinuationFetch(),
1405
+ ...baseURL ? { baseURL } : {}
1406
+ }, model));
1407
+ }
1408
+ },
1409
+ {
1410
+ descriptor: {
1411
+ id: "qwen",
1412
+ name: "Qwen / Alibaba Cloud Model Studio",
1413
+ defaultModel: bundledDefaultModel("qwen"),
1414
+ credentialNames: ["DASHSCOPE_API_KEY", "QWEN_API_KEY"],
1415
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
1416
+ support: "certified"
1417
+ },
1418
+ diagnostics: {
1419
+ endpointEnvironmentVariable: "QWEN_BASE_URL",
1420
+ presence: [{ key: "workspaceId", environmentVariable: "QWEN_WORKSPACE_ID" }],
1421
+ enums: [{
1422
+ key: "region",
1423
+ environmentVariable: "QWEN_REGION",
1424
+ allowedValues: QWEN_REGIONS
1425
+ }]
1426
+ },
1427
+ factory: ({ model, env, credentials }) => {
1428
+ const baseURL = env.QWEN_BASE_URL?.trim();
1429
+ const workspaceId = env.QWEN_WORKSPACE_ID?.trim();
1430
+ const region = optionalQwenRegion(env.QWEN_REGION?.trim());
1431
+ return withQwenHostedPolicy(withQwenDurableToolCallIds(createQwen({
1432
+ apiKey: credentials.require(),
1433
+ ...baseURL ? { baseURL } : {},
1434
+ ...workspaceId ? { workspaceId } : {},
1435
+ ...region ? { region } : {}
1436
+ })(model)));
1437
+ }
1438
+ },
1439
+ {
1440
+ descriptor: {
1441
+ id: "openai",
1442
+ name: "OpenAI",
1443
+ defaultModel: bundledDefaultModel("openai"),
1444
+ credentialNames: ["OPENAI_API_KEY"],
1445
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
1446
+ support: "certified"
1447
+ },
1448
+ diagnostics: { endpointEnvironmentVariable: "OPENAI_BASE_URL" },
1449
+ factory: ({ model, env, credentials }) => {
1450
+ const baseURL = env.OPENAI_BASE_URL?.trim();
1451
+ return withOpenAIToolResultEnvelopes(createOpenAI({
1452
+ apiKey: credentials.require(),
1453
+ ...baseURL ? { baseURL } : {}
1454
+ })(model));
1455
+ }
1456
+ },
1457
+ {
1458
+ descriptor: {
1459
+ id: "gemini",
1460
+ name: "Google Gemini",
1461
+ defaultModel: bundledDefaultModel("gemini"),
1462
+ credentialNames: ["GEMINI_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"],
1463
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
1464
+ support: "provisional"
1465
+ },
1466
+ diagnostics: { endpointEnvironmentVariable: "GEMINI_BASE_URL" },
1467
+ factory: ({ model, env, credentials }) => {
1468
+ const baseURL = env.GEMINI_BASE_URL?.trim();
1469
+ return createGemini({
1470
+ apiKey: credentials.require(),
1471
+ ...baseURL ? { baseURL } : {}
1472
+ })(model);
1473
+ }
1474
+ }
1475
+ ]);
1476
+ var DEFAULT_PROVIDER_REGISTRY = createProviderRegistry(BUILTIN_PROVIDER_REGISTRATIONS);
1477
+ var PROVIDER_DESCRIPTORS = DEFAULT_PROVIDER_REGISTRY.descriptors;
1478
+
1479
+ // src/providers/reasoning.ts
1480
+ var reasoningEffortSchema = z2.enum(["default", "none", "minimal", "low", "medium", "high", "xhigh", "max"]);
1481
+ var supportedReasoningEfforts = (capabilities) => ["default", ...capabilities.reasoning ? capabilities.reasoningEfforts ?? [] : []];
1482
+ function modelReasoningEfforts(provider, model) {
1483
+ if (!DEFAULT_PROVIDER_REGISTRY.has(provider))
1484
+ return ["default"];
1485
+ const descriptor = DEFAULT_PROVIDER_REGISTRY.descriptor(provider);
1486
+ const env = Object.fromEntries(descriptor.credentialNames.map((name) => [name, "capability-inspection-only"]));
1487
+ return supportedReasoningEfforts(DEFAULT_PROVIDER_REGISTRY.createModel({ provider, model }, env).capabilities);
1488
+ }
1489
+ function withReasoningEffort(model, value) {
1490
+ if (!value || value === "default")
1491
+ return model;
1492
+ if (!supportedReasoningEfforts(model.capabilities).includes(value))
1493
+ throw new Error(`Reasoning effort ${value} is not declared for ${model.provider}/${model.modelId}. Use default or a supported level.`);
1494
+ return wrapLanguageModel4(model, [{
1495
+ name: `harness-reasoning-${value}`,
1496
+ async wrapGenerate(context, next) {
1497
+ context.input.reasoning = { ...context.input.reasoning, effort: value };
1498
+ return next();
1499
+ },
1500
+ async wrapStream(context, next) {
1501
+ context.input.reasoning = { ...context.input.reasoning, effort: value };
1502
+ return next();
1503
+ }
1504
+ }]);
1505
+ }
1506
+
1507
+ // src/runtime/config.ts
1508
+ import { createHash as createHash2 } from "node:crypto";
1509
+ import { realpathSync } from "node:fs";
1510
+ import path from "node:path";
1511
+
1512
+ // src/runtime/errors.ts
1513
+ var HARNESS_ERROR_CODES = [
1514
+ "CONFIG_INVALID",
1515
+ "CLI_USAGE_INVALID",
1516
+ "WORKSPACE_UNSAFE",
1517
+ "STATE_CONFLICT",
1518
+ "PROVIDER_UNAVAILABLE",
1519
+ "APPROVAL_REQUIRED",
1520
+ "EXECUTION_FAILED"
1521
+ ];
1522
+ var HARNESS_ERROR_SCHEMA_VERSION = 1;
1523
+
1524
+ class HarnessError extends Error {
1525
+ code;
1526
+ category;
1527
+ retryable;
1528
+ constructor(message, options) {
1529
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
1530
+ this.name = "HarnessError";
1531
+ this.code = options.code;
1532
+ this.category = options.category;
1533
+ this.retryable = options.retryable ?? false;
1534
+ }
1535
+ }
1536
+
1537
+ class HarnessConfigError extends HarnessError {
1538
+ constructor(message, options = {}) {
1539
+ super(message, {
1540
+ code: "CONFIG_INVALID",
1541
+ category: "configuration",
1542
+ ...options.cause === undefined ? {} : { cause: options.cause }
1543
+ });
1544
+ this.name = "HarnessConfigError";
1545
+ }
1546
+ }
1547
+
1548
+ class HarnessWorkspaceError extends HarnessError {
1549
+ constructor(message, options = {}) {
1550
+ super(message, {
1551
+ code: "WORKSPACE_UNSAFE",
1552
+ category: "workspace",
1553
+ ...options.cause === undefined ? {} : { cause: options.cause }
1554
+ });
1555
+ this.name = "HarnessWorkspaceError";
1556
+ }
1557
+ }
1558
+
1559
+ class HarnessStateConflictError extends HarnessError {
1560
+ constructor(message, options = {}) {
1561
+ super(message, {
1562
+ code: "STATE_CONFLICT",
1563
+ category: "state",
1564
+ retryable: options.retryable ?? false,
1565
+ ...options.cause === undefined ? {} : { cause: options.cause }
1566
+ });
1567
+ this.name = "HarnessStateConflictError";
1568
+ }
1569
+ }
1570
+
1571
+ class HarnessProviderError extends HarnessError {
1572
+ constructor(message, options = {}) {
1573
+ super(message, {
1574
+ code: "PROVIDER_UNAVAILABLE",
1575
+ category: "provider",
1576
+ retryable: options.retryable ?? true,
1577
+ ...options.cause === undefined ? {} : { cause: options.cause }
1578
+ });
1579
+ this.name = "HarnessProviderError";
1580
+ }
1581
+ }
1582
+ class HarnessExecutionError extends HarnessError {
1583
+ constructor(message, options = {}) {
1584
+ super(message, {
1585
+ code: "EXECUTION_FAILED",
1586
+ category: "execution",
1587
+ retryable: options.retryable ?? false,
1588
+ ...options.cause === undefined ? {} : { cause: options.cause }
1589
+ });
1590
+ this.name = "HarnessExecutionError";
1591
+ }
1592
+ }
1593
+ var harnessErrorDocument = (error) => {
1594
+ const normalized = normalizeHarnessError(error);
1595
+ return {
1596
+ schemaVersion: HARNESS_ERROR_SCHEMA_VERSION,
1597
+ kind: "error",
1598
+ error: {
1599
+ code: normalized.code,
1600
+ category: normalized.category,
1601
+ retryable: normalized.retryable
1602
+ }
1603
+ };
1604
+ };
1605
+ var numericStatuses = (error) => {
1606
+ if (!error || typeof error !== "object")
1607
+ return;
1608
+ const record = error;
1609
+ const values = [record.status, record.statusCode].filter((value) => typeof value === "number" && Number.isSafeInteger(value));
1610
+ return values.length > 0 ? values : undefined;
1611
+ };
1612
+ var normalizeHarnessError = (error) => {
1613
+ if (error instanceof HarnessError)
1614
+ return error;
1615
+ const message = error instanceof Error ? error.message : "Harness execution failed.";
1616
+ const statuses = numericStatuses(error) ?? [];
1617
+ if (statuses.some((status) => status === 429 || status >= 500 && status <= 599)) {
1618
+ return new HarnessProviderError(message, { cause: error, retryable: true });
1619
+ }
1620
+ const name = error && typeof error === "object" && typeof error.name === "string" ? error.name : "";
1621
+ const code = error && typeof error === "object" && typeof error.code === "string" ? error.code : "";
1622
+ if (name === "ConflictError" || code === "SQLITE_BUSY" || code === "SQLITE_LOCKED") {
1623
+ return new HarnessStateConflictError(message, {
1624
+ cause: error,
1625
+ retryable: code === "SQLITE_BUSY" || code === "SQLITE_LOCKED"
1626
+ });
1627
+ }
1628
+ return new HarnessExecutionError(message, { cause: error });
1629
+ };
1630
+
1631
+ // src/runtime/config.ts
1632
+ var DEFAULT_ALLOWED_CHECKS = ["test", "typecheck", "lint", "build"];
1633
+ var HARNESS_CONFIG_SCHEMA_VERSION = 5;
1634
+ var HARNESS_CONFIG_MIGRATABLE_SCHEMA_VERSIONS = [4, 5];
1635
+ var HARNESS_STORE_BACKENDS = ["sqlite", "file"];
1636
+ var HARNESS_EXECUTION_BACKENDS = ["none", "oci"];
1637
+ var HARNESS_OCI_RUNTIMES = ["docker", "podman"];
1638
+ var HARNESS_OCI_SHELL_MODES = ["deny", "ask"];
1639
+ var HARNESS_EXECUTION_POLICY_VERSION = "2026-09-19-v5-scoped-oci";
1640
+ var DEFAULT_OCI_EXECUTION = {
1641
+ runtime: "docker",
1642
+ image: "node:24-bookworm-slim",
1643
+ allowedCommands: ["node", "npm"],
1644
+ shellMode: "deny",
1645
+ maxProcessRuntimeMs: 120000,
1646
+ maxProcessOutputBytes: 20000,
1647
+ maxMemoryMb: 1024,
1648
+ maxPids: 128,
1649
+ maxCpus: 2,
1650
+ maxWorkspaceBytes: 64 * 1024 * 1024,
1651
+ maxFileWriteBytes: 1024 * 1024,
1652
+ tmpfsMb: 256
1653
+ };
1654
+ var HARNESS_REQUIRED_CAPABILITIES = [
1655
+ "streaming",
1656
+ "tools",
1657
+ "structured-output",
1658
+ "parallel-tools",
1659
+ "reasoning",
1660
+ "web-search"
1661
+ ];
1662
+ var HARNESS_SUBAGENT_PROFILES = [
1663
+ "explorer",
1664
+ "implementer",
1665
+ "tester",
1666
+ "reviewer"
1667
+ ];
1668
+ var DEFAULT_HARNESS_MAX_STEPS = 50;
1669
+ var DEFAULT_HARNESS_BUDGET = {
1670
+ maxToolCalls: 32,
1671
+ maxToolErrors: 4,
1672
+ maxInputTokens: 1e5,
1673
+ maxOutputTokens: 30000,
1674
+ maxTotalTokens: 120000,
1675
+ includeChildRuns: true
1676
+ };
1677
+ var DEFAULT_HARNESS_COMPACTION = {
1678
+ maxMessages: 60,
1679
+ maxEstimatedInputTokens: 40000,
1680
+ keepRecentMessages: 12
1681
+ };
1682
+ var DEFAULT_SUBAGENT_BUDGET = {
1683
+ maxSteps: 8,
1684
+ maxToolCalls: 16,
1685
+ maxToolErrors: 3,
1686
+ maxInputTokens: 30000,
1687
+ maxOutputTokens: 8000,
1688
+ maxTotalTokens: 36000,
1689
+ includeChildRuns: false
1690
+ };
1691
+ var migrateHarnessConfigInput = (input) => {
1692
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
1693
+ throw new HarnessConfigError("Harness config migration requires an object.");
1694
+ }
1695
+ const fromVersion = input.schemaVersion;
1696
+ if (fromVersion !== 4 && fromVersion !== HARNESS_CONFIG_SCHEMA_VERSION) {
1697
+ throw new HarnessConfigError(`Unsupported config schema version for migration: ${String(fromVersion)}. ` + `Supported source versions: ${HARNESS_CONFIG_MIGRATABLE_SCHEMA_VERSIONS.join(", ")}.`);
1698
+ }
1699
+ if (fromVersion === HARNESS_CONFIG_SCHEMA_VERSION) {
1700
+ return {
1701
+ fromVersion,
1702
+ toVersion: HARNESS_CONFIG_SCHEMA_VERSION,
1703
+ config: { ...input },
1704
+ notes: []
1705
+ };
1706
+ }
1707
+ return {
1708
+ fromVersion,
1709
+ toVersion: HARNESS_CONFIG_SCHEMA_VERSION,
1710
+ config: {
1711
+ ...input,
1712
+ schemaVersion: HARNESS_CONFIG_SCHEMA_VERSION,
1713
+ projectContext: false,
1714
+ ...input.executionBackend === "oci" ? { ociShellMode: "deny" } : {}
1715
+ },
1716
+ notes: [
1717
+ "Project context remains disabled to preserve schema 4 behavior; enable it explicitly after reviewing repository instructions.",
1718
+ ...input.executionBackend === "oci" ? ["OCI shell exposure remains denied to preserve schema 4 behavior."] : []
1719
+ ]
1720
+ };
1721
+ };
1722
+ var integerOption = (name, input, envValue, fallback, minimum, maximum) => {
1723
+ const value = input ?? (envValue === undefined ? fallback : Number(envValue));
1724
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
1725
+ throw new HarnessConfigError(`${name} must be an integer between ${minimum} and ${maximum}.`);
1726
+ }
1727
+ return value;
1728
+ };
1729
+ var scopeValue = (name, value, fallback) => {
1730
+ const configured = value?.trim();
1731
+ const normalized = configured || fallback?.trim();
1732
+ if (!normalized) {
1733
+ return;
1734
+ }
1735
+ if (normalized.length > 128 || /[\u0000-\u001f\u007f]/.test(normalized)) {
1736
+ throw new HarnessConfigError(`${name} must contain 1-128 printable characters.`);
1737
+ }
1738
+ if (name === "userId" && normalized === "*") {
1739
+ throw new HarnessConfigError('userId cannot be the reserved "*" scope marker.');
1740
+ }
1741
+ return normalized;
1742
+ };
1743
+ var nonNegativeNumberOption = (name, input, envValue) => {
1744
+ if (input === undefined && envValue === undefined) {
1745
+ return;
1746
+ }
1747
+ const value = input ?? Number(envValue);
1748
+ if (!Number.isFinite(value) || value < 0) {
1749
+ throw new HarnessConfigError(`${name} must be a non-negative number.`);
1750
+ }
1751
+ return value;
1752
+ };
1753
+ var defaultHarnessNamespace = (workspace) => `workspace-${createHash2("sha256").update(workspace).digest("hex").slice(0, 16)}`;
1754
+ var canonicalStateDirectory = (requestedWorkspace, canonicalWorkspace, configured) => {
1755
+ if (!configured) {
1756
+ return path.join(canonicalWorkspace, ".zhivex-harness", "runs");
1757
+ }
1758
+ const requestedStateDirectory = path.resolve(configured);
1759
+ const relative = path.relative(requestedWorkspace, requestedStateDirectory);
1760
+ const insideRequestedWorkspace = relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
1761
+ return insideRequestedWorkspace ? path.resolve(canonicalWorkspace, relative) : requestedStateDirectory;
1762
+ };
1763
+ var resolveAllowedChecks = (configured) => {
1764
+ const source = configured ?? (process.env.ZHIVEX_HARNESS_ALLOWED_CHECKS === undefined ? DEFAULT_ALLOWED_CHECKS : process.env.ZHIVEX_HARNESS_ALLOWED_CHECKS.split(","));
1765
+ const checks = [...new Set(source.map((value) => value.trim()).filter(Boolean))];
1766
+ if (checks.length > 50) {
1767
+ throw new HarnessConfigError("allowedChecks cannot contain more than 50 script names.");
1768
+ }
1769
+ const invalid = checks.find((value) => !/^[A-Za-z0-9][A-Za-z0-9:_-]{0,63}$/.test(value));
1770
+ if (invalid) {
1771
+ throw new HarnessConfigError(`Invalid allowed check name: ${invalid}. Use 1-64 letters, digits, colon, underscore, or hyphen.`);
1772
+ }
1773
+ return checks;
1774
+ };
1775
+ var commaSeparatedValues = (configured, envValue, fallback) => configured ?? (envValue === undefined ? fallback : envValue.split(","));
1776
+ var resolveRequiredCapabilities = (configured) => {
1777
+ const values = commaSeparatedValues(configured, process.env.ZHIVEX_HARNESS_REQUIRED_CAPABILITIES, ["streaming", "tools"]);
1778
+ const capabilities = [...new Set(values.map((value) => value.trim()).filter(Boolean))];
1779
+ const invalid = capabilities.find((value) => !HARNESS_REQUIRED_CAPABILITIES.includes(value));
1780
+ if (invalid) {
1781
+ throw new HarnessConfigError(`Unknown required capability: ${invalid}. Use ${HARNESS_REQUIRED_CAPABILITIES.join(", ")}.`);
1782
+ }
1783
+ return capabilities;
1784
+ };
1785
+ var resolveSubagentProfiles = (configured) => {
1786
+ const values = commaSeparatedValues(configured, process.env.ZHIVEX_HARNESS_SUBAGENTS, HARNESS_SUBAGENT_PROFILES);
1787
+ const profiles = [...new Set(values.map((value) => value.trim()).filter(Boolean))];
1788
+ const invalid = profiles.find((value) => !HARNESS_SUBAGENT_PROFILES.includes(value));
1789
+ if (invalid) {
1790
+ throw new HarnessConfigError(`Unknown subagent profile: ${invalid}. Use ${HARNESS_SUBAGENT_PROFILES.join(", ")}.`);
1791
+ }
1792
+ return profiles;
1793
+ };
1794
+ var resolveOciAllowedCommands = (configured) => {
1795
+ const values = commaSeparatedValues(configured, process.env.ZHIVEX_HARNESS_OCI_ALLOWED_COMMANDS, DEFAULT_OCI_EXECUTION.allowedCommands);
1796
+ const commands = [...new Set(values.map((value) => value.trim()).filter(Boolean))];
1797
+ if (commands.length === 0 || commands.length > 50) {
1798
+ throw new HarnessConfigError("ociAllowedCommands must contain between 1 and 50 executable names.");
1799
+ }
1800
+ const invalid = commands.find((value) => !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/.test(value));
1801
+ if (invalid) {
1802
+ throw new HarnessConfigError(`Invalid OCI command name: ${invalid}. Use a bare executable name without a path.`);
1803
+ }
1804
+ if (!commands.some((command) => ["npm", "pnpm", "yarn", "bun"].includes(command))) {
1805
+ throw new HarnessConfigError("ociAllowedCommands must include npm, pnpm, yarn, or bun so declared package checks remain available.");
1806
+ }
1807
+ return commands;
1808
+ };
1809
+ var resolveExecutionConfig = (input) => {
1810
+ const backend = (input.executionBackend ?? process.env.ZHIVEX_HARNESS_EXECUTION ?? "none").trim().toLowerCase();
1811
+ if (!HARNESS_EXECUTION_BACKENDS.includes(backend)) {
1812
+ throw new HarnessConfigError(`executionBackend must be one of: ${HARNESS_EXECUTION_BACKENDS.join(", ")}.`);
1813
+ }
1814
+ if (backend === "none") {
1815
+ return { backend: "none" };
1816
+ }
1817
+ const runtime = (input.ociRuntime ?? process.env.ZHIVEX_HARNESS_OCI_RUNTIME ?? DEFAULT_OCI_EXECUTION.runtime).trim().toLowerCase();
1818
+ if (!HARNESS_OCI_RUNTIMES.includes(runtime)) {
1819
+ throw new HarnessConfigError(`ociRuntime must be one of: ${HARNESS_OCI_RUNTIMES.join(", ")}.`);
1820
+ }
1821
+ const image = (input.ociImage ?? process.env.ZHIVEX_HARNESS_OCI_IMAGE ?? DEFAULT_OCI_EXECUTION.image).trim();
1822
+ if (!image || image.length > 512 || /[\u0000-\u001f\u007f\s]/.test(image) || image.startsWith("-")) {
1823
+ throw new HarnessConfigError("ociImage must be a non-empty OCI image reference without whitespace or control characters.");
1824
+ }
1825
+ const shellMode = (input.ociShellMode ?? process.env.ZHIVEX_HARNESS_OCI_SHELL ?? DEFAULT_OCI_EXECUTION.shellMode).trim().toLowerCase();
1826
+ if (!HARNESS_OCI_SHELL_MODES.includes(shellMode)) {
1827
+ throw new HarnessConfigError(`ociShellMode must be one of: ${HARNESS_OCI_SHELL_MODES.join(", ")}.`);
1828
+ }
1829
+ const maxWorkspaceBytes = integerOption("ociMaxWorkspaceBytes", input.ociMaxWorkspaceBytes, process.env.ZHIVEX_HARNESS_OCI_MAX_WORKSPACE_BYTES, DEFAULT_OCI_EXECUTION.maxWorkspaceBytes, 1024 * 1024, 1024 * 1024 * 1024);
1830
+ const maxFileWriteBytes = integerOption("ociMaxFileWriteBytes", input.ociMaxFileWriteBytes, process.env.ZHIVEX_HARNESS_OCI_MAX_FILE_WRITE_BYTES, DEFAULT_OCI_EXECUTION.maxFileWriteBytes, 1024, 16 * 1024 * 1024);
1831
+ if (maxFileWriteBytes > maxWorkspaceBytes) {
1832
+ throw new HarnessConfigError("ociMaxFileWriteBytes cannot exceed ociMaxWorkspaceBytes.");
1833
+ }
1834
+ return {
1835
+ backend: "oci",
1836
+ policyVersion: HARNESS_EXECUTION_POLICY_VERSION,
1837
+ runtime,
1838
+ image,
1839
+ allowedCommands: resolveOciAllowedCommands(input.ociAllowedCommands),
1840
+ shellMode,
1841
+ maxProcessRuntimeMs: integerOption("ociMaxProcessRuntimeMs", input.ociMaxProcessRuntimeMs, process.env.ZHIVEX_HARNESS_OCI_MAX_PROCESS_RUNTIME_MS, DEFAULT_OCI_EXECUTION.maxProcessRuntimeMs, 1000, 60 * 60000),
1842
+ maxProcessOutputBytes: integerOption("ociMaxProcessOutputBytes", input.ociMaxProcessOutputBytes, process.env.ZHIVEX_HARNESS_OCI_MAX_PROCESS_OUTPUT_BYTES, DEFAULT_OCI_EXECUTION.maxProcessOutputBytes, 1024, 1024 * 1024),
1843
+ maxMemoryMb: integerOption("ociMaxMemoryMb", input.ociMaxMemoryMb, process.env.ZHIVEX_HARNESS_OCI_MAX_MEMORY_MB, DEFAULT_OCI_EXECUTION.maxMemoryMb, 64, 64 * 1024),
1844
+ maxPids: integerOption("ociMaxPids", input.ociMaxPids, process.env.ZHIVEX_HARNESS_OCI_MAX_PIDS, DEFAULT_OCI_EXECUTION.maxPids, 16, 4096),
1845
+ maxCpus: integerOption("ociMaxCpus", input.ociMaxCpus, process.env.ZHIVEX_HARNESS_OCI_MAX_CPUS, DEFAULT_OCI_EXECUTION.maxCpus, 1, 64),
1846
+ maxWorkspaceBytes,
1847
+ maxFileWriteBytes,
1848
+ tmpfsMb: integerOption("ociTmpfsMb", input.ociTmpfsMb, process.env.ZHIVEX_HARNESS_OCI_TMPFS_MB, DEFAULT_OCI_EXECUTION.tmpfsMb, 16, 4096)
1849
+ };
1850
+ };
1851
+ var canonicalWorkspaceFile = (requestedWorkspace, canonicalWorkspace, configured) => {
1852
+ if (!configured?.trim()) {
1853
+ return;
1854
+ }
1855
+ const requestedPath = path.resolve(requestedWorkspace, configured.trim());
1856
+ const relative = path.relative(requestedWorkspace, requestedPath);
1857
+ const insideRequestedWorkspace = relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
1858
+ return insideRequestedWorkspace ? path.resolve(canonicalWorkspace, relative) : requestedPath;
1859
+ };
1860
+ var canonicalProjectContextFile = (requestedWorkspace, canonicalWorkspace, configured) => {
1861
+ const candidate = canonicalWorkspaceFile(requestedWorkspace, canonicalWorkspace, configured);
1862
+ const relative = path.relative(canonicalWorkspace, candidate);
1863
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
1864
+ throw new HarnessConfigError("contextConfigPath must resolve inside the workspace.");
1865
+ }
1866
+ return candidate;
1867
+ };
1868
+ var parseProvider = (value, registry = DEFAULT_PROVIDER_REGISTRY) => {
1869
+ try {
1870
+ return registry.parse(value);
1871
+ } catch (error) {
1872
+ if (error instanceof HarnessConfigError)
1873
+ throw error;
1874
+ throw new HarnessConfigError(error instanceof Error ? error.message : String(error), { cause: error });
1875
+ }
1876
+ };
1877
+ var providerDescriptor = (provider, registry = DEFAULT_PROVIDER_REGISTRY) => registry.descriptor(provider);
1878
+ var resolveHarnessConfig = (input = {}, registry = DEFAULT_PROVIDER_REGISTRY) => {
1879
+ if (input.schemaVersion !== undefined && input.schemaVersion !== HARNESS_CONFIG_SCHEMA_VERSION) {
1880
+ throw new HarnessConfigError(`Unsupported config schema version: ${input.schemaVersion}. Expected ${HARNESS_CONFIG_SCHEMA_VERSION}.`);
1881
+ }
1882
+ const provider = parseProvider(input.provider ?? process.env.ZHIVEX_HARNESS_PROVIDER, registry);
1883
+ const descriptor = providerDescriptor(provider, registry);
1884
+ const requestedWorkspace = path.resolve(input.workspace ?? process.cwd());
1885
+ let workspace = requestedWorkspace;
1886
+ try {
1887
+ workspace = realpathSync(requestedWorkspace);
1888
+ } catch {}
1889
+ const maxSteps = integerOption("maxSteps", input.maxSteps, process.env.ZHIVEX_HARNESS_MAX_STEPS, DEFAULT_HARNESS_MAX_STEPS, 1, Number.MAX_SAFE_INTEGER);
1890
+ const storeBackend = (input.storeBackend ?? process.env.ZHIVEX_HARNESS_STORE ?? "sqlite").trim().toLowerCase();
1891
+ if (!HARNESS_STORE_BACKENDS.includes(storeBackend)) {
1892
+ throw new HarnessConfigError(`storeBackend must be one of: ${HARNESS_STORE_BACKENDS.join(", ")}.`);
1893
+ }
1894
+ const tenantId = scopeValue("tenantId", input.tenantId ?? process.env.ZHIVEX_HARNESS_TENANT_ID, "local");
1895
+ const userId = scopeValue("userId", input.userId ?? process.env.ZHIVEX_HARNESS_USER_ID);
1896
+ const namespace = scopeValue("namespace", input.namespace ?? process.env.ZHIVEX_HARNESS_NAMESPACE, defaultHarnessNamespace(workspace));
1897
+ const timeoutMs = integerOption("timeoutMs", input.timeoutMs, process.env.ZHIVEX_HARNESS_TIMEOUT_MS, 15 * 60000, 1000, 24 * 60 * 60000);
1898
+ if (input.unlimitedTokens !== undefined && typeof input.unlimitedTokens !== "boolean") {
1899
+ throw new HarnessConfigError("unlimitedTokens must be a boolean.");
1900
+ }
1901
+ const tokenMode = input.unlimitedTokens === undefined ? {} : { unlimitedTokens: input.unlimitedTokens };
1902
+ const budget = {
1903
+ ...tokenMode,
1904
+ maxSteps,
1905
+ maxToolCalls: integerOption("maxToolCalls", input.maxToolCalls, process.env.ZHIVEX_HARNESS_MAX_TOOL_CALLS, DEFAULT_HARNESS_BUDGET.maxToolCalls, 0, Number.MAX_SAFE_INTEGER),
1906
+ maxToolErrors: integerOption("maxToolErrors", input.maxToolErrors, process.env.ZHIVEX_HARNESS_MAX_TOOL_ERRORS, DEFAULT_HARNESS_BUDGET.maxToolErrors, 0, 100),
1907
+ maxInputTokens: integerOption("maxInputTokens", input.maxInputTokens, process.env.ZHIVEX_HARNESS_MAX_INPUT_TOKENS, DEFAULT_HARNESS_BUDGET.maxInputTokens, 1, 1e7),
1908
+ maxOutputTokens: integerOption("maxOutputTokens", input.maxOutputTokens, process.env.ZHIVEX_HARNESS_MAX_OUTPUT_TOKENS, DEFAULT_HARNESS_BUDGET.maxOutputTokens, 1, 1e7),
1909
+ maxTotalTokens: integerOption("maxTotalTokens", input.maxTotalTokens, process.env.ZHIVEX_HARNESS_MAX_TOTAL_TOKENS, DEFAULT_HARNESS_BUDGET.maxTotalTokens, 1, 20000000),
1910
+ includeChildRuns: true
1911
+ };
1912
+ if (budget.maxTotalTokens < budget.maxInputTokens || budget.maxTotalTokens < budget.maxOutputTokens) {
1913
+ throw new HarnessConfigError("maxTotalTokens must be greater than or equal to the input and output token limits.");
1914
+ }
1915
+ const maxCostUsd = nonNegativeNumberOption("maxCostUsd", input.maxCostUsd, process.env.ZHIVEX_HARNESS_MAX_COST_USD);
1916
+ const inputCostPerMillion = nonNegativeNumberOption("inputCostPerMillion", input.inputCostPerMillion, process.env.ZHIVEX_HARNESS_INPUT_COST_PER_MILLION);
1917
+ const outputCostPerMillion = nonNegativeNumberOption("outputCostPerMillion", input.outputCostPerMillion, process.env.ZHIVEX_HARNESS_OUTPUT_COST_PER_MILLION);
1918
+ if (maxCostUsd !== undefined && inputCostPerMillion === undefined && outputCostPerMillion === undefined) {
1919
+ throw new HarnessConfigError("maxCostUsd requires inputCostPerMillion or outputCostPerMillion pricing.");
1920
+ }
1921
+ if (maxCostUsd === undefined && (inputCostPerMillion !== undefined || outputCostPerMillion !== undefined)) {
1922
+ throw new HarnessConfigError("Token pricing requires maxCostUsd.");
1923
+ }
1924
+ const compaction = {
1925
+ ...input.compactionModel ? { model: { provider: input.compactionProvider ?? provider, model: input.compactionModel } } : {},
1926
+ maxMessages: integerOption("compactionMaxMessages", input.compactionMaxMessages, process.env.ZHIVEX_HARNESS_COMPACTION_MAX_MESSAGES, DEFAULT_HARNESS_COMPACTION.maxMessages, 4, 1e4),
1927
+ maxEstimatedInputTokens: integerOption("compactionMaxEstimatedInputTokens", input.compactionMaxEstimatedInputTokens, process.env.ZHIVEX_HARNESS_COMPACTION_MAX_INPUT_TOKENS, DEFAULT_HARNESS_COMPACTION.maxEstimatedInputTokens, 1000, 1e7),
1928
+ keepRecentMessages: integerOption("compactionKeepRecentMessages", input.compactionKeepRecentMessages, process.env.ZHIVEX_HARNESS_COMPACTION_KEEP_RECENT, DEFAULT_HARNESS_COMPACTION.keepRecentMessages, 2, 1000)
1929
+ };
1930
+ if (compaction.keepRecentMessages >= compaction.maxMessages) {
1931
+ throw new HarnessConfigError("compactionKeepRecentMessages must be smaller than compactionMaxMessages.");
1932
+ }
1933
+ if (input.compactionProvider && !input.compactionModel)
1934
+ throw new HarnessConfigError("compactionProvider requires compactionModel.");
1935
+ if (compaction.model && (!/^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,159}$/.test(compaction.model.model) || !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/.test(compaction.model.provider)))
1936
+ throw new HarnessConfigError("Invalid compaction model route.");
1937
+ const childBudget = {
1938
+ ...tokenMode,
1939
+ maxSteps: integerOption("subagentMaxSteps", input.subagentMaxSteps, process.env.ZHIVEX_HARNESS_SUBAGENT_MAX_STEPS, DEFAULT_SUBAGENT_BUDGET.maxSteps, 1, Number.MAX_SAFE_INTEGER),
1940
+ maxToolCalls: integerOption("subagentMaxToolCalls", input.subagentMaxToolCalls, process.env.ZHIVEX_HARNESS_SUBAGENT_MAX_TOOL_CALLS, DEFAULT_SUBAGENT_BUDGET.maxToolCalls, 0, Number.MAX_SAFE_INTEGER),
1941
+ maxToolErrors: integerOption("subagentMaxToolErrors", input.subagentMaxToolErrors, process.env.ZHIVEX_HARNESS_SUBAGENT_MAX_TOOL_ERRORS, DEFAULT_SUBAGENT_BUDGET.maxToolErrors, 0, 50),
1942
+ maxInputTokens: integerOption("subagentMaxInputTokens", input.subagentMaxInputTokens, process.env.ZHIVEX_HARNESS_SUBAGENT_MAX_INPUT_TOKENS, DEFAULT_SUBAGENT_BUDGET.maxInputTokens, 1, 2000000),
1943
+ maxOutputTokens: integerOption("subagentMaxOutputTokens", input.subagentMaxOutputTokens, process.env.ZHIVEX_HARNESS_SUBAGENT_MAX_OUTPUT_TOKENS, DEFAULT_SUBAGENT_BUDGET.maxOutputTokens, 1, 2000000),
1944
+ maxTotalTokens: integerOption("subagentMaxTotalTokens", input.subagentMaxTotalTokens, process.env.ZHIVEX_HARNESS_SUBAGENT_MAX_TOTAL_TOKENS, DEFAULT_SUBAGENT_BUDGET.maxTotalTokens, 1, 4000000),
1945
+ includeChildRuns: false
1946
+ };
1947
+ if (childBudget.maxTotalTokens < childBudget.maxInputTokens || childBudget.maxTotalTokens < childBudget.maxOutputTokens) {
1948
+ throw new HarnessConfigError("subagentMaxTotalTokens must be greater than or equal to the child input and output token limits.");
1949
+ }
1950
+ const orchestration = {
1951
+ profiles: resolveSubagentProfiles(input.subagentProfiles),
1952
+ childBudget,
1953
+ childTimeoutMs: integerOption("subagentTimeoutMs", input.subagentTimeoutMs, process.env.ZHIVEX_HARNESS_SUBAGENT_TIMEOUT_MS, Math.min(timeoutMs, 5 * 60000), 1000, 24 * 60 * 60000),
1954
+ maxParallelReviews: integerOption("maxParallelReviews", input.maxParallelReviews, process.env.ZHIVEX_HARNESS_MAX_PARALLEL_REVIEWS, 2, 1, 4)
1955
+ };
1956
+ const mcpConfigPath = canonicalWorkspaceFile(requestedWorkspace, workspace, input.mcpConfigPath ?? process.env.ZHIVEX_HARNESS_MCP_CONFIG);
1957
+ const projectContext = input.projectContext ?? process.env.ZHIVEX_HARNESS_PROJECT_CONTEXT !== "0";
1958
+ const contextConfigPath = canonicalProjectContextFile(requestedWorkspace, workspace, input.contextConfigPath ?? process.env.ZHIVEX_HARNESS_CONTEXT_CONFIG ?? ".zhivex/harness.json");
1959
+ const execution = resolveExecutionConfig(input);
1960
+ if (input.requireVerifiedDelivery !== undefined && typeof input.requireVerifiedDelivery !== "boolean")
1961
+ throw new HarnessConfigError("requireVerifiedDelivery must be boolean.");
1962
+ return {
1963
+ schemaVersion: HARNESS_CONFIG_SCHEMA_VERSION,
1964
+ provider,
1965
+ model: input.model ?? process.env.ZHIVEX_HARNESS_MODEL ?? descriptor.defaultModel,
1966
+ ...input.reasoningEffort === undefined || input.reasoningEffort === "default" ? {} : { reasoningEffort: reasoningEffortSchema.parse(input.reasoningEffort) },
1967
+ workspace,
1968
+ stateDirectory: canonicalStateDirectory(requestedWorkspace, workspace, input.stateDirectory),
1969
+ storeBackend,
1970
+ scope: {
1971
+ tenantId,
1972
+ ...userId ? { userId } : {},
1973
+ ...namespace ? { namespace } : {}
1974
+ },
1975
+ maxSteps,
1976
+ timeoutMs,
1977
+ requireVerifiedDelivery: input.requireVerifiedDelivery ?? false,
1978
+ budget,
1979
+ ...maxCostUsd === undefined ? {} : {
1980
+ costBudget: {
1981
+ maxCostUsd,
1982
+ inputCostPer1kTokens: (inputCostPerMillion ?? 0) / 1000,
1983
+ outputCostPer1kTokens: (outputCostPerMillion ?? 0) / 1000
1984
+ }
1985
+ },
1986
+ compaction,
1987
+ allowedChecks: resolveAllowedChecks(input.allowedChecks),
1988
+ requiredCapabilities: resolveRequiredCapabilities(input.requiredCapabilities),
1989
+ orchestration,
1990
+ context: {
1991
+ enabled: projectContext,
1992
+ configPath: contextConfigPath
1993
+ },
1994
+ execution,
1995
+ ...mcpConfigPath ? { mcpConfigPath } : {}
1996
+ };
1997
+ };
1998
+ var createProviderModel = (config, env = process.env, registry = DEFAULT_PROVIDER_REGISTRY) => {
1999
+ try {
2000
+ return registry.createModel(config, env);
2001
+ } catch (error) {
2002
+ throw new HarnessProviderError(`Provider ${config.provider} could not be initialized.`, {
2003
+ cause: error,
2004
+ retryable: false
2005
+ });
2006
+ }
2007
+ };
2008
+ var providerAvailability = (env = process.env, registry = DEFAULT_PROVIDER_REGISTRY) => registry.availability(env);
2009
+ export { HARNESS_ERROR_CODES, HARNESS_ERROR_SCHEMA_VERSION, HarnessError, HarnessConfigError, HarnessWorkspaceError, HarnessStateConflictError, HarnessProviderError, HarnessExecutionError, harnessErrorDocument, normalizeHarnessError, modelCatalogSchema, bundledModelCatalog, catalogModels, modelDescription, PROVIDERS, createProviderRegistry, BUILTIN_PROVIDER_REGISTRATIONS, DEFAULT_PROVIDER_REGISTRY, PROVIDER_DESCRIPTORS, reasoningEffortSchema, modelReasoningEfforts, withReasoningEffort, DEFAULT_ALLOWED_CHECKS, HARNESS_CONFIG_SCHEMA_VERSION, HARNESS_CONFIG_MIGRATABLE_SCHEMA_VERSIONS, HARNESS_STORE_BACKENDS, HARNESS_EXECUTION_BACKENDS, HARNESS_OCI_RUNTIMES, HARNESS_OCI_SHELL_MODES, HARNESS_EXECUTION_POLICY_VERSION, DEFAULT_OCI_EXECUTION, HARNESS_REQUIRED_CAPABILITIES, HARNESS_SUBAGENT_PROFILES, DEFAULT_HARNESS_MAX_STEPS, DEFAULT_HARNESS_BUDGET, DEFAULT_HARNESS_COMPACTION, DEFAULT_SUBAGENT_BUDGET, migrateHarnessConfigInput, defaultHarnessNamespace, parseProvider, providerDescriptor, resolveHarnessConfig, createProviderModel, providerAvailability };