@sema-agent/server 7.70.0 → 7.71.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 +6 -3
- 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 +16 -43
- package/dist/boot/runner-deps.js +10 -4
- package/dist/brain.d.ts +3 -1
- package/dist/brain.js +2 -1
- package/dist/config-catalog.js +3 -1
- package/dist/config-types.d.ts +31 -1
- package/dist/config-types.js +1 -0
- package/dist/config.js +15 -3
- 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/server.js +17 -5
- package/dist/index.d.ts +1 -1
- package/dist/leader/wire.js +4 -3
- package/dist/main.js +4 -2
- 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/metrics.d.ts +4 -0
- package/dist/observability/metrics.js +2 -0
- package/dist/observability/run-terminal-log.d.ts +4 -3
- package/dist/observability/run-terminal-log.js +4 -15
- 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 +19 -2
- package/dist/plugins/checkpoint-store-sql.js +5 -5
- 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 +54 -18
- package/dist/plugins/store-contracts.js +17 -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-local.js +3 -0
- package/dist/task-a2a.js +2 -2
- package/dist/task-mcp.js +2 -2
- package/dist/task-settings.d.ts +21 -8
- package/dist/task-settings.js +2 -2
- package/dist/trace/core-keyset-guard.d.ts +128 -8
- 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 +21 -4
- package/dist/trace/project.js +5 -3
- package/dist/trace/redact.js +10 -5
- 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
|
|
@@ -163,7 +165,7 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
163
165
|
- 成本计量**自动开**:从 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
166
|
- **近预算降级 vs 硬闸**:降级(`MODEL_DEGRADE_TO`,到 `atCostFraction` 切便宜模型)是**撑长**预算、任务仍完成(出口质量下降、发 `task.degraded` 事件可告警);硬闸(`maxCostUsd` 全额)仍在,切了便宜模型还超全额 → `limits.max_cost_exceeded` 停。
|
|
165
167
|
- **`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}
|
|
168
|
+
- **`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
169
|
- 预算闸是 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
170
|
- 🔴 **三道 $ 闸的分工(别拿进场门当中途停机用)**:三根旋钮名字都带 cost,但**判的时刻**和**能不能打断正在烧的那张单**完全不同。按「谁触发 / 什么时刻判 / 拒或停长什么样」对表:
|
|
169
171
|
|
|
@@ -322,7 +324,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
|
|
|
322
324
|
| `MEMORY_ORG_UNAVAILABLE_BACKOFF_MS` | `10000`(`[500, 600000]`) | 目录取不到之后的退避窗(只用于取数失败臂,不用于负结果) |
|
|
323
325
|
| `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
326
|
| `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"`
|
|
327
|
+
| `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
328
|
|
|
327
329
|
⚠️ **拒启动**:多租户 + 记忆面点亮 + `projects[].defaultScopes` 里有 org 键 + 目录源缺席 ⇒ 启动报错
|
|
328
330
|
并点名 projectId(该部署的每个此类请求都会在 prepare 期整拒,响亮拒启动比静默全拒服务诚实)。
|
|
@@ -1264,7 +1266,8 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1264
1266
|
| HTTP `200` + `terminal.kind:"completed"` | 成功 | 取 `result` |
|
|
1265
1267
|
| `terminal.kind:"blocked"` | agent 主动报卡住 | 看 `terminal.reason`,补信息再发 |
|
|
1266
1268
|
| `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`(毫秒) |
|
|
1269
|
+
| `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`(毫秒) |
|
|
1270
|
+
| `terminal:{kind:"failed", code:"limits.max_turns_exceeded"}` | 轮数到帽。🔴 **同上收窄**(引擎 7.13.0):帽子若落在**那一轮答案自己**身上 ⇒ `completed` + `task.interrupt_unconsumed{origin:"turns"}`,不铸本码 | 提高 `limits.maxTurns` / 拆小任务 |
|
|
1268
1271
|
| HTTP `401` | 缺 `Authorization` / 缺 `x-agent-principal`(要求时) | 补头 |
|
|
1269
1272
|
| HTTP `403` / `404`(session/run) | 不是该 principal 的资源 | 用正确身份 |
|
|
1270
1273
|
| HTTP `409`(`/v1/runs`) | 同 session 已有活跃 run | 等它完成 / 用返回的 `activeTaskId` |
|
|
@@ -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 重构、零行为变化**)。
|
|
@@ -116,12 +116,14 @@ export interface RunnerDepsCtx {
|
|
|
116
116
|
backend: MemoryBackend;
|
|
117
117
|
root: string;
|
|
118
118
|
} | 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
|
-
|
|
119
|
+
/** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2)/ S-215:会话 capture opt-out 记录的**按平面取用源**
|
|
120
|
+
* (`backend.sessionCaptureRecords()`,main.ts 取**一次**、与四只 operator 面引擎同一只源 —— 全站同批
|
|
121
|
+
* 注入律,机器钉 = test/memory-capture-optout-lane.test.ts ②)。在场 ⇒ `capture:"off"` 声明与中途翻转
|
|
122
|
+
* verb 真持久;缺席 ⇒ core 读作「无店」,而 core 的 `captureCarrierUnsupported` 判据是**席位在不在**
|
|
123
|
+
* (不是「文件形能不能用」),叠上 `isRemoteExecutionEnv` 的接口形鸭子判 ⇒ 两条 ingress 都拒
|
|
124
|
+
* `config.memory_capture_unsupported`。S-215 起三条车道都供(SQL 双方言 twin / local 的控制面文件三腿),
|
|
125
|
+
* 所以缺席今天只剩一个成因:**记忆面暗**的 local 部署(那时 core 的 capture 腿本就不参与)。 */
|
|
126
|
+
sessionCaptureRecords: SessionCaptureRecordSource | undefined;
|
|
125
127
|
memorySyncRunner: MemorySyncRunner | undefined;
|
|
126
128
|
/** [ref] 件A([ref] 件3③):org 记忆准入两 seam 的装配产物(boot/org-memory.ts)。进**共享
|
|
127
129
|
* 基座**——主/sub 两 Runner 必须同源:委托子代的 prepare 同样跑准入(委托冻结的重判腿),漏挂
|
|
@@ -214,43 +216,14 @@ export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDeny
|
|
|
214
216
|
* 属性,比再抽一层共享基座更强的同源保证),不重复收纳进这里。
|
|
215
217
|
*/
|
|
216
218
|
/** 基座真实消费的窄面(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
|
-
*/
|
|
219
|
+
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">;
|
|
220
|
+
/** 转发席只需要 `inc` —— 刻意窄到这一个动词,免得把整只 Metrics 拖进这条纯转发腿。 */
|
|
221
|
+
export interface EngineNoticeMetricsSeat {
|
|
222
|
+
inc: (name: string, labels?: Record<string, string>) => void;
|
|
223
|
+
}
|
|
251
224
|
export declare function createEngineNoticeForwarder(logger: {
|
|
252
225
|
warn: (msg: string, fields?: Record<string, unknown>) => void;
|
|
253
|
-
}, router?: EngineNoticeRouter): NonNullable<RunnerDeps["onNotice"]>;
|
|
226
|
+
}, router?: EngineNoticeRouter, metrics?: EngineNoticeMetricsSeat): NonNullable<RunnerDeps["onNotice"]>;
|
|
254
227
|
/**
|
|
255
228
|
* `onNotice` **席**的装配判据本身 —— 「`warn` 真在场才铸席」这一条从此是**一个可直接调用的函数**,
|
|
256
229
|
* 不是散在装配点、只能靠扫源码文本去猜的一行三元(codex 交叉复审 R1-[medium],验真后修)。
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -23,9 +23,15 @@ export const CONFIG_ADVISORY_CLASSIFICATIONS = new Set([
|
|
|
23
23
|
"shared-memory-not-mounted",
|
|
24
24
|
"shell-gate-off",
|
|
25
25
|
]);
|
|
26
|
-
|
|
26
|
+
const CEILING_ORIGINS = new Set(["walltime", "turns"]);
|
|
27
|
+
export function createEngineNoticeForwarder(logger, router = defaultEngineNoticeRouter, metrics) {
|
|
27
28
|
return (notice) => {
|
|
28
29
|
logger.warn("engine_notice", { code: notice.code, message: notice.message, ...(notice.detail !== undefined ? { detail: notice.detail } : {}) });
|
|
30
|
+
if (metrics !== undefined && notice.code === "task.interrupt_unconsumed") {
|
|
31
|
+
const origin = notice.detail?.origin;
|
|
32
|
+
if (typeof origin === "string" && CEILING_ORIGINS.has(origin))
|
|
33
|
+
metrics.inc("limit_ceiling_after_answer_total", { origin });
|
|
34
|
+
}
|
|
29
35
|
router.route(notice);
|
|
30
36
|
};
|
|
31
37
|
}
|
|
@@ -53,7 +59,7 @@ export function buildDeploymentPostureSeats(config) {
|
|
|
53
59
|
export function createSharedRunnerDeps(ctx) {
|
|
54
60
|
const shared = {
|
|
55
61
|
brain: ctx.brain,
|
|
56
|
-
onNotice: createEngineNoticeForwarder(ctx.logger),
|
|
62
|
+
onNotice: createEngineNoticeForwarder(ctx.logger, defaultEngineNoticeRouter, ctx.metrics),
|
|
57
63
|
tiers: ctx.config.tiers,
|
|
58
64
|
models: ctx.config.models,
|
|
59
65
|
roles: ctx.config.roles,
|
|
@@ -84,7 +90,7 @@ export function createSharedRunnerDeps(ctx) {
|
|
|
84
90
|
return shared;
|
|
85
91
|
}
|
|
86
92
|
export function createRunnerDeps(ctx) {
|
|
87
|
-
const { config, logger, metrics, localRoot, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine,
|
|
93
|
+
const { config, logger, metrics, localRoot, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine, sessionCaptureRecords, memorySyncRunner, runtimeCapsResolver, fileHistoryStore, legacyRewindBoundaryProbe, permissionRuleStore, fleetBus, workflowRunStore, workflowJournalStore, workflowAgentRegistry, workflowNotifyGate, workflowCompletionInbox, deliverWorkflowCompletion, getRunStore, sessionShellGateExplicitlyOff, } = ctx;
|
|
88
94
|
const sharedRunnerDeps = createSharedRunnerDeps(ctx);
|
|
89
95
|
const workflowModelAllowlist = workflowModelAllowlistFor(config);
|
|
90
96
|
const selfOrchestrationDeps = config.selfOrchestrationEnabled
|
|
@@ -152,7 +158,7 @@ export function createRunnerDeps(ctx) {
|
|
|
152
158
|
sessionStore,
|
|
153
159
|
memoryBackend: memoryEngine ? memoryEngine.backend : undefined,
|
|
154
160
|
memoryEngineDir: memoryEngine ? memoryEngine.root : undefined,
|
|
155
|
-
memoryCaptureRecordStore:
|
|
161
|
+
memoryCaptureRecordStore: sessionCaptureRecords !== undefined ? (plane) => sessionCaptureRecords(plane) : undefined,
|
|
156
162
|
onMemoryHarvestReport: memoryEngine
|
|
157
163
|
? (report, info) => {
|
|
158
164
|
metrics.inc("memory_harvest_total", { ok: String(report.ok), phase: info.phase, incident: report.incident?.kind ?? "none" });
|
package/dist/brain.d.ts
CHANGED
|
@@ -15,7 +15,9 @@ export declare const FAST_FAIL_MAX_RETRIES = 2;
|
|
|
15
15
|
* - **Tiered timeouts** (1.38, `connectTimeoutMs`/`firstTokenTimeoutMs`) are passed into each
|
|
16
16
|
* openai-compatible brain at creation — they bound "gateway never accepts the connection" and
|
|
17
17
|
* "stream opened but hangs without a token" as retryable `[network]` failures. Default ON
|
|
18
|
-
* (30s
|
|
18
|
+
* (connect 30s per the fleet-audit recommendation; first-token 600s since S-202 — a self-hosted backend's long
|
|
19
|
+
* prefill legitimately takes minutes to the first byte, and 120s turned that into a retry handing the
|
|
20
|
+
* same prompt back to the same queue. env =0 disables; env =120000 restores the old posture).
|
|
19
21
|
* - **Circuit breaker** (`createCircuitBreakerBrain`) wraps the *primary* gateway route(s) when
|
|
20
22
|
* `MODEL_CIRCUIT_BREAKER=true`: after N transient failures it opens and fast-fails, so failover
|
|
21
23
|
* switches to the bare backup immediately instead of waiting out each timeout. The last gateway
|
package/dist/brain.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { adjudicateModelRoute, createAnthropicBrain, createCircuitBreakerBrain, createDegradingBrain, createFailoverBrain, createOpenAIBrain, createRoutingBrain, hasAuthCarrier, resolveRouteCredential, routeRefusalText, } from "@sema-agent/core";
|
|
2
2
|
import { normalizeBaseUrl } from "@sema-agent/core";
|
|
3
|
+
import { MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT } from "./config-types.js";
|
|
3
4
|
import { resolveModelApiKey } from "./key-resolver.js";
|
|
4
5
|
function isOffDeploymentRoot(baseUrl, config) {
|
|
5
6
|
if (!baseUrl)
|
|
@@ -16,7 +17,7 @@ function degradeDeclaredRoot(fallbackBaseUrl, config) {
|
|
|
16
17
|
export const FAST_FAIL_MAX_RETRIES = 2;
|
|
17
18
|
const DEFAULT_RESILIENCE = {
|
|
18
19
|
connectTimeoutMs: 30_000,
|
|
19
|
-
firstTokenTimeoutMs:
|
|
20
|
+
firstTokenTimeoutMs: MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT,
|
|
20
21
|
idleTimeoutMs: 300_000,
|
|
21
22
|
circuitBreaker: false,
|
|
22
23
|
failureThreshold: 5,
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
2
|
import { THINKING_FORMATS } from "./model-compat.js";
|
|
3
|
+
import { MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT } from "./config-types.js";
|
|
3
4
|
import { webSearchConfigFromEnv } from "./plugins/web-search.js";
|
|
4
5
|
import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
|
|
5
6
|
export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
|
|
@@ -274,7 +275,7 @@ export const CONFIG_CATALOG = [
|
|
|
274
275
|
r("MODEL_DEGRADE_TO", "modelPlane", "string", "降级目标模型(设了=开降级;外部网关名不必在目录)", { danger: BILL }),
|
|
275
276
|
r("MODEL_DEGRADE_TO_VISION", "modelPlane", "boolean", "降级目标的 vision 声明(false ⇒ 带图任务丢降级不丢图)", { staticDefault: "false" }),
|
|
276
277
|
r("MODEL_EXTRA_BODY", "modelPlane", "opaque", "生成参数直通 JSON(top_k/logit_bias 等;必须静态 —— 前缀缓存键序)", { danger: SEC }),
|
|
277
|
-
r("MODEL_FIRST_TOKEN_TIMEOUT_MS", "modelPlane", "number", "首 token 超时(ms;0=关 ——
|
|
278
|
+
r("MODEL_FIRST_TOKEN_TIMEOUT_MS", "modelPlane", "number", "首 token 超时(ms;0=关 —— 开流不吐字的唯一看门狗;自托管长 prefill 后端按此档留足)", { staticDefault: String(MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT) }),
|
|
278
279
|
r("MODEL_FREQUENCY_PENALTY", "modelPlane", "number", "frequency_penalty 直通(压过 EXTRA_BODY 同键)"),
|
|
279
280
|
r("MODEL_GATEWAY_BASEURL", "modelPlane", "url", "openai 兼容网关 base URL(默认 localhost 占位 —— 公开分发不烤内网坐标)", { staticDefault: "http://127.0.0.1:8000/v1", configKey: "gatewayBaseUrl", center: cModels("models[].baseUrl") }),
|
|
280
281
|
r("MODEL_GATEWAY_FALLBACK_URLS", "modelPlane", "url", "网关故障转移地址表(CSV)", { configKey: "gatewayFallbackUrls" }),
|
|
@@ -345,6 +346,7 @@ export const CONFIG_CATALOG = [
|
|
|
345
346
|
r("REAP_INTERVAL_SEC", "limitsHttp", "number", "reaper 周期(秒)", { staticDefault: "60", configKey: "reapIntervalSec" }),
|
|
346
347
|
r("REAP_RUN_STALE_SEC", "limitsHttp", "number", "run 活性判陈旧阈(秒)", { staticDefault: "120", configKey: "runStaleSec", center: cLimits("runStaleSec") }),
|
|
347
348
|
r("REMOTE_EXEC", "orchestration", "enum", "执行车道选择(点名而配不齐/认不出 ⇒ 拒启,绝不静默降级到 host/stub)", { enumValues: ["e2b", "k8s", "ssh", "adb", "host", "local-docker", "device"], danger: SEC, derivedDefaultNote: "缺席:CONFIG_PROVIDER=local ⇒ host;其余 ⇒ 进程内 stub", configKey: "remoteExec", resolve: (c) => c.remoteExec?.provider ?? null }),
|
|
349
|
+
r("REMOTE_EXEC_HOME_DIR", "orchestration", "path", "执行环境的 home 目录(`~/` 形权限规则的基;core ExecutionEnv.homeDir 座)", { danger: SEC, derivedDefaultNote: "缺席:host/in-process ⇒ os.homedir();e2b ⇒ /home/user;k8s/local-docker/ssh/adb/device ⇒ 无(`~/` 规则 422 响亮拒)", configKey: "remoteExecHomeDir", resolve: (c) => c.remoteExecHomeDir ?? null }),
|
|
348
350
|
r("REQUIRE_PRINCIPAL", "auth", "boolean", "多租户开关(posture 判别位:true ⇒ CC 能力族默认收紧)", { staticDefault: "false", danger: SEC, configKey: "requirePrincipal" }),
|
|
349
351
|
r("RESOURCE_SUSPEND", "approval", "boolean", "durable 路径 opt-in core resourceSuspend(预算/preempt ⇒ 可恢复挂起)", { staticDefault: "false", configKey: "resourceSuspend" }),
|
|
350
352
|
r("RESOURCE_SUSPEND_MAX_SLICES", "approval", "number", "resume 链片数上限(0=不设;零进展循环兜底)", { staticDefault: "0", configKey: "resourceSuspendMaxSlices" }),
|