@sema-agent/server 7.115.0 → 7.116.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 (116) hide show
  1. package/MIGRATION.md +167 -4
  2. package/USAGE.md +6 -3
  3. package/dist/approval-ask-audit-store.d.ts +25 -6
  4. package/dist/approval-ask-audit-store.js +6 -1
  5. package/dist/approval-card.d.ts +8 -4
  6. package/dist/approval-card.js +2 -0
  7. package/dist/approval-crash-convergence.d.ts +60 -0
  8. package/dist/approval-crash-convergence.js +75 -0
  9. package/dist/approval-deny-reasons.d.ts +13 -0
  10. package/dist/approval-deny-reasons.js +2 -0
  11. package/dist/approval.js +2 -1
  12. package/dist/boot/coordinators.js +0 -12
  13. package/dist/boot/execution-env.js +2 -1
  14. package/dist/boot/resolve-spec.js +1 -0
  15. package/dist/boot/stage-07-capability-layer.js +21 -15
  16. package/dist/boot/stores.js +4 -4
  17. package/dist/capabilities/hands-lane.d.ts +0 -23
  18. package/dist/capabilities/hands-lane.js +3 -2
  19. package/dist/capabilities/send-user-file-tool.d.ts +10 -22
  20. package/dist/capabilities/send-user-file-tool.js +1 -8
  21. package/dist/capability-facts.d.ts +296 -0
  22. package/dist/capability-facts.js +354 -0
  23. package/dist/config-center/apply-effective.js +1 -1
  24. package/dist/config.d.ts +16 -13
  25. package/dist/config.js +12 -12
  26. package/dist/http/admission.d.ts +6 -0
  27. package/dist/http/admission.js +22 -5
  28. package/dist/http/principal-gate.d.ts +3 -0
  29. package/dist/http/principal-gate.js +3 -0
  30. package/dist/http/resume-legs.js +10 -1
  31. package/dist/http/retired-skill-manifest.d.ts +23 -0
  32. package/dist/http/retired-skill-manifest.js +22 -0
  33. package/dist/http/route-table.d.ts +3 -2
  34. package/dist/http/route-table.js +5 -5
  35. package/dist/http/routes/a2a-serve.js +14 -21
  36. package/dist/http/routes/admin-config-refresh.js +7 -2
  37. package/dist/http/routes/admin-drain.js +7 -2
  38. package/dist/http/routes/admin-memory-optout.js +20 -6
  39. package/dist/http/routes/adoption.js +19 -5
  40. package/dist/http/routes/agents-roster.js +4 -2
  41. package/dist/http/routes/approvals-assistant.js +41 -18
  42. package/dist/http/routes/background.d.ts +0 -2
  43. package/dist/http/routes/background.js +4 -5
  44. package/dist/http/routes/capabilities.d.ts +2 -1
  45. package/dist/http/routes/capabilities.js +62 -65
  46. package/dist/http/routes/config-catalog.d.ts +4 -5
  47. package/dist/http/routes/config-catalog.js +16 -2
  48. package/dist/http/routes/devices.js +17 -11
  49. package/dist/http/routes/diagnostics.js +6 -1
  50. package/dist/http/routes/fleet.js +4 -2
  51. package/dist/http/routes/images.js +30 -3
  52. package/dist/http/routes/leader.js +7 -1
  53. package/dist/http/routes/mcp-reconnect.js +17 -10
  54. package/dist/http/routes/memory-bundle.js +20 -6
  55. package/dist/http/routes/memory-compliance.js +19 -5
  56. package/dist/http/routes/memory-consolidation.d.ts +9 -12
  57. package/dist/http/routes/memory-consolidation.js +17 -3
  58. package/dist/http/routes/memory-origin.js +19 -5
  59. package/dist/http/routes/notify-wake.js +5 -3
  60. package/dist/http/routes/observability.js +19 -5
  61. package/dist/http/routes/retention-ops.js +6 -1
  62. package/dist/http/routes/rules.js +49 -21
  63. package/dist/http/routes/run-memory-capture.js +5 -2
  64. package/dist/http/routes/runs.js +90 -60
  65. package/dist/http/routes/session-sync.js +64 -53
  66. package/dist/http/routes/sessions-list.js +8 -5
  67. package/dist/http/routes/sessions.js +39 -24
  68. package/dist/http/routes/shared-memory.js +5 -3
  69. package/dist/http/routes/tasks.js +2 -1
  70. package/dist/http/routes/trace-usage.js +18 -11
  71. package/dist/http/routes/workflows.js +36 -13
  72. package/dist/http/server.js +4 -1
  73. package/dist/http/wire-types.d.ts +6 -0
  74. package/dist/namespaced-rule-name.d.ts +54 -0
  75. package/dist/namespaced-rule-name.js +33 -0
  76. package/dist/observability/corrupt-read-seat.d.ts +4 -3
  77. package/dist/observability/corrupt-read-seat.js +4 -3
  78. package/dist/observability/fail-open.d.ts +5 -1
  79. package/dist/observability/fail-open.js +5 -1
  80. package/dist/observability/permission-rule-events.d.ts +3 -0
  81. package/dist/observability/permission-rule-events.js +4 -0
  82. package/dist/plugins/file-run-store.d.ts +7 -1
  83. package/dist/plugins/file-run-store.js +20 -7
  84. package/dist/plugins/memory-run-store.js +2 -1
  85. package/dist/plugins/one-time-migrations.d.ts +4 -2
  86. package/dist/plugins/one-time-migrations.js +26 -3
  87. package/dist/plugins/pg-session-storage.js +1 -1
  88. package/dist/plugins/run-store-sql.js +4 -4
  89. package/dist/plugins/session-policy-store-sql.js +4 -2
  90. package/dist/plugins/store-backend.d.ts +6 -1
  91. package/dist/plugins/store-backend.js +2 -1
  92. package/dist/plugins/store-contracts.d.ts +27 -8
  93. package/dist/plugins/store-contracts.js +12 -3
  94. package/dist/plugins/tidb-session-store.js +1 -1
  95. package/dist/request-key-closure.js +1 -1
  96. package/dist/run-local.d.ts +37 -3
  97. package/dist/run-local.js +48 -5
  98. package/dist/runs.d.ts +0 -58
  99. package/dist/runs.js +15 -20
  100. package/dist/session-policy-wire.d.ts +40 -60
  101. package/dist/session-policy-wire.js +60 -32
  102. package/dist/spec-fields.d.ts +32 -0
  103. package/dist/spec-fields.js +31 -16
  104. package/dist/standing-answer-grant.d.ts +8 -0
  105. package/dist/standing-answer-grant.js +5 -0
  106. package/dist/task-settings.d.ts +13 -3
  107. package/dist/tool-approval.d.ts +16 -0
  108. package/dist/tool-approval.js +29 -7
  109. package/dist/trace/core-keyset-guard.d.ts +4 -4
  110. package/dist/trace/self-minted-wire.d.ts +42 -0
  111. package/dist/trace/self-minted-wire.js +13 -0
  112. package/dist/user-input-ledger.d.ts +8 -5
  113. package/package.json +4 -2
  114. package/scripts/drain-check-local.mjs +115 -25
  115. package/scripts/drain-check.mysql.sql +67 -0
  116. package/scripts/drain-check.pg.sql +71 -0
