@safebrowse/daemon 0.1.4 → 0.1.5

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/server.js CHANGED
@@ -3,8 +3,9 @@ import { readFile, stat } from "node:fs/promises";
3
3
  import { createServer } from "node:http";
4
4
  import { resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { applyV4FailClosedMediation, applyV5ObservationMediation, attachCapabilitiesToPlannerInput, brokerArtifact, brokerArtifactV2, buildReplayBundle, compilePolicy, createApprovalGrantHash, createApprovalIntentPayloadV5, evaluateAction, evaluateCapabilityUse, evaluateCapabilityUseV5, evaluateMemoryWrite, evaluateMemoryWriteV4, evaluateMemoryWriteV5, evaluateToolRequest, mintCapabilitiesForObservation, mintCapabilitiesForObservationV5, mintMemoryPromotionCapabilityV5, prepareToolOnboarding, prepareToolOnboardingV4, prepareToolOnboardingV5, promoteMemoryRecordV4, promoteMemoryRecordV5, promoteMemoryRecordV6, promoteStagedMemoryRecordV5, rollbackMemoryRecordV4, rollbackMemoryRecordV5, sanitizeObservation, stageMemoryRecordV6, stageMemoryRecordV5, issueApprovalEnvelopeV5, verifyApprovalIntentSignatureV5, verifyToolCallback, verifyToolCallbackV4, verifyToolCallbackV5 } from "@safebrowse/core";
7
- import { buildRegistryDefaults, loadKnowledgeBaseContext, loadVerifiedRegistryBundle, loadPolicyPackFromPaths, resolvePolicyLayerFiles } from "./loaders.js";
6
+ import { applyModelGuardAssessment, applyV6ObservationMediation, buildReplayBundle, buildModelGuardObservationRequest, compilePolicy, computeToolManifestHash, computeToolSchemaHash, createApprovalIntentPayloadV6, evaluateCapabilityUseV6, extractAttachmentGraphV6, issueApprovalEnvelopeV6, mintCapabilitiesForObservationV6, mintMemoryPromotionCapabilityV6, prepareToolOnboardingV6, promoteMemoryRecordV6, rollbackMemoryRecordV6, stageMemoryRecordV6, tightenAuthoritiesWithModelGuard, verifyApprovalIntentSignatureV6, verifyToolCallbackV6 } from "@safebrowse/core";
7
+ import { buildRegistryDefaults, loadKnowledgeBaseContext, loadPolicyPackFromPaths, loadVerifiedRegistryBundle, resolvePolicyLayerFiles } from "./loaders.js";
8
+ import { createModelGuardClient } from "./modelGuard.js";
8
9
  import { createParserIsolationService } from "./parserIsolation.js";
9
10
  const PARSER_HEALTH_REFRESH_INTERVAL_MS = 30_000;
10
11
  function hashValue(input) {
@@ -29,17 +30,6 @@ function logServerError(error) {
29
30
  }
30
31
  console.error("SafeBrowse daemon error:", error);
31
32
  }
32
- function legacyResponseMeta(route) {
33
- return {
34
- deprecated: true,
35
- telemetry: {
36
- deprecated: true,
37
- claimScope: "legacy_compatibility",
38
- preventionClaim: false,
39
- routeVersion: route
40
- }
41
- };
42
- }
43
33
  async function fileExists(path) {
44
34
  try {
45
35
  await stat(path);
@@ -60,10 +50,8 @@ async function resolveDefaultRootDir() {
60
50
  }
61
51
  async function buildRuntimeContext(options) {
62
52
  const rootDir = options.rootDir ?? (await resolveDefaultRootDir());
63
- const deploymentProfile = options.deploymentProfile === "secure_v6"
64
- ? "secure_v5"
65
- : (options.deploymentProfile ?? "development");
66
- const secureDeployment = deploymentProfile === "secure_v5";
53
+ const deploymentProfile = options.deploymentProfile ?? "development";
54
+ const secureDeployment = deploymentProfile === "secure_v6";
67
55
  const policyPack = options.policyPack ??
68
56
  (await loadPolicyPackFromPaths(resolvePolicyLayerFiles(resolve(rootDir))));
69
57
  const knowledgeBase = options.knowledgeBase ?? (await loadKnowledgeBaseContext(resolve(rootDir, "knowledge_base")));
@@ -87,12 +75,11 @@ async function buildRuntimeContext(options) {
87
75
  : undefined,
88
76
  approvalBrokerConfigured: Boolean(approvalBrokerPublicKeyPem),
89
77
  approvalBrokerMode: secureDeployment ? "external_service" : options.approvalBrokerMode ?? "signature_verification",
90
- claimBearingReady: false
78
+ modelGuardBaseUrl: options.modelGuardBaseUrl?.trim() || undefined,
79
+ modelGuardTimeoutMs: options.modelGuardTimeoutMs ?? 2_500,
80
+ modelGuardEnforcementMode: options.modelGuardEnforcementMode ?? "off"
91
81
  };
92
82
  }
93
- function plusMinutes(value, minutes) {
94
- return new Date(new Date(value).getTime() + minutes * 60_000).toISOString();
95
- }
96
83
  function plusSeconds(value, seconds) {
97
84
  return new Date(new Date(value).getTime() + seconds * 1_000).toISOString();
98
85
  }
@@ -101,172 +88,75 @@ function createWorkflowHash(payload) {
101
88
  taskId: payload.taskId,
102
89
  userGoal: payload.userGoal,
103
90
  phase: payload.phase ?? "",
91
+ taskPurposeClass: payload.taskPurposeClass ?? "",
92
+ taskPhase: payload.taskPhase ?? "",
104
93
  allowedOrigins: payload.allowedOrigins ?? [],
105
94
  allowedVerbs: payload.allowedVerbs ?? [],
106
- forbiddenSinks: payload.forbiddenSinks ?? []
95
+ forbiddenSinks: payload.forbiddenSinks ?? [],
96
+ allowedPathClasses: payload.allowedPathClasses ?? [],
97
+ approvalRequiredPathClasses: payload.approvalRequiredPathClasses ?? []
107
98
  });
108
99
  }
109
- function createSessionState(request, runtime) {
100
+ function secureParserIsolationSatisfied(probe) {
101
+ return (probe.mode === "node_permission_process" &&
102
+ probe.processIsolated &&
103
+ probe.egressDenied &&
104
+ probe.permissionModelEnabled &&
105
+ probe.childProcessDenied &&
106
+ probe.workerThreadsDenied &&
107
+ probe.envKeys.length === 0);
108
+ }
109
+ function claimBearingReady(runtime, probe) {
110
+ return (runtime.deploymentProfile === "secure_v6" &&
111
+ runtime.approvalBrokerConfigured &&
112
+ Boolean(runtime.approvalBrokerPublicKey) &&
113
+ runtime.approvalBrokerMode === "external_service" &&
114
+ runtime.verifiedRegistry?.signatureVerified === true &&
115
+ runtime.parserIsolationMode === "node_permission_process" &&
116
+ secureParserIsolationSatisfied(probe));
117
+ }
118
+ function createSessionState(request, runtime, secureReady) {
110
119
  const createdAt = new Date().toISOString();
111
- const allowedOrigins = request.allowedOrigins ??
112
- [...runtime.policy.readOnlyOrigins, ...runtime.policy.writableOrigins];
120
+ const allowedOrigins = request.allowedOrigins ?? [...runtime.policy.readOnlyOrigins, ...runtime.policy.writableOrigins];
113
121
  const allowedVerbs = request.allowedVerbs ?? [...runtime.policy.allowedActions];
114
- const forbiddenSinks = request.forbiddenSinks ?? [];
115
122
  const session = {
116
123
  sessionId: randomUUID(),
117
124
  taskId: request.taskId,
118
125
  userGoal: request.userGoal,
119
126
  phase: request.phase,
127
+ taskPurposeClass: request.taskPurposeClass,
128
+ taskPhase: request.taskPhase,
120
129
  allowedOrigins,
121
130
  allowedVerbs,
122
- forbiddenSinks,
131
+ forbiddenSinks: request.forbiddenSinks ?? [],
132
+ allowedPathClasses: request.allowedPathClasses,
133
+ approvalRequiredPathClasses: request.approvalRequiredPathClasses,
123
134
  workflowHash: createWorkflowHash({
124
- taskId: request.taskId,
125
- userGoal: request.userGoal,
126
- phase: request.phase,
135
+ ...request,
127
136
  allowedOrigins,
128
- allowedVerbs,
129
- forbiddenSinks
137
+ allowedVerbs
130
138
  }),
131
139
  currentStep: 0,
132
140
  createdAt,
133
141
  expiresAt: plusSeconds(createdAt, request.expiresInSeconds ?? 1800),
134
- claimProfile: runtime.claimBearingReady ? secureClaimProfile(runtime) : undefined,
135
- approvalBrokerRequired: isSecureDeploymentProfile(runtime.deploymentProfile),
136
- legacyRoutesDisabled: isSecureDeploymentProfile(runtime.deploymentProfile)
142
+ claimProfile: secureReady ? "secure_v6" : undefined,
143
+ approvalBrokerRequired: runtime.deploymentProfile === "secure_v6",
144
+ legacyRoutesDisabled: true
137
145
  };
138
- const memorySourceClasses = new Map();
139
146
  return {
140
147
  session,
141
148
  observations: new Map(),
142
- capabilities: new Map(),
143
- usedCapabilities: new Set(),
144
- authorityReduced: false,
145
- authorityReductionReasons: [],
146
- approvalGrants: new Map(),
149
+ authorities: new Map(),
150
+ usedAuthorities: new Set(),
151
+ approvalEnvelopes: new Map(),
152
+ onboardingSessions: new Map(),
153
+ connectorHandles: new Map(),
147
154
  memoryRecords: new Map(),
155
+ memorySourceClasses: new Map(),
148
156
  memorySnapshots: new Map(),
149
- onboardingSessions: new Map(),
150
- observationsV5: new Map(),
151
- capabilitiesV5: new Map(),
152
- consumedCapabilitiesV5: new Map(),
153
- usedCapabilitiesV5: new Set(),
154
- approvalEnvelopesV5: new Map(),
155
- onboardingSessionsV5: new Map(),
156
- connectorHandlesV5: new Map(),
157
- memorySourceClassesV5: memorySourceClasses,
158
- memorySourceClassesV6: memorySourceClasses,
159
157
  replayEvents: []
160
158
  };
161
159
  }
162
- function secureParserIsolationSatisfied(probe) {
163
- return (probe.mode === "node_permission_process" &&
164
- probe.processIsolated &&
165
- probe.egressDenied &&
166
- probe.permissionModelEnabled &&
167
- probe.childProcessDenied &&
168
- probe.workerThreadsDenied &&
169
- probe.envKeys.length === 0);
170
- }
171
- function isSecureDeploymentProfile(deploymentProfile) {
172
- return deploymentProfile === "secure_v5";
173
- }
174
- function secureClaimProfile(runtime) {
175
- return "secure_v5";
176
- }
177
- function claimBearingReady(runtime, probe) {
178
- return (isSecureDeploymentProfile(runtime.deploymentProfile) &&
179
- runtime.approvalBrokerConfigured &&
180
- Boolean(runtime.approvalBrokerPublicKey) &&
181
- runtime.approvalBrokerMode === "external_service" &&
182
- runtime.verifiedRegistry?.signatureVerified === true &&
183
- runtime.parserIsolationMode === "node_permission_process" &&
184
- secureParserIsolationSatisfied(probe));
185
- }
186
- function shouldReduceAuthority(sessionState, observation) {
187
- if (sessionState.authorityReduced) {
188
- return true;
189
- }
190
- const hasPriorSurface = Boolean(sessionState.latestObservation);
191
- const hasMeaningfulRisk = observation.parseStatus !== "compiled" ||
192
- observation.riskFindings.length > 0 ||
193
- observation.secretFindings.length > 0;
194
- return hasPriorSurface && hasMeaningfulRisk;
195
- }
196
- function applyAuthorityReduction(sessionState, observation, plannerInput) {
197
- const reasons = [
198
- ...sessionState.authorityReductionReasons,
199
- ...(observation.parseStatus !== "compiled"
200
- ? [`parse_status_${observation.parseStatus}`]
201
- : []),
202
- ...observation.riskFindings,
203
- ...(observation.secretFindings.length ? ["secret_redaction_boundary"] : [])
204
- ];
205
- sessionState.authorityReduced = true;
206
- sessionState.authorityReductionReasons = [...new Set(reasons)];
207
- sessionState.capabilities.clear();
208
- return {
209
- ...plannerInput,
210
- candidateCapabilities: [],
211
- riskMarkers: [
212
- ...new Set([
213
- ...plannerInput.riskMarkers,
214
- "multimodal_reducer_active",
215
- ...sessionState.authorityReductionReasons.map((reason) => `chain:${reason}`)
216
- ])
217
- ]
218
- };
219
- }
220
- function createOnboardingSession(request, runtime, approvalGrant, verifiedRegistryEntry) {
221
- const callbackUri = verifiedRegistryEntry?.allowedRedirectUris[0] ??
222
- request.oauthContext?.callbackUri ??
223
- request.callbackUri ??
224
- request.oauthContext?.redirectUri ??
225
- request.requestedRedirectUri;
226
- if (!callbackUri) {
227
- return undefined;
228
- }
229
- const callbackOrigin = verifiedRegistryEntry?.allowedCallbackOrigins[0] ?? new URL(callbackUri).origin;
230
- const createdAt = (runtime.now?.() ?? new Date()).toISOString();
231
- return {
232
- sessionId: randomUUID(),
233
- approvalBindingId: approvalGrant.approvalGrantId,
234
- workflowBindingId: request.sourceObservationId,
235
- toolId: request.toolId,
236
- registryEntryId: verifiedRegistryEntry?.registryEntryId ?? request.registryEntryId ?? request.toolId,
237
- registryBundleId: verifiedRegistryEntry?.bundleId ??
238
- request.registryBundleId ??
239
- runtime.verifiedRegistry?.bundleId ??
240
- "unverified-registry",
241
- callbackUri,
242
- callbackOrigin,
243
- requestedScopes: approvalGrant.scopes,
244
- state: randomUUID(),
245
- pkceMethod: "S256",
246
- createdAt,
247
- expiresAt: plusMinutes(createdAt, 5),
248
- status: "prepared"
249
- };
250
- }
251
- function issueApprovalGrant(request, sessionState) {
252
- const issuedAt = new Date().toISOString();
253
- const grantWithoutHash = {
254
- approvalGrantId: randomUUID(),
255
- sessionId: sessionState.session.sessionId,
256
- workflowHash: sessionState.session.workflowHash,
257
- connectorId: request.connectorId,
258
- scopes: request.scopes ?? [],
259
- sinkClass: request.sinkClass,
260
- capabilityIds: request.capabilityIds ?? [],
261
- targetOrigin: request.targetOrigin,
262
- issuedAt,
263
- expiresAt: plusSeconds(issuedAt, request.expiresInSeconds ?? 600)
264
- };
265
- return {
266
- ...grantWithoutHash,
267
- grantHash: createApprovalGrantHash(grantWithoutHash)
268
- };
269
- }
270
160
  function findSessionState(sessions, sessionId) {
271
161
  const sessionState = sessions.get(sessionId);
272
162
  if (!sessionState) {
@@ -278,1095 +168,224 @@ function findSessionState(sessions, sessionId) {
278
168
  }
279
169
  return sessionState;
280
170
  }
281
- function legacyRoutesDisabled(runtime) {
282
- return isSecureDeploymentProfile(runtime.deploymentProfile);
171
+ function appendReplayEvent(sessionState, event) {
172
+ if (!sessionState) {
173
+ return undefined;
174
+ }
175
+ const replayEvent = {
176
+ eventId: randomUUID(),
177
+ timestamp: new Date().toISOString(),
178
+ ...event
179
+ };
180
+ sessionState.replayEvents.push(replayEvent);
181
+ return replayEvent.eventId;
283
182
  }
284
- function isLegacyRoute(url) {
285
- return url.startsWith("/v1/") || url.startsWith("/v2/") || url.startsWith("/v4/");
183
+ function authorityCandidateFromDescriptor(authority) {
184
+ return {
185
+ authorityId: authority.capabilityId,
186
+ authorityDigest: authority.capabilityDigest,
187
+ semanticDigest: authority.semanticDigest,
188
+ title: authority.title,
189
+ kind: authority.kind,
190
+ operationClass: authority.operationClass,
191
+ targetPathClass: authority.targetPathClass,
192
+ requiresApproval: authority.requiresApproval,
193
+ evidenceSpanIds: authority.evidenceSpanIds,
194
+ providerId: authority.providerId,
195
+ operationId: authority.operationId,
196
+ method: authority.method,
197
+ pathTemplate: authority.pathTemplate,
198
+ mailboxId: authority.mailboxId,
199
+ accountId: authority.accountId,
200
+ messageId: authority.messageId,
201
+ threadId: authority.threadId,
202
+ recipientSetHash: authority.recipientSetHash,
203
+ subjectHash: authority.subjectHash,
204
+ bodyDigest: authority.bodyDigest,
205
+ attachmentDigestSet: authority.attachmentDigestSet,
206
+ parameterSchema: authority.parameterSchema,
207
+ expiresAt: authority.expiresAt
208
+ };
209
+ }
210
+ function consumeAuthority(sessionState, authority) {
211
+ sessionState.authorities.set(authority.capabilityId, {
212
+ ...authority,
213
+ consumedAt: new Date().toISOString()
214
+ });
215
+ sessionState.usedAuthorities.add(authority.capabilityId);
286
216
  }
287
217
  function lookupVerifiedRegistryEntry(runtime, toolId, registryEntryId) {
288
218
  return runtime.verifiedRegistry?.entries.find((entry) => entry.registryEntryId === registryEntryId ||
289
219
  entry.registryEntryId === toolId ||
290
220
  entry.adapterId === toolId);
291
221
  }
292
- function resolveToolManifestRegistryEntry(runtime, capture) {
293
- return runtime.verifiedRegistry?.entries.find((entry) => {
294
- if (capture.toolId !== entry.adapterId && capture.toolId !== entry.registryEntryId) {
295
- return false;
296
- }
297
- if (capture.authType && capture.authType !== entry.authType) {
298
- return false;
299
- }
300
- if (capture.callbackUri && !entry.allowedRedirectUris.includes(capture.callbackUri)) {
301
- return false;
302
- }
303
- if (capture.callbackOrigin &&
304
- !entry.allowedCallbackOrigins.includes(capture.callbackOrigin)) {
305
- return false;
306
- }
307
- if (capture.requestedScopes?.length &&
308
- capture.requestedScopes.some((scope) => !entry.allowedScopes.includes(scope))) {
309
- return false;
310
- }
311
- return true;
312
- });
313
- }
314
- function getCapabilityV5(sessionState, capabilityId) {
315
- if (!sessionState || !capabilityId) {
316
- return undefined;
317
- }
318
- return (sessionState.capabilitiesV5.get(capabilityId) ??
319
- sessionState.consumedCapabilitiesV5.get(capabilityId));
320
- }
321
- function consumeCapabilityV5(sessionState, capability, consumedAt = new Date().toISOString()) {
322
- const consumedCapability = {
323
- ...capability,
324
- consumedAt
222
+ function lookupVerifiedApiProviderEntry(runtime, providerId) {
223
+ return runtime.verifiedRegistry?.apiProviders?.find((entry) => entry.providerId === providerId);
224
+ }
225
+ function buildArtifactRef(capture, observation) {
226
+ const extractionMethod = capture.surfaceType === "html"
227
+ ? "dom"
228
+ : capture.surfaceType === "email_message"
229
+ ? "mime"
230
+ : capture.surfaceType === "docx" ||
231
+ capture.surfaceType === "xlsx" ||
232
+ capture.surfaceType === "pptx"
233
+ ? "ooxml"
234
+ : capture.surfaceType === "attachment_bundle"
235
+ ? "extractor"
236
+ : capture.surfaceType === "tool_manifest" || capture.surfaceType === "memory_candidate"
237
+ ? "api"
238
+ : capture.surfaceType === "external_api_response"
239
+ ? "api"
240
+ : capture.surfaceType === "image"
241
+ ? "ocr"
242
+ : "download";
243
+ const surfaceKind = capture.surfaceType === "tool_manifest"
244
+ ? "tool_manifest"
245
+ : capture.surfaceType === "memory_candidate"
246
+ ? "memory"
247
+ : capture.surfaceType;
248
+ return {
249
+ artifactId: observation.observationId,
250
+ surfaceKind,
251
+ sourceOrigin: observation.sourceOrigin,
252
+ viewerOrigin: observation.frameOrigin,
253
+ mismatchSignals: observation.parseStatus === "compiled" ? [] : [`parse_status_${observation.parseStatus}`],
254
+ metadataSignals: observation.policyFindings.map((finding) => finding.code),
255
+ provenance: {
256
+ extractionMethod,
257
+ lineageChain: [],
258
+ derivedTaintClass: "tainted"
259
+ },
260
+ authorityEligible: observation.authorityEligible
325
261
  };
326
- sessionState.capabilitiesV5.delete(capability.capabilityId);
327
- sessionState.consumedCapabilitiesV5.set(capability.capabilityId, consumedCapability);
328
- sessionState.usedCapabilitiesV5.add(capability.capabilityId);
329
- return consumedCapability;
330
- }
331
- function getActiveApprovalEnvelopeForCapability(sessionState, capabilityId) {
332
- if (!sessionState || !capabilityId) {
333
- return undefined;
334
- }
335
- return [...sessionState.approvalEnvelopesV5.values()].find((envelope) => envelope.capabilityId === capabilityId &&
336
- !envelope.consumedAt &&
337
- new Date(envelope.expiresAt).getTime() > Date.now());
338
262
  }
339
263
  function buildMemorySnapshotState(sessionState, record) {
340
- const priorTrustedBaseline = [...sessionState.memoryRecords.values()]
341
- .filter((candidate) => candidate.sessionId === record.sessionId &&
264
+ const priorTrustedRecord = [...sessionState.memoryRecords.values()]
265
+ .filter((candidate) => candidate.recordId !== record.recordId &&
342
266
  candidate.key === record.key &&
343
- candidate.tier === "trusted_durable" &&
344
- candidate.recordId !== record.recordId)
267
+ candidate.tier === "trusted_durable")
345
268
  .sort((left, right) => right.createdAt.localeCompare(left.createdAt))[0];
346
269
  return {
347
- snapshotId: randomUUID(),
348
- sessionId: record.sessionId,
270
+ snapshotId: "",
271
+ sessionId: sessionState.session.sessionId,
349
272
  recordId: record.recordId,
350
273
  key: record.key,
351
274
  createdAt: new Date().toISOString(),
352
- baselineAbsent: !priorTrustedBaseline,
353
- snapshotRecord: priorTrustedBaseline
354
- ? {
355
- ...priorTrustedBaseline
356
- }
357
- : undefined
358
- };
359
- }
360
- function retireObservationCapabilitiesV5(sessionState) {
361
- const retiredCapabilityIds = [];
362
- for (const [capabilityId, capability] of sessionState.capabilitiesV5.entries()) {
363
- if (capability.kind !== "memory_promote") {
364
- sessionState.capabilitiesV5.delete(capabilityId);
365
- retiredCapabilityIds.push(capabilityId);
366
- }
367
- }
368
- if (!retiredCapabilityIds.length) {
369
- return;
370
- }
371
- for (const [approvalId, envelope] of sessionState.approvalEnvelopesV5.entries()) {
372
- if (retiredCapabilityIds.includes(envelope.capabilityId) && !envelope.consumedAt) {
373
- sessionState.approvalEnvelopesV5.delete(approvalId);
374
- }
375
- }
376
- }
377
- function buildV5ExecutionPlan(capability) {
378
- if (!capability) {
379
- return undefined;
380
- }
381
- return {
382
- verb: capability.kind,
383
- targetUrl: capability.targetUrl,
384
- targetOrigin: capability.targetOrigin,
385
- selector: capability.selector,
386
- connectorId: capability.connectorId,
387
- callbackUri: capability.callbackUri,
388
- callbackOrigin: capability.callbackOrigin,
389
- memoryRecordId: capability.memoryRecordId,
390
- derivedSinkClass: capability.derivedSinkClass,
391
- derivedSensitiveSink: capability.derivedSensitiveSink,
392
- semanticDigest: capability.semanticDigest
393
- };
394
- }
395
- function summarizeCapabilityV5(capability) {
396
- return {
397
- capabilityId: capability.capabilityId,
398
- capabilityDigest: capability.capabilityDigest,
399
- semanticDigest: capability.semanticDigest,
400
- title: capability.title,
401
- kind: capability.kind,
402
- parameterSchema: capability.parameterSchema,
403
- expiresAt: capability.expiresAt
275
+ baselineAbsent: !priorTrustedRecord,
276
+ snapshotRecord: priorTrustedRecord
404
277
  };
405
278
  }
406
- function summarizeAuthorityCandidate(capability) {
279
+ function retiredRouteResponse(route) {
407
280
  return {
408
- authorityId: capability.capabilityId,
409
- authorityDigest: capability.capabilityDigest,
410
- capabilityId: capability.capabilityId,
411
- capabilityDigest: capability.capabilityDigest,
412
- semanticDigest: capability.semanticDigest,
413
- title: capability.title,
414
- kind: capability.kind,
415
- parameterSchema: capability.parameterSchema,
416
- expiresAt: capability.expiresAt
281
+ error: "route_retired_use_v6",
282
+ route,
283
+ replacementPrefix: "/v6",
284
+ deprecated: true
417
285
  };
418
286
  }
419
- function relabelClaimProfile(verdict, claimProfile) {
420
- return {
421
- ...verdict,
422
- safeConstraints: {
423
- ...(verdict.safeConstraints ?? {}),
424
- claim_profile: claimProfile
425
- }
426
- };
287
+ function isRetiredRoute(url) {
288
+ return (url.startsWith("/v1/") ||
289
+ url.startsWith("/v2/") ||
290
+ url.startsWith("/v4/") ||
291
+ url.startsWith("/v5/"));
427
292
  }
428
- function appendReplayEvent(sessionState, event) {
429
- const replayEvent = {
430
- eventId: randomUUID(),
431
- timestamp: new Date().toISOString(),
432
- ...event
433
- };
434
- sessionState?.replayEvents.push(replayEvent);
435
- return replayEvent;
436
- }
437
- function buildV5ObservationDecision(compiledObservation) {
438
- if (compiledObservation.parseStatus !== "compiled") {
439
- return {
440
- decision: "BLOCK",
441
- reasonCodes: [
442
- compiledObservation.parseStatus === "partial"
443
- ? "PARSE_STATUS_PARTIAL"
444
- : "PARSE_STATUS_UNSUPPORTED"
445
- ],
446
- riskScore: 0.98,
447
- safeConstraints: {
448
- authority_eligible: false
449
- },
450
- telemetryTags: ["v5_observation", "block"]
451
- };
452
- }
453
- return {
454
- decision: compiledObservation.authorityEligible ? "ALLOW" : "REPLAN_READ_ONLY",
455
- reasonCodes: compiledObservation.authorityEligible ? [] : ["AUTHORITY_REDUCED_TO_FACTS_ONLY"],
456
- riskScore: compiledObservation.riskScore,
457
- safeConstraints: {
458
- authority_eligible: compiledObservation.authorityEligible
459
- },
460
- telemetryTags: [
461
- "v5_observation",
462
- compiledObservation.authorityEligible ? "authority_eligible" : "facts_only"
463
- ]
464
- };
465
- }
466
- function buildV6ObservationDecision(compiledObservation) {
467
- return buildV5ObservationDecision(compiledObservation);
468
- }
469
- function buildObservationDecisionForClaimProfile(compiledObservation, claimProfile) {
470
- if (!compiledObservation) {
471
- return relabelClaimProfile({
472
- decision: "BLOCK",
473
- reasonCodes: ["OBSERVATION_REQUIRED"],
474
- riskScore: 0.99,
475
- safeConstraints: {},
476
- telemetryTags: [`${claimProfile}_observation`, "block"]
477
- }, claimProfile);
478
- }
479
- return relabelClaimProfile(buildV5ObservationDecision(compiledObservation), claimProfile);
480
- }
481
- function canonicalReplayRoute(route) {
482
- return route.replace(/^\/v6\//, "/v5/");
483
- }
484
- function buildLegacyObservationCapture(payload) {
485
- if (payload.surfaceType === "html") {
486
- return {
487
- mimeType: "text/html",
488
- sourceOrigin: payload.url,
489
- viewerOrigin: payload.frameUrl ?? payload.url,
490
- renderedText: payload.visibleText,
491
- extractedText: payload.visibleText,
492
- metadataText: payload.metadataText,
493
- annotations: payload.annotations,
494
- extractionMethod: "dom",
495
- trustSignals: payload.trustSignals
496
- };
497
- }
498
- if (payload.surfaceType === "pdf") {
499
- return {
500
- mimeType: "application/pdf",
501
- sourceOrigin: payload.url,
502
- viewerOrigin: payload.frameUrl ?? payload.url,
503
- renderedText: payload.renderedText,
504
- extractedText: payload.extractedText,
505
- ocrText: payload.ocrText,
506
- annotations: payload.annotations,
507
- metadataText: payload.metadataText,
508
- extractionMethod: "download",
509
- trustSignals: payload.trustSignals
510
- };
511
- }
512
- if (payload.surfaceType === "image") {
513
- return {
514
- mimeType: "image/png",
515
- sourceOrigin: payload.url,
516
- viewerOrigin: payload.frameUrl ?? payload.url,
517
- ocrText: payload.ocrText,
518
- metadataText: payload.metadataText,
519
- extractionMethod: "ocr",
520
- trustSignals: payload.trustSignals
521
- };
522
- }
523
- if (payload.surfaceType === "tool_manifest") {
524
- return {
525
- mimeType: "application/json",
526
- surfaceKind: "tool_manifest",
527
- sourceOrigin: payload.url,
528
- viewerOrigin: payload.frameUrl ?? payload.url,
529
- extractedText: payload.description,
530
- metadataText: payload.schemaDescriptions,
531
- extractionMethod: "api",
532
- trustSignals: payload.trustSignals
533
- };
534
- }
535
- return undefined;
536
- }
537
- function buildV5ArtifactRef(artifact, authorityEligible) {
538
- return {
539
- artifactId: artifact.artifactId,
540
- surfaceKind: artifact.surfaceKind,
541
- sourceOrigin: artifact.sourceOrigin,
542
- viewerOrigin: artifact.viewerOrigin,
543
- mismatchSignals: artifact.mismatchSignals,
544
- metadataSignals: artifact.metadataSignals,
545
- provenance: {
546
- extractionMethod: artifact.extractionMethod,
547
- lineageChain: artifact.lineageChain,
548
- derivedTaintClass: artifact.derivedTaintClass
549
- },
550
- authorityEligible
551
- };
552
- }
553
- const buildV6ArtifactRef = buildV5ArtifactRef;
554
293
  export async function createSafeBrowseServer(options = {}) {
555
294
  const runtime = await buildRuntimeContext(options);
556
- const unifiedV5InSecureV6 = false;
295
+ const modelGuardClient = createModelGuardClient({
296
+ baseUrl: runtime.modelGuardBaseUrl,
297
+ timeoutMs: runtime.modelGuardTimeoutMs,
298
+ enforcementMode: runtime.modelGuardEnforcementMode
299
+ });
557
300
  const parserIsolationService = createParserIsolationService(runtime.parserIsolationMode, {
558
301
  allowlistedEgress: runtime.parserAllowlistedEgress,
559
- runtime: {
560
- knowledgeBase: runtime.knowledgeBase
561
- }
302
+ runtime
562
303
  });
563
304
  let parserProbeSnapshot = await parserIsolationService.refreshProbe();
564
- runtime.claimBearingReady = claimBearingReady(runtime, parserProbeSnapshot.probe);
565
- if (isSecureDeploymentProfile(runtime.deploymentProfile)) {
566
- const secureProfile = runtime.deploymentProfile;
567
- if (!runtime.verifiedRegistry?.signatureVerified) {
568
- throw new Error(`${secureProfile} requires a signature-verified registry bundle`);
569
- }
570
- if (!runtime.approvalBrokerConfigured || !runtime.approvalBrokerPublicKey) {
571
- throw new Error(`${secureProfile} requires an approval broker public key`);
572
- }
573
- if (runtime.approvalBrokerMode !== "external_service") {
574
- throw new Error(`${secureProfile} requires approvalBrokerMode=external_service`);
575
- }
576
- if (runtime.parserIsolationMode !== "node_permission_process") {
577
- throw new Error(`${secureProfile} requires parserIsolationMode=node_permission_process`);
578
- }
579
- if (!secureParserIsolationSatisfied(parserProbeSnapshot.probe)) {
580
- throw new Error(`${secureProfile} requires a permission-constrained parser process with denied egress and scrubbed env`);
581
- }
582
- }
583
- const onboardingSessions = new Map();
305
+ let modelGuardHealthSnapshot = await modelGuardClient.refreshHealth();
306
+ const secureReady = claimBearingReady(runtime, parserProbeSnapshot.probe);
584
307
  const sessions = new Map();
585
308
  const parserHealthRefreshTimer = setInterval(() => {
586
309
  void parserIsolationService
587
310
  .refreshProbe()
588
311
  .then((snapshot) => {
589
312
  parserProbeSnapshot = snapshot;
590
- runtime.claimBearingReady = claimBearingReady(runtime, snapshot.probe);
591
313
  })
592
- .catch(() => {
593
- runtime.claimBearingReady = false;
594
- });
314
+ .catch(() => undefined);
315
+ void modelGuardClient
316
+ .refreshHealth()
317
+ .then((snapshot) => {
318
+ modelGuardHealthSnapshot = snapshot;
319
+ })
320
+ .catch(() => undefined);
595
321
  }, PARSER_HEALTH_REFRESH_INTERVAL_MS);
596
- parserHealthRefreshTimer.unref();
322
+ parserHealthRefreshTimer.unref?.();
597
323
  const server = createServer(async (request, response) => {
598
- if (!request.url) {
599
- writeJson(response, 400, { error: "missing_url" });
600
- return;
601
- }
602
- if (request.url.startsWith("/v6/")) {
603
- request.url = canonicalReplayRoute(request.url);
604
- }
605
324
  try {
606
- if (request.method === "GET" && request.url === "/health") {
607
- parserProbeSnapshot = await parserIsolationService.getCachedProbe();
608
- const claimReady = claimBearingReady(runtime, parserProbeSnapshot.probe);
609
- runtime.claimBearingReady = claimReady;
325
+ const requestUrl = request.url ?? "/";
326
+ if (request.method === "GET" && requestUrl === "/health") {
610
327
  writeJson(response, 200, {
611
328
  status: "ok",
612
- profile: runtime.policy.profile,
613
329
  deploymentProfile: runtime.deploymentProfile,
614
- claimBearingReady: claimReady,
615
- version: runtime.policy.version,
616
- policyLayers: runtime.policy.layerProvenance,
617
- legacyRoutesEnabled: !legacyRoutesDisabled(runtime),
618
- verifiedRegistry: runtime.verifiedRegistry
619
- ? {
620
- bundleId: runtime.verifiedRegistry.bundleId,
621
- version: runtime.verifiedRegistry.version,
622
- signatureVerified: runtime.verifiedRegistry.signatureVerified,
623
- entryCount: runtime.verifiedRegistry.entries.length,
624
- required: isSecureDeploymentProfile(runtime.deploymentProfile)
625
- }
626
- : undefined,
627
- parserIsolation: {
628
- ...parserProbeSnapshot.probe,
629
- lastCheckedAt: parserProbeSnapshot.lastCheckedAt,
630
- configuredMode: runtime.parserIsolationMode,
631
- enforced: isSecureDeploymentProfile(runtime.deploymentProfile)
632
- },
330
+ claimBearingReady: claimBearingReady(runtime, parserProbeSnapshot.probe),
331
+ legacyRoutesEnabled: false,
633
332
  approvalBroker: {
634
- required: isSecureDeploymentProfile(runtime.deploymentProfile),
635
333
  configured: runtime.approvalBrokerConfigured,
636
334
  mode: runtime.approvalBrokerMode
637
- }
638
- });
639
- return;
640
- }
641
- if (request.method !== "POST") {
642
- writeJson(response, 405, { error: "method_not_allowed" });
643
- return;
644
- }
645
- if (legacyRoutesDisabled(runtime) && isLegacyRoute(request.url)) {
646
- writeJson(response, 403, {
647
- error: `route_disabled_in_${runtime.deploymentProfile}`,
648
- route: request.url,
649
- claimProfile: secureClaimProfile(runtime)
650
- });
651
- return;
652
- }
653
- if (request.url === "/v1/observe") {
654
- const payload = await readJson(request);
655
- writeJson(response, 200, {
656
- ...sanitizeObservation(payload, runtime),
657
- ...legacyResponseMeta("/v1/observe")
658
- });
659
- return;
660
- }
661
- if (request.url === "/v1/action") {
662
- const payload = await readJson(request);
663
- writeJson(response, 200, {
664
- ...evaluateAction(payload, runtime),
665
- ...legacyResponseMeta("/v1/action")
666
- });
667
- return;
668
- }
669
- if (request.url === "/v1/artifact") {
670
- const payload = await readJson(request);
671
- writeJson(response, 200, {
672
- ...brokerArtifact(payload, runtime),
673
- ...legacyResponseMeta("/v1/artifact")
674
- });
675
- return;
676
- }
677
- if (request.url === "/v1/tool") {
678
- const payload = await readJson(request);
679
- writeJson(response, 200, {
680
- ...evaluateToolRequest(payload, runtime),
681
- ...legacyResponseMeta("/v1/tool")
682
- });
683
- return;
684
- }
685
- if (request.url === "/v1/memory") {
686
- const payload = await readJson(request);
687
- writeJson(response, 200, {
688
- ...evaluateMemoryWrite(payload, runtime),
689
- ...legacyResponseMeta("/v1/memory")
690
- });
691
- return;
692
- }
693
- if (request.url === "/v1/replay") {
694
- const payload = await readJson(request);
695
- writeJson(response, 200, {
696
- ...buildReplayBundle(payload.events, runtime),
697
- ...legacyResponseMeta("/v1/replay")
698
- });
699
- return;
700
- }
701
- if (request.url === "/v2/tool/prepare") {
702
- const payload = await readJson(request);
703
- const prepared = prepareToolOnboarding(payload, runtime);
704
- const onboardingSession = prepared.verdict.decision === "ALLOW" && payload.authType === "oauth"
705
- ? createOnboardingSession(payload, runtime, {
706
- approvalGrantId: payload.approvalBindingId ?? randomUUID(),
707
- sessionId: "legacy-session",
708
- workflowHash: hashValue(payload.sourceObservationId ?? payload.requestId),
709
- connectorId: payload.toolId,
710
- scopes: payload.requestedScopes ?? [],
711
- sinkClass: "connector_oauth",
712
- capabilityIds: payload.capabilityId ? [payload.capabilityId] : [],
713
- targetOrigin: payload.callbackOrigin ??
714
- payload.oauthContext?.callbackOrigin ??
715
- payload.callbackUri ??
716
- payload.requestedRedirectUri ??
717
- "unknown",
718
- issuedAt: new Date().toISOString(),
719
- expiresAt: plusMinutes(new Date().toISOString(), 10),
720
- grantHash: "legacy"
721
- })
722
- : undefined;
723
- if (onboardingSession) {
724
- onboardingSessions.set(onboardingSession.sessionId, onboardingSession);
725
- }
726
- writeJson(response, 200, {
727
- verdict: prepared.verdict,
728
- verifiedRegistryEntry: prepared.verifiedRegistryEntry,
729
- workflowBinding: prepared.workflowBinding,
730
- onboardingSession,
731
- ...legacyResponseMeta("/v2/tool/prepare")
732
- });
733
- return;
734
- }
735
- if (request.url === "/v2/tool/callback/verify") {
736
- const payload = await readJson(request);
737
- const session = onboardingSessions.get(payload.sessionId);
738
- const result = verifyToolCallback(payload, session, runtime);
739
- if (session) {
740
- onboardingSessions.set(payload.sessionId, {
741
- ...session,
742
- status: result.verdict.decision === "ALLOW" ? "used" : session.status
743
- });
744
- }
745
- writeJson(response, 200, {
746
- ...result,
747
- ...legacyResponseMeta("/v2/tool/callback/verify")
748
- });
749
- return;
750
- }
751
- if (request.url === "/v2/artifact") {
752
- const payload = await readJson(request);
753
- writeJson(response, 200, {
754
- ...brokerArtifactV2(payload, runtime),
755
- ...legacyResponseMeta("/v2/artifact")
756
- });
757
- return;
758
- }
759
- if (request.url === "/v6/session/start" ||
760
- (unifiedV5InSecureV6 && request.url === "/v5/session/start")) {
761
- const payload = await readJson(request);
762
- const sessionState = createSessionState(payload, runtime);
763
- sessions.set(sessionState.session.sessionId, sessionState);
764
- appendReplayEvent(sessionState, {
765
- kind: "verdict",
766
- actor: "system",
767
- payload: {
768
- route: canonicalReplayRoute(request.url),
769
- sessionId: sessionState.session.sessionId,
770
- claimProfile: sessionState.session.claimProfile ?? "secure_v6"
771
- }
772
- });
773
- writeJson(response, 200, {
774
- session: sessionState.session
775
- });
776
- return;
777
- }
778
- if (request.url === "/v6/observe" || (unifiedV5InSecureV6 && request.url === "/v5/observe")) {
779
- const payload = await readJson(request);
780
- const sessionState = findSessionState(sessions, payload.sessionId);
781
- if (!sessionState) {
782
- writeJson(response, 404, { error: "unknown_session" });
783
- return;
784
- }
785
- const capture = {
786
- ...payload.capture,
787
- sessionId: payload.sessionId,
788
- taskId: sessionState.session.taskId
789
- };
790
- const observationResult = await parserIsolationService.compileObservation({
791
- capture,
792
- workflowHash: sessionState.session.workflowHash,
793
- compilerVersion: "v5"
794
- });
795
- const compiledObservation = observationResult.compiledObservation;
796
- const plannerView = observationResult.plannerView;
797
- const verifiedEntry = capture.surfaceType === "tool_manifest"
798
- ? resolveToolManifestRegistryEntry(runtime, capture)
799
- : undefined;
800
- const manifestHash = observationResult.toolManifestDigests?.manifestHash;
801
- const schemaHash = observationResult.toolManifestDigests?.schemaHash;
802
- retireObservationCapabilitiesV5(sessionState);
803
- const capabilities = mintCapabilitiesForObservationV5(sessionState.session, compiledObservation, plannerView, {
804
- verifiedRegistryEntry: verifiedEntry,
805
- registryEntryId: capture.surfaceType === "tool_manifest" ? verifiedEntry?.registryEntryId : undefined,
806
- connectorId: capture.surfaceType === "tool_manifest"
807
- ? verifiedEntry?.adapterId ?? capture.toolId
808
- : undefined,
809
- requestedScopes: capture.surfaceType === "tool_manifest" ? capture.requestedScopes : undefined,
810
- callbackUri: capture.surfaceType === "tool_manifest" ? capture.callbackUri : undefined,
811
- callbackOrigin: capture.surfaceType === "tool_manifest" ? capture.callbackOrigin : undefined,
812
- manifestAuthType: capture.surfaceType === "tool_manifest" ? capture.authType : undefined,
813
- manifestHash,
814
- schemaHash
815
- });
816
- sessionState.latestObservationV5 = compiledObservation;
817
- sessionState.observationsV5.set(compiledObservation.observationId, compiledObservation);
818
- for (const capability of capabilities) {
819
- sessionState.capabilitiesV5.set(capability.capabilityId, capability);
820
- }
821
- const observationVerdict = relabelClaimProfile(buildV6ObservationDecision(compiledObservation), "secure_v6");
822
- const artifactCapture = buildLegacyObservationCapture(capture);
823
- const artifactRefs = artifactCapture
824
- ? [
825
- buildV6ArtifactRef(brokerArtifact(artifactCapture, runtime).artifact, compiledObservation.authorityEligible)
826
- ]
827
- : [];
828
- const authorityCandidates = capabilities.map((capability) => ({
829
- authorityId: capability.capabilityId,
830
- authorityDigest: capability.capabilityDigest,
831
- semanticDigest: capability.semanticDigest,
832
- title: capability.title,
833
- kind: capability.kind,
834
- parameterSchema: capability.parameterSchema,
835
- expiresAt: capability.expiresAt
836
- }));
837
- const capabilityAliases = authorityCandidates.map((candidate) => ({
838
- capabilityId: candidate.authorityId,
839
- capabilityDigest: candidate.authorityDigest,
840
- semanticDigest: candidate.semanticDigest,
841
- title: candidate.title,
842
- kind: candidate.kind,
843
- parameterSchema: candidate.parameterSchema,
844
- expiresAt: candidate.expiresAt
845
- }));
846
- const replayEvent = appendReplayEvent(sessionState, {
847
- kind: "observation",
848
- actor: "sdk",
849
- payload: {
850
- route: canonicalReplayRoute(request.url),
851
- observationId: compiledObservation.observationId,
852
- surfaceType: compiledObservation.surfaceType,
853
- authorityEligible: compiledObservation.authorityEligible,
854
- authorityCandidateCount: capabilities.length,
855
- verdict: observationVerdict.decision
856
- }
857
- });
858
- writeJson(response, 200, {
859
- compiledObservation,
860
- plannerView,
861
- capabilities: capabilityAliases,
862
- authorityCandidates,
863
- artifactRefs,
864
- observationVerdict,
865
- replayEventId: replayEvent.eventId
866
- });
867
- return;
868
- }
869
- if (request.url === "/v6/action/evaluate" ||
870
- (unifiedV5InSecureV6 && request.url === "/v5/action/evaluate")) {
871
- const payload = await readJson(request);
872
- const authorityId = payload.authorityId ?? payload.capabilityId;
873
- const authorityDigest = payload.authorityDigest ?? payload.capabilityDigest;
874
- const sessionState = findSessionState(sessions, payload.sessionId);
875
- const capability = authorityId ? getCapabilityV5(sessionState, authorityId) : undefined;
876
- const authorityDecision = relabelClaimProfile(evaluateCapabilityUseV5({
877
- sessionId: payload.sessionId,
878
- capabilityId: authorityId ?? "",
879
- capabilityDigest: authorityDigest ?? "",
880
- parameters: payload.parameters
881
- }, sessionState?.session, capability, {
882
- alreadyUsed: authorityId
883
- ? (sessionState?.usedCapabilitiesV5.has(authorityId) ?? false)
884
- : false
885
- }), "secure_v6");
886
- const observationDecision = relabelClaimProfile(sessionState?.latestObservationV5
887
- ? buildV6ObservationDecision(sessionState.latestObservationV5)
888
- : {
889
- decision: "BLOCK",
890
- reasonCodes: ["OBSERVATION_REQUIRED"],
891
- riskScore: 0.99,
892
- safeConstraints: {},
893
- telemetryTags: ["v6_observation", "block"]
894
- }, "secure_v6");
895
- const effectDecision = observationDecision.decision === "ALLOW" &&
896
- authorityDecision.decision === "ALLOW"
897
- ? authorityDecision
898
- : relabelClaimProfile({
899
- decision: "BLOCK",
900
- reasonCodes: [...new Set([
901
- ...(observationDecision.decision === "ALLOW"
902
- ? []
903
- : ["OBSERVATION_NOT_EFFECT_ELIGIBLE"]),
904
- ...(authorityDecision.decision === "ALLOW"
905
- ? []
906
- : authorityDecision.reasonCodes)
907
- ])],
908
- riskScore: Math.max(observationDecision.riskScore, authorityDecision.riskScore),
909
- safeConstraints: {
910
- target_class: capability?.targetClass ?? "unknown"
911
- },
912
- telemetryTags: ["v6_action", "block"]
913
- }, "secure_v6");
914
- if (effectDecision.decision === "ALLOW" && sessionState && capability) {
915
- consumeCapabilityV5(sessionState, capability);
916
- sessionState.session = {
917
- ...sessionState.session,
918
- currentStep: sessionState.session.currentStep + 1
919
- };
920
- }
921
- appendReplayEvent(sessionState, {
922
- kind: "action",
923
- actor: "sdk",
924
- payload: {
925
- route: canonicalReplayRoute(request.url),
926
- authorityId: authorityId ?? null,
927
- observationDecision: observationDecision.decision,
928
- authorityDecision: authorityDecision.decision,
929
- effectDecision: effectDecision.decision
930
- }
931
- });
932
- writeJson(response, 200, {
933
- observationDecision,
934
- authorityDecision,
935
- effectDecision,
936
- verdict: effectDecision,
937
- executionPlan: buildV5ExecutionPlan(capability)
938
- });
939
- return;
940
- }
941
- if (request.url === "/v6/approval/issue" ||
942
- (unifiedV5InSecureV6 && request.url === "/v5/approval/issue")) {
943
- const payload = await readJson(request);
944
- const sessionState = findSessionState(sessions, payload.sessionId);
945
- const capability = sessionState?.capabilitiesV5.get(payload.capabilityId);
946
- if (sessionState?.usedCapabilitiesV5.has(payload.capabilityId)) {
947
- writeJson(response, 200, {
948
- verdict: relabelClaimProfile({
949
- decision: "BLOCK",
950
- reasonCodes: ["CAPABILITY_REPLAYED"],
951
- riskScore: 0.99,
952
- safeConstraints: {},
953
- telemetryTags: ["approval_v6_issue", "block"]
954
- }, "secure_v6")
955
- });
956
- return;
957
- }
958
- if (capability && payload.capabilityDigest !== capability.capabilityDigest) {
959
- writeJson(response, 200, {
960
- verdict: relabelClaimProfile({
961
- decision: "BLOCK",
962
- reasonCodes: ["CAPABILITY_DIGEST_MISMATCH"],
963
- riskScore: 0.99,
964
- safeConstraints: {},
965
- telemetryTags: ["approval_v6_issue", "block"]
966
- }, "secure_v6")
967
- });
968
- return;
969
- }
970
- const activeApproval = getActiveApprovalEnvelopeForCapability(sessionState, payload.capabilityId);
971
- if (activeApproval) {
972
- writeJson(response, 200, {
973
- verdict: relabelClaimProfile({
974
- decision: "BLOCK",
975
- reasonCodes: ["APPROVAL_ALREADY_ISSUED_FOR_CAPABILITY"],
976
- riskScore: 0.99,
977
- safeConstraints: {},
978
- telemetryTags: ["approval_v6_issue", "block"]
979
- }, "secure_v6"),
980
- approvalEnvelope: activeApproval
981
- });
982
- return;
983
- }
984
- const brokerPayload = sessionState && capability
985
- ? createApprovalIntentPayloadV5({
986
- sessionId: sessionState.session.sessionId,
987
- workflowHash: sessionState.session.workflowHash,
988
- capabilityId: capability.capabilityId,
989
- capabilityDigest: capability.capabilityDigest,
990
- expiresInSeconds: payload.expiresInSeconds
991
- })
992
- : "";
993
- const brokerSignatureVerified = verifyApprovalIntentSignatureV5(brokerPayload, payload.brokerSignature, runtime.approvalBrokerPublicKey);
994
- const issued = issueApprovalEnvelopeV5({
995
- session: sessionState?.session,
996
- capability,
997
- brokerSignature: payload.brokerSignature,
998
- brokerSignatureVerified,
999
- expiresInSeconds: payload.expiresInSeconds
1000
- });
1001
- if (issued.approvalEnvelope && sessionState) {
1002
- sessionState.approvalEnvelopesV5.set(issued.approvalEnvelope.approvalId, issued.approvalEnvelope);
1003
- }
1004
- appendReplayEvent(sessionState, {
1005
- kind: "tool",
1006
- actor: "sdk",
1007
- payload: {
1008
- route: canonicalReplayRoute(request.url),
1009
- capabilityId: payload.capabilityId,
1010
- verdict: issued.verdict.decision
1011
- }
1012
- });
1013
- writeJson(response, 200, {
1014
- ...issued,
1015
- verdict: relabelClaimProfile(issued.verdict, "secure_v6")
1016
- });
1017
- return;
1018
- }
1019
- if (request.url === "/v6/tool/prepare" ||
1020
- (unifiedV5InSecureV6 && request.url === "/v5/tool/prepare")) {
1021
- const payload = await readJson(request);
1022
- const sessionState = findSessionState(sessions, payload.sessionId);
1023
- const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(payload.approvalId);
1024
- const capability = approvalEnvelope
1025
- ? getCapabilityV5(sessionState, approvalEnvelope.capabilityId)
1026
- : undefined;
1027
- const verifiedEntry = approvalEnvelope
1028
- ? lookupVerifiedRegistryEntry(runtime, approvalEnvelope.connectorId, approvalEnvelope.registryEntryId)
1029
- : undefined;
1030
- const prepared = prepareToolOnboardingV5({
1031
- session: sessionState?.session,
1032
- capability,
1033
- approvalEnvelope,
1034
- verifiedRegistryEntry: verifiedEntry
1035
- });
1036
- if (sessionState && prepared.onboardingSession) {
1037
- const consumedAt = new Date().toISOString();
1038
- if (capability) {
1039
- consumeCapabilityV5(sessionState, capability, consumedAt);
1040
- }
1041
- if (approvalEnvelope) {
1042
- sessionState.approvalEnvelopesV5.set(payload.approvalId, {
1043
- ...approvalEnvelope,
1044
- consumedAt,
1045
- onboardingSessionId: prepared.onboardingSession.onboardingSessionId
1046
- });
1047
- }
1048
- sessionState.onboardingSessionsV5.set(prepared.onboardingSession.onboardingSessionId, prepared.onboardingSession);
1049
- sessionState.session = {
1050
- ...sessionState.session,
1051
- currentStep: sessionState.session.currentStep + 1
1052
- };
1053
- }
1054
- appendReplayEvent(sessionState, {
1055
- kind: "tool",
1056
- actor: "sdk",
1057
- payload: {
1058
- route: canonicalReplayRoute(request.url),
1059
- approvalId: payload.approvalId,
1060
- verdict: prepared.verdict.decision
1061
- }
1062
- });
1063
- writeJson(response, 200, {
1064
- verdict: relabelClaimProfile(prepared.verdict, "secure_v6"),
1065
- approvalEnvelope: sessionState?.approvalEnvelopesV5.get(payload.approvalId) ?? approvalEnvelope,
1066
- verifiedRegistryEntry: verifiedEntry,
1067
- onboardingSession: prepared.onboardingSession
1068
- });
1069
- return;
1070
- }
1071
- if (request.url === "/v6/tool/callback/verify" ||
1072
- (unifiedV5InSecureV6 && request.url === "/v5/tool/callback/verify")) {
1073
- const payload = await readJson(request);
1074
- const sessionState = findSessionState(sessions, payload.sessionId);
1075
- const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(payload.approvalId);
1076
- const capability = approvalEnvelope
1077
- ? getCapabilityV5(sessionState, approvalEnvelope.capabilityId)
1078
- : undefined;
1079
- const onboardingSession = sessionState?.onboardingSessionsV5.get(payload.onboardingSessionId);
1080
- const verifiedEntry = approvalEnvelope
1081
- ? lookupVerifiedRegistryEntry(runtime, approvalEnvelope.connectorId, approvalEnvelope.registryEntryId)
1082
- : undefined;
1083
- const verified = verifyToolCallbackV5({
1084
- session: sessionState?.session,
1085
- capability,
1086
- approvalEnvelope,
1087
- onboardingSession,
1088
- verifiedRegistryEntry: verifiedEntry,
1089
- request: payload.request
1090
- });
1091
- if (sessionState && onboardingSession && verified.verdict.decision === "ALLOW") {
1092
- sessionState.onboardingSessionsV5.set(payload.onboardingSessionId, {
1093
- ...onboardingSession,
1094
- status: "used"
1095
- });
1096
- }
1097
- if (sessionState && verified.connectorHandle) {
1098
- sessionState.connectorHandlesV5.set(verified.connectorHandle.handleId, verified.connectorHandle);
1099
- }
1100
- appendReplayEvent(sessionState, {
1101
- kind: "tool",
1102
- actor: "sdk",
1103
- payload: {
1104
- route: canonicalReplayRoute(request.url),
1105
- onboardingSessionId: payload.onboardingSessionId,
1106
- verdict: verified.verdict.decision
1107
- }
1108
- });
1109
- writeJson(response, 200, {
1110
- ...verified,
1111
- verdict: relabelClaimProfile(verified.verdict, "secure_v6")
1112
- });
1113
- return;
1114
- }
1115
- if (request.url === "/v6/artifact/ingest") {
1116
- const payload = await readJson(request);
1117
- const sessionState = findSessionState(sessions, payload.sessionId);
1118
- if (!sessionState) {
1119
- writeJson(response, 404, { error: "unknown_session" });
1120
- return;
1121
- }
1122
- const capture = {
1123
- ...payload.capture,
1124
- sessionId: payload.sessionId,
1125
- taskId: sessionState.session.taskId
1126
- };
1127
- const observationResult = await parserIsolationService.compileObservation({
1128
- capture,
1129
- workflowHash: sessionState.session.workflowHash,
1130
- compilerVersion: "v5"
1131
- });
1132
- const compiledObservation = observationResult.compiledObservation;
1133
- const plannerView = observationResult.plannerView;
1134
- const artifactCapture = buildLegacyObservationCapture(capture);
1135
- const brokeredArtifact = artifactCapture
1136
- ? brokerArtifact(artifactCapture, runtime)
1137
- : brokerArtifact({
1138
- mimeType: "application/octet-stream",
1139
- surfaceKind: "document",
1140
- sourceOrigin: capture.url,
1141
- viewerOrigin: capture.frameUrl ?? capture.url,
1142
- extractionMethod: "download",
1143
- trustSignals: capture.trustSignals
1144
- }, runtime);
1145
- const authorityEligible = compiledObservation.parseStatus === "compiled" &&
1146
- compiledObservation.authorityEligible &&
1147
- !brokeredArtifact.artifact.mismatchSignals.length &&
1148
- !brokeredArtifact.artifact.metadataSignals.length;
1149
- const artifactVerdict = relabelClaimProfile(authorityEligible
1150
- ? {
1151
- decision: "ALLOW",
1152
- reasonCodes: [],
1153
- riskScore: Math.max(compiledObservation.riskScore, brokeredArtifact.verdict.riskScore),
1154
- safeConstraints: {
1155
- authority_eligible: true,
1156
- handoff_mode: "artifact_reference"
1157
- },
1158
- telemetryTags: ["artifact_v6", "allow"]
1159
- }
1160
- : {
1161
- decision: brokeredArtifact.verdict.decision === "QUARANTINE_ARTIFACT" ||
1162
- brokeredArtifact.artifact.mismatchSignals.length > 0 ||
1163
- brokeredArtifact.artifact.metadataSignals.length > 0
1164
- ? "QUARANTINE_ARTIFACT"
1165
- : "BLOCK",
1166
- reasonCodes: [...new Set([
1167
- ...(compiledObservation.parseStatus === "compiled"
1168
- ? ["ARTIFACT_NOT_AUTHORITY_ELIGIBLE"]
1169
- : [
1170
- compiledObservation.parseStatus === "partial"
1171
- ? "PARSE_STATUS_PARTIAL"
1172
- : "PARSE_STATUS_UNSUPPORTED"
1173
- ]),
1174
- ...brokeredArtifact.verdict.reasonCodes
1175
- ])],
1176
- riskScore: Math.max(0.95, brokeredArtifact.verdict.riskScore),
1177
- safeConstraints: {
1178
- authority_eligible: false,
1179
- handoff_mode: "artifact_reference"
1180
- },
1181
- telemetryTags: ["artifact_v6", "quarantine"]
1182
- }, "secure_v6");
1183
- sessionState.latestObservationV5 = compiledObservation;
1184
- sessionState.observationsV5.set(compiledObservation.observationId, compiledObservation);
1185
- const replayEvent = appendReplayEvent(sessionState, {
1186
- kind: "artifact",
1187
- actor: "sdk",
1188
- payload: {
1189
- route: canonicalReplayRoute(request.url),
1190
- artifactId: brokeredArtifact.artifact.artifactId,
1191
- surfaceKind: brokeredArtifact.artifact.surfaceKind,
1192
- verdict: artifactVerdict.decision
1193
- }
1194
- });
1195
- writeJson(response, 200, {
1196
- compiledObservation,
1197
- plannerView,
1198
- artifactRef: buildV6ArtifactRef(brokeredArtifact.artifact, authorityEligible),
1199
- mismatchSignals: brokeredArtifact.artifact.mismatchSignals,
1200
- artifactVerdict,
1201
- replayEventId: replayEvent.eventId
1202
- });
1203
- return;
1204
- }
1205
- if (request.url === "/v6/memory/stage" ||
1206
- (unifiedV5InSecureV6 && request.url === "/v5/memory/stage")) {
1207
- const payload = await readJson(request);
1208
- const sessionState = findSessionState(sessions, payload.sessionId);
1209
- const result = stageMemoryRecordV6(payload, sessionState?.session);
1210
- let promotionTicket;
1211
- if (sessionState && result.record) {
1212
- sessionState.memoryRecords.set(result.record.recordId, result.record);
1213
- sessionState.memorySourceClassesV6.set(result.record.recordId, payload.sourceClass);
1214
- if (result.record.tier === "candidate_durable") {
1215
- const capability = mintMemoryPromotionCapabilityV5(sessionState.session, {
1216
- recordId: result.record.recordId,
1217
- sourceDigest: result.record.sourceDigest,
1218
- sourceObservationId: result.record.sourceObservationId,
1219
- key: result.record.key,
1220
- valueDigest: result.record.sourceDigest ?? hashValue(result.record.value)
1221
- });
1222
- sessionState.capabilitiesV5.set(capability.capabilityId, capability);
1223
- promotionTicket = {
1224
- ticketId: capability.capabilityId,
1225
- ticketDigest: capability.capabilityDigest,
1226
- semanticDigest: capability.semanticDigest,
1227
- recordId: result.record.recordId,
1228
- sourceClass: payload.sourceClass,
1229
- expiresAt: capability.expiresAt
1230
- };
1231
- }
1232
- }
1233
- appendReplayEvent(sessionState, {
1234
- kind: "memory",
1235
- actor: "sdk",
1236
- payload: {
1237
- route: canonicalReplayRoute(request.url),
1238
- recordId: result.record?.recordId ?? null,
1239
- sourceClass: payload.sourceClass,
1240
- verdict: result.verdict.decision
1241
- }
1242
- });
1243
- writeJson(response, 200, {
1244
- ...result,
1245
- verdict: relabelClaimProfile(result.verdict, "secure_v6"),
1246
- promotionTicket
1247
- });
1248
- return;
1249
- }
1250
- if (request.url === "/v6/memory/promote" ||
1251
- (unifiedV5InSecureV6 && request.url === "/v5/memory/promote")) {
1252
- const payload = await readJson(request);
1253
- const normalizedPayload = {
1254
- ...payload,
1255
- ticketId: payload.ticketId ?? payload.capabilityId ?? "",
1256
- ticketDigest: payload.ticketDigest ?? payload.capabilityDigest ?? ""
1257
- };
1258
- const sessionState = findSessionState(sessions, normalizedPayload.sessionId);
1259
- const record = sessionState?.memoryRecords.get(normalizedPayload.recordId);
1260
- const capability = sessionState?.capabilitiesV5.get(normalizedPayload.ticketId);
1261
- const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(normalizedPayload.approvalId);
1262
- const snapshotState = sessionState && record ? buildMemorySnapshotState(sessionState, record) : undefined;
1263
- const result = promoteMemoryRecordV6(normalizedPayload, sessionState?.session, record, capability, approvalEnvelope, {
1264
- sourceClass: record ? sessionState?.memorySourceClassesV6.get(record.recordId) : undefined,
1265
- priorTrustedRecord: snapshotState?.snapshotRecord
1266
- });
1267
- if (sessionState && result.promotedRecord) {
1268
- if (snapshotState && result.promotedRecord.snapshotId) {
1269
- sessionState.memorySnapshots.set(result.promotedRecord.snapshotId, {
1270
- ...snapshotState,
1271
- snapshotId: result.promotedRecord.snapshotId
1272
- });
1273
- }
1274
- sessionState.memoryRecords.set(result.promotedRecord.recordId, result.promotedRecord);
1275
- if (capability) {
1276
- consumeCapabilityV5(sessionState, capability);
1277
- }
1278
- if (approvalEnvelope) {
1279
- sessionState.approvalEnvelopesV5.set(payload.approvalId, {
1280
- ...approvalEnvelope,
1281
- consumedAt: new Date().toISOString()
1282
- });
1283
- }
1284
- sessionState.session = {
1285
- ...sessionState.session,
1286
- currentStep: sessionState.session.currentStep + 1
1287
- };
1288
- }
1289
- appendReplayEvent(sessionState, {
1290
- kind: "memory",
1291
- actor: "sdk",
1292
- payload: {
1293
- route: canonicalReplayRoute(request.url),
1294
- recordId: normalizedPayload.recordId,
1295
- verdict: result.verdict.decision
1296
- }
1297
- });
1298
- writeJson(response, 200, {
1299
- ...result,
1300
- verdict: relabelClaimProfile(result.verdict, "secure_v6"),
1301
- approvalEnvelope: normalizedPayload.approvalId && sessionState
1302
- ? sessionState.approvalEnvelopesV5.get(normalizedPayload.approvalId)
1303
- : approvalEnvelope
1304
- });
1305
- return;
1306
- }
1307
- if (request.url === "/v6/memory/rollback" ||
1308
- (unifiedV5InSecureV6 && request.url === "/v5/memory/rollback")) {
1309
- const payload = await readJson(request);
1310
- const sessionState = findSessionState(sessions, payload.sessionId);
1311
- const record = sessionState?.memoryRecords.get(payload.recordId);
1312
- const snapshotState = sessionState?.memorySnapshots.get(payload.snapshotId);
1313
- const result = rollbackMemoryRecordV5(payload, sessionState?.session, record, {
1314
- snapshotRecord: snapshotState?.snapshotRecord,
1315
- baselineAbsent: snapshotState?.baselineAbsent
1316
- });
1317
- if (sessionState && result.verdict.decision === "ALLOW") {
1318
- if (result.restoredRecord) {
1319
- sessionState.memoryRecords.set(result.restoredRecord.recordId, result.restoredRecord);
1320
- }
1321
- else {
1322
- sessionState.memoryRecords.delete(payload.recordId);
1323
- }
1324
- }
1325
- appendReplayEvent(sessionState, {
1326
- kind: "memory",
1327
- actor: "sdk",
1328
- payload: {
1329
- route: canonicalReplayRoute(request.url),
1330
- recordId: payload.recordId,
1331
- snapshotId: payload.snapshotId,
1332
- verdict: result.verdict.decision
1333
- }
1334
- });
1335
- writeJson(response, 200, {
1336
- ...result,
1337
- verdict: relabelClaimProfile(result.verdict, "secure_v6"),
1338
- rollbackEvent: result.verdict.decision === "ALLOW"
335
+ },
336
+ parserIsolation: {
337
+ configuredMode: runtime.parserIsolationMode,
338
+ lastCheckedAt: parserProbeSnapshot.lastCheckedAt,
339
+ ...parserProbeSnapshot.probe
340
+ },
341
+ verifiedRegistry: runtime.verifiedRegistry
1339
342
  ? {
1340
- recordId: payload.recordId,
1341
- snapshotId: payload.snapshotId,
1342
- appliedAt: new Date().toISOString()
343
+ bundleId: runtime.verifiedRegistry.bundleId,
344
+ version: runtime.verifiedRegistry.version,
345
+ signer: runtime.verifiedRegistry.signer,
346
+ signatureVerified: runtime.verifiedRegistry.signatureVerified,
347
+ apiProviderCount: runtime.verifiedRegistry.apiProviders?.length ?? 0,
348
+ extractorProfileCount: runtime.verifiedRegistry.extractorProfiles?.length ?? 0
1343
349
  }
1344
- : undefined
350
+ : undefined,
351
+ policyLayers: runtime.policy.layerProvenance,
352
+ captureAttestation: {
353
+ htmlDom: true,
354
+ required: true
355
+ },
356
+ modelGuard: {
357
+ configured: modelGuardHealthSnapshot.configured,
358
+ ready: modelGuardHealthSnapshot.ready,
359
+ runtimeMode: modelGuardHealthSnapshot.runtimeMode,
360
+ enforcementMode: modelGuardHealthSnapshot.enforcementMode,
361
+ bundleVersion: modelGuardHealthSnapshot.bundleVersion,
362
+ featureSchemaVersion: modelGuardHealthSnapshot.featureSchemaVersion,
363
+ bundleDigest: modelGuardHealthSnapshot.bundleDigest,
364
+ componentDigests: modelGuardHealthSnapshot.componentDigests,
365
+ validationError: modelGuardHealthSnapshot.validationError
366
+ }
1345
367
  });
1346
368
  return;
1347
369
  }
1348
- if (request.url === "/v6/replay/bundle" ||
1349
- (unifiedV5InSecureV6 && request.url === "/v5/replay/bundle")) {
1350
- const payload = await readJson(request);
1351
- const sessionState = findSessionState(sessions, payload.sessionId);
1352
- if (!sessionState) {
1353
- writeJson(response, 404, { error: "unknown_session" });
1354
- return;
1355
- }
1356
- writeJson(response, 200, buildReplayBundle(sessionState.replayEvents, runtime));
370
+ if (isRetiredRoute(requestUrl)) {
371
+ writeJson(response, 410, retiredRouteResponse(requestUrl));
372
+ return;
373
+ }
374
+ if (request.method !== "POST" || !requestUrl.startsWith("/v6/")) {
375
+ writeJson(response, 404, { error: "not_found" });
1357
376
  return;
1358
377
  }
1359
- if (request.url === "/v5/session/start") {
378
+ if (requestUrl === "/v6/session/start") {
1360
379
  const payload = await readJson(request);
1361
- const sessionState = createSessionState(payload, runtime);
380
+ const sessionState = createSessionState(payload, runtime, secureReady);
1362
381
  sessions.set(sessionState.session.sessionId, sessionState);
1363
382
  appendReplayEvent(sessionState, {
1364
- kind: "verdict",
1365
- actor: "system",
383
+ kind: "observation",
384
+ actor: "sdk",
1366
385
  payload: {
1367
- route: request.url,
386
+ route: requestUrl,
1368
387
  sessionId: sessionState.session.sessionId,
1369
- claimProfile: sessionState.session.claimProfile ?? "secure_v5"
388
+ taskId: sessionState.session.taskId
1370
389
  }
1371
390
  });
1372
391
  writeJson(response, 200, {
@@ -1374,7 +393,7 @@ export async function createSafeBrowseServer(options = {}) {
1374
393
  });
1375
394
  return;
1376
395
  }
1377
- if (request.url === "/v5/observe") {
396
+ if (requestUrl === "/v6/observe") {
1378
397
  const payload = await readJson(request);
1379
398
  const sessionState = findSessionState(sessions, payload.sessionId);
1380
399
  if (!sessionState) {
@@ -1386,141 +405,172 @@ export async function createSafeBrowseServer(options = {}) {
1386
405
  sessionId: payload.sessionId,
1387
406
  taskId: sessionState.session.taskId
1388
407
  };
1389
- const observationResult = await parserIsolationService.compileObservation({
408
+ const parsed = await parserIsolationService.compileObservation({
1390
409
  capture,
1391
410
  workflowHash: sessionState.session.workflowHash,
1392
- compilerVersion: "v5"
1393
- });
1394
- const compiledObservation = observationResult.compiledObservation;
1395
- const plannerView = observationResult.plannerView;
1396
- const mediated = applyV5ObservationMediation(compiledObservation, plannerView);
1397
- const verifiedEntry = capture.surfaceType === "tool_manifest"
1398
- ? resolveToolManifestRegistryEntry(runtime, capture)
411
+ runtime,
412
+ compilerVersion: "v6"
413
+ });
414
+ let compiledObservation = parsed.compiledObservation;
415
+ let plannerView = parsed.plannerView;
416
+ let mediated = applyV6ObservationMediation(compiledObservation, plannerView);
417
+ const verifiedRegistryEntry = capture.surfaceType === "tool_manifest"
418
+ ? lookupVerifiedRegistryEntry(runtime, capture.toolId, capture.toolId)
419
+ : undefined;
420
+ const verifiedApiProviderEntry = "providerId" in capture && capture.providerId
421
+ ? lookupVerifiedApiProviderEntry(runtime, capture.providerId)
1399
422
  : undefined;
1400
- const manifestHash = observationResult.toolManifestDigests?.manifestHash;
1401
- const schemaHash = observationResult.toolManifestDigests?.schemaHash;
1402
- retireObservationCapabilitiesV5(sessionState);
1403
- const capabilities = mediated.failClosed
423
+ const manifestHash = capture.surfaceType === "tool_manifest"
424
+ ? parsed.toolManifestDigests?.manifestHash ??
425
+ computeToolManifestHash({
426
+ toolId: capture.toolId,
427
+ description: capture.description,
428
+ authType: capture.authType,
429
+ requestedScopes: capture.requestedScopes,
430
+ callbackUri: capture.callbackUri
431
+ })
432
+ : undefined;
433
+ const schemaHash = capture.surfaceType === "tool_manifest"
434
+ ? parsed.toolManifestDigests?.schemaHash ??
435
+ computeToolSchemaHash(capture.schemaDescriptions)
436
+ : undefined;
437
+ if (modelGuardClient.configured &&
438
+ modelGuardClient.enforcementMode !== "off" &&
439
+ mediated.verdict.decision === "ALLOW") {
440
+ try {
441
+ const scored = await modelGuardClient.scoreObservation(buildModelGuardObservationRequest(sessionState.session, compiledObservation, plannerView));
442
+ if (modelGuardClient.enforcementMode === "shadow") {
443
+ compiledObservation = {
444
+ ...compiledObservation,
445
+ modelAssessment: scored.assessment
446
+ };
447
+ }
448
+ else {
449
+ const applied = applyModelGuardAssessment(compiledObservation, plannerView, mediated.verdict, scored.assessment);
450
+ compiledObservation = applied.compiledObservation;
451
+ plannerView = applied.plannerView;
452
+ mediated = {
453
+ plannerView,
454
+ verdict: applied.verdict,
455
+ failClosed: false
456
+ };
457
+ }
458
+ }
459
+ catch {
460
+ if (modelGuardClient.enforcementMode === "tighten") {
461
+ compiledObservation = {
462
+ ...compiledObservation,
463
+ authorityEligible: false
464
+ };
465
+ plannerView = {
466
+ ...plannerView,
467
+ visibleExcerpt: "",
468
+ riskMarkers: [...new Set([...plannerView.riskMarkers, "model_guard_unavailable"])]
469
+ };
470
+ mediated = {
471
+ plannerView,
472
+ verdict: {
473
+ decision: "REPLAN_READ_ONLY",
474
+ reasonCodes: ["MODEL_GUARD_UNAVAILABLE"],
475
+ riskScore: Math.max(0.6, compiledObservation.riskScore),
476
+ safeConstraints: {
477
+ claim_profile: "secure_v6",
478
+ authority_eligible: false
479
+ },
480
+ telemetryTags: ["v6_observation", "model_guard_unavailable"]
481
+ },
482
+ failClosed: false
483
+ };
484
+ }
485
+ }
486
+ }
487
+ const authorities = mediated.verdict.decision === "BLOCK"
1404
488
  ? []
1405
- : mintCapabilitiesForObservationV5(sessionState.session, compiledObservation, mediated.plannerView, {
1406
- verifiedRegistryEntry: verifiedEntry,
1407
- registryEntryId: capture.surfaceType === "tool_manifest" ? verifiedEntry?.registryEntryId : undefined,
1408
- connectorId: capture.surfaceType === "tool_manifest" ? verifiedEntry?.adapterId ?? capture.toolId : undefined,
1409
- requestedScopes: capture.surfaceType === "tool_manifest" ? capture.requestedScopes : undefined,
1410
- callbackUri: capture.surfaceType === "tool_manifest" ? capture.callbackUri : undefined,
1411
- callbackOrigin: capture.surfaceType === "tool_manifest" ? capture.callbackOrigin : undefined,
1412
- manifestAuthType: capture.surfaceType === "tool_manifest" ? capture.authType : undefined,
1413
- manifestHash,
1414
- schemaHash
489
+ : mintCapabilitiesForObservationV6(sessionState.session, compiledObservation, plannerView, {
490
+ policy: runtime.policy,
491
+ ...(capture.surfaceType === "tool_manifest" && verifiedRegistryEntry
492
+ ? {
493
+ verifiedRegistryEntry,
494
+ registryEntryId: capture.toolId,
495
+ connectorId: capture.toolId,
496
+ requestedScopes: capture.requestedScopes,
497
+ callbackUri: capture.callbackUri,
498
+ callbackOrigin: capture.callbackOrigin,
499
+ manifestAuthType: capture.authType,
500
+ manifestHash,
501
+ schemaHash
502
+ }
503
+ : verifiedApiProviderEntry
504
+ ? {
505
+ verifiedApiProviderEntry
506
+ }
507
+ : {})
1415
508
  });
1416
- sessionState.latestObservationV5 = compiledObservation;
1417
- sessionState.observationsV5.set(compiledObservation.observationId, compiledObservation);
1418
- for (const capability of capabilities) {
1419
- sessionState.capabilitiesV5.set(capability.capabilityId, capability);
1420
- }
1421
- const artifactCapture = buildLegacyObservationCapture(capture);
1422
- const artifactRefs = artifactCapture
1423
- ? [
1424
- buildV5ArtifactRef(brokerArtifact(artifactCapture, runtime).artifact, compiledObservation.authorityEligible)
1425
- ]
1426
- : [];
1427
- const observationVerdict = relabelClaimProfile(buildV5ObservationDecision(compiledObservation), "secure_v5");
1428
- const replayEvent = appendReplayEvent(sessionState, {
509
+ const tightenedAuthorities = tightenAuthoritiesWithModelGuard(authorities, modelGuardClient.enforcementMode === "tighten"
510
+ ? compiledObservation.modelAssessment
511
+ : undefined);
512
+ for (const authority of tightenedAuthorities) {
513
+ sessionState.authorities.set(authority.capabilityId, authority);
514
+ }
515
+ sessionState.latestObservation = compiledObservation;
516
+ sessionState.latestObservationVerdict = mediated.verdict;
517
+ sessionState.observations.set(compiledObservation.observationId, compiledObservation);
518
+ const replayEventId = appendReplayEvent(sessionState, {
1429
519
  kind: "observation",
1430
520
  actor: "sdk",
1431
521
  payload: {
1432
- route: request.url,
522
+ route: requestUrl,
1433
523
  observationId: compiledObservation.observationId,
1434
- surfaceType: compiledObservation.surfaceType,
1435
- authorityEligible: compiledObservation.authorityEligible,
1436
- authorityCandidateCount: capabilities.length,
1437
- verdict: observationVerdict.decision
524
+ parseStatus: compiledObservation.parseStatus,
525
+ authorityCount: tightenedAuthorities.length,
526
+ decision: mediated.verdict.decision,
527
+ surfaceType: capture.surfaceType,
528
+ providerId: "providerId" in capture ? capture.providerId ?? null : null,
529
+ extractorIds: "extractionAttestation" in capture && capture.extractionAttestation
530
+ ? [capture.extractionAttestation.extractorId]
531
+ : "extractionAttestations" in capture
532
+ ? capture.extractionAttestations?.map((entry) => entry.extractorId) ?? []
533
+ : [],
534
+ modelAssessment: compiledObservation.modelAssessment
535
+ ? {
536
+ bundleVersion: compiledObservation.modelAssessment.bundleVersion,
537
+ calibratedDecisionLabel: compiledObservation.modelAssessment.calibratedDecisionLabel,
538
+ coarseReasonCodes: compiledObservation.modelAssessment.coarseReasonCodes,
539
+ evidenceChunkIds: compiledObservation.modelAssessment.evidenceChunkIds
540
+ }
541
+ : null
1438
542
  }
1439
543
  });
1440
544
  writeJson(response, 200, {
1441
545
  compiledObservation,
1442
- plannerView: mediated.plannerView,
1443
- capabilities: capabilities.map(summarizeCapabilityV5),
1444
- authorityCandidates: capabilities.map(summarizeAuthorityCandidate),
1445
- artifactRefs,
1446
- observationVerdict,
1447
- replayEventId: replayEvent.eventId
1448
- });
1449
- return;
1450
- }
1451
- if (request.url === "/v5/action/evaluate") {
1452
- const payload = await readJson(request);
1453
- const authorityId = payload.authorityId ?? payload.capabilityId;
1454
- const authorityDigest = payload.authorityDigest ?? payload.capabilityDigest;
1455
- const sessionState = findSessionState(sessions, payload.sessionId);
1456
- const capability = authorityId ? getCapabilityV5(sessionState, authorityId) : undefined;
1457
- const authorityDecision = relabelClaimProfile(evaluateCapabilityUseV5({
1458
- sessionId: payload.sessionId,
1459
- capabilityId: authorityId ?? "",
1460
- capabilityDigest: authorityDigest ?? "",
1461
- parameters: payload.parameters
1462
- }, sessionState?.session, capability, {
1463
- alreadyUsed: authorityId
1464
- ? (sessionState?.usedCapabilitiesV5.has(authorityId) ?? false)
1465
- : false
1466
- }), "secure_v5");
1467
- const observationDecision = buildObservationDecisionForClaimProfile(sessionState?.latestObservationV5, "secure_v5");
1468
- const effectDecision = observationDecision.decision === "ALLOW" && authorityDecision.decision === "ALLOW"
1469
- ? authorityDecision
1470
- : relabelClaimProfile({
1471
- decision: "BLOCK",
1472
- reasonCodes: [
1473
- ...new Set([
1474
- ...(observationDecision.decision === "ALLOW"
1475
- ? []
1476
- : ["OBSERVATION_NOT_EFFECT_ELIGIBLE"]),
1477
- ...(authorityDecision.decision === "ALLOW"
1478
- ? []
1479
- : authorityDecision.reasonCodes)
1480
- ])
1481
- ],
1482
- riskScore: Math.max(observationDecision.riskScore, authorityDecision.riskScore),
1483
- safeConstraints: {
1484
- target_class: capability?.targetClass ?? "unknown"
1485
- },
1486
- telemetryTags: ["v5_action", "block"]
1487
- }, "secure_v5");
1488
- if (effectDecision.decision === "ALLOW" && sessionState && capability) {
1489
- consumeCapabilityV5(sessionState, capability);
1490
- sessionState.session = {
1491
- ...sessionState.session,
1492
- currentStep: sessionState.session.currentStep + 1
1493
- };
1494
- }
1495
- appendReplayEvent(sessionState, {
1496
- kind: "action",
1497
- actor: "sdk",
1498
- payload: {
1499
- route: request.url,
1500
- authorityId: authorityId ?? null,
1501
- observationDecision: observationDecision.decision,
1502
- authorityDecision: authorityDecision.decision,
1503
- effectDecision: effectDecision.decision
1504
- }
1505
- });
1506
- writeJson(response, 200, {
1507
- observationDecision,
1508
- authorityDecision,
1509
- effectDecision,
1510
- verdict: effectDecision,
1511
- executionPlan: buildV5ExecutionPlan(capability)
546
+ plannerView,
547
+ authorityCandidates: tightenedAuthorities.map(authorityCandidateFromDescriptor),
548
+ capabilities: tightenedAuthorities.map(authorityCandidateFromDescriptor),
549
+ artifactRefs: [],
550
+ observationVerdict: mediated.verdict,
551
+ replayEventId: replayEventId ?? randomUUID()
1512
552
  });
1513
553
  return;
1514
554
  }
1515
- if (request.url === "/v5/capability/use") {
555
+ if (requestUrl === "/v6/action/evaluate") {
1516
556
  const payload = await readJson(request);
1517
557
  const sessionState = findSessionState(sessions, payload.sessionId);
1518
- const capability = getCapabilityV5(sessionState, payload.capabilityId);
1519
- const verdict = evaluateCapabilityUseV5(payload, sessionState?.session, capability, {
1520
- alreadyUsed: sessionState?.usedCapabilitiesV5.has(payload.capabilityId) ?? false
558
+ const authority = sessionState?.authorities.get(payload.authorityId);
559
+ const approvalEnvelope = payload.approvalId && sessionState
560
+ ? sessionState.approvalEnvelopes.get(payload.approvalId)
561
+ : undefined;
562
+ const authorityDecision = evaluateCapabilityUseV6(payload, sessionState?.session, authority, {
563
+ alreadyUsed: sessionState?.usedAuthorities.has(payload.authorityId) ?? false,
564
+ approvalEnvelope
1521
565
  });
1522
- if (verdict.decision === "ALLOW" && sessionState && capability) {
1523
- consumeCapabilityV5(sessionState, capability);
566
+ if (sessionState && authority && authorityDecision.decision === "ALLOW") {
567
+ consumeAuthority(sessionState, authority);
568
+ if (approvalEnvelope) {
569
+ sessionState.approvalEnvelopes.set(approvalEnvelope.approvalId, {
570
+ ...approvalEnvelope,
571
+ consumedAt: new Date().toISOString()
572
+ });
573
+ }
1524
574
  sessionState.session = {
1525
575
  ...sessionState.session,
1526
576
  currentStep: sessionState.session.currentStep + 1
@@ -1530,131 +580,115 @@ export async function createSafeBrowseServer(options = {}) {
1530
580
  kind: "action",
1531
581
  actor: "sdk",
1532
582
  payload: {
1533
- route: request.url,
1534
- authorityId: payload.capabilityId,
1535
- observationDecision: buildObservationDecisionForClaimProfile(sessionState?.latestObservationV5, "secure_v5").decision,
1536
- authorityDecision: verdict.decision,
1537
- effectDecision: verdict.decision
583
+ route: requestUrl,
584
+ authorityId: payload.authorityId,
585
+ decision: authorityDecision.decision,
586
+ operationClass: authority?.operationClass ?? null,
587
+ providerId: authority?.providerId ?? null,
588
+ operationId: authority?.operationId ?? null
1538
589
  }
1539
590
  });
1540
591
  writeJson(response, 200, {
1541
- verdict: relabelClaimProfile(verdict, "secure_v5"),
1542
- executionPlan: buildV5ExecutionPlan(capability)
592
+ observationDecision: sessionState?.latestObservationVerdict ?? {
593
+ decision: "ALLOW",
594
+ reasonCodes: [],
595
+ riskScore: 0
596
+ },
597
+ authorityDecision,
598
+ effectDecision: authorityDecision,
599
+ executionPlan: authorityDecision.decision === "ALLOW" && authority
600
+ ? {
601
+ verb: authority.kind,
602
+ operationClass: authority.operationClass,
603
+ targetUrl: authority.targetUrl,
604
+ targetOrigin: authority.targetOrigin,
605
+ selector: authority.selector,
606
+ targetPathClass: authority.targetPathClass,
607
+ derivedSinkClass: authority.derivedSinkClass,
608
+ derivedSensitiveSink: authority.derivedSensitiveSink,
609
+ providerId: authority.providerId,
610
+ operationId: authority.operationId,
611
+ method: authority.method,
612
+ pathTemplate: authority.pathTemplate,
613
+ mailboxId: authority.mailboxId,
614
+ accountId: authority.accountId,
615
+ messageId: authority.messageId,
616
+ threadId: authority.threadId,
617
+ recipientSetHash: authority.recipientSetHash,
618
+ subjectHash: authority.subjectHash,
619
+ bodyDigest: authority.bodyDigest,
620
+ attachmentDigestSet: authority.attachmentDigestSet
621
+ }
622
+ : undefined
1543
623
  });
1544
624
  return;
1545
625
  }
1546
- if (request.url === "/v5/approval/issue") {
626
+ if (requestUrl === "/v6/approval/issue") {
1547
627
  const payload = await readJson(request);
1548
628
  const sessionState = findSessionState(sessions, payload.sessionId);
1549
- const capability = sessionState?.capabilitiesV5.get(payload.capabilityId);
1550
- if (sessionState?.usedCapabilitiesV5.has(payload.capabilityId)) {
1551
- writeJson(response, 200, {
1552
- verdict: {
1553
- decision: "BLOCK",
1554
- reasonCodes: ["CAPABILITY_REPLAYED"],
1555
- riskScore: 0.99,
1556
- safeConstraints: {
1557
- claim_profile: "secure_v5"
1558
- },
1559
- telemetryTags: ["approval_v5_issue", "block"]
1560
- }
1561
- });
1562
- return;
1563
- }
1564
- if (capability && payload.capabilityDigest !== capability.capabilityDigest) {
1565
- writeJson(response, 200, {
1566
- verdict: {
1567
- decision: "BLOCK",
1568
- reasonCodes: ["CAPABILITY_DIGEST_MISMATCH"],
1569
- riskScore: 0.99,
1570
- safeConstraints: {
1571
- claim_profile: "secure_v5"
1572
- },
1573
- telemetryTags: ["approval_v5_issue", "block"]
1574
- }
629
+ const authorityId = payload.authorityId ?? payload.capabilityId ?? "";
630
+ const authorityDigest = payload.authorityDigest ?? payload.capabilityDigest ?? "";
631
+ const authority = sessionState?.authorities.get(authorityId);
632
+ if (!sessionState || !authority) {
633
+ writeJson(response, 404, {
634
+ error: !sessionState ? "unknown_session" : "unknown_authority"
1575
635
  });
1576
636
  return;
1577
637
  }
1578
- const activeApproval = getActiveApprovalEnvelopeForCapability(sessionState, payload.capabilityId);
1579
- if (activeApproval) {
1580
- writeJson(response, 200, {
1581
- verdict: {
1582
- decision: "BLOCK",
1583
- reasonCodes: ["APPROVAL_ALREADY_ISSUED_FOR_CAPABILITY"],
1584
- riskScore: 0.99,
1585
- safeConstraints: {
1586
- claim_profile: "secure_v5"
1587
- },
1588
- telemetryTags: ["approval_v5_issue", "block"]
1589
- },
1590
- approvalEnvelope: activeApproval
1591
- });
1592
- return;
1593
- }
1594
- const brokerPayload = sessionState && capability
1595
- ? createApprovalIntentPayloadV5({
638
+ const signatureValid = authority.capabilityDigest === authorityDigest &&
639
+ verifyApprovalIntentSignatureV6(createApprovalIntentPayloadV6({
1596
640
  sessionId: sessionState.session.sessionId,
1597
641
  workflowHash: sessionState.session.workflowHash,
1598
- capabilityId: capability.capabilityId,
1599
- capabilityDigest: capability.capabilityDigest,
642
+ capabilityId: authority.capabilityId,
643
+ capabilityDigest: authority.capabilityDigest,
1600
644
  expiresInSeconds: payload.expiresInSeconds
1601
- })
1602
- : "";
1603
- const brokerSignatureVerified = verifyApprovalIntentSignatureV5(brokerPayload, payload.brokerSignature, runtime.approvalBrokerPublicKey);
1604
- const issued = issueApprovalEnvelopeV5({
1605
- session: sessionState?.session,
1606
- capability,
645
+ }), payload.brokerSignature, runtime.approvalBrokerPublicKey);
646
+ const issued = issueApprovalEnvelopeV6({
647
+ session: sessionState.session,
648
+ capability: authority,
1607
649
  brokerSignature: payload.brokerSignature,
1608
- brokerSignatureVerified,
650
+ brokerSignatureVerified: signatureValid,
1609
651
  expiresInSeconds: payload.expiresInSeconds
1610
652
  });
1611
- if (issued.approvalEnvelope && sessionState) {
1612
- sessionState.approvalEnvelopesV5.set(issued.approvalEnvelope.approvalId, issued.approvalEnvelope);
653
+ if (issued.approvalEnvelope) {
654
+ sessionState.approvalEnvelopes.set(issued.approvalEnvelope.approvalId, issued.approvalEnvelope);
1613
655
  }
1614
656
  appendReplayEvent(sessionState, {
1615
657
  kind: "tool",
1616
658
  actor: "sdk",
1617
659
  payload: {
1618
- route: request.url,
1619
- capabilityId: payload.capabilityId,
1620
- verdict: issued.verdict.decision
660
+ route: requestUrl,
661
+ authorityId,
662
+ decision: issued.verdict.decision
1621
663
  }
1622
664
  });
1623
- writeJson(response, 200, {
1624
- ...issued,
1625
- verdict: relabelClaimProfile(issued.verdict, "secure_v5")
1626
- });
665
+ writeJson(response, 200, issued);
1627
666
  return;
1628
667
  }
1629
- if (request.url === "/v5/tool/prepare") {
668
+ if (requestUrl === "/v6/tool/prepare") {
1630
669
  const payload = await readJson(request);
1631
670
  const sessionState = findSessionState(sessions, payload.sessionId);
1632
- const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(payload.approvalId);
1633
- const capability = approvalEnvelope
1634
- ? getCapabilityV5(sessionState, approvalEnvelope.capabilityId)
671
+ const approvalEnvelope = sessionState?.approvalEnvelopes.get(payload.approvalId);
672
+ const authority = approvalEnvelope && sessionState
673
+ ? sessionState.authorities.get(approvalEnvelope.capabilityId)
1635
674
  : undefined;
1636
- const verifiedEntry = approvalEnvelope
1637
- ? lookupVerifiedRegistryEntry(runtime, approvalEnvelope.connectorId, approvalEnvelope.registryEntryId)
675
+ const verifiedRegistryEntry = authority?.registryEntryId
676
+ ? lookupVerifiedRegistryEntry(runtime, authority.connectorId, authority.registryEntryId)
1638
677
  : undefined;
1639
- const prepared = prepareToolOnboardingV5({
678
+ const prepared = prepareToolOnboardingV6({
1640
679
  session: sessionState?.session,
1641
- capability,
680
+ capability: authority,
1642
681
  approvalEnvelope,
1643
- verifiedRegistryEntry: verifiedEntry
1644
- });
1645
- if (sessionState && prepared.onboardingSession) {
1646
- const consumedAt = new Date().toISOString();
1647
- if (capability) {
1648
- consumeCapabilityV5(sessionState, capability, consumedAt);
1649
- }
1650
- if (approvalEnvelope) {
1651
- sessionState.approvalEnvelopesV5.set(payload.approvalId, {
1652
- ...approvalEnvelope,
1653
- consumedAt,
1654
- onboardingSessionId: prepared.onboardingSession.onboardingSessionId
1655
- });
1656
- }
1657
- sessionState.onboardingSessionsV5.set(prepared.onboardingSession.onboardingSessionId, prepared.onboardingSession);
682
+ verifiedRegistryEntry
683
+ });
684
+ if (sessionState && approvalEnvelope && authority && prepared.onboardingSession) {
685
+ sessionState.onboardingSessions.set(prepared.onboardingSession.onboardingSessionId, prepared.onboardingSession);
686
+ sessionState.approvalEnvelopes.set(approvalEnvelope.approvalId, {
687
+ ...approvalEnvelope,
688
+ consumedAt: new Date().toISOString(),
689
+ onboardingSessionId: prepared.onboardingSession.onboardingSessionId
690
+ });
691
+ consumeAuthority(sessionState, authority);
1658
692
  sessionState.session = {
1659
693
  ...sessionState.session,
1660
694
  currentStep: sessionState.session.currentStep + 1
@@ -1664,63 +698,53 @@ export async function createSafeBrowseServer(options = {}) {
1664
698
  kind: "tool",
1665
699
  actor: "sdk",
1666
700
  payload: {
1667
- route: request.url,
701
+ route: requestUrl,
1668
702
  approvalId: payload.approvalId,
1669
- verdict: prepared.verdict.decision
703
+ decision: prepared.verdict.decision
1670
704
  }
1671
705
  });
1672
- writeJson(response, 200, {
1673
- verdict: relabelClaimProfile(prepared.verdict, "secure_v5"),
1674
- approvalEnvelope: sessionState?.approvalEnvelopesV5.get(payload.approvalId) ?? approvalEnvelope,
1675
- verifiedRegistryEntry: verifiedEntry,
1676
- onboardingSession: prepared.onboardingSession
1677
- });
706
+ writeJson(response, 200, prepared);
1678
707
  return;
1679
708
  }
1680
- if (request.url === "/v5/tool/callback/verify") {
709
+ if (requestUrl === "/v6/tool/callback/verify") {
1681
710
  const payload = await readJson(request);
1682
711
  const sessionState = findSessionState(sessions, payload.sessionId);
1683
- const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(payload.approvalId);
1684
- const capability = approvalEnvelope
1685
- ? getCapabilityV5(sessionState, approvalEnvelope.capabilityId)
712
+ const approvalEnvelope = sessionState?.approvalEnvelopes.get(payload.approvalId);
713
+ const onboardingSession = sessionState?.onboardingSessions.get(payload.onboardingSessionId);
714
+ const authority = approvalEnvelope && sessionState
715
+ ? sessionState.authorities.get(approvalEnvelope.capabilityId)
1686
716
  : undefined;
1687
- const onboardingSession = sessionState?.onboardingSessionsV5.get(payload.onboardingSessionId);
1688
- const verifiedEntry = approvalEnvelope
1689
- ? lookupVerifiedRegistryEntry(runtime, approvalEnvelope.connectorId, approvalEnvelope.registryEntryId)
717
+ const verifiedRegistryEntry = authority?.registryEntryId
718
+ ? lookupVerifiedRegistryEntry(runtime, authority.connectorId, authority.registryEntryId)
1690
719
  : undefined;
1691
- const verified = verifyToolCallbackV5({
720
+ const verified = verifyToolCallbackV6({
1692
721
  session: sessionState?.session,
1693
- capability,
722
+ capability: authority,
1694
723
  approvalEnvelope,
1695
724
  onboardingSession,
1696
- verifiedRegistryEntry: verifiedEntry,
725
+ verifiedRegistryEntry,
1697
726
  request: payload.request
1698
727
  });
1699
- if (sessionState && onboardingSession && verified.verdict.decision === "ALLOW") {
1700
- sessionState.onboardingSessionsV5.set(payload.onboardingSessionId, {
728
+ if (sessionState && onboardingSession && verified.connectorHandle) {
729
+ sessionState.onboardingSessions.set(onboardingSession.onboardingSessionId, {
1701
730
  ...onboardingSession,
1702
731
  status: "used"
1703
732
  });
1704
- }
1705
- if (sessionState && verified.connectorHandle) {
1706
- sessionState.connectorHandlesV5.set(verified.connectorHandle.handleId, verified.connectorHandle);
733
+ sessionState.connectorHandles.set(verified.connectorHandle.handleId, verified.connectorHandle);
1707
734
  }
1708
735
  appendReplayEvent(sessionState, {
1709
736
  kind: "tool",
1710
737
  actor: "sdk",
1711
738
  payload: {
1712
- route: request.url,
739
+ route: requestUrl,
1713
740
  onboardingSessionId: payload.onboardingSessionId,
1714
- verdict: verified.verdict.decision
741
+ decision: verified.verdict.decision
1715
742
  }
1716
743
  });
1717
- writeJson(response, 200, {
1718
- ...verified,
1719
- verdict: relabelClaimProfile(verified.verdict, "secure_v5")
1720
- });
744
+ writeJson(response, 200, verified);
1721
745
  return;
1722
746
  }
1723
- if (request.url === "/v5/artifact/ingest") {
747
+ if (requestUrl === "/v6/artifact/ingest") {
1724
748
  const payload = await readJson(request);
1725
749
  const sessionState = findSessionState(sessions, payload.sessionId);
1726
750
  if (!sessionState) {
@@ -1732,216 +756,125 @@ export async function createSafeBrowseServer(options = {}) {
1732
756
  sessionId: payload.sessionId,
1733
757
  taskId: sessionState.session.taskId
1734
758
  };
1735
- const observationResult = await parserIsolationService.compileObservation({
759
+ const parsed = await parserIsolationService.compileObservation({
1736
760
  capture,
1737
761
  workflowHash: sessionState.session.workflowHash,
1738
- compilerVersion: "v5"
1739
- });
1740
- const compiledObservation = observationResult.compiledObservation;
1741
- const plannerView = observationResult.plannerView;
1742
- const artifactCapture = buildLegacyObservationCapture(capture);
1743
- const brokeredArtifact = artifactCapture
1744
- ? brokerArtifact(artifactCapture, runtime)
1745
- : brokerArtifact({
1746
- mimeType: "application/octet-stream",
1747
- surfaceKind: "document",
1748
- sourceOrigin: capture.url,
1749
- viewerOrigin: capture.frameUrl ?? capture.url,
1750
- extractionMethod: "download",
1751
- trustSignals: capture.trustSignals
1752
- }, runtime);
1753
- const artifactAuthoritative = compiledObservation.parseStatus === "compiled" &&
1754
- compiledObservation.authorityEligible &&
1755
- !brokeredArtifact.artifact.mismatchSignals.length &&
1756
- !brokeredArtifact.artifact.metadataSignals.length;
1757
- const artifactVerdict = relabelClaimProfile(artifactAuthoritative
1758
- ? {
1759
- decision: "ALLOW",
1760
- reasonCodes: [],
1761
- riskScore: Math.max(compiledObservation.riskScore, brokeredArtifact.verdict.riskScore),
1762
- safeConstraints: {
1763
- authority_eligible: true,
1764
- handoff_mode: "artifact_reference"
1765
- },
1766
- telemetryTags: ["artifact_v5", "allow"]
1767
- }
1768
- : {
1769
- decision: brokeredArtifact.verdict.decision === "QUARANTINE_ARTIFACT" ||
1770
- brokeredArtifact.artifact.mismatchSignals.length > 0 ||
1771
- brokeredArtifact.artifact.metadataSignals.length > 0
1772
- ? "QUARANTINE_ARTIFACT"
1773
- : "BLOCK",
1774
- reasonCodes: [...new Set([
1775
- ...(compiledObservation.parseStatus === "compiled"
1776
- ? ["ARTIFACT_NOT_AUTHORITY_ELIGIBLE"]
1777
- : [
1778
- compiledObservation.parseStatus === "partial"
1779
- ? "PARSE_STATUS_PARTIAL"
1780
- : "PARSE_STATUS_UNSUPPORTED"
1781
- ]),
1782
- ...brokeredArtifact.verdict.reasonCodes
1783
- ])],
1784
- riskScore: Math.max(0.95, brokeredArtifact.verdict.riskScore),
1785
- safeConstraints: {
1786
- authority_eligible: false,
1787
- handoff_mode: "artifact_reference"
1788
- },
1789
- telemetryTags: ["artifact_v5", "quarantine"]
1790
- }, "secure_v5");
1791
- sessionState.latestObservationV5 = compiledObservation;
1792
- sessionState.observationsV5.set(compiledObservation.observationId, compiledObservation);
1793
- const replayEvent = appendReplayEvent(sessionState, {
762
+ runtime,
763
+ compilerVersion: "v6"
764
+ });
765
+ const mediated = applyV6ObservationMediation(parsed.compiledObservation, parsed.plannerView);
766
+ const artifactRef = buildArtifactRef(capture, parsed.compiledObservation);
767
+ sessionState.latestObservation = parsed.compiledObservation;
768
+ sessionState.latestObservationVerdict = mediated.verdict;
769
+ sessionState.observations.set(parsed.compiledObservation.observationId, parsed.compiledObservation);
770
+ const replayEventId = appendReplayEvent(sessionState, {
1794
771
  kind: "artifact",
1795
772
  actor: "sdk",
1796
773
  payload: {
1797
- route: request.url,
1798
- artifactId: brokeredArtifact?.artifact.artifactId ?? null,
1799
- surfaceKind: brokeredArtifact?.artifact.surfaceKind ?? capture.surfaceType,
1800
- verdict: artifactVerdict.decision
774
+ route: requestUrl,
775
+ observationId: parsed.compiledObservation.observationId,
776
+ decision: mediated.verdict.decision,
777
+ surfaceType: capture.surfaceType,
778
+ providerId: "providerId" in capture ? capture.providerId ?? null : null,
779
+ extractorIds: "extractionAttestation" in capture && capture.extractionAttestation
780
+ ? [capture.extractionAttestation.extractorId]
781
+ : []
1801
782
  }
1802
783
  });
1803
784
  writeJson(response, 200, {
1804
- compiledObservation,
1805
- plannerView,
1806
- artifactVerdict,
1807
- artifactRef: buildV5ArtifactRef(brokeredArtifact.artifact, artifactAuthoritative),
1808
- mismatchSignals: brokeredArtifact.artifact.mismatchSignals,
1809
- metadataSignals: brokeredArtifact.artifact.metadataSignals,
1810
- replayEventId: replayEvent.eventId
785
+ compiledObservation: parsed.compiledObservation,
786
+ plannerView: mediated.plannerView,
787
+ artifactRef,
788
+ mismatchSignals: artifactRef.mismatchSignals,
789
+ artifactVerdict: mediated.verdict,
790
+ replayEventId: replayEventId ?? randomUUID()
1811
791
  });
1812
792
  return;
1813
793
  }
1814
- if (request.url === "/v5/memory/stage") {
794
+ if (requestUrl === "/v6/artifact/extract") {
1815
795
  const payload = await readJson(request);
1816
796
  const sessionState = findSessionState(sessions, payload.sessionId);
1817
- const result = stageMemoryRecordV5(payload, sessionState?.session);
1818
- let promotionCapability;
1819
- let promotionTicket;
1820
- if (sessionState && result.record) {
1821
- sessionState.memoryRecords.set(result.record.recordId, result.record);
1822
- sessionState.memorySourceClassesV5.set(result.record.recordId, payload.sourceClass);
1823
- if (result.record.tier === "candidate_durable") {
1824
- promotionCapability = mintMemoryPromotionCapabilityV5(sessionState.session, {
1825
- recordId: result.record.recordId,
1826
- sourceDigest: result.record.sourceDigest,
1827
- sourceObservationId: result.record.sourceObservationId,
1828
- key: result.record.key,
1829
- valueDigest: result.record.sourceDigest ?? hashValue(result.record.value)
1830
- });
1831
- sessionState.capabilitiesV5.set(promotionCapability.capabilityId, promotionCapability);
1832
- promotionTicket = {
1833
- ticketId: promotionCapability.capabilityId,
1834
- ticketDigest: promotionCapability.capabilityDigest,
1835
- semanticDigest: promotionCapability.semanticDigest,
1836
- recordId: result.record.recordId,
1837
- sourceClass: payload.sourceClass,
1838
- expiresAt: promotionCapability.expiresAt
1839
- };
1840
- }
797
+ if (!sessionState) {
798
+ writeJson(response, 404, { error: "unknown_session" });
799
+ return;
1841
800
  }
1842
- appendReplayEvent(sessionState, {
1843
- kind: "memory",
801
+ const result = extractAttachmentGraphV6({
802
+ ...payload.capture,
803
+ sessionId: payload.sessionId,
804
+ taskId: sessionState.session.taskId
805
+ }, runtime);
806
+ const replayEventId = appendReplayEvent(sessionState, {
807
+ kind: "artifact",
1844
808
  actor: "sdk",
1845
809
  payload: {
1846
- route: request.url,
1847
- recordId: result.record?.recordId ?? null,
1848
- sourceClass: payload.sourceClass,
1849
- verdict: result.verdict.decision
810
+ route: requestUrl,
811
+ rootNodeCount: result.artifactGraph.rootNodeIds.length,
812
+ blockedChildren: result.blockedChildren.length,
813
+ unsupportedChildren: result.unsupportedChildren.length,
814
+ extractorIds: result.extractionAttestations.map((entry) => entry.extractorId),
815
+ decision: result.artifactVerdict.decision
1850
816
  }
1851
817
  });
1852
818
  writeJson(response, 200, {
1853
819
  ...result,
1854
- verdict: relabelClaimProfile(result.verdict, "secure_v5"),
1855
- record: result.record
1856
- ? {
1857
- ...result.record,
1858
- sourceClass: payload.sourceClass
1859
- }
1860
- : undefined,
1861
- promotionCapability: promotionCapability
1862
- ? {
1863
- capabilityId: promotionCapability.capabilityId,
1864
- capabilityDigest: promotionCapability.capabilityDigest,
1865
- semanticDigest: promotionCapability.semanticDigest,
1866
- title: promotionCapability.title,
1867
- kind: promotionCapability.kind,
1868
- expiresAt: promotionCapability.expiresAt
1869
- }
1870
- : undefined,
1871
- promotionTicket
820
+ replayEventId: replayEventId ?? randomUUID()
1872
821
  });
1873
822
  return;
1874
823
  }
1875
- if (request.url === "/v5/memory/write") {
824
+ if (requestUrl === "/v6/memory/stage") {
1876
825
  const payload = await readJson(request);
1877
826
  const sessionState = findSessionState(sessions, payload.sessionId);
1878
- const result = evaluateMemoryWriteV5(payload, sessionState?.session);
1879
- let promotionCapability;
827
+ const result = stageMemoryRecordV6(payload, sessionState?.session);
828
+ let promotionAuthority;
1880
829
  if (sessionState && result.record) {
1881
830
  sessionState.memoryRecords.set(result.record.recordId, result.record);
1882
- sessionState.memorySourceClassesV5.set(result.record.recordId, "user_note");
1883
- if (result.record.tier === "candidate_durable") {
1884
- promotionCapability = mintMemoryPromotionCapabilityV5(sessionState.session, {
1885
- recordId: result.record.recordId,
1886
- sourceDigest: result.record.sourceDigest,
1887
- sourceObservationId: result.record.sourceObservationId,
1888
- key: result.record.key,
1889
- valueDigest: result.record.sourceDigest ?? hashValue(result.record.value)
1890
- });
1891
- sessionState.capabilitiesV5.set(promotionCapability.capabilityId, promotionCapability);
1892
- }
831
+ sessionState.memorySourceClasses.set(result.record.recordId, payload.sourceClass);
832
+ promotionAuthority = mintMemoryPromotionCapabilityV6(sessionState.session, {
833
+ recordId: result.record.recordId,
834
+ sourceDigest: result.record.sourceDigest,
835
+ sourceObservationId: result.record.sourceObservationId,
836
+ key: result.record.key,
837
+ valueDigest: result.record.sourceDigest ?? hashValue(result.record.value)
838
+ });
839
+ sessionState.authorities.set(promotionAuthority.capabilityId, promotionAuthority);
1893
840
  }
1894
841
  appendReplayEvent(sessionState, {
1895
842
  kind: "memory",
1896
843
  actor: "sdk",
1897
844
  payload: {
1898
- route: request.url,
845
+ route: requestUrl,
1899
846
  recordId: result.record?.recordId ?? null,
1900
- sourceClass: "user_note",
1901
- verdict: result.verdict.decision
847
+ decision: result.verdict.decision
1902
848
  }
1903
849
  });
1904
850
  writeJson(response, 200, {
1905
851
  ...result,
1906
- verdict: relabelClaimProfile(result.verdict, "secure_v5"),
1907
- promotionCapability: promotionCapability
1908
- ? {
1909
- capabilityId: promotionCapability.capabilityId,
1910
- capabilityDigest: promotionCapability.capabilityDigest,
1911
- semanticDigest: promotionCapability.semanticDigest,
1912
- title: promotionCapability.title,
1913
- kind: promotionCapability.kind,
1914
- expiresAt: promotionCapability.expiresAt
1915
- }
852
+ promotionAuthority: promotionAuthority
853
+ ? authorityCandidateFromDescriptor(promotionAuthority)
1916
854
  : undefined,
1917
- promotionTicket: promotionCapability
855
+ promotionTicket: promotionAuthority
1918
856
  ? {
1919
- ticketId: promotionCapability.capabilityId,
1920
- ticketDigest: promotionCapability.capabilityDigest,
1921
- semanticDigest: promotionCapability.semanticDigest,
857
+ ticketId: promotionAuthority.capabilityId,
858
+ ticketDigest: promotionAuthority.capabilityDigest,
859
+ semanticDigest: promotionAuthority.semanticDigest,
1922
860
  recordId: result.record?.recordId,
1923
- sourceClass: "user_note",
1924
- expiresAt: promotionCapability.expiresAt
861
+ sourceClass: payload.sourceClass,
862
+ expiresAt: promotionAuthority.expiresAt
1925
863
  }
1926
864
  : undefined
1927
865
  });
1928
866
  return;
1929
867
  }
1930
- if (request.url === "/v5/memory/promote") {
868
+ if (requestUrl === "/v6/memory/promote") {
1931
869
  const payload = await readJson(request);
1932
- const normalizedPayload = {
1933
- ...payload,
1934
- ticketId: payload.ticketId ?? payload.capabilityId ?? "",
1935
- ticketDigest: payload.ticketDigest ?? payload.capabilityDigest ?? ""
1936
- };
1937
- const sessionState = findSessionState(sessions, normalizedPayload.sessionId);
1938
- const record = sessionState?.memoryRecords.get(normalizedPayload.recordId);
1939
- const capability = sessionState?.capabilitiesV5.get(normalizedPayload.ticketId);
1940
- const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(normalizedPayload.approvalId);
870
+ const sessionState = findSessionState(sessions, payload.sessionId);
871
+ const record = sessionState?.memoryRecords.get(payload.recordId);
872
+ const authority = sessionState?.authorities.get(payload.ticketId);
873
+ const approvalEnvelope = sessionState?.approvalEnvelopes.get(payload.approvalId);
1941
874
  const snapshotState = sessionState && record ? buildMemorySnapshotState(sessionState, record) : undefined;
1942
- const result = promoteStagedMemoryRecordV5(normalizedPayload, sessionState?.session, record, capability, approvalEnvelope, {
875
+ const result = promoteMemoryRecordV6(payload, sessionState?.session, record, authority, approvalEnvelope, {
1943
876
  sourceClass: record
1944
- ? (sessionState?.memorySourceClassesV5.get(record.recordId) ?? "user_note")
877
+ ? sessionState?.memorySourceClasses.get(record.recordId)
1945
878
  : undefined,
1946
879
  priorTrustedRecord: snapshotState?.snapshotRecord
1947
880
  });
@@ -1953,11 +886,11 @@ export async function createSafeBrowseServer(options = {}) {
1953
886
  });
1954
887
  }
1955
888
  sessionState.memoryRecords.set(result.promotedRecord.recordId, result.promotedRecord);
1956
- if (capability) {
1957
- consumeCapabilityV5(sessionState, capability);
889
+ if (authority) {
890
+ consumeAuthority(sessionState, authority);
1958
891
  }
1959
892
  if (approvalEnvelope) {
1960
- sessionState.approvalEnvelopesV5.set(normalizedPayload.approvalId, {
893
+ sessionState.approvalEnvelopes.set(payload.approvalId, {
1961
894
  ...approvalEnvelope,
1962
895
  consumedAt: new Date().toISOString()
1963
896
  });
@@ -1971,26 +904,25 @@ export async function createSafeBrowseServer(options = {}) {
1971
904
  kind: "memory",
1972
905
  actor: "sdk",
1973
906
  payload: {
1974
- route: request.url,
1975
- recordId: normalizedPayload.recordId,
1976
- verdict: result.verdict.decision
907
+ route: requestUrl,
908
+ recordId: payload.recordId,
909
+ decision: result.verdict.decision
1977
910
  }
1978
911
  });
1979
912
  writeJson(response, 200, {
1980
913
  ...result,
1981
- verdict: relabelClaimProfile(result.verdict, "secure_v5"),
1982
- approvalEnvelope: normalizedPayload.approvalId && sessionState
1983
- ? sessionState.approvalEnvelopesV5.get(normalizedPayload.approvalId)
914
+ approvalEnvelope: payload.approvalId && sessionState
915
+ ? sessionState.approvalEnvelopes.get(payload.approvalId)
1984
916
  : approvalEnvelope
1985
917
  });
1986
918
  return;
1987
919
  }
1988
- if (request.url === "/v5/memory/rollback") {
920
+ if (requestUrl === "/v6/memory/rollback") {
1989
921
  const payload = await readJson(request);
1990
922
  const sessionState = findSessionState(sessions, payload.sessionId);
1991
923
  const record = sessionState?.memoryRecords.get(payload.recordId);
1992
924
  const snapshotState = sessionState?.memorySnapshots.get(payload.snapshotId);
1993
- const result = rollbackMemoryRecordV5(payload, sessionState?.session, record, {
925
+ const result = rollbackMemoryRecordV6(payload, sessionState?.session, record, {
1994
926
  snapshotRecord: snapshotState?.snapshotRecord,
1995
927
  baselineAbsent: snapshotState?.baselineAbsent
1996
928
  });
@@ -2006,15 +938,14 @@ export async function createSafeBrowseServer(options = {}) {
2006
938
  kind: "memory",
2007
939
  actor: "sdk",
2008
940
  payload: {
2009
- route: request.url,
941
+ route: requestUrl,
2010
942
  recordId: payload.recordId,
2011
943
  snapshotId: payload.snapshotId,
2012
- verdict: result.verdict.decision
944
+ decision: result.verdict.decision
2013
945
  }
2014
946
  });
2015
947
  writeJson(response, 200, {
2016
948
  ...result,
2017
- verdict: relabelClaimProfile(result.verdict, "secure_v5"),
2018
949
  rollbackEvent: result.verdict.decision === "ALLOW"
2019
950
  ? {
2020
951
  recordId: payload.recordId,
@@ -2025,7 +956,7 @@ export async function createSafeBrowseServer(options = {}) {
2025
956
  });
2026
957
  return;
2027
958
  }
2028
- if (request.url === "/v5/replay/bundle") {
959
+ if (requestUrl === "/v6/replay/bundle") {
2029
960
  const payload = await readJson(request);
2030
961
  const sessionState = findSessionState(sessions, payload.sessionId);
2031
962
  if (!sessionState) {
@@ -2035,249 +966,6 @@ export async function createSafeBrowseServer(options = {}) {
2035
966
  writeJson(response, 200, buildReplayBundle(sessionState.replayEvents, runtime));
2036
967
  return;
2037
968
  }
2038
- if (request.url === "/v4/session/start") {
2039
- const payload = await readJson(request);
2040
- const sessionState = createSessionState(payload, runtime);
2041
- sessions.set(sessionState.session.sessionId, sessionState);
2042
- writeJson(response, 200, {
2043
- session: sessionState.session
2044
- });
2045
- return;
2046
- }
2047
- if (request.url === "/v4/observe") {
2048
- const payload = await readJson(request);
2049
- const sessionState = findSessionState(sessions, payload.sessionId);
2050
- if (!sessionState) {
2051
- writeJson(response, 404, { error: "unknown_session" });
2052
- return;
2053
- }
2054
- const capture = {
2055
- ...payload.capture,
2056
- sessionId: payload.sessionId,
2057
- taskId: sessionState.session.taskId
2058
- };
2059
- const observation = await parserIsolationService.compileObservation({
2060
- capture,
2061
- workflowHash: sessionState.session.workflowHash,
2062
- compilerVersion: "v4"
2063
- });
2064
- const failClosedObservation = applyV4FailClosedMediation(observation.compiledObservation, observation.plannerInput, "observe");
2065
- let capabilities = mintCapabilitiesForObservation(sessionState.session, observation.compiledObservation, {
2066
- sourceObservationId: observation.compiledObservation.observationId
2067
- });
2068
- let plannerInput = failClosedObservation.plannerInput;
2069
- if (shouldReduceAuthority(sessionState, observation.compiledObservation)) {
2070
- plannerInput = applyAuthorityReduction(sessionState, observation.compiledObservation, plannerInput);
2071
- capabilities = [];
2072
- }
2073
- sessionState.capabilities.clear();
2074
- for (const capability of capabilities) {
2075
- sessionState.capabilities.set(capability.capabilityId, capability);
2076
- }
2077
- sessionState.latestObservation = observation.compiledObservation;
2078
- sessionState.observations.set(observation.compiledObservation.observationId, observation.compiledObservation);
2079
- writeJson(response, 200, {
2080
- compiledObservation: observation.compiledObservation,
2081
- observationVerdict: failClosedObservation.verdict,
2082
- plannerInput: attachCapabilitiesToPlannerInput(plannerInput, capabilities)
2083
- });
2084
- return;
2085
- }
2086
- if (request.url === "/v4/action/evaluate") {
2087
- const payload = await readJson(request);
2088
- const sessionState = findSessionState(sessions, payload.sessionId);
2089
- const capability = sessionState?.capabilities.get(payload.capabilityId);
2090
- const verdict = evaluateCapabilityUse(payload, sessionState?.session, capability, {
2091
- alreadyUsed: sessionState?.usedCapabilities.has(payload.capabilityId) ?? false
2092
- });
2093
- if (verdict.decision === "ALLOW" && sessionState && capability) {
2094
- sessionState.capabilities.delete(payload.capabilityId);
2095
- sessionState.usedCapabilities.add(payload.capabilityId);
2096
- sessionState.session = {
2097
- ...sessionState.session,
2098
- currentStep: sessionState.session.currentStep + 1
2099
- };
2100
- }
2101
- writeJson(response, 200, {
2102
- verdict,
2103
- executionPlan: verdict.decision === "ALLOW" && capability
2104
- ? {
2105
- verb: capability.kind,
2106
- targetUrl: capability.targetUrl,
2107
- targetOrigin: capability.targetOrigin,
2108
- selector: capability.selector,
2109
- derivedSinkClass: capability.derivedSinkClass,
2110
- derivedSensitiveSink: capability.derivedSensitiveSink
2111
- }
2112
- : undefined
2113
- });
2114
- return;
2115
- }
2116
- if (request.url === "/v4/approval/grant") {
2117
- const payload = await readJson(request);
2118
- const sessionState = findSessionState(sessions, payload.sessionId);
2119
- if (!sessionState) {
2120
- writeJson(response, 404, { error: "unknown_session" });
2121
- return;
2122
- }
2123
- const unknownCapability = (payload.capabilityIds ?? []).find((capabilityId) => !sessionState.capabilities.has(capabilityId));
2124
- if (unknownCapability) {
2125
- writeJson(response, 400, {
2126
- error: "unknown_capability",
2127
- capabilityId: unknownCapability
2128
- });
2129
- return;
2130
- }
2131
- if (payload.sinkClass === "connector_oauth" &&
2132
- !(payload.capabilityIds ?? []).length) {
2133
- writeJson(response, 400, {
2134
- error: "capability_ids_required",
2135
- sinkClass: payload.sinkClass
2136
- });
2137
- return;
2138
- }
2139
- const approvalGrant = issueApprovalGrant(payload, sessionState);
2140
- sessionState.approvalGrants.set(approvalGrant.approvalGrantId, approvalGrant);
2141
- writeJson(response, 200, {
2142
- approvalGrant
2143
- });
2144
- return;
2145
- }
2146
- if (request.url === "/v4/tool/prepare") {
2147
- const payload = await readJson(request);
2148
- const sessionState = findSessionState(sessions, payload.sessionId);
2149
- const approvalGrant = sessionState?.approvalGrants.get(payload.approvalGrantId);
2150
- const prepared = prepareToolOnboardingV4({
2151
- ...payload.request,
2152
- approvalGrantId: payload.approvalGrantId
2153
- }, sessionState?.session, approvalGrant, runtime);
2154
- const onboardingSession = prepared.verdict.decision === "ALLOW" && payload.request.authType === "oauth" && approvalGrant
2155
- ? createOnboardingSession(payload.request, runtime, approvalGrant, prepared.verifiedRegistryEntry)
2156
- : undefined;
2157
- if (sessionState && onboardingSession) {
2158
- sessionState.onboardingSessions.set(onboardingSession.sessionId, onboardingSession);
2159
- }
2160
- writeJson(response, 200, {
2161
- verdict: prepared.verdict,
2162
- approvalVerdict: prepared.approvalVerdict,
2163
- verifiedRegistryEntry: prepared.verifiedRegistryEntry,
2164
- workflowBinding: prepared.workflowBinding,
2165
- onboardingSession
2166
- });
2167
- return;
2168
- }
2169
- if (request.url === "/v4/tool/callback/verify") {
2170
- const payload = await readJson(request);
2171
- const sessionState = findSessionState(sessions, payload.sessionId);
2172
- const approvalGrant = sessionState?.approvalGrants.get(payload.approvalGrantId);
2173
- const session = sessionState?.onboardingSessions.get(payload.request.sessionId);
2174
- const result = verifyToolCallbackV4(payload.request, sessionState?.session, session, approvalGrant, runtime);
2175
- if (sessionState && session) {
2176
- sessionState.onboardingSessions.set(payload.request.sessionId, {
2177
- ...session,
2178
- status: result.verdict.decision === "ALLOW" ? "used" : session.status
2179
- });
2180
- }
2181
- writeJson(response, 200, result);
2182
- return;
2183
- }
2184
- if (request.url === "/v4/artifact/ingest") {
2185
- const payload = await readJson(request);
2186
- const sessionState = findSessionState(sessions, payload.sessionId);
2187
- if (!sessionState) {
2188
- writeJson(response, 404, { error: "unknown_session" });
2189
- return;
2190
- }
2191
- const capture = {
2192
- ...payload.capture,
2193
- sessionId: payload.sessionId,
2194
- taskId: sessionState.session.taskId
2195
- };
2196
- const observation = await parserIsolationService.compileObservation({
2197
- capture,
2198
- workflowHash: sessionState.session.workflowHash,
2199
- compilerVersion: "v4"
2200
- });
2201
- const failClosedArtifact = applyV4FailClosedMediation(observation.compiledObservation, observation.plannerInput, "artifact");
2202
- const legacyArtifact = buildLegacyObservationCapture(capture);
2203
- const artifactResult = legacyArtifact !== undefined ? brokerArtifact(legacyArtifact, runtime) : undefined;
2204
- sessionState.latestObservation = observation.compiledObservation;
2205
- sessionState.observations.set(observation.compiledObservation.observationId, observation.compiledObservation);
2206
- const effectiveArtifactVerdict = failClosedArtifact.failClosed
2207
- ? failClosedArtifact.verdict
2208
- : artifactResult?.verdict;
2209
- const effectiveArtifact = failClosedArtifact.failClosed && artifactResult?.artifact
2210
- ? {
2211
- ...artifactResult.artifact,
2212
- toolActivationPolicy: "block",
2213
- approvalRequiredForFollowOn: true
2214
- }
2215
- : artifactResult?.artifact;
2216
- const plannerInput = shouldReduceAuthority(sessionState, observation.compiledObservation)
2217
- ? applyAuthorityReduction(sessionState, observation.compiledObservation, failClosedArtifact.plannerInput)
2218
- : failClosedArtifact.plannerInput;
2219
- writeJson(response, 200, {
2220
- compiledObservation: observation.compiledObservation,
2221
- plannerInput,
2222
- artifactVerdict: effectiveArtifactVerdict,
2223
- artifact: effectiveArtifact
2224
- });
2225
- return;
2226
- }
2227
- if (request.url === "/v4/memory/write") {
2228
- const payload = await readJson(request);
2229
- const sessionState = findSessionState(sessions, payload.sessionId);
2230
- const result = evaluateMemoryWriteV4(payload, sessionState?.session, runtime);
2231
- if (sessionState && result.record) {
2232
- sessionState.memoryRecords.set(result.record.recordId, result.record);
2233
- }
2234
- writeJson(response, 200, result);
2235
- return;
2236
- }
2237
- if (request.url === "/v4/memory/promote") {
2238
- const payload = await readJson(request);
2239
- const sessionState = findSessionState(sessions, payload.sessionId);
2240
- const record = sessionState?.memoryRecords.get(payload.recordId);
2241
- const approvalGrant = payload.approvalGrantId && sessionState
2242
- ? sessionState.approvalGrants.get(payload.approvalGrantId)
2243
- : undefined;
2244
- const result = promoteMemoryRecordV4(payload, sessionState?.session, record, approvalGrant);
2245
- if (sessionState && result.promotedRecord) {
2246
- if (record && result.promotedRecord.snapshotId) {
2247
- sessionState.memorySnapshots.set(result.promotedRecord.snapshotId, {
2248
- ...record,
2249
- tier: "trusted_durable",
2250
- summaryOnly: false,
2251
- snapshotId: result.promotedRecord.snapshotId,
2252
- rollbackPointId: result.promotedRecord.rollbackPointId
2253
- });
2254
- }
2255
- sessionState.memoryRecords.set(result.promotedRecord.recordId, result.promotedRecord);
2256
- }
2257
- writeJson(response, 200, result);
2258
- return;
2259
- }
2260
- if (request.url === "/v4/memory/rollback") {
2261
- const payload = await readJson(request);
2262
- const sessionState = findSessionState(sessions, payload.sessionId);
2263
- const record = sessionState?.memoryRecords.get(payload.recordId);
2264
- const snapshotRecord = sessionState?.memorySnapshots.get(payload.snapshotId);
2265
- const result = rollbackMemoryRecordV4(payload, sessionState?.session, record, snapshotRecord);
2266
- if (sessionState && result.restoredRecord) {
2267
- sessionState.memoryRecords.set(result.restoredRecord.recordId, result.restoredRecord);
2268
- }
2269
- writeJson(response, 200, {
2270
- ...result,
2271
- rollbackEvent: result.verdict.decision === "ALLOW"
2272
- ? {
2273
- recordId: payload.recordId,
2274
- snapshotId: payload.snapshotId,
2275
- appliedAt: new Date().toISOString()
2276
- }
2277
- : undefined
2278
- });
2279
- return;
2280
- }
2281
969
  writeJson(response, 404, { error: "not_found" });
2282
970
  }
2283
971
  catch (error) {
@@ -2291,7 +979,7 @@ export async function createSafeBrowseServer(options = {}) {
2291
979
  server.close = ((callback) => {
2292
980
  clearInterval(parserHealthRefreshTimer);
2293
981
  return originalClose((error) => {
2294
- void parserIsolationService.close().finally(() => {
982
+ void Promise.allSettled([parserIsolationService.close(), modelGuardClient.close()]).finally(() => {
2295
983
  callback?.(error);
2296
984
  });
2297
985
  });