@zhivex-ai/harness 0.6.1 → 0.8.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.
package/dist/index.js CHANGED
@@ -1,12 +1,359 @@
1
1
  // @bun
2
2
  // src/config.ts
3
- import { createHash } from "crypto";
3
+ import { createHash as createHash2 } from "crypto";
4
4
  import { realpathSync } from "fs";
5
5
  import path from "path";
6
+
7
+ // src/providers.ts
8
+ import { createHash } from "crypto";
9
+ import { createGemini } from "@zhivex-ai/gemini";
6
10
  import { createMeta } from "@zhivex-ai/meta";
7
11
  import { createOpenAI } from "@zhivex-ai/openai";
8
12
  import { createQwen } from "@zhivex-ai/qwen";
9
- var PROVIDERS = ["meta", "qwen", "openai"];
13
+ var PROVIDERS = ["meta", "qwen", "openai", "gemini"];
14
+ var HARNESS_PROVIDER_CAPABILITIES = [
15
+ "streaming",
16
+ "tool-calling",
17
+ "approval-resume"
18
+ ];
19
+ var QWEN_REGIONS = [
20
+ "singapore",
21
+ "beijing",
22
+ "hong-kong",
23
+ "tokyo",
24
+ "frankfurt",
25
+ "virginia"
26
+ ];
27
+ var PROVIDER_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
28
+ var ENVIRONMENT_VARIABLE = /^[A-Z_][A-Z0-9_]{0,127}$/;
29
+ var DIAGNOSTIC_KEY = /^[a-z][A-Za-z0-9]{0,63}$/;
30
+ var requiredText = (name, value) => {
31
+ const normalized = value.trim();
32
+ if (!normalized || /[\u0000-\u001f\u007f]/.test(normalized)) {
33
+ throw new Error(`${name} must be a non-empty printable string.`);
34
+ }
35
+ return normalized;
36
+ };
37
+ var environmentVariable = (name, value) => {
38
+ if (!ENVIRONMENT_VARIABLE.test(value)) {
39
+ throw new Error(`${name} must be a valid environment variable name.`);
40
+ }
41
+ return value;
42
+ };
43
+ var diagnosticKey = (value) => {
44
+ if (!DIAGNOSTIC_KEY.test(value)) {
45
+ throw new Error(`Invalid provider diagnostic key: ${value}.`);
46
+ }
47
+ return value;
48
+ };
49
+ var normalizedDescriptor = (descriptor) => {
50
+ const id = descriptor.id.trim().toLowerCase();
51
+ if (descriptor.id !== id || !PROVIDER_ID.test(id)) {
52
+ throw new Error(`Invalid provider id: ${descriptor.id}. Use 1-64 lowercase letters, digits, dot, underscore, or hyphen.`);
53
+ }
54
+ if (!(descriptor.support === "certified" || descriptor.support === "provisional")) {
55
+ throw new Error(`Invalid support level for provider ${id}.`);
56
+ }
57
+ const credentialNames = [...new Set(descriptor.credentialNames.map((name) => environmentVariable(`Credential for provider ${id}`, name)))];
58
+ const capabilities = [...new Set(descriptor.capabilities)];
59
+ const invalidCapability = capabilities.find((capability) => !HARNESS_PROVIDER_CAPABILITIES.includes(capability));
60
+ if (invalidCapability) {
61
+ throw new Error(`Invalid capability for provider ${id}: ${invalidCapability}.`);
62
+ }
63
+ return Object.freeze({
64
+ id,
65
+ name: requiredText(`Name for provider ${id}`, descriptor.name),
66
+ defaultModel: requiredText(`Default model for provider ${id}`, descriptor.defaultModel),
67
+ credentialNames: Object.freeze(credentialNames),
68
+ capabilities: Object.freeze(capabilities),
69
+ support: descriptor.support
70
+ });
71
+ };
72
+ var normalizedDiagnostics = (provider, diagnostics) => {
73
+ const endpointEnvironmentVariable = diagnostics?.endpointEnvironmentVariable === undefined ? undefined : environmentVariable(`Endpoint diagnostic for provider ${provider}`, diagnostics.endpointEnvironmentVariable);
74
+ const seenKeys = new Set;
75
+ const presence = (diagnostics?.presence ?? []).map((entry) => {
76
+ const key = diagnosticKey(entry.key);
77
+ if (seenKeys.has(key)) {
78
+ throw new Error(`Duplicate diagnostic key for provider ${provider}: ${key}.`);
79
+ }
80
+ seenKeys.add(key);
81
+ return Object.freeze({
82
+ key,
83
+ environmentVariable: environmentVariable(`Presence diagnostic for provider ${provider}`, entry.environmentVariable)
84
+ });
85
+ });
86
+ const enums = (diagnostics?.enums ?? []).map((entry) => {
87
+ const key = diagnosticKey(entry.key);
88
+ if (seenKeys.has(key)) {
89
+ throw new Error(`Duplicate diagnostic key for provider ${provider}: ${key}.`);
90
+ }
91
+ seenKeys.add(key);
92
+ const allowedValues = [...new Set(entry.allowedValues.map((value) => requiredText(`Allowed diagnostic value for provider ${provider}`, value)))];
93
+ if (allowedValues.length === 0) {
94
+ throw new Error(`Enum diagnostic ${key} for provider ${provider} needs allowed values.`);
95
+ }
96
+ return Object.freeze({
97
+ key,
98
+ environmentVariable: environmentVariable(`Enum diagnostic for provider ${provider}`, entry.environmentVariable),
99
+ allowedValues: Object.freeze(allowedValues)
100
+ });
101
+ });
102
+ return Object.freeze({
103
+ ...endpointEnvironmentVariable ? { endpointEnvironmentVariable } : {},
104
+ ...presence.length > 0 ? { presence: Object.freeze(presence) } : {},
105
+ ...enums.length > 0 ? { enums: Object.freeze(enums) } : {}
106
+ });
107
+ };
108
+ var endpointConfiguration = (value) => {
109
+ const configured = Boolean(value?.trim());
110
+ if (!configured) {
111
+ return {
112
+ customEndpoint: false,
113
+ endpointValid: true,
114
+ endpointSecure: true
115
+ };
116
+ }
117
+ try {
118
+ const endpoint = new URL(value.trim());
119
+ const supportedProtocol = endpoint.protocol === "https:" || endpoint.protocol === "http:";
120
+ return {
121
+ customEndpoint: true,
122
+ endpointValid: supportedProtocol && !endpoint.username && !endpoint.password && !endpoint.hash && !endpoint.search,
123
+ endpointSecure: endpoint.protocol === "https:"
124
+ };
125
+ } catch {
126
+ return {
127
+ customEndpoint: true,
128
+ endpointValid: false,
129
+ endpointSecure: false
130
+ };
131
+ }
132
+ };
133
+ var createCredentials = (env, provider, names) => Object.freeze({
134
+ names,
135
+ get: (name) => {
136
+ if (!names.includes(name)) {
137
+ throw new Error(`Credential ${name} is not declared for provider ${provider}.`);
138
+ }
139
+ return env[name]?.trim() || undefined;
140
+ },
141
+ first: () => {
142
+ for (const name of names) {
143
+ const value = env[name]?.trim();
144
+ if (value) {
145
+ return value;
146
+ }
147
+ }
148
+ return;
149
+ },
150
+ require: () => {
151
+ for (const name of names) {
152
+ const value = env[name]?.trim();
153
+ if (value) {
154
+ return value;
155
+ }
156
+ }
157
+ throw new Error(names.length === 0 ? `Provider ${provider} does not declare a credential.` : `Missing credentials for ${provider}. Set ${names.join(" or ")} in the environment.`);
158
+ }
159
+ });
160
+ var normalizeRegistration = (registration) => {
161
+ if (typeof registration.factory !== "function") {
162
+ throw new Error("Provider registration factory must be a function.");
163
+ }
164
+ const descriptor = normalizedDescriptor(registration.descriptor);
165
+ return Object.freeze({
166
+ descriptor,
167
+ factory: registration.factory,
168
+ diagnostics: normalizedDiagnostics(descriptor.id, registration.diagnostics)
169
+ });
170
+ };
171
+ var providerConfiguration = (diagnostics, env) => {
172
+ const configuration = endpointConfiguration(diagnostics.endpointEnvironmentVariable ? env[diagnostics.endpointEnvironmentVariable] : undefined);
173
+ for (const entry of diagnostics.presence ?? []) {
174
+ configuration[`${entry.key}Configured`] = Boolean(env[entry.environmentVariable]?.trim());
175
+ }
176
+ for (const entry of diagnostics.enums ?? []) {
177
+ const value = env[entry.environmentVariable]?.trim();
178
+ configuration[`${entry.key}Configured`] = Boolean(value);
179
+ configuration[`${entry.key}Valid`] = !value || entry.allowedValues.includes(value);
180
+ }
181
+ return Object.freeze(configuration);
182
+ };
183
+ var parseWithRegistrations = (value, registrations) => {
184
+ const defaultProvider = registrations.has("openai") ? "openai" : registrations.keys().next().value;
185
+ const normalized = (value ?? defaultProvider ?? "").trim().toLowerCase();
186
+ if (registrations.has(normalized)) {
187
+ return normalized;
188
+ }
189
+ throw new Error(`Unknown provider: ${value}. Use ${[...registrations.keys()].join(", ")}.`);
190
+ };
191
+ var createProviderRegistry = (registrations) => {
192
+ if (registrations.length === 0) {
193
+ throw new Error("Provider registry must contain at least one provider.");
194
+ }
195
+ const entries = new Map;
196
+ for (const registration of registrations) {
197
+ const normalized = normalizeRegistration(registration);
198
+ if (entries.has(normalized.descriptor.id)) {
199
+ throw new Error(`Duplicate provider registration: ${normalized.descriptor.id}.`);
200
+ }
201
+ entries.set(normalized.descriptor.id, normalized);
202
+ }
203
+ const frozenRegistrations = Object.freeze([...entries.values()]);
204
+ const descriptors = Object.freeze(frozenRegistrations.map((entry) => entry.descriptor));
205
+ const registry = Object.freeze({
206
+ descriptors,
207
+ has: (provider) => entries.has(provider.trim().toLowerCase()),
208
+ parse: (value) => parseWithRegistrations(value, entries),
209
+ descriptor: (provider) => {
210
+ const registration = entries.get(provider);
211
+ if (!registration) {
212
+ throw new Error(`No descriptor is available for provider ${provider}.`);
213
+ }
214
+ return registration.descriptor;
215
+ },
216
+ createModel: (config, env = process.env) => {
217
+ const registration = entries.get(config.provider);
218
+ if (!registration) {
219
+ throw new Error(`No factory is available for provider ${config.provider}.`);
220
+ }
221
+ return registration.factory(Object.freeze({
222
+ provider: config.provider,
223
+ model: config.model,
224
+ env,
225
+ credentials: createCredentials(env, config.provider, registration.descriptor.credentialNames)
226
+ }));
227
+ },
228
+ availability: (env = process.env) => Object.freeze(frozenRegistrations.map((registration) => {
229
+ const credentials = Object.freeze(registration.descriptor.credentialNames.map((name) => Object.freeze({ name, present: Boolean(env[name]?.trim()) })));
230
+ return Object.freeze({
231
+ ...registration.descriptor,
232
+ configured: credentials.length === 0 || credentials.some((credential) => credential.present),
233
+ credentials,
234
+ configuration: providerConfiguration(registration.diagnostics, env)
235
+ });
236
+ })),
237
+ transportFingerprint: (env = process.env) => {
238
+ const transport = frozenRegistrations.map((registration) => {
239
+ const names = [
240
+ registration.diagnostics.endpointEnvironmentVariable,
241
+ ...(registration.diagnostics.presence ?? []).map((entry) => entry.environmentVariable),
242
+ ...(registration.diagnostics.enums ?? []).map((entry) => entry.environmentVariable)
243
+ ].filter((name) => Boolean(name));
244
+ return {
245
+ provider: registration.descriptor.id,
246
+ values: names.map((name) => [name, env[name]?.trim() ?? ""])
247
+ };
248
+ });
249
+ return `sha256:${createHash("sha256").update(JSON.stringify(transport)).digest("hex")}`;
250
+ },
251
+ extend: (additional) => createProviderRegistry([
252
+ ...frozenRegistrations,
253
+ ...additional
254
+ ])
255
+ });
256
+ return registry;
257
+ };
258
+ var optionalQwenRegion = (value) => {
259
+ if (!value) {
260
+ return;
261
+ }
262
+ if (QWEN_REGIONS.includes(value)) {
263
+ return value;
264
+ }
265
+ throw new Error(`Invalid QWEN_REGION: ${value}.`);
266
+ };
267
+ var BUILTIN_PROVIDER_REGISTRATIONS = Object.freeze([
268
+ {
269
+ descriptor: {
270
+ id: "meta",
271
+ name: "Meta Model API",
272
+ defaultModel: "muse-spark-1.2",
273
+ credentialNames: ["MODEL_API_KEY"],
274
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
275
+ support: "certified"
276
+ },
277
+ diagnostics: { endpointEnvironmentVariable: "META_BASE_URL" },
278
+ factory: ({ model, env, credentials }) => {
279
+ const baseURL = env.META_BASE_URL?.trim();
280
+ return createMeta({
281
+ apiKey: credentials.require(),
282
+ ...baseURL ? { baseURL } : {}
283
+ })(model);
284
+ }
285
+ },
286
+ {
287
+ descriptor: {
288
+ id: "qwen",
289
+ name: "Qwen / Alibaba Cloud Model Studio",
290
+ defaultModel: "qwen3.8-max",
291
+ credentialNames: ["DASHSCOPE_API_KEY", "QWEN_API_KEY"],
292
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
293
+ support: "certified"
294
+ },
295
+ diagnostics: {
296
+ endpointEnvironmentVariable: "QWEN_BASE_URL",
297
+ presence: [{ key: "workspaceId", environmentVariable: "QWEN_WORKSPACE_ID" }],
298
+ enums: [{
299
+ key: "region",
300
+ environmentVariable: "QWEN_REGION",
301
+ allowedValues: QWEN_REGIONS
302
+ }]
303
+ },
304
+ factory: ({ model, env, credentials }) => {
305
+ const baseURL = env.QWEN_BASE_URL?.trim();
306
+ const workspaceId = env.QWEN_WORKSPACE_ID?.trim();
307
+ const region = optionalQwenRegion(env.QWEN_REGION?.trim());
308
+ return createQwen({
309
+ apiKey: credentials.require(),
310
+ ...baseURL ? { baseURL } : {},
311
+ ...workspaceId ? { workspaceId } : {},
312
+ ...region ? { region } : {}
313
+ })(model);
314
+ }
315
+ },
316
+ {
317
+ descriptor: {
318
+ id: "openai",
319
+ name: "OpenAI",
320
+ defaultModel: "gpt-5.6-luna",
321
+ credentialNames: ["OPENAI_API_KEY"],
322
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
323
+ support: "certified"
324
+ },
325
+ diagnostics: { endpointEnvironmentVariable: "OPENAI_BASE_URL" },
326
+ factory: ({ model, env, credentials }) => {
327
+ const baseURL = env.OPENAI_BASE_URL?.trim();
328
+ return createOpenAI({
329
+ apiKey: credentials.require(),
330
+ ...baseURL ? { baseURL } : {}
331
+ })(model);
332
+ }
333
+ },
334
+ {
335
+ descriptor: {
336
+ id: "gemini",
337
+ name: "Google Gemini",
338
+ defaultModel: "gemini-3.6-flash",
339
+ credentialNames: ["GEMINI_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"],
340
+ capabilities: HARNESS_PROVIDER_CAPABILITIES,
341
+ support: "provisional"
342
+ },
343
+ diagnostics: { endpointEnvironmentVariable: "GEMINI_BASE_URL" },
344
+ factory: ({ model, env, credentials }) => {
345
+ const baseURL = env.GEMINI_BASE_URL?.trim();
346
+ return createGemini({
347
+ apiKey: credentials.require(),
348
+ ...baseURL ? { baseURL } : {}
349
+ })(model);
350
+ }
351
+ }
352
+ ]);
353
+ var DEFAULT_PROVIDER_REGISTRY = createProviderRegistry(BUILTIN_PROVIDER_REGISTRATIONS);
354
+ var PROVIDER_DESCRIPTORS = DEFAULT_PROVIDER_REGISTRY.descriptors;
355
+
356
+ // src/config.ts
10
357
  var DEFAULT_ALLOWED_CHECKS = ["test", "typecheck", "lint", "build"];
11
358
  var HARNESS_CONFIG_SCHEMA_VERSION = 4;
12
359
  var HARNESS_STORE_BACKENDS = ["sqlite", "file"];
@@ -62,45 +409,6 @@ var DEFAULT_SUBAGENT_BUDGET = {
62
409
  maxTotalTokens: 36000,
63
410
  includeChildRuns: false
64
411
  };
