@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
package/dist/index.cjs
CHANGED
|
@@ -46,7 +46,7 @@ __export(src_exports, {
|
|
|
46
46
|
module.exports = __toCommonJS(src_exports);
|
|
47
47
|
|
|
48
48
|
// src/server.ts
|
|
49
|
-
var
|
|
49
|
+
var import_subagent_engine_sdk13 = require("@zhushanwen/subagent-engine-sdk");
|
|
50
50
|
|
|
51
51
|
// src/constants.ts
|
|
52
52
|
var ZCODE_ENGINE_ID = "zcode";
|
|
@@ -72,7 +72,6 @@ function parseZcodeTurnTimeoutEnv(raw) {
|
|
|
72
72
|
if (!Number.isFinite(parsed)) return { state: "invalid" };
|
|
73
73
|
return { state: "valid", ms: parsed };
|
|
74
74
|
}
|
|
75
|
-
var ZCODE_SHARED_POOL_KEY = "shared";
|
|
76
75
|
var ZCODE_APPSERVER_STOP_TIMEOUT_MS = 3e3;
|
|
77
76
|
var ZCODE_APPSERVER_ABORT_GRACE_MS = 3e3;
|
|
78
77
|
var ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING = -32603;
|
|
@@ -81,15 +80,18 @@ var ZCODE_APPSERVER_HARVEST_GRACE_MS = 1e3;
|
|
|
81
80
|
function isFailedTerminalStatus(status) {
|
|
82
81
|
return status === "failed" || status === "error";
|
|
83
82
|
}
|
|
83
|
+
var ZCODE_RESUME_HISTORY_TOKEN_BUDGET = 24e3;
|
|
84
|
+
var ZCODE_RESUME_CHARS_PER_TOKEN = 4;
|
|
85
|
+
var ZCODE_SESSION_SWEEP_DEFER_MS = 50;
|
|
84
86
|
|
|
85
87
|
// src/registration.ts
|
|
86
|
-
var
|
|
88
|
+
var import_subagent_engine_sdk11 = require("@zhushanwen/subagent-engine-sdk");
|
|
87
89
|
|
|
88
90
|
// src/zcode-engine.ts
|
|
89
|
-
var
|
|
91
|
+
var fs6 = __toESM(require("fs"), 1);
|
|
90
92
|
var path4 = __toESM(require("path"), 1);
|
|
91
|
-
var import_subagent_engine_sdk8 = require("@zhushanwen/subagent-engine-sdk");
|
|
92
93
|
var import_subagent_engine_sdk9 = require("@zhushanwen/subagent-engine-sdk");
|
|
94
|
+
var import_subagent_engine_sdk10 = require("@zhushanwen/subagent-engine-sdk");
|
|
93
95
|
|
|
94
96
|
// src/journal-io.ts
|
|
95
97
|
var import_subagent_engine_sdk = require("@zhushanwen/subagent-engine-sdk");
|
|
@@ -801,7 +803,7 @@ var AppServerConnection = class {
|
|
|
801
803
|
}
|
|
802
804
|
const timeoutMs = opts?.timeoutMs ?? this.requestTimeoutMs;
|
|
803
805
|
const id = ++this.reqSeq;
|
|
804
|
-
return new Promise((
|
|
806
|
+
return new Promise((resolve2, reject) => {
|
|
805
807
|
const timer = setTimeout(() => {
|
|
806
808
|
this.pending.delete(id);
|
|
807
809
|
reject(new Error(`\u8BF7\u6C42 ${method} \u8D85\u65F6\uFF08${timeoutMs}ms\uFF09`));
|
|
@@ -809,7 +811,7 @@ var AppServerConnection = class {
|
|
|
809
811
|
this.pending.set(id, {
|
|
810
812
|
// 泛型 T 是调用方对线上 unknown 结果的视图声明,连接层不做运行时校验
|
|
811
813
|
// (协议层保持透明,结构校验归会话层/消费方)。
|
|
812
|
-
resolve,
|
|
814
|
+
resolve: resolve2,
|
|
813
815
|
reject,
|
|
814
816
|
timer,
|
|
815
817
|
method
|
|
@@ -1160,6 +1162,46 @@ function extractReadUsage(readResult) {
|
|
|
1160
1162
|
}
|
|
1161
1163
|
return isRecord3(readResult.usage) ? readResult.usage : void 0;
|
|
1162
1164
|
}
|
|
1165
|
+
function extractResumeHistory(resumeResult) {
|
|
1166
|
+
if (!isRecord3(resumeResult) || !Array.isArray(resumeResult.messages)) return [];
|
|
1167
|
+
const turns = [];
|
|
1168
|
+
for (const m of resumeResult.messages) {
|
|
1169
|
+
if (!isRecord3(m)) continue;
|
|
1170
|
+
const role = messageRole(m);
|
|
1171
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
1172
|
+
const text = partsToText(m.parts) ?? contentToText(m.content);
|
|
1173
|
+
if (text === void 0 || text === "") continue;
|
|
1174
|
+
turns.push({ role, text });
|
|
1175
|
+
}
|
|
1176
|
+
return turns;
|
|
1177
|
+
}
|
|
1178
|
+
function accumulateTokenFields(source, keys, acc) {
|
|
1179
|
+
for (const key of keys) {
|
|
1180
|
+
const raw = source[key];
|
|
1181
|
+
const v = typeof raw === "number" ? raw : Number(raw);
|
|
1182
|
+
if (Number.isFinite(v) && v > 0) {
|
|
1183
|
+
acc.total += v;
|
|
1184
|
+
acc.has = true;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
function accumulateStepFinishTokens(parts, acc) {
|
|
1189
|
+
for (const p of parts) {
|
|
1190
|
+
if (!isRecord3(p) || p.type !== "step-finish" || !isRecord3(p.tokens)) continue;
|
|
1191
|
+
const t = p.tokens;
|
|
1192
|
+
accumulateTokenFields(t, ["input", "output"], acc);
|
|
1193
|
+
if (isRecord3(t.cache)) accumulateTokenFields(t.cache, ["read", "write"], acc);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
function extractResumeTotalTokens(resumeResult) {
|
|
1197
|
+
if (!isRecord3(resumeResult) || !Array.isArray(resumeResult.messages)) return void 0;
|
|
1198
|
+
const acc = { total: 0, has: false };
|
|
1199
|
+
for (const m of resumeResult.messages) {
|
|
1200
|
+
if (!isRecord3(m) || messageRole(m) !== "assistant" || !Array.isArray(m.parts)) continue;
|
|
1201
|
+
accumulateStepFinishTokens(m.parts, acc);
|
|
1202
|
+
}
|
|
1203
|
+
return acc.has ? acc.total : void 0;
|
|
1204
|
+
}
|
|
1163
1205
|
var TurnTimeoutError = class extends Error {
|
|
1164
1206
|
kind;
|
|
1165
1207
|
elapsed;
|
|
@@ -1320,6 +1362,17 @@ var SessionChannel = class {
|
|
|
1320
1362
|
);
|
|
1321
1363
|
}
|
|
1322
1364
|
}
|
|
1365
|
+
/**
|
|
1366
|
+
* session/resume 读通道([U6 / §3.2.6 要点 3,P-1 探针实证]):应答自带完整
|
|
1367
|
+
* 双向 messages 历史(user/assistant 全量、tokens、parts)——zcode 续聊选型的
|
|
1368
|
+
* 历史来源(读通道成立;原地 resume 续写被 -32031 卡死,写通道不可用)。错误
|
|
1369
|
+
* 原样上抛(调用方决定降级形态——引擎侧 warn 后无历史前缀开新会话)。
|
|
1370
|
+
* 经 conn.request 惰性启动连接(resume 是 run 的**首个**请求——调用时点连接
|
|
1371
|
+
* 可能尚未启动,与 readBestEffort 的「已死跳过」判据不同场景,不做 alive 守卫)。
|
|
1372
|
+
*/
|
|
1373
|
+
async resumeSession(sessionId) {
|
|
1374
|
+
return this.conn.request("session/resume", { sessionId }, { timeoutMs: ZCODE_APPSERVER_TURN_READ_TIMEOUT_MS });
|
|
1375
|
+
}
|
|
1323
1376
|
/**
|
|
1324
1377
|
* session/close(A.2 ⑦,D4 用后即毁——回收引擎驻留内存,SQLite 持久化保留)。
|
|
1325
1378
|
* best-effort:失败只 warn 不抛(会话已终态,close 失败只泄漏一个驻留会话,
|
|
@@ -1666,8 +1719,166 @@ var SessionChannel = class {
|
|
|
1666
1719
|
}
|
|
1667
1720
|
};
|
|
1668
1721
|
|
|
1669
|
-
// src/
|
|
1722
|
+
// src/session-db-maintenance.ts
|
|
1723
|
+
var fs5 = __toESM(require("fs"), 1);
|
|
1724
|
+
var import_subagent_engine_sdk8 = require("@zhushanwen/subagent-engine-sdk");
|
|
1670
1725
|
var logger3 = (0, import_subagent_engine_sdk8.getLogger)("subagents");
|
|
1726
|
+
var ZCODE_SESSION_TTL_MS = 2592e6;
|
|
1727
|
+
var ZCODE_SESSION_SWEEP_INTERVAL_MS = 864e5;
|
|
1728
|
+
var SWEEP_BUSY_TIMEOUT_MS = 5e3;
|
|
1729
|
+
var SESSION_CHILD_TABLES = [
|
|
1730
|
+
"message",
|
|
1731
|
+
"todo",
|
|
1732
|
+
"session_entry",
|
|
1733
|
+
"session_input",
|
|
1734
|
+
"session_target",
|
|
1735
|
+
"model_usage",
|
|
1736
|
+
"turn_usage",
|
|
1737
|
+
"tool_usage"
|
|
1738
|
+
];
|
|
1739
|
+
function loadDatabaseSyncCtor() {
|
|
1740
|
+
const getBuiltin = process.getBuiltinModule;
|
|
1741
|
+
if (typeof getBuiltin !== "function") return void 0;
|
|
1742
|
+
const mod = getBuiltin("node:sqlite");
|
|
1743
|
+
return typeof mod?.DatabaseSync === "function" ? mod.DatabaseSync : void 0;
|
|
1744
|
+
}
|
|
1745
|
+
function sessionLastActivity(row) {
|
|
1746
|
+
for (const v of [row.time_updated, row.time_created]) {
|
|
1747
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1748
|
+
}
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
function collectExpiredSessions(db, cutoff, keep) {
|
|
1752
|
+
const rows = db.prepare("SELECT id, time_updated, time_created FROM session").all();
|
|
1753
|
+
const expired = [];
|
|
1754
|
+
let keptActive = 0;
|
|
1755
|
+
for (const row of rows) {
|
|
1756
|
+
if (typeof row.id !== "string" || row.id === "") continue;
|
|
1757
|
+
const last = sessionLastActivity(row);
|
|
1758
|
+
if (last === null || last >= cutoff) continue;
|
|
1759
|
+
if (keep.has(row.id)) {
|
|
1760
|
+
keptActive++;
|
|
1761
|
+
continue;
|
|
1762
|
+
}
|
|
1763
|
+
expired.push(row.id);
|
|
1764
|
+
}
|
|
1765
|
+
return { expired, keptActive };
|
|
1766
|
+
}
|
|
1767
|
+
function ensureForeignKeysOn(db) {
|
|
1768
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
1769
|
+
const fkOn = db.prepare("PRAGMA foreign_keys").get();
|
|
1770
|
+
if (fkOn?.foreign_keys !== 1) {
|
|
1771
|
+
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");
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
function listPresentTables(db) {
|
|
1775
|
+
return new Set(
|
|
1776
|
+
db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((r) => typeof r.name === "string" ? r.name : "")
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
var DELETE_BATCH_SIZE = 500;
|
|
1780
|
+
function chunkIds(ids, size) {
|
|
1781
|
+
const batches = [];
|
|
1782
|
+
for (let i = 0; i < ids.length; i += size) batches.push(ids.slice(i, i + size));
|
|
1783
|
+
return batches;
|
|
1784
|
+
}
|
|
1785
|
+
function deleteExpiredSessionsInTx(db, expired, present) {
|
|
1786
|
+
db.exec("BEGIN");
|
|
1787
|
+
try {
|
|
1788
|
+
let swept = 0;
|
|
1789
|
+
for (const batch of chunkIds(expired, DELETE_BATCH_SIZE)) {
|
|
1790
|
+
const ph = batch.map(() => "?").join(", ");
|
|
1791
|
+
for (const table of SESSION_CHILD_TABLES) {
|
|
1792
|
+
if (present.has(table)) db.prepare(`DELETE FROM ${table} WHERE session_id IN (${ph})`).run(...batch);
|
|
1793
|
+
}
|
|
1794
|
+
if (present.has("session_task_link")) {
|
|
1795
|
+
db.prepare(`DELETE FROM session_task_link WHERE child_session_id IN (${ph})`).run(...batch);
|
|
1796
|
+
db.prepare(`UPDATE session_task_link SET parent_session_id = NULL WHERE parent_session_id IN (${ph})`).run(...batch);
|
|
1797
|
+
}
|
|
1798
|
+
if (present.has("workflow_run")) {
|
|
1799
|
+
db.prepare(`UPDATE workflow_run SET parent_session_id = NULL WHERE parent_session_id IN (${ph})`).run(...batch);
|
|
1800
|
+
}
|
|
1801
|
+
if (present.has("workflow_activity")) {
|
|
1802
|
+
db.prepare(`UPDATE workflow_activity SET child_session_id = NULL WHERE child_session_id IN (${ph})`).run(...batch);
|
|
1803
|
+
}
|
|
1804
|
+
if (present.has("input_history")) {
|
|
1805
|
+
db.prepare(`DELETE FROM input_history WHERE session_id IN (${ph})`).run(...batch);
|
|
1806
|
+
}
|
|
1807
|
+
const res = db.prepare(`DELETE FROM session WHERE id IN (${ph})`).run(...batch);
|
|
1808
|
+
swept += res.changes ?? batch.length;
|
|
1809
|
+
}
|
|
1810
|
+
db.exec("COMMIT");
|
|
1811
|
+
return swept;
|
|
1812
|
+
} catch (err) {
|
|
1813
|
+
try {
|
|
1814
|
+
db.exec("ROLLBACK");
|
|
1815
|
+
} catch (rollbackErr) {
|
|
1816
|
+
void rollbackErr;
|
|
1817
|
+
}
|
|
1818
|
+
throw err;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
function sweepExpiredZcodeSessions(dbPath, opts = {}) {
|
|
1822
|
+
const noOp = { swept: 0, keptActive: 0 };
|
|
1823
|
+
if (!fs5.existsSync(dbPath)) return noOp;
|
|
1824
|
+
const Ctor = loadDatabaseSyncCtor();
|
|
1825
|
+
if (Ctor === void 0) {
|
|
1826
|
+
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");
|
|
1827
|
+
return noOp;
|
|
1828
|
+
}
|
|
1829
|
+
const now = opts.nowMs ?? Date.now();
|
|
1830
|
+
const ttl = opts.ttlMs ?? ZCODE_SESSION_TTL_MS;
|
|
1831
|
+
const cutoff = now - ttl;
|
|
1832
|
+
const keep = opts.keepSessionIds ?? /* @__PURE__ */ new Set();
|
|
1833
|
+
let db;
|
|
1834
|
+
try {
|
|
1835
|
+
db = new Ctor(dbPath);
|
|
1836
|
+
db.exec(`PRAGMA busy_timeout = ${SWEEP_BUSY_TIMEOUT_MS}`);
|
|
1837
|
+
const { expired, keptActive } = collectExpiredSessions(db, cutoff, keep);
|
|
1838
|
+
if (expired.length === 0) {
|
|
1839
|
+
db.close();
|
|
1840
|
+
return { swept: 0, keptActive };
|
|
1841
|
+
}
|
|
1842
|
+
ensureForeignKeysOn(db);
|
|
1843
|
+
const present = listPresentTables(db);
|
|
1844
|
+
const swept = deleteExpiredSessionsInTx(db, expired, present);
|
|
1845
|
+
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 });
|
|
1846
|
+
try {
|
|
1847
|
+
db.prepare("PRAGMA wal_checkpoint(PASSIVE)").get();
|
|
1848
|
+
} catch (err) {
|
|
1849
|
+
void err;
|
|
1850
|
+
}
|
|
1851
|
+
return { swept, keptActive };
|
|
1852
|
+
} catch (err) {
|
|
1853
|
+
logger3.warn(
|
|
1854
|
+
`[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)}`,
|
|
1855
|
+
{ dbPath }
|
|
1856
|
+
);
|
|
1857
|
+
return noOp;
|
|
1858
|
+
} finally {
|
|
1859
|
+
try {
|
|
1860
|
+
db?.close();
|
|
1861
|
+
} catch (err) {
|
|
1862
|
+
void err;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
var sweepThrottle = /* @__PURE__ */ new Map();
|
|
1867
|
+
function maybeSweepExpiredZcodeSessions(dbPath, opts = {}) {
|
|
1868
|
+
const now = Date.now();
|
|
1869
|
+
const last = sweepThrottle.get(dbPath);
|
|
1870
|
+
if (!opts.force && last !== void 0 && now - last < ZCODE_SESSION_SWEEP_INTERVAL_MS) {
|
|
1871
|
+
return { swept: 0, keptActive: 0 };
|
|
1872
|
+
}
|
|
1873
|
+
const result = sweepExpiredZcodeSessions(dbPath, {
|
|
1874
|
+
...opts.keepSessionIds !== void 0 ? { keepSessionIds: opts.keepSessionIds } : {}
|
|
1875
|
+
});
|
|
1876
|
+
sweepThrottle.set(dbPath, now);
|
|
1877
|
+
return result;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// src/zcode-engine.ts
|
|
1881
|
+
var logger4 = (0, import_subagent_engine_sdk9.getLogger)("subagents");
|
|
1671
1882
|
var PROBE_VERSION_TIMEOUT_MS = 15e3;
|
|
1672
1883
|
var COMMON_THOUGHT_LEVELS = ["low", "high", "max"];
|
|
1673
1884
|
var ZcodeEngine = class {
|
|
@@ -1694,8 +1905,11 @@ var ZcodeEngine = class {
|
|
|
1694
1905
|
schemaEnforcement: "emulated",
|
|
1695
1906
|
// send-while-running 恒 -32010 硬错误(旧实测)——app-server 常驻化不改变此判据
|
|
1696
1907
|
steer: "unsupported",
|
|
1697
|
-
//
|
|
1698
|
-
|
|
1908
|
+
// [U6 / §3.2.6 要点 4] "cold" = 冷恢复会话:session/resume 读通道取结构化
|
|
1909
|
+
// 历史 + session/create 新会话注入首轮(无热 steering——interrupt 维持
|
|
1910
|
+
// kill-only 如实声明)。P-1 探针:原地 resume 续写被 -32031 卡死,热会话
|
|
1911
|
+
// 通道不可用,声明不越级。
|
|
1912
|
+
conversation: "cold",
|
|
1699
1913
|
// 无 --append-system-prompt flag(实测拒收)——persona 只能拼进 prompt
|
|
1700
1914
|
personaInjection: "prompt",
|
|
1701
1915
|
// app-server 推送流实时流出(session/event payload.delta → text_delta)
|
|
@@ -1739,7 +1953,7 @@ var ZcodeEngine = class {
|
|
|
1739
1953
|
}
|
|
1740
1954
|
/** check 1:二进制存在性(isFile 才算——同名目录不是可执行入口)。 */
|
|
1741
1955
|
probeBinaryCheck(cliPath) {
|
|
1742
|
-
const binaryOk =
|
|
1956
|
+
const binaryOk = fs6.existsSync(cliPath) && fs6.statSync(cliPath).isFile();
|
|
1743
1957
|
return {
|
|
1744
1958
|
name: "binary",
|
|
1745
1959
|
ok: binaryOk,
|
|
@@ -1777,8 +1991,8 @@ var ZcodeEngine = class {
|
|
|
1777
1991
|
/**
|
|
1778
1992
|
* 常驻路径主编排:模型解析(v2 单源校验)→ 惰性连接 + runTurn(事件时序前移:
|
|
1779
1993
|
* text_delta 流式、终态后 message_end/turn_end)→ schema 仿真重试 → outcome/handle。
|
|
1780
|
-
*
|
|
1781
|
-
*
|
|
1994
|
+
* onHandleReady 在 create 应答后(§3.4 不变量 3;原 onPoolResolved prepare 期
|
|
1995
|
+
* 声明已随 [池抽象降级 2026-09-13] poolKey 协议面退役删除)。
|
|
1782
1996
|
*/
|
|
1783
1997
|
async runViaAppServer(task, ctx) {
|
|
1784
1998
|
const startedAt = Date.now();
|
|
@@ -1788,10 +2002,10 @@ var ZcodeEngine = class {
|
|
|
1788
2002
|
const modelRef = resolveZcodeModelRef(task.model, this.deps.sources);
|
|
1789
2003
|
this.warnIgnoredCtxModel(task, ctx, modelRef);
|
|
1790
2004
|
this.warnThoughtLevelUncommon(task, ctx);
|
|
1791
|
-
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
1792
2005
|
const cwd = task.cwd ?? process.cwd();
|
|
1793
2006
|
const schema = isPlainObject(task.schema) ? task.schema : void 0;
|
|
1794
|
-
const
|
|
2007
|
+
const resumePrefix = await this.buildResumeHistoryPrefix(ctx);
|
|
2008
|
+
const basePrompt = resumePrefix === void 0 ? this.buildPrompt(task, schema) : resumePrefix + this.buildPrompt(task, schema);
|
|
1795
2009
|
const usageAcc = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, has: false };
|
|
1796
2010
|
const final = await this.runAppServerAttemptsWithRetry(task, ctx, modelRef, cwd, basePrompt, schema, usageAcc);
|
|
1797
2011
|
const outcome = this.finalizeOutcome(task, ctx, final, usageAcc, startedAt);
|
|
@@ -1799,7 +2013,6 @@ var ZcodeEngine = class {
|
|
|
1799
2013
|
}
|
|
1800
2014
|
/** pre-aborted 短路收口:合成中止 outcome + 'shared' 锚定 handle。 */
|
|
1801
2015
|
abortedAppServerRun(task, ctx, startedAt) {
|
|
1802
|
-
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
1803
2016
|
const outcome = this.finalizeOutcome(
|
|
1804
2017
|
task,
|
|
1805
2018
|
ctx,
|
|
@@ -1833,11 +2046,11 @@ var ZcodeEngine = class {
|
|
|
1833
2046
|
if (final.kind === "run-failed" && final.transient !== void 0 && ctx.signal?.aborted !== true && !this.disposed) {
|
|
1834
2047
|
const budget = resolveTransientRetryBudget(explicitTurnBudgetMs(), Date.now() - attemptStartedAt);
|
|
1835
2048
|
if (budget.state === "depleted") {
|
|
1836
|
-
|
|
2049
|
+
logger4.warn(
|
|
1837
2050
|
`[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`
|
|
1838
2051
|
);
|
|
1839
2052
|
} else {
|
|
1840
|
-
|
|
2053
|
+
logger4.warn(
|
|
1841
2054
|
`[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")
|
|
1842
2055
|
);
|
|
1843
2056
|
const retry = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt, {
|
|
@@ -1873,7 +2086,6 @@ var ZcodeEngine = class {
|
|
|
1873
2086
|
dbPath: zcodeSessionDbPath(this.deps.engineDataDir()),
|
|
1874
2087
|
...outcome.sessionId !== void 0 ? { sessionId: outcome.sessionId } : {}
|
|
1875
2088
|
},
|
|
1876
|
-
poolKey: ZCODE_SHARED_POOL_KEY,
|
|
1877
2089
|
...this.probeCache?.engineVersion !== void 0 && this.probeCache.engineVersion !== "" ? { engineVersion: this.probeCache.engineVersion } : {},
|
|
1878
2090
|
adapterVersion: ZCODE_ADAPTER_VERSION
|
|
1879
2091
|
}
|
|
@@ -1893,8 +2105,8 @@ var ZcodeEngine = class {
|
|
|
1893
2105
|
const createParams = buildAppServerCreateParams(task, providerId, modelId, cwd);
|
|
1894
2106
|
let currentSessionId;
|
|
1895
2107
|
let signalSessionCreated;
|
|
1896
|
-
const sessionCreated = new Promise((
|
|
1897
|
-
signalSessionCreated =
|
|
2108
|
+
const sessionCreated = new Promise((resolve2) => {
|
|
2109
|
+
signalSessionCreated = resolve2;
|
|
1898
2110
|
});
|
|
1899
2111
|
const turn = rt.channel.runTurn(createParams, prompt, {
|
|
1900
2112
|
// 事件时序前移:payload.delta → text_delta 实时流出(stream 粒度,D5);
|
|
@@ -1904,16 +2116,16 @@ var ZcodeEngine = class {
|
|
|
1904
2116
|
onThinkingDelta: (delta) => ctx.onEvent?.({ type: "thinking_delta", delta }),
|
|
1905
2117
|
// [PR3] 工具执行期活性:非终态非增量的 session/event 帧(tool.updated progress
|
|
1906
2118
|
// 等,真机探针实证每 ~1s 一帧)→ activity 事件(零载荷纯活性信号——宿主无进展
|
|
1907
|
-
// 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode
|
|
1908
|
-
// conversation:"
|
|
2119
|
+
// 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode 引擎声明
|
|
2120
|
+
// conversation:"cold"(续聊 = session-store 冷恢复重建 + 新 run + resume 锚点,
|
|
2121
|
+
// 无热 steering/chat 域接线),run 域是唯一发射面。
|
|
1909
2122
|
onActivity: () => ctx.onEvent?.({ type: "activity" }),
|
|
1910
2123
|
onSessionCreated: (sessionId) => {
|
|
1911
2124
|
currentSessionId = sessionId;
|
|
1912
2125
|
rt.activeSessions.add(sessionId);
|
|
1913
2126
|
signalSessionCreated?.();
|
|
1914
2127
|
ctx.onHandleReady?.({
|
|
1915
|
-
sessionRef: { dbPath: zcodeSessionDbPath(this.deps.engineDataDir()), sessionId }
|
|
1916
|
-
poolKey: ZCODE_SHARED_POOL_KEY
|
|
2128
|
+
sessionRef: { dbPath: zcodeSessionDbPath(this.deps.engineDataDir()), sessionId }
|
|
1917
2129
|
});
|
|
1918
2130
|
},
|
|
1919
2131
|
// 显式总上界传参(D2/U4:缺省缺席——channel 侧 resolveTurnTimerMs 走 env→默认)
|
|
@@ -1961,14 +2173,14 @@ var ZcodeEngine = class {
|
|
|
1961
2173
|
*/
|
|
1962
2174
|
async awaitConnFinalized(rt) {
|
|
1963
2175
|
if (!rt.conn.alive) return;
|
|
1964
|
-
await new Promise((
|
|
2176
|
+
await new Promise((resolve2) => {
|
|
1965
2177
|
const timer = setTimeout(() => finish(), ZCODE_APPSERVER_HARVEST_GRACE_MS);
|
|
1966
2178
|
if (typeof timer.unref === "function") timer.unref();
|
|
1967
2179
|
const off = rt.conn.onClose(() => finish());
|
|
1968
2180
|
function finish() {
|
|
1969
2181
|
clearTimeout(timer);
|
|
1970
2182
|
off();
|
|
1971
|
-
|
|
2183
|
+
resolve2();
|
|
1972
2184
|
}
|
|
1973
2185
|
});
|
|
1974
2186
|
}
|
|
@@ -2002,7 +2214,7 @@ var ZcodeEngine = class {
|
|
|
2002
2214
|
delayResolved(ZCODE_APPSERVER_ABORT_GRACE_MS, false)
|
|
2003
2215
|
]);
|
|
2004
2216
|
if (settled) return "settled-in-grace";
|
|
2005
|
-
|
|
2217
|
+
logger4.warn(
|
|
2006
2218
|
`[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`
|
|
2007
2219
|
);
|
|
2008
2220
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
@@ -2027,19 +2239,19 @@ var ZcodeEngine = class {
|
|
|
2027
2239
|
} catch (err) {
|
|
2028
2240
|
if (entry.escalateOn === "stop-outcome") {
|
|
2029
2241
|
if (isAppServerRpcError(err)) {
|
|
2030
|
-
|
|
2242
|
+
logger4.debug(
|
|
2031
2243
|
`[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`
|
|
2032
2244
|
);
|
|
2033
2245
|
return "stop-rejected";
|
|
2034
2246
|
}
|
|
2035
|
-
|
|
2247
|
+
logger4.warn(
|
|
2036
2248
|
`[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`
|
|
2037
2249
|
);
|
|
2038
2250
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
2039
2251
|
await this.awaitConnFinalized(rt);
|
|
2040
2252
|
return "stop-unreachable-killed";
|
|
2041
2253
|
}
|
|
2042
|
-
|
|
2254
|
+
logger4.debug(
|
|
2043
2255
|
`[zcode-engine] session/stop \u5931\u8D25\uFF08${errMessage3(err)}\uFF09\u2014\u2014grace \u540E\u8D70 killChain \u515C\u5E95`
|
|
2044
2256
|
);
|
|
2045
2257
|
}
|
|
@@ -2068,7 +2280,7 @@ var ZcodeEngine = class {
|
|
|
2068
2280
|
const sessionDbPath = zcodeSessionDbPath(engineDataDir);
|
|
2069
2281
|
env.ZCODE_SESSION_DB_PATH = sessionDbPath;
|
|
2070
2282
|
delete env.ZCODE_SESSION_DB;
|
|
2071
|
-
|
|
2283
|
+
fs6.mkdirSync(path4.dirname(sessionDbPath), { recursive: true });
|
|
2072
2284
|
const conn = new AppServerConnection({
|
|
2073
2285
|
cliPath,
|
|
2074
2286
|
dataDir: engineDataDir,
|
|
@@ -2086,6 +2298,11 @@ var ZcodeEngine = class {
|
|
|
2086
2298
|
activeSessions: /* @__PURE__ */ new Set()
|
|
2087
2299
|
};
|
|
2088
2300
|
this.appserverRuntime = rt;
|
|
2301
|
+
const sweepDbPath = sessionDbPath;
|
|
2302
|
+
const sweepTimer = setTimeout(() => {
|
|
2303
|
+
maybeSweepExpiredZcodeSessions(sweepDbPath, { keepSessionIds: this.appserverRuntime?.activeSessions });
|
|
2304
|
+
}, ZCODE_SESSION_SWEEP_DEFER_MS);
|
|
2305
|
+
if (typeof sweepTimer.unref === "function") sweepTimer.unref();
|
|
2089
2306
|
return rt;
|
|
2090
2307
|
}
|
|
2091
2308
|
/**
|
|
@@ -2103,10 +2320,10 @@ var ZcodeEngine = class {
|
|
|
2103
2320
|
* 先到时 onClose 收割先行,dispose 收割幂等 no-op(零回归)。
|
|
2104
2321
|
*/
|
|
2105
2322
|
async shutdownRuntimeAndDisposeChannel(rt) {
|
|
2106
|
-
const harvested = new Promise((
|
|
2323
|
+
const harvested = new Promise((resolve2) => {
|
|
2107
2324
|
const off = rt.conn.onClose(() => {
|
|
2108
2325
|
off();
|
|
2109
|
-
|
|
2326
|
+
resolve2();
|
|
2110
2327
|
});
|
|
2111
2328
|
});
|
|
2112
2329
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
@@ -2133,17 +2350,6 @@ var ZcodeEngine = class {
|
|
|
2133
2350
|
}
|
|
2134
2351
|
await this.shutdownRuntimeAndDisposeChannel(rt);
|
|
2135
2352
|
}
|
|
2136
|
-
/**
|
|
2137
|
-
* [u7a D5] 引擎在途只读快照:activeSessions 非空才算在途——poolKey 'shared' 的
|
|
2138
|
-
* app-server **空闲常驻进程恒活,禁止按进程存在判定**(进程在 ≠ 在途,否则推迟
|
|
2139
|
-
* 判定恒真、30min 上限从兜底变常态路径,设计 §3.3 D5 显式排除)。计数权威 =
|
|
2140
|
-
* activeSessions(attempt 入 create 应答后 add / finally settle 后 delete,与
|
|
2141
|
-
* dispose 的 close 帧目标集同一状态源,无双记账)。运行时未初始化(从未 run /
|
|
2142
|
-
* 已 dispose)→ 恒 0。
|
|
2143
|
-
*/
|
|
2144
|
-
inFlightSnapshot() {
|
|
2145
|
-
return { inFlight: this.appserverRuntime?.activeSessions.size ?? 0 };
|
|
2146
|
-
}
|
|
2147
2353
|
/** 终态合成(extension-conventions 函数 80 行上限,从 run 提取):aborted / run-failed / parsed 三分支。 */
|
|
2148
2354
|
finalizeOutcome(task, ctx, final, usageAcc, startedAt) {
|
|
2149
2355
|
const outcome = {
|
|
@@ -2170,7 +2376,7 @@ var ZcodeEngine = class {
|
|
|
2170
2376
|
/** abort 合成终态:exitCode=null(record 正常收尾,不留僵尸)。 */
|
|
2171
2377
|
applyAbortedOutcome(outcome, task, ctx, final, emit) {
|
|
2172
2378
|
outcome.exitCode = null;
|
|
2173
|
-
outcome.error = isHostTimeoutAbort(ctx) ? (0,
|
|
2379
|
+
outcome.error = isHostTimeoutAbort(ctx) ? (0, import_subagent_engine_sdk10.synthesizeTimeoutOutcome)(task, final.output.stdoutText, ZCODE_ENGINE_ID).error ?? (0, import_subagent_engine_sdk10.engineTimeoutDetail)(final.output.stdoutText) : final.abortMessage ?? `engine_run_failed: zcode \u4EFB\u52A1\u88AB\u4E2D\u6B62\uFF08app-server abort \u94FE\u6536\u53E3\uFF0C\u5BBF\u4E3B\u5408\u6210\u7EC8\u6001\uFF09\u3002\u8F93\u51FA\u5C3E\u90E8: ${final.output.stdoutText.slice(-ZCODE_ERROR_TAIL_CHARS)}`;
|
|
2174
2380
|
emit({ type: "error", message: outcome.error });
|
|
2175
2381
|
}
|
|
2176
2382
|
/**
|
|
@@ -2232,8 +2438,12 @@ var ZcodeEngine = class {
|
|
|
2232
2438
|
* sessionId 缺失(解析失败的 run 无法定位 session)跳过①级;②级依赖
|
|
2233
2439
|
* handle.journalPath(宿主 run 后回填)。dbPath:新 handle 恒为隔离库绝对路径
|
|
2234
2440
|
* (zcodeSessionDbPath(engineDataDir),tier1 白名单集合见方法体——宿主路径仅
|
|
2235
|
-
* 「共享 HOME 时代」存量兼容);旧 records
|
|
2236
|
-
*
|
|
2441
|
+
* 「共享 HOME 时代」存量兼容);旧 records(池时代)的相对路径仍按池目录锚定
|
|
2442
|
+
* 解析(read 兼容旧数据,池目录不存在时自然落②级 journal 降级)。[池抽象降级
|
|
2443
|
+
* 2026-09-13] 锚定 key 用常量 SHARED_POOL_KEY 承载——协议面 poolKey 已删,池
|
|
2444
|
+
* 时代真实池 key 不再随 handle 传输;若旧 record 的池目录恰为 shared 布局则①级
|
|
2445
|
+
* 仍可达,其余池 key 的旧 record 直接走②级 journal 降级(30 天 TTL 同尺度衰减,
|
|
2446
|
+
* 行为安全)。
|
|
2237
2447
|
*/
|
|
2238
2448
|
async read(handle) {
|
|
2239
2449
|
if (handle.data.engineId !== ZCODE_ENGINE_ID) {
|
|
@@ -2247,21 +2457,28 @@ var ZcodeEngine = class {
|
|
|
2247
2457
|
if (zcodeDbPathAllowlist(this.deps.engineDataDir()).includes(dbPathRaw)) {
|
|
2248
2458
|
dbPath = dbPathRaw;
|
|
2249
2459
|
} else {
|
|
2250
|
-
|
|
2460
|
+
logger4.warn("[zcode-engine] record dbPath \u975E\u767D\u540D\u5355 db \u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u62D2\u7EDD \u2460\u7EA7\u8BFB\u53D6\u964D journal", {
|
|
2251
2461
|
dbPath: dbPathRaw
|
|
2252
2462
|
});
|
|
2253
2463
|
}
|
|
2254
2464
|
} else {
|
|
2255
|
-
|
|
2256
|
-
(0,
|
|
2257
|
-
dbPathRaw
|
|
2465
|
+
const poolDir = path4.resolve(
|
|
2466
|
+
(0, import_subagent_engine_sdk10.resolvePoolDir)(this.deps.engineDataDir(), ZCODE_ENGINE_ID, import_subagent_engine_sdk10.SHARED_POOL_KEY)
|
|
2258
2467
|
);
|
|
2468
|
+
const resolved = path4.resolve(poolDir, dbPathRaw);
|
|
2469
|
+
if (resolved.startsWith(poolDir + path4.sep)) {
|
|
2470
|
+
dbPath = resolved;
|
|
2471
|
+
} else {
|
|
2472
|
+
logger4.warn("[zcode-engine] record dbPath \u76F8\u5BF9\u8DEF\u5F84\u9003\u9038\u6C60\u76EE\u5F55\uFF0C\u62D2\u7EDD \u2460\u7EA7\u8BFB\u53D6\u964D journal", {
|
|
2473
|
+
dbPath: dbPathRaw
|
|
2474
|
+
});
|
|
2475
|
+
}
|
|
2259
2476
|
}
|
|
2260
2477
|
if (dbPath !== void 0) {
|
|
2261
2478
|
try {
|
|
2262
2479
|
return await readZcodeSessionView(dbPath, sessionId);
|
|
2263
2480
|
} catch (err) {
|
|
2264
|
-
|
|
2481
|
+
logger4.warn("[zcode-engine] native session read failed, degrade to journal replay", {
|
|
2265
2482
|
dbPath,
|
|
2266
2483
|
sessionId,
|
|
2267
2484
|
reason: (0, import_subagent_engine_sdk3.toErrorMessage)(err)
|
|
@@ -2287,7 +2504,7 @@ var ZcodeEngine = class {
|
|
|
2287
2504
|
const thoughtLevel = task.thinkingLevel?.trim();
|
|
2288
2505
|
if (thoughtLevel === void 0 || thoughtLevel === "") return;
|
|
2289
2506
|
if (COMMON_THOUGHT_LEVELS.includes(thoughtLevel)) return;
|
|
2290
|
-
|
|
2507
|
+
logger4.warn(
|
|
2291
2508
|
`[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`,
|
|
2292
2509
|
{ taskId: ctx.taskId }
|
|
2293
2510
|
);
|
|
@@ -2305,11 +2522,40 @@ var ZcodeEngine = class {
|
|
|
2305
2522
|
if (ctx.ctxModel === void 0) return;
|
|
2306
2523
|
const requested = task.model?.trim();
|
|
2307
2524
|
if (requested !== void 0 && requested !== "") return;
|
|
2308
|
-
|
|
2525
|
+
logger4.warn(
|
|
2309
2526
|
`[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}`,
|
|
2310
2527
|
{ taskId: ctx.taskId }
|
|
2311
2528
|
);
|
|
2312
2529
|
}
|
|
2530
|
+
/**
|
|
2531
|
+
* [U6 / §3.2.6 要点 3] resume 锚 → 历史前缀(interact-resume 的读半段)。
|
|
2532
|
+
*
|
|
2533
|
+
* 无锚 / 非 zcode 锚形态 → undefined(行为不变)。读通道失败(条目被 TTL 清 /
|
|
2534
|
+
* 会话失效 / 控制面错误)= 宿主侧锚判据通过后、引擎派发前的窄竞态窗——降级为
|
|
2535
|
+
* **无历史前缀的新会话**(warn 留痕;宿主 reopen 摘要承担主降级面),不炸轮:
|
|
2536
|
+
* 续聊消息本身仍可送达(模型缺历史上下文,比整轮失败可用性高,与 reopen 降级
|
|
2537
|
+
* 的「带摘要重开」语义同族)。
|
|
2538
|
+
*/
|
|
2539
|
+
async buildResumeHistoryPrefix(ctx) {
|
|
2540
|
+
const anchor = zcodeResumeAnchorOf(ctx, this.deps.engineDataDir());
|
|
2541
|
+
if (anchor === void 0) return void 0;
|
|
2542
|
+
const rt = this.ensureAppServerRuntime();
|
|
2543
|
+
let history;
|
|
2544
|
+
let tokens;
|
|
2545
|
+
try {
|
|
2546
|
+
const result = await rt.channel.resumeSession(anchor.sessionId);
|
|
2547
|
+
history = extractResumeHistory(result);
|
|
2548
|
+
tokens = extractResumeTotalTokens(result);
|
|
2549
|
+
} catch (err) {
|
|
2550
|
+
logger4.warn(
|
|
2551
|
+
`[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)}`,
|
|
2552
|
+
{ taskId: ctx.taskId }
|
|
2553
|
+
);
|
|
2554
|
+
return void 0;
|
|
2555
|
+
}
|
|
2556
|
+
if (history.length === 0) return void 0;
|
|
2557
|
+
return buildResumeInjectionSegment(history, anchor.sessionId, tokens);
|
|
2558
|
+
}
|
|
2313
2559
|
/**
|
|
2314
2560
|
* persona 拼接后的完整 prompt(personaInjection: 'prompt'——zcode 无 flag 通道):
|
|
2315
2561
|
* appendSystemPrompt 段在前(人设/约束语境——D6 合流后 persona≡skillPath+
|
|
@@ -2319,10 +2565,42 @@ var ZcodeEngine = class {
|
|
|
2319
2565
|
buildPrompt(task, schema) {
|
|
2320
2566
|
const segments = [...task.appendSystemPrompt ?? []];
|
|
2321
2567
|
segments.push(task.prompt);
|
|
2322
|
-
if (schema !== void 0) segments.push((0,
|
|
2568
|
+
if (schema !== void 0) segments.push((0, import_subagent_engine_sdk10.buildSchemaEmulationSegment)(schema));
|
|
2323
2569
|
return segments.join("\n\n");
|
|
2324
2570
|
}
|
|
2325
2571
|
};
|
|
2572
|
+
function zcodeResumeAnchorOf(ctx, engineDataDir) {
|
|
2573
|
+
const ref = ctx.resume?.resume?.sessionRef;
|
|
2574
|
+
if (ref === void 0) return void 0;
|
|
2575
|
+
const sessionId = ref["sessionId"];
|
|
2576
|
+
const dbPath = ref["dbPath"];
|
|
2577
|
+
if (typeof sessionId !== "string" || sessionId === "") return void 0;
|
|
2578
|
+
if (typeof dbPath !== "string" || dbPath === "") return void 0;
|
|
2579
|
+
if (!zcodeDbPathAllowlist(engineDataDir).includes(dbPath)) return void 0;
|
|
2580
|
+
return { sessionId, dbPath };
|
|
2581
|
+
}
|
|
2582
|
+
function buildResumeInjectionSegment(history, priorSessionId, totalTokens, tokenBudget = ZCODE_RESUME_HISTORY_TOKEN_BUDGET) {
|
|
2583
|
+
const budgetTokens = tokenBudget;
|
|
2584
|
+
const charBudget = budgetTokens * ZCODE_RESUME_CHARS_PER_TOKEN;
|
|
2585
|
+
const lines = history.map((t) => `${t.role}: ${t.text}`);
|
|
2586
|
+
let kept = lines;
|
|
2587
|
+
let omitted = 0;
|
|
2588
|
+
while (kept.length > 1 && kept.reduce((n, l) => n + l.length + 1, 0) > charBudget) {
|
|
2589
|
+
kept = kept.slice(1);
|
|
2590
|
+
omitted++;
|
|
2591
|
+
}
|
|
2592
|
+
const omissionNote = omitted > 0 ? `[... ${omitted} older turn(s) omitted to fit the ${tokenBudget}-token history budget ...]
|
|
2593
|
+
` : "";
|
|
2594
|
+
const usageNote = totalTokens !== void 0 ? `
|
|
2595
|
+
(prior session retained ~${totalTokens} tokens of history)` : "";
|
|
2596
|
+
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.
|
|
2597
|
+
|
|
2598
|
+
<conversation_history>
|
|
2599
|
+
${omissionNote}${kept.join("\n")}
|
|
2600
|
+
</conversation_history>${usageNote}
|
|
2601
|
+
|
|
2602
|
+
`;
|
|
2603
|
+
}
|
|
2326
2604
|
var ZCODE_TURN_RETRY_MIN_BUDGET_MS = 3e5;
|
|
2327
2605
|
function resolveTransientRetryBudget(totalBudgetMs, consumedMs) {
|
|
2328
2606
|
if (totalBudgetMs === void 0) return { state: "unbounded" };
|
|
@@ -2340,8 +2618,8 @@ function errMessage3(err) {
|
|
|
2340
2618
|
return (0, import_subagent_engine_sdk3.toErrorMessage)(err);
|
|
2341
2619
|
}
|
|
2342
2620
|
function delayResolved(ms, value) {
|
|
2343
|
-
return new Promise((
|
|
2344
|
-
const t = setTimeout(() =>
|
|
2621
|
+
return new Promise((resolve2) => {
|
|
2622
|
+
const t = setTimeout(() => resolve2(value), ms);
|
|
2345
2623
|
if (typeof t.unref === "function") t.unref();
|
|
2346
2624
|
});
|
|
2347
2625
|
}
|
|
@@ -2420,7 +2698,7 @@ function parsedAppServerAttempt(task, r) {
|
|
|
2420
2698
|
kind: "parsed",
|
|
2421
2699
|
output: syntheticAppServerOutput(0),
|
|
2422
2700
|
payload: turnResultToPayload(r),
|
|
2423
|
-
...schema !== void 0 ? { schemaResult: (0,
|
|
2701
|
+
...schema !== void 0 ? { schemaResult: (0, import_subagent_engine_sdk10.extractAndValidateStructuredOutput)(r.response, schema) } : {}
|
|
2424
2702
|
};
|
|
2425
2703
|
}
|
|
2426
2704
|
function failedTerminalAppServerAttempt(r) {
|
|
@@ -2491,16 +2769,16 @@ function buildAppServerTimeoutMessage(err, sessionId, stopPath, retried = false)
|
|
|
2491
2769
|
${rerunGuide}${selfHelp}\u82E5\u6301\u7EED\u51FA\u73B0\uFF0C\u68C0\u67E5 ZCode \u684C\u9762\u7AEF\u6A21\u578B\u8FDE\u901A\u6027\u6216\u6539\u7528 engine: pi\u3002`;
|
|
2492
2770
|
}
|
|
2493
2771
|
function isHostTimeoutAbort(ctx) {
|
|
2494
|
-
return ctx.signal?.aborted === true && ctx.signal.reason ===
|
|
2772
|
+
return ctx.signal?.aborted === true && ctx.signal.reason === import_subagent_engine_sdk10.HOST_TIMEOUT_ABORT_REASON;
|
|
2495
2773
|
}
|
|
2496
2774
|
var PROBE_STDOUT_CAP_CHARS = 4096;
|
|
2497
2775
|
async function defaultProbeVersion(cliPath, dataDir) {
|
|
2498
2776
|
try {
|
|
2499
|
-
return await new Promise((
|
|
2500
|
-
const child = (0,
|
|
2777
|
+
return await new Promise((resolve2, reject) => {
|
|
2778
|
+
const child = (0, import_subagent_engine_sdk10.spawnEngineChild)({
|
|
2501
2779
|
command: "node",
|
|
2502
2780
|
args: [cliPath, "--version"],
|
|
2503
|
-
env: (0,
|
|
2781
|
+
env: (0, import_subagent_engine_sdk10.buildEngineChildEnv)(process.env, { dataDir }),
|
|
2504
2782
|
stdout: "pipe",
|
|
2505
2783
|
stderr: "ignore",
|
|
2506
2784
|
signal: AbortSignal.timeout(PROBE_VERSION_TIMEOUT_MS)
|
|
@@ -2513,11 +2791,11 @@ async function defaultProbeVersion(cliPath, dataDir) {
|
|
|
2513
2791
|
child.once("error", reject);
|
|
2514
2792
|
child.once("close", (code) => {
|
|
2515
2793
|
if (code !== 0) reject(new Error(`zcode --version exited with code ${code}`));
|
|
2516
|
-
else
|
|
2794
|
+
else resolve2(out.trim().split("\n")[0]?.trim() || void 0);
|
|
2517
2795
|
});
|
|
2518
2796
|
});
|
|
2519
2797
|
} catch (err) {
|
|
2520
|
-
|
|
2798
|
+
logger4.debug(
|
|
2521
2799
|
`[zcode-engine] probe version check failed (best-effort continue): ${(0, import_subagent_engine_sdk3.toErrorMessage)(err)}`
|
|
2522
2800
|
);
|
|
2523
2801
|
return void 0;
|
|
@@ -2526,7 +2804,7 @@ async function defaultProbeVersion(cliPath, dataDir) {
|
|
|
2526
2804
|
|
|
2527
2805
|
// src/registration.ts
|
|
2528
2806
|
function defaultEngineDataDir() {
|
|
2529
|
-
return (0,
|
|
2807
|
+
return (0, import_subagent_engine_sdk11.resolveEngineDataDir)(process.env);
|
|
2530
2808
|
}
|
|
2531
2809
|
function createZcodeEngine(deps) {
|
|
2532
2810
|
return new ZcodeEngine(deps);
|
|
@@ -2539,7 +2817,7 @@ function createDefaultZcodeEngine() {
|
|
|
2539
2817
|
}
|
|
2540
2818
|
|
|
2541
2819
|
// src/port-types.ts
|
|
2542
|
-
var
|
|
2820
|
+
var import_subagent_engine_sdk12 = require("@zhushanwen/subagent-engine-sdk");
|
|
2543
2821
|
|
|
2544
2822
|
// src/server.ts
|
|
2545
2823
|
var REVERSE_TIMEOUT_DEFAULT_MS = 6e4;
|
|
@@ -2560,11 +2838,11 @@ var EngineProtocolServer = class {
|
|
|
2560
2838
|
}
|
|
2561
2839
|
/** 入站帧消费(请求帧 + 反向请求应答帧;main.ts 的行解析器拆行后喂入)。 */
|
|
2562
2840
|
handleFrame(frame) {
|
|
2563
|
-
if ((0,
|
|
2841
|
+
if ((0, import_subagent_engine_sdk13.isResponseFrame)(frame)) {
|
|
2564
2842
|
this.settleReverse(frame.id, frame);
|
|
2565
2843
|
return;
|
|
2566
2844
|
}
|
|
2567
|
-
if ((0,
|
|
2845
|
+
if ((0, import_subagent_engine_sdk13.isReverseRequestFrame)(frame)) {
|
|
2568
2846
|
this.write({
|
|
2569
2847
|
id: 0,
|
|
2570
2848
|
error: {
|
|
@@ -2590,7 +2868,7 @@ var EngineProtocolServer = class {
|
|
|
2590
2868
|
async dispatch(id, method, params) {
|
|
2591
2869
|
const handler = this.methodHandlers[method];
|
|
2592
2870
|
if (handler === void 0) {
|
|
2593
|
-
throw new
|
|
2871
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2594
2872
|
"engine_protocol_unknown_method",
|
|
2595
2873
|
`unknown protocol method: ${method} (request id ${id})`,
|
|
2596
2874
|
"The engine speaks protocol v1; check the installed engine package version vs the host."
|
|
@@ -2618,17 +2896,17 @@ var EngineProtocolServer = class {
|
|
|
2618
2896
|
};
|
|
2619
2897
|
// ── initialize:版本协商(越界 → engine_protocol_mismatch)+ 能力应答 ──
|
|
2620
2898
|
initialize(params) {
|
|
2621
|
-
if (params?.protocolVersion !==
|
|
2622
|
-
throw new
|
|
2899
|
+
if (params?.protocolVersion !== import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION) {
|
|
2900
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2623
2901
|
"engine_protocol_mismatch",
|
|
2624
|
-
`host protocol version ${String(params?.protocolVersion)} is not compatible with engine protocol v${
|
|
2625
|
-
`Upgrade the engine package or the host so both speak protocol v${
|
|
2902
|
+
`host protocol version ${String(params?.protocolVersion)} is not compatible with engine protocol v${import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION}`,
|
|
2903
|
+
`Upgrade the engine package or the host so both speak protocol v${import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION}.`
|
|
2626
2904
|
);
|
|
2627
2905
|
}
|
|
2628
2906
|
this.initialized = true;
|
|
2629
2907
|
const models = this.engine.listModels?.() ?? null;
|
|
2630
2908
|
return {
|
|
2631
|
-
protocolVersion:
|
|
2909
|
+
protocolVersion: import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION,
|
|
2632
2910
|
engineId: this.engine.id,
|
|
2633
2911
|
engineVersion: ZCODE_ADAPTER_VERSION,
|
|
2634
2912
|
adapterVersion: ZCODE_ADAPTER_VERSION,
|
|
@@ -2639,7 +2917,7 @@ var EngineProtocolServer = class {
|
|
|
2639
2917
|
// ── run:协议载荷 → 本地 AgentCallOpts/RunContext;事件 → 通知/host 通道 ──
|
|
2640
2918
|
async run(params) {
|
|
2641
2919
|
if (!this.initialized) {
|
|
2642
|
-
throw new
|
|
2920
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2643
2921
|
"engine_protocol_not_initialized",
|
|
2644
2922
|
"run before initialize is a protocol violation",
|
|
2645
2923
|
"The host must complete the initialize handshake before dispatching runs."
|
|
@@ -2650,24 +2928,23 @@ var EngineProtocolServer = class {
|
|
|
2650
2928
|
const active = { controller, seq: 0 };
|
|
2651
2929
|
this.activeRuns.set(runId, active);
|
|
2652
2930
|
const fullTask = { ...task, ...ctx.model !== void 0 ? { model: ctx.model } : {} };
|
|
2653
|
-
const ctxModel = (0,
|
|
2931
|
+
const ctxModel = (0, import_subagent_engine_sdk12.parseCtxModel)(ctx.ctxModel);
|
|
2654
2932
|
const stream = ctx.streamMode === "stream" ? { onDelta: (delta) => {
|
|
2655
2933
|
void this.reverseRequestInternal("host/streamDelta", { runId, delta });
|
|
2656
2934
|
} } : void 0;
|
|
2657
2935
|
const runCtx = {
|
|
2658
2936
|
taskId: runId,
|
|
2659
|
-
poolKey: ctx.poolKey,
|
|
2660
2937
|
signal: controller.signal,
|
|
2661
2938
|
onEvent: (event) => this.emitEvent(runId, event),
|
|
2662
2939
|
...ctxModel !== void 0 ? { ctxModel } : {},
|
|
2663
2940
|
...stream !== void 0 ? { stream } : {},
|
|
2664
2941
|
...ctx.schemaEnv !== void 0 ? { schemaEnv: ctx.schemaEnv } : {},
|
|
2665
2942
|
...ctx.engineFallback !== void 0 ? { engineFallback: ctx.engineFallback } : {},
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
},
|
|
2943
|
+
// [U6 / §3.2.6 要点 3] resume 锚点透传(宿主 → 引擎的续聊通道:zcode 锚
|
|
2944
|
+
// sessionRef {sessionId, dbPath},引擎侧 resume 读 + 新 session 注入消费)。
|
|
2945
|
+
...params.resume !== void 0 ? { resume: params.resume } : {},
|
|
2669
2946
|
onHandleReady: (partial) => {
|
|
2670
|
-
void this.reverseRequestInternal("host/handleReady", { runId, sessionRef: partial.sessionRef
|
|
2947
|
+
void this.reverseRequestInternal("host/handleReady", { runId, sessionRef: partial.sessionRef });
|
|
2671
2948
|
}
|
|
2672
2949
|
};
|
|
2673
2950
|
try {
|
|
@@ -2687,7 +2964,7 @@ var EngineProtocolServer = class {
|
|
|
2687
2964
|
}
|
|
2688
2965
|
validateModel(params) {
|
|
2689
2966
|
if (this.engine.validateModel === void 0) {
|
|
2690
|
-
throw new
|
|
2967
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2691
2968
|
"engine_capability_unsupported",
|
|
2692
2969
|
"engine does not implement validateModel",
|
|
2693
2970
|
"Manifest omits modelCatalog; model validation falls back to run-time engine checks."
|
|
@@ -2713,14 +2990,14 @@ var EngineProtocolServer = class {
|
|
|
2713
2990
|
}
|
|
2714
2991
|
reverseRequestInternal(method, params) {
|
|
2715
2992
|
const id = `rev-${++this.revSeq}`;
|
|
2716
|
-
return new Promise((
|
|
2993
|
+
return new Promise((resolve2, reject) => {
|
|
2717
2994
|
const timer = setTimeout(() => {
|
|
2718
2995
|
this.reversePending.delete(id);
|
|
2719
2996
|
this.reverseClock?.settled(id);
|
|
2720
2997
|
reject(new Error(`reverse request ${method} (${id}) timed out after ${this.reverseTimeoutMs}ms`));
|
|
2721
2998
|
}, this.reverseTimeoutMs);
|
|
2722
2999
|
if (typeof timer.unref === "function") timer.unref();
|
|
2723
|
-
this.reversePending.set(id, { resolve, reject, timer });
|
|
3000
|
+
this.reversePending.set(id, { resolve: resolve2, reject, timer });
|
|
2724
3001
|
this.reverseClock?.started(id);
|
|
2725
3002
|
this.write({ id, method, params });
|
|
2726
3003
|
});
|
|
@@ -2741,7 +3018,7 @@ function probeParamsOf(params) {
|
|
|
2741
3018
|
return typeof params === "object" && params !== null ? params : void 0;
|
|
2742
3019
|
}
|
|
2743
3020
|
function toProtocolError(err) {
|
|
2744
|
-
if (err instanceof
|
|
3021
|
+
if (err instanceof import_subagent_engine_sdk13.EngineSdkError) return err.toStructured();
|
|
2745
3022
|
return {
|
|
2746
3023
|
code: "engine_run_failed",
|
|
2747
3024
|
message: (0, import_subagent_engine_sdk3.toErrorMessage)(err),
|