@sema-agent/server 7.70.0 → 7.72.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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +8 -4
- package/dist/approval-card.d.ts +16 -17
- package/dist/approval-card.js +0 -9
- package/dist/approval-content-kind.d.ts +13 -7
- package/dist/approval-content-kind.js +3 -1
- package/dist/approval.js +3 -2
- package/dist/bench/s1/live-deps.js +2 -0
- package/dist/boot/execution-env.js +12 -2
- package/dist/boot/memory-consolidation.d.ts +2 -1
- package/dist/boot/parked-revive-gate.d.ts +2 -0
- package/dist/boot/parked-revive-gate.js +5 -1
- package/dist/boot/resolve-spec.js +8 -3
- package/dist/boot/runner-deps.d.ts +48 -59
- package/dist/boot/runner-deps.js +10 -4
- package/dist/brain.d.ts +3 -1
- package/dist/brain.js +2 -8
- package/dist/budget.d.ts +12 -1
- package/dist/budget.js +1 -1
- package/dist/config-catalog.js +13 -11
- package/dist/config-center/apply-effective.js +2 -4
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/restart-signal.js +2 -2
- package/dist/config-center/skills-mcp.js +15 -0
- package/dist/config-types.d.ts +86 -4
- package/dist/config-types.js +19 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.js +71 -22
- package/dist/deployment-governance.d.ts +6 -3
- package/dist/deployment-governance.js +2 -1
- package/dist/execution-lane-caps.d.ts +51 -37
- package/dist/execution-lane-caps.js +23 -0
- package/dist/fleet/fleet-bus.d.ts +22 -1
- package/dist/fleet/fleet-bus.js +2 -1
- package/dist/http/durable-run-leg.d.ts +64 -0
- package/dist/http/durable-run-leg.js +63 -0
- package/dist/http/route-ctx.d.ts +14 -0
- package/dist/http/route-ctx.js +3 -0
- package/dist/http/routes/approvals-assistant.js +3 -3
- package/dist/http/routes/capabilities.js +2 -2
- package/dist/http/routes/runs.js +10 -58
- package/dist/http/routes/workflows.js +1 -0
- package/dist/http/server.js +72 -22
- package/dist/index.d.ts +1 -1
- package/dist/leader/wire.js +4 -3
- package/dist/main.js +8 -3
- package/dist/memory-operator-faces.d.ts +1 -1
- package/dist/memory-scope.d.ts +24 -0
- package/dist/memory-scope.js +12 -3
- package/dist/observability/fail-open.d.ts +1 -1
- package/dist/observability/fail-open.js +1 -1
- package/dist/observability/metrics.d.ts +4 -0
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/run-terminal-log.d.ts +4 -3
- package/dist/observability/run-terminal-log.js +4 -15
- package/dist/observability/tool-trace.d.ts +18 -1
- package/dist/observability/tool-trace.js +4 -3
- package/dist/orchestration/workflow-agent-session-index.d.ts +85 -2
- package/dist/orchestration/workflow-agent-session-index.js +38 -5
- package/dist/parked-decide.d.ts +40 -1
- package/dist/parked-decide.js +22 -4
- package/dist/plugins/checkpoint-store-sql.js +5 -5
- package/dist/plugins/e2b-orphan-reclaim.d.ts +4 -4
- package/dist/plugins/e2b-orphan-reclaim.js +18 -20
- package/dist/plugins/local-checkpoint-store.js +3 -3
- package/dist/plugins/remote-env-adb.d.ts +11 -0
- package/dist/plugins/remote-env-adb.js +2 -0
- package/dist/plugins/remote-env-device.d.ts +9 -0
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +11 -0
- package/dist/plugins/remote-env-e2b.js +2 -0
- package/dist/plugins/remote-env-host.d.ts +11 -0
- package/dist/plugins/remote-env-host.js +2 -0
- package/dist/plugins/remote-env-k8s.d.ts +11 -0
- package/dist/plugins/remote-env-k8s.js +2 -0
- package/dist/plugins/remote-env-local-docker.d.ts +11 -0
- package/dist/plugins/remote-env-local-docker.js +2 -0
- package/dist/plugins/remote-env-ssh.d.ts +11 -0
- package/dist/plugins/remote-env-ssh.js +2 -0
- package/dist/plugins/store-backend.d.ts +34 -12
- package/dist/plugins/store-backend.js +12 -3
- package/dist/plugins/store-contracts.d.ts +81 -18
- package/dist/plugins/store-contracts.js +18 -30
- package/dist/plugins/workflow-run-store-sql.d.ts +31 -9
- package/dist/plugins/workflow-run-store-sql.js +28 -1
- package/dist/run-cancel-context.js +1 -1
- package/dist/run-local.js +7 -1
- package/dist/runs.d.ts +16 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +5 -3
- package/dist/task-a2a.js +2 -1
- package/dist/task-mcp.js +2 -1
- package/dist/task-settings.d.ts +21 -8
- package/dist/task-settings.js +2 -2
- package/dist/tool-approval.d.ts +26 -21
- package/dist/tool-approval.js +2 -2
- package/dist/trace/core-keyset-guard.d.ts +136 -10
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/ledger-events.d.ts +5 -1
- package/dist/trace/ledger-sink.js +8 -1
- package/dist/trace/project.d.ts +33 -4
- package/dist/trace/project.js +24 -6
- package/dist/trace/redact.js +10 -5
- package/dist/usage-analytics.d.ts +9 -1
- package/dist/usage-analytics.js +8 -0
- package/dist/wire-name-bound.d.ts +14 -0
- package/dist/wire-name-bound.js +13 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ The server is configured entirely through environment variables. The most import
|
|
|
168
168
|
| `PERMISSIONS_DISABLE_AUTO_MODE` | `false` | Local mirror of CC `permissions.disableAutoMode` — the **org deny** bit for `permissionMode:"auto"` (paired with core's intent-arming rule). **Tighten-only**: `true` folds every principal's `runtimeCaps.autoMode` to `false` (a center grant cannot flip it back); unset leaves caps untouched, so on a center-less box a shell asking for `auto` **arms** the classifier once the paired core (intent-arming rule "requested ∧ classifier seat ∧ `autoMode !== false`" — absence is not a deny) is installed; on core 7.2.0 the engine still uses the old "org grant" rule (`permissionModeAuto.intentArming:false`), so the self-check answers `armed:true` only for a center-granted principal and `deployment_incapable` on a center-less box. Boolean word table; any other value refuses to start. Self-check: `GET /v1/capabilities?permissionMode=auto` → `permissionModeAuto.{armed, reason, model}` (USAGE §9.4) |
|
|
169
169
|
| `SCRATCHPAD_SWEEP_TTL_MS` | 7 days | Idle-reap window for per-session scratchpad dirs (by dir mtime; `0` disables). The scratchpad is **ephemeral by contract**: replica-local disk, NOT part of the durable-suspend persistence set — a resume on a different replica, or after a sweep, starts with an empty dir (same two-track posture as the Agent SDK hosting doc: conversation persists, working-directory artifacts don't). Raise/disable only on single-replica deployments that park approvals for longer than the window |
|
|
170
170
|
| `MODEL_CONNECT_TIMEOUT_MS` | `30000` | Gateway connect timeout |
|
|
171
|
-
| `MODEL_FIRST_TOKEN_TIMEOUT_MS` | `
|
|
171
|
+
| `MODEL_FIRST_TOKEN_TIMEOUT_MS` | `600000` | First-token timeout (the only watchdog for a stream that opens and never emits; `0` = off. Raised from `120000` in 7.71.0 — a self-hosted backend's long prefill legitimately takes minutes to the first byte; set `120000` to restore the old posture) |
|
|
172
172
|
| `MODEL_IDLE_TIMEOUT_MS` | `300000` | Mid-stream idle timeout (`0` = off) |
|
|
173
173
|
| `LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` (structured JSON logs) |
|
|
174
174
|
|
package/README.zh-CN.md
CHANGED
|
@@ -151,7 +151,7 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
|
|
|
151
151
|
| `WRITE_PROTECTED_EXTRA` | 未设(引擎缺省表) | 给 core 的**写保护名表**加行(字面名表:命中即把幸存的 `allow` 降级成 `ask`,作用于 Write/Edit/NotebookEdit)。逗号分隔裸名(裸名匹配**任意路径段**,含 `/` 的名匹配连续段)或 JSON 数组(`"name"` 串 / `{name, kind}` 行,`kind`:`basename` \| `segment` \| `segment-run`)。值按 `[...core 缺省表, …]` 组合,**丢不掉任何缺省行**。未设 = 不铸座 = 引擎缺省表在岗(本仓从不复制那张表)。两形按**内容**判而不是猜首字符:值里出现 JSON 结构字符(`[ ] { } "`)即按 JSON 解析,且顶层**必须是数组**(少写一对方括号 ⇒ 拒启,而不是被拆成垃圾裸名静默收下)。空值 / 坏值(通配符、未知 kind、kind 与名字段数矛盾)/ 与 `WRITE_PROTECTED_TABLE_REPLACE` 同时设置 ⇒ **启动即拒**。读面:`GET /v1/capabilities` 的 `writeProtection.{armed,rows,replaced}`;`GET /v1/diagnostics/wiring` 的 `writeProtection.{rows,source,droppedDefaultRows}`(operator-only) |
|
|
152
152
|
| `WRITE_PROTECTED_TABLE_REPLACE` | 未设(引擎缺省表) | **整表替换**写保护名表(core 的座按契约就是整表)。只收 JSON 数组 —— 刻意不给逗号简写:一个手滑的裸串会把 51 行换成 1 行。`[]` = 显式「完全不要这张表」。替换时 boot 期发一条**响亮**日志逐名列出被丢的缺省行(`write_protection_table_replaced`;空表走 `write_protection_table_disabled`)—— 想「加两行」请用 `WRITE_PROTECTED_EXTRA`。拒启条件同姊妹键,外加:两根同写 = 一条语义面两个写者 ⇒ 拒启 |
|
|
153
153
|
| `MODEL_CONNECT_TIMEOUT_MS` | `30000` | 网关连接超时 |
|
|
154
|
-
| `MODEL_FIRST_TOKEN_TIMEOUT_MS` | `
|
|
154
|
+
| `MODEL_FIRST_TOKEN_TIMEOUT_MS` | `600000` | 首 token 超时(开流不吐字的唯一看门狗;`0`=关。7.71.0 起由 `120000` 提高 —— 自托管后端长 prefill 下首字合理地就要等几分钟;要旧姿态显式设 `120000`) |
|
|
155
155
|
| `MODEL_IDLE_TIMEOUT_MS` | `300000` | 流中 idle 超时(`0` 关) |
|
|
156
156
|
| `LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error`(结构化 JSON 日志) |
|
|
157
157
|
|
package/USAGE.md
CHANGED
|
@@ -63,6 +63,8 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
|
|
|
63
63
|
# ③ 分级超时:任务级 limits.maxWalltimeMs(6.0.0 起,毫秒)之下补两段,各记为可重试的 [network] → 触发重试/断路器/failover
|
|
64
64
|
MODEL_CONNECT_TIMEOUT_MS=8000 # fetch 迟迟不返响应头(网关连不上)→ 中止
|
|
65
65
|
MODEL_FIRST_TOKEN_TIMEOUT_MS=30000 # SSE 已开但迟迟不吐第一个 delta(网关 hang);reasoning 的首个 thinking 也算首 token
|
|
66
|
+
# 缺省 600000(7.71.0 起,原 120000):自托管后端长 prefill 下首字合理地要等几分钟,
|
|
67
|
+
# 120s 会把一次正常的慢启动判成 [network] 失败去重试。快失败部署显式设回 120000;0=关
|
|
66
68
|
MODEL_IDLE_TIMEOUT_MS=20000 # (1.40.1)出过 token 后中途卡死:每个 delta 重置,静默超时→中止(补 first-token 只管首字)
|
|
67
69
|
# ④ 断路器:主网关连败 N 次即"开路"→ 快速失败,让 failover 立刻切备(不再逐个等超时)
|
|
68
70
|
MODEL_CIRCUIT_BREAKER=true MODEL_CB_FAILURE_THRESHOLD=5 MODEL_CB_COOLDOWN_MS=30000
|
|
@@ -159,11 +161,12 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
159
161
|
**坏形/坏词/未知键 ⇒ 丢掉这条 `compat` 声明、模型本体保留**,并打一条 `model_compat_dropped`
|
|
160
162
|
点名 warn(引擎回落到自己的推断 = 今天的行为);一条写坏的声明**不会**让这只模型或整套目录消失。
|
|
161
163
|
- 反应式降级 brain 包在**最外层**;fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**坑**:`MODEL_DEGRADE_TO` 最好别和被限流的是同一网关/账号,否则反应式切过去照样撞同一个 rate_limit。
|
|
162
|
-
- **定价怎么设**:`model.cost` 来自 `MODEL_COST_INPUT/OUTPUT/CACHE_READ/CACHE_WRITE`(**USD per 1M tokens
|
|
164
|
+
- **定价怎么设**:`model.cost` 来自 `MODEL_COST_INPUT/OUTPUT/CACHE_READ/CACHE_WRITE`(**USD per 1M tokens**)。**四键一个都不设 = 未定价**:成本读面**缺席**(`result.stats.costMicroUsd` 整键不出、`costBreakdown` 不产、`/v1/capabilities.pricingConfigured=false`),不是 `$0` —— 消费端应渲成「未知」。设其中任一键 ⇒ 四键在场(未设的按 0)。本地自托管(qwen)真的没有 per-token 外部花费时,请**显式**把四键设成 `0`:那是一次「声明免费」,读面照常发 `costMicroUsd:0` 且 `pricingConfigured=true`。云模型(如 review-gw 的 deepseek-v4-flash)必须设真价,否则 spend 无从计算。字段名是 `costUsd`,**非美元计价的网关要先折算**(如 DeepSeek 官方 CNY ÷ 汇率)。配置控制面管的模型走 `CenterModel.cost`(中心存价、不存 secret;名册行不带 `cost` = 该模型未定价,同上)。
|
|
165
|
+
- **成本天花板要有价才咬得住**:`MAX_TASK_COST_USD` / `MAX_PRINCIPAL_COST_USD` 量的是 `costUsd`,未定价的模型算不出成本 ⇒ 这两道闸**永不触发**。目录里一只带价模型都没有却配了天花板时,boot 会打一条 `cost_ceiling_without_pricing` 告警(不拒启);token 轴的 `MAX_TASK_TOKENS` 不受影响。
|
|
163
166
|
- 成本计量**自动开**:从 config 的 `model.cost`(per-1M 绝对 USD)注入 `pricing`,core 算出权威的整数 `costMicroUsd`(避免浮点累计误差)。`/metrics` 新增:`model_cost_micro_usd_total{model}`(覆盖所有 brain 调用=主任务+异步+council 子任务的总花费)、`brain_first_token_ms`(网关 hang 早警)、`brain_call_latency_ms`、`tool_calls_total{name,ok}`、`budget_exceeded_total{code}`、`cost_quota_rejected_total`、`degraded_total{reason}`(1.40 降级)。
|
|
164
167
|
- **近预算降级 vs 硬闸**:降级(`MODEL_DEGRADE_TO`,到 `atCostFraction` 切便宜模型)是**撑长**预算、任务仍完成(出口质量下降、发 `task.degraded` 事件可告警);硬闸(`maxCostUsd` 全额)仍在,切了便宜模型还超全额 → `limits.max_cost_exceeded` 停。
|
|
165
168
|
- **`METRICS_TOKEN`**(可选,只读):设了它,`GET /metrics`+`/metrics/summary` 接受**它或** `SERVICE_AUTH_TOKEN`。**全 fleet 设同一个值** → 管理端(sema-web,规划名 sema-admin)用**一个** token 拉所有 worker 的指标,**无需持有各 worker 的全权 `SERVICE_AUTH_TOKEN`**(不破坏 secret 边界)。即使外泄也只暴露指标(只读)。
|
|
166
|
-
- **`GET /metrics/summary`**(token-gated,同 `/metrics`):`/metrics` 的**精炼 JSON**——`{model, runsActive, tasks{status}, tokensTotal, costUsd, costUsdByModel, taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, rateLimited, costQuotaRejected, budgetExceeded, degraded, cascade, verifications, councilRuns, toolErrors}
|
|
169
|
+
- **`GET /metrics/summary`**(token-gated,同 `/metrics`):`/metrics` 的**精炼 JSON**——`{model, runsActive, tasks{status}, tokensTotal, costUsd, costUsdByModel, taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, rateLimited, costQuotaRejected, budgetExceeded, limitCeilingAfterAnswer, degraded, cascade, verifications, councilRuns, toolErrors}`(`limitCeilingAfterAnswer` 自 7.71.0,按 `origin` 分桶的「答后撞顶」计数;与 `budgetExceeded` **总体不同** —— 前者每条引擎 run 都过,后者只由 HTTP 提交腿递增 —— 不要无条件相加)。给**轻量 fleet 看板**用(管理端 sema-web 的 fleet 页按 worker 拉、渲染卡片、按轮询算速率;**不用 Prometheus/Grafana**)。counter 是累计值、histogram 报均值。
|
|
167
170
|
- 预算闸是 core 强制的:`maxCostUsd` pre-call 估算(没花钱就拒)+ 流中途取消 + turn 边界(`limits.max_cost_exceeded`);`maxTokens` 超 → `limits.max_tokens_exceeded`(core 5.8.0 起 `budget.*` 前缀退役)。**review 网关**建议设 `MAX_TASK_COST_USD` 防单任务烧掉共享云 key。
|
|
168
171
|
- 🔴 **三道 $ 闸的分工(别拿进场门当中途停机用)**:三根旋钮名字都带 cost,但**判的时刻**和**能不能打断正在烧的那张单**完全不同。按「谁触发 / 什么时刻判 / 拒或停长什么样」对表:
|
|
169
172
|
|
|
@@ -322,7 +325,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
|
|
|
322
325
|
| `MEMORY_ORG_UNAVAILABLE_BACKOFF_MS` | `10000`(`[500, 600000]`) | 目录取不到之后的退避窗(只用于取数失败臂,不用于负结果) |
|
|
323
326
|
| `MEMORY_DELEGATION_EVIDENCE` | 缺省(=引擎自缺省 `static-face`) | **委派臂的证据标准**(core 5.45.0 起)。`static-face`=今日行为:委派的 attestation 缺失/未知**且**静态工具面够得着外部内容 ⇒ 标记本会话记忆为已污染(可能性即暴露)。`attested-only`=**只**豁免那一条静态面标记,且**真的豁免了一次**才响亮通告(`engine_notice` 的 `memory.delegation_static_mark_waived`,每个 prepared leg **至多**一次);送达 `external` attestation 照标、非委派的污染类工具照标、委派工具自身被分类为污染的照标。⚠️ 通告**不是 leg 计数器**:不含委派的 leg、污染面没挂载的 leg、送达了 attestation 的委派都不发 —— 看不到通告的常态含义是「没有可豁免的事」,不是「配置坏了」。坏值**启动期拒**。**部署席 only**(TaskSpec 无同名键,任务/受治脚本无法据此放松部署)。⚠️ **已接受的代价**:`attested-only` 下**后台**子代的真实外部接触不标记本会话(其内容经 TaskOutput / 任务通知注入 / AgentTranscript 摘要回流,三条都不带 attestation);异常收尾的前台子代同理。已标记的会话永不回滚清洗。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.delegationEvidence`(`null`=本部署未设,引擎自缺省) |
|
|
324
327
|
| `MEMORY_PROVENANCE` | 缺省(=引擎自缺省 `carry`) | **记忆 provenance 总开关**(core 5.46.0 起)。一个会话被判为**已暴露**之后,它的记忆写怎么处置。`carry`=引擎缺省:**普通**记忆写照常提交,但带上引擎铸的 `origin` 标记(随条目走后端/同步/导出包);指令形文件(feedback / pinned / triggers / applies-when)仍被扣下隔离;派生索引的会话散文照回滚;内容扫描门原样跑(**标记不是豁免**)。`off`=此前的行为:不铸 origin 标记,已暴露会话的 harvest **一条都不收**(整体隔离候人审)。⚠️ 已提交的标记在编辑时照样带下去 —— `off` 停的是**铸**,从不抹掉已记的事实。与 `MEMORY_DELEGATION_EVIDENCE` **正交**(四种组合全合法):那一键决定**什么时候**判会话为已暴露,本键决定判了之后**对写做什么**。坏值**启动期拒**(`OFF`/`false`/`none` 这类「看起来像关掉」的写法一律不折成缺省 —— 静默回默认 = 一台自认为「暴露会话零收录」的部署其实在照常收录)。**部署席 only**(TaskSpec 无同名键,任务/受治脚本无法据此改姿态);**不冻进 checkpoint**(resume 腿跟当前部署配置走)。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.provenance`(`null`=本部署未设,引擎自缺省) |
|
|
325
|
-
| `MEMORY_CAPTURE_POLICY` | 缺省(=引擎自缺省 `open`) | **记忆采集 opt-out 的部署姿态**(core 7.0.0 起)。任务可带 `memory.capture:"off"` 声明「本会话不进长期记忆」(一次性,resume 恒不采集,无反向拼写),本键决定怎么裁那条声明:`open`=按面值生效,**只有**该 principal 的显式 `false` verdict 拒(403 `memory.capture_optout_denied`),resolver 故障**放行**+披露(隐私轴 fail-safe:错拒=记了用户明说不要记的会话,不可逆);`governed`=verdict **必答**(混合车队:部分用户强制留存),故障/缺席**拒跑**(fail-closed);`capture-required`=全体声明拒(一行配置)。verdict 源**两条**(析取):①SQL 店后端上的授权表 `memory_optout_grant`(三层折叠:per-principal 行 → 部署缺省行 → 代码缺省 allow;管理面 `GET|PUT /v1/admin/memory-optout`(PUT=部署缺省行)· `PUT|DELETE …/:principal`,operator-only,能力位 `memoryOptOutGrant`,零缓存写后即生效);②非 dry-run 的 config center 在该 principal 的 entitlement caps 里发 `allowMemoryOptOut`。🔴 **`governed` 而两源皆无 ⇒ 启动期拒启**(无源 = 每个声明都被拒且没人知道为什么;接 `DB_BACKEND=mysql|pg`、接非 dry-run center,或改姿态)。⚠️ **center-only 源的语义**(只配了 center、没有 SQL 授权表):`allowMemoryOptOut` 在 entitlement schema 里是**可选键** —— center 不发它 = 该 principal 的 verdict **缺席** = `governed` 下每次 opt-out 恒拒(403 `memory.capture_optout_denied`),而 worker **照常起动**;这一形部署级证不死(per-principal 授权只有运行期才知道),所以 server 两处响亮:启动期一行 `memory_capture_governed_center_only_source`、运行期首次真缺席一行 `memory_capture_governed_verdict_absent`(每进程一次,纯披露不改判)。要确定性 verdict 就接 SQL 授权表(它三层折叠后恒答显式 boolean)。坏值**启动期拒**(`Governed`/`required`/`off` 一律不折成缺省:两种姿态的故障极性**相反**)。**部署席 only**;**不冻进 checkpoint**。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。记录载体(core 7.0.2 起):SQL 后端(`DB_BACKEND=mysql\|pg`)上记录落 `session_capture_optout` 表(跨副本存活,随中央 schema 自建零旋钮)——**远端车道(e2b/k8s)的 `capture:"off"`
|
|
328
|
+
| `MEMORY_CAPTURE_POLICY` | 缺省(=引擎自缺省 `open`) | **记忆采集 opt-out 的部署姿态**(core 7.0.0 起)。任务可带 `memory.capture:"off"` 声明「本会话不进长期记忆」(一次性,resume 恒不采集,无反向拼写),本键决定怎么裁那条声明:`open`=按面值生效,**只有**该 principal 的显式 `false` verdict 拒(403 `memory.capture_optout_denied`),resolver 故障**放行**+披露(隐私轴 fail-safe:错拒=记了用户明说不要记的会话,不可逆);`governed`=verdict **必答**(混合车队:部分用户强制留存),故障/缺席**拒跑**(fail-closed);`capture-required`=全体声明拒(一行配置)。verdict 源**两条**(析取):①SQL 店后端上的授权表 `memory_optout_grant`(三层折叠:per-principal 行 → 部署缺省行 → 代码缺省 allow;管理面 `GET|PUT /v1/admin/memory-optout`(PUT=部署缺省行)· `PUT|DELETE …/:principal`,operator-only,能力位 `memoryOptOutGrant`,零缓存写后即生效);②非 dry-run 的 config center 在该 principal 的 entitlement caps 里发 `allowMemoryOptOut`。🔴 **`governed` 而两源皆无 ⇒ 启动期拒启**(无源 = 每个声明都被拒且没人知道为什么;接 `DB_BACKEND=mysql|pg`、接非 dry-run center,或改姿态)。⚠️ **center-only 源的语义**(只配了 center、没有 SQL 授权表):`allowMemoryOptOut` 在 entitlement schema 里是**可选键** —— center 不发它 = 该 principal 的 verdict **缺席** = `governed` 下每次 opt-out 恒拒(403 `memory.capture_optout_denied`),而 worker **照常起动**;这一形部署级证不死(per-principal 授权只有运行期才知道),所以 server 两处响亮:启动期一行 `memory_capture_governed_center_only_source`、运行期首次真缺席一行 `memory_capture_governed_verdict_absent`(每进程一次,纯披露不改判)。要确定性 verdict 就接 SQL 授权表(它三层折叠后恒答显式 boolean)。坏值**启动期拒**(`Governed`/`required`/`off` 一律不折成缺省:两种姿态的故障极性**相反**)。**部署席 only**;**不冻进 checkpoint**。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。记录载体(core 7.0.2 起):SQL 后端(`DB_BACKEND=mysql\|pg`)上记录落 `session_capture_optout` 表(跨副本存活,随中央 schema 自建零旋钮)——**远端车道(e2b/k8s)的 `capture:"off"` 声明自此真持久**;`DB_BACKEND=local` 自 7.71.0 起在记忆引擎接线时供 core 的控制面文件三腿(钉在本部署记忆引擎那只控制面目录上,按记忆平面取用)⇒ 单机默认部署(`CONFIG_PROVIDER=local`、`REMOTE_EXEC` 未设)上声明与中途翻转 verb 从 409 转为真受理(此前该 409 在这一形上是结构性的:core 判「远端」看的是执行环境的接口形,host 车道同被判 remote);记忆面暗的部署本席位缺席而 core 也不走采集腿。今天仍答 409 `config.memory_capture_unsupported` 的只剩把记忆引擎接了却没给任何店的自建装配形。用户面四口:提交键 `memoryCapture:"off"`(声明形)· `POST /v1/runs/:id/memory/capture-optout`(live 中途翻转,owner 门)· `POST /v1/sessions/:id/memory/erase`(所有者自助抹除,select 硬限本会话)· `GET /v1/sessions/:id/memory-status`(状态读面,owner 门,五可选键诚实缺席)。契约全文 `docs/ASSISTANT-WIRE-CONTRACT.md` §12 |
|
|
326
329
|
|
|
327
330
|
⚠️ **拒启动**:多租户 + 记忆面点亮 + `projects[].defaultScopes` 里有 org 键 + 目录源缺席 ⇒ 启动报错
|
|
328
331
|
并点名 projectId(该部署的每个此类请求都会在 prepare 期整拒,响亮拒启动比静默全拒服务诚实)。
|
|
@@ -1264,7 +1267,8 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1264
1267
|
| HTTP `200` + `terminal.kind:"completed"` | 成功 | 取 `result` |
|
|
1265
1268
|
| `terminal.kind:"blocked"` | agent 主动报卡住 | 看 `terminal.reason`,补信息再发 |
|
|
1266
1269
|
| `terminal:{kind:"failed", code:"conflict"}` | 跨实例乐观锁丢失 | 直接重试(幂等) |
|
|
1267
|
-
| `terminal:{kind:"failed", code:"limits.max_walltime_exceeded"}` | 墙钟到限(6.0.0 起 `status:"timeout"` 退役;7.64.0 起平面 `status` 键退役) | 拆小任务 / 提高 `limits.maxWalltimeMs`(毫秒) |
|
|
1270
|
+
| `terminal:{kind:"failed", code:"limits.max_walltime_exceeded"}` | 墙钟到限(6.0.0 起 `status:"timeout"` 退役;7.64.0 起平面 `status` 键退役)。🔴 **7.71.0 / 引擎 7.13.0 收窄**:上限若在最后一轮 turn **已经给出干净答案之后**才响,run 记 `completed`、答案就是 `result`,**不铸本码**——那次停机的唯一痕迹是 `engine_notice` 的 `task.interrupt_unconsumed{origin:"walltime"}`(附录 D.3)。本码此后只在**工作被真切断**时出现。⚠️ 按码计数的运维面要把 `origin ∈ walltime\|turns` 的那一帧计入才守恒(server 侧对偶计量 `limit_ceiling_after_answer_total{origin}`,与 `budget_exceeded_total{code}` **相加**才是撞顶总量) | 拆小任务 / 提高 `limits.maxWalltimeMs`(毫秒) |
|
|
1271
|
+
| `terminal:{kind:"failed", code:"limits.max_turns_exceeded"}` | 轮数到帽。🔴 **同上收窄**(引擎 7.13.0):帽子若落在**那一轮答案自己**身上 ⇒ `completed` + `task.interrupt_unconsumed{origin:"turns"}`,不铸本码 | 提高 `limits.maxTurns` / 拆小任务 |
|
|
1268
1272
|
| HTTP `401` | 缺 `Authorization` / 缺 `x-agent-principal`(要求时) | 补头 |
|
|
1269
1273
|
| HTTP `403` / `404`(session/run) | 不是该 principal 的资源 | 用正确身份 |
|
|
1270
1274
|
| HTTP `409`(`/v1/runs`) | 同 session 已有活跃 run | 等它完成 / 用返回的 `activeTaskId` |
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -328,27 +328,26 @@ declare const DenialLimitFallbackSchema: z.ZodObject<{
|
|
|
328
328
|
autoDenyAfterMs: z.ZodNumber;
|
|
329
329
|
}, z.core.$strict>;
|
|
330
330
|
export type DenialLimitFallback = z.infer<typeof DenialLimitFallbackSchema>;
|
|
331
|
-
/**
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
331
|
+
/**
|
|
332
|
+
* ⚰️ **退役成员的形(server 7.69.0–7.71.0 铸过,7.72.0 起永不再铸)** —— core 7.14.0 [ref] 把
|
|
333
|
+
* `classifierUnavailable` 从 ask 侧**整族**删掉(事实改骑 `GateDisposition.denied.cause`),所以本仓
|
|
334
|
+
* 的**铸造**腿(`buildApprovalCard` / 活卡帧 / inbox 行)本批全部摘掉;窄读函数
|
|
335
|
+
* `readClassifierUnavailable` 与对 core 型面的合规钉连同它们的素材一起删(素材没了,钉的两端只剩一端)。
|
|
336
|
+
*
|
|
337
|
+
* 🔴 **这只 schema 本身留着,而且是承重的,不是没删干净**:`ApprovalCardSchema` 是 `.strict()`,
|
|
338
|
+
* 而它同时是 `approval_ask.card_json` 这张**耐久列**的读契约(读侧三个 `safeParse`:重放腿、幂等重入、
|
|
339
|
+
* 对账扫描)。把成员从 strict schema 上删掉 ⇒ 7.69.0–7.71.0 铸下、升级时**仍然 pending** 的那些行
|
|
340
|
+
* 当场判假 ⇒ 「跳过 + 一次 warn」:人手上那张卡在重连后变不回来、幂等重入退成 park。
|
|
341
|
+
* ⇒ 一条耐久投影的 schema **必须读得懂它自己写过的每一版**;写侧只写当前引擎供得出的键,读侧接受
|
|
342
|
+
* 全部历史成员。这不是给退役键开特例,这是 strict + durable 这一组合的内生义务(同样的处置将适用于
|
|
343
|
+
* 未来任何一次键退役)。
|
|
344
|
+
*
|
|
345
|
+
* 形保持原样(单成员 `cause` 必填、`.strict()`):它描述的是**已经写在库里的字节**,不许再动。
|
|
346
|
+
*/
|
|
338
347
|
declare const ClassifierUnavailableSchema: z.ZodObject<{
|
|
339
348
|
cause: z.ZodString;
|
|
340
349
|
}, z.core.$strict>;
|
|
341
350
|
export type ClassifierUnavailable = z.infer<typeof ClassifierUnavailableSchema>;
|
|
342
|
-
/**
|
|
343
|
-
* `AskRequest.classifierUnavailable`(core 7.10.0 [ref])的**边界窄读** —— 活卡帧与 `card_json` 的唯一
|
|
344
|
-
* 铸造点(与 {@link readDenialLimitFallback} 同款分工),两面结构性同值。
|
|
345
|
-
*
|
|
346
|
-
* 🔴 **缺席不是断言**:缺席同时覆盖「分类器答上了」「这只 ask 没资格走分类器」「本部署没接分类器」
|
|
347
|
-
* 三形(core d.ts 逐字:read presence, never absence),消费端禁读成「分类器好着呢」。
|
|
348
|
-
* 🔴 **echo-only**:server 不据它做任何裁决 —— 熔断/超时的处置全在 core 的分类器站,据本键在本仓自铸
|
|
349
|
-
* 第二套回落判据就是同一语义面两个写者(源头修复纪律)。
|
|
350
|
-
*/
|
|
351
|
-
export declare function readClassifierUnavailable(req: unknown): ClassifierUnavailable | undefined;
|
|
352
351
|
/**
|
|
353
352
|
* `AskRequest.denialLimitFallback`(core 7.4.0 [ref])的**边界窄读** —— 活卡帧与 `card_json` 的唯一铸造点
|
|
354
353
|
* (与 {@link readRuleOffersAbsence} / {@link readProbeCause} 同款分工),两面结构性同值。
|
package/dist/approval-card.js
CHANGED
|
@@ -238,13 +238,6 @@ const _denialLimitFallbackConformance = [true, true];
|
|
|
238
238
|
void _denialLimitFallbackConformance;
|
|
239
239
|
const DenialLimitFallbackEnvelopeSchema = z.object({ denialLimitFallback: DenialLimitFallbackSchema.optional() });
|
|
240
240
|
const ClassifierUnavailableSchema = z.object({ cause: z.string().min(1).max(MAX_IDENT) }).strict();
|
|
241
|
-
const _classifierUnavailableConformance = [true];
|
|
242
|
-
void _classifierUnavailableConformance;
|
|
243
|
-
const ClassifierUnavailableEnvelopeSchema = z.object({ classifierUnavailable: ClassifierUnavailableSchema.optional() });
|
|
244
|
-
export function readClassifierUnavailable(req) {
|
|
245
|
-
const parsed = ClassifierUnavailableEnvelopeSchema.safeParse(req);
|
|
246
|
-
return parsed.success ? parsed.data.classifierUnavailable : undefined;
|
|
247
|
-
}
|
|
248
241
|
export function readDenialLimitFallback(req) {
|
|
249
242
|
const parsed = DenialLimitFallbackEnvelopeSchema.safeParse(req);
|
|
250
243
|
return parsed.success ? parsed.data.denialLimitFallback : undefined;
|
|
@@ -312,7 +305,6 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
312
305
|
const ruleEvidence = readRuleEvidence(req);
|
|
313
306
|
const ruleOffersAbsence = readRuleOffersAbsence(req);
|
|
314
307
|
const denialLimitFallback = readDenialLimitFallback(req);
|
|
315
|
-
const classifierUnavailable = readClassifierUnavailable(req);
|
|
316
308
|
const toolCallId = clip(source.toolCallId, MAX_IDENT);
|
|
317
309
|
const sourceTaskId = clip(source.sourceTaskId, MAX_IDENT);
|
|
318
310
|
const sourceAgentName = clip(source.sourceAgentName, MAX_AGENT_NAME);
|
|
@@ -335,7 +327,6 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
335
327
|
...(source.ruleOffers !== undefined && source.ruleOffers.length > 0 ? { ruleOffers: source.ruleOffers.map(copyRuleOffer) } : {}),
|
|
336
328
|
...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
|
|
337
329
|
...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
|
|
338
|
-
...(classifierUnavailable !== undefined ? { classifierUnavailable } : {}),
|
|
339
330
|
...(source.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
340
331
|
...(sourceTaskId !== undefined ? { sourceTaskId } : {}),
|
|
341
332
|
...(sourceAgentName !== undefined ? { sourceAgentName } : {}),
|
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* [ref]
|
|
2
|
+
* [ref]:`contentKind` 投影键的**单一词属主**(server 侧)。
|
|
3
3
|
*
|
|
4
4
|
* core 在 `summarizeCheckpoint`(dist/core/checkpoint-store.js)对 tool_approval 门铸
|
|
5
|
-
* `contentKind: "content_ask"`,判据一行:`tool.toolName === ASK_USER_QUESTION_TOOL_NAME
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* `contentKind: "content_ask"`,判据一行:`tool.toolName === ASK_USER_QUESTION_TOOL_NAME`。
|
|
6
|
+
* 该常量 **core 从包根导出**(`@sema-agent/core` 的 `index.d.ts`;声明在 `dist/core/ask-question.d.ts`,
|
|
7
|
+
* 值 `"AskUserQuestion"`)⇒ 本仓 **re-export 那一枚**,一个字节都不自铸。
|
|
8
|
+
* (S-213①:此处旧注曾写「该常量未从 core 包根导出」并据此在全仓手抄了 13 处同字面量 —— 那句前提
|
|
9
|
+
* 失实,而每一份手抄都是一个独立写者:core 哪天改了保留工具名,手抄的那些当场与引擎分岔且全部静默。)
|
|
8
10
|
* 三条 server 投影面(SQL `listPending` 列读 / LOCAL `listPending` blob 读 / live `listLivePending`)
|
|
9
11
|
* 全部经 {@link contentKindOf} 派生,恒与 core 的 summarize 面同词 —— 词漂移由 parity 钉看住
|
|
10
12
|
* (`test/local-checkpoint-store.test.ts` [ref] parity 格:core `summarizeCheckpoint` 对 AskUserQuestion
|
|
11
|
-
* checkpoint
|
|
13
|
+
* checkpoint 的铸值必须逐字等于本函数的答案;同文件 S-213① 格再普查「src/ 里零第二份手抄字面量」)。
|
|
12
14
|
*
|
|
13
15
|
* 🔴 与 `hasBidiControls` 的「server 不重算」纪律**不冲突**:那一位是有界扫描的结果(重算可分家),
|
|
14
16
|
* 而 contentKind 是行上 core 铸的 `toolName` 的**全函数**(一行确定性判)——派生 ≠ 重扫。
|
|
15
|
-
* 🔴 OMIT 契约与同族键一致(`boundCallId`/`gateKind`/`hasBidiControls`
|
|
17
|
+
* 🔴 OMIT 契约与同族键一致(`boundCallId`/`gateKind`/`hasBidiControls` 的纪律):
|
|
16
18
|
* `"content_ask"` 或**缺席**,恒不铸 `null`/`false`;缺席 = 不是内容问句门(普通工具门 / 非工具 park /
|
|
17
19
|
* toolName 缺席的存量行)。展示/分诊/渲染分型用,永不参与 resume / gate / CAS。
|
|
18
20
|
*/
|
|
19
|
-
export
|
|
21
|
+
export { ASK_USER_QUESTION_TOOL_NAME } from "@sema-agent/core";
|
|
22
|
+
/** 保留工具名的 **SQL 字面量**形(单引号已包)。SQL 里也不许再手抄那个词:片段从同一枚常量派生,
|
|
23
|
+
* 与 `tool-approval.ts` 的 `APPROVAL_GATE_KINDS_SQL_IN`(闭集数组 → IN 片段)同一属主纪律。
|
|
24
|
+
* 值来自 core 的常量(标识符形,无引号/反斜杠),故直接内插不产生注入面。 */
|
|
25
|
+
export declare const ASK_USER_QUESTION_SQL_LITERAL = "'AskUserQuestion'";
|
|
20
26
|
/** 三读面共用的派生:AskUserQuestion 工具门 ⇒ `"content_ask"`;其余(含 null/缺席)⇒ undefined(键缺席)。 */
|
|
21
27
|
export declare function contentKindOf(toolName: string | null | undefined): "content_ask" | undefined;
|
|
22
28
|
//# sourceMappingURL=approval-content-kind.d.ts.map
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { ASK_USER_QUESTION_TOOL_NAME } from "@sema-agent/core";
|
|
2
|
+
import { ASK_USER_QUESTION_TOOL_NAME } from "@sema-agent/core";
|
|
3
|
+
export const ASK_USER_QUESTION_SQL_LITERAL = `'${ASK_USER_QUESTION_TOOL_NAME}'`;
|
|
2
4
|
export function contentKindOf(toolName) {
|
|
3
5
|
return toolName === ASK_USER_QUESTION_TOOL_NAME ? "content_ask" : undefined;
|
|
4
6
|
}
|
package/dist/approval.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "@sema-agent/core";
|
|
2
2
|
import { operatorAsk } from "./operator-ask.js";
|
|
3
|
+
import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
|
|
3
4
|
import { isExplicitPosture } from "./posture-source.js";
|
|
4
5
|
export function hasOperatorGateIntent(config) {
|
|
5
6
|
return (config.approvalRequire.length > 0 ||
|
|
@@ -58,7 +59,7 @@ export function createDurableAskPolicy(opts) {
|
|
|
58
59
|
if (remEarly !== undefined && remEarly <= 0) {
|
|
59
60
|
return operatorAsk("RESOURCE_SUSPEND budget exhausted", `durable resource budget exhausted — human approval required to run "${req.toolName}"`);
|
|
60
61
|
}
|
|
61
|
-
if (opts.exempt && toolName !==
|
|
62
|
+
if (opts.exempt && toolName !== ASK_USER_QUESTION_TOOL_NAME) {
|
|
62
63
|
return (async () => {
|
|
63
64
|
let exempted = false;
|
|
64
65
|
try {
|
|
@@ -84,7 +85,7 @@ export function createDurableAskPolicy(opts) {
|
|
|
84
85
|
if (rem !== undefined && rem <= 0) {
|
|
85
86
|
return operatorAsk("RESOURCE_SUSPEND budget exhausted", `durable resource budget exhausted — human approval required to run "${req.toolName}"`);
|
|
86
87
|
}
|
|
87
|
-
if (toolName !==
|
|
88
|
+
if (toolName !== ASK_USER_QUESTION_TOOL_NAME && budget > 0 && autoApproved < budget) {
|
|
88
89
|
autoApproved++;
|
|
89
90
|
return { action: "allow" };
|
|
90
91
|
}
|
|
@@ -4,6 +4,7 @@ import { loadConfig } from "../../config.js";
|
|
|
4
4
|
import { createBrain } from "../../brain.js";
|
|
5
5
|
import { buildPricing } from "../../budget.js";
|
|
6
6
|
import { RemoteContainerExecutionEnv } from "../../plugins/remote-env-e2b.js";
|
|
7
|
+
import { E2B_SANDBOX_HOME } from "../../execution-lane-caps.js";
|
|
7
8
|
import { createDurableAskPolicy } from "../../approval.js";
|
|
8
9
|
import { createLeaderRunner } from "../../leader/wire.js";
|
|
9
10
|
import { runOracle as runStandaloneOracle } from "./oracle.js";
|
|
@@ -67,6 +68,7 @@ const sh = (env) => async (cmd) => {
|
|
|
67
68
|
async function provisionSandbox(rt, role, cellId) {
|
|
68
69
|
const env = new RemoteContainerExecutionEnv({
|
|
69
70
|
apiKey: rt.e2bApiKey,
|
|
71
|
+
homeDir: E2B_SANDBOX_HOME,
|
|
70
72
|
...(rt.e2bTemplate ? { template: rt.e2bTemplate } : {}),
|
|
71
73
|
timeoutMs: rt.sandboxTimeoutMs ?? 600_000,
|
|
72
74
|
metadata: { s1Cell: cellId, s1Role: role },
|
|
@@ -20,6 +20,7 @@ import { reapOrphanWorktrees, withWorktreeIsolation } from "../plugins/worktree-
|
|
|
20
20
|
import { customPkgSourceFromEnv, derivePkgSourceEnv } from "../sandbox-pkg-source.js";
|
|
21
21
|
import { effectiveHostWorkspace } from "../task-cwd.js";
|
|
22
22
|
import { isSandboxPathAdjudicationLane, sandboxPathEnvSlots, withSandboxPathEnvSlot } from "./deferred-sandbox-path-env.js";
|
|
23
|
+
import { executionLaneHomeDir } from "../execution-lane-caps.js";
|
|
23
24
|
function boundedSessionSetter(map, maxEntries) {
|
|
24
25
|
return (sid, value) => {
|
|
25
26
|
map.delete(sid);
|
|
@@ -68,6 +69,8 @@ export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
|
|
|
68
69
|
}
|
|
69
70
|
export function createExecutionEnv(ctx) {
|
|
70
71
|
const { config, logger, metrics, taskAttachmentStore } = ctx;
|
|
72
|
+
const laneHomeDir = executionLaneHomeDir(config);
|
|
73
|
+
const homeDirSeat = laneHomeDir !== undefined ? { homeDir: laneHomeDir } : {};
|
|
71
74
|
const perTaskImage = new PerTaskImageRegistry();
|
|
72
75
|
const sessionEnvSelection = new SessionEnvironmentSelection();
|
|
73
76
|
const sessionScoped = createSessionScopedRegistries();
|
|
@@ -78,6 +81,7 @@ export function createExecutionEnv(ctx) {
|
|
|
78
81
|
if (config.remoteExec?.provider === "e2b") {
|
|
79
82
|
executionEnvFactory = e2bExecutionEnvFactory({
|
|
80
83
|
apiKey: config.remoteExec.apiKey,
|
|
84
|
+
...homeDirSeat,
|
|
81
85
|
...(config.remoteExec.template ? { template: config.remoteExec.template } : {}),
|
|
82
86
|
...(config.remoteExec.timeoutMs != null ? { timeoutMs: config.remoteExec.timeoutMs } : {}),
|
|
83
87
|
...(config.remoteExec.livenessMs != null ? { livenessMs: config.remoteExec.livenessMs } : {}),
|
|
@@ -94,6 +98,7 @@ export function createExecutionEnv(ctx) {
|
|
|
94
98
|
else if (config.remoteExec?.provider === "k8s") {
|
|
95
99
|
const k8sCfg = {
|
|
96
100
|
image: config.remoteExec.image,
|
|
101
|
+
...homeDirSeat,
|
|
97
102
|
...(config.remoteExec.apiUrl ? { apiUrl: config.remoteExec.apiUrl } : {}),
|
|
98
103
|
...(config.remoteExec.token ? { token: config.remoteExec.token } : {}),
|
|
99
104
|
...(config.remoteExec.caCert ? { caCert: config.remoteExec.caCert } : {}),
|
|
@@ -116,6 +121,7 @@ export function createExecutionEnv(ctx) {
|
|
|
116
121
|
executionEnvFactory = sshExecutionEnvFactory({
|
|
117
122
|
host: config.remoteExec.host,
|
|
118
123
|
username: config.remoteExec.username,
|
|
124
|
+
...homeDirSeat,
|
|
119
125
|
privateKey: config.remoteExec.privateKey,
|
|
120
126
|
...(config.remoteExec.port != null ? { port: config.remoteExec.port } : {}),
|
|
121
127
|
...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
|
|
@@ -128,6 +134,7 @@ export function createExecutionEnv(ctx) {
|
|
|
128
134
|
hub: ctx.deviceLane.hub,
|
|
129
135
|
store: ctx.deviceLane.store,
|
|
130
136
|
enrollment: ctx.deviceLane.enrollment,
|
|
137
|
+
...homeDirSeat,
|
|
131
138
|
...(config.remoteExec.execTimeoutMs != null ? { execTimeoutMs: config.remoteExec.execTimeoutMs } : {}),
|
|
132
139
|
perSessionCwd,
|
|
133
140
|
perSessionShellEnv,
|
|
@@ -138,6 +145,7 @@ export function createExecutionEnv(ctx) {
|
|
|
138
145
|
else if (config.remoteExec?.provider === "adb") {
|
|
139
146
|
executionEnvFactory = adbExecutionEnvFactory({
|
|
140
147
|
serial: config.remoteExec.serial,
|
|
148
|
+
...homeDirSeat,
|
|
141
149
|
...(config.remoteExec.adbPath ? { adbPath: config.remoteExec.adbPath } : {}),
|
|
142
150
|
...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
|
|
143
151
|
});
|
|
@@ -160,6 +168,7 @@ export function createExecutionEnv(ctx) {
|
|
|
160
168
|
if (hostScheduler)
|
|
161
169
|
logger.info(`R7: self-wake scheduler enabled (host lane), store=${config.schedulerStorePath ?? defaultSchedulerStorePath()}`);
|
|
162
170
|
const hostCfg = {
|
|
171
|
+
...homeDirSeat,
|
|
163
172
|
...(config.remoteExec.workspaceBase ? { workspaceBase: config.remoteExec.workspaceBase } : {}),
|
|
164
173
|
...(config.remoteExec.commandTimeoutMs != null ? { commandTimeoutMs: config.remoteExec.commandTimeoutMs } : {}),
|
|
165
174
|
...(hostScheduler ? { scheduler: hostScheduler } : {}),
|
|
@@ -181,6 +190,7 @@ export function createExecutionEnv(ctx) {
|
|
|
181
190
|
executionEnvFactory = localDockerExecutionEnvFactory({
|
|
182
191
|
image: config.remoteExec.image,
|
|
183
192
|
...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
|
|
193
|
+
...homeDirSeat,
|
|
184
194
|
...(config.remoteExec.dockerPath ? { dockerPath: config.remoteExec.dockerPath } : {}),
|
|
185
195
|
...(config.remoteExec.dockerHost ? { dockerHost: config.remoteExec.dockerHost } : {}),
|
|
186
196
|
...(config.remoteExec.memory ? { memory: config.remoteExec.memory } : {}),
|
|
@@ -206,14 +216,14 @@ export function createExecutionEnv(ctx) {
|
|
|
206
216
|
});
|
|
207
217
|
}
|
|
208
218
|
else if (config.remoteExec?.provider === "host") {
|
|
209
|
-
const gitBaseEnv = new RemoteHostExecutionEnv({ workspaceDir: config.worktreeIsolation.repoRoot, inheritEnv: "all", backgroundShell: config.requirePrincipal !== true });
|
|
219
|
+
const gitBaseEnv = new RemoteHostExecutionEnv({ ...homeDirSeat, workspaceDir: config.worktreeIsolation.repoRoot, inheritEnv: "all", backgroundShell: config.requirePrincipal !== true });
|
|
210
220
|
executionEnvFactory = withWorktreeIsolation(executionEnvFactory, {
|
|
211
221
|
repoRoot: config.worktreeIsolation.repoRoot,
|
|
212
222
|
...(config.worktreeIsolation.allowedRoots ? { allowedRoots: config.worktreeIsolation.allowedRoots } : {}),
|
|
213
223
|
...(config.worktreeIsolation.commit ? { commit: config.worktreeIsolation.commit } : {}),
|
|
214
224
|
baseEnvForGit: gitBaseEnv,
|
|
215
225
|
rootEnvAt: (dir) => {
|
|
216
|
-
const env = new RemoteHostExecutionEnv({ workspaceDir: dir, backgroundShell: config.requirePrincipal !== true });
|
|
226
|
+
const env = new RemoteHostExecutionEnv({ ...homeDirSeat, workspaceDir: dir, backgroundShell: config.requirePrincipal !== true });
|
|
217
227
|
return lspManager ? evictLspOnDestroy(env, lspManager) : env;
|
|
218
228
|
},
|
|
219
229
|
logger,
|
|
@@ -191,7 +191,8 @@ export declare function createMemoryConsolidationFaces(store: {
|
|
|
191
191
|
* 🔴 这只引擎是四只 operator 面里**真读名册**的那只:`consolidationEligibility` 第四臂
|
|
192
192
|
* (engine.js `listCaptureOptOutSessions`)拿它筛掉 opt-out 会话的贡献条目 —— SQL 部署上漏喂
|
|
193
193
|
* = 驱动器读文件空名册 ⇒ 蒸馏器放大用户明说不要记的内容(S-5 真实失效形;全站同批注入律,
|
|
194
|
-
* 机器钉 capture-optout-lane ②)
|
|
194
|
+
* 机器钉 capture-optout-lane ②)。S-215 起 local 车道在引擎接线时也供(core 文件三腿,同一只控制面);
|
|
195
|
+
* 缺席 ⇒ 记忆面暗(引擎未接线),与 Runner 同本。 */
|
|
195
196
|
captureRecordStore?: SessionCaptureRecordStore;
|
|
196
197
|
}): MemoryConsolidationFaces;
|
|
197
198
|
//# sourceMappingURL=memory-consolidation.d.ts.map
|
|
@@ -6,6 +6,8 @@ import { type SandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
|
6
6
|
/** 本腿读的配置切面(`ServiceConfig` 结构满足)。两个构造口各自的切面 + lane 判别式读的 `remoteExec`。 */
|
|
7
7
|
export interface ParkedReviveGateConfigView extends DeploymentGovernanceConfigView, ApprovalBaselineConfigView {
|
|
8
8
|
readonly remoteExec?: ServiceConfig["remoteExec"];
|
|
9
|
+
/** S-213④:`executionLaneHomeDir` 读的运维声明(切面必须点名本腿消费的每一个字段,否则窄视图字面量会静默丢掉旋钮)。 */
|
|
10
|
+
readonly remoteExecHomeDir?: ServiceConfig["remoteExecHomeDir"];
|
|
9
11
|
/** host 腿裁决 env 的 cwd 占位根(见下方 `hostGateCwd`)。 */
|
|
10
12
|
readonly localDataRoot?: ServiceConfig["localDataRoot"];
|
|
11
13
|
}
|
|
@@ -6,6 +6,7 @@ import { decodeCheckpointScope } from "../security.js";
|
|
|
6
6
|
import { createApprovalBaselinePolicy, createDeploymentGovernanceInputs, } from "../deployment-governance.js";
|
|
7
7
|
import { applyRuntimeGovernance } from "../runtime-governance.js";
|
|
8
8
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
9
|
+
import { executionLaneHomeDir } from "../execution-lane-caps.js";
|
|
9
10
|
function hostGateCwd(config, localRoot) {
|
|
10
11
|
return join(config.localDataRoot ?? localRoot, "parked-revive-unrooted");
|
|
11
12
|
}
|
|
@@ -14,6 +15,8 @@ export function createParkedReviveInheritedGate(deps) {
|
|
|
14
15
|
const slots = deps.slots ?? sandboxPathEnvSlots;
|
|
15
16
|
return async (row, identity) => {
|
|
16
17
|
const metadata = await chainMetadataOf(deps, row, identity);
|
|
18
|
+
const laneHome = executionLaneHomeDir(config);
|
|
19
|
+
const homeBase = laneHome !== undefined ? { taskHome: laneHome } : {};
|
|
17
20
|
const pathAdjudication = isSandboxPathAdjudicationLane(config.remoteExec?.provider)
|
|
18
21
|
? {
|
|
19
22
|
env: new DeferredSandboxPathEnv({
|
|
@@ -21,8 +24,9 @@ export function createParkedReviveInheritedGate(deps) {
|
|
|
21
24
|
slots,
|
|
22
25
|
logger: { warn: (event, fields) => logger.warn(event, { ...(fields ?? {}), lane: config.remoteExec?.provider, leg: "parked_revive" }) },
|
|
23
26
|
}),
|
|
27
|
+
...homeBase,
|
|
24
28
|
}
|
|
25
|
-
: { env: createHostAdjudicationEnv(config, localRoot) };
|
|
29
|
+
: { env: createHostAdjudicationEnv(config, localRoot), ...homeBase };
|
|
26
30
|
const rootSessionId = row.rootSessionId;
|
|
27
31
|
const baseline = createApprovalBaselinePolicy(config, {
|
|
28
32
|
question,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { realpathSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
3
|
import { NodeExecutionEnv, QUESTION_AWAITS_RESUME, deriveAskEffective, expandTiers, resolveAskSeamForm, tightenTaskSpec } from "@sema-agent/core";
|
|
5
4
|
import { cappedCeiling } from "../budget.js";
|
|
6
5
|
import { centerPromptProvider, centerIdentityAssembled } from "../capabilities/center-prompts.js";
|
|
@@ -35,6 +34,7 @@ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSet
|
|
|
35
34
|
import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
|
|
36
35
|
import { redactSecrets } from "../trace/redact.js";
|
|
37
36
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
37
|
+
import { executionLaneHomeDir } from "../execution-lane-caps.js";
|
|
38
38
|
import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
|
|
39
39
|
export function createResolveSpec(ctx) {
|
|
40
40
|
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, noteSessionShellGate, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
|
|
@@ -409,17 +409,20 @@ export function createResolveSpec(ctx) {
|
|
|
409
409
|
const foldGovernanceAndSettings = async (body, auth, spec, gated, effMode) => {
|
|
410
410
|
const { appendLessPack, acceptedAppend, parsedSettings } = gated;
|
|
411
411
|
const hostSemanticsLane = !isSandboxPathAdjudicationLane(config.remoteExec?.provider);
|
|
412
|
+
const laneHomeBase = () => {
|
|
413
|
+
const home = executionLaneHomeDir(config);
|
|
414
|
+
return home !== undefined ? { taskHome: home } : {};
|
|
415
|
+
};
|
|
412
416
|
const pathAdjudication = hostSemanticsLane
|
|
413
417
|
? (() => {
|
|
414
418
|
const sessionCwd = auth?.sessionId ? effectiveHostWorkspace(perSessionCwd.get(auth.sessionId), {}) : undefined;
|
|
415
419
|
const realRoot = sessionCwd ?? (config.remoteExec === undefined ? process.cwd() : undefined);
|
|
416
420
|
const gateCwd = realRoot ?? join(config.localDataRoot ?? localRoot, "fs-write-gate-unrooted");
|
|
417
|
-
const realHome = config.remoteExec === undefined ? homedir() : undefined;
|
|
418
421
|
return {
|
|
419
422
|
env: new NodeExecutionEnv({ cwd: gateCwd }),
|
|
420
423
|
cwd: gateCwd,
|
|
421
424
|
...(realRoot !== undefined ? { taskRoot: realRoot } : {}),
|
|
422
|
-
...(
|
|
425
|
+
...laneHomeBase(),
|
|
423
426
|
};
|
|
424
427
|
})()
|
|
425
428
|
: {
|
|
@@ -428,6 +431,7 @@ export function createResolveSpec(ctx) {
|
|
|
428
431
|
slots: sandboxPathEnvSlots,
|
|
429
432
|
logger: { warn: (event, fields) => logger.warn(event, { ...(fields ?? {}), lane: config.remoteExec?.provider }) },
|
|
430
433
|
}),
|
|
434
|
+
...laneHomeBase(),
|
|
431
435
|
};
|
|
432
436
|
const governedBase = applyRuntimeGovernance(spec, createDeploymentGovernanceInputs(config, pathAdjudication, { engineDataRoot: memoryEngine?.root }));
|
|
433
437
|
let governed = governedBase;
|
|
@@ -476,6 +480,7 @@ export function createResolveSpec(ctx) {
|
|
|
476
480
|
const sandboxScratchpadDir = isRemoteScratchpadLane(config.remoteExec?.provider) ? remoteScratchpadDirFor(auth?.sessionId) : undefined;
|
|
477
481
|
return {
|
|
478
482
|
env: pathAdjudication.env,
|
|
483
|
+
...(pathAdjudication.taskHome !== undefined ? { taskHome: pathAdjudication.taskHome } : {}),
|
|
479
484
|
...gateCommon,
|
|
480
485
|
...(sandboxScratchpadDir ? { scratchpadDir: sandboxScratchpadDir } : {}),
|
|
481
486
|
...(gateCommon.isExempt
|
|
@@ -21,10 +21,10 @@ import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js"
|
|
|
21
21
|
import { type WorkflowCompletionInbox } from "../orchestration/workflow-completion-inbox.js";
|
|
22
22
|
import { type EngineNoticeRouter } from "../trace/engine-notice-wire.js";
|
|
23
23
|
import { WorkflowNotifyGate, type WorkflowCompletionPayload } from "../orchestration/workflow-notify-journal.js";
|
|
24
|
-
import type { ServiceWorkflowJournalStore, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
|
|
24
|
+
import type { ServiceWorkflowJournalStore, SessionCaptureRecordSource, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
|
|
25
25
|
import type { QuestionCoordinator } from "../question.js";
|
|
26
26
|
import type { ToolApprovalCoordinator } from "../tool-approval.js";
|
|
27
|
-
import type { MemoryBackend
|
|
27
|
+
import type { MemoryBackend } from "@sema-agent/core";
|
|
28
28
|
import type { MemorySyncRunner } from "../memory-sync-client.js";
|
|
29
29
|
/**
|
|
30
30
|
* `RunnerDeps.onAsk` 的装配([ref] 件4:从内联闭包提为具名导出,**纯 testability 重构、零行为变化**)。
|
|
@@ -84,26 +84,42 @@ export interface RunnerDepsCtx {
|
|
|
84
84
|
*
|
|
85
85
|
* 本键从「差异键」升进{@link createSharedRunnerDeps 共享基座}。修前它**只**挂在 subRunner 上,主
|
|
86
86
|
* runner 靠 per-task `spec.checkpointStore` 拿店 —— 而 core 的 `run_workflow` 是挂在**主** Runner
|
|
87
|
-
* (`prepare-caps-and-workflow.js:
|
|
88
|
-
* :249 是 7.11.2 的坐标
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* `
|
|
93
|
-
*
|
|
87
|
+
* (`prepare-caps-and-workflow.js:279` 的 `runner: runnerSelf`;**core 7.14.0 亲核行号**,7.12.0 时是
|
|
88
|
+
* :244、B-060 票据引的 :249 是 7.11.2 的坐标 —— 这一格每版都在漂,引用前必须对着装树重读)上的。
|
|
89
|
+
*
|
|
90
|
+
* ⚠️ **B-060 当初写下的病理有一半已被 core 7.14.0 消解,如实订正**([ref] 的 `CheckpointSeat` 重构):
|
|
91
|
+
* · **7.13.0 及以前**:`orchestration/workflow-primitives.js` 只在父侧
|
|
92
|
+
* `parentCheckpointStoreDisabled === true` 时给子代盖 `"disabled"`,**不把父侧真店透传给子代**
|
|
93
|
+
* ⇒ 子代的 `resolveCheckpointStore(spec, deps)` 落到主 runner 这一格 = `undefined`
|
|
94
|
+
* ⇒ `parkLane {capable:false, reasons:["no_checkpoint_store"]}` ⇒ 受门的调用不是 park 而是
|
|
95
|
+
* `delegation.ask_unresolvable{parkLaneExisted:false}` 直接拒(过度拒绝)。
|
|
96
|
+
* · **7.14.0 起**:core 在 prepare 一次铸出 `checkpointSeat = checkpointSeatOf(spec, resolved)`
|
|
97
|
+
* (`prepare-task.js:262`),两条委派车道**逐字复制**它到子代 —— workflow 腿经
|
|
98
|
+
* `parentCheckpointStore: checkpointSeat`(`prepare-caps-and-workflow.js:335`)落到
|
|
99
|
+
* `workflow-primitives.js:80`(现在的条件是「父侧有座 ∧(座是 disabled ∨ 子 spec 没自带店)」⇒
|
|
100
|
+
* **真店也透传**),Task 腿经 `ToolExecuteContext.checkpointStoreForChildren`。
|
|
101
|
+
* ⇒ 「子代拿不到店」这条病路 core 侧已经堵死。**但本席仍然承重**,理由是那只座**本身**就从
|
|
102
|
+
* `resolveCheckpointStore(spec, deps)` 来:主 runner 这一格空着、而 spec 又没带店时,座就是
|
|
103
|
+
* `undefined`,子代照样什么都拿不到。本席不是 core 单源的第二个写者,它就是 core 读的那个 deps 格。
|
|
94
104
|
*
|
|
95
105
|
* 🔴 host 腿的读数判据是**core 侧硬的**,不是本仓的但愿:`resolveCheckpointStore`
|
|
96
|
-
* (`checkpoint-store.js:
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
106
|
+
* (`checkpoint-store.js:370`,7.14.0 亲核)**spec 优先**——`spec.checkpointStore` 在场就用它、
|
|
107
|
+
* `"disabled"` 就是关、只有缺席才落 deps。而本仓 host 腿恒经 `resolveSpec` 的 durableEnabled 分支盖上
|
|
108
|
+
* **同一个实例**,且 `durableEnabled ⇔ checkpointStore !== undefined`(`boot/coordinators.ts:70` 与
|
|
109
|
+
* main.ts 的构造条件同源)⇒ 每一条经 `resolveSpec` 的腿看到的店逐字同一只。
|
|
110
|
+
*
|
|
111
|
+
* 🔴 **core 7.14.0 新加的响亮拒(装配纪律,本仓已合规但必须知道)**:`agents/subagent.js` 在
|
|
112
|
+
* `wantsBackground` 臂上会检查「`ScenarioDeps.checkpointStore`(→ `bg.checkpointStore`)与本 run 的座
|
|
113
|
+
* **不是同一只**」⇒ `configError("config.invalid_checkpoint_store")` **抛**。本仓 main.ts 的
|
|
114
|
+
* `scenarioDeps.checkpointStore` 与本席是同一个 `const`,run-local 两边都不接 ⇒ 两条腿都不触发;
|
|
115
|
+
* 哪天有人给这两处接了不同的店,失败是响亮的而不是静默半场。
|
|
100
116
|
*
|
|
101
117
|
* ⚠️ **如实登记一处不经解析器的直读**(codex 交叉复审提名,亲读 core dist 确认):
|
|
102
|
-
* `core/runner/run-terminal-adoption.js:
|
|
103
|
-
* `
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
118
|
+
* `core/runner/run-terminal-adoption.js:80`(7.14.0 亲核;7.13.0 时是 :74)读的是
|
|
119
|
+
* `runner.deps.checkpointStore` **本身**,不走 `resolveCheckpointStore`,**也不走座** —— 所以 7.14.0
|
|
120
|
+
* 的座重构对它一个字节都没改,本席对它仍是唯一来源。它是「宿主任务 park 时,把没排空的 steer /
|
|
121
|
+
* follow-up 迁移到 park token 上」那一步 —— 修前主 runner 这一格是空的,于是**整段迁移静默不跑**
|
|
122
|
+
* (那些输入随本腿一起丢,只留 `task.user_followup_undrained` 一类通告)。接上之后它开始真的跑。
|
|
107
123
|
*
|
|
108
124
|
* 🔴 这一席是**部署腿级**的答案,不是「哪只 Runner」的属性:一条腿要么有赎回口(HTTP 面的
|
|
109
125
|
* `/v1/approvals/:id/decide` + resume 家族)、于是它的每只 Runner 都该有店;要么没有(`run-local`
|
|
@@ -116,12 +132,14 @@ export interface RunnerDepsCtx {
|
|
|
116
132
|
backend: MemoryBackend;
|
|
117
133
|
root: string;
|
|
118
134
|
} | undefined;
|
|
119
|
-
/** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2):会话 capture opt-out
|
|
120
|
-
* (`backend.sessionCaptureRecords()`,main.ts 取**一次**、与四只 operator
|
|
121
|
-
* 注入律,机器钉 = test/memory-capture-optout-lane.test.ts ②)
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
|
|
135
|
+
/** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2)/ S-215:会话 capture opt-out 记录的**按平面取用源**
|
|
136
|
+
* (`backend.sessionCaptureRecords()`,main.ts 取**一次**、与四只 operator 面引擎同一只源 —— 全站同批
|
|
137
|
+
* 注入律,机器钉 = test/memory-capture-optout-lane.test.ts ②)。在场 ⇒ `capture:"off"` 声明与中途翻转
|
|
138
|
+
* verb 真持久;缺席 ⇒ core 读作「无店」,而 core 的 `captureCarrierUnsupported` 判据是**席位在不在**
|
|
139
|
+
* (不是「文件形能不能用」),叠上 `isRemoteExecutionEnv` 的接口形鸭子判 ⇒ 两条 ingress 都拒
|
|
140
|
+
* `config.memory_capture_unsupported`。S-215 起三条车道都供(SQL 双方言 twin / local 的控制面文件三腿),
|
|
141
|
+
* 所以缺席今天只剩一个成因:**记忆面暗**的 local 部署(那时 core 的 capture 腿本就不参与)。 */
|
|
142
|
+
sessionCaptureRecords: SessionCaptureRecordSource | undefined;
|
|
125
143
|
memorySyncRunner: MemorySyncRunner | undefined;
|
|
126
144
|
/** [ref] 件A([ref] 件3③):org 记忆准入两 seam 的装配产物(boot/org-memory.ts)。进**共享
|
|
127
145
|
* 基座**——主/sub 两 Runner 必须同源:委托子代的 prepare 同样跑准入(委托冻结的重判腿),漏挂
|
|
@@ -214,43 +232,14 @@ export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDeny
|
|
|
214
232
|
* 属性,比再抽一层共享基座更强的同源保证),不重复收纳进这里。
|
|
215
233
|
*/
|
|
216
234
|
/** 基座真实消费的窄面(Pick)——subRunner 调用点(main.ts)只需凑这 13 个字段,不必造全量 ctx。 */
|
|
217
|
-
export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "checkpointStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger" | "governanceSeams" | "mcpRevocations">;
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
* (`src/leader/wire.ts` 自拼 deps 的五处 `new Runner({...})`)一处都没有 —— core 的契约是「接线 ⇒ 走
|
|
223
|
-
* sink,缺席 ⇒ 逐字打 `console.warn`」,所以缺席不是静默而是**同一部署两条通告通道**:主车道进结构化
|
|
224
|
-
* JSON 行,leader 车道落裸 stderr,按 code 采集(`tool_result.offload_put_failed` 等)的运维面漏掉后者。
|
|
225
|
-
* 两处各写一份转发体必漂(键名/字段集),故收成一个函数;入参取**结构形** logger(leader 的 cfg 只带
|
|
226
|
-
* 可选 `warn`/`info` 两格,不是全形 `Logger`)。
|
|
227
|
-
*
|
|
228
|
-
* 🔴 `warn` 是**必填**(codex 复审第一轮,验真后修):可选链 `logger.warn?.(…)` 形在「只装了 `info` 的
|
|
229
|
-
* logger」上会把通告**吞掉** —— 比不接线还坏(不接线时 core 至少还打它自己的 `console.warn`)。所以
|
|
230
|
-
* 「有没有 warn」这个判断留在**装配点**:没有就别铸这一席,让 core 的响亮权留在原处。
|
|
231
|
-
*
|
|
232
|
-
* ## [ref]:双终点分流器(设计稿 = 黑板 [ref])
|
|
233
|
-
* 本体自 [ref] 起是**分流器**,两个终点:
|
|
234
|
-
* 1. **日志(全族,逐字保留)** —— 上面那段契约一个字节不变:任何 code、detail 原样、缺 detail 不铸键。
|
|
235
|
-
* 运维观察面零回退是本腿的硬前提(wire 半场是**增量**,不是搬家)。
|
|
236
|
-
* 2. **wire(白名单 + sessionId 路由)** —— `router.route(notice)`:码在
|
|
237
|
-
* {@link import("../trace/engine-notice-wire.js").ENGINE_NOTICE_WIRE_CODES} 内**且**归属键在场时,
|
|
238
|
-
* 投给那条会话的 run 腿(SSE + durable 账本);否则一帧不出。归属键 = 顶层 `EngineNotice.sessionId`
|
|
239
|
-
* **优先**、缺席回落 `detail.sessionId`(单点 `engineNoticeSessionId`;此前这一行只写了 detail 那一半,
|
|
240
|
-
* 顶层键 core 5.46+ 就到了)。判据整条住在
|
|
241
|
-
* `src/trace/engine-notice-wire.ts`(白名单/归属/消毒/隔离四件同源),这里只负责**先日志后分流**的次序。
|
|
242
|
-
*
|
|
243
|
-
* 🔴 次序是承重的:日志在前 ⇒ 即便 wire 半场整条坏掉(路由抛、口抛),事实仍然完整落在运维面。
|
|
244
|
-
* 反过来写就是「wire 故障吃掉运维日志」,那正是本仓 [ref] 纪律要根除的形。route 自身按 sink 隔离 +
|
|
245
|
-
* F 类留痕(`server.engine-notice.wire-sink-threw`),不向 core 回抛。
|
|
246
|
-
*
|
|
247
|
-
* 🔴 `router` **有默认值**(进程单例)而不是必填:这一席有五个以上装配点(共享基座 / leader 车道四处 /
|
|
248
|
-
* 未来任何新车道),必填会让"新车道忘了传 router"变成**静默半聋**(日志有、wire 无)——与本函数当初
|
|
249
|
-
* 被抽成具名导出的理由逐字同一个。测试要隔离时显式传自己 new 的实例。
|
|
250
|
-
*/
|
|
235
|
+
export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "checkpointStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger" | "governanceSeams" | "mcpRevocations" | "metrics">;
|
|
236
|
+
/** 转发席只需要 `inc` —— 刻意窄到这一个动词,免得把整只 Metrics 拖进这条纯转发腿。 */
|
|
237
|
+
export interface EngineNoticeMetricsSeat {
|
|
238
|
+
inc: (name: string, labels?: Record<string, string>) => void;
|
|
239
|
+
}
|
|
251
240
|
export declare function createEngineNoticeForwarder(logger: {
|
|
252
241
|
warn: (msg: string, fields?: Record<string, unknown>) => void;
|
|
253
|
-
}, router?: EngineNoticeRouter): NonNullable<RunnerDeps["onNotice"]>;
|
|
242
|
+
}, router?: EngineNoticeRouter, metrics?: EngineNoticeMetricsSeat): NonNullable<RunnerDeps["onNotice"]>;
|
|
254
243
|
/**
|
|
255
244
|
* `onNotice` **席**的装配判据本身 —— 「`warn` 真在场才铸席」这一条从此是**一个可直接调用的函数**,
|
|
256
245
|
* 不是散在装配点、只能靠扫源码文本去猜的一行三元(codex 交叉复审 R1-[medium],验真后修)。
|