@zhushanwen/zcode-subagent-cli 0.3.2 → 0.4.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.
@@ -4,7 +4,6 @@ var ZCODE_ADAPTER_VERSION = "2.0.0";
4
4
  var ZCODE_CLI_DEFAULT_PATH = "/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs";
5
5
  var ZCODE_V2_CONFIG_PATH_SUFFIX = [".zcode", "v2", "config.json"];
6
6
  var ZCODE_FALLBACK_DEFAULT_MODEL = "builtin:bigmodel-coding-plan/GLM-5.3";
7
- var ZCODE_HOST_DB_SUFFIX = [".zcode", "cli", "db", "db.sqlite"];
8
7
  var ZCODE_KILL_GRACE_MS = 5e3;
9
8
  var ZCODE_ERROR_TAIL_CHARS = 2e3;
10
9
  var ZCODE_APPSERVER_REQUEST_TIMEOUT_MS = 15e3;
@@ -45,8 +44,12 @@ var ZCODE_SESSION_SWEEP_DEFER_MS = 50;
45
44
  // src/db-path.ts
46
45
  import * as os from "os";
47
46
  import * as path from "path";
47
+ import {
48
+ ZCODE_HOST_DB_SUFFIX,
49
+ ZCODE_ISOLATED_DB_SEGMENTS
50
+ } from "@zhushanwen/subagent-engine-sdk";
48
51
  function zcodeSessionDbPath(engineDataDir) {
49
- return path.join(engineDataDir, "engines", "zcode", "session-db", "db.sqlite");
52
+ return path.join(engineDataDir, ...ZCODE_ISOLATED_DB_SEGMENTS);
50
53
  }
51
54
  function hostZcodeDbPath() {
52
55
  return path.join(os.homedir(), ...ZCODE_HOST_DB_SUFFIX);
@@ -98,26 +101,40 @@ function usageFromStepFinish(part) {
98
101
  cacheWrite: finiteOr(cache.write, 0)
99
102
  };
100
103
  }
101
- function toolFromPart(part) {
102
- const toolName = typeof part.tool === "string" ? part.tool : "unknown";
103
- let state;
104
+ function stateFromString(raw) {
105
+ try {
106
+ const v = JSON.parse(raw);
107
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
108
+ } catch {
109
+ return void 0;
110
+ }
111
+ }
112
+ function stateFromPart(part) {
113
+ if (typeof part.state === "object" && part.state !== null && !Array.isArray(part.state)) {
114
+ return part.state;
115
+ }
104
116
  if (typeof part.state === "string") {
105
- try {
106
- const v = JSON.parse(part.state);
107
- state = typeof v === "object" && v !== null ? v : void 0;
108
- } catch {
109
- state = void 0;
110
- }
117
+ return stateFromString(part.state);
111
118
  }
119
+ return void 0;
120
+ }
121
+ function toolResultFromOutput(errorText, output) {
122
+ if (errorText !== void 0) return { content: [errorText] };
123
+ if (typeof output === "string") return { content: [output] };
124
+ if (typeof output === "object" && output !== null) return { details: output };
125
+ return void 0;
126
+ }
127
+ function toolFromPart(part) {
128
+ const toolName = typeof part.tool === "string" ? part.tool : "unknown";
129
+ const state = stateFromPart(part);
112
130
  const status = typeof state?.status === "string" ? state.status : "unknown";
113
131
  const output = state?.output;
132
+ const errorText = status !== "completed" && typeof state?.error === "string" ? state.error : void 0;
133
+ const result = toolResultFromOutput(errorText, output);
114
134
  return {
115
135
  toolName,
116
136
  ...state?.input !== void 0 ? { args: state.input } : {},
117
- // 输出形态(实测):string(内容)或 object(结构化)——分别映射到 pi ToolCallResult
118
- // 的 content[] / details,不发明第二形状
119
- ...typeof output === "string" ? { result: { content: [output] } } : {},
120
- ...typeof output === "object" && output !== null ? { result: { details: output } } : {},
137
+ ...result !== void 0 ? { result } : {},
121
138
  ...status !== "completed" ? { isError: true } : {}
122
139
  };
123
140
  }
@@ -581,8 +598,63 @@ if (text !== null) {
581
598
  };
582
599
  }
583
600
 