@@ -1,7 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { sendJson, sendError, httpErrorCode } from "../send.js";
3
- import { gatedPrincipal, explicitOperatorOk, sendUnauthorized } from "../principal-gate.js";
3
+ import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendUnauthorized } from "../principal-gate.js";
4
4
  import { assertNever } from "../route-ctx.js";
5
+ import * as facts from "../../capability-facts.js";
5
6
  import { createLogger } from "../../observability/logger.js";
6
7
  import { RULE_IMPORT_RETRY_AFTER_SEC, MAX_RULE_SCOPE_CHARS, parseRuleScope, serializeRuleScope } from "../../rules-consent.js";
7
8
  import { CONTROL_AND_BIDI_CHARS } from "../../text-bidi.js";
@@ -93,13 +94,14 @@ export async function handleRules(req, res, match, ctx) {
93
94
  sendUnauthorized(res, "identity");
94
95
  return;
95
96
  }
96
- const lane = deps.ruleConsent;
97
- if (lane === undefined) {
97
+ const ruleFact = facts.ruleLane(deps);
98
+ if (!ruleFact.available) {
98
99
  sendError(res, 501, "capability.rule_store_required", "this deployment has no permission-rule store wired");
99
100
  return;
100
101
  }
102
+ const lane = ruleFact.lane;
101
103
  if (match.decl.id === "rules-list") {
102
- await handleRuleList(res, match.search, principal, lane, deps.config.operatorPrincipals);
104
+ await handleRuleList(res, match.search, principal, lane, deps.config);
103
105
  return;
104
106
  }
105
107
  let rawBody;
@@ -115,11 +117,11 @@ export async function handleRules(req, res, match, ctx) {
115
117
  return;
116
118
  }
117
119
  if (match.decl.id === "rules-revoke") {
118
- await handleRuleRevoke(res, rawBody, principal, lane, deps.config.operatorPrincipals);
120
+ await handleRuleRevoke(res, rawBody, principal, lane, deps.config);
119
121
  return;
120
122
  }
121
123
  if (match.decl.id === "rules-write") {
122
- await handleRuleWrite(res, rawBody, principal, lane, deps.config.operatorPrincipals);
124
+ await handleRuleWrite(res, rawBody, principal, lane, deps.config);
123
125
  return;
124
126
  }
125
127
  if (match.decl.id === "rules-local-import-prepare") {
@@ -201,19 +203,29 @@ export async function handleRules(req, res, match, ctx) {
201
203
  }
202
204
  sendJson(res, 200, { result: redeemed.result });
203
205
  }
204
- function resolveTargetPrincipal(requested, caller, operatorPrincipals) {
206
+ function resolveTargetPrincipal(requested, caller, config) {
205
207
  if (requested === undefined || requested === caller)
206
208
  return { ok: true, principal: caller };
207
- if (!explicitOperatorOk(caller, operatorPrincipals))
208
- return { ok: false };
209
+ if (!facts.operatorLane({ config }).available)
210
+ return { ok: false, why: "operator_principals_empty" };
211
+ if (!explicitOperatorOk(caller, config.operatorPrincipals))
212
+ return { ok: false, why: "not_operator" };
209
213
  return { ok: true, principal: requested };
210
214
  }
211
- async function handleRuleList(res, search, caller, lane, operatorPrincipals) {
215
+ async function handleRuleList(res, search, caller, lane, config) {
212
216
  const q = new URLSearchParams(search);
213
- const target = resolveTargetPrincipal(q.get("principal") ?? undefined, caller, operatorPrincipals);
217
+ const target = resolveTargetPrincipal(q.get("principal") ?? undefined, caller, config);
214
218
  if (!target.ok) {
215
- sendError(res, 403, "auth.operator_only", "reading another principal's rules is an operator-only action");
216
- return;
219
+ switch (target.why) {
220
+ case "operator_principals_empty":
221
+ sendOperatorPrincipalsRequired(res);
222
+ return;
223
+ case "not_operator":
224
+ sendError(res, 403, "auth.operator_only", "reading another principal's rules is an operator-only action");
225
+ return;
226
+ default:
227
+ return assertNever(target.why);
228
+ }
217
229
  }
218
230
  const limit = Math.min(RULES_PAGE_MAX, Math.max(1, Number(q.get("limit") ?? RULES_PAGE_DEFAULT) || RULES_PAGE_DEFAULT));
219
231
  const scopeRaw = q.get("scope");
@@ -280,7 +292,7 @@ function sendRuleWriteFailure(res, verb, committed) {
280
292
  }
281
293
  }
282
294
  }
283
- async function handleRuleWrite(res, rawBody, caller, lane, operatorPrincipals) {
295
+ async function handleRuleWrite(res, rawBody, caller, lane, config) {
284
296
  const parsed = RuleIdentityBodySchema.safeParse(rawBody);
285
297
  if (!parsed.success) {
286
298
  sendError(res, 400, "request.body_shape", "body must be {behavior, rule, scope} with an optional principal");
@@ -296,10 +308,18 @@ async function handleRuleWrite(res, rawBody, caller, lane, operatorPrincipals) {
296
308
  sendError(res, 400, "request.body_shape", "scope must be 'global' or 'project:<root>' with a non-empty root");
297
309
  return;
298
310
  }
299
- const target = resolveTargetPrincipal(parsed.data.principal, caller, operatorPrincipals);
311
+ const target = resolveTargetPrincipal(parsed.data.principal, caller, config);
300
312
  if (!target.ok) {
301
- sendError(res, 403, "auth.operator_only", "writing another principal's rules is an operator-only action");
302
- return;
313
+ switch (target.why) {
314
+ case "operator_principals_empty":
315
+ sendOperatorPrincipalsRequired(res);
316
+ return;
317
+ case "not_operator":
318
+ sendError(res, 403, "auth.operator_only", "writing another principal's rules is an operator-only action");
319
+ return;
320
+ default:
321
+ return assertNever(target.why);
322
+ }
303
323
  }
304
324
  const written = await lane.writeRule({ principal: target.principal, behavior, rule: parsed.data.rule, scope });
305
325
  switch (written.status) {
@@ -326,7 +346,7 @@ async function handleRuleWrite(res, rawBody, caller, lane, operatorPrincipals) {
326
346
  }
327
347
  }
328
348
  }
329
- async function handleRuleRevoke(res, rawBody, caller, lane, operatorPrincipals) {
349
+ async function handleRuleRevoke(res, rawBody, caller, lane, config) {
330
350
  const parsed = RuleIdentityBodySchema.safeParse(rawBody);
331
351
  if (!parsed.success) {
332
352
  sendError(res, 400, "request.body_shape", "body must be {behavior, rule, scope} with an optional principal");
@@ -337,10 +357,18 @@ async function handleRuleRevoke(res, rawBody, caller, lane, operatorPrincipals)
337
357
  sendError(res, 400, "request.body_shape", "scope must be 'global' or 'project:<root>' with a non-empty root");
338
358
  return;
339
359
  }
340
- const target = resolveTargetPrincipal(parsed.data.principal, caller, operatorPrincipals);
360
+ const target = resolveTargetPrincipal(parsed.data.principal, caller, config);
341
361
  if (!target.ok) {
342
- sendError(res, 403, "auth.operator_only", "revoking another principal's rules is an operator-only action");
343
- return;
362
+ switch (target.why) {
363
+ case "operator_principals_empty":
364
+ sendOperatorPrincipalsRequired(res);
365
+ return;
366
+ case "not_operator":
367
+ sendError(res, 403, "auth.operator_only", "revoking another principal's rules is an operator-only action");
368
+ return;
369
+ default:
370
+ return assertNever(target.why);
371
+ }
344
372
  }
345
373
  const outcome = await lane.removeRule({ principal: target.principal, behavior: parsed.data.behavior, rule: parsed.data.rule, scope });
346
374
  switch (outcome.status) {
@@ -4,6 +4,7 @@ import { sendJson, sendError } from "../send.js";
4
4
  import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
5
5
  import { RUN_NOT_FOUND_MESSAGE, runNotFoundMessage } from "../route-ctx.js";
6
6
  import { assertNever } from "../route-ctx.js";
7
+ import * as facts from "../../capability-facts.js";
7
8
  const BodySchema = z.object({ reason: z.string().min(1).max(500).optional() }).strict();
8
9
  export async function handleRunMemoryCapture(req, res, match, ctx) {
9
10
  const { deps } = ctx;
@@ -11,10 +12,12 @@ export async function handleRunMemoryCapture(req, res, match, ctx) {
11
12
  case "run-memory-capture-optout": {
12
13
  if (ctx.helpers.rateLimited(req, res))
13
14
  return;
14
- if (!deps.runStore) {
15
+ const runs = facts.durableRuns(deps);
16
+ if (!runs.available) {
15
17
  sendError(res, 501, "capability.run_store_required", "async runs require a durable run store (DB_BACKEND=mysql|pg|local)");
16
18
  return;
17
19
  }
20
+ const runStore = runs.runStore;
18
21
  const principal = gatedPrincipal(req, deps.config);
19
22
  if (deps.config.requirePrincipal && principal === undefined) {
20
23
  sendPrincipalRefusal(res, req, deps.config);
@@ -26,7 +29,7 @@ export async function handleRunMemoryCapture(req, res, match, ctx) {
26
29
  sendError(res, 400, "request.body_shape", "body must be {} or { reason: string (non-empty, at most 500 chars) } — the session memory-capture opt-out verb takes no other keys");
27
30
  return;
28
31
  }
29
- const run = await deps.runStore.getRun(taskId);
32
+ const run = await runStore.getRun(taskId);
30
33
  if (!run) {
31
34
  sendError(res, 404, "not_found.run", runNotFoundMessage(taskId));
32
35
  return;