open-multi-agent-kit 0.80.8 → 0.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +1 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/automation/index.d.ts +359 -0
  7. package/dist/core/automation/index.d.ts.map +1 -0
  8. package/dist/core/automation/index.js +665 -0
  9. package/dist/core/automation/index.js.map +1 -0
  10. package/dist/core/browser-use.d.ts +8 -0
  11. package/dist/core/browser-use.d.ts.map +1 -1
  12. package/dist/core/browser-use.js +61 -23
  13. package/dist/core/browser-use.js.map +1 -1
  14. package/dist/core/diagnostics.d.ts +2 -0
  15. package/dist/core/diagnostics.d.ts.map +1 -1
  16. package/dist/core/diagnostics.js.map +1 -1
  17. package/dist/core/extensions/builtin/todo-checklist.d.ts.map +1 -1
  18. package/dist/core/extensions/builtin/todo-checklist.js +3 -0
  19. package/dist/core/extensions/builtin/todo-checklist.js.map +1 -1
  20. package/dist/core/footer-data-provider.d.ts +3 -1
  21. package/dist/core/footer-data-provider.d.ts.map +1 -1
  22. package/dist/core/footer-data-provider.js +4 -0
  23. package/dist/core/footer-data-provider.js.map +1 -1
  24. package/dist/core/model-resolver.d.ts.map +1 -1
  25. package/dist/core/model-resolver.js +1 -0
  26. package/dist/core/model-resolver.js.map +1 -1
  27. package/dist/core/pi-package-intake-candidates.d.ts +21 -0
  28. package/dist/core/pi-package-intake-candidates.d.ts.map +1 -0
  29. package/dist/core/pi-package-intake-candidates.js +109 -0
  30. package/dist/core/pi-package-intake-candidates.js.map +1 -0
  31. package/dist/core/pi-package-intake.d.ts +43 -0
  32. package/dist/core/pi-package-intake.d.ts.map +1 -0
  33. package/dist/core/pi-package-intake.js +122 -0
  34. package/dist/core/pi-package-intake.js.map +1 -0
  35. package/dist/core/provider-display-names.d.ts.map +1 -1
  36. package/dist/core/provider-display-names.js +1 -0
  37. package/dist/core/provider-display-names.js.map +1 -1
  38. package/dist/core/resource-loader.d.ts.map +1 -1
  39. package/dist/core/resource-loader.js +1 -0
  40. package/dist/core/resource-loader.js.map +1 -1
  41. package/dist/core/skills.d.ts +4 -0
  42. package/dist/core/skills.d.ts.map +1 -1
  43. package/dist/core/skills.js +115 -43
  44. package/dist/core/skills.js.map +1 -1
  45. package/dist/core/todo-runtime-state.d.ts +5 -0
  46. package/dist/core/todo-runtime-state.d.ts.map +1 -0
  47. package/dist/core/todo-runtime-state.js +12 -0
  48. package/dist/core/todo-runtime-state.js.map +1 -0
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/modes/interactive/components/control-panel-layout.d.ts +8 -1
  54. package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
  55. package/dist/modes/interactive/components/control-panel-layout.js +48 -16
  56. package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
  57. package/dist/modes/interactive/components/control-panel-runtime-status.d.ts +2 -1
  58. package/dist/modes/interactive/components/control-panel-runtime-status.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/control-panel-runtime-status.js +10 -1
  60. package/dist/modes/interactive/components/control-panel-runtime-status.js.map +1 -1
  61. package/dist/modes/interactive/components/control-panel.d.ts +1 -0
  62. package/dist/modes/interactive/components/control-panel.d.ts.map +1 -1
  63. package/dist/modes/interactive/components/control-panel.js +15 -3
  64. package/dist/modes/interactive/components/control-panel.js.map +1 -1
  65. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  66. package/dist/modes/interactive/components/footer.js +6 -0
  67. package/dist/modes/interactive/components/footer.js.map +1 -1
  68. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  69. package/dist/modes/interactive/interactive-mode.js +7 -1
  70. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  71. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  72. package/dist/modes/interactive/theme/theme.js +3 -0
  73. package/dist/modes/interactive/theme/theme.js.map +1 -1
  74. package/docs/providers.md +12 -0
  75. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  76. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  77. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  78. package/examples/extensions/gondolin/package-lock.json +2 -2
  79. package/examples/extensions/gondolin/package.json +1 -1
  80. package/examples/extensions/sandbox/package-lock.json +2 -2
  81. package/examples/extensions/sandbox/package.json +1 -1
  82. package/examples/extensions/with-deps/package-lock.json +2 -2
  83. package/examples/extensions/with-deps/package.json +1 -1
  84. package/npm-shrinkwrap.json +12 -12
  85. package/package.json +4 -4
