@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,5 @@
|
|
|
1
1
|
const DEFAULT_BUCKETS = [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60];
|
|
2
2
|
function labelKey(labels) {
|
|
3
|
-
// Escape the delimiters (`\` `,` `=`) IN THE VALUE so distinct label sets can't collide onto one series:
|
|
4
|
-
// without this, {a:"1,b=2"} and {a:"1",b:"2"} both key to "a=1,b=2" and silently merge (tool names come from
|
|
5
|
-
// MCP, model ids from config — not strongly constrained). This key is internal series identity only;
|
|
6
|
-
// Prometheus rendering escapes separately via renderLabels/esc.
|
|
7
3
|
return Object.keys(labels)
|
|
8
4
|
.sort()
|
|
9
5
|
.map((k) => `${k}=${String(labels[k]).replace(/\\/g, "\\\\").replace(/,/g, "\\,").replace(/=/g, "\\=")}`)
|
|
@@ -39,11 +35,6 @@ export class Metrics {
|
|
|
39
35
|
if (!this.histos.has(name))
|
|
40
36
|
this.histos.set(name, { help, buckets, series: new Map() });
|
|
41
37
|
}
|
|
42
|
-
// 根因升类:inc 对未注册名曾是静默 no-op — 15 个只 inc 未 counter() 的计数器
|
|
43
|
-
// (permission_denied_total / compaction_events_total / hook_llm_calls_total / fleet_lease_* /
|
|
44
|
-
// metric_label_overflow_total[溢出护栏自己的计数器!]等)整批从未出现在 /metrics。观测面的
|
|
45
|
-
// fail-silent 正是观测要消灭的东西 → inc/setGauge/addGauge 改为首用自动注册(help 标注
|
|
46
|
-
// auto-registered,提醒补显式注册拿正经 HELP 文案);已知 15 名已在 createMetrics 补显式注册。
|
|
47
38
|
inc(name, labels = {}, n = 1) {
|
|
48
39
|
let c = this.counters.get(name);
|
|
49
40
|
if (!c) {
|
|
@@ -87,11 +78,6 @@ export class Metrics {
|
|
|
87
78
|
s.count += 1;
|
|
88
79
|
h.series.set(k, s);
|
|
89
80
|
}
|
|
90
|
-
/** [2557] 2-j:dynamic gauge 求值抛错不再折成 0 —— 0 是该 gauge 的合法读数,折 0 让故障态与真实 0
|
|
91
|
-
* 在 wire 上逐字节不可区分(与上方 inc 静默 no-op 同族:观测面的 fail-silent 正是观测要消灭的东西)。
|
|
92
|
-
* 披露方向:错帧**不发样本行**(缺席=读数未知,面板显示 no data 而非假 0)+ 计
|
|
93
|
-
* `metric_gauge_eval_errors_total{name}`(响亮、可告警)。每次求值(每次刮取/snapshot)错一次计一次;
|
|
94
|
-
* 仍然绝不 propagate(/metrics 不因单个读数器坏而 500)。 */
|
|
95
81
|
evalDynamic(name, fn) {
|
|
96
82
|
try {
|
|
97
83
|
return fn();
|
|
@@ -101,9 +87,7 @@ export class Metrics {
|
|
|
101
87
|
return undefined;
|
|
102
88
|
}
|
|
103
89
|
}
|
|
104
|
-
/** Prometheus text exposition (content-type text/plain; version=0.0.4). */
|
|
105
90
|
render() {
|
|
106
|
-
// dynamic 先求值(2-j):错帧的 inc 要落进**本次**刮取的 counters 段,不能等下一次。
|
|
107
91
|
const dyn = [];
|
|
108
92
|
for (const [name, d] of this.dynamic) {
|
|
109
93
|
const v = this.evalDynamic(name, d.fn);
|
|
@@ -136,10 +120,7 @@ export class Metrics {
|
|
|
136
120
|
}
|
|
137
121
|
return out.join("\n") + "\n";
|
|
138
122
|
}
|
|
139
|
-
/** Structured snapshot of the whole registry — the OTLP exporter maps this to OTel data points
|
|
140
|
-
* (counters→Sum monotonic, gauges/dynamic→Gauge, histograms→Histogram). Read-only copy. */
|
|
141
123
|
snapshot() {
|
|
142
|
-
// dynamic 先求值(2-j,同 render):错帧的 inc 要进本次快照的 counters。
|
|
143
124
|
const dynVals = [...this.dynamic].map(([name, d]) => ({ name, help: d.help, v: this.evalDynamic(name, d.fn) }));
|
|
144
125
|
const counters = [...this.counters].map(([name, c]) => ({
|
|
145
126
|
name,
|
|
@@ -151,7 +132,6 @@ export class Metrics {
|
|
|
151
132
|
help: g.help,
|
|
152
133
|
series: [...g.series.values()].map((s) => ({ labels: s.labels, value: s.value })),
|
|
153
134
|
}));
|
|
154
|
-
// 错帧 series 空:名字/HELP 仍披露(指标存在),但绝不铸 {value:0} 假数据点。
|
|
155
135
|
for (const { name, help, v } of dynVals)
|
|
156
136
|
gauges.push({ name, help, series: v === undefined ? [] : [{ labels: {}, value: v }] });
|
|
157
137
|
const histograms = [...this.histos].map(([name, h]) => ({
|
|
@@ -162,9 +142,6 @@ export class Metrics {
|
|
|
162
142
|
}));
|
|
163
143
|
return { counters, gauges, histograms };
|
|
164
144
|
}
|
|
165
|
-
/** Curated health summary (friendly JSON) for a lightweight fleet dashboard — the sema-registry's
|
|
166
|
-
* fleet page fetches this per worker and renders cards + computes rates over polls (no Prometheus
|
|
167
|
-
* needed). Counters are cumulative totals; histograms are reported as averages (sum/count). */
|
|
168
145
|
summarize() {
|
|
169
146
|
const ctotal = (name) => [...(this.counters.get(name)?.series.values() ?? [])].reduce((a, s) => a + s.value, 0);
|
|
170
147
|
const cby = (name, label) => {
|
|
@@ -186,9 +163,6 @@ export class Metrics {
|
|
|
186
163
|
tokensTotal: ctotal("task_tokens_total"),
|
|
187
164
|
costUsd: ctotal("model_cost_micro_usd_total") / 1e6,
|
|
188
165
|
costUsdByModel: Object.fromEntries(Object.entries(cby("model_cost_micro_usd_total", "model")).map(([k, v]) => [k, v / 1e6])),
|
|
189
|
-
// D5([2122] 挂账,additive v1):unpriced 部署下 costUsd 恒 0 而消费端读成「零花费」。
|
|
190
|
-
// costUnknown=true ⇒ costUsd/costUsdByModel 是**下界**(RB-368 缺席=未知语义;显式 0 = declared
|
|
191
|
-
// free 不计入 unpriced)。计数点在 budget.ts 的 brain.call 臂。
|
|
192
166
|
costUnknown: ctotal("brain_calls_unpriced_total") > 0,
|
|
193
167
|
unpricedCalls: ctotal("brain_calls_unpriced_total"),
|
|
194
168
|
taskDurationAvgSec: havg("task_duration_seconds"),
|
|
@@ -215,7 +189,6 @@ export class Metrics {
|
|
|
215
189
|
};
|
|
216
190
|
}
|
|
217
191
|
}
|
|
218
|
-
/** Register the service's standard metric set. */
|
|
219
192
|
export function createMetrics() {
|
|
220
193
|
const m = new Metrics();
|
|
221
194
|
m.counter("metric_gauge_eval_errors_total", "Dynamic gauge eval errors by gauge name (an erroring gauge emits NO sample that scrape — absence means unknown, not 0)");
|
|
@@ -228,20 +201,10 @@ export function createMetrics() {
|
|
|
228
201
|
m.gauge("runs_active", "Async runs currently executing");
|
|
229
202
|
m.counter("council_runs_total", "Code-review council runs (L1+L3)");
|
|
230
203
|
m.histogram("council_tokens", "Total model tokens per council review (lenses + arbiter)");
|
|
231
|
-
// Prefix-cache effectiveness (core 1.22): per-task hit rate (cost-critical) + a count of tasks the
|
|
232
|
-
// Runner flagged as low-hit (unstable/poisoned prefix) so it can be alerted on. The observed value is
|
|
233
|
-
// `result.stats.cacheHitRate` — core-computed (core 3.0.0: cachedTokens/totalInputTokens, the normalized
|
|
234
|
-
// total-including-cache denominator; runs.ts / http/server.ts just forward it), not derived here — this
|
|
235
|
-
// description names the field, not a formula this file owns (E3: don't restate a formula whose owner
|
|
236
|
-
// can change it without telling us; core 3.0.0's denominator switched from `promptTokens` to
|
|
237
|
-
// `totalInputTokens`, which the old "(cachedTokens/promptTokens)" text would have silently mis-described).
|
|
238
204
|
m.histogram("task_cache_hit_rate", "Prefix-cache hit rate per task (core-computed stats.cacheHitRate, 0..1)", [
|
|
239
205
|
0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 1,
|
|
240
206
|
]);
|
|
241
207
|
m.counter("prompt_cache_low_hit_total", "Tasks the Runner flagged with a low prefix-cache hit rate");
|
|
242
|
-
// Cost & trace observability (core 1.37). Spend = the integer micro-USD `costMicroUsd` summed over
|
|
243
|
-
// ALL brain calls (top-level + async + council sub-tasks) via the tracer — the authoritative billing
|
|
244
|
-
// counter. firstTokenMs is the gateway-hang early signal; tool calls + budget cutoffs round it out.
|
|
245
208
|
m.counter("model_cost_micro_usd_total", "Authoritative model spend (integer micro-USD) over all brain calls, by model");
|
|
246
209
|
m.histogram("brain_first_token_ms", "Time to first content token per brain call (gateway-hang signal)", [
|
|
247
210
|
50, 100, 250, 500, 1000, 2000, 5000, 10000, 30000,
|
|
@@ -253,106 +216,50 @@ export function createMetrics() {
|
|
|
253
216
|
m.counter("budget_exceeded_total", "Tasks failed by a budget/limit ceiling, by errorCode");
|
|
254
217
|
m.counter("cost_quota_rejected_total", "Tasks refused because the principal is over its cumulative cost quota");
|
|
255
218
|
m.counter("degraded_total", "Tasks degraded to a cheaper model (1.40), by reason (budget/rate_limit/breaker_open)");
|
|
256
|
-
// #233 / A-002.8:名单外的模型选择在 **resume 腿**被放行(fresh 提交是 400)。读法:非零 = 还有存量
|
|
257
|
-
// 会话跑在 @-mention 名单之外 —— 那是刻意的(砖死 resume 比降级糟),但治理面必须看得见它有多少。
|
|
258
219
|
m.counter("task_model_not_allowlisted_resume_total", "Resumed legs whose model/compactionModel is outside the @-mention allowlist (allowed to keep running by design — the allowlist gates fresh picks only)");
|
|
259
220
|
m.counter("prompt_constitution_total", "Prompt assembly constitution mode per task (core 1.243 提示词主权): steady state = core; provider-assembled/replaced/legacy warrant eyes");
|
|
260
221
|
m.counter("rewind_snapshot_failed_total", "Per-turn rewind file snapshots that failed, by code (too_large = big working tree, expected/benign; other codes warrant eyes)");
|
|
261
222
|
m.counter("verifications_total", "Tasks run through the verification gate (1.44), by final verdict");
|
|
262
223
|
m.counter("cascade_total", "Tasks run through the model cascade (1.45), by outcome (passed/exhausted)");
|
|
263
|
-
// Degenerate-repetition salvage classifier (core 1.59): on errorCode=output.degenerate, where was the
|
|
264
|
-
// useful answer? a=in the degenerate turn itself, b=in an earlier turn, unknown=nowhere. Frequencies
|
|
265
|
-
// feed back to core's decision on whether the "last-substantive-turn" salvage (②) is worth building.
|
|
266
224
|
m.counter("degenerate_total", "Degenerate-output failures (1.59), by salvage class (a/b/unknown)");
|
|
267
|
-
// Plan-cache recurrence probe (INSTRUMENT-FIRST, core design/42): does a tenant re-pose near-duplicate
|
|
268
|
-
// task objectives? Fleet-level totals; per-scope detail is at GET /metrics/plan-cache (not a label, to
|
|
269
|
-
// avoid high tenant cardinality). Decides whether core builds the plan-caching producer hook.
|
|
270
225
|
m.counter("plan_cache_recurrence_total", "Finished tasks whose objective recurs vs scope history, by hit (true/false)");
|
|
271
226
|
m.histogram("plan_cache_qsig", "Significant-token count of task objectives (qSig length)", [1, 2, 3, 5, 10, 20]);
|
|
272
|
-
// Memory consolidation (1.62 B-full): the reconcile-LLM store writes, by op. A spike in update/delete is
|
|
273
|
-
// the prod signal for over-merging / a mis-calibrated band. Recorded by the store on each call, so it does
|
|
274
|
-
// NOT need the fire-and-forget consolidation() to be awaited.
|
|
275
227
|
m.counter("memory_consolidation_ops_total", "Memory consolidation store ops (1.62 B-full), by op (update/delete)");
|
|
276
228
|
m.counter("mcp_server_unavailable_total", "MCP servers skipped fail-open at task start (1.68), unreachable/misconfigured");
|
|
277
|
-
// DESIGN-269 车1:A2A peer 在任务起手不可用(卡取不到 / skill 铸不出)—— 与 mcp 同族的 fail-open 计数。
|
|
278
229
|
m.counter("a2a_peer_unavailable_total", "A2A peers skipped at task start (DESIGN-269 client leg): card unreachable / spec unusable");
|
|
279
|
-
// DESIGN-269 车2:server-as-peer 的**对外**面。两只计数器刻意分开,它们回答两个不同的问题:
|
|
280
|
-
// · `a2a_serve_rpc_total{method,outcome}` = 每个方法各被调了多少次、结果是什么(ok / refused /
|
|
281
|
-
// error_<code> / method_not_found / internal_error)。`method_not_found` 那一格是**路线图信号**:
|
|
282
|
-
// 外部 peer 在真实拓扑里到底在要哪个还没做的方法(message/stream?tasks/cancel?),比我们猜准。
|
|
283
|
-
// · `a2a_serve_rejected_total{reason}` = **没能进到方法层**的那些(凭据/形/版本以外的门),
|
|
284
|
-
// 它是「有人在扫我们 / 有人接错了」的第一手材料。混进上面那只会让「协议用错」与「跑失败了」
|
|
285
|
-
// 在同一条曲线上不可分。
|
|
286
230
|
m.counter("a2a_serve_rpc_total", "A2A server-as-peer JSON-RPC calls by method and outcome (DESIGN-269 车2)");
|
|
287
231
|
m.counter("a2a_serve_rejected_total", "A2A server-as-peer requests refused before reaching a method handler, by reason (DESIGN-269 车2)");
|
|
288
|
-
// Remote execution env (design/48 E2B adapter): lifecycle outcomes by provider/result.
|
|
289
232
|
m.counter("remote_env_connect_total", "E2B remote-env connect outcomes (provider/result)");
|
|
290
233
|
m.counter("remote_env_destroy_total", "E2B remote-env destroy outcomes (provider/result)");
|
|
291
234
|
m.counter("remote_env_suspend_total", "E2B remote-env suspendVM outcomes (v1.5, provider/result)");
|
|
292
235
|
m.counter("remote_env_resume_total", "E2B remote-env resumeVM outcomes (v1.5, provider/result)");
|
|
293
236
|
m.counter("remote_env_exec_stream_read_timeout_total", "E2B execStream idle read-timeouts (#1128, provider)");
|
|
294
|
-
// E18 resume-at: a per-turn anchor capture (getLeafId→put) failed (e.g. an un-migrated DB missing resume_anchor) —
|
|
295
|
-
// best-effort, never fails the run; this surfaces a SYSTEMATIC capture outage instead of a silent feature stoppage.
|
|
296
237
|
m.counter("resume_anchor_capture_failed", "E18 resume-at per-turn anchor captures that failed (best-effort, run unaffected)");
|
|
297
|
-
// Silent-fallback telemetry P0 batch (sema-internal server/docs/design/SILENT-FALLBACK-TELEMETRY.md, service half):
|
|
298
|
-
// S4 — brain-layer liveness phases (rate_limited/retrying/reconnecting/circuit_open) were previously visible
|
|
299
|
-
// only on the live SSE stream; count them so upstream throttling/outage is graphable per phase.
|
|
300
238
|
m.counter("brain_retry_total", "Brain-layer status events (S4), by phase (rate_limited/retrying/reconnecting/circuit_open)");
|
|
301
|
-
// S5 — the auto store-backend probe degraded this replica to in-memory at boot (configured DB unreachable).
|
|
302
|
-
// 1 = degraded (this replica is NOT persisting); scrape-level alert target. /health carries the twin flag.
|
|
303
239
|
m.gauge("store_backend_degraded", "1 when the auto DB probe failed at boot and this replica degraded to in-memory (S5)");
|
|
304
|
-
// S6 — images submitted to a model whose declared input has no "image": core degrades them to text
|
|
305
|
-
// placeholders (1.233.1). Counted at submit so the silent modality drop is visible per model.
|
|
306
240
|
m.counter("images_omitted_total", "Tasks whose images will be degraded to text placeholders (no-vision model), by model (S6)");
|
|
307
|
-
// S7 — background reapers: how many rows each sweep actually flipped (previously discarded), by kind.
|
|
308
|
-
// A spike = an instance died and its orphans were batch-failed (distinguishable from organic failures).
|
|
309
241
|
m.counter("runs_reaped_total", "Run rows flipped by the background reapers (S7), by kind (stale/suspended/expired_checkpoint)");
|
|
310
242
|
m.counter("checkpoints_reaped_total", "Durable checkpoints expired by the backstop reaper (S7)");
|
|
311
243
|
m.counter("bakes_reaped_total", "Image bakes failed by the stale-lease reaper (S7)");
|
|
312
|
-
// S9 — cross-replica write-behind counters (rate limit / cost quota): flush/refresh failures previously
|
|
313
|
-
// vanished in a bare catch → the fleet silently degrades to per-replica counting. Streak gauge = current
|
|
314
|
-
// consecutive failures (0 = healthy); the total counts every failed attempt.
|
|
315
244
|
m.counter("counter_flush_failed_total", "Write-behind counter flush/refresh failures (S9), by table and kind");
|
|
316
245
|
m.gauge("counter_flush_fail_streak", "Current consecutive write-behind flush/refresh failures (S9), by table and kind");
|
|
317
|
-
// S14 — near-budget degrade was configured but DROPPED for this task (e.g. image task + text-only degrade
|
|
318
|
-
// target): the task keeps its main model and will hard-fail on cost instead. Previously undetectable.
|
|
319
246
|
m.counter("degrade_dropped_total", "Tasks whose near-budget degrade was dropped at submit (S14), by reason");
|
|
320
|
-
// Silent-fallback P1 batch (second sweep, same doc §P1):
|
|
321
|
-
// S18 — every brain call by provider+model; provider="unknown" = the routing layer fell through to the
|
|
322
|
-
// gateway default without a recognized provider (previously invisible).
|
|
323
247
|
m.counter("brain_calls_total", "Brain calls by provider/model (S18); provider=unknown = unrecognized routing fallthrough");
|
|
324
|
-
// S10 — DB wall-clock minus local wall-clock (ms, can be negative). The write-behind counters bucket on
|
|
325
|
-
// LOCAL floor(now/windowMs); replica clock skew splits a fleet window into disjoint buckets (soft-limit leak).
|
|
326
248
|
m.gauge("fleet_counter_bucket_skew_ms", "DB clock minus local clock in ms (S10) — skew splits cross-replica counter windows");
|
|
327
249
|
m.gauge("fleet_clock_probe_ok", "1 = last DB clock probe succeeded, 0 = failing (skew gauge is stale while 0)");
|
|
328
|
-
// #96 redesign⑤ (§DESIGN-V2 E): LSP sandbox-bridge lifecycle + session outcomes — previously zero metrics
|
|
329
|
-
// existed for the whole subsystem (a dead bridge or a stuck restart budget had no fleet-level signal).
|
|
330
250
|
m.counter("lsp_bridge_start_total", "LSP sandbox bridge (re)starts attempted, by provider and generation");
|
|
331
251
|
m.counter("lsp_bridge_dead_total", "LSP sandbox bridge generations that died, by reason (the word IS the model-facing unavailableReason)");
|
|
332
252
|
m.counter("lsp_session_open_total", "LSP session opens, by language and outcome (ok/failed)");
|
|
333
253
|
m.counter("lsp_session_heal_total", "LSP session heals (dead-transport reopen), by outcome (ok/failed)");
|
|
334
|
-
// S21 — which backend snapshot blobs actually land on (minio|sql). Partial MINIO_* config silently falls to sql.
|
|
335
254
|
m.gauge("snapshot_blob_backend", "1 on the active snapshot-blob backend series (S21), by backend (minio|sql)");
|
|
336
|
-
// S22 — memory embed/KNN runtime failures (boot said vector; runtime silently degrades to lexical).
|
|
337
|
-
// HELP 文案按**唯一活发射点**写(#228 接线后的复扫):stores.ts 的 onFailure 腿打 `{ backend: dialect }`,
|
|
338
|
-
// 从没有过 `where` 维度(那是退役的 MEMORY_BACKEND/EMBEDDING_* store 面的形)—— 运维照旧文案写
|
|
339
|
-
// `sum by (where)` 得到的是空序列。USAGE 的 `memory_embed_failed_total{backend}` 才是对的那一份。
|
|
340
255
|
m.counter("memory_embed_failed_total", "Memory embedding calls that failed at runtime (S22), by backend (the memory-engine dialect that owns the embedder)");
|
|
341
256
|
m.counter("memory_knn_query_failed_total", "Memory KNN searchScored queries that failed at runtime (S22)");
|
|
342
|
-
// LOW — dedup fold vs new insert (a fold UPDATEs the existing row; previously indistinguishable), and the
|
|
343
|
-
// dedup probe failing over to a plain INSERT.
|
|
344
257
|
m.counter("memory_dedup_folded_total", "Memory writes folded into an existing near-duplicate row (LOW)");
|
|
345
258
|
m.counter("memory_dedup_probe_failed_total", "Memory dedup KNN probes that failed and fell through to INSERT (LOW)");
|
|
346
|
-
// S24 — redaction rewrites by pattern (over/under-redaction previously had zero fingerprint).
|
|
347
259
|
m.counter("redactions_applied_total", "redactSecrets rewrites (S24), by pattern label");
|
|
348
|
-
// S25 — warm session-cache entries evicted by a cross-instance write (import/replace) while still cached.
|
|
349
260
|
m.counter("stale_cache_conflict_total", "Warm session-cache entries evicted by a cross-instance write while cached (S25)");
|
|
350
|
-
// LOW — cross-replica breaker write-through failures (advisory swallow previously silent).
|
|
351
261
|
m.counter("breaker_writethrough_failed_total", "Circuit-breaker cross-replica write-through failures (LOW), by backend");
|
|
352
|
-
// LOW — web-search provider returned a non-array payload (model silently gets zero results).
|
|
353
262
|
m.counter("web_search_bad_payload_total", "Web-search provider responses whose results field was not an array (LOW), by provider");
|
|
354
|
-
// 收账批(2026-07-08):这 15 个计数器一直只有 inc 没注册 — 在 auto-register 落地前
|
|
355
|
-
// 整批从未上过 /metrics(inc 曾对未注册名静默 no-op)。补显式注册拿正经 HELP 文案。
|
|
356
263
|
m.counter("permission_denied_total", "Tool-gate denials by source (core's PermissionDeniedSource, incl. org governance; 'other' = a word this build does not know; goal B4 always-on deny meter)");
|
|
357
264
|
m.counter("permission_rule_events_total", "Persisted allow-rule seam events by event (persisted_rule_allowed = a rule authorized a call with nobody asked; rule_store_unreadable = the store failed to read so the call was adjudicated with ZERO rules)");
|
|
358
265
|
m.counter("compaction_events_total", "Compaction lifecycle events by outcome (started/completed/failed/skipped) and trigger");
|
|
@@ -369,7 +276,6 @@ export function createMetrics() {
|
|
|
369
276
|
m.counter("remote_env_rpc_timeout_total", "Remote-env RPC timeouts, by provider");
|
|
370
277
|
m.counter("task_outcomes_total", "Task outcome-ledger rows recorded, by status and oracle green flag");
|
|
371
278
|
m.counter("workflow_runs_total", "Self-orchestration workflow runs, by status");
|
|
372
|
-
// 鉴权桥 verify 延迟:热路径(kid 命中)亚毫秒;冷路径(JWKS 拉取)带网络 RTT——首请求/轮换尾延迟可见。
|
|
373
279
|
m.histogram("auth_bridge_verify_seconds", "Registry SSO JWT verify latency (hot path sub-ms; cold path includes a JWKS fetch)", [0.001, 0.005, 0.02, 0.1, 0.5, 2, 10]);
|
|
374
280
|
return m;
|
|
375
281
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { recordFailOpen } from "./fail-open.js";
|
|
2
|
-
const CUMULATIVE = 2;
|
|
3
|
-
/** uint64 nanos as a string (OTLP/JSON encodes 64-bit ints as strings; ms×1e6 overflows Number). */
|
|
2
|
+
const CUMULATIVE = 2;
|
|
4
3
|
const nano = (ms) => (BigInt(ms) * 1000000n).toString();
|
|
5
4
|
const attrs = (labels) => Object.entries(labels).map(([key, v]) => ({ key, value: { stringValue: String(v) } }));
|
|
6
|
-
/** Build the OTLP/JSON metrics payload from a registry snapshot. Pure → unit-testable. */
|
|
7
5
|
export function buildOtlpPayload(snap, opts) {
|
|
8
6
|
const start = nano(opts.startMs);
|
|
9
7
|
const now = nano(opts.nowMs);
|
|
@@ -38,15 +36,13 @@ export function buildOtlpPayload(snap, opts) {
|
|
|
38
36
|
description: h.help,
|
|
39
37
|
histogram: {
|
|
40
38
|
dataPoints: h.series.map((s) => {
|
|
41
|
-
// our buckets are CUMULATIVE (Prometheus-style: counts[i] = #obs ≤ bounds[i]); OTLP wants
|
|
42
|
-
// PER-bucket counts with a trailing +Inf overflow bucket (length = bounds.length + 1).
|
|
43
39
|
const bucketCounts = [];
|
|
44
40
|
let prev = 0;
|
|
45
41
|
for (let i = 0; i < h.buckets.length; i++) {
|
|
46
42
|
bucketCounts.push(String(s.counts[i] - prev));
|
|
47
43
|
prev = s.counts[i];
|
|
48
44
|
}
|
|
49
|
-
bucketCounts.push(String(s.count - prev));
|
|
45
|
+
bucketCounts.push(String(s.count - prev));
|
|
50
46
|
return { attributes: attrs(s.labels), startTimeUnixNano: start, timeUnixNano: now, count: String(s.count), sum: s.sum, bucketCounts, explicitBounds: h.buckets };
|
|
51
47
|
}),
|
|
52
48
|
aggregationTemporality: CUMULATIVE,
|
|
@@ -62,14 +58,10 @@ export function buildOtlpPayload(snap, opts) {
|
|
|
62
58
|
],
|
|
63
59
|
};
|
|
64
60
|
}
|
|
65
|
-
/** Start the periodic push loop (unref'd so it never holds the process open). Returns a stop handle. */
|
|
66
61
|
export function startOtlpExporter(metrics, opts) {
|
|
67
62
|
const startMs = Date.now();
|
|
68
63
|
const url = `${opts.endpoint}/v1/metrics`;
|
|
69
64
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
70
|
-
// A collector that accepts the connection but never responds would otherwise let one fetch per tick pile
|
|
71
|
-
// up (each holding a full snapshot payload + socket) — bound each request and skip a tick while one is
|
|
72
|
-
// still in flight, so a half-dead collector can never accumulate back-pressure inside the process.
|
|
73
65
|
let inFlight = false;
|
|
74
66
|
const exportOnce = async () => {
|
|
75
67
|
if (inFlight)
|
|
@@ -83,8 +75,6 @@ export function startOtlpExporter(metrics, opts) {
|
|
|
83
75
|
body: JSON.stringify(payload),
|
|
84
76
|
signal: AbortSignal.timeout(Math.max(1_000, opts.intervalMs)),
|
|
85
77
|
});
|
|
86
|
-
// #193 车5(P1-3):两条失败臂都要过 fail-open 计数——onError 只到日志轴,而 Prometheus-only
|
|
87
|
-
// 部署只看 metrics,此前 collector 持续宕机在 metrics 轴零信号(遥测腿自盲)。
|
|
88
78
|
if (!res.ok) {
|
|
89
79
|
recordFailOpen("server.otel.export-failed", `http_${res.status}`);
|
|
90
80
|
opts.onError?.(new Error(`OTLP export HTTP ${res.status}`));
|
|
@@ -92,7 +82,7 @@ export function startOtlpExporter(metrics, opts) {
|
|
|
92
82
|
}
|
|
93
83
|
catch (err) {
|
|
94
84
|
recordFailOpen("server.otel.export-failed", String(err));
|
|
95
|
-
opts.onError?.(err);
|
|
85
|
+
opts.onError?.(err);
|
|
96
86
|
}
|
|
97
87
|
finally {
|
|
98
88
|
inFlight = false;
|
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-principal execution context (AsyncLocalStorage). The brain-call tracer fires for EVERY model
|
|
3
|
-
* call — including council/team sub-tasks that run on the shared `subRunner` (a per-task tracer can't
|
|
4
|
-
* reach those). To attribute spend to the right principal we wrap a task's execution in this ALS:
|
|
5
|
-
* `node:async_hooks` propagates the store through every `await`, so a sub-task spawned inside a tool
|
|
6
|
-
* call still reads the originating principal. Absent store = unattributed (dev / no principal).
|
|
7
|
-
*/
|
|
8
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
2
|
export const principalContext = new AsyncLocalStorage();
|
|
10
|
-
/** Run `fn` with `principal` as the ambient context (no-op wrapper when principal is undefined). */
|
|
11
3
|
export function withPrincipal(principal, fn) {
|
|
12
4
|
return principal ? principalContext.run(principal, fn) : fn();
|
|
13
5
|
}
|
|
14
|
-
/** The principal attributed to the current async execution, if any. */
|
|
15
6
|
export function currentPrincipal() {
|
|
16
7
|
return principalContext.getStore();
|
|
17
8
|
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
// design/158 N18: the prompt-manifest / config-assembled observability face moved VERBATIM out of budget.ts
|
|
2
|
-
// (lens4 §4.1 — budget.ts's name says cost/budget, but this half is wire-observability projection of core's
|
|
3
|
-
// `prompt.assembled`/`config.assembled` trace events, unrelated to spend). budget.ts re-exports all four for
|
|
4
|
-
// compat (its own createTracer still consumes promptManifestRecordOf/configAssembledRecordOf/PromptManifestTracker).
|
|
5
|
-
/** Whitelist one `prompt.assembled` trace event into a {@link PromptManifestRecord} (v1 face required,
|
|
6
|
-
* v2 `sections`/`tools` additive — absent stays absent). */
|
|
7
1
|
export function promptManifestRecordOf(e) {
|
|
8
2
|
return {
|
|
9
3
|
constitution: String(e.constitution),
|
|
@@ -14,8 +8,6 @@ export function promptManifestRecordOf(e) {
|
|
|
14
8
|
...(Array.isArray(e.tools)
|
|
15
9
|
? { tools: e.tools.map((t) => ({ wireName: String(t.wireName), aliases: (Array.isArray(t.aliases) ? t.aliases : []).map(String), contractId: String(t.contractId), implementationRevision: String(t.implementationRevision), cardId: String(t.cardId), shapeDigest: String(t.shapeDigest), wireSchemaDigest: String(t.wireSchemaDigest) })) }
|
|
16
10
|
: {}),
|
|
17
|
-
// toolDisclosure(core 5.15.0 #181,[2872] 提货②):三键逐挑——数值有限、两词字符串非空才铸;
|
|
18
|
-
// 畸形按缺席(不铸假值,与 sections/tools 的 additive 同规)。
|
|
19
11
|
...(typeof e.toolDisclosure === "object" && e.toolDisclosure !== null &&
|
|
20
12
|
typeof e.toolDisclosure.deferredTools === "number" && Number.isFinite(e.toolDisclosure.deferredTools) &&
|
|
21
13
|
typeof e.toolDisclosure.strategy === "string" && e.toolDisclosure.strategy.length > 0 &&
|
|
@@ -25,23 +17,6 @@ export function promptManifestRecordOf(e) {
|
|
|
25
17
|
totalChars: e.totalChars,
|
|
26
18
|
};
|
|
27
19
|
}
|
|
28
|
-
/**
|
|
29
|
-
* [998]② per-task prompt-manifest accumulator — the `ModelUsageTracker`(budget.ts)pattern: the
|
|
30
|
-
* `prompt.assembled` tracer hook is SYNC/no-IO, so it cannot persist; it RECORDS here (registered
|
|
31
|
-
* top-level runs only — the same leak fence: sub-task manifests are never registered, never leak), and
|
|
32
|
-
* the durable run lifecycle DRAINS the pending records into `prompt_assembled` events. Two departures
|
|
33
|
-
* from the ModelUsage shape (release-review findings):
|
|
34
|
-
* - The pending value is an ordered QUEUE, not a single slot: a cascade/verify leg re-prepares under
|
|
35
|
-
* ONE taskId (core's runCascade passes the same spec taskId to every rung), and each prepare's
|
|
36
|
-
* manifest must persist — latest-wins would hide the prompt actually used by earlier billed rungs.
|
|
37
|
-
* - {@link register} returns a per-leg LEASE and {@link clear} requires it: after setSuspended makes
|
|
38
|
-
* the row claimable, a fast sibling resume can re-register the same taskId before the parked leg's
|
|
39
|
-
* finally runs (the codebase's documented B-3 window) — an unconditional clear there would silently
|
|
40
|
-
* delete the NEW leg's registration/pending manifest. Identity-guarded like the sibling
|
|
41
|
-
* inflight/steerable teardowns.
|
|
42
|
-
*/
|
|
43
|
-
/** [1301]③ — `config.assembled` 的白名单投影(core 1.346 trace:catalogVersion/fields[来源标注]/
|
|
44
|
-
* overrideReasons)。fields 已是 core 精选的观测形(digest-free、有界);32KiB 罩防未来膨胀。 */
|
|
45
20
|
export function configAssembledRecordOf(e) {
|
|
46
21
|
const rec = {
|
|
47
22
|
catalogVersion: e.catalogVersion,
|
|
@@ -51,7 +26,7 @@ export function configAssembledRecordOf(e) {
|
|
|
51
26
|
};
|
|
52
27
|
try {
|
|
53
28
|
if (JSON.stringify(rec).length > 32_768)
|
|
54
|
-
return undefined;
|
|
29
|
+
return undefined;
|
|
55
30
|
}
|
|
56
31
|
catch {
|
|
57
32
|
return undefined;
|
|
@@ -60,14 +35,9 @@ export function configAssembledRecordOf(e) {
|
|
|
60
35
|
}
|
|
61
36
|
export class PromptManifestTracker {
|
|
62
37
|
byTask = new Map();
|
|
63
|
-
/** [1301]③:config.assembled 的同生命周期伴随记录(register 同门、drainConfig 同拍)。队列形
|
|
64
|
-
* (codex F2):cascade 每 rung 独立解析配置,后到覆盖会把先前已计费 rung 的生效配置抹掉——与
|
|
65
|
-
* prompt manifests 同姿势逐条保序。 */
|
|
66
38
|
configByTask = new Map();
|
|
67
39
|
active = new Map();
|
|
68
40
|
leaseCounter = 0;
|
|
69
|
-
/** Mark a TOP-LEVEL run active (same fence as `ModelUsageTracker.register`, budget.ts). Returns this leg's
|
|
70
|
-
* lease — pass it to {@link clear} so only the CURRENT owner can tear the slot down. */
|
|
71
41
|
register(taskId) {
|
|
72
42
|
if (!taskId)
|
|
73
43
|
return 0;
|
|
@@ -75,7 +45,6 @@ export class PromptManifestTracker {
|
|
|
75
45
|
this.active.set(taskId, lease);
|
|
76
46
|
return lease;
|
|
77
47
|
}
|
|
78
|
-
/** Queue this prepare's manifest — ONLY for a registered top-level run (else a no-op). Sync, never throws. */
|
|
79
48
|
record(taskId, m) {
|
|
80
49
|
if (!taskId || !this.active.has(taskId))
|
|
81
50
|
return;
|
|
@@ -85,7 +54,6 @@ export class PromptManifestTracker {
|
|
|
85
54
|
else
|
|
86
55
|
this.byTask.set(taskId, [m]);
|
|
87
56
|
}
|
|
88
|
-
/** [1301]③:队一条 config.assembled 投影(同 register 门;逐条保序——cascade 每 rung 各留其形)。 */
|
|
89
57
|
recordConfig(taskId, rec) {
|
|
90
58
|
if (!taskId || !this.active.has(taskId))
|
|
91
59
|
return;
|
|
@@ -95,7 +63,6 @@ export class PromptManifestTracker {
|
|
|
95
63
|
else
|
|
96
64
|
this.configByTask.set(taskId, [rec]);
|
|
97
65
|
}
|
|
98
|
-
/** Return-and-clear ALL pending config records in emission order; `undefined` when none. */
|
|
99
66
|
drainConfig(taskId) {
|
|
100
67
|
const q = this.configByTask.get(taskId);
|
|
101
68
|
if (!q || q.length === 0)
|
|
@@ -103,7 +70,6 @@ export class PromptManifestTracker {
|
|
|
103
70
|
this.configByTask.delete(taskId);
|
|
104
71
|
return q;
|
|
105
72
|
}
|
|
106
|
-
/** Return-and-clear ALL pending manifests in emission order; `undefined` when none. */
|
|
107
73
|
drain(taskId) {
|
|
108
74
|
const q = this.byTask.get(taskId);
|
|
109
75
|
if (!q || q.length === 0)
|
|
@@ -111,8 +77,6 @@ export class PromptManifestTracker {
|
|
|
111
77
|
this.byTask.delete(taskId);
|
|
112
78
|
return q;
|
|
113
79
|
}
|
|
114
|
-
/** Drop pending + unregister (run end / abandoned leg) — but ONLY while `lease` still owns the slot;
|
|
115
|
-
* a stale leg's teardown after a fast re-register is a no-op (the new leg owns it now). */
|
|
116
80
|
clear(taskId, lease) {
|
|
117
81
|
if (this.active.get(taskId) !== lease)
|
|
118
82
|
return;
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
export class RateLimiter {
|
|
2
2
|
windowMs;
|
|
3
3
|
windows = new Map();
|
|
4
|
-
/** 🔴 不再 `readonly`:限额是**比较参数**,不是窗表状态 —— 换代只动它,窗内已计的数一个不动(§5)。 */
|
|
5
4
|
limit;
|
|
6
5
|
constructor(limit, windowMs = 60_000) {
|
|
7
6
|
this.windowMs = windowMs;
|
|
8
7
|
this.limit = limit;
|
|
9
8
|
}
|
|
10
9
|
check(key) {
|
|
11
|
-
// 关断哨兵:零比较、零窗表写入。off 期间不攒计数是刻意的——0→N 打开时应该是一个完整的新窗口,
|
|
12
|
-
// 而不是「上来就超」(那段时间根本没有 ceiling,补记账等于追溯执法)。
|
|
13
10
|
if (this.limit <= 0)
|
|
14
11
|
return { allowed: true, retryAfterSec: 0, remaining: Number.POSITIVE_INFINITY };
|
|
15
12
|
const now = Date.now();
|
|
@@ -27,7 +24,6 @@ export class RateLimiter {
|
|
|
27
24
|
setLimits(next) {
|
|
28
25
|
this.limit = next.rateLimitPerMin;
|
|
29
26
|
}
|
|
30
|
-
/** Drop expired windows (call periodically). */
|
|
31
27
|
sweep(now = Date.now()) {
|
|
32
28
|
for (const [k, w] of this.windows)
|
|
33
29
|
if (w.resetAt <= now)
|
|
@@ -1,53 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A-033.3(#256)—— **密钥形 env 剥除的留痕面**(core D1 `scrubSecretEnv` 的可观测半场)。
|
|
3
|
-
*
|
|
4
|
-
* ## 它解决的问题
|
|
5
|
-
* `scrubSecretEnv` 是本仓两条 shell/子进程腿的**安全默认**:模型可驱动的 shell(`inheritEnv:"scrub"`)与
|
|
6
|
-
* git 子进程(钩子/凭证助手)拿到的 env 里,`*_KEY`/`*_TOKEN`/`*_SECRET`/`*_PASSWORD`… 一律先丢掉。
|
|
7
|
-
* 两处都以**单参形**调用,于是「这一次到底丢了哪些 key、按哪条规则(suffix-rule / exact-name)、置信度多少」
|
|
8
|
-
* **零留痕** —— 部署方既证明不了保护生效过,也发现不了它悄悄不生效了(词表漂移 / 一个新的密钥命名法从
|
|
9
|
-
* 规则的缝里穿过去 / 某条腿被改成 `inheritEnv:"all"`)。core 早就把收集口开好了(`findings?:
|
|
10
|
-
* SecretEnvFinding[]`,d.ts 原话:the VALUE is never carried — this record is meant to be loggable),
|
|
11
|
-
* 缺的只是本仓这半场。本模块就是那半场,**不是**一个新的执法面:它一个字节也不改变剥除行为。
|
|
12
|
-
*
|
|
13
|
-
* ## 形(照 `fail-open.ts` 的仓内先例)
|
|
14
|
-
* 1. **计数(逐次)** —— `secret_env_scrubbed_total{site,kind,confidence}`;
|
|
15
|
-
* 2. **结构化披露(每 site×key 一次)** —— 首次在某条腿上丢掉某个 key 时一条 `info`,之后同 key 只走计数。
|
|
16
|
-
* 不是「每 site 一次」:一个**新**的密钥形 key 后来才出现(部署加了一个凭证)时,那才是最该看见的一行。
|
|
17
|
-
* 值永不进任何一件(core 的 finding 本身就不携带值)。
|
|
18
|
-
*
|
|
19
|
-
* ## 为什么是进程级自由函数而不是注入
|
|
20
|
-
* 与 `recordFailOpen` 同因:两个调用点都在**没有 logger/metrics 句柄**的深处
|
|
21
|
-
* (`plugins/remote-env-host.ts` 的 `mergeEnv` 全文零观测通道;`project-memory.ts` 的 `git()` 是个自由函数)。
|
|
22
|
-
* 装配层用 {@link installSecretEnvScrubRecorder} 把句柄接上,装配之前命中的次数不会丢(计数照常累加,
|
|
23
|
-
* install 时一次性补进 metrics),披露行则在下一次命中时补发(未成功披露的 key 不进 `disclosed` 集)。
|
|
24
|
-
*/
|
|
25
1
|
import { summarizeRedactions } from "@sema-agent/core";
|
|
26
|
-
/** 登记过的剥除现场。闭集:未登记的 site 传不进 {@link recordSecretEnvScrub}(编译期拒,同 FAIL_OPEN_TAGS 纪律)。 */
|
|
27
2
|
export const SECRET_ENV_SCRUB_SITES = {
|
|
28
3
|
"server.remote-env-host.inherit-scrub": "模型可驱动的 host shell 继承 env(`inheritEnv:\"scrub\"` 默认腿)——这条腿上的一次泄漏等于把编排器的脑钥/仓库令牌交给模型选的命令",
|
|
29
4
|
"server.project-memory.git-subprocess": "project-memory 读仓叙事时的 git 子进程 env —— 仓内的钩子/凭证助手会以本进程身份运行",
|
|
30
5
|
};
|
|
31
|
-
/** 计数器名。 */
|
|
32
6
|
const SCRUB_COUNTER = "secret_env_scrubbed_total";
|
|
33
|
-
/** 已披露集的上限。key 集在一个进程里实际上是常量(恒扫同一份 `process.env`),这个帽只是防病态输入
|
|
34
|
-
* 把集合撑爆;越帽之后只停止**新增披露**,计数照常(缺席的是行,不是账)。 */
|
|
35
7
|
const DISCLOSED_CAP = 512;
|
|
36
|
-
/** 桶键与披露键的分隔符。用 `=` 是有据的:POSIX 环境变量名里唯二不可能出现的字节就是 `=` 与 NUL
|
|
37
|
-
* (NUL 不写进源码 —— 裸控制字节会让 grep 对整个文件失明,本仓有机械门盯它)。用空格/冒号分隔则会让
|
|
38
|
-
* `A B`+`C` 与 `A`+`B C` 撞成同一个键 —— 标签串台 = 遥测说谎。 */
|
|
39
8
|
const SEP = "=";
|
|
40
9
|
let sink = {};
|
|
41
|
-
/** `site=kind=confidence` → 累计次数(逐次,进程级)。 */
|
|
42
10
|
let counts = new Map();
|
|
43
|
-
/** 已经推进 metrics 的部分 —— 差额即欠账,install 时一次性补。 */
|
|
44
11
|
let pushed = new Map();
|
|
45
|
-
/** `site=key` —— 已经出过披露行的现场×键。 */
|
|
46
12
|
let disclosed = new Set();
|
|
47
13
|
function bucketKey(site, f) {
|
|
48
14
|
return `${site}${SEP}${f.kind}${SEP}${f.confidence}`;
|
|
49
15
|
}
|
|
50
|
-
/** 把 counts 与 pushed 的差额推进 metrics。metrics 缺席时什么也不做(差额留着,下次补)。 */
|
|
51
16
|
function drainToMetrics() {
|
|
52
17
|
const m = sink.metrics;
|
|
53
18
|
if (m === undefined)
|
|
@@ -59,17 +24,12 @@ function drainToMetrics() {
|
|
|
59
24
|
const [site, kind, confidence] = k.split(SEP);
|
|
60
25
|
try {
|
|
61
26
|
m.inc(SCRUB_COUNTER, { site: site, kind: kind, confidence: confidence }, n - already);
|
|
62
|
-
pushed.set(k, n);
|
|
27
|
+
pushed.set(k, n);
|
|
63
28
|
}
|
|
64
29
|
catch {
|
|
65
|
-
/* 下一次命中/下一次装配再补 */
|
|
66
30
|
}
|
|
67
31
|
}
|
|
68
32
|
}
|
|
69
|
-
/**
|
|
70
|
-
* 记一次剥除。**总不抛** —— 观测本身绝不能变成故障(同 `recordFailOpen` / reapers 的判据)。
|
|
71
|
-
* `findings` 为空(这一次没丢任何 key)时什么也不做:那不是事件,是常态。
|
|
72
|
-
*/
|
|
73
33
|
export function recordSecretEnvScrub(site, findings) {
|
|
74
34
|
if (findings.length === 0)
|
|
75
35
|
return;
|
|
@@ -81,13 +41,7 @@ export function recordSecretEnvScrub(site, findings) {
|
|
|
81
41
|
drainToMetrics();
|
|
82
42
|
const logger = sink.logger;
|
|
83
43
|
if (logger === undefined)
|
|
84
|
-
return;
|
|
85
|
-
// 🔴 帽是**对这一次的批也生效**的(codex 复审 round2 medium 真修):先按剩余容量截断,再用截断后的
|
|
86
|
-
// 那一批去发行与登记。两条都靠它:
|
|
87
|
-
// ① 帽满 ⇒ 停止披露,而不是「喊了但记不下」—— 后者会把「每 site×key 一次」在 key 最多的时候
|
|
88
|
-
// 反转成「每次调用一行」(mergeEnv 是逐命令调用的,那是刷屏);
|
|
89
|
-
// ② 单批越界 ⇒ 也得截断 —— 只在批**之前**判帽,一次 513 条的病态 env 会一次性把 513 条全发出去
|
|
90
|
-
// (一行超大日志)并全部留驻,帽等于没有。计数不受截断影响:账永远是全的,缺席的只是行。
|
|
44
|
+
return;
|
|
91
45
|
const room = DISCLOSED_CAP - disclosed.size;
|
|
92
46
|
if (room <= 0)
|
|
93
47
|
return;
|
|
@@ -97,34 +51,26 @@ export function recordSecretEnvScrub(site, findings) {
|
|
|
97
51
|
logger.info("secret_env_scrubbed", {
|
|
98
52
|
site,
|
|
99
53
|
note: SECRET_ENV_SCRUB_SITES[site],
|
|
100
|
-
// 只报**本次新见**的 key(值永不携带 —— core 的 finding 按契约只带键名/规则/置信度/来源)。
|
|
101
54
|
keys: fresh.map((f) => f.key),
|
|
102
|
-
// 一行人读摘要走 core 的 `summarizeRedactions`(SecretEnvFinding 满足 SummarizableFinding),
|
|
103
|
-
// 不在本仓另写一份分组渲染 —— 那正是本票要消灭的那种孪生。
|
|
104
55
|
summary: summarizeRedactions(fresh),
|
|
105
56
|
});
|
|
106
57
|
for (const f of fresh)
|
|
107
58
|
disclosed.add(`${site}${SEP}${f.key}`);
|
|
108
59
|
}
|
|
109
60
|
catch {
|
|
110
|
-
/* 观测绝不抛 */
|
|
111
61
|
}
|
|
112
62
|
}
|
|
113
|
-
/** 装配层把 logger/metrics 接上(main.ts,与 `installFailOpenRecorder` 同一处)。装配前累计的次数在此补进 metrics。 */
|
|
114
63
|
export function installSecretEnvScrubRecorder(deps) {
|
|
115
64
|
sink = { ...deps };
|
|
116
65
|
deps.metrics?.counter(SCRUB_COUNTER, "secret-pattern env entries dropped before spawning a child process, by site/rule/confidence (core scrubSecretEnv findings)");
|
|
117
66
|
drainToMetrics();
|
|
118
67
|
}
|
|
119
|
-
/** 已留驻的「现场×键」条数(测试/诊断读面)。恒 ≤ {@link DISCLOSED_CAP}。 */
|
|
120
68
|
export function secretEnvScrubDisclosedSize() {
|
|
121
69
|
return disclosed.size;
|
|
122
70
|
}
|
|
123
|
-
/** 本进程各桶的累计次数(测试/诊断读面)。 */
|
|
124
71
|
export function secretEnvScrubCounts() {
|
|
125
72
|
return new Map(counts);
|
|
126
73
|
}
|
|
127
|
-
/** 测试用重置(进程级状态在同一个 vitest worker 里跨用例存活)。 */
|
|
128
74
|
export function resetSecretEnvScrubRecorderForTest(deps = {}) {
|
|
129
75
|
counts = new Map();
|
|
130
76
|
pushed = new Map();
|