@zhushanwen/zcode-subagent-cli 0.2.1 → 0.3.1
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/dist/{chunk-WQ7NVGKB.js → chunk-I5OX22FU.js} +342 -64
- package/dist/index.cjs +363 -86
- package/dist/index.d.cts +24 -20
- package/dist/index.d.ts +24 -20
- package/dist/index.js +1 -1
- package/dist/main.cjs +366 -89
- package/dist/main.js +1 -1
- package/package.json +3 -3
|
@@ -22,7 +22,6 @@ function parseZcodeTurnTimeoutEnv(raw) {
|
|
|
22
22
|
if (!Number.isFinite(parsed)) return { state: "invalid" };
|
|
23
23
|
return { state: "valid", ms: parsed };
|
|
24
24
|
}
|
|
25
|
-
var ZCODE_SHARED_POOL_KEY = "shared";
|
|
26
25
|
var ZCODE_APPSERVER_STOP_TIMEOUT_MS = 3e3;
|
|
27
26
|
var ZCODE_APPSERVER_ABORT_GRACE_MS = 3e3;
|
|
28
27
|
var ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING = -32603;
|
|
@@ -31,6 +30,9 @@ var ZCODE_APPSERVER_HARVEST_GRACE_MS = 1e3;
|
|
|
31
30
|
function isFailedTerminalStatus(status) {
|
|
32
31
|
return status === "failed" || status === "error";
|
|
33
32
|
}
|
|
33
|
+
var ZCODE_RESUME_HISTORY_TOKEN_BUDGET = 24e3;
|
|
34
|
+
var ZCODE_RESUME_CHARS_PER_TOKEN = 4;
|
|
35
|
+
var ZCODE_SESSION_SWEEP_DEFER_MS = 50;
|
|
34
36
|
|
|
35
37
|
// src/db-path.ts
|
|
36
38
|
import * as os from "os";
|
|
@@ -278,9 +280,9 @@ async function readZcodeSessionView(dbPath, sessionId) {
|
|
|
278
280
|
}
|
|
279
281
|
|
|
280
282
|
// src/zcode-engine.ts
|
|
281
|
-
import * as
|
|
283
|
+
import * as fs6 from "fs";
|
|
282
284
|
import * as path4 from "path";
|
|
283
|
-
import { getLogger as
|
|
285
|
+
import { getLogger as getLogger4 } from "@zhushanwen/subagent-engine-sdk";
|
|
284
286
|
import {
|
|
285
287
|
buildEngineChildEnv as buildEngineChildEnv2,
|
|
286
288
|
buildSchemaEmulationSegment,
|
|
@@ -289,6 +291,7 @@ import {
|
|
|
289
291
|
synthesizeTimeoutOutcome,
|
|
290
292
|
engineTimeoutDetail,
|
|
291
293
|
resolvePoolDir,
|
|
294
|
+
SHARED_POOL_KEY,
|
|
292
295
|
spawnEngineChild as spawnEngineChild2
|
|
293
296
|
} from "@zhushanwen/subagent-engine-sdk";
|
|
294
297
|
|
|
@@ -779,7 +782,7 @@ var AppServerConnection = class {
|
|
|
779
782
|
}
|
|
780
783
|
const timeoutMs = opts?.timeoutMs ?? this.requestTimeoutMs;
|
|
781
784
|
const id = ++this.reqSeq;
|
|
782
|
-
return new Promise((
|
|
785
|
+
return new Promise((resolve2, reject) => {
|
|
783
786
|
const timer = setTimeout(() => {
|
|
784
787
|
this.pending.delete(id);
|
|
785
788
|
reject(new Error(`\u8BF7\u6C42 ${method} \u8D85\u65F6\uFF08${timeoutMs}ms\uFF09`));
|
|
@@ -787,7 +790,7 @@ var AppServerConnection = class {
|
|
|
787
790
|
this.pending.set(id, {
|
|
788
791
|
// 泛型 T 是调用方对线上 unknown 结果的视图声明,连接层不做运行时校验
|
|
789
792
|
// (协议层保持透明,结构校验归会话层/消费方)。
|
|
790
|
-
resolve,
|
|
793
|
+
resolve: resolve2,
|
|
791
794
|
reject,
|
|
792
795
|
timer,
|
|
793
796
|
method
|
|
@@ -1138,6 +1141,46 @@ function extractReadUsage(readResult) {
|
|
|
1138
1141
|
}
|
|
1139
1142
|
return isRecord3(readResult.usage) ? readResult.usage : void 0;
|
|
1140
1143
|
}
|
|
1144
|
+
function extractResumeHistory(resumeResult) {
|
|
1145
|
+
if (!isRecord3(resumeResult) || !Array.isArray(resumeResult.messages)) return [];
|
|
1146
|
+
const turns = [];
|
|
1147
|
+
for (const m of resumeResult.messages) {
|
|
1148
|
+
if (!isRecord3(m)) continue;
|
|
1149
|
+
const role = messageRole(m);
|
|
1150
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
1151
|
+
const text = partsToText(m.parts) ?? contentToText(m.content);
|
|
1152
|
+
if (text === void 0 || text === "") continue;
|
|
1153
|
+
turns.push({ role, text });
|
|
1154
|
+
}
|
|
1155
|
+
return turns;
|
|
1156
|
+
}
|
|
1157
|
+
function accumulateTokenFields(source, keys, acc) {
|
|
1158
|
+
for (const key of keys) {
|
|
1159
|
+
const raw = source[key];
|
|
1160
|
+
const v = typeof raw === "number" ? raw : Number(raw);
|
|
1161
|
+
if (Number.isFinite(v) && v > 0) {
|
|
1162
|
+
acc.total += v;
|
|
1163
|
+
acc.has = true;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
function accumulateStepFinishTokens(parts, acc) {
|
|
1168
|
+
for (const p of parts) {
|
|
1169
|
+
if (!isRecord3(p) || p.type !== "step-finish" || !isRecord3(p.tokens)) continue;
|
|
1170
|
+
const t = p.tokens;
|
|
1171
|
+
accumulateTokenFields(t, ["input", "output"], acc);
|
|
1172
|
+
if (isRecord3(t.cache)) accumulateTokenFields(t.cache, ["read", "write"], acc);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function extractResumeTotalTokens(resumeResult) {
|
|
1176
|
+
if (!isRecord3(resumeResult) || !Array.isArray(resumeResult.messages)) return void 0;
|
|
1177
|
+
const acc = { total: 0, has: false };
|
|
1178
|
+
for (const m of resumeResult.messages) {
|
|
1179
|
+
if (!isRecord3(m) || messageRole(m) !== "assistant" || !Array.isArray(m.parts)) continue;
|
|
1180
|
+
accumulateStepFinishTokens(m.parts, acc);
|
|
1181
|
+
}
|
|
1182
|
+
return acc.has ? acc.total : void 0;
|
|
1183
|
+
}
|
|
1141
1184
|
var TurnTimeoutError = class extends Error {
|
|
1142
1185
|
kind;
|
|
1143
1186
|
elapsed;
|
|
@@ -1298,6 +1341,17 @@ var SessionChannel = class {
|
|
|
1298
1341
|
);
|
|
1299
1342
|
}
|
|
1300
1343
|
}
|
|
1344
|
+
/**
|
|
1345
|
+
* session/resume 读通道([U6 / §3.2.6 要点 3,P-1 探针实证]):应答自带完整
|
|
1346
|
+
* 双向 messages 历史(user/assistant 全量、tokens、parts)——zcode 续聊选型的
|
|
1347
|
+
* 历史来源(读通道成立;原地 resume 续写被 -32031 卡死,写通道不可用)。错误
|
|
1348
|
+
* 原样上抛(调用方决定降级形态——引擎侧 warn 后无历史前缀开新会话)。
|
|
1349
|
+
* 经 conn.request 惰性启动连接(resume 是 run 的**首个**请求——调用时点连接
|
|
1350
|
+
* 可能尚未启动,与 readBestEffort 的「已死跳过」判据不同场景,不做 alive 守卫)。
|
|
1351
|
+
*/
|
|
1352
|
+
async resumeSession(sessionId) {
|
|
1353
|
+
return this.conn.request("session/resume", { sessionId }, { timeoutMs: ZCODE_APPSERVER_TURN_READ_TIMEOUT_MS });
|
|
1354
|
+
}
|
|
1301
1355
|
/**
|
|
1302
1356
|
* session/close(A.2 ⑦,D4 用后即毁——回收引擎驻留内存,SQLite 持久化保留)。
|
|
1303
1357
|
* best-effort:失败只 warn 不抛(会话已终态,close 失败只泄漏一个驻留会话,
|
|
@@ -1644,8 +1698,166 @@ var SessionChannel = class {
|
|
|
1644
1698
|
}
|
|
1645
1699
|
};
|
|
1646
1700
|
|
|
1647
|
-
// src/
|
|
1701
|
+
// src/session-db-maintenance.ts
|
|
1702
|
+
import * as fs5 from "fs";
|
|
1703
|
+
import { getLogger as getLogger3 } from "@zhushanwen/subagent-engine-sdk";
|
|
1648
1704
|
var logger3 = getLogger3("subagents");
|
|
1705
|
+
var ZCODE_SESSION_TTL_MS = 2592e6;
|
|
1706
|
+
var ZCODE_SESSION_SWEEP_INTERVAL_MS = 864e5;
|
|
1707
|
+
var SWEEP_BUSY_TIMEOUT_MS = 5e3;
|
|
1708
|
+
var SESSION_CHILD_TABLES = [
|
|
1709
|
+
"message",
|
|
1710
|
+
"todo",
|
|
1711
|
+
"session_entry",
|
|
1712
|
+
"session_input",
|
|
1713
|
+
"session_target",
|
|
1714
|
+
"model_usage",
|
|
1715
|
+
"turn_usage",
|
|
1716
|
+
"tool_usage"
|
|
1717
|
+
];
|
|
1718
|
+
function loadDatabaseSyncCtor() {
|
|
1719
|
+
const getBuiltin = process.getBuiltinModule;
|
|
1720
|
+
if (typeof getBuiltin !== "function") return void 0;
|
|
1721
|
+
const mod = getBuiltin("node:sqlite");
|
|
1722
|
+
return typeof mod?.DatabaseSync === "function" ? mod.DatabaseSync : void 0;
|
|
1723
|
+
}
|
|
1724
|
+
function sessionLastActivity(row) {
|
|
1725
|
+
for (const v of [row.time_updated, row.time_created]) {
|
|
1726
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1727
|
+
}
|
|
1728
|
+
return null;
|
|
1729
|
+
}
|
|
1730
|
+
function collectExpiredSessions(db, cutoff, keep) {
|
|
1731
|
+
const rows = db.prepare("SELECT id, time_updated, time_created FROM session").all();
|
|
1732
|
+
const expired = [];
|
|
1733
|
+
let keptActive = 0;
|
|
1734
|
+
for (const row of rows) {
|
|
1735
|
+
if (typeof row.id !== "string" || row.id === "") continue;
|
|
1736
|
+
const last = sessionLastActivity(row);
|
|
1737
|
+
if (last === null || last >= cutoff) continue;
|
|
1738
|
+
if (keep.has(row.id)) {
|
|
1739
|
+
keptActive++;
|
|
1740
|
+
continue;
|
|
1741
|
+
}
|
|
1742
|
+
expired.push(row.id);
|
|
1743
|
+
}
|
|
1744
|
+
return { expired, keptActive };
|
|
1745
|
+
}
|
|
1746
|
+
function ensureForeignKeysOn(db) {
|
|
1747
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
1748
|
+
const fkOn = db.prepare("PRAGMA foreign_keys").get();
|
|
1749
|
+
if (fkOn?.foreign_keys !== 1) {
|
|
1750
|
+
throw new Error("PRAGMA foreign_keys=ON \u672A\u751F\u6548\u2014\u2014\u4E2D\u6B62\u5220\u9664\uFF08\u9632\u5B64\u513F\u884C\uFF09\uFF0C\u672C\u8F6E sweep \u653E\u5F03");
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
function listPresentTables(db) {
|
|
1754
|
+
return new Set(
|
|
1755
|
+
db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((r) => typeof r.name === "string" ? r.name : "")
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
var DELETE_BATCH_SIZE = 500;
|
|
1759
|
+
function chunkIds(ids, size) {
|
|
1760
|
+
const batches = [];
|
|
1761
|
+
for (let i = 0; i < ids.length; i += size) batches.push(ids.slice(i, i + size));
|
|
1762
|
+
return batches;
|
|
1763
|
+
}
|
|
1764
|
+
function deleteExpiredSessionsInTx(db, expired, present) {
|
|
1765
|
+
db.exec("BEGIN");
|
|
1766
|
+
try {
|
|
1767
|
+
let swept = 0;
|
|
1768
|
+
for (const batch of chunkIds(expired, DELETE_BATCH_SIZE)) {
|
|
1769
|
+
const ph = batch.map(() => "?").join(", ");
|
|
1770
|
+
for (const table of SESSION_CHILD_TABLES) {
|
|
1771
|
+
if (present.has(table)) db.prepare(`DELETE FROM ${table} WHERE session_id IN (${ph})`).run(...batch);
|
|
1772
|
+
}
|
|
1773
|
+
if (present.has("session_task_link")) {
|
|
1774
|
+
db.prepare(`DELETE FROM session_task_link WHERE child_session_id IN (${ph})`).run(...batch);
|
|
1775
|
+
db.prepare(`UPDATE session_task_link SET parent_session_id = NULL WHERE parent_session_id IN (${ph})`).run(...batch);
|
|
1776
|
+
}
|
|
1777
|
+
if (present.has("workflow_run")) {
|
|
1778
|
+
db.prepare(`UPDATE workflow_run SET parent_session_id = NULL WHERE parent_session_id IN (${ph})`).run(...batch);
|
|
1779
|
+
}
|
|
1780
|
+
if (present.has("workflow_activity")) {
|
|
1781
|
+
db.prepare(`UPDATE workflow_activity SET child_session_id = NULL WHERE child_session_id IN (${ph})`).run(...batch);
|
|
1782
|
+
}
|
|
1783
|
+
if (present.has("input_history")) {
|
|
1784
|
+
db.prepare(`DELETE FROM input_history WHERE session_id IN (${ph})`).run(...batch);
|
|
1785
|
+
}
|
|
1786
|
+
const res = db.prepare(`DELETE FROM session WHERE id IN (${ph})`).run(...batch);
|
|
1787
|
+
swept += res.changes ?? batch.length;
|
|
1788
|
+
}
|
|
1789
|
+
db.exec("COMMIT");
|
|
1790
|
+
return swept;
|
|
1791
|
+
} catch (err) {
|
|
1792
|
+
try {
|
|
1793
|
+
db.exec("ROLLBACK");
|
|
1794
|
+
} catch (rollbackErr) {
|
|
1795
|
+
void rollbackErr;
|
|
1796
|
+
}
|
|
1797
|
+
throw err;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
function sweepExpiredZcodeSessions(dbPath, opts = {}) {
|
|
1801
|
+
const noOp = { swept: 0, keptActive: 0 };
|
|
1802
|
+
if (!fs5.existsSync(dbPath)) return noOp;
|
|
1803
|
+
const Ctor = loadDatabaseSyncCtor();
|
|
1804
|
+
if (Ctor === void 0) {
|
|
1805
|
+
logger3.warn("[zcode-ttl] node:sqlite \u4E0D\u53EF\u7528\uFF08\u9700 Node \u226522.3 \u7684 process.getBuiltinModule\uFF09\u2014\u2014\u672C\u8F6E sweep \u8DF3\u8FC7");
|
|
1806
|
+
return noOp;
|
|
1807
|
+
}
|
|
1808
|
+
const now = opts.nowMs ?? Date.now();
|
|
1809
|
+
const ttl = opts.ttlMs ?? ZCODE_SESSION_TTL_MS;
|
|
1810
|
+
const cutoff = now - ttl;
|
|
1811
|
+
const keep = opts.keepSessionIds ?? /* @__PURE__ */ new Set();
|
|
1812
|
+
let db;
|
|
1813
|
+
try {
|
|
1814
|
+
db = new Ctor(dbPath);
|
|
1815
|
+
db.exec(`PRAGMA busy_timeout = ${SWEEP_BUSY_TIMEOUT_MS}`);
|
|
1816
|
+
const { expired, keptActive } = collectExpiredSessions(db, cutoff, keep);
|
|
1817
|
+
if (expired.length === 0) {
|
|
1818
|
+
db.close();
|
|
1819
|
+
return { swept: 0, keptActive };
|
|
1820
|
+
}
|
|
1821
|
+
ensureForeignKeysOn(db);
|
|
1822
|
+
const present = listPresentTables(db);
|
|
1823
|
+
const swept = deleteExpiredSessionsInTx(db, expired, present);
|
|
1824
|
+
logger3.debug(`[zcode-ttl] \u9694\u79BB\u5E93\u8D85\u7A97\u6761\u76EE\u5DF2\u6E05\uFF1A${swept} \u6761\uFF08TTL ${ttl}ms\uFF0C\u8C41\u514D\u6D3B\u8DC3 ${keptActive}\uFF09`, { dbPath });
|
|
1825
|
+
try {
|
|
1826
|
+
db.prepare("PRAGMA wal_checkpoint(PASSIVE)").get();
|
|
1827
|
+
} catch (err) {
|
|
1828
|
+
void err;
|
|
1829
|
+
}
|
|
1830
|
+
return { swept, keptActive };
|
|
1831
|
+
} catch (err) {
|
|
1832
|
+
logger3.warn(
|
|
1833
|
+
`[zcode-ttl] sweep \u5931\u8D25\uFF08\u8F85\u52A9\u8D44\u6E90\u9762\uFF0C\u4E0D\u5F71\u54CD run \u4E3B\u94FE\u8DEF\uFF1B\u4E0B\u4E2A\u8282\u6D41\u7A97\u91CD\u8BD5\uFF09: ${err instanceof Error ? err.message : String(err)}`,
|
|
1834
|
+
{ dbPath }
|
|
1835
|
+
);
|
|
1836
|
+
return noOp;
|
|
1837
|
+
} finally {
|
|
1838
|
+
try {
|
|
1839
|
+
db?.close();
|
|
1840
|
+
} catch (err) {
|
|
1841
|
+
void err;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
var sweepThrottle = /* @__PURE__ */ new Map();
|
|
1846
|
+
function maybeSweepExpiredZcodeSessions(dbPath, opts = {}) {
|
|
1847
|
+
const now = Date.now();
|
|
1848
|
+
const last = sweepThrottle.get(dbPath);
|
|
1849
|
+
if (!opts.force && last !== void 0 && now - last < ZCODE_SESSION_SWEEP_INTERVAL_MS) {
|
|
1850
|
+
return { swept: 0, keptActive: 0 };
|
|
1851
|
+
}
|
|
1852
|
+
const result = sweepExpiredZcodeSessions(dbPath, {
|
|
1853
|
+
...opts.keepSessionIds !== void 0 ? { keepSessionIds: opts.keepSessionIds } : {}
|
|
1854
|
+
});
|
|
1855
|
+
sweepThrottle.set(dbPath, now);
|
|
1856
|
+
return result;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// src/zcode-engine.ts
|
|
1860
|
+
var logger4 = getLogger4("subagents");
|
|
1649
1861
|
var PROBE_VERSION_TIMEOUT_MS = 15e3;
|
|
1650
1862
|
var COMMON_THOUGHT_LEVELS = ["low", "high", "max"];
|
|
1651
1863
|
var ZcodeEngine = class {
|
|
@@ -1672,8 +1884,11 @@ var ZcodeEngine = class {
|
|
|
1672
1884
|
schemaEnforcement: "emulated",
|
|
1673
1885
|
// send-while-running 恒 -32010 硬错误(旧实测)——app-server 常驻化不改变此判据
|
|
1674
1886
|
steer: "unsupported",
|
|
1675
|
-
//
|
|
1676
|
-
|
|
1887
|
+
// [U6 / §3.2.6 要点 4] "cold" = 冷恢复会话:session/resume 读通道取结构化
|
|
1888
|
+
// 历史 + session/create 新会话注入首轮(无热 steering——interrupt 维持
|
|
1889
|
+
// kill-only 如实声明)。P-1 探针:原地 resume 续写被 -32031 卡死,热会话
|
|
1890
|
+
// 通道不可用,声明不越级。
|
|
1891
|
+
conversation: "cold",
|
|
1677
1892
|
// 无 --append-system-prompt flag(实测拒收)——persona 只能拼进 prompt
|
|
1678
1893
|
personaInjection: "prompt",
|
|
1679
1894
|
// app-server 推送流实时流出(session/event payload.delta → text_delta)
|
|
@@ -1717,7 +1932,7 @@ var ZcodeEngine = class {
|
|
|
1717
1932
|
}
|
|
1718
1933
|
/** check 1:二进制存在性(isFile 才算——同名目录不是可执行入口)。 */
|
|
1719
1934
|
probeBinaryCheck(cliPath) {
|
|
1720
|
-
const binaryOk =
|
|
1935
|
+
const binaryOk = fs6.existsSync(cliPath) && fs6.statSync(cliPath).isFile();
|
|
1721
1936
|
return {
|
|
1722
1937
|
name: "binary",
|
|
1723
1938
|
ok: binaryOk,
|
|
@@ -1755,8 +1970,8 @@ var ZcodeEngine = class {
|
|
|
1755
1970
|
/**
|
|
1756
1971
|
* 常驻路径主编排:模型解析(v2 单源校验)→ 惰性连接 + runTurn(事件时序前移:
|
|
1757
1972
|
* text_delta 流式、终态后 message_end/turn_end)→ schema 仿真重试 → outcome/handle。
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
1973
|
+
* onHandleReady 在 create 应答后(§3.4 不变量 3;原 onPoolResolved prepare 期
|
|
1974
|
+
* 声明已随 [池抽象降级 2026-09-13] poolKey 协议面退役删除)。
|
|
1760
1975
|
*/
|
|
1761
1976
|
async runViaAppServer(task, ctx) {
|
|
1762
1977
|
const startedAt = Date.now();
|
|
@@ -1766,10 +1981,10 @@ var ZcodeEngine = class {
|
|
|
1766
1981
|
const modelRef = resolveZcodeModelRef(task.model, this.deps.sources);
|
|
1767
1982
|
this.warnIgnoredCtxModel(task, ctx, modelRef);
|
|
1768
1983
|
this.warnThoughtLevelUncommon(task, ctx);
|
|
1769
|
-
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
1770
1984
|
const cwd = task.cwd ?? process.cwd();
|
|
1771
1985
|
const schema = isPlainObject(task.schema) ? task.schema : void 0;
|
|
1772
|
-
const
|
|
1986
|
+
const resumePrefix = await this.buildResumeHistoryPrefix(ctx);
|
|
1987
|
+
const basePrompt = resumePrefix === void 0 ? this.buildPrompt(task, schema) : resumePrefix + this.buildPrompt(task, schema);
|
|
1773
1988
|
const usageAcc = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, has: false };
|
|
1774
1989
|
const final = await this.runAppServerAttemptsWithRetry(task, ctx, modelRef, cwd, basePrompt, schema, usageAcc);
|
|
1775
1990
|
const outcome = this.finalizeOutcome(task, ctx, final, usageAcc, startedAt);
|
|
@@ -1777,7 +1992,6 @@ var ZcodeEngine = class {
|
|
|
1777
1992
|
}
|
|
1778
1993
|
/** pre-aborted 短路收口:合成中止 outcome + 'shared' 锚定 handle。 */
|
|
1779
1994
|
abortedAppServerRun(task, ctx, startedAt) {
|
|
1780
|
-
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
1781
1995
|
const outcome = this.finalizeOutcome(
|
|
1782
1996
|
task,
|
|
1783
1997
|
ctx,
|
|
@@ -1811,11 +2025,11 @@ var ZcodeEngine = class {
|
|
|
1811
2025
|
if (final.kind === "run-failed" && final.transient !== void 0 && ctx.signal?.aborted !== true && !this.disposed) {
|
|
1812
2026
|
const budget = resolveTransientRetryBudget(explicitTurnBudgetMs(), Date.now() - attemptStartedAt);
|
|
1813
2027
|
if (budget.state === "depleted") {
|
|
1814
|
-
|
|
2028
|
+
logger4.warn(
|
|
1815
2029
|
`[zcode-engine] \u672B\u6B21 attempt \u77AC\u65F6\u5931\u8D25\uFF08${final.transient}\uFF09\u2014\u2014\u663E\u5F0F\u603B\u4E0A\u754C\u9884\u7B97\u5269\u4F59\u4E0D\u8DB3 ${ZCODE_TURN_RETRY_MIN_BUDGET_MS}ms\uFF0C\u4E0D\u91CD\u8BD5\u76F4\u63A5\u7EC8\u6001\u5316\uFF08\u9884\u7B97\u7EE7\u627F\uFF1A\u91CD\u8BD5\u4E0D\u91CD\u7F6E\u603B\u9884\u7B97\uFF09`
|
|
1816
2030
|
);
|
|
1817
2031
|
} else {
|
|
1818
|
-
|
|
2032
|
+
logger4.warn(
|
|
1819
2033
|
`[zcode-engine] \u672B\u6B21 attempt \u77AC\u65F6\u5931\u8D25\uFF08${final.transient}\uFF09\u2014\u2014\u6B62\u635F\u94FE\u5DF2\u7EC8\u5C40\uFF0C\u65B0\u4F1A\u8BDD\u81EA\u52A8\u91CD\u8BD5\u4E00\u6B21` + (budget.state === "inherit" ? `\uFF08\u9884\u7B97\u7EE7\u627F\uFF1A\u91CD\u8BD5\u8F6E\u603B\u4E0A\u754C=\u5269\u4F59 ${budget.remainingMs}ms\uFF0C\u4E0D\u91CD\u7F6E\u603B\u9884\u7B97\uFF09` : "\uFF08\u65E0\u663E\u5F0F\u603B\u4E0A\u754C\u9884\u7B97\uFF0C\u91CD\u8BD5\u8F6E\u8D70 env/\u9ED8\u8BA4\u4E0A\u754C\uFF09")
|
|
1820
2034
|
);
|
|
1821
2035
|
const retry = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt, {
|
|
@@ -1851,7 +2065,6 @@ var ZcodeEngine = class {
|
|
|
1851
2065
|
dbPath: zcodeSessionDbPath(this.deps.engineDataDir()),
|
|
1852
2066
|
...outcome.sessionId !== void 0 ? { sessionId: outcome.sessionId } : {}
|
|
1853
2067
|
},
|
|
1854
|
-
poolKey: ZCODE_SHARED_POOL_KEY,
|
|
1855
2068
|
...this.probeCache?.engineVersion !== void 0 && this.probeCache.engineVersion !== "" ? { engineVersion: this.probeCache.engineVersion } : {},
|
|
1856
2069
|
adapterVersion: ZCODE_ADAPTER_VERSION
|
|
1857
2070
|
}
|
|
@@ -1871,8 +2084,8 @@ var ZcodeEngine = class {
|
|
|
1871
2084
|
const createParams = buildAppServerCreateParams(task, providerId, modelId, cwd);
|
|
1872
2085
|
let currentSessionId;
|
|
1873
2086
|
let signalSessionCreated;
|
|
1874
|
-
const sessionCreated = new Promise((
|
|
1875
|
-
signalSessionCreated =
|
|
2087
|
+
const sessionCreated = new Promise((resolve2) => {
|
|
2088
|
+
signalSessionCreated = resolve2;
|
|
1876
2089
|
});
|
|
1877
2090
|
const turn = rt.channel.runTurn(createParams, prompt, {
|
|
1878
2091
|
// 事件时序前移:payload.delta → text_delta 实时流出(stream 粒度,D5);
|
|
@@ -1882,16 +2095,16 @@ var ZcodeEngine = class {
|
|
|
1882
2095
|
onThinkingDelta: (delta) => ctx.onEvent?.({ type: "thinking_delta", delta }),
|
|
1883
2096
|
// [PR3] 工具执行期活性:非终态非增量的 session/event 帧(tool.updated progress
|
|
1884
2097
|
// 等,真机探针实证每 ~1s 一帧)→ activity 事件(零载荷纯活性信号——宿主无进展
|
|
1885
|
-
// 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode
|
|
1886
|
-
// conversation:"
|
|
2098
|
+
// 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode 引擎声明
|
|
2099
|
+
// conversation:"cold"(续聊 = session-store 冷恢复重建 + 新 run + resume 锚点,
|
|
2100
|
+
// 无热 steering/chat 域接线),run 域是唯一发射面。
|
|
1887
2101
|
onActivity: () => ctx.onEvent?.({ type: "activity" }),
|
|
1888
2102
|
onSessionCreated: (sessionId) => {
|
|
1889
2103
|
currentSessionId = sessionId;
|
|
1890
2104
|
rt.activeSessions.add(sessionId);
|
|
1891
2105
|
signalSessionCreated?.();
|
|
1892
2106
|
ctx.onHandleReady?.({
|
|
1893
|
-
sessionRef: { dbPath: zcodeSessionDbPath(this.deps.engineDataDir()), sessionId }
|
|
1894
|
-
poolKey: ZCODE_SHARED_POOL_KEY
|
|
2107
|
+
sessionRef: { dbPath: zcodeSessionDbPath(this.deps.engineDataDir()), sessionId }
|
|
1895
2108
|
});
|
|
1896
2109
|
},
|
|
1897
2110
|
// 显式总上界传参(D2/U4:缺省缺席——channel 侧 resolveTurnTimerMs 走 env→默认)
|
|
@@ -1939,14 +2152,14 @@ var ZcodeEngine = class {
|
|
|
1939
2152
|
*/
|
|
1940
2153
|
async awaitConnFinalized(rt) {
|
|
1941
2154
|
if (!rt.conn.alive) return;
|
|
1942
|
-
await new Promise((
|
|
2155
|
+
await new Promise((resolve2) => {
|
|
1943
2156
|
const timer = setTimeout(() => finish(), ZCODE_APPSERVER_HARVEST_GRACE_MS);
|
|
1944
2157
|
if (typeof timer.unref === "function") timer.unref();
|
|
1945
2158
|
const off = rt.conn.onClose(() => finish());
|
|
1946
2159
|
function finish() {
|
|
1947
2160
|
clearTimeout(timer);
|
|
1948
2161
|
off();
|
|
1949
|
-
|
|
2162
|
+
resolve2();
|
|
1950
2163
|
}
|
|
1951
2164
|
});
|
|
1952
2165
|
}
|
|
@@ -1980,7 +2193,7 @@ var ZcodeEngine = class {
|
|
|
1980
2193
|
delayResolved(ZCODE_APPSERVER_ABORT_GRACE_MS, false)
|
|
1981
2194
|
]);
|
|
1982
2195
|
if (settled) return "settled-in-grace";
|
|
1983
|
-
|
|
2196
|
+
logger4.warn(
|
|
1984
2197
|
`[zcode-engine] abort grace \u7A97\u53E3\u5185\u672A\u89C1\u7EC8\u6001\u2014\u2014killChain \u6536\u5272\u5171\u4EAB\u8FDB\u7A0B\uFF08\u63A5\u53D7\u8FDE\u5750\uFF0C\u5728\u9014\u4EFB\u52A1\u8D70\u5D29\u6E83\u8DEF\u5F84\uFF09`
|
|
1985
2198
|
);
|
|
1986
2199
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
@@ -2005,19 +2218,19 @@ var ZcodeEngine = class {
|
|
|
2005
2218
|
} catch (err) {
|
|
2006
2219
|
if (entry.escalateOn === "stop-outcome") {
|
|
2007
2220
|
if (isAppServerRpcError(err)) {
|
|
2008
|
-
|
|
2221
|
+
logger4.debug(
|
|
2009
2222
|
`[zcode-engine] session/stop \u62A5\u534F\u8BAE\u6027 error\uFF08${errMessage3(err)}\uFF09\u2014\u2014\u4F1A\u8BDD\u5DF2\u56DE\u6536\uFF0C\u63A7\u5236\u9762\u5B58\u6D3B\uFF0C\u4E0D\u5347\u7EA7\u6740\u94FE`
|
|
2010
2223
|
);
|
|
2011
2224
|
return "stop-rejected";
|
|
2012
2225
|
}
|
|
2013
|
-
|
|
2226
|
+
logger4.warn(
|
|
2014
2227
|
`[zcode-engine] session/stop \u65E0\u5E94\u7B54\uFF08${errMessage3(err)}\uFF09\u2014\u2014\u5347\u7EA7 killChain \u6536\u5272\u5171\u4EAB\u8FDB\u7A0B\uFF08\u8D85\u65F6\u5165\u53E3\uFF0C\u63A5\u53D7\u8FDE\u5750\uFF09`
|
|
2015
2228
|
);
|
|
2016
2229
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
2017
2230
|
await this.awaitConnFinalized(rt);
|
|
2018
2231
|
return "stop-unreachable-killed";
|
|
2019
2232
|
}
|
|
2020
|
-
|
|
2233
|
+
logger4.debug(
|
|
2021
2234
|
`[zcode-engine] session/stop \u5931\u8D25\uFF08${errMessage3(err)}\uFF09\u2014\u2014grace \u540E\u8D70 killChain \u515C\u5E95`
|
|
2022
2235
|
);
|
|
2023
2236
|
}
|
|
@@ -2046,7 +2259,7 @@ var ZcodeEngine = class {
|
|
|
2046
2259
|
const sessionDbPath = zcodeSessionDbPath(engineDataDir);
|
|
2047
2260
|
env.ZCODE_SESSION_DB_PATH = sessionDbPath;
|
|
2048
2261
|
delete env.ZCODE_SESSION_DB;
|
|
2049
|
-
|
|
2262
|
+
fs6.mkdirSync(path4.dirname(sessionDbPath), { recursive: true });
|
|
2050
2263
|
const conn = new AppServerConnection({
|
|
2051
2264
|
cliPath,
|
|
2052
2265
|
dataDir: engineDataDir,
|
|
@@ -2064,6 +2277,11 @@ var ZcodeEngine = class {
|
|
|
2064
2277
|
activeSessions: /* @__PURE__ */ new Set()
|
|
2065
2278
|
};
|
|
2066
2279
|
this.appserverRuntime = rt;
|
|
2280
|
+
const sweepDbPath = sessionDbPath;
|
|
2281
|
+
const sweepTimer = setTimeout(() => {
|
|
2282
|
+
maybeSweepExpiredZcodeSessions(sweepDbPath, { keepSessionIds: this.appserverRuntime?.activeSessions });
|
|
2283
|
+
}, ZCODE_SESSION_SWEEP_DEFER_MS);
|
|
2284
|
+
if (typeof sweepTimer.unref === "function") sweepTimer.unref();
|
|
2067
2285
|
return rt;
|
|
2068
2286
|
}
|
|
2069
2287
|
/**
|
|
@@ -2081,10 +2299,10 @@ var ZcodeEngine = class {
|
|
|
2081
2299
|
* 先到时 onClose 收割先行,dispose 收割幂等 no-op(零回归)。
|
|
2082
2300
|
*/
|
|
2083
2301
|
async shutdownRuntimeAndDisposeChannel(rt) {
|
|
2084
|
-
const harvested = new Promise((
|
|
2302
|
+
const harvested = new Promise((resolve2) => {
|
|
2085
2303
|
const off = rt.conn.onClose(() => {
|
|
2086
2304
|
off();
|
|
2087
|
-
|
|
2305
|
+
resolve2();
|
|
2088
2306
|
});
|
|
2089
2307
|
});
|
|
2090
2308
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
@@ -2111,17 +2329,6 @@ var ZcodeEngine = class {
|
|
|
2111
2329
|
}
|
|
2112
2330
|
await this.shutdownRuntimeAndDisposeChannel(rt);
|
|
2113
2331
|
}
|
|
2114
|
-
/**
|
|
2115
|
-
* [u7a D5] 引擎在途只读快照:activeSessions 非空才算在途——poolKey 'shared' 的
|
|
2116
|
-
* app-server **空闲常驻进程恒活,禁止按进程存在判定**(进程在 ≠ 在途,否则推迟
|
|
2117
|
-
* 判定恒真、30min 上限从兜底变常态路径,设计 §3.3 D5 显式排除)。计数权威 =
|
|
2118
|
-
* activeSessions(attempt 入 create 应答后 add / finally settle 后 delete,与
|
|
2119
|
-
* dispose 的 close 帧目标集同一状态源,无双记账)。运行时未初始化(从未 run /
|
|
2120
|
-
* 已 dispose)→ 恒 0。
|
|
2121
|
-
*/
|
|
2122
|
-
inFlightSnapshot() {
|
|
2123
|
-
return { inFlight: this.appserverRuntime?.activeSessions.size ?? 0 };
|
|
2124
|
-
}
|
|
2125
2332
|
/** 终态合成(extension-conventions 函数 80 行上限,从 run 提取):aborted / run-failed / parsed 三分支。 */
|
|
2126
2333
|
finalizeOutcome(task, ctx, final, usageAcc, startedAt) {
|
|
2127
2334
|
const outcome = {
|
|
@@ -2210,8 +2417,12 @@ var ZcodeEngine = class {
|
|
|
2210
2417
|
* sessionId 缺失(解析失败的 run 无法定位 session)跳过①级;②级依赖
|
|
2211
2418
|
* handle.journalPath(宿主 run 后回填)。dbPath:新 handle 恒为隔离库绝对路径
|
|
2212
2419
|
* (zcodeSessionDbPath(engineDataDir),tier1 白名单集合见方法体——宿主路径仅
|
|
2213
|
-
* 「共享 HOME 时代」存量兼容);旧 records
|
|
2214
|
-
*
|
|
2420
|
+
* 「共享 HOME 时代」存量兼容);旧 records(池时代)的相对路径仍按池目录锚定
|
|
2421
|
+
* 解析(read 兼容旧数据,池目录不存在时自然落②级 journal 降级)。[池抽象降级
|
|
2422
|
+
* 2026-09-13] 锚定 key 用常量 SHARED_POOL_KEY 承载——协议面 poolKey 已删,池
|
|
2423
|
+
* 时代真实池 key 不再随 handle 传输;若旧 record 的池目录恰为 shared 布局则①级
|
|
2424
|
+
* 仍可达,其余池 key 的旧 record 直接走②级 journal 降级(30 天 TTL 同尺度衰减,
|
|
2425
|
+
* 行为安全)。
|
|
2215
2426
|
*/
|
|
2216
2427
|
async read(handle) {
|
|
2217
2428
|
if (handle.data.engineId !== ZCODE_ENGINE_ID) {
|
|
@@ -2225,21 +2436,28 @@ var ZcodeEngine = class {
|
|
|
2225
2436
|
if (zcodeDbPathAllowlist(this.deps.engineDataDir()).includes(dbPathRaw)) {
|
|
2226
2437
|
dbPath = dbPathRaw;
|
|
2227
2438
|
} else {
|
|
2228
|
-
|
|
2439
|
+
logger4.warn("[zcode-engine] record dbPath \u975E\u767D\u540D\u5355 db \u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u62D2\u7EDD \u2460\u7EA7\u8BFB\u53D6\u964D journal", {
|
|
2229
2440
|
dbPath: dbPathRaw
|
|
2230
2441
|
});
|
|
2231
2442
|
}
|
|
2232
2443
|
} else {
|
|
2233
|
-
|
|
2234
|
-
resolvePoolDir(this.deps.engineDataDir(), ZCODE_ENGINE_ID,
|
|
2235
|
-
dbPathRaw
|
|
2444
|
+
const poolDir = path4.resolve(
|
|
2445
|
+
resolvePoolDir(this.deps.engineDataDir(), ZCODE_ENGINE_ID, SHARED_POOL_KEY)
|
|
2236
2446
|
);
|
|
2447
|
+
const resolved = path4.resolve(poolDir, dbPathRaw);
|
|
2448
|
+
if (resolved.startsWith(poolDir + path4.sep)) {
|
|
2449
|
+
dbPath = resolved;
|
|
2450
|
+
} else {
|
|
2451
|
+
logger4.warn("[zcode-engine] record dbPath \u76F8\u5BF9\u8DEF\u5F84\u9003\u9038\u6C60\u76EE\u5F55\uFF0C\u62D2\u7EDD \u2460\u7EA7\u8BFB\u53D6\u964D journal", {
|
|
2452
|
+
dbPath: dbPathRaw
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2237
2455
|
}
|
|
2238
2456
|
if (dbPath !== void 0) {
|
|
2239
2457
|
try {
|
|
2240
2458
|
return await readZcodeSessionView(dbPath, sessionId);
|
|
2241
2459
|
} catch (err) {
|
|
2242
|
-
|
|
2460
|
+
logger4.warn("[zcode-engine] native session read failed, degrade to journal replay", {
|
|
2243
2461
|
dbPath,
|
|
2244
2462
|
sessionId,
|
|
2245
2463
|
reason: toErrorMessage(err)
|
|
@@ -2265,7 +2483,7 @@ var ZcodeEngine = class {
|
|
|
2265
2483
|
const thoughtLevel = task.thinkingLevel?.trim();
|
|
2266
2484
|
if (thoughtLevel === void 0 || thoughtLevel === "") return;
|
|
2267
2485
|
if (COMMON_THOUGHT_LEVELS.includes(thoughtLevel)) return;
|
|
2268
|
-
|
|
2486
|
+
logger4.warn(
|
|
2269
2487
|
`[zcode-engine] thinkingLevel=${thoughtLevel} \u5DF2\u900F\u4F20\u4E3A thoughtLevel\uFF08\u975E\u5E38\u89C1\u6863\u4F4D\uFF09\uFF1A\u82E5\u76EE\u6807\u6A21\u578B\u4E0D\u652F\u6301\u8BE5\u6863\u4F4D\u5C06\u88AB\u5FFD\u7565/\u56DE\u843D\u5230\u6A21\u578B\u7F3A\u7701\u63A8\u7406\u6863\u4F4D\uFF08\u5E38\u89C1\u6863\u4F4D\uFF1A${COMMON_THOUGHT_LEVELS.join("/")}\uFF09\uFF1B\u6863\u4F4D\u662F\u5426\u751F\u6548\u4EE5\u6A21\u578B\u5B9E\u9645\u884C\u4E3A\u4E3A\u51C6`,
|
|
2270
2488
|
{ taskId: ctx.taskId }
|
|
2271
2489
|
);
|
|
@@ -2283,11 +2501,40 @@ var ZcodeEngine = class {
|
|
|
2283
2501
|
if (ctx.ctxModel === void 0) return;
|
|
2284
2502
|
const requested = task.model?.trim();
|
|
2285
2503
|
if (requested !== void 0 && requested !== "") return;
|
|
2286
|
-
|
|
2504
|
+
logger4.warn(
|
|
2287
2505
|
`[zcode-engine] ctx.ctxModel\uFF08${ctx.ctxModel.id}\uFF09\u88AB\u5FFD\u7565\u2014\u2014ctxModel \u662F pi \u94FE\u8DEF\u515C\u5E95\uFF0Czcode \u4E0D\u6D88\u8D39\uFF1Btask.model \u672A\u663E\u5F0F\u6307\u5B9A\uFF0C\u5B9E\u9645\u4F7F\u7528\u5F15\u64CE\u7F3A\u7701\u6A21\u578B ${modelRef}`,
|
|
2288
2506
|
{ taskId: ctx.taskId }
|
|
2289
2507
|
);
|
|
2290
2508
|
}
|
|
2509
|
+
/**
|
|
2510
|
+
* [U6 / §3.2.6 要点 3] resume 锚 → 历史前缀(interact-resume 的读半段)。
|
|
2511
|
+
*
|
|
2512
|
+
* 无锚 / 非 zcode 锚形态 → undefined(行为不变)。读通道失败(条目被 TTL 清 /
|
|
2513
|
+
* 会话失效 / 控制面错误)= 宿主侧锚判据通过后、引擎派发前的窄竞态窗——降级为
|
|
2514
|
+
* **无历史前缀的新会话**(warn 留痕;宿主 reopen 摘要承担主降级面),不炸轮:
|
|
2515
|
+
* 续聊消息本身仍可送达(模型缺历史上下文,比整轮失败可用性高,与 reopen 降级
|
|
2516
|
+
* 的「带摘要重开」语义同族)。
|
|
2517
|
+
*/
|
|
2518
|
+
async buildResumeHistoryPrefix(ctx) {
|
|
2519
|
+
const anchor = zcodeResumeAnchorOf(ctx, this.deps.engineDataDir());
|
|
2520
|
+
if (anchor === void 0) return void 0;
|
|
2521
|
+
const rt = this.ensureAppServerRuntime();
|
|
2522
|
+
let history;
|
|
2523
|
+
let tokens;
|
|
2524
|
+
try {
|
|
2525
|
+
const result = await rt.channel.resumeSession(anchor.sessionId);
|
|
2526
|
+
history = extractResumeHistory(result);
|
|
2527
|
+
tokens = extractResumeTotalTokens(result);
|
|
2528
|
+
} catch (err) {
|
|
2529
|
+
logger4.warn(
|
|
2530
|
+
`[zcode-engine] session/resume \u8BFB\u5386\u53F2\u5931\u8D25\uFF08\u951A ${anchor.sessionId}\uFF09\u2014\u2014\u964D\u7EA7\u4E3A\u65E0\u5386\u53F2\u524D\u7F00\u7684\u65B0\u4F1A\u8BDD: ${errMessage3(err)}`,
|
|
2531
|
+
{ taskId: ctx.taskId }
|
|
2532
|
+
);
|
|
2533
|
+
return void 0;
|
|
2534
|
+
}
|
|
2535
|
+
if (history.length === 0) return void 0;
|
|
2536
|
+
return buildResumeInjectionSegment(history, anchor.sessionId, tokens);
|
|
2537
|
+
}
|
|
2291
2538
|
/**
|
|
2292
2539
|
* persona 拼接后的完整 prompt(personaInjection: 'prompt'——zcode 无 flag 通道):
|
|
2293
2540
|
* appendSystemPrompt 段在前(人设/约束语境——D6 合流后 persona≡skillPath+
|
|
@@ -2301,6 +2548,38 @@ var ZcodeEngine = class {
|
|
|
2301
2548
|
return segments.join("\n\n");
|
|
2302
2549
|
}
|
|
2303
2550
|
};
|
|
2551
|
+
function zcodeResumeAnchorOf(ctx, engineDataDir) {
|
|
2552
|
+
const ref = ctx.resume?.resume?.sessionRef;
|
|
2553
|
+
if (ref === void 0) return void 0;
|
|
2554
|
+
const sessionId = ref["sessionId"];
|
|
2555
|
+
const dbPath = ref["dbPath"];
|
|
2556
|
+
if (typeof sessionId !== "string" || sessionId === "") return void 0;
|
|
2557
|
+
if (typeof dbPath !== "string" || dbPath === "") return void 0;
|
|
2558
|
+
if (!zcodeDbPathAllowlist(engineDataDir).includes(dbPath)) return void 0;
|
|
2559
|
+
return { sessionId, dbPath };
|
|
2560
|
+
}
|
|
2561
|
+
function buildResumeInjectionSegment(history, priorSessionId, totalTokens, tokenBudget = ZCODE_RESUME_HISTORY_TOKEN_BUDGET) {
|
|
2562
|
+
const budgetTokens = tokenBudget;
|
|
2563
|
+
const charBudget = budgetTokens * ZCODE_RESUME_CHARS_PER_TOKEN;
|
|
2564
|
+
const lines = history.map((t) => `${t.role}: ${t.text}`);
|
|
2565
|
+
let kept = lines;
|
|
2566
|
+
let omitted = 0;
|
|
2567
|
+
while (kept.length > 1 && kept.reduce((n, l) => n + l.length + 1, 0) > charBudget) {
|
|
2568
|
+
kept = kept.slice(1);
|
|
2569
|
+
omitted++;
|
|
2570
|
+
}
|
|
2571
|
+
const omissionNote = omitted > 0 ? `[... ${omitted} older turn(s) omitted to fit the ${tokenBudget}-token history budget ...]
|
|
2572
|
+
` : "";
|
|
2573
|
+
const usageNote = totalTokens !== void 0 ? `
|
|
2574
|
+
(prior session retained ~${totalTokens} tokens of history)` : "";
|
|
2575
|
+
return `[Continued conversation] You are continuing an existing subagent conversation on a NEW session (prior session id: ${priorSessionId}). The new session has no memory of its own \u2014 the full prior conversation history recovered from the session store follows. Continue seamlessly from it; the user message after this block is the next turn of the SAME conversation.
|
|
2576
|
+
|
|
2577
|
+
<conversation_history>
|
|
2578
|
+
${omissionNote}${kept.join("\n")}
|
|
2579
|
+
</conversation_history>${usageNote}
|
|
2580
|
+
|
|
2581
|
+
`;
|
|
2582
|
+
}
|
|
2304
2583
|
var ZCODE_TURN_RETRY_MIN_BUDGET_MS = 3e5;
|
|
2305
2584
|
function resolveTransientRetryBudget(totalBudgetMs, consumedMs) {
|
|
2306
2585
|
if (totalBudgetMs === void 0) return { state: "unbounded" };
|
|
@@ -2318,8 +2597,8 @@ function errMessage3(err) {
|
|
|
2318
2597
|
return toErrorMessage(err);
|
|
2319
2598
|
}
|
|
2320
2599
|
function delayResolved(ms, value) {
|
|
2321
|
-
return new Promise((
|
|
2322
|
-
const t = setTimeout(() =>
|
|
2600
|
+
return new Promise((resolve2) => {
|
|
2601
|
+
const t = setTimeout(() => resolve2(value), ms);
|
|
2323
2602
|
if (typeof t.unref === "function") t.unref();
|
|
2324
2603
|
});
|
|
2325
2604
|
}
|
|
@@ -2474,7 +2753,7 @@ function isHostTimeoutAbort(ctx) {
|
|
|
2474
2753
|
var PROBE_STDOUT_CAP_CHARS = 4096;
|
|
2475
2754
|
async function defaultProbeVersion(cliPath, dataDir) {
|
|
2476
2755
|
try {
|
|
2477
|
-
return await new Promise((
|
|
2756
|
+
return await new Promise((resolve2, reject) => {
|
|
2478
2757
|
const child = spawnEngineChild2({
|
|
2479
2758
|
command: "node",
|
|
2480
2759
|
args: [cliPath, "--version"],
|
|
@@ -2491,11 +2770,11 @@ async function defaultProbeVersion(cliPath, dataDir) {
|
|
|
2491
2770
|
child.once("error", reject);
|
|
2492
2771
|
child.once("close", (code) => {
|
|
2493
2772
|
if (code !== 0) reject(new Error(`zcode --version exited with code ${code}`));
|
|
2494
|
-
else
|
|
2773
|
+
else resolve2(out.trim().split("\n")[0]?.trim() || void 0);
|
|
2495
2774
|
});
|
|
2496
2775
|
});
|
|
2497
2776
|
} catch (err) {
|
|
2498
|
-
|
|
2777
|
+
logger4.debug(
|
|
2499
2778
|
`[zcode-engine] probe version check failed (best-effort continue): ${toErrorMessage(err)}`
|
|
2500
2779
|
);
|
|
2501
2780
|
return void 0;
|
|
@@ -2643,18 +2922,17 @@ var EngineProtocolServer = class {
|
|
|
2643
2922
|
} } : void 0;
|
|
2644
2923
|
const runCtx = {
|
|
2645
2924
|
taskId: runId,
|
|
2646
|
-
poolKey: ctx.poolKey,
|
|
2647
2925
|
signal: controller.signal,
|
|
2648
2926
|
onEvent: (event) => this.emitEvent(runId, event),
|
|
2649
2927
|
...ctxModel !== void 0 ? { ctxModel } : {},
|
|
2650
2928
|
...stream !== void 0 ? { stream } : {},
|
|
2651
2929
|
...ctx.schemaEnv !== void 0 ? { schemaEnv: ctx.schemaEnv } : {},
|
|
2652
2930
|
...ctx.engineFallback !== void 0 ? { engineFallback: ctx.engineFallback } : {},
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
},
|
|
2931
|
+
// [U6 / §3.2.6 要点 3] resume 锚点透传(宿主 → 引擎的续聊通道:zcode 锚
|
|
2932
|
+
// sessionRef {sessionId, dbPath},引擎侧 resume 读 + 新 session 注入消费)。
|
|
2933
|
+
...params.resume !== void 0 ? { resume: params.resume } : {},
|
|
2656
2934
|
onHandleReady: (partial) => {
|
|
2657
|
-
void this.reverseRequestInternal("host/handleReady", { runId, sessionRef: partial.sessionRef
|
|
2935
|
+
void this.reverseRequestInternal("host/handleReady", { runId, sessionRef: partial.sessionRef });
|
|
2658
2936
|
}
|
|
2659
2937
|
};
|
|
2660
2938
|
try {
|
|
@@ -2700,14 +2978,14 @@ var EngineProtocolServer = class {
|
|
|
2700
2978
|
}
|
|
2701
2979
|
reverseRequestInternal(method, params) {
|
|
2702
2980
|
const id = `rev-${++this.revSeq}`;
|
|
2703
|
-
return new Promise((
|
|
2981
|
+
return new Promise((resolve2, reject) => {
|
|
2704
2982
|
const timer = setTimeout(() => {
|
|
2705
2983
|
this.reversePending.delete(id);
|
|
2706
2984
|
this.reverseClock?.settled(id);
|
|
2707
2985
|
reject(new Error(`reverse request ${method} (${id}) timed out after ${this.reverseTimeoutMs}ms`));
|
|
2708
2986
|
}, this.reverseTimeoutMs);
|
|
2709
2987
|
if (typeof timer.unref === "function") timer.unref();
|
|
2710
|
-
this.reversePending.set(id, { resolve, reject, timer });
|
|
2988
|
+
this.reversePending.set(id, { resolve: resolve2, reject, timer });
|
|
2711
2989
|
this.reverseClock?.started(id);
|
|
2712
2990
|
this.write({ id, method, params });
|
|
2713
2991
|
});
|