@sema-agent/server 7.63.0 → 7.64.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 (55) hide show
  1. package/MIGRATION.md +8 -0
  2. package/USAGE.md +17 -15
  3. package/deploy/sema-up/smoke.sh +2 -2
  4. package/dist/approval-ask-audit-store.d.ts +149 -3
  5. package/dist/approval-ask-audit-store.js +11 -4
  6. package/dist/bench/s1/arms.d.ts +2 -2
  7. package/dist/bench/s1/arms.js +20 -15
  8. package/dist/bench/s1/live-deps.js +3 -1
  9. package/dist/bench/s1/run-firm.js +2 -2
  10. package/dist/degenerate-instrument.d.ts +0 -33
  11. package/dist/degenerate-instrument.js +2 -1
  12. package/dist/fleet/fleet-bus.d.ts +9 -4
  13. package/dist/fleet/fleet-bus.js +2 -1
  14. package/dist/host-decision.d.ts +39 -0
  15. package/dist/host-decision.js +6 -0
  16. package/dist/http/route-ctx.d.ts +8 -5
  17. package/dist/http/routes/a2a-serve.js +4 -2
  18. package/dist/http/routes/approvals-assistant.d.ts +0 -13
  19. package/dist/http/routes/approvals-assistant.js +2 -1
  20. package/dist/http/routes/runs.js +3 -2
  21. package/dist/http/routes/tasks.js +33 -29
  22. package/dist/http/routes/workflows.d.ts +0 -11
  23. package/dist/http/routes/workflows.js +18 -14
  24. package/dist/http/server.js +50 -46
  25. package/dist/leader/fanout.d.ts +6 -23
  26. package/dist/leader/fanout.js +8 -19
  27. package/dist/leader/leader.d.ts +13 -10
  28. package/dist/leader/leader.js +30 -22
  29. package/dist/leader/repair-wire.d.ts +4 -1
  30. package/dist/leader/repair-wire.js +7 -5
  31. package/dist/leader/wire.js +5 -4
  32. package/dist/main.js +5 -3
  33. package/dist/observability/fail-open.d.ts +8 -0
  34. package/dist/observability/fail-open.js +8 -0
  35. package/dist/observability/metrics.js +1 -1
  36. package/dist/observability/run-terminal-log.d.ts +25 -7
  37. package/dist/observability/run-terminal-log.js +15 -4
  38. package/dist/observability/tool-trace.d.ts +13 -12
  39. package/dist/observability/tool-trace.js +8 -13
  40. package/dist/parked-decide.d.ts +8 -4
  41. package/dist/parked-decide.js +1 -1
  42. package/dist/plugins/file-run-store.js +12 -21
  43. package/dist/plugins/memory-run-store.js +3 -2
  44. package/dist/plugins/run-store-sql.js +3 -2
  45. package/dist/run-local.js +6 -4
  46. package/dist/runs.d.ts +4 -19
  47. package/dist/runs.js +49 -47
  48. package/dist/terminal.d.ts +100 -0
  49. package/dist/terminal.js +55 -0
  50. package/dist/trace/core-keyset-guard.d.ts +20 -4
  51. package/dist/trace/ledger-sink.d.ts +2 -1
  52. package/dist/trace/ledger-sink.js +14 -7
  53. package/dist/trace/project.d.ts +9 -12
  54. package/dist/trace/project.js +54 -9
  55. package/package.json +4 -4
@@ -17,7 +17,7 @@
17
17
  * ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
18
18
  * TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
19
19
  */
20
- import type { TaskNotificationPayload, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult } from "@sema-agent/core";
20
+ import type { TaskNotificationPayload, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, GateOutcome, Settlement, PausedCause } from "@sema-agent/core";
21
21
  /** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。 */
22
22
  type AssertAllKeysHandled<T extends never> = T;
23
23
  type NotificationProjected = "task_id" | "task_type" | "toolUseId" | "status" | "summary" | "result" | "output_file" | "usage" | "sessionId" | "seq" | "lines" | "stoppedBy" | "source" | "exitCode" | "partial" | "diagnostics" | "recentSteps" | "editedFiles" | "resumable" | "completionId" | "error" | "errorCode" | "apiFailure" | "agentMessage" | "_sema_provenance";
@@ -52,10 +52,20 @@ type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requi
52
52
  type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars";
53
53
  type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
54
54
  type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
