@sema-agent/server 7.140.0 → 7.142.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.
- package/MIGRATION.md +505 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +142 -25
- package/dist/approval-ask-audit-store.d.ts +54 -38
- package/dist/approval-ask-audit-store.js +58 -39
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +39 -24
- package/dist/approval-card.js +6 -3
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +33 -1
- package/dist/approval-reconciler.js +43 -40
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +10 -0
- package/dist/audit.d.ts +4 -2
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +16 -0
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +11 -0
- package/dist/boot/device-lane.js +16 -0
- package/dist/boot/engine-lease.d.ts +24 -3
- package/dist/boot/engine-lease.js +9 -2
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +34 -27
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/memory-consolidation.js +3 -2
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/reapers.js +4 -3
- package/dist/boot/resolve-spec.js +18 -6
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.js +16 -13
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +8 -6
- package/dist/boot/shutdown.js +8 -2
- package/dist/boot/stage-04-stores.js +3 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.js +11 -7
- package/dist/boot/stage-06-runners.d.ts +1 -3
- package/dist/boot/stage-06-runners.js +3 -2
- package/dist/boot/stage-07-capability-layer.d.ts +1 -2
- package/dist/boot/stage-07-capability-layer.js +10 -11
- package/dist/boot/stage-08-reapers.d.ts +1 -2
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +1 -2
- package/dist/boot/stage-10-http-server.d.ts +1 -2
- package/dist/boot/stage-10-http-server.js +6 -4
- package/dist/boot/stores.js +13 -6
- package/dist/boot/web-admin.js +18 -17
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +33 -0
- package/dist/capability-facts.js +32 -0
- package/dist/card-void.d.ts +38 -0
- package/dist/card-void.js +23 -0
- package/dist/config-catalog.js +12 -8
- package/dist/config-provider.d.ts +13 -13
- package/dist/config-provider.js +19 -33
- package/dist/config-types.d.ts +39 -10
- package/dist/config.d.ts +6 -0
- package/dist/config.js +72 -49
- package/dist/delegated-session-owner.d.ts +21 -19
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +3 -0
- package/dist/execution-config-root.d.ts +22 -0
- package/dist/execution-config-root.js +35 -0
- package/dist/execution-lane-caps.d.ts +50 -2
- package/dist/execution-lane-caps.js +90 -13
- package/dist/execution-placement.d.ts +3 -0
- package/dist/execution-placement.js +7 -0
- package/dist/fleet/drain-entry.d.ts +3 -0
- package/dist/fleet/fleet-bus.d.ts +6 -3
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +30 -1
- package/dist/hosted-posture.d.ts +8 -0
- package/dist/hosted-posture.js +4 -0
- package/dist/http/admission.js +3 -3
- package/dist/http/caller-trust.d.ts +3 -2
- package/dist/http/caller-trust.js +2 -1
- package/dist/http/dispatch.js +8 -2
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -0
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.js +42 -29
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +10 -6
- package/dist/http/routes/diagnostics.d.ts +0 -19
- package/dist/http/routes/diagnostics.js +16 -2
- package/dist/http/routes/memory-bundle.d.ts +2 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-policy.js +27 -7
- package/dist/http/routes/run-permission-mode.d.ts +75 -0
- package/dist/http/routes/run-permission-mode.js +170 -0
- package/dist/http/routes/runs.js +3 -3
- package/dist/http/routes/session-memory-sync.d.ts +63 -0
- package/dist/http/routes/session-memory-sync.js +157 -0
- package/dist/http/routes/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/sessions.js +5 -4
- package/dist/http/routes/tasks.js +70 -71
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +10 -4
- package/dist/http/send.d.ts +6 -1
- package/dist/http/send.js +10 -3
- package/dist/http/server.d.ts +20 -7
- package/dist/http/server.js +2 -4
- package/dist/http/sse-log.d.ts +3 -8
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +3 -1
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +1 -1
- package/dist/memory-sync-client.d.ts +35 -23
- package/dist/memory-sync-client.js +122 -52
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +7 -1
- package/dist/memory-sync.js +6 -3
- package/dist/observability/fail-open.d.ts +57 -12
- package/dist/observability/fail-open.js +50 -9
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +93 -31
- package/dist/observability/run-terminal.js +175 -171
- package/dist/observability/secret-env-scrub.d.ts +20 -1
- package/dist/observability/secret-env-scrub.js +14 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
- package/dist/orchestration/workflow-completion-inbox.js +13 -5
- package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -1
- package/dist/permission-mode-admission.d.ts +68 -0
- package/dist/permission-mode-admission.js +23 -0
- package/dist/permission-rule-list.d.ts +55 -6
- package/dist/permission-rule-list.js +52 -5
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +48 -7
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +9 -8
- package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
- package/dist/plugins/approval-ask-store-sql.js +49 -19
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +74 -3
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +4 -1
- package/dist/plugins/execution-placement-sql.d.ts +11 -1
- package/dist/plugins/execution-placement-sql.js +12 -1
- package/dist/plugins/file-run-store.d.ts +7 -2
- package/dist/plugins/file-run-store.js +12 -2
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/local-checkpoint-store.d.ts +10 -1
- package/dist/plugins/local-checkpoint-store.js +22 -0
- package/dist/plugins/local-session-store.d.ts +260 -39
- package/dist/plugins/local-session-store.js +582 -57
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +7 -2
- package/dist/plugins/memory-run-store.js +12 -2
- package/dist/plugins/one-time-migrations.d.ts +68 -2
- package/dist/plugins/one-time-migrations.js +120 -4
- package/dist/plugins/pg-pool.js +3 -2
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +47 -7
- package/dist/plugins/remote-env-e2b.js +242 -46
- package/dist/plugins/remote-env-k8s.d.ts +51 -6
- package/dist/plugins/remote-env-k8s.js +234 -44
- package/dist/plugins/remote-env-local-docker.d.ts +40 -14
- package/dist/plugins/remote-env-local-docker.js +127 -33
- package/dist/plugins/run-store-sql.d.ts +11 -3
- package/dist/plugins/run-store-sql.js +13 -4
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/session-ownership.d.ts +2 -1
- package/dist/plugins/session-placement.d.ts +17 -4
- package/dist/plugins/session-placement.js +2 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/store-backend.d.ts +41 -7
- package/dist/plugins/store-backend.js +22 -4
- package/dist/plugins/store-contracts.d.ts +35 -3
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +221 -28
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +3 -2
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/run-local.js +2 -8
- package/dist/runs.d.ts +39 -12
- package/dist/runs.js +69 -55
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +11 -2
- package/dist/security.js +8 -0
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +46 -4
- package/dist/session-error-wire.js +23 -0
- package/dist/session-incarnation.d.ts +9 -3
- package/dist/session-sync-kernel.d.ts +42 -1
- package/dist/session-sync-kernel.js +17 -0
- package/dist/session-sync.js +16 -4
- package/dist/sql-engine-coordinates.d.ts +49 -0
- package/dist/sql-engine-coordinates.js +27 -0
- package/dist/standing-answer-grant.d.ts +31 -2
- package/dist/standing-answer-grant.js +14 -2
- package/dist/store-posture.d.ts +1 -1
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +62 -27
- package/dist/task-settings.js +105 -29
- package/dist/terminal.d.ts +97 -20
- package/dist/terminal.js +107 -18
- package/dist/tool-approval.d.ts +4 -3
- package/dist/tool-approval.js +12 -14
- package/dist/trace/core-keyset-guard.d.ts +196 -2
- package/dist/trace/engine-notice-wire.d.ts +8 -2
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +9 -1
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +8 -4
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -0
- package/dist/trace/redact.js +323 -90
- package/dist/trace/self-minted-wire.d.ts +419 -4
- package/dist/trace/self-minted-wire.js +145 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/verifier-pause-card.d.ts +8 -0
- package/dist/verifier-pause-card.js +19 -0
- package/dist/web-admin-client/apply-effective.d.ts +5 -7
- package/dist/web-admin-client/apply-effective.js +22 -2
- package/dist/web-admin-client/candidate-gate.d.ts +1 -1
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/dist/web-admin-client/types.d.ts +4 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.141.0.json +367 -0
- package/wire-changes/server@7.142.0.json +639 -0
|
@@ -29,6 +29,6 @@ import type { ParkTriggerCause } from "./approval-deny-reasons.js";
|
|
|
29
29
|
* `batchId` 必须是**行上**的批(幂等命中的存量行可能是别的政策 / 旧版本铸的整腿批,错 scope 的 claim 如实 `absent`)。
|
|
30
30
|
*/
|
|
31
31
|
export declare function settleByPolicy(store: Pick<ApprovalAskStore, "claimTerminal">, row: Pick<AskRow, "askId" | "batchId">, policy: UnattendedApprovalPolicy,
|
|
32
|
-
/** 转 park 的触发成因(窗到期 / 广播全灭 / 有序关停):`park` 政策的 `expire` 赢下 PARKING 时随同一步写进行(`
|
|
32
|
+
/** 转 park 的触发成因(窗到期 / 广播全灭 / 有序关停):`park` 政策的 `expire` 赢下 PARKING 时随同一步写进行(`park_trigger`),跨副本的读者据它归因;`deny` 政策落的是一条决议,不带。 */
|
|
33
33
|
cause: ParkTriggerCause): Promise<AskTerminalClaimOutcome>;
|
|
34
34
|
//# sourceMappingURL=approval-policy-settlement.d.ts.map
|
|
@@ -123,6 +123,17 @@ export declare function classifyGateMatch(ask: AskRow, candidates: readonly Chec
|
|
|
123
123
|
export declare function selectGateCandidate(ask: AskRow, candidates: readonly CheckpointAskCandidate[]): CheckpointAskCandidate | undefined;
|
|
124
124
|
/** 判据表 v2 的判定(纯函数;顺序 = 设计稿 §9 尾的五臂汇总,注见文件头)。 */
|
|
125
125
|
export declare function decideReconcileAction(input: ReconcileInput): ReconcileAction;
|
|
126
|
+
/** 判据 1 执行体({@link bindParkingAskAtGate})一次尝试的结果。没绑上 ⇒ 交回批的真实态,周期扫据它续判 ②③④⑤。 */
|
|
127
|
+
export type GateBindAttempt = {
|
|
128
|
+
bound: true;
|
|
129
|
+
match: GateMatchOutcome;
|
|
130
|
+
} | {
|
|
131
|
+
bound: false;
|
|
132
|
+
match?: GateMatchOutcome;
|
|
133
|
+
batchState: BatchState | "MISSING" | null;
|
|
134
|
+
batchBoundAskId: string | null;
|
|
135
|
+
irreversibleBatch: boolean;
|
|
136
|
+
};
|
|
126
137
|
/** 收敛器一次 tick 的产出(可观测 + 测试断言面)。 */
|
|
127
138
|
export interface ReconcileStats {
|
|
128
139
|
/** 段一扫到的 `PARKING` 行数。 */
|
|
@@ -202,7 +213,7 @@ export type ApprovalRevokeEmitter = (frame: ApprovalRevokeFrame, target: {
|
|
|
202
213
|
sessionId: string;
|
|
203
214
|
taskId: string;
|
|
204
215
|
}) => void;
|
|
205
|
-
/** 判据 1 的读口(窄接口,不绑具体 checkpoint
|
|
216
|
+
/** 判据 1 的读口(窄接口,不绑具体 checkpoint 店实现;三个后端的 checkpoint 店都有这一面,后端的只读口 `checkpointReadView()` 也交它 —— 起服收敛读那一口)。 */
|
|
206
217
|
export interface ReconcileCheckpointPort {
|
|
207
218
|
findCheckpointCandidatesForAsk(scope: string, sessionId: string, toolCallId: string, sinceMs: number): Promise<CheckpointAskCandidate[]>;
|
|
208
219
|
}
|
|
@@ -245,5 +256,26 @@ export interface ApprovalReconciler {
|
|
|
245
256
|
* 段级错误由调用方(reaper 腿的 throttled catch)兜。 */
|
|
246
257
|
runOnce(nowMs: number): Promise<ReconcileStats>;
|
|
247
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* {@link bindParkingAskAtGate} 的依赖(三个调用方交同一形):批 / 绑两口、判据 1 的候选读口、warn 与撤卡两个出口。
|
|
261
|
+
*/
|
|
262
|
+
export interface GateBindDeps {
|
|
263
|
+
readonly askStore: Pick<ApprovalAskStore, "getBatch" | "bindBatch">;
|
|
264
|
+
/** 缺席 ⇒ 候选集恒空 ⇒ 判据 1 恒不命中(诚实降级,与 {@link ApprovalReconcilerDeps.checkpoints} 同一句)。 */
|
|
265
|
+
readonly checkpoints: ReconcileCheckpointPort | undefined;
|
|
266
|
+
/** warn 的出口(调用方保证它不抛 —— 可观测面绝不成为故障源)。 */
|
|
267
|
+
readonly warn: (event: string, fields: Record<string, unknown>) => void;
|
|
268
|
+
/** 被 `bindBatch` 连坐的兄弟的撤卡帧(帧是通知,真源是行;失败不回滚 CAS)。 */
|
|
269
|
+
readonly revoke: (row: AskRow, askIds: readonly string[], reason: ApprovalRevokeFrame["reason"], nowMs: number) => void;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 🔴 **判据 1 的唯一执行体**([ref] 起一只判官;[ref] 起三个调用方):⓪ 批态先读 → 候选读口 → {@link classifyGateMatch} → 命中即 `bindBatch`
|
|
273
|
+
* (+ 被连坐兄弟的撤卡帧)。调用方:
|
|
274
|
+
* · 周期扫(`createApprovalReconciler` 的 `reconcileOne`)—— 不命中 / 绑失败时拿回批的真实态,续判 ②③④⑤;
|
|
275
|
+
* · park 提交那一拍(`bindCommittedPark`)—— 只问「这一行此刻能不能收口」,不命中即止(其余臂是周期扫的事);
|
|
276
|
+
* · local 车道起服收敛(`approval-crash-convergence.ts`)—— 审计判为孤儿的前世 `PARKING` 行:命中 ⇒ `PARKED`;不命中 ⇒ 调用方收 `VOID`。
|
|
277
|
+
* 判据、读口、写口一个字都不分叉:事件路径与起服都是本函数的即时调用,不是第二份判据。
|
|
278
|
+
*/
|
|
279
|
+
export declare function bindParkingAskAtGate(deps: GateBindDeps, ask: AskRow, nowMs: number, deadlineMs?: number): Promise<GateBindAttempt>;
|
|
248
280
|
export declare function createApprovalReconciler(deps: ApprovalReconcilerDeps): ApprovalReconciler;
|
|
249
281
|
//# sourceMappingURL=approval-reconciler.d.ts.map
|
|
@@ -79,6 +79,45 @@ export function decideReconcileAction(input) {
|
|
|
79
79
|
void candidates;
|
|
80
80
|
return { kind: "hold" };
|
|
81
81
|
}
|
|
82
|
+
export async function bindParkingAskAtGate(deps, ask, nowMs, deadlineMs) {
|
|
83
|
+
const { askStore, checkpoints, warn, revoke } = deps;
|
|
84
|
+
const expired = () => deadlineMs !== undefined && Date.now() >= deadlineMs;
|
|
85
|
+
const batchRow = await withDeadline(askStore.getBatch(ask.batchId), "getBatch");
|
|
86
|
+
let batchState = batchRow?.state ?? "MISSING";
|
|
87
|
+
let batchBoundAskId = batchRow?.boundAskId ?? null;
|
|
88
|
+
const irreversibleBatch = batchState === "ABORTED" || (batchState === "ROUTING_BOUND" && batchBoundAskId !== null && batchBoundAskId !== ask.askId);
|
|
89
|
+
let gateMatch;
|
|
90
|
+
if (checkpoints && !irreversibleBatch && !expired()) {
|
|
91
|
+
const candidates = await withDeadline(checkpoints.findCheckpointCandidatesForAsk(encodeCheckpointScope(ask.owner), ask.sessionId, ask.toolCallId, ask.createdAtMs), "findCheckpointCandidatesForAsk");
|
|
92
|
+
gateMatch = classifyGateMatch(ask, candidates);
|
|
93
|
+
if (gateMatch.kind === "hash_mismatch") {
|
|
94
|
+
warn("approval_reconcile_hash_mismatch", {
|
|
95
|
+
askId: ask.askId,
|
|
96
|
+
taskId: ask.taskId,
|
|
97
|
+
toolCallId: ask.toolCallId,
|
|
98
|
+
candidates: gateMatch.candidates,
|
|
99
|
+
reason: "ask and checkpoint minted the same bound input under the same (sourceTaskId, toolCallId) identity but the digests differ — " +
|
|
100
|
+
"the known causes are deployment-side (a hook/policy mutating an object it still holds a reference to between the two mints, " +
|
|
101
|
+
"or a stateful getter returning a fresh value per read), not out-of-band interference; this ask keeps its fail-safe route (no park, no denial)",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const match = gateMatch.kind === "match" ? gateMatch.candidate : undefined;
|
|
105
|
+
if (match && !expired()) {
|
|
106
|
+
const res = await withDeadline(askStore.bindBatch(ask.batchId, ask.askId, {
|
|
107
|
+
gateToken: match.token,
|
|
108
|
+
gateBoundCallId: match.boundCallId,
|
|
109
|
+
gateBoundInputHash: match.boundInputHash,
|
|
110
|
+
}), "bindBatch");
|
|
111
|
+
if (res.ok) {
|
|
112
|
+
revoke(ask, res.voidedSiblings, "superseded_by_park", nowMs);
|
|
113
|
+
return { bound: true, match: gateMatch };
|
|
114
|
+
}
|
|
115
|
+
batchState = res.batchState;
|
|
116
|
+
batchBoundAskId = res.boundAskId ?? null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { bound: false, ...(gateMatch !== undefined ? { match: gateMatch } : {}), batchState, batchBoundAskId, irreversibleBatch };
|
|
120
|
+
}
|
|
82
121
|
export function createApprovalReconciler(deps) {
|
|
83
122
|
const { askStore, checkpoints, runs, logger, metrics, batchLimit, pendingGraceMs, adhocGraceMs, orphanTtlMs, unattendedPolicy, emitRevoke } = deps;
|
|
84
123
|
const warn = (event, fields) => {
|
|
@@ -98,44 +137,8 @@ export function createApprovalReconciler(deps) {
|
|
|
98
137
|
warn("approval_revoke_emit_failed", { batchId: row.batchId, count: askIds.length, err: err instanceof Error ? err.message : String(err) });
|
|
99
138
|
}
|
|
100
139
|
};
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
const batchRow = await withDeadline(askStore.getBatch(ask.batchId), "getBatch");
|
|
104
|
-
let batchState = batchRow?.state ?? "MISSING";
|
|
105
|
-
let batchBoundAskId = batchRow?.boundAskId ?? null;
|
|
106
|
-
const irreversibleBatch = batchState === "ABORTED" || (batchState === "ROUTING_BOUND" && batchBoundAskId !== null && batchBoundAskId !== ask.askId);
|
|
107
|
-
let gateMatch;
|
|
108
|
-
if (checkpoints && !irreversibleBatch && !expired()) {
|
|
109
|
-
const candidates = await withDeadline(checkpoints.findCheckpointCandidatesForAsk(encodeCheckpointScope(ask.owner), ask.sessionId, ask.toolCallId, ask.createdAtMs), "findCheckpointCandidatesForAsk");
|
|
110
|
-
gateMatch = classifyGateMatch(ask, candidates);
|
|
111
|
-
if (gateMatch.kind === "hash_mismatch") {
|
|
112
|
-
warn("approval_reconcile_hash_mismatch", {
|
|
113
|
-
askId: ask.askId,
|
|
114
|
-
taskId: ask.taskId,
|
|
115
|
-
toolCallId: ask.toolCallId,
|
|
116
|
-
candidates: gateMatch.candidates,
|
|
117
|
-
reason: "ask and checkpoint minted the same bound input under the same (sourceTaskId, toolCallId) identity but the digests differ — " +
|
|
118
|
-
"the known causes are deployment-side (a hook/policy mutating an object it still holds a reference to between the two mints, " +
|
|
119
|
-
"or a stateful getter returning a fresh value per read), not out-of-band interference; this ask keeps its fail-safe route (no park, no denial)",
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
const match = gateMatch.kind === "match" ? gateMatch.candidate : undefined;
|
|
123
|
-
if (match && !expired()) {
|
|
124
|
-
const res = await withDeadline(askStore.bindBatch(ask.batchId, ask.askId, {
|
|
125
|
-
gateToken: match.token,
|
|
126
|
-
gateBoundCallId: match.boundCallId,
|
|
127
|
-
gateBoundInputHash: match.boundInputHash,
|
|
128
|
-
}), "bindBatch");
|
|
129
|
-
if (res.ok) {
|
|
130
|
-
revoke(ask, res.voidedSiblings, "superseded_by_park", nowMs);
|
|
131
|
-
return { bound: true, match: gateMatch };
|
|
132
|
-
}
|
|
133
|
-
batchState = res.batchState;
|
|
134
|
-
batchBoundAskId = res.boundAskId ?? null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return { bound: false, ...(gateMatch !== undefined ? { match: gateMatch } : {}), batchState, batchBoundAskId, irreversibleBatch };
|
|
138
|
-
};
|
|
140
|
+
const gate = { askStore, checkpoints, warn, revoke };
|
|
141
|
+
const bindAtGate = (ask, nowMs, deadlineMs) => bindParkingAskAtGate(gate, ask, nowMs, deadlineMs);
|
|
139
142
|
const reconcileOne = async (ask, nowMs) => {
|
|
140
143
|
const attempt = await bindAtGate(ask, nowMs);
|
|
141
144
|
if (attempt.bound)
|
|
@@ -150,11 +153,11 @@ export function createApprovalReconciler(deps) {
|
|
|
150
153
|
return { outcome: "held", match: gateMatch };
|
|
151
154
|
}
|
|
152
155
|
case "deny": {
|
|
153
|
-
const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "DENIED", {
|
|
156
|
+
const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "DENIED", { endingCause: action.reason, updatedAtMs: nowMs }), "transitionAsk(DENIED)");
|
|
154
157
|
return { outcome: won ? "denied" : "held", match: gateMatch };
|
|
155
158
|
}
|
|
156
159
|
case "void": {
|
|
157
|
-
const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "VOID", {
|
|
160
|
+
const won = await withDeadline(askStore.transitionAsk(ask.askId, "PARKING", "VOID", { endingCause: action.reason, updatedAtMs: nowMs }), "transitionAsk(VOID)");
|
|
158
161
|
if (!won)
|
|
159
162
|
return { outcome: "held", match: gateMatch };
|
|
160
163
|
if (action.reason === APPROVAL_ENDING_CAUSES.ORPHAN_TTL_EXCEEDED) {
|
package/dist/approval.d.ts
CHANGED
|
@@ -95,6 +95,21 @@ export declare function buildNoResponderBootWarns(input: {
|
|
|
95
95
|
hint: string;
|
|
96
96
|
};
|
|
97
97
|
}>;
|
|
98
|
+
/**
|
|
99
|
+
* 单用户 turnkey 起服行 `tool_policy_single_user_default` 的**载荷**(纯数据 ⇒ `build*`;打不打、用哪只 logger 归 stage-07 的调用点,
|
|
100
|
+
* 打的判据是 {@link singleUserAutoAcceptBaselineApplies})。文案只此一处。
|
|
101
|
+
*
|
|
102
|
+
* 它说的是**部署工具策略层**的姿势:这一层铺的是一条在场的、看效果的 allow-all 基线(`createApprovalBaselinePolicy(config, { kind: "absent" })`),
|
|
103
|
+
* 好让引擎的「写手无裁决」告警不再响 —— 这一层自己不问也不拒。它**不是逐调用的放行**:每次调用仍由本次运行的权限模式与引擎写门裁(`auto` 下 shell 的写 / 删
|
|
104
|
+
* 照样出问并交分类器)。修前这一句写成「ALL write/hand/MCP tool surfaces auto-allowed」,读者(含取证)把它读成逐调用放行,与运行期读数相反。
|
|
105
|
+
*/
|
|
106
|
+
export declare function buildSingleUserDefaultPostureNotice(): {
|
|
107
|
+
event: "tool_policy_single_user_default";
|
|
108
|
+
fields: {
|
|
109
|
+
posture: string;
|
|
110
|
+
tighten: string;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
98
113
|
/**
|
|
99
114
|
* [ref] 件⑩ —— **center 的 `forceDurableGate` 在无 checkpoint 店的部署上会静默 inert**,补一条 boot 披露。
|
|
100
115
|
*
|
package/dist/approval.js
CHANGED
|
@@ -53,6 +53,16 @@ export function buildNoResponderBootWarns(input) {
|
|
|
53
53
|
}
|
|
54
54
|
return out;
|
|
55
55
|
}
|
|
56
|
+
export function buildSingleUserDefaultPostureNotice() {
|
|
57
|
+
return {
|
|
58
|
+
event: "tool_policy_single_user_default",
|
|
59
|
+
fields: {
|
|
60
|
+
posture: "deployment tool-policy baseline is allow-all — this layer asks no question and refuses nothing of its own (core's per-surface UNGATED enumeration is suppressed for single-user turnkey). " +
|
|
61
|
+
"It is not a per-call verdict: each call is still judged by the run's permission mode and the engine's write gate — a shell write or delete can still be asked, or handed to the classifier under auto",
|
|
62
|
+
tighten: "APPROVAL_REQUIRE / AUTONOMY / commandPolicy (re-surfaces + gates specific surfaces)",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
56
66
|
export function buildForceDurableGateInertNotice(input) {
|
|
57
67
|
if (!input.entitlementSourceWired || input.checkpointStorePresent)
|
|
58
68
|
return undefined;
|
package/dist/audit.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type SessionPlacementRecord, type StoredPromptEpoch } from "@sema-agent/core";
|
|
2
2
|
import type { Pool } from "mysql2/promise";
|
|
3
3
|
import type { Pool as PgPool } from "pg";
|
|
4
|
+
import { type LocalSessionRows } from "./plugins/local-session-window.js";
|
|
4
5
|
/**
|
|
5
6
|
* Audit view of a session = its **current context** (the live messages + the last compaction's
|
|
6
7
|
* summary), i.e. the floor-bounded window — never the full raw transcript. This matches the
|
|
@@ -76,10 +77,11 @@ export declare function buildSessionAudit(pool: Pool, sessionId: string, handles
|
|
|
76
77
|
* audit face was TiDB-only and a PG/local deployment 404'd the whole web session area. */
|
|
77
78
|
export declare function buildSessionAuditPg(pool: PgPool, sessionId: string, handles?: RewindHandles): Promise<SessionAudit | undefined>;
|
|
78
79
|
/** The LOCAL (File) leg — web session area on DB_BACKEND=local: the same view over `LocalSessionWindow.wake`
|
|
79
|
-
* (`plugins/local-session-window.ts`)
|
|
80
|
+
* (`plugins/local-session-window.ts`), read from the local session store's rows (its row projection and its log export — the view's
|
|
81
|
+
* `createdAt` is the row's, as on the SQL twins). All three legs read one way — a bounded window that carries the epoch anchor below its
|
|
80
82
|
* floor — and on every backend that is what the run reads: the SQL twins' run wakes the same window; the local run wakes the whole
|
|
81
83
|
* tree, and the local window is cut from that tree's branch, with the anchor walking the branch below the floor. */
|
|
82
|
-
export declare function buildSessionAuditLocal(
|
|
84
|
+
export declare function buildSessionAuditLocal(rows: LocalSessionRows, sessionId: string, handles?: RewindHandles): Promise<SessionAudit | undefined>;
|
|
83
85
|
/**
|
|
84
86
|
* 🔴 **会话视图的读面口 —— `GET /v1/sessions/:id` 的全部取法(整读 / `?tail` / `?before` / `?blobCap` / `?message=N`)共用这一只**。
|
|
85
87
|
* 次序 = **选 → 脱 → 截**(路由 `http/routes/sessions.ts`):整条消息的选窗不切字符串,先选窗不改任何一条的脱敏读数、只把脱敏的量降到选中的那几条;
|
package/dist/audit.js
CHANGED
|
@@ -34,8 +34,8 @@ export async function buildSessionAudit(pool, sessionId, handles) {
|
|
|
34
34
|
export async function buildSessionAuditPg(pool, sessionId, handles) {
|
|
35
35
|
return auditFromStorage(await PgSessionStorage.wake(pool, sessionId), sessionId, handles);
|
|
36
36
|
}
|
|
37
|
-
export async function buildSessionAuditLocal(
|
|
38
|
-
return auditFromStorage(await LocalSessionWindow.wake(
|
|
37
|
+
export async function buildSessionAuditLocal(rows, sessionId, handles) {
|
|
38
|
+
return auditFromStorage(await LocalSessionWindow.wake(rows, sessionId), sessionId, handles);
|
|
39
39
|
}
|
|
40
40
|
async function auditFromStorage(storage, sessionId, handles) {
|
|
41
41
|
if (!storage)
|
package/dist/auth-bridge.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { VerifiedIdentity, type AuthBridgeErrorCode } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
2
2
|
import { type TokenScopeRefusal } from "@sema-agent/settings-schema/api/scopes";
|
|
3
3
|
import type { Logger } from "./observability/logger.js";
|
|
4
4
|
import type { Metrics } from "./observability/metrics.js";
|
|
@@ -52,12 +52,13 @@ type BridgeSinks = {
|
|
|
52
52
|
readonly logger?: Logger | undefined;
|
|
53
53
|
readonly metrics?: Pick<Metrics, "inc"> | undefined;
|
|
54
54
|
};
|
|
55
|
-
/** 注入边界收的结局:与 {@link VerifyOutcome} 同形,只是成功身份的 `scope` 是 `unknown` ——
|
|
56
|
-
* (席是嵌入方可换的:`""` 过得了 `scope: string`,JS 侧连缺省 / 非串都交得进来)。`VerifyOutcome` 可赋给它,调用方零改动。 */
|
|
55
|
+
/** 注入边界收的结局:与 {@link VerifyOutcome} 同形,只是成功身份的 `scope` 与 `principalInc` 是 `unknown` —— 这两位正是边界要判的形,它不信型面
|
|
56
|
+
* (席是嵌入方可换的:`""` 过得了 `scope: string` / `principalInc?: string`,JS 侧连缺省 / 非串都交得进来)。`VerifyOutcome` 可赋给它,调用方零改动。 */
|
|
57
57
|
type ReportedOutcome = {
|
|
58
58
|
ok: true;
|
|
59
|
-
identity: Omit<BridgeIdentity, "scope"> & {
|
|
59
|
+
identity: Omit<BridgeIdentity, "scope" | "principalInc"> & {
|
|
60
60
|
scope?: unknown;
|
|
61
|
+
principalInc?: unknown;
|
|
61
62
|
};
|
|
62
63
|
} | {
|
|
63
64
|
ok: false;
|
|
@@ -65,8 +66,10 @@ type ReportedOutcome = {
|
|
|
65
66
|
};
|
|
66
67
|
/**
|
|
67
68
|
* 注入边界:任何验证器(内建 {@link createWebAdminJwtVerifier},或嵌入方注入的 `ServiceDeps.webAdminJwtVerifier`)交来的结局,在这里定「装不装身份」。
|
|
68
|
-
* 成功身份的 scope 经**同一只** `readTokenScope`
|
|
69
|
-
*
|
|
69
|
+
* 成功身份的 scope 经**同一只** `readTokenScope` 再读一次;化身号经契约的**同一只**判官 `VerifiedIdentity.shape.principalInc`(= `PrincipalIncarnation.optional()`,
|
|
70
|
+
* 契约验证器读 `sub_inc` 用的就是它)再判一次(入参型见 {@link ReportedOutcome})。
|
|
71
|
+
* 读不出 scope ⇒ 记 `readTokenScope` 的拒词;化身号在场而不合形 ⇒ 记 `malformed`(与契约验证器同一条律:声明在场坏形 ⇒ 令牌 malformed)。两种都经同一只记账口、不交身份
|
|
72
|
+
* (调用方按验证失败处置:不设身份 ⇒ 门答 generic 401)。验证失败的结局验证器自己已记过账,这里只答「不装」。
|
|
70
73
|
*/
|
|
71
74
|
export declare function admitBridgeIdentity(outcome: ReportedOutcome, sinks: BridgeSinks): BridgeIdentity | undefined;
|
|
72
75
|
/** The live object `createWebAdminJwtVerifier` produces — a single `verify` entry point over the process-level JWKS cache. */
|
package/dist/auth-bridge.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createPublicKey, verify as cryptoVerify } from "node:crypto";
|
|
2
|
-
import { AUTH_BRIDGE_ALGS, DEFAULT_CLOCK_SKEW_SEC, DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC, DEFAULT_OFFLINE_GRACE_SEC, WEB_ADMIN_JWKS_PATH, WebAdminJwks, classifyTimeClaims, jwksWithinOfflineGrace, shouldForceJwksRefresh, toVerifiedIdentity, } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
2
|
+
import { AUTH_BRIDGE_ALGS, DEFAULT_CLOCK_SKEW_SEC, DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC, DEFAULT_OFFLINE_GRACE_SEC, WEB_ADMIN_JWKS_PATH, WebAdminJwks, classifyTimeClaims, jwksWithinOfflineGrace, shouldForceJwksRefresh, toVerifiedIdentity, VerifiedIdentity, } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
3
3
|
import { readTokenScope } from "@sema-agent/settings-schema/api/scopes";
|
|
4
4
|
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
5
5
|
import { centerJwksFetch } from "./web-admin-client/center-request.js";
|
|
@@ -17,12 +17,18 @@ function recordBridgeRefusal(sinks, error, at = {}) {
|
|
|
17
17
|
export function admitBridgeIdentity(outcome, sinks) {
|
|
18
18
|
if (!outcome.ok)
|
|
19
19
|
return undefined;
|
|
20
|
-
const
|
|
20
|
+
const { scope, principalInc, ...rest } = outcome.identity;
|
|
21
|
+
const scoped = readTokenScope({ scope });
|
|
21
22
|
if ("refused" in scoped) {
|
|
22
23
|
recordBridgeRefusal(sinks, scoped.refused);
|
|
23
24
|
return undefined;
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
const inc = VerifiedIdentity.shape.principalInc.safeParse(principalInc);
|
|
27
|
+
if (!inc.success) {
|
|
28
|
+
recordBridgeRefusal(sinks, "malformed");
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return { ...rest, scope: scoped.scope, ...(inc.data !== undefined ? { principalInc: inc.data } : {}) };
|
|
26
32
|
}
|
|
27
33
|
export const DEFAULT_JWKS_TTL_SEC = 15 * 60;
|
|
28
34
|
export const JWKS_FETCH_TIMEOUT_MS = 10_000;
|
package/dist/boot/adoption.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createAdoptionRunner } from "../adoption/runner.js";
|
|
2
|
+
import { seatText } from "../trace/exit-seat.js";
|
|
2
3
|
export async function runAdoptionBootScan(ctx) {
|
|
3
4
|
const store = ctx.backend?.adoptionLog?.();
|
|
4
5
|
if (!store)
|
|
@@ -29,7 +30,7 @@ export async function runAdoptionBootScan(ctx) {
|
|
|
29
30
|
return reading;
|
|
30
31
|
}
|
|
31
32
|
catch (err) {
|
|
32
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
33
|
+
const error = seatText("operator-log-text", err instanceof Error ? err.message : String(err));
|
|
33
34
|
ctx.logger.error?.("adoption_boot_scan_failed", { error });
|
|
34
35
|
return { scanned: 0, resumed: [], stalled: [], rejected: [], failed: [], error };
|
|
35
36
|
}
|
|
@@ -11,6 +11,22 @@ export interface BudgetTracingCtx {
|
|
|
11
11
|
backend: StoreBackend | undefined;
|
|
12
12
|
breakerState: ReturnType<BreakerStateStore["startRefresh"]> | undefined;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* S9 (SILENT-FALLBACK P0-e): write-behind flush/refresh failures previously vanished in a bare catch —
|
|
16
|
+
* the fleet silently degrades to per-replica counting (soft limits stop being fleet-wide). Every failure
|
|
17
|
+
* ticks the counter + streak gauge; warn once per episode (streak hits 3), info on the recovery edge.
|
|
18
|
+
* `info.error` is the client exception's text (the counter turns the rejection into it) ⇒ the warn's field passes the exit seat.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createCounterDegradeHook({ logger, metrics }: {
|
|
21
|
+
logger: Pick<Logger, "warn" | "info">;
|
|
22
|
+
metrics: Pick<Metrics, "setGauge" | "inc">;
|
|
23
|
+
}): (info: {
|
|
24
|
+
table: string;
|
|
25
|
+
kind: string;
|
|
26
|
+
streak: number;
|
|
27
|
+
prevStreak: number;
|
|
28
|
+
error?: string;
|
|
29
|
+
}) => void;
|
|
14
30
|
export declare function createBudgetAndTracing(ctx: BudgetTracingCtx): {
|
|
15
31
|
brain: import("@sema-agent/core").Brain;
|
|
16
32
|
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
@@ -6,19 +6,23 @@ import { centerLaneConfigured } from "../web-admin-client/center-request.js";
|
|
|
6
6
|
import { CostQuota } from "../observability/cost-quota.js";
|
|
7
7
|
import { buildQuotaBounds } from "./limit-sync.js";
|
|
8
8
|
import { assembleFileHistory } from "../file-history-capture.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const pricing = buildPricing(config.models);
|
|
13
|
-
const counterDegradeHook = (info) => {
|
|
9
|
+
import { seatText } from "../trace/exit-seat.js";
|
|
10
|
+
export function createCounterDegradeHook({ logger, metrics }) {
|
|
11
|
+
return (info) => {
|
|
14
12
|
metrics.setGauge("counter_flush_fail_streak", info.streak, { table: info.table, kind: info.kind });
|
|
15
13
|
if (info.streak > 0)
|
|
16
14
|
metrics.inc("counter_flush_failed_total", { table: info.table, kind: info.kind });
|
|
17
15
|
if (info.streak === 3)
|
|
18
|
-
logger.warn("counter_flush_degraded", { table: info.table, kind: info.kind, streak: info.streak, ...(info.error ? { error: info.error } : {}) });
|
|
16
|
+
logger.warn("counter_flush_degraded", { table: info.table, kind: info.kind, streak: info.streak, ...(info.error ? { error: seatText("operator-log-text", info.error) } : {}) });
|
|
19
17
|
else if (info.streak === 0 && info.prevStreak >= 3)
|
|
20
18
|
logger.info("counter_flush_recovered", { table: info.table, kind: info.kind, prevStreak: info.prevStreak });
|
|
21
19
|
};
|
|
20
|
+
}
|
|
21
|
+
export function createBudgetAndTracing(ctx) {
|
|
22
|
+
const { config, logger, metrics, backend, breakerState } = ctx;
|
|
23
|
+
const { brain, degradeRoute: degradeRouteWatch } = createBrainAssembly(config, { breakerState });
|
|
24
|
+
const pricing = buildPricing(config.models);
|
|
25
|
+
const counterDegradeHook = createCounterDegradeHook({ logger, metrics });
|
|
22
26
|
const bounds = buildQuotaBounds(config);
|
|
23
27
|
const costQuota = backend?.costQuota
|
|
24
28
|
? backend.costQuota(bounds.limitMicroUsd, bounds.windowMs, counterDegradeHook).startRefresh()
|
|
@@ -18,6 +18,7 @@ import type { DeviceStore } from "../device-store.js";
|
|
|
18
18
|
import type { ExecutionPlacementSeat } from "../execution-placement.js";
|
|
19
19
|
import { type DeviceWsHub } from "../device-ws-hub.js";
|
|
20
20
|
import type { ServiceConfig } from "../config.js";
|
|
21
|
+
import type { DeviceRelayDeclaration } from "../config-types.js";
|
|
21
22
|
import type { Logger } from "../observability/logger.js";
|
|
22
23
|
import type { Metrics } from "../observability/metrics.js";
|
|
23
24
|
import type { StoreBackend } from "../plugins/store-backend.js";
|
|
@@ -75,4 +76,14 @@ export declare function createDeviceLane(ctx: DeviceLaneCtx): DeviceLane | undef
|
|
|
75
76
|
* 抛而不是 warn:降级的代价由用户承担(他以为命令跑在自己机器上、看到的却是云上一个空目录的结果)。
|
|
76
77
|
*/
|
|
77
78
|
export declare function assertDeviceLaneWired(isDeviceLane: boolean, lane: DeviceLane | undefined, hasFactory: boolean): void;
|
|
79
|
+
/**
|
|
80
|
+
* 设备入口 × 已绑设备的起服一行(拒的那一半在配置期:device 车道上 `DEVICE_RELAY_URL` 必填 —— 缺席拒起是车道必需 env 门(`config.ts` `declaredExecutionLane` → `laneMissingEnv`),坏形 / 带凭据形拒启是 `parseDeviceRelayUrlEnv`;
|
|
81
|
+
* 起不起服不随表里有几条绑定变)。
|
|
82
|
+
*
|
|
83
|
+
* 字面 `none` = 运维声明「设备拨本引擎自己的地址 + `wsPath`」。此刻根会话行上若有设备绑定(writer(device, D) 的格),这些会话的设备从此只能拨那个入口 ——
|
|
84
|
+
* 撤中继声明的回滚序是先恢复引擎自己的入口、再写 `none`,本行让运维看见押在那个入口上的绑定有几条:`device_relay_withdrawn_with_bindings { bindings }`
|
|
85
|
+
* (只交数,不交会话 id)。绑定 0 ⇒ 不响。URL ⇒ 不读表(设备读能力面拨它,与绑定数无关)。
|
|
86
|
+
* 读数 = 设备店的绑定数(唯一读法 `boundDeviceOf` 的聚合);读故障原样抛 ⇒ 起服失败(不折成 0 —— 折 0 就是在有绑定时不响)。
|
|
87
|
+
*/
|
|
88
|
+
export declare function noteDeviceRelayBindings(relay: DeviceRelayDeclaration, store: Pick<DeviceStore, "countDeviceBindings">, logger: Pick<Logger, "warn">): Promise<void>;
|
|
78
89
|
//# sourceMappingURL=device-lane.d.ts.map
|
package/dist/boot/device-lane.js
CHANGED
|
@@ -56,4 +56,20 @@ export function assertDeviceLaneWired(isDeviceLane, lane, hasFactory) {
|
|
|
56
56
|
"an assembly bug, not a configuration one — refusing to start rather than silently execute in-process (#157)");
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
export async function noteDeviceRelayBindings(relay, store, logger) {
|
|
60
|
+
switch (relay.kind) {
|
|
61
|
+
case "url":
|
|
62
|
+
return;
|
|
63
|
+
case "none": {
|
|
64
|
+
const bindings = await store.countDeviceBindings();
|
|
65
|
+
if (bindings > 0)
|
|
66
|
+
logger.warn("device_relay_withdrawn_with_bindings", { bindings });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
default: {
|
|
70
|
+
const unknownKind = relay;
|
|
71
|
+
throw new Error(`noteDeviceRelayBindings: no arm for ${JSON.stringify(unknownKind)}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
59
75
|
//# sourceMappingURL=device-lane.js.map
|
|
@@ -130,6 +130,29 @@ export interface EngineLeaseExitReport {
|
|
|
130
130
|
/** 成孤儿到自退之间隔了多久;非孤儿退出时键缺席(不编 0)。 */
|
|
131
131
|
orphanedForMs?: number;
|
|
132
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* [ref] —— 租约窗的 wire 读数(`GET /health` 的 `engineLease`;壳持租约的触点节拍据此定,别处起的短窗引擎不再让第二句失败)。
|
|
135
|
+
*
|
|
136
|
+
* ## 报的是什么
|
|
137
|
+
* · `lingerMs` = 本腿**会执行的最短静默窗**:父还活着的窗({@link EngineLeaseDeps.lingerMs})与孤儿窗({@link EngineLeaseOrphanLane.lingerMs},只在看门狗装了时存在)取小。
|
|
138
|
+
* 取**最短**而不是「此刻生效」:此刻生效的窗在父死那一拍从 owned 窗掉到孤儿窗,按旧读数定节拍的壳会在下一次读 `/health` 之前被拆台;
|
|
139
|
+
* 最短窗是配置的性质、不随状态变,壳的触点只要比它密,在任何状态下都留得住引擎。
|
|
140
|
+
* · `tickMs` = 自退检查的采样拍。自退发生在**生效窗**满之后、至多再晚一拍(顶注 8);生效窗恒 ≥ `lingerMs`(`lingerMs` 是各窗里最短的一个),所以触点比 `lingerMs` 密 ⇒ 在任何状态下都先于自退。
|
|
141
|
+
* 读数**不预言**自退时刻:父还活着时生效的是 owned 窗,自退只会更晚。
|
|
142
|
+
*
|
|
143
|
+
* ## 一源
|
|
144
|
+
* 与租约腿自己的行为同一处铸出:`engine_lease_armed` 日志、自退判据用的窗、拍频推导(`tickMs = min(15s, 最短窗)`)和本读数读的是同一组数 ——
|
|
145
|
+
* 不存在第二处去「算一遍租约窗」。一个永远不到期的窗(owned 窗 = `Infinity` 且没有孤儿车道)不是租约:读数整个缺席,不补 0、不把 `Infinity` 送进 JSON。
|
|
146
|
+
*/
|
|
147
|
+
export interface EngineLeaseFacts {
|
|
148
|
+
readonly lingerMs: number;
|
|
149
|
+
readonly tickMs: number;
|
|
150
|
+
}
|
|
151
|
+
/** {@link createEngineLeaseTracker} 的把手:`stop()` 收摊;`facts` 现读 —— **腿还在观察**时才有(收摊 / 自退触发之后缺席,says-yes ⟺ 腿在转)。 */
|
|
152
|
+
export interface EngineLeaseHandle {
|
|
153
|
+
stop(): void;
|
|
154
|
+
readonly facts: EngineLeaseFacts | undefined;
|
|
155
|
+
}
|
|
133
156
|
export interface EngineLeaseDeps {
|
|
134
157
|
/** 活附着数 = 本副本当前**打开着的 SSE 流**条数(`drainState.attachedStreams`,由 `createHttpServer` 自赋)。 */
|
|
135
158
|
attachedStreams: () => number;
|
|
@@ -167,7 +190,5 @@ export interface EngineLeaseDeps {
|
|
|
167
190
|
now?: () => number;
|
|
168
191
|
}
|
|
169
192
|
/** 装配并**立刻**起自退检查(装配即 `engine_lease_armed`)。返回停机链用的 `stop()`。 */
|
|
170
|
-
export declare function createEngineLeaseTracker(deps: EngineLeaseDeps):
|
|
171
|
-
stop(): void;
|
|
172
|
-
};
|
|
193
|
+
export declare function createEngineLeaseTracker(deps: EngineLeaseDeps): EngineLeaseHandle;
|
|
173
194
|
//# sourceMappingURL=engine-lease.d.ts.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export const ENGINE_LEASE_TICK_MS = 15_000;
|
|
2
2
|
export function createEngineLeaseTracker(deps) {
|
|
3
3
|
const orphanLane = deps.orphan;
|
|
4
|
-
const
|
|
4
|
+
const shortestLingerMs = Math.min(deps.lingerMs, orphanLane?.lingerMs ?? Number.POSITIVE_INFINITY);
|
|
5
|
+
const tickMs = deps.tickMs ?? Math.min(ENGINE_LEASE_TICK_MS, shortestLingerMs);
|
|
6
|
+
const leaseFacts = Number.isFinite(shortestLingerMs) ? Object.freeze({ lingerMs: shortestLingerMs, tickMs }) : undefined;
|
|
5
7
|
const now = deps.now ?? (() => performance.now());
|
|
6
8
|
let stopped = false;
|
|
7
9
|
let idleSince = now();
|
|
@@ -66,6 +68,11 @@ export function createEngineLeaseTracker(deps) {
|
|
|
66
68
|
stopped = true;
|
|
67
69
|
clearInterval(timer);
|
|
68
70
|
}
|
|
69
|
-
return {
|
|
71
|
+
return {
|
|
72
|
+
stop,
|
|
73
|
+
get facts() {
|
|
74
|
+
return stopped ? undefined : leaseFacts;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
70
77
|
}
|
|
71
78
|
//# sourceMappingURL=engine-lease.js.map
|
|
@@ -8,10 +8,13 @@ import type { DeviceEnrollment } from "../device-enrollment.js";
|
|
|
8
8
|
import type { DeviceStore } from "../device-store.js";
|
|
9
9
|
import type { ExecutionPlacementSeat } from "../execution-placement.js";
|
|
10
10
|
import type { DeviceWsHub } from "../device-ws-hub.js";
|
|
11
|
+
import { type RemoteContainerEnvDeps } from "../plugins/remote-env-e2b.js";
|
|
12
|
+
import { type WorkspaceFillOf } from "../plugins/workspace-fill.js";
|
|
11
13
|
import { k8sExecutionEnvFactory, type K8sEnvConfig, type K8sEnvDeps, type SandboxSecretSeat } from "../plugins/remote-env-k8s.js";
|
|
12
|
-
import { type
|
|
14
|
+
import { type SandboxGitPlaneSeat, type SandboxGitRunRows } from "../sandbox-git-plane.js";
|
|
13
15
|
import { type SandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
|
|
14
16
|
import { type SpaceCommitRows } from "../workspace-space-commit.js";
|
|
17
|
+
import { type LocalDockerEnvDeps } from "../plugins/remote-env-local-docker.js";
|
|
15
18
|
import * as facts from "../capability-facts.js";
|
|
16
19
|
import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
|
|
17
20
|
import type { TaskInheritEnv } from "../request-value-closure.js";
|
|
@@ -77,10 +80,19 @@ export declare function createSessionScopedRegistries(maxEntries?: number): Sess
|
|
|
77
80
|
* · 运维行座(`logger`)随部署级配置走。
|
|
78
81
|
* `deps` = 适配器自己的测试缝(假集群 / 假对象存储),生产装配不传。返回的是工厂闭包 ⇒ `create*`。
|
|
79
82
|
*/
|
|
83
|
+
/**
|
|
84
|
+
* 一条 run 是不是它工作区的属主:它的放置根就是它自己的会话(core `ExecutionEnvFactoryContext.placementRootSessionId`)。委派子代 / 后台代理与父 run 并发
|
|
85
|
+
* (一只工作区两个写者)、本服务为自己起的 run(放置根 = `INTERNAL_RUN_PLACEMENT_ROOT`)都不是。两个读者同一判:k8s 工作区空间(子代依律关)与
|
|
86
|
+
* 恢复点的播种(只有属主播种)。
|
|
87
|
+
*/
|
|
88
|
+
export declare function ownsItsWorkspace(ctx: {
|
|
89
|
+
readonly sessionId: string;
|
|
90
|
+
readonly placementRootSessionId: string;
|
|
91
|
+
}): boolean;
|
|
80
92
|
export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
|
|
81
93
|
imageOf(sessionId: string): string | undefined;
|
|
82
94
|
/** The member git-credential broker seat for the pod's run (`runId` = the one reading of the run that owns the pod, the same value its
|
|
83
|
-
* `sema.io/run` label carries; `sandbox-git-plane.ts` `
|
|
95
|
+
* `sema.io/run` label carries; `sandbox-git-plane.ts` `createSandboxBrokerSeatOf` over the plane seat's reading at that pod and the run row's identity);
|
|
84
96
|
* absent seat / `undefined` answer ⇒ the pod carries no broker. */
|
|
85
97
|
brokerSeatOf?(runId: string | undefined): Promise<K8sEnvConfig["sandboxBroker"]>;
|
|
86
98
|
/** The session store's by-id head read (`SessionStore.headOf`, picked once by `headReadOf`); the occupant is its live-occupant projection
|
|
@@ -90,11 +102,13 @@ export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
|
|
|
90
102
|
/** The run store's commit-fence rows (`workspace_space_commit`); answers `undefined` only before stage 7 binds the run store
|
|
91
103
|
* or without a store backend — the latter `assertWorkspaceSpaceReadable` refuses together with `space`. */
|
|
92
104
|
spaceCommits(): SpaceCommitRows | undefined;
|
|
105
|
+
/** The restore point's fill seat of the run (`workspace-fill.ts`; `createExecutionEnv` mints the one reading). Absent / `undefined` ⇒ the pod as it comes. */
|
|
106
|
+
workspaceFillOf?: WorkspaceFillOf;
|
|
93
107
|
}, deps?: K8sEnvDeps): ReturnType<typeof k8sExecutionEnvFactory>;
|
|
94
108
|
/**
|
|
95
109
|
* k8s 车道沙箱秘密座的**起服前提**(装配后的真门,第 5 段 await)。法只有一条:**秘密座非空(`K8S_SANDBOX_ENV` 点到了本进程环境里有的键)⇒
|
|
96
110
|
* 问一次 SelfSubjectAccessReview(env 座那一行动词 {@link SANDBOX_SECRET_SEAT_VERBS}`.env`,沙箱命名空间)** —— 只问这一座自己发的调用,
|
|
97
|
-
* 不问租约座的 `patch`(
|
|
111
|
+
* 不问租约座的 `patch`(那一座由凭据平面座在它的前提都成立时另问一次,见 `sandbox-git-plane.ts` 的 `createSandboxGitPlaneSeat` 的 `settle`):
|
|
98
112
|
* · 答了「不许」(任一动词)⇒ 拒启,一行拒句点名缺的动词、命名空间、Role 规则原文与出路(`[sandbox-secret-rbac]`)—— 不等第一个 run 才失败;
|
|
99
113
|
* · 问不到(请求失败 / 审查本身被拒 / 答复没有 `status.allowed`)⇒ 照常起服:F 类兜底口 `server.sandbox-secret.seat-unverified` 计数 + 一行
|
|
100
114
|
* `sandbox_secret_seat_unverified`。放行的最坏后果:RBAC 真缺时第一个要建 Secret 的 run 连接即拒(`auth_failed`,pod 被删)—— 秘密值仍然
|
|
@@ -141,6 +155,12 @@ export interface ExecutionEnvCtx {
|
|
|
141
155
|
/** run 店的按 id 读(成员凭据代理座读 run 行上受理时记下的身份元组 `RunRecord.identity`)。取值闭包:run 店第 7 段才绑(`late-binds.ts`),
|
|
142
156
|
* 平面开着时每个 run 的 pod 建起前求值一次。不传 / 答 `undefined`(没有存储后端)⇒ 读不到身份 ⇒ 不挂代理。 */
|
|
143
157
|
runRows?: () => SandboxGitRunRows | undefined;
|
|
158
|
+
/** 隔离车道三只适配器自己的测试缝(假 e2b SDK / 假集群 / 假 docker CLI),原样交给各自的工厂 —— 与 `K8sEnvDeps` 同一条律:生产装配不传。 */
|
|
159
|
+
laneDeps?: {
|
|
160
|
+
readonly e2b?: RemoteContainerEnvDeps;
|
|
161
|
+
readonly k8s?: K8sEnvDeps;
|
|
162
|
+
readonly localDocker?: LocalDockerEnvDeps;
|
|
163
|
+
};
|
|
144
164
|
}
|
|
145
165
|
export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
|
|
146
166
|
perTaskImage: PerTaskImageRegistry;
|
|
@@ -158,6 +178,6 @@ export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
|
|
|
158
178
|
spaceReclaimer: SpaceReclaimer | undefined;
|
|
159
179
|
sandboxSecretSeatGate: (() => Promise<void>) | undefined;
|
|
160
180
|
sandboxPodReaper: SandboxPodReaper | undefined;
|
|
161
|
-
|
|
181
|
+
sandboxGitPlane: SandboxGitPlaneSeat;
|
|
162
182
|
};
|
|
163
183
|
//# sourceMappingURL=execution-env.d.ts.map
|