@zhivex-ai/harness 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +29 -10
  2. package/README.md +7 -5
  3. package/SUPPORT.md +2 -2
  4. package/contracts/client-protocol-v1.schema.json +593 -0
  5. package/contracts/engine-api.json +156 -0
  6. package/contracts/public-api.json +12 -0
  7. package/contracts/stable-api-signatures.json +33 -33
  8. package/dist/acp-cli.js +54 -20
  9. package/dist/audit/index.d.ts +3 -0
  10. package/dist/audit/index.js +482 -0
  11. package/dist/audit/pack.d.ts +72 -0
  12. package/dist/audit-pack-e4vaq7np.js +532 -0
  13. package/dist/cli/arguments.d.ts +3 -1
  14. package/dist/cli/audit-pack.d.ts +24 -0
  15. package/dist/cli/cli-credentials.d.ts +6 -0
  16. package/dist/cli/cli-profiles.d.ts +4 -2
  17. package/dist/cli/node-engine.d.ts +2 -0
  18. package/dist/{cli-2h37qyvg.js → cli-1cf4dcnj.js} +245 -642
  19. package/dist/{cli-entry-w0hterjs.js → cli-entry-3s63zn4w.js} +40 -13
  20. package/dist/cli-entry-7f0cen32.js +9990 -0
  21. package/dist/cli-entry-a890x3kq.js +10348 -0
  22. package/dist/{cli-entry-20w6nh0k.js → cli-entry-hdtfef27.js} +439 -19
  23. package/dist/cli-entry-rgs6bxmy.js +484 -0
  24. package/dist/cli.js +27 -2
  25. package/dist/client/json-contracts.d.ts +4 -0
  26. package/dist/client/protocol.d.ts +133 -0
  27. package/dist/client/task-control-host.d.ts +400 -0
  28. package/dist/client/task-projection-host.d.ts +12 -0
  29. package/dist/client/task-projection.d.ts +168 -0
  30. package/dist/compat/code-support.js +28 -22
  31. package/dist/{config-qt69hkqj.js → config-r9fdhce6.js} +3 -3
  32. package/dist/{config-j9624t6t.js → config-vyajmksy.js} +1 -1
  33. package/dist/engine/acp.js +15 -11
  34. package/dist/engine/client.js +14 -10
  35. package/dist/engine/desktop-providers-v1.d.ts +2 -0
  36. package/dist/engine/desktop-providers-v1.js +23 -3
  37. package/dist/engine/desktop-state-v1.js +11 -8
  38. package/dist/engine/index.d.ts +3 -0
  39. package/dist/engine/index.js +101 -554
  40. package/dist/engine/mcp-stdio-v1.js +8 -6
  41. package/dist/engine/models.js +76 -4
  42. package/dist/engine/protocol.d.ts +2 -0
  43. package/dist/engine/protocol.js +110 -0
  44. package/dist/engine/service.js +15 -11
  45. package/dist/hostile-repository-demo.js +1095 -505
  46. package/dist/{index-937cb7y8.js → index-02pqtbbg.js} +559 -33
  47. package/dist/{index-pp474137.js → index-0zs2dz26.js} +766 -931
  48. package/dist/{index-8zjmnyw5.js → index-1265j7t2.js} +73 -6
  49. package/dist/index-4fx5f3k5.js +269 -0
  50. package/dist/{index-nmjdwd4p.js → index-4gt045j0.js} +109 -1
  51. package/dist/{index-6dft2vev.js → index-6599mpmy.js} +5 -3
  52. package/dist/{index-tfzta544.js → index-74kkpyqk.js} +9 -7
  53. package/dist/{index-61gthdz6.js → index-8sm2a1wa.js} +1 -1
  54. package/dist/index-c39197wq.js +552 -0
  55. package/dist/{index-5jhgatga.js → index-crta5wzv.js} +7 -6
  56. package/dist/{index-m34q25jj.js → index-d9zkmhz3.js} +143 -824
  57. package/dist/{index-pdmevfxv.js → index-dtb2c6ds.js} +5 -259
  58. package/dist/index-f6mqa5gk.js +1535 -0
  59. package/dist/index-j6zxnc8m.js +490 -0
  60. package/dist/{index-rkbf7dqg.js → index-mv8sb1ns.js} +5 -3
  61. package/dist/{index-xqxeypt5.js → index-mzydbzp8.js} +76 -4
  62. package/dist/{index-wg1sd3at.js → index-qfsrrnz6.js} +1 -1
  63. package/dist/{index-m82cfspw.js → index-sk3cap35.js} +877 -963
  64. package/dist/index-sstgcsr0.js +511 -0
  65. package/dist/index-wm9xeq1n.js +329 -0
  66. package/dist/index-xdbkcccf.js +719 -0
  67. package/dist/{index-s2k2edts.js → index-zxg4ve4j.js} +41 -1548
  68. package/dist/index.d.ts +3 -0
  69. package/dist/index.js +93 -77
  70. package/dist/internal/desktop/providers.d.ts +2 -0
  71. package/dist/models/catalog.d.ts +2 -0
  72. package/dist/persistence/sqlite-database.d.ts +1 -0
  73. package/dist/providers/azure-openai.d.ts +14 -0
  74. package/dist/providers/bedrock.d.ts +23 -0
  75. package/dist/providers/providers.d.ts +1 -1
  76. package/dist/runtime/durable-review-group.d.ts +33 -0
  77. package/dist/runtime/harness.d.ts +11 -0
  78. package/dist/runtime/run-interruption.d.ts +7 -0
  79. package/dist/runtime/shared-budget.d.ts +30 -0
  80. package/dist/runtime/task-continuity.d.ts +300 -77
  81. package/dist/service-cli.js +15 -11
  82. package/dist/{service-client-cli-fn54ev6w.js → service-client-cli-eqrdc9zq.js} +400 -305
  83. package/dist/verify-historical-migrations.js +1095 -505
  84. package/dist/zhx.js +27 -2
  85. package/docs/AUDIT_PACK.md +99 -0
  86. package/docs/AZURE_OPENAI.md +53 -0
  87. package/docs/BEDROCK.md +56 -0
  88. package/docs/CLI.md +15 -0
  89. package/docs/CLIENT_PROTOCOL.md +178 -0
  90. package/docs/CODE_RELEASE.md +31 -1
  91. package/docs/CODE_WEB_TASKS.md +116 -0
  92. package/docs/CONTEXT_ENGINEERING.md +59 -0
  93. package/docs/CREDENTIALS.md +21 -0
  94. package/docs/DURABLE_OPERATIONS.md +93 -1
  95. package/docs/ENGINE_API.md +5 -2
  96. package/docs/EXTENSIBILITY.md +2 -2
  97. package/docs/FIRST_USE.md +6 -6
  98. package/docs/MINIMUM_TASK_AUTHORITY.md +3 -3
  99. package/docs/PROJECT_MEMORY.md +1 -1
  100. package/docs/README.md +6 -1
  101. package/docs/STABILITY.md +3 -1
  102. package/docs/SUPPORT_MATRIX.md +1 -1
  103. package/docs/SUPPORT_MATRIX_1_1.md +1 -1
  104. package/docs/USAGE.md +3 -1
  105. package/docs/VERTEX.md +2 -2
  106. package/docs/WEB_LIMITS.md +4 -3
  107. package/docs/adr/0002-isolated-mcp-stdio.md +6 -3
  108. package/docs/support-matrix-1.1.json +3 -1
  109. package/docs/support-matrix.json +6 -4
  110. package/evaluations/code-task-ui.json +20 -0
  111. package/evaluations/representative-assembly-matrix.json +10 -1
  112. package/evaluations/task-projection.json +10 -0
  113. package/evaluations/task-recovery-lab.json +60 -0
  114. package/examples/README.md +3 -3
  115. package/package.json +23 -13
  116. package/dist/cli-entry-4ngptr62.js +0 -19900
  117. package/dist/index-m49jx72n.js +0 -220