55
- type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "status" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "blockedReason" | "checkpointId" | "checkpointGate" | "workspaceRestoreMode" | "remoteEnvFailures" | "errorMessage" | "errorCode" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveMemoryScopes" | "stats" | "editedFiles";
56
- type TaskResultStripped = "checkpointToken";
55
+ type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveMemoryScopes" | "stats" | "editedFiles";
56
+ type TaskResultStripped = never;
57
57
  type _GuardTaskResult = AssertAllKeysHandled<Exclude<keyof TaskResult, TaskResultForwarded | TaskResultStripped>>;
58
58
  type _GuardTaskResultReverse = AssertAllKeysHandled<Exclude<TaskResultForwarded | TaskResultStripped, keyof TaskResult>>;
59
+ type PausedCauseForwarded = "kind" | "gate" | "checkpointId" | "restoreMode";
60
+ type PausedCauseStripped = "token";
61
+ type _GuardPausedCause = AssertAllKeysHandled<Exclude<keyof PausedCause, PausedCauseForwarded | PausedCauseStripped>>;
62
+ type _NoDoubleBookPausedCause = AssertAllKeysHandled<Extract<PausedCauseForwarded, PausedCauseStripped>>;
63
+ type _GuardPausedCauseReverse = AssertAllKeysHandled<Exclude<PausedCauseForwarded | PausedCauseStripped, keyof PausedCause>>;
64
+ type SettlementKeys = Settlement extends infer S ? (S extends object ? keyof S : never) : never;
65
+ type SettlementWhoKeys = Settlement["who"] extends infer W ? (W extends object ? keyof W : never) : never;
66
+ type _GuardGateOutcome = AssertAllKeysHandled<Exclude<keyof GateOutcome, "disposition" | "settlement" | "origin">>;
67
+ type _GuardSettlement = AssertAllKeysHandled<Exclude<SettlementKeys, "kind" | "who" | "when" | "note">>;
68
+ type _GuardSettlementWho = AssertAllKeysHandled<Exclude<SettlementWhoKeys, "party" | "approver" | "window">>;
59
69
  export type CoreKeysetGuards = [
60
70
  _GuardNotification,
61
71
  _GuardBgNotif,
@@ -71,7 +81,13 @@ export type CoreKeysetGuards = [
71
81
  _GuardTaskSpec,
72
82
  _GuardTaskSpecReverse,
73
83
  _GuardTaskResult,
74
- _GuardTaskResultReverse
84
+ _GuardTaskResultReverse,
85
+ _GuardGateOutcome,
86
+ _GuardSettlement,
87
+ _GuardSettlementWho,
88
+ _GuardPausedCause,
89
+ _NoDoubleBookPausedCause,
90
+ _GuardPausedCauseReverse
75
91
  ];
76
92
  export {};
77
93
  //# sourceMappingURL=core-keyset-guard.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { type TerminalCause } from "@sema-agent/core";
1
2
  import { brainStatusEventData } from "./project.js";
2
3
  import { NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
3
4
  import type { LedgerEventType } from "./ledger-events.js";
@@ -73,7 +74,7 @@ export interface LedgerSink {
73
74
  /** Convenience for the sync detach lane (no model-usage interleave there): flush, then project the result
74
75
  * to its park/terminal event exactly like the bg leg's done case. `result` must already be stripped. */
75
76
  onDone(result: {
76
- status?: string;
77
+ terminal: TerminalCause;
77
78
  }): Promise<void>;
78
79
  }
79
80
  /**
@@ -1,3 +1,4 @@
1
+ import { terminalProjection } from "@sema-agent/core";
1
2
  import { redactSecrets } from "./redact.js";
2
3
  import { toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, humanInputEventData, turnEndEventData, contextUsageEventData } from "./project.js";
3
4
  import { NotifiedKeys, taskNotificationStreamKey } from "../orchestration/workflow-completion-inbox.js";
@@ -143,16 +144,22 @@ export function createLedgerSink(opts) {
143
144
  beat("status", statusData);
144
145
  return append("status", statusData);
145
146
  };
146
- const appendParked = (kind, result) => append(kind, {
147
- gate: result?.checkpointGate ?? null,
148
- ...(typeof result?.toolCallId === "string" ? { toolCallId: result.toolCallId } : {}),
149
- });
147
+ const appendParked = (kind, result) => {
148
+ const t = result?.terminal;
149
+ return append(kind, {
150
+ gate: t?.kind === "paused" ? t.gate : null,
151
+ ...(typeof result?.toolCallId === "string" ? { toolCallId: result.toolCallId } : {}),
152
+ });
153
+ };
150
154
  const appendDone = (result) => append("done", { result });
151
155
  const onDone = async (result) => {
152
156
  await flush();
153
- if (result.status === "suspended" || result.status === "needs_review") {
154
- await appendParked(result.status, result);
155
- return;
157
+ if (result.terminal.kind === "paused") {
158
+ const parkKind = terminalProjection(result.terminal).status;
159
+ if (parkKind === "suspended" || parkKind === "needs_review") {
160
+ await appendParked(parkKind, result);
161
+ return;
162
+ }
156
163
  }
157
164
  await appendDone(result);
158
165
  };
@@ -12,8 +12,7 @@
12
12
  * old session-join `toolOutput` hook is gone, there's a real source now); `eventId`/`parentToolCallId` (§E2) ride
13
13
  * each content event, additive / tolerate-absent.
14
14
  */
15
- import type { ApprovalSettledBy, TaskResult, SystemInjectionPriority } from "@sema-agent/core";
16
- import { type AskDenyResolution } from "@sema-agent/core";
15
+ import type { GateOutcome, McpDelivered, TaskResult, SystemInjectionPriority } from "@sema-agent/core";
17
16
  import type { RunEvent, RunRecord } from "../plugins/store-contracts.js";
18
17
  import type { ModelUsageDelta, ModelUsageTracker, PromptManifestRecord } from "../budget.js";
19
18
  import { type SemaProvenanceInput } from "./sema-provenance.js";
@@ -95,10 +94,8 @@ export type TraceBlock = {
95
94
  totalChars?: number;
96
95
  structured?: unknown;
97
96
  errorCode?: string;
98
- settledBy?: ApprovalSettledBy;
99
- resolution?: AskDenyResolution;
100
- autoDenied?: true;
101
- approver?: string;
97
+ delivered?: McpDelivered;
98
+ gate?: GateOutcome;
102
99
  gatedCallId?: string;
103
100
  eventId?: string;
104
101
  parentToolCallId?: string;
@@ -122,6 +119,7 @@ export declare function toolStartEventData(ev: {
122
119
  eventId?: string;
123
120
  parentToolCallId?: string;
124
121
  }): Record<string, unknown>;
122
+ export declare function screenedGate(v: unknown): GateOutcome | undefined;
125
123
  /** The durable `tool_end` payload (shared by both append sites). E1: the model-facing `output` is UNTRUSTED RAW
126
124
  * from core (file/shell/API bodies) — core only SIZE-bounds it (`truncated`), REDACTION is ours (same contract as
127
125
  * `tool_start.args`); never re-fed to a model. E2: identity. Absent `output` ⇒ the tool produced no content body.
@@ -141,10 +139,8 @@ export declare function toolEndEventData(ev: {
141
139
  totalChars?: number;
142
140
  structured?: unknown;
143
141
  errorCode?: string;
144
- settledBy?: ApprovalSettledBy;
145
- resolution?: AskDenyResolution;
146
- autoDenied?: true;
147
- approver?: string;
142
+ delivered?: McpDelivered;
143
+ gate?: GateOutcome;
148
144
  gatedCallId?: string;
149
145
  eventId?: string;
150
146
  parentToolCallId?: string;
@@ -389,8 +385,9 @@ export declare function workspaceChangedEventData(ev: {
389
385
  * 那条端点的真源是 server 自己的部署面台账(server 自铸的二态),本段的真源是 core 这条腿
390
386
  * materialize 的连接时快照;同名不同源,合并只会造出一个「哪个才算数」的新问题。
391
387
  * ② **值的形**:`name`(spec 声明名)/ `source`(声明方自述的层级标签,core 已 single-line + 长度封顶
392
- * 逐字回显)/ `status`(三词闭集)/ `errorCode`(core `MCP_FAILURE_CODES` 十词闭集 + `http_<status>`)/
393
- * `toolCount`(有限数)—— 全是引擎或**声明方**产的闭形值,非用户内容 ⇒ 不脱敏、逐字。
388
+ * 逐字回显)/ `status`(三词闭集)/ `errorCode`(core 7.6.0 起 `McpFailureKind` 十词闭集)/
389
+ * `delivered`(投递判词三词闭集)/ `httpStatus`(有限数)/ `toolCount`(有限数)—— 全是引擎或
390
+ * **声明方**产的闭形值,非用户内容 ⇒ 不脱敏、逐字。
394
391
  * ⚠️ `errorCode` **不是纯闭集**:十个具名词之外还有 `http_<status>` 形(core 自陈)⇒ 本仓既不枚举
395
392
  * 那十个词、也不给它套形正则(词的单一属主在 core;抄一份会把 core 加的新词与新形一起吞成缺席),
396
393
  * 只判非空串,消费端按具名词写的 switch 必须带 default 臂。
@@ -1,5 +1,6 @@
1
1
  import { redactErrorMessage } from "../observability/run-terminal-log.js";
2
- import { isApprovalSettledBy, isAskDenyResolution } from "@sema-agent/core";
2
+ import { MCP_DELIVERY_VERDICTS, screenGateOutcome } from "@sema-agent/core";
3
+ import { recordFailOpen } from "../observability/fail-open.js";
3
4
  import { redactSecrets, redactDeep } from "./redact.js";
4
5
  import { injectionTierOrAbsent } from "./injection-tier.js";
5
6
  import { semaProvenanceOrAbsent } from "./sema-provenance.js";
@@ -83,7 +84,52 @@ function identityFields(src) {
83
84
  export function toolStartEventData(ev) {
84
85
  return { toolCallId: ev.toolCallId, toolName: ev.toolName, ...(ev.label !== undefined ? { label: ev.label } : {}), args: redactDeep(ev.args), ...identityFields(ev) };
85
86
  }
87
+ const SETTLEMENT_NOTE_MAX = 500;
88
+ export function screenedGate(v) {
89
+ if (v === null || typeof v !== "object")
90
+ return undefined;
91
+ const g = v;
92
+ const d = g.disposition;
93
+ if (d === null || typeof d !== "object" || (d.kind !== "allowed" && d.kind !== "denied")) {
94
+ recordFailOpen("server.trace.gate-outcome-defective", "disposition is not one of the two arms");
95
+ return undefined;
96
+ }
97
+ const defects = screenGateOutcome(g);
98
+ if (defects.length > 0) {
99
+ recordFailOpen("server.trace.gate-outcome-defective", defects.join("; "));
100
+ return undefined;
101
+ }
102
+ const settlement = g.settlement === undefined ? undefined : projectSettlement(g.settlement);
103
+ if (g.settlement !== undefined && settlement === undefined) {
104
+ recordFailOpen("server.trace.gate-outcome-defective", "settlement.who is not an object with a party in the closed set");
105
+ return undefined;
106
+ }
107
+ return {
108
+ disposition: d.kind === "denied" ? { kind: "denied", deniedBy: d.deniedBy } : { kind: "allowed" },
109
+ ...(settlement === undefined ? {} : { settlement }),
110
+ ...(g.origin === undefined ? {} : { origin: g.origin }),
111
+ };
112
+ }
113
+ const SETTLEMENT_PARTIES = new Set(["person", "engine", "host", "none"]);
114
+ function projectSettlement(st) {
115
+ const whoV = st.who;
116
+ const partyV = whoV === null || typeof whoV !== "object" ? undefined : whoV.party;
117
+ if (typeof partyV !== "string" || !SETTLEMENT_PARTIES.has(partyV))
118
+ return undefined;
119
+ const who = whoV;
120
+ return {
121
+ kind: st.kind,
122
+ when: st.when,
123
+ who: {
124
+ party: who.party,
125
+ ...(typeof who.approver === "string" && who.approver !== "" ? { approver: redactSecrets(who.approver) } : {}),
126
+ ...(typeof who.window === "string" ? { window: who.window } : {}),
127
+ },
128
+ ...(typeof st.note === "string" ? { note: redactSecrets(st.note).slice(0, SETTLEMENT_NOTE_MAX) } : {}),
129
+ };
130
+ }
86
131
  export function toolEndEventData(ev) {
132
+ const gate = ev.gate === undefined ? undefined : screenedGate(ev.gate);
87
133
  return {
88
134
  toolCallId: ev.toolCallId,
89
135
  toolName: ev.toolName,
@@ -94,10 +140,8 @@ export function toolEndEventData(ev) {
94
140
  ...(Number.isFinite(ev.totalChars) ? { totalChars: ev.totalChars } : {}),
95
141
  ...(ev.structured !== undefined ? { structured: redactDeep(ev.structured) } : {}),
96
142
  ...(typeof ev.errorCode === "string" && ev.errorCode.length <= 128 ? { errorCode: ev.errorCode } : {}),
97
- ...(isApprovalSettledBy(ev.settledBy) ? { settledBy: ev.settledBy } : {}),
98
- ...(isAskDenyResolution(ev.resolution) ? { resolution: ev.resolution } : {}),
99
- ...(ev.autoDenied === true ? { autoDenied: true } : {}),
100
- ...(typeof ev.approver === "string" && ev.approver !== "" ? { approver: redactSecrets(ev.approver) } : {}),
143
+ ...(typeof ev.delivered === "string" && MCP_DELIVERY_VERDICTS.includes(ev.delivered) ? { delivered: ev.delivered } : {}),
144
+ ...(gate !== undefined ? { gate } : {}),
101
145
  ...(typeof ev.gatedCallId === "string" && ev.gatedCallId !== "" ? { gatedCallId: ev.gatedCallId } : {}),
102
146
  ...identityFields(ev),
103
147
  };
@@ -289,6 +333,8 @@ export function wiringManifestEventData(ev) {
289
333
  ...(num(e.toolCount) !== undefined ? { toolCount: e.toolCount } : {}),
290
334
  ...(str(e.source) !== undefined ? { source: e.source } : {}),
291
335
  ...(str(e.errorCode) !== undefined ? { errorCode: e.errorCode } : {}),
336
+ ...(typeof e.delivered === "string" && MCP_DELIVERY_VERDICTS.includes(e.delivered) ? { delivered: e.delivered } : {}),
337
+ ...(num(e.httpStatus) !== undefined ? { httpStatus: e.httpStatus } : {}),
292
338
  });
293
339
  }
294
340
  return rows;
@@ -447,6 +493,7 @@ export function toolCallFieldsOf(d) {
447
493
  };
448
494
  }
449
495
  export function toolResultFieldsOf(d) {
496
+ const gate = d.gate === undefined ? undefined : screenedGate(d.gate);
450
497
  return {
451
498
  callId: String(d.toolCallId ?? ""),
452
499
  ...(typeof d.label === "string" ? { label: d.label } : {}),
@@ -454,10 +501,8 @@ export function toolResultFieldsOf(d) {
454
501
  ...(Number.isFinite(d.totalChars) ? { totalChars: d.totalChars } : {}),
455
502
  ...(d.structured !== undefined ? { structured: d.structured } : {}),
456
503
  ...(typeof d.errorCode === "string" ? { errorCode: d.errorCode } : {}),
457
- ...(isApprovalSettledBy(d.settledBy) ? { settledBy: d.settledBy } : {}),
458
- ...(typeof d.approver === "string" && d.approver !== "" ? { approver: d.approver } : {}),
459
- ...(isAskDenyResolution(d.resolution) ? { resolution: d.resolution } : {}),
460
- ...(d.autoDenied === true ? { autoDenied: true } : {}),
504
+ ...(typeof d.delivered === "string" && MCP_DELIVERY_VERDICTS.includes(d.delivered) ? { delivered: d.delivered } : {}),
505
+ ...(gate !== undefined ? { gate } : {}),
461
506
  ...(typeof d.gatedCallId === "string" && d.gatedCallId !== "" ? { gatedCallId: d.gatedCallId } : {}),
462
507
  ...identityFields(d),
463
508
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.63.0",
3
+ "version": "7.64.0",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -54,8 +54,8 @@
54
54
  "build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
55
55
  },
56
56
  "dependencies": {
57
- "@sema-agent/core": "7.5.2",
58
- "@sema-agent/settings-schema": "1.7.2",
57
+ "@sema-agent/core": "7.6.2",
58
+ "@sema-agent/settings-schema": "1.8.0",
59
59
  "e2b": "^2.28.0",
60
60
  "libsodium-wrappers": "^0.8.4",
61
61
  "mysql2": "^3.22.4",
@@ -69,7 +69,7 @@
69
69
  "sharp": "^0.35.3"
70
70
  },
71
71
  "devDependencies": {
72
- "@sema-agent/sdk": "8.3.0",
72
+ "@sema-agent/sdk": "8.4.0",
73
73
  "@types/libsodium-wrappers": "^0.7.14",
74
74
  "@types/node": "22.10.2",
75
75
  "@types/pg": "^8.20.0",