@zhushanwen/zcode-subagent-cli 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -369,6 +369,7 @@ function synthesizeCoarseEvents(response, usage) {
369
369
  import * as fs2 from "fs";
370
370
  import * as os2 from "os";
371
371
  import * as path2 from "path";
372
+ import { resolveEngineDataDir, ZCODE_PI_SYNCED_PROVIDER_CONFIG_SEGMENTS } from "@zhushanwen/subagent-engine-sdk";
372
373
  var ZcodePrepareError = class extends Error {
373
374
  code;
374
375
  constructor(code, message) {
@@ -377,35 +378,81 @@ var ZcodePrepareError = class extends Error {
377
378
  this.code = code;
378
379
  }
379
380
  };
381
+ var TAIJI_SYNC_GUIDANCE = "\u82E5\u4F7F\u7528 taiji \u540C\u6B65\u526F\u672C/\u9ED8\u8BA4\u6A21\u578B\uFF0C\u5230 taiji settings \u6838\u5BF9\u6216\u91CD\u9009\uFF0C\u91CD\u542F taiji \u751F\u6548\u3002";
380
382
  function isRecord(v) {
381
383
  return typeof v === "object" && v !== null && !Array.isArray(v);
382
384
  }
383
385
  function strArray(v) {
384
386
  return Array.isArray(v) ? v.filter((m) => typeof m === "string" && m.trim() !== "") : [];
385
387
  }
386
- function readSourceConfig(absPath) {
387
- const empty = { providers: /* @__PURE__ */ new Map(), order: [] };
388
+ function readSourceJsonOrUndefined(absPath) {
388
389
  let raw;
389
390
  try {
390
391
  raw = fs2.readFileSync(absPath, "utf8");
391
392
  } catch {
392
- return empty;
393
+ return void 0;
393
394
  }
394
- let parsed;
395
395
  try {
396
- parsed = JSON.parse(raw);
396
+ return JSON.parse(raw);
397
397
  } catch {
398
- return empty;
399
- }
400
- if (!isRecord(parsed)) return empty;
401
- const rules = parsed["config"]?.["providerConfigRules"];
402
- const providerRules = !isRecord(rules) ? void 0 : rules["providerRules"];
403
- const orderSrc = parsed["config"]?.["providerOrder"];
404
- const conf = { providers: /* @__PURE__ */ new Map(), order: strArray(orderSrc) };
405
- if (!Array.isArray(providerRules)) return conf;
406
- collectProviderRules(providerRules, conf);
398
+ return void 0;
399
+ }
400
+ }
401
+ function extractSourceConfig(parsed) {
402
+ const conf = { providers: /* @__PURE__ */ new Map(), order: [] };
403
+ if (!isRecord(parsed)) return conf;
404
+ const config = parsed["config"];
405
+ if (!isRecord(config)) return conf;
406
+ conf.order = strArray(config["providerOrder"]);
407
+ const rules = config["providerConfigRules"];
408
+ const providerRules = isRecord(rules) ? rules["providerRules"] : void 0;
409
+ if (Array.isArray(providerRules)) collectProviderRules(providerRules, conf);
407
410
  return conf;
408
411
  }
412
+ function readSourceConfig(absPath) {
413
+ return extractSourceConfig(readSourceJsonOrUndefined(absPath));
414
+ }
415
+ function mergePersonalProviderConfig(realObj, copyObj) {
416
+ if (!isRecord(copyObj)) return realObj;
417
+ if (!isRecord(realObj)) return copyObj;
418
+ const cfgOf = (obj) => isRecord(obj["config"]) ? obj["config"] : {};
419
+ const rulesOf = (obj) => {
420
+ const pcr = cfgOf(obj)["providerConfigRules"];
421
+ const rules = isRecord(pcr) ? pcr["providerRules"] : void 0;
422
+ return Array.isArray(rules) ? rules : [];
423
+ };
424
+ const mergedRules = [];
425
+ const seen = /* @__PURE__ */ new Set();
426
+ for (const rule of rulesOf(realObj)) {
427
+ mergedRules.push(rule);
428
+ if (isRecord(rule)) {
429
+ const id = rule["providerId"];
430
+ if (typeof id === "string" && id !== "") seen.add(id);
431
+ }
432
+ }
433
+ for (const rule of rulesOf(copyObj)) {
434
+ if (!isRecord(rule)) continue;
435
+ const id = rule["providerId"];
436
+ if (typeof id !== "string" || id === "") continue;
437
+ if (seen.has(id)) continue;
438
+ seen.add(id);
439
+ mergedRules.push(rule);
440
+ }
441
+ const mergedOrder = [];
442
+ for (const id of [...strArray(cfgOf(realObj)["providerOrder"]), ...strArray(cfgOf(copyObj)["providerOrder"])]) {
443
+ if (!mergedOrder.includes(id)) mergedOrder.push(id);
444
+ }
445
+ const realCfg = cfgOf(realObj);
446
+ const realPcr = isRecord(realCfg["providerConfigRules"]) ? realCfg["providerConfigRules"] : {};
447
+ return {
448
+ ...realObj,
449
+ config: {
450
+ ...realCfg,
451
+ providerOrder: mergedOrder,
452
+ providerConfigRules: { ...realPcr, providerRules: mergedRules }
453
+ }
454
+ };
455
+ }
409
456
  function collectProviderRules(providerRules, conf) {
410
457
  for (const rule of providerRules) {
411
458
  if (!isRecord(rule)) continue;
@@ -440,6 +487,40 @@ function defaultPersonalProviderConfigPath() {
440
487
  function defaultV2ConfigPath() {
441
488
  return path2.join(os2.homedir(), ...ZCODE_V2_CONFIG_PATH_SUFFIX);
442
489
  }
490
+ function piSyncedProviderConfigPath(engineDataDir) {
491
+ return path2.join(engineDataDir, ...ZCODE_PI_SYNCED_PROVIDER_CONFIG_SEGMENTS);
492
+ }
493
+ function envEngineDataDirOrUndefined() {
494
+ try {
495
+ return resolveEngineDataDir(process.env);
496
+ } catch {
497
+ return void 0;
498
+ }
499
+ }
500
+ function resolvePersonalProviderSource(sources, engineDataDir) {
501
+ const explicit = sources?.personalProviderConfigPath;
502
+ if (explicit !== void 0) return { kind: "explicit", path: explicit };
503
+ const dataDir = engineDataDir ?? envEngineDataDirOrUndefined();
504
+ if (dataDir !== void 0 && fs2.existsSync(piSyncedProviderConfigPath(dataDir))) {
505
+ return { kind: "copy", path: piSyncedProviderConfigPath(dataDir), realPath: defaultPersonalProviderConfigPath() };
506
+ }
507
+ return { kind: "real", path: defaultPersonalProviderConfigPath() };
508
+ }
509
+ function resolvePersonalProviderConfigPath(sources, engineDataDir) {
510
+ return resolvePersonalProviderSource(sources, engineDataDir).path;
511
+ }
512
+ function readPersonalProviderView(sources) {
513
+ const resolution = resolvePersonalProviderSource(sources);
514
+ if (resolution.kind !== "copy") {
515
+ return { conf: readSourceConfig(resolution.path), sourceDesc: resolution.path };
516
+ }
517
+ const copyJson = readSourceJsonOrUndefined(resolution.path);
518
+ const realJson = readSourceJsonOrUndefined(resolution.realPath);
519
+ return {
520
+ conf: extractSourceConfig(mergePersonalProviderConfig(realJson, copyJson)),
521
+ sourceDesc: `merge(\u771F\u5B9E ${resolution.realPath} + \u526F\u672C ${resolution.path})`
522
+ };
523
+ }
443
524
  var SEMVER_SEGMENTS = 3;
444
525
  function locateZcodeBuiltinCatalog(sources) {
445
526
  if (sources?.builtinCatalogPath !== void 0) return sources.builtinCatalogPath;
@@ -530,15 +611,14 @@ function defaultProviderForShortName(conf, withKey) {
530
611
  return withKey[0][0];
531
612
  }
532
613
  function resolveZcodeModelRef(requested, sources) {
533
- const srcPath = sources?.personalProviderConfigPath ?? defaultPersonalProviderConfigPath();
534
- const conf = readSourceConfig(srcPath);
614
+ const { conf, sourceDesc } = readPersonalProviderView(sources);
535
615
  const withKey = [...conf.providers.entries()].filter(([, e]) => hasApiKey(e));
536
616
  const wanted = requested?.trim() || void 0;
537
617
  if (withKey.length === 0) {
538
618
  const hint = wanted ? `model="${wanted}" \u4E0D\u80FD\u6821\u9A8C` : "\u7F3A\u7701\u6A21\u578B\u89E3\u6790\u4E0D\u80FD\u6821\u9A8C";
539
619
  throw new ZcodePrepareError(
540
620
  "engine_credential_missing",
541
- `zcode \u5F15\u64CE\u627E\u4E0D\u5230\u4EFB\u4F55\u5E26 apiKey \u7684\u4E2A\u4EBA provider\uFF08${hint}\uFF09\u3002\u5DF2\u8BFB\u6E90\uFF1A${srcPath}\u3002\u6062\u590D\u6307\u5F15\uFF1A\u5728 ZCode \u684C\u9762\u7AEF\u914D\u7F6E\u81F3\u5C11\u4E00\u4E2A\u4E2A\u4EBA provider\uFF08provider_config.json \u5185\u5B58\u5728 config.access.apiKey \u975E\u7A7A\u6761\u76EE\uFF09\u540E\u91CD\u8BD5\uFF1Bplan \u5957\u9910\u65CF\uFF08builtin:/account: \u524D\u7F00\uFF09\u7ECF\u8D26\u53F7 entitlement \u95E8\u63A7\uFF0C\u5916\u90E8 spawn \u7684\u5F15\u64CE\u6CE8\u518C\u8868\u4E0D\u88C5\u8F7D\uFF0C\u4E0D\u53EF\u4F5C\u4E3A\u5F15\u64CE\u6A21\u578B\u3002`
621
+ `zcode \u5F15\u64CE\u627E\u4E0D\u5230\u4EFB\u4F55\u5E26 apiKey \u7684\u4E2A\u4EBA provider\uFF08${hint}\uFF09\u3002\u5DF2\u8BFB\u6E90\uFF1A${sourceDesc}\u3002\u6062\u590D\u6307\u5F15\uFF1A\u5728 ZCode \u684C\u9762\u7AEF\u914D\u7F6E\u81F3\u5C11\u4E00\u4E2A\u4E2A\u4EBA provider\uFF08provider_config.json \u5185\u5B58\u5728 config.access.apiKey \u975E\u7A7A\u6761\u76EE\uFF09\u540E\u91CD\u8BD5\uFF1Bplan \u5957\u9910\u65CF\uFF08builtin:/account: \u524D\u7F00\uFF09\u7ECF\u8D26\u53F7 entitlement \u95E8\u63A7\uFF0C\u5916\u90E8 spawn \u7684\u5F15\u64CE\u6CE8\u518C\u8868\u4E0D\u88C5\u8F7D\uFF0C\u4E0D\u53EF\u4F5C\u4E3A\u5F15\u64CE\u6A21\u578B\u3002` + TAIJI_SYNC_GUIDANCE
542
622
  );
543
623
  }
544
624
  if (wanted === void 0) return "";
@@ -549,26 +629,26 @@ function resolveZcodeModelRef(requested, sources) {
549
629
  const known = withKey.map(([id]) => id).join(", ");
550
630
  throw new ZcodePrepareError(
551
631
  "model_not_available",
552
- `\u672A\u77E5 provider "${provider}"\uFF08\u5E26\u51ED\u636E\u7684\u4E2A\u4EBA provider: ${known}\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u6539\u7528\u4E0A\u8FF0 provider \u4E4B\u4E00\uFF08\u5168\u540D provider/model\uFF09\uFF0C\u6216\u5728 ZCode \u684C\u9762\u7AEF\u914D\u7F6E\u8BE5 provider \u540E\u91CD\u8BD5\uFF1B\u7701\u7565 model \u53C2\u6570\u5219\u8D70\u5F15\u64CE\u7F3A\u7701\u89E3\u6790\uFF08CLI \u81EA\u8EAB\u9009\u53D6\u53EF\u7528\u6A21\u578B\uFF09\u3002`
632
+ `\u672A\u77E5 provider "${provider}"\uFF08\u5E26\u51ED\u636E\u7684\u4E2A\u4EBA provider: ${known}\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u6539\u7528\u4E0A\u8FF0 provider \u4E4B\u4E00\uFF08\u5168\u540D provider/model\uFF09\uFF0C\u6216\u5728 ZCode \u684C\u9762\u7AEF\u914D\u7F6E\u8BE5 provider \u540E\u91CD\u8BD5\uFF1B\u7701\u7565 model \u53C2\u6570\u5219\u8D70\u5F15\u64CE\u7F3A\u7701\u89E3\u6790\uFF08CLI \u81EA\u8EAB\u9009\u53D6\u53EF\u7528\u6A21\u578B\uFF09\u3002` + TAIJI_SYNC_GUIDANCE
553
633
  );
554
634
  }
555
635
  if (!hasApiKey(entry)) {
556
636
  throw new ZcodePrepareError(
557
637
  "engine_credential_missing",
558
- `provider "${provider}" \u5B58\u5728\u4F46\u672A\u914D\u7F6E apiKey\u3002\u6062\u590D\u6307\u5F15\uFF1A\u5728 ZCode \u684C\u9762\u7AEF\u4E3A\u8BE5 provider \u914D\u7F6E\u51ED\u636E\uFF0C\u6216\u6539\u7528\u5DF2\u914D\u51ED\u636E\u7684 provider\uFF08${withKey.map(([id]) => id).join(", ")}\uFF09\u540E\u91CD\u8BD5\u3002`
638
+ `provider "${provider}" \u5B58\u5728\u4F46\u672A\u914D\u7F6E apiKey\u3002\u6062\u590D\u6307\u5F15\uFF1A\u5728 ZCode \u684C\u9762\u7AEF\u4E3A\u8BE5 provider \u914D\u7F6E\u51ED\u636E\uFF0C\u6216\u6539\u7528\u5DF2\u914D\u51ED\u636E\u7684 provider\uFF08${withKey.map(([id]) => id).join(", ")}\uFF09\u540E\u91CD\u8BD5\u3002` + TAIJI_SYNC_GUIDANCE
559
639
  );
560
640
  }
561
641
  const models = entry.models ?? [];
562
642
  if (models.length > 0 && !models.includes(short)) {
563
643
  throw new ZcodePrepareError(
564
644
  "model_not_available",
565
- `\u672A\u77E5\u6A21\u578B "${short}"\uFF08provider ${provider} \u4E0B\u53EF\u7528: ${models.join(", ")}\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u6539\u7528\u8BE5 provider \u4E0B\u7684\u6A21\u578B\uFF0C\u6216\u5728 ZCode \u684C\u9762\u7AEF\u542F\u7528\u76EE\u6807\u6A21\u578B\u540E\u91CD\u8BD5\u3002`
645
+ `\u672A\u77E5\u6A21\u578B "${short}"\uFF08provider ${provider} \u4E0B\u53EF\u7528: ${models.join(", ")}\uFF09\u3002\u6062\u590D\u6307\u5F15\uFF1A\u6539\u7528\u8BE5 provider \u4E0B\u7684\u6A21\u578B\uFF0C\u6216\u5728 ZCode \u684C\u9762\u7AEF\u542F\u7528\u76EE\u6807\u6A21\u578B\u540E\u91CD\u8BD5\u3002` + TAIJI_SYNC_GUIDANCE
566
646
  );
567
647
  }
568
648
  return `${provider}/${short}`;
569
649
  }
570
650
  function listZcodeModels(sources) {
571
- const conf = readSourceConfig(sources?.personalProviderConfigPath ?? defaultPersonalProviderConfigPath());
651
+ const { conf } = readPersonalProviderView(sources);
572
652
  const out = [];
573
653
  for (const [pid, entry] of conf.providers) {
574
654
  if (!hasApiKey(entry)) continue;
@@ -600,6 +680,12 @@ const os = require('node:os');
600
680
 
601
681
  const CLI_PATH = process.env.ZCODE_ENG_CLI_PATH;
602
682
  const V2_PATH = process.env.ZCODE_ENG_V2_CONFIG || path.join(os.homedir(), '.zcode', 'v2', 'config.json');
683
+ // \u771F\u5B9E provider_config.json\uFF08Registry \u76F4\u8BFB\u8DEF\u5F84\uFF0CA0 \u63A2\u9488\u5B9E\u8BC1\uFF09\u2014\u2014\u4E0E CONFIG_PATH \u540C\u6B3E
684
+ // homedir \u63A8\u5BFC\u5F62\u6001\uFF0C\u4E0D\u53D7 env \u91CD\u5B9A\u5411\uFF08merge \u5206\u652F\u8981\u8BFB\u7684\u6B63\u662F\u8FD9\u4E2A\u771F\u5B9E\u8DEF\u5F84\u672C\u8EAB\uFF09
685
+ const REAL_PROVIDER_CONFIG_PATH = path.join(os.homedir(), '.zcode', 'v2', 'provider_config.json');
686
+ // taiji \u540C\u6B65\u526F\u672C\u8DEF\u5F84\uFF08ZCODE_ENG_PROVIDER_CONFIG \u8BED\u4E49\u6536\u7A84\uFF1B'' = \u672A\u8BBE \u2192 merge \u5206\u652F\u4E0D\u6FC0\u6D3B\uFF09
687
+ const PI_SYNCED_PATH = process.env.ZCODE_ENG_PROVIDER_CONFIG || '';
688
+ const PROVIDER_CONFIG_PATH = process.env.ZCODE_ENG_PROVIDER_CONFIG || REAL_PROVIDER_CONFIG_PATH;
603
689
  const CONFIG_PATH = path.join(os.homedir(), '.zcode', 'cli', 'config.json');
604
690
 
605
691
  // \u5408\u5E76\u5F62\u6001\uFF1A\u771F\u5B9E cli config \u539F\u6837\uFF08model/plugins/mcp/subagents \u7B49 worker \u7EE7\u627F\u9762\u4E0D\u53D8\uFF09
@@ -608,6 +694,10 @@ const CONFIG_PATH = path.join(os.homedir(), '.zcode', 'cli', 'config.json');
608
694
  // \u914D\u7F6E\uFF082026-08-25 \u4E8B\u6545\uFF1A\u6B8B\u7559\u65E7 key \u538B\u8FC7\u65B0\u51ED\u636E\u81F4 turn 0 401\uFF09\uFF0C\u6545\u4E0D\u53D6 real \u4F18\u5148\u3002
609
695
  // model.main \u7F3A\u5931\u65F6\u53EA\u4ECE v2 \u900F\u4F20\uFF08v2 \u4E5F\u6CA1\u6709\u5219\u4E0D\u4F2A\u9020\u2014\u2014CLI \u7F3A\u7701\u89E3\u6790\u81EA\u4F1A\u843D\u5230\u6CE8\u518C\u8868
610
696
  // \u53EF\u7528\u6A21\u578B\uFF1Bplan \u5BB6\u65CF id \u5DF2\u4E0D\u53EF\u7528\uFF0C\u4E0D\u518D\u515C\u5E95\u4F2A\u9020\uFF09\u3002
697
+ // [D3 \u987A\u5E26\u53D1\u73B0 6] provider_config.json\uFF08\u5F15\u64CE\u4FA7\u6A21\u578B\u6821\u9A8C\u6E90\uFF09\u8865\u6CE8\u5165\uFF1A\u4EC5\u5B58\u5728\u4E8E
698
+ // providerRules \u7684\u4E2A\u4EBA provider\uFF08v2 \u5B57\u5178\u672A\u7269\u5316\uFF09\u4E0D\u5728\u6CE8\u5165\u96C6 \u2192 app-server \u6CE8\u518C\u8868
699
+ // \u7F3A\u6761\u76EE\uFF0C\u663E\u5F0F\u6A21\u578B\u9996\u6D3E\u5373\u62A5\u300CProvider Registry \u4E2D\u4E0D\u5B58\u5728 Model\u300D\u3002\u4E3A\u5E26 apiKey+baseUrl
700
+ // \u4E14 v2/real \u5747\u7F3A\u5931\u7684 rule \u5408\u6210 cli-config \u6761\u76EE\uFF08\u5F62\u72B6\u7167\u6284\u73B0\u7F51\u4E2A\u4EBA\u6761\u76EE\uFF09\u3002
611
701
  // \u8FD4\u56DE null = \u65E0\u53EF\u6CE8\u5165\u51ED\u636E\uFF08v2 \u65E0 provider \u6761\u76EE\uFF09\u2014\u2014\u4E0D patch\uFF0C\u8BA9\u539F\u751F\u62A5\u9519\u900F\u51FA\u3002
612
702
  // \u8BFB\u6E90\u7ECF readOrig \u95F4\u63A5\u53D6\uFF1Apatch \u524D\u9996\u8C03 = fs.readFileSync \u672C\u4F53\uFF1Bpatch \u540E = \u539F\u51FD\u6570
613
703
  // \uFF08\u9632 patch \u540E\u7684\u9012\u5F52\u81EA\u8C03\u7528\uFF09\u3002
@@ -625,6 +715,7 @@ function injectedConfigText() {
625
715
  }
626
716
  return null;
627
717
  }
718
+ const realProviders = real.provider && typeof real.provider === 'object' ? real.provider : {};
628
719
  const v2Providers = v2.provider && typeof v2.provider === 'object' ? v2.provider : {};
629
720
  const hasKey = (entry) => entry && typeof entry === 'object'
630
721
  && entry.options && typeof entry.options.apiKey === 'string' && entry.options.apiKey !== '';
@@ -632,9 +723,63 @@ function injectedConfigText() {
632
723
  for (const [id, entry] of Object.entries(v2Providers)) {
633
724
  if (hasKey(entry)) injectable[id] = entry;
634
725
  }
726
+ // provider_config.json \u8865\u6CE8\u5165\uFF08\u8BFB\u53D6\u5931\u8D25\u9759\u9ED8\u964D\u7EA7\u2014\u2014\u6821\u9A8C\u6E90\u7F3A\u5E2D\u65F6\u5F15\u64CE\u4FA7\u6821\u9A8C\u540C\u6837\u4E0D\u53EF
727
+ // \u7528\uFF0C\u6CE8\u5165\u9762\u7EF4\u6301 v2-only \u73B0\u72B6\uFF1BproviderRules \u7F3A\u5931\u4E0D\u6784\u6210\u51ED\u636E\u9519\u8BEF\uFF09\u3002
728
+ try {
729
+ const pc = JSON.parse(readOrig(PROVIDER_CONFIG_PATH, 'utf8'));
730
+ const pcCfg = pc && typeof pc.config === 'object' && pc.config ? pc.config : {};
731
+ const pcr = typeof pcCfg.providerConfigRules === 'object' && pcCfg.providerConfigRules ? pcCfg.providerConfigRules : {};
732
+ const rules = Array.isArray(pcr.providerRules) ? pcr.providerRules : [];
733
+ const mcr = typeof pcCfg.modelConfigRules === 'object' && pcCfg.modelConfigRules ? pcCfg.modelConfigRules : {};
734
+ const modelRules = Array.isArray(mcr.providerModelRules) ? mcr.providerModelRules : [];
735
+ const modelMeta = {};
736
+ for (const mr of modelRules) {
737
+ if (!mr || typeof mr !== 'object' || typeof mr.providerId !== 'string' || typeof mr.modelId !== 'string') continue;
738
+ (modelMeta[mr.providerId] = modelMeta[mr.providerId] || []).push(mr);
739
+ }
740
+ for (const rule of rules) {
741
+ if (!rule || typeof rule !== 'object') continue;
742
+ const id = typeof rule.providerId === 'string' ? rule.providerId : '';
743
+ const cfg = rule.config && typeof rule.config === 'object' ? rule.config : {};
744
+ const access = cfg.access && typeof cfg.access === 'object' ? cfg.access : {};
745
+ const api = cfg.api && typeof cfg.api === 'object' ? cfg.api : {};
746
+ // v2/real \u5DF2\u6709 \u2192 \u4E0D\u91CD\u590D\u5408\u6210\uFF08v2 \u6761\u76EE\u66F4\u5168\uFF0C\u6574\u6761\u4F18\u5148\u8BED\u4E49\u4E0D\u53D7\u5F71\u54CD\uFF09
747
+ if (!id || injectable[id] || realProviders[id]) continue;
748
+ // hasKey \u7EAA\u5F8B\u540C\u6B3E\uFF1A\u65E0 apiKey \u4E0D\u5408\u6210\uFF1B\u65E0 baseUrl\uFF08endpoint \u7531\u6A21\u677F\u63D0\u4F9B\u7684\u5F62\u6001\uFF09
749
+ // \u4E0D\u5408\u6210\u2014\u2014\u6B8B\u7F3A\u6761\u76EE\u6BD4\u4E0D\u6CE8\u5165\u66F4\u7CDF\uFF0C\u539F\u751F\u300C\u4E0D\u5B58\u5728 Model\u300D\u62A5\u9519\u7167\u65E7\u900F\u51FA
750
+ if (typeof access.apiKey !== 'string' || access.apiKey === '') continue;
751
+ if (typeof api.baseUrl !== 'string' || api.baseUrl === '') continue;
752
+ const order = Array.isArray(cfg.modelOrder) ? cfg.modelOrder
753
+ : (Array.isArray(cfg.personalModelIds) ? cfg.personalModelIds : []);
754
+ const models = {};
755
+ for (const mr of modelMeta[id] || []) {
756
+ const mrCfg = mr.config && typeof mr.config === 'object' ? mr.config : {};
757
+ if (mrCfg.enabled === false) continue; // \u7981\u7528\u6A21\u578B\u4E0D\u8FDB\u6CE8\u518C\u8868\uFF08GUI \u8BED\u4E49\u4E00\u81F4\uFF09
758
+ const props = mrCfg.properties && typeof mrCfg.properties === 'object' ? mrCfg.properties : {};
759
+ models[mr.modelId] = typeof props.contextWindow === 'number'
760
+ ? { limit: { context: props.contextWindow } }
761
+ : {};
762
+ }
763
+ for (const mid of order) {
764
+ if (typeof mid === 'string' && mid !== '' && !(mid in models)) models[mid] = {};
765
+ }
766
+ // kind \u6309\u6761\u76EE api.type \u6D3E\u751F\uFF08D3\uFF09\uFF1Aanthropic-messages \u2192 anthropic\uFF0C\u5176\u4F59
767
+ // \uFF08openai-chat-completions / api.type \u7F3A\u5E2D / \u672A\u77E5\u503C\uFF09\u2192 openai-compatible
768
+ // \u2014\u2014\u5BF9\u9F50 v2 config.json \u65E2\u6709 kind \u4E24\u503C\u5F62\u6001\uFF1B\u4F5C\u7528\u4E8E\u8865\u6CE8\u5165\u6BB5\u5168\u90E8\u6761\u76EE\uFF08\u542B\u900F\u4F20
769
+ // \u771F\u5B9E\u6761\u76EE\uFF0Canthropic \u534F\u8BAE\u771F\u5B9E\u6761\u76EE\u4ECE\u65E7\u9519\u914D\u503C openai-compatible \u987A\u5E26\u4FEE\u6B63\uFF09\u3002
770
+ // \u672A\u77E5\u534F\u8BAE\u503C\u9519\u914D\u65F6\u8BF7\u6C42\u671F\u534F\u8BAE\u9519\u8BEF\u53EF\u89C1\uFF08\u4E0D\u963B\u585E\u6CE8\u518C\u8868\u88C5\u8F7D\uFF09\uFF0C\u771F\u673A\u6F02\u79FB\u4FE1\u53F7\u660E\u786E
771
+ injectable[id] = {
772
+ name: typeof rule.providerName === 'string' ? rule.providerName : id,
773
+ kind: api.type === 'anthropic-messages' ? 'anthropic' : 'openai-compatible',
774
+ options: { apiKey: access.apiKey, baseURL: api.baseUrl },
775
+ source: 'custom',
776
+ models: models,
777
+ };
778
+ }
779
+ } catch { /* provider_config \u7F3A\u5931/\u635F\u574F \u2192 \u8865\u6CE8\u5165\u9762\u9759\u9ED8\u964D\u7EA7\uFF08v2 \u6CE8\u5165\u4E0D\u53D7\u5F71\u54CD\uFF09 */ }
635
780
  if (Object.keys(injectable).length === 0) return null;
636
781
  const merged = { ...real };
637
- merged.provider = { ...(real.provider && typeof real.provider === 'object' ? real.provider : {}), ...injectable };
782
+ merged.provider = { ...realProviders, ...injectable };
638
783
  if ((!merged.model || typeof merged.model !== 'object' || !merged.model.main)
639
784
  && v2.model && typeof v2.model === 'object' && v2.model.main) {
640
785
  merged.model = { ...(merged.model || {}), main: v2.model.main };
@@ -642,6 +787,77 @@ function injectedConfigText() {
642
787
  return JSON.stringify(merged);
643
788
  }
644
789
 
790
+ // [merge-fn-start]\uFF08\u5BF9\u8D26\u5355\u6D4B\u63D0\u53D6\u951A\u2014\u2014appserver-launcher.test.ts \u6309\u6807\u8BB0\u622A\u53D6\u672C\u51FD\u6570
791
+ // eval \u6267\u884C\uFF1Bpreparer.ts mergePersonalProviderConfig \u4E0E\u672C\u51FD\u6570\u540C\u89C4\u5219\u9501\u6B65\uFF0C\u6539\u4EFB\u4E00\u4FA7
792
+ // \u987B\u540C\u6B65\u53E6\u4E00\u4FA7\u5E76\u8DD1\u5BF9\u8D26\u5355\u6D4B\uFF09
793
+ // merge(\u771F\u5B9E, \u526F\u672C)\u2014\u2014D3 \u6362\u8F68\u7248\u89C4\u5219\uFF1A
794
+ // - \u526F\u672C\u975E\u5BF9\u8C61 \u2192 \u539F\u6837\u8FD4\u56DE real\uFF08\u526F\u672C\u7F3A\u5E2D\u56DE\u843D\u8BED\u4E49\uFF1Bwrapper \u4FA7\u66F4\u65E9\u77ED\u8DEF\uFF0C\u9632\u5FA1\u5F62\u6001\uFF09
795
+ // - \u771F\u5B9E\u975E\u5BF9\u8C61\uFF08ENOENT/\u635F\u574F\uFF09\u2192 \u539F\u6837\u8FD4\u56DE copy\uFF08\u526F\u672C\u72EC\u6709\u6761\u76EE\u4E3A\u5168\u96C6\uFF1AproviderRules
796
+ // \u4EC5\u526F\u672C\u6761\u76EE + order \u4EC5\u526F\u672C\u5E8F\uFF0C\u9876\u5C42\u5B57\u6BB5\u968F\u526F\u672C\u4FDD\u6301\u5F62\u6001\u5B8C\u6574\uFF09
797
+ // - providerRules\uFF1A\u771F\u5B9E\u6761\u76EE\u6574\u6761\u4FDD\u7559\u5728\u524D\uFF0C\u526F\u672C\u72EC\u6709 id \u8FFD\u52A0\uFF08\u771F\u5B9E\u4F18\u5148 = \u540C\u540D\u8BA9\u4F4D
798
+ // \u7684\u8FD0\u884C\u65F6\u7B2C\u4E8C\u9053\u9632\u7EBF\uFF09\uFF1B\u65E0\u6709\u6548 providerId \u7684\u526F\u672C\u6761\u76EE\u4E22\u5F03\uFF0C\u771F\u5B9E\u6761\u76EE\u539F\u6837\u4FDD\u7559
799
+ // - providerOrder\uFF1A\u771F\u5B9E\u73B0\u503C\u5E8F + \u526F\u672C\u72EC\u6709 id \u8FFD\u52A0\uFF08\u53BB\u91CD\u4FDD\u5E8F\uFF09
800
+ // - \u5176\u4F59\u5B57\u6BB5\uFF08\u542B config \u5185\u5176\u4ED6\u952E\uFF09\u53D6\u771F\u5B9E\u73B0\u503C
801
+ function mergeProviderConfig(realObj, copyObj) {
802
+ const isRec = (v) => v && typeof v === 'object' && !Array.isArray(v);
803
+ if (!isRec(copyObj)) return realObj;
804
+ if (!isRec(realObj)) return copyObj;
805
+ const cfgOf = (obj) => (isRec(obj.config) ? obj.config : {});
806
+ const rulesOf = (obj) => {
807
+ const pcr = cfgOf(obj).providerConfigRules;
808
+ const r = isRec(pcr) ? pcr.providerRules : undefined;
809
+ return Array.isArray(r) ? r : [];
810
+ };
811
+ const strArr = (v) => (Array.isArray(v) ? v.filter((s) => typeof s === 'string' && s.trim() !== '') : []);
812
+ const mergedRules = [];
813
+ const seen = {};
814
+ for (const rule of rulesOf(realObj)) {
815
+ mergedRules.push(rule);
816
+ if (isRec(rule) && typeof rule.providerId === 'string' && rule.providerId !== '') seen[rule.providerId] = true;
817
+ }
818
+ for (const rule of rulesOf(copyObj)) {
819
+ if (!isRec(rule) || typeof rule.providerId !== 'string' || rule.providerId === '') continue;
820
+ if (seen[rule.providerId]) continue;
821
+ seen[rule.providerId] = true;
822
+ mergedRules.push(rule);
823
+ }
824
+ const mergedOrder = [];
825
+ for (const id of strArr(cfgOf(realObj).providerOrder).concat(strArr(cfgOf(copyObj).providerOrder))) {
826
+ if (mergedOrder.indexOf(id) === -1) mergedOrder.push(id);
827
+ }
828
+ const outCfg = Object.assign({}, cfgOf(realObj), {
829
+ providerOrder: mergedOrder,
830
+ providerConfigRules: Object.assign(
831
+ {},
832
+ isRec(cfgOf(realObj).providerConfigRules) ? cfgOf(realObj).providerConfigRules : {},
833
+ { providerRules: mergedRules },
834
+ ),
835
+ });
836
+ return Object.assign({}, realObj, { config: outCfg });
837
+ }
838
+ // [merge-fn-end]
839
+
840
+ // D3 \u6362\u8F68\u88C5\u8F7D\u901A\u9053\uFF1A\u547D\u4E2D\u771F\u5B9E provider_config.json \u7684\u8BFB\u53D6\u91CD\u5B9A\u5411\u4E3A merge(\u771F\u5B9E\u5B9E\u65F6
841
+ // \u5185\u5BB9, taiji \u540C\u6B65\u526F\u672C)\u2014\u2014\u6BCF\u6B21\u8BFB\u53D6\u91CD\u7B97\uFF08\u4E8B\u5B9E\u9A71\u52A8\uFF0C\u65E0\u7F13\u5B58/\u65F6\u95F4\u7A97\uFF0CZCode \u7AEF\u53D8\u66F4
842
+ // \u5B9E\u65F6\u751F\u6548\uFF09\u3002\u526F\u672C\u7F3A\u5931/\u4E0D\u53EF\u8BFB \u2192 null = \u8C03\u7528\u65B9\u77ED\u8DEF\u56DE\u539F\u6587\u4EF6\uFF08\u56DE\u843D\u771F\u5B9E\u6587\u4EF6\u8BED\u4E49\uFF0C
843
+ // \u96F6\u884C\u4E3A\u53D8\u5316\uFF09\uFF1Benv \u672A\u8BBE \u2192 null\uFF08\u5206\u652F\u4E0D\u6FC0\u6D3B\uFF09\u3002\u8BFB\u53D6\u4E00\u5F8B\u7ECF readOrig \u95F4\u63A5\u53D6\uFF08\u9632
844
+ // patch \u9762\u9012\u5F52\u81EA\u8C03\u7528\uFF09\u3002
845
+ function mergedProviderConfigText() {
846
+ if (!PI_SYNCED_PATH) return null;
847
+ const readOrig = fs.__origReadFileSync || fs.readFileSync;
848
+ let copyObj;
849
+ try {
850
+ copyObj = JSON.parse(readOrig(PI_SYNCED_PATH, 'utf8'));
851
+ } catch { return null; }
852
+ if (!copyObj || typeof copyObj !== 'object' || Array.isArray(copyObj)) return null;
853
+ let realObj = null;
854
+ try {
855
+ const parsed = JSON.parse(readOrig(REAL_PROVIDER_CONFIG_PATH, 'utf8'));
856
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) realObj = parsed;
857
+ } catch { /* ENOENT/\u635F\u574F \u2192 \u526F\u672C\u72EC\u6709\u6761\u76EE\u4E3A\u5168\u96C6 */ }
858
+ return JSON.stringify(mergeProviderConfig(realObj, copyObj));
859
+ }
860
+
645
861
  if (!CLI_PATH) {
646
862
  process.stderr.write('[zcode-launcher] ZCODE_ENG_CLI_PATH \u672A\u8BBE\u7F6E\uFF0C\u65E0\u6CD5\u542F\u52A8 app-server\uFF08\u8BE5 env \u7531\u5F15\u64CE\u81EA\u52A8\u8BBE\u7F6E\uFF1B\u624B\u5DE5\u8FD0\u884C wrapper \u9700\u663E\u5F0F\u4F20\u5165\uFF09\\n');
647
863
  process.exit(2);
@@ -665,6 +881,11 @@ if (text !== null) {
665
881
  const cur = injectedConfigText();
666
882
  if (cur !== null) return shape(rest, cur);
667
883
  }
884
+ // D3 \u6362\u8F68\uFF1ARegistry \u76F4\u8BFB\u771F\u5B9E provider_config.json \u2192 merge(\u771F\u5B9E, \u526F\u672C) \u89C6\u56FE
885
+ if (typeof p === 'string' && p === REAL_PROVIDER_CONFIG_PATH) {
886
+ const cur = mergedProviderConfigText();
887
+ if (cur !== null) return shape(rest, cur);
888
+ }
668
889
  return fs.__origReadFileSync.call(fs, p, ...rest);
669
890
  };
670
891
  fs.promises.__origReadFile = fs.promises.readFile;
@@ -673,17 +894,27 @@ if (text !== null) {
673
894
  const cur = injectedConfigText();
674
895
  if (cur !== null) return Promise.resolve(shape(rest, cur));
675
896
  }
897
+ if (typeof p === 'string' && p === REAL_PROVIDER_CONFIG_PATH) {
898
+ const cur = mergedProviderConfigText();
899
+ if (cur !== null) return Promise.resolve(shape(rest, cur));
900
+ }
676
901
  return fs.promises.__origReadFile.call(fs.promises, p, ...rest);
677
902
  };
678
903
  // existsSync \u62E6\u622A\uFF1A\u771F\u5B9E bundle \u7684\u7528\u6237 config \u8BFB\u53D6\uFF08loadFileConfig\uFF09\u4EE5 existsSync
679
904
  // \u4E3A\u77ED\u8DEF\u95E8\u2014\u2014\u6587\u4EF6\u4E0D\u5B58\u5728\u65F6\u8FD4\u56DE\u7A7A config \u4E14 readFileSync \u4E0D\u88AB\u8C03\u7528\uFF0CGUI-only
680
- // \u767B\u5F55\u5BBF\u4E3B\uFF08cli/config.json \u4ECE\u672A\u88AB CLI \u4FA7\u5199\u8FC7\uFF09\u4E0A\u6CE8\u5165\u53EA\u6302\u5728\u8BFB\u53D6\u9762 = \u4E0D\u53EF\u89C1
905
+ // \u767B\u5F55\u5BBF\u4E3B\uFF08cli/config.json \u4ECE\u672A\u88AB CLI \u4FA7\u5199\u8FC7\uFF09\u4E0A\u6CE8\u5165\u53EA\u6302\u5728\u8BFB\u53D6\u9762 = \u4E0D\u53EF\u89C1\u3002
906
+ // D3 \u6362\u8F68\uFF1A\u771F\u5B9E provider_config.json \u7684\u5B58\u5728\u6027 = \u771F\u5B9E\u6216\u526F\u672C\u4EFB\u4E00\u5B58\u5728\u5373 true
907
+ //\uFF08\u771F\u5B9E\u7F3A\u5931 + \u526F\u672C\u5B58\u5728\u65F6 Registry \u7684 existsSync \u95E8\u4E0D\u5F97\u62E6\u6389 merge \u89C6\u56FE\u8BFB\u53D6\uFF09
681
908
  fs.__origExistsSync = fs.existsSync;
682
909
  fs.existsSync = function (p) {
683
910
  if (typeof p === 'string' && p === CONFIG_PATH) {
684
911
  const cur = injectedConfigText();
685
912
  if (cur !== null) return true;
686
913
  }
914
+ if (typeof p === 'string' && p === REAL_PROVIDER_CONFIG_PATH && PI_SYNCED_PATH) {
915
+ if (fs.__origExistsSync.call(fs, REAL_PROVIDER_CONFIG_PATH)) return true;
916
+ return fs.__origExistsSync.call(fs, PI_SYNCED_PATH);
917
+ }
687
918
  return fs.__origExistsSync.call(fs, p);
688
919
  };
689
920
  }
@@ -1973,7 +2204,10 @@ var ZcodeEngine = class {
1973
2204
  // --mode build/edit/plan/yolo 原生权限档位
1974
2205
  permissionMode: "native",
1975
2206
  // [D3-④] 无 turn_end 语义,轮数上限不可兑现(预检 gate 据此同步拒绝)
1976
- maxTurns: false
2207
+ maxTurns: false,
2208
+ // [subagent-model-switch] zcode 引擎不在模型切换设计范围(协议面引擎中立预留,
2209
+ // 后续另立项接入)——setModel 通路未接通,unsupported 如实声明
2210
+ setModel: "unsupported"
1977
2211
  };
1978
2212
  }
1979
2213
  /** 探针(D7):二进制存在 + 版本解析(zcode 无公开契约,版本漂移的入口信号)。 */
@@ -2290,6 +2524,7 @@ var ZcodeEngine = class {
2290
2524
  const env = buildAppServerEnv(this.deps.processEnv ?? process.env);
2291
2525
  env.ZCODE_ENG_CLI_PATH = cliPath;
2292
2526
  env.ZCODE_ENG_V2_CONFIG = this.deps.sources?.v2ConfigPath ?? defaultV2ConfigPath();
2527
+ env.ZCODE_ENG_PROVIDER_CONFIG = resolvePersonalProviderConfigPath(this.deps.sources, engineDataDir);
2293
2528
  const sessionDbPath = zcodeSessionDbPath(engineDataDir);
2294
2529
  env.ZCODE_SESSION_DB_PATH = sessionDbPath;
2295
2530
  delete env.ZCODE_SESSION_DB;
@@ -2823,9 +3058,9 @@ async function defaultProbeVersion(cliPath, dataDir) {
2823
3058
  }
2824
3059
 
2825
3060
  // src/registration.ts
2826
- import { resolveEngineDataDir } from "@zhushanwen/subagent-engine-sdk";
3061
+ import { resolveEngineDataDir as resolveEngineDataDir2 } from "@zhushanwen/subagent-engine-sdk";
2827
3062
  function defaultEngineDataDir() {
2828
- return resolveEngineDataDir(process.env);
3063
+ return resolveEngineDataDir2(process.env);
2829
3064
  }
2830
3065
  function createZcodeEngine(deps) {
2831
3066
  return new ZcodeEngine(deps);