@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/main.cjs
CHANGED
|
@@ -23,10 +23,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/main.ts
|
|
26
|
-
var
|
|
26
|
+
var import_subagent_engine_sdk14 = require("@zhushanwen/subagent-engine-sdk");
|
|
27
27
|
|
|
28
28
|
// src/server.ts
|
|
29
|
-
var
|
|
29
|
+
var import_subagent_engine_sdk13 = require("@zhushanwen/subagent-engine-sdk");
|
|
30
30
|
|
|
31
31
|
// src/constants.ts
|
|
32
32
|
var ZCODE_ENGINE_ID = "zcode";
|
|
@@ -52,7 +52,6 @@ function parseZcodeTurnTimeoutEnv(raw) {
|
|
|
52
52
|
if (!Number.isFinite(parsed)) return { state: "invalid" };
|
|
53
53
|
return { state: "valid", ms: parsed };
|
|
54
54
|
}
|
|
55
|
-
var ZCODE_SHARED_POOL_KEY = "shared";
|
|
56
55
|
var ZCODE_APPSERVER_STOP_TIMEOUT_MS = 3e3;
|
|
57
56
|
var ZCODE_APPSERVER_ABORT_GRACE_MS = 3e3;
|
|
58
57
|
var ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING = -32603;
|
|
@@ -61,15 +60,18 @@ var ZCODE_APPSERVER_HARVEST_GRACE_MS = 1e3;
|
|
|
61
60
|
function isFailedTerminalStatus(status) {
|
|
62
61
|
return status === "failed" || status === "error";
|
|
63
62
|
}
|
|
63
|
+
var ZCODE_RESUME_HISTORY_TOKEN_BUDGET = 24e3;
|
|
64
|
+
var ZCODE_RESUME_CHARS_PER_TOKEN = 4;
|
|
65
|
+
var ZCODE_SESSION_SWEEP_DEFER_MS = 50;
|
|
64
66
|
|
|
65
67
|
// src/registration.ts
|
|
66
|
-
var
|
|
68
|
+
var import_subagent_engine_sdk11 = require("@zhushanwen/subagent-engine-sdk");
|
|
67
69
|
|
|
68
70
|
// src/zcode-engine.ts
|
|
69
|
-
var
|
|
71
|
+
var fs6 = __toESM(require("fs"), 1);
|
|
70
72
|
var path4 = __toESM(require("path"), 1);
|
|
71
|
-
var import_subagent_engine_sdk8 = require("@zhushanwen/subagent-engine-sdk");
|
|
72
73
|
var import_subagent_engine_sdk9 = require("@zhushanwen/subagent-engine-sdk");
|
|
74
|
+
var import_subagent_engine_sdk10 = require("@zhushanwen/subagent-engine-sdk");
|
|
73
75
|
|
|
74
76
|
// src/journal-io.ts
|
|
75
77
|
var import_subagent_engine_sdk = require("@zhushanwen/subagent-engine-sdk");
|
|
@@ -781,7 +783,7 @@ var AppServerConnection = class {
|
|
|
781
783
|
}
|
|
782
784
|
const timeoutMs = opts?.timeoutMs ?? this.requestTimeoutMs;
|
|
783
785
|
const id = ++this.reqSeq;
|
|
784
|
-
return new Promise((
|
|
786
|
+
return new Promise((resolve2, reject) => {
|
|
785
787
|
const timer = setTimeout(() => {
|
|
786
788
|
this.pending.delete(id);
|
|
787
789
|
reject(new Error(`\u8BF7\u6C42 ${method} \u8D85\u65F6\uFF08${timeoutMs}ms\uFF09`));
|
|
@@ -789,7 +791,7 @@ var AppServerConnection = class {
|
|
|
789
791
|
this.pending.set(id, {
|
|
790
792
|
// 泛型 T 是调用方对线上 unknown 结果的视图声明,连接层不做运行时校验
|
|
791
793
|
// (协议层保持透明,结构校验归会话层/消费方)。
|
|
792
|
-
resolve,
|
|
794
|
+
resolve: resolve2,
|
|
793
795
|
reject,
|
|
794
796
|
timer,
|
|
795
797
|
method
|
|
@@ -1140,6 +1142,46 @@ function extractReadUsage(readResult) {
|
|
|
1140
1142
|
}
|
|
1141
1143
|
return isRecord3(readResult.usage) ? readResult.usage : void 0;
|
|
1142
1144
|
}
|
|
1145
|
+
function extractResumeHistory(resumeResult) {
|
|
1146
|
+
if (!isRecord3(resumeResult) || !Array.isArray(resumeResult.messages)) return [];
|
|
1147
|
+
const turns = [];
|
|
1148
|
+
for (const m of resumeResult.messages) {
|
|
1149
|
+
if (!isRecord3(m)) continue;
|
|
1150
|
+
const role = messageRole(m);
|
|
1151
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
1152
|
+
const text = partsToText(m.parts) ?? contentToText(m.content);
|
|
1153
|
+
if (text === void 0 || text === "") continue;
|
|
1154
|
+
turns.push({ role, text });
|
|
1155
|
+
}
|
|
1156
|
+
return turns;
|
|
1157
|
+
}
|
|
1158
|
+
function accumulateTokenFields(source, keys, acc) {
|
|
1159
|
+
for (const key of keys) {
|
|
1160
|
+
const raw = source[key];
|
|
1161
|
+
const v = typeof raw === "number" ? raw : Number(raw);
|
|
1162
|
+
if (Number.isFinite(v) && v > 0) {
|
|
1163
|
+
acc.total += v;
|
|
1164
|
+
acc.has = true;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function accumulateStepFinishTokens(parts, acc) {
|
|
1169
|
+
for (const p of parts) {
|
|
1170
|
+
if (!isRecord3(p) || p.type !== "step-finish" || !isRecord3(p.tokens)) continue;
|
|
1171
|
+
const t = p.tokens;
|
|
1172
|
+
accumulateTokenFields(t, ["input", "output"], acc);
|
|
1173
|
+
if (isRecord3(t.cache)) accumulateTokenFields(t.cache, ["read", "write"], acc);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
function extractResumeTotalTokens(resumeResult) {
|
|
1177
|
+
if (!isRecord3(resumeResult) || !Array.isArray(resumeResult.messages)) return void 0;
|
|
1178
|
+
const acc = { total: 0, has: false };
|
|
1179
|
+
for (const m of resumeResult.messages) {
|
|
1180
|
+
if (!isRecord3(m) || messageRole(m) !== "assistant" || !Array.isArray(m.parts)) continue;
|
|
1181
|
+
accumulateStepFinishTokens(m.parts, acc);
|
|
1182
|
+
}
|
|
1183
|
+
return acc.has ? acc.total : void 0;
|
|
1184
|
+
}
|
|
1143
1185
|
var TurnTimeoutError = class extends Error {
|
|
1144
1186
|
kind;
|
|
1145
1187
|
elapsed;
|
|
@@ -1300,6 +1342,17 @@ var SessionChannel = class {
|
|
|
1300
1342
|
);
|
|
1301
1343
|
}
|
|
1302
1344
|
}
|
|
1345
|
+
/**
|
|
1346
|
+
* session/resume 读通道([U6 / §3.2.6 要点 3,P-1 探针实证]):应答自带完整
|
|
1347
|
+
* 双向 messages 历史(user/assistant 全量、tokens、parts)——zcode 续聊选型的
|
|
1348
|
+
* 历史来源(读通道成立;原地 resume 续写被 -32031 卡死,写通道不可用)。错误
|
|
1349
|
+
* 原样上抛(调用方决定降级形态——引擎侧 warn 后无历史前缀开新会话)。
|
|
1350
|
+
* 经 conn.request 惰性启动连接(resume 是 run 的**首个**请求——调用时点连接
|
|
1351
|
+
* 可能尚未启动,与 readBestEffort 的「已死跳过」判据不同场景,不做 alive 守卫)。
|
|
1352
|
+
*/
|
|
1353
|
+
async resumeSession(sessionId) {
|
|
1354
|
+
return this.conn.request("session/resume", { sessionId }, { timeoutMs: ZCODE_APPSERVER_TURN_READ_TIMEOUT_MS });
|
|
1355
|
+
}
|
|
1303
1356
|
/**
|
|
1304
1357
|
* session/close(A.2 ⑦,D4 用后即毁——回收引擎驻留内存,SQLite 持久化保留)。
|
|
1305
1358
|
* best-effort:失败只 warn 不抛(会话已终态,close 失败只泄漏一个驻留会话,
|
|
@@ -1646,8 +1699,166 @@ var SessionChannel = class {
|
|
|
1646
1699
|
}
|
|
1647
1700
|
};
|
|
1648
1701
|
|
|
1649
|
-
// src/
|
|
1702
|
+
// src/session-db-maintenance.ts
|
|
1703
|
+
var fs5 = __toESM(require("fs"), 1);
|
|
1704
|
+
var import_subagent_engine_sdk8 = require("@zhushanwen/subagent-engine-sdk");
|
|
1650
1705
|
var logger3 = (0, import_subagent_engine_sdk8.getLogger)("subagents");
|
|
1706
|
+
var ZCODE_SESSION_TTL_MS = 2592e6;
|
|
1707
|
+
var ZCODE_SESSION_SWEEP_INTERVAL_MS = 864e5;
|
|
1708
|
+
var SWEEP_BUSY_TIMEOUT_MS = 5e3;
|
|
1709
|
+
var SESSION_CHILD_TABLES = [
|
|
1710
|
+
"message",
|
|
1711
|
+
"todo",
|
|
1712
|
+
"session_entry",
|
|
1713
|
+
"session_input",
|
|
1714
|
+
"session_target",
|
|
1715
|
+
"model_usage",
|
|
1716
|
+
"turn_usage",
|
|
1717
|
+
"tool_usage"
|
|
1718
|
+
];
|
|
1719
|
+
function loadDatabaseSyncCtor() {
|
|
1720
|
+
const getBuiltin = process.getBuiltinModule;
|
|
1721
|
+
if (typeof getBuiltin !== "function") return void 0;
|
|
1722
|
+
const mod = getBuiltin("node:sqlite");
|
|
1723
|
+
return typeof mod?.DatabaseSync === "function" ? mod.DatabaseSync : void 0;
|
|
1724
|
+
}
|
|
1725
|
+
function sessionLastActivity(row) {
|
|
1726
|
+
for (const v of [row.time_updated, row.time_created]) {
|
|
1727
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1728
|
+
}
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
function collectExpiredSessions(db, cutoff, keep) {
|
|
1732
|
+
const rows = db.prepare("SELECT id, time_updated, time_created FROM session").all();
|
|
1733
|
+
const expired = [];
|
|
1734
|
+
let keptActive = 0;
|
|
1735
|
+
for (const row of rows) {
|
|
1736
|
+
if (typeof row.id !== "string" || row.id === "") continue;
|
|
1737
|
+
const last = sessionLastActivity(row);
|
|
1738
|
+
if (last === null || last >= cutoff) continue;
|
|
1739
|
+
if (keep.has(row.id)) {
|
|
1740
|
+
keptActive++;
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
expired.push(row.id);
|
|
1744
|
+
}
|
|
1745
|
+
return { expired, keptActive };
|
|
1746
|
+
}
|
|
1747
|
+
function ensureForeignKeysOn(db) {
|
|
1748
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
1749
|
+
const fkOn = db.prepare("PRAGMA foreign_keys").get();
|
|
1750
|
+
if (fkOn?.foreign_keys !== 1) {
|
|
1751
|
+
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");
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
function listPresentTables(db) {
|
|
1755
|
+
return new Set(
|
|
1756
|
+
db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((r) => typeof r.name === "string" ? r.name : "")
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
var DELETE_BATCH_SIZE = 500;
|
|
1760
|
+
function chunkIds(ids, size) {
|
|
1761
|
+
const batches = [];
|
|
1762
|
+
for (let i = 0; i < ids.length; i += size) batches.push(ids.slice(i, i + size));
|
|
1763
|
+
return batches;
|
|
1764
|
+
}
|
|
1765
|
+
function deleteExpiredSessionsInTx(db, expired, present) {
|
|
1766
|
+
db.exec("BEGIN");
|
|
1767
|
+
try {
|
|
1768
|
+
let swept = 0;
|
|
1769
|
+
for (const batch of chunkIds(expired, DELETE_BATCH_SIZE)) {
|
|
1770
|
+
const ph = batch.map(() => "?").join(", ");
|
|
1771
|
+
for (const table of SESSION_CHILD_TABLES) {
|
|
1772
|
+
if (present.has(table)) db.prepare(`DELETE FROM ${table} WHERE session_id IN (${ph})`).run(...batch);
|
|
1773
|
+
}
|
|
1774
|
+
if (present.has("session_task_link")) {
|
|
1775
|
+
db.prepare(`DELETE FROM session_task_link WHERE child_session_id IN (${ph})`).run(...batch);
|
|
1776
|
+
db.prepare(`UPDATE session_task_link SET parent_session_id = NULL WHERE parent_session_id IN (${ph})`).run(...batch);
|
|
1777
|
+
}
|
|
1778
|
+
if (present.has("workflow_run")) {
|
|
1779
|
+
db.prepare(`UPDATE workflow_run SET parent_session_id = NULL WHERE parent_session_id IN (${ph})`).run(...batch);
|
|
1780
|
+
}
|
|
1781
|
+
if (present.has("workflow_activity")) {
|
|
1782
|
+
db.prepare(`UPDATE workflow_activity SET child_session_id = NULL WHERE child_session_id IN (${ph})`).run(...batch);
|
|
1783
|
+
}
|
|
1784
|
+
if (present.has("input_history")) {
|
|
1785
|
+
db.prepare(`DELETE FROM input_history WHERE session_id IN (${ph})`).run(...batch);
|
|
1786
|
+
}
|
|
1787
|
+
const res = db.prepare(`DELETE FROM session WHERE id IN (${ph})`).run(...batch);
|
|
1788
|
+
swept += res.changes ?? batch.length;
|
|
1789
|
+
}
|
|
1790
|
+
db.exec("COMMIT");
|
|
1791
|
+
return swept;
|
|
1792
|
+
} catch (err) {
|
|
1793
|
+
try {
|
|
1794
|
+
db.exec("ROLLBACK");
|
|
1795
|
+
} catch (rollbackErr) {
|
|
1796
|
+
void rollbackErr;
|
|
1797
|
+
}
|
|
1798
|
+
throw err;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
function sweepExpiredZcodeSessions(dbPath, opts = {}) {
|
|
1802
|
+
const noOp = { swept: 0, keptActive: 0 };
|
|
1803
|
+
if (!fs5.existsSync(dbPath)) return noOp;
|
|
1804
|
+
const Ctor = loadDatabaseSyncCtor();
|
|
1805
|
+
if (Ctor === void 0) {
|
|
1806
|
+
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");
|
|
1807
|
+
return noOp;
|
|
1808
|
+
}
|
|
1809
|
+
const now = opts.nowMs ?? Date.now();
|
|
1810
|
+
const ttl = opts.ttlMs ?? ZCODE_SESSION_TTL_MS;
|
|
1811
|
+
const cutoff = now - ttl;
|
|
1812
|
+
const keep = opts.keepSessionIds ?? /* @__PURE__ */ new Set();
|
|
1813
|
+
let db;
|
|
1814
|
+
try {
|
|
1815
|
+
db = new Ctor(dbPath);
|
|
1816
|
+
db.exec(`PRAGMA busy_timeout = ${SWEEP_BUSY_TIMEOUT_MS}`);
|
|
1817
|
+
const { expired, keptActive } = collectExpiredSessions(db, cutoff, keep);
|
|
1818
|
+
if (expired.length === 0) {
|
|
1819
|
+
db.close();
|
|
1820
|
+
return { swept: 0, keptActive };
|
|
1821
|
+
}
|
|
1822
|
+
ensureForeignKeysOn(db);
|
|
1823
|
+
const present = listPresentTables(db);
|
|
1824
|
+
const swept = deleteExpiredSessionsInTx(db, expired, present);
|
|
1825
|
+
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 });
|
|
1826
|
+
try {
|
|
1827
|
+
db.prepare("PRAGMA wal_checkpoint(PASSIVE)").get();
|
|
1828
|
+
} catch (err) {
|
|
1829
|
+
void err;
|
|
1830
|
+
}
|
|
1831
|
+
return { swept, keptActive };
|
|
1832
|
+
} catch (err) {
|
|
1833
|
+
logger3.warn(
|
|
1834
|
+
`[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)}`,
|
|
1835
|
+
{ dbPath }
|
|
1836
|
+
);
|
|
1837
|
+
return noOp;
|
|
1838
|
+
} finally {
|
|
1839
|
+
try {
|
|
1840
|
+
db?.close();
|
|
1841
|
+
} catch (err) {
|
|
1842
|
+
void err;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
var sweepThrottle = /* @__PURE__ */ new Map();
|
|
1847
|
+
function maybeSweepExpiredZcodeSessions(dbPath, opts = {}) {
|
|
1848
|
+
const now = Date.now();
|
|
1849
|
+
const last = sweepThrottle.get(dbPath);
|
|
1850
|
+
if (!opts.force && last !== void 0 && now - last < ZCODE_SESSION_SWEEP_INTERVAL_MS) {
|
|
1851
|
+
return { swept: 0, keptActive: 0 };
|
|
1852
|
+
}
|
|
1853
|
+
const result = sweepExpiredZcodeSessions(dbPath, {
|
|
1854
|
+
...opts.keepSessionIds !== void 0 ? { keepSessionIds: opts.keepSessionIds } : {}
|
|
1855
|
+
});
|
|
1856
|
+
sweepThrottle.set(dbPath, now);
|
|
1857
|
+
return result;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// src/zcode-engine.ts
|
|
1861
|
+
var logger4 = (0, import_subagent_engine_sdk9.getLogger)("subagents");
|
|
1651
1862
|
var PROBE_VERSION_TIMEOUT_MS = 15e3;
|
|
1652
1863
|
var COMMON_THOUGHT_LEVELS = ["low", "high", "max"];
|
|
1653
1864
|
var ZcodeEngine = class {
|
|
@@ -1674,8 +1885,11 @@ var ZcodeEngine = class {
|
|
|
1674
1885
|
schemaEnforcement: "emulated",
|
|
1675
1886
|
// send-while-running 恒 -32010 硬错误(旧实测)——app-server 常驻化不改变此判据
|
|
1676
1887
|
steer: "unsupported",
|
|
1677
|
-
//
|
|
1678
|
-
|
|
1888
|
+
// [U6 / §3.2.6 要点 4] "cold" = 冷恢复会话:session/resume 读通道取结构化
|
|
1889
|
+
// 历史 + session/create 新会话注入首轮(无热 steering——interrupt 维持
|
|
1890
|
+
// kill-only 如实声明)。P-1 探针:原地 resume 续写被 -32031 卡死,热会话
|
|
1891
|
+
// 通道不可用,声明不越级。
|
|
1892
|
+
conversation: "cold",
|
|
1679
1893
|
// 无 --append-system-prompt flag(实测拒收)——persona 只能拼进 prompt
|
|
1680
1894
|
personaInjection: "prompt",
|
|
1681
1895
|
// app-server 推送流实时流出(session/event payload.delta → text_delta)
|
|
@@ -1719,7 +1933,7 @@ var ZcodeEngine = class {
|
|
|
1719
1933
|
}
|
|
1720
1934
|
/** check 1:二进制存在性(isFile 才算——同名目录不是可执行入口)。 */
|
|
1721
1935
|
probeBinaryCheck(cliPath) {
|
|
1722
|
-
const binaryOk =
|
|
1936
|
+
const binaryOk = fs6.existsSync(cliPath) && fs6.statSync(cliPath).isFile();
|
|
1723
1937
|
return {
|
|
1724
1938
|
name: "binary",
|
|
1725
1939
|
ok: binaryOk,
|
|
@@ -1757,8 +1971,8 @@ var ZcodeEngine = class {
|
|
|
1757
1971
|
/**
|
|
1758
1972
|
* 常驻路径主编排:模型解析(v2 单源校验)→ 惰性连接 + runTurn(事件时序前移:
|
|
1759
1973
|
* text_delta 流式、终态后 message_end/turn_end)→ schema 仿真重试 → outcome/handle。
|
|
1760
|
-
*
|
|
1761
|
-
*
|
|
1974
|
+
* onHandleReady 在 create 应答后(§3.4 不变量 3;原 onPoolResolved prepare 期
|
|
1975
|
+
* 声明已随 [池抽象降级 2026-09-13] poolKey 协议面退役删除)。
|
|
1762
1976
|
*/
|
|
1763
1977
|
async runViaAppServer(task, ctx) {
|
|
1764
1978
|
const startedAt = Date.now();
|
|
@@ -1768,10 +1982,10 @@ var ZcodeEngine = class {
|
|
|
1768
1982
|
const modelRef = resolveZcodeModelRef(task.model, this.deps.sources);
|
|
1769
1983
|
this.warnIgnoredCtxModel(task, ctx, modelRef);
|
|
1770
1984
|
this.warnThoughtLevelUncommon(task, ctx);
|
|
1771
|
-
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
1772
1985
|
const cwd = task.cwd ?? process.cwd();
|
|
1773
1986
|
const schema = isPlainObject(task.schema) ? task.schema : void 0;
|
|
1774
|
-
const
|
|
1987
|
+
const resumePrefix = await this.buildResumeHistoryPrefix(ctx);
|
|
1988
|
+
const basePrompt = resumePrefix === void 0 ? this.buildPrompt(task, schema) : resumePrefix + this.buildPrompt(task, schema);
|
|
1775
1989
|
const usageAcc = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, has: false };
|
|
1776
1990
|
const final = await this.runAppServerAttemptsWithRetry(task, ctx, modelRef, cwd, basePrompt, schema, usageAcc);
|
|
1777
1991
|
const outcome = this.finalizeOutcome(task, ctx, final, usageAcc, startedAt);
|
|
@@ -1779,7 +1993,6 @@ var ZcodeEngine = class {
|
|
|
1779
1993
|
}
|
|
1780
1994
|
/** pre-aborted 短路收口:合成中止 outcome + 'shared' 锚定 handle。 */
|
|
1781
1995
|
abortedAppServerRun(task, ctx, startedAt) {
|
|
1782
|
-
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
1783
1996
|
const outcome = this.finalizeOutcome(
|
|
1784
1997
|
task,
|
|
1785
1998
|
ctx,
|
|
@@ -1813,11 +2026,11 @@ var ZcodeEngine = class {
|
|
|
1813
2026
|
if (final.kind === "run-failed" && final.transient !== void 0 && ctx.signal?.aborted !== true && !this.disposed) {
|
|
1814
2027
|
const budget = resolveTransientRetryBudget(explicitTurnBudgetMs(), Date.now() - attemptStartedAt);
|
|
1815
2028
|
if (budget.state === "depleted") {
|
|
1816
|
-
|
|
2029
|
+
logger4.warn(
|
|
1817
2030
|
`[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`
|
|
1818
2031
|
);
|
|
1819
2032
|
} else {
|
|
1820
|
-
|
|
2033
|
+
logger4.warn(
|
|
1821
2034
|
`[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")
|
|
1822
2035
|
);
|
|
1823
2036
|
const retry = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt, {
|
|
@@ -1853,7 +2066,6 @@ var ZcodeEngine = class {
|
|
|
1853
2066
|
dbPath: zcodeSessionDbPath(this.deps.engineDataDir()),
|
|
1854
2067
|
...outcome.sessionId !== void 0 ? { sessionId: outcome.sessionId } : {}
|
|
1855
2068
|
},
|
|
1856
|
-
poolKey: ZCODE_SHARED_POOL_KEY,
|
|
1857
2069
|
...this.probeCache?.engineVersion !== void 0 && this.probeCache.engineVersion !== "" ? { engineVersion: this.probeCache.engineVersion } : {},
|
|
1858
2070
|
adapterVersion: ZCODE_ADAPTER_VERSION
|
|
1859
2071
|
}
|
|
@@ -1873,8 +2085,8 @@ var ZcodeEngine = class {
|
|
|
1873
2085
|
const createParams = buildAppServerCreateParams(task, providerId, modelId, cwd);
|
|
1874
2086
|
let currentSessionId;
|
|
1875
2087
|
let signalSessionCreated;
|
|
1876
|
-
const sessionCreated = new Promise((
|
|
1877
|
-
signalSessionCreated =
|
|
2088
|
+
const sessionCreated = new Promise((resolve2) => {
|
|
2089
|
+
signalSessionCreated = resolve2;
|
|
1878
2090
|
});
|
|
1879
2091
|
const turn = rt.channel.runTurn(createParams, prompt, {
|
|
1880
2092
|
// 事件时序前移:payload.delta → text_delta 实时流出(stream 粒度,D5);
|
|
@@ -1884,16 +2096,16 @@ var ZcodeEngine = class {
|
|
|
1884
2096
|
onThinkingDelta: (delta) => ctx.onEvent?.({ type: "thinking_delta", delta }),
|
|
1885
2097
|
// [PR3] 工具执行期活性:非终态非增量的 session/event 帧(tool.updated progress
|
|
1886
2098
|
// 等,真机探针实证每 ~1s 一帧)→ activity 事件(零载荷纯活性信号——宿主无进展
|
|
1887
|
-
// 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode
|
|
1888
|
-
// conversation:"
|
|
2099
|
+
// 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode 引擎声明
|
|
2100
|
+
// conversation:"cold"(续聊 = session-store 冷恢复重建 + 新 run + resume 锚点,
|
|
2101
|
+
// 无热 steering/chat 域接线),run 域是唯一发射面。
|
|
1889
2102
|
onActivity: () => ctx.onEvent?.({ type: "activity" }),
|
|
1890
2103
|
onSessionCreated: (sessionId) => {
|
|
1891
2104
|
currentSessionId = sessionId;
|
|
1892
2105
|
rt.activeSessions.add(sessionId);
|
|
1893
2106
|
signalSessionCreated?.();
|
|
1894
2107
|
ctx.onHandleReady?.({
|
|
1895
|
-
sessionRef: { dbPath: zcodeSessionDbPath(this.deps.engineDataDir()), sessionId }
|
|
1896
|
-
poolKey: ZCODE_SHARED_POOL_KEY
|
|
2108
|
+
sessionRef: { dbPath: zcodeSessionDbPath(this.deps.engineDataDir()), sessionId }
|
|
1897
2109
|
});
|
|
1898
2110
|
},
|
|
1899
2111
|
// 显式总上界传参(D2/U4:缺省缺席——channel 侧 resolveTurnTimerMs 走 env→默认)
|
|
@@ -1941,14 +2153,14 @@ var ZcodeEngine = class {
|
|
|
1941
2153
|
*/
|
|
1942
2154
|
async awaitConnFinalized(rt) {
|
|
1943
2155
|
if (!rt.conn.alive) return;
|
|
1944
|
-
await new Promise((
|
|
2156
|
+
await new Promise((resolve2) => {
|
|
1945
2157
|
const timer = setTimeout(() => finish(), ZCODE_APPSERVER_HARVEST_GRACE_MS);
|
|
1946
2158
|
if (typeof timer.unref === "function") timer.unref();
|
|
1947
2159
|
const off = rt.conn.onClose(() => finish());
|
|
1948
2160
|
function finish() {
|
|
1949
2161
|
clearTimeout(timer);
|
|
1950
2162
|
off();
|
|
1951
|
-
|
|
2163
|
+
resolve2();
|
|
1952
2164
|
}
|
|
1953
2165
|
});
|
|
1954
2166
|
}
|
|
@@ -1982,7 +2194,7 @@ var ZcodeEngine = class {
|
|
|
1982
2194
|
delayResolved(ZCODE_APPSERVER_ABORT_GRACE_MS, false)
|
|
1983
2195
|
]);
|
|
1984
2196
|
if (settled) return "settled-in-grace";
|
|
1985
|
-
|
|
2197
|
+
logger4.warn(
|
|
1986
2198
|
`[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`
|
|
1987
2199
|
);
|
|
1988
2200
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
@@ -2007,19 +2219,19 @@ var ZcodeEngine = class {
|
|
|
2007
2219
|
} catch (err) {
|
|
2008
2220
|
if (entry.escalateOn === "stop-outcome") {
|
|
2009
2221
|
if (isAppServerRpcError(err)) {
|
|
2010
|
-
|
|
2222
|
+
logger4.debug(
|
|
2011
2223
|
`[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`
|
|
2012
2224
|
);
|
|
2013
2225
|
return "stop-rejected";
|
|
2014
2226
|
}
|
|
2015
|
-
|
|
2227
|
+
logger4.warn(
|
|
2016
2228
|
`[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`
|
|
2017
2229
|
);
|
|
2018
2230
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
2019
2231
|
await this.awaitConnFinalized(rt);
|
|
2020
2232
|
return "stop-unreachable-killed";
|
|
2021
2233
|
}
|
|
2022
|
-
|
|
2234
|
+
logger4.debug(
|
|
2023
2235
|
`[zcode-engine] session/stop \u5931\u8D25\uFF08${errMessage3(err)}\uFF09\u2014\u2014grace \u540E\u8D70 killChain \u515C\u5E95`
|
|
2024
2236
|
);
|
|
2025
2237
|
}
|
|
@@ -2048,7 +2260,7 @@ var ZcodeEngine = class {
|
|
|
2048
2260
|
const sessionDbPath = zcodeSessionDbPath(engineDataDir);
|
|
2049
2261
|
env.ZCODE_SESSION_DB_PATH = sessionDbPath;
|
|
2050
2262
|
delete env.ZCODE_SESSION_DB;
|
|
2051
|
-
|
|
2263
|
+
fs6.mkdirSync(path4.dirname(sessionDbPath), { recursive: true });
|
|
2052
2264
|
const conn = new AppServerConnection({
|
|
2053
2265
|
cliPath,
|
|
2054
2266
|
dataDir: engineDataDir,
|
|
@@ -2066,6 +2278,11 @@ var ZcodeEngine = class {
|
|
|
2066
2278
|
activeSessions: /* @__PURE__ */ new Set()
|
|
2067
2279
|
};
|
|
2068
2280
|
this.appserverRuntime = rt;
|
|
2281
|
+
const sweepDbPath = sessionDbPath;
|
|
2282
|
+
const sweepTimer = setTimeout(() => {
|
|
2283
|
+
maybeSweepExpiredZcodeSessions(sweepDbPath, { keepSessionIds: this.appserverRuntime?.activeSessions });
|
|
2284
|
+
}, ZCODE_SESSION_SWEEP_DEFER_MS);
|
|
2285
|
+
if (typeof sweepTimer.unref === "function") sweepTimer.unref();
|
|
2069
2286
|
return rt;
|
|
2070
2287
|
}
|
|
2071
2288
|
/**
|
|
@@ -2083,10 +2300,10 @@ var ZcodeEngine = class {
|
|
|
2083
2300
|
* 先到时 onClose 收割先行,dispose 收割幂等 no-op(零回归)。
|
|
2084
2301
|
*/
|
|
2085
2302
|
async shutdownRuntimeAndDisposeChannel(rt) {
|
|
2086
|
-
const harvested = new Promise((
|
|
2303
|
+
const harvested = new Promise((resolve2) => {
|
|
2087
2304
|
const off = rt.conn.onClose(() => {
|
|
2088
2305
|
off();
|
|
2089
|
-
|
|
2306
|
+
resolve2();
|
|
2090
2307
|
});
|
|
2091
2308
|
});
|
|
2092
2309
|
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
@@ -2113,17 +2330,6 @@ var ZcodeEngine = class {
|
|
|
2113
2330
|
}
|
|
2114
2331
|
await this.shutdownRuntimeAndDisposeChannel(rt);
|
|
2115
2332
|
}
|
|
2116
|
-
/**
|
|
2117
|
-
* [u7a D5] 引擎在途只读快照:activeSessions 非空才算在途——poolKey 'shared' 的
|
|
2118
|
-
* app-server **空闲常驻进程恒活,禁止按进程存在判定**(进程在 ≠ 在途,否则推迟
|
|
2119
|
-
* 判定恒真、30min 上限从兜底变常态路径,设计 §3.3 D5 显式排除)。计数权威 =
|
|
2120
|
-
* activeSessions(attempt 入 create 应答后 add / finally settle 后 delete,与
|
|
2121
|
-
* dispose 的 close 帧目标集同一状态源,无双记账)。运行时未初始化(从未 run /
|
|
2122
|
-
* 已 dispose)→ 恒 0。
|
|
2123
|
-
*/
|
|
2124
|
-
inFlightSnapshot() {
|
|
2125
|
-
return { inFlight: this.appserverRuntime?.activeSessions.size ?? 0 };
|
|
2126
|
-
}
|
|
2127
2333
|
/** 终态合成(extension-conventions 函数 80 行上限,从 run 提取):aborted / run-failed / parsed 三分支。 */
|
|
2128
2334
|
finalizeOutcome(task, ctx, final, usageAcc, startedAt) {
|
|
2129
2335
|
const outcome = {
|
|
@@ -2150,7 +2356,7 @@ var ZcodeEngine = class {
|
|
|
2150
2356
|
/** abort 合成终态:exitCode=null(record 正常收尾,不留僵尸)。 */
|
|
2151
2357
|
applyAbortedOutcome(outcome, task, ctx, final, emit) {
|
|
2152
2358
|
outcome.exitCode = null;
|
|
2153
|
-
outcome.error = isHostTimeoutAbort(ctx) ? (0,
|
|
2359
|
+
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)}`;
|
|
2154
2360
|
emit({ type: "error", message: outcome.error });
|
|
2155
2361
|
}
|
|
2156
2362
|
/**
|
|
@@ -2212,8 +2418,12 @@ var ZcodeEngine = class {
|
|
|
2212
2418
|
* sessionId 缺失(解析失败的 run 无法定位 session)跳过①级;②级依赖
|
|
2213
2419
|
* handle.journalPath(宿主 run 后回填)。dbPath:新 handle 恒为隔离库绝对路径
|
|
2214
2420
|
* (zcodeSessionDbPath(engineDataDir),tier1 白名单集合见方法体——宿主路径仅
|
|
2215
|
-
* 「共享 HOME 时代」存量兼容);旧 records
|
|
2216
|
-
*
|
|
2421
|
+
* 「共享 HOME 时代」存量兼容);旧 records(池时代)的相对路径仍按池目录锚定
|
|
2422
|
+
* 解析(read 兼容旧数据,池目录不存在时自然落②级 journal 降级)。[池抽象降级
|
|
2423
|
+
* 2026-09-13] 锚定 key 用常量 SHARED_POOL_KEY 承载——协议面 poolKey 已删,池
|
|
2424
|
+
* 时代真实池 key 不再随 handle 传输;若旧 record 的池目录恰为 shared 布局则①级
|
|
2425
|
+
* 仍可达,其余池 key 的旧 record 直接走②级 journal 降级(30 天 TTL 同尺度衰减,
|
|
2426
|
+
* 行为安全)。
|
|
2217
2427
|
*/
|
|
2218
2428
|
async read(handle) {
|
|
2219
2429
|
if (handle.data.engineId !== ZCODE_ENGINE_ID) {
|
|
@@ -2227,21 +2437,28 @@ var ZcodeEngine = class {
|
|
|
2227
2437
|
if (zcodeDbPathAllowlist(this.deps.engineDataDir()).includes(dbPathRaw)) {
|
|
2228
2438
|
dbPath = dbPathRaw;
|
|
2229
2439
|
} else {
|
|
2230
|
-
|
|
2440
|
+
logger4.warn("[zcode-engine] record dbPath \u975E\u767D\u540D\u5355 db \u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u62D2\u7EDD \u2460\u7EA7\u8BFB\u53D6\u964D journal", {
|
|
2231
2441
|
dbPath: dbPathRaw
|
|
2232
2442
|
});
|
|
2233
2443
|
}
|
|
2234
2444
|
} else {
|
|
2235
|
-
|
|
2236
|
-
(0,
|
|
2237
|
-
dbPathRaw
|
|
2445
|
+
const poolDir = path4.resolve(
|
|
2446
|
+
(0, import_subagent_engine_sdk10.resolvePoolDir)(this.deps.engineDataDir(), ZCODE_ENGINE_ID, import_subagent_engine_sdk10.SHARED_POOL_KEY)
|
|
2238
2447
|
);
|
|
2448
|
+
const resolved = path4.resolve(poolDir, dbPathRaw);
|
|
2449
|
+
if (resolved.startsWith(poolDir + path4.sep)) {
|
|
2450
|
+
dbPath = resolved;
|
|
2451
|
+
} else {
|
|
2452
|
+
logger4.warn("[zcode-engine] record dbPath \u76F8\u5BF9\u8DEF\u5F84\u9003\u9038\u6C60\u76EE\u5F55\uFF0C\u62D2\u7EDD \u2460\u7EA7\u8BFB\u53D6\u964D journal", {
|
|
2453
|
+
dbPath: dbPathRaw
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2239
2456
|
}
|
|
2240
2457
|
if (dbPath !== void 0) {
|
|
2241
2458
|
try {
|
|
2242
2459
|
return await readZcodeSessionView(dbPath, sessionId);
|
|
2243
2460
|
} catch (err) {
|
|
2244
|
-
|
|
2461
|
+
logger4.warn("[zcode-engine] native session read failed, degrade to journal replay", {
|
|
2245
2462
|
dbPath,
|
|
2246
2463
|
sessionId,
|
|
2247
2464
|
reason: (0, import_subagent_engine_sdk3.toErrorMessage)(err)
|
|
@@ -2267,7 +2484,7 @@ var ZcodeEngine = class {
|
|
|
2267
2484
|
const thoughtLevel = task.thinkingLevel?.trim();
|
|
2268
2485
|
if (thoughtLevel === void 0 || thoughtLevel === "") return;
|
|
2269
2486
|
if (COMMON_THOUGHT_LEVELS.includes(thoughtLevel)) return;
|
|
2270
|
-
|
|
2487
|
+
logger4.warn(
|
|
2271
2488
|
`[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`,
|
|
2272
2489
|
{ taskId: ctx.taskId }
|
|
2273
2490
|
);
|
|
@@ -2285,11 +2502,40 @@ var ZcodeEngine = class {
|
|
|
2285
2502
|
if (ctx.ctxModel === void 0) return;
|
|
2286
2503
|
const requested = task.model?.trim();
|
|
2287
2504
|
if (requested !== void 0 && requested !== "") return;
|
|
2288
|
-
|
|
2505
|
+
logger4.warn(
|
|
2289
2506
|
`[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}`,
|
|
2290
2507
|
{ taskId: ctx.taskId }
|
|
2291
2508
|
);
|
|
2292
2509
|
}
|
|
2510
|
+
/**
|
|
2511
|
+
* [U6 / §3.2.6 要点 3] resume 锚 → 历史前缀(interact-resume 的读半段)。
|
|
2512
|
+
*
|
|
2513
|
+
* 无锚 / 非 zcode 锚形态 → undefined(行为不变)。读通道失败(条目被 TTL 清 /
|
|
2514
|
+
* 会话失效 / 控制面错误)= 宿主侧锚判据通过后、引擎派发前的窄竞态窗——降级为
|
|
2515
|
+
* **无历史前缀的新会话**(warn 留痕;宿主 reopen 摘要承担主降级面),不炸轮:
|
|
2516
|
+
* 续聊消息本身仍可送达(模型缺历史上下文,比整轮失败可用性高,与 reopen 降级
|
|
2517
|
+
* 的「带摘要重开」语义同族)。
|
|
2518
|
+
*/
|
|
2519
|
+
async buildResumeHistoryPrefix(ctx) {
|
|
2520
|
+
const anchor = zcodeResumeAnchorOf(ctx, this.deps.engineDataDir());
|
|
2521
|
+
if (anchor === void 0) return void 0;
|
|
2522
|
+
const rt = this.ensureAppServerRuntime();
|
|
2523
|
+
let history;
|
|
2524
|
+
let tokens;
|
|
2525
|
+
try {
|
|
2526
|
+
const result = await rt.channel.resumeSession(anchor.sessionId);
|
|
2527
|
+
history = extractResumeHistory(result);
|
|
2528
|
+
tokens = extractResumeTotalTokens(result);
|
|
2529
|
+
} catch (err) {
|
|
2530
|
+
logger4.warn(
|
|
2531
|
+
`[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)}`,
|
|
2532
|
+
{ taskId: ctx.taskId }
|
|
2533
|
+
);
|
|
2534
|
+
return void 0;
|
|
2535
|
+
}
|
|
2536
|
+
if (history.length === 0) return void 0;
|
|
2537
|
+
return buildResumeInjectionSegment(history, anchor.sessionId, tokens);
|
|
2538
|
+
}
|
|
2293
2539
|
/**
|
|
2294
2540
|
* persona 拼接后的完整 prompt(personaInjection: 'prompt'——zcode 无 flag 通道):
|
|
2295
2541
|
* appendSystemPrompt 段在前(人设/约束语境——D6 合流后 persona≡skillPath+
|
|
@@ -2299,10 +2545,42 @@ var ZcodeEngine = class {
|
|
|
2299
2545
|
buildPrompt(task, schema) {
|
|
2300
2546
|
const segments = [...task.appendSystemPrompt ?? []];
|
|
2301
2547
|
segments.push(task.prompt);
|
|
2302
|
-
if (schema !== void 0) segments.push((0,
|
|
2548
|
+
if (schema !== void 0) segments.push((0, import_subagent_engine_sdk10.buildSchemaEmulationSegment)(schema));
|
|
2303
2549
|
return segments.join("\n\n");
|
|
2304
2550
|
}
|
|
2305
2551
|
};
|
|
2552
|
+
function zcodeResumeAnchorOf(ctx, engineDataDir) {
|
|
2553
|
+
const ref = ctx.resume?.resume?.sessionRef;
|
|
2554
|
+
if (ref === void 0) return void 0;
|
|
2555
|
+
const sessionId = ref["sessionId"];
|
|
2556
|
+
const dbPath = ref["dbPath"];
|
|
2557
|
+
if (typeof sessionId !== "string" || sessionId === "") return void 0;
|
|
2558
|
+
if (typeof dbPath !== "string" || dbPath === "") return void 0;
|
|
2559
|
+
if (!zcodeDbPathAllowlist(engineDataDir).includes(dbPath)) return void 0;
|
|
2560
|
+
return { sessionId, dbPath };
|
|
2561
|
+
}
|
|
2562
|
+
function buildResumeInjectionSegment(history, priorSessionId, totalTokens, tokenBudget = ZCODE_RESUME_HISTORY_TOKEN_BUDGET) {
|
|
2563
|
+
const budgetTokens = tokenBudget;
|
|
2564
|
+
const charBudget = budgetTokens * ZCODE_RESUME_CHARS_PER_TOKEN;
|
|
2565
|
+
const lines = history.map((t) => `${t.role}: ${t.text}`);
|
|
2566
|
+
let kept = lines;
|
|
2567
|
+
let omitted = 0;
|
|
2568
|
+
while (kept.length > 1 && kept.reduce((n, l) => n + l.length + 1, 0) > charBudget) {
|
|
2569
|
+
kept = kept.slice(1);
|
|
2570
|
+
omitted++;
|
|
2571
|
+
}
|
|
2572
|
+
const omissionNote = omitted > 0 ? `[... ${omitted} older turn(s) omitted to fit the ${tokenBudget}-token history budget ...]
|
|
2573
|
+
` : "";
|
|
2574
|
+
const usageNote = totalTokens !== void 0 ? `
|
|
2575
|
+
(prior session retained ~${totalTokens} tokens of history)` : "";
|
|
2576
|
+
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.
|
|
2577
|
+
|
|
2578
|
+
<conversation_history>
|
|
2579
|
+
${omissionNote}${kept.join("\n")}
|
|
2580
|
+
</conversation_history>${usageNote}
|
|
2581
|
+
|
|
2582
|
+
`;
|
|
2583
|
+
}
|
|
2306
2584
|
var ZCODE_TURN_RETRY_MIN_BUDGET_MS = 3e5;
|
|
2307
2585
|
function resolveTransientRetryBudget(totalBudgetMs, consumedMs) {
|
|
2308
2586
|
if (totalBudgetMs === void 0) return { state: "unbounded" };
|
|
@@ -2320,8 +2598,8 @@ function errMessage3(err) {
|
|
|
2320
2598
|
return (0, import_subagent_engine_sdk3.toErrorMessage)(err);
|
|
2321
2599
|
}
|
|
2322
2600
|
function delayResolved(ms, value) {
|
|
2323
|
-
return new Promise((
|
|
2324
|
-
const t = setTimeout(() =>
|
|
2601
|
+
return new Promise((resolve2) => {
|
|
2602
|
+
const t = setTimeout(() => resolve2(value), ms);
|
|
2325
2603
|
if (typeof t.unref === "function") t.unref();
|
|
2326
2604
|
});
|
|
2327
2605
|
}
|
|
@@ -2400,7 +2678,7 @@ function parsedAppServerAttempt(task, r) {
|
|
|
2400
2678
|
kind: "parsed",
|
|
2401
2679
|
output: syntheticAppServerOutput(0),
|
|
2402
2680
|
payload: turnResultToPayload(r),
|
|
2403
|
-
...schema !== void 0 ? { schemaResult: (0,
|
|
2681
|
+
...schema !== void 0 ? { schemaResult: (0, import_subagent_engine_sdk10.extractAndValidateStructuredOutput)(r.response, schema) } : {}
|
|
2404
2682
|
};
|
|
2405
2683
|
}
|
|
2406
2684
|
function failedTerminalAppServerAttempt(r) {
|
|
@@ -2471,16 +2749,16 @@ function buildAppServerTimeoutMessage(err, sessionId, stopPath, retried = false)
|
|
|
2471
2749
|
${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`;
|
|
2472
2750
|
}
|
|
2473
2751
|
function isHostTimeoutAbort(ctx) {
|
|
2474
|
-
return ctx.signal?.aborted === true && ctx.signal.reason ===
|
|
2752
|
+
return ctx.signal?.aborted === true && ctx.signal.reason === import_subagent_engine_sdk10.HOST_TIMEOUT_ABORT_REASON;
|
|
2475
2753
|
}
|
|
2476
2754
|
var PROBE_STDOUT_CAP_CHARS = 4096;
|
|
2477
2755
|
async function defaultProbeVersion(cliPath, dataDir) {
|
|
2478
2756
|
try {
|
|
2479
|
-
return await new Promise((
|
|
2480
|
-
const child = (0,
|
|
2757
|
+
return await new Promise((resolve2, reject) => {
|
|
2758
|
+
const child = (0, import_subagent_engine_sdk10.spawnEngineChild)({
|
|
2481
2759
|
command: "node",
|
|
2482
2760
|
args: [cliPath, "--version"],
|
|
2483
|
-
env: (0,
|
|
2761
|
+
env: (0, import_subagent_engine_sdk10.buildEngineChildEnv)(process.env, { dataDir }),
|
|
2484
2762
|
stdout: "pipe",
|
|
2485
2763
|
stderr: "ignore",
|
|
2486
2764
|
signal: AbortSignal.timeout(PROBE_VERSION_TIMEOUT_MS)
|
|
@@ -2493,11 +2771,11 @@ async function defaultProbeVersion(cliPath, dataDir) {
|
|
|
2493
2771
|
child.once("error", reject);
|
|
2494
2772
|
child.once("close", (code) => {
|
|
2495
2773
|
if (code !== 0) reject(new Error(`zcode --version exited with code ${code}`));
|
|
2496
|
-
else
|
|
2774
|
+
else resolve2(out.trim().split("\n")[0]?.trim() || void 0);
|
|
2497
2775
|
});
|
|
2498
2776
|
});
|
|
2499
2777
|
} catch (err) {
|
|
2500
|
-
|
|
2778
|
+
logger4.debug(
|
|
2501
2779
|
`[zcode-engine] probe version check failed (best-effort continue): ${(0, import_subagent_engine_sdk3.toErrorMessage)(err)}`
|
|
2502
2780
|
);
|
|
2503
2781
|
return void 0;
|
|
@@ -2506,7 +2784,7 @@ async function defaultProbeVersion(cliPath, dataDir) {
|
|
|
2506
2784
|
|
|
2507
2785
|
// src/registration.ts
|
|
2508
2786
|
function defaultEngineDataDir() {
|
|
2509
|
-
return (0,
|
|
2787
|
+
return (0, import_subagent_engine_sdk11.resolveEngineDataDir)(process.env);
|
|
2510
2788
|
}
|
|
2511
2789
|
function createZcodeEngine(deps) {
|
|
2512
2790
|
return new ZcodeEngine(deps);
|
|
@@ -2519,7 +2797,7 @@ function createDefaultZcodeEngine() {
|
|
|
2519
2797
|
}
|
|
2520
2798
|
|
|
2521
2799
|
// src/port-types.ts
|
|
2522
|
-
var
|
|
2800
|
+
var import_subagent_engine_sdk12 = require("@zhushanwen/subagent-engine-sdk");
|
|
2523
2801
|
|
|
2524
2802
|
// src/server.ts
|
|
2525
2803
|
var REVERSE_TIMEOUT_DEFAULT_MS = 6e4;
|
|
@@ -2540,11 +2818,11 @@ var EngineProtocolServer = class {
|
|
|
2540
2818
|
}
|
|
2541
2819
|
/** 入站帧消费(请求帧 + 反向请求应答帧;main.ts 的行解析器拆行后喂入)。 */
|
|
2542
2820
|
handleFrame(frame) {
|
|
2543
|
-
if ((0,
|
|
2821
|
+
if ((0, import_subagent_engine_sdk13.isResponseFrame)(frame)) {
|
|
2544
2822
|
this.settleReverse(frame.id, frame);
|
|
2545
2823
|
return;
|
|
2546
2824
|
}
|
|
2547
|
-
if ((0,
|
|
2825
|
+
if ((0, import_subagent_engine_sdk13.isReverseRequestFrame)(frame)) {
|
|
2548
2826
|
this.write({
|
|
2549
2827
|
id: 0,
|
|
2550
2828
|
error: {
|
|
@@ -2570,7 +2848,7 @@ var EngineProtocolServer = class {
|
|
|
2570
2848
|
async dispatch(id, method, params) {
|
|
2571
2849
|
const handler = this.methodHandlers[method];
|
|
2572
2850
|
if (handler === void 0) {
|
|
2573
|
-
throw new
|
|
2851
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2574
2852
|
"engine_protocol_unknown_method",
|
|
2575
2853
|
`unknown protocol method: ${method} (request id ${id})`,
|
|
2576
2854
|
"The engine speaks protocol v1; check the installed engine package version vs the host."
|
|
@@ -2598,17 +2876,17 @@ var EngineProtocolServer = class {
|
|
|
2598
2876
|
};
|
|
2599
2877
|
// ── initialize:版本协商(越界 → engine_protocol_mismatch)+ 能力应答 ──
|
|
2600
2878
|
initialize(params) {
|
|
2601
|
-
if (params?.protocolVersion !==
|
|
2602
|
-
throw new
|
|
2879
|
+
if (params?.protocolVersion !== import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION) {
|
|
2880
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2603
2881
|
"engine_protocol_mismatch",
|
|
2604
|
-
`host protocol version ${String(params?.protocolVersion)} is not compatible with engine protocol v${
|
|
2605
|
-
`Upgrade the engine package or the host so both speak protocol v${
|
|
2882
|
+
`host protocol version ${String(params?.protocolVersion)} is not compatible with engine protocol v${import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION}`,
|
|
2883
|
+
`Upgrade the engine package or the host so both speak protocol v${import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION}.`
|
|
2606
2884
|
);
|
|
2607
2885
|
}
|
|
2608
2886
|
this.initialized = true;
|
|
2609
2887
|
const models = this.engine.listModels?.() ?? null;
|
|
2610
2888
|
return {
|
|
2611
|
-
protocolVersion:
|
|
2889
|
+
protocolVersion: import_subagent_engine_sdk13.ENGINE_PROTOCOL_VERSION,
|
|
2612
2890
|
engineId: this.engine.id,
|
|
2613
2891
|
engineVersion: ZCODE_ADAPTER_VERSION,
|
|
2614
2892
|
adapterVersion: ZCODE_ADAPTER_VERSION,
|
|
@@ -2619,7 +2897,7 @@ var EngineProtocolServer = class {
|
|
|
2619
2897
|
// ── run:协议载荷 → 本地 AgentCallOpts/RunContext;事件 → 通知/host 通道 ──
|
|
2620
2898
|
async run(params) {
|
|
2621
2899
|
if (!this.initialized) {
|
|
2622
|
-
throw new
|
|
2900
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2623
2901
|
"engine_protocol_not_initialized",
|
|
2624
2902
|
"run before initialize is a protocol violation",
|
|
2625
2903
|
"The host must complete the initialize handshake before dispatching runs."
|
|
@@ -2630,24 +2908,23 @@ var EngineProtocolServer = class {
|
|
|
2630
2908
|
const active = { controller, seq: 0 };
|
|
2631
2909
|
this.activeRuns.set(runId, active);
|
|
2632
2910
|
const fullTask = { ...task, ...ctx.model !== void 0 ? { model: ctx.model } : {} };
|
|
2633
|
-
const ctxModel = (0,
|
|
2911
|
+
const ctxModel = (0, import_subagent_engine_sdk12.parseCtxModel)(ctx.ctxModel);
|
|
2634
2912
|
const stream = ctx.streamMode === "stream" ? { onDelta: (delta) => {
|
|
2635
2913
|
void this.reverseRequestInternal("host/streamDelta", { runId, delta });
|
|
2636
2914
|
} } : void 0;
|
|
2637
2915
|
const runCtx = {
|
|
2638
2916
|
taskId: runId,
|
|
2639
|
-
poolKey: ctx.poolKey,
|
|
2640
2917
|
signal: controller.signal,
|
|
2641
2918
|
onEvent: (event) => this.emitEvent(runId, event),
|
|
2642
2919
|
...ctxModel !== void 0 ? { ctxModel } : {},
|
|
2643
2920
|
...stream !== void 0 ? { stream } : {},
|
|
2644
2921
|
...ctx.schemaEnv !== void 0 ? { schemaEnv: ctx.schemaEnv } : {},
|
|
2645
2922
|
...ctx.engineFallback !== void 0 ? { engineFallback: ctx.engineFallback } : {},
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
},
|
|
2923
|
+
// [U6 / §3.2.6 要点 3] resume 锚点透传(宿主 → 引擎的续聊通道:zcode 锚
|
|
2924
|
+
// sessionRef {sessionId, dbPath},引擎侧 resume 读 + 新 session 注入消费)。
|
|
2925
|
+
...params.resume !== void 0 ? { resume: params.resume } : {},
|
|
2649
2926
|
onHandleReady: (partial) => {
|
|
2650
|
-
void this.reverseRequestInternal("host/handleReady", { runId, sessionRef: partial.sessionRef
|
|
2927
|
+
void this.reverseRequestInternal("host/handleReady", { runId, sessionRef: partial.sessionRef });
|
|
2651
2928
|
}
|
|
2652
2929
|
};
|
|
2653
2930
|
try {
|
|
@@ -2667,7 +2944,7 @@ var EngineProtocolServer = class {
|
|
|
2667
2944
|
}
|
|
2668
2945
|
validateModel(params) {
|
|
2669
2946
|
if (this.engine.validateModel === void 0) {
|
|
2670
|
-
throw new
|
|
2947
|
+
throw new import_subagent_engine_sdk13.EngineSdkError(
|
|
2671
2948
|
"engine_capability_unsupported",
|
|
2672
2949
|
"engine does not implement validateModel",
|
|
2673
2950
|
"Manifest omits modelCatalog; model validation falls back to run-time engine checks."
|
|
@@ -2693,14 +2970,14 @@ var EngineProtocolServer = class {
|
|
|
2693
2970
|
}
|
|
2694
2971
|
reverseRequestInternal(method, params) {
|
|
2695
2972
|
const id = `rev-${++this.revSeq}`;
|
|
2696
|
-
return new Promise((
|
|
2973
|
+
return new Promise((resolve2, reject) => {
|
|
2697
2974
|
const timer = setTimeout(() => {
|
|
2698
2975
|
this.reversePending.delete(id);
|
|
2699
2976
|
this.reverseClock?.settled(id);
|
|
2700
2977
|
reject(new Error(`reverse request ${method} (${id}) timed out after ${this.reverseTimeoutMs}ms`));
|
|
2701
2978
|
}, this.reverseTimeoutMs);
|
|
2702
2979
|
if (typeof timer.unref === "function") timer.unref();
|
|
2703
|
-
this.reversePending.set(id, { resolve, reject, timer });
|
|
2980
|
+
this.reversePending.set(id, { resolve: resolve2, reject, timer });
|
|
2704
2981
|
this.reverseClock?.started(id);
|
|
2705
2982
|
this.write({ id, method, params });
|
|
2706
2983
|
});
|
|
@@ -2721,7 +2998,7 @@ function probeParamsOf(params) {
|
|
|
2721
2998
|
return typeof params === "object" && params !== null ? params : void 0;
|
|
2722
2999
|
}
|
|
2723
3000
|
function toProtocolError(err) {
|
|
2724
|
-
if (err instanceof
|
|
3001
|
+
if (err instanceof import_subagent_engine_sdk13.EngineSdkError) return err.toStructured();
|
|
2725
3002
|
return {
|
|
2726
3003
|
code: "engine_run_failed",
|
|
2727
3004
|
message: (0, import_subagent_engine_sdk3.toErrorMessage)(err),
|
|
@@ -2730,8 +3007,8 @@ function toProtocolError(err) {
|
|
|
2730
3007
|
}
|
|
2731
3008
|
|
|
2732
3009
|
// src/main.ts
|
|
2733
|
-
var clock = (0,
|
|
2734
|
-
(0,
|
|
3010
|
+
var clock = (0, import_subagent_engine_sdk14.armEngineSelfDestruct)({ stdin: process.stdin });
|
|
3011
|
+
(0, import_subagent_engine_sdk14.runEngineCliEntry)({
|
|
2735
3012
|
component: "zcode-engine-cli",
|
|
2736
3013
|
server: new EngineProtocolServer({
|
|
2737
3014
|
write: (frame) => {
|