@sema-agent/server 7.103.1 → 7.104.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 +362 -3
- package/README.md +6 -1
- package/README.zh-CN.md +5 -1
- package/USAGE.md +279 -18
- package/deploy/sema-up/README.md +57 -7
- package/deploy/sema-up/cluster-up.sh +18 -4
- package/deploy/sema-up/kube-up.sh +16 -0
- package/deploy/sema-up/lib/dataroot.sh +142 -0
- package/deploy/sema-up/lib/flagshape.sh +69 -0
- package/deploy/sema-up/lib/lanes.py +14 -4
- package/deploy/sema-up/lib/shell-access.sh +41 -0
- package/deploy/sema-up/preflight.sh +14 -1
- package/deploy/sema-up/sema-up.sh +134 -37
- package/deploy/sema-up/smoke.sh +5 -0
- package/dist/auto-mode-face.js +3 -4
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/config-center.d.ts +6 -2
- package/dist/boot/config-center.js +5 -59
- package/dist/boot/execution-env.d.ts +11 -0
- package/dist/boot/execution-env.js +14 -0
- package/dist/boot/execution-lane.d.ts +25 -0
- package/dist/boot/execution-lane.js +95 -0
- package/dist/boot/memory-consolidation.d.ts +185 -9
- package/dist/boot/memory-consolidation.js +192 -32
- package/dist/boot/resolve-spec.d.ts +2 -0
- package/dist/boot/resolve-spec.js +28 -24
- package/dist/boot/runner-deps.d.ts +6 -2
- package/dist/boot/runner-deps.js +4 -0
- package/dist/boot/session-faces.d.ts +5 -0
- package/dist/boot/session-faces.js +18 -2
- package/dist/boot/session-incarnation-seat.d.ts +41 -0
- package/dist/boot/session-incarnation-seat.js +69 -0
- package/dist/boot/stage-01-config.js +2 -2
- package/dist/boot/stage-02-registry-pull.js +3 -1
- package/dist/boot/stage-04-stores.d.ts +2 -0
- package/dist/boot/stage-04-stores.js +3 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -0
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +4 -1
- package/dist/boot/stage-06-runners.js +7 -14
- package/dist/boot/stage-07-capability-layer.d.ts +4 -1
- package/dist/boot/stage-08-reapers.d.ts +4 -1
- package/dist/boot/stage-09-leader.d.ts +6 -1
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +6 -1
- package/dist/boot/stage-10-http-server.js +4 -1
- package/dist/brain.d.ts +1 -1
- package/dist/config-catalog.d.ts +16 -1
- package/dist/config-catalog.js +9 -9
- package/dist/config-center/restart-signal.d.ts +4 -4
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +37 -6
- package/dist/config-provider.d.ts +42 -10
- package/dist/config-provider.js +18 -16
- package/dist/config-types.d.ts +37 -16
- package/dist/config-types.js +1 -0
- package/dist/config.d.ts +13 -28
- package/dist/config.js +53 -70
- package/dist/env-facts.d.ts +13 -3
- package/dist/env-facts.js +15 -3
- package/dist/execution-lane-caps.d.ts +15 -0
- package/dist/execution-lane-caps.js +8 -0
- package/dist/file-history-capture.d.ts +42 -35
- package/dist/file-history-capture.js +15 -10
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/http/admission.d.ts +11 -4
- package/dist/http/admission.js +5 -1
- package/dist/http/dispatch.js +3 -3
- package/dist/http/principal-gate.d.ts +22 -0
- package/dist/http/principal-gate.js +20 -0
- package/dist/http/resume-legs.js +9 -5
- package/dist/http/routes/agents-roster.d.ts +7 -0
- package/dist/http/routes/agents-roster.js +6 -2
- package/dist/http/routes/approvals-assistant.d.ts +48 -19
- package/dist/http/routes/approvals-assistant.js +117 -50
- package/dist/http/routes/capabilities.js +3 -2
- package/dist/http/routes/config-catalog.js +1 -0
- package/dist/http/routes/memory-consolidation.d.ts +3 -2
- package/dist/http/routes/memory-policy.js +3 -3
- package/dist/http/routes/rules.js +2 -2
- package/dist/http/routes/runs.js +27 -2
- package/dist/http/routes/session-sync.js +8 -14
- package/dist/http/routes/sessions-list.js +3 -2
- package/dist/http/routes/sessions.js +44 -23
- package/dist/http/routes/shared-memory.js +2 -2
- package/dist/http/routes/tasks.js +22 -19
- package/dist/http/routes/trace-usage.js +4 -4
- package/dist/http/send.d.ts +12 -0
- package/dist/http/send.js +12 -1
- package/dist/http/server.d.ts +21 -4
- package/dist/http/server.js +7 -4
- package/dist/http/sync-submit-body.d.ts +43 -0
- package/dist/http/sync-submit-body.js +4 -0
- package/dist/http/wire-types.d.ts +23 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/leader/merge.d.ts +8 -3
- package/dist/memory-incident-sink.js +2 -1
- package/dist/model-select.d.ts +9 -0
- package/dist/model-select.js +5 -1
- package/dist/observability/corrupt-read-seat.d.ts +151 -16
- package/dist/observability/corrupt-read-seat.js +158 -15
- package/dist/observability/fail-open.d.ts +12 -0
- package/dist/observability/fail-open.js +12 -0
- package/dist/observability/run-terminal.d.ts +15 -1
- package/dist/observability/run-terminal.js +4 -0
- package/dist/parked-decide.d.ts +8 -3
- package/dist/parked-decide.js +56 -20
- package/dist/parked-revive-surface.d.ts +14 -7
- package/dist/parked-revive-surface.js +5 -1
- package/dist/plugins/approval-ask-store-file.d.ts +4 -1
- package/dist/plugins/approval-ask-store-file.js +9 -12
- package/dist/plugins/approval-ask-store-sql.d.ts +4 -0
- package/dist/plugins/approval-ask-store-sql.js +4 -1
- package/dist/plugins/approval-exemption-store.d.ts +21 -1
- package/dist/plugins/approval-exemption-store.js +36 -7
- package/dist/plugins/approval-nonce-store.d.ts +16 -2
- package/dist/plugins/approval-nonce-store.js +17 -7
- package/dist/plugins/caching-session-store.d.ts +5 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/file-resume-anchor-store.d.ts +7 -2
- package/dist/plugins/file-resume-anchor-store.js +19 -5
- package/dist/plugins/file-run-store.d.ts +21 -8
- package/dist/plugins/file-run-store.js +60 -38
- package/dist/plugins/local-session-store.d.ts +22 -0
- package/dist/plugins/local-session-store.js +74 -11
- package/dist/plugins/memory-run-store.d.ts +7 -5
- package/dist/plugins/memory-run-store.js +11 -4
- package/dist/plugins/one-time-migrations.d.ts +38 -1
- package/dist/plugins/one-time-migrations.js +143 -15
- package/dist/plugins/permission-rule-store-file.d.ts +9 -2
- package/dist/plugins/permission-rule-store-file.js +37 -51
- package/dist/plugins/pg-session-storage.d.ts +3 -0
- package/dist/plugins/pg-session-storage.js +19 -10
- package/dist/plugins/run-store-sql.d.ts +12 -4
- package/dist/plugins/run-store-sql.js +15 -13
- package/dist/plugins/send-file-ledger.d.ts +5 -1
- package/dist/plugins/send-file-ledger.js +11 -6
- package/dist/plugins/session-event-payload.d.ts +14 -4
- package/dist/plugins/session-event-payload.js +5 -2
- package/dist/plugins/store-backend.d.ts +7 -3
- package/dist/plugins/store-backend.js +10 -6
- package/dist/plugins/store-contracts.d.ts +7 -2
- package/dist/plugins/tidb-pool.js +8 -2
- package/dist/plugins/tidb-session-store.d.ts +6 -1
- package/dist/plugins/tidb-session-store.js +17 -10
- package/dist/project-identity.d.ts +4 -6
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +72 -3
- package/dist/request-value-closure.js +12 -4
- package/dist/roster-bound-http-status.d.ts +6 -0
- package/dist/roster-bound-http-status.js +6 -0
- package/dist/run-local.js +22 -27
- package/dist/runs.d.ts +22 -2
- package/dist/runs.js +29 -7
- package/dist/sealed-key.d.ts +31 -4
- package/dist/sealed-key.js +9 -0
- package/dist/security.d.ts +5 -1
- package/dist/server-secret-env.d.ts +2 -2
- package/dist/session-children-retire.d.ts +92 -0
- package/dist/session-children-retire.js +85 -0
- package/dist/session-error-wire.d.ts +58 -0
- package/dist/session-error-wire.js +38 -0
- package/dist/session-incarnation.d.ts +61 -0
- package/dist/session-incarnation.js +43 -0
- package/dist/session-records-skipped-notice.d.ts +20 -0
- package/dist/session-records-skipped-notice.js +27 -0
- package/dist/tool-approval.d.ts +65 -0
- package/dist/tool-approval.js +36 -9
- package/dist/trace/core-keyset-guard.d.ts +8 -7
- package/dist/trace/engine-notice-wire.d.ts +3 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/project.d.ts +1 -1
- package/dist/trace/project.js +9 -0
- package/dist/trace/wire-projection-faces.d.ts +10 -3
- package/dist/trace/wire-projection-faces.js +8 -2
- package/package.json +3 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { loadRemoteExec } from "@sema-agent/settings-schema/node";
|
|
2
|
+
import { crossInvariant } from "../config-invariants.js";
|
|
3
|
+
import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
|
|
4
|
+
export const FILE_SOURCED_LANES = ["host", "local-docker"];
|
|
5
|
+
export async function composeExecutionLane(config, ctx) {
|
|
6
|
+
if (ctx.configSource === "local")
|
|
7
|
+
await applyRemoteExecFile(config, ctx);
|
|
8
|
+
assertExecutionLaneInvariants(config);
|
|
9
|
+
}
|
|
10
|
+
export function assertExecutionLaneInvariants(config) {
|
|
11
|
+
const judge = (id) => crossInvariant(id).check({ live: config, next: config });
|
|
12
|
+
const leader = judge("leader-host-lane");
|
|
13
|
+
if (leader !== undefined) {
|
|
14
|
+
throw new Error(leader);
|
|
15
|
+
}
|
|
16
|
+
const shared = judge("multi-tenant-shared-lane");
|
|
17
|
+
if (shared !== undefined) {
|
|
18
|
+
throw new Error(shared);
|
|
19
|
+
}
|
|
20
|
+
const deviceStore = judge("device-durable-store");
|
|
21
|
+
if (deviceStore !== undefined) {
|
|
22
|
+
throw new Error(deviceStore);
|
|
23
|
+
}
|
|
24
|
+
const deviceApproval = judge("device-durable-approval");
|
|
25
|
+
if (deviceApproval !== undefined) {
|
|
26
|
+
throw new Error(deviceApproval);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function applyRemoteExecFile(config, ctx) {
|
|
30
|
+
const { localRoot, logger } = ctx;
|
|
31
|
+
let fileRx;
|
|
32
|
+
try {
|
|
33
|
+
fileRx = await loadRemoteExec(localRoot);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
logger.error("remote_exec_file_invalid", { root: localRoot, err: String(err) });
|
|
37
|
+
throw new Error(`config.d/remote-exec.json under ${localRoot} is invalid: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
38
|
+
}
|
|
39
|
+
if (!fileRx)
|
|
40
|
+
return;
|
|
41
|
+
const preemptedLane = executionLaneOf(config.remoteExec?.provider);
|
|
42
|
+
const isolatedLane = (lane) => executionLaneCaps(lane).isolation === "supported";
|
|
43
|
+
const disclosePreemption = (winner) => {
|
|
44
|
+
if (winner === preemptedLane)
|
|
45
|
+
return;
|
|
46
|
+
const downgrade = isolatedLane(preemptedLane) && !isolatedLane(winner);
|
|
47
|
+
logger.warn("remote_exec_lane_preempted_by_file", {
|
|
48
|
+
preemptedLane,
|
|
49
|
+
effectiveLane: winner,
|
|
50
|
+
root: localRoot,
|
|
51
|
+
isolationDowngrade: downgrade,
|
|
52
|
+
note: `config.d/remote-exec.json (CONFIG_PROVIDER=local, file-wins) replaced the env-derived execution lane ` +
|
|
53
|
+
`"${preemptedLane}" with "${winner}" — any REMOTE_EXEC/lane coordinate env for "${preemptedLane}" is now INERT` +
|
|
54
|
+
(downgrade ? `. This is an ISOLATION DOWNGRADE: tasks run on a non-isolated target. Delete/fix the file, or unset the lane env if the file is the intended source.` : `. Delete/fix the file, or unset the lane env if the file is the intended source.`),
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const drives = (lane) => ctx.drivesFromFile.includes(lane);
|
|
58
|
+
if (fileRx.provider === "host" && drives("host")) {
|
|
59
|
+
config.remoteExec = {
|
|
60
|
+
provider: "host",
|
|
61
|
+
...(fileRx.workdir ? { workspaceBase: fileRx.workdir } : {}),
|
|
62
|
+
...(fileRx.commandTimeoutMs != null ? { commandTimeoutMs: fileRx.commandTimeoutMs } : {}),
|
|
63
|
+
};
|
|
64
|
+
disclosePreemption("host");
|
|
65
|
+
logger.info("remote_exec_from_file", { provider: "host", root: localRoot });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (fileRx.provider === "local-docker" && drives("local-docker")) {
|
|
69
|
+
config.remoteExec = {
|
|
70
|
+
provider: "local-docker",
|
|
71
|
+
image: fileRx.image,
|
|
72
|
+
...(fileRx.mountPath ? { mountPath: fileRx.mountPath } : fileRx.workdir ? { mountPath: fileRx.workdir } : {}),
|
|
73
|
+
...(fileRx.memory ? { memory: fileRx.memory } : {}),
|
|
74
|
+
...(fileRx.cpus != null ? { cpus: fileRx.cpus } : {}),
|
|
75
|
+
...(fileRx.network ? { network: fileRx.network } : {}),
|
|
76
|
+
};
|
|
77
|
+
disclosePreemption("local-docker");
|
|
78
|
+
logger.info("remote_exec_from_file", { provider: "local-docker", root: localRoot, image: fileRx.image });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const fileLane = fileRx.provider;
|
|
82
|
+
const sourceable = FILE_SOURCED_LANES.some((lane) => lane === fileLane);
|
|
83
|
+
logger.warn("remote_exec_file_isolated_lane_unwired", {
|
|
84
|
+
provider: fileLane,
|
|
85
|
+
effectiveLane: preemptedLane,
|
|
86
|
+
reason: sourceable ? "entry-has-no-executor" : "needs-name-resolution",
|
|
87
|
+
note: sourceable
|
|
88
|
+
? `config.d/remote-exec.json names "${fileLane}", but this entry has no executor for it (it drives ${ctx.drivesFromFile.join(" / ")} ` +
|
|
89
|
+
`from the file; the HTTP service drives ${FILE_SOURCED_LANES.join(" / ")}) — the env-derived lane above stays in force`
|
|
90
|
+
: `this entry drives ${ctx.drivesFromFile.join(" / ")} from config.d/remote-exec.json; "${fileLane}" (like the other isolated lanes ` +
|
|
91
|
+
`e2b / k8s / ssh / adb) is sourced from REMOTE_EXEC env until file-source NAME→value resolution lands (center field map) — the ` +
|
|
92
|
+
`env-derived lane above stays in force`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=execution-lane.js.map
|
|
@@ -1,9 +1,51 @@
|
|
|
1
1
|
import type { RestartSliceCtx } from "../config-center/restart-signal.js";
|
|
2
|
+
import type { MemoryConsolidationDriverConfig, ServiceConfig } from "../config-types.js";
|
|
2
3
|
import { type ConsolidationDriverRunRow, type ConsolidationRunReceipt, type MemoryBackend, type MemoryConsolidationDriverDeps, type RunMemoryConsolidationOptions, type SessionCaptureRecordStore } from "@sema-agent/core";
|
|
3
4
|
/** D3 投影的座标签闭集。`role:<name>` 里的 name 只可能是 core 的 role 链两跳之一。 */
|
|
4
5
|
export type ConsolidationSeatLabel = "explicit" | "role:consolidate" | "role:summarize";
|
|
5
6
|
/** {@link buildConsolidationValveAudit} 的告警名闭集(事件名进日志 ⇒ 运维可按名过滤/告警)。 */
|
|
6
7
|
export type ConsolidationValveWarningTag = "memory_consolidation_valve_unreachable" | "memory_consolidation_run_credential_gated";
|
|
8
|
+
/** {@link resolveConsolidationValve} 的起服提示名闭集(S-824 / [ref];事件名进日志 ⇒ 运维可按名过滤/告警)。 */
|
|
9
|
+
export type ConsolidationValveNoticeTag = "memory_consolidation_default_off_no_cheap_model" | "memory_consolidation_on_main_model";
|
|
10
|
+
/**
|
|
11
|
+
* 阀门判决(S-824 / [ref])。`build*`/`create*` 之外的第三种:纯数据,由 {@link resolveConsolidationValve} 产出。
|
|
12
|
+
* 阀门开关的**唯一**属主:座解析、操作面、引擎面两席、审计告警、武装日志全读 `enabled`,不再各读一次表态。
|
|
13
|
+
*/
|
|
14
|
+
export interface ConsolidationValve {
|
|
15
|
+
readonly enabled: boolean;
|
|
16
|
+
/** 这一位是谁定的:运维的显式词(`explicit`),还是出厂缺省派生(`derived`)。 */
|
|
17
|
+
readonly source: "explicit" | "derived";
|
|
18
|
+
/**
|
|
19
|
+
* 这一判决**随目录变**吗(S-824 codex 车内复审 F2)。真 = 派生判决读了整理座落在谁身上(未设 ∧ 结构上跑得起来),
|
|
20
|
+
* 中心热改 roles / 档位就可能翻它 ⇒ 要有一份重启读数盯着({@link createConsolidationValveWatch})。显式词、以及结构上
|
|
21
|
+
* 跑不起来的派生关 ⇒ 假(目录怎么改都翻不动)。
|
|
22
|
+
*/
|
|
23
|
+
readonly catalogDependent: boolean;
|
|
24
|
+
/** 起服时要落的那一行(缺席 = 没有要说的)。 */
|
|
25
|
+
readonly notice?: {
|
|
26
|
+
readonly tag: ConsolidationValveNoticeTag;
|
|
27
|
+
readonly detail: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 整理座若开,会落在哪只模型上 —— **相对主模型**(S-824 的「便宜模型在场」判据;{@link consolidationSeatBillingOf} 产出)。
|
|
32
|
+
* · `own-model` = 座解到的目录条目**不是**主模型那一条(按条目身份 = 对象引用判,不比 id 串;`MODEL_CHEAP_ID`,或目录的
|
|
33
|
+
* consolidate / summarize / flash 档位绑定);
|
|
34
|
+
* · `main-model` = 座解到主模型那一条本身(env 角色表在 `MODEL_CHEAP_ID` 缺席 —— 或与 `MODEL_ID` 同 id、不铸 cheap 槽 —— 时把
|
|
35
|
+
* summarize 指回 `default`)⇒ 整库折叠按主模型计费;
|
|
36
|
+
* · `unresolvable`= 座解不出来(core 的 role 链刻意不回落主模型;`fault` = core 的原句,当数据交出、不吞)。
|
|
37
|
+
*/
|
|
38
|
+
export type ConsolidationSeatBilling = {
|
|
39
|
+
readonly kind: "own-model";
|
|
40
|
+
readonly seatModel: string;
|
|
41
|
+
readonly mainModel: string;
|
|
42
|
+
} | {
|
|
43
|
+
readonly kind: "main-model";
|
|
44
|
+
readonly mainModel: string;
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: "unresolvable";
|
|
47
|
+
readonly fault: string;
|
|
48
|
+
};
|
|
7
49
|
/** 座解析的产物:core 的 options 原样 + 给投影用的两位自证。 */
|
|
8
50
|
export interface ConsolidationDriverSeat {
|
|
9
51
|
/** `runMemoryConsolidationDriver` 的第三参,core 的 `resolveMemoryConsolidationDriver` 原样产出。 */
|
|
@@ -66,8 +108,6 @@ export interface ConsolidationSeatWatch {
|
|
|
66
108
|
}
|
|
67
109
|
/** 解席的输入面 = boot 期真席用的同三件(活引用:refresh 就地换代,`read()` 每次现取)。 */
|
|
68
110
|
export type ConsolidationSeatPlane = Pick<MemoryConsolidationDriverDeps, "models" | "tiers" | "roles">;
|
|
69
|
-
/** `create*`:返回带捕获状态(boot 那只指纹)的活对象。须在真席解析的**同一时刻**调(同一份输入)。 */
|
|
70
|
-
export declare function createConsolidationSeatWatch(plane: () => ConsolidationSeatPlane): ConsolidationSeatWatch;
|
|
71
111
|
/**
|
|
72
112
|
* **每 scope 单飞**(codex 交叉复审 [high],验真后修)—— 同一 scope 上在飞的那一轮被后来者**并入**,
|
|
73
113
|
* 而不是各起一轮。`create*`:返回带状态的活对象。
|
|
@@ -105,18 +145,146 @@ export declare function createPerScopeSingleFlight<T>(): (scope: string, run: ()
|
|
|
105
145
|
* 写进句子 —— core 的那句话只提到 provenance,而运维要知道是哪一根把这台机器拦下的。
|
|
106
146
|
*
|
|
107
147
|
* 阀门关着 ⇒ 整条门不判(缺省姿态,[ref]「缺席不在律内」)。
|
|
148
|
+
*
|
|
149
|
+
* 🔴 S-824 起判据拆成「判官 + 拒启包装」两半:判官 {@link consolidationDriverUnwirable} 是三条臂的**唯一**
|
|
150
|
+
* 属主,拒启包装只在阀门开着时把它的拒因抛出去;出厂缺省派生({@link resolveConsolidationValve})读同一只判官决定
|
|
151
|
+
* 「结构上跑不起来 ⇒ 缺省恒关」—— 两处读一只,不是两份三臂。
|
|
108
152
|
*/
|
|
109
|
-
export declare function assertConsolidationDriverWirable(input: {
|
|
153
|
+
export declare function assertConsolidationDriverWirable(input: ConsolidationWiringFacts & {
|
|
110
154
|
enabled: boolean;
|
|
155
|
+
}): void;
|
|
156
|
+
/** 阀门跑不跑得起来的三件结构事实(换部署形态才会变 —— 所以判在启动期)。 */
|
|
157
|
+
export interface ConsolidationWiringFacts {
|
|
111
158
|
/** 本部署接了记忆引擎吗(`boot/stores.ts` 的 `memoryEngine !== undefined`)。 */
|
|
112
|
-
memoryEngineWired: boolean;
|
|
159
|
+
readonly memoryEngineWired: boolean;
|
|
113
160
|
/** 记忆后端自带控制面归属吗(`typeof backend.controlPlaneRoot === "string"` 且非空)。 */
|
|
114
|
-
controlPlaneOwned: boolean;
|
|
161
|
+
readonly controlPlaneOwned: boolean;
|
|
115
162
|
/** 只进文案 —— 让「该去拧哪根旋钮」这句话指向该腿上**真的**有效的那一根。 */
|
|
116
|
-
memoryEngineBackend: "file" | "pg" | "tidb";
|
|
163
|
+
readonly memoryEngineBackend: "file" | "pg" | "tidb";
|
|
117
164
|
/** 部署声明的 provenance 姿态(`config.memoryProvenance`;缺席 ≡ core 缺省 `carry`)。 */
|
|
118
|
-
provenance?: "off" | "carry";
|
|
119
|
-
}
|
|
165
|
+
readonly provenance?: "off" | "carry";
|
|
166
|
+
}
|
|
167
|
+
/** 「阀门若开,这台部署结构上跑不起来」的三种成因(闭集;加一种 = 下面两只穷举 switch 编译红)。 */
|
|
168
|
+
export type ConsolidationUnwirable = "no-memory-engine" | "no-control-plane" | "provenance-off";
|
|
169
|
+
/**
|
|
170
|
+
* 「阀门若开,这台部署结构上跑不跑得起来」的**判官**(三臂逐条理由见 {@link assertConsolidationDriverWirable} 头注)。
|
|
171
|
+
* 返回成因词;`undefined` = 跑得起来。两处读它:显式 on 的拒启(句子 = {@link unwirableRefusal}),以及出厂缺省派生
|
|
172
|
+
* (成因在场 ⇒ 缺省恒关零提示;半配拒启的「为什么没开」从句 = {@link unwirableClause})—— 派生形的句子不再借显式 on 的口吻
|
|
173
|
+
* (车PI 修复轮 ⑦(c):修前派生形的从句里夹着一句「MEMORY_CONSOLIDATION_DRIVER=on arms…」,而那台机器根本没写 on)。
|
|
174
|
+
*/
|
|
175
|
+
export declare function consolidationDriverUnwirable(input: ConsolidationWiringFacts): ConsolidationUnwirable | undefined;
|
|
176
|
+
/**
|
|
177
|
+
* 整理座若开会落在哪只模型上(S-824 的「便宜模型在场」判据)—— **问 core 的同一只解析器**,不抄 role 链
|
|
178
|
+
* ({@link seatLabel} 注里同一条理由:抄一份就是第二真源,而 `ROLE_TIER_DEFAULTS` 的 flash 缺省会让手抄版答错)。
|
|
179
|
+
*
|
|
180
|
+
* 为什么不按 env `MODEL_CHEAP_ID` 的拼写判:它只是供座的一条路。env 角色表在它缺席时把 summarize 指回
|
|
181
|
+
* `default`(`config.ts` 的 `cheapRef`),而目录(`config.d/models.json` / 配置中心)的 roles / 档位同样能供
|
|
182
|
+
* consolidate / summarize 两跳 —— 判「装配后的座落不落主模型」才与计费事实同形(整理跑按座的模型计费)。
|
|
183
|
+
*
|
|
184
|
+
* 主模型 = 本仓给每条 task 铸的 `DEFAULT_MODEL_REF` 解到的那只(`boot/resolve-spec.ts` 每条 spec 都盖 `spec.model`),
|
|
185
|
+
* **不是** core 的 `default` 角色链 —— 与 `auto-mode-face.ts` 的 `classifierRouteFor` 同一口径(共用 `model-select.ts`
|
|
186
|
+
* `resolveMainModel`);喂**展开**目录,与引擎构造期 `expandTiers` 之后再解逐字同源。比的是**目录条目身份**(对象引用),
|
|
187
|
+
* **不比 `Model.id` 串**(车PI 修复轮 ⑤ / codex C-2:两条不同名的条目可以共享上游 id 却各带不同的 baseUrl / 凭据 / 价,那是两只
|
|
188
|
+
* 模型;与 `model-select.ts` `isModelAllowlisted` 同一口径)。env 的「cheap id == 主 id」不会走到这里变成两只:`config.ts` 在铸造处
|
|
189
|
+
* 就不铸那只影子条目(车PI 验修轮 B)。
|
|
190
|
+
* 探针 brain 零请求:座解析只 `brainToRuntime` 它一次,真被调用 = 装配缺陷,响亮抛。
|
|
191
|
+
* ⚠️ **爆炸半径**(车PI 验修轮 F10,如实登记):判决的每一臂 —— 包括未设 + 没配便宜模型的派生关(TOC 最常见的形)—— 都走
|
|
192
|
+
* {@link probeConsolidationSeat} 的 chat 探针。core 哪天改了 chat 闭包的次序(先做别的再问凭据席),**所有没显式 off 的部署**
|
|
193
|
+
* 起服即拒(句子点名这是提货缺陷),不再只是阀门开着的那几台。接受这个半径的理由:判据只有一只(不为派生关另写一份不走探针的
|
|
194
|
+
* 解析);钉在 test/memory-consolidation-driver.test.ts ⑭ 的真 core 探针格,提货批当场红,红在发版之前。
|
|
195
|
+
*/
|
|
196
|
+
export declare function consolidationSeatBillingOf(plane: ConsolidationSeatPlane): ConsolidationSeatBilling;
|
|
197
|
+
/**
|
|
198
|
+
* 🔴 S-824 / clay **[ref]** —— **阀门判决**:开不开、谁定的、起服时要说什么。阀门开关的**唯一**属主(stage-06 的座解析 /
|
|
199
|
+
* 操作面 / 引擎面两席 / 审计告警 / 武装日志全读它的 `enabled`)。
|
|
200
|
+
*
|
|
201
|
+
* · **显式词恒赢**:`off` ⇒ 关、零提示、**零探针**(一眼都不看角色表 —— off = 零装配零探针那句承诺);`on` ⇒ 开
|
|
202
|
+
* (结构上跑不起来由下游 D1 三条拒启臂照拒,本判决不吞);开而座落主模型 ⇒ 开 + 一行
|
|
203
|
+
* `memory_consolidation_on_main_model`(部署方显式承担,但必须知道账按主模型记)。
|
|
204
|
+
* · **未设 = 出厂缺省派生**([ref] 取代 [ref] ① 的「出厂 off」实况):
|
|
205
|
+
* 结构上跑不起来(判官 {@link consolidationDriverUnwirable} 有成因:多租户 / `MEMORY_ENGINE=off` / pg·tidb
|
|
206
|
+
* 记忆后端 / `MEMORY_PROVENANCE=off`)⇒ **恒关、零提示、零探针** —— 派生开撞上 D1 拒启臂 = 每台配了便宜模型的
|
|
207
|
+
* 多租户 worker 升级即起不来;而配不配便宜模型它都开不了,一句指向 `MODEL_CHEAP_ID` 的提示是误导;
|
|
208
|
+
* 座不落主模型({@link consolidationSeatBillingOf} = `own-model`)⇒ **开**;
|
|
209
|
+
* 否则(落主模型 / 解不出)⇒ **关** + 一行 `memory_consolidation_default_off_no_cheap_model`(点名 `MODEL_CHEAP_ID`,
|
|
210
|
+
* 出路 = 配便宜模型,或显式 `off` 静音)。
|
|
211
|
+
* · **半配拒启**(`MEMORY_EMBEDDER_*` 同族,S-824 起判定点挪到这里):scope 表 / 节律配了而阀门最终**没开** ⇒ 拒启,
|
|
212
|
+
* 拒因说清「为什么没开」。在 `loadConfig` 判会把「未设 + scope + 便宜模型在场」这台本该派生成开的部署当场拒掉。
|
|
213
|
+
*
|
|
214
|
+
* ⚠️ **起服判一次**(restart-to-apply):判决落在 boot 第 4 段的装配点 {@link createConsolidationValve} —— 目录已应用
|
|
215
|
+
* (`config.d/models.json` / 配置中心的 boot 采纳或 LKG)、记忆引擎已接线;座解析在第 6 段读这份判决。之后中心热改了 roles / 档位(或 boot 拉取超窗、目录迟到),**阀门不跟着翻**,
|
|
216
|
+
* 但翻的**需要**由 `memory-consolidation` 重启片报出来({@link createConsolidationValveWatch}):开着的那台盯座位漂移;
|
|
217
|
+
* 派生关的那台盯「此刻重判会不会开」(codex 车内复审 F2:修前派生关没有任何读数,热加一只便宜模型零信号、阀门永远关着)。
|
|
218
|
+
*/
|
|
219
|
+
export declare function resolveConsolidationValve(input: {
|
|
220
|
+
/** 部署表态(`config.memoryConsolidationDriver`)。 */
|
|
221
|
+
driver: MemoryConsolidationDriverConfig;
|
|
222
|
+
/** 判官 {@link consolidationDriverUnwirable} 对本部署的答案(成因 + 句子要用的结构事实;`undefined` = 跑得起来)。 */
|
|
223
|
+
unwirable: {
|
|
224
|
+
readonly code: ConsolidationUnwirable;
|
|
225
|
+
readonly facts: ConsolidationWiringFacts;
|
|
226
|
+
} | undefined;
|
|
227
|
+
/** 装配后的 models / tiers / roles —— 与真座解析同一份输入。只在需要时读。 */
|
|
228
|
+
plane: ConsolidationSeatPlane;
|
|
229
|
+
}): ConsolidationValve;
|
|
230
|
+
/**
|
|
231
|
+
* 阀门判决的**重启读数** —— `memory-consolidation` 重启片的两侧值,**一只** watch(车PI 修复轮 ⑥,codex C-3)。`create*`:返回
|
|
232
|
+
* 带捕获状态的活对象。两侧都是**同一个投影**:「判决开着 ⇒ `{ valve: "on", seat: 座位指纹 }`;关着 ⇒ `{ valve: "off" }`」——
|
|
233
|
+
* baked = boot 判决的投影,current = 此刻拿活配置**重判**一次的投影(`plane` 给活引用的 getter,refresh 就地换代后现取)。
|
|
234
|
+
* · 判决翻了(派生开 → 关:主模型被挪到整理座那一条上,或座被挪回主模型;派生关 → 开:目录给整理座绑上别的模型 / 目录迟到)
|
|
235
|
+
* ⇒ 两侧不等 ⇒ 签重启(重启后 boot 重判)。修前是两臂:开时只盯座位指纹、关时只盯「会不会开」—— 只动主模型的那一形零信号。
|
|
236
|
+
* · 开着而座漂了(中心改了座会解到的那只模型)⇒ 座位指纹不等 ⇒ 签(S-668 波 0 的既有信号,显式 on 同走这一臂)。
|
|
237
|
+
* · 关着而目录怎么变都不翻 ⇒ 两侧同为 `{ valve: "off" }` ⇒ 不签(主模型换代而判决不翻,不造一次什么都不改变的重启)。
|
|
238
|
+
* 主模型条目是判决的**输入**(它决定开 / 关),不进投影本身 —— 进了就会在每一次主模型换代时签一次不翻判决的重启。
|
|
239
|
+
* 显式 off / 结构上跑不起来的派生关 ⇒ `undefined`(判决不随目录变,没有可盯的东西)。
|
|
240
|
+
* 🔴 车PI 验修轮 F13([ref] 验修 opus):baked 与 current 出自**同一只**判决函数 —— baked = 构造这一拍拿同一组输入判一次。修前 baked
|
|
241
|
+
* 取自调用方另递的 `valve`,一个事实两个来源:递来的与 driver / unwirable / plane 不一致,第一拍就签一次假重启。入参里因此没有 valve 位。
|
|
242
|
+
*/
|
|
243
|
+
export declare function createConsolidationValveWatch(input: {
|
|
244
|
+
driver: MemoryConsolidationDriverConfig;
|
|
245
|
+
unwirable: Parameters<typeof resolveConsolidationValve>[0]["unwirable"];
|
|
246
|
+
plane: () => ConsolidationSeatPlane;
|
|
247
|
+
}): ConsolidationSeatWatch | undefined;
|
|
248
|
+
/**
|
|
249
|
+
* 阀门的**装配点**(车PI 修复轮 ⑦(a)(e):boot 第 4 段调它,测试调同一只 —— 不在测试里手抄接线)。`create*`:产出带行为的 watch。
|
|
250
|
+
*
|
|
251
|
+
* 一次做完四件,顺序即契约:纯判({@link judgeConsolidationValve}:判官 → 判决,半配拒启在这里抛;`run-local` 只调这一步)→ 判决带出的
|
|
252
|
+
* 起服提示落日志 → D1 的结构拒启(显式 on 而跑不起来)→ 重启读数({@link createConsolidationValveWatch})。
|
|
253
|
+
* ⚠️ **位置**:判决要两件 openStores(第 3 段)的装配结果 —— 记忆引擎接没接线、后端有没有控制面归属 —— 更早判就得在配置层把那两件
|
|
254
|
+
* 复算一遍(第二份判据),所以落在第 4 段(第一个同时持有已应用目录与店的位置),早于执行环境 / Runner 装配;座解析与操作面引擎
|
|
255
|
+
* 仍在第 6 段(要 brain 与凭据席)。`config` 给活引用:重启读数的 plane getter 每次现取 refresh 换代后的三张表。
|
|
256
|
+
*/
|
|
257
|
+
export declare function createConsolidationValve(input: ConsolidationValveInput & {
|
|
258
|
+
logger: {
|
|
259
|
+
warn: (event: string, fields?: Record<string, unknown>) => void;
|
|
260
|
+
};
|
|
261
|
+
}): {
|
|
262
|
+
valve: ConsolidationValve;
|
|
263
|
+
watch: ConsolidationSeatWatch | undefined;
|
|
264
|
+
};
|
|
265
|
+
/** 阀门判决的两件输入(装配点与纯判共用一只形 —— 两个入口递的是同一组事实)。 */
|
|
266
|
+
export interface ConsolidationValveInput {
|
|
267
|
+
readonly config: Pick<ServiceConfig, "memoryConsolidationDriver" | "memoryEngineBackend" | "memoryProvenance" | "models" | "tiers" | "roles">;
|
|
268
|
+
/** 只读两件:在不在(记忆引擎接没接线)、后端有没有控制面归属(`backendOwnsControlPlane` 只问那一个面)。 */
|
|
269
|
+
readonly memoryEngine: {
|
|
270
|
+
readonly backend: unknown;
|
|
271
|
+
} | undefined;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 🔴 车QK F5(7.104.0 合并复审 opus 腿)—— 阀门的**纯判**:结构事实(判官 {@link consolidationDriverUnwirable})→ 判决
|
|
275
|
+
* ({@link resolveConsolidationValve},**半配拒启在这里抛**)。不落日志、不判 D1、不建重启读数 —— 那三件属于有整理座的入口
|
|
276
|
+
* ({@link createConsolidationValve} 在它之上做完)。
|
|
277
|
+
*
|
|
278
|
+
* 两个入口各调它一次:HTTP 服务第 4 段经 {@link createConsolidationValve};`run-local` 在记忆引擎接线之后直接调(它没有整理座 ——
|
|
279
|
+
* 不挂手动口、不接 `RunnerDeps` 的整理两席 —— 判决本身对它没有用处,要的只是拒因)。修前这道拒只长在 HTTP 那条装配链上
|
|
280
|
+
* (S-824 从 `loadConfig` 挪过来时 `run-local` 没跟上):共享 `.env` 上同一组键,HTTP 服务拒启、`run-local` 带着零效果的 scope 表
|
|
281
|
+
* 静默起跑。判据只有这一只 ⇒ 两个入口对同一份 env 同判同句(未设 + 便宜模型在场 ⇒ 两边都派生开、都照起)。
|
|
282
|
+
*/
|
|
283
|
+
export declare function judgeConsolidationValve(input: ConsolidationValveInput): {
|
|
284
|
+
valve: ConsolidationValve;
|
|
285
|
+
wiring: ConsolidationWiringFacts;
|
|
286
|
+
unwirable: Parameters<typeof resolveConsolidationValve>[0]["unwirable"];
|
|
287
|
+
};
|
|
120
288
|
/**
|
|
121
289
|
* 阀门上了场之后的**可达性**告警两条(自查 + codex 交叉复审;姿态=warn 不拒启)。
|
|
122
290
|
*
|
|
@@ -137,9 +305,17 @@ export declare function assertConsolidationDriverWirable(input: {
|
|
|
137
305
|
* 同族先例逐字:`permission_rules_lane_unavailable`(旋钮开着却上不了场 ⇒ 打一行,不拒启)。
|
|
138
306
|
* 反过来说,**沉默**同样不行:那正是「设了但没生效」病族,而这一形连能力位都是诚实的假,唯一缺的
|
|
139
307
|
* 就是一句把因果说清楚的话。
|
|
308
|
+
*
|
|
309
|
+
* ## 只审**显式**开的阀门(S-824 / [ref] 起)
|
|
310
|
+
*
|
|
311
|
+
* 两条告警的前提都是「运维**显式**拧了 on」(上面那句原话)——它们是一次**表态**够不着的诊断。出厂缺省**派生**开的
|
|
312
|
+
* 那台(配了便宜模型的单用户部署)没有表态可审:引擎面的自动整理照跑(不走这两个口),手动口是一件它没要过的东西。
|
|
313
|
+
* 照审的后果是每一台这样的部署升级后每次起服多两行 warn,点名一根它从没设过的 `MEMORY_CONSOLIDATION_DRIVER=on`,
|
|
314
|
+
* 教它去配一个它不需要的操作面。判据读阀门判决的 `source`,不读表态原词(判决是阀门开关的唯一属主)。
|
|
140
315
|
*/
|
|
141
316
|
export declare function buildConsolidationValveAudit(input: {
|
|
142
|
-
enabled
|
|
317
|
+
/** 阀门判决({@link resolveConsolidationValve} 的 `enabled` + `source`)。 */
|
|
318
|
+
valve: Pick<ConsolidationValve, "enabled" | "source">;
|
|
143
319
|
operatorPrincipals: readonly string[];
|
|
144
320
|
/** 这台部署配了任何一把 service 凭证吗(`config.authToken` 或 `config.authTokens` 非空)。 */
|
|
145
321
|
hasServiceAuth: boolean;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { redactSecrets } from "../trace/redact.js";
|
|
2
2
|
import { memoryMountRootOf } from "../memory-scope.js";
|
|
3
|
+
import { DEFAULT_MODEL_REF, resolveMainModel } from "../model-select.js";
|
|
4
|
+
import { backendOwnsControlPlane } from "../memory-operator-faces.js";
|
|
3
5
|
import { expandTiers, MemoryEngine, readConsolidationDriverRun, resolveMemoryConsolidationDriver, runMemoryConsolidationDriver, } from "@sema-agent/core";
|
|
4
6
|
export function resolveConsolidationDriverSeat(deps) {
|
|
5
7
|
let options;
|
|
@@ -16,11 +18,7 @@ export function resolveConsolidationDriverSeat(deps) {
|
|
|
16
18
|
}
|
|
17
19
|
return { options, seat: seatLabel(deps), model: options.model };
|
|
18
20
|
}
|
|
19
|
-
|
|
20
|
-
const baked = consolidationSeatFingerprint(plane());
|
|
21
|
-
return { read: () => ({ baked, current: consolidationSeatFingerprint(plane()) }) };
|
|
22
|
-
}
|
|
23
|
-
function consolidationSeatFingerprint(plane) {
|
|
21
|
+
function probeConsolidationSeat(plane) {
|
|
24
22
|
const stop = new Error("consolidation seat probe: stopped before any request");
|
|
25
23
|
const refuse = () => {
|
|
26
24
|
throw stop;
|
|
@@ -43,12 +41,18 @@ function consolidationSeatFingerprint(plane) {
|
|
|
43
41
|
}
|
|
44
42
|
void options.chat({ system: "", user: "", maxTokens: 1, temperature: 0 }).catch(() => undefined);
|
|
45
43
|
if (seen.model === undefined) {
|
|
46
|
-
throw new Error("
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
44
|
+
throw new Error("the memory-consolidation valve's seat probe could not observe which model the engine's seat resolver chose (the " +
|
|
45
|
+
"resolved chat seat no longer asks the per-model credential seat before its first await) — refusing rather than " +
|
|
46
|
+
"leave the valve's decision and restart signal blind. This is an engine-pickup defect in " +
|
|
47
|
+
"boot/memory-consolidation.ts (probeConsolidationSeat), not a deployment setting.");
|
|
50
48
|
}
|
|
51
|
-
|
|
49
|
+
return { entry: seen.model };
|
|
50
|
+
}
|
|
51
|
+
function consolidationSeatFingerprint(plane) {
|
|
52
|
+
const seat = probeConsolidationSeat(plane);
|
|
53
|
+
if ("unresolvable" in seat)
|
|
54
|
+
return seat;
|
|
55
|
+
const { cost: _cost, ...route } = seat.entry;
|
|
52
56
|
return route;
|
|
53
57
|
}
|
|
54
58
|
function seatLabel(deps) {
|
|
@@ -81,29 +85,183 @@ export function createPerScopeSingleFlight() {
|
|
|
81
85
|
export function assertConsolidationDriverWirable(input) {
|
|
82
86
|
if (!input.enabled)
|
|
83
87
|
return;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
const code = consolidationDriverUnwirable(input);
|
|
89
|
+
if (code !== undefined)
|
|
90
|
+
throw new Error(unwirableRefusal(code, input));
|
|
91
|
+
}
|
|
92
|
+
export function consolidationDriverUnwirable(input) {
|
|
93
|
+
if (!input.memoryEngineWired)
|
|
94
|
+
return "no-memory-engine";
|
|
95
|
+
if (!input.controlPlaneOwned)
|
|
96
|
+
return "no-control-plane";
|
|
97
|
+
if (input.provenance === "off")
|
|
98
|
+
return "provenance-off";
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
function unwirableRefusal(code, facts) {
|
|
102
|
+
switch (code) {
|
|
103
|
+
case "no-memory-engine":
|
|
104
|
+
return (`MEMORY_CONSOLIDATION_DRIVER=on arms the memory-consolidation valve, but this deployment wires NO memory engine ` +
|
|
105
|
+
`— there is no library to fold, and the two admin endpoints would answer over an engine that does not exist. ` +
|
|
106
|
+
`(The engine is wired only on single-user deployments with MEMORY_ENGINE on; a multi-tenant worker keeps memory ` +
|
|
107
|
+
`dark by design.) Wire the memory engine, or set MEMORY_CONSOLIDATION_DRIVER=off.`);
|
|
108
|
+
case "no-control-plane":
|
|
109
|
+
return (`MEMORY_CONSOLIDATION_DRIVER=on arms the memory-consolidation valve, but the wired memory backend ` +
|
|
110
|
+
`(MEMORY_ENGINE_BACKEND=${facts.memoryEngineBackend}) does NOT own an engine control plane (controlPlaneRoot) — ` +
|
|
111
|
+
`core would derive one on THIS replica's local disk, and a consolidation run's resumable account would die with ` +
|
|
112
|
+
`the pod (a lost run re-mints the whole library at full model cost on the next attempt). The file memory backend ` +
|
|
113
|
+
`owns a control plane; the pg/tidb memory backends do not. Set MEMORY_ENGINE_BACKEND=file, or set ` +
|
|
114
|
+
`MEMORY_CONSOLIDATION_DRIVER=off.`);
|
|
115
|
+
case "provenance-off":
|
|
116
|
+
return (`MEMORY_CONSOLIDATION_DRIVER=on is incompatible with MEMORY_PROVENANCE=off — core refuses to enable consolidation ` +
|
|
117
|
+
`under provenance "off" because the fold law must be able to MINT origin markers (a marked input's product would ` +
|
|
118
|
+
`otherwise commit unmarked, i.e. the fold would launder provenance). Set MEMORY_PROVENANCE=carry (or unset it, ` +
|
|
119
|
+
`which is the same posture), or set MEMORY_CONSOLIDATION_DRIVER=off.`);
|
|
120
|
+
default: {
|
|
121
|
+
const unreachable = code;
|
|
122
|
+
throw new Error(`unhandled consolidation unwirable code: ${String(unreachable)}`);
|
|
123
|
+
}
|
|
89
124
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`
|
|
125
|
+
}
|
|
126
|
+
function unwirableClause(code, facts) {
|
|
127
|
+
switch (code) {
|
|
128
|
+
case "no-memory-engine":
|
|
129
|
+
return "no memory engine is wired here (multi-tenant posture, or MEMORY_ENGINE=off), so there is no library to fold";
|
|
130
|
+
case "no-control-plane":
|
|
131
|
+
return `the memory backend (MEMORY_ENGINE_BACKEND=${facts.memoryEngineBackend}) owns no engine control plane, so a consolidation run could not keep its account`;
|
|
132
|
+
case "provenance-off":
|
|
133
|
+
return "MEMORY_PROVENANCE=off, and consolidation needs provenance to mint origin markers";
|
|
134
|
+
default: {
|
|
135
|
+
const unreachable = code;
|
|
136
|
+
throw new Error(`unhandled consolidation unwirable code: ${String(unreachable)}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export function consolidationSeatBillingOf(plane) {
|
|
141
|
+
const seat = probeConsolidationSeat(plane);
|
|
142
|
+
if ("unresolvable" in seat)
|
|
143
|
+
return { kind: "unresolvable", fault: seat.unresolvable };
|
|
144
|
+
let main;
|
|
145
|
+
try {
|
|
146
|
+
main = resolveMainModel(plane);
|
|
97
147
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
148
|
+
catch (err) {
|
|
149
|
+
return { kind: "unresolvable", fault: `the main model (${DEFAULT_MODEL_REF}) does not resolve: ${err instanceof Error ? err.message : String(err)}` };
|
|
150
|
+
}
|
|
151
|
+
return seat.entry === main ? { kind: "main-model", mainModel: main.id } : { kind: "own-model", seatModel: seat.entry.id, mainModel: main.id };
|
|
152
|
+
}
|
|
153
|
+
export function resolveConsolidationValve(input) {
|
|
154
|
+
const decided = decideConsolidationValve(input);
|
|
155
|
+
const { intervalSec, scopes } = input.driver;
|
|
156
|
+
if (decided.offBecause !== undefined && (intervalSec > 0 || scopes.length > 0)) {
|
|
157
|
+
throw new Error(`MEMORY_CONSOLIDATION_INTERVAL_SEC / MEMORY_CONSOLIDATION_SCOPES are configured but MEMORY_CONSOLIDATION_DRIVER is not "on" ` +
|
|
158
|
+
`(${decided.offBecause}) — the consolidation valve is OFF, so neither knob reaches anything (the two admin endpoints are not ` +
|
|
159
|
+
`even mounted). Set MEMORY_CONSOLIDATION_DRIVER=on (or, while it is unset, configure a cheaper consolidation model via ` +
|
|
160
|
+
`MODEL_CHEAP_ID so the factory default arms the valve), or unset the other two.`);
|
|
161
|
+
}
|
|
162
|
+
return decided.valve;
|
|
163
|
+
}
|
|
164
|
+
export function createConsolidationValveWatch(input) {
|
|
165
|
+
const decide = (plane) => decideConsolidationValve({ driver: input.driver, unwirable: input.unwirable, plane }).valve;
|
|
166
|
+
const project = (valve, plane) => (valve.enabled ? { valve: "on", seat: consolidationSeatFingerprint(plane) } : { valve: "off" });
|
|
167
|
+
const bootPlane = input.plane();
|
|
168
|
+
const bakedValve = decide(bootPlane);
|
|
169
|
+
if (!bakedValve.enabled && !bakedValve.catalogDependent)
|
|
170
|
+
return undefined;
|
|
171
|
+
const baked = project(bakedValve, bootPlane);
|
|
172
|
+
return {
|
|
173
|
+
read: () => {
|
|
174
|
+
const plane = input.plane();
|
|
175
|
+
return { baked, current: project(decide(plane), plane) };
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export function createConsolidationValve(input) {
|
|
180
|
+
const { config, logger } = input;
|
|
181
|
+
const { valve, wiring, unwirable } = judgeConsolidationValve(input);
|
|
182
|
+
if (valve.notice !== undefined)
|
|
183
|
+
logger.warn(valve.notice.tag, { detail: valve.notice.detail });
|
|
184
|
+
assertConsolidationDriverWirable({ ...wiring, enabled: valve.enabled });
|
|
185
|
+
const plane = () => ({ models: config.models, tiers: config.tiers, roles: config.roles });
|
|
186
|
+
return { valve, watch: createConsolidationValveWatch({ driver: config.memoryConsolidationDriver, unwirable, plane }) };
|
|
187
|
+
}
|
|
188
|
+
export function judgeConsolidationValve(input) {
|
|
189
|
+
const { config, memoryEngine } = input;
|
|
190
|
+
const wiring = {
|
|
191
|
+
memoryEngineWired: memoryEngine !== undefined,
|
|
192
|
+
controlPlaneOwned: backendOwnsControlPlane(memoryEngine?.backend),
|
|
193
|
+
memoryEngineBackend: config.memoryEngineBackend,
|
|
194
|
+
...(config.memoryProvenance !== undefined ? { provenance: config.memoryProvenance } : {}),
|
|
195
|
+
};
|
|
196
|
+
const code = consolidationDriverUnwirable(wiring);
|
|
197
|
+
const unwirable = code !== undefined ? { code, facts: wiring } : undefined;
|
|
198
|
+
const valve = resolveConsolidationValve({ driver: config.memoryConsolidationDriver, unwirable, plane: { models: config.models, tiers: config.tiers, roles: config.roles } });
|
|
199
|
+
return { valve, wiring, unwirable };
|
|
200
|
+
}
|
|
201
|
+
function decideConsolidationValve(input) {
|
|
202
|
+
const declared = input.driver.valve;
|
|
203
|
+
switch (declared) {
|
|
204
|
+
case "off":
|
|
205
|
+
return { valve: { enabled: false, source: "explicit", catalogDependent: false }, offBecause: `it is "off"` };
|
|
206
|
+
case "on": {
|
|
207
|
+
if (input.unwirable !== undefined)
|
|
208
|
+
return { valve: { enabled: true, source: "explicit", catalogDependent: false } };
|
|
209
|
+
const billing = consolidationSeatBillingOf(input.plane);
|
|
210
|
+
if (billing.kind !== "main-model")
|
|
211
|
+
return { valve: { enabled: true, source: "explicit", catalogDependent: false } };
|
|
212
|
+
return {
|
|
213
|
+
valve: {
|
|
214
|
+
enabled: true,
|
|
215
|
+
source: "explicit",
|
|
216
|
+
catalogDependent: false,
|
|
217
|
+
notice: {
|
|
218
|
+
tag: "memory_consolidation_on_main_model",
|
|
219
|
+
detail: `MEMORY_CONSOLIDATION_DRIVER=on armed the memory-consolidation valve, but its consolidation seat resolves to the main model ` +
|
|
220
|
+
`"${billing.mainModel}" (no MODEL_CHEAP_ID naming another model, and no catalog roles.consolidate / roles.summarize / "flash" tier binding to another ` +
|
|
221
|
+
`model) — every consolidation round (the manual endpoint, and the automatic one after each recommending task unless ` +
|
|
222
|
+
`MEMORY_AUTO_CONSOLIDATION=off) reads the whole library (~1e5 prompt tokens per cycle) at the main model's rate. Honored because ` +
|
|
223
|
+
`it was declared explicitly; set MODEL_CHEAP_ID to a cheaper model on the same gateway to move consolidation off the main model.`,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
case undefined: {
|
|
229
|
+
if (input.unwirable !== undefined) {
|
|
230
|
+
return { valve: { enabled: false, source: "derived", catalogDependent: false }, offBecause: `it is unset, and the valve cannot run on this deployment: ${unwirableClause(input.unwirable.code, input.unwirable.facts)}` };
|
|
231
|
+
}
|
|
232
|
+
const billing = consolidationSeatBillingOf(input.plane);
|
|
233
|
+
if (billing.kind === "own-model")
|
|
234
|
+
return { valve: { enabled: true, source: "derived", catalogDependent: true } };
|
|
235
|
+
const seatFact = billing.kind === "main-model"
|
|
236
|
+
? `Here the seat resolves to the main model "${billing.mainModel}" (no MODEL_CHEAP_ID naming another model, and no catalog roles.consolidate / roles.summarize / "flash" tier binding to another model)`
|
|
237
|
+
: `Here no consolidation seat resolves at all (${billing.fault})`;
|
|
238
|
+
return {
|
|
239
|
+
valve: {
|
|
240
|
+
enabled: false,
|
|
241
|
+
source: "derived",
|
|
242
|
+
catalogDependent: true,
|
|
243
|
+
notice: {
|
|
244
|
+
tag: "memory_consolidation_default_off_no_cheap_model",
|
|
245
|
+
detail: `MEMORY_CONSOLIDATION_DRIVER is unset, so the memory-consolidation valve takes its factory default: ON only when the ` +
|
|
246
|
+
`consolidation seat runs on a model other than the main one. ${seatFact} — so the valve stays OFF (no manual endpoints, ` +
|
|
247
|
+
`no automatic whole-library folds): one consolidation round reads the whole library (~1e5 prompt tokens per cycle) and ` +
|
|
248
|
+
`would bill at the main model's rate. Set MODEL_CHEAP_ID to a cheaper model on the same gateway (or bind roles.consolidate / ` +
|
|
249
|
+
`roles.summarize / a "flash" tier to one in the catalog) to arm it; set MEMORY_CONSOLIDATION_DRIVER=off to silence this ` +
|
|
250
|
+
`line (MEMORY_CONSOLIDATION_DRIVER=on arms it on the main model deliberately).`,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
offBecause: `it is unset, and the factory default arms the valve only when the consolidation seat runs on a model other than the main ` +
|
|
254
|
+
`one — ${billing.kind === "main-model" ? `here it resolves to the main model "${billing.mainModel}"` : `here no seat resolves (${billing.fault})`}`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
default: {
|
|
258
|
+
const unreachable = declared;
|
|
259
|
+
throw new Error(`unhandled MEMORY_CONSOLIDATION_DRIVER declaration: ${String(unreachable)}`);
|
|
260
|
+
}
|
|
103
261
|
}
|
|
104
262
|
}
|
|
105
263
|
export function buildConsolidationValveAudit(input) {
|
|
106
|
-
if (!input.enabled)
|
|
264
|
+
if (!input.valve.enabled || input.valve.source !== "explicit")
|
|
107
265
|
return { warnings: [] };
|
|
108
266
|
const warnings = [];
|
|
109
267
|
if (input.operatorPrincipals.length === 0) {
|
|
@@ -151,10 +309,12 @@ export function assertAutoConsolidationWirable(input) {
|
|
|
151
309
|
`recommended one — memory content EGRESSES to the configured consolidation model), but this deployment wires NO ` +
|
|
152
310
|
`engine-side consolidation seat (RunnerDeps.memoryConsolidation). The engine refuses that combination at EVERY ` +
|
|
153
311
|
`prepare (config.auto_consolidation), so this worker would boot fine and then fail every single task. ` +
|
|
154
|
-
`The engine-side seats are wired IFF
|
|
155
|
-
`
|
|
156
|
-
`
|
|
157
|
-
`
|
|
312
|
+
`The engine-side seats are wired IFF the consolidation valve is on and a memory engine is wired — the valve is on ` +
|
|
313
|
+
`with MEMORY_CONSOLIDATION_DRIVER=on, or with it unset when the consolidation seat runs on a model other than the ` +
|
|
314
|
+
`main one (MODEL_CHEAP_ID; the factory default) — so either set MEMORY_CONSOLIDATION_DRIVER=on (which also mounts the ` +
|
|
315
|
+
`manual valve, and note that BOTH knobs then bill whole-library model calls), configure MODEL_CHEAP_ID and leave it ` +
|
|
316
|
+
`unset, or set MEMORY_AUTO_CONSOLIDATION=off (unsetting it is NOT the same: the engine's named default is ON as of ` +
|
|
317
|
+
`core 7.21.1).`);
|
|
158
318
|
}
|
|
159
319
|
export function createMemoryConsolidationFaces(store, input) {
|
|
160
320
|
const engine = new MemoryEngine({
|
|
@@ -39,6 +39,8 @@ export interface ResolveSpecCtx {
|
|
|
39
39
|
/** 两张 per-session 登记簿的**租户围栏**(codex R1-F1):每次提交对拍本会话的主人,换主即清。
|
|
40
40
|
* 论证(为什么 device lane 把这条从「不需要」变成「必需」)逐字见 `createSessionScopedRegistries` 头注。 */
|
|
41
41
|
fenceSessionOwner: (sid: string, owner: string | null) => void;
|
|
42
|
+
/** S-506([ref] §5):装配期化身读数的写腿(per-session 登记簿,与围栏同一只;记忆座同步从那里读)。 */
|
|
43
|
+
noteSessionIncarnation: (sid: string, incarnation: string | undefined) => void;
|
|
42
44
|
hookLlm: ReturnType<typeof createHookLlm>["hookLlm"];
|
|
43
45
|
hookAgent: HookLlmCall;
|
|
44
46
|
fleetBus: FleetEventBus;
|