protect-mcp 0.7.3 → 0.7.4

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
@@ -1002,10 +1002,10 @@ var require_types = __commonJS({
1002
1002
  if (!jwtRegex.test(jwt))
1003
1003
  return false;
1004
1004
  try {
1005
- const [header] = jwt.split(".");
1006
- if (!header)
1005
+ const [header2] = jwt.split(".");
1006
+ if (!header2)
1007
1007
  return false;
1008
- const base64 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "=");
1008
+ const base64 = header2.replace(/-/g, "+").replace(/_/g, "/").padEnd(header2.length + (4 - header2.length % 4) % 4, "=");
1009
1009
  const decoded = JSON.parse(atob(base64));
1010
1010
  if (typeof decoded !== "object" || decoded === null)
1011
1011
  return false;
@@ -6486,10 +6486,10 @@ var require_schemas = __commonJS({
6486
6486
  const tokensParts = token.split(".");
6487
6487
  if (tokensParts.length !== 3)
6488
6488
  return false;
6489
- const [header] = tokensParts;
6490
- if (!header)
6489
+ const [header2] = tokensParts;
6490
+ if (!header2)
6491
6491
  return false;
6492
- const parsedHeader = JSON.parse(atob(header));
6492
+ const parsedHeader = JSON.parse(atob(header2));
6493
6493
  if ("typ" in parsedHeader && parsedHeader?.typ !== "JWT")
6494
6494
  return false;
6495
6495
  if (!parsedHeader.alg)
@@ -35019,7 +35019,9 @@ var require_mcp = __commonJS({
35019
35019
  var index_exports = {};
35020
35020
  __export(index_exports, {
35021
35021
  BUILTIN_PATTERNS: () => BUILTIN_PATTERNS,
35022
+ CONNECTOR_PILOTS: () => CONNECTOR_PILOTS,
35022
35023
  ConfidentialGate: () => ConfidentialGate,
35024
+ POLICY_PACKS: () => POLICY_PACKS,
35023
35025
  ProtectGateway: () => ProtectGateway,
35024
35026
  ReceiptPropagator: () => ReceiptPropagator,
35025
35027
  ScopeBlindBridge: () => ScopeBlindBridge,
@@ -35029,6 +35031,9 @@ __export(index_exports, {
35029
35031
  collectSignedReceipts: () => collectSignedReceipts,
35030
35032
  computeCalibration: () => computeCalibration,
35031
35033
  confidentialInference: () => confidentialInference,
35034
+ connectorDirectory: () => connectorDirectory,
35035
+ connectorDoctor: () => connectorDoctor,
35036
+ connectorPilotIds: () => connectorPilotIds,
35032
35037
  createApprovalChallenge: () => createApprovalChallenge,
35033
35038
  createApprovalReceiptPayload: () => createApprovalReceiptPayload,
35034
35039
  createAttestationField: () => createAttestationField,
@@ -35040,6 +35045,7 @@ __export(index_exports, {
35040
35045
  createReceiptChannel: () => createReceiptChannel,
35041
35046
  createSandbox: () => createSandbox,
35042
35047
  createSandboxServer: () => createSandboxServer,
35048
+ createSelectiveDisclosurePackage: () => createSelectiveDisclosurePackage,
35043
35049
  destroySandbox: () => destroySandbox,
35044
35050
  discloseField: () => discloseField,
35045
35051
  ed25519ToDIDKey: () => ed25519ToDIDKey,
@@ -35060,6 +35066,8 @@ __export(index_exports, {
35060
35066
  generateSampleCedarPolicy: () => generateSampleCedarPolicy,
35061
35067
  generateSchemaStub: () => generateSchemaStub,
35062
35068
  generateVerifyReceiptSkill: () => generateVerifyReceiptSkill,
35069
+ getConnectorPilot: () => getConnectorPilot,
35070
+ getPolicyPack: () => getPolicyPack,
35063
35071
  getScopeBlindBridge: () => getScopeBlindBridge,
35064
35072
  getSignerInfo: () => getSignerInfo,
35065
35073
  getToolPolicy: () => getToolPolicy,
@@ -35080,8 +35088,10 @@ __export(index_exports, {
35080
35088
  parseLogFile: () => parseLogFile,
35081
35089
  parseNotificationConfigFromEnv: () => parseNotificationConfigFromEnv,
35082
35090
  parseRateLimit: () => parseRateLimit,
35091
+ policyPackIds: () => policyPackIds,
35083
35092
  policySetFromSource: () => policySetFromSource,
35084
35093
  queryExternalPDP: () => queryExternalPDP,
35094
+ readInstalledConnectorPilots: () => readInstalledConnectorPilots,
35085
35095
  receiptToVP: () => receiptToVP,
35086
35096
  receiptsToHFRows: () => receiptsToHFRows,
35087
35097
  redactFields: () => redactFields,
@@ -35105,13 +35115,15 @@ __export(index_exports, {
35105
35115
  verifyApprovalAssertion: () => verifyApprovalAssertion,
35106
35116
  verifyCommitment: () => verifyCommitment,
35107
35117
  verifyEvidenceAttestation: () => verifyEvidenceAttestation,
35108
- verifyRekorAnchor: () => verifyRekorAnchor
35118
+ verifyRekorAnchor: () => verifyRekorAnchor,
35119
+ verifySelectiveDisclosurePackage: () => verifySelectiveDisclosurePackage,
35120
+ writeConnectorPilots: () => writeConnectorPilots
35109
35121
  });
35110
35122
  module.exports = __toCommonJS(index_exports);
35111
35123
 
35112
35124
  // src/gateway.ts
35113
35125
  var import_node_child_process = require("child_process");
35114
- var import_node_crypto3 = require("crypto");
35126
+ var import_node_crypto4 = require("crypto");
35115
35127
  var import_node_readline = require("readline");
35116
35128
  var import_node_fs6 = require("fs");
35117
35129
  var import_node_path4 = require("path");
@@ -35541,6 +35553,7 @@ function signDecision(entry) {
35541
35553
  if (entry.timing) payload.timing = entry.timing;
35542
35554
  if (entry.swarm) payload.swarm = entry.swarm;
35543
35555
  if (entry.payload_digest) payload.payload_digest = entry.payload_digest;
35556
+ if (entry.action_readback) payload.action_readback = entry.action_readback;
35544
35557
  if (entry.deny_iteration) payload.deny_iteration = entry.deny_iteration;
35545
35558
  const result = artifactsModule.createSignedArtifact(
35546
35559
  artifactType,
@@ -36347,6 +36360,95 @@ function handleListApprovals(res, approvalStore) {
36347
36360
  res.end(JSON.stringify({ grants }));
36348
36361
  }
36349
36362
 
36363
+ // src/action-readback.ts
36364
+ var import_node_crypto3 = require("crypto");
36365
+ var SECRET_KEY_RE = /(api[_-]?key|authorization|bearer|credential|password|secret|session|token|private[_-]?key)/i;
36366
+ var DESTINATION_KEYS = [
36367
+ "path",
36368
+ "file_path",
36369
+ "filePath",
36370
+ "url",
36371
+ "uri",
36372
+ "endpoint",
36373
+ "host",
36374
+ "hostname",
36375
+ "repo",
36376
+ "repository",
36377
+ "branch",
36378
+ "channel",
36379
+ "to",
36380
+ "recipient",
36381
+ "symbol",
36382
+ "account",
36383
+ "bucket",
36384
+ "database",
36385
+ "table",
36386
+ "service"
36387
+ ];
36388
+ function stableStringify(value) {
36389
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
36390
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
36391
+ const obj = value;
36392
+ return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`).join(",")}}`;
36393
+ }
36394
+ function redact(value, path = [], redacted = [], disclosed = [], depth = 0) {
36395
+ if (depth > 4) return "[truncated-depth]";
36396
+ if (value === null || value === void 0) return value;
36397
+ if (typeof value !== "object") {
36398
+ if (path.length > 0) disclosed.push(path.join("."));
36399
+ if (typeof value === "string" && value.length > 240) return `${value.slice(0, 240)}...`;
36400
+ return value;
36401
+ }
36402
+ if (Array.isArray(value)) {
36403
+ return value.slice(0, 20).map((item, idx) => redact(item, [...path, String(idx)], redacted, disclosed, depth + 1));
36404
+ }
36405
+ const out = {};
36406
+ for (const [key, child] of Object.entries(value)) {
36407
+ const childPath = [...path, key];
36408
+ if (SECRET_KEY_RE.test(key)) {
36409
+ redacted.push(childPath.join("."));
36410
+ out[key] = "[redacted]";
36411
+ continue;
36412
+ }
36413
+ out[key] = redact(child, childPath, redacted, disclosed, depth + 1);
36414
+ }
36415
+ return out;
36416
+ }
36417
+ function firstStringValue(input, keys) {
36418
+ for (const key of keys) {
36419
+ const value = input[key];
36420
+ if (typeof value === "string" && value.trim()) return value.trim();
36421
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
36422
+ }
36423
+ return void 0;
36424
+ }
36425
+ function actionFor(tool, input) {
36426
+ const explicit = firstStringValue(input, ["action", "operation", "method", "verb", "command"]);
36427
+ if (explicit) return explicit.length > 90 ? `${explicit.slice(0, 90)}...` : explicit;
36428
+ return tool;
36429
+ }
36430
+ function buildActionReadback(tool, input) {
36431
+ const normalized = input && typeof input === "object" && !Array.isArray(input) ? input : { value: input };
36432
+ const canonical = stableStringify(normalized);
36433
+ const redactedFields = [];
36434
+ const disclosedFields = [];
36435
+ const payloadPreview = redact(normalized, [], redactedFields, disclosedFields);
36436
+ const action = actionFor(tool, normalized);
36437
+ const destination = firstStringValue(normalized, DESTINATION_KEYS);
36438
+ const summary = destination ? `${tool} -> ${destination}` : `${tool} request`;
36439
+ return {
36440
+ tool,
36441
+ action,
36442
+ destination,
36443
+ payload_preview: payloadPreview,
36444
+ payload_hash: (0, import_node_crypto3.createHash)("sha256").update(canonical).digest("hex"),
36445
+ payload_bytes: Buffer.byteLength(canonical, "utf-8"),
36446
+ disclosed_fields: [...new Set(disclosedFields)].slice(0, 80),
36447
+ redacted_fields: [...new Set(redactedFields)].slice(0, 80),
36448
+ summary
36449
+ };
36450
+ }
36451
+
36350
36452
  // src/gateway.ts
36351
36453
  var LOG_FILE2 = ".protect-mcp-log.jsonl";
36352
36454
  var RECEIPTS_FILE = ".protect-mcp-receipts.jsonl";
@@ -36362,7 +36464,7 @@ var ProtectGateway = class {
36362
36464
  /** Approval grants keyed by request_id (scoped to the specific action that was requested) */
36363
36465
  approvalStore = /* @__PURE__ */ new Map();
36364
36466
  /** Random nonce generated at startup — required for approval endpoint authentication */
36365
- approvalNonce = (0, import_node_crypto3.randomBytes)(16).toString("hex");
36467
+ approvalNonce = (0, import_node_crypto4.randomBytes)(16).toString("hex");
36366
36468
  currentTier = "unknown";
36367
36469
  admissionResult = null;
36368
36470
  /** Notification config for approval gates (SMS, webhook, email) */
@@ -36516,8 +36618,10 @@ var ProtectGateway = class {
36516
36618
  }
36517
36619
  async interceptToolCall(request) {
36518
36620
  const toolName = request.params?.name || "unknown";
36519
- const requestId = (0, import_node_crypto3.randomUUID)().slice(0, 12);
36621
+ const requestId = (0, import_node_crypto4.randomUUID)().slice(0, 12);
36520
36622
  const mode = this.config.enforce ? "enforce" : "shadow";
36623
+ const toolInput = request.params?.arguments && typeof request.params.arguments === "object" ? request.params.arguments : request.params || {};
36624
+ const actionReadback = buildActionReadback(toolName, toolInput);
36521
36625
  let resolvedAgentKid = this.admissionResult?.agent_id;
36522
36626
  let effectiveToolPolicy;
36523
36627
  if (this.config.multiAgent?.enabled) {
@@ -36527,7 +36631,7 @@ var ProtectGateway = class {
36527
36631
  if (agentOverrides && agentOverrides[toolName]) {
36528
36632
  effectiveToolPolicy = { ...getToolPolicy(toolName, this.config.policy), ...agentOverrides[toolName] };
36529
36633
  } else if (!resolvedAgentKid && this.config.multiAgent.unknownAgentPolicy === "deny") {
36530
- this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "unknown_agent_denied", request_id: requestId, tier: this.currentTier });
36634
+ this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "unknown_agent_denied", request_id: requestId, tier: this.currentTier, action_readback: actionReadback });
36531
36635
  if (this.config.enforce) {
36532
36636
  return this.makeErrorResponse(request.id, -32600, `Tool "${toolName}" denied: unidentified agent`);
36533
36637
  }
@@ -36548,7 +36652,7 @@ var ProtectGateway = class {
36548
36652
  if (cred.resolved) {
36549
36653
  credentialRef = cred.label;
36550
36654
  } else if (cred.error && !cred.error.includes("not configured")) {
36551
- this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "credential_error", request_id: requestId, tier: this.currentTier, credential_ref: toolName });
36655
+ this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "credential_error", request_id: requestId, tier: this.currentTier, credential_ref: toolName, action_readback: actionReadback });
36552
36656
  if (this.config.enforce) {
36553
36657
  return this.makeErrorResponse(request.id, -32600, `Credential error for tool "${toolName}"`);
36554
36658
  }
@@ -36563,13 +36667,13 @@ var ProtectGateway = class {
36563
36667
  });
36564
36668
  if (!cedarDecision.allowed) {
36565
36669
  const reason = cedarDecision.reason || "cedar_deny";
36566
- this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: reason, request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36670
+ this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: reason, request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36567
36671
  if (this.config.enforce) {
36568
36672
  return this.makeErrorResponse(request.id, -32600, `Tool "${toolName}" denied by Cedar policy`);
36569
36673
  }
36570
36674
  return null;
36571
36675
  }
36572
- this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "cedar_allow", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36676
+ this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "cedar_allow", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36573
36677
  return null;
36574
36678
  } catch (err) {
36575
36679
  if (this.config.verbose) this.log(`Cedar evaluation error: ${err instanceof Error ? err.message : err}`);
@@ -36587,7 +36691,7 @@ var ProtectGateway = class {
36587
36691
  const externalDecision = await queryExternalPDP(ctx, this.config.policy.external);
36588
36692
  if (!externalDecision.allowed) {
36589
36693
  const reason = `external_pdp_deny${externalDecision.reason ? ": " + externalDecision.reason : ""}`;
36590
- this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: reason, request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36694
+ this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: reason, request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36591
36695
  if (this.config.enforce) {
36592
36696
  return this.makeErrorResponse(request.id, -32600, `Tool "${toolName}" denied by external policy engine`);
36593
36697
  }
@@ -36599,7 +36703,7 @@ var ProtectGateway = class {
36599
36703
  }
36600
36704
  if (toolPolicy.min_tier) {
36601
36705
  if (!meetsMinTier(this.currentTier, toolPolicy.min_tier)) {
36602
- this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "tier_insufficient", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36706
+ this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "tier_insufficient", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36603
36707
  if (this.config.enforce) {
36604
36708
  return this.makeErrorResponse(request.id, -32600, `Tool "${toolName}" requires tier "${toolPolicy.min_tier}"`);
36605
36709
  }
@@ -36607,7 +36711,7 @@ var ProtectGateway = class {
36607
36711
  }
36608
36712
  }
36609
36713
  if (toolPolicy.block) {
36610
- this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "policy_block", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36714
+ this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "policy_block", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36611
36715
  if (this.config.enforce) {
36612
36716
  return this.makeErrorResponse(request.id, -32600, `Tool "${toolName}" is blocked by policy`);
36613
36717
  }
@@ -36618,10 +36722,10 @@ var ProtectGateway = class {
36618
36722
  const alwaysGrant = this.approvalStore.get(`always:${toolName}`);
36619
36723
  if (grant && Date.now() < grant.expires_at || alwaysGrant && Date.now() < alwaysGrant.expires_at) {
36620
36724
  if (grant && grant.mode === "once") this.approvalStore.delete(requestId);
36621
- this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "approval_granted", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36725
+ this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "approval_granted", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36622
36726
  return null;
36623
36727
  }
36624
- this.emitDecisionLog({ tool: toolName, decision: "require_approval", reason_code: "requires_human_approval", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36728
+ this.emitDecisionLog({ tool: toolName, decision: "require_approval", reason_code: "requires_human_approval", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36625
36729
  if (this.notificationConfig) {
36626
36730
  sendApprovalNotification(this.notificationConfig, {
36627
36731
  requestId,
@@ -36644,7 +36748,7 @@ var ProtectGateway = class {
36644
36748
  content: [
36645
36749
  {
36646
36750
  type: "text",
36647
- text: `REQUIRES_APPROVAL: The tool "${toolName}" requires human approval before execution. Request ID: ${requestId}. Approval nonce: ${this.approvalNonce}. Tell the user you need their approval to use "${toolName}" and will retry when granted. Do NOT retry this tool call until the user explicitly approves it.`
36751
+ text: `REQUIRES_APPROVAL: The tool "${toolName}" requires human approval before execution. Exact action: ${actionReadback.summary}. Payload hash: ${actionReadback.payload_hash.slice(0, 16)}\u2026 Request ID: ${requestId}. Approval nonce: ${this.approvalNonce}. Tell the user you need their approval to use "${toolName}" and will retry when granted. Do NOT retry this tool call until the user explicitly approves it.`
36648
36752
  }
36649
36753
  ],
36650
36754
  isError: true
@@ -36660,19 +36764,19 @@ var ProtectGateway = class {
36660
36764
  const key = `tool:${toolName}:${this.currentTier}`;
36661
36765
  const { allowed, remaining } = checkRateLimit(key, limit, this.rateLimitStore);
36662
36766
  if (!allowed) {
36663
- this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "rate_limit_exceeded", request_id: requestId, rate_limit_remaining: 0, tier: this.currentTier, credential_ref: credentialRef });
36767
+ this.emitDecisionLog({ tool: toolName, decision: "deny", reason_code: "rate_limit_exceeded", request_id: requestId, rate_limit_remaining: 0, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36664
36768
  if (this.config.enforce) {
36665
36769
  return this.makeErrorResponse(request.id, -32600, `Tool "${toolName}" rate limit exceeded (${rateSpec})`);
36666
36770
  }
36667
36771
  return null;
36668
36772
  }
36669
- this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "policy_allow", request_id: requestId, rate_limit_remaining: remaining, tier: this.currentTier, credential_ref: credentialRef });
36773
+ this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "policy_allow", request_id: requestId, rate_limit_remaining: remaining, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36670
36774
  } catch {
36671
- this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "default_allow", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36775
+ this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: "default_allow", request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36672
36776
  }
36673
36777
  } else {
36674
36778
  const reasonCode = this.config.enforce ? "policy_allow" : "observe_mode";
36675
- this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: reasonCode, request_id: requestId, tier: this.currentTier, credential_ref: credentialRef });
36779
+ this.emitDecisionLog({ tool: toolName, decision: "allow", reason_code: reasonCode, request_id: requestId, tier: this.currentTier, credential_ref: credentialRef, action_readback: actionReadback });
36676
36780
  }
36677
36781
  return null;
36678
36782
  }
@@ -36694,8 +36798,8 @@ var ProtectGateway = class {
36694
36798
  */
36695
36799
  emitDecisionLog(entry) {
36696
36800
  const mode = this.config.enforce ? "enforce" : "shadow";
36697
- const otelTraceId = entry.otel_trace_id || (0, import_node_crypto3.randomBytes)(16).toString("hex");
36698
- const otelSpanId = entry.otel_span_id || (0, import_node_crypto3.randomBytes)(8).toString("hex");
36801
+ const otelTraceId = entry.otel_trace_id || (0, import_node_crypto4.randomBytes)(16).toString("hex");
36802
+ const otelSpanId = entry.otel_span_id || (0, import_node_crypto4.randomBytes)(8).toString("hex");
36699
36803
  const log = {
36700
36804
  v: 2,
36701
36805
  tool: entry.tool || "unknown",
@@ -36703,12 +36807,13 @@ var ProtectGateway = class {
36703
36807
  reason_code: entry.reason_code || "default_allow",
36704
36808
  policy_digest: this.config.policyDigest,
36705
36809
  policy_engine: this.config.policy?.policy_engine || "built-in",
36706
- request_id: entry.request_id || (0, import_node_crypto3.randomUUID)().slice(0, 12),
36810
+ request_id: entry.request_id || (0, import_node_crypto4.randomUUID)().slice(0, 12),
36707
36811
  timestamp: Date.now(),
36708
36812
  mode,
36709
36813
  ...entry.rate_limit_remaining !== void 0 && { rate_limit_remaining: entry.rate_limit_remaining },
36710
36814
  ...entry.tier && { tier: entry.tier },
36711
36815
  ...entry.credential_ref && { credential_ref: entry.credential_ref },
36816
+ ...entry.action_readback && { action_readback: entry.action_readback },
36712
36817
  otel_trace_id: otelTraceId,
36713
36818
  otel_span_id: otelSpanId
36714
36819
  };
@@ -39058,6 +39163,50 @@ function collectPath(leaves, index, out) {
39058
39163
  out.push(merkleRoot(leaves.slice(0, k)));
39059
39164
  }
39060
39165
  }
39166
+ function verifyProof(expectedRootHex, leafHash, proof) {
39167
+ if (proof.index < 0 || proof.index >= proof.treeSize) return false;
39168
+ if (proof.treeSize === 1) {
39169
+ return proof.siblings.length === 0 && bytesToHex(leafHash).toLowerCase() === expectedRootHex.toLowerCase();
39170
+ }
39171
+ let result;
39172
+ try {
39173
+ result = reconstructRoot(
39174
+ leafHash,
39175
+ proof.index,
39176
+ proof.treeSize,
39177
+ proof.siblings
39178
+ );
39179
+ } catch {
39180
+ return false;
39181
+ }
39182
+ return bytesToHex(result).toLowerCase() === expectedRootHex.toLowerCase();
39183
+ }
39184
+ function reconstructRoot(leafHash, index, treeSize, siblings) {
39185
+ if (treeSize === 1) {
39186
+ if (siblings.length !== 0) {
39187
+ throw new Error("reconstructRoot: extra siblings at single-leaf level");
39188
+ }
39189
+ return leafHash;
39190
+ }
39191
+ if (siblings.length === 0) {
39192
+ throw new Error("reconstructRoot: ran out of siblings before single-leaf");
39193
+ }
39194
+ const k = largestPowerOfTwoLessThan(treeSize);
39195
+ const outermostSibling = hexToBytes(siblings[siblings.length - 1]);
39196
+ const innerSiblings = siblings.slice(0, -1);
39197
+ if (index < k) {
39198
+ const leftHash = reconstructRoot(leafHash, index, k, innerSiblings);
39199
+ return hashInternal(leftHash, outermostSibling);
39200
+ } else {
39201
+ const rightHash = reconstructRoot(
39202
+ leafHash,
39203
+ index - k,
39204
+ treeSize - k,
39205
+ innerSiblings
39206
+ );
39207
+ return hashInternal(outermostSibling, rightHash);
39208
+ }
39209
+ }
39061
39210
  function largestPowerOfTwoLessThan(n) {
39062
39211
  if (n < 2) {
39063
39212
  throw new Error(`largestPowerOfTwoLessThan: n must be >= 2 (got ${n})`);
@@ -39085,6 +39234,17 @@ function base64urlNoPad(bytes) {
39085
39234
  const std = typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64") : btoa(String.fromCharCode(...bytes));
39086
39235
  return std.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
39087
39236
  }
39237
+ function base64urlDecode(s) {
39238
+ const std = s.replace(/-/g, "+").replace(/_/g, "/");
39239
+ const padded = std + "=".repeat((4 - std.length % 4) % 4);
39240
+ if (typeof Buffer !== "undefined") {
39241
+ return new Uint8Array(Buffer.from(padded, "base64"));
39242
+ }
39243
+ const bin = atob(padded);
39244
+ const out = new Uint8Array(bin.length);
39245
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
39246
+ return out;
39247
+ }
39088
39248
  function encodeLeaf(field) {
39089
39249
  const obj = {
39090
39250
  name: field.name,
@@ -39218,11 +39378,128 @@ function discloseField(receiptHash, fieldName, openings) {
39218
39378
  proof
39219
39379
  };
39220
39380
  }
39381
+ function createSelectiveDisclosurePackage(receipt, fieldNames, openings) {
39382
+ const receiptHash = receiptHashHex(receipt);
39383
+ const committedFieldsRoot = typeof receipt.committed_fields_root === "string" ? receipt.committed_fields_root : "";
39384
+ if (!committedFieldsRoot) {
39385
+ throw new Error("selective disclosure requires a committed receipt with committed_fields_root");
39386
+ }
39387
+ const committedFieldNames = committedFieldNamesFromReceipt(receipt, openings);
39388
+ const uniqueFields = Array.from(new Set(fieldNames));
39389
+ for (const fieldName of uniqueFields) {
39390
+ if (!committedFieldNames.includes(fieldName)) {
39391
+ throw new Error(`selective disclosure: field "${fieldName}" is not committed by this receipt`);
39392
+ }
39393
+ }
39394
+ const disclosures = uniqueFields.map((fieldName) => discloseField(receiptHash, fieldName, openings));
39395
+ const hiddenFields = committedFieldNames.filter((fieldName) => !uniqueFields.includes(fieldName));
39396
+ return {
39397
+ type: "scopeblind.selective_disclosure.v0",
39398
+ version: 0,
39399
+ parent_receipt_hash: receiptHash,
39400
+ committed_fields_root: committedFieldsRoot,
39401
+ disclosed_fields: uniqueFields,
39402
+ hidden_fields: hiddenFields,
39403
+ disclosures,
39404
+ verifier_explanation: {
39405
+ summary: "This package opens selected committed receipt fields and leaves the rest hidden.",
39406
+ disclosed: uniqueFields.length ? `Disclosed fields: ${uniqueFields.join(", ")}.` : "No fields were disclosed.",
39407
+ hidden: hiddenFields.length ? `Hidden committed fields: ${hiddenFields.join(", ")}. Their salted commitments remain bound to the signed receipt root.` : "No committed fields remain hidden.",
39408
+ limitation: "Selective Disclosure v0 uses salted SHA-256 commitments and Merkle proofs. It is not a full zero-knowledge proof."
39409
+ }
39410
+ };
39411
+ }
39412
+ function verifySelectiveDisclosurePackage(receipt, disclosure) {
39413
+ const errors = [];
39414
+ if (disclosure.type !== "scopeblind.selective_disclosure.v0") {
39415
+ errors.push("disclosure.type is not scopeblind.selective_disclosure.v0");
39416
+ }
39417
+ const actualReceiptHash = receiptHashHex(receipt);
39418
+ const receiptHashValid = disclosure.parent_receipt_hash === actualReceiptHash;
39419
+ if (!receiptHashValid) {
39420
+ errors.push("parent_receipt_hash does not match the supplied receipt");
39421
+ }
39422
+ const root = typeof receipt.committed_fields_root === "string" ? receipt.committed_fields_root : "";
39423
+ const commitmentRootValid = Boolean(root) && disclosure.committed_fields_root === root;
39424
+ if (!commitmentRootValid) {
39425
+ errors.push("committed_fields_root does not match the supplied receipt");
39426
+ }
39427
+ const signatureValid = verifyCommittedReceiptSignature(receipt);
39428
+ if (signatureValid === false) {
39429
+ errors.push("receipt signature failed verification");
39430
+ }
39431
+ const committedFieldNames = committedFieldNamesFromReceipt(receipt, {});
39432
+ const disclosed = /* @__PURE__ */ new Set();
39433
+ for (const item of disclosure.disclosures || []) {
39434
+ if (item.parent_receipt_hash !== disclosure.parent_receipt_hash) {
39435
+ errors.push(`disclosure for "${item.name}" targets a different receipt hash`);
39436
+ continue;
39437
+ }
39438
+ if (!committedFieldNames.includes(item.name)) {
39439
+ errors.push(`field "${item.name}" is not listed in committed_field_names`);
39440
+ continue;
39441
+ }
39442
+ const leafBytes = encodeLeaf({
39443
+ name: item.name,
39444
+ salt: base64urlDecode(item.salt),
39445
+ value: item.value
39446
+ });
39447
+ const ok = root ? verifyProof(root, hashLeaf(leafBytes), item.proof) : false;
39448
+ if (!ok) {
39449
+ errors.push(`field "${item.name}" failed Merkle inclusion verification`);
39450
+ } else {
39451
+ disclosed.add(item.name);
39452
+ }
39453
+ }
39454
+ const disclosedFields = Array.from(disclosed);
39455
+ const hiddenFields = committedFieldNames.filter((fieldName) => !disclosed.has(fieldName));
39456
+ const valid = errors.length === 0 && receiptHashValid && commitmentRootValid && signatureValid !== false;
39457
+ const explanation = [
39458
+ valid ? "Selective disclosure verified: the disclosed fields open to the signed receipt commitment root." : "Selective disclosure failed verification.",
39459
+ signatureValid === true ? "Receipt signature verified against the embedded Ed25519 public key." : signatureValid === null ? "Receipt signature was not checked because the committed receipt did not carry an embedded Ed25519 signature object." : "Receipt signature did not verify.",
39460
+ disclosedFields.length ? `Disclosed fields: ${disclosedFields.join(", ")}.` : "No fields were disclosed.",
39461
+ hiddenFields.length ? `Hidden fields: ${hiddenFields.join(", ")}. These remain private but bound to the same commitment root.` : "No committed fields remain hidden.",
39462
+ "Limitation: this is salted commitment disclosure, not full zero-knowledge."
39463
+ ];
39464
+ return {
39465
+ valid,
39466
+ receipt_hash_valid: receiptHashValid,
39467
+ signature_valid: signatureValid,
39468
+ commitment_root_valid: commitmentRootValid,
39469
+ disclosed_fields: disclosedFields,
39470
+ hidden_fields: hiddenFields,
39471
+ errors,
39472
+ explanation
39473
+ };
39474
+ }
39475
+ function committedFieldNamesFromReceipt(receipt, openings) {
39476
+ const fromReceipt = Array.isArray(receipt.committed_field_names) ? receipt.committed_field_names.filter((fieldName) => typeof fieldName === "string") : [];
39477
+ const names = fromReceipt.length ? fromReceipt : Object.keys(openings);
39478
+ return Array.from(new Set(names)).sort();
39479
+ }
39480
+ function receiptHashHex(receipt) {
39481
+ return bytesToHex(sha2562(new TextEncoder().encode(jcs(receipt))));
39482
+ }
39483
+ function verifyCommittedReceiptSignature(receipt) {
39484
+ const signature = receipt.signature;
39485
+ if (!signature || typeof signature !== "object") return null;
39486
+ const sig = signature;
39487
+ if (sig.alg !== "EdDSA" || typeof sig.sig !== "string" || typeof sig.public_key !== "string") {
39488
+ return null;
39489
+ }
39490
+ const { signature: _signature, ...payloadWithoutSig } = receipt;
39491
+ const messageHash = sha2562(new TextEncoder().encode(jcs(payloadWithoutSig)));
39492
+ try {
39493
+ return ed25519.verify(base64urlDecode(sig.sig), messageHash, hexToBytes(sig.public_key));
39494
+ } catch {
39495
+ return false;
39496
+ }
39497
+ }
39221
39498
 
39222
39499
  // src/bundle.ts
39223
39500
  function createAuditBundle(opts) {
39224
39501
  const receipts = opts.receipts.filter(
39225
- (r) => r && typeof r === "object" && typeof r.signature === "string"
39502
+ (r) => r && typeof r === "object" && (typeof r.signature === "string" || r.signature !== null && typeof r.signature === "object")
39226
39503
  );
39227
39504
  if (receipts.length === 0) {
39228
39505
  throw new Error("Audit bundle requires at least one signed receipt");
@@ -39251,10 +39528,18 @@ function createAuditBundle(opts) {
39251
39528
  time_range: timeRange,
39252
39529
  receipts,
39253
39530
  anchors: opts.anchors || [],
39531
+ selective_disclosures: opts.selectiveDisclosures || [],
39532
+ privacy: {
39533
+ selective_disclosure: {
39534
+ supported: true,
39535
+ model: "salted_commitments_merkle_v0",
39536
+ statement: "Committed receipts may disclose selected fields with salted Merkle openings. Undisclosed committed fields remain hidden while staying bound to the signed commitment root."
39537
+ }
39538
+ },
39254
39539
  verification: {
39255
39540
  algorithm: "ed25519",
39256
39541
  signing_keys: Array.from(keyMap.values()),
39257
- instructions: `Verify each receipt by: (1) remove the "signature" field, (2) canonicalize the remaining object with JCS (sorted keys at every level), (3) encode as UTF-8 bytes, (4) verify the Ed25519 signature using the signing key matching the receipt's "kid" field. CLI: npx @veritasacta/verify bundle.json --bundle`
39542
+ instructions: `Verify each receipt by: (1) remove the "signature" field, (2) canonicalize the remaining object with JCS (sorted keys at every level), (3) encode as UTF-8 bytes, (4) verify the Ed25519 signature using the signing key matching the receipt's "kid" field. For scopeblind.selective_disclosure.v0 packages, recompute each disclosed leaf and verify it against the receipt committed_fields_root; fields not disclosed remain hidden. CLI: npx @veritasacta/verify bundle.json --bundle`
39258
39543
  }
39259
39544
  };
39260
39545
  }
@@ -39717,7 +40002,7 @@ function validateEvidenceReceipt(receipt) {
39717
40002
 
39718
40003
  // src/hook-server.ts
39719
40004
  var import_node_http2 = require("http");
39720
- var import_node_crypto4 = require("crypto");
40005
+ var import_node_crypto5 = require("crypto");
39721
40006
  var import_node_fs9 = require("fs");
39722
40007
  var import_node_path5 = require("path");
39723
40008
 
@@ -39900,7 +40185,7 @@ function computePayloadDigest(input) {
39900
40185
  return void 0;
39901
40186
  }
39902
40187
  return {
39903
- input_hash: (0, import_node_crypto4.createHash)("sha256").update(content).digest("hex"),
40188
+ input_hash: (0, import_node_crypto5.createHash)("sha256").update(content).digest("hex"),
39904
40189
  input_size: size,
39905
40190
  truncated: true,
39906
40191
  preview: content.slice(0, 256)
@@ -39913,7 +40198,7 @@ function computeOutputDigest(output) {
39913
40198
  return void 0;
39914
40199
  }
39915
40200
  return {
39916
- output_hash: (0, import_node_crypto4.createHash)("sha256").update(content).digest("hex"),
40201
+ output_hash: (0, import_node_crypto5.createHash)("sha256").update(content).digest("hex"),
39917
40202
  output_size: size
39918
40203
  };
39919
40204
  }
@@ -39936,13 +40221,14 @@ function detectSandboxState() {
39936
40221
  async function handlePreToolUse(input, state) {
39937
40222
  const hookStart = Date.now();
39938
40223
  const toolName = input.toolName || "unknown";
39939
- const requestId = input.toolUseId || (0, import_node_crypto4.randomUUID)().slice(0, 12);
40224
+ const requestId = input.toolUseId || (0, import_node_crypto5.randomUUID)().slice(0, 12);
39940
40225
  state.inflightTools.set(requestId, {
39941
40226
  tool: toolName,
39942
40227
  startedAt: hookStart,
39943
40228
  requestId
39944
40229
  });
39945
40230
  const payloadDigest = computePayloadDigest(input.toolInput);
40231
+ const actionReadback = buildActionReadback(toolName, input.toolInput || {});
39946
40232
  const swarm = {
39947
40233
  ...state.swarmContext,
39948
40234
  ...input.agentId && { agent_id: input.agentId },
@@ -39982,6 +40268,7 @@ async function handlePreToolUse(input, state) {
39982
40268
  swarm: swarm.team_name ? swarm : void 0,
39983
40269
  timing: { hook_latency_ms: hookLatency2, started_at: hookStart },
39984
40270
  payload_digest: payloadDigest,
40271
+ action_readback: actionReadback,
39985
40272
  deny_iteration: denyCount,
39986
40273
  sandbox_state: detectSandboxState(),
39987
40274
  plan_receipt_id: state.activePlanReceiptId || void 0
@@ -40016,6 +40303,7 @@ async function handlePreToolUse(input, state) {
40016
40303
  swarm: swarm.team_name ? swarm : void 0,
40017
40304
  timing: { hook_latency_ms: hookLatency2, started_at: hookStart },
40018
40305
  payload_digest: payloadDigest,
40306
+ action_readback: actionReadback,
40019
40307
  sandbox_state: detectSandboxState()
40020
40308
  });
40021
40309
  return {
@@ -40040,6 +40328,7 @@ async function handlePreToolUse(input, state) {
40040
40328
  swarm: swarm.team_name ? swarm : void 0,
40041
40329
  timing: { hook_latency_ms: hookLatency2, started_at: hookStart },
40042
40330
  payload_digest: payloadDigest,
40331
+ action_readback: actionReadback,
40043
40332
  sandbox_state: detectSandboxState()
40044
40333
  });
40045
40334
  return {
@@ -40060,13 +40349,15 @@ async function handlePreToolUse(input, state) {
40060
40349
  hook_event: "PreToolUse",
40061
40350
  swarm: swarm.team_name ? swarm : void 0,
40062
40351
  timing: { hook_latency_ms: hookLatency2, started_at: hookStart },
40352
+ payload_digest: payloadDigest,
40353
+ action_readback: actionReadback,
40063
40354
  sandbox_state: detectSandboxState()
40064
40355
  });
40065
40356
  return {
40066
40357
  hookSpecificOutput: {
40067
40358
  hookEventName: "PreToolUse",
40068
40359
  permissionDecision: "ask",
40069
- permissionDecisionReason: `[ScopeBlind] "${toolName}" requires human approval. Policy: ${state.policyDigest}`
40360
+ permissionDecisionReason: `[ScopeBlind] Approval required for exactly this action: ${actionReadback.summary}. Payload hash: ${actionReadback.payload_hash.slice(0, 16)}\u2026 Policy: ${state.policyDigest}`
40070
40361
  }
40071
40362
  };
40072
40363
  }
@@ -40111,6 +40402,7 @@ async function handlePreToolUse(input, state) {
40111
40402
  swarm: swarm.team_name ? swarm : void 0,
40112
40403
  timing: { hook_latency_ms: hookLatency, started_at: hookStart },
40113
40404
  payload_digest: payloadDigest,
40405
+ action_readback: actionReadback,
40114
40406
  sandbox_state: detectSandboxState(),
40115
40407
  plan_receipt_id: state.activePlanReceiptId || void 0
40116
40408
  });
@@ -40127,7 +40419,7 @@ async function handlePreToolUse(input, state) {
40127
40419
  }
40128
40420
  async function handlePostToolUse(input, state) {
40129
40421
  const toolName = input.toolName || "unknown";
40130
- const requestId = input.toolUseId || (0, import_node_crypto4.randomUUID)().slice(0, 12);
40422
+ const requestId = input.toolUseId || (0, import_node_crypto5.randomUUID)().slice(0, 12);
40131
40423
  const now = Date.now();
40132
40424
  const inflight = state.inflightTools.get(requestId);
40133
40425
  const timing = {
@@ -40139,7 +40431,7 @@ async function handlePostToolUse(input, state) {
40139
40431
  state.inflightTools.delete(requestId);
40140
40432
  }
40141
40433
  const outputDigest = computeOutputDigest(input.toolResult);
40142
- const receiptId = (0, import_node_crypto4.randomUUID)().slice(0, 8);
40434
+ const receiptId = (0, import_node_crypto5.randomUUID)().slice(0, 8);
40143
40435
  const policyName = state.cedarPolicies ? `cedar:${state.policyDigest}` : state.policyDigest;
40144
40436
  const additionalContext = `[ScopeBlind] Tool call receipted. Policy: ${policyName}. Decision: allow. Receipt: #${receiptId}.` + (timing.tool_duration_ms !== void 0 ? ` Duration: ${timing.tool_duration_ms}ms.` : "") + (timing.hook_latency_ms !== void 0 ? ` Overhead: ${timing.hook_latency_ms}ms.` : "");
40145
40437
  emitDecisionLog(state, {
@@ -40171,7 +40463,7 @@ function handleSubagentStart(input, state) {
40171
40463
  tool: `subagent:${agentId}`,
40172
40464
  decision: "allow",
40173
40465
  reason_code: "subagent_started",
40174
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40466
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40175
40467
  hook_event: "SubagentStart",
40176
40468
  swarm: {
40177
40469
  ...state.swarmContext,
@@ -40192,7 +40484,7 @@ function handleSubagentStop(input, state) {
40192
40484
  tool: `subagent:${agentId}`,
40193
40485
  decision: "allow",
40194
40486
  reason_code: "subagent_stopped",
40195
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40487
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40196
40488
  hook_event: "SubagentStop",
40197
40489
  swarm: {
40198
40490
  ...state.swarmContext,
@@ -40207,7 +40499,7 @@ function handleTaskCreated(input, state) {
40207
40499
  tool: `task:${input.taskId || "unknown"}`,
40208
40500
  decision: "allow",
40209
40501
  reason_code: "task_created",
40210
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40502
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40211
40503
  hook_event: "TaskCreated",
40212
40504
  swarm: {
40213
40505
  ...state.swarmContext,
@@ -40221,7 +40513,7 @@ function handleTaskCompleted(input, state) {
40221
40513
  tool: `task:${input.taskId || "unknown"}`,
40222
40514
  decision: "allow",
40223
40515
  reason_code: "task_completed",
40224
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40516
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40225
40517
  hook_event: "TaskCompleted",
40226
40518
  swarm: state.swarmContext
40227
40519
  });
@@ -40232,7 +40524,7 @@ function handleSessionStart(input, state) {
40232
40524
  tool: "session",
40233
40525
  decision: "allow",
40234
40526
  reason_code: "session_started",
40235
- request_id: input.sessionId || (0, import_node_crypto4.randomUUID)().slice(0, 12),
40527
+ request_id: input.sessionId || (0, import_node_crypto5.randomUUID)().slice(0, 12),
40236
40528
  hook_event: "SessionStart",
40237
40529
  swarm: state.swarmContext,
40238
40530
  sandbox_state: detectSandboxState()
@@ -40255,7 +40547,7 @@ function handleSessionEnd(input, state) {
40255
40547
  tool: "session",
40256
40548
  decision: "allow",
40257
40549
  reason_code: "session_ended",
40258
- request_id: input.sessionId || (0, import_node_crypto4.randomUUID)().slice(0, 12),
40550
+ request_id: input.sessionId || (0, import_node_crypto5.randomUUID)().slice(0, 12),
40259
40551
  hook_event: "SessionEnd",
40260
40552
  swarm: state.swarmContext
40261
40553
  });
@@ -40266,7 +40558,7 @@ function handleTeammateIdle(input, state) {
40266
40558
  tool: `teammate:${input.agentId || "unknown"}`,
40267
40559
  decision: "allow",
40268
40560
  reason_code: "teammate_idle",
40269
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40561
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40270
40562
  hook_event: "TeammateIdle",
40271
40563
  swarm: {
40272
40564
  ...state.swarmContext,
@@ -40294,7 +40586,7 @@ function handleConfigChange(input, state) {
40294
40586
  tool: "config",
40295
40587
  decision: "deny",
40296
40588
  reason_code: "config_tamper_detected",
40297
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40589
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40298
40590
  hook_event: "ConfigChange",
40299
40591
  swarm: state.swarmContext
40300
40592
  });
@@ -40303,7 +40595,7 @@ function handleConfigChange(input, state) {
40303
40595
  tool: "config",
40304
40596
  decision: "allow",
40305
40597
  reason_code: "config_changed",
40306
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40598
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40307
40599
  hook_event: "ConfigChange"
40308
40600
  });
40309
40601
  }
@@ -40325,7 +40617,7 @@ function handleStop(input, state) {
40325
40617
  tool: "session",
40326
40618
  decision: "allow",
40327
40619
  reason_code: "agent_stopped",
40328
- request_id: (0, import_node_crypto4.randomUUID)().slice(0, 12),
40620
+ request_id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
40329
40621
  hook_event: "Stop",
40330
40622
  swarm: state.swarmContext
40331
40623
  });
@@ -40333,8 +40625,8 @@ function handleStop(input, state) {
40333
40625
  }
40334
40626
  function emitDecisionLog(state, entry) {
40335
40627
  const mode = state.enforce ? "enforce" : "shadow";
40336
- const otelTraceId = (0, import_node_crypto4.randomBytes)(16).toString("hex");
40337
- const otelSpanId = (0, import_node_crypto4.randomBytes)(8).toString("hex");
40628
+ const otelTraceId = (0, import_node_crypto5.randomBytes)(16).toString("hex");
40629
+ const otelSpanId = (0, import_node_crypto5.randomBytes)(8).toString("hex");
40338
40630
  const log = {
40339
40631
  v: 2,
40340
40632
  tool: entry.tool || "unknown",
@@ -40342,7 +40634,7 @@ function emitDecisionLog(state, entry) {
40342
40634
  reason_code: entry.reason_code || "default_allow",
40343
40635
  policy_digest: state.policyDigest,
40344
40636
  policy_engine: state.cedarPolicies ? "cedar" : "built-in",
40345
- request_id: entry.request_id || (0, import_node_crypto4.randomUUID)().slice(0, 12),
40637
+ request_id: entry.request_id || (0, import_node_crypto5.randomUUID)().slice(0, 12),
40346
40638
  timestamp: Date.now(),
40347
40639
  mode,
40348
40640
  otel_trace_id: otelTraceId,
@@ -41321,8 +41613,515 @@ function generateSchemaStub(namespace = "ScopeBlind") {
41321
41613
  ].join("\n");
41322
41614
  }
41323
41615
 
41616
+ // src/policy-packs.ts
41617
+ var header = (id, description) => `// ScopeBlind protect-mcp policy pack: ${id}
41618
+ // ${description}
41619
+ // Start in shadow mode, review receipts, then run with --enforce.
41620
+
41621
+ `;
41622
+ var defaultPermit = `
41623
+ // Default posture: allow non-matching calls so teams can start in shadow mode.
41624
+ // Tighten this after reviewing your local action dashboard.
41625
+ permit(principal, action == Action::"MCP::Tool::call", resource);
41626
+ `;
41627
+ var filesystemSafe = `${header("filesystem-safe", "Block common destructive filesystem and secret-file access patterns.")}// Destructive file tools are never safe as an unattended default.
41628
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"delete_file");
41629
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"remove_file");
41630
+
41631
+ // Secret-like reads by path.
41632
+ forbid(principal, action == Action::"MCP::Tool::call", resource) when {
41633
+ context has "input" && context.input has "path" && (
41634
+ context.input.path like "*/.env*" ||
41635
+ context.input.path like "*/id_rsa*" ||
41636
+ context.input.path like "*/.ssh/*" ||
41637
+ context.input.path like "*secret*" ||
41638
+ context.input.path like "*credential*"
41639
+ )
41640
+ };
41641
+
41642
+ // Dangerous shell operations that mutate or destroy local state.
41643
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
41644
+ context has "command" && (
41645
+ context.command like "*rm -rf*" ||
41646
+ context.command like "*mkfs*" ||
41647
+ context.command like "*dd if=*" ||
41648
+ context.command like "*chmod -R 777*" ||
41649
+ context.command like "*chown -R*"
41650
+ )
41651
+ };
41652
+ ${defaultPermit}`;
41653
+ var gitSafe = `${header("git-safe", "Prevent unattended history rewrites, force pushes, and destructive repo cleanup.")}forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
41654
+ context has "command" && (
41655
+ context.command like "*git push --force*" ||
41656
+ context.command like "*git push -f*" ||
41657
+ context.command like "*git reset --hard*" ||
41658
+ context.command like "*git clean -fd*" ||
41659
+ context.command like "*git checkout --*" ||
41660
+ context.command like "*git branch -D*" ||
41661
+ context.command like "*gh repo delete*"
41662
+ )
41663
+ };
41664
+ ${defaultPermit}`;
41665
+ var emailSafe = `${header("email-safe", "Permit drafting but block unattended external sends.")}forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"mail.send");
41666
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"email.send");
41667
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"send_email");
41668
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"gmail.send");
41669
+
41670
+ // Shell fallbacks that send mail are blocked too.
41671
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
41672
+ context has "command" && (
41673
+ context.command like "*sendmail*" ||
41674
+ context.command like "*mailx*" ||
41675
+ context.command like "*smtp*"
41676
+ )
41677
+ };
41678
+ ${defaultPermit}`;
41679
+ var databaseSafe = `${header("database-safe", "Allow reads, block write/admin SQL unless explicitly approved elsewhere.")}forbid(principal, action == Action::"MCP::Tool::call", resource) when {
41680
+ context has "input" && context.input has "query" && (
41681
+ context.input.query like "*DROP *" ||
41682
+ context.input.query like "*TRUNCATE *" ||
41683
+ context.input.query like "*DELETE *" ||
41684
+ context.input.query like "*UPDATE *" ||
41685
+ context.input.query like "*INSERT *" ||
41686
+ context.input.query like "*ALTER *" ||
41687
+ context.input.query like "*GRANT *" ||
41688
+ context.input.query like "*REVOKE *"
41689
+ )
41690
+ };
41691
+ ${defaultPermit}`;
41692
+ var cloudSpendSafe = `${header("cloud-spend-safe", "Block cloud actions that can create spend or destroy infrastructure.")}forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
41693
+ context has "command" && (
41694
+ context.command like "*terraform destroy*" ||
41695
+ context.command like "*terraform apply*" ||
41696
+ context.command like "*pulumi up*" ||
41697
+ context.command like "*pulumi destroy*" ||
41698
+ context.command like "*aws ec2 run-instances*" ||
41699
+ context.command like "*aws rds create*" ||
41700
+ context.command like "*gcloud compute instances create*" ||
41701
+ context.command like "*az vm create*" ||
41702
+ context.command like "*kubectl delete*"
41703
+ )
41704
+ };
41705
+ ${defaultPermit}`;
41706
+ var secretsSafe = `${header("secrets-safe", "Block secret exfiltration from files, env, shell, and common credential tools.")}forbid(principal, action == Action::"MCP::Tool::call", resource) when {
41707
+ context has "input" && context.input has "path" && (
41708
+ context.input.path like "*/.env*" ||
41709
+ context.input.path like "*/.aws/credentials*" ||
41710
+ context.input.path like "*/.npmrc*" ||
41711
+ context.input.path like "*/.netrc*" ||
41712
+ context.input.path like "*/id_rsa*" ||
41713
+ context.input.path like "*secret*" ||
41714
+ context.input.path like "*token*"
41715
+ )
41716
+ };
41717
+
41718
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash") when {
41719
+ context has "command" && (
41720
+ context.command like "*printenv*" ||
41721
+ context.command like "*env |*" ||
41722
+ context.command like "*security find-generic-password*" ||
41723
+ context.command like "*aws secretsmanager get-secret-value*" ||
41724
+ context.command like "*gcloud secrets versions access*" ||
41725
+ context.command like "*op read*"
41726
+ )
41727
+ };
41728
+ ${defaultPermit}`;
41729
+ var financeMandateSafe = `${header("finance-mandate-safe", "Block restricted-list and concentration-limit breaches in booking tools.")}forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"pms.book") when {
41730
+ context has "input" && context.input has "on_restricted_list" && context.input.on_restricted_list == true
41731
+ };
41732
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"booking.execute") when {
41733
+ context has "input" && context.input has "on_restricted_list" && context.input.on_restricted_list == true
41734
+ };
41735
+ forbid(principal, action == Action::"MCP::Tool::call", resource == Tool::"booking.ticket") when {
41736
+ context has "input" && context.input has "on_restricted_list" && context.input.on_restricted_list == true
41737
+ };
41738
+
41739
+ // Default example caps: single-name > 10%, gross > 200%, net > 100%.
41740
+ forbid(principal, action == Action::"MCP::Tool::call", resource) when {
41741
+ context has "input" && context.input has "post_trade_weight_bps" && context.input.post_trade_weight_bps > 1000
41742
+ };
41743
+ forbid(principal, action == Action::"MCP::Tool::call", resource) when {
41744
+ context has "input" && context.input has "post_trade_gross_exposure_bps" && context.input.post_trade_gross_exposure_bps > 20000
41745
+ };
41746
+ forbid(principal, action == Action::"MCP::Tool::call", resource) when {
41747
+ context has "input" && context.input has "post_trade_net_exposure_bps" && context.input.post_trade_net_exposure_bps > 10000
41748
+ };
41749
+ ${defaultPermit}`;
41750
+ var POLICY_PACKS = [
41751
+ {
41752
+ id: "filesystem-safe",
41753
+ name: "Filesystem Safe",
41754
+ description: "Blocks destructive filesystem calls and secret-like path reads.",
41755
+ recommendedMode: "shadow-first",
41756
+ files: [{ path: "filesystem-safe.cedar", contents: filesystemSafe }]
41757
+ },
41758
+ {
41759
+ id: "git-safe",
41760
+ name: "Git Safe",
41761
+ description: "Blocks force pushes, hard resets, destructive cleanup, and repo deletion.",
41762
+ recommendedMode: "shadow-first",
41763
+ files: [{ path: "git-safe.cedar", contents: gitSafe }]
41764
+ },
41765
+ {
41766
+ id: "email-safe",
41767
+ name: "Email Safe",
41768
+ description: "Allows drafting workflows while blocking unattended sends.",
41769
+ recommendedMode: "shadow-first",
41770
+ files: [{ path: "email-safe.cedar", contents: emailSafe }]
41771
+ },
41772
+ {
41773
+ id: "database-safe",
41774
+ name: "Database Safe",
41775
+ description: "Allows read-oriented DB tools while blocking mutating/admin SQL.",
41776
+ recommendedMode: "shadow-first",
41777
+ files: [{ path: "database-safe.cedar", contents: databaseSafe }]
41778
+ },
41779
+ {
41780
+ id: "cloud-spend-safe",
41781
+ name: "Cloud Spend Safe",
41782
+ description: "Blocks obvious cloud spend creation and infrastructure destruction.",
41783
+ recommendedMode: "shadow-first",
41784
+ files: [{ path: "cloud-spend-safe.cedar", contents: cloudSpendSafe }]
41785
+ },
41786
+ {
41787
+ id: "secrets-safe",
41788
+ name: "Secrets Safe",
41789
+ description: "Blocks common file, env, shell, and cloud secret exfiltration paths.",
41790
+ recommendedMode: "enforce-ready",
41791
+ files: [{ path: "secrets-safe.cedar", contents: secretsSafe }]
41792
+ },
41793
+ {
41794
+ id: "finance-mandate-safe",
41795
+ name: "Finance Mandate Safe",
41796
+ description: "Blocks restricted-list and concentration breaches in booking flows.",
41797
+ recommendedMode: "shadow-first",
41798
+ files: [{ path: "finance-mandate-safe.cedar", contents: financeMandateSafe }]
41799
+ }
41800
+ ];
41801
+ function getPolicyPack(id) {
41802
+ return POLICY_PACKS.find((pack) => pack.id === id);
41803
+ }
41804
+ function policyPackIds() {
41805
+ return POLICY_PACKS.map((pack) => pack.id);
41806
+ }
41807
+
41808
+ // src/connector-pilots.ts
41809
+ var import_node_fs10 = require("fs");
41810
+ var import_node_path6 = require("path");
41811
+ var defaultPermit2 = `
41812
+ // Default posture: observe all non-matching tools so the connector can be piloted in shadow mode.
41813
+ permit(principal, action == Action::"MCP::Tool::call", resource);
41814
+ `;
41815
+ var CONNECTOR_PILOTS = [
41816
+ {
41817
+ id: "github",
41818
+ category: "code",
41819
+ name: "GitHub pull-request control",
41820
+ status: "usable-pilot",
41821
+ description: "Controls GitHub REST/MCP calls for issue, PR, branch, and workflow actions.",
41822
+ value: "Useful when agents already have repo access through GitHub MCP, gh, or a GitHub-backed tool server.",
41823
+ env: [
41824
+ { name: "GITHUB_TOKEN", required: true, description: "Fine-grained token scoped to the pilot repo." },
41825
+ { name: "GITHUB_REPOSITORY", required: true, description: "owner/repo target for the pilot." }
41826
+ ],
41827
+ tools: ["github.rest.request", "github.issue.create", "github.pull_request.merge", "github.workflow.dispatch"],
41828
+ actions: [
41829
+ { name: "Read repo metadata", tool: "github.rest.request", risk: "low", mode: "observe", description: "GET-only repository and PR inspection." },
41830
+ { name: "Create issue or comment", tool: "github.issue.create", risk: "medium", mode: "require_approval", description: "External write to the system of record." },
41831
+ { name: "Merge PR / dispatch workflow", tool: "github.pull_request.merge", risk: "high", mode: "require_approval", description: "Code-changing or CI-triggering action." }
41832
+ ],
41833
+ setup: [
41834
+ "Create a fine-grained GitHub token for one repository.",
41835
+ "Set GITHUB_TOKEN and GITHUB_REPOSITORY.",
41836
+ "Run the agent through protect-mcp and review GitHub tool calls in the dashboard."
41837
+ ],
41838
+ config: {
41839
+ type: "scopeblind.connector_pilot.v1",
41840
+ provider: "github",
41841
+ target_env: ["GITHUB_TOKEN", "GITHUB_REPOSITORY"],
41842
+ safe_read_probe: "GET /repos/{GITHUB_REPOSITORY}",
41843
+ controlled_tools: ["github.rest.request", "github.issue.create", "github.pull_request.merge", "github.workflow.dispatch"],
41844
+ approval_required_for: ["POST", "PATCH", "PUT", "DELETE", "merge", "workflow_dispatch"],
41845
+ receipt_fields: ["method", "path", "repo", "actor", "payload_hash", "approval_reason"]
41846
+ },
41847
+ cedar: `${defaultPermit2}
41848
+ // GitHub pilot: reads are observed; writes and merges need exact-action approval.
41849
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41850
+ when { context.tool == "github.pull_request.merge" };
41851
+
41852
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41853
+ when { context.tool == "github.workflow.dispatch" && !context.approved };
41854
+
41855
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41856
+ when { context.tool == "github.issue.create" && !context.approved };
41857
+ `
41858
+ },
41859
+ {
41860
+ id: "email-gmail",
41861
+ category: "communications",
41862
+ name: "Gmail self-send / draft approval",
41863
+ status: "usable-pilot",
41864
+ description: "Uses the existing Gmail OAuth connector path and restricts send mode to email.self for the first production pilot.",
41865
+ value: "Makes external communications reviewable before an agent can send mail.",
41866
+ env: [
41867
+ { name: "GOOGLE_CLIENT_ID", required: true, description: "OAuth client for Gmail." },
41868
+ { name: "GOOGLE_CLIENT_SECRET", required: true, description: "OAuth client secret." },
41869
+ { name: "CONNECTOR_TOKEN_KEY", required: true, description: "AES-GCM key material for sealed connector tokens." }
41870
+ ],
41871
+ tools: ["gmail.draft.create", "gmail.send.email_self", "email.send"],
41872
+ actions: [
41873
+ { name: "Create draft", tool: "gmail.draft.create", risk: "medium", mode: "require_approval", description: "Draft content can leak sensitive information." },
41874
+ { name: "Self-send test", tool: "gmail.send.email_self", risk: "medium", mode: "require_approval", description: "First release allows only sending to the account owner." },
41875
+ { name: "External send", tool: "email.send", risk: "high", mode: "deny", description: "Direct external send stays blocked until a customer-specific allowlist exists." }
41876
+ ],
41877
+ setup: [
41878
+ "Configure Google OAuth redirect /fn/connectors/gmail/callback.",
41879
+ "Connect Gmail through the hosted console or local connector flow.",
41880
+ "Keep send mode to email.self until the customer approves recipient allowlists."
41881
+ ],
41882
+ config: {
41883
+ type: "scopeblind.connector_pilot.v1",
41884
+ provider: "gmail",
41885
+ hosted_functions: ["/fn/connectors/gmail/start", "/fn/connectors/gmail/callback", "/fn/connectors/gmail/send", "/fn/connectors/gmail/status"],
41886
+ first_release_scope: "email.self",
41887
+ denied_until_configured: ["email.send.external", "email.bulk_send"],
41888
+ receipt_fields: ["to_hash", "subject_hash", "body_hash", "approval_reason", "gmail_message_id"]
41889
+ },
41890
+ cedar: `${defaultPermit2}
41891
+ // Email pilot: no direct external send. Draft/self-send require exact approval.
41892
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41893
+ when { context.tool == "email.send" };
41894
+
41895
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41896
+ when { context.tool == "gmail.draft.create" && !context.approved };
41897
+
41898
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41899
+ when { context.tool == "gmail.send.email_self" && !context.approved };
41900
+ `
41901
+ },
41902
+ {
41903
+ id: "filesystem-git",
41904
+ category: "local-computer",
41905
+ name: "Filesystem and Git control",
41906
+ status: "usable-pilot",
41907
+ description: "Controls reads, writes, shell commands, and Git mutation in the local project.",
41908
+ value: "Immediately useful with Claude Code, Codex, Cursor, and any agent that edits files or runs shell commands.",
41909
+ env: [],
41910
+ tools: ["Read", "Write", "Edit", "MultiEdit", "Bash", "git.commit", "git.push"],
41911
+ actions: [
41912
+ { name: "Read files", tool: "Read", risk: "low", mode: "observe", description: "Observe file reads for audit context." },
41913
+ { name: "Write/edit files", tool: "Write", risk: "medium", mode: "require_approval", description: "Require approval for sensitive paths or broad rewrites." },
41914
+ { name: "Git push/reset", tool: "Bash", risk: "high", mode: "require_approval", description: "Commands that publish, reset, or delete require exact-action approval." }
41915
+ ],
41916
+ setup: [
41917
+ "Run protect-mcp init-hooks in the project.",
41918
+ "Install filesystem-safe and Git-safe policy packs.",
41919
+ "Review the dashboard before turning on enforce mode."
41920
+ ],
41921
+ config: {
41922
+ type: "scopeblind.connector_pilot.v1",
41923
+ provider: "filesystem-git",
41924
+ local_only: true,
41925
+ protected_paths: [".env", ".ssh", "keys/", "secrets/", "node_modules/"],
41926
+ dangerous_command_patterns: ["rm -rf", "git push", "git reset --hard", "curl | sh", "chmod 777"],
41927
+ receipt_fields: ["tool", "path_hash", "command_hash", "diff_hash", "approval_reason"]
41928
+ },
41929
+ cedar: `${defaultPermit2}
41930
+ // Filesystem/Git pilot: dangerous shell and protected-path writes need approval.
41931
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41932
+ when { context.tool == "Bash" && context.command_pattern.contains("git reset --hard") && !context.approved };
41933
+
41934
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41935
+ when { context.tool == "Bash" && context.command_pattern.contains("git push") && !context.approved };
41936
+
41937
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41938
+ when { ["Write", "Edit", "MultiEdit"].contains(context.tool) && context.path.contains(".env") && !context.approved };
41939
+ `
41940
+ },
41941
+ {
41942
+ id: "slack-teams",
41943
+ category: "communications",
41944
+ name: "Slack or Teams outbound approval",
41945
+ status: "usable-pilot",
41946
+ description: "Controls messages to Slack channels or Microsoft Teams webhooks.",
41947
+ value: "Makes high-impact internal broadcasts and client channels approval-gated.",
41948
+ env: [
41949
+ { name: "SLACK_BOT_TOKEN", required: false, description: "Slack bot token for chat.postMessage pilots." },
41950
+ { name: "SLACK_CHANNEL_ID", required: false, description: "Default Slack channel for the pilot." },
41951
+ { name: "TEAMS_WEBHOOK_URL", required: false, description: "Teams incoming webhook URL if Teams is preferred." }
41952
+ ],
41953
+ tools: ["slack.chat.postMessage", "slack.files.upload", "teams.webhook.post"],
41954
+ actions: [
41955
+ { name: "Post internal message", tool: "slack.chat.postMessage", risk: "medium", mode: "require_approval", description: "Message text and channel are read back before send." },
41956
+ { name: "Upload file", tool: "slack.files.upload", risk: "high", mode: "require_approval", description: "Files can leak customer data and need explicit approval." },
41957
+ { name: "Teams webhook post", tool: "teams.webhook.post", risk: "medium", mode: "require_approval", description: "Webhook destination and payload hash are receipted." }
41958
+ ],
41959
+ setup: [
41960
+ "Choose Slack or Teams for the first pilot, not both.",
41961
+ "Set the relevant token/webhook environment variables.",
41962
+ "Start with a private test channel and exact-action approval for every send."
41963
+ ],
41964
+ config: {
41965
+ type: "scopeblind.connector_pilot.v1",
41966
+ provider: "slack-or-teams",
41967
+ supported_modes: ["slack.chat.postMessage", "teams.webhook.post"],
41968
+ require_channel_allowlist: true,
41969
+ receipt_fields: ["channel_hash", "message_hash", "file_hash", "approval_reason", "provider_message_id"]
41970
+ },
41971
+ cedar: `${defaultPermit2}
41972
+ // Slack/Teams pilot: all outbound posts and uploads require approval by default.
41973
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
41974
+ when { ["slack.chat.postMessage", "slack.files.upload", "teams.webhook.post"].contains(context.tool) && !context.approved };
41975
+ `
41976
+ },
41977
+ {
41978
+ id: "finance-pms",
41979
+ category: "finance",
41980
+ name: "Finance PMS mock-to-real adapter",
41981
+ status: "usable-pilot",
41982
+ description: "Stages orders into a PMS adapter contract, with mock mode locally and real mode through PMS_ADAPTER_URL.",
41983
+ value: "Gives hedge funds the controlled booking path: parse, mandate-check, approve, book, corroborate, receipt.",
41984
+ env: [
41985
+ { name: "PMS_ADAPTER_URL", required: false, description: "Customer-owned adapter endpoint. Omit for local mock mode." },
41986
+ { name: "PMS_ADAPTER_TOKEN", required: false, description: "Bearer token for the customer-owned PMS adapter." }
41987
+ ],
41988
+ tools: ["pms.order.stage", "pms.order.book", "pms.order.cancel", "pms.reconcile"],
41989
+ actions: [
41990
+ { name: "Stage order", tool: "pms.order.stage", risk: "medium", mode: "require_approval", description: "Creates a booking ticket but does not execute." },
41991
+ { name: "Book order", tool: "pms.order.book", risk: "high", mode: "require_approval", description: "Must pass mandate checks and human readback." },
41992
+ { name: "Cancel/order correction", tool: "pms.order.cancel", risk: "high", mode: "require_approval", description: "Mutates book state and requires approval." }
41993
+ ],
41994
+ setup: [
41995
+ "Run local mock mode first with the Legate finance pilot pack.",
41996
+ "Point PMS_ADAPTER_URL at a customer-owned bridge when ready.",
41997
+ "Require mandate checks and exact-action approval before pms.order.book."
41998
+ ],
41999
+ config: {
42000
+ type: "scopeblind.connector_pilot.v1",
42001
+ provider: "finance-pms",
42002
+ mode: "mock-first",
42003
+ adapter_contract: {
42004
+ stage: "POST /orders/stage",
42005
+ book: "POST /orders/book",
42006
+ cancel: "POST /orders/{client_order_id}/cancel",
42007
+ reconcile: "GET /orders/{client_order_id}"
42008
+ },
42009
+ receipt_fields: ["client_order_id", "side", "symbol_hash", "qty", "price", "mandate_digest", "approval_reason", "external_confirmation_hash"]
42010
+ },
42011
+ cedar: `${defaultPermit2}
42012
+ // Finance/PMS pilot: booking actions require mandate pass and exact approval.
42013
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
42014
+ when { ["pms.order.stage", "pms.order.book", "pms.order.cancel"].contains(context.tool) && !context.approved };
42015
+
42016
+ forbid(principal, action == Action::"MCP::Tool::call", resource)
42017
+ when { context.tool == "pms.order.book" && context.mandate_passed != true };
42018
+ `
42019
+ }
42020
+ ];
42021
+ function connectorPilotIds() {
42022
+ return CONNECTOR_PILOTS.map((pilot) => pilot.id);
42023
+ }
42024
+ function getConnectorPilot(id) {
42025
+ return CONNECTOR_PILOTS.find((pilot) => pilot.id === id);
42026
+ }
42027
+ function connectorDirectory(dir) {
42028
+ return (0, import_node_path6.join)(dir, ".protect-mcp", "connectors");
42029
+ }
42030
+ function writeConnectorPilots(opts) {
42031
+ const directory = connectorDirectory(opts.dir);
42032
+ (0, import_node_fs10.mkdirSync)(directory, { recursive: true });
42033
+ const selected = opts.ids && opts.ids.length > 0 && !opts.ids.includes("all") ? opts.ids.map((id) => {
42034
+ const pilot = getConnectorPilot(id);
42035
+ if (!pilot) throw new Error(`Unknown connector pilot: ${id}`);
42036
+ return pilot;
42037
+ }) : CONNECTOR_PILOTS;
42038
+ const written = [];
42039
+ for (const pilot of selected) {
42040
+ const configPath = (0, import_node_path6.join)(directory, `${pilot.id}.json`);
42041
+ const policyPath = (0, import_node_path6.join)(directory, `${pilot.id}.cedar`);
42042
+ if (!opts.force && ((0, import_node_fs10.existsSync)(configPath) || (0, import_node_fs10.existsSync)(policyPath))) {
42043
+ throw new Error(`Refusing to overwrite ${pilot.id}. Re-run with --force if intentional.`);
42044
+ }
42045
+ (0, import_node_fs10.writeFileSync)(configPath, JSON.stringify({ ...pilot.config, id: pilot.id, name: pilot.name, category: pilot.category, tools: pilot.tools, actions: pilot.actions, setup: pilot.setup }, null, 2) + "\n");
42046
+ (0, import_node_fs10.writeFileSync)(policyPath, pilot.cedar.endsWith("\n") ? pilot.cedar : `${pilot.cedar}
42047
+ `);
42048
+ written.push(configPath, policyPath);
42049
+ }
42050
+ (0, import_node_fs10.writeFileSync)((0, import_node_path6.join)(directory, "README.md"), renderConnectorReadme(selected));
42051
+ written.push((0, import_node_path6.join)(directory, "README.md"));
42052
+ return { written, pilots: selected, directory };
42053
+ }
42054
+ function readInstalledConnectorPilots(dir) {
42055
+ const directory = connectorDirectory(dir);
42056
+ if (!(0, import_node_fs10.existsSync)(directory)) return [];
42057
+ return (0, import_node_fs10.readdirSync)(directory).filter((name) => name.endsWith(".json")).map((name) => {
42058
+ const configPath = (0, import_node_path6.join)(directory, name);
42059
+ try {
42060
+ const parsed = JSON.parse((0, import_node_fs10.readFileSync)(configPath, "utf-8"));
42061
+ const id = String(parsed.id || name.replace(/\.json$/, ""));
42062
+ const pilot = getConnectorPilot(id);
42063
+ return {
42064
+ id,
42065
+ name: String(parsed.name || pilot?.name || id),
42066
+ category: String(parsed.category || pilot?.category || "unknown"),
42067
+ status: String(parsed.status || parsed.type || "installed"),
42068
+ config_path: configPath,
42069
+ policy_path: (0, import_node_path6.join)(directory, `${id}.cedar`)
42070
+ };
42071
+ } catch {
42072
+ return null;
42073
+ }
42074
+ }).filter(Boolean);
42075
+ }
42076
+ function connectorDoctor(dir, env = process.env) {
42077
+ const installed = new Set(readInstalledConnectorPilots(dir).map((pilot) => pilot.id));
42078
+ return CONNECTOR_PILOTS.map((pilot) => {
42079
+ const envRows = pilot.env.map((item) => ({
42080
+ name: item.name,
42081
+ required: item.required,
42082
+ present: Boolean(env[item.name]),
42083
+ description: item.description
42084
+ }));
42085
+ const missingRequired = envRows.filter((item) => item.required && !item.present).map((item) => item.name);
42086
+ const optionalPresent = envRows.filter((item) => !item.required && item.present).map((item) => item.name);
42087
+ const optionalProviderReady = pilot.id === "slack-teams" ? Boolean(env.SLACK_BOT_TOKEN || env.TEAMS_WEBHOOK_URL) : pilot.id === "finance-pms" ? Boolean(env.PMS_ADAPTER_URL) : false;
42088
+ const mockModeReady = pilot.id === "finance-pms";
42089
+ return {
42090
+ id: pilot.id,
42091
+ name: pilot.name,
42092
+ category: pilot.category,
42093
+ installed: installed.has(pilot.id),
42094
+ usable: missingRequired.length === 0 && (pilot.env.some((item) => item.required) || pilot.env.length === 0 || optionalProviderReady || mockModeReady),
42095
+ mode: pilot.id === "finance-pms" && !env.PMS_ADAPTER_URL ? "mock" : pilot.id === "slack-teams" && !env.SLACK_BOT_TOKEN && !env.TEAMS_WEBHOOK_URL ? "needs_provider_choice" : "configured_or_local",
42096
+ missing_required: missingRequired,
42097
+ optional_present: optionalPresent,
42098
+ tools: pilot.tools,
42099
+ next: missingRequired.length > 0 ? `Set ${missingRequired.join(", ")}` : installed.has(pilot.id) ? "Run through protect-mcp and inspect the dashboard." : `Install with protect-mcp connectors init ${pilot.id}`
42100
+ };
42101
+ });
42102
+ }
42103
+ function renderConnectorReadme(pilots) {
42104
+ return `# protect-mcp connector pilots
42105
+
42106
+ These files make real tool classes visible and controllable without uploading raw prompts or payloads.
42107
+
42108
+ ${pilots.map((pilot) => `## ${pilot.name}
42109
+
42110
+ ${pilot.description}
42111
+
42112
+ Value: ${pilot.value}
42113
+
42114
+ Tools: ${pilot.tools.map((tool) => `\`${tool}\``).join(", ")}
42115
+
42116
+ Setup:
42117
+ ${pilot.setup.map((step) => `- ${step}`).join("\n")}
42118
+ `).join("\n")}
42119
+ Next: run \`npx protect-mcp dashboard --open\` and review tool inventory, policy coverage, approvals, and receipts.
42120
+ `;
42121
+ }
42122
+
41324
42123
  // src/rekor-anchor.ts
41325
- var import_node_crypto5 = require("crypto");
42124
+ var import_node_crypto6 = require("crypto");
41326
42125
  var REKOR_API = "https://rekor.sigstore.dev/api/v1";
41327
42126
  async function anchorToRekor(receiptHash, signature, publicKeyPem) {
41328
42127
  const entry = {
@@ -41391,7 +42190,7 @@ async function verifyRekorAnchor(logIndex, expectedHash) {
41391
42190
  }
41392
42191
  function hashReceipt(receipt) {
41393
42192
  const canonical = JSON.stringify(receipt, Object.keys(receipt).sort());
41394
- return (0, import_node_crypto5.createHash)("sha256").update(canonical).digest("hex");
42193
+ return (0, import_node_crypto6.createHash)("sha256").update(canonical).digest("hex");
41395
42194
  }
41396
42195
  function createLogAnchorField(anchor) {
41397
42196
  return {
@@ -41404,7 +42203,7 @@ function createLogAnchorField(anchor) {
41404
42203
  }
41405
42204
 
41406
42205
  // src/selective-disclosure.ts
41407
- var import_node_crypto6 = require("crypto");
42206
+ var import_node_crypto7 = require("crypto");
41408
42207
  function redactFields(receipt, fieldsToRedact) {
41409
42208
  const redacted = JSON.parse(JSON.stringify(receipt));
41410
42209
  const salts = [];
@@ -41420,7 +42219,7 @@ function redactFields(receipt, fieldsToRedact) {
41420
42219
  if (i === parts.length - 1) {
41421
42220
  if (key in current) {
41422
42221
  const originalValue = current[key];
41423
- const salt = (0, import_node_crypto6.randomBytes)(16).toString("hex");
42222
+ const salt = (0, import_node_crypto7.randomBytes)(16).toString("hex");
41424
42223
  const commitment = computeCommitment(salt, originalValue);
41425
42224
  salts.push({ field: fieldPath, salt, originalValue });
41426
42225
  current[key] = `sha256(salt + ${typeof originalValue === "string" ? "..." : JSON.stringify(originalValue).slice(0, 20) + "..."})`;
@@ -41492,11 +42291,11 @@ function createDisclosurePackage(allSalts, fieldsToDisclose) {
41492
42291
  }
41493
42292
  function computeCommitment(salt, value) {
41494
42293
  const serialized = typeof value === "string" ? value : JSON.stringify(value);
41495
- return (0, import_node_crypto6.createHash)("sha256").update(salt + serialized).digest("hex");
42294
+ return (0, import_node_crypto7.createHash)("sha256").update(salt + serialized).digest("hex");
41496
42295
  }
41497
42296
  function hashObject(obj) {
41498
42297
  const canonical = JSON.stringify(obj, Object.keys(obj).sort());
41499
- return (0, import_node_crypto6.createHash)("sha256").update(canonical).digest("hex");
42298
+ return (0, import_node_crypto7.createHash)("sha256").update(canonical).digest("hex");
41500
42299
  }
41501
42300
 
41502
42301
  // src/huggingface-export.ts
@@ -41637,10 +42436,10 @@ MIT
41637
42436
  }
41638
42437
 
41639
42438
  // src/webauthn-approval.ts
41640
- var import_node_crypto7 = require("crypto");
42439
+ var import_node_crypto8 = require("crypto");
41641
42440
  function createApprovalChallenge(requestId, toolName, agentId, rpId = "scopeblind.com", timeoutSeconds = 300) {
41642
- const challengeBytes = (0, import_node_crypto7.randomBytes)(32);
41643
- const contextHash = (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify({ requestId, toolName, agentId, timestamp: Date.now() })).digest("hex");
42441
+ const challengeBytes = (0, import_node_crypto8.randomBytes)(32);
42442
+ const contextHash = (0, import_node_crypto8.createHash)("sha256").update(JSON.stringify({ requestId, toolName, agentId, timestamp: Date.now() })).digest("hex");
41644
42443
  return {
41645
42444
  challenge: base64urlEncode(challengeBytes),
41646
42445
  requestId,
@@ -41655,14 +42454,14 @@ function createApprovalChallenge(requestId, toolName, agentId, rpId = "scopeblin
41655
42454
  function toCredentialRequestOptions(challenge, allowCredentials) {
41656
42455
  return {
41657
42456
  publicKey: {
41658
- challenge: base64urlDecode(challenge.challenge).buffer,
42457
+ challenge: base64urlDecode2(challenge.challenge).buffer,
41659
42458
  rpId: challenge.rpId,
41660
42459
  timeout: challenge.timeoutSeconds * 1e3,
41661
42460
  userVerification: "required",
41662
42461
  // Always require biometric
41663
42462
  ...allowCredentials ? {
41664
42463
  allowCredentials: allowCredentials.map((c) => ({
41665
- id: base64urlDecode(c.id).buffer,
42464
+ id: base64urlDecode2(c.id).buffer,
41666
42465
  type: "public-key"
41667
42466
  }))
41668
42467
  } : {}
@@ -41683,7 +42482,7 @@ function verifyApprovalAssertion(challenge, assertion) {
41683
42482
  approvedAt: (/* @__PURE__ */ new Date()).toISOString()
41684
42483
  };
41685
42484
  }
41686
- const authData = base64urlDecode(assertion.authenticatorData);
42485
+ const authData = base64urlDecode2(assertion.authenticatorData);
41687
42486
  const flags = authData[32];
41688
42487
  const userPresent = !!(flags & 1);
41689
42488
  const userVerified = !!(flags & 4);
@@ -41691,7 +42490,7 @@ function verifyApprovalAssertion(challenge, assertion) {
41691
42490
  const signCount = authData.length >= 37 ? authData[33] << 24 | authData[34] << 16 | authData[35] << 8 | authData[36] : 0;
41692
42491
  let authenticatorType = "unknown";
41693
42492
  try {
41694
- const clientData = JSON.parse(Buffer.from(base64urlDecode(assertion.clientDataJSON)).toString());
42493
+ const clientData = JSON.parse(Buffer.from(base64urlDecode2(assertion.clientDataJSON)).toString());
41695
42494
  if (clientData.type === "webauthn.get") {
41696
42495
  authenticatorType = "platform";
41697
42496
  }
@@ -41720,13 +42519,13 @@ function createApprovalReceiptPayload(challenge, result) {
41720
42519
  context_hash: result.contextHash,
41721
42520
  approved_at: result.approvedAt,
41722
42521
  // Hash the credential ID for privacy — don't store the raw ID
41723
- credential_id_hash: (0, import_node_crypto7.createHash)("sha256").update(result.credentialId).digest("hex").slice(0, 16)
42522
+ credential_id_hash: (0, import_node_crypto8.createHash)("sha256").update(result.credentialId).digest("hex").slice(0, 16)
41724
42523
  };
41725
42524
  }
41726
42525
  function base64urlEncode(buffer) {
41727
42526
  return Buffer.from(buffer).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
41728
42527
  }
41729
- function base64urlDecode(str) {
42528
+ function base64urlDecode2(str) {
41730
42529
  const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
41731
42530
  const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
41732
42531
  return new Uint8Array(Buffer.from(padded, "base64"));
@@ -41974,7 +42773,7 @@ function evaluatePolicy(tool, policy) {
41974
42773
  }
41975
42774
 
41976
42775
  // src/evidence-authenticity.ts
41977
- var import_node_crypto8 = require("crypto");
42776
+ var import_node_crypto9 = require("crypto");
41978
42777
  async function createEvidenceAttestation(input) {
41979
42778
  const tlsNotaryAvailable = await isTLSNotaryAvailable();
41980
42779
  if (tlsNotaryAvailable) {
@@ -42034,14 +42833,14 @@ async function verifyEvidenceAttestation(attestation) {
42034
42833
  }
42035
42834
  }
42036
42835
  function hashResponseBody(body) {
42037
- return (0, import_node_crypto8.createHash)("sha256").update(typeof body === "string" ? body : body).digest("hex");
42836
+ return (0, import_node_crypto9.createHash)("sha256").update(typeof body === "string" ? body : body).digest("hex");
42038
42837
  }
42039
42838
  function createAttestationField(attestation) {
42040
42839
  return {
42041
42840
  evidence_authenticity: {
42042
42841
  version: attestation.version,
42043
42842
  method: attestation.method,
42044
- url_hash: (0, import_node_crypto8.createHash)("sha256").update(attestation.url).digest("hex").slice(0, 16),
42843
+ url_hash: (0, import_node_crypto9.createHash)("sha256").update(attestation.url).digest("hex").slice(0, 16),
42045
42844
  response_hash: attestation.responseHash,
42046
42845
  fetched_at: attestation.fetchedAt,
42047
42846
  verified: attestation.verified,
@@ -42072,7 +42871,7 @@ async function createTLSNotaryAttestation(input) {
42072
42871
  }
42073
42872
 
42074
42873
  // src/c2pa-credentials.ts
42075
- var import_node_crypto9 = require("crypto");
42874
+ var import_node_crypto10 = require("crypto");
42076
42875
  function createC2PAManifest(receipts, options) {
42077
42876
  const generator = options.generator || "protect-mcp";
42078
42877
  const version = options.version || "0.3.3";
@@ -42086,7 +42885,7 @@ function createC2PAManifest(receipts, options) {
42086
42885
  (r) => r.payload?.decision === "deny"
42087
42886
  );
42088
42887
  const receiptHashes = receipts.map(
42089
- (r) => (0, import_node_crypto9.createHash)("sha256").update(JSON.stringify(r)).digest("hex")
42888
+ (r) => (0, import_node_crypto10.createHash)("sha256").update(JSON.stringify(r)).digest("hex")
42090
42889
  );
42091
42890
  const merkleRoot2 = computeMerkleRoot(receiptHashes);
42092
42891
  const assertions = [
@@ -42228,7 +43027,7 @@ function computeMerkleRoot(hashes) {
42228
43027
  const left = hashes[i];
42229
43028
  const right = i + 1 < hashes.length ? hashes[i + 1] : left;
42230
43029
  nextLevel.push(
42231
- (0, import_node_crypto9.createHash)("sha256").update(left + right).digest("hex")
43030
+ (0, import_node_crypto10.createHash)("sha256").update(left + right).digest("hex")
42232
43031
  );
42233
43032
  }
42234
43033
  return computeMerkleRoot(nextLevel);
@@ -42281,7 +43080,7 @@ function toManifoldFormat(prediction) {
42281
43080
  }
42282
43081
 
42283
43082
  // src/agent-exchange.ts
42284
- var import_node_crypto10 = require("crypto");
43083
+ var import_node_crypto11 = require("crypto");
42285
43084
  var ReceiptPropagator = class {
42286
43085
  issuer;
42287
43086
  signer;
@@ -42302,7 +43101,7 @@ var ReceiptPropagator = class {
42302
43101
  delegate(delegateId, options) {
42303
43102
  const now = /* @__PURE__ */ new Date();
42304
43103
  const receipt = {
42305
- receipt_id: `del_${(0, import_node_crypto10.randomUUID)().slice(0, 12)}`,
43104
+ receipt_id: `del_${(0, import_node_crypto11.randomUUID)().slice(0, 12)}`,
42306
43105
  receipt_type: "delegation",
42307
43106
  issuer_id: this.issuer,
42308
43107
  event_time: now.toISOString(),
@@ -42359,7 +43158,7 @@ var ReceiptPropagator = class {
42359
43158
  const currentCount = this.delegationCallCounts.get(options.delegation_receipt) || 0;
42360
43159
  this.delegationCallCounts.set(options.delegation_receipt, currentCount + 1);
42361
43160
  const receipt = {
42362
- receipt_id: `act_${(0, import_node_crypto10.randomUUID)().slice(0, 12)}`,
43161
+ receipt_id: `act_${(0, import_node_crypto11.randomUUID)().slice(0, 12)}`,
42363
43162
  receipt_type: "execution",
42364
43163
  issuer_id: this.issuer,
42365
43164
  event_time: (/* @__PURE__ */ new Date()).toISOString(),
@@ -42451,7 +43250,7 @@ function createReceiptChannel(orchestratorId) {
42451
43250
  async withDelegation(delegateId, tools, fn, options) {
42452
43251
  const delegation = propagator.delegate(delegateId, {
42453
43252
  tools,
42454
- scope: options?.scope || `task-${(0, import_node_crypto10.randomUUID)().slice(0, 8)}`,
43253
+ scope: options?.scope || `task-${(0, import_node_crypto11.randomUUID)().slice(0, 8)}`,
42455
43254
  ttl: options?.ttl || 3600,
42456
43255
  maxCalls: options?.maxCalls
42457
43256
  });
@@ -42601,6 +43400,48 @@ var TOOLS = [
42601
43400
  },
42602
43401
  required: ["environment"]
42603
43402
  }
43403
+ },
43404
+ {
43405
+ name: "github_create_pr",
43406
+ description: "Create a GitHub pull request",
43407
+ inputSchema: {
43408
+ type: "object",
43409
+ properties: {
43410
+ repo: { type: "string", description: "Repository name" },
43411
+ branch: { type: "string", description: "Source branch" },
43412
+ title: { type: "string", description: "Pull request title" }
43413
+ },
43414
+ required: ["repo", "branch", "title"]
43415
+ }
43416
+ },
43417
+ {
43418
+ name: "send_email",
43419
+ description: "Send an email",
43420
+ inputSchema: {
43421
+ type: "object",
43422
+ properties: {
43423
+ to: { type: "string", description: "Recipient email address" },
43424
+ subject: { type: "string", description: "Subject line" },
43425
+ body: { type: "string", description: "Message body" }
43426
+ },
43427
+ required: ["to", "subject"]
43428
+ }
43429
+ },
43430
+ {
43431
+ name: "pms_book_fill",
43432
+ description: "Book a fill into the mock portfolio management system",
43433
+ inputSchema: {
43434
+ type: "object",
43435
+ properties: {
43436
+ account: { type: "string", description: "Portfolio or fund account" },
43437
+ symbol: { type: "string", description: "Instrument symbol" },
43438
+ side: { type: "string", enum: ["BUY", "SELL"] },
43439
+ quantity: { type: "number" },
43440
+ price: { type: "number" },
43441
+ strategy: { type: "string" }
43442
+ },
43443
+ required: ["account", "symbol", "side", "quantity", "price"]
43444
+ }
42604
43445
  }
42605
43446
  ];
42606
43447
  function handleRequest(request) {
@@ -42648,6 +43489,15 @@ Contents: Hello from protect-mcp demo server!`;
42648
43489
  case "deploy":
42649
43490
  resultText = `[demo] Deployed to ${args.environment || "staging"}${args.reason ? ` (reason: ${args.reason})` : ""}`;
42650
43491
  break;
43492
+ case "github_create_pr":
43493
+ resultText = `[demo] Created PR in ${args.repo || "scopeblind/demo"} from ${args.branch || "agent/demo"}: ${args.title || "Agent change"}`;
43494
+ break;
43495
+ case "send_email":
43496
+ resultText = `[demo] Drafted email to ${args.to || "pm@example.com"}: ${args.subject || "Agent update"}`;
43497
+ break;
43498
+ case "pms_book_fill":
43499
+ resultText = `[demo] Booked ${args.side || "BUY"} ${args.quantity || 0} ${args.symbol || "AAPL"} @ ${args.price || 0} into ${args.account || "Demo Fund"} (${args.strategy || "Unassigned"})`;
43500
+ break;
42651
43501
  default:
42652
43502
  resultText = `[demo] Unknown tool: ${toolName}`;
42653
43503
  }
@@ -42720,12 +43570,38 @@ function createSandboxServer() {
42720
43570
  { environment: z.enum(["staging", "production"]).describe("Target environment"), reason: z.string().optional().describe("Deployment reason") },
42721
43571
  async (args) => ({ content: [{ type: "text", text: `[demo] Deployed to ${args.environment}` }] })
42722
43572
  );
43573
+ server.tool("github_create_pr", "Create a GitHub pull request", {
43574
+ repo: z.string(),
43575
+ branch: z.string(),
43576
+ title: z.string()
43577
+ }, async (args) => ({
43578
+ content: [{ type: "text", text: `[demo] PR created in ${args.repo} from ${args.branch}: ${args.title}` }]
43579
+ }));
43580
+ server.tool("send_email", "Send an email", {
43581
+ to: z.string(),
43582
+ subject: z.string(),
43583
+ body: z.string().optional()
43584
+ }, async (args) => ({
43585
+ content: [{ type: "text", text: `[demo] Email prepared for ${args.to}: ${args.subject}` }]
43586
+ }));
43587
+ server.tool("pms_book_fill", "Book a fill into a mock PMS", {
43588
+ account: z.string(),
43589
+ symbol: z.string(),
43590
+ side: z.enum(["BUY", "SELL"]),
43591
+ quantity: z.number(),
43592
+ price: z.number(),
43593
+ strategy: z.string().optional()
43594
+ }, async (args) => ({
43595
+ content: [{ type: "text", text: `[demo] Booked ${args.side} ${args.quantity} ${args.symbol} @ ${args.price} into ${args.account}` }]
43596
+ }));
42723
43597
  return server;
42724
43598
  }
42725
43599
  // Annotate the CommonJS export names for ESM import in node:
42726
43600
  0 && (module.exports = {
42727
43601
  BUILTIN_PATTERNS,
43602
+ CONNECTOR_PILOTS,
42728
43603
  ConfidentialGate,
43604
+ POLICY_PACKS,
42729
43605
  ProtectGateway,
42730
43606
  ReceiptPropagator,
42731
43607
  ScopeBlindBridge,
@@ -42735,6 +43611,9 @@ function createSandboxServer() {
42735
43611
  collectSignedReceipts,
42736
43612
  computeCalibration,
42737
43613
  confidentialInference,
43614
+ connectorDirectory,
43615
+ connectorDoctor,
43616
+ connectorPilotIds,
42738
43617
  createApprovalChallenge,
42739
43618
  createApprovalReceiptPayload,
42740
43619
  createAttestationField,
@@ -42746,6 +43625,7 @@ function createSandboxServer() {
42746
43625
  createReceiptChannel,
42747
43626
  createSandbox,
42748
43627
  createSandboxServer,
43628
+ createSelectiveDisclosurePackage,
42749
43629
  destroySandbox,
42750
43630
  discloseField,
42751
43631
  ed25519ToDIDKey,
@@ -42766,6 +43646,8 @@ function createSandboxServer() {
42766
43646
  generateSampleCedarPolicy,
42767
43647
  generateSchemaStub,
42768
43648
  generateVerifyReceiptSkill,
43649
+ getConnectorPilot,
43650
+ getPolicyPack,
42769
43651
  getScopeBlindBridge,
42770
43652
  getSignerInfo,
42771
43653
  getToolPolicy,
@@ -42786,8 +43668,10 @@ function createSandboxServer() {
42786
43668
  parseLogFile,
42787
43669
  parseNotificationConfigFromEnv,
42788
43670
  parseRateLimit,
43671
+ policyPackIds,
42789
43672
  policySetFromSource,
42790
43673
  queryExternalPDP,
43674
+ readInstalledConnectorPilots,
42791
43675
  receiptToVP,
42792
43676
  receiptsToHFRows,
42793
43677
  redactFields,
@@ -42811,7 +43695,9 @@ function createSandboxServer() {
42811
43695
  verifyApprovalAssertion,
42812
43696
  verifyCommitment,
42813
43697
  verifyEvidenceAttestation,
42814
- verifyRekorAnchor
43698
+ verifyRekorAnchor,
43699
+ verifySelectiveDisclosurePackage,
43700
+ writeConnectorPilots
42815
43701
  });
42816
43702
  /**
42817
43703
  * scopeblind-bridge.ts