584
- // \u4EE5\u539F argv \u5F62\u6001\u542F\u52A8 zcode.cjs\uFF08argv[0]=node, argv[1]=\u672C wrapper\uFF0Cargv[2]=app-server\u2014\u2014
585
- // zcode.cjs \u7684 includes('app-server') \u5224\u5B9A\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002import() \u517C\u5BB9 CJS/ESM \u5165\u53E3\u3002
601
+ // \u5185\u5EFA provider \u914D\u7F6E\u5B9A\u4F4D\uFF08\u6F02\u79FB\u9762\u2461\uFF09\uFF1A3.12.x \u8D77\u914D\u7F6E\u6587\u4EF6\u632A\u4F4D + CLI \u4F9D\u8D56\u5BBF\u4E3B\u6CE8\u5165
602
+ // ZCODE_BUILTIN_PROVIDER_CONFIG_FILE \u76F4\u63A5\u5B9A\u4F4D\uFF1BZCode \u684C\u9762 spawn \u94FE\u81EA\u5E26\u8BE5\u952E\uFF0C
603
+ // \u672C wrapper \u94FE\u9700\u81EA\u7ED9\u3002\u663E\u5F0F\u4F20\u5165\u4F18\u5148 \u2192 v2 runtime \u76EE\u5F55\u4E0B\u5E73\u53F0\u76EE\u5F55\uFF08\u7CBE\u786E
604
+ // <plat>-<arch> \u4F18\u5148\uFF0CRust arch \u53D8\u4F53\u5982 darwin-aarch64 \u5B57\u5178\u5E8F\u8DDF\u540E\uFF09\xD7 \u7248\u672C\u76EE\u5F55
605
+ // \uFF08semver \u6700\u5927\uFF09\u7684 endpoint-*/zcode-builtin.json \u2192 \u90FD\u627E\u4E0D\u5230\u5219\u4E0D\u8BBE\u952E\uFF0C\u8BA9 CLI
606
+ // \u539F\u751F\u62A5\u9519\u900F\u51FA\uFF08\u9519\u8BEF\u6587\u6848\u542B\u6062\u590D\u6307\u5F15\uFF09\u3002
607
+ if (!process.env.ZCODE_BUILTIN_PROVIDER_CONFIG_FILE) {
608
+ let located = null;
609
+ try {
610
+ const providerRoot = path.join(os.homedir(), '.zcode', 'v2', 'runtime', 'provider');
611
+ // \u5E73\u53F0\u76EE\u5F55\u53CC\u547D\u540D\uFF1ANode \u5F62\u6001 <plat>-<arch>\uFF08darwin-arm64\uFF09\u4E0E\u684C\u9762 app \u7684 Rust
612
+ // arch \u547D\u540D\uFF08darwin-aarch64/linux-x86_64\uFF09\u5E76\u5B58\u2014\u2014\u6309\u5E73\u53F0\u524D\u7F00\u6536\u96C6\u5B50\u76EE\u5F55\uFF0C
613
+ // \u7CBE\u786E\u5F62\u6001\u6392\u6700\u5148\uFF08\u5411\u540E\u517C\u5BB9\u81EA\u5EFA\u5E03\u5C40\uFF09\uFF0CRust \u53D8\u4F53\u6309\u5B57\u5178\u5E8F\u8DDF\u540E\u9010\u4E2A\u5C1D\u8BD5
614
+ const exact = process.platform + '-' + process.arch;
615
+ const platDirs = fs.readdirSync(providerRoot, { withFileTypes: true })
616
+ .filter((d) => d.isDirectory() && d.name.startsWith(process.platform + '-'))
617
+ .map((d) => d.name)
618
+ .sort((a, b) => {
619
+ if ((a === exact) !== (b === exact)) return a === exact ? -1 : 1;
620
+ return a < b ? -1 : a > b ? 1 : 0;
621
+ });
622
+ const byVerDesc = (a, b) => {
623
+ const pa = a.split('.'), pb = b.split('.');
624
+ for (let i = 0; i < 3; i++) {
625
+ const na = parseInt(pa[i], 10) || 0, nb = parseInt(pb[i], 10) || 0;
626
+ if (na !== nb) return nb - na;
627
+ }
628
+ return 0;
629
+ };
630
+ for (const plat of platDirs) {
631
+ const rtRoot = path.join(providerRoot, plat);
632
+ let vers = [];
633
+ try { vers = fs.readdirSync(rtRoot).filter((v) => /^\\d+\\.\\d+/.test(v)).sort(byVerDesc); } catch { /* \u5E73\u53F0\u76EE\u5F55\u4E0D\u53EF\u8BFB \u2192 \u8BD5\u4E0B\u4E00\u4E2A */ }
634
+ for (const ver of vers) {
635
+ const verDir = path.join(rtRoot, ver);
636
+ let endpoints = [];
637
+ try { endpoints = fs.readdirSync(verDir).filter((e) => e.startsWith('endpoint-')).sort(); } catch { /* \u7248\u672C\u76EE\u5F55\u4E0D\u53EF\u8BFB \u2192 \u8DF3\u8FC7 */ }
638
+ for (const ep of endpoints) {
639
+ const candidate = path.join(verDir, ep, 'zcode-builtin.json');
640
+ if (fs.existsSync(candidate)) { located = candidate; break; }
641
+ }
642
+ if (located) break;
643
+ }
644
+ if (located) break;
645
+ }
646
+ } catch { /* provider \u76EE\u5F55\u7F3A\u5931/\u4E0D\u53EF\u8BFB \u2192 \u4E0D\u8BBE\u952E */ }
647
+ if (located) process.env.ZCODE_BUILTIN_PROVIDER_CONFIG_FILE = located;
648
+ }
649
+
650
+ // \u4E0A\u6E38 provider bootstrap \u4EE5 argv[1] \u4E3A\u5165\u53E3\u951A\uFF0C\u4ECE\u8BE5\u8DEF\u5F84\u90BB\u8FD1\u5B9A\u4F4D CLI \u5185\u5EFA
651
+ // provider \u914D\u7F6E\u2014\u2014wrapper \u5F62\u6001\u4E0B argv[1] = \u672C wrapper \u843D\u76D8\u8DEF\u5F84\uFF0Cbootstrap \u4F1A\u5728
652
+ // wrapper \u90BB\u57DF\u627E provider \u914D\u7F6E\u800C\u5931\u8D25\uFF08\u771F\u673A\u62A5\u300C\u65E0\u6CD5\u5B9A\u4F4D CLI ZCode Built-in Provider
653
+ // Config\u300D\u542F\u52A8\u5373\u9000\uFF09\u3002import \u524D\u628A argv[1] \u6539\u5199\u4E3A CLI_PATH\uFF1ACLI \u770B\u5230\u7684 argv \u5E8F\u5217\u4E0E
654
+ // \u65E0 wrapper \u76F4\u8DD1\uFF08node <cliPath> app-server ...\uFF09\u9010\u4F4D\u4E00\u81F4\uFF0Cargv[0]=node\u3001
655
+ // argv[2]='app-server' \u4E0D\u53D8\uFF0Cincludes('app-server') \u5B50\u547D\u4EE4\u5224\u5B9A\u4E0D\u53D7\u5F71\u54CD\u3002
656
+ // import() \u517C\u5BB9 CJS/ESM \u5165\u53E3\u3002
657
+ process.argv[1] = CLI_PATH;
586
658
  import(CLI_PATH).catch((err) => {
587
659
  process.stderr.write('[zcode-launcher] zcode.cjs \u52A0\u8F7D\u5931\u8D25: ' + (err && err.message || err) + '\\n');
588
660
  process.exit(3);
@@ -1353,7 +1425,8 @@ var SessionChannel = class {
1353
1425
  * session/resume 读通道([U6 / §3.2.6 要点 3,P-1 探针实证]):应答自带完整
1354
1426
  * 双向 messages 历史(user/assistant 全量、tokens、parts)——zcode 续聊选型的
1355
1427
  * 历史来源(读通道成立;原地 resume 续写被 -32031 卡死,写通道不可用)。错误
1356
- * 原样上抛(调用方决定降级形态——引擎侧 warn 后无历史前缀开新会话)。
1428
+ * 原样上抛(调用方决定降级形态——引擎侧 warn 后注入锚失效声明段继续执行,
1429
+ * run 不失败,[U3])。
1357
1430
  * 经 conn.request 惰性启动连接(resume 是 run 的**首个**请求——调用时点连接
1358
1431
  * 可能尚未启动,与 readBestEffort 的「已死跳过」判据不同场景,不做 alive 守卫)。
1359
1432
  */
@@ -1901,8 +1974,9 @@ var ZcodeEngine = class {
1901
1974
  personaInjection: "prompt",
1902
1975
  // app-server 推送流实时流出(session/event payload.delta → text_delta)
1903
1976
  eventGranularity: "stream",
1904
- // 首期未接 worktree 隔离(公共层 worktree-manager 接入后升 emulated)
1905
- sandbox: "none",
1977
+ // 无 OS sandbox;worktree 隔离由公共层 worktree-manager 承担(引擎侧仅消费
1978
+ // task.cwd → session/create 的 workspacePath)= emulated(pi 同款声明语义)
1979
+ sandbox: "emulated",
1906
1980
  // sqlite 三级 JOIN 完整重建 turns(reader 实测)
1907
1981
  sessionRead: "full",
1908
1982
  // --resume 冷启动可用(实测)
@@ -1986,8 +2060,9 @@ var ZcodeEngine = class {
1986
2060
  if (ctx.signal?.aborted === true) {
1987
2061
  return this.abortedAppServerRun(task, ctx, startedAt);
1988
2062
  }
1989
- const modelRef = resolveZcodeModelRef(task.model, this.deps.sources);
1990
- this.warnIgnoredCtxModel(task, ctx, modelRef);
2063
+ const requestedModel = task.model?.trim();
2064
+ const modelRef = requestedModel !== void 0 && requestedModel !== "" ? resolveZcodeModelRef(requestedModel, this.deps.sources) : void 0;
2065
+ this.warnIgnoredCtxModel(task, ctx);
1991
2066
  this.warnThoughtLevelUncommon(task, ctx);
1992
2067
  const cwd = task.cwd ?? process.cwd();
1993
2068
  const schema = isPlainObject(task.schema) ? task.schema : void 0;
@@ -2088,8 +2163,7 @@ var ZcodeEngine = class {
2088
2163
  */
2089
2164
  async attemptAppServerTurn(task, ctx, modelRef, cwd, prompt, opts = {}) {
2090
2165
  const rt = this.ensureAppServerRuntime();
2091
- const { providerId, modelId } = splitZcodeModelRef(modelRef);
2092
- const createParams = buildAppServerCreateParams(task, providerId, modelId, cwd);
2166
+ const createParams = buildAppServerCreateParams(task, modelRef, cwd);
2093
2167
  let currentSessionId;
2094
2168
  let signalSessionCreated;
2095
2169
  const sessionCreated = new Promise((resolve2) => {
@@ -2410,10 +2484,17 @@ var ZcodeEngine = class {
2410
2484
  }
2411
2485
  /**
2412
2486
  * [u-h2 D2-2] 派发同步期 model 校验:委托 resolveZcodeModelRef(与 run prepare 期
2413
- * 同一函数——canonicalRef 归一化、短名缺省 provider、凭据与清单校验单一权威,无双实现)。
2414
- * modelRef undefined = 返回引擎缺省模型 canonical 全名(ZCODE_FALLBACK_DEFAULT_MODEL,
2415
- * D2-1 ctxModel 不透传的承接面)。校验失败原样抛 ZcodePrepareError,由编排层
2487
+ * 显式路径同一函数——canonicalRef 归一化、短名缺省 provider、凭据与清单校验单一
2488
+ * 权威,无双实现)。校验失败原样抛 ZcodePrepareError,由编排层
2416
2489
  * (engine/model-validation.ts)包装成「引擎与模型不配套」文案。
2490
+ *
2491
+ * [R4/D6-②] 缺席语义:本实现是进程内形态 + 协议诊断面(宿主 cli 形态消费的是
2492
+ * RemoteEngine 的 manifest 本地判定,不经本方法——SDK protocol methods.ts 明示
2493
+ * validateModel 为诊断面)。缺席 modelRef 时返回 ZCODE_FALLBACK_DEFAULT_MODEL
2494
+ * canonical 全名作为「引擎缺省模型」的呈现值(诊断面只读不 create,不参与
2495
+ * create 缺席不携带的 G3 行为链)。帧应答形态维持 {canonicalRef: string}(SDK
2496
+ * port-contract 契约必填——「帧字段缺席」的 optional 对齐需放宽 SDK 契约与
2497
+ * server handler 签名,非本包单方面可完成)。
2417
2498
  */
2418
2499
  validateModel(modelRef) {
2419
2500
  return { canonicalRef: resolveZcodeModelRef(modelRef, this.deps.sources) };
@@ -2497,30 +2578,33 @@ var ZcodeEngine = class {
2497
2578
  }
2498
2579
  /**
2499
2580
  * [F16b] ctxModel 忽略留痕:ctxModel 是 pi 链路的第三层兜底(port.ts 契约——
2500
- * 依赖 pi resolveModel 链的引擎才消费它),zcode 自带 provider 体系与缺省模型
2501
- * (resolveZcodeModelRef:requested > ZCODE_FALLBACK_DEFAULT_MODEL),不消费
2502
- * ctxModel。「调用方给了 ctxModel 但 task.model 未显式指定」时出声一行,说明
2503
- * 实际落引擎缺省模型(含实际 model id)——防静默降档无据可查。只在「ctx 有模型
2504
- * 但被忽略」场景输出:显式 task.model 走正常解析链、ctx 本就无模型属预期缺省,
2505
- * 均不出声(避免噪音)。
2581
+ * 依赖 pi resolveModel 链的引擎才消费它),zcode 不消费。「调用方给了 ctxModel 但
2582
+ * task.model 未显式指定」时出声一行。只在「ctx 有模型但被忽略」场景输出:显式
2583
+ * task.model 走正常解析链、ctx 本就无模型属预期缺省,均不出声(避免噪音)。
2584
+ * [R4/G3] 文案更新:缺席 model 时 create 不携带 model 键(缺省模型由 zcode 自身
2585
+ * 解析——用户 defaultModelSelection 优先),不再声称「实际使用引擎缺省模型
2586
+ * <fallback>」(恒传时代的表述,与缺席不携带的新行为不符)。modelRef 参数已随
2587
+ * R4 条件携带移除——warn 可达即缺席态(显式 task.model 在 requested 非空早退处
2588
+ * 返回),无第三形态。
2506
2589
  */
2507
- warnIgnoredCtxModel(task, ctx, modelRef) {
2590
+ warnIgnoredCtxModel(task, ctx) {
2508
2591
  if (ctx.ctxModel === void 0) return;
2509
2592
  const requested = task.model?.trim();
2510
2593
  if (requested !== void 0 && requested !== "") return;
2511
2594
  logger4.warn(
2512
- `[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}`,
2595
+ `[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\uFF0Ccreate \u4E0D\u643A\u5E26 model \u952E\uFF0C\u7F3A\u7701\u6A21\u578B\u7531 zcode \u81EA\u8EAB\u89E3\u6790\uFF08\u7528\u6237 defaultModelSelection \u4F18\u5148\uFF09`,
2513
2596
  { taskId: ctx.taskId }
2514
2597
  );
2515
2598
  }
2516
2599
  /**
2517
2600
  * [U6 / §3.2.6 要点 3] resume 锚 → 历史前缀(interact-resume 的读半段)。
2518
2601
  *
2519
- * 无锚 / 非 zcode 锚形态 → undefined(行为不变)。读通道失败(条目被 TTL 清 /
2520
- * 会话失效 / 控制面错误)= 宿主侧锚判据通过后、引擎派发前的窄竞态窗——降级为
2521
- * **无历史前缀的新会话**(warn 留痕;宿主 reopen 摘要承担主降级面),不炸轮:
2522
- * 续聊消息本身仍可送达(模型缺历史上下文,比整轮失败可用性高,与 reopen 降级
2523
- * 的「带摘要重开」语义同族)。
2602
+ * 无锚 / 非 zcode 锚形态 → undefined(行为不变)。读通道失败(会话失效 / 控制
2603
+ * 面错误)= 锚真失效的权威信号——宿主侧库投影预检查已退役(app-server 落库滞后
2604
+ * 于 create 应答,分钟级窗 + 部分行永不落库,预检查系统性误判;resume 走
2605
+ * app-server resident 内存态才是真实活性判据),因此此处不再静默降级为无前缀
2606
+ * 裸跑,而是返回锚失效声明段:让模型知情「延续但无历史」,基于最新消息独立
2607
+ * 续推,而非在缺上下文时臆测连续性。用户消息照常执行,不炸轮。
2524
2608
  */
2525
2609
  async buildResumeHistoryPrefix(ctx) {
2526
2610
  const anchor = zcodeResumeAnchorOf(ctx, this.deps.engineDataDir());
@@ -2534,10 +2618,10 @@ var ZcodeEngine = class {
2534
2618
  tokens = extractResumeTotalTokens(result);
2535
2619
  } catch (err) {
2536
2620
  logger4.warn(
2537
- `[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)}`,
2621
+ `[zcode-engine] session/resume \u8BFB\u5386\u53F2\u5931\u8D25\uFF08\u951A ${anchor.sessionId}\uFF09\u2014\u2014\u5224\u5B9A\u951A\u771F\u5931\u6548\uFF0C\u6CE8\u5165\u951A\u5931\u6548\u58F0\u660E\u6BB5\u7EE7\u7EED\u6267\u884C: ${errMessage3(err)}`,
2538
2622
  { taskId: ctx.taskId }
2539
2623
  );
2540
- return void 0;
2624
+ return buildResumeUnavailableNoticeSegment();
2541
2625
  }
2542
2626
  if (history.length === 0) return void 0;
2543
2627
  return buildResumeInjectionSegment(history, anchor.sessionId, tokens);
@@ -2587,6 +2671,9 @@ ${omissionNote}${kept.join("\n")}
2587
2671
 
2588
2672
  `;
2589
2673
  }
2674
+ function buildResumeUnavailableNoticeSegment() {
2675
+ return "[\u4F1A\u8BDD\u5EF6\u7EED\u63D0\u793A] \u672C\u6D88\u606F\u662F\u540C\u4E00\u4EFB\u52A1\u7684\u5EF6\u7EED\u4F1A\u8BDD\uFF0C\u4F46\u4E0A\u4E00\u4F1A\u8BDD\u7684\u5386\u53F2\u8BB0\u5F55\u4E0D\u53EF\u6062\u590D\uFF08\u539F\u59CB\u4F1A\u8BDD\u5DF2\u5931\u6548\uFF09\u3002\n\u6CA1\u6709\u66F4\u65E9\u7684\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u53EF\u5F15\u7528\u2014\u2014\u8BF7\u57FA\u4E8E\u4E0B\u65B9\u6700\u65B0\u6D88\u606F\u72EC\u7ACB\u5224\u65AD\u5E76\u7EE7\u7EED\u63A8\u8FDB\u4EFB\u52A1\u3002\n\n";
2676
+ }
2590
2677
  var ZCODE_TURN_RETRY_MIN_BUDGET_MS = 3e5;
2591
2678
  function resolveTransientRetryBudget(totalBudgetMs, consumedMs) {
2592
2679
  if (totalBudgetMs === void 0) return { state: "unbounded" };
@@ -2683,15 +2770,16 @@ function appendSchemaRetryDirective(basePrompt, validationError) {
2683
2770
  Your previous answer failed schema validation: ${validationError}
2684
2771
  Answer again. Output ONLY the JSON value conforming to the schema above \u2014 no prose, no markdown fences, no extra text.`;
2685
2772
  }
2686
- function buildAppServerCreateParams(task, providerId, modelId, cwd) {
2773
+ function buildAppServerCreateParams(task, modelRef, cwd) {
2687
2774
  const denyTools = (task.denyTools ?? []).filter((t) => typeof t === "string" && t.trim() !== "");
2688
2775
  const thoughtLevel = task.thinkingLevel?.trim();
2776
+ const model = modelRef !== void 0 && modelRef !== "" ? splitZcodeModelRef(modelRef) : void 0;
2689
2777
  return {
2690
2778
  workspacePath: cwd,
2691
2779
  mode: "yolo",
2692
- // per-session model(G3):create 参数透传(A.2 ① strict 对象)——同进程任务
2693
- // 各用各的模型,互不干扰
2694
- model: { providerId, modelId },
2780
+ // per-session model(G3):显式指定时 create 参数透传(A.2 ① strict 对象)——同进程任务
2781
+ // 各用各的模型,互不干扰;缺席不设键(zcode 自身缺省解析)
2782
+ ...model !== void 0 ? { model } : {},
2695
2783
  ...thoughtLevel !== void 0 && thoughtLevel !== "" ? { thoughtLevel } : {},
2696
2784
  ...denyTools.length > 0 ? { toolDenylist: denyTools } : {}
2697
2785
  };
@@ -2947,7 +3035,11 @@ var EngineProtocolServer = class {
2947
3035
  const controller = new AbortController();
2948
3036
  const active = { controller, seq: 0 };
2949
3037
  this.activeRuns.set(runId, active);
2950
- const fullTask = { ...task, ...ctx.model !== void 0 ? { model: ctx.model } : {} };
3038
+ const fullTask = {
3039
+ ...task,
3040
+ ...ctx.model !== void 0 ? { model: ctx.model } : {},
3041
+ ...ctx.cwd !== void 0 ? { cwd: ctx.cwd } : {}
3042
+ };
2951
3043
  const ctxModel = parseCtxModel(ctx.ctxModel);
2952
3044
  const stream = ctx.streamMode === "stream" ? { onDelta: (delta) => {
2953
3045
  void this.reverseRequestInternal("host/streamDelta", { runId, delta });