65
- var HARNESS_PROVIDER_CAPABILITIES = [
66
- "streaming",
67
- "tool-calling",
68
- "approval-resume"
69
- ];
70
- var PROVIDER_DESCRIPTORS = [
71
- {
72
- id: "meta",
73
- name: "Meta Model API",
74
- defaultModel: "muse-spark-1.2",
75
- credentialNames: ["MODEL_API_KEY"],
76
- capabilities: HARNESS_PROVIDER_CAPABILITIES,
77
- support: "certified"
78
- },
79
- {
80
- id: "qwen",
81
- name: "Qwen / Alibaba Cloud Model Studio",
82
- defaultModel: "qwen3.8-max",
83
- credentialNames: ["DASHSCOPE_API_KEY", "QWEN_API_KEY"],
84
- capabilities: HARNESS_PROVIDER_CAPABILITIES,
85
- support: "certified"
86
- },
87
- {
88
- id: "openai",
89
- name: "OpenAI",
90
- defaultModel: "gpt-5.4",
91
- credentialNames: ["OPENAI_API_KEY"],
92
- capabilities: HARNESS_PROVIDER_CAPABILITIES,
93
- support: "certified"
94
- }
95
- ];
96
- var QWEN_REGIONS = [
97
- "singapore",
98
- "beijing",
99
- "hong-kong",
100
- "tokyo",
101
- "frankfurt",
102
- "virginia"
103
- ];
104
412
  var integerOption = (name, input, envValue, fallback, minimum, maximum) => {
105
413
  const value = input ?? (envValue === undefined ? fallback : Number(envValue));
106
414
  if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
@@ -129,7 +437,7 @@ var nonNegativeNumberOption = (name, input, envValue) => {
129
437
  }
130
438
  return value;
131
439
  };
