@sema-agent/server 7.99.0 → 7.100.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/MIGRATION.md +13 -0
- package/USAGE.md +75 -52
- package/dist/approval-ask-machine.d.ts +10 -0
- package/dist/approval-ask-machine.js +3 -0
- package/dist/approval-card.d.ts +74 -1
- package/dist/approval-card.js +41 -8
- package/dist/approval.d.ts +44 -20
- package/dist/approval.js +14 -6
- package/dist/boot/config-center.d.ts +16 -8
- package/dist/boot/config-center.js +39 -81
- package/dist/boot/execution-env.d.ts +1 -2
- package/dist/boot/execution-env.js +2 -2
- package/dist/boot/leader.js +12 -9
- package/dist/boot/memory-consolidation.d.ts +38 -0
- package/dist/boot/memory-consolidation.js +35 -0
- package/dist/boot/resolve-spec.d.ts +0 -1
- package/dist/boot/resolve-spec.js +4 -3
- package/dist/boot/runner-deps.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +3 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -3
- package/dist/boot/stage-07-capability-layer.js +6 -6
- package/dist/boot/stage-08-reapers.d.ts +3 -4
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -3
- package/dist/boot/stage-10-http-server.d.ts +2 -3
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/capabilities/center-plugins.d.ts +14 -2
- package/dist/capabilities/center-plugins.js +13 -5
- package/dist/capabilities/hands-lane.js +2 -1
- package/dist/capabilities/scenarios.js +18 -5
- package/dist/capabilities/tool-defer.d.ts +2 -2
- package/dist/config-catalog.d.ts +2 -2
- package/dist/config-catalog.js +12 -13
- package/dist/config-center/apply-effective.d.ts +34 -39
- package/dist/config-center/apply-effective.js +100 -64
- package/dist/config-center/apply-ledger.d.ts +35 -16
- package/dist/config-center/apply-ledger.js +25 -18
- package/dist/config-center/facade.d.ts +11 -5
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/hot-keys-registry.d.ts +11 -4
- package/dist/config-center/hot-keys-registry.js +11 -7
- package/dist/config-center/restart-signal.d.ts +21 -14
- package/dist/config-center/restart-signal.js +16 -22
- package/dist/config-center/types.d.ts +3 -3
- package/dist/config-types.d.ts +22 -7
- package/dist/cross-session-settings.js +1 -1
- package/dist/host-lsp-manager.d.ts +29 -0
- package/dist/host-lsp-manager.js +14 -0
- package/dist/http/active-run-conflict.d.ts +2 -12
- package/dist/http/active-run-conflict.js +1 -6
- package/dist/http/admission.js +8 -0
- package/dist/http/resume-legs.d.ts +1 -1
- package/dist/http/resume-legs.js +14 -10
- package/dist/http/route-ctx.d.ts +2 -2
- package/dist/http/routes/approvals-assistant.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +2 -2
- package/dist/http/routes/memory-compliance.js +5 -0
- package/dist/http/routes/memory-origin.js +5 -0
- package/dist/http/routes/sessions.js +6 -0
- package/dist/http/routes/tasks.js +7 -6
- package/dist/http/server.d.ts +6 -5
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/leader/wire.d.ts +20 -12
- package/dist/leader/wire.js +7 -6
- package/dist/memory-layer-preflight.d.ts +46 -0
- package/dist/memory-layer-preflight.js +121 -0
- package/dist/memory-operator-faces.js +17 -1
- package/dist/observability/corrupt-read-seat.d.ts +21 -4
- package/dist/observability/corrupt-read-seat.js +31 -0
- package/dist/observability/fail-open.d.ts +8 -4
- package/dist/observability/fail-open.js +8 -4
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -0
- package/dist/observability/secret-env-scrub.js +1 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
- package/dist/orchestration/workflow-completion-inbox.js +8 -3
- package/dist/plugins/approval-ask-store-file.d.ts +20 -2
- package/dist/plugins/approval-ask-store-file.js +21 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
- package/dist/plugins/approval-ask-store-memory.js +39 -13
- package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
- package/dist/plugins/approval-ask-store-sql.js +27 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
- package/dist/plugins/checkpoint-store-sql.js +19 -2
- package/dist/plugins/local-checkpoint-store.js +2 -0
- package/dist/plugins/mailbox-store-sql.d.ts +7 -5
- package/dist/plugins/mailbox-store-sql.js +6 -4
- package/dist/plugins/permission-rule-store-file.js +3 -2
- package/dist/plugins/sql-json-column.d.ts +16 -3
- package/dist/plugins/sql-json-column.js +15 -4
- package/dist/plugins/web-search.d.ts +42 -22
- package/dist/plugins/web-search.js +141 -49
- package/dist/run-local.js +6 -5
- package/dist/runs.js +6 -5
- package/dist/runtime-governance.d.ts +50 -3
- package/dist/runtime-governance.js +5 -0
- package/dist/server-secret-env.d.ts +4 -2
- package/dist/task-settings.d.ts +1 -1
- package/dist/tool-approval.d.ts +107 -19
- package/dist/tool-approval.js +64 -15
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +22 -5
- package/dist/trace/projection-drop.d.ts +2 -0
- package/dist/trace/projection-drop.js +6 -0
- package/dist/trace/sema-provenance.d.ts +4 -1
- package/dist/trace/sema-provenance.js +1 -1
- package/dist/trace/task-notification-facets.d.ts +65 -0
- package/dist/trace/task-notification-facets.js +31 -0
- package/dist/trace/wire-projection-faces.d.ts +3 -3
- package/dist/trace/wire-projection-faces.js +3 -3
- package/package.json +3 -3
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { resolveActiveTiers } from "@sema-agent/settings-schema";
|
|
2
|
+
import { DEFAULT_PLUGIN_ALLOW_HOSTS, materializablePlugins } from "../capabilities/center-plugins.js";
|
|
2
3
|
import { resolveDefaultModelName } from "./apply-effective.js";
|
|
3
4
|
import { CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
|
|
4
|
-
const RESTART_SLICES = ["skills", "mcp", "a2a", "scenarios", "
|
|
5
|
+
const RESTART_SLICES = ["skills", "mcp", "a2a", "scenarios", "degrade-route", "read-face", "memory-consolidation"];
|
|
5
6
|
export function stableStringify(v) {
|
|
6
7
|
if (v === null || typeof v !== "object")
|
|
7
8
|
return JSON.stringify(v) ?? "null";
|
|
@@ -26,31 +27,29 @@ function planeEntriesFingerprint(rows) {
|
|
|
26
27
|
const enabled = enabledOnly(rows);
|
|
27
28
|
return enabled ? enabled.map((m) => catalogEntryFingerprint(m)) : null;
|
|
28
29
|
}
|
|
30
|
+
function restartSlicePair(boot, current, slice, ctx) {
|
|
31
|
+
if (slice === "memory-consolidation") {
|
|
32
|
+
const seat = ctx?.consolidationSeat;
|
|
33
|
+
return seat === undefined ? [null, null] : [seat.baked, seat.current];
|
|
34
|
+
}
|
|
35
|
+
return [restartSliceValue(boot, slice, ctx), restartSliceValue(current, slice, ctx)];
|
|
36
|
+
}
|
|
29
37
|
function restartSliceValue(eff, slice, ctx) {
|
|
30
38
|
if (!eff)
|
|
31
39
|
return null;
|
|
32
40
|
switch (slice) {
|
|
33
|
-
case "skills":
|
|
34
|
-
|
|
41
|
+
case "skills": {
|
|
42
|
+
const skills = enabledOnly(eff.skills?.skills);
|
|
43
|
+
const materialized = materializablePlugins(eff, ctx?.pluginsAllowHosts ?? DEFAULT_PLUGIN_ALLOW_HOSTS);
|
|
44
|
+
const plugins = materialized.length > 0 ? materialized : null;
|
|
45
|
+
return skills === null && plugins === null ? null : { skills, plugins };
|
|
46
|
+
}
|
|
35
47
|
case "mcp":
|
|
36
48
|
return enabledOnly(eff.mcp?.servers);
|
|
37
49
|
case "a2a":
|
|
38
50
|
return enabledOnly(eff.a2a?.peers);
|
|
39
51
|
case "scenarios":
|
|
40
52
|
return enabledOnly(eff.scenarios?.scenarios);
|
|
41
|
-
case "runtime-gates": {
|
|
42
|
-
const ar = eff.governance?.approvalRequire;
|
|
43
|
-
return Array.isArray(ar) ? { approvalRequire: ar } : null;
|
|
44
|
-
}
|
|
45
|
-
case "models-tiers": {
|
|
46
|
-
const active = eff.models ? resolveActiveTiers(eff.models) : undefined;
|
|
47
|
-
if (!active || Object.keys(active).length === 0)
|
|
48
|
-
return null;
|
|
49
|
-
const enabled = enabledOnly(eff.models?.models);
|
|
50
|
-
const names = new Set((enabled ?? []).map((m) => m.name));
|
|
51
|
-
const def = resolveDefaultModelName(eff, (n) => names.has(n), enabled?.[0]?.name ?? "");
|
|
52
|
-
return { models: planeEntriesFingerprint(eff.models?.models), tiers: active, default: def.name };
|
|
53
|
-
}
|
|
54
53
|
case "read-face": {
|
|
55
54
|
const d = eff.readFace;
|
|
56
55
|
if (!d)
|
|
@@ -70,8 +69,7 @@ function restartSliceValue(eff, slice, ctx) {
|
|
|
70
69
|
}
|
|
71
70
|
export function restartReasons(boot, current, ctx) {
|
|
72
71
|
return RESTART_SLICES.filter((s) => {
|
|
73
|
-
const b =
|
|
74
|
-
const c = restartSliceValue(current, s, ctx);
|
|
72
|
+
const [b, c] = restartSlicePair(boot, current, s, ctx);
|
|
75
73
|
if (stableStringify(b) === stableStringify(c))
|
|
76
74
|
return false;
|
|
77
75
|
if (s === "mcp" && ctx?.mcpRevocationLive === true && Array.isArray(b) && Array.isArray(c) && c.length < b.length) {
|
|
@@ -89,10 +87,6 @@ export function restartReasons(boot, current, ctx) {
|
|
|
89
87
|
return true;
|
|
90
88
|
});
|
|
91
89
|
}
|
|
92
|
-
export function planeHasActiveTiers(eff) {
|
|
93
|
-
const active = eff.models ? resolveActiveTiers(eff.models) : undefined;
|
|
94
|
-
return active !== undefined && Object.keys(active).length > 0;
|
|
95
|
-
}
|
|
96
90
|
export function modelPlaneChanged(prev, next) {
|
|
97
91
|
if (!prev)
|
|
98
92
|
return true;
|
|
@@ -303,7 +303,7 @@ export interface EffectiveConfig {
|
|
|
303
303
|
* settings-schema 0.6 起住 `governance` 域,0.7.1 撤了 runtime 位的双写镜像;此后 `RuntimeConfig` 是 strip
|
|
304
304
|
* 的 zod object,两条腿(远端 `readEffectiveWire` / 本地 FileConfigStore→`buildEffective`)都把旧位 strip
|
|
305
305
|
* 掉、由 `legacyGovernanceFromRuntime` 抬进 governance ⇒ 本仓在 runtime 位上读它们的每一行都是死码。
|
|
306
|
-
* 旧类型面上留着这三键,让 `runtime-gates` 重启片一直读着一个生产上恒缺席的位(审批闸收紧静默不生效)。 */
|
|
306
|
+
* 旧类型面上留着这三键,让(已于 S-668 波 2 退役的)`runtime-gates` 重启片一直读着一个生产上恒缺席的位(审批闸收紧静默不生效)。 */
|
|
307
307
|
runtime?: {
|
|
308
308
|
rateLimitPerMin?: number;
|
|
309
309
|
maxTaskCostUsd?: number;
|
|
@@ -342,8 +342,8 @@ export interface EffectiveConfig {
|
|
|
342
342
|
/** settings-schema 0.6 起治理三件(autonomy/commandPolicy/approvalRequire)的**唯一**承运域(0.7.1 撤
|
|
343
343
|
* runtime 位双写镜像;顺序契约当年已走完:消费端先切、center 再撤)。键集与 schema `GovernanceConfig.shape`
|
|
344
344
|
* 逐字相等([ref] 件3 对账门钉)。消费:autonomy/commandPolicy 逐请求热(`stageRuntimeHot`,boot+refresh);
|
|
345
|
-
* approvalRequire
|
|
346
|
-
* 同一 presence 规则(undefined = 不管 →
|
|
345
|
+
* approvalRequire 自 S-668 波 2 起同样热(`stageApprovalGate`,boot+refresh 双腿,有自己的拒绝臂)。
|
|
346
|
+
* 同一 presence 规则(undefined = 不管 → 回 env 基线,非粘)。 */
|
|
347
347
|
governance?: {
|
|
348
348
|
autonomy?: Autonomy;
|
|
349
349
|
commandPolicy?: CommandRule[];
|
package/dist/config-types.d.ts
CHANGED
|
@@ -472,8 +472,9 @@ export interface ServiceConfigFlat {
|
|
|
472
472
|
/** registry-core 0.9.0 档位组: the resolved active tier table (`tier word → catalog model name`,
|
|
473
473
|
* e.g. `{pro: "ds-pro"}`) fed to `RunnerDeps.tiers` (core 1.265 expandTiers — tier words + CC aliases become
|
|
474
474
|
* catalog keys). NO env source — populated ONLY by applyEffective from `models.tierGroups`/`activeTierGroup`
|
|
475
|
-
* via registry-core `resolveActiveTiers` (single semantic source). Empty = INERT (core contract).
|
|
476
|
-
* Runner
|
|
475
|
+
* via registry-core `resolveActiveTiers` (single semantic source). Empty = INERT (core contract). Expanded at
|
|
476
|
+
* Runner construction and re-expanded on every refresh-time plane swap (`Runner.swapDeps`) — HOT, same as
|
|
477
|
+
* models/roles; mutateInPlace keeps the reference. */
|
|
477
478
|
tiers: Record<string, string>;
|
|
478
479
|
/** 142-S4(registry-core 0.10.0 projects 域):center 项目登记簿的 service 消费面 —
|
|
479
480
|
* 键=projectId(PROJECT_ID_REGEX,generic lowercase UUID)。消费点:①`memoryScopeFor` 第三参数
|
|
@@ -1738,6 +1739,12 @@ export interface ServiceConfigFlat {
|
|
|
1738
1739
|
* 位:与「查无活流」同一件事实的两个时刻,详见 `askBroadcast` 的该臂注))。`"timeout"` 宿主自报只留在 (b)(c) ——
|
|
1739
1740
|
* 结束等待的确实是本仓的窗;其余三臂结束等待的是容量/连接/装配,自报恒缺席。
|
|
1740
1741
|
*
|
|
1742
|
+
* 🔴 **`deny` 交给引擎的形(S-689)**:部署政策拒对象臂 `{allow:false, settledBy:"policy", reason}` ⇒ 引擎结算
|
|
1743
|
+
* `policy_refused`(`who.party:"none"`)、模型读策略句(「部署政策拒了、此处没有交互审批,别重试、做不需审批的
|
|
1744
|
+
* 部分」)、run 不以「用户拒绝」收束而是继续。修前是裸 `false` —— 引擎契约里那就是「一个人拒了」
|
|
1745
|
+
* (`human_refused` / `haltedOnUserRejection`),与本键「没有人会来」的自声明正好相反。(b)(c) 两臂照旧带
|
|
1746
|
+
* `"timeout"` 自报(`approval_window_expired`)。
|
|
1747
|
+
*
|
|
1741
1748
|
* 🔴 **射程边界(成文,不是遗漏)**:
|
|
1742
1749
|
* · 唯一让本旋钮**无处施加**的键是 `TOOL_APPROVAL_ENABLED=false`(协调器整体不构造,core 走它自己的
|
|
1743
1750
|
* headless 姿态)。**不造推断腿**:旋钮只在协调器在场时有意义,这一句是成文而不是代码分支。
|
|
@@ -1747,16 +1754,24 @@ export interface ServiceConfigFlat {
|
|
|
1747
1754
|
* 而不是 server 的 ask 行,「无 askStore 但有 durable 设施」正是 R-13 点名的**主受益部署**。
|
|
1748
1755
|
* 要回滚 R-13 的行为面,键是 `UNATTENDED_APPROVAL_POLICY=deny`,**不是**协议开关。
|
|
1749
1756
|
* 射程钉:`test/unattended-approval-policy.test.ts` 的 codex R2-F2 两格。
|
|
1750
|
-
* · APPROVAL_REQUIRE
|
|
1751
|
-
*
|
|
1757
|
+
* · APPROVAL_REQUIRE 名单工具:**要不要 ask** 由 `createApprovalBaselinePolicy` 的 durable gate 类裁决,那一层
|
|
1758
|
+
* 结构上读不到本旋钮;但那只 ask 的**答法**在协调器在场时与治理 ask 同走流内臂、同政策(引擎先问活席
|
|
1759
|
+
* `onAsk`,答 `"unavailable"` 才 park)⇒ `deny` 部署对名单工具同样当场拒。旧注「不经流内臂、不受本旋钮
|
|
1760
|
+
* 影响」失实(test 黑盒证伪;S-689 的真 HTTP 格 `test/unattended-deny-settlement-e2e.test.ts` 用的正是
|
|
1761
|
+
* 名单工具)。
|
|
1752
1762
|
* · **持久回放**(窗到期后向真源收敛、`ensureAsk` 幂等重入撞上已终结的行)读出 PARKING/PARKED 时
|
|
1753
1763
|
* **同样按旋钮改判** —— 它们与 (a) 是同一件事(赢家可能是别的副本或上一次调用),不统一的话同一只
|
|
1754
1764
|
* ask 的终局会取决于「谁赢了那把 CAS / 重试没重试」。人的真决议回放不受影响。
|
|
1755
1765
|
* · 唯一**不受**改判的是 `ensureAsk` 身份未定 / 坏行两臂(「身份不确定」的处置,不是「没有人」的
|
|
1756
1766
|
* 处置),于是 `deny` 部署上仍可能落极少数 park(店抖动期),已知且有意的残余。
|
|
1757
|
-
* · `deny`
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
1767
|
+
* · 🔴 `deny` 部署上政策结算**当场落行**(NP-2,7.100.0):窗到期 / emit 全灭两臂的店终局 claim 是 `refuse` 意图
|
|
1768
|
+
* —— 这一只 ask 行 CAS 成 DECIDED(deny) + `settled_by="policy"`(按 ask 作用域,不碰批、不连坐兄弟),与交给
|
|
1769
|
+
* 引擎的那一只同一个来源词 ⇒ 重入 / 收敛后重入回放同一个政策拒,不退化成裸 `false`(人拒)。修前行停 PARKING,
|
|
1770
|
+
* run 完成后收敛器转 DENIED 且不带来源。仍按 PARKING 记账的只剩别的写者推进的行(park 政策副本 / 切换政策之前)
|
|
1771
|
+
* 与崩溃后收敛器孤儿腿代打 expire 的行(后者 run 终局后成 DENIED、归因 `routing_failure`,已知残余)。
|
|
1772
|
+
* · 🔴 `deny` 部署上**批是单 ask 批**(S-689 codex R1-F1;`deriveAskScopedBatchId`):批是 park 路由的单位,
|
|
1773
|
+
* 整腿一批的前提是「批一关腿就随 park 结束」;deny 下引擎拿到部署政策拒后腿继续跑,整腿一批会让首只到期
|
|
1774
|
+
* 把批关掉(同腿后续的卡人批不动)、把兄弟连坐成 VOID(裸 `false` = 人拒)。
|
|
1760
1775
|
*/
|
|
1761
1776
|
unattendedApprovalPolicy: UnattendedApprovalPolicy;
|
|
1762
1777
|
/** [ref]① (clay A 案 = workflow 权限全面 CC parity): workflow 子 agent 的默认权限基线与主 LLM 同权(base `{}`)。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const CROSS_SESSION_INBOUND_LAYER_NAMES = ["managed", "user", "repo"];
|
|
2
2
|
export const CROSS_SESSION_INBOUND_LAYER_SOURCES = {
|
|
3
3
|
managed: {
|
|
4
|
-
source: "CROSS_SESSION_INBOUND —— 部署/组织层旋钮(
|
|
4
|
+
source: "CROSS_SESSION_INBOUND —— 部署/组织层旋钮(词表取自 core CROSS_SESSION_INBOUND_SETTINGS:7.30.0 起 wake|next-turn|hold|refuse,旧词 accept 拒启;未设=本层不设)",
|
|
5
5
|
wired: true,
|
|
6
6
|
},
|
|
7
7
|
user: {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-616(core 7.30.1 #1073 提货)—— **宿主车道本地语言服务器**的唯一构造点。
|
|
3
|
+
*
|
|
4
|
+
* ## 病(DEBTS S-684 同轴的第二条,7.99.0 修复验证 opus 腿普查带出)
|
|
5
|
+
* core 的 `NodeLspManager` 默认 spawn(`defaultLspSpawn`)在 7.30.1 之前**不带 env** ⇒ 语言服务器整份继承本进程的
|
|
6
|
+
* `process.env`,脑钥 / 仓库令牌 / 本仓自己的凭据键全在里面,而语言服务器会执行仓里的代码(构建脚本、插件、
|
|
7
|
+
* 工作区配置)。7.30.1 起 core 默认改成 `scrubSecretEnv(process.env)`(按键名**形状**剥),并开了
|
|
8
|
+
* `NodeLspManagerOptions.env` 一格让宿主自己交 env(core 帖 [ref] ④;core 确认本仓的正确读法 = 交 host 车道**同一只**
|
|
9
|
+
* `scrubServerSecrets` 的结果)。
|
|
10
|
+
*
|
|
11
|
+
* ## 规则(一句)
|
|
12
|
+
* 语言服务器子进程的 env = {@link scrubServerSecrets}(process.env) —— 与模型可驱动的 host shell(`inheritEnv:"scrub"`)
|
|
13
|
+
* 和 project-memory 的 git 子进程**同一只函数**:目录 `type:"secret"` 键按名剥 ∘ core 形状规则按形剥。只交 core 的默认
|
|
14
|
+
* 会漏掉形状规则认不出的本仓凭据键(今天是 `OTEL_EXPORTER_OTLP_HEADERS`),那正是 S-684 当初的病形。
|
|
15
|
+
* 剥掉了什么经既有观测面留痕(site = `server.lsp.spawn-env`,每 site×key 一次披露 + 逐次计数)—— core 那侧
|
|
16
|
+
* 刻意不报(无收集器,KL),本仓这一格补上。
|
|
17
|
+
*
|
|
18
|
+
* ## 时刻
|
|
19
|
+
* env 在**构造时**取一次快照(boot / run-local 起腿时),而不是 core 默认那样每次 spawn 现取:`NodeLspManagerOptions.env`
|
|
20
|
+
* 是一份静态记录。本仓的 `process.env` 是 boot 期输入(热配置走配置中心,不改 `process.env`),两者在本仓部署上同值。
|
|
21
|
+
*
|
|
22
|
+
* ## 两个调用点
|
|
23
|
+
* `boot/execution-env.ts`(服务的 host 车道)与 `run-local.ts`(纯本地 CLI)此前各写一遍 `new NodeLspManager({ log })`;
|
|
24
|
+
* 现在都经本函数 —— 构造形只有一份,剥除规则也就只能有一份(源码对账钉在 `test/secret-env-scrub-disclosure.test.ts`)。
|
|
25
|
+
*/
|
|
26
|
+
import { NodeLspManager } from "@sema-agent/core";
|
|
27
|
+
/** 构造宿主车道的本地语言服务器管理器(`create*`:返回活对象)。`log` = 管理器的观测钩(open/heal/degrade/evict 事件)。 */
|
|
28
|
+
export declare function createHostLspManager(log: (event: string, fields: Record<string, unknown>) => void): NodeLspManager;
|
|
29
|
+
//# sourceMappingURL=host-lsp-manager.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NodeLspManager } from "@sema-agent/core";
|
|
2
|
+
import { recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
|
|
3
|
+
import { scrubServerSecrets } from "./server-secret-env.js";
|
|
4
|
+
export function createHostLspManager(log) {
|
|
5
|
+
const findings = [];
|
|
6
|
+
const scrubbed = scrubServerSecrets(process.env, findings);
|
|
7
|
+
recordSecretEnvScrub("server.lsp.spawn-env", findings);
|
|
8
|
+
const env = {};
|
|
9
|
+
for (const [k, v] of Object.entries(scrubbed))
|
|
10
|
+
if (v !== undefined)
|
|
11
|
+
env[k] = v;
|
|
12
|
+
return new NodeLspManager({ log, env });
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=host-lsp-manager.js.map
|
|
@@ -13,15 +13,10 @@
|
|
|
13
13
|
* (`gate_not_tool_approval`/`gate_not_resumable`/`wake.gate_pending` 的指路句)。三处文案与本表若漂移,
|
|
14
14
|
* test/session-active-conflict-materials.test.ts 的分门用例会红。认不出的 kind ⇒ null(诚实缺席,不铸假门)。
|
|
15
15
|
*/
|
|
16
|
-
import { type
|
|
16
|
+
import { type GovernancePosture } from "../runtime-governance.js";
|
|
17
17
|
import type { ShellGateDoctrine } from "@sema-agent/core";
|
|
18
18
|
import { type RunRecord } from "../plugins/store-contracts.js";
|
|
19
19
|
import { type PendingRowBinding } from "../plugins/checkpoint-store-sql.js";
|
|
20
|
-
/** [ref] 归因的部署侧入参(两个治理旋钮;判据属主见 `governanceMandatesShellGateAlways`)。 */
|
|
21
|
-
export interface GovernancePosture {
|
|
22
|
-
autonomy?: Autonomy;
|
|
23
|
-
manualModeShellGate?: "always" | "classify";
|
|
24
|
-
}
|
|
25
20
|
/** 与 runs.ts/tasks.ts 三个 409 位共享的旧文案(byte-frozen:api-error-text-freeze 门认这句)。 */
|
|
26
21
|
export declare const ACTIVE_RUN_CONFLICT_BASE_TEXT = "session already has an active run \u2014 POST /v1/runs/{activeTaskId}/cancel stops it (same-instance interactive runs abort immediately)";
|
|
27
22
|
/**
|
|
@@ -123,11 +118,6 @@ export declare function resumeEntryForGate(kind: string, ids: {
|
|
|
123
118
|
sessionId: string;
|
|
124
119
|
taskId: string;
|
|
125
120
|
}): string | null;
|
|
126
|
-
export declare function governanceOriginOf(gate: {
|
|
127
|
-
riskDescriptor?: {
|
|
128
|
-
shellGateDoctrine?: ShellGateDoctrine;
|
|
129
|
-
};
|
|
130
|
-
} | undefined, governance: GovernancePosture | undefined): true | undefined;
|
|
131
121
|
/** token 泛型:真身是 branded CheckpointToken(秘密能力,只在本函数内部流转,绝不进响应体)。 */
|
|
132
122
|
/**
|
|
133
123
|
* SSE 车道的 done 帧 result(拒绝形)。与 409 body **同一铸体处** —— 此前 tasks.ts 在 res.write 里
|
|
@@ -177,7 +167,7 @@ interface ConflictProbeDeps<TToken> {
|
|
|
177
167
|
findPendingTokenBySession?: (sessionId: string, scope?: string, binding?: PendingRowBinding) => Promise<TToken | null | undefined>;
|
|
178
168
|
/** 结构形(不 import core 的 `Checkpoint`):只声明本模块**真读**的几格。`riskDescriptor` 是
|
|
179
169
|
* core 在 mint 时挂到升级门上的判读元数据(display/triage only),`shellGateDoctrine` 是 2026-08-05
|
|
180
|
-
* 裁定加的取证格 ——
|
|
170
|
+
* 裁定加的取证格 —— 见 `runtime-governance.ts` 的 `governanceOriginOf` 推导论证。`checkpointId`([ref],core 5.42.0)
|
|
181
171
|
* 是行的非秘密稳定身份,可选 —— legacy 行没有它(见 `PendingGateMaterial.checkpointId`)。 */
|
|
182
172
|
get?: (token: TToken) => Promise<{
|
|
183
173
|
checkpointId?: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { governanceOriginOf } from "../runtime-governance.js";
|
|
2
2
|
import { isParkedRunStatus } from "../plugins/store-contracts.js";
|
|
3
3
|
import { LIVE_RUN_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
4
4
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
@@ -27,11 +27,6 @@ function safeCheckpointIdOf(raw, token) {
|
|
|
27
27
|
return undefined;
|
|
28
28
|
return raw;
|
|
29
29
|
}
|
|
30
|
-
export function governanceOriginOf(gate, governance) {
|
|
31
|
-
if (gate?.riskDescriptor?.shellGateDoctrine !== "always")
|
|
32
|
-
return undefined;
|
|
33
|
-
return governanceMandatesShellGateAlways(governance ?? {}) ? true : undefined;
|
|
34
|
-
}
|
|
35
30
|
export function toDoneFrameResult(body) {
|
|
36
31
|
return {
|
|
37
32
|
status: "failed",
|
package/dist/http/admission.js
CHANGED
|
@@ -7,6 +7,7 @@ import { singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
|
7
7
|
import { selfOrchestrationDenial } from "../task-workflow.js";
|
|
8
8
|
import { centerEntitlementSourceWired } from "../runtime-caps-resolver.js";
|
|
9
9
|
import { parseHooksConfig } from "../hooks/hook-runner.js";
|
|
10
|
+
import { judgeRequestWebSearch } from "../plugins/web-search.js";
|
|
10
11
|
import { normalizeApproachNotice, unknownTaskAgentModelRef, validateTaskAgents } from "../spec-fields.js";
|
|
11
12
|
import { isValidCwd, MAX_ADDITIONAL_DIRS, MAX_CWD_CHARS } from "../task-cwd.js";
|
|
12
13
|
import { verifyRoundsFromBody } from "./verify-rounds.js";
|
|
@@ -193,6 +194,13 @@ export function createAdmission(ctx) {
|
|
|
193
194
|
return null;
|
|
194
195
|
}
|
|
195
196
|
}
|
|
197
|
+
{
|
|
198
|
+
const webSearchVerdict = judgeRequestWebSearch(st.webSearch);
|
|
199
|
+
if (webSearchVerdict.kind === "malformed") {
|
|
200
|
+
sendError(res, 400, "request.field_invalid", webSearchVerdict.issue);
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
196
204
|
}
|
|
197
205
|
const skillsErr = validateUserSkills(body.skills);
|
|
198
206
|
if (skillsErr) {
|
|
@@ -29,7 +29,7 @@ export declare function createResumeLegs(ctx: {
|
|
|
29
29
|
routeCtxBase: RouteCtxBase;
|
|
30
30
|
resumeCheckpoint: (sessionId: string, decision: "approve" | "deny", reason: string | undefined, hostDecision: HostDecision, req?: IncomingMessage, answer?: QuestionAnswer, binding?: DecideBinding, onResumeCommitted?: (grant: {
|
|
31
31
|
overrideSessionId?: string;
|
|
32
|
-
|
|
32
|
+
rememberableToolName: string | null;
|
|
33
33
|
}) => Promise<void>, acceptEarly?: boolean, decider?: {
|
|
34
34
|
principal?: string;
|
|
35
35
|
explicitOperator: boolean;
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, validatePendingSteer, GLOBAL_USAGE_KEY, PAUSE_REGISTRY } from "@sema-agent/core";
|
|
3
|
+
import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY, PAUSE_REGISTRY } from "@sema-agent/core";
|
|
4
4
|
import { decideParkedAgent, denyWorkflowParksUnreadable, findParkOrigin, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
5
5
|
import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
6
6
|
import { evictIfConflict, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
@@ -9,10 +9,10 @@ import { mintCancelledResult } from "../run-cancel-context.js";
|
|
|
9
9
|
import {} from "../host-decision.js";
|
|
10
10
|
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
11
11
|
import { recordResumeResourceWindow } from "../resource-window.js";
|
|
12
|
-
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry,
|
|
12
|
+
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
13
13
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../fleet/fleet-bus.js";
|
|
14
14
|
import { defaultSubagentTailBus } from "../fleet/subagent-tail-bus.js";
|
|
15
|
-
import { APPROVAL_GATE_KINDS, createApprovalCardEmitter, isApprovalGateKind, resolveApprovalLeg, resolveStreamApprovalGate } from "../tool-approval.js";
|
|
15
|
+
import { APPROVAL_GATE_KINDS, createApprovalCardEmitter, isApprovalGateKind, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerMayRetire } from "../tool-approval.js";
|
|
16
16
|
import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
|
|
17
17
|
import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
18
18
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
@@ -198,11 +198,14 @@ export function createResumeLegs(ctx) {
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
|
|
201
|
-
const
|
|
201
|
+
const rememberableTool = paForGrant !== undefined &&
|
|
202
|
+
paForGrant.toolName &&
|
|
203
|
+
paForGrant.toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
204
|
+
standingAnswerMayRetire(parkRowStandingAnswerFacts(paForGrant, cp.gate, deps.config))
|
|
202
205
|
? paForGrant.toolName
|
|
203
206
|
: null;
|
|
204
207
|
const boundGrant = onResumeCommitted
|
|
205
|
-
? (overrideSessionId) => onResumeCommitted({ ...(overrideSessionId !== undefined ? { overrideSessionId } : {}),
|
|
208
|
+
? (overrideSessionId) => onResumeCommitted({ ...(overrideSessionId !== undefined ? { overrideSessionId } : {}), rememberableToolName: rememberableTool })
|
|
206
209
|
: undefined;
|
|
207
210
|
const lateSettleSink = deps.approvalLateSettle;
|
|
208
211
|
const lateSettleAnchor = lateSettleSink !== undefined && req !== undefined && token
|
|
@@ -496,8 +499,9 @@ export function createResumeLegs(ctx) {
|
|
|
496
499
|
const { type, ...rest } = frame;
|
|
497
500
|
const f = frame;
|
|
498
501
|
const write = rs0.appendEvent(taskId, drainSeq++, type, rest);
|
|
499
|
-
|
|
500
|
-
|
|
502
|
+
const fKey = f.type === "task_notification" ? taskNotificationStreamKeyOf(f) : undefined;
|
|
503
|
+
if (fKey !== undefined)
|
|
504
|
+
resumeNotifiedKeys.set(fKey, write.then(() => true, () => false));
|
|
501
505
|
return write;
|
|
502
506
|
}, { route: "resume-open", connection: "durable-append", log: (m, x) => deps.logger?.info?.(m, x) });
|
|
503
507
|
}
|
|
@@ -544,7 +548,7 @@ export function createResumeLegs(ctx) {
|
|
|
544
548
|
void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(sessionId, principal ?? null, n, Date.now(), taskId, opts?.priority)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "resume-verify", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
|
|
545
549
|
}
|
|
546
550
|
else if (resumeLegLive) {
|
|
547
|
-
const key =
|
|
551
|
+
const key = taskNotificationDedupKey(n);
|
|
548
552
|
if (resumeNotifiedKeys.get(key) === undefined) {
|
|
549
553
|
resumeNotifiedKeys.set(key, vAppend("task_notification", taskNotificationEventData({ notification: n, ...(opts?.priority !== undefined ? { priority: opts.priority } : {}) })).then(() => true, () => false));
|
|
550
554
|
}
|
|
@@ -671,7 +675,7 @@ export function createResumeLegs(ctx) {
|
|
|
671
675
|
void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(sessionId, principal ?? null, n, Date.now(), taskId, opts?.priority)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "resume", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
|
|
672
676
|
}
|
|
673
677
|
else if (resumeLegLive) {
|
|
674
|
-
const key =
|
|
678
|
+
const key = taskNotificationDedupKey(n);
|
|
675
679
|
if (resumeNotifiedKeys.get(key) === undefined) {
|
|
676
680
|
resumeNotifiedKeys.set(key, append("task_notification", taskNotificationEventData({ notification: n, ...(opts?.priority !== undefined ? { priority: opts.priority } : {}) })).then(() => true, () => false));
|
|
677
681
|
}
|
|
@@ -780,7 +784,7 @@ export function createResumeLegs(ctx) {
|
|
|
780
784
|
case "task_notification": {
|
|
781
785
|
await flush();
|
|
782
786
|
const tnr = ev.notification;
|
|
783
|
-
const tnrKey =
|
|
787
|
+
const tnrKey = taskNotificationStreamKeyOf(tnr);
|
|
784
788
|
const tnrPrior = tnrKey !== undefined ? resumeNotifiedKeys.get(tnrKey) : undefined;
|
|
785
789
|
if (tnrPrior === undefined || !(await tnrPrior)) {
|
|
786
790
|
const write = append("task_notification", taskNotificationEventData(ev));
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -357,11 +357,11 @@ export interface RouteLegs {
|
|
|
357
357
|
resumeCheckpoint(sessionId: string, decision: "approve" | "deny", reason: string | undefined, hostDecision: HostDecision, req?: IncomingMessage, answer?: QuestionAnswer, binding?: DecideBinding,
|
|
358
358
|
/** [ref] codex R4(验真后修):remember 豁免的 grant 挂点。**工具名不再由路由自己读**(那是与本函数
|
|
359
359
|
* 载入行分歧的第三次 scope-less 读,三读分歧下会给他人工具落豁免=提权)——由本函数把**已属主校验的
|
|
360
|
-
* 载入 cp**
|
|
360
|
+
* 载入 cp** 的可记工具名(`rememberableToolName`,非 tool_approval / AskUserQuestion / legacy null / S-663 强制卡 ⇒ `null`
|
|
361
361
|
* = 无可记)一并回传,路由只对这个逐字落店。`overrideSessionId` = parked 腿的 root/host 会话 grant 锚。 */
|
|
362
362
|
onResumeCommitted?: (grant: {
|
|
363
363
|
overrideSessionId?: string;
|
|
364
|
-
|
|
364
|
+
rememberableToolName: string | null;
|
|
365
365
|
}) => Promise<void>,
|
|
366
366
|
/** [ref]:透传给 {@link DriveResumeArgs.acceptEarly}(200 受理语义)。HTTP `/decide` 腿传 `true`;
|
|
367
367
|
* 内部 D-D SLA deny-sweep 不传 —— 它没有在等响应的人,提前受理对它零收益而少一条同步失败面。 */
|
|
@@ -2,7 +2,7 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
2
2
|
import type { QuestionAnswer } from "@sema-agent/core";
|
|
3
3
|
import type { CheckpointStoreFull } from "../../plugins/store-backend.js";
|
|
4
4
|
import { type ToolApprovalCoordinator } from "../../tool-approval.js";
|
|
5
|
-
import { type GovernancePosture } from "
|
|
5
|
+
import { type GovernancePosture } from "../../runtime-governance.js";
|
|
6
6
|
import { type RouteCtx, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
|
|
7
7
|
import { type KeyClosureIssue } from "../../request-key-closure.js";
|
|
8
8
|
/**
|
|
@@ -16,7 +16,7 @@ import { sleep } from "../sse-log.js";
|
|
|
16
16
|
import { bindSseLifecycle } from "../sse-lifecycle.js";
|
|
17
17
|
import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "../send.js";
|
|
18
18
|
import { gatedPrincipal, explicitOperatorOk, isOperator, sendPrincipalRefusal } from "../principal-gate.js";
|
|
19
|
-
import { governanceOriginOf } from "
|
|
19
|
+
import { governanceOriginOf } from "../../runtime-governance.js";
|
|
20
20
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
21
21
|
import { assertNever } from "../route-ctx.js";
|
|
22
22
|
import { readResumeCtx, resumeCtxCorruptAnswer } from "../resume-ctx-read.js";
|
|
@@ -689,7 +689,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
689
689
|
let pendingGrant;
|
|
690
690
|
const grantOnCommit = remember && deps.approvalExemptionStore
|
|
691
691
|
? async (grant) => {
|
|
692
|
-
const canonical = grant.
|
|
692
|
+
const canonical = grant.rememberableToolName;
|
|
693
693
|
if (!canonical)
|
|
694
694
|
return;
|
|
695
695
|
pendingGrant = { sessionId: grant.overrideSessionId ?? sessionId, toolName: canonical };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ControlPlaneCorruptError } from "@sema-agent/core";
|
|
3
3
|
import { erasureInputSmugglesPrototypeKey } from "../../memory-operator-faces.js";
|
|
4
|
+
import { MemoryLayerUnwritableError } from "../../memory-layer-preflight.js";
|
|
4
5
|
import { sendJson, sendError } from "../send.js";
|
|
5
6
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
7
|
import { assertNever } from "../route-ctx.js";
|
|
@@ -35,6 +36,10 @@ function codeOf(err) {
|
|
|
35
36
|
return typeof code === "string" ? code : undefined;
|
|
36
37
|
}
|
|
37
38
|
function sentClassifiedFailure(res, ctx, face, err) {
|
|
39
|
+
if (face === "erase" && err instanceof MemoryLayerUnwritableError) {
|
|
40
|
+
sendError(res, 409, "memory.layer_unwritable", `memory layer directories are not writable on disk: ${err.dirList} — refused before the engine was called: nothing was written and no journal was left. Restore the owner write bit with \`chmod u+w ${err.chmodTargets}\` and retry. This is a known issue of the memory engine's mount: it leaves a layer that a session reads but does not write read-only on disk (directories 0555, entries 0444) and does not restore it, so a later project session can lock it again`);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
38
43
|
const coreCode = codeOf(err);
|
|
39
44
|
if (coreCode !== undefined && sentCoreCode(res, coreCode, err instanceof Error ? err.message : String(err)))
|
|
40
45
|
return true;
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { ControlPlaneCorruptError } from "@sema-agent/core";
|
|
3
3
|
import { sendJson, sendError } from "../send.js";
|
|
4
4
|
import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
|
|
5
|
+
import { MemoryLayerUnwritableError } from "../../memory-layer-preflight.js";
|
|
5
6
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
7
|
import { assertNever } from "../route-ctx.js";
|
|
7
8
|
export const MEMORY_ORIGIN_EXTERNAL_PATH = "/v1/memory/origin/external";
|
|
@@ -47,6 +48,10 @@ function codeOf(err) {
|
|
|
47
48
|
return typeof code === "string" ? code : undefined;
|
|
48
49
|
}
|
|
49
50
|
function sentClassifiedFailure(res, ctx, face, err) {
|
|
51
|
+
if (face === "clear" && err instanceof MemoryLayerUnwritableError) {
|
|
52
|
+
sendError(res, 409, "memory.layer_unwritable", `memory layer directories are not writable on disk: ${err.dirList} — refused before the engine was called: nothing was written and no journal was left. Restore the owner write bit with \`chmod u+w ${err.chmodTargets}\` and retry. This is a known issue of the memory engine's mount: it leaves a layer that a session reads but does not write read-only on disk (directories 0555, entries 0444) and does not restore it, so a later project session can lock it again`);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
50
55
|
if (face === "clear") {
|
|
51
56
|
const coreCode = codeOf(err);
|
|
52
57
|
if (coreCode !== undefined && sentClearCode(res, coreCode, err instanceof Error ? err.message : String(err)))
|
|
@@ -9,6 +9,7 @@ import { readSessionPermissionRules, describeSessionPermissionRulesProblem } fro
|
|
|
9
9
|
import { sendJson, sendError, sendSessionPolicyError, sseHeaders } from "../send.js";
|
|
10
10
|
import { gatedPrincipal, explicitOperatorOk, runOwnerReadable, sendPrincipalRefusal } from "../principal-gate.js";
|
|
11
11
|
import { assertNever } from "../route-ctx.js";
|
|
12
|
+
import { MemoryLayerUnwritableError } from "../../memory-layer-preflight.js";
|
|
12
13
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
13
14
|
const MCP_MATERIALIZE_TIMEOUT_MS = 10_000;
|
|
14
15
|
const A2A_STATUS_TTL_MS = 5_000;
|
|
@@ -499,6 +500,11 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
499
500
|
sendJson(res, 200, await faces.erase({ requestId: raw.requestId, select: { sessionId }, ...(raw.allowUnevidenced !== undefined ? { allowUnevidenced: raw.allowUnevidenced } : {}) }));
|
|
500
501
|
}
|
|
501
502
|
catch (err) {
|
|
503
|
+
if (err instanceof MemoryLayerUnwritableError) {
|
|
504
|
+
deps.logger?.warn?.("session_memory_erase_layer_unwritable", { sessionId, dirs: err.dirs });
|
|
505
|
+
sendError(res, 409, "memory.layer_unwritable", "memory erasure refused: a memory layer directory on this server is not writable on disk, so the erase was stopped before anything was written (nothing was deleted, no journal was left). Ask whoever operates this deployment to restore the owner write bit on the memory layer directories (a known issue of the memory engine's mount), then retry with the same requestId");
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
502
508
|
const code = err.code;
|
|
503
509
|
if (code === "config.memory_erasure_request") {
|
|
504
510
|
sendError(res, 400, "config.memory_erasure_request", err instanceof Error ? err.message : "invalid memory erasure request");
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead, redactTerminalResult } from "../../observability/run-terminal.js";
|
|
2
|
-
import { runWithVerification, runCascade, uuidv7 } from "@sema-agent/core";
|
|
2
|
+
import { runWithVerification, runCascade, taskNotificationDedupKey, uuidv7 } from "@sema-agent/core";
|
|
3
3
|
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, resumeAtHttpStatus, HEARTBEAT_MS } from "../../runs.js";
|
|
4
4
|
import { sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
5
5
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
6
6
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
|
|
7
7
|
import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
|
|
8
8
|
import { createReasoningSegments, reasoningStreamOfTask } from "../../trace/model-segment.js";
|
|
9
|
-
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry,
|
|
9
|
+
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../../orchestration/workflow-completion-inbox.js";
|
|
10
10
|
import { createLedgerSink } from "../../trace/ledger-sink.js";
|
|
11
11
|
import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
|
|
12
12
|
import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
|
|
@@ -393,7 +393,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
393
393
|
void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(prepared.spec.sessionId, askOwner, n, Date.now(), durableTaskId, opts?.priority)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "sync-stream", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
|
|
394
394
|
}
|
|
395
395
|
else if (deliverable) {
|
|
396
|
-
const key =
|
|
396
|
+
const key = taskNotificationDedupKey(n);
|
|
397
397
|
if (syncNotifiedKeys.get(key) === undefined) {
|
|
398
398
|
syncNotifiedKeys.set(key, Promise.resolve(true));
|
|
399
399
|
sseData(res, { type: "task_notification", ...taskNotificationEventData({ notification: n, ...(opts?.priority !== undefined ? { priority: opts.priority } : {}) }) });
|
|
@@ -465,7 +465,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
465
465
|
}
|
|
466
466
|
else if (ev.type === "task_notification") {
|
|
467
467
|
const tnv = ev.notification;
|
|
468
|
-
const tnvKey =
|
|
468
|
+
const tnvKey = taskNotificationStreamKeyOf(tnv);
|
|
469
469
|
const tnvPrior = tnvKey !== undefined ? syncNotifiedKeys.get(tnvKey) : undefined;
|
|
470
470
|
if (tnvPrior === undefined || !(await tnvPrior)) {
|
|
471
471
|
if (tnvKey !== undefined)
|
|
@@ -553,8 +553,9 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
553
553
|
if (closed || res.writableEnded || res.destroyed)
|
|
554
554
|
throw new Error("stream ended before the completion frame was written");
|
|
555
555
|
const f = frame;
|
|
556
|
-
|
|
557
|
-
|
|
556
|
+
const fKey = f.type === "task_notification" ? taskNotificationStreamKeyOf(f) : undefined;
|
|
557
|
+
if (fKey !== undefined)
|
|
558
|
+
syncNotifiedKeys.set(fKey, Promise.resolve(true));
|
|
558
559
|
sseData(res, frame);
|
|
559
560
|
}, { route: "sync-stream-open", connection: "live-sse", log: (m, x) => deps.logger?.info?.(m, x) });
|
|
560
561
|
const withElicit = deps.elicitation
|
package/dist/http/server.d.ts
CHANGED
|
@@ -659,7 +659,7 @@ export interface ServiceDeploymentDeps {
|
|
|
659
659
|
* registry 部署),现有部署零影响。main.ts 在 applyEffective 落模型后翻 true(one-way latch)。 */
|
|
660
660
|
modelReady?: () => boolean;
|
|
661
661
|
/** Sema-registry restart signal: the refresh loop sets this when a pull carries restart-to-apply
|
|
662
|
-
* changes (skills/mcp/scenarios
|
|
662
|
+
* changes (skills/mcp/a2a/scenarios/… baked at boot — the `RestartSlice` closed set) that DIFFER from this process's boot config.
|
|
663
663
|
* Surfaced at GET /health as `restartRequired` + `restart{reasons,version,since}` so the orchestrator can
|
|
664
664
|
* auto rolling-restart the worker. Change-detected (not presence) — absent when nothing baked-at-boot
|
|
665
665
|
* changed, so polling it never causes a restart loop. */
|
|
@@ -686,10 +686,11 @@ export interface ServiceDeploymentDeps {
|
|
|
686
686
|
mcpRevocationState?: () => {
|
|
687
687
|
revoked: string[];
|
|
688
688
|
} | undefined;
|
|
689
|
-
/** codex R18
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
689
|
+
/** codex R18: the persistent "retained WITHOUT a next-boot handoff" state — a candidate whose restart signal is
|
|
690
|
+
* withheld because no restart-surviving handoff exists yet (durable-declared LKG publication incomplete, or a
|
|
691
|
+
* boot skills debt not yet proven repairable). Surfaced on /health (`modelPlaneDeferred` — the key keeps its birth
|
|
692
|
+
* name: it was born for the model-plane deferral, which retired with the `models-tiers` slice in S-668 波 0) so
|
|
693
|
+
* the stuck state outlives the one-shot warn (the etag advances → later ticks 304). */
|
|
693
694
|
planeDeferredState?: () => {
|
|
694
695
|
version: number;
|
|
695
696
|
since: number;
|
|
@@ -450,9 +450,12 @@ export interface TaskRequestBody {
|
|
|
450
450
|
/** The caller's per-request settings stamp (the client-side `SemaSettings` shape). The KEY SET is deliberately
|
|
451
451
|
* NOT mirrored here — a hand-written second shape on this interface would be the classic mirror-drift form.
|
|
452
452
|
* Per-key ownership is split, not single: `parseTaskSettings` (src/task-settings.ts) defensively owns the
|
|
453
|
-
* shared settings keys on every path, while `settings.webSearch` has its OWN
|
|
454
|
-
*
|
|
455
|
-
*
|
|
453
|
+
* shared settings keys on every path, while `settings.webSearch` has its OWN judge (plugins/web-search.ts
|
|
454
|
+
* `judgeRequestWebSearch`, S-199): a malformed block — including a missing or unknown `provider` — 400s on submit
|
|
455
|
+
* on every lane, and only the single-user
|
|
456
|
+
* scenario lane honors a well-formed one (capabilities/scenarios.ts — the multi-tenant lane ignores it by design;
|
|
457
|
+
* a malformed block replayed on resume mounts no WebSearch for that call — never a substitute destination — with a
|
|
458
|
+
* fail-open count). The declared type is the INTENDED caller form (sibling convention: `oneShot` declares boolean,
|
|
456
459
|
* runtime drops garbage): a non-array object bag, or omit the key. A `null` reads as absence on both the
|
|
457
460
|
* fresh-submit wall (prepareSpec) and replay (parseTaskSettings), and malformed forms 400 on submit /
|
|
458
461
|
* no-op on replay. ([ref] 件1 leftover key, declared 2026-08-31; ownership wording per codex adversarial round.)
|