@@ -0,0 +1,329 @@
1
+ import {
2
+ createEditProposal,
3
+ editChangesSchema,
4
+ workspaceFilePathSchema
5
+ } from "./index-xdbkcccf.js";
6
+
7
+ // src/runtime/tool-policy.ts
8
+ import { createHash } from "node:crypto";
9
+ import { z } from "zod";
10
+ var normalizedPath = workspaceFilePathSchema.refine((value) => !value.includes("\\"), "Use forward slashes.");
11
+ var nameSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/);
12
+ var harnessToolPolicySchema = z.strictObject({
13
+ schemaVersion: z.literal(1),
14
+ explicitReview: z.strictObject({ schemaVersion: z.literal(1) }).optional(),
15
+ rules: z.array(z.strictObject({
16
+ id: nameSchema,
17
+ tools: z.array(nameSchema).min(1).max(128),
18
+ paths: z.array(normalizedPath).min(1).max(256).optional(),
19
+ decision: z.enum(["allow", "ask_user", "deny"]),
20
+ reason: z.string().min(1).max(512)
21
+ })).max(128)
22
+ }).superRefine((policy, context) => {
23
+ if (new Set(policy.rules.map((rule) => rule.id)).size !== policy.rules.length) {
24
+ context.addIssue({ code: "custom", message: "Tool policy rule ids must be unique." });
25
+ }
26
+ });
27
+ var rank = { allow: 0, ask_user: 1, deny: 2 };
28
+ var createHarnessToolPolicy = (input) => {
29
+ const policy = harnessToolPolicySchema.parse(input);
30
+ const canonical = { schemaVersion: 1, ...policy.explicitReview ? { explicitReview: policy.explicitReview } : {}, rules: policy.rules.map((rule) => ({
31
+ ...rule,
32
+ tools: [...new Set(rule.tools)].sort(),
33
+ ...rule.paths ? { paths: [...new Set(rule.paths)].sort() } : {}
34
+ })).sort((a, b) => a.id.localeCompare(b.id)) };
35
+ const digest = `sha256:${createHash("sha256").update(JSON.stringify(canonical)).digest("hex")}`;
36
+ const evaluate = (request) => {
37
+ if (request.hardDenied)
38
+ return { decision: "deny", ruleIds: [], reason: "Denied by the harness permission boundary." };
39
+ const paths = request.paths?.map((value) => normalizedPath.parse(value)) ?? [];
40
+ if (paths.length === 0 && canonical.rules.some((rule) => rule.tools.includes(request.toolName) && rule.paths)) {
41
+ return { decision: "deny", ruleIds: [], reason: "Path-scoped policy cannot evaluate a request without resolved paths." };
42
+ }
43
+ const matching = canonical.rules.filter((rule) => rule.tools.includes(request.toolName) && (!rule.paths || paths.some((target) => rule.paths?.includes(target))));
44
+ let decision = request.requiresApproval ? "ask_user" : "allow";
45
+ for (const rule of matching)
46
+ if (rank[rule.decision] > rank[decision])
47
+ decision = rule.decision;
48
+ const decisive = matching.filter((rule) => rule.decision === decision);
49
+ return { decision, ruleIds: decisive.map((rule) => rule.id), reason: decisive.length > 0 ? decisive.map((rule) => rule.reason).join("; ") : request.requiresApproval ? "Existing tool approval is required." : "No additional restriction from application policy." };
50
+ };
51
+ return { digest, evaluate, policy: structuredClone(canonical) };
52
+ };
53
+
54
+ class HarnessToolPolicyDeniedError extends Error {
55
+ policyDecision;
56
+ constructor(policyDecision) {
57
+ super(`Tool denied by application policy${policyDecision.ruleIds.length ? ` (${policyDecision.ruleIds.join(", ")})` : ""}: ${policyDecision.reason}`);
58
+ this.policyDecision = policyDecision;
59
+ this.name = "HarnessToolPolicyDeniedError";
60
+ }
61
+ }
62
+ var applyHarnessToolPolicy = (tools, input, options = {}) => {
63
+ const engine = createHarnessToolPolicy(input);
64
+ if (engine.policy.rules.some((rule) => rule.paths) && !options.resolvePaths) {
65
+ throw new Error("Path-scoped tool policy requires a trusted resolvePaths callback.");
66
+ }
67
+ const result = {};
68
+ for (const [name, definition] of Object.entries(tools)) {
69
+ const additionalApproval = engine.policy.rules.some((rule) => rule.tools.includes(name) && rule.decision === "ask_user");
70
+ const requiresApproval = definition.requiresApproval === true || additionalApproval;
71
+ const execute = "execute" in definition ? definition.execute : undefined;
72
+ if (!execute && engine.policy.rules.some((rule) => rule.tools.includes(name))) {
73
+ throw new Error(`Tool policy cannot wrap provider-executed tool ${name}.`);
74
+ }
75
+ result[name] = {
76
+ ...definition,
77
+ ...requiresApproval ? {
78
+ independent: false,
79
+ requiresApproval: true,
80
+ approvalMode: "interrupt",
81
+ approvalVersion: `${("approvalVersion" in definition ? definition.approvalVersion : undefined) ?? "policy"}:${engine.digest}`
82
+ } : {},
83
+ metadata: { ...definition.metadata, harnessToolPolicyDigest: engine.digest },
84
+ ...execute ? { execute: async (toolInput, context) => {
85
+ const decision = engine.evaluate({
86
+ toolName: name,
87
+ ...options.resolvePaths ? { paths: options.resolvePaths(name, toolInput) } : {},
88
+ requiresApproval
89
+ });
90
+ await options.onDecision?.(name, decision);
91
+ if (decision.decision === "deny")
92
+ throw new HarnessToolPolicyDeniedError(decision);
93
+ return execute(toolInput, context);
94
+ } } : {}
95
+ };
96
+ }
97
+ return result;
98
+ };
99
+
100
+ // src/runtime/policy-inspection.ts
101
+ import { createRedactionPolicy } from "@zhivex-ai/agents";
102
+ var redaction = createRedactionPolicy({ includeEmails: true });
103
+ var sanitizePolicyText = (text) => redaction.redactText(text).replace(/\b(?:sk|ghp|github_pat|xox[baprs])[-_][A-Za-z0-9_-]+/gi, "[REDACTED]").replace(/[\x00-\x1f\x7f-\x9f]/g, "").replace(/(?:[A-Za-z]:\\|\/)[^\s;,]+/g, "[PRIVATE_PATH]");
104
+ var inspections = new WeakMap;
105
+ function bindHarnessPolicyInspection(host, input) {
106
+ if (inspections.has(host))
107
+ throw new Error("POLICY_INSPECTION_ALREADY_BOUND");
108
+ const compiled = input.policy ? createHarnessToolPolicy(input.policy) : undefined;
109
+ const execution = input.config.execution;
110
+ const limits = {
111
+ maxSteps: input.config.maxSteps,
112
+ timeoutMs: input.config.timeoutMs,
113
+ ...input.config.unlimitedDuration === undefined ? {} : { unlimitedDuration: input.config.unlimitedDuration },
114
+ budget: structuredClone(input.config.budget),
115
+ ...execution.backend === "oci" ? { oci: {
116
+ maxProcessRuntimeMs: execution.maxProcessRuntimeMs,
117
+ maxProcessOutputBytes: execution.maxProcessOutputBytes,
118
+ maxMemoryMb: execution.maxMemoryMb,
119
+ maxPids: execution.maxPids,
120
+ maxCpus: execution.maxCpus,
121
+ maxWorkspaceBytes: execution.maxWorkspaceBytes,
122
+ maxFileWriteBytes: execution.maxFileWriteBytes,
123
+ tmpfsMb: execution.tmpfsMb
124
+ } } : {}
125
+ };
126
+ inspections.set(host, {
127
+ schemaVersion: 1,
128
+ kind: "policy-inspection",
129
+ digest: compiled?.digest ?? null,
130
+ source: compiled ? input.operatorFile ? "operator-file" : "application" : "baseline",
131
+ explicitReviewRequired: compiled?.policy.explicitReview?.schemaVersion === 1,
132
+ tools: Object.entries(input.tools).sort(([a], [b]) => a.localeCompare(b)).map(([name, tool]) => ({ name: sanitizePolicyText(name), requiresApproval: tool.requiresApproval === true })),
133
+ restrictions: (compiled?.policy.rules ?? []).map((rule) => ({
134
+ ruleId: sanitizePolicyText(rule.id),
135
+ tools: rule.tools.map(sanitizePolicyText),
136
+ decision: rule.decision,
137
+ reason: sanitizePolicyText(rule.reason),
138
+ pathCount: rule.paths?.length ?? 0
139
+ })),
140
+ execution: { configuredBackend: execution.backend, activeBackend: input.hasOciEnvironment ? "oci" : "none", evidence: "configuration-only" },
141
+ limits
142
+ });
143
+ }
144
+ function inspectHarnessPolicy(host) {
145
+ const inspection = inspections.get(host);
146
+ if (!inspection)
147
+ throw new Error("POLICY_INSPECTION_HOST_UNAVAILABLE");
148
+ return structuredClone(inspection);
149
+ }
150
+
151
+ // src/approvals/approval-history.ts
152
+ import { createHash as createHash2 } from "node:crypto";
153
+ import { z as z2 } from "zod";
154
+ var APPROVAL_HISTORY_KEY = "clientApprovalDecisionsV1";
155
+ var canonical = (value) => Array.isArray(value) ? `[${value.map(canonical).join(",")}]` : value && typeof value === "object" ? `{${Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(",")}}` : JSON.stringify(value) ?? "null";
156
+ var approvalInputDigest = (value) => createHash2("sha256").update(canonical(value)).digest("hex");
157
+ var approvalProvenanceSchema = z2.strictObject({
158
+ schemaVersion: z2.literal(1),
159
+ origin: z2.enum(["interactive", "automatic", "application", "unknown"]),
160
+ channel: z2.string().regex(/^[a-z][a-z0-9._-]{0,79}$/),
161
+ policyDigest: z2.string().regex(/^sha256:[a-f0-9]{64}$/).nullable()
162
+ });
163
+ var rowSchema = z2.object({ approvalId: z2.string().max(256), digest: z2.string().regex(/^[a-f0-9]{64}$/), toolCallId: z2.string().max(256).optional(), name: z2.string().max(256), inputDigest: z2.string().regex(/^[a-f0-9]{64}$/).optional(), approved: z2.boolean(), decidedAt: z2.number().int().nonnegative(), reviewedRevision: z2.number().int().nonnegative(), provenance: approvalProvenanceSchema.optional() }).strict();
164
+ function readApprovalDecisions(state) {
165
+ const value = state.metadata?.[APPROVAL_HISTORY_KEY];
166
+ if (value === undefined)
167
+ return [];
168
+ return z2.array(rowSchema).max(512).parse(value).map((row) => ({ ...row, provenance: row.provenance ?? {
169
+ schemaVersion: 1,
170
+ origin: "unknown",
171
+ channel: "unknown",
172
+ policyDigest: null
173
+ } }));
174
+ }
175
+ function approvalDecisionViews(state, journal, offset = 0) {
176
+ return readApprovalDecisions(state).slice(offset, offset + 25).map((row) => {
177
+ if (!row.approved)
178
+ return { ...row, status: "rejected" };
179
+ const entry = journal.find((entry2) => entry2.runId === state.runId && entry2.providerToolCallId === row.toolCallId && entry2.toolName === row.name && row.inputDigest !== undefined && approvalInputDigest(entry2.input) === row.inputDigest);
180
+ if (!entry)
181
+ return { ...row, status: ["created", "running", "waiting_approval", "cancel_requested"].includes(state.status) ? "approved" : "unknown" };
182
+ const evidence = { journalRevision: entry.revision, ...entry.completedAt === undefined ? {} : { completedAt: entry.completedAt } };
183
+ if (entry.status === "failed")
184
+ return { ...row, status: "failed", evidence };
185
+ if (entry.status !== "completed")
186
+ return { ...row, status: "unknown", evidence };
187
+ const out = entry.output && typeof entry.output === "object" && !Array.isArray(entry.output) ? entry.output : {};
188
+ if (row.name === "run_check") {
189
+ if (typeof out.exitCode !== "number" || !Number.isInteger(out.exitCode))
190
+ return { ...row, status: "unknown", evidence };
191
+ evidence.exitCode = out.exitCode;
192
+ if (typeof out.timedOut === "boolean")
193
+ evidence.timedOut = out.timedOut;
194
+ return { ...row, status: out.exitCode === 0 && !out.timedOut ? "succeeded" : "failed", evidence };
195
+ }
196
+ if (["apply_environment_patch", "verify_and_apply_environment_patch", "verify_and_apply_reviewed_edits"].includes(row.name)) {
197
+ const sha = z2.string().regex(/^sha256:[a-f0-9]{64}$/);
198
+ const importedSchema = z2.object({ kind: z2.literal("environment-patch-import"), patchId: sha, runId: z2.string(), changes: z2.array(z2.object({ path: z2.string().max(1024), beforeDigest: sha.optional(), afterDigest: sha.optional() })).max(50) });
199
+ const args = entry.input && typeof entry.input === "object" && !Array.isArray(entry.input) ? entry.input : {};
200
+ const verified = row.name !== "apply_environment_patch";
201
+ const imported = importedSchema.safeParse(verified ? out.imported : out);
202
+ if (!imported.success || imported.data.runId !== state.runId)
203
+ return { ...row, status: "unknown", evidence };
204
+ if (verified) {
205
+ const proof = z2.object({ kind: z2.enum(["verified-environment-patch-import", "verified-reviewed-edit-import"]), patchId: sha, proposalId: sha.optional(), verification: z2.object({ command: z2.array(z2.string()).min(1).max(257), exitCode: z2.literal(0), timedOut: z2.literal(false) }) }).safeParse(out);
206
+ if (!proof.success || proof.data.patchId !== imported.data.patchId || JSON.stringify(proof.data.verification.command) !== JSON.stringify([args.command, ...Array.isArray(args.args) ? args.args : []]))
207
+ return { ...row, status: "unknown", evidence };
208
+ if (row.name === "verify_and_apply_reviewed_edits") {
209
+ const changes = editChangesSchema.safeParse(args.changes);
210
+ if (!changes.success || proof.data.kind !== "verified-reviewed-edit-import" || createEditProposal({ changes: changes.data }).proposalId !== proof.data.proposalId)
211
+ return { ...row, status: "unknown", evidence };
212
+ evidence.reviewedProposalId = proof.data.proposalId;
213
+ } else if (proof.data.kind !== "verified-environment-patch-import" || args.patchId !== proof.data.patchId)
214
+ return { ...row, status: "unknown", evidence };
215
+ evidence.verifiedPatchId = proof.data.patchId;
216
+ evidence.command = proof.data.verification.command;
217
+ evidence.exitCode = 0;
218
+ evidence.timedOut = false;
219
+ } else if (args.patchId !== imported.data.patchId)
220
+ return { ...row, status: "unknown", evidence };
221
+ evidence.proposalId = imported.data.patchId;
222
+ evidence.effects = imported.data.changes.map((change) => ({ ...change, beforeDigest: change.beforeDigest ?? null, afterDigest: change.afterDigest ?? null }));
223
+ return { ...row, status: "applied", evidence };
224
+ }
225
+ if (["apply_patch", "apply_reviewed_edits", "apply_reviewed_replacement"].includes(row.name)) {
226
+ const receipt = z2.object({ kind: z2.literal("patch-result"), result: z2.object({ proposalId: z2.string().regex(/^sha256:[a-f0-9]{64}$/), changes: z2.array(z2.object({ path: z2.string().max(1024), beforeDigest: z2.string().regex(/^sha256:[a-f0-9]{64}$/).nullable().optional(), afterDigest: z2.string().regex(/^sha256:[a-f0-9]{64}$/).nullable() })).min(1).max(50) }) }).safeParse(out);
227
+ if (!receipt.success)
228
+ return { ...row, status: "unknown", evidence };
229
+ evidence.proposalId = receipt.data.result.proposalId;
230
+ evidence.effects = receipt.data.result.changes.map((change) => ({ ...change, beforeDigest: change.beforeDigest ?? null }));
231
+ return { ...row, status: "applied", evidence };
232
+ }
233
+ return { ...row, status: "succeeded", evidence };
234
+ });
235
+ }
236
+
237
+ // src/runtime/policy-decisions.ts
238
+ import { AsyncLocalStorage } from "node:async_hooks";
239
+ import { z as z3 } from "zod";
240
+ var harnessPolicyDecisionEventSchema = z3.strictObject({
241
+ schemaVersion: z3.literal(1),
242
+ type: z3.literal("policy-decision"),
243
+ phase: z3.enum(["approval-request", "tool-entry"]).default("tool-entry"),
244
+ toolName: z3.string().max(512),
245
+ decision: z3.enum(["allow", "ask_user", "deny"]),
246
+ ruleIds: z3.array(z3.string().max(512)).max(128),
247
+ reason: z3.string().max(4096),
248
+ reasonTruncated: z3.boolean(),
249
+ policyDigest: z3.string().regex(/^sha256:[a-f0-9]{64}$/).nullable(),
250
+ source: z3.enum(["operator-file", "application", "baseline"]),
251
+ approvalRequired: z3.boolean(),
252
+ explicitReviewRequired: z3.boolean(),
253
+ executionBackend: z3.enum(["none", "oci"]),
254
+ evidence: z3.literal("policy-evaluation")
255
+ });
256
+ var observers = new AsyncLocalStorage;
257
+ var observeHarnessPolicyDecisions = (observer, work) => observers.run(observer, work);
258
+ async function publishHarnessPolicyDecision(host, toolName, decision, phase = "tool-entry") {
259
+ const observer = observers.getStore();
260
+ if (!observer)
261
+ return;
262
+ const view = inspectHarnessPolicy(host);
263
+ const reason = sanitizePolicyText(decision.reason);
264
+ const event = harnessPolicyDecisionEventSchema.parse({
265
+ schemaVersion: 1,
266
+ type: "policy-decision",
267
+ phase,
268
+ toolName: sanitizePolicyText(toolName).slice(0, 512),
269
+ decision: decision.decision,
270
+ ruleIds: decision.ruleIds.map((id) => sanitizePolicyText(id).slice(0, 512)),
271
+ reason: reason.slice(0, 4096),
272
+ reasonTruncated: reason.length > 4096,
273
+ policyDigest: view.digest,
274
+ source: view.source,
275
+ approvalRequired: decision.decision === "ask_user",
276
+ explicitReviewRequired: decision.decision === "ask_user" && view.explicitReviewRequired,
277
+ executionBackend: view.execution.activeBackend,
278
+ evidence: "policy-evaluation"
279
+ });
280
+ await observer(event);
281
+ }
282
+ function observeBaselineToolPolicy(tools, host) {
283
+ const result = {};
284
+ for (const [name, definition] of Object.entries(tools)) {
285
+ const execute = "execute" in definition ? definition.execute : undefined;
286
+ result[name] = { ...definition, ...execute ? { execute: async (input, context) => {
287
+ const owner = host();
288
+ if (owner)
289
+ await publishHarnessPolicyDecision(owner, name, {
290
+ decision: definition.requiresApproval === true ? "ask_user" : "allow",
291
+ ruleIds: [],
292
+ reason: definition.requiresApproval === true ? "Existing tool approval is required; entering the executor after approval." : "No additional application restriction; tool permission checks still apply."
293
+ });
294
+ return execute(input, context);
295
+ } } : {} };
296
+ }
297
+ return result;
298
+ }
299
+ async function publishPendingPolicyDecision(host, toolName, promoted) {
300
+ if (!observers.getStore())
301
+ return;
302
+ const view = inspectHarnessPolicy(host);
303
+ const rules = view.restrictions.filter((rule) => rule.tools.includes(toolName) && rule.decision === "ask_user");
304
+ await publishHarnessPolicyDecision(host, toolName, {
305
+ decision: "ask_user",
306
+ ruleIds: rules.map((rule) => rule.ruleId),
307
+ reason: promoted ? "Approval promoted from a child run; review is pending and no execution is certified." : rules.length ? rules.map((rule) => rule.reason).join("; ") + (rules.some((rule) => rule.pathCount > 0) ? "; Path-scoped ask rules conservatively require review for this tool." : "") : "Existing tool approval is required; review is pending and no execution is certified."
308
+ }, "approval-request");
309
+ }
310
+ function createPolicyDecisionEvidence() {
311
+ const events = [];
312
+ let observed = 0;
313
+ let bytes = 0;
314
+ return {
315
+ append(event) {
316
+ observed++;
317
+ const size = Buffer.byteLength(JSON.stringify(event));
318
+ if (events.length < 64 && bytes + size <= 256 * 1024) {
319
+ events.push(structuredClone(event));
320
+ bytes += size;
321
+ }
322
+ },
323
+ snapshot() {
324
+ return { schemaVersion: 1, scope: "invocation", observed, truncated: observed > events.length, events: structuredClone(events) };
325
+ }
326
+ };
327
+ }
328
+
329
+ export { APPROVAL_HISTORY_KEY, approvalInputDigest, readApprovalDecisions, approvalDecisionViews, harnessToolPolicySchema, createHarnessToolPolicy, applyHarnessToolPolicy, bindHarnessPolicyInspection, inspectHarnessPolicy, harnessPolicyDecisionEventSchema, observeHarnessPolicyDecisions, publishHarnessPolicyDecision, observeBaselineToolPolicy, publishPendingPolicyDecision, createPolicyDecisionEvidence };