@sema-agent/server 7.43.0 → 7.44.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/USAGE.md +18 -0
- package/dist/adoption/plan.js +31 -192
- package/dist/adoption/quiesce.js +3 -86
- package/dist/adoption/runner.js +3 -140
- package/dist/adoption/sql.js +0 -74
- package/dist/adoption/wire.js +0 -80
- package/dist/approval-ask-machine.js +0 -75
- package/dist/approval-card.js +0 -323
- package/dist/approval-deny-reasons.js +0 -42
- package/dist/approval-hmac.js +0 -35
- package/dist/approval-reconciler.js +8 -166
- package/dist/approval.js +4 -67
- package/dist/audit.js +1 -44
- package/dist/auth-bridge.js +4 -47
- package/dist/auth-keys.js +0 -23
- package/dist/bake-runner/main.js +4 -65
- package/dist/bake-runner/protocol.js +3 -58
- package/dist/bake-runner/runner.js +5 -91
- package/dist/bench/l8/artifact.js +2 -61
- package/dist/bench/l8/escape.js +0 -25
- package/dist/bench/l8/index.js +0 -14
- package/dist/bench/l8/probes.js +0 -60
- package/dist/bench/l8/run-probes.js +2 -45
- package/dist/bench/s1/arms.js +14 -189
- package/dist/bench/s1/live-deps.js +11 -253
- package/dist/bench/s1/oracle.js +1 -36
- package/dist/bench/s1/repair-oracle-adapter.js +0 -30
- package/dist/bench/s1/reviewer.js +0 -34
- package/dist/bench/s1/row.js +0 -62
- package/dist/bench/s1/run-firm.js +4 -70
- package/dist/bench/s1/runner-ctx.js +0 -40
- package/dist/bench/s1/tasks.js +0 -115
- package/dist/boot/adoption.js +0 -20
- package/dist/boot/budget-tracing.js +3 -55
- package/dist/boot/config-center.js +43 -551
- package/dist/boot/coordinators.js +1 -69
- package/dist/boot/crash-last.js +0 -18
- package/dist/boot/deferred-sandbox-path-env.js +3 -119
- package/dist/boot/execution-env.js +5 -171
- package/dist/boot/governance-seams.js +0 -92
- package/dist/boot/leader.js +0 -69
- package/dist/boot/limit-sync.js +0 -2
- package/dist/boot/memory-boundary.js +3 -91
- package/dist/boot/org-memory.js +1 -25
- package/dist/boot/parked-revive-gate.js +0 -150
- package/dist/boot/permission-rules-audit.js +1 -56
- package/dist/boot/reapers.js +15 -273
- package/dist/boot/resolve-spec.js +9 -768
- package/dist/boot/retention-lane.js +4 -87
- package/dist/boot/runner-deps.js +3 -297
- package/dist/boot/runtime-caps.js +0 -40
- package/dist/boot/session-faces.js +2 -133
- package/dist/boot/shutdown.js +9 -83
- package/dist/boot/side-query-lane.js +2 -137
- package/dist/boot/stores.js +15 -215
- package/dist/boot/task-list-lane.js +0 -18
- package/dist/boot/webfetch-summarize-lane.js +0 -47
- package/dist/boot/workflow-orchestration.js +3 -99
- package/dist/boot-reclaim.js +0 -20
- package/dist/bounded-session-map.js +0 -19
- package/dist/brain.js +2 -139
- package/dist/budget.js +9 -244
- package/dist/capabilities/builtin-tools.js +0 -3
- package/dist/capabilities/center-plugins.js +6 -51
- package/dist/capabilities/center-prompts.js +6 -71
- package/dist/capabilities/code-review-council.js +7 -40
- package/dist/capabilities/collab-workflows.js +1 -44
- package/dist/capabilities/hands-lane.js +0 -65
- package/dist/capabilities/memory-notice.js +0 -70
- package/dist/capabilities/prompt.js +0 -14
- package/dist/capabilities/prompts/code-review.js +0 -14
- package/dist/capabilities/prompts/identity.js +0 -6
- package/dist/capabilities/prompts/team.js +0 -4
- package/dist/capabilities/repo-tools.js +0 -34
- package/dist/capabilities/sandbox-file-send.js +3 -92
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.js +18 -279
- package/dist/capabilities/select-environment-tool.js +0 -32
- package/dist/capabilities/send-user-file-tool.js +2 -66
- package/dist/capabilities/skills.d.ts +1 -2
- package/dist/capabilities/skills.js +3 -21
- package/dist/capabilities/team.d.ts +3 -11
- package/dist/capabilities/team.js +3 -51
- package/dist/capabilities/tool-defer.js +0 -3
- package/dist/config-center/apply-effective.js +15 -330
- package/dist/config-center/apply-ledger.js +5 -38
- package/dist/config-center/facade.js +0 -41
- package/dist/config-center/hot-keys-registry.js +0 -20
- package/dist/config-center/http-client.js +2 -125
- package/dist/config-center/mcp-revocation.js +2 -34
- package/dist/config-center/read-face.js +0 -59
- package/dist/config-center/restart-signal.js +1 -85
- package/dist/config-center/skills-mcp.d.ts +1 -1
- package/dist/config-center/skills-mcp.js +9 -76
- package/dist/config-center/stage-limits.js +8 -35
- package/dist/config-invariants.js +0 -16
- package/dist/config-lkg.js +0 -42
- package/dist/config-provider.js +3 -186
- package/dist/config-types.js +0 -5
- package/dist/config.js +107 -1145
- package/dist/degenerate-instrument.js +3 -67
- package/dist/deployment-governance.js +0 -124
- package/dist/digest-form.js +0 -12
- package/dist/elicitation.js +3 -86
- package/dist/env-facts.js +7 -75
- package/dist/fleet/fleet-bus.js +34 -507
- package/dist/fleet/fleet-reconciler.js +9 -149
- package/dist/fleet/fleet-terminal-window.js +10 -178
- package/dist/fleet/subagent-tail-bus.js +3 -72
- package/dist/fleet-client.js +10 -70
- package/dist/fleet-lease.js +5 -79
- package/dist/git-api-kind.js +0 -3
- package/dist/governance-ask-marks.js +2 -78
- package/dist/hooks/branch-transcript.js +0 -74
- package/dist/hooks/cc-agent-hook-prompt.js +0 -29
- package/dist/hooks/cc-stop-prompt.js +1 -46
- package/dist/hooks/hook-llm.js +1 -53
- package/dist/hooks/hook-runner.js +20 -414
- package/dist/http/active-run-conflict.js +4 -129
- package/dist/http/cursor-fingerprint.d.ts +5 -0
- package/dist/http/cursor-fingerprint.js +5 -0
- package/dist/http/idempotency.js +0 -37
- package/dist/http/principal-gate.js +3 -40
- package/dist/http/route-ctx.js +0 -9
- package/dist/http/routes/a2a-serve.js +7 -319
- package/dist/http/routes/admin-config-refresh.js +0 -4
- package/dist/http/routes/admin-drain.js +0 -10
- package/dist/http/routes/adoption.js +1 -29
- package/dist/http/routes/agents-roster.js +1 -47
- package/dist/http/routes/approvals-assistant.js +32 -395
- package/dist/http/routes/attachments.js +4 -22
- package/dist/http/routes/capabilities.js +3 -420
- package/dist/http/routes/diagnostics.js +1 -81
- package/dist/http/routes/fleet.js +9 -185
- package/dist/http/routes/images.js +14 -239
- package/dist/http/routes/leader.js +0 -13
- package/dist/http/routes/memory-bundle.js +1 -61
- package/dist/http/routes/memory-policy.js +9 -99
- package/dist/http/routes/notify-wake.js +3 -37
- package/dist/http/routes/observability.js +2 -19
- package/dist/http/routes/retention-ops.js +2 -34
- package/dist/http/routes/rules.js +1 -93
- package/dist/http/routes/runs.js +64 -873
- package/dist/http/routes/session-sync.js +19 -258
- package/dist/http/routes/sessions-list.js +17 -43
- package/dist/http/routes/sessions.js +34 -226
- package/dist/http/routes/shared-memory.js +5 -36
- package/dist/http/routes/side-query.js +1 -87
- package/dist/http/routes/tasks.js +52 -722
- package/dist/http/routes/trace-usage.js +32 -195
- package/dist/http/routes/workflows.js +19 -195
- package/dist/http/run-meta.js +0 -6
- package/dist/http/send.js +0 -32
- package/dist/http/server.js +92 -1597
- package/dist/http/sse-lifecycle.js +2 -13
- package/dist/http/sse-log.js +3 -48
- package/dist/http/tar.js +5 -21
- package/dist/http/verify-rounds.js +0 -5
- package/dist/http/wire-gate.js +0 -9
- package/dist/http/workspace-content.js +0 -10
- package/dist/images/bake-validate.js +1 -70
- package/dist/images/manifest.js +1 -6
- package/dist/index.js +0 -21
- package/dist/key-resolver.js +2 -17
- package/dist/leader/diffout.js +1 -20
- package/dist/leader/diffup.js +0 -47
- package/dist/leader/endpoint.js +2 -57
- package/dist/leader/fanout.js +3 -45
- package/dist/leader/grader-env-factory.js +3 -72
- package/dist/leader/leader.js +5 -156
- package/dist/leader/merge.js +8 -115
- package/dist/leader/planner.js +3 -54
- package/dist/leader/repair-oracle.js +1 -60
- package/dist/leader/repair-wire.js +2 -79
- package/dist/leader/wire.js +8 -307
- package/dist/lsp/e2b-bridge.js +4 -64
- package/dist/lsp/e2b-manager.js +6 -94
- package/dist/lsp/lsp-frames.js +0 -12
- package/dist/lsp/manager.js +4 -96
- package/dist/lsp/ws-transport.js +5 -55
- package/dist/lsp-evict.js +1 -15
- package/dist/main.js +57 -765
- package/dist/memory-bundle-engine.js +0 -55
- package/dist/memory-export.js +0 -4
- package/dist/memory-posture.js +1 -15
- package/dist/memory-scope.js +11 -148
- package/dist/memory-sync-client.js +2 -44
- package/dist/memory-sync.js +1 -80
- package/dist/model-select.js +3 -80
- package/dist/observability/cost-quota.js +1 -17
- package/dist/observability/cost-taxonomy.js +0 -34
- package/dist/observability/fail-open.js +7 -86
- package/dist/observability/logger.js +0 -6
- package/dist/observability/metrics.js +0 -94
- package/dist/observability/otel-exporter.js +3 -13
- package/dist/observability/principal-context.js +0 -9
- package/dist/observability/prompt-manifest.js +1 -37
- package/dist/observability/rate-limit.js +0 -4
- package/dist/observability/secret-env-scrub.js +2 -56
- package/dist/observability/tool-trace.js +1 -70
- package/dist/orchestration/hardened-vm-runner.js +4 -118
- package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
- package/dist/orchestration/hardened-vm-worker.js +0 -27
- package/dist/orchestration/subagent-steer.js +1 -45
- package/dist/orchestration/workflow-agent-steer.js +1 -80
- package/dist/orchestration/workflow-completion-inbox.js +32 -285
- package/dist/orchestration/workflow-notify-journal.js +16 -259
- package/dist/org-memory-admission.js +3 -47
- package/dist/parent-watch.js +2 -48
- package/dist/parked-decide.js +1 -109
- package/dist/per-task-image.js +0 -57
- package/dist/plan-cache-probe.js +3 -27
- package/dist/plugins/adoption-log-sql.js +2 -119
- package/dist/plugins/approval-ask-store-memory.js +3 -38
- package/dist/plugins/approval-ask-store-sql.js +6 -188
- package/dist/plugins/approval-exemption-store.js +2 -28
- package/dist/plugins/background-agent-store-sql.js +4 -105
- package/dist/plugins/background-shell-support.js +14 -122
- package/dist/plugins/blob-backend.js +6 -169
- package/dist/plugins/breaker-state-sql.js +8 -46
- package/dist/plugins/caching-session-store.js +4 -106
- package/dist/plugins/checkpoint-store-sql.js +18 -505
- package/dist/plugins/e2b-orphan-reclaim.js +0 -45
- package/dist/plugins/file-outcome-sink.js +0 -9
- package/dist/plugins/file-resume-anchor-store.js +4 -43
- package/dist/plugins/file-run-store.js +26 -364
- package/dist/plugins/file-snapshot-store-sql.js +10 -181
- package/dist/plugins/fork-routing-session-store.js +8 -111
- package/dist/plugins/host-platform.js +2 -91
- package/dist/plugins/image-bake-store-sql.js +7 -250
- package/dist/plugins/image-index-sql.js +4 -123
- package/dist/plugins/k8s-bg-scripts.js +4 -88
- package/dist/plugins/k8s-exec-protocol.js +0 -41
- package/dist/plugins/leader-run-store-sql.js +0 -101
- package/dist/plugins/local-checkpoint-store.js +8 -128
- package/dist/plugins/local-session-store.js +34 -296
- package/dist/plugins/local-task-attachment-store.js +2 -16
- package/dist/plugins/mailbox-store-sql.js +8 -65
- package/dist/plugins/memory-embedder-fingerprint.js +5 -166
- package/dist/plugins/memory-embedder.js +4 -70
- package/dist/plugins/memory-engine-pg.js +6 -170
- package/dist/plugins/memory-engine-tidb.js +7 -155
- package/dist/plugins/memory-engine-vector-util.js +0 -10
- package/dist/plugins/memory-key-guards.js +0 -34
- package/dist/plugins/memory-origin-law.js +0 -187
- package/dist/plugins/memory-resume-anchor-store.js +0 -17
- package/dist/plugins/memory-run-store.js +12 -89
- package/dist/plugins/memory-session-policy-store.js +0 -17
- package/dist/plugins/memory-sync-store-pg.js +4 -49
- package/dist/plugins/memory-sync-store-tidb.js +3 -35
- package/dist/plugins/outcome-ledger-sql.js +3 -97
- package/dist/plugins/permission-rule-store-file.js +5 -133
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +50 -307
- package/dist/plugins/pg-cost-quota.js +0 -7
- package/dist/plugins/pg-pool.js +0 -92
- package/dist/plugins/pg-rate-limiter.js +2 -13
- package/dist/plugins/pg-safe-json.js +4 -40
- package/dist/plugins/pg-session-storage.js +25 -189
- package/dist/plugins/posix-shell-fs.js +1 -31
- package/dist/plugins/remote-env-adb.js +12 -101
- package/dist/plugins/remote-env-e2b.js +40 -371
- package/dist/plugins/remote-env-file-error.js +0 -37
- package/dist/plugins/remote-env-host.js +63 -483
- package/dist/plugins/remote-env-k8s.js +32 -326
- package/dist/plugins/remote-env-local-docker.js +24 -157
- package/dist/plugins/remote-env-ssh.js +20 -128
- package/dist/plugins/remote-scratchpad.js +2 -32
- package/dist/plugins/remote-shell.js +1 -32
- package/dist/plugins/resume-anchor-store-sql.js +0 -11
- package/dist/plugins/retention-lane-store-sql.js +0 -108
- package/dist/plugins/retention-store-sql.js +4 -383
- package/dist/plugins/roster-store-sql.js +0 -55
- package/dist/plugins/run-store-sql.js +14 -251
- package/dist/plugins/s3-presign.js +2 -49
- package/dist/plugins/scheduler-support.js +3 -80
- package/dist/plugins/send-file-ledger.js +4 -53
- package/dist/plugins/send-user-file.js +4 -94
- package/dist/plugins/session-placement.js +1 -89
- package/dist/plugins/session-policy-store-sql.js +3 -81
- package/dist/plugins/session-store.js +0 -57
- package/dist/plugins/shared-memory-store-sql.js +4 -167
- package/dist/plugins/sql-driver.js +0 -17
- package/dist/plugins/sql-errors.js +0 -7
- package/dist/plugins/sql-escape.js +0 -8
- package/dist/plugins/sql-row-helpers.js +0 -25
- package/dist/plugins/store-backend.js +42 -222
- package/dist/plugins/store-contracts.js +2 -46
- package/dist/plugins/task-attachment-store.js +3 -49
- package/dist/plugins/task-list-store-sql.js +0 -79
- package/dist/plugins/tidb-cost-quota.js +1 -4
- package/dist/plugins/tidb-pool.js +1 -207
- package/dist/plugins/tidb-rate-limiter.js +3 -9
- package/dist/plugins/tidb-session-storage.js +4 -70
- package/dist/plugins/tidb-session-store.js +19 -341
- package/dist/plugins/tool-result-store-sql.js +4 -179
- package/dist/plugins/usage-window-store-sql.js +0 -8
- package/dist/plugins/web-search.js +10 -132
- package/dist/plugins/workflow-journal-store-sql.js +2 -58
- package/dist/plugins/workflow-run-store-sql.js +6 -91
- package/dist/plugins/worktree-isolation.js +6 -126
- package/dist/plugins/write-behind-counter.js +16 -75
- package/dist/principal-jwt.js +5 -60
- package/dist/project-memory.js +15 -146
- package/dist/prompts-domain-validate.js +1 -51
- package/dist/question.js +2 -111
- package/dist/resource-suspend.js +0 -18
- package/dist/router/route-orchestration.js +0 -77
- package/dist/rules-consent.d.ts +44 -5
- package/dist/rules-consent.js +75 -215
- package/dist/run-local.js +14 -381
- package/dist/runs.js +29 -617
- package/dist/runtime-caps-resolver.js +6 -132
- package/dist/runtime-governance.js +1 -232
- package/dist/sandbox-pkg-source.js +0 -37
- package/dist/sealed-key.js +3 -68
- package/dist/security.js +6 -291
- package/dist/session-leaf-bus.js +0 -32
- package/dist/session-sync-content.js +1 -69
- package/dist/session-sync-kernel.js +3 -60
- package/dist/session-sync.js +3 -66
- package/dist/session-titler.js +7 -41
- package/dist/session-watch.js +9 -73
- package/dist/shared-memory-scope-authorizer.js +0 -18
- package/dist/sighup-idle.js +1 -12
- package/dist/spec-fields.js +4 -143
- package/dist/store-live-probe.js +3 -49
- package/dist/task-a2a.js +1 -126
- package/dist/task-cwd.js +1 -103
- package/dist/task-mcp.js +1 -89
- package/dist/task-settings.js +10 -300
- package/dist/task-workflow.js +8 -75
- package/dist/tool-approval.d.ts +28 -1
- package/dist/tool-approval.js +70 -1430
- package/dist/trace/artifacts.js +5 -20
- package/dist/trace/engine-notice-wire.js +6 -138
- package/dist/trace/ledger-sink.js +7 -96
- package/dist/trace/project.js +4 -448
- package/dist/trace/redact.js +9 -77
- package/dist/turn-activity.js +1 -27
- package/dist/usage-analytics.js +4 -34
- package/dist/wall-clock-jump-guard.js +1 -68
- package/package.json +3 -3
- package/dist/capabilities/scenario-alias.d.ts +0 -27
- package/dist/capabilities/scenario-alias.js +0 -61
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
4
|
-
/** 纯数据产物(`build*`):三个门的当前读数。`streamApproval` 走**单一谓词**
|
|
5
|
-
* `resolveStreamApprovalGate` —— 与 `/v1/capabilities` 的 `streamApproval` 位、与回决口的 501 门同一个
|
|
6
|
-
* 符号,所以诊断页读到的与消费端撞到的行为不可能不同。 */
|
|
7
4
|
export function buildServerWiringGates(input) {
|
|
8
5
|
const gate = resolveStreamApprovalGate({
|
|
9
6
|
toolApprovalEnabled: input.toolApprovalEnabled,
|
|
@@ -17,25 +14,6 @@ export function buildServerWiringGates(input) {
|
|
|
17
14
|
checkpointStore: input.checkpointStore !== undefined,
|
|
18
15
|
};
|
|
19
16
|
}
|
|
20
|
-
/**
|
|
21
|
-
* 三项对表(设计稿件二)。判据全是**两个独立谓词对同一件事的答案**,矛盾即装配谎言 —— 装配谎言不是
|
|
22
|
-
* 「配置不理想」,是「服务端对自己接了什么线的两套说法互斥」,继续启动只会把矛盾带进运行期的每一条腿。
|
|
23
|
-
*
|
|
24
|
-
* ① park 设施:core 从 `spec.checkpointStore ?? deps.checkpointStore` 判 `parkLane.capable`,我方从
|
|
25
|
-
* `backend.checkpoint ∧ DURABLE_APPROVAL` 判 `parkFacility` 并据此决定流内审批协议上不上场。两者
|
|
26
|
-
* 不等 ⇒ 协议按「窗到期能 park」上场而引擎侧根本不 park(结局是 core fail-closed deny),或反之
|
|
27
|
-
* 白白关掉一个可用的协议。`capable` 真而 `effective` 不真同样致命:能力在、这条部署形上却不会真的
|
|
28
|
-
* park,窗到期依旧无降级目的地。
|
|
29
|
-
* ② 会话店持久性:manifest 声明 `declared_durable` 而后端是 local / 根本没有后端 ⇒ 「重启存活」的声明
|
|
30
|
-
* 没有介质兑现。这条与 memory+durable 两条既有拒启同族(声明与介质必须对得上)。
|
|
31
|
-
* ③ interaction posture 声明 `interactive` 却没有任何 question 投递面 ⇒ 只 **warn**:core 在 prepare 时
|
|
32
|
-
* 会按腿拒,boot 先一步把话说成人话。不拒启,因为部署形本身可能只是暂时没接活体面,而 core 侧已有
|
|
33
|
-
* 兜底门 —— 拒启会把一个可运行的部署挡在门外。
|
|
34
|
-
* ④ park 车道的持久性读数不可信时 ⇒ **warn**(理由与这条判据为什么不是拒启,见其行内注)。
|
|
35
|
-
*
|
|
36
|
-
* 判决分两级是有判据的:**拒启**留给「两个谓词对同一件事互斥」(其中一个必错,继续跑就是带着谎言服务);
|
|
37
|
-
* **警告**留给「读数不可信/声明不完整」(信号本身还不足以断定谁错,据此拒启会挡住正常部署)。
|
|
38
|
-
*/
|
|
39
17
|
export function buildStaticWiringAudit(facts) {
|
|
40
18
|
const refusals = [];
|
|
41
19
|
const warnings = [];
|
|
@@ -49,23 +27,10 @@ export function buildStaticWiringAudit(facts) {
|
|
|
49
27
|
refusals.push(`park facility contradiction: this server assembled its approval gates with parkFacility=true, but the engine's static wiring resolves parkLane.effective=${JSON.stringify(park.effective)} (reasons: ${park.reasons.join(", ") || "none"}). ` +
|
|
50
28
|
`A window expiry would then have no degradation target and adjudicate as a fail-closed deny — refusing to start.`);
|
|
51
29
|
}
|
|
52
|
-
// 判据写成「不在持久集合里」而不是「是 local」是有方向的:将来多一种 backend kind 时,它会**拒启**
|
|
53
|
-
// 直到有人显式把它判进持久集,而不是静默通过 —— 声明与介质的对表,失败方向必须朝拒绝那边。
|
|
54
30
|
if (facts.manifest.session.store === "declared_durable" && facts.backendKind !== "mysql" && facts.backendKind !== "pg") {
|
|
55
31
|
refusals.push(`session store durability contradiction: the engine's static wiring reports session.store="declared_durable" while the store backend is ${facts.backendKind === undefined ? "absent (env-only worker)" : `"${facts.backendKind}"`}. ` +
|
|
56
32
|
`A durable declaration with no durable medium is a claim nothing can honor — refusing to start. Wire DB_BACKEND=mysql|pg, or fix the store's durability declaration.`);
|
|
57
33
|
}
|
|
58
|
-
// park 车道的**持久性读数**(codex 交叉复审 2026-08-07 R2-F2,验真后部分采纳)。`capable` 说的是
|
|
59
|
-
// 「有没有 checkpoint 店」、`effective` 说的是「这条部署形会不会真 park」—— 两者都为真时仍可能是一个
|
|
60
|
-
// 重启即丢的店,那样窗到期后被 park 的审批在重启后无法赎回,而流内审批协议正是拿 park 当降级目的地。
|
|
61
|
-
// ✅ **读数已转可信**(#168 件5,2026-08-07):树上三条 checkpoint 店(SQL 双生 + local 文件形)现在都
|
|
62
|
-
// 如实声明了 `durability`,所以这条读数不再对真持久部署恒假 —— 它现在说的就是字面意思。
|
|
63
|
-
//
|
|
64
|
-
// 🔴 仍然是 **warn 而不是拒启**,判据与 session 那条**刻意不同**,理由写在这里免得下一个人以为是漏改:
|
|
65
|
-
// session 那条对表的是「声明 durable × 介质不 durable」——两个已知量互相矛盾,拒启是查出了谎。这一条
|
|
66
|
-
// 对表的是「有没有人声明」,而 core 把**缺席**折成 `process-local`;一个合法的第三方/新增 checkpoint
|
|
67
|
-
// 店没表态时,拒启会挡住一个本来跑得好好的部署,而**没有任何旋钮**能让它继续启动(补偿为零 ⇒ 不升格)。
|
|
68
|
-
// 升格的前置件 = core 把「未声明」与「声明为进程内」分成两个可区分读数,或本仓给出一个显式承认旋钮。
|
|
69
34
|
if (facts.parkFacility && facts.manifest.parkLane.checkpointDurability !== "declared_durable") {
|
|
70
35
|
warnings.push(`park facility is enabled but the checkpoint store does not declare durable storage ` +
|
|
71
36
|
`(parkLane.checkpointDurability=${JSON.stringify(facts.manifest.parkLane.checkpointDurability ?? null)}). ` +
|
|
@@ -78,10 +43,6 @@ export function buildStaticWiringAudit(facts) {
|
|
|
78
43
|
}
|
|
79
44
|
return { refusals, warnings };
|
|
80
45
|
}
|
|
81
|
-
/**
|
|
82
|
-
* boot 消费口:`refusals` 非空即抛(拒启,与 memory+durable 两条既有拒启同族的 fail-loud 形),
|
|
83
|
-
* `warnings` 逐条 warn。返回判决本体,调用方可以继续把读数打进启动 info 行。
|
|
84
|
-
*/
|
|
85
46
|
export function assertStaticWiringConsistent(facts, logger) {
|
|
86
47
|
const audit = buildStaticWiringAudit(facts);
|
|
87
48
|
for (const w of audit.warnings)
|
|
@@ -91,24 +52,10 @@ export function assertStaticWiringConsistent(facts, logger) {
|
|
|
91
52
|
}
|
|
92
53
|
return audit;
|
|
93
54
|
}
|
|
94
|
-
/**
|
|
95
|
-
* [3399]③ clay 裁 (b)(2026-08-12,core [3648]④ + cli [3649]① 双背书):**零凭证 turnkey 形**的
|
|
96
|
-
* loopback 来源门判据(单一属主,端点与测试同吃)。
|
|
97
|
-
*
|
|
98
|
-
* 只在「operator 名单空 ∧ 非多租户 ∧ 全局 service-credential 门也不在场(没配 SERVICE_AUTH_TOKEN /
|
|
99
|
-
* AUTH_TOKENS)」的部署形上判——那正是全局门整个不设、诊断端点此前无认证可读的形。诊断面泄的是装配
|
|
100
|
-
* 拓扑(store 接线/能力位/治理姿态),对误暴露公网端口的 turnkey 机器构成侦察面。(b) 对本机自用零摩擦
|
|
101
|
-
* (壳缺省钉 BIND_HOST=127.0.0.1,local curl 照常),对远程侦察 fail-closed;配了 token 的部署远程读
|
|
102
|
-
* 走认证(全局门),不进本臂。
|
|
103
|
-
*
|
|
104
|
-
* 判源=TCP 对端地址(`req.socket.remoteAddress`),**非任何头**——XFF 类头在无反代的 turnkey 形上本就
|
|
105
|
-
* 是伪造面;有反代的部署必然配 token(反代后 remoteAddress 恒 loopback,门自然放行,语义仍对:该形的
|
|
106
|
-
* 边界由反代+token 承担)。`remoteAddress` 缺席(socket 已断等边缘)⇒ 判拒(fail-closed,安全轴方向)。
|
|
107
|
-
*/
|
|
108
55
|
export function zeroCredentialLoopbackDenied(remoteAddress, config) {
|
|
109
56
|
const anyServiceAuth = Boolean(config.authToken) || Object.keys(config.authTokens ?? {}).length > 0;
|
|
110
57
|
if (anyServiceAuth)
|
|
111
|
-
return false;
|
|
58
|
+
return false;
|
|
112
59
|
const addr = remoteAddress ?? "";
|
|
113
60
|
const isLoopback = addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
114
61
|
return !isLoopback;
|
|
@@ -120,22 +67,6 @@ export async function handleDiagnostics(req, res, url, ctx) {
|
|
|
120
67
|
}
|
|
121
68
|
async function handleDiagnosticsBody(req, res, url, ctx, miss) {
|
|
122
69
|
const { deps } = ctx;
|
|
123
|
-
// 件三:「这台 worker 到底接了什么线」一屏看清 = core 的静态半场(含 `governance` 段 —— 本面**就是**
|
|
124
|
-
// 那个段的 operator 受众)+ server 自己三个装配谓词的读数。
|
|
125
|
-
//
|
|
126
|
-
// 门 = `explicitOperatorOk`(**不是** `isOperator`)。执法条件是**名单在场 ∨ 多租户**,不是只看
|
|
127
|
-
// `REQUIRE_PRINCIPAL`:本件的 SSE 腿对同一段 governance 恒判 `explicitOperatorOk`,若这里只在
|
|
128
|
-
// REQUIRE_PRINCIPAL 时判,「列了 operator 名单却没开 REQUIRE_PRINCIPAL」的部署上就成了流里关着、
|
|
129
|
-
// 端点上开着 —— 一个秘密两套门,严的那道白严(codex 交叉复审 2026-08-07 R2-F1,验真后采纳)。
|
|
130
|
-
// 名单为空 ∧ 非多租户 = 单租户 turnkey:该部署没有 operator 这个概念,唯一的用户就是 operator,
|
|
131
|
-
// 边界由全局 service-credential 门把守(与 `GET /v1/outcomes` 同裁)。
|
|
132
|
-
// 401 由更上游的全局门给出,本域只判 operator 与否。
|
|
133
|
-
//
|
|
134
|
-
// ⚠️ 与 SSE 腿的**残余不对称**(显式裁定,不是疏忽):turnkey 形上流里仍然谁都拿不到治理段,而本端点
|
|
135
|
-
// 给。理由=流是多订阅面且身份来自连接、端点是一次显式的 operator 请求;失败方向因此在流那侧更严。
|
|
136
|
-
//
|
|
137
|
-
// **不含** per-leg effective 历史:治理段只存在于 live 帧的 operator 投影里,durable 账本恒存剥后形
|
|
138
|
-
// (账本写时不知道未来读者是谁)。历史考古是另一件事,不在本面伪装成有。
|
|
139
70
|
if (req.method === "GET" && url === "/v1/diagnostics/wiring") {
|
|
140
71
|
if (deps.config.operatorPrincipals.length > 0 || deps.config.requirePrincipal) {
|
|
141
72
|
const principal = gatedPrincipal(req, deps.config);
|
|
@@ -148,25 +79,14 @@ async function handleDiagnosticsBody(req, res, url, ctx, miss) {
|
|
|
148
79
|
sendError(res, 403, "auth.loopback_only", "wiring diagnostics on a zero-credential deployment are readable from loopback only — configure SERVICE_AUTH_TOKEN to read remotely");
|
|
149
80
|
return;
|
|
150
81
|
}
|
|
151
|
-
// 静态半场是 **composition root 在 boot 算的**(与拒启自检同一份产物,不可能与它不同)。缺席 ⇒ 这个
|
|
152
|
-
// 进程不是由 main.ts 装起来的(测试夹具形),此面诚实地不存在 —— 落到全局 404,而不是回一个空壳 200。
|
|
153
82
|
if (!deps.staticWiring) {
|
|
154
83
|
miss.fell = true;
|
|
155
84
|
return;
|
|
156
85
|
}
|
|
157
86
|
sendJson(res, 200, {
|
|
158
87
|
static: deps.staticWiring,
|
|
159
|
-
// #252 件2:记忆面姿态(backend / vectorMode / 点亮态 / dark 成因 / embedder 身份)。此前这些事实
|
|
160
|
-
// **只**活在启动日志里,而日志不可查询、跨副本不可聚合 —— cli 的记忆 UX 与运维都只能读日志或按 env
|
|
161
|
-
// 自己推(推不出来:多租户 file 底座上 `MEMORY_ENGINE=on` 其实是暗的)。additive 键,与 `serverGates`
|
|
162
|
-
// 同受众(operator-only,门在上方,本段不自带更松的门)。
|
|
163
|
-
// 缺席 ⇒ 显式 `null`(「这个进程说不出来」),不铸一个看起来像真答案的空壳对象。
|
|
164
88
|
memoryPosture: deps.memoryPosture ?? null,
|
|
165
|
-
// #322 世代账的 operator 面:按组的 appliedVersion / deferredKeys+铸因 / lastRejected(消毒形——
|
|
166
|
-
// 只有键名与判据文案,值从不进读面)。/health 那三键是同一份读数的窄投影,两面不可能各说各话。
|
|
167
|
-
// 缺席 ⇒ 显式 `null`(与 memoryPosture 同姿势:「这个进程说不出来」,不铸空壳)。
|
|
168
89
|
configApply: deps.configApplyState?.() ?? null,
|
|
169
|
-
// #324:mid-turn MCP 撤销名单(design/338)。null=本进程没有配置管道(说不出来),与上两键同姿势。
|
|
170
90
|
mcpRevocations: deps.mcpRevocationState?.() ?? null,
|
|
171
91
|
serverGates: buildServerWiringGates({
|
|
172
92
|
toolApprovalEnabled: deps.toolApproval !== undefined,
|
|
@@ -7,106 +7,42 @@ export async function handleFleet(req, res, url, ctx) {
|
|
|
7
7
|
await handleFleetBody(req, res, url, ctx, miss);
|
|
8
8
|
return !miss.fell;
|
|
9
9
|
}
|
|
10
|
-
/** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。裸 `return;`
|
|
11
|
-
* = 「本域已应答」;走到函数尾才是「没匹配上」。跨域的东西一律在首行解构出来。 */
|
|
12
10
|
async function handleFleetBody(req, res, url, ctx, miss) {
|
|
13
11
|
const { deps } = ctx;
|
|
14
12
|
const { sessionOwnerScope } = ctx.helpers;
|
|
15
|
-
void url;
|
|
16
|
-
// MF-Fleet (shell-host data contract): GET /v1/fleet/stream — the LIVE multi-row task/workflow fleet view as
|
|
17
|
-
// an SSE PUSH stream (status/elapsed/tokens/doneCount transitions over time). Owner-gated by scope (the
|
|
18
|
-
// caller's own active runs) UNLESS fleet-wide (a deployment trace/auth token sees every tenant's rows, like
|
|
19
|
-
// the trace API). Before the service-credential gate (self-gates by principal), parity with the workflow routes.
|
|
13
|
+
void url;
|
|
20
14
|
if (req.method === "GET" && new URL(req.url ?? "", "http://x").pathname === "/v1/fleet/stream") {
|
|
21
15
|
if (!deps.fleetBus) {
|
|
22
16
|
sendError(res, 501, "capability.self_orchestration_required", "fleet stream requires self-orchestration / fleet wiring");
|
|
23
17
|
return;
|
|
24
18
|
}
|
|
25
|
-
const { fleetWide } = sessionOwnerScope(req);
|
|
26
|
-
const principal = gatedPrincipal(req, deps.config);
|
|
19
|
+
const { fleetWide } = sessionOwnerScope(req);
|
|
20
|
+
const principal = gatedPrincipal(req, deps.config);
|
|
27
21
|
if (deps.config.requirePrincipal && !principal && !fleetWide) {
|
|
28
22
|
sendError(res, 401, "auth.principal_required", `missing principal header '${deps.config.principalHeader}'`);
|
|
29
23
|
return;
|
|
30
24
|
}
|
|
31
|
-
// #3 fleet per-session scope (clay 2026-06-30): the shell sends its current sessionId as `?session=` to see ONLY
|
|
32
|
-
// this session's rows (not other sessions of the same principal — the "他会话 token" leak). Absent ⇒
|
|
33
|
-
// unscoped (all of the principal's rows — back-compat).
|
|
34
|
-
// 🔴 cli[1524] HIGH-1(V5c 回归锁首跑抓获):此前 fleetWide 把 callerSession 也强制 null——无 auth
|
|
35
|
-
// 部署(isFleetWide 第一臂:无 token/无 traceToken/无 requirePrincipal=恒 fleetWide,TOC loopback
|
|
36
|
-
// 默认形全中)下会话过滤/通知 fail-closed 门整体旁路成全量广播,而 meta 的 bgNotifyFailClosed 照报
|
|
37
|
-
// true=标说谎;1.245 的注入 fail-closed 与 1.247 的代际标在默认部署从未真正生效(一直是壳侧台账
|
|
38
|
-
// 在兜)。修:fleetWide 是**能力上限**(可看全量=豁免 scope 授权轴),不是「强制 unscoped」——
|
|
39
|
-
// 显式带 `?session=` 的请求无论 fleetWide 与否都按会话过滤(观察者要全量=不带 session;要跟单
|
|
40
|
-
// 会话且不烧 durable 副本=带 session+observe=1,既有防烧口)。meta 的 sessionScoped 由此自动如实,
|
|
41
|
-
// bgNotifyFailClosed 对 session-bound 连接从此真实。
|
|
42
25
|
const fleetParams = new URL(req.url ?? "", "http://x").searchParams;
|
|
43
26
|
const callerSession = fleetParams.get("session");
|
|
44
|
-
// `?observe=1` = a session-bound lane that must NOT serve-fence the durable copy
|
|
45
|
-
// (a non-shell observer following one session). Default (absent) keeps the frame-即-投递 semantics —
|
|
46
|
-
// today's only session-bound consumer IS the model-injecting shell.
|
|
47
27
|
const observeOnly = fleetParams.get("observe") === "1";
|
|
48
|
-
// #189:终态行窗的 durable 读交给 streamFleet 在**订阅注册之后**做(见那里的缓冲段)——放在这里
|
|
49
|
-
// (开流之前)会让"帧已扇出而本连接尚未订阅"的窗口凭空多出一个读的时长,codex R1-H1 红先复现过。
|
|
50
|
-
// [3156] 轮2 真红修:终态窗的查询键必须与可见性判据**同源**——fleetWide 连接(callerScope=null,
|
|
51
|
-
// visW 放行全 scope)此前窗却按 caller principal("default")查,而行的 scope 是提交方自报
|
|
52
|
-
// principal 的透传值(cli 壳本地单用户模式自发 "anon:shell-live";引擎不发明该字面量,[3225]
|
|
53
|
-
// 后续侦察定谳)⇒ durable pull 恒 miss。fleetWide ⇒ null(窗走进程内 seed 缓存,全 scope)。
|
|
54
|
-
// 读侧 `?? "default"` 三处与 core 写侧铸链同源(core prepare-task `spec.principal ?? "default"`,
|
|
55
|
-
// 其 CHANGELOG 成文「Single-tenant spelling is an explicit scope:"default"」)——在 core 撤其
|
|
56
|
-
// 写侧铸点前,server 单方面撤读侧会把无主部署的 durable pull 面弄瞎,故保持同源不动。
|
|
57
28
|
await streamFleet(req, res, deps.fleetBus, fleetWide ? null : (principal ?? "default"), callerSession || null, observeOnly ? undefined : deps.workflowCompletionInbox, { store: deps.workflowRunStore, scope: fleetWide ? null : (principal ?? "default"), pullScope: principal ?? "default", ready: deps.fleetRecoveryDone });
|
|
58
29
|
return;
|
|
59
30
|
}
|
|
60
31
|
miss.fell = true;
|
|
61
32
|
}
|
|
62
|
-
/** MF-Fleet: SSE the live fleet (active task + workflow rows) — a SNAPSHOT on connect, then every per-row delta
|
|
63
|
-
* the {@link FleetEventBus} fans out. `callerScope === null` ⇒ fleet-wide (a trace/auth token sees all tenants);
|
|
64
|
-
* else owner-scoped (only rows whose `scope` matches). The tenant-internal `scope` field is STRIPPED from every
|
|
65
|
-
* emitted row (it's a gate input, never rendered). A removal is forwarded ONLY for a row this connection actually
|
|
66
|
-
* saw (no id-existence oracle for an unseen tenant's run). Heartbeat + dual-close cleanup unsubscribe the bus. */
|
|
67
33
|
function streamFleet(req, res, bus, callerScope, callerSession = null, completionInbox, terminal) {
|
|
68
34
|
return new Promise((resolve) => {
|
|
69
35
|
sseHeaders(res);
|
|
70
|
-
// [1508] 1.247 `bgNotifyFailClosed` — the GENERATION marker cli asked for ([1500]📌/[1502] F2): declares
|
|
71
|
-
// this server's bg_notification injection path fails CLOSED for scoped subscribers (1.245+ [1493] fix), so
|
|
72
|
-
// a new-generation shell can drop its frame-level own/foreign checks entirely (incl. the cross-shell
|
|
73
|
-
// restart edge) while keeping them against older servers whose meta lacks the field. Additive (version
|
|
74
|
-
// stays 1): older shells ignore unknown fields.
|
|
75
|
-
// `type: "meta"` 与 `event: meta` 同名双发(tasks/trace-usage/approvals 三流同款):部分代理只转发
|
|
76
|
-
// `data:` 行,按 data.type 分派的消费端否则认不出首帧。additive,老消费端忽略未知键。
|
|
77
|
-
// [4119] P-31 Q2(cli design/285,additive):session-bound 连接回显**本连接实际生效**的 session id。
|
|
78
|
-
// 客户端 `server_fail_closed` 可信臂零内容自校——它信的是「server 按会话过滤了」这句话本身;把过滤
|
|
79
|
-
// 判据说出来,客户端才能与本 ledger 会话锚**比对相等**后解封最强档,而不是在 keyed 多会话宿主上
|
|
80
|
-
// 永远诚实封顶。键与 `sessionScoped` 对偶:unscoped 连接缺席(不发空串);值=`callerSession` 逐字
|
|
81
|
-
// (即 streamFleet 真用于行过滤的那一个量,不另铸第二读点)。
|
|
82
36
|
res.write(`event: meta\ndata: ${JSON.stringify({ type: "meta", version: 1, scoped: callerScope !== null, sessionScoped: callerSession !== null, ...(callerSession !== null ? { session: callerSession } : {}), bgNotifyFailClosed: true })}\n\n`);
|
|
83
37
|
const seenTasks = new Set();
|
|
84
38
|
const seenWf = new Set();
|
|
85
|
-
// #3 fleet per-session scope (clay 2026-06-30): a row is visible iff it matches the caller's principal scope AND
|
|
86
|
-
// — when the caller sent `?session=` — its `sessionId`. A row WITHOUT a sessionId stays visible (back-compat for
|
|
87
|
-
// unscoped rows); the leak this closes is a row whose sessionId is ANOTHER session of the same principal.
|
|
88
39
|
const sessionOk = (rowSession) => callerSession === null || rowSession === undefined || rowSession === callerSession;
|
|
89
40
|
const visT = (r) => (callerScope === null || r.scope === callerScope) && sessionOk(r.sessionId);
|
|
90
41
|
const visW = (r) => (callerScope === null || r.scope === callerScope) && sessionOk(r.sessionId);
|
|
91
|
-
// [1493] fable5 R1 (display plane): an UNATTRIBUTED row (no sessionId — restart lazy-mint / core version
|
|
92
|
-
// skew edges; post-Part-A every normal spawn is tagged) stays fail-open VISIBLE to a scoped subscriber
|
|
93
|
-
// (liveness back-compat: your own row after a restart still shows as running), but its content fields are
|
|
94
|
-
// conversation-derived text — `name` is the objective preview, `description`/`agentType`/`agentName` label
|
|
95
|
-
// the ask, `currentAction` is a live tool line. None of that may cross the session boundary, so a SCOPED
|
|
96
|
-
// subscriber gets a content-scrubbed projection (ids/status/progress/timing kept); a fleet-wide observer
|
|
97
|
-
// (trace/dashboard, callerSession === null) keeps the full row. Rows tagged with the caller's own session
|
|
98
|
-
// pass sessionOk and are never scrubbed (r.sessionId present ⇒ it equals callerSession here).
|
|
99
|
-
// codex R6:FleetTaskRow 逐键处置表——`satisfies Record<keyof FleetTaskRow,…>` 使**任何**行新键先在
|
|
100
|
-
// 编译期红(TS2741),强迫对跨会话可见性表态;"keep" 键必须同步进下方白名单构造,配套钉在
|
|
101
|
-
// test/fleet-bg-child.test.ts(R6 格:kept 键集 ⊆ 表内 keep∪rewrite 集)。
|
|
102
42
|
const SCOPED_TASK_KEY_DISPOSITION = {
|
|
103
43
|
id: "keep", parentId: "keep", parentToolCallId: "keep", workflowRunId: "keep", status: "keep",
|
|
104
44
|
startedAt: "keep", elapsedMs: "keep", tokens: "keep", queuedCount: "keep",
|
|
105
45
|
awaitingPlanApproval: "keep", toolUses: "keep", usage: "keep", resumable: "keep", sourceLane: "keep",
|
|
106
|
-
// #261 §2:两键都是**结构事实**,零会话内容 —— `cycleSeq` 是 core registry 行的代际号
|
|
107
|
-
// (数值,消费端判「复活 vs 前代迟到帧」的唯一钥匙),`retiredBy` 说的是「这条终态是对账腿从
|
|
108
|
-
// durable 投影的、不是发布方亲报的」。降权投影里丢掉它们 = 未署名会话的行**判不了代**,而复活
|
|
109
|
-
// 与迟到帧在无代际号时不可区分(正是本 issue 要消灭的那类静默错判)。故 keep。
|
|
110
46
|
cycleSeq: "keep", retiredBy: "keep",
|
|
111
47
|
name: "rewrite", scope: "strip", sessionId: "strip",
|
|
112
48
|
description: "scrub", agentType: "scrub", agentName: "scrub", currentAction: "scrub",
|
|
@@ -116,9 +52,6 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
116
52
|
const stripT = (r) => {
|
|
117
53
|
const { scope: _s, sessionId: _sid, ...rest } = r;
|
|
118
54
|
if (callerSession !== null && r.sessionId === undefined) {
|
|
119
|
-
// [3136]②→codex R6(确认轮):黑名单形被两次击穿(currentTool、editedFiles)——同一信任边界的键
|
|
120
|
-
// 靠逐个想起来列,漏一个就是跨会话内容泄漏。改**白名单构造**:kept 对象逐键显式拼,未列键默认
|
|
121
|
-
// 不过;新键先过上方处置表的编译期门。
|
|
122
55
|
return {
|
|
123
56
|
id: rest.id,
|
|
124
57
|
status: rest.status,
|
|
@@ -135,14 +68,12 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
135
68
|
...(rest.usage !== undefined ? { usage: rest.usage } : {}),
|
|
136
69
|
...(rest.resumable !== undefined ? { resumable: rest.resumable } : {}),
|
|
137
70
|
...(rest.sourceLane !== undefined ? { sourceLane: rest.sourceLane } : {}),
|
|
138
|
-
...(rest.cycleSeq !== undefined ? { cycleSeq: rest.cycleSeq } : {}),
|
|
71
|
+
...(rest.cycleSeq !== undefined ? { cycleSeq: rest.cycleSeq } : {}),
|
|
139
72
|
...(rest.retiredBy !== undefined ? { retiredBy: rest.retiredBy } : {}),
|
|
140
73
|
};
|
|
141
74
|
}
|
|
142
75
|
return rest;
|
|
143
76
|
};
|
|
144
|
-
// A-002.5(#194 病族 F2):workflow 行与 task 行同一条降权跨会话边界——stripT 的黑名单三次被
|
|
145
|
-
// 击穿后改了白名单,stripW 当时漏改(同函数体孪生)。处置表同姿势:行加新键先编译红强迫表态。
|
|
146
77
|
const SCOPED_WORKFLOW_KEY_DISPOSITION = {
|
|
147
78
|
id: "keep", status: "keep", doneCount: "keep", totalCount: "keep", failedCount: "keep",
|
|
148
79
|
startedCount: "keep", elapsedMs: "keep", tokens: "keep",
|
|
@@ -153,7 +84,6 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
153
84
|
const stripW = (r) => {
|
|
154
85
|
const { scope: _s, sessionId: _sid, ...rest } = r;
|
|
155
86
|
if (callerSession !== null && r.sessionId === undefined) {
|
|
156
|
-
// 白名单构造:kept 逐键显式拼,未列键默认不过(与 stripT 同形;"keep" 键必须同步进处置表)。
|
|
157
87
|
return {
|
|
158
88
|
id: rest.id,
|
|
159
89
|
status: rest.status,
|
|
@@ -168,22 +98,11 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
168
98
|
}
|
|
169
99
|
return rest;
|
|
170
100
|
};
|
|
171
|
-
// [2373]C-12:data 里补 `type`(=event 名)双发——代理剥 `event:` 行/onmessage 忽略具名事件时,
|
|
172
|
-
// 按 data.type 分派的客户端在本流拿到的是 undefined(approvals 流注释里的真实约束,同理适用)。
|
|
173
|
-
// 单点注入:调用方已带 type 的帧(hook_notice/bg_notification)保持原值。
|
|
174
101
|
const send = (event, data) => { if (!res.writableEnded)
|
|
175
102
|
res.write(`event: ${event}\ndata: ${JSON.stringify({ type: event, ...data })}\n\n`); };
|
|
176
|
-
/** #189:快照里那些**按窗保留**的 workflow 行 id(来自 durable 终态窗,或握手期间才翻终态的行)
|
|
177
|
-
* —— 见 workflow / workflow_remove 两臂。 */
|
|
178
103
|
const retainedTerminalWf = new Set();
|
|
179
|
-
/** 现在是否正在重放握手期缓冲(只在这段里,终态行帧才升级为"保留行")。 */
|
|
180
104
|
let replayingBuffer = false;
|
|
181
|
-
/** [3193] codex R4:首见即终态的历史行保留**预算**(每连接)。retainedTerminalWf 本身不积累
|
|
182
|
-
* (每个 id 的撤行帧即刻消费),真正会无界的是「被豁免撤行、留在面板与 seenWf 里的历史行数」。
|
|
183
|
-
* 预算=终态窗容量同阶(×2);耗尽后退回旧「终帧→撤行」契约(行瞬态可见)——有界降级,不吞帧。
|
|
184
|
-
* 快照径的保留行不占预算(已由窗的行数+窗龄双界)。 */
|
|
185
105
|
let retainedHistoryBudget = FLEET_SNAPSHOT_TERMINAL_MAX_ROWS * 2;
|
|
186
|
-
/** 一条增量帧的处理(**先注册订阅、后组装快照**,故与快照解耦成具名函数——见下方缓冲段)。 */
|
|
187
106
|
const handleFrame = (frame) => {
|
|
188
107
|
switch (frame.type) {
|
|
189
108
|
case "task":
|
|
@@ -194,15 +113,6 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
194
113
|
break;
|
|
195
114
|
case "workflow":
|
|
196
115
|
if (visW(frame.row)) {
|
|
197
|
-
// #189 / codex R4-M3 → [3193] ①a 泛化:**本连接首见即终态**的行(重放期或活径皆然)与终态窗
|
|
198
|
-
// 里的行是同一件事——它对这条连接从来不是"活跃集成员",是历史;它的撤行帧描述的活跃集离场
|
|
199
|
-
// 对本连接无意义,照发只会让"看不看得见刚结束的 workflow"取决于 recover 落在握手窗哪一侧
|
|
200
|
-
// (cli [3193] 插桩实证:落在重放窗内=保留可见,落在重放窗后=终帧+撤行背靠背、行瞬现即消)。
|
|
201
|
-
// ⇒ 判据=「重放期 ∪ 首见即终态」并集(#189 原判据的严格超集):重放期臂保住"快照见过
|
|
202
|
-
// running、缓冲里才翻终态"的行(R4-M3);首见臂保住活径上 recover 晚到的行(①a)。见过
|
|
203
|
-
// running 的行在活径仍按既有契约走(终帧→撤行),在线消费端语义不变。豁免仍一次性,集合有界如旧。
|
|
204
|
-
// [3193] codex R5:已在保留集里的 id(快照径收的)不重复扣预算——否则握手重放把同一批行
|
|
205
|
-
// 按"快照+重放"记两次账,40 预算被快照行白耗光,其后真正的首见历史行立刻失去豁免。
|
|
206
116
|
if ((replayingBuffer || !seenWf.has(frame.row.id)) && isTerminalWorkflowRowStatus(frame.row.status) && !retainedTerminalWf.has(frame.row.id) && retainedHistoryBudget > 0) {
|
|
207
117
|
retainedTerminalWf.add(frame.row.id);
|
|
208
118
|
retainedHistoryBudget--;
|
|
@@ -212,9 +122,6 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
212
122
|
}
|
|
213
123
|
break;
|
|
214
124
|
case "task_remove":
|
|
215
|
-
// #261 §2③/§5 Q-d:两个 additive 键随帧透传。撤行帧本就**只投给见过这一行的连接**
|
|
216
|
-
// (`seenTasks.delete` 的返回值即判据)—— 这正是 Q-d 那条边界钉的服务端半场:不持行的
|
|
217
|
-
// 订阅方连撤行帧都收不到,自然没有"结算"义务。`removeReason` 缺席 = 终态之后的常规清场。
|
|
218
125
|
if (seenTasks.delete(frame.id)) {
|
|
219
126
|
send("task_remove", {
|
|
220
127
|
id: frame.id,
|
|
@@ -225,96 +132,41 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
225
132
|
}
|
|
226
133
|
break;
|
|
227
134
|
case "workflow_remove":
|
|
228
|
-
// 🔴 #189 / codex R2-M1:按窗**刻意保留**的历史行不受本连接的撤行帧影响。行在 durable 读
|
|
229
|
-
// 期间翻终态时,活写路径的「终帧 + 撤行」会落进缓冲,重放到这里就会把快照刚保住的那一行
|
|
230
|
-
// 又撤掉 —— 于是「看不看得见刚结束的 workflow」重新取决于连接时机(晚 1ms 连上的人看得见,
|
|
231
|
-
// 撞上这一拍的人看不见),正是本 issue 要消灭的竞态。撤行帧描述的是**活跃集**离场,而这些
|
|
232
|
-
// 行进快照时就已经是终态历史,不在活跃集语义里。id 不复用(runId 每次新铸,终态不回滚),
|
|
233
|
-
// 故这层豁免既有界又不会误伤后来的真活行。
|
|
234
|
-
// 豁免**一次性**:配对的那一帧撤行到达即消费(codex R5-M3)。否则这个 id 被永久噤声——
|
|
235
|
-
// 同 id 若之后又出现活行(帧乱序/未来的行复用),它的撤行帧也会被吞,面板留一条撤不掉的死行;
|
|
236
|
-
// 顺带,每条长连接的保留集不再随握手期完成数无界增长。
|
|
237
135
|
if (retainedTerminalWf.delete(frame.id))
|
|
238
136
|
break;
|
|
239
137
|
if (seenWf.delete(frame.id))
|
|
240
138
|
send("workflow_remove", { id: frame.id, ts: frame.ts });
|
|
241
139
|
break;
|
|
242
140
|
case "hook_notice": {
|
|
243
|
-
// cli [1786]:一次 hook 判定**未能完成**的观测帧。可见性与 bg_notification **同为 fail-CLOSED** ——
|
|
244
|
-
// 一条关于**别人会话**的「这轮没看住」落到这个壳上,会让用户以为是自己这轮出了问题
|
|
245
|
-
// (而且 `/goal enforced` 面板会据此说一句不属于这个会话的话)。
|
|
246
|
-
// ⇒ scoped 订阅方只收能证明是自己的:`ownerSessionId === callerSession`;`undefined` ⇒ **丢弃**。
|
|
247
|
-
// fleet-wide 观察者(callerSession === null:trace/dashboard)照收。
|
|
248
141
|
const hn = frame.notice;
|
|
249
142
|
const hScopeOk = callerScope === null || hn.ownerScope === callerScope;
|
|
250
143
|
const hSessionOk = callerSession === null || hn.ownerSessionId === callerSession;
|
|
251
144
|
if (hScopeOk && hSessionOk) {
|
|
252
145
|
const { ownerScope: _hs, ownerSessionId: _hsid, ...wire } = hn;
|
|
253
|
-
// 与 bg_notification 同一帧契约:`type` 也放进 payload(壳的帧桥按 data.type 分派)。
|
|
254
146
|
send("hook_notice", { type: "hook_notice", ...wire, ts: frame.ts });
|
|
255
147
|
}
|
|
256
148
|
break;
|
|
257
149
|
}
|
|
258
150
|
case "bg_notification": {
|
|
259
|
-
// design/129-B 缺口②(帧契约): a bg child's completion pushed on THIS always-open lane — the
|
|
260
|
-
// idle shell renders the receipt immediately (no more one-input-cycle durable-drain lag). Visibility:
|
|
261
|
-
// tenant filter on ownerScope (same rule as rows; a notification without one is operator/fleet-wide
|
|
262
|
-
// visible only — fail-closed for scoped callers) + `?session=` on ownerSessionId (the HOST session;
|
|
263
|
-
// the payload sessionId is the CHILD's uuid — the contract's stable anchor — and never matches the caller's).
|
|
264
151
|
const n = frame.notification;
|
|
265
152
|
const scopeOk = callerScope === null || n.ownerScope === callerScope;
|
|
266
|
-
// [1493] fail-CLOSED on the MODEL-INJECTION path. bg_notification is auto-injected into the idle
|
|
267
|
-
// shell's model (the receipt drives the LLM's next turn). Unlike the display rows — whose absent-
|
|
268
|
-
// session `sessionOk` stays fail-OPEN for back-compat (an untagged row is at worst visible, never
|
|
269
|
-
// actionable) — an untagged completion delivered to a SCOPED subscriber would inject a DIFFERENT
|
|
270
|
-
// session's result into this shell's model (clay's dual-shell repro: two shells sharing one engine +
|
|
271
|
-
// principal). So a scoped caller (callerSession !== null) receives a completion ONLY when it is
|
|
272
|
-
// provably its own: ownerSessionId === callerSession. undefined ⇒ NOT proven ⇒ dropped. A fleet-wide
|
|
273
|
-
// observer (callerSession === null: trace/dashboard) still sees everything. Part A (fleet-bus) is what
|
|
274
|
-
// populates ownerSessionId for the legit owner so this gate delivers to it and drops only strangers.
|
|
275
153
|
const sessionInjectOk = callerSession === null || n.ownerSessionId === callerSession;
|
|
276
154
|
if (scopeOk && sessionInjectOk) {
|
|
277
155
|
const { ownerScope: _os, ownerSessionId: _osid, ...wire } = n;
|
|
278
|
-
// The frame contract puts `type:'bg_notification'` INSIDE the object (the shell's frame
|
|
279
|
-
// bridge dispatches on data.type, not only the SSE event name) — carry it in the payload too.
|
|
280
156
|
send("bg_notification", { type: "bg_notification", ...wire, ts: frame.ts });
|
|
281
|
-
// The frame IS the delivery when it reaches the OWNING shell's session-bound lane —
|
|
282
|
-
// serve-fence the durable park (both orders hold: an already-parked entry is acked; a park landing
|
|
283
|
-
// AFTER this is refused by the serve fence) so the next stream-open drain can't re-inject the same
|
|
284
|
-
// completion into the model (the engine-side steer-inject double the shell cannot dedup — the
|
|
285
|
-
// 129-bg variant of that double-injection class, proven from real session logs). ONLY a `?session=`-bound subscriber counts: a fleet-wide
|
|
286
|
-
// dashboard consuming the frame must NOT burn the owning shell's durable copy — the park keeps its
|
|
287
|
-
// offline/no-active-lane semantics. Keys line up by contract: frame taskId = BackgroundChildEvent
|
|
288
|
-
// .taskId = the a*-domain registry handle (stable across kinds) = the parked entry's
|
|
289
|
-
// runId (leg onTaskNotification's n.task_id, same domain); ownerSessionId = HOST session = entry key.
|
|
290
|
-
// design/144 键升级:park 键=域:taskId:status[#seq],fleet 帧不带 seq → 传家族前缀
|
|
291
|
-
// (taskNotificationFoldKey 无 seq 形),markTerminalServed 家族 ack 清同族 seq 键成员。
|
|
292
|
-
// PAIR-REVIEW F-2:mark 延至 **write flush 回调** + socket 存活双检——「res.write 已调用」≠投递
|
|
293
|
-
// (对端已断本端未感知的窗口里,帧写进死 socket 而 durable 副本被烧=通知永丢,1.334 教训)。
|
|
294
|
-
// flush 回调=数据出到内核缓冲(尽力而为的最强本端信号;真端到端 ack 需协议位,另议),回调时
|
|
295
|
-
// 再验 socket.destroyed——断连时 Node 会先触发 destroy,此时不 ack,park 副本留给下次开流 drain。
|
|
296
|
-
// fence 的防双注入语义不变(已 ack 的重复帧仍被拒),只推迟 ack 时点。
|
|
297
157
|
if (completionInbox && n.ownerSessionId && callerSession !== null && callerSession === n.ownerSessionId && !res.writableEnded) {
|
|
298
158
|
const ownerSid = n.ownerSessionId;
|
|
299
|
-
// codex R1-H1(修车批):帧带 seq(1.239/core 1.352)→ **精确键** ack(servedAt 只 fence 该
|
|
300
|
-
// 周期;revive 周期 N+1 的 park/enqueue 不被连坐)。legacy 无 seq 帧保持家族键 ack(原语义)。
|
|
301
159
|
const family = taskNotificationFoldKey({ task_id: n.taskId, status: n.status, ...(n.seq !== undefined ? { seq: n.seq } : {}) });
|
|
302
|
-
// codex R2-H1(server belt):revive 周期的 park 键在 **uuid 域**(core ≤1.353 的 revive 通知
|
|
303
|
-
// task_id=childSessionId,subagent.js:598/655——identity 对齐转 core 在单),帧键在 a\* 域——
|
|
304
|
-
// 单键 ack 必 miss=开流二次注入。帧带 sessionId(uuid 别名)时**双域各 ack 一次**,两种 core
|
|
305
|
-
// 版本的 park 键都被 fence 住;core 修齐后 uuid 臂自然空转(无同键 park,无害)。
|
|
306
160
|
const familyUuid = n.sessionId !== undefined && n.sessionId !== n.taskId
|
|
307
161
|
? taskNotificationFoldKey({ task_id: n.sessionId, status: n.status, ...(n.seq !== undefined ? { seq: n.seq } : {}) })
|
|
308
162
|
: undefined;
|
|
309
163
|
const ackIfAlive = () => {
|
|
310
164
|
if (res.writableEnded || res.socket?.destroyed)
|
|
311
|
-
return;
|
|
165
|
+
return;
|
|
312
166
|
void completionInbox.markTerminalServed(ownerSid, family).catch(() => undefined);
|
|
313
167
|
if (familyUuid !== undefined)
|
|
314
168
|
void completionInbox.markTerminalServed(ownerSid, familyUuid).catch(() => undefined);
|
|
315
169
|
};
|
|
316
|
-
// res.write 的 flush 回调在上面 send() 内不可达(send 不透传 cb)——补一个零字节注释帧承载回调:
|
|
317
|
-
// SSE 注释行(`: ack\n\n`)对消费者不可见(SDK 吞注释),flush 语义等同。
|
|
318
170
|
res.write(`: ack
|
|
319
171
|
|
|
320
172
|
`, ackIfAlive);
|
|
@@ -323,17 +175,9 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
323
175
|
break;
|
|
324
176
|
}
|
|
325
177
|
default:
|
|
326
|
-
break;
|
|
178
|
+
break;
|
|
327
179
|
}
|
|
328
180
|
};
|
|
329
|
-
// 🔴 #189 / codex R1-H1:订阅**先于**快照注册,快照组装期间到达的帧先进缓冲、发完快照再按序重放。
|
|
330
|
-
// 快照第一次有了一段 await(durable 终态窗读),而 fleet bus 只扇给**当前**订阅者、不重放 —— 先读后
|
|
331
|
-
// 订阅就等于开了一个"这段时间的帧对本连接不存在"的窗口(bg_notification 是模型注入面,丢一帧=这次
|
|
332
|
-
// 完成回执要等到下次开流才由 durable inbox 补;task_remove/hook_notice 同理)。
|
|
333
|
-
// 顺序安全:`bus.snapshot()` 取在**读之后**,缓冲里的行帧描述的都是快照那一刻或更早的状态,重放
|
|
334
|
-
// 只会把同一状态再叠一次(upsert 幂等);撤行帧仍按 seen 集判(没见过的行不发撤除)。
|
|
335
|
-
// 缓冲无显式上限:窗口被读预算硬界在 2s,且缓冲存的是**帧对象引用**(不复制行);设一个会丢帧的
|
|
336
|
-
// 上限反而正好丢掉这段代码要保住的东西。
|
|
337
181
|
let buffered = [];
|
|
338
182
|
const unsubscribe = bus.subscribe((frame) => {
|
|
339
183
|
if (buffered !== null)
|
|
@@ -341,7 +185,6 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
341
185
|
else
|
|
342
186
|
handleFrame(frame);
|
|
343
187
|
});
|
|
344
|
-
// Real frame (not an SSE comment) — a per-frame-parsing BFF drops comments (zero-frame window downstream)
|
|
345
188
|
const hb = setInterval(() => { if (!res.writableEnded)
|
|
346
189
|
res.write(`event: heartbeat\ndata: {}\n\n`); }, 15_000);
|
|
347
190
|
if (typeof hb.unref === "function")
|
|
@@ -357,18 +200,10 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
357
200
|
res.end();
|
|
358
201
|
resolve();
|
|
359
202
|
};
|
|
360
|
-
// 关闭钩子在 await **之前**挂上:快照还在组装时对端就断了,也照常收摊(不然那段时间的断连没人处理)。
|
|
361
203
|
req.on("close", shutdown);
|
|
362
204
|
res.on("close", shutdown);
|
|
363
205
|
void (async () => {
|
|
364
|
-
// [3193] 病理1:boot recover(判死前世 running 行)与快照组装赛跑——SIGKILL 后壳重连常抢在
|
|
365
|
-
// recover 之前,此刻 seed/durable 两径皆空,快照永远缺那一行。屏障=recovery 完成 promise
|
|
366
|
-
// (main.ts 铸,memory 后端/无 gate 部署=已 resolve),**有界**等待(帧订阅已挂上,等待期帧全
|
|
367
|
-
// 进缓冲不丢):超时=recover 真挂了,按旧行为出快照(可用性路径,行还有 first-sight-terminal
|
|
368
|
-
// 豁免与下次重连兜底),不无限拖住连接。
|
|
369
206
|
if (terminal?.ready !== undefined) {
|
|
370
|
-
// A-067 C5 补遗(merge-rescan 车证伪「唯二反例」的第三处):竞速一结束 finally 清
|
|
371
|
-
// (A-002.10 同款)——unref 只是不吊进程退,不回收定时器本身。
|
|
372
207
|
let readyTimer;
|
|
373
208
|
await Promise.race([terminal.ready, new Promise((r) => { readyTimer = setTimeout(r, 3000); if (typeof readyTimer.unref === "function")
|
|
374
209
|
readyTimer.unref(); })]).finally(() => {
|
|
@@ -378,19 +213,12 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
378
213
|
if (done || res.writableEnded)
|
|
379
214
|
return;
|
|
380
215
|
}
|
|
381
|
-
// #189:durable 近期终态行窗(有界 + 永不抛;详见 fleet-terminal-window.ts)。
|
|
382
216
|
const terminalWindow = terminal === undefined ? [] : await recentTerminalWorkflowRows(terminal.store, terminal.scope, terminal.pullScope);
|
|
383
217
|
if (done || res.writableEnded)
|
|
384
|
-
return;
|
|
385
|
-
// connect-time snapshot (filtered + scope-stripped) — the visible rows become this connection's "seen" set.
|
|
218
|
+
return;
|
|
386
219
|
const snap = bus.snapshot();
|
|
387
220
|
const tasks = snap.tasks.filter(visT);
|
|
388
221
|
const live = snap.workflows.filter(visW);
|
|
389
|
-
// #189:活行(bus 的活跃集)+ durable 终态窗。
|
|
390
|
-
// · 一行一 id:同 id 两侧都有(行刚翻转的那一拍)时 **活行优先** —— 活行是唯一写者写的当前态,
|
|
391
|
-
// 终态窗是读时快照,后者覆盖前者会让面板倒退。
|
|
392
|
-
// · 并入行同样过 `visW`(scope + `?session=`),与活行**同一套判据**:终态行泄跨租户是事故。
|
|
393
|
-
// · bus 的 Map **不被触碰** —— 单写者不变量原样:活跃集里永远没有终态行,增量帧语义逐字不变。
|
|
394
222
|
const liveIds = new Set(live.map((r) => r.id));
|
|
395
223
|
const retained = terminalWindow.filter((r) => !liveIds.has(r.id) && visW(r));
|
|
396
224
|
retained.forEach((r) => retainedTerminalWf.add(r.id));
|
|
@@ -399,20 +227,16 @@ function streamFleet(req, res, bus, callerScope, callerSession = null, completio
|
|
|
399
227
|
workflows.forEach((r) => seenWf.add(r.id));
|
|
400
228
|
send("snapshot", { tasks: tasks.map(stripT), workflows: workflows.map(stripW), ts: snap.ts });
|
|
401
229
|
const pending = buffered ?? [];
|
|
402
|
-
buffered = null;
|
|
230
|
+
buffered = null;
|
|
403
231
|
replayingBuffer = true;
|
|
404
232
|
try {
|
|
405
233
|
for (const frame of pending)
|
|
406
234
|
handleFrame(frame);
|
|
407
235
|
}
|
|
408
236
|
finally {
|
|
409
|
-
replayingBuffer = false;
|
|
237
|
+
replayingBuffer = false;
|
|
410
238
|
}
|
|
411
239
|
})().catch(() => shutdown());
|
|
412
|
-
// ⚠️ 上面这个 catch 不是"静默降级":快照组装本身抛错(写向已销毁的 socket 那一族)时**没有**可以继续
|
|
413
|
-
// 的语义——这条连接已经没救了,干净收摊即正解。它真正防的是把原先同步执行器里的抛错(会直接 reject
|
|
414
|
-
// 出去)变成一条 **unhandled rejection**(Node 默认带走整个进程)。订阅回调自身的抛错另有隔离层
|
|
415
|
-
// (fleet bus 的 subscribe,记 fail-open),不经这里。
|
|
416
240
|
});
|
|
417
241
|
}
|
|
418
242
|
//# sourceMappingURL=fleet.js.map
|