132
- var defaultHarnessNamespace = (workspace) => `workspace-${createHash("sha256").update(workspace).digest("hex").slice(0, 16)}`;
440
+ var defaultHarnessNamespace = (workspace) => `workspace-${createHash2("sha256").update(workspace).digest("hex").slice(0, 16)}`;
133
441
  var canonicalStateDirectory = (requestedWorkspace, canonicalWorkspace, configured) => {
134
442
  if (!configured) {
135
443
  return path.join(canonicalWorkspace, ".zhivex-harness", "runs");
@@ -231,26 +539,14 @@ var canonicalWorkspaceFile = (requestedWorkspace, canonicalWorkspace, configured
231
539
  const insideRequestedWorkspace = relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
232
540
  return insideRequestedWorkspace ? path.resolve(canonicalWorkspace, relative) : requestedPath;
233
541
  };
234
- var parseProvider = (value) => {
235
- const normalized = (value ?? "openai").trim().toLowerCase();
236
- if (PROVIDERS.includes(normalized)) {
237
- return normalized;
238
- }
239
- throw new Error(`Unknown provider: ${value}. Use meta, qwen, or openai.`);
240
- };
241
- var providerDescriptor = (provider) => {
242
- const descriptor = PROVIDER_DESCRIPTORS.find((entry) => entry.id === provider);
243
- if (!descriptor) {
244
- throw new Error(`No descriptor is available for provider ${provider}.`);
245
- }
246
- return descriptor;
247
- };
248
- var resolveHarnessConfig = (input = {}) => {
542
+ var parseProvider = (value, registry = DEFAULT_PROVIDER_REGISTRY) => registry.parse(value);
543
+ var providerDescriptor = (provider, registry = DEFAULT_PROVIDER_REGISTRY) => registry.descriptor(provider);
544
+ var resolveHarnessConfig = (input = {}, registry = DEFAULT_PROVIDER_REGISTRY) => {
249
545
  if (input.schemaVersion !== undefined && input.schemaVersion !== HARNESS_CONFIG_SCHEMA_VERSION) {
250
546
  throw new Error(`Unsupported config schema version: ${input.schemaVersion}. Expected ${HARNESS_CONFIG_SCHEMA_VERSION}.`);
251
547
  }
252
- const provider = parseProvider(input.provider ?? process.env.ZHIVEX_HARNESS_PROVIDER);
253
- const descriptor = providerDescriptor(provider);
548
+ const provider = parseProvider(input.provider ?? process.env.ZHIVEX_HARNESS_PROVIDER, registry);
549
+ const descriptor = providerDescriptor(provider, registry);
254
550
  const requestedWorkspace = path.resolve(input.workspace ?? process.cwd());
255
551
  let workspace = requestedWorkspace;
256
552
  try {
@@ -344,121 +640,896 @@ var resolveHarnessConfig = (input = {}) => {
344
640
  ...mcpConfigPath ? { mcpConfigPath } : {}
345
641
  };
346
642
  };
347
- var requiredCredential = (env, provider, names) => {
348
- for (const name of names) {
349
- const value = env[name]?.trim();
350
- if (value) {
351
- return value;
643
+ var createProviderModel = (config, env = process.env, registry = DEFAULT_PROVIDER_REGISTRY) => registry.createModel(config, env);
644
+ var providerAvailability = (env = process.env, registry = DEFAULT_PROVIDER_REGISTRY) => registry.availability(env);
645
+ // src/routing.ts
646
+ var routeError = (value) => new Error(`Invalid route: ${value}. Use <profile>=<provider>[:<model>].`);
647
+ var parseHarnessModelRoute = (value, registry = DEFAULT_PROVIDER_REGISTRY) => {
648
+ const separator = value.indexOf("=");
649
+ if (separator <= 0 || separator === value.length - 1) {
650
+ throw routeError(value);
651
+ }
652
+ const profile = value.slice(0, separator).trim().toLowerCase();
653
+ if (!HARNESS_SUBAGENT_PROFILES.includes(profile)) {
654
+ throw routeError(value);
655
+ }
656
+ const target = value.slice(separator + 1).trim();
657
+ const modelSeparator = target.indexOf(":");
658
+ const providerInput = (modelSeparator === -1 ? target : target.slice(0, modelSeparator)).trim();
659
+ const provider = registry.parse(providerInput);
660
+ const configuredModel = modelSeparator === -1 ? "" : target.slice(modelSeparator + 1).trim();
661
+ if (modelSeparator !== -1 && !configuredModel) {
662
+ throw routeError(value);
663
+ }
664
+ const model = configuredModel || registry.descriptor(provider).defaultModel;
665
+ if (/[\u0000-\u001f\u007f]/.test(model) || model.length > 256) {
666
+ throw routeError(value);
667
+ }
668
+ return Object.freeze({
669
+ profile,
670
+ provider,
671
+ model
672
+ });
673
+ };
674
+ var resolveHarnessModelRoutes = (values = [], registry = DEFAULT_PROVIDER_REGISTRY) => {
675
+ const routes = new Map;
676
+ for (const value of values) {
677
+ const route = parseHarnessModelRoute(value, registry);
678
+ if (routes.has(route.profile)) {
679
+ throw new Error(`Duplicate model route for ${route.profile}.`);
352
680
  }
681
+ routes.set(route.profile, route);
353
682
  }
354
- throw new Error(`Missing credentials for ${provider}. Set ${names.join(" or ")} in the environment.`);
683
+ return routes;
355
684
  };
356
- var optionalQwenRegion = (value) => {
357
- if (!value) {
358
- return;
685
+ var createHarnessRouteModels = (routes, env = process.env, registry = DEFAULT_PROVIDER_REGISTRY) => Object.fromEntries([...routes].map(([profile, route]) => [
686
+ profile,
687
+ registry.createModel(route, env)
688
+ ]));
689
+ var serializeHarnessModelRoutes = (routes) => Object.fromEntries([...routes].map(([profile, route]) => [
690
+ profile,
691
+ { provider: route.provider, model: route.model }
692
+ ]));
693
+ // src/cli-stream.ts
694
+ var CLI_JSON_SCHEMA_VERSION = 1;
695
+ var CLI_EVENT_SCHEMA_VERSION = 1;
696
+ var streamEventDocument = (event, sequence = 0) => {
697
+ if (!Number.isSafeInteger(sequence) || sequence < 0) {
698
+ throw new Error("JSONL event sequence must be a non-negative integer.");
699
+ }
700
+ const base = {
701
+ schemaVersion: CLI_EVENT_SCHEMA_VERSION,
702
+ kind: "run-event",
703
+ sequence,
704
+ type: event.type
705
+ };
706
+ switch (event.type) {
707
+ case "text-delta":
708
+ return { ...base, textDelta: event.textDelta };
709
+ case "tool-call":
710
+ return { ...base, toolCallId: event.toolCall.id, toolName: event.toolCall.name };
711
+ case "tool-result":
712
+ return {
713
+ ...base,
714
+ toolCallId: event.toolResult.toolCallId,
715
+ toolName: event.toolResult.toolName,
716
+ isError: event.toolResult.isError
717
+ };
718
+ case "tool-approval-request":
719
+ case "agent-approval-request":
720
+ return {
721
+ ...base,
722
+ approvalRequestId: event.approval.id,
723
+ approvalKind: event.approval.kind ?? "provider",
724
+ toolName: event.approval.name
725
+ };
726
+ case "agent-approval-resolved":
727
+ return {
728
+ ...base,
729
+ approvalRequestId: event.approval.approvalRequestId,
730
+ approved: event.approval.approve
731
+ };
732
+ case "provider-data":
733
+ return { ...base, provider: event.provider };
734
+ case "image-generation":
735
+ return {
736
+ ...base,
737
+ provider: event.provider,
738
+ partial: event.partial,
739
+ ...event.id ? { id: event.id } : {},
740
+ ...event.index !== undefined ? { index: event.index } : {}
741
+ };
742
+ case "finish":
743
+ return {
744
+ ...base,
745
+ ...event.finishReason ? { finishReason: event.finishReason } : {},
746
+ ...event.usage ? { usage: event.usage } : {}
747
+ };
748
+ case "error":
749
+ return { ...base, error: "Provider stream failed." };
750
+ case "agent-run-start":
751
+ return { ...base, currentStep: event.currentStep, maxSteps: event.maxSteps };
752
+ case "agent-step-start":
753
+ return { ...base, stepIndex: event.stepIndex };
754
+ case "agent-step-finish":
755
+ return {
756
+ ...base,
757
+ stepIndex: event.step.index,
758
+ status: event.step.status,
759
+ toolCalls: event.step.toolResults.length
760
+ };
761
+ case "agent-compaction":
762
+ return {
763
+ ...base,
764
+ compactionId: event.compaction.id,
765
+ reasons: event.compaction.reasons,
766
+ messageCountBefore: event.compaction.messageCountBefore,
767
+ messageCountAfter: event.compaction.messageCountAfter
768
+ };
769
+ case "agent-run-finish":
770
+ return {
771
+ ...base,
772
+ runId: event.state.runId,
773
+ status: event.status,
774
+ provider: event.state.provider,
775
+ model: event.state.modelId
776
+ };
359
777
  }
360
- if (QWEN_REGIONS.includes(value)) {
361
- return value;
778
+ };
779
+ var serializeStreamEvent = (event, sequence = 0) => JSON.stringify(streamEventDocument(event, sequence));
780
+ var streamResultDocument = (result, sequence = 0) => {
781
+ if (!Number.isSafeInteger(sequence) || sequence < 0) {
782
+ throw new Error("JSONL result sequence must be a non-negative integer.");
362
783
  }
363
- throw new Error(`Invalid QWEN_REGION: ${value}.`);
784
+ return {
785
+ schemaVersion: CLI_EVENT_SCHEMA_VERSION,
786
+ kind: "run-result",
787
+ sequence,
788
+ runId: result.runId,
789
+ status: result.status,
790
+ provider: result.provider,
791
+ model: result.model,
792
+ steps: result.steps,
793
+ toolCalls: result.toolCalls,
794
+ pendingApprovals: (result.pendingApprovals ?? []).map((approval) => ({
795
+ id: approval.id,
796
+ kind: approval.kind,
797
+ name: approval.name,
798
+ ...approval.childRunId ? { childRunId: approval.childRunId } : {},
799
+ ...approval.childAgentId ? { childAgentId: approval.childAgentId } : {}
800
+ })),
801
+ children: (result.children ?? []).map((child) => ({
802
+ runId: child.runId,
803
+ status: child.status
804
+ }))
805
+ };
364
806
  };
365
- var createProviderModel = (config, env = process.env) => {
366
- switch (config.provider) {
367
- case "meta": {
368
- const baseURL = env.META_BASE_URL?.trim();
369
- const provider = createMeta({
370
- apiKey: requiredCredential(env, "meta", ["MODEL_API_KEY"]),
371
- ...baseURL ? { baseURL } : {}
372
- });
373
- return provider(config.model);
374
- }
375
- case "qwen": {
376
- const baseURL = env.QWEN_BASE_URL?.trim();
377
- const workspaceId = env.QWEN_WORKSPACE_ID?.trim();
378
- const region = optionalQwenRegion(env.QWEN_REGION?.trim());
379
- const provider = createQwen({
380
- apiKey: requiredCredential(env, "qwen", ["DASHSCOPE_API_KEY", "QWEN_API_KEY"]),
381
- ...baseURL ? { baseURL } : {},
382
- ...workspaceId ? { workspaceId } : {},
383
- ...region ? { region } : {}
384
- });
385
- return provider(config.model);
807
+ var serializeStreamResult = (result, sequence = 0) => JSON.stringify(streamResultDocument(result, sequence));
808
+ // src/sessions.ts
809
+ import { createHash as createHash3, randomUUID } from "crypto";
810
+ import { chmod, lstat as lstat2, mkdir, open, realpath, stat } from "fs/promises";
811
+ import path3 from "path";
812
+ import { createRedactionPolicy } from "@zhivex-ai/agents";
813
+ import { Database } from "bun:sqlite";
814
+
815
+ // src/state-directory.ts
816
+ import { lstat } from "fs/promises";
817
+ import path2 from "path";
818
+ var SENSITIVE_STATE_SEGMENTS = new Set([".git", ".env", ".npmrc", "dist", "node_modules", "src"]);
819
+ var isInside = (root, candidate) => {
820
+ const relative = path2.relative(root, candidate);
821
+ return relative === "" || !relative.startsWith(`..${path2.sep}`) && relative !== ".." && !path2.isAbsolute(relative);
822
+ };
823
+ var validateStateDirectory = async (workspace, stateDirectory) => {
824
+ if (stateDirectory === workspace || stateDirectory === path2.parse(stateDirectory).root) {
825
+ throw new Error("The state directory cannot be the workspace or filesystem root.");
826
+ }
827
+ const insideWorkspace = isInside(workspace, stateDirectory);
828
+ const relativeSegments = insideWorkspace ? path2.relative(workspace, stateDirectory).split(path2.sep).filter(Boolean) : stateDirectory.slice(path2.parse(stateDirectory).root.length).split(path2.sep).filter(Boolean);
829
+ const sensitiveSegment = insideWorkspace ? relativeSegments.find((segment) => SENSITIVE_STATE_SEGMENTS.has(segment)) : undefined;
830
+ if (sensitiveSegment) {
831
+ throw new Error(`The state directory is inside the protected workspace path: ${sensitiveSegment}.`);
832
+ }
833
+ if (!insideWorkspace) {
834
+ try {
835
+ const externalEntry = await lstat(stateDirectory);
836
+ if (externalEntry.isSymbolicLink()) {
837
+ throw new Error(`The state directory must not be a symbolic link: ${stateDirectory}.`);
838
+ }
839
+ if (!externalEntry.isDirectory()) {
840
+ throw new Error(`The state directory is not a directory: ${stateDirectory}.`);
841
+ }
842
+ } catch (error) {
843
+ if (error.code !== "ENOENT") {
844
+ throw error;
845
+ }
386
846
  }
387
- case "openai": {
388
- const baseURL = env.OPENAI_BASE_URL?.trim();
389
- const provider = createOpenAI({
390
- apiKey: requiredCredential(env, "openai", ["OPENAI_API_KEY"]),
391
- ...baseURL ? { baseURL } : {}
392
- });
393
- return provider(config.model);
847
+ return;
848
+ }
849
+ let current = workspace;
850
+ for (const segment of relativeSegments) {
851
+ current = path2.join(current, segment);
852
+ try {
853
+ const entry = await lstat(current);
854
+ if (entry.isSymbolicLink()) {
855
+ throw new Error(`The state directory must not resolve through a symbolic link: ${current}.`);
856
+ }
857
+ if (!entry.isDirectory()) {
858
+ throw new Error(`The state directory path contains a non-directory entry: ${current}.`);
859
+ }
860
+ } catch (error) {
861
+ if (error.code === "ENOENT") {
862
+ break;
863
+ }
864
+ throw error;
394
865
  }
395
866
  }
396
867
  };
397
- var endpointConfiguration = (value) => {
398
- const configured = Boolean(value?.trim());
399
- if (!configured) {
400
- return {
401
- customEndpoint: false,
402
- endpointValid: true,
403
- endpointSecure: true
404
- };
868
+
869
+ // src/sessions.ts
870
+ var HARNESS_SESSION_INDEX_FILE = "operations.sqlite";
871
+ var HARNESS_SESSION_SCHEMA_VERSION = 1;
872
+ var SESSION_RUN_STATUSES = [
873
+ "created",
874
+ "running",
875
+ "waiting_approval",
876
+ "cancel_requested",
877
+ "completed",
878
+ "failed",
879
+ "cancelled",
880
+ "timed_out"
881
+ ];
882
+ var TERMINAL_SESSION_RUN_STATUSES = [
883
+ "completed",
884
+ "failed",
885
+ "cancelled",
886
+ "timed_out"
887
+ ];
888
+ var DEFAULT_RETENTION_MS = 90 * 24 * 60 * 60 * 1000;
889
+ var DEFAULT_MAX_SESSIONS = 200;
890
+ var DEFAULT_MAX_RUNS = 1000;
891
+ var DEFAULT_MAX_METADATA_BYTES = 4096;
892
+ var DEFAULT_MAX_INDEX_BYTES = 16 * 1024 * 1024;
893
+ var MAX_LIST_LIMIT = 200;
894
+ var ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@/+\-]{0,255}$/;
895
+ var TERMINAL_STATUSES = new Set(TERMINAL_SESSION_RUN_STATUSES);
896
+ var ACTIVE_STATUSES = new Set([
897
+ "created",
898
+ "running",
899
+ "waiting_approval",
900
+ "cancel_requested"
901
+ ]);
902
+ var STATUS_TRANSITIONS = {
903
+ created: new Set(["created", "running", "waiting_approval", "completed", "failed", "cancelled", "timed_out"]),
904
+ running: new Set(["running", "waiting_approval", "cancel_requested", "completed", "failed", "cancelled", "timed_out"]),
905
+ waiting_approval: new Set(["waiting_approval", "running", "cancel_requested", "completed", "failed", "cancelled", "timed_out"]),
906
+ cancel_requested: new Set(["cancel_requested", "cancelled", "failed", "timed_out"]),
907
+ completed: new Set(["completed"]),
908
+ failed: new Set(["failed"]),
909
+ cancelled: new Set(["cancelled"]),
910
+ timed_out: new Set(["timed_out"])
911
+ };
912
+ var metadataRedaction = createRedactionPolicy({
913
+ includeEmails: true,
914
+ rules: [
915
+ { name: "private-key", pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g },
916
+ { name: "common-provider-key", pattern: /\b(?:sk|ghp|github_pat|xox[baprs])[-_][A-Za-z0-9_-]{8,}\b/gi },
917
+ { name: "url-credentials", pattern: /:\/\/[^\s/@:]+:[^\s/@]+@/g, replacement: "://[REDACTED]@" }
918
+ ]
919
+ });
920
+ var byteLength = (value) => Buffer.byteLength(value, "utf8");
921
+ var boundedInteger = (name, value, fallback, minimum) => {
922
+ const resolved = value ?? fallback;
923
+ if (!Number.isSafeInteger(resolved) || resolved < minimum) {
924
+ throw new Error(`${name} must be an integer greater than or equal to ${minimum}.`);
405
925
  }
926
+ return resolved;
927
+ };
928
+ var assertTimestamp = (name, value) => {
929
+ if (!Number.isSafeInteger(value) || value < 0) {
930
+ throw new Error(`${name} must be a non-negative integer timestamp.`);
931
+ }
932
+ };
933
+ var assertIdentifier = (name, value) => {
934
+ if (!ID_PATTERN.test(value)) {
935
+ throw new Error(`${name} contains unsupported characters or exceeds 256 bytes.`);
936
+ }
937
+ if (metadataRedaction.redactText(value) !== value) {
938
+ throw new Error(`${name} resembles sensitive metadata and cannot be stored in the session index.`);
939
+ }
940
+ return value;
941
+ };
942
+ var assertScopeSegment = (name, value) => {
943
+ const normalized = value.trim();
944
+ if (!normalized || normalized.length > 128 || /[\u0000-\u001f\u007f]/.test(normalized)) {
945
+ throw new Error(`${name} must contain 1-128 printable characters.`);
946
+ }
947
+ return normalized;
948
+ };
949
+ var normalizeTitle = (value, maxMetadataBytes) => {
950
+ if (value === undefined || value === null) {
951
+ return value;
952
+ }
953
+ const normalized = metadataRedaction.redactText(value).replace(/[\u0000-\u001f\u007f]+/g, " ").trim();
954
+ if (!normalized) {
955
+ throw new Error("Session title must not be empty.");
956
+ }
957
+ if (byteLength(normalized) > maxMetadataBytes) {
958
+ throw new Error(`Session title exceeds the ${maxMetadataBytes}-byte metadata limit.`);
959
+ }
960
+ return normalized;
961
+ };
962
+ var normalizeScope = (scope) => {
963
+ const tenantId = assertScopeSegment("scope.tenantId", scope.tenantId);
964
+ const userId = scope.userId === undefined ? "" : assertScopeSegment("scope.userId", scope.userId);
965
+ const namespace = scope.namespace === undefined ? "" : assertScopeSegment("scope.namespace", scope.namespace);
966
+ return `${tenantId}\x00${userId}\x00${namespace}`;
967
+ };
968
+ var stableKey = (kind, value) => createHash3("sha256").update(kind).update("\x00").update(value).digest("hex");
969
+ var sessionId = () => `ses_${randomUUID()}`;
970
+ var turnId = () => `turn_${randomUUID()}`;
971
+ var isMissing = (error) => error.code === "ENOENT";
972
+ var ensurePrivateDatabase = async (workspace, stateDirectory) => {
973
+ const requestedStateDirectory = path3.resolve(stateDirectory);
974
+ await validateStateDirectory(workspace, requestedStateDirectory);
975
+ await mkdir(requestedStateDirectory, { recursive: true, mode: 448 });
976
+ const directoryEntry = await lstat2(requestedStateDirectory);
977
+ if (directoryEntry.isSymbolicLink() || !directoryEntry.isDirectory()) {
978
+ throw new Error(`The session state directory must be a real directory: ${requestedStateDirectory}.`);
979
+ }
980
+ await chmod(requestedStateDirectory, 448);
981
+ const canonicalStateDirectory2 = await realpath(requestedStateDirectory);
982
+ const databasePath = path3.join(canonicalStateDirectory2, HARNESS_SESSION_INDEX_FILE);
983
+ let databaseEntry;
406
984
  try {
407
- const endpoint = new URL(value.trim());
408
- const supportedProtocol = endpoint.protocol === "https:" || endpoint.protocol === "http:";
409
- return {
410
- customEndpoint: true,
411
- endpointValid: supportedProtocol && !endpoint.username && !endpoint.password && !endpoint.hash,
412
- endpointSecure: endpoint.protocol === "https:"
413
- };
414
- } catch {
415
- return {
416
- customEndpoint: true,
417
- endpointValid: false,
418
- endpointSecure: false
419
- };
985
+ databaseEntry = await lstat2(databasePath);
986
+ } catch (error) {
987
+ if (!isMissing(error)) {
988
+ throw error;
989
+ }
990
+ try {
991
+ const handle = await open(databasePath, "wx", 384);
992
+ await handle.close();
993
+ } catch (creationError) {
994
+ if (creationError.code !== "EEXIST") {
995
+ throw creationError;
996
+ }
997
+ }
998
+ databaseEntry = await lstat2(databasePath);
999
+ }
1000
+ if (databaseEntry.isSymbolicLink() || !databaseEntry.isFile()) {
1001
+ throw new Error(`The session SQLite path must be a real file: ${databasePath}.`);
420
1002
  }
1003
+ await chmod(databasePath, 384);
1004
+ return { databasePath, databaseEntry };
421
1005
  };
422
- var providerAvailability = (env = process.env) => PROVIDER_DESCRIPTORS.map((descriptor) => {
423
- const credentials = descriptor.credentialNames.map((name) => ({
424
- name,
425
- present: Boolean(env[name]?.trim())
426
- }));
427
- const endpointVariable = descriptor.id === "meta" ? "META_BASE_URL" : descriptor.id === "qwen" ? "QWEN_BASE_URL" : "OPENAI_BASE_URL";
428
- const regionValue = descriptor.id === "qwen" ? env.QWEN_REGION?.trim() : undefined;
429
- return {
430
- ...descriptor,
431
- configured: credentials.some((credential) => credential.present),
432
- credentials,
433
- configuration: {
434
- ...endpointConfiguration(env[endpointVariable]),
435
- ...descriptor.id === "qwen" ? {
436
- workspaceIdConfigured: Boolean(env.QWEN_WORKSPACE_ID?.trim()),
437
- regionConfigured: Boolean(regionValue),
438
- regionValid: !regionValue || QWEN_REGIONS.includes(regionValue)
439
- } : {}
1006
+ var toRunReference = (row) => ({
1007
+ turnId: row.turn_id,
1008
+ ...row.source_turn_id ? { sourceTurnId: row.source_turn_id } : {},
1009
+ sequence: row.ordinal,
1010
+ runId: row.run_id,
1011
+ provider: row.provider,
1012
+ model: row.model,
1013
+ ...row.role ? { role: row.role } : {},
1014
+ status: row.status,
1015
+ createdAt: row.created_at,
1016
+ updatedAt: row.updated_at,
1017
+ ...row.completed_at === null ? {} : { completedAt: row.completed_at }
1018
+ });
1019
+ var metadataSize = (input) => Object.values(input).reduce((total, value) => total + (typeof value === "string" ? byteLength(value) : 0), 0);
1020
+ var isTerminal = (status) => TERMINAL_STATUSES.has(status);
1021
+ var openCliSessionStore = async (options) => {
1022
+ const workspace = await realpath(path3.resolve(options.workspace));
1023
+ const workspaceEntry = await stat(workspace);
1024
+ if (!workspaceEntry.isDirectory()) {
1025
+ throw new Error(`The session workspace is not a directory: ${workspace}.`);
1026
+ }
1027
+ const scopeValue2 = normalizeScope(options.scope);
1028
+ const workspaceKey = stableKey("workspace", workspace);
1029
+ const scopeKey = stableKey("scope", scopeValue2);
1030
+ const retentionMs = boundedInteger("retentionMs", options.retentionMs, DEFAULT_RETENTION_MS, 0);
1031
+ const maxSessions = boundedInteger("maxSessionsPerWorkspace", options.maxSessionsPerWorkspace, DEFAULT_MAX_SESSIONS, 1);
1032
+ const maxRuns = boundedInteger("maxRunsPerSession", options.maxRunsPerSession, DEFAULT_MAX_RUNS, 1);
1033
+ const maxMetadataBytes = boundedInteger("maxMetadataBytes", options.maxMetadataBytes, DEFAULT_MAX_METADATA_BYTES, 32);
1034
+ const maxIndexBytes = boundedInteger("maxIndexBytes", options.maxIndexBytes, DEFAULT_MAX_INDEX_BYTES, 1024);
1035
+ const now = options.now ?? Date.now;
1036
+ const { databasePath, databaseEntry } = await ensurePrivateDatabase(workspace, options.stateDirectory);
1037
+ const database = new Database(databasePath, { create: false, strict: true });
1038
+ let closed = false;
1039
+ const assertOpen = () => {
1040
+ if (closed) {
1041
+ throw new Error("The CLI session store is closed.");
440
1042
  }
441
1043
  };
442
- });
1044
+ const transaction = (operation) => {
1045
+ assertOpen();
1046
+ database.exec("BEGIN IMMEDIATE");
1047
+ try {
1048
+ const result = operation();
1049
+ const size = database.query(`
1050
+ SELECT
1051
+ COALESCE((SELECT SUM(metadata_bytes) FROM zhivex_cli_sessions WHERE workspace_key = ?1 AND scope_key = ?2), 0) +
1052
+ COALESCE((SELECT SUM(r.metadata_bytes) FROM zhivex_cli_session_runs r JOIN zhivex_cli_sessions s ON s.session_id = r.session_id WHERE s.workspace_key = ?1 AND s.scope_key = ?2), 0)
1053
+ AS size
1054
+ `).get(workspaceKey, scopeKey)?.size ?? 0;
1055
+ if (size > maxIndexBytes) {
1056
+ throw new Error(`Session index metadata exceeds the ${maxIndexBytes}-byte limit.`);
1057
+ }
1058
+ database.exec("COMMIT");
1059
+ return result;
1060
+ } catch (error) {
1061
+ try {
1062
+ database.exec("ROLLBACK");
1063
+ } catch {}
1064
+ throw error;
1065
+ }
1066
+ };
1067
+ const ensureRevision = (row, expected) => {
1068
+ if (expected !== undefined && row.revision !== expected) {
1069
+ throw new Error(`Session ${row.session_id} changed concurrently (expected revision ${expected}, found ${row.revision}).`);
1070
+ }
1071
+ };
1072
+ const selectSessionRow = (candidateSessionId, includeDeleted = false) => {
1073
+ assertIdentifier("sessionId", candidateSessionId);
1074
+ return database.query(`
1075
+ SELECT session_id, title, parent_session_id, forked_from_turn_id, revision, activity_seq,
1076
+ created_at, updated_at, archived_at, deleted_at
1077
+ FROM zhivex_cli_sessions
1078
+ WHERE session_id = ?1 AND workspace_key = ?2 AND scope_key = ?3
1079
+ ${includeDeleted ? "" : "AND deleted_at IS NULL"}
1080
+ `).get(candidateSessionId, workspaceKey, scopeKey);
1081
+ };
1082
+ const requireSessionRow = (candidateSessionId, includeDeleted = false) => {
1083
+ const row = selectSessionRow(candidateSessionId, includeDeleted);
1084
+ if (!row) {
1085
+ throw new Error(`Session ${candidateSessionId} was not found in this workspace and scope.`);
1086
+ }
1087
+ return row;
1088
+ };
1089
+ const selectRuns = (candidateSessionId) => database.query(`
1090
+ SELECT turn_id, source_turn_id, ordinal, run_id, provider, model, role, status,
1091
+ created_at, updated_at, completed_at
1092
+ FROM zhivex_cli_session_runs
1093
+ WHERE session_id = ?1
1094
+ ORDER BY ordinal ASC
1095
+ `).all(candidateSessionId).map(toRunReference);
1096
+ const nextActivitySequence = () => database.query(`
1097
+ SELECT COALESCE(MAX(activity_seq), 0) + 1 AS count
1098
+ FROM zhivex_cli_sessions
1099
+ WHERE workspace_key = ?1 AND scope_key = ?2
1100
+ `).get(workspaceKey, scopeKey)?.count ?? 1;
1101
+ const sessionBase = (row) => ({
1102
+ schemaVersion: HARNESS_SESSION_SCHEMA_VERSION,
1103
+ sessionId: row.session_id,
1104
+ workspaceKey,
1105
+ scopeKey,
1106
+ ...row.title ? { title: row.title } : {},
1107
+ ...row.parent_session_id ? { parentSessionId: row.parent_session_id } : {},
1108
+ ...row.forked_from_turn_id ? { forkedFromTurnId: row.forked_from_turn_id } : {},
1109
+ revision: row.revision,
1110
+ createdAt: row.created_at,
1111
+ updatedAt: row.updated_at,
1112
+ ...row.archived_at === null ? {} : { archivedAt: row.archived_at },
1113
+ ...row.deleted_at === null ? {} : { deletedAt: row.deleted_at }
1114
+ });
1115
+ const hydrate = (row) => ({
1116
+ ...sessionBase(row),
1117
+ runs: selectRuns(row.session_id)
1118
+ });
1119
+ const latestRunRow = (candidateSessionId) => database.query(`
1120
+ SELECT turn_id, source_turn_id, ordinal, run_id, provider, model, role, status,
1121
+ created_at, updated_at, completed_at
1122
+ FROM zhivex_cli_session_runs
1123
+ WHERE session_id = ?1
1124
+ ORDER BY ordinal DESC
1125
+ LIMIT 1
1126
+ `).get(candidateSessionId);
1127
+ const assertNoActiveRun = (candidateSessionId, action) => {
1128
+ const latest = latestRunRow(candidateSessionId);
1129
+ if (latest && ACTIVE_STATUSES.has(latest.status)) {
1130
+ throw new Error(`Cannot ${action} session ${candidateSessionId} while run ${latest.run_id} is ${latest.status}.`);
1131
+ }
1132
+ };
1133
+ const insertRun = (candidateSessionId, input, sequence, sourceTurnId) => {
1134
+ const runId = assertIdentifier("runId", input.runId);
1135
+ const provider = assertIdentifier("provider", input.provider);
1136
+ const model = assertIdentifier("model", input.model);
1137
+ const role = input.role === undefined ? null : assertIdentifier("role", input.role);
1138
+ const status = input.status ?? "created";
1139
+ if (!SESSION_RUN_STATUSES.includes(status)) {
1140
+ throw new Error(`Unsupported session run status: ${String(status)}.`);
1141
+ }
1142
+ const createdAt = input.createdAt ?? now();
1143
+ assertTimestamp("createdAt", createdAt);
1144
+ const entryBytes = metadataSize({ runId, provider, model, role });
1145
+ if (entryBytes > maxMetadataBytes) {
1146
+ throw new Error(`Run reference metadata exceeds the ${maxMetadataBytes}-byte limit.`);
1147
+ }
1148
+ const candidateTurnId = turnId();
1149
+ database.query(`
1150
+ INSERT INTO zhivex_cli_session_runs (
1151
+ turn_id, source_turn_id, session_id, ordinal, run_id, provider, model, role,
1152
+ status, created_at, updated_at, completed_at, metadata_bytes
1153
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?10, ?11, ?12)
1154
+ `).run(candidateTurnId, sourceTurnId ?? null, candidateSessionId, sequence, runId, provider, model, role, status, createdAt, isTerminal(status) ? createdAt : null, entryBytes);
1155
+ };
1156
+ try {
1157
+ const openedEntry = await lstat2(databasePath);
1158
+ if (openedEntry.isSymbolicLink() || !openedEntry.isFile() || openedEntry.dev !== databaseEntry.dev || openedEntry.ino !== databaseEntry.ino) {
1159
+ throw new Error(`The session SQLite path changed while it was being opened: ${databasePath}.`);
1160
+ }
1161
+ database.exec("PRAGMA journal_mode = WAL");
1162
+ database.exec("PRAGMA busy_timeout = 5000");
1163
+ database.exec("PRAGMA foreign_keys = ON");
1164
+ database.exec(`
1165
+ CREATE TABLE IF NOT EXISTS zhivex_cli_session_schema (
1166
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
1167
+ version INTEGER NOT NULL
1168
+ );
1169
+ INSERT OR IGNORE INTO zhivex_cli_session_schema (singleton, version)
1170
+ VALUES (1, ${HARNESS_SESSION_SCHEMA_VERSION});
1171
+ CREATE TABLE IF NOT EXISTS zhivex_cli_sessions (
1172
+ session_id TEXT PRIMARY KEY,
1173
+ workspace_key TEXT NOT NULL,
1174
+ scope_key TEXT NOT NULL,
1175
+ title TEXT,
1176
+ parent_session_id TEXT,
1177
+ forked_from_turn_id TEXT,
1178
+ revision INTEGER NOT NULL DEFAULT 0,
1179
+ activity_seq INTEGER NOT NULL,
1180
+ created_at INTEGER NOT NULL,
1181
+ updated_at INTEGER NOT NULL,
1182
+ archived_at INTEGER,
1183
+ deleted_at INTEGER,
1184
+ metadata_bytes INTEGER NOT NULL,
1185
+ CHECK (length(session_id) <= 260),
1186
+ CHECK (metadata_bytes >= 0)
1187
+ );
1188
+ CREATE INDEX IF NOT EXISTS zhivex_cli_sessions_latest
1189
+ ON zhivex_cli_sessions (workspace_key, scope_key, deleted_at, archived_at, activity_seq DESC);
1190
+ CREATE TABLE IF NOT EXISTS zhivex_cli_session_runs (
1191
+ turn_id TEXT PRIMARY KEY,
1192
+ source_turn_id TEXT,
1193
+ session_id TEXT NOT NULL REFERENCES zhivex_cli_sessions(session_id) ON DELETE CASCADE,
1194
+ ordinal INTEGER NOT NULL,
1195
+ run_id TEXT NOT NULL,
1196
+ provider TEXT NOT NULL,
1197
+ model TEXT NOT NULL,
1198
+ role TEXT,
1199
+ status TEXT NOT NULL CHECK (status IN ('created', 'running', 'waiting_approval', 'cancel_requested', 'completed', 'failed', 'cancelled', 'timed_out')),
1200
+ created_at INTEGER NOT NULL,
1201
+ updated_at INTEGER NOT NULL,
1202
+ completed_at INTEGER,
1203
+ metadata_bytes INTEGER NOT NULL,
1204
+ UNIQUE (session_id, ordinal),
1205
+ UNIQUE (session_id, run_id),
1206
+ CHECK (ordinal >= 0),
1207
+ CHECK (metadata_bytes >= 0)
1208
+ );
1209
+ CREATE INDEX IF NOT EXISTS zhivex_cli_session_runs_latest
1210
+ ON zhivex_cli_session_runs (session_id, ordinal DESC);
1211
+ `);
1212
+ const schemaVersion = database.query("SELECT version FROM zhivex_cli_session_schema WHERE singleton = 1").get()?.version;
1213
+ if (schemaVersion !== HARNESS_SESSION_SCHEMA_VERSION) {
1214
+ throw new Error(`Unsupported CLI session schema version: ${String(schemaVersion)}.`);
1215
+ }
1216
+ await chmod(databasePath, 384);
1217
+ } catch (error) {
1218
+ database.close(false);
1219
+ throw error;
1220
+ }
1221
+ const prune = async (input = {}) => transaction(() => {
1222
+ const before = input.before ?? Math.max(0, now() - retentionMs);
1223
+ assertTimestamp("before", before);
1224
+ const deletedSessions = database.query(`
1225
+ SELECT COUNT(*) AS count FROM zhivex_cli_sessions
1226
+ WHERE workspace_key = ?1 AND scope_key = ?2
1227
+ AND updated_at < ?3
1228
+ AND (archived_at IS NOT NULL OR deleted_at IS NOT NULL)
1229
+ `).get(workspaceKey, scopeKey, before)?.count ?? 0;
1230
+ database.query(`
1231
+ DELETE FROM zhivex_cli_sessions
1232
+ WHERE workspace_key = ?1 AND scope_key = ?2
1233
+ AND updated_at < ?3
1234
+ AND (archived_at IS NOT NULL OR deleted_at IS NOT NULL)
1235
+ `).run(workspaceKey, scopeKey, before);
1236
+ return { before, deletedSessions };
1237
+ });
1238
+ const store = {
1239
+ databasePath,
1240
+ workspaceKey,
1241
+ scopeKey,
1242
+ async create(input = {}) {
1243
+ return transaction(() => {
1244
+ const cutoff = Math.max(0, now() - retentionMs);
1245
+ database.query(`
1246
+ DELETE FROM zhivex_cli_sessions
1247
+ WHERE workspace_key = ?1 AND scope_key = ?2 AND updated_at < ?3
1248
+ AND (archived_at IS NOT NULL OR deleted_at IS NOT NULL)
1249
+ `).run(workspaceKey, scopeKey, cutoff);
1250
+ const count = database.query(`
1251
+ SELECT COUNT(*) AS count FROM zhivex_cli_sessions
1252
+ WHERE workspace_key = ?1 AND scope_key = ?2
1253
+ `).get(workspaceKey, scopeKey)?.count ?? 0;
1254
+ if (count >= maxSessions) {
1255
+ throw new Error(`Session limit reached for this workspace and scope (${maxSessions}). Archive and prune an older session.`);
1256
+ }
1257
+ const createdAt = now();
1258
+ assertTimestamp("now", createdAt);
1259
+ const title = normalizeTitle(input.title, maxMetadataBytes) ?? null;
1260
+ const candidateSessionId = sessionId();
1261
+ const activitySequence = nextActivitySequence();
1262
+ database.query(`
1263
+ INSERT INTO zhivex_cli_sessions (
1264
+ session_id, workspace_key, scope_key, title, revision, created_at,
1265
+ updated_at, activity_seq, metadata_bytes
1266
+ ) VALUES (?1, ?2, ?3, ?4, 0, ?5, ?5, ?6, ?7)
1267
+ `).run(candidateSessionId, workspaceKey, scopeKey, title, createdAt, activitySequence, metadataSize({ title }));
1268
+ if (input.initialRun) {
1269
+ insertRun(candidateSessionId, input.initialRun, 0);
1270
+ }
1271
+ return hydrate(requireSessionRow(candidateSessionId));
1272
+ });
1273
+ },
1274
+ async get(candidateSessionId, input = {}) {
1275
+ assertOpen();
1276
+ const row = selectSessionRow(candidateSessionId, input.includeDeleted ?? false);
1277
+ return row ? hydrate(row) : undefined;
1278
+ },
1279
+ async list(query = {}) {
1280
+ assertOpen();
1281
+ const limit = boundedInteger("limit", query.limit, 50, 1);
1282
+ if (limit > MAX_LIST_LIMIT) {
1283
+ throw new Error(`limit cannot exceed ${MAX_LIST_LIMIT}.`);
1284
+ }
1285
+ const rows = database.query(`
1286
+ SELECT session_id, title, parent_session_id, forked_from_turn_id, revision, activity_seq,
1287
+ created_at, updated_at, archived_at, deleted_at
1288
+ FROM zhivex_cli_sessions
1289
+ WHERE workspace_key = ?1 AND scope_key = ?2
1290
+ ${query.includeArchived ? "" : "AND archived_at IS NULL"}
1291
+ ${query.includeDeleted ? "" : "AND deleted_at IS NULL"}
1292
+ ORDER BY activity_seq DESC
1293
+ LIMIT ?3
1294
+ `).all(workspaceKey, scopeKey, limit);
1295
+ return rows.map((row) => {
1296
+ const latest = latestRunRow(row.session_id);
1297
+ const runCount = database.query("SELECT COUNT(*) AS count FROM zhivex_cli_session_runs WHERE session_id = ?1").get(row.session_id)?.count ?? 0;
1298
+ return {
1299
+ ...sessionBase(row),
1300
+ runCount,
1301
+ ...latest ? { latestRun: toRunReference(latest) } : {}
1302
+ };
1303
+ });
1304
+ },
1305
+ async latest(input = {}) {
1306
+ assertOpen();
1307
+ const row = database.query(`
1308
+ SELECT session_id, title, parent_session_id, forked_from_turn_id, revision, activity_seq,
1309
+ created_at, updated_at, archived_at, deleted_at
1310
+ FROM zhivex_cli_sessions
1311
+ WHERE workspace_key = ?1 AND scope_key = ?2 AND deleted_at IS NULL
1312
+ ${input.includeArchived ? "" : "AND archived_at IS NULL"}
1313
+ ORDER BY activity_seq DESC
1314
+ LIMIT 1
1315
+ `).get(workspaceKey, scopeKey);
1316
+ return row ? hydrate(row) : undefined;
1317
+ },
1318
+ async update(candidateSessionId, input) {
1319
+ return transaction(() => {
1320
+ const row = requireSessionRow(candidateSessionId);
1321
+ ensureRevision(row, input.expectedRevision);
1322
+ if (input.archived === true) {
1323
+ assertNoActiveRun(candidateSessionId, "archive");
1324
+ }
1325
+ const title = input.title === undefined ? row.title : normalizeTitle(input.title, maxMetadataBytes) ?? null;
1326
+ const updatedAt = now();
1327
+ assertTimestamp("now", updatedAt);
1328
+ const archivedAt = input.archived === undefined ? row.archived_at : input.archived ? updatedAt : null;
1329
+ const activitySequence = nextActivitySequence();
1330
+ database.query(`
1331
+ UPDATE zhivex_cli_sessions
1332
+ SET title = ?1, archived_at = ?2, updated_at = ?3, revision = revision + 1,
1333
+ metadata_bytes = ?4, activity_seq = ?5
1334
+ WHERE session_id = ?6 AND workspace_key = ?7 AND scope_key = ?8 AND revision = ?9
1335
+ `).run(title, archivedAt, updatedAt, metadataSize({ title }), activitySequence, candidateSessionId, workspaceKey, scopeKey, row.revision);
1336
+ return hydrate(requireSessionRow(candidateSessionId));
1337
+ });
1338
+ },
1339
+ async rename(candidateSessionId, title, input = {}) {
1340
+ return store.update(candidateSessionId, {
1341
+ title,
1342
+ ...input.expectedRevision === undefined ? {} : { expectedRevision: input.expectedRevision }
1343
+ });
1344
+ },
1345
+ async appendRun(candidateSessionId, input, appendOptions = {}) {
1346
+ return transaction(() => {
1347
+ const row = requireSessionRow(candidateSessionId);
1348
+ ensureRevision(row, appendOptions.expectedRevision);
1349
+ if (row.archived_at !== null) {
1350
+ throw new Error(`Cannot append a run to archived session ${candidateSessionId}.`);
1351
+ }
1352
+ const existing = database.query(`
1353
+ SELECT turn_id, source_turn_id, ordinal, run_id, provider, model, role, status,
1354
+ created_at, updated_at, completed_at
1355
+ FROM zhivex_cli_session_runs WHERE session_id = ?1 AND run_id = ?2
1356
+ `).get(candidateSessionId, input.runId);
1357
+ if (existing) {
1358
+ const requestedStatus = input.status ?? "created";
1359
+ if (existing.provider === input.provider && existing.model === input.model && (existing.role ?? undefined) === input.role && existing.status === requestedStatus) {
1360
+ return hydrate(row);
1361
+ }
1362
+ throw new Error(`Run ${input.runId} is already immutably bound to ${existing.provider}/${existing.model} in this session.`);
1363
+ }
1364
+ assertNoActiveRun(candidateSessionId, "start another run in");
1365
+ const count = database.query("SELECT COUNT(*) AS count FROM zhivex_cli_session_runs WHERE session_id = ?1").get(candidateSessionId)?.count ?? 0;
1366
+ if (count >= maxRuns) {
1367
+ throw new Error(`Run-reference limit reached for session ${candidateSessionId} (${maxRuns}).`);
1368
+ }
1369
+ insertRun(candidateSessionId, input, count);
1370
+ const updatedAt = now();
1371
+ assertTimestamp("now", updatedAt);
1372
+ const activitySequence = nextActivitySequence();
1373
+ database.query(`
1374
+ UPDATE zhivex_cli_sessions SET updated_at = ?1, activity_seq = ?2, revision = revision + 1
1375
+ WHERE session_id = ?3 AND revision = ?4
1376
+ `).run(updatedAt, activitySequence, candidateSessionId, row.revision);
1377
+ return hydrate(requireSessionRow(candidateSessionId));
1378
+ });
1379
+ },
1380
+ async updateRun(candidateSessionId, runId, input) {
1381
+ return transaction(() => {
1382
+ const session = requireSessionRow(candidateSessionId);
1383
+ ensureRevision(session, input.expectedSessionRevision);
1384
+ assertIdentifier("runId", runId);
1385
+ const run = database.query(`
1386
+ SELECT turn_id, source_turn_id, ordinal, run_id, provider, model, role, status,
1387
+ created_at, updated_at, completed_at
1388
+ FROM zhivex_cli_session_runs WHERE session_id = ?1 AND run_id = ?2
1389
+ `).get(candidateSessionId, runId);
1390
+ if (!run) {
1391
+ throw new Error(`Run ${runId} is not linked to session ${candidateSessionId}.`);
1392
+ }
1393
+ if (!STATUS_TRANSITIONS[run.status].has(input.status)) {
1394
+ throw new Error(`Run ${runId} cannot transition from ${run.status} to ${input.status}.`);
1395
+ }
1396
+ const updatedAt = input.updatedAt ?? now();
1397
+ assertTimestamp("updatedAt", updatedAt);
1398
+ if (updatedAt < run.updated_at) {
1399
+ throw new Error(`Run ${runId} update timestamp cannot move backwards.`);
1400
+ }
1401
+ const completedAt = isTerminal(input.status) ? input.completedAt ?? updatedAt : null;
1402
+ if (completedAt !== null) {
1403
+ assertTimestamp("completedAt", completedAt);
1404
+ if (completedAt < run.created_at || completedAt > updatedAt) {
1405
+ throw new Error("completedAt must be between the run creation and update timestamps.");
1406
+ }
1407
+ }
1408
+ database.query(`
1409
+ UPDATE zhivex_cli_session_runs
1410
+ SET status = ?1, updated_at = ?2, completed_at = ?3
1411
+ WHERE session_id = ?4 AND run_id = ?5
1412
+ `).run(input.status, updatedAt, completedAt, candidateSessionId, runId);
1413
+ const activitySequence = nextActivitySequence();
1414
+ database.query(`
1415
+ UPDATE zhivex_cli_sessions SET updated_at = ?1, activity_seq = ?2, revision = revision + 1
1416
+ WHERE session_id = ?3 AND revision = ?4
1417
+ `).run(updatedAt, activitySequence, candidateSessionId, session.revision);
1418
+ return hydrate(requireSessionRow(candidateSessionId));
1419
+ });
1420
+ },
1421
+ async fork(candidateSessionId, input = {}) {
1422
+ return transaction(() => {
1423
+ const source = requireSessionRow(candidateSessionId);
1424
+ ensureRevision(source, input.expectedRevision);
1425
+ const sourceRuns = selectRuns(candidateSessionId);
1426
+ let forkRuns = sourceRuns;
1427
+ let forkedFromTurnId;
1428
+ if (input.atTurnId) {
1429
+ const index = sourceRuns.findIndex((run) => run.turnId === input.atTurnId);
1430
+ if (index < 0) {
1431
+ throw new Error(`Turn ${input.atTurnId} is not part of session ${candidateSessionId}.`);
1432
+ }
1433
+ forkRuns = sourceRuns.slice(0, index + 1);
1434
+ forkedFromTurnId = input.atTurnId;
1435
+ } else if (sourceRuns.length > 0) {
1436
+ forkedFromTurnId = sourceRuns.at(-1).turnId;
1437
+ }
1438
+ const branchPoint = forkRuns.at(-1);
1439
+ if (branchPoint && !isTerminal(branchPoint.status)) {
1440
+ throw new Error(`Cannot fork from non-terminal run ${branchPoint.runId} (${branchPoint.status}).`);
1441
+ }
1442
+ if (forkRuns.length > maxRuns) {
1443
+ throw new Error(`Fork history exceeds the ${maxRuns}-run limit.`);
1444
+ }
1445
+ const count = database.query(`
1446
+ SELECT COUNT(*) AS count FROM zhivex_cli_sessions
1447
+ WHERE workspace_key = ?1 AND scope_key = ?2
1448
+ `).get(workspaceKey, scopeKey)?.count ?? 0;
1449
+ if (count >= maxSessions) {
1450
+ throw new Error(`Session limit reached for this workspace and scope (${maxSessions}).`);
1451
+ }
1452
+ const createdAt = now();
1453
+ assertTimestamp("now", createdAt);
1454
+ const title = normalizeTitle(input.title, maxMetadataBytes) ?? source.title;
1455
+ const forkSessionId = sessionId();
1456
+ const activitySequence = nextActivitySequence();
1457
+ database.query(`
1458
+ INSERT INTO zhivex_cli_sessions (
1459
+ session_id, workspace_key, scope_key, title, parent_session_id,
1460
+ forked_from_turn_id, revision, created_at, updated_at, activity_seq, metadata_bytes
1461
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, 0, ?7, ?7, ?8, ?9)
1462
+ `).run(forkSessionId, workspaceKey, scopeKey, title, candidateSessionId, forkedFromTurnId ?? null, createdAt, activitySequence, metadataSize({ title }));
1463
+ for (const [index, run] of forkRuns.entries()) {
1464
+ insertRun(forkSessionId, {
1465
+ runId: run.runId,
1466
+ provider: run.provider,
1467
+ model: run.model,
1468
+ ...run.role ? { role: run.role } : {},
1469
+ status: run.status,
1470
+ createdAt: run.createdAt
1471
+ }, index, run.turnId);
1472
+ database.query(`
1473
+ UPDATE zhivex_cli_session_runs
1474
+ SET updated_at = ?1, completed_at = ?2
1475
+ WHERE session_id = ?3 AND ordinal = ?4
1476
+ `).run(run.updatedAt, run.completedAt ?? null, forkSessionId, index);
1477
+ }
1478
+ return hydrate(requireSessionRow(forkSessionId));
1479
+ });
1480
+ },
1481
+ async archive(candidateSessionId, input = {}) {
1482
+ return store.update(candidateSessionId, {
1483
+ archived: true,
1484
+ ...input.expectedRevision === undefined ? {} : { expectedRevision: input.expectedRevision }
1485
+ });
1486
+ },
1487
+ async delete(candidateSessionId, input = {}) {
1488
+ return transaction(() => {
1489
+ const row = requireSessionRow(candidateSessionId);
1490
+ ensureRevision(row, input.expectedRevision);
1491
+ assertNoActiveRun(candidateSessionId, "delete");
1492
+ const deletedAt = now();
1493
+ assertTimestamp("now", deletedAt);
1494
+ const activitySequence = nextActivitySequence();
1495
+ database.query(`
1496
+ UPDATE zhivex_cli_sessions
1497
+ SET deleted_at = ?1, updated_at = ?1, activity_seq = ?2, revision = revision + 1
1498
+ WHERE session_id = ?3 AND revision = ?4
1499
+ `).run(deletedAt, activitySequence, candidateSessionId, row.revision);
1500
+ return hydrate(requireSessionRow(candidateSessionId, true));
1501
+ });
1502
+ },
1503
+ prune,
1504
+ close() {
1505
+ if (!closed) {
1506
+ closed = true;
1507
+ database.close(false);
1508
+ }
1509
+ }
1510
+ };
1511
+ return store;
1512
+ };
1513
+ var openSessionStore = openCliSessionStore;
443
1514
  // src/execution-environment.ts
444
- import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
1515
+ import { createHash as createHash6, randomUUID as randomUUID3 } from "crypto";
445
1516
  import {
446
- chmod,
447
- lstat as lstat2,
448
- mkdir as mkdir2,
1517
+ chmod as chmod2,
1518
+ lstat as lstat4,
1519
+ mkdir as mkdir3,
449
1520
  readFile as readFile2,
450
1521
  readdir as readdir2,
451
- realpath as realpath2,
1522
+ realpath as realpath3,
452
1523
  rename as rename2,
453
1524
  rm,
454
1525
  writeFile
455
1526
  } from "fs/promises";
456
- import path4 from "path";
1527
+ import path6 from "path";
457
1528
  import { createAgentExecutionEnvironmentBinding } from "@zhivex-ai/agents/beta";
458
1529
 
459
1530
  // src/edit-contracts.ts
460
- import { createHash as createHash2 } from "crypto";
461
- import path2 from "path";
1531
+ import { createHash as createHash4 } from "crypto";
1532
+ import path4 from "path";
462
1533
  import { z } from "zod";
463
1534
  var EDIT_CONTRACT_SCHEMA_VERSION = 1;
464
1535
  var EDIT_DIGEST_ALGORITHM = "sha256";
@@ -467,7 +1538,7 @@ var MAX_EDIT_FILE_BYTES = 1024 * 1024;
467
1538
  var MAX_EDIT_PROPOSAL_BYTES = 4 * 1024 * 1024;
468
1539
  var SHA256_DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
469
1540
  var fileDigestSchema = z.string().regex(SHA256_DIGEST_PATTERN, "Digest must use the format sha256:<64 lowercase hexadecimal characters>.");
470
- var workspaceFilePathSchema = z.string().min(1).max(1024).refine((value) => !value.includes("\x00"), "Path cannot contain a NUL byte.").refine((value) => !path2.isAbsolute(value) && !/^[A-Za-z]:[\\/]/.test(value), "Path must be relative.").refine((value) => {
1541
+ var workspaceFilePathSchema = z.string().min(1).max(1024).refine((value) => !value.includes("\x00"), "Path cannot contain a NUL byte.").refine((value) => !path4.isAbsolute(value) && !/^[A-Za-z]:[\\/]/.test(value), "Path must be relative.").refine((value) => {
471
1542
  const segments = value.split(/[\\/]+/);
472
1543
  return segments.every((segment) => segment !== "" && segment !== "." && segment !== "..");
473
1544
  }, "Path must be normalized and cannot contain empty, dot, or parent segments.");
@@ -557,7 +1628,7 @@ var mutationAuditEntrySchema = z.strictObject({
557
1628
  timestamp: z.iso.datetime(),
558
1629
  quarantineId: z.string().min(1).max(200).optional()
559
1630
  });
560
- var digest = (value) => `sha256:${createHash2("sha256").update(value).digest("hex")}`;
1631
+ var digest = (value) => `sha256:${createHash4("sha256").update(value).digest("hex")}`;
561
1632
  var canonicalProposalPayload = (changes) => ({
562
1633
  schemaVersion: EDIT_CONTRACT_SCHEMA_VERSION,
563
1634
  kind: "edit-proposal",
@@ -592,9 +1663,9 @@ var editContractDocument = (kind, result) => ({
592
1663
  });
593
1664
 
594
1665
  // src/workspace.ts
595
- import { createHash as createHash3, randomUUID } from "crypto";
596
- import { link, lstat, mkdir, open, readFile, readdir, realpath, rename, stat, unlink } from "fs/promises";
597
- import path3 from "path";
1666
+ import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
1667
+ import { link, lstat as lstat3, mkdir as mkdir2, open as open2, readFile, readdir, realpath as realpath2, rename, stat as stat2, unlink } from "fs/promises";
1668
+ import path5 from "path";
598
1669
  var HARD_IGNORES = new Set([".git", ".next", ".turbo", ".zhivex-harness", "coverage", "dist", "node_modules"]);
599
1670
  var IGNORE_FILES = [".gitignore", ".zhivex-harnessignore"];
600
1671
  var DEFAULT_CHECKS = ["test", "typecheck", "lint", "build"];
@@ -605,12 +1676,12 @@ var isSensitiveName = (name) => {
605
1676
  return normalized === ".env" || normalized.startsWith(".env.") && normalized !== ".env.example" || normalized === ".npmrc" || normalized === "id_rsa" || normalized === "id_ed25519" || normalized.endsWith(".key") || normalized.endsWith(".pem") || normalized.endsWith(".p12") || normalized.endsWith(".pfx");
606
1677
  };
607
1678
  var isHardIgnored = (relativePath) => relativePath.split("/").some((segment) => HARD_IGNORES.has(segment.toLocaleLowerCase()) || isSensitiveName(segment));
608
- var isInside = (root, candidate) => {
609
- const relative = path3.relative(root, candidate);
610
- return relative === "" || !relative.startsWith(`..${path3.sep}`) && relative !== ".." && !path3.isAbsolute(relative);
1679
+ var isInside2 = (root, candidate) => {
1680
+ const relative = path5.relative(root, candidate);
1681
+ return relative === "" || !relative.startsWith(`..${path5.sep}`) && relative !== ".." && !path5.isAbsolute(relative);
611
1682
  };
612
- var wirePath = (value) => value.split(path3.sep).join("/");
613
- var digestBytes = (value) => `sha256:${createHash3("sha256").update(value).digest("hex")}`;
1683
+ var wirePath = (value) => value.split(path5.sep).join("/");
1684
+ var digestBytes = (value) => `sha256:${createHash5("sha256").update(value).digest("hex")}`;
614
1685
  var truncate = (value, maxLength = MAX_TOOL_OUTPUT) => value.length <= maxLength ? value : `${value.slice(0, maxLength)}
615
1686
  \u2026 output truncated (${value.length - maxLength} characters omitted)`;
616
1687
  var cursorEncode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
@@ -716,18 +1787,18 @@ class Workspace {
716
1787
  this.root = root;
717
1788
  }
718
1789
  static async open(root) {
719
- const resolved = await realpath(path3.resolve(root));
720
- if (!(await stat(resolved)).isDirectory())
1790
+ const resolved = await realpath2(path5.resolve(root));
1791
+ if (!(await stat2(resolved)).isDirectory())
721
1792
  throw new Error(`The workspace is not a directory: ${root}`);
722
1793
  return new Workspace(resolved);
723
1794
  }
724
1795
  lexicalPath(relativePath) {
725
1796
  if (!relativePath || relativePath.includes("\x00"))
726
1797
  throw new Error("The path must be a valid relative path.");
727
- const candidate = path3.resolve(this.root, relativePath);
728
- if (!isInside(this.root, candidate))
1798
+ const candidate = path5.resolve(this.root, relativePath);
1799
+ if (!isInside2(this.root, candidate))
729
1800
  throw new Error(`The path escapes the workspace: ${relativePath}`);
730
- const relative = wirePath(path3.relative(this.root, candidate));
1801
+ const relative = wirePath(path5.relative(this.root, candidate));
731
1802
  if (relative && isHardIgnored(relative)) {
732
1803
  const blocked = relative.split("/").find((segment) => HARD_IGNORES.has(segment.toLocaleLowerCase()) || isSensitiveName(segment));
733
1804
  throw new Error(`The path is protected by the harness policy: ${blocked}`);
@@ -738,13 +1809,13 @@ class Workspace {
738
1809
  const candidate = this.lexicalPath(relativePath);
739
1810
  if (candidate === this.root && options.requireFile)
740
1811
  throw new Error("The workspace root is not a regular file.");
741
- const segments = path3.relative(this.root, candidate).split(path3.sep).filter(Boolean);
1812
+ const segments = path5.relative(this.root, candidate).split(path5.sep).filter(Boolean);
742
1813
  let current = this.root;
743
1814
  for (let index = 0;index < segments.length; index += 1) {
744
- current = path3.join(current, segments[index]);
1815
+ current = path5.join(current, segments[index]);
745
1816
  let entry;
746
1817
  try {
747
- entry = await lstat(current);
1818
+ entry = await lstat3(current);
748
1819
  } catch (error) {
749
1820
  if (options.allowMissing && error.code === "ENOENT") {
750
1821
  return { path: candidate, exists: false };
@@ -764,18 +1835,18 @@ class Workspace {
764
1835
  }
765
1836
  async readStableFile(relativePath, allowBinary = true) {
766
1837
  const safe = await this.safePath(relativePath, { requireFile: true });
767
- const before = await lstat(safe.path);
1838
+ const before = await lstat3(safe.path);
768
1839
  if (before.size > MAX_FILE_BYTES)
769
1840
  throw new Error(`The file exceeds the ${MAX_FILE_BYTES}-byte limit.`);
770
1841
  const contents = await readFile(safe.path);
771
- const after = await lstat(safe.path);
1842
+ const after = await lstat3(safe.path);
772
1843
  if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs) {
773
1844
  throw new Error(`The file changed while it was being read: ${relativePath}`);
774
1845
  }
775
1846
  if (!allowBinary && contents.includes(0))
776
1847
  throw new Error("The file appears to be binary and cannot be read as text.");
777
1848
  return {
778
- path: wirePath(path3.relative(this.root, safe.path)),
1849
+ path: wirePath(path5.relative(this.root, safe.path)),
779
1850
  absolutePath: safe.path,
780
1851
  contents,
781
1852
  digest: digestBytes(contents),
@@ -786,8 +1857,8 @@ class Workspace {
786
1857
  const rules = [...inherited];
787
1858
  for (const filename of IGNORE_FILES) {
788
1859
  try {
789
- const ignorePath = path3.join(directory, filename);
790
- const entry = await lstat(ignorePath);
1860
+ const ignorePath = path5.join(directory, filename);
1861
+ const entry = await lstat3(ignorePath);
791
1862
  if (entry.isFile() && entry.size <= MAX_FILE_BYTES) {
792
1863
  rules.push(...parseIgnoreRules(await readFile(ignorePath, "utf8"), base));
793
1864
  }
@@ -807,11 +1878,11 @@ class Workspace {
807
1878
  const relative = base ? `${base}/${entry.name}` : entry.name;
808
1879
  if (isHardIgnored(relative) || entry.isSymbolicLink() || isIgnoredByRules(relative, entry.isDirectory(), rules))
809
1880
  continue;
810
- const absolute = path3.join(directory, entry.name);
1881
+ const absolute = path5.join(directory, entry.name);
811
1882
  if (entry.isDirectory())
812
1883
  await walk(absolute, relative, rules);
813
1884
  else if (entry.isFile())
814
- files.push({ path: relative, size: (await lstat(absolute)).size });
1885
+ files.push({ path: relative, size: (await lstat3(absolute)).size });
815
1886
  }
816
1887
  };
817
1888
  await walk(this.root, "", []);
@@ -819,9 +1890,9 @@ class Workspace {
819
1890
  }
820
1891
  async listFiles(relativePath = ".", options = {}) {
821
1892
  const start = await this.safePath(relativePath);
822
- if (!(await lstat(start.path)).isDirectory())
1893
+ if (!(await lstat3(start.path)).isDirectory())
823
1894
  throw new Error("list_files requires a directory.");
824
- const requestPath = wirePath(path3.relative(this.root, start.path)) || ".";
1895
+ const requestPath = wirePath(path5.relative(this.root, start.path)) || ".";
825
1896
  const input = typeof options === "number" ? { limit: options } : options;
826
1897
  const limit = input.limit ?? 200;
827
1898
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 5000)
@@ -883,9 +1954,9 @@ class Workspace {
883
1954
  if (!query || query.length > 200)
884
1955
  throw new Error("The search query must be between 1 and 200 characters.");
885
1956
  const start = await this.safePath(relativePath);
886
- if (!(await lstat(start.path)).isDirectory())
1957
+ if (!(await lstat3(start.path)).isDirectory())
887
1958
  throw new Error("search_files requires a directory.");
888
- const requestPath = wirePath(path3.relative(this.root, start.path)) || ".";
1959
+ const requestPath = wirePath(path5.relative(this.root, start.path)) || ".";
889
1960
  const limit = options.limit ?? options.maxMatches ?? 100;
890
1961
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 500)
891
1962
  throw new Error("limit must be between 1 and 500.");
@@ -940,7 +2011,7 @@ class Workspace {
940
2011
  };
941
2012
  }
942
2013
  audit(entry) {
943
- const record = { id: randomUUID(), timestamp: new Date().toISOString(), ...entry };
2014
+ const record = { id: randomUUID2(), timestamp: new Date().toISOString(), ...entry };
944
2015
  this.auditEntries.push(record);
945
2016
  return record;
946
2017
  }
@@ -948,16 +2019,16 @@ class Workspace {
948
2019
  return this.auditEntries.map((entry) => ({ ...entry }));
949
2020
  }
950
2021
  async secureWorkspaceDirectory(directory, mode = 493) {
951
- const resolvedInput = path3.resolve(directory);
952
- if (!isInside(this.root, resolvedInput)) {
2022
+ const resolvedInput = path5.resolve(directory);
2023
+ if (!isInside2(this.root, resolvedInput)) {
953
2024
  throw new Error("A mutation directory escapes the workspace.");
954
2025
  }
955
- const segments = path3.relative(this.root, resolvedInput).split(path3.sep).filter(Boolean);
2026
+ const segments = path5.relative(this.root, resolvedInput).split(path5.sep).filter(Boolean);
956
2027
  let current = this.root;
957
2028
  for (const segment of segments) {
958
- current = path3.join(current, segment);
2029
+ current = path5.join(current, segment);
959
2030
  try {
960
- const entry = await lstat(current);
2031
+ const entry = await lstat3(current);
961
2032
  if (entry.isSymbolicLink())
962
2033
  throw new Error("A mutation directory must not contain a symbolic link.");
963
2034
  if (!entry.isDirectory())
@@ -966,26 +2037,26 @@ class Workspace {
966
2037
  if (error.code !== "ENOENT")
967
2038
  throw error;
968
2039
  try {
969
- await mkdir(current, { mode });
2040
+ await mkdir2(current, { mode });
970
2041
  } catch (mkdirError) {
971
2042
  if (mkdirError.code !== "EEXIST")
972
2043
  throw mkdirError;
973
- const raced = await lstat(current);
2044
+ const raced = await lstat3(current);
974
2045
  if (raced.isSymbolicLink() || !raced.isDirectory()) {
975
2046
  throw new Error("A mutation directory changed during creation.");
976
2047
  }
977
2048
  }
978
2049
  }
979
2050
  }
980
- const canonical = await realpath(current);
981
- if (!isInside(this.root, canonical))
2051
+ const canonical = await realpath2(current);
2052
+ if (!isInside2(this.root, canonical))
982
2053
  throw new Error("A mutation directory resolves outside the workspace.");
983
2054
  return canonical;
984
2055
  }
985
2056
  async stageFile(target, contents, mode) {
986
- const directory = await this.secureWorkspaceDirectory(path3.dirname(target));
987
- const temporary = path3.join(directory, `.${path3.basename(target)}.zhivex-${randomUUID()}.tmp`);
988
- const handle = await open(temporary, "wx", mode);
2057
+ const directory = await this.secureWorkspaceDirectory(path5.dirname(target));
2058
+ const temporary = path5.join(directory, `.${path5.basename(target)}.zhivex-${randomUUID2()}.tmp`);
2059
+ const handle = await open2(temporary, "wx", mode);
989
2060
  try {
990
2061
  await handle.writeFile(contents);
991
2062
  await handle.chmod(mode);
@@ -1142,7 +2213,7 @@ class Workspace {
1142
2213
  const destination = await this.safePath(parsed.destination, { allowMissing: true });
1143
2214
  if (destination.exists)
1144
2215
  throw new Error(`Move destination already exists: ${parsed.destination}.`);
1145
- await this.secureWorkspaceDirectory(path3.dirname(destination.path));
2216
+ await this.secureWorkspaceDirectory(path5.dirname(destination.path));
1146
2217
  const destinationRecheck = await this.safePath(parsed.destination, { allowMissing: true });
1147
2218
  if (destinationRecheck.exists)
1148
2219
  throw new Error(`Move destination already exists: ${parsed.destination}.`);
@@ -1169,9 +2240,9 @@ class Workspace {
1169
2240
  const segments = [".zhivex-harness", "quarantine"];
1170
2241
  let current = this.root;
1171
2242
  for (const segment of segments) {
1172
- current = path3.join(current, segment);
2243
+ current = path5.join(current, segment);
1173
2244
  try {
1174
- const entry = await lstat(current);
2245
+ const entry = await lstat3(current);
1175
2246
  if (entry.isSymbolicLink()) {
1176
2247
  throw new Error("Harness quarantine must not resolve through a symbolic link.");
1177
2248
  }
@@ -1182,37 +2253,37 @@ class Workspace {
1182
2253
  if (error.code !== "ENOENT" || !create)
1183
2254
  throw error;
1184
2255
  try {
1185
- await mkdir(current, { mode: 448 });
2256
+ await mkdir2(current, { mode: 448 });
1186
2257
  } catch (mkdirError) {
1187
2258
  if (mkdirError.code !== "EEXIST")
1188
2259
  throw mkdirError;
1189
- const racedEntry = await lstat(current);
2260
+ const racedEntry = await lstat3(current);
1190
2261
  if (racedEntry.isSymbolicLink() || !racedEntry.isDirectory()) {
1191
2262
  throw new Error("Harness quarantine path changed during creation.");
1192
2263
  }
1193
2264
  }
1194
2265
  }
1195
2266
  }
1196
- const resolved = await realpath(current);
1197
- if (!isInside(this.root, resolved))
2267
+ const resolved = await realpath2(current);
2268
+ if (!isInside2(this.root, resolved))
1198
2269
  throw new Error("Harness quarantine resolves outside the workspace.");
1199
2270
  return resolved;
1200
2271
  }
1201
2272
  async writeManifest(manifest) {
1202
2273
  const directory = await this.secureQuarantineDirectory(true);
1203
- const target = path3.join(directory, `${manifest.quarantineId}.json`);
2274
+ const target = path5.join(directory, `${manifest.quarantineId}.json`);
1204
2275
  const temporary = await this.stageFile(target, `${JSON.stringify(manifest, null, 2)}
1205
2276
  `, 384);
1206
2277
  await rename(temporary, target);
1207
2278
  }
1208
2279
  async readInternalRegularFile(target, label, maxBytes) {
1209
- const before = await lstat(target);
2280
+ const before = await lstat3(target);
1210
2281
  if (before.isSymbolicLink() || !before.isFile())
1211
2282
  throw new Error(`${label} must be a regular non-symlink file.`);
1212
2283
  if (before.size > maxBytes)
1213
2284
  throw new Error(`${label} exceeds its size limit.`);
1214
2285
  const contents = await readFile(target);
1215
- const after = await lstat(target);
2286
+ const after = await lstat3(target);
1216
2287
  if (after.isSymbolicLink() || !after.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs) {
1217
2288
  throw new Error(`${label} changed while it was being read.`);
1218
2289
  }
@@ -1223,9 +2294,9 @@ class Workspace {
1223
2294
  const source = await this.readStableFile(parsed.path);
1224
2295
  if (source.digest !== parsed.expectedDigest)
1225
2296
  throw new Error(`Stale quarantine rejected for ${parsed.path}.`);
1226
- const quarantineId = `${Date.now()}-${randomUUID()}`;
2297
+ const quarantineId = `${Date.now()}-${randomUUID2()}`;
1227
2298
  const directory = await this.secureQuarantineDirectory(true);
1228
- const dataPath = path3.join(directory, `${quarantineId}.data`);
2299
+ const dataPath = path5.join(directory, `${quarantineId}.data`);
1229
2300
  if ((await this.readStableFile(parsed.path)).digest !== parsed.expectedDigest)
1230
2301
  throw new Error(`Stale quarantine rejected for ${parsed.path}.`);
1231
2302
  await link(source.absolutePath, dataPath);
@@ -1247,7 +2318,7 @@ class Workspace {
1247
2318
  await unlink(source.absolutePath);
1248
2319
  } catch (error) {
1249
2320
  await unlink(dataPath).catch(() => {});
1250
- await unlink(path3.join(directory, `${quarantineId}.json`)).catch(() => {});
2321
+ await unlink(path5.join(directory, `${quarantineId}.json`)).catch(() => {});
1251
2322
  throw error;
1252
2323
  }
1253
2324
  const audit = this.audit({ operation: "quarantine", path: parsed.path, beforeDigest: source.digest, quarantineId });
@@ -1258,7 +2329,7 @@ class Workspace {
1258
2329
  const directory = await this.secureQuarantineDirectory(false);
1259
2330
  let manifest;
1260
2331
  try {
1261
- const manifestFile = await this.readInternalRegularFile(path3.join(directory, `${parsed.quarantineId}.json`), "Quarantine manifest", 64 * 1024);
2332
+ const manifestFile = await this.readInternalRegularFile(path5.join(directory, `${parsed.quarantineId}.json`), "Quarantine manifest", 64 * 1024);
1262
2333
  manifest = JSON.parse(manifestFile.contents.toString("utf8"));
1263
2334
  } catch {
1264
2335
  throw new Error(`Quarantine entry was not found: ${parsed.quarantineId}.`);
@@ -1266,7 +2337,7 @@ class Workspace {
1266
2337
  if (manifest.schemaVersion !== 1 || manifest.quarantineId !== parsed.quarantineId || manifest.status !== "quarantined") {
1267
2338
  throw new Error(`Quarantine entry is not restorable: ${parsed.quarantineId}.`);
1268
2339
  }
1269
- const dataPath = path3.join(directory, `${parsed.quarantineId}.data`);
2340
+ const dataPath = path5.join(directory, `${parsed.quarantineId}.data`);
1270
2341
  const dataFile = await this.readInternalRegularFile(dataPath, "Quarantine payload", MAX_FILE_BYTES);
1271
2342
  const digest2 = digestBytes(dataFile.contents);
1272
2343
  if (digest2 !== manifest.digest || parsed.expectedDigest && parsed.expectedDigest !== digest2) {
@@ -1276,16 +2347,16 @@ class Workspace {
1276
2347
  const destination = await this.safePath(destinationPath, { allowMissing: true });
1277
2348
  if (destination.exists)
1278
2349
  throw new Error(`Restore destination already exists: ${destinationPath}.`);
1279
- await this.secureWorkspaceDirectory(path3.dirname(destination.path));
2350
+ await this.secureWorkspaceDirectory(path5.dirname(destination.path));
1280
2351
  const destinationRecheck = await this.safePath(destinationPath, { allowMissing: true });
1281
2352
  if (destinationRecheck.exists)
1282
2353
  throw new Error(`Restore destination already exists: ${destinationPath}.`);
1283
- const dataRecheck = await lstat(dataPath);
2354
+ const dataRecheck = await lstat3(dataPath);
1284
2355
  if (dataRecheck.isSymbolicLink() || !dataRecheck.isFile() || dataRecheck.dev !== dataFile.stat.dev || dataRecheck.ino !== dataFile.stat.ino || dataRecheck.size !== dataFile.stat.size || dataRecheck.mtimeMs !== dataFile.stat.mtimeMs) {
1285
2356
  throw new Error("Quarantine payload changed before restore commit.");
1286
2357
  }
1287
2358
  await link(dataPath, destination.path);
1288
- const linkedDestination = await lstat(destination.path);
2359
+ const linkedDestination = await lstat3(destination.path);
1289
2360
  if (linkedDestination.isSymbolicLink() || !linkedDestination.isFile() || linkedDestination.dev !== dataFile.stat.dev || linkedDestination.ino !== dataFile.stat.ino || digestBytes(await readFile(destination.path)) !== digest2) {
1290
2361
  await unlink(destination.path).catch(() => {});
1291
2362
  throw new Error("Restore destination did not match the quarantined payload.");
@@ -1354,7 +2425,7 @@ class Workspace {
1354
2425
  var HARNESS_EXECUTION_ARTIFACT_SCHEMA_VERSION = 1;
1355
2426
  var HARNESS_OCI_LABEL = "com.zhivex.harness.execution";
1356
2427
  var HARNESS_OCI_LABEL_VALUE = "v1";
1357
- var TERMINAL_STATUSES = new Set([
2428
+ var TERMINAL_STATUSES2 = new Set([
1358
2429
  "completed",
1359
2430
  "failed",
1360
2431
  "cancelled",
@@ -1377,22 +2448,22 @@ var BUILT_IN_TOOL_NAMES = new Set([
1377
2448
  "apply_environment_patch",
1378
2449
  "environment_status"
1379
2450
  ]);
1380
- var digest2 = (value) => `sha256:${createHash4("sha256").update(value).digest("hex")}`;
1381
- var runHash = (runId) => createHash4("sha256").update(runId).digest("hex").slice(0, 24);
1382
- var isInside2 = (root, candidate) => {
1383
- const relative = path4.relative(root, candidate);
1384
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path4.sep}`) && !path4.isAbsolute(relative);
2451
+ var digest2 = (value) => `sha256:${createHash6("sha256").update(value).digest("hex")}`;
2452
+ var runHash = (runId) => createHash6("sha256").update(runId).digest("hex").slice(0, 24);
2453
+ var isInside3 = (root, candidate) => {
2454
+ const relative = path6.relative(root, candidate);
2455
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path6.sep}`) && !path6.isAbsolute(relative);
1385
2456
  };
1386
2457
  var privateDirectory = async (directory) => {
1387
- await mkdir2(directory, { recursive: true, mode: 448 });
1388
- const entry = await lstat2(directory);
2458
+ await mkdir3(directory, { recursive: true, mode: 448 });
2459
+ const entry = await lstat4(directory);
1389
2460
  if (entry.isSymbolicLink() || !entry.isDirectory()) {
1390
2461
  throw new Error(`Execution artifact directory must be a real directory: ${directory}.`);
1391
2462
  }
1392
- await chmod(directory, 448);
2463
+ await chmod2(directory, 448);
1393
2464
  };
1394
2465
  var atomicJson = async (target, value) => {
1395
- const temporary = `${target}.${randomUUID2()}.tmp`;
2466
+ const temporary = `${target}.${randomUUID3()}.tmp`;
1396
2467
  await writeFile(temporary, `${JSON.stringify(value, null, 2)}
1397
2468
  `, { encoding: "utf8", mode: 384 });
1398
2469
  await rename2(temporary, target);
@@ -1494,8 +2565,8 @@ var validateSynchronizedSnapshot = async (stagedRoot, previousRoot, maxWorkspace
1494
2565
  const visit = async (directory, relativeDirectory = "") => {
1495
2566
  for (const name of await readdir2(directory)) {
1496
2567
  const relative = relativeDirectory ? `${relativeDirectory}/${name}` : name;
1497
- const absolute = path4.join(directory, name);
1498
- if (!isInside2(stagedRoot, absolute) || relative.length > 1024) {
2568
+ const absolute = path6.join(directory, name);
2569
+ if (!isInside3(stagedRoot, absolute) || relative.length > 1024) {
1499
2570
  throw new Error(`OCI workspace export contains an invalid path: ${relative}.`);
1500
2571
  }
1501
2572
  entries += 1;
@@ -1506,7 +2577,7 @@ var validateSynchronizedSnapshot = async (stagedRoot, previousRoot, maxWorkspace
1506
2577
  await rm(absolute, { recursive: true, force: true });
1507
2578
  continue;
1508
2579
  }
1509
- const entry = await lstat2(absolute);
2580
+ const entry = await lstat4(absolute);
1510
2581
  if (entry.isSymbolicLink()) {
1511
2582
  throw new Error(`OCI workspace export contains a symbolic link: ${relative}.`);
1512
2583
  }
@@ -1522,10 +2593,10 @@ var validateSynchronizedSnapshot = async (stagedRoot, previousRoot, maxWorkspace
1522
2593
  throw new Error(`OCI workspace export exceeds the ${maxWorkspaceBytes}-byte workspace limit.`);
1523
2594
  }
1524
2595
  if (entry.size > maxFileWriteBytes) {
1525
- const previous = path4.join(previousRoot, ...relative.split("/"));
2596
+ const previous = path6.join(previousRoot, ...relative.split("/"));
1526
2597
  let unchanged = false;
1527
2598
  try {
1528
- const previousEntry = await lstat2(previous);
2599
+ const previousEntry = await lstat4(previous);
1529
2600
  unchanged = previousEntry.isFile() && !previousEntry.isSymbolicLink() && previousEntry.size === entry.size && digest2(await readFile2(previous)) === digest2(await readFile2(absolute));
1530
2601
  } catch (error) {
1531
2602
  if (error.code !== "ENOENT")
@@ -1540,7 +2611,7 @@ var validateSynchronizedSnapshot = async (stagedRoot, previousRoot, maxWorkspace
1540
2611
  await visit(stagedRoot);
1541
2612
  };
1542
2613
  var replaceSnapshot = async (snapshotRoot, stagedRoot) => {
1543
- const backupRoot = `${snapshotRoot}.${randomUUID2()}.previous`;
2614
+ const backupRoot = `${snapshotRoot}.${randomUUID3()}.previous`;
1544
2615
  await rename2(snapshotRoot, backupRoot);
1545
2616
  try {
1546
2617
  await rename2(stagedRoot, snapshotRoot);
@@ -1609,7 +2680,7 @@ ${result.stdout}`)) {
1609
2680
  }
1610
2681
  }
1611
2682
  async run(request) {
1612
- const resourceName = `zhivex-harness-${runHash(request.runId)}-${randomUUID2().slice(0, 8)}`;
2683
+ const resourceName = `zhivex-harness-${runHash(request.runId)}-${randomUUID3().slice(0, 8)}`;
1613
2684
  const name = `${resourceName}-job`;
1614
2685
  const exportName = `${resourceName}-export`;
1615
2686
  const volumeName = `${resourceName}-workspace`;
@@ -1731,7 +2802,7 @@ ${result.stdout}`)) {
1731
2802
  await forcedRemoval;
1732
2803
  } else if (result.exitCode === 0) {
1733
2804
  await this.cli(["pause", name]);
1734
- const stagedRoot = path4.join(path4.dirname(request.snapshotRoot), `.workspace-sync-${randomUUID2()}`);
2805
+ const stagedRoot = path6.join(path6.dirname(request.snapshotRoot), `.workspace-sync-${randomUUID3()}`);
1735
2806
  try {
1736
2807
  await privateDirectory(stagedRoot);
1737
2808
  try {
@@ -1865,14 +2936,14 @@ var collectSnapshotFiles = async (workspace) => {
1865
2936
  do {
1866
2937
  const page = await workspace.listFiles(".", { limit: 500, ...cursor ? { cursor } : {} });
1867
2938
  for (const file of page.files) {
1868
- const absolute = path4.join(workspace.root, ...file.path.split("/"));
1869
- if (!isInside2(workspace.root, absolute))
2939
+ const absolute = path6.join(workspace.root, ...file.path.split("/"));
2940
+ if (!isInside3(workspace.root, absolute))
1870
2941
  throw new Error(`Snapshot file escaped its workspace: ${file.path}.`);
1871
- const before = await lstat2(absolute);
2942
+ const before = await lstat4(absolute);
1872
2943
  if (before.isSymbolicLink() || !before.isFile())
1873
2944
  throw new Error(`Snapshot source is not a regular file: ${file.path}.`);
1874
2945
  const contents = await readFile2(absolute);
1875
- const after = await lstat2(absolute);
2946
+ const after = await lstat4(absolute);
1876
2947
  if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs) {
1877
2948
  throw new Error(`Snapshot source changed while reading: ${file.path}.`);
1878
2949
  }
@@ -1896,10 +2967,10 @@ var copyWorkspaceSnapshot = async (source, baseRoot, snapshotRoot, maxWorkspaceB
1896
2967
  }
1897
2968
  for (const file of files.values()) {
1898
2969
  for (const root of [baseRoot, snapshotRoot]) {
1899
- const target = path4.join(root, ...file.path.split("/"));
1900
- await mkdir2(path4.dirname(target), { recursive: true, mode: 448 });
2970
+ const target = path6.join(root, ...file.path.split("/"));
2971
+ await mkdir3(path6.dirname(target), { recursive: true, mode: 448 });
1901
2972
  await writeFile(target, file.contents, { mode: file.mode });
1902
- await chmod(target, file.mode);
2973
+ await chmod2(target, file.mode);
1903
2974
  }
1904
2975
  }
1905
2976
  return { files: files.size, bytes: totalBytes };
@@ -2110,21 +3181,21 @@ var createHarnessOciExecutionEnvironment = async (options) => {
2110
3181
  }
2111
3182
  };
2112
3183
  const binding = createAgentExecutionEnvironmentBinding(manifest);
2113
- const environmentRoot = path4.join(options.stateDirectory, "environments");
3184
+ const environmentRoot = path6.join(options.stateDirectory, "environments");
2114
3185
  return {
2115
3186
  manifest,
2116
3187
  image,
2117
3188
  runtime,
2118
3189
  async acquire(request) {
2119
3190
  await privateDirectory(environmentRoot);
2120
- const directory = path4.join(environmentRoot, runHash(request.runId));
3191
+ const directory = path6.join(environmentRoot, runHash(request.runId));
2121
3192
  await privateDirectory(directory);
2122
- const metadataPath = path4.join(directory, "environment.json");
2123
- const baseRoot = path4.join(directory, "base");
2124
- const snapshotRoot = path4.join(directory, "workspace");
3193
+ const metadataPath = path6.join(directory, "environment.json");
3194
+ const baseRoot = path6.join(directory, "base");
3195
+ const snapshotRoot = path6.join(directory, "workspace");
2125
3196
  let metadata;
2126
3197
  try {
2127
- const entry = await lstat2(metadataPath);
3198
+ const entry = await lstat4(metadataPath);
2128
3199
  if (entry.isSymbolicLink() || !entry.isFile())
2129
3200
  throw new Error("Execution metadata must be a regular file.");
2130
3201
  metadata = JSON.parse(await readFile2(metadataPath, "utf8"));
@@ -2153,14 +3224,14 @@ var createHarnessOciExecutionEnvironment = async (options) => {
2153
3224
  await atomicJson(metadataPath, metadata);
2154
3225
  const workspace = await Workspace.open(snapshotRoot);
2155
3226
  const base = await Workspace.open(baseRoot);
2156
- const dependencyPath = path4.join(options.workspace.root, "node_modules");
3227
+ const dependencyPath = path6.join(options.workspace.root, "node_modules");
2157
3228
  let dependencyRoot;
2158
3229
  try {
2159
- const entry = await lstat2(dependencyPath);
3230
+ const entry = await lstat4(dependencyPath);
2160
3231
  if (entry.isSymbolicLink() || !entry.isDirectory()) {
2161
3232
  throw new Error("node_modules must be a real directory before it can be mounted read-only.");
2162
3233
  }
2163
- dependencyRoot = await realpath2(dependencyPath);
3234
+ dependencyRoot = await realpath3(dependencyPath);
2164
3235
  } catch (error) {
2165
3236
  if (error.code !== "ENOENT") {
2166
3237
  throw error;
@@ -2261,7 +3332,7 @@ var createHarnessOciExecutionEnvironment = async (options) => {
2261
3332
  }
2262
3333
  let manifestDocument;
2263
3334
  try {
2264
- manifestDocument = JSON.parse(await readFile2(path4.join(snapshotRoot, "package.json"), "utf8"));
3335
+ manifestDocument = JSON.parse(await readFile2(path6.join(snapshotRoot, "package.json"), "utf8"));
2265
3336
  } catch (error) {
2266
3337
  if (error.code === "ENOENT") {
2267
3338
  throw new Error("The environment snapshot does not contain package.json.");
@@ -2299,7 +3370,7 @@ var createHarnessOciExecutionEnvironment = async (options) => {
2299
3370
  };
2300
3371
  };
2301
3372
  var cleanupHarnessExecutionArtifacts = async (stateDirectory, before) => {
2302
- const root = path4.join(stateDirectory, "environments");
3373
+ const root = path6.join(stateDirectory, "environments");
2303
3374
  const result = { scanned: 0, deleted: 0, skipped: 0 };
2304
3375
  let entries;
2305
3376
  try {
@@ -2315,20 +3386,20 @@ var cleanupHarnessExecutionArtifacts = async (stateDirectory, before) => {
2315
3386
  continue;
2316
3387
  }
2317
3388
  result.scanned += 1;
2318
- const directory = path4.join(root, entry.name);
2319
- const resolved = await realpath2(directory);
2320
- if (!isInside2(root, resolved)) {
3389
+ const directory = path6.join(root, entry.name);
3390
+ const resolved = await realpath3(directory);
3391
+ if (!isInside3(root, resolved)) {
2321
3392
  result.skipped += 1;
2322
3393
  continue;
2323
3394
  }
2324
3395
  try {
2325
- const metadataPath = path4.join(directory, "environment.json");
2326
- const metadataEntry = await lstat2(metadataPath);
3396
+ const metadataPath = path6.join(directory, "environment.json");
3397
+ const metadataEntry = await lstat4(metadataPath);
2327
3398
  if (metadataEntry.isSymbolicLink() || !metadataEntry.isFile())
2328
3399
  throw new Error("unsafe metadata");
2329
3400
  const metadata = JSON.parse(await readFile2(metadataPath, "utf8"));
2330
3401
  const releasedAt = metadata.releasedAt ? Date.parse(metadata.releasedAt) : Number.NaN;
2331
- if (!metadata.status || !TERMINAL_STATUSES.has(metadata.status) || !Number.isFinite(releasedAt) || releasedAt >= before) {
3402
+ if (!metadata.status || !TERMINAL_STATUSES2.has(metadata.status) || !Number.isFinite(releasedAt) || releasedAt >= before) {
2332
3403
  result.skipped += 1;
2333
3404
  continue;
2334
3405
  }
@@ -2398,8 +3469,8 @@ var selectHarnessModel = (candidates, required) => {
2398
3469
  };
2399
3470
  };
2400
3471
  // src/mcp.ts
2401
- import { lstat as lstat3, readFile as readFile3, realpath as realpath3 } from "fs/promises";
2402
- import path5 from "path";
3472
+ import { lstat as lstat5, readFile as readFile3, realpath as realpath4 } from "fs/promises";
3473
+ import path7 from "path";
2403
3474
  import {
2404
3475
  createMcpToolSet,
2405
3476
  serializeJsonValue
@@ -2449,8 +3520,8 @@ var configurationSchema = z2.object({
2449
3520
  servers: z2.array(serverSchema).max(20)
2450
3521
  }).strict();
2451
3522
  var isInsidePath = (root, candidate) => {
2452
- const relative = path5.relative(root, candidate);
2453
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path5.sep}`) && !path5.isAbsolute(relative);
3523
+ const relative = path7.relative(root, candidate);
3524
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path7.sep}`) && !path7.isAbsolute(relative);
2454
3525
  };
2455
3526
  var validatedHttpUrl = (value, serverName) => {
2456
3527
  let url;
@@ -2534,7 +3605,7 @@ var normalizeHarnessMcpConfiguration = (value) => {
2534
3605
  return { schemaVersion: HARNESS_MCP_CONFIG_SCHEMA_VERSION, servers };
2535
3606
  };
2536
3607
  var loadHarnessMcpConfiguration = async (workspace, configPath) => {
2537
- const entry = await lstat3(configPath);
3608
+ const entry = await lstat5(configPath);
2538
3609
  if (entry.isSymbolicLink() || !entry.isFile()) {
2539
3610
  throw new Error(`MCP configuration must be a regular non-symlink file: ${configPath}.`);
2540
3611
  }
@@ -2542,8 +3613,8 @@ var loadHarnessMcpConfiguration = async (workspace, configPath) => {
2542
3613
  throw new Error("MCP configuration cannot exceed 1 MiB.");
2543
3614
  }
2544
3615
  const [canonicalWorkspace, canonicalConfig] = await Promise.all([
2545
- realpath3(workspace),
2546
- realpath3(configPath)
3616
+ realpath4(workspace),
3617
+ realpath4(configPath)
2547
3618
  ]);
2548
3619
  if (!isInsidePath(canonicalWorkspace, canonicalConfig)) {
2549
3620
  throw new Error("MCP configuration must remain inside the canonical workspace.");
@@ -2649,7 +3720,7 @@ var createHttpMcpClient = (server, env = process.env, fetchImplementation = fetc
2649
3720
  throw new Error(`MCP server ${server.name} is not configured for HTTP transport.`);
2650
3721
  }
2651
3722
  const staticHeaders = resolveHeaders(server, env);
2652
- let sessionId;
3723
+ let sessionId2;
2653
3724
  let requestId = 0;
2654
3725
  let initialization;
2655
3726
  const post = async (payload, options = {}, expectResponse = true) => {
@@ -2661,7 +3732,7 @@ var createHttpMcpClient = (server, env = process.env, fetchImplementation = fetc
2661
3732
  "content-type": "application/json",
2662
3733
  accept: "application/json, text/event-stream",
2663
3734
  "mcp-protocol-version": "2025-06-18",
2664
- ...sessionId ? { "mcp-session-id": sessionId } : {}
3735
+ ...sessionId2 ? { "mcp-session-id": sessionId2 } : {}
2665
3736
  },
2666
3737
  body: JSON.stringify(payload),
2667
3738
  ...options.abortSignal ? { signal: options.abortSignal } : {}
@@ -2670,7 +3741,7 @@ var createHttpMcpClient = (server, env = process.env, fetchImplementation = fetc
2670
3741
  await response.body?.cancel();
2671
3742
  throw new Error(`MCP server ${server.name} returned HTTP ${response.status}.`);
2672
3743
  }
2673
- sessionId = response.headers.get("mcp-session-id") ?? sessionId;
3744
+ sessionId2 = response.headers.get("mcp-session-id") ?? sessionId2;
2674
3745
  if (!expectResponse) {
2675
3746
  await response.body?.cancel();
2676
3747
  return;
@@ -2788,7 +3859,7 @@ var createHarnessMcpTools = async (configuration, options = {}) => {
2788
3859
  };
2789
3860
  var mcpConfigurationFingerprintInput = (configuration) => serializeJsonValue(configuration);
2790
3861
  // src/orchestration.ts
2791
- import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
3862
+ import { createHash as createHash7, randomUUID as randomUUID4 } from "crypto";
2792
3863
  import {
2793
3864
  applySafetyPolicyToAgent,
2794
3865
  createBudgetGuard,
@@ -2835,7 +3906,7 @@ var childHarnessBinding = (parent, profile, model, toolNames, config) => ({
2835
3906
  schemaVersion: 1,
2836
3907
  id: `${parent.id}-${profile}`,
2837
3908
  version: parent.version,
2838
- fingerprint: `sha256:${createHash5("sha256").update(JSON.stringify({
3909
+ fingerprint: `sha256:${createHash7("sha256").update(JSON.stringify({
2839
3910
  parent: parent.fingerprint,
2840
3911
  profile,
2841
3912
  model: {
@@ -2935,7 +4006,7 @@ var runHarnessReviewGroup = async (runtime, input, profiles = ["explorer", "revi
2935
4006
  }
2936
4007
  return { name: profile, agent };
2937
4008
  });
2938
- const groupId = input.groupId ?? `review_${randomUUID3()}`;
4009
+ const groupId = input.groupId ?? `review_${randomUUID4()}`;
2939
4010
  const result = await runAgentGroup(members, {
2940
4011
  prompt: input.prompt,
2941
4012
  ...input.scope ? { scope: input.scope } : {},
@@ -2958,13 +4029,13 @@ var runHarnessReviewGroup = async (runtime, input, profiles = ["explorer", "revi
2958
4029
  };
2959
4030
  };
2960
4031
  // src/harness.ts
2961
- import { createHash as createHash6 } from "crypto";
4032
+ import { createHash as createHash8 } from "crypto";
2962
4033
  import {
2963
4034
  Agent,
2964
4035
  applySafetyPolicyToAgent as applySafetyPolicyToAgent2,
2965
4036
  createBudgetGuard as createBudgetGuard2,
2966
4037
  createProductionSafetyPolicy as createProductionSafetyPolicy2,
2967
- createRedactionPolicy as createRedactionPolicy2,
4038
+ createRedactionPolicy as createRedactionPolicy3,
2968
4039
  getAgentBudgetStatus as getAgentBudgetStatus2,
2969
4040
  tool
2970
4041
  } from "@zhivex-ai/agents";
@@ -2976,12 +4047,12 @@ import { serializeJsonValue as serializeJsonValue2 } from "@zhivex-ai/core";
2976
4047
  import { z as z3 } from "zod";
2977
4048
 
2978
4049
  // src/operations.ts
2979
- import { chmod as chmod2, lstat as lstat5, mkdir as mkdir3, open as open2 } from "fs/promises";
2980
- import path7 from "path";
4050
+ import { chmod as chmod3, lstat as lstat6, mkdir as mkdir4, open as open3 } from "fs/promises";
4051
+ import path8 from "path";
2981
4052
  import {
2982
4053
  cancelAgentRun,
2983
4054
  cancelAgentRunTree,
2984
- createRedactionPolicy,
4055
+ createRedactionPolicy as createRedactionPolicy2,
2985
4056
  getAgentBudgetStatus
2986
4057
  } from "@zhivex-ai/agents";
2987
4058
  import {
@@ -2994,63 +4065,7 @@ import {
2994
4065
  createSqliteAgentRunStore
2995
4066
  } from "@zhivex-ai/agents/ops";
2996
4067
  import { createAgentRunLedger } from "@zhivex-ai/agents/control-plane";
2997
- import { Database } from "bun:sqlite";
2998
-
2999
- // src/state-directory.ts
3000
- import { lstat as lstat4 } from "fs/promises";
3001
- import path6 from "path";
3002
- var SENSITIVE_STATE_SEGMENTS = new Set([".git", ".env", ".npmrc", "dist", "node_modules", "src"]);
3003
- var isInside3 = (root, candidate) => {
3004
- const relative = path6.relative(root, candidate);
3005
- return relative === "" || !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative);
3006
- };
3007
- var validateStateDirectory = async (workspace, stateDirectory) => {
3008
- if (stateDirectory === workspace || stateDirectory === path6.parse(stateDirectory).root) {
3009
- throw new Error("The state directory cannot be the workspace or filesystem root.");
3010
- }
3011
- const insideWorkspace = isInside3(workspace, stateDirectory);
3012
- const relativeSegments = insideWorkspace ? path6.relative(workspace, stateDirectory).split(path6.sep).filter(Boolean) : stateDirectory.slice(path6.parse(stateDirectory).root.length).split(path6.sep).filter(Boolean);
3013
- const sensitiveSegment = insideWorkspace ? relativeSegments.find((segment) => SENSITIVE_STATE_SEGMENTS.has(segment)) : undefined;
3014
- if (sensitiveSegment) {
3015
- throw new Error(`The state directory is inside the protected workspace path: ${sensitiveSegment}.`);
3016
- }
3017
- if (!insideWorkspace) {
3018
- try {
3019
- const externalEntry = await lstat4(stateDirectory);
3020
- if (externalEntry.isSymbolicLink()) {
3021
- throw new Error(`The state directory must not be a symbolic link: ${stateDirectory}.`);
3022
- }
3023
- if (!externalEntry.isDirectory()) {
3024
- throw new Error(`The state directory is not a directory: ${stateDirectory}.`);
3025
- }
3026
- } catch (error) {
3027
- if (error.code !== "ENOENT") {
3028
- throw error;
3029
- }
3030
- }
3031
- return;
3032
- }
3033
- let current = workspace;
3034
- for (const segment of relativeSegments) {
3035
- current = path6.join(current, segment);
3036
- try {
3037
- const entry = await lstat4(current);
3038
- if (entry.isSymbolicLink()) {
3039
- throw new Error(`The state directory must not resolve through a symbolic link: ${current}.`);
3040
- }
3041
- if (!entry.isDirectory()) {
3042
- throw new Error(`The state directory path contains a non-directory entry: ${current}.`);
3043
- }
3044
- } catch (error) {
3045
- if (error.code === "ENOENT") {
3046
- break;
3047
- }
3048
- throw error;
3049
- }
3050
- }
3051
- };
3052
-
3053
- // src/operations.ts
4068
+ import { Database as Database2 } from "bun:sqlite";
3054
4069
  var HARNESS_SQLITE_FILE = "operations.sqlite";
3055
4070
  var HARNESS_OPERATIONS_SCHEMA_VERSION = 1;
3056
4071
  var TERMINAL_RUN_STATUSES = ["completed", "failed", "cancelled", "timed_out"];
@@ -3098,7 +4113,7 @@ var inspectHarnessRun = async (store, config, runId) => {
3098
4113
  if (!state) {
3099
4114
  throw new Error(`Run ${runId} was not found in ${config.stateDirectory}.`);
3100
4115
  }
3101
- const redaction = createRedactionPolicy({ includeEmails: true });
4116
+ const redaction = createRedactionPolicy2({ includeEmails: true });
3102
4117
  const traceOptions = createProductionTraceOptions({
3103
4118
  includeMessages: false,
3104
4119
  includeToolInputs: false,
@@ -3220,12 +4235,12 @@ var sqliteAdapter = (database) => ({
3220
4235
  }
3221
4236
  });
3222
4237
  var ensurePrivateStateDirectory = async (directory) => {
3223
- await mkdir3(directory, { recursive: true, mode: 448 });
3224
- const entry = await lstat5(directory);
4238
+ await mkdir4(directory, { recursive: true, mode: 448 });
4239
+ const entry = await lstat6(directory);
3225
4240
  if (entry.isSymbolicLink() || !entry.isDirectory()) {
3226
4241
  throw new Error(`The state directory must be a real directory: ${directory}.`);
3227
4242
  }
3228
- await chmod2(directory, 448);
4243
+ await chmod3(directory, 448);
3229
4244
  };
3230
4245
  var migrateJournalEntry = (entry, state, config) => ({
3231
4246
  ...entry,
@@ -3277,35 +4292,35 @@ var openHarnessPersistence = async (config, options = {}) => {
3277
4292
  backend: "file",
3278
4293
  store,
3279
4294
  memory: createFileAgentMemoryStore({
3280
- directory: path7.join(config.stateDirectory, "memory"),
4295
+ directory: path8.join(config.stateDirectory, "memory"),
3281
4296
  scope: config.scope
3282
4297
  }),
3283
4298
  migration,
3284
4299
  close() {}
3285
4300
  };
3286
4301
  }
3287
- const databasePath = path7.join(config.stateDirectory, HARNESS_SQLITE_FILE);
4302
+ const databasePath = path8.join(config.stateDirectory, HARNESS_SQLITE_FILE);
3288
4303
  let databaseEntry;
3289
4304
  try {
3290
- databaseEntry = await lstat5(databasePath);
4305
+ databaseEntry = await lstat6(databasePath);
3291
4306
  } catch (error) {
3292
4307
  if (error.code !== "ENOENT") {
3293
4308
  throw error;
3294
4309
  }
3295
4310
  try {
3296
- const handle = await open2(databasePath, "wx", 384);
4311
+ const handle = await open3(databasePath, "wx", 384);
3297
4312
  await handle.close();
3298
4313
  } catch (creationError) {
3299
4314
  if (creationError.code !== "EEXIST") {
3300
4315
  throw creationError;
3301
4316
  }
3302
4317
  }
3303
- databaseEntry = await lstat5(databasePath);
4318
+ databaseEntry = await lstat6(databasePath);
3304
4319
  }
3305
4320
  if (databaseEntry.isSymbolicLink() || !databaseEntry.isFile()) {
3306
4321
  throw new Error(`The SQLite state path must be a real file: ${databasePath}.`);
3307
4322
  }
3308
- const database = new Database(databasePath, { create: false, strict: true });
4323
+ const database = new Database2(databasePath, { create: false, strict: true });
3309
4324
  let closed = false;
3310
4325
  const closeDatabase = () => {
3311
4326
  if (!closed) {
@@ -3314,14 +4329,14 @@ var openHarnessPersistence = async (config, options = {}) => {
3314
4329
  }
3315
4330
  };
3316
4331
  try {
3317
- const openedEntry = await lstat5(databasePath);
4332
+ const openedEntry = await lstat6(databasePath);
3318
4333
  if (openedEntry.isSymbolicLink() || !openedEntry.isFile() || openedEntry.dev !== databaseEntry.dev || openedEntry.ino !== databaseEntry.ino) {
3319
4334
  throw new Error(`The SQLite state path changed while it was being opened: ${databasePath}.`);
3320
4335
  }
3321
4336
  database.exec("PRAGMA journal_mode = WAL");
3322
4337
  database.exec("PRAGMA busy_timeout = 5000");
3323
4338
  database.exec("PRAGMA foreign_keys = ON");
3324
- await chmod2(databasePath, 384);
4339
+ await chmod3(databasePath, 384);
3325
4340
  const databaseLike = sqliteAdapter(database);
3326
4341
  const store = createSqliteAgentRunStore({ db: databaseLike, scope: config.scope });
3327
4342
  const memory = createSqliteAgentMemoryStore({ db: databaseLike, scope: config.scope });
@@ -3343,11 +4358,11 @@ var openHarnessPersistence = async (config, options = {}) => {
3343
4358
  // src/harness.ts
3344
4359
  var APPROVAL_VERSION = "2026-08-17-v5";
3345
4360
  var TOOL_CONTRACT_VERSION = "enforced-execution-v1";
3346
- var createHarnessBinding = (config, mcpConfiguration, model, subagentModels, executionEnvironment) => ({
4361
+ var createHarnessBinding = (config, mcpConfiguration, model, subagentModels, providerTransportFingerprint, executionEnvironment) => ({
3347
4362
  schemaVersion: 1,
3348
4363
  id: "zhivex-harness",
3349
4364
  version: HARNESS_VERSION,
3350
- fingerprint: `sha256:${createHash6("sha256").update(JSON.stringify({
4365
+ fingerprint: `sha256:${createHash8("sha256").update(JSON.stringify({
3351
4366
  configSchemaVersion: HARNESS_CONFIG_SCHEMA_VERSION,
3352
4367
  approvalVersion: APPROVAL_VERSION,
3353
4368
  toolContractVersion: TOOL_CONTRACT_VERSION,
@@ -3355,6 +4370,7 @@ var createHarnessBinding = (config, mcpConfiguration, model, subagentModels, exe
3355
4370
  provider: config.provider,
3356
4371
  model: config.model,
3357
4372
  runtimeModel: inspectHarnessModelCapabilities(model),
4373
+ providerTransportFingerprint,
3358
4374
  subagentModels: Object.fromEntries(config.orchestration.profiles.map((profile) => [
3359
4375
  profile,
3360
4376
  inspectHarnessModelCapabilities(subagentModels?.[profile] ?? model)
@@ -3376,6 +4392,7 @@ Rules:
3376
4392
  - Make the smallest coherent change that fully addresses the task.
3377
4393
  - For every file edit, first read its digest and call propose_edits. Apply exactly that reviewed proposal with apply_patch.
3378
4394
  - apply_patch, move_file, quarantine_file, restore_file, and run_check require explicit approval from the operator.
4395
+ - Calling an approval-gated tool is how you request that approval: call the tool with its complete reviewed arguments, then let the runtime pause before execution. Never replace the tool call with a textual approval request.
3379
4396
  - With enforced OCI execution enabled, workspace tools operate on an ephemeral snapshot. Use inspect_environment_patch and obtain a separate approval through apply_environment_patch before changing the host workspace.
3380
4397
  - run_environment_command executes one allowlisted argv command without a host shell. Network, privileges, resources, environment variables, and output are bounded by the OCI policy.
3381
4398
  - Never overwrite stale content. If an expected digest no longer matches, inspect the file again and create a new proposal.
@@ -3426,7 +4443,7 @@ var createWorkspaceTools = (workspace, allowedChecks) => ({
3426
4443
  cursor: z3.string().min(1).max(2000).optional()
3427
4444
  }),
3428
4445
  metadata: readOnlyMetadata,
3429
- execute: async ({ path: path8, limit, cursor }, context) => serializeJsonValue2(await (harnessExecutionSession(context)?.workspace ?? workspace).listFiles(path8, {
4446
+ execute: async ({ path: path9, limit, cursor }, context) => serializeJsonValue2(await (harnessExecutionSession(context)?.workspace ?? workspace).listFiles(path9, {
3430
4447
  limit,
3431
4448
  ...cursor ? { cursor } : {}
3432
4449
  }))
@@ -3440,7 +4457,7 @@ var createWorkspaceTools = (workspace, allowedChecks) => ({
3440
4457
  endLine: z3.number().int().min(1).optional()
3441
4458
  }),
3442
4459
  metadata: readOnlyMetadata,
3443
- execute: async ({ path: path8, startLine, endLine }, context) => serializeJsonValue2(await (harnessExecutionSession(context)?.workspace ?? workspace).readFile(path8, startLine, endLine))
4460
+ execute: async ({ path: path9, startLine, endLine }, context) => serializeJsonValue2(await (harnessExecutionSession(context)?.workspace ?? workspace).readFile(path9, startLine, endLine))
3444
4461
  }),
3445
4462
  search_files: tool({
3446
4463
  name: "search_files",
@@ -3453,7 +4470,7 @@ var createWorkspaceTools = (workspace, allowedChecks) => ({
3453
4470
  cursor: z3.string().min(1).max(2000).optional()
3454
4471
  }),
3455
4472
  metadata: readOnlyMetadata,
3456
- execute: async ({ query, path: path8, caseSensitive, limit, cursor }, context) => serializeJsonValue2(await (harnessExecutionSession(context)?.workspace ?? workspace).searchFiles(query, path8, {
4473
+ execute: async ({ query, path: path9, caseSensitive, limit, cursor }, context) => serializeJsonValue2(await (harnessExecutionSession(context)?.workspace ?? workspace).searchFiles(query, path9, {
3457
4474
  caseSensitive,
3458
4475
  limit,
3459
4476
  ...cursor ? { cursor } : {}
@@ -3471,7 +4488,7 @@ var createWorkspaceTools = (workspace, allowedChecks) => ({
3471
4488
  }),
3472
4489
  apply_patch: tool({
3473
4490
  name: "apply_patch",
3474
- description: "Atomically apply one reviewed multi-file proposal. Every existing file requires its exact expected digest; expectedDigest=null is create-only.",
4491
+ description: "Request approval to atomically apply one reviewed multi-file proposal; the runtime pauses before execution. Every existing file requires its exact expected digest; expectedDigest=null is create-only. Call this tool instead of asking for approval in text.",
3475
4492
  schema: applyEditProposalInputSchema,
3476
4493
  ...mutationApproval,
3477
4494
  execute: async (input, context) => {
@@ -3577,32 +4594,49 @@ var createExecutionEnvironmentTools = (workspace) => ({
3577
4594
  })
3578
4595
  });
3579
4596
  var estimateMessageTokens = (messages) => Math.max(1, Math.ceil(JSON.stringify(messages).length / 4));
3580
- var createHarnessCompactor = () => {
3581
- const redaction = createRedactionPolicy2({ includeEmails: true });
3582
- return async ({ messages }) => {
3583
- const summaries = messages.map((message, index) => {
3584
- const record = message;
3585
- const parts = (record.parts ?? []).map((part) => {
3586
- if (part.type === "text" && typeof part.text === "string") {
3587
- const text = redaction.redactText(part.text).replace(/\s+/g, " ").trim();
3588
- return text.length > 160 ? `${text.slice(0, 160)}\u2026` : text;
3589
- }
3590
- if (part.type === "tool-call") {
3591
- const call = part.toolCall;
3592
- return `tool-call:${typeof call?.name === "string" ? call.name : "unknown"}`;
3593
- }
3594
- if (part.type === "tool-result") {
3595
- const result = part.toolResult;
3596
- return `tool-result:${typeof result?.toolName === "string" ? result.toolName : "unknown"}`;
3597
- }
3598
- return typeof part.type === "string" ? part.type : "part";
3599
- });
3600
- return `${index + 1}. ${record.role ?? "message"}: ${parts.join(" | ")}`;
4597
+ var summarizeHarnessMessages = (messages) => {
4598
+ const redaction = createRedactionPolicy3({ includeEmails: true });
4599
+ const summaries = messages.map((message, index) => {
4600
+ const record = message;
4601
+ const parts = (record.parts ?? []).map((part) => {
4602
+ if (part.type === "text" && typeof part.text === "string") {
4603
+ const text = redaction.redactText(part.text).replace(/\bBearer\s+\S+/gi, "Bearer [REDACTED]").replace(/\b([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)|api[_-]?key|access[_-]?token|password)\s*([=:])\s*(?:"[^"]*"|'[^']*'|\S+)/gi, "$1$2[REDACTED]").replace(/\b(?:sk|ghp|gho|github_pat)-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\s+/g, " ").trim();
4604
+ return text.length > 160 ? `${text.slice(0, 160)}\u2026` : text;
4605
+ }
4606
+ if (part.type === "tool-call") {
4607
+ const call = part.toolCall;
4608
+ return `tool-call:${typeof call?.name === "string" ? call.name : "unknown"}`;
4609
+ }
4610
+ if (part.type === "tool-result") {
4611
+ const result = part.toolResult;
4612
+ return `tool-result:${typeof result?.toolName === "string" ? result.toolName : "unknown"}`;
4613
+ }
4614
+ return typeof part.type === "string" ? part.type : "part";
3601
4615
  });
3602
- const summary = summaries.join(`
4616
+ return `${index + 1}. ${record.role ?? "message"}: ${parts.join(" | ")}`;
4617
+ });
4618
+ const summary = summaries.join(`
3603
4619
  `);
4620
+ return summary.length > 4000 ? `${summary.slice(0, 4000)}\u2026` : summary;
4621
+ };
4622
+ var compactHarnessMessages = (messages) => {
4623
+ if (messages.length === 0)
4624
+ return [];
4625
+ return [
4626
+ {
4627
+ role: "user",
4628
+ parts: [{
4629
+ type: "text",
4630
+ text: `[Compacted conversation context]
4631
+ ${summarizeHarnessMessages(messages)}`
4632
+ }]
4633
+ }
4634
+ ];
4635
+ };
4636
+ var createHarnessCompactor = () => {
4637
+ return async ({ messages }) => {
3604
4638
  return {
3605
- summary: summary.length > 4000 ? `${summary.slice(0, 4000)}\u2026` : summary,
4639
+ summary: summarizeHarnessMessages(messages),
3606
4640
  metadata: {
3607
4641
  strategy: "deterministic-redacted-transcript",
3608
4642
  sourceMessages: messages.length
@@ -3665,7 +4699,7 @@ var createProviderCompatibleBudget = (config) => {
3665
4699
  };
3666
4700
  };
3667
4701
  var createHarness = async (options = {}) => {
3668
- const config = resolveHarnessConfig(options);
4702
+ const config = resolveHarnessConfig(options, options.providerRegistry);
3669
4703
  const workspace = await Workspace.open(config.workspace);
3670
4704
  await validateStateDirectory(config.workspace, config.stateDirectory);
3671
4705
  const executionEnvironment = config.execution.backend === "oci" ? await createHarnessOciExecutionEnvironment({
@@ -3674,7 +4708,7 @@ var createHarness = async (options = {}) => {
3674
4708
  stateDirectory: config.stateDirectory,
3675
4709
  ...options.ociRuntimeAdapter ? { runtime: options.ociRuntimeAdapter } : {}
3676
4710
  }) : undefined;
3677
- const model = options.modelInstance ?? createProviderModel(config, options.env ?? process.env);
4711
+ const model = options.modelInstance ?? createProviderModel(config, options.env ?? process.env, options.providerRegistry);
3678
4712
  const capabilityRequirements = [...new Set([
3679
4713
  ...config.requiredCapabilities,
3680
4714
  ...config.orchestration.profiles.length > 0 || config.mcpConfigPath || options.mcpConfiguration ? ["tools", "streaming"] : []
@@ -3716,7 +4750,7 @@ var createHarness = async (options = {}) => {
3716
4750
  await options.onTelemetryEvent?.(event);
3717
4751
  };
3718
4752
  const costGuardrails = createCostGuardrails(config);
3719
- const binding = createHarnessBinding(config, mcpConfiguration, model, options.subagentModels, executionEnvironment);
4753
+ const binding = createHarnessBinding(config, mcpConfiguration, model, options.subagentModels, (options.providerRegistry ?? DEFAULT_PROVIDER_REGISTRY).transportFingerprint(options.env ?? process.env), executionEnvironment);
3720
4754
  const subagentRuntime = createHarnessSubagents({
3721
4755
  config,
3722
4756
  parentBinding: binding,
@@ -3847,16 +4881,25 @@ export {
3847
4881
  workspaceFilePathSchema,
3848
4882
  validateStateDirectory,
3849
4883
  validateEditProposal,
4884
+ streamResultDocument,
4885
+ streamEventDocument,
4886
+ serializeStreamResult,
4887
+ serializeStreamEvent,
4888
+ serializeHarnessModelRoutes,
3850
4889
  selectHarnessModel,
3851
4890
  runHarnessReviewGroup,
3852
4891
  runHarness,
3853
4892
  restoreFileInputSchema,
4893
+ resolveHarnessModelRoutes,
3854
4894
  resolveHarnessConfig,
3855
4895
  quarantineFileInputSchema,
3856
4896
  providerDescriptor,
3857
4897
  providerAvailability,
3858
4898
  parseProvider,
4899
+ parseHarnessModelRoute,
4900
+ openSessionStore,
3859
4901
  openHarnessPersistence,
4902
+ openCliSessionStore,
3860
4903
  normalizeHarnessMcpConfiguration,
3861
4904
  mutationOperationSchema,
3862
4905
  mutationAuditEntrySchema,
@@ -3869,6 +4912,7 @@ export {
3869
4912
  harnessExecutionSession,
3870
4913
  fileDigestSchema,
3871
4914
  executionFingerprintInput,
4915
+ estimateMessageTokens,
3872
4916
  editProposalSchema,
3873
4917
  editProposalInputSchema,
3874
4918
  editProposalChangeSchema,
@@ -3877,14 +4921,17 @@ export {
3877
4921
  editChangeSchema,
3878
4922
  describeOciCommand,
3879
4923
  defaultHarnessNamespace,
4924
+ createProviderRegistry,
3880
4925
  createProviderModel,
3881
4926
  createHttpMcpClient,
3882
4927
  createHarnessSubagents,
4928
+ createHarnessRouteModels,
3883
4929
  createHarnessOciExecutionEnvironment,
3884
4930
  createHarnessMcpTools,
3885
4931
  createHarness,
3886
4932
  createExecutionEnvironmentTools,
3887
4933
  createEditProposal,
4934
+ compactHarnessMessages,
3888
4935
  cleanupHarnessRuns,
3889
4936
  cleanupHarnessExecutionArtifacts,
3890
4937
  cancelHarnessRun,
@@ -3892,7 +4939,9 @@ export {
3892
4939
  applyEditProposalInputSchema,
3893
4940
  appendUserMessage,
3894
4941
  Workspace,
4942
+ TERMINAL_SESSION_RUN_STATUSES,
3895
4943
  TERMINAL_RUN_STATUSES,
4944
+ SESSION_RUN_STATUSES,
3896
4945
  PROVIDER_DESCRIPTORS,
3897
4946
  PROVIDERS,
3898
4947
  MAX_EDIT_PROPOSAL_BYTES,
@@ -3902,6 +4951,8 @@ export {
3902
4951
  HARNESS_SUBAGENT_PROFILE_DESCRIPTORS,
3903
4952
  HARNESS_SUBAGENT_PROFILES,
3904
4953
  HARNESS_SQLITE_FILE,
4954
+ HARNESS_SESSION_SCHEMA_VERSION,
4955
+ HARNESS_SESSION_INDEX_FILE,
3905
4956
  HARNESS_REQUIRED_CAPABILITIES,
3906
4957
  HARNESS_OPERATIONS_SCHEMA_VERSION,
3907
4958
  HARNESS_OCI_RUNTIMES,
@@ -3917,8 +4968,12 @@ export {
3917
4968
  EDIT_DIGEST_ALGORITHM,
3918
4969
  EDIT_CONTRACT_SCHEMA_VERSION,
3919
4970
  DEFAULT_SUBAGENT_BUDGET,
4971
+ DEFAULT_PROVIDER_REGISTRY,
3920
4972
  DEFAULT_OCI_EXECUTION,
3921
4973
  DEFAULT_ALLOWED_CHECKS,
3922
4974
  CliOciRuntimeAdapter,
3923
- BUN_ENGINE_RANGE
4975
+ CLI_JSON_SCHEMA_VERSION,
4976
+ CLI_EVENT_SCHEMA_VERSION,
4977
+ BUN_ENGINE_RANGE,
4978
+ BUILTIN_PROVIDER_REGISTRATIONS
3924
4979
  };