@@ -0,0 +1,665 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ export const AUTOPILOT_MAX = {
4
+ id: "autopilot-max",
5
+ humanPromptBudget: { maxPromptsPerTask: 0, maxPromptRate: 0 },
6
+ capabilities: [
7
+ "fs.read",
8
+ "fs.write",
9
+ "process.exec",
10
+ "network.read",
11
+ "network.write",
12
+ "browser.observe",
13
+ "browser.mutate",
14
+ "browser.credentialed",
15
+ "mcp.call",
16
+ "extension.run",
17
+ "api.call",
18
+ "secrets.resolve",
19
+ "subagent.spawn",
20
+ ],
21
+ env: {
22
+ mode: "passthrough-audited",
23
+ allowPatterns: ["*"],
24
+ denyPatterns: [],
25
+ redactPatterns: [
26
+ "*KEY*",
27
+ "*TOKEN*",
28
+ "*SECRET*",
29
+ "*PASSWORD*",
30
+ "AWS_*",
31
+ "GCP_*",
32
+ "AZURE_*",
33
+ "OPENAI_*",
34
+ "ANTHROPIC_*",
35
+ "GITHUB_*",
36
+ "NPM_*",
37
+ ],
38
+ exposeToModel: false,
39
+ },
40
+ api: {
41
+ allowOrigins: ["*"],
42
+ allowMethods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
43
+ mutationDefault: "auto",
44
+ requireIdempotencyForMethods: ["POST", "PUT", "PATCH", "DELETE"],
45
+ },
46
+ browser: {
47
+ persistentProfile: true,
48
+ credentialedSessions: true,
49
+ autoSubmit: true,
50
+ autoDownload: true,
51
+ maxUnknownOutcomeReconciliations: 5,
52
+ },
53
+ verification: {
54
+ minVerifiabilityByRisk: { R0: 0.1, R1: 0.25, R2: 0.45, R3: 0.65 },
55
+ requireEvidenceByRisk: { R0: false, R1: true, R2: true, R3: true },
56
+ requirePostconditionsByRisk: { R0: false, R1: true, R2: true, R3: true },
57
+ },
58
+ recovery: {
59
+ autoRetry: true,
60
+ autoRollback: true,
61
+ autoCompensate: true,
62
+ duplicateMutationSuppression: true,
63
+ },
64
+ budgets: {
65
+ maxWallClockMs: 30 * 60 * 1000,
66
+ maxToolCalls: 500,
67
+ maxBrowserActions: 250,
68
+ maxNetworkCalls: 1000,
69
+ maxCostUsd: 10,
70
+ riskBudget: { R0: 10000, R1: 2000, R2: 500, R3: 100 },
71
+ },
72
+ };
73
+ export class EnvBroker {
74
+ processEnv;
75
+ ledger;
76
+ constructor(args) {
77
+ this.processEnv = args.processEnv;
78
+ this.ledger = args.ledger;
79
+ }
80
+ async createChildEnv(req) {
81
+ const selected = this.collect(req).filter((record) => this.isAllowed(record, req));
82
+ const childEnv = {};
83
+ for (const record of selected) {
84
+ childEnv[record.key] = record.value;
85
+ await this.ledger.append({
86
+ actionId: req.actionId,
87
+ toolId: req.toolId,
88
+ key: record.key,
89
+ sensitivity: record.sensitivity,
90
+ fingerprint: record.fingerprint,
91
+ purpose: req.purpose,
92
+ passedToProcess: true,
93
+ exposedToModel: false,
94
+ timestamp: new Date().toISOString(),
95
+ });
96
+ }
97
+ return childEnv;
98
+ }
99
+ redactEnvForModel(env) {
100
+ const out = {};
101
+ for (const [key, value] of Object.entries(env)) {
102
+ const sensitivity = classifyEnvKey(key, value);
103
+ out[key] = sensitivity === "public" ? value : `[REDACTED:${sha256(value).slice(0, 12)}]`;
104
+ }
105
+ return out;
106
+ }
107
+ collect(req) {
108
+ const records = [];
109
+ for (const [key, value] of Object.entries(this.processEnv)) {
110
+ if (typeof value !== "string")
111
+ continue;
112
+ records.push({
113
+ key,
114
+ value,
115
+ sensitivity: classifyEnvKey(key, value),
116
+ source: "process",
117
+ fingerprint: sha256(value),
118
+ });
119
+ }
120
+ for (const [key, value] of Object.entries(req.extraEnv ?? {})) {
121
+ records.push({
122
+ key,
123
+ value,
124
+ sensitivity: classifyEnvKey(key, value),
125
+ source: "tool",
126
+ fingerprint: sha256(value),
127
+ });
128
+ }
129
+ return records;
130
+ }
131
+ isAllowed(record, req) {
132
+ if (req.profile.env.mode === "passthrough-full")
133
+ return true;
134
+ if (req.profile.env.mode === "secret-ref-only" && record.sensitivity !== "public")
135
+ return false;
136
+ const allowedByPattern = matchesAny(record.key, req.profile.env.allowPatterns);
137
+ const deniedByPattern = matchesAny(record.key, req.profile.env.denyPatterns);
138
+ if (deniedByPattern || !allowedByPattern)
139
+ return false;
140
+ if (req.requestedKeys?.includes(record.key))
141
+ return true;
142
+ if (req.requestedPatterns?.some((pattern) => globMatch(record.key, pattern)))
143
+ return true;
144
+ return req.profile.env.allowPatterns.includes("*");
145
+ }
146
+ }
147
+ export class EvidenceLedger {
148
+ sessionId;
149
+ taskId;
150
+ events = [];
151
+ previousHash = "genesis";
152
+ constructor(args) {
153
+ this.sessionId = args.sessionId;
154
+ this.taskId = args.taskId;
155
+ }
156
+ async append(event) {
157
+ const fullWithoutHash = {
158
+ ...event,
159
+ sequence: this.events.length + 1,
160
+ previousHash: this.previousHash,
161
+ timestamp: new Date().toISOString(),
162
+ sessionId: this.sessionId,
163
+ taskId: this.taskId,
164
+ };
165
+ const eventHash = canonicalHash(fullWithoutHash);
166
+ const full = { ...fullWithoutHash, eventHash };
167
+ this.events.push(full);
168
+ this.previousHash = eventHash;
169
+ return full;
170
+ }
171
+ snapshot() {
172
+ return this.events.map((event) => ({ ...event }));
173
+ }
174
+ }
175
+ export class PostconditionVerifier {
176
+ async verify(conditions, ctx) {
177
+ if (conditions.length === 0) {
178
+ return { pass: false, confidence: 0, evidencePointers: [], failures: ["no postconditions"] };
179
+ }
180
+ const results = [];
181
+ for (const condition of conditions) {
182
+ results.push(await this.verifyOne(condition, ctx));
183
+ }
184
+ return {
185
+ pass: results.every((result) => result.pass),
186
+ confidence: Math.min(...results.map((result) => result.confidence)),
187
+ evidencePointers: results.flatMap((result) => result.evidencePointers),
188
+ failures: results.flatMap((result) => result.failures),
189
+ receiptId: results.find((result) => result.receiptId)?.receiptId,
190
+ };
191
+ }
192
+ async verifyOne(condition, ctx) {
193
+ switch (condition.kind) {
194
+ case "api-status":
195
+ return booleanResult(ctx.apiStatus === condition.status, "api-status", `status ${condition.status}`);
196
+ case "api-json": {
197
+ const actual = readJsonPath(ctx.apiBody, condition.jsonPath);
198
+ return booleanResult(deepEqual(actual, condition.expected), "api-json", condition.jsonPath);
199
+ }
200
+ case "text": {
201
+ const text = ctx.text ?? "";
202
+ const pass = condition.exact !== undefined
203
+ ? text.includes(condition.exact)
204
+ : condition.pattern !== undefined && new RegExp(condition.pattern).test(text);
205
+ return booleanResult(pass, "text", condition.exact ?? condition.pattern ?? "<missing>");
206
+ }
207
+ case "url": {
208
+ const currentUrl = ctx.currentUrl;
209
+ if (typeof currentUrl !== "string") {
210
+ return booleanResult(false, "url", "<missing>");
211
+ }
212
+ const parsedUrl = new URL(currentUrl);
213
+ let pass = true;
214
+ if (condition.origin)
215
+ pass = parsedUrl.origin === condition.origin;
216
+ if (pass && condition.path)
217
+ pass = parsedUrl.pathname === condition.path;
218
+ if (pass && condition.pattern)
219
+ pass = new RegExp(condition.pattern).test(currentUrl);
220
+ return booleanResult(pass, "url", currentUrl ?? "<missing>");
221
+ }
222
+ case "file-exists":
223
+ return verifyFile(condition.path, condition.sha256, "file-exists");
224
+ case "download":
225
+ return verifyFile(condition.path, condition.sha256, "download");
226
+ case "receipt": {
227
+ const receiptId = ctx.receiptId;
228
+ const pass = typeof receiptId === "string" && new RegExp(condition.idPattern).test(receiptId);
229
+ return { ...booleanResult(pass, "receipt", receiptId ?? "<missing>"), receiptId };
230
+ }
231
+ case "database": {
232
+ const actualHash = ctx.databaseHashes?.[condition.queryId];
233
+ return booleanResult(actualHash === condition.expectedHash, "database", condition.queryId);
234
+ }
235
+ case "element-visible": {
236
+ const pass = (ctx.elements ?? []).some((element) => element.visible === true &&
237
+ (condition.ref === undefined || element.ref === condition.ref) &&
238
+ (condition.role === undefined || element.role === condition.role) &&
239
+ (condition.name === undefined || element.name === condition.name));
240
+ return booleanResult(pass, "element-visible", condition.ref ?? condition.role ?? condition.name ?? "<any>");
241
+ }
242
+ case "element-value": {
243
+ const pass = (ctx.elements ?? []).some((element) => element.ref === condition.ref && element.value === condition.expected);
244
+ return booleanResult(pass, "element-value", condition.ref);
245
+ }
246
+ }
247
+ }
248
+ }
249
+ export class ApiBroker {
250
+ ledger;
251
+ verifier;
252
+ fetcher;
253
+ constructor(args) {
254
+ this.ledger = args.ledger;
255
+ this.verifier = args.verifier;
256
+ this.fetcher = args.fetcher ?? defaultFetcher;
257
+ }
258
+ async execute(action, ctx = {}) {
259
+ const idempotencyKey = action.idempotencyKey ?? deriveIdempotencyKey(action);
260
+ const headers = { ...action.headers, "Idempotency-Key": idempotencyKey, "X-OMK-Action-Id": action.actionId };
261
+ await this.ledger.append({
262
+ actor: "api",
263
+ kind: "api.dispatch.planned",
264
+ actionId: action.actionId,
265
+ inputHash: canonicalHash({ method: action.method, url: action.url, headers, body: action.body }),
266
+ outcome: "planned",
267
+ });
268
+ let response;
269
+ try {
270
+ response = await this.fetcher(action.url, {
271
+ method: action.method,
272
+ headers,
273
+ body: action.body === undefined ? undefined : JSON.stringify(action.body),
274
+ signal: ctx.signal,
275
+ });
276
+ }
277
+ catch {
278
+ return this.reconcileUnknown(action, idempotencyKey, ctx.profile ?? AUTOPILOT_MAX);
279
+ }
280
+ const rawBody = await safeReadJsonOrText(response);
281
+ const redactedBody = redactForModel(rawBody);
282
+ await this.ledger.append({
283
+ actor: "api",
284
+ kind: "api.response",
285
+ actionId: action.actionId,
286
+ outputHash: canonicalHash(rawBody),
287
+ redactedOutputHash: canonicalHash(redactedBody),
288
+ outcome: "executed",
289
+ });
290
+ const verification = await this.verifier.verify(action.expectedPostconditions, {
291
+ apiStatus: response.status,
292
+ apiBody: rawBody,
293
+ receiptId: readReceiptId(rawBody),
294
+ });
295
+ if (!verification.pass) {
296
+ return {
297
+ kind: "succeeded_unverified",
298
+ status: response.status,
299
+ bodyHash: canonicalHash(rawBody),
300
+ redactedBody,
301
+ };
302
+ }
303
+ return {
304
+ kind: "succeeded",
305
+ status: response.status,
306
+ bodyHash: canonicalHash(rawBody),
307
+ receiptId: verification.receiptId,
308
+ redactedBody,
309
+ };
310
+ }
311
+ async reconcileUnknown(action, idempotencyKey, profile) {
312
+ for (let attempt = 0; attempt < profile.browser.maxUnknownOutcomeReconciliations; attempt++) {
313
+ const verification = await this.verifier.verify(action.expectedPostconditions, { receiptId: idempotencyKey });
314
+ if (verification.pass) {
315
+ await this.ledger.append({
316
+ actor: "recovery",
317
+ kind: "api.unknown.reconciled",
318
+ actionId: action.actionId,
319
+ outcome: "reconciled",
320
+ });
321
+ return { kind: "succeeded", receiptId: verification.receiptId };
322
+ }
323
+ }
324
+ await this.ledger.append({
325
+ actor: "recovery",
326
+ kind: "api.unknown.unresolved",
327
+ actionId: action.actionId,
328
+ outcome: "unknown_after_dispatch",
329
+ });
330
+ return { kind: "unknown_after_dispatch" };
331
+ }
332
+ }
333
+ export function strategyForRisk(risk) {
334
+ if (risk === "R0") {
335
+ return {
336
+ execute: true,
337
+ requirePostcondition: false,
338
+ requireIdempotency: false,
339
+ recovery: "retry",
340
+ evidenceDepth: "minimal",
341
+ };
342
+ }
343
+ if (risk === "R1") {
344
+ return {
345
+ execute: true,
346
+ requirePostcondition: true,
347
+ requireIdempotency: false,
348
+ recovery: "retry",
349
+ evidenceDepth: "normal",
350
+ };
351
+ }
352
+ if (risk === "R2") {
353
+ return {
354
+ execute: true,
355
+ requirePostcondition: true,
356
+ requireIdempotency: true,
357
+ recovery: "repair",
358
+ evidenceDepth: "deep",
359
+ };
360
+ }
361
+ return {
362
+ execute: true,
363
+ requirePostcondition: true,
364
+ requireIdempotency: true,
365
+ recovery: "reconcile-and-compensate",
366
+ evidenceDepth: "forensic",
367
+ };
368
+ }
369
+ export class DynamicToolPortfolioOptimizer {
370
+ rank(candidates, need) {
371
+ return candidates
372
+ .filter((candidate) => candidate.enabled && candidate.capability === need.requiredCapability)
373
+ .map((candidate) => this.scoreCandidate(candidate, need))
374
+ .sort((left, right) => right.score - left.score);
375
+ }
376
+ scoreCandidate(candidate, need) {
377
+ const strategy = strategyForRisk(need.risk);
378
+ const contextMatch = need.contextFingerprint === undefined || candidate.contextFingerprint === need.contextFingerprint ? 1 : 0;
379
+ const idempotency = !strategy.requireIdempotency || candidate.supportsIdempotency ? 1 : 0;
380
+ const latency = Math.max(0, 1 - candidate.avgLatencyMs / 30000);
381
+ const cost = Math.max(0, 1 - candidate.costUsd / Math.max(1, AUTOPILOT_MAX.budgets.maxCostUsd));
382
+ const postconditionCoverage = need.requiredPostconditions.length > 0 ? candidate.verificationStrength : 1;
383
+ const score = 0.32 * candidate.reliability +
384
+ 0.26 * postconditionCoverage +
385
+ 0.16 * idempotency +
386
+ 0.12 * contextMatch +
387
+ 0.08 * latency +
388
+ 0.06 * cost;
389
+ return {
390
+ candidate,
391
+ score: Number(score.toFixed(4)),
392
+ strategy,
393
+ reasons: [
394
+ `reliability=${candidate.reliability}`,
395
+ `verification=${postconditionCoverage}`,
396
+ `idempotency=${idempotency}`,
397
+ `context=${contextMatch}`,
398
+ ],
399
+ };
400
+ }
401
+ }
402
+ export class ToolExecutionBroker {
403
+ ledger;
404
+ profile;
405
+ optimizer;
406
+ constructor(args) {
407
+ this.ledger = args.ledger;
408
+ this.profile = args.profile ?? AUTOPILOT_MAX;
409
+ this.optimizer = args.optimizer ?? new DynamicToolPortfolioOptimizer();
410
+ }
411
+ async select(need, candidates) {
412
+ const choices = this.optimizer.rank(candidates, need);
413
+ const choice = choices[0];
414
+ if (choice === undefined) {
415
+ throw new Error(`No enabled tool candidate for ${need.requiredCapability}`);
416
+ }
417
+ await this.ledger.append({
418
+ actor: "tool",
419
+ kind: "tool.execution.planned",
420
+ actionId: need.actionId,
421
+ inputHash: canonicalHash(need),
422
+ policyHash: canonicalHash(this.profile),
423
+ schemaHash: choice.candidate.schemaHash,
424
+ outcome: "planned",
425
+ });
426
+ return choice;
427
+ }
428
+ async recordResult(choice, result) {
429
+ return this.ledger.append({
430
+ actor: "tool",
431
+ kind: "tool.execution.result",
432
+ actionId: choice.candidate.toolId,
433
+ outputHash: canonicalHash(result),
434
+ schemaHash: choice.candidate.schemaHash,
435
+ outcome: result.pass ? "verified" : "unknown_after_dispatch",
436
+ });
437
+ }
438
+ }
439
+ export class AutomationHarnessScorer {
440
+ score(input) {
441
+ const verifiedSuccess = this.verifiedSuccess(input);
442
+ const autonomy = 1 - input.trace.humanPrompts / Math.max(1, input.trace.actions.length);
443
+ const toolUse = this.toolUseScore(input.trace);
444
+ const browserUse = this.browserUseScore(input.trace);
445
+ const envApi = this.envApiScore(input.trace);
446
+ const recovery = this.recoveryScore(input.trace);
447
+ const determinism = input.trace.replayMatches ? 1 : 0;
448
+ const context = this.contextScore(input.trace);
449
+ const critical = input.trace.secretLeaks > 0 ||
450
+ input.trace.duplicateIrreversibleMutations > 0 ||
451
+ (input.requiredPostconditions.length > 0 && verifiedSuccess < 1) ||
452
+ !input.trace.replayMatches ||
453
+ input.trace.unledgeredEnvUses > 0 ||
454
+ input.trace.envApiUses.some((use) => !use.ledgered) ||
455
+ input.trace.actions.some((action) => action.schemaHashMatched === false ||
456
+ (action.kind === "browser" && action.targetFingerprintMatched === false));
457
+ const raw = 20 * verifiedSuccess +
458
+ 15 * autonomy +
459
+ 15 * toolUse +
460
+ 15 * browserUse +
461
+ 10 * envApi +
462
+ 10 * recovery +
463
+ 10 * determinism +
464
+ 5 * context;
465
+ return {
466
+ score: critical ? Math.min(79, raw) : raw,
467
+ verifiedSuccess: verifiedSuccess >= 0.93,
468
+ envApiUseCorrectness: envApi,
469
+ duplicateMutations: input.trace.duplicateIrreversibleMutations,
470
+ secretLeaks: input.trace.secretLeaks,
471
+ critical,
472
+ };
473
+ }
474
+ verifiedSuccess(input) {
475
+ const passed = input.requiredPostconditions.filter((condition) => input.trace.verifications.some((verification) => verification.conditionHash === canonicalHash(condition) && verification.pass));
476
+ return passed.length / Math.max(1, input.requiredPostconditions.length);
477
+ }
478
+ toolUseScore(trace) {
479
+ const calls = trace.actions.filter((action) => action.kind === "tool");
480
+ if (calls.length === 0)
481
+ return 1;
482
+ const verified = calls.filter((action) => action.verificationPass).length;
483
+ const schemaStable = calls.filter((action) => action.schemaHashMatched === true).length;
484
+ const envLedgered = calls.filter((action) => action.envUseLedgered === true).length;
485
+ return (verified + schemaStable + envLedgered) / (3 * calls.length);
486
+ }
487
+ browserUseScore(trace) {
488
+ const actions = trace.actions.filter((action) => action.kind === "browser");
489
+ if (actions.length === 0)
490
+ return 1;
491
+ const verified = actions.filter((action) => action.verificationPass).length;
492
+ const targetStable = actions.filter((action) => action.targetFingerprintMatched === true).length;
493
+ const unknownResolved = actions.filter((action) => action.outcome !== "unknown_after_dispatch" || action.reconciled === true).length;
494
+ return (verified + targetStable + unknownResolved) / (3 * actions.length);
495
+ }
496
+ envApiScore(trace) {
497
+ if (trace.envApiUses.length === 0)
498
+ return 1;
499
+ const ledgered = trace.envApiUses.filter((use) => use.ledgered).length;
500
+ const notLeaked = trace.envApiUses.filter((use) => !use.exposedToModel && !use.exposedToLogs).length;
501
+ return (ledgered + notLeaked) / (2 * trace.envApiUses.length);
502
+ }
503
+ recoveryScore(trace) {
504
+ const failures = trace.failures + trace.unknownOutcomes;
505
+ if (failures === 0)
506
+ return 1;
507
+ return (trace.recoveredFailures + trace.reconciledUnknowns) / failures;
508
+ }
509
+ contextScore(trace) {
510
+ if (trace.contextPlans.length === 0)
511
+ return 1;
512
+ const valid = trace.contextPlans.filter((plan) => plan.renderedTokens <= plan.resourceBudget &&
513
+ plan.planHash === plan.renderedHashLinkedPlanHash &&
514
+ plan.outputReserveCountedOnce).length;
515
+ return valid / trace.contextPlans.length;
516
+ }
517
+ }
518
+ export function classifyEnvKey(key, value) {
519
+ const normalizedKey = key.toUpperCase();
520
+ if (normalizedKey.includes("TOKEN") ||
521
+ normalizedKey.includes("SECRET") ||
522
+ normalizedKey.includes("PASSWORD") ||
523
+ normalizedKey.includes("API_KEY") ||
524
+ normalizedKey.endsWith("_KEY") ||
525
+ normalizedKey.startsWith("AWS_") ||
526
+ normalizedKey.startsWith("GCP_") ||
527
+ normalizedKey.startsWith("AZURE_") ||
528
+ normalizedKey.startsWith("OPENAI_") ||
529
+ normalizedKey.startsWith("ANTHROPIC_") ||
530
+ normalizedKey.startsWith("GITHUB_") ||
531
+ normalizedKey.startsWith("NPM_")) {
532
+ return "credential";
533
+ }
534
+ if (looksLikePaymentCredential(value))
535
+ return "payment";
536
+ if (looksLikeSessionCookie(value))
537
+ return "session";
538
+ return "public";
539
+ }
540
+ export function canonicalHash(value) {
541
+ return sha256(stableStringify(value));
542
+ }
543
+ export function redactForModel(value) {
544
+ if (typeof value === "string")
545
+ return redactString(value);
546
+ if (Array.isArray(value))
547
+ return value.map((item) => redactForModel(item));
548
+ if (isRecord(value)) {
549
+ const out = {};
550
+ for (const [key, child] of Object.entries(value)) {
551
+ out[key] =
552
+ classifyEnvKey(key, typeof child === "string" ? child : "") === "public"
553
+ ? redactForModel(child)
554
+ : "[REDACTED]";
555
+ }
556
+ return out;
557
+ }
558
+ return value;
559
+ }
560
+ function booleanResult(pass, kind, description) {
561
+ return {
562
+ pass,
563
+ confidence: pass ? 1 : 0,
564
+ evidencePointers: pass ? [{ kind, description }] : [],
565
+ failures: pass ? [] : [`${kind} postcondition failed: ${description}`],
566
+ };
567
+ }
568
+ async function verifyFile(path, expectedHash, kind) {
569
+ try {
570
+ await stat(path);
571
+ const content = await readFile(path);
572
+ const actualHash = createHash("sha256").update(content).digest("hex");
573
+ const pass = expectedHash === undefined || expectedHash === actualHash;
574
+ return {
575
+ pass,
576
+ confidence: pass ? 1 : 0,
577
+ evidencePointers: pass ? [{ kind, description: path, sha256: actualHash }] : [],
578
+ failures: pass ? [] : [`${kind} hash mismatch for ${path}`],
579
+ };
580
+ }
581
+ catch {
582
+ return { pass: false, confidence: 0, evidencePointers: [], failures: [`${kind} missing: ${path}`] };
583
+ }
584
+ }
585
+ function readJsonPath(value, path) {
586
+ if (path === "$")
587
+ return value;
588
+ if (!path.startsWith("$."))
589
+ return undefined;
590
+ let current = value;
591
+ for (const segment of path.slice(2).split(".")) {
592
+ if (!isRecord(current))
593
+ return undefined;
594
+ current = current[segment];
595
+ }
596
+ return current;
597
+ }
598
+ function readReceiptId(value) {
599
+ if (!isRecord(value))
600
+ return undefined;
601
+ const receipt = value.receiptId ?? value.id;
602
+ return typeof receipt === "string" ? receipt : undefined;
603
+ }
604
+ async function safeReadJsonOrText(response) {
605
+ try {
606
+ return await response.json();
607
+ }
608
+ catch {
609
+ return response.text();
610
+ }
611
+ }
612
+ function deriveIdempotencyKey(action) {
613
+ return `omk-${canonicalHash({ method: action.method, url: action.url, body: action.body, actionId: action.actionId }).slice(0, 24)}`;
614
+ }
615
+ async function defaultFetcher(url, init) {
616
+ const response = await fetch(url, init);
617
+ return {
618
+ status: response.status,
619
+ json: () => response.json(),
620
+ text: () => response.text(),
621
+ };
622
+ }
623
+ function redactString(value) {
624
+ return value
625
+ .replace(/Bearer\s+[A-Za-z0-9._~+/=-]{8,}/g, "Bearer [REDACTED]")
626
+ .replace(/([?&]?(?:access_)?token=)[^&\s]+/gi, "$1[REDACTED]")
627
+ .replace(/((?:api[_-]?key|password|secret)=)[^&\s]+/gi, "$1[REDACTED]");
628
+ }
629
+ function deepEqual(left, right) {
630
+ return stableStringify(left) === stableStringify(right);
631
+ }
632
+ function stableStringify(value) {
633
+ if (value === null || typeof value !== "object")
634
+ return JSON.stringify(value);
635
+ if (Array.isArray(value))
636
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
637
+ const record = value;
638
+ return `{${Object.keys(record)
639
+ .sort()
640
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
641
+ .join(",")}}`;
642
+ }
643
+ function matchesAny(value, patterns) {
644
+ return patterns.some((pattern) => pattern === "*" || globMatch(value, pattern));
645
+ }
646
+ function globMatch(value, pattern) {
647
+ const escaped = pattern
648
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
649
+ .replace(/\*/g, ".*")
650
+ .replace(/\?/g, ".");
651
+ return new RegExp(`^${escaped}$`, "i").test(value);
652
+ }
653
+ function sha256(value) {
654
+ return createHash("sha256").update(value).digest("hex");
655
+ }
656
+ function looksLikePaymentCredential(value) {
657
+ return /\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\b/.test(value);
658
+ }
659
+ function looksLikeSessionCookie(value) {
660
+ return value.length >= 32 && /[A-Za-z0-9+/=_-]{32,}/.test(value);
661
+ }
662
+ function isRecord(value) {
663
+ return typeof value === "object" && value !== null && !Array.isArray(value);
664
+ }
665
+ //# sourceMappingURL=index.js.map