@sema-agent/server 7.79.2 → 7.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/boot/late-binds.d.ts +35 -0
  2. package/dist/boot/late-binds.js +20 -0
  3. package/dist/boot/stage-01-config.d.ts +7 -0
  4. package/dist/boot/stage-01-config.js +60 -0
  5. package/dist/boot/stage-02-registry-pull.d.ts +10 -0
  6. package/dist/boot/stage-02-registry-pull.js +11 -0
  7. package/dist/boot/stage-03-store-backend.d.ts +32 -0
  8. package/dist/boot/stage-03-store-backend.js +11 -0
  9. package/dist/boot/stage-04-stores.d.ts +69 -0
  10. package/dist/boot/stage-04-stores.js +49 -0
  11. package/dist/boot/stage-05-execution-env.d.ts +82 -0
  12. package/dist/boot/stage-05-execution-env.js +20 -0
  13. package/dist/boot/stage-06-runners.d.ts +143 -0
  14. package/dist/boot/stage-06-runners.js +207 -0
  15. package/dist/boot/stage-07-capability-layer.d.ts +157 -0
  16. package/dist/boot/stage-07-capability-layer.js +162 -0
  17. package/dist/boot/stage-08-reapers.d.ts +172 -0
  18. package/dist/boot/stage-08-reapers.js +78 -0
  19. package/dist/boot/stage-09-leader.d.ts +204 -0
  20. package/dist/boot/stage-09-leader.js +47 -0
  21. package/dist/boot/stage-10-http-server.d.ts +204 -0
  22. package/dist/boot/stage-10-http-server.js +289 -0
  23. package/dist/config-invariants.d.ts +9 -0
  24. package/dist/config-invariants.js +7 -1
  25. package/dist/config-types.d.ts +1 -1
  26. package/dist/http/dispatch.js +28 -8
  27. package/dist/http/route-ctx.d.ts +101 -4
  28. package/dist/http/route-ctx.js +4 -0
  29. package/dist/http/route-table.d.ts +71 -43
  30. package/dist/http/route-table.js +111 -187
  31. package/dist/http/routes/a2a-serve.d.ts +18 -3
  32. package/dist/http/routes/a2a-serve.js +25 -23
  33. package/dist/http/routes/admin-config-refresh.d.ts +9 -2
  34. package/dist/http/routes/admin-config-refresh.js +32 -31
  35. package/dist/http/routes/admin-drain.d.ts +9 -2
  36. package/dist/http/routes/admin-drain.js +32 -31
  37. package/dist/http/routes/admin-memory-optout.d.ts +28 -2
  38. package/dist/http/routes/admin-memory-optout.js +26 -39
  39. package/dist/http/routes/adoption.d.ts +15 -2
  40. package/dist/http/routes/adoption.js +52 -52
  41. package/dist/http/routes/agents-roster.d.ts +9 -2
  42. package/dist/http/routes/agents-roster.js +82 -80
  43. package/dist/http/routes/approvals-assistant.d.ts +58 -5
  44. package/dist/http/routes/approvals-assistant.js +37 -38
  45. package/dist/http/routes/attachments.d.ts +22 -2
  46. package/dist/http/routes/attachments.js +81 -72
  47. package/dist/http/routes/capabilities.d.ts +21 -2
  48. package/dist/http/routes/capabilities.js +174 -170
  49. package/dist/http/routes/config-catalog.d.ts +9 -2
  50. package/dist/http/routes/config-catalog.js +28 -31
  51. package/dist/http/routes/devices.d.ts +24 -2
  52. package/dist/http/routes/devices.js +19 -21
  53. package/dist/http/routes/diagnostics.d.ts +9 -2
  54. package/dist/http/routes/diagnostics.js +41 -43
  55. package/dist/http/routes/fleet.d.ts +11 -2
  56. package/dist/http/routes/fleet.js +23 -22
  57. package/dist/http/routes/images.d.ts +63 -9
  58. package/dist/http/routes/images.js +53 -46
  59. package/dist/http/routes/leader.d.ts +20 -2
  60. package/dist/http/routes/leader.js +43 -40
  61. package/dist/http/routes/memory-bundle.d.ts +15 -2
  62. package/dist/http/routes/memory-bundle.js +38 -38
  63. package/dist/http/routes/memory-compliance.d.ts +16 -4
  64. package/dist/http/routes/memory-compliance.js +44 -45
  65. package/dist/http/routes/memory-consolidation.d.ts +14 -2
  66. package/dist/http/routes/memory-consolidation.js +66 -66
  67. package/dist/http/routes/memory-origin.d.ts +23 -4
  68. package/dist/http/routes/memory-origin.js +65 -65
  69. package/dist/http/routes/memory-policy.d.ts +21 -2
  70. package/dist/http/routes/memory-policy.js +119 -115
  71. package/dist/http/routes/notify-wake.d.ts +21 -11
  72. package/dist/http/routes/notify-wake.js +118 -116
  73. package/dist/http/routes/observability.d.ts +16 -2
  74. package/dist/http/routes/observability.js +54 -51
  75. package/dist/http/routes/retention-ops.d.ts +19 -2
  76. package/dist/http/routes/retention-ops.js +20 -22
  77. package/dist/http/routes/rules.d.ts +36 -2
  78. package/dist/http/routes/rules.js +22 -24
  79. package/dist/http/routes/run-memory-capture.d.ts +10 -3
  80. package/dist/http/routes/run-memory-capture.js +84 -84
  81. package/dist/http/routes/runs.d.ts +102 -22
  82. package/dist/http/routes/runs.js +71 -88
  83. package/dist/http/routes/session-sync.d.ts +41 -2
  84. package/dist/http/routes/session-sync.js +25 -23
  85. package/dist/http/routes/sessions-list.d.ts +16 -2
  86. package/dist/http/routes/sessions-list.js +78 -75
  87. package/dist/http/routes/sessions.d.ts +77 -2
  88. package/dist/http/routes/sessions.js +50 -47
  89. package/dist/http/routes/shared-memory.d.ts +19 -2
  90. package/dist/http/routes/shared-memory.js +65 -63
  91. package/dist/http/routes/side-query.d.ts +11 -2
  92. package/dist/http/routes/side-query.js +81 -79
  93. package/dist/http/routes/tasks.d.ts +17 -2
  94. package/dist/http/routes/tasks.js +18 -11
  95. package/dist/http/routes/trace-usage.d.ts +40 -2
  96. package/dist/http/routes/trace-usage.js +46 -35
  97. package/dist/http/routes/workflows.d.ts +35 -3
  98. package/dist/http/routes/workflows.js +29 -29
  99. package/dist/main.js +20 -870
  100. package/dist/service-version.d.ts +5 -0
  101. package/dist/service-version.js +12 -0
  102. package/dist/trace/redact.d.ts +7 -6
  103. package/dist/trace/redact.js +419 -229
  104. package/package.json +1 -1
@@ -7,6 +7,7 @@ import { DEVICE_PROTOCOL_VERSION, DEVICE_WS_PATH } from "../../device-ws-protoco
7
7
  import { a2aInjectionHonored } from "../../task-a2a.js";
8
8
  import { mcpInjectionHonored } from "../../task-mcp.js";
9
9
  import { sendJson, sendError } from "../send.js";
10
+ import { assertNever } from "../route-ctx.js";
10
11
  import { resolveStreamApprovalGate } from "../../tool-approval.js";
11
12
  import { projectMemoryEngineCapability } from "../../memory-posture.js";
12
13
  import { projectWriteProtectionCapability } from "../../write-protection.js";
@@ -29,181 +30,184 @@ function retentionAdvertisement(config) {
29
30
  return null;
30
31
  return { mode: sweep.mode, maxAgeDays: policy.maxAgeDays };
31
32
  }
32
- export async function handleCapabilities(req, res, url, ctx) {
33
- const miss = { fell: false };
34
- await handleCapabilitiesBody(req, res, url, ctx, miss);
35
- return !miss.fell;
36
- }
37
- async function handleCapabilitiesBody(req, res, url, ctx, miss) {
33
+ export async function handleCapabilities(req, res, match, ctx) {
38
34
  const { deps } = ctx;
39
- if (req.method === "GET" && url.startsWith("/v1/capabilities/scenarios/")) {
40
- const name = url.slice("/v1/capabilities/scenarios/".length).split("?")[0];
41
- const detail = deps.scenarioDetails?.[name];
42
- if (!detail) {
43
- sendError(res, 404, "scenario_not_found", "scenario not found");
35
+ switch (match.decl.id) {
36
+ case "capability-scenario": {
37
+ const name = match.params.name;
38
+ const detail = deps.scenarioDetails?.[name];
39
+ if (!detail) {
40
+ sendError(res, 404, "scenario_not_found", "scenario not found");
41
+ return;
42
+ }
43
+ sendJson(res, 200, detail);
44
44
  return;
45
45
  }
46
- sendJson(res, 200, detail);
47
- return;
48
- }
49
- if (req.method === "GET" && url === "/v1/capabilities") {
50
- const workflowsEngineCan = deps.workflowsCapable ?? Boolean(deps.workflowRunStore);
51
- const workflowsDenial = selfOrchestrationDenial({ selfOrchestrationEnabled: deps.config.selfOrchestrationEnabled, requirePrincipal: deps.config.requirePrincipal }, centerEntitlementSourceWired(deps.config));
52
- const requestedModeRaw = new URL(req.url ?? "", "http://x").searchParams.get("permissionMode");
53
- const requestedMode = requestedModeRaw === null ? undefined : coercePermissionModeStrict(requestedModeRaw);
54
- if (requestedModeRaw !== null && requestedMode === undefined) {
55
- sendError(res, 400, "request.field_invalid", 'permissionMode must be one of "default" | "acceptEdits" | "plan" | "bypassPermissions" | "auto"');
46
+ case "capabilities": {
47
+ const workflowsEngineCan = deps.workflowsCapable ?? Boolean(deps.workflowRunStore);
48
+ const workflowsDenial = selfOrchestrationDenial({ selfOrchestrationEnabled: deps.config.selfOrchestrationEnabled, requirePrincipal: deps.config.requirePrincipal }, centerEntitlementSourceWired(deps.config));
49
+ const requestedModeRaw = new URLSearchParams(match.search).get("permissionMode");
50
+ const requestedMode = requestedModeRaw === null ? undefined : coercePermissionModeStrict(requestedModeRaw);
51
+ if (requestedModeRaw !== null && requestedMode === undefined) {
52
+ sendError(res, 400, "request.field_invalid", 'permissionMode must be one of "default" | "acceptEdits" | "plan" | "bypassPermissions" | "auto"');
53
+ return;
54
+ }
55
+ const entitlementSource = centerEntitlementSourceWired(deps.config);
56
+ const { fault: autoModeFault, ...autoMode } = await judgeAutoModeArming(deps.autoModeFace, { requestedMode, principal: gatedPrincipal(req, deps.config), entitlementSource, localDeny: deps.config.permissionsDisableAutoMode });
57
+ if (autoModeFault !== undefined)
58
+ deps.logger?.warn("auto_mode_face_resolver_fault", { fault: autoModeFault });
59
+ sendJson(res, 200, {
60
+ ...deps.capabilities,
61
+ asyncRuns: Boolean(deps.runStore),
62
+ artifacts: Boolean(deps.runStore),
63
+ approvals: Boolean(deps.checkpointStore),
64
+ sessions: Boolean(deps.sessionAudit),
65
+ deviceExecutor: deps.deviceHub
66
+ ? { enabled: true, protocolVersion: DEVICE_PROTOCOL_VERSION, maxInflightPerDevice: deps.config.remoteExec?.provider === "device" ? (deps.config.remoteExec.maxInflightPerDevice ?? 4) : 4, wsPath: DEVICE_WS_PATH }
67
+ : false,
68
+ sessionEvents: Boolean(deps.sessionWatch && deps.sessionStorage?.getLeafId && deps.sessionStorage?.ownerOf),
69
+ fleet: deps.fleetBus
70
+ ? { stream: true, sessionScope: true, observe: true, resume: "snapshot", bgNotifyFailClosed: true, steerPriority: "advisory", hookFailureNotice: true }
71
+ : false,
72
+ workspace: false,
73
+ leader: Boolean(deps.leaderEndpoint),
74
+ images: Boolean(deps.imageIndex),
75
+ memory: false,
76
+ memoryWrite: false,
77
+ memoryEngine: projectMemoryEngineCapability(deps.memoryPosture),
78
+ sql: projectSqlEngineCapability(deps.sqlEngineFacts?.()),
79
+ sessionBackgroundable: Boolean(deps.runStore),
80
+ runMemoryCaptureOptOut: Boolean(deps.runStore),
81
+ sharedMemory: Boolean(deps.sharedMemoryStore && deps.orgMemoryDirectory),
82
+ adoption: Boolean(deps.backend?.adoptionLog) && deps.config.operatorPrincipals.length > 0,
83
+ memoryBundle: Boolean(deps.memoryBundleExport && deps.memoryBundleImport) && deps.config.operatorPrincipals.length > 0,
84
+ memoryCompliance: Boolean(deps.memoryCompliance) && deps.config.operatorPrincipals.length > 0,
85
+ memoryOrigin: Boolean(deps.memoryOriginFace) && deps.config.operatorPrincipals.length > 0,
86
+ memoryConsolidationDriver: Boolean(deps.memoryConsolidation) && deps.config.operatorPrincipals.length > 0,
87
+ memoryOptOutGrant: Boolean(deps.memoryOptOutGrant) && deps.config.operatorPrincipals.length > 0,
88
+ outcomeLedger: Boolean(deps.outcomeSink?.summary) && (deps.config.requirePrincipal !== true || deps.config.operatorPrincipals.length > 0),
89
+ permissionRules: Boolean(deps.ruleConsent) && gatedPrincipal(req, deps.config) !== undefined,
90
+ permissionRulesRevoke: Boolean(deps.ruleConsent),
91
+ respondFreeFormRules: true,
92
+ respondBatchRuleOffers: true,
93
+ oneShot: true,
94
+ policy: true,
95
+ usage: Boolean(deps.costQuota),
96
+ permissionModeWrite: false,
97
+ modelSelection: true,
98
+ effortSelection: true,
99
+ toolOutput: true,
100
+ messageIdentity: true,
101
+ forwardSubagentEvents: true,
102
+ scratchpadDir: true,
103
+ subagentSteer: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
104
+ subagentResume: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
105
+ taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: cwdHonored(deps.config) || deviceCwdHonored(deps.config), hooks: deps.config.requirePrincipal !== true },
106
+ modeShellGateTranslation: true,
107
+ permissionModeAuto: {
108
+ accepted: true,
109
+ classifierSeat: deps.autoModeFace?.seatMounted === true,
110
+ entitlementSource,
111
+ intentArming: deps.autoModeFace?.engineArmsOnIntent === true,
112
+ ...autoMode,
113
+ },
114
+ readFace: deps.config.readFace ?? null,
115
+ writeProtection: projectWriteProtectionCapability(deps.writeProtection),
116
+ appendSystemPrompt: true,
117
+ compactionModel: true,
118
+ subagentOutput: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
119
+ subagentStream: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
120
+ taskHandles: Boolean(deps.taskHandleOutput) && Boolean(deps.taskHandleStop) && Boolean(deps.runStore),
121
+ taskAgents: deps.config.requirePrincipal !== true,
122
+ retainBackgroundProcesses: deps.config.requirePrincipal !== true,
123
+ interactiveTools: true,
124
+ projectContext: cwdHonored(deps.config),
125
+ callerCwd: cwdHonored(deps.config) || deviceCwdHonored(deps.config),
126
+ mcpInjection: mcpInjectionHonored(deps.config),
127
+ a2a: true,
128
+ a2aInjection: a2aInjectionHonored(deps.config),
129
+ a2aServe: deps.config.a2aServe !== undefined,
130
+ modelUsage: Boolean(deps.runStore && deps.modelUsage),
131
+ sessionInit: true,
132
+ mcp: true,
133
+ mcpElicitation: Boolean(deps.elicitation),
134
+ askUserQuestion: Boolean(deps.question),
135
+ toolApproval: Boolean(deps.toolApproval),
136
+ streamApproval: resolveStreamApprovalGate({
137
+ toolApprovalEnabled: Boolean(deps.toolApproval),
138
+ streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
139
+ backend: deps.backend,
140
+ parkFacility: deps.checkpointStore !== undefined,
141
+ }).active,
142
+ approvalDecisionNote: true,
143
+ sideQuery: true,
144
+ agentRoster: Boolean(deps.backgroundAgentStore),
145
+ retention: retentionAdvertisement(deps.config),
146
+ sessionList: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
147
+ sessionSearch: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
148
+ sessionFork: Boolean(deps.sessionStorage?.fork),
149
+ sessionDelete: Boolean(deps.purgeSession),
150
+ resumeAt: Boolean(deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
151
+ promptSuggestions: true,
152
+ sessionPolicy: Boolean(deps.sessionPolicyStore),
153
+ rewindFiles: Boolean(deps.fileHistoryStore),
154
+ restoreFiles: Boolean(deps.fileHistoryStore),
155
+ rewindFilesTo: Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
156
+ scheduler: Boolean(deps.config.schedulerEnabled && deps.config.requirePrincipal !== true && deps.config.remoteExec?.provider === "host"),
157
+ sessionSync: Boolean(deps.backend && deps.sessionStorage?.exportEntries && deps.sessionStorage?.beginImportStaging && deps.fileHistoryStore),
158
+ sendUserFile: Boolean(deps.sendUserFile && deps.config.sendUserFile?.publicEndpoint),
159
+ s3PublicEndpoint: deps.config.sendUserFile?.publicEndpoint ?? null,
160
+ sendUserFileLedger: Boolean(deps.sendFileLedger),
161
+ workflows: workflowsEngineCan && workflowsDenial === null,
162
+ workflowsList: Boolean(deps.workflowRunStore),
163
+ workflowsGate: { engineCan: workflowsEngineCan, denial: workflowsDenial },
164
+ workflowModels: (() => {
165
+ const allow = deps.workflowModelAllowlist
166
+ ?? workflowModelAllowlistFor({
167
+ selfOrchestrationModels: deps.config.selfOrchestrationModels ?? [],
168
+ requirePrincipal: deps.config.requirePrincipal,
169
+ models: deps.config.models ?? {},
170
+ })
171
+ ?? [];
172
+ const bare = deps.config.models ?? {};
173
+ const live = expandTiers(bare, deps.config.tiers ?? {}) ?? bare;
174
+ return allow.filter((n) => Object.hasOwn(live, n));
175
+ })(),
176
+ manualCompact: Boolean(deps.runStore),
177
+ configCatalog: deps.config.configCatalogEnabled === true && deps.config.operatorPrincipals.length > 0,
178
+ });
56
179
  return;
57
180
  }
58
- const entitlementSource = centerEntitlementSourceWired(deps.config);
59
- const { fault: autoModeFault, ...autoMode } = await judgeAutoModeArming(deps.autoModeFace, { requestedMode, principal: gatedPrincipal(req, deps.config), entitlementSource, localDeny: deps.config.permissionsDisableAutoMode });
60
- if (autoModeFault !== undefined)
61
- deps.logger?.warn("auto_mode_face_resolver_fault", { fault: autoModeFault });
62
- sendJson(res, 200, {
63
- ...deps.capabilities,
64
- asyncRuns: Boolean(deps.runStore),
65
- artifacts: Boolean(deps.runStore),
66
- approvals: Boolean(deps.checkpointStore),
67
- sessions: Boolean(deps.sessionAudit),
68
- deviceExecutor: deps.deviceHub
69
- ? { enabled: true, protocolVersion: DEVICE_PROTOCOL_VERSION, maxInflightPerDevice: deps.config.remoteExec?.provider === "device" ? (deps.config.remoteExec.maxInflightPerDevice ?? 4) : 4, wsPath: DEVICE_WS_PATH }
70
- : false,
71
- sessionEvents: Boolean(deps.sessionWatch && deps.sessionStorage?.getLeafId && deps.sessionStorage?.ownerOf),
72
- fleet: deps.fleetBus
73
- ? { stream: true, sessionScope: true, observe: true, resume: "snapshot", bgNotifyFailClosed: true, steerPriority: "advisory", hookFailureNotice: true }
74
- : false,
75
- workspace: false,
76
- leader: Boolean(deps.leaderEndpoint),
77
- images: Boolean(deps.imageIndex),
78
- memory: false,
79
- memoryWrite: false,
80
- memoryEngine: projectMemoryEngineCapability(deps.memoryPosture),
81
- sql: projectSqlEngineCapability(deps.sqlEngineFacts?.()),
82
- sessionBackgroundable: Boolean(deps.runStore),
83
- runMemoryCaptureOptOut: Boolean(deps.runStore),
84
- sharedMemory: Boolean(deps.sharedMemoryStore && deps.orgMemoryDirectory),
85
- adoption: Boolean(deps.backend?.adoptionLog) && deps.config.operatorPrincipals.length > 0,
86
- memoryBundle: Boolean(deps.memoryBundleExport && deps.memoryBundleImport) && deps.config.operatorPrincipals.length > 0,
87
- memoryCompliance: Boolean(deps.memoryCompliance) && deps.config.operatorPrincipals.length > 0,
88
- memoryOrigin: Boolean(deps.memoryOriginFace) && deps.config.operatorPrincipals.length > 0,
89
- memoryConsolidationDriver: Boolean(deps.memoryConsolidation) && deps.config.operatorPrincipals.length > 0,
90
- memoryOptOutGrant: Boolean(deps.memoryOptOutGrant) && deps.config.operatorPrincipals.length > 0,
91
- outcomeLedger: Boolean(deps.outcomeSink?.summary) && (deps.config.requirePrincipal !== true || deps.config.operatorPrincipals.length > 0),
92
- permissionRules: Boolean(deps.ruleConsent) && gatedPrincipal(req, deps.config) !== undefined,
93
- permissionRulesRevoke: Boolean(deps.ruleConsent),
94
- respondFreeFormRules: true,
95
- respondBatchRuleOffers: true,
96
- oneShot: true,
97
- policy: true,
98
- usage: Boolean(deps.costQuota),
99
- permissionModeWrite: false,
100
- modelSelection: true,
101
- effortSelection: true,
102
- toolOutput: true,
103
- messageIdentity: true,
104
- forwardSubagentEvents: true,
105
- scratchpadDir: true,
106
- subagentSteer: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
107
- subagentResume: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
108
- taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: cwdHonored(deps.config) || deviceCwdHonored(deps.config), hooks: deps.config.requirePrincipal !== true },
109
- modeShellGateTranslation: true,
110
- permissionModeAuto: {
111
- accepted: true,
112
- classifierSeat: deps.autoModeFace?.seatMounted === true,
113
- entitlementSource,
114
- intentArming: deps.autoModeFace?.engineArmsOnIntent === true,
115
- ...autoMode,
116
- },
117
- readFace: deps.config.readFace ?? null,
118
- writeProtection: projectWriteProtectionCapability(deps.writeProtection),
119
- appendSystemPrompt: true,
120
- compactionModel: true,
121
- subagentOutput: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
122
- subagentStream: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
123
- taskHandles: Boolean(deps.taskHandleOutput) && Boolean(deps.taskHandleStop) && Boolean(deps.runStore),
124
- taskAgents: deps.config.requirePrincipal !== true,
125
- retainBackgroundProcesses: deps.config.requirePrincipal !== true,
126
- interactiveTools: true,
127
- projectContext: cwdHonored(deps.config),
128
- callerCwd: cwdHonored(deps.config) || deviceCwdHonored(deps.config),
129
- mcpInjection: mcpInjectionHonored(deps.config),
130
- a2a: true,
131
- a2aInjection: a2aInjectionHonored(deps.config),
132
- a2aServe: deps.config.a2aServe !== undefined,
133
- modelUsage: Boolean(deps.runStore && deps.modelUsage),
134
- sessionInit: true,
135
- mcp: true,
136
- mcpElicitation: Boolean(deps.elicitation),
137
- askUserQuestion: Boolean(deps.question),
138
- toolApproval: Boolean(deps.toolApproval),
139
- streamApproval: resolveStreamApprovalGate({
140
- toolApprovalEnabled: Boolean(deps.toolApproval),
141
- streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
142
- backend: deps.backend,
143
- parkFacility: deps.checkpointStore !== undefined,
144
- }).active,
145
- approvalDecisionNote: true,
146
- sideQuery: true,
147
- agentRoster: Boolean(deps.backgroundAgentStore),
148
- retention: retentionAdvertisement(deps.config),
149
- sessionList: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
150
- sessionSearch: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
151
- sessionFork: Boolean(deps.sessionStorage?.fork),
152
- sessionDelete: Boolean(deps.purgeSession),
153
- resumeAt: Boolean(deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
154
- promptSuggestions: true,
155
- sessionPolicy: Boolean(deps.sessionPolicyStore),
156
- rewindFiles: Boolean(deps.fileHistoryStore),
157
- restoreFiles: Boolean(deps.fileHistoryStore),
158
- rewindFilesTo: Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
159
- scheduler: Boolean(deps.config.schedulerEnabled && deps.config.requirePrincipal !== true && deps.config.remoteExec?.provider === "host"),
160
- sessionSync: Boolean(deps.backend && deps.sessionStorage?.exportEntries && deps.sessionStorage?.beginImportStaging && deps.fileHistoryStore),
161
- sendUserFile: Boolean(deps.sendUserFile && deps.config.sendUserFile?.publicEndpoint),
162
- s3PublicEndpoint: deps.config.sendUserFile?.publicEndpoint ?? null,
163
- sendUserFileLedger: Boolean(deps.sendFileLedger),
164
- workflows: workflowsEngineCan && workflowsDenial === null,
165
- workflowsList: Boolean(deps.workflowRunStore),
166
- workflowsGate: { engineCan: workflowsEngineCan, denial: workflowsDenial },
167
- workflowModels: (() => {
168
- const allow = deps.workflowModelAllowlist
169
- ?? workflowModelAllowlistFor({
170
- selfOrchestrationModels: deps.config.selfOrchestrationModels ?? [],
171
- requirePrincipal: deps.config.requirePrincipal,
172
- models: deps.config.models ?? {},
173
- })
174
- ?? [];
175
- const bare = deps.config.models ?? {};
176
- const live = expandTiers(bare, deps.config.tiers ?? {}) ?? bare;
177
- return allow.filter((n) => Object.hasOwn(live, n));
178
- })(),
179
- manualCompact: Boolean(deps.runStore),
180
- configCatalog: deps.config.configCatalogEnabled === true && deps.config.operatorPrincipals.length > 0,
181
- });
182
- return;
183
- }
184
- if (req.method === "GET" && url === "/v1/models") {
185
- const atCatalog = expandTiers(deps.config.models, deps.config.tiers ?? {}) ?? deps.config.models;
186
- const atAllowlist = deps.config.atModelAllowlist ?? [];
187
- const models = Object.entries(deps.config.models)
188
- .filter(([name]) => name !== "default")
189
- .map(([name, m]) => ({
190
- name,
191
- id: m.id,
192
- provider: m.provider,
193
- reasoning: m.reasoning,
194
- vision: Array.isArray(m.input) ? m.input.includes("image") : false,
195
- ...(m.contextWindow ? { contextWindow: m.contextWindow } : {}),
196
- ...(m.maxTokens ? { maxOutputTokens: m.maxTokens } : {}),
197
- ...((levels) => (levels !== undefined ? { supportedEffortLevels: [...levels] } : {}))(reasoningEffortLevelsOf(m)),
198
- ...(m.compat !== undefined ? { compat: m.compat } : {}),
199
- ...(atAllowlist.length > 0 ? { atMentionable: isModelAllowlisted(name, atCatalog, atAllowlist) } : {}),
200
- routePairing: routePairingOf(deps, name, m),
201
- }));
202
- const nonAlias = Object.entries(deps.config.models).filter(([n]) => n !== "default");
203
- const defaultName = (nonAlias.find(([, m]) => m === deps.config.model) ?? nonAlias.find(([, m]) => m.id === deps.config.model.id))?.[0];
204
- sendJson(res, 200, { models, default: deps.config.model.id, defaultModel: { id: deps.config.model.id, ...(defaultName ? { name: defaultName } : {}) } });
205
- return;
181
+ case "models": {
182
+ const atCatalog = expandTiers(deps.config.models, deps.config.tiers ?? {}) ?? deps.config.models;
183
+ const atAllowlist = deps.config.atModelAllowlist ?? [];
184
+ const models = Object.entries(deps.config.models)
185
+ .filter(([name]) => name !== "default")
186
+ .map(([name, m]) => ({
187
+ name,
188
+ id: m.id,
189
+ provider: m.provider,
190
+ reasoning: m.reasoning,
191
+ vision: Array.isArray(m.input) ? m.input.includes("image") : false,
192
+ ...(m.contextWindow ? { contextWindow: m.contextWindow } : {}),
193
+ ...(m.maxTokens ? { maxOutputTokens: m.maxTokens } : {}),
194
+ ...((levels) => (levels !== undefined ? { supportedEffortLevels: [...levels] } : {}))(reasoningEffortLevelsOf(m)),
195
+ ...(m.compat !== undefined ? { compat: m.compat } : {}),
196
+ ...(atAllowlist.length > 0 ? { atMentionable: isModelAllowlisted(name, atCatalog, atAllowlist) } : {}),
197
+ routePairing: routePairingOf(deps, name, m),
198
+ }));
199
+ const nonAlias = Object.entries(deps.config.models).filter(([n]) => n !== "default");
200
+ const defaultName = (nonAlias.find(([, m]) => m === deps.config.model) ?? nonAlias.find(([, m]) => m.id === deps.config.model.id))?.[0];
201
+ sendJson(res, 200, { models, default: deps.config.model.id, defaultModel: { id: deps.config.model.id, ...(defaultName ? { name: defaultName } : {}) } });
202
+ return;
203
+ }
204
+ default:
205
+ return assertNever(match.decl.id);
206
206
  }
207
- miss.fell = true;
208
207
  }
208
+ export const CAPABILITIES_ROUTES = [
209
+ { id: "models", path: "/v1/models", methods: ["GET"] },
210
+ { id: "capabilities", path: "/v1/capabilities", methods: ["GET"] },
211
+ { id: "capability-scenario", pattern: /^\/v1\/capabilities\/scenarios\/(?<name>.*)$/, label: "/v1/capabilities/scenarios/:name", methods: ["GET"] },
212
+ ];
209
213
  //# sourceMappingURL=capabilities.js.map
@@ -22,7 +22,14 @@
22
22
  * 本文件不再碰值 —— 一处裁剪,机器钉 = test/config-catalog.test.ts 的响应体 canary 扫描格。
23
23
  */
24
24
  import type { IncomingMessage, ServerResponse } from "node:http";
25
- import type { RouteCtx } from "../route-ctx.js";
25
+ import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
26
26
  export declare const CONFIG_CATALOG_PATH = "/v1/config/catalog";
27
- export declare function handleConfigCatalog(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
27
+ export declare function handleConfigCatalog(req: IncomingMessage, res: ServerResponse, match: RouteMatch<ConfigCatalogRouteId>, ctx: RouteCtx): Promise<void>;
28
+ export declare const CONFIG_CATALOG_ROUTES: readonly [{
29
+ readonly id: "config-catalog";
30
+ readonly path: "/v1/config/catalog";
31
+ readonly methods: readonly ["GET"];
32
+ }];
33
+ /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
34
+ export type ConfigCatalogRouteId = RouteIdsOf<typeof CONFIG_CATALOG_ROUTES>;
28
35
  //# sourceMappingURL=config-catalog.d.ts.map
@@ -1,42 +1,39 @@
1
1
  import { DOMAINS } from "@sema-agent/settings-schema";
2
2
  import { sendJson, sendError } from "../send.js";
3
3
  import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
4
+ import { assertNever } from "../route-ctx.js";
4
5
  import { configKnobTable } from "../../config.js";
5
6
  import { buildConfigCatalog } from "../../config-catalog.js";
6
7
  export const CONFIG_CATALOG_PATH = "/v1/config/catalog";
7
8
  const OPERATOR_ONLY_MESSAGE = "the config catalog is operator-only: the caller principal is not in OPERATOR_PRINCIPALS (an empty list means NO one is an operator — configure it to enable this endpoint)";
8
- export async function handleConfigCatalog(req, res, url, ctx) {
9
- const miss = { fell: false };
10
- handleConfigCatalogBody(req, res, url, ctx, miss);
11
- return !miss.fell;
12
- }
13
- function handleConfigCatalogBody(req, res, url, ctx, miss) {
9
+ export async function handleConfigCatalog(req, res, match, ctx) {
14
10
  const { deps } = ctx;
15
- const path = url.split("?")[0] ?? url;
16
- if (req.method !== "GET" || path !== CONFIG_CATALOG_PATH) {
17
- miss.fell = true;
18
- return;
19
- }
20
- if (deps.config.configCatalogEnabled !== true) {
21
- miss.fell = true;
22
- return;
23
- }
24
- const principal = gatedPrincipal(req, deps.config);
25
- if (deps.config.requirePrincipal && !principal) {
26
- sendPrincipalRefusal(res, req, deps.config);
27
- return;
28
- }
29
- if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
30
- sendError(res, 403, "auth.operator_only", OPERATOR_ONLY_MESSAGE);
31
- return;
11
+ switch (match.decl.id) {
12
+ case "config-catalog": {
13
+ const principal = gatedPrincipal(req, deps.config);
14
+ if (deps.config.requirePrincipal && !principal) {
15
+ sendPrincipalRefusal(res, req, deps.config);
16
+ return;
17
+ }
18
+ if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
19
+ sendError(res, 403, "auth.operator_only", OPERATOR_ONLY_MESSAGE);
20
+ return;
21
+ }
22
+ res.setHeader("Cache-Control", "no-store");
23
+ sendJson(res, 200, buildConfigCatalog({
24
+ config: deps.config,
25
+ env: process.env,
26
+ knobs: configKnobTable(),
27
+ centerDomains: DOMAINS,
28
+ centerManagedKeys: deps.configCenterManagedKeys?.(),
29
+ }));
30
+ return;
31
+ }
32
+ default:
33
+ return assertNever(match.decl.id);
32
34
  }
33
- res.setHeader("Cache-Control", "no-store");
34
- sendJson(res, 200, buildConfigCatalog({
35
- config: deps.config,
36
- env: process.env,
37
- knobs: configKnobTable(),
38
- centerDomains: DOMAINS,
39
- centerManagedKeys: deps.configCenterManagedKeys?.(),
40
- }));
41
35
  }
36
+ export const CONFIG_CATALOG_ROUTES = [
37
+ { id: "config-catalog", path: CONFIG_CATALOG_PATH, methods: ["GET"] },
38
+ ];
42
39
  //# sourceMappingURL=config-catalog.js.map
@@ -53,12 +53,34 @@
53
53
  * 编译器执行,两道门同时成立;device-store.ts「禁手抄」条的本义是禁**脱离闭集**的字面量)。
54
54
  */
55
55
  import type { IncomingMessage, ServerResponse } from "node:http";
56
- import type { RouteCtx } from "../route-ctx.js";
56
+ import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
57
57
  import { type DeviceOwner } from "../../device-store.js";
58
58
  /**
59
59
  * 调用方 device 域的**唯一铸造点**(文件头注)。undefined = 判不出域(无 principal,或无 tenant
60
60
  * claim)——消费点必须走 explicit operator 或响亮 403,禁静默空视图。
61
61
  */
62
62
  export declare function deviceOwnerDomainOf(req: IncomingMessage, config: RouteCtx["deps"]["config"]): DeviceOwner | undefined;
63
- export declare function handleDevices(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
63
+ export declare function handleDevices(req: IncomingMessage, res: ServerResponse, match: RouteMatch<DevicesRouteId>, ctx: RouteCtx): Promise<void>;
64
+ export declare const DEVICES_ROUTES: readonly [{
65
+ readonly id: "devices-list";
66
+ readonly path: "/v1/devices";
67
+ readonly credentialGated: readonly ["GET"];
68
+ readonly methods: readonly ["GET"];
69
+ }, {
70
+ readonly id: "device-session-rebind";
71
+ readonly pattern: RegExp;
72
+ readonly label: "/v1/devices/sessions/:id/rebind";
73
+ readonly methods: readonly ["POST"];
74
+ }, {
75
+ readonly id: "device-session";
76
+ readonly pattern: RegExp;
77
+ readonly label: "/v1/devices/sessions/:id";
78
+ readonly credentialGated: readonly ["GET"];
79
+ readonly methods: readonly ["GET"];
80
+ }, {
81
+ readonly prefix: "/v1/devices/";
82
+ readonly credentialGated: readonly ["POST"];
83
+ }];
84
+ /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
85
+ export type DevicesRouteId = RouteIdsOf<typeof DEVICES_ROUTES>;
64
86
  //# sourceMappingURL=devices.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { sendError, sendJson } from "../send.js";
2
2
  import { explicitOperatorOk, gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
3
+ import { assertNever } from "../route-ctx.js";
3
4
  import { ssoVerifiedScope } from "../../security.js";
4
5
  import { assertDeviceIdShape, assertRootSessionIdShape, sameDeviceOwner, } from "../../device-store.js";
5
6
  const DEVICE_NOT_FOUND_MESSAGE = "device not found";
@@ -39,25 +40,17 @@ function deviceListRow(row, online) {
39
40
  function boundDeviceOf(row) {
40
41
  return { deviceId: row.deviceId, rev: row.rev, boundAt: new Date(row.boundAtMs).toISOString() };
41
42
  }
42
- export async function handleDevices(req, res, url, ctx) {
43
- const miss = { fell: false };
44
- await handleDevicesBody(req, res, url, ctx, miss);
45
- return !miss.fell;
46
- }
47
- async function handleDevicesBody(req, res, url, ctx, miss) {
43
+ export async function handleDevices(req, res, match, ctx) {
44
+ switch (match.decl.id) {
45
+ case "devices-list":
46
+ case "device-session":
47
+ case "device-session-rebind":
48
+ break;
49
+ default:
50
+ return assertNever(match.decl.id);
51
+ }
48
52
  const { deps } = ctx;
49
53
  const { readJson, safeDecode } = ctx.helpers;
50
- if (url !== "/v1/devices" && !url.startsWith("/v1/devices/")) {
51
- miss.fell = true;
52
- return;
53
- }
54
- const listHit = req.method === "GET" && url === "/v1/devices";
55
- const readM = req.method === "GET" ? /^\/v1\/devices\/sessions\/([^/]+)$/.exec(url) : null;
56
- const rebindM = req.method === "POST" ? /^\/v1\/devices\/sessions\/([^/]+)\/rebind$/.exec(url) : null;
57
- if (!listHit && !readM && !rebindM) {
58
- miss.fell = true;
59
- return;
60
- }
61
54
  const principal = gatedPrincipal(req, deps.config);
62
55
  if (deps.config.requirePrincipal && principal === undefined) {
63
56
  sendPrincipalRefusal(res, req, deps.config);
@@ -71,7 +64,7 @@ async function handleDevicesBody(req, res, url, ctx, miss) {
71
64
  }
72
65
  const operator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
73
66
  const domain = deviceOwnerDomainOf(req, deps.config);
74
- if (listHit) {
67
+ if (match.decl.id === "devices-list") {
75
68
  if (!operator && domain === undefined) {
76
69
  sendError(res, 403, "auth.forbidden", NO_DEVICE_DOMAIN_MESSAGE);
77
70
  return;
@@ -81,8 +74,7 @@ async function handleDevicesBody(req, res, url, ctx, miss) {
81
74
  sendJson(res, 200, { devices: rows.filter((r) => r.status !== "revoked").map((r) => deviceListRow(r, online)) });
82
75
  return;
83
76
  }
84
- const seg = (readM ?? rebindM)[1];
85
- const decoded = safeDecode(seg);
77
+ const decoded = safeDecode(match.params.rootSessionId);
86
78
  if (decoded === null) {
87
79
  sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
88
80
  return;
@@ -92,7 +84,7 @@ async function handleDevicesBody(req, res, url, ctx, miss) {
92
84
  return;
93
85
  }
94
86
  const rootSessionId = decoded;
95
- if (readM) {
87
+ if (match.decl.id === "device-session") {
96
88
  if (!operator && domain === undefined) {
97
89
  sendError(res, 403, "auth.forbidden", NO_DEVICE_DOMAIN_MESSAGE);
98
90
  return;
@@ -170,4 +162,10 @@ async function handleDevicesBody(req, res, url, ctx, miss) {
170
162
  }
171
163
  }
172
164
  }
165
+ export const DEVICES_ROUTES = [
166
+ { id: "devices-list", path: "/v1/devices", credentialGated: ["GET"], methods: ["GET"] },
167
+ { id: "device-session-rebind", pattern: /^\/v1\/devices\/sessions\/(?<rootSessionId>[^/]+)\/rebind$/, label: "/v1/devices/sessions/:id/rebind", methods: ["POST"] },
168
+ { id: "device-session", pattern: /^\/v1\/devices\/sessions\/(?<rootSessionId>[^/]+)$/, label: "/v1/devices/sessions/:id", credentialGated: ["GET"], methods: ["GET"] },
169
+ { prefix: "/v1/devices/", credentialGated: ["POST"] },
170
+ ];
173
171
  //# sourceMappingURL=devices.js.map
@@ -13,7 +13,7 @@ import { type PostureKnobReading, type PostureSource } from "../../posture-sourc
13
13
  import type { IncomingMessage, ServerResponse } from "node:http";
14
14
  import type { WiringManifest } from "@sema-agent/core";
15
15
  import { type StreamApprovalGate, type StreamApprovalGateInput } from "../../tool-approval.js";
16
- import type { RouteCtx } from "../route-ctx.js";
16
+ import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
17
17
  /** 流内审批门的读数形:上场即 `"active"`,否则是那五个合取项里**第一个**不满足的原因词
18
18
  * (与 `resolveStreamApprovalGate` 同一闭集,新增 reason 在此处编译红)。 */
19
19
  export type StreamApprovalGateReading = "active" | Extract<StreamApprovalGate, {
@@ -112,5 +112,12 @@ export declare function zeroCredentialLoopbackDenied(remoteAddress: string | und
112
112
  authToken?: string;
113
113
  authTokens?: Record<string, string>;
114
114
  }): boolean;
115
- export declare function handleDiagnostics(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
115
+ export declare function handleDiagnostics(req: IncomingMessage, res: ServerResponse, match: RouteMatch<DiagnosticsRouteId>, ctx: RouteCtx): Promise<void>;
116
+ export declare const DIAGNOSTICS_ROUTES: readonly [{
117
+ readonly id: "diagnostics-wiring";
118
+ readonly path: "/v1/diagnostics/wiring";
119
+ readonly methods: readonly ["GET"];
120
+ }];
121
+ /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
122
+ export type DiagnosticsRouteId = RouteIdsOf<typeof DIAGNOSTICS_ROUTES>;
116
123
  //# sourceMappingURL=diagnostics.d.ts.map