@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
package/dist/trace/redact.js
CHANGED
|
@@ -1,49 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trace thinking-block redaction (service-side). Per core [R57]: the agent's reasoning/CoT is emitted on the
|
|
3
|
-
* live TaskEvent stream but DELIBERATELY kept out of the metadata Tracer because CoT may contain reasoned-about
|
|
4
|
-
* secrets (and is large / ToS-sensitive for some providers). Persisting it for the trace UI is the deployment's
|
|
5
|
-
* call — so when we DO persist it, redact unambiguous secret formats first. Conservative on purpose: only clear
|
|
6
|
-
* key/token shapes are redacted. ⚠️ It is NOT exhaustive: unrecognised secret formats (a free-text password,
|
|
7
|
-
* a JWT) pass through, and the broad `Bearer <20+ chars>` rule can over-match legitimate CoT (false positives
|
|
8
|
-
* tolerated — over-redacting trace text is safe). For sensitive deployments, disable thinking capture entirely
|
|
9
|
-
* via TRACE_THINKING=false. This is a seam — deployments can extend the patterns.
|
|
10
|
-
*/
|
|
11
1
|
const SECRET_PATTERNS = [
|
|
12
|
-
[/-----BEGIN[^-]+PRIVATE KEY(?: BLOCK)?-----[\s\S]{0,8192}?-----END[^-]+PRIVATE KEY(?: BLOCK)?-----/g, "«redacted:private-key»"],
|
|
13
|
-
[/\bsk-[A-Za-z0-9_-]{16,}\b/g, "«redacted:key»"],
|
|
14
|
-
[/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/g, "«redacted:gh-token»"],
|
|
15
|
-
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, "«redacted:slack-token»"],
|
|
16
|
-
[/\bAKIA[0-9A-Z]{16}\b/g, "«redacted:aws-key»"],
|
|
17
|
-
[/\b[Bb]earer\s+[A-Za-z0-9._~+/-]{20,}=*/g, "Bearer «redacted»"],
|
|
18
|
-
[/\bBasic\s+[A-Za-z0-9+/]{16,}={0,2}/g, "Basic «redacted»"],
|
|
19
|
-
// BL-5: credentials embedded in a clone/push URL (`scheme://user:PASS@host`) — keep scheme+user, drop the
|
|
20
|
-
// password. Port-only URLs (`host:9999/path`, no `@`) don't match (the userinfo `:pass@` is required).
|
|
2
|
+
[/-----BEGIN[^-]+PRIVATE KEY(?: BLOCK)?-----[\s\S]{0,8192}?-----END[^-]+PRIVATE KEY(?: BLOCK)?-----/g, "«redacted:private-key»"],
|
|
3
|
+
[/\bsk-[A-Za-z0-9_-]{16,}\b/g, "«redacted:key»"],
|
|
4
|
+
[/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/g, "«redacted:gh-token»"],
|
|
5
|
+
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, "«redacted:slack-token»"],
|
|
6
|
+
[/\bAKIA[0-9A-Z]{16}\b/g, "«redacted:aws-key»"],
|
|
7
|
+
[/\b[Bb]earer\s+[A-Za-z0-9._~+/-]{20,}=*/g, "Bearer «redacted»"],
|
|
8
|
+
[/\bBasic\s+[A-Za-z0-9+/]{16,}={0,2}/g, "Basic «redacted»"],
|
|
21
9
|
[/\b([A-Za-z][A-Za-z0-9+.-]*:\/\/[^/@\s:]+):[^/@\s]+@/g, "$1:«redacted»@"],
|
|
22
|
-
// BL-5: Gitea / PAT `Authorization: token <hex>` scheme (the Bearer/Basic rules don't cover the `token` keyword).
|
|
23
10
|
[/\b[Tt]oken\s+[A-Za-z0-9._~+/-]{20,}/g, "token «redacted»"],
|
|
24
|
-
[/("(?:private_key|client_secret|password|api[_-]?key|secret|token)"\s*:\s*)"[^"\\]*(?:\\.[^"\\]*)*"/gi, '$1"«redacted»"'],
|
|
11
|
+
[/("(?:private_key|client_secret|password|api[_-]?key|secret|token)"\s*:\s*)"[^"\\]*(?:\\.[^"\\]*)*"/gi, '$1"«redacted»"'],
|
|
25
12
|
];
|
|
26
|
-
/** Redact unambiguous secret formats from reasoning/CoT before it is persisted to the durable trace log. */
|
|
27
|
-
/** S24 (SILENT-FALLBACK P1): optional redaction observer — rewrites previously had zero fingerprint, so
|
|
28
|
-
* over-redaction (mangling legit content) and under-redaction (a pattern that never fires) were both
|
|
29
|
-
* invisible. main.ts wires this to `redactions_applied_total{pattern}`. Hot path: the observer only runs
|
|
30
|
-
* when a pattern actually matched; the label is derived once per pattern at module load. */
|
|
31
13
|
let onRedaction;
|
|
32
14
|
export function setRedactionObserver(fn) {
|
|
33
15
|
onRedaction = fn;
|
|
34
16
|
}
|
|
35
17
|
const PATTERN_LABELS = SECRET_PATTERNS.map(([, repl]) => repl.replace(/[«»]/g, "").replace(/^redacted:/, ""));
|
|
36
|
-
/**
|
|
37
|
-
* `opts.observe: false` = **照常脱敏,但这一遍不计数**(合并码重扫)。
|
|
38
|
-
*
|
|
39
|
-
* 为什么需要它:本函数对自己的输出**值幂等、计数不幂等** —— URL-userinfo 与 JSON 秘密字段两条 pattern
|
|
40
|
-
* 会命中自己已经替换过的输出并再计一次。于是任何**重扫已脱敏文本**的读路径(耐久待批队列的回读腿,
|
|
41
|
-
* `/v1/approvals/stream` 每 3s 一拍)都会持续给 `redactions_applied_total` 加数,把那个用来识别
|
|
42
|
-
* 「过度脱敏 / pattern 从不触发」的指标灌成不可读。
|
|
43
|
-
*
|
|
44
|
-
* 🔴 关的是**计数**,不是脱敏:重扫这一遍仍是纵深防御(手改过的行、未来某条写路径漏了脱敏,读面照样
|
|
45
|
-
* 兜住)—— 少脱一次的代价是把凭证投上跨租户可见的运维队列,那条永远不能省。
|
|
46
|
-
*/
|
|
47
18
|
export function redactSecrets(text, opts) {
|
|
48
19
|
const observe = opts?.observe ?? true;
|
|
49
20
|
let out = text;
|
|
@@ -53,10 +24,6 @@ export function redactSecrets(text, opts) {
|
|
|
53
24
|
out = out.replace(re, repl);
|
|
54
25
|
continue;
|
|
55
26
|
}
|
|
56
|
-
// Count-then-replace (1.127.0): only the STRING form of `replace` expands `$1` capture refs —
|
|
57
|
-
// a function replacer returns its result verbatim, so the URL/JSON-field patterns emitted a literal `$1`
|
|
58
|
-
// instead of the preserved scheme/key prefix. The extra match() scan runs only when an observer is
|
|
59
|
-
// installed, on text that is already preview-capped.
|
|
60
27
|
const n = out.match(re)?.length ?? 0;
|
|
61
28
|
out = out.replace(re, repl);
|
|
62
29
|
if (n > 0) {
|
|
@@ -64,29 +31,12 @@ export function redactSecrets(text, opts) {
|
|
|
64
31
|
onRedaction(PATTERN_LABELS[i], n);
|
|
65
32
|
}
|
|
66
33
|
catch {
|
|
67
|
-
/* observability must never break redaction */
|
|
68
34
|
}
|
|
69
35
|
}
|
|
70
36
|
}
|
|
71
37
|
return out;
|
|
72
38
|
}
|
|
73
|
-
/** Recursively redact secret-shaped strings inside a structured value (e.g. tool-call `args`), preserving shape.
|
|
74
|
-
* Tool arguments are persisted to the durable trace + replayed to any trace consumer, so a secret passed AS a
|
|
75
|
-
* tool arg must be stripped too (council: redactSecrets covered only free reasoning text, not tool args). */
|
|
76
|
-
/** 递归上限 —— 与「一条畸形输入不得打死流」这条目标匹配的量级:真实工具输出的嵌套远低于此,
|
|
77
|
-
* 而爆栈发生在数千层。超限的子树替换成一个**标记**,而不是抛错、也不是静默截断成 `{}`。 */
|
|
78
39
|
const REDACT_MAX_DEPTH = 64;
|
|
79
|
-
/**
|
|
80
|
-
* 🔴 环 + 深度预算(2026-07-25 补)。原实现是纯递归、**既不记已访问节点也不限深度**,而它的调用位置是
|
|
81
|
-
* **最不可信的边界**:`toolStart`/`toolEnd` 把工具的 `args`/`output`/`structured` 直接喂进来。后果两条:
|
|
82
|
-
* · **循环引用** ⇒ 无限递归 ⇒ `RangeError` **抛出构造函数**。在 sync 的 SSE 腿上那个 throw 就落在
|
|
83
|
-
* `for await (…) res.write(…)` 的循环里 ⇒ **一个畸形工具输出能打死一条正在跑的流**;在 ledger-sink 腿上
|
|
84
|
-
* 则是那一帧 append 直接失败(事件永久不落盘)。
|
|
85
|
-
* · 超深嵌套 ⇒ 同样爆栈。
|
|
86
|
-
* 同文件的 `redactedPreview` 的文档里早把这条记成"已复审的 MEDIUM"并**绕开** redactDeep ——
|
|
87
|
-
* 也就是说这个洞是知道的,只是没堵。现在堵在 redactDeep 本体:环与超深都换成**可见的标记**,
|
|
88
|
-
* 让下游看到"这里有东西被省略了",而不是丢一个空对象或炸掉整条流。
|
|
89
|
-
*/
|
|
90
40
|
export function redactDeep(value) {
|
|
91
41
|
return redactDeepInner(value, 0, new WeakSet());
|
|
92
42
|
}
|
|
@@ -95,7 +45,6 @@ function redactDeepInner(value, depth, seen) {
|
|
|
95
45
|
return redactSecrets(value);
|
|
96
46
|
if (!value || typeof value !== "object")
|
|
97
47
|
return value;
|
|
98
|
-
// 环:同一个对象在**当前这条路径上**再次出现 ⇒ 标记后停,不再往下走。
|
|
99
48
|
if (seen.has(value))
|
|
100
49
|
return "[circular]";
|
|
101
50
|
if (depth >= REDACT_MAX_DEPTH)
|
|
@@ -110,33 +59,16 @@ function redactDeepInner(value, depth, seen) {
|
|
|
110
59
|
return out;
|
|
111
60
|
}
|
|
112
61
|
finally {
|
|
113
|
-
// 出栈即移除:`seen` 记的是**当前路径**,不是"整棵树里出现过" —— 否则同一个对象被两个兄弟字段
|
|
114
|
-
// 各引用一次(DAG,完全合法)会被误标成 circular。
|
|
115
62
|
seen.delete(value);
|
|
116
63
|
}
|
|
117
64
|
}
|
|
118
|
-
/**
|
|
119
|
-
* A REDACTED, length-BOUNDED string preview of a structured value — for surfacing a pending tool call's `args`
|
|
120
|
-
* in the assistant inbox ("what is this ask about") WITHOUT leaking secrets or dumping an unbounded payload (a
|
|
121
|
-
* write_file `content` can be megabytes). {@link redactDeep} scrubs secret-shaped strings first; the result is
|
|
122
|
-
* JSON-stringified and capped (truncation is marked). `null`/`undefined` → `null` (nothing to preview). Defense
|
|
123
|
-
* in depth: the redaction is conservative (NOT exhaustive — see {@link redactSecrets}); the inbox is already
|
|
124
|
-
* owner-scoped, so this preview is only ever shown to the ask's own principal (or an operator inspecting it).
|
|
125
|
-
*/
|
|
126
65
|
export function redactedPreview(value, cap = 2000) {
|
|
127
66
|
if (value === null || value === undefined)
|
|
128
67
|
return null;
|
|
129
|
-
// A bare string arg → preview it directly (not JSON-quoted), redacted.
|
|
130
68
|
if (typeof value === "string") {
|
|
131
69
|
const r = redactSecrets(value);
|
|
132
70
|
return r.length === 0 ? null : r.length > cap ? `${r.slice(0, cap)}…[truncated]` : r;
|
|
133
71
|
}
|
|
134
|
-
// Structured value → cycle-safe, leaf-bounded JSON, THEN string-level redaction. We do NOT use redactDeep here
|
|
135
|
-
// (review MEDIUM): redactDeep has no cycle/depth/size budget, so a cyclic arg would recurse to a stack overflow
|
|
136
|
-
// BEFORE any try/catch, a BigInt would throw, and a huge leaf would be fully copied before truncation. The
|
|
137
|
-
// replacer below caps long string leaves up front (bounds the buffer), tags cycles, and stringifies BigInt;
|
|
138
|
-
// redactSecrets on the resulting JSON catches the same secret shapes (the standalone key patterns + the JSON
|
|
139
|
-
// secret-field rule both operate on the flat string — equivalent-or-better than per-leaf redaction).
|
|
140
72
|
const seen = new WeakSet();
|
|
141
73
|
let s;
|
|
142
74
|
try {
|
|
@@ -155,7 +87,7 @@ export function redactedPreview(value, cap = 2000) {
|
|
|
155
87
|
}) ?? "";
|
|
156
88
|
}
|
|
157
89
|
catch {
|
|
158
|
-
s = "[unserializable]";
|
|
90
|
+
s = "[unserializable]";
|
|
159
91
|
}
|
|
160
92
|
if (s.length === 0)
|
|
161
93
|
return null;
|
package/dist/turn-activity.js
CHANGED
|
@@ -1,32 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* #245 S1([3777] 决策卡死墙攻关):进程内 per-task「turn 最后活性时刻」——running 形 409 与
|
|
3
|
-
* GET /v1/runs/:id 读面的判别材料 `msSinceLastActivity` 的唯一数据源。
|
|
4
|
-
*
|
|
5
|
-
* 【为什么需要它】run 行的 `updatedAt` 由 liveness heartbeat(独立 `setInterval`,runs.ts)推进——
|
|
6
|
-
* 它只证明 **server 进程**活着;turn 卡死在某个 await 上时心跳照走([3771] 推理漏洞的公开更正)。
|
|
7
|
-
* 「turn 真在推进」的证据 = ledger sink 的 durable append(text/tool/status/park/done 全族都过那一条
|
|
8
|
-
* 单写串行链)。cli 的 running 形保守臂([3779] C3)缺的就是这一维:没有它,「真忙的后台 run」与
|
|
9
|
-
* 「断连僵局」在 wire 上不可分辨,只能猜。
|
|
10
|
-
*
|
|
11
|
-
* 【语义边界(与消费端约定,勿漂)】
|
|
12
|
-
* - **同副本 best-effort**:记录只活在本进程。跨副本命中(claim 持有者在别的副本)或本副本重启后
|
|
13
|
-
* ⇒ 读不到 ⇒ wire 键**诚实缺席**——缺席 = 「无法证明」,不是「不活」。绝不铸 0(0 会被读成
|
|
14
|
-
* 「刚刚还活着」,错误方向与 [2255] 「宁缺毋假」纪律同族)。
|
|
15
|
-
* - 不落库、不进 SQL 面:这是分诊提示,不是状态机输入;任何门/CAS/resume 判定都不许读它。
|
|
16
|
-
*
|
|
17
|
-
* 【生命周期】终局**不主动清**——409 只在 `running` 行上消费本记录,终局行不进那个分支,残留无害;
|
|
18
|
-
* 主动清反而制造「done append 已打点、行还没翻终态」窗口里的缺席假象。有界性由插入序近似 LRU 兜底
|
|
19
|
-
* (CAP 条,超限逐最老;每条 ≈ 60B,上限 ≈ 0.5MB,常驻可接受)。
|
|
20
|
-
* 🔴 但**新世代必须清**(codex S1-F2,2026-08-13):session purge 删行后客户端合法复用自带 taskId 重
|
|
21
|
-
* 提交——不清的话新 run 首次打点前会读到旧 run 的时刻 = 假「刚有活性」证据。清点 = 三个 createRun
|
|
22
|
-
* 成功分支(routes/runs.ts + routes/tasks.ts×2,接线锚在 session-active-conflict-materials 测试)。
|
|
23
|
-
*/
|
|
24
1
|
const CAP = 8192;
|
|
25
2
|
const lastActivityByTask = new Map();
|
|
26
|
-
/** ledger sink 每次 durable append 时打点(装配点:runs.ts / routes/tasks.ts 的 createLedgerSink)。 */
|
|
27
3
|
export function recordTurnActivity(taskId) {
|
|
28
4
|
if (lastActivityByTask.has(taskId)) {
|
|
29
|
-
lastActivityByTask.delete(taskId);
|
|
5
|
+
lastActivityByTask.delete(taskId);
|
|
30
6
|
}
|
|
31
7
|
else if (lastActivityByTask.size >= CAP) {
|
|
32
8
|
const oldest = lastActivityByTask.keys().next().value;
|
|
@@ -35,11 +11,9 @@ export function recordTurnActivity(taskId) {
|
|
|
35
11
|
}
|
|
36
12
|
lastActivityByTask.set(taskId, Date.now());
|
|
37
13
|
}
|
|
38
|
-
/** 最后活性时刻(epoch ms);无记录 ⇒ undefined(缺席语义见顶注)。 */
|
|
39
14
|
export function readTurnActivityMs(taskId) {
|
|
40
15
|
return lastActivityByTask.get(taskId);
|
|
41
16
|
}
|
|
42
|
-
/** 测试隔离用(生产路径不调:终局不清的理由见顶注)。 */
|
|
43
17
|
export function clearTurnActivity(taskId) {
|
|
44
18
|
lastActivityByTask.delete(taskId);
|
|
45
19
|
}
|
package/dist/usage-analytics.js
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 用量统计查询面(registry AnalyticsSource id="service-usage" 槽位的数据源)。
|
|
3
|
-
*
|
|
4
|
-
* 数据源 = task_run 账本的 result JSON(E8 `stats.modelUsage` per-model echo:inputTokens/outputTokens/
|
|
5
|
-
* cacheReadTokens/cacheWriteTokens/costMicroUsd——costMicroUsd 是 authoritative micro-USD 记账非估算)。
|
|
6
|
-
* 架构 = store 侧薄扫窗(usageScan:SQL 只按时间窗过滤取四列,行内 JSON 提取在 store 完成)+ 本文件的
|
|
7
|
-
* 【纯函数】聚合(三后端同代码,零方言分叉;test 直接喂行)。
|
|
8
|
-
*
|
|
9
|
-
* 口径(回帖契约,2026-07-09):
|
|
10
|
-
* - tokensIn = Σ(inputTokens + cacheReadTokens + cacheWriteTokens)(即"进模型的上下文 token 总量",
|
|
11
|
-
* cache 命中也计——计费口径;cache 明细在 breakdown 的 cacheReadTokens 单列可见);
|
|
12
|
-
* - tokensOut = Σ outputTokens;
|
|
13
|
-
* - costUsd = Σ costMicroUsd / 1e6(authoritative);
|
|
14
|
-
* - 旧行/无 modelUsage echo 的行(1.x 早期或非终态)fallback `stats.tokens` 整数计入 tokensOut 并置
|
|
15
|
-
* `estimated:true` 于该聚合(诚实标注,契约要求"token 数标注估算与否");#59 起 `estimated` 还覆盖
|
|
16
|
-
* 第二种口径不完整:**有 echo 行但缺数值键**(该键按 0 计入 ⇒ 总量偏低)——两臂同义:别把总数当精确值;
|
|
17
|
-
* - 行内数值键一律走 {@link numOf} 守卫:数据源是 DB 里的 JSON,裸算术遇缺席/非数会 NaN 毒掉整窗(#59);
|
|
18
|
-
* - 扫窗有行数上限(store 侧 LIMIT)——触顶时结果携带 `truncated:true`(诚实截断,绝不静默)。
|
|
19
|
-
*/
|
|
20
|
-
/** JSON 来源的数值键:非 number / 非有限 ⇒ undefined(缺席),**绝不返回 NaN**,也绝不让字符串滑进 `+`。 */
|
|
21
1
|
function numOf(d, key) {
|
|
22
2
|
const v = d[key];
|
|
23
3
|
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
24
4
|
}
|
|
25
5
|
const emptyTotals = () => ({ tasks: 0, tokensIn: 0, tokensOut: 0, costUsd: 0, estimated: false });
|
|
26
|
-
/** 折入一行 per-model echo(summary/series 的 foldRow 与 breakdown 的 model 维共用——两处写两遍必漂移)。
|
|
27
|
-
* 缺席/非数键按 0 计入并置 `estimated`(诚实标注偏低,绝不静默,也绝不 NaN 毒窗)。 */
|
|
28
6
|
function foldModelEntry(t, d) {
|
|
29
7
|
const input = numOf(d, "inputTokens");
|
|
30
8
|
const output = numOf(d, "outputTokens");
|
|
@@ -36,8 +14,6 @@ function foldModelEntry(t, d) {
|
|
|
36
14
|
t.costUsd += (cost ?? 0) / 1e6;
|
|
37
15
|
if (input === undefined || output === undefined || cacheRead === undefined || cacheWrite === undefined || cost === undefined)
|
|
38
16
|
t.estimated = true;
|
|
39
|
-
// S10/V2-⑤:上面这条 tokensIn 求和公式只在「分量制」行上正确。缺标记(存量行,某族历史上 inputTokens
|
|
40
|
-
// 含 cache ⇒ 双算)或本读端不认识的标记值(将来别的口径)⇒ 走既有不精确通道,不显示为精确值。
|
|
41
17
|
if (d.usageBasis !== "uncached-components-v1")
|
|
42
18
|
t.estimated = true;
|
|
43
19
|
}
|
|
@@ -49,20 +25,18 @@ function foldRow(t, row) {
|
|
|
49
25
|
foldModelEntry(t, d);
|
|
50
26
|
}
|
|
51
27
|
else if (row.stats?.tokens) {
|
|
52
|
-
t.tokensOut += row.stats.tokens;
|
|
28
|
+
t.tokensOut += row.stats.tokens;
|
|
53
29
|
t.estimated = true;
|
|
54
30
|
}
|
|
55
31
|
}
|
|
56
32
|
const round6 = (n) => Math.round(n * 1e6) / 1e6;
|
|
57
33
|
const finish = (t) => ({ ...t, costUsd: round6(t.costUsd) });
|
|
58
|
-
/** GET /v1/usage/summary — 窗内总量。 */
|
|
59
34
|
export function usageSummary(rows) {
|
|
60
35
|
const t = emptyTotals();
|
|
61
36
|
for (const r of rows)
|
|
62
37
|
foldRow(t, r);
|
|
63
38
|
return finish(t);
|
|
64
39
|
}
|
|
65
|
-
/** GET /v1/usage/series — 时间序列(UTC 桶;空桶不发行,消费方按需补零)。 */
|
|
66
40
|
export function usageSeries(rows, metric, granularity) {
|
|
67
41
|
const bucketMs = granularity === "hour" ? 3_600_000 : 86_400_000;
|
|
68
42
|
const buckets = new Map();
|
|
@@ -77,8 +51,6 @@ export function usageSeries(rows, metric, granularity) {
|
|
|
77
51
|
.sort((a, b) => a[0] - b[0])
|
|
78
52
|
.map(([b, t]) => ({ t: new Date(b).toISOString(), v: metric === "costUsd" ? round6(t.costUsd) : t[metric] }));
|
|
79
53
|
}
|
|
80
|
-
/** GET /v1/usage/breakdown — 按维度分桶。dimension=model 时按 per-model echo 展开(一行可贡献多个 model 桶;
|
|
81
|
-
* fallback 行归 "(unattributed)");dimension=principal 时 owner NULL 归 "(anonymous)"。 */
|
|
82
54
|
export function usageBreakdown(rows, dimension) {
|
|
83
55
|
const buckets = new Map();
|
|
84
56
|
const bucket = (key) => {
|
|
@@ -96,8 +68,8 @@ export function usageBreakdown(rows, dimension) {
|
|
|
96
68
|
if (mu && Object.keys(mu).length > 0) {
|
|
97
69
|
for (const [model, d] of Object.entries(mu)) {
|
|
98
70
|
const t = bucket(model);
|
|
99
|
-
t.tasks += 1;
|
|
100
|
-
foldModelEntry(t, d);
|
|
71
|
+
t.tasks += 1;
|
|
72
|
+
foldModelEntry(t, d);
|
|
101
73
|
}
|
|
102
74
|
}
|
|
103
75
|
else {
|
|
@@ -108,7 +80,6 @@ export function usageBreakdown(rows, dimension) {
|
|
|
108
80
|
.sort((a, b) => b[1].costUsd - a[1].costUsd || b[1].tokensOut - a[1].tokensOut || a[0].localeCompare(b[0]))
|
|
109
81
|
.map(([key, t]) => ({ key, ...finish(t) }));
|
|
110
82
|
}
|
|
111
|
-
/** store 行投影 helper:result JSON(已 parse 或原始字符串)→ UsageRow.stats。所有后端复用(方言只在 SQL)。 */
|
|
112
83
|
export function projectUsageStats(result) {
|
|
113
84
|
try {
|
|
114
85
|
const parsed = typeof result === "string" ? JSON.parse(result) : result;
|
|
@@ -124,9 +95,8 @@ export function projectUsageStats(result) {
|
|
|
124
95
|
return out;
|
|
125
96
|
}
|
|
126
97
|
catch {
|
|
127
|
-
return undefined;
|
|
98
|
+
return undefined;
|
|
128
99
|
}
|
|
129
100
|
}
|
|
130
|
-
/** usageScan 的统一行数上限(触顶=结果 truncated:true;试点规模远在限内)。 */
|
|
131
101
|
export const USAGE_SCAN_LIMIT = 50_000;
|
|
132
102
|
//# sourceMappingURL=usage-analytics.js.map
|
|
@@ -1,73 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 墙钟阶跃守卫 —— 给「按墙钟绝对 deadline 做终局判定」的清算腿加一道**单调钟佐证**。
|
|
3
|
-
*
|
|
4
|
-
* 病灶(test [4232] 场景①,libfaketime 真实构造,宿主真钟全程未碰):审批 SLA=45s,把进程内墙钟
|
|
5
|
-
* **前拨 1 小时**,真实只等了约 14 秒审批卡就被 D-D deny-sweep 判超时自动拒绝。链条是清楚的:
|
|
6
|
-
* `checkpoint.deadline` 是 core 在 `put` 时按**当时的墙钟**铸的绝对时刻(必须是墙钟——它要跨进程
|
|
7
|
-
* 重启存活,单调基准一重启就没了),而 sweep 每拍拿 `Date.now()` 去比 `deadline <= cutoff`。墙钟凭空
|
|
8
|
-
* 前跳 J 毫秒 ⇒ 判定钟凭空多走 J 毫秒 ⇒ 人还没走开,窗就没了。
|
|
9
|
-
*
|
|
10
|
-
* [4235] core 半场三定性把判定半场划给本仓:`totalWaitMs` 的墙钟**记账**是设计如此(要跨 durable
|
|
11
|
-
* resume 延续),不改形;**判定**侧可以在进程内用单调钟做窗口佐证,与账面字段互不牵连。本模块就是那道佐证。
|
|
12
|
-
*
|
|
13
|
-
* ## 判据(为什么是「宽限一个完整 SLA」)
|
|
14
|
-
* 直觉修法是把 cutoff 改成 `now - 累计前跳量`。它不成立:core 在跳变**之后**铸的 deadline 用的是**新**
|
|
15
|
-
* 墙钟,拿旧墙钟去比 ⇒ 新卡凭空多活 J 毫秒,而 J 可以是任意大。补偿量若衰减,cutoff 又会以 2 倍速前进。
|
|
16
|
-
* 两条路都把一个有界缺陷换成一个无界缺陷。
|
|
17
|
-
*
|
|
18
|
-
* 本模块改判**宽限**:检出一次前跳后,按**单调钟**静默 `grace = slaMs` 毫秒不做判定。
|
|
19
|
-
*
|
|
20
|
-
* 🔴 **宽限量是一个完整 SLA,不是 `min(J, S)`**(codex 对抗复审 R1 [high] 三,验真后修 —— 第一版正是
|
|
21
|
-
* `min(J, S)`,而那条「J < S 时宽限恰好补平损失」的证明**是错的**)。错在哪:宽限只是**推迟**判定,
|
|
22
|
-
* 它消不掉墙钟从此**永久领先** J 毫秒这件事。反例(逐字复算):S=45s,gate 在真实 t=0 铸出(deadline =
|
|
23
|
-
* W0+45s),真实 t=1s 时前跳 J=10s。`min(J,S)`=10s ⇒ 宽限到真实 t=11s;此后墙钟恒为 `W0+t+10`,
|
|
24
|
-
* 于是 deadline 在 **真实 t=35s** 就被判到期 —— 仍旧少给了整整 10 秒。
|
|
25
|
-
*
|
|
26
|
-
* 取满一个 S 才成立,证明是:宽限结束的时刻 t_g = t_jump + S(单调钟量)。
|
|
27
|
-
* · 跳变**之前或当时**就在等的 gate:它在 t_g 之前不可能被判,故真实等待 ≥ S ✔
|
|
28
|
-
* · 跳变**之后**在 t_x 铸出的 gate:deadline 用的是跳变后的钟 ⇒ 它按墙钟到期的时刻恰好对应真实
|
|
29
|
-
* t_x + S(墙钟与单调钟从跳变后起同速),宽限只可能把它再往后推,绝不会提前 ✔
|
|
30
|
-
* 即**任一审批席都拿得到不少于一个完整 SLA 的真实时间**;单次阶跃下判定最多被推迟一个 S。
|
|
31
|
-
*
|
|
32
|
-
* 🔴 **「有界且一次性自清」只对单次阶跃成立**(codex 对抗复审 R2 [high] 五,验真后修 —— 第一版把这句话
|
|
33
|
-
* 写成了无条件的,那是错的):每次前跳都把窗延到「此刻 + 一个 S」,所以**只要每个 S 内再跳一次**,窗就
|
|
34
|
-
* 永远续下去,deny 腿被静默关停 —— 而它是一条 **fail-closed** 腿,永久不跑比跑早了更坏。
|
|
35
|
-
* 收口 = 一串连续跳变的**累计宽限总上限**(缺省 `2 × S`:一个 S 覆盖有证明的单次阶跃,第二个 S 容一串
|
|
36
|
-
* 短促跳变即 NTP 连续大步修正的真实形);撞上上限就**响亮**恢复判定(`onGraceCapped`),此后宁可按一个
|
|
37
|
-
* 不稳的钟判,也不让 fail-closed 腿永远不跑。绝对兜底始终另有一层:`checkpoint.terminal_at_ms` 由别的
|
|
38
|
-
* reap 腿执行、不经本守卫。
|
|
39
|
-
*
|
|
40
|
-
* ## 方向与响亮度(#157 安全轴)
|
|
41
|
-
* 宽限只会**推迟**一个 fail-closed 动作(deny),绝不会放行任何东西 —— 方向安全。但它必须**响亮**:
|
|
42
|
-
* 检出即 warn(带跳变量与宽限量),宽限期内每拍 info。绝对兜底仍在:`checkpoint.terminal_at_ms`
|
|
43
|
-
* (put 时钉死的绝对上限)由另一条 reap 腿执行,不经本守卫 ⇒ 悬挂不可能无限。
|
|
44
|
-
*
|
|
45
|
-
* 后拨(墙钟相对单调钟**倒退**)不触发宽限:它只会让 deadline 显得更远 ⇒ 判定被推迟,已经是安全方向。
|
|
46
|
-
* 仍然 warn —— 一次没人知道的钟倒退是运维事实,不是可以吞掉的噪声。
|
|
47
|
-
*/
|
|
48
|
-
/** 判定为「阶跃」的漂移阈值。低于它的漂移是 tick 抖动 / 事件循环延迟 / NTP 缓变(≤500ppm,60s 拍上仅 30ms),
|
|
49
|
-
* 不是拨表。误判的代价只是**推迟**一次 deny ≤ 宽限量,所以宁可取得低一些。 */
|
|
50
1
|
export const DEFAULT_CLOCK_JUMP_THRESHOLD_MS = 2_000;
|
|
51
|
-
/** `slaMs` 缺省(部署没配 `APPROVAL_TIMEOUT_SEC` 时)—— 与 `tool-approval.ts` 的
|
|
52
|
-
* `DEFAULT_APPROVAL_TTL_MS` 同值同义:一次审批交互的量级。 */
|
|
53
2
|
export const DEFAULT_CLOCK_JUMP_GRACE_MS = 5 * 60_000;
|
|
54
3
|
export function createWallClockJumpGuard(opts = {}) {
|
|
55
4
|
const monotonic = opts.monotonic ?? (() => performance.now());
|
|
56
5
|
const threshold = opts.jumpThresholdMs ?? DEFAULT_CLOCK_JUMP_THRESHOLD_MS;
|
|
57
6
|
const graceCap = opts.slaMs !== undefined && opts.slaMs > 0 ? opts.slaMs : DEFAULT_CLOCK_JUMP_GRACE_MS;
|
|
58
|
-
// codex R2 [high] 五(验真后修):首拍锚点**真的**在构造时取(旧形只初始化了单调锚,墙钟锚留 undefined,
|
|
59
|
-
// 而注释却写着「构造时取」—— 注在撒谎,且后果具体:构造之后、第一次 admit 之前的那次前跳测不出来,
|
|
60
|
-
// 那正是「进程刚起来、pending gate 已在库里」的真实窗口)。
|
|
61
7
|
let lastWall = (opts.now ?? Date.now)();
|
|
62
8
|
let lastMono = monotonic();
|
|
63
9
|
let graceUntilMono;
|
|
64
|
-
/** 本串连续跳变的起点(单调钟)。`undefined` = 当前不在宽限中。 */
|
|
65
10
|
let burstStartMono;
|
|
66
|
-
// 一串跳变累计宽限的总上限。为什么需要它(codex R2 [high] 五的第二半):每次前跳都把窗延到「此刻 +
|
|
67
|
-
// 一个 SLA」,于是**只要每个 SLA 内再跳一次**,窗就永远续下去 —— deny 腿被静默关停,而它是一条
|
|
68
|
-
// **fail-closed** 腿,永久不跑比跑早了更坏。旧注「有界且一次性自清」在连续跳变下是假的。
|
|
69
|
-
// 2×SLA 的取值:一个 SLA 覆盖单次阶跃(那是有证明的那一支),第二个 SLA 容一串短促跳变(NTP 连续
|
|
70
|
-
// 大步修正的真实形);再往后钟已属病态,此时**宁可按一个不稳的钟判**也不能让 fail-closed 腿永远不跑。
|
|
71
11
|
const maxTotalGrace = opts.maxTotalGraceMs !== undefined && opts.maxTotalGraceMs > 0 ? opts.maxTotalGraceMs : graceCap * 2;
|
|
72
12
|
return {
|
|
73
13
|
admit(wallNowMs) {
|
|
@@ -76,29 +16,22 @@ export function createWallClockJumpGuard(opts = {}) {
|
|
|
76
16
|
const monoDelta = mono - lastMono;
|
|
77
17
|
lastWall = wallNowMs;
|
|
78
18
|
lastMono = mono;
|
|
79
|
-
// 漂移 = 墙钟走的量 − 单调钟走的量。两者本该逐毫秒并进;差额就是拨表量。
|
|
80
19
|
const driftMs = wallNowMs - prevWall - monoDelta;
|
|
81
20
|
if (driftMs > threshold) {
|
|
82
|
-
// codex R1 [high] 三:**整一个 SLA**,与跳变量无关(`min(driftMs, graceCap)` 的证明是错的,
|
|
83
|
-
// 反例逐字见模块头注 —— 宽限推迟不了「墙钟从此永久领先」这件事)。
|
|
84
21
|
const graceMs = graceCap;
|
|
85
22
|
if (burstStartMono === undefined)
|
|
86
|
-
burstStartMono = mono;
|
|
23
|
+
burstStartMono = mono;
|
|
87
24
|
const until = mono + graceMs;
|
|
88
|
-
// 取 max:宽限期内又跳一次 ⇒ 窗延到更晚的那个,不被后一次的小跳缩短。
|
|
89
25
|
if (graceUntilMono === undefined || until > graceUntilMono)
|
|
90
26
|
graceUntilMono = until;
|
|
91
27
|
opts.onJump?.({ jumpMs: Math.round(driftMs), graceMs: Math.round(graceMs) });
|
|
92
28
|
}
|
|
93
29
|
else if (driftMs < -threshold) {
|
|
94
|
-
// 后拨:判定被推迟=安全方向,不宽限(宽限只会把安全方向再推远)。但要留痕。
|
|
95
30
|
opts.onJump?.({ jumpMs: Math.round(driftMs), graceMs: 0 });
|
|
96
31
|
}
|
|
97
32
|
if (graceUntilMono !== undefined && burstStartMono !== undefined) {
|
|
98
33
|
const elapsedInBurst = mono - burstStartMono;
|
|
99
34
|
if (elapsedInBurst >= maxTotalGrace) {
|
|
100
|
-
// 撞上总上限 ⇒ **响亮**恢复判定(不是悄悄放行)。绝对兜底另有一层:`checkpoint.terminal_at_ms`
|
|
101
|
-
// 由别的 reap 腿执行、不经本守卫,所以即使这里也失灵,悬挂仍不可能无限。
|
|
102
35
|
opts.onGraceCapped?.({ totalGraceMs: Math.round(elapsedInBurst) });
|
|
103
36
|
graceUntilMono = undefined;
|
|
104
37
|
burstStartMono = undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.44.0",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
|
|
39
|
-
"build": "tsc -p tsconfig.json",
|
|
39
|
+
"build": "rm -rf dist && tsc -p tsconfig.build-js.json && tsc -p tsconfig.build-dts.json",
|
|
40
40
|
"prepublishOnly": "npm run build",
|
|
41
41
|
"start": "node dist/main.js",
|
|
42
42
|
"dev": "tsx src/main.ts",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sema-agent/core": "^5.
|
|
57
|
+
"@sema-agent/core": "^5.56.0",
|
|
58
58
|
"@sema-agent/registry-core": "^0.19.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { Logger } from "../observability/logger.js";
|
|
2
|
-
export declare const SCENARIO_ALIASES: Readonly<Record<string, string>>;
|
|
3
|
-
/**
|
|
4
|
-
* 把请求/配置里的场景名归一到现役主名。
|
|
5
|
-
*
|
|
6
|
-
* - `known` 里**真有**这个名字 ⇒ 原样返回(live 定义优先于 alias)。⚠️ 窗内 center **无法**用旧名
|
|
7
|
-
* 另立场景:`centerScenarios` 把 alias 名当**保留名**响亮跳过(理由见那里——否则请求面归影子场景、
|
|
8
|
-
* 而按场景键的配置在 alias 窗里两边都算,MCP/A2A 会静默漏进主名的 roster)。所以这一支今天只是
|
|
9
|
-
* 纵深防御(万一将来有别的路径往表里塞同名场景,它至少不会被别名劫持)。
|
|
10
|
-
* - 否则命中 alias 表 ⇒ 返回主名 + 响亮 `scenario_alias_deprecated`(每 logger 每旧名一次)。
|
|
11
|
-
* - 都不命中 ⇒ 原样返回(未知名的处置归调用方:请求面 typed 400,指派面走 unknown→default 回退)。
|
|
12
|
-
*/
|
|
13
|
-
export declare function canonicalScenarioName(name: string, known: readonly string[] | undefined, logger?: Logger): string;
|
|
14
|
-
/**
|
|
15
|
-
* 「为场景 `name` 查按场景键的运维配置时,应命中哪些键」= 主名 + 其过渡期旧名。
|
|
16
|
-
*
|
|
17
|
-
* 🔴 **无二义的前提**(codex 对抗复审 [high] 的修法):旧名在窗内是**保留名** —— `centerScenarios` /
|
|
18
|
-
* `centerScenarioDetails` 拒绝任何以 alias 名声明的 center 场景,所以「标了旧名的配置」在本部署里
|
|
19
|
-
* 只可能是冲着(改名前的)主名去的,不存在第二个属主可漏。二义在源头掐掉,而不是在这四个取用口
|
|
20
|
-
* 各塞一份 live 名册判据(那才是「同一语义多个属主」)。
|
|
21
|
-
*
|
|
22
|
-
* 请求面归一之后,运维**已写好的**旧名配置(skill frontmatter 的 `scenarios:`、center mcp/a2a 的
|
|
23
|
-
* `scenarios`、center prompts 的 `scenarioOverrides`)若不再被命中,就会在改名当版**静默失效**
|
|
24
|
-
* —— 静默是这里唯一不可接受的形态,所以窗内两名皆认。窗撤销时随 {@link SCENARIO_ALIASES} 一起塌回单键。
|
|
25
|
-
*/
|
|
26
|
-
export declare function scenarioConfigKeys(name: string): readonly string[];
|
|
27
|
-
//# sourceMappingURL=scenario-alias.d.ts.map
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// ── #273 / C-R14 场景名 alias 过渡窗(clay 直裁 [4123],server 表态 [4127])──────────────────────
|
|
2
|
-
//
|
|
3
|
-
// 「team」一词全生态只指 agent-teams(持久具名团队);一次性多 agent 讨论一律叫「discuss」。本仓的
|
|
4
|
-
// `team` 场景正中射程 ⇒ 主名改 `discuss`,旧名保**一版**入口别名。
|
|
5
|
-
//
|
|
6
|
-
// 🔴 **单点解析**:归一发生在 {@link canonicalScenarioName},由 {@link gateScenarioRequest}(HTTP lane)
|
|
7
|
-
// 与 run-local 各调**一次**;此后整条下游(`scenarioName` 的每个判据点、trace 字段、skills/mcp/a2a/
|
|
8
|
-
// prompts 的按场景过滤)只见现役主名。**禁**在判据点上一处一处加双名判 —— 改名批最经典的漏改形就是
|
|
9
|
-
// 「改了三处、漏了第四处」,而漏掉的那处会静默按 default 跑(讨论任务被 2400s 墙钟砍腰之类)。
|
|
10
|
-
//
|
|
11
|
-
// ⏳ 窗一版:下一 minor 删本表 + team.ts 的 `run_team` 别名工具(两处同批删,发车帖点名壳/center)。
|
|
12
|
-
export const SCENARIO_ALIASES = Object.freeze({ team: "discuss" });
|
|
13
|
-
/** 一次性 deprecation warn 的去重位:按 logger 实例记已喊过的旧名。部署的 logger 是进程单例 ⇒ 每个
|
|
14
|
-
* 旧名每进程喊一次(不是每请求刷屏);测试各建各的 logger ⇒ 每格都看得见自己的那一声。 */
|
|
15
|
-
const aliasWarned = new WeakMap();
|
|
16
|
-
/**
|
|
17
|
-
* 把请求/配置里的场景名归一到现役主名。
|
|
18
|
-
*
|
|
19
|
-
* - `known` 里**真有**这个名字 ⇒ 原样返回(live 定义优先于 alias)。⚠️ 窗内 center **无法**用旧名
|
|
20
|
-
* 另立场景:`centerScenarios` 把 alias 名当**保留名**响亮跳过(理由见那里——否则请求面归影子场景、
|
|
21
|
-
* 而按场景键的配置在 alias 窗里两边都算,MCP/A2A 会静默漏进主名的 roster)。所以这一支今天只是
|
|
22
|
-
* 纵深防御(万一将来有别的路径往表里塞同名场景,它至少不会被别名劫持)。
|
|
23
|
-
* - 否则命中 alias 表 ⇒ 返回主名 + 响亮 `scenario_alias_deprecated`(每 logger 每旧名一次)。
|
|
24
|
-
* - 都不命中 ⇒ 原样返回(未知名的处置归调用方:请求面 typed 400,指派面走 unknown→default 回退)。
|
|
25
|
-
*/
|
|
26
|
-
export function canonicalScenarioName(name, known, logger) {
|
|
27
|
-
if (known?.includes(name))
|
|
28
|
-
return name;
|
|
29
|
-
if (!Object.hasOwn(SCENARIO_ALIASES, name))
|
|
30
|
-
return name;
|
|
31
|
-
const resolved = SCENARIO_ALIASES[name];
|
|
32
|
-
if (resolved === undefined)
|
|
33
|
-
return name;
|
|
34
|
-
if (logger) {
|
|
35
|
-
let seen = aliasWarned.get(logger);
|
|
36
|
-
if (!seen)
|
|
37
|
-
aliasWarned.set(logger, (seen = new Set()));
|
|
38
|
-
if (!seen.has(name)) {
|
|
39
|
-
seen.add(name);
|
|
40
|
-
logger.warn("scenario_alias_deprecated", { requested: name, resolved });
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return resolved;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* 「为场景 `name` 查按场景键的运维配置时,应命中哪些键」= 主名 + 其过渡期旧名。
|
|
47
|
-
*
|
|
48
|
-
* 🔴 **无二义的前提**(codex 对抗复审 [high] 的修法):旧名在窗内是**保留名** —— `centerScenarios` /
|
|
49
|
-
* `centerScenarioDetails` 拒绝任何以 alias 名声明的 center 场景,所以「标了旧名的配置」在本部署里
|
|
50
|
-
* 只可能是冲着(改名前的)主名去的,不存在第二个属主可漏。二义在源头掐掉,而不是在这四个取用口
|
|
51
|
-
* 各塞一份 live 名册判据(那才是「同一语义多个属主」)。
|
|
52
|
-
*
|
|
53
|
-
* 请求面归一之后,运维**已写好的**旧名配置(skill frontmatter 的 `scenarios:`、center mcp/a2a 的
|
|
54
|
-
* `scenarios`、center prompts 的 `scenarioOverrides`)若不再被命中,就会在改名当版**静默失效**
|
|
55
|
-
* —— 静默是这里唯一不可接受的形态,所以窗内两名皆认。窗撤销时随 {@link SCENARIO_ALIASES} 一起塌回单键。
|
|
56
|
-
*/
|
|
57
|
-
export function scenarioConfigKeys(name) {
|
|
58
|
-
const olds = Object.entries(SCENARIO_ALIASES).filter(([, to]) => to === name).map(([from]) => from);
|
|
59
|
-
return olds.length > 0 ? [name, ...olds] : [name];
|
|
60
|
-
}
|
|
61
|
-
//# sourceMappingURL=scenario-alias.js.map
|