@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,24 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 把 `onClose` 接到这条连接的两个关闭信号上,并保证**至多执行一次**。
|
|
3
|
-
*
|
|
4
|
-
* 连接在调用本函数**之前**就已经断了(接线排在慢查询之后的那种形),`onClose` 会在本函数内
|
|
5
|
-
* **同步**跑一次 —— 调用方据此可以假定:本函数返回后,「已断连」这件事一定已经被通知过了。
|
|
6
|
-
* 于是 `closed` 旗在泵进入循环前就已翻好,不需要每条腿再自己判一次 `res.destroyed`。
|
|
7
|
-
*
|
|
8
|
-
* @param onClose 本连接的全部清理动作(置 `closed` 旗、`clearInterval`、迭代器 `return()` 等)。
|
|
9
|
-
*/
|
|
10
1
|
export function bindSseLifecycle(req, res, onClose) {
|
|
11
2
|
let fired = false;
|
|
12
3
|
const once = () => {
|
|
13
4
|
if (fired)
|
|
14
5
|
return;
|
|
15
|
-
fired = true;
|
|
6
|
+
fired = true;
|
|
16
7
|
onClose();
|
|
17
8
|
};
|
|
18
|
-
res.on("close", once);
|
|
9
|
+
res.on("close", once);
|
|
19
10
|
req.on("close", once);
|
|
20
|
-
// 回看:接线之前就断掉的连接,两个事件都已烧完,只有状态位还留着证据(理由与判据选取见顶注)。
|
|
21
|
-
// 先注册再回看,不是反过来 —— 反过来会在「回看与注册之间」新开一个同样的窗。
|
|
22
11
|
if (res.destroyed || res.closed)
|
|
23
12
|
once();
|
|
24
13
|
}
|
package/dist/http/sse-log.js
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import { bindSseLifecycle } from "./sse-lifecycle.js";
|
|
2
2
|
import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "./send.js";
|
|
3
|
-
/** #151 车3 §5.3:preamble 的**有界**等待上限。挂起的 store 不许拖住开流——开流是壳的主路径,
|
|
4
|
-
* 而 preamble 只是一个锦上添花的对账基准。超时即按「无卡集基准」继续(与今天等同)。 */
|
|
5
3
|
export const SSE_PREAMBLE_TIMEOUT_MS = 2_000;
|
|
6
|
-
/**
|
|
7
|
-
* 跑一次 preamble,**有界 + fail-open**(#151 车3 §5.3)。
|
|
8
|
-
*
|
|
9
|
-
* 🔴 只 catch throw 是不够的:store **挂起**(不抛)会把整条开流一起拖住。所以这里是 `Promise.race`
|
|
10
|
-
* 而不是 try/catch —— 两种失败形(抛 / 不返回)各自有一条出路,都落到「零帧 + 一次 warn + 继续开流」。
|
|
11
|
-
*
|
|
12
|
-
* 🔴 #151 车3 刀 3b(§14.2 遗留记账兑现):超时**同时 abort** 一个交给 `preamble` 的 `AbortSignal`。
|
|
13
|
-
* 此前只是「丢结果」,原 promise 仍在飞 —— DB 挂死时的重连风暴会一轮轮往连接池里堆等待者。现在读口
|
|
14
|
-
* 拿到 abort 会当场拒绝,调用链不再累积;库那侧的那条查询仍会跑完(驱动无取消 seam,残留边界见
|
|
15
|
-
* `ApprovalAskStore.listPendingBySession` 的注)。丢结果这条语义不变:preamble 只读,丢弃零副作用。
|
|
16
|
-
*
|
|
17
|
-
* 导出给 `routes/tasks.ts` 的 sync 腿直接用 —— 那条腿不走 `streamSseLog`(它不是账本 tail),但
|
|
18
|
-
* 「有界 + fail-open」这条判据必须是**同一份实现**,不能各腿各写一遍。
|
|
19
|
-
*/
|
|
20
4
|
export async function collectSsePreamble(preamble, onFailure, timeoutMs = SSE_PREAMBLE_TIMEOUT_MS) {
|
|
21
5
|
const timedOut = Symbol("preamble-timeout");
|
|
22
6
|
let timer;
|
|
@@ -26,10 +10,10 @@ export async function collectSsePreamble(preamble, onFailure, timeoutMs = SSE_PR
|
|
|
26
10
|
preamble(ac.signal),
|
|
27
11
|
new Promise((resolve) => {
|
|
28
12
|
timer = setTimeout(() => {
|
|
29
|
-
ac.abort();
|
|
13
|
+
ac.abort();
|
|
30
14
|
resolve(timedOut);
|
|
31
15
|
}, timeoutMs);
|
|
32
|
-
timer.unref?.();
|
|
16
|
+
timer.unref?.();
|
|
33
17
|
}),
|
|
34
18
|
]);
|
|
35
19
|
if (raced === timedOut) {
|
|
@@ -47,14 +31,10 @@ export async function collectSsePreamble(preamble, onFailure, timeoutMs = SSE_PR
|
|
|
47
31
|
clearTimeout(timer);
|
|
48
32
|
}
|
|
49
33
|
}
|
|
50
|
-
/** The shared resumable SSE pump (P2.8) — provider supplies the log-specific differences; the liveness/
|
|
51
|
-
* heartbeat/416/terminal-re-fetch/15-min-cap logic is identical for task_run + image_bake. */
|
|
52
34
|
export async function streamSseLog(req, res, provider, id, staleMs) {
|
|
53
35
|
const lastId = Number(req.headers["last-event-id"] ?? new URL(req.url ?? "", "http://x").searchParams.get("from") ?? 0);
|
|
54
36
|
let from = Number.isFinite(lastId) ? lastId : 0;
|
|
55
37
|
if (from > 0) {
|
|
56
|
-
// Resume point evicted past retention → 416 so the client re-syncs, instead of a silent incomplete stream
|
|
57
|
-
// (parity with streamTaskTrace; council). Inert until retention evicts (retainedFrom stays 1 with no TTL).
|
|
58
38
|
const retainedFrom = await provider.retainedFrom(id);
|
|
59
39
|
if (retainedFrom > from + 1) {
|
|
60
40
|
sendError(res, 416, "limit.retention_evicted", "resume point evicted past retention", { retainedFrom });
|
|
@@ -62,26 +42,13 @@ export async function streamSseLog(req, res, provider, id, staleMs) {
|
|
|
62
42
|
}
|
|
63
43
|
}
|
|
64
44
|
sseHeaders(res);
|
|
65
|
-
// 🔴 断连接线必须**先于** preamble 的等待窗接上(codex 复审 2026-08-06 的 medium):Node 的 `'close'`
|
|
66
|
-
// 只发一次,在等待窗里断连的客户端其 close 事件会在监听器注册之前烧掉 ⇒ `closed` 恒 false ⇒ 泵照常
|
|
67
|
-
// 进轮询循环,对着已死的 res 反复 getEvents/statusOf 直到终态/stale/15 分钟帽。本行原本紧跟
|
|
68
|
-
// sseHeaders,是 preamble 插进来才多出这个窗 —— 所以搬接线、不搬 preamble。
|
|
69
|
-
//
|
|
70
|
-
// design/245 件1:接线本身收到共享助手(req+res 双挂 + 一次性闸)。此前只挂 `req`,而请求流被读干
|
|
71
|
-
// 之后 `req` 的 close 不再补发 —— 本泵是 `task_run` 与 `image_bake` 两条日志共用的 seam,调用方
|
|
72
|
-
// 是谁、在它之前有没有人碰过请求流,不该由本泵去赌。理由全文在 `sse-lifecycle.ts` 顶注。
|
|
73
45
|
let closed = false;
|
|
74
46
|
bindSseLifecycle(req, res, () => {
|
|
75
47
|
closed = true;
|
|
76
48
|
});
|
|
77
|
-
|
|
78
|
-
// 见 collectSsePreamble 顶注;`id:` 行故意不写(非账本行,不推进 Last-Event-ID 游标)。
|
|
79
|
-
const preamble = provider.preamble?.bind(provider); // bind:接口按方法签名声明,实现方允许用 `this`
|
|
49
|
+
const preamble = provider.preamble?.bind(provider);
|
|
80
50
|
if (preamble) {
|
|
81
51
|
const frames = await collectSsePreamble(preamble, (reason, err) => provider.onPreambleFailure?.(reason, err));
|
|
82
|
-
// 等待窗里断连了就别再写(`closed` 由上面的 close 监听器置;`writableEnded`/`destroyed` 是
|
|
83
|
-
// 第二道 —— codex 复审第二轮 medium 当时指出 res 侧的 close 面本 helper 没听,那半条已由
|
|
84
|
-
// design/245 件1 的 `bindSseLifecycle` 补上;这道判据保留为第二重,与 routes/tasks.ts 心跳处逐字同源)。
|
|
85
52
|
if (!closed && !res.writableEnded && !res.destroyed) {
|
|
86
53
|
for (const f of frames)
|
|
87
54
|
res.write(`event: ${f.event}\ndata: ${JSON.stringify(f.data)}\n\n`);
|
|
@@ -97,20 +64,11 @@ export async function streamSseLog(req, res, provider, id, staleMs) {
|
|
|
97
64
|
}
|
|
98
65
|
};
|
|
99
66
|
while (!closed) {
|
|
100
|
-
// Independent reads — issue concurrently to shave the SSE poll latency. The terminal-event gap is
|
|
101
|
-
// still covered by the extra getEvents below (a `done` landing between these two is re-fetched).
|
|
102
67
|
const [events, row] = await Promise.all([provider.getEvents(id, from), provider.statusOf(id)]);
|
|
103
68
|
write(events);
|
|
104
69
|
if (!row)
|
|
105
70
|
break;
|
|
106
71
|
if (row.status !== "running") {
|
|
107
|
-
// Terminal. The `done`/`failed` event is appended BEFORE the coarse status flips terminal (runs.ts +
|
|
108
|
-
// the bake terminal-write), so it can land in the gap between the getEvents and statusOf above — fetch
|
|
109
|
-
// ONCE MORE so the stream never ends before delivering the terminal event. (The ONE exception is E12's
|
|
110
|
-
// post-completion `suggestions` event, appended AFTER setTerminal once the fire-and-forget pass settles —
|
|
111
|
-
// it is intentionally NOT delivered on this live tail; a poller reads it from GET /v1/runs/:id, which is the
|
|
112
|
-
// `await result() THEN await suggestions()` contract. Releasing the session lock at `done` beats blocking it
|
|
113
|
-
// on the suggestion LLM pass just to stream a UI nicety.)
|
|
114
72
|
write(await provider.getEvents(id, from));
|
|
115
73
|
break;
|
|
116
74
|
}
|
|
@@ -121,13 +79,10 @@ export async function streamSseLog(req, res, provider, id, staleMs) {
|
|
|
121
79
|
break;
|
|
122
80
|
}
|
|
123
81
|
if (Date.now() - start > SSE_MAX_STREAM_MS) {
|
|
124
|
-
// BL-16: cap reached but the run continues — emit a terminal reconnect signal (not a silent close, which
|
|
125
|
-
// a client couldn't distinguish from a completed run). Distinct from the `failed`/`done` run events.
|
|
126
82
|
res.write(`event: error\ndata: ${JSON.stringify({ type: "error", errorCode: "STREAM_MAX_DURATION", message: "stream reached its 15-minute cap — reconnect with Last-Event-ID to continue (the run is still active)" })}\n\n`);
|
|
127
83
|
break;
|
|
128
84
|
}
|
|
129
85
|
if (events.length === 0) {
|
|
130
|
-
// Heartbeat during a long idle tail (e.g. a debate phase) so the relay's idle timeout doesn't fire.
|
|
131
86
|
if (Date.now() - lastBeat > SSE_HEARTBEAT_IDLE_MS) {
|
|
132
87
|
res.write(`event: heartbeat\ndata: {}\n\n`);
|
|
133
88
|
lastBeat = Date.now();
|
package/dist/http/tar.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 极小 ustar 写器(workspace 浏览面 #3 的 archive 端点用,[1882]/[1894])。零依赖(仓内 no-SDK 姿势,
|
|
3
|
-
* 同 s3-presign/OTLP 先例)。只写 regular file 条目:512B ustar 头(prefix 155/name 100 拆分)+
|
|
4
|
-
* 内容 512 对齐 + 两块零尾。**不做** PAX/GNU 长名扩展——快照 relPath 超过 prefix+name 可承载
|
|
5
|
-
* (>255 或不可在 "/" 处拆)属病态路径,调用方按 fail-loud 处理(archive 面 422 点名)。
|
|
6
|
-
*/
|
|
7
1
|
const BLOCK = 512;
|
|
8
|
-
/** 八进制右对齐 NUL 结尾字段(ustar 数字字段形)。 */
|
|
9
2
|
function octal(n, width) {
|
|
10
3
|
const s = n.toString(8).padStart(width - 1, "0");
|
|
11
4
|
return Buffer.from(s + "\0", "ascii");
|
|
12
5
|
}
|
|
13
|
-
/** 把 relPath 拆成 ustar 的 (prefix ≤155, name ≤100);不可拆 ⇒ null(调用方 fail-loud)。 */
|
|
14
6
|
export function splitTarPath(relPath) {
|
|
15
7
|
const bytes = Buffer.byteLength(relPath, "utf8");
|
|
16
8
|
if (bytes <= 100)
|
|
17
9
|
return { prefix: "", name: relPath };
|
|
18
10
|
if (bytes > 255)
|
|
19
11
|
return null;
|
|
20
|
-
// 从右往左找一个 "/",使 name ≤100 且 prefix ≤155(按字节算——多字节字符路径保守处理)。
|
|
21
12
|
for (let i = relPath.length - 1; i > 0; i--) {
|
|
22
13
|
if (relPath[i] !== "/")
|
|
23
14
|
continue;
|
|
@@ -28,11 +19,6 @@ export function splitTarPath(relPath) {
|
|
|
28
19
|
}
|
|
29
20
|
return null;
|
|
30
21
|
}
|
|
31
|
-
/** 一个 regular-file 条目的 512B ustar 头。`mtimeSec` 由调用方给(快照无 per-file mtime ⇒ 统一用
|
|
32
|
-
* 快照时刻/0——确定性输出,同輸入同字节)。 */
|
|
33
|
-
/** ustar 的 size 字段是 12 字节八进制(11 位数字 + NUL)⇒ 上限 8 GiB−1。超了写进去会溢出到相邻
|
|
34
|
-
* 字段/丢末位并**静默**产出坏头(复审 F8/A-LOW 实测:2^36 解析回来是值/8)。fail-loud,与
|
|
35
|
-
* splitTarPath 的病态路径同姿势。当前配置下不可达(快照总字节帽 256MiB),属防御纵深。 */
|
|
36
22
|
const USTAR_MAX_SIZE = 8 * 1024 * 1024 * 1024 - 1;
|
|
37
23
|
export function tarHeader(relPath, sizeBytes, mtimeSec) {
|
|
38
24
|
if (!Number.isFinite(sizeBytes) || sizeBytes < 0 || sizeBytes > USTAR_MAX_SIZE) {
|
|
@@ -43,13 +29,13 @@ export function tarHeader(relPath, sizeBytes, mtimeSec) {
|
|
|
43
29
|
throw new Error(`tar: path not representable in ustar (must be ≤255 bytes and splittable at "/"): ${relPath}`);
|
|
44
30
|
const h = Buffer.alloc(BLOCK, 0);
|
|
45
31
|
h.write(split.name, 0, 100, "utf8");
|
|
46
|
-
octal(0o644, 8).copy(h, 100);
|
|
47
|
-
octal(0, 8).copy(h, 108);
|
|
48
|
-
octal(0, 8).copy(h, 116);
|
|
32
|
+
octal(0o644, 8).copy(h, 100);
|
|
33
|
+
octal(0, 8).copy(h, 108);
|
|
34
|
+
octal(0, 8).copy(h, 116);
|
|
49
35
|
octal(sizeBytes, 12).copy(h, 124);
|
|
50
36
|
octal(Math.max(0, Math.floor(mtimeSec)), 12).copy(h, 136);
|
|
51
|
-
h.fill(0x20, 148, 156);
|
|
52
|
-
h.write("0", 156, 1, "ascii");
|
|
37
|
+
h.fill(0x20, 148, 156);
|
|
38
|
+
h.write("0", 156, 1, "ascii");
|
|
53
39
|
h.write("ustar\0", 257, 6, "ascii");
|
|
54
40
|
h.write("00", 263, 2, "ascii");
|
|
55
41
|
h.write(split.prefix, 345, 155, "utf8");
|
|
@@ -59,11 +45,9 @@ export function tarHeader(relPath, sizeBytes, mtimeSec) {
|
|
|
59
45
|
Buffer.from(sum.toString(8).padStart(6, "0") + "\0 ", "ascii").copy(h, 148);
|
|
60
46
|
return h;
|
|
61
47
|
}
|
|
62
|
-
/** 内容块的 512 对齐尾垫(0 长内容=0 垫)。 */
|
|
63
48
|
export function tarPadding(sizeBytes) {
|
|
64
49
|
const rem = sizeBytes % BLOCK;
|
|
65
50
|
return Buffer.alloc(rem === 0 ? 0 : BLOCK - rem, 0);
|
|
66
51
|
}
|
|
67
|
-
/** 归档终止:两块 512 零。 */
|
|
68
52
|
export const TAR_END = Buffer.alloc(BLOCK * 2, 0);
|
|
69
53
|
//# sourceMappingURL=tar.js.map
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/** Verification rounds requested by a caller, clamped to a safe [1,5] (default 2). */
|
|
2
1
|
export function clampVerifyRounds(v) {
|
|
3
2
|
const n = typeof v === "number" && Number.isFinite(v) ? Math.floor(v) : 2;
|
|
4
3
|
return Math.min(5, Math.max(1, n));
|
|
5
4
|
}
|
|
6
|
-
/** `undefined` when `body.verify` is not `true` (the caller didn't opt into the gate); otherwise the clamped
|
|
7
|
-
* rounds + (if `maxCostUsd` is a positive number) the same USD→micro-USD cost ceiling the cascade lane uses.
|
|
8
|
-
* Same expression that lived inline at all four verify/resume call sites in http/server.ts (fresh-submit,
|
|
9
|
-
* resumeCheckpoint, resumePreempted, resumePlanReview) — collected here VERBATIM (design/97 R9). */
|
|
10
5
|
export function verifyRoundsFromBody(body) {
|
|
11
6
|
return body.verify === true
|
|
12
7
|
? {
|
package/dist/http/wire-gate.js
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* D-0 (design/80 §1): the single source of truth for the `CheckpointGate.kind` values the service emits on the
|
|
3
|
-
* wire. The suspend path surfaces the gate VERBATIM (`append("suspended", { gate })`, server.ts), and the
|
|
4
|
-
* agent-client SDK renders these as typed arms (an unknown kind falls through the SDK's open `(string & {})`
|
|
5
|
-
* arm and renders generically). This explicit mirror exists so the compile-time guards below + the contract
|
|
6
|
-
* test (`test/wire-gate-contract.test.ts`) keep the wire/SDK in lockstep with core — the guarantee the SDK's
|
|
7
|
-
* own `spec.test.ts` cannot give (it hardcodes the kinds without importing core, so it would stay GREEN the
|
|
8
|
-
* moment core adds a 6th kind; design/80 §3 #10 was therefore unenforced until this).
|
|
9
|
-
*/
|
|
10
1
|
export const WIRE_GATE_KINDS = ["human", "irreversible_ask", "resource_limit", "needs_review", "task_done", "plan_review"];
|
|
11
2
|
const _wireCoversCore = true;
|
|
12
3
|
const _noStaleWireKind = true;
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* design/158 A9 seam —— workspace 文件面的内容类型/二进制判别,从 `http/server.ts` 整段搬出的原文。
|
|
3
|
-
* routes/sessions.ts 的 workspace 腿要用它,所以住在 http/ 叶子里(routes/* 不可值 import server.ts)。
|
|
4
|
-
*/
|
|
5
|
-
/** workspace file 面的 content-type(#3,[1894]③):保守扩展名表——只收「浏览器渲染无歧义且
|
|
6
|
-
* 无主动内容」的类型,其余一律 octet-stream(nosniff 恒带;html/svg 等可执行内容**故意**不进表,
|
|
7
|
-
* agent 产物=不可信,按下载处理)。 */
|
|
8
1
|
export function workspaceContentType(relPath) {
|
|
9
|
-
// 🔴 复审 A-LOW:先确认**真有** `.` 再取扩展名——`lastIndexOf` 返回 -1 时 `slice(0)` 会把整个
|
|
10
|
-
// basename 当扩展名,于是一个名为 `png`/`pdf` 的任意内容文件会命中保守表按 image/png 发出。
|
|
11
2
|
const base = relPath.slice(relPath.lastIndexOf("/") + 1);
|
|
12
3
|
const dot = base.lastIndexOf(".");
|
|
13
4
|
const ext = dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
|
|
@@ -19,7 +10,6 @@ export function workspaceContentType(relPath) {
|
|
|
19
10
|
};
|
|
20
11
|
return TABLE[ext] ?? "application/octet-stream";
|
|
21
12
|
}
|
|
22
|
-
/** 二进制判别([1894]③):首 8KiB 含 NUL 即二进制(git 同判据——快、稳、无误伤 UTF-8)。 */
|
|
23
13
|
export function looksBinary(bytes) {
|
|
24
14
|
const n = Math.min(bytes.byteLength, 8192);
|
|
25
15
|
for (let i = 0; i < n; i++)
|
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bake input WHITELIST + vetted-argv builder (IMAGE-API-DESIGN.md §P2.4 — the anti-RCE core).
|
|
3
|
-
*
|
|
4
|
-
* bands/profile/baseRef/bump compose a Dockerfile + a `FROM` + a `docker build` = arbitrary code on the build
|
|
5
|
-
* host. So EVERY field is validated SERVER-SIDE against a CLOSED SET *before* an argv is built; off-list → an
|
|
6
|
-
* error, never coerced. The runner only ever sees the VETTED argv this module emits (never operator free-text)
|
|
7
|
-
* and only runs `build.sh`, which composes Dockerfiles from the in-repo `bands/` via `gen-dockerfile.sh` — there
|
|
8
|
-
* is no path here to inject a raw Dockerfile or a shell command.
|
|
9
|
-
*
|
|
10
|
-
* Set membership is the gate (NOT regex): `^[a-z0-9-]+$` is belt-only — `python; rm -rf /` ∉ the frozen set →
|
|
11
|
-
* rejected, and the argv is a RAW vetted VECTOR the runner array-spawns (each element in its own argv slot, no
|
|
12
|
-
* shell), never concatenated into a command string.
|
|
13
|
-
*
|
|
14
|
-
* The frozen catalogs below MIRROR the recipe source of truth (`gen-dockerfile.sh` `profile_bands()` + the
|
|
15
|
-
* `bands/` dir). A DRIFT test (test/image-bake-validate.test.ts) asserts they still match — a silently-drifted
|
|
16
|
-
* list would either reject a valid profile or admit a removed band. Keep them in lockstep with the recipes.
|
|
17
|
-
*/
|
|
18
1
|
import { SHA256_DIGEST_RE as DIGEST_RE } from "../digest-form.js";
|
|
19
|
-
/**
|
|
20
|
-
* FROZEN profile catalog — mirrored from `e2b-template/dev-sandbox/gen-dockerfile.sh` `profile_bands()`.
|
|
21
|
-
* (db / browser-nodriver are opt-in overlays, NOT in `code-full` — `code-full-db` adds db; per design §4.)
|
|
22
|
-
*/
|
|
23
2
|
export const FROZEN_PROFILES = [
|
|
24
3
|
"code-python",
|
|
25
4
|
"code-node",
|
|
@@ -41,7 +20,6 @@ export const FROZEN_PROFILES = [
|
|
|
41
20
|
"code-full",
|
|
42
21
|
"code-full-db",
|
|
43
22
|
];
|
|
44
|
-
/** FROZEN band set — mirrored from the `e2b-template/dev-sandbox/bands/` directory (one dir per band). */
|
|
45
23
|
export const FROZEN_BANDS = [
|
|
46
24
|
"python",
|
|
47
25
|
"node",
|
|
@@ -63,11 +41,6 @@ export const FROZEN_BANDS = [
|
|
|
63
41
|
"browser-nodriver",
|
|
64
42
|
"db",
|
|
65
43
|
];
|
|
66
|
-
/**
|
|
67
|
-
* FROZEN bump-tool set — the toolchain identifiers a `--bump <tool>=<ver>` may target. Conservative union of the
|
|
68
|
-
* version-overridable toolchains the bands accept (`<TOOL>_VERSION` env, build.sh forwards `--bump tool=ver`).
|
|
69
|
-
* Set-membership is the gate; the value is range-checked by VER_RE. Off-list tool → 400 (never coerced).
|
|
70
|
-
*/
|
|
71
44
|
export const FROZEN_BUMP_TOOLS = [
|
|
72
45
|
"node",
|
|
73
46
|
"python",
|
|
@@ -86,28 +59,14 @@ export const FROZEN_BUMP_TOOLS = [
|
|
|
86
59
|
const PROFILE_SET = new Set(FROZEN_PROFILES);
|
|
87
60
|
const BAND_SET = new Set(FROZEN_BANDS);
|
|
88
61
|
const BUMP_TOOL_SET = new Set(FROZEN_BUMP_TOOLS);
|
|
89
|
-
/** Bump value range (P2.4d): printable version-ish tokens only, ≤40 chars — folds into the lockhash. */
|
|
90
62
|
export const VER_RE = /^[A-Za-z0-9._-]{1,40}$/;
|
|
91
|
-
/** Belt-only band shape (set-membership is the real gate); a defense-in-depth reject of structurally weird ids. */
|
|
92
63
|
export const BAND_SHAPE_RE = /^[a-z0-9-]+$/;
|
|
93
64
|
function reject(status, error, conflictBands) {
|
|
94
65
|
return conflictBands ? { ok: false, status, error, conflictBands } : { ok: false, status, error };
|
|
95
66
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Validate + normalize a `POST /v1/images/bakes` body into a vetted argv, or a typed rejection.
|
|
98
|
-
*
|
|
99
|
-
* Order is deliberate: profile XOR bands → bands set-membership (dupes/empties/shape) → bump set+range →
|
|
100
|
-
* baseRef shape (the index allow-list check is done by the caller via `baseRefAllowed`, async) → build the
|
|
101
|
-
* raw vetted argv. The output repo is server-fixed (`sema-images/<profile>`); the caller CANNOT set
|
|
102
|
-
* `--tag` (build.sh auto-computes the immutable tag — kills namespace-squat) nor an arbitrary `FROM`.
|
|
103
|
-
*
|
|
104
|
-
* `baseRefAllowed` is checked HERE for shape only; the digest-in-index allow-list is the caller's async step
|
|
105
|
-
* (it owns the TiDB index). A present-but-malformed `baseRef` is rejected before any DB hit.
|
|
106
|
-
*/
|
|
107
67
|
export function validateBake(body) {
|
|
108
68
|
const hasProfile = body.profile !== undefined && body.profile !== null && body.profile !== "";
|
|
109
69
|
const hasBands = body.bands !== undefined && body.bands !== null;
|
|
110
|
-
// profile XOR bands — exactly one required.
|
|
111
70
|
if (hasProfile && hasBands)
|
|
112
71
|
return reject(400, "provide profile OR bands, not both");
|
|
113
72
|
if (!hasProfile && !hasBands)
|
|
@@ -126,7 +85,6 @@ export function validateBake(body) {
|
|
|
126
85
|
return reject(400, "bands must be an array of band ids");
|
|
127
86
|
if (body.bands.length === 0)
|
|
128
87
|
return reject(400, "bands must be a non-empty array");
|
|
129
|
-
// Cap length at the all-bands count — a longer list can only be dupes/garbage.
|
|
130
88
|
if (body.bands.length > FROZEN_BANDS.length)
|
|
131
89
|
return reject(400, `at most ${FROZEN_BANDS.length} bands`);
|
|
132
90
|
const seen = new Set();
|
|
@@ -139,21 +97,18 @@ export function validateBake(body) {
|
|
|
139
97
|
if (seen.has(raw))
|
|
140
98
|
return reject(400, `duplicate band '${raw}'`);
|
|
141
99
|
seen.add(raw);
|
|
142
|
-
// "custom"/tenant bands are a P4 affordance → 501 (not a 400) so the UI distinguishes "not yet" from "bad".
|
|
143
100
|
if (!BAND_SET.has(raw))
|
|
144
101
|
offList.push(raw);
|
|
145
102
|
}
|
|
146
103
|
if (offList.length > 0) {
|
|
147
|
-
// An off-list band that is the reserved tenant marker routes to P4; any other off-list id is an unknown band.
|
|
148
104
|
if (offList.includes("custom")) {
|
|
149
105
|
return reject(501, "custom/tenant bake is P4");
|
|
150
106
|
}
|
|
151
107
|
return reject(400, `unknown band(s): ${offList.join(", ")}`, offList);
|
|
152
108
|
}
|
|
153
109
|
bands = body.bands;
|
|
154
|
-
profile = "custom";
|
|
110
|
+
profile = "custom";
|
|
155
111
|
}
|
|
156
|
-
// bump — {tool:ver}: tool ∈ FROZEN_BUMP_TOOLS, ver ∈ VER_RE. Off-list/range → 400.
|
|
157
112
|
let bump = null;
|
|
158
113
|
if (body.bump !== undefined && body.bump !== null) {
|
|
159
114
|
if (typeof body.bump !== "object" || Array.isArray(body.bump)) {
|
|
@@ -170,8 +125,6 @@ export function validateBake(body) {
|
|
|
170
125
|
}
|
|
171
126
|
bump = Object.keys(out).length > 0 ? out : null;
|
|
172
127
|
}
|
|
173
|
-
// baseRef — caller CANNOT pass an arbitrary FROM. Shape-check here (the index allow-list is the caller's async
|
|
174
|
-
// step). Default = the pinned current base, injected by the caller when baseRef is absent.
|
|
175
128
|
let baseRef = null;
|
|
176
129
|
if (body.baseRef !== undefined && body.baseRef !== null && body.baseRef !== "") {
|
|
177
130
|
if (typeof body.baseRef !== "string" || !DIGEST_RE.test(body.baseRef)) {
|
|
@@ -182,7 +135,6 @@ export function validateBake(body) {
|
|
|
182
135
|
const push = body.push === true;
|
|
183
136
|
const dryRun = body.dryRun === true;
|
|
184
137
|
const logs = body.logs === true;
|
|
185
|
-
// pkgSource — RFC B4 closed set (cn|global); off-list → 400, never coerced (same discipline as every field).
|
|
186
138
|
let pkgSource = null;
|
|
187
139
|
if (body.pkgSource !== undefined && body.pkgSource !== null && body.pkgSource !== "") {
|
|
188
140
|
if (body.pkgSource !== "cn" && body.pkgSource !== "global") {
|
|
@@ -200,25 +152,9 @@ export function validateBake(body) {
|
|
|
200
152
|
dryRun,
|
|
201
153
|
logs,
|
|
202
154
|
pkgSource,
|
|
203
|
-
// argv is built by the caller AFTER it resolves the effective baseRef (the index allow-list + default base).
|
|
204
155
|
argv: [],
|
|
205
156
|
};
|
|
206
157
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Build the VETTED, NORMALIZED build.sh argv from an accepted validation + the caller-resolved effective fields
|
|
209
|
-
* (the index-checked baseRef and the build-host CACHE_BASE — both runner-environment facts image-api injects,
|
|
210
|
-
* never operator free-text). The elements are RAW (no shell-quoting): the runner array-spawns build.sh so each
|
|
211
|
-
* element lands in its own argv slot (no shell can re-interpret a frozen-set value). The runner runs
|
|
212
|
-
* `build.sh <argv...>` and ONLY this.
|
|
213
|
-
*
|
|
214
|
-
* Server-fixed invariants enforced here (NOT caller-settable, P2.4d):
|
|
215
|
-
* - `--json` always (machine output the runner parses).
|
|
216
|
-
* - selection is `--profile <p>` XOR `--bands a,b,c` (already vetted).
|
|
217
|
-
* - NO `--tag` (build.sh auto-computes the immutable tag — kills namespace-squat / overwrite-by-naming).
|
|
218
|
-
* - the output repo is server-fixed `sema-images/<profile>` (build.sh's IMAGE_PREFIX default; not forwarded).
|
|
219
|
-
* - `--base-ref` is the index-checked effective base (caller resolves the default when absent).
|
|
220
|
-
* - `--cache-base` is the build-host promoted cache (runner env).
|
|
221
|
-
*/
|
|
222
158
|
export function buildBakeArgv(v, effective) {
|
|
223
159
|
const args = [];
|
|
224
160
|
if (v.bands && v.bands.length > 0) {
|
|
@@ -242,11 +178,6 @@ export function buildBakeArgv(v, effective) {
|
|
|
242
178
|
if (v.dryRun)
|
|
243
179
|
args.push("--dry-run");
|
|
244
180
|
args.push("--json");
|
|
245
|
-
// Return the RAW vetted vector. Set-membership against FROZEN_PROFILES/BANDS/BUMP_TOOLS is the RCE gate; the
|
|
246
|
-
// runner array-spawns build.sh with each element in its own argv slot (no shell), so a frozen-set value can
|
|
247
|
-
// never be re-interpreted by a shell. Shell-quoting here would embed literal single-quotes into each slot →
|
|
248
|
-
// build.sh sees `'--profile'` (quotes included) and dies "unknown arg" on the first one (H2). Quoting is wrong
|
|
249
|
-
// for an array-spawn boundary; the injection boundary is the array slot, not a joined command string.
|
|
250
181
|
return args;
|
|
251
182
|
}
|
|
252
183
|
//# sourceMappingURL=bake-validate.js.map
|
package/dist/images/manifest.js
CHANGED
|
@@ -4,18 +4,16 @@ const NESTED_BUILD_MODES = [
|
|
|
4
4
|
"rootless-buildkit",
|
|
5
5
|
"privileged-dind",
|
|
6
6
|
];
|
|
7
|
-
/** Project the manifest's `nestedBuild` (string posture OR bool) → the enum (center contract). */
|
|
8
7
|
export function nestedBuildModeOf(v) {
|
|
9
8
|
if (typeof v === "string") {
|
|
10
9
|
return NESTED_BUILD_MODES.includes(v)
|
|
11
10
|
? v
|
|
12
11
|
: v
|
|
13
|
-
? "rootless-buildkit"
|
|
12
|
+
? "rootless-buildkit"
|
|
14
13
|
: "none";
|
|
15
14
|
}
|
|
16
15
|
return v ? "rootless-buildkit" : "none";
|
|
17
16
|
}
|
|
18
|
-
/** Parse a docker-style size ("64m" / "512M" / "1g") → MB. Undefined on an unparseable / absent value. */
|
|
19
17
|
export function shmToMB(v) {
|
|
20
18
|
if (!v)
|
|
21
19
|
return undefined;
|
|
@@ -25,14 +23,12 @@ export function shmToMB(v) {
|
|
|
25
23
|
const n = Number(m[1]);
|
|
26
24
|
return (m[2] ?? "m").toLowerCase() === "g" ? n * 1024 : n;
|
|
27
25
|
}
|
|
28
|
-
/** --bands builds record profile="custom", so the tag's repo part ("code-flutter:tag" → "code-flutter") wins. */
|
|
29
26
|
export function profileFromManifest(m) {
|
|
30
27
|
if (m.profile && m.profile !== "custom")
|
|
31
28
|
return m.profile;
|
|
32
29
|
const repo = (m.tag ?? "").split(":")[0] ?? "";
|
|
33
30
|
return repo || m.profile || "custom";
|
|
34
31
|
}
|
|
35
|
-
/** Pure projection: a build.sh manifest + the push-time facts → an index upsert. */
|
|
36
32
|
export function manifestToIndexEntry(m, opts) {
|
|
37
33
|
const profile = profileFromManifest(m);
|
|
38
34
|
const tag = (m.tag ?? "").includes(":") ? (m.tag ?? "").split(":")[1] : (m.tag ?? "");
|
|
@@ -61,7 +57,6 @@ export function manifestToIndexEntry(m, opts) {
|
|
|
61
57
|
sizeBytes: opts.sizeBytes ?? m.sizeBytes ?? null,
|
|
62
58
|
status: opts.status ?? "published",
|
|
63
59
|
visibility,
|
|
64
|
-
// a tenant image carries its owner; the standard pool's "standard-pool" owner is NOT a tenant.
|
|
65
60
|
tenantId: visibility === "tenant" && m.owner && m.owner !== "standard-pool" ? m.owner : null,
|
|
66
61
|
manifestSha: opts.manifestSha ?? null,
|
|
67
62
|
recipeGitSha: m.recipeGitSha && m.recipeGitSha !== "nogit" ? m.recipeGitSha : null,
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
// Library surface (for embedding the service wiring in another Node app — e.g. the shell's seamless local mode
|
|
2
|
-
// runs the sema-server HTTP wire in-process via createHttpServer + a local StoreBackend).
|
|
3
1
|
export { loadConfig } from "./config.js";
|
|
4
2
|
export { createBrain } from "./brain.js";
|
|
5
3
|
export { createSessionStore } from "./plugins/session-store.js";
|
|
6
|
-
// design/158 B5-①([1971] 声明后落):中立后端缝 —— 嵌入姿势(见顶注)= createHttpServer + StoreBackend。
|
|
7
4
|
export { createStoreBackend } from "./plugins/store-backend.js";
|
|
8
|
-
// ── B5-②(clay 两拍终裁 2026-07-30:cli/core 零消费,弃用窗撤销直接删)——6 个 TiDB 方言专属导出已摘 ──
|
|
9
|
-
// 判据:①backend-可插铁律(公面即承诺,方言类在公面=邀请绕过中立缝直连方言);②公面瘸腿(PG 孪生
|
|
10
|
-
// 从未导出);③全生态扫描零消费。替代:构造走 createStoreBackend(kind mysql|pg|local),DDL 审阅走
|
|
11
|
-
// docs/schema/baseline-{mysql,pg}.sql(生成基线,机器门逐字节)。类/函数本体仍是内部实现(深径 import)。
|
|
12
5
|
export { CachingSessionStore } from "./plugins/caching-session-store.js";
|
|
13
6
|
export { runInBackground } from "./runs.js";
|
|
14
7
|
export { createHardenedVmRunner, runInHardenedVm } from "./orchestration/hardened-vm-runner.js";
|
|
@@ -22,25 +15,11 @@ export { nowTool } from "./capabilities/builtin-tools.js";
|
|
|
22
15
|
export { createCouncilTool } from "./capabilities/code-review-council.js";
|
|
23
16
|
export { loadSkills, skillsForScenario } from "./capabilities/skills.js";
|
|
24
17
|
export { buildScenarios, selectScenario, } from "./capabilities/scenarios.js";
|
|
25
|
-
// #318:会话级任务清单车道(嵌入式装配自己组 ScenarioDeps 时接 `taskListStoreFor` 的属主工厂)。
|
|
26
|
-
// 两条 `ensure*TaskListSchema` 一并导出:车道**不建表**(建表属主 = 调它的装配层,见车道顶注),
|
|
27
|
-
// 所以走 SQL 臂的嵌入式装配必须自己先 await 对应方言的 ensure —— 不导出等于把那一步藏起来。
|
|
28
18
|
export { createTaskListLane, taskListKeyFor, MAX_TASK_LIST_SESSIONS } from "./boot/task-list-lane.js";
|
|
29
19
|
export { ensureTiDBTaskListSchema, ensurePgTaskListSchema } from "./plugins/task-list-store-sql.js";
|
|
30
20
|
export { createAuthorizer, principalFrom, HttpError, } from "./security.js";
|
|
31
21
|
export { memoryScopeFor, memoryEngineBackendFor } from "./memory-scope.js";
|
|
32
22
|
export { createHttpServer, } from "./http/server.js";
|
|
33
|
-
// ── A-057.8:`ServiceCoreDeps.sideQuery` 是**必填**席,而它的装配链此前整条不在公面上 ──
|
|
34
|
-
// CHANGELOG 对自建 `ServiceDeps` 的嵌入方逐字指路「按 `createSideQueryLane` 装配即可」,但
|
|
35
|
-
// `package.json` 的 `exports` 只开 `.` / `./main` / `./package.json`(无通配、无 typesVersions),
|
|
36
|
-
// `files` 又只出 `dist` ⇒ 安装态里被指路的那个源文件读都读不到,深径 import 必
|
|
37
|
-
// `ERR_PACKAGE_PATH_NOT_EXPORTED`。嵌入方于是只剩「手抄一份 per-model key 解析 + poison 处理」,
|
|
38
|
-
// 而抄漏 poison 那半 = 拿共享网关账号去打别家上游,正是 #303 立案要修的凭据外泄病灶本身。
|
|
39
|
-
// 🔴 poison 的判别式(`isSealedKeyPoison` / `SealedKeyPoisonedError`)与工厂**同批**出面是刻意的:
|
|
40
|
-
// 只给工厂不给判据,嵌入方对「sealed key 解不开」就只能靠 message 猜,而猜错的方向恰好是静默回落
|
|
41
|
-
// 网关 key(安全轴上没有静默降级 —— CLAUDE.md #157)。
|
|
42
|
-
// 🔴 这五个值导出必须保持**值**形态(不许改成 `export type`):门 = `test/export-liveness-gate.test.ts`
|
|
43
|
-
// 的 gate:embedder-assembly-exports(表内符号缺一即红;runtime 类被 type-only 化同样红)。
|
|
44
23
|
export { createSideQueryLane, createPerModelKeyBrain } from "./boot/side-query-lane.js";
|
|
45
24
|
export { createKeyResolver, resolveModelApiKey } from "./key-resolver.js";
|
|
46
25
|
export { isSealedKeyPoison, SealedKeyPoisonedError } from "./sealed-key.js";
|
package/dist/key-resolver.js
CHANGED
|
@@ -7,30 +7,15 @@ export function createKeyResolver(modelApiKeyEnv, env = process.env, modelApiKey
|
|
|
7
7
|
return apiKey ? { apiKey } : undefined;
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
11
|
-
* The ONE per-model key chain, synchronous: **sealed custody → env-NAME reference → (caller's) gateway key**,
|
|
12
|
-
* with a poisoned sealed entry THROWING instead of returning undefined. `createKeyResolver` is the async
|
|
13
|
-
* `getApiKeyAndHeaders` face of exactly this function — every other consumer must call it rather than
|
|
14
|
-
* re-implement a subset of the chain.
|
|
15
|
-
*
|
|
16
|
-
* 复审 2026-07-29(同族缺口):`brain.ts` 的反应式降级 hop 曾只查 `modelApiKeyEnv` —— sealed-box 托管密钥的
|
|
17
|
-
* 降级目标(按互斥契约**永不**落 modelApiKeyEnv)于是静默拿网关 key 打自己的网关(错 key 打上游),毒丸更
|
|
18
|
-
* 被当成"没配 key"。链只有一条,重复实现就会长出这种半条链的偏差,故此处抽出同步内核共用。
|
|
19
|
-
*
|
|
20
|
-
* `undefined` = this model has no per-model key ⇒ the CALLER's gateway/default credential applies (the
|
|
21
|
-
* additive contract). An empty env value counts as unset (unchanged from the original inline chain).
|
|
22
|
-
*/
|
|
23
10
|
export function resolveModelApiKey(modelName, modelApiKeyEnv, env = process.env, modelApiKeys = {}) {
|
|
24
11
|
const sealed = modelApiKeys[modelName];
|
|
25
|
-
// Poison pill: the operator configured a sealed key for this model but it cannot be unsealed —
|
|
26
|
-
// fail the brain call LOUD (never undefined = never the gateway fallback).
|
|
27
12
|
if (isSealedKeyPoison(sealed))
|
|
28
13
|
throw new SealedKeyPoisonedError(modelName, sealed);
|
|
29
14
|
if (sealed)
|
|
30
|
-
return sealed;
|
|
15
|
+
return sealed;
|
|
31
16
|
const envName = modelApiKeyEnv[modelName];
|
|
32
17
|
if (!envName)
|
|
33
|
-
return undefined;
|
|
18
|
+
return undefined;
|
|
34
19
|
return env[envName] || undefined;
|
|
35
20
|
}
|
|
36
21
|
//# sourceMappingURL=key-resolver.js.map
|
package/dist/leader/diffout.js
CHANGED
|
@@ -1,33 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 一次 diff 的字节帽(pull 腿与 push/上行腿**共用同一个数**)。
|
|
3
|
-
*
|
|
4
|
-
* 🔴 exported 是判据的一部分(2026-08-08,#176 二轮扫描 finding):`diffup.ts` 原先另抄了一份同值常量,
|
|
5
|
-
* 而它**本就已经** `import { safeRev } from "./diffout.js"` —— 依赖边在,共享零成本。两份手抄的后果不是
|
|
6
|
-
* 「多写一行」,是**单边改值后上行/pull 两腿对同一个 diff 的过/拒判定分家**:同一份补丁在一条腿上过、
|
|
7
|
-
* 另一条腿上被截断,而两处都「按自己的常量」正确工作,没有任何一处会报错。
|
|
8
|
-
* (同族先例:`checkpoint-store-sql.ts` 的 `export const TERMINAL_GRACE_MS`。)
|
|
9
|
-
*/
|
|
10
1
|
export const DEFAULT_MAX_DIFF_BYTES = 5 * 1024 * 1024;
|
|
11
2
|
const DEFAULT_READ_TIMEOUT_MS = 30_000;
|
|
12
|
-
/** A safe git rev token: sha, branch/tag name, or `A..B`. No shell metacharacters, no leading dash.
|
|
13
|
-
* 🔗 近亲判据:`leader/planner.ts` 的 branch 校验用同一套字符类 + 同一个 200 上限 + 同样拒前导 `-`,
|
|
14
|
-
* 只在 `..` 上有意分家(这里要放行 `A..B` 区间形,那边全拒)。差异与候裁说明写在 planner 那处
|
|
15
|
-
* (#176 二轮扫描 finding)——单边加字符类成员时,请两处一起看。 */
|
|
16
3
|
const SAFE_REV = /^[A-Za-z0-9._/-]{1,200}$/;
|
|
17
4
|
export function safeRev(rev) {
|
|
18
5
|
return SAFE_REV.test(rev) && !rev.startsWith("-") && !rev.includes("..--");
|
|
19
6
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Pull `<baseSha>..<ref>` from a worker container as a `git format-patch` series. Never throws — failures
|
|
22
|
-
* (bad rev, non-zero git, truncation, transport loss, empty diff) are returned as `{ ok: false }`.
|
|
23
|
-
*/
|
|
24
7
|
export async function pullDiff(env, baseSha, opts = {}) {
|
|
25
8
|
const ref = opts.ref ?? "HEAD";
|
|
26
9
|
if (!safeRev(baseSha) || !safeRev(ref)) {
|
|
27
10
|
return { ok: false, error: `unsafe git rev (baseSha=${JSON.stringify(baseSha)} ref=${JSON.stringify(ref)})` };
|
|
28
11
|
}
|
|
29
12
|
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_DIFF_BYTES;
|
|
30
|
-
// --stdout (council#14): emit the whole series to stdout, not .patch files. Newest-last (default order).
|
|
31
13
|
const cmd = `git format-patch --stdout ${baseSha}..${ref}`;
|
|
32
14
|
let patch = "";
|
|
33
15
|
let bytes = 0;
|
|
@@ -36,7 +18,7 @@ export async function pullDiff(env, baseSha, opts = {}) {
|
|
|
36
18
|
try {
|
|
37
19
|
for await (const chunk of env.execStream(cmd, {
|
|
38
20
|
...(opts.cwd ? { cwd: opts.cwd } : {}),
|
|
39
|
-
maxOutputBytes: maxBytes + 1,
|
|
21
|
+
maxOutputBytes: maxBytes + 1,
|
|
40
22
|
readTimeoutMs: opts.readTimeoutMs ?? DEFAULT_READ_TIMEOUT_MS,
|
|
41
23
|
...(opts.signal ? { signal: opts.signal } : {}),
|
|
42
24
|
})) {
|
|
@@ -56,7 +38,6 @@ export async function pullDiff(env, baseSha, opts = {}) {
|
|
|
56
38
|
}
|
|
57
39
|
}
|
|
58
40
|
catch (e) {
|
|
59
|
-
// execStream throws on transport loss / suspendVM-aborted in-flight command (no exit chunk).
|
|
60
41
|
return { ok: false, error: `execStream failed before exit: ${e instanceof Error ? e.message : String(e)}` };
|
|
61
42
|
}
|
|
62
43
|
if (exitCode === undefined) {
|