@zhushanwen/zcode-subagent-cli 0.8.2 → 0.8.4

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/index.cjs CHANGED
@@ -74,6 +74,7 @@ function parseZcodePositiveMsEnv(raw) {
74
74
  var ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING = -32603;
75
75
  var ZCODE_APPSERVER_ERR_BUSY_SESSION = -32010;
76
76
  var ZCODE_APPSERVER_HARVEST_GRACE_MS = 1e3;
77
+ var ZCODE_APPSERVER_HARVEST_GRACE_ENV = "TAIJI_ZCODE_TEST_HARVEST_GRACE_MS";
77
78
  function isFailedTerminalStatus(status) {
78
79
  return status === "failed" || status === "error";
79
80
  }
@@ -780,11 +781,20 @@ function patchedCliSource(path) {
780
781
  }
781
782
  }
782
783
  }
783
- // \u951A\u70B9 B\uFF08\u56DE\u9000\uFF09\uFF1A\u8C03\u7528\u70B9\u6CE8\u5165 create:r(()=><fn>( \u2192 (<arg>,{standalone:{}})
784
+ // \u951A\u70B9 B\uFF08\u56DE\u9000\uFF09\uFF1A\u8C03\u7528\u70B9\u6CE8\u5165 create:r(()=><fn>( \u2192 (<arg>,{standalone:{}})\u3002
785
+ // \u7ED3\u6784\u6821\u9A8C\u4E0E\u5B9A\u4E49\u951A\u540C\u53E3\u5F84\uFF082026-10 review\xB7N-1\uFF09\uFF1A\u5168\u6587\u4EF6\u51FA\u73B0\u6B21\u6570\u5FC5\u987B\u6070\u597D 1
786
+ // \u2014\u2014\u66FE\u53D6\u9996\u4E2A\u6587\u672C\u547D\u4E2D\uFF0C\u672A\u6765\u7248\u672C\u51FA\u73B0\u4E24\u5904\u540C\u6B3E\u8C03\u7528\u65F6\u6CE8\u5165\u843D\u5728\u65E0\u5173\u8C03\u7528\u4E0A\u3001
787
+ // standaloneApplied \u4ECD\u7F6E true = \u771F\u5B9E\u534F\u8BAE\u8DEF\u5F84 host \u6A21\u5F0F\u800C\u95E8\u62A5 armed \u7684\u5047\u6210\u529F\u3002
788
+ // count > 1 = \u65E0\u6CD5\u786E\u5B9A\u534F\u8BAE\u8DEF\u5F84\u8C03\u7528\u70B9 \u2192 \u653E\u5F03\u672C\u951A\uFF08not armed\uFF09\u3002
784
789
  if (!standaloneApplied) {
785
790
  const callAnchor = 'create:r(()=>' + fnName + '(';
786
- const callIdx = src.indexOf(callAnchor);
787
- if (callIdx >= 0) {
791
+ let callCount = 0;
792
+ let callIdx = -1;
793
+ for (let scan = src.indexOf(callAnchor); scan >= 0; scan = src.indexOf(callAnchor, scan + 1)) {
794
+ callCount++;
795
+ callIdx = scan;
796
+ }
797
+ if (callCount === 1) {
788
798
  const argStart = callIdx + callAnchor.length;
789
799
  // \u62EC\u53F7\u914D\u5E73\u627E\u4E0E fnName( \u914D\u5BF9\u7684\u95ED\u62EC\u53F7\uFF08\u626B\u63CF\u5668\u8DF3\u8FC7\u5B57\u9762\u91CF/\u6A21\u677F/\u6CE8\u91CA\u2014\u2014\u5B9E\u53C2
790
800
  // \u542B\u5185\u8054\u4E32\u6216\u5185\u5C42\u8C03\u7528\u65F6\u9996\u4E2A ')' \u843D\u5728\u9519\u8BEF\u5C42\u7EA7 = \u6CE8\u5165\u9519\u4F4D\u7684\u5047\u6210\u529F\uFF09
@@ -877,7 +887,9 @@ var ACCOUNT_METADATA_SECTION = `
877
887
  // \u5F15\u64CE\u4FA7\u65E2\u6709\u6587\u6848\u627F\u8F7D\uFF09\u3002catches\uFF1A\u6587\u4EF6\u7F3A\u5931/\u574F JSON/\u76EE\u5F55\u4E0D\u53EF\u5199\u3002
878
888
  // \u5E76\u53D1\u8FB9\u754C\uFF08ADR-0143 \u767B\u8BB0\uFF09\uFF1A\u5BF9\u4E0E GUI \u5171\u4EAB\u7684\u51ED\u636E\u5E93\u505A\u65E0\u9501 read-modify-write \u5168\u91CF
879
889
  // \u5199\u56DE\uFF0C\u4E0E GUI token \u5237\u65B0\u5E76\u53D1\u5B58\u5728\u6BEB\u79D2\u7EA7\u4E22\u66F4\u65B0\u7A97\u53E3\u2014\u2014\u540E\u679C\u81EA\u6108\uFF08GUI \u4FA7\u91CD\u8BD5\u5237\u65B0 /
880
- // \u672C\u4FA7\u7F3A\u952E\u4E0B\u6B21 spawn \u5E42\u7B49\u8865\u9F50\uFF09\uFF0C\u4E8B\u5B9E\u9A71\u52A8\u539F\u5219\u4E0B\u4E0D\u52A0\u9501\u3001\u4E0D\u505A\u65F6\u95F4\u8865\u507F\u3002
890
+ // \u672C\u4FA7\u7F3A\u952E\u4E0B\u6B21 spawn \u5E42\u7B49\u8865\u9F50\uFF09\uFF0C\u4E8B\u5B9E\u9A71\u52A8\u539F\u5219\u4E0B\u4E0D\u52A0\u9501\u3001\u4E0D\u505A\u65F6\u95F4\u8865\u507F\u3002\u5143\u6570\u636E\u952E
891
+ // \u9762\u53E6\u52A0 compare-and-skip \u9632\u7EBF\uFF082026-10 review\xB7N-3\uFF09\uFF1A\u5199\u56DE\u524D\u91CD\u8BFB\u78C1\u76D8\u6BD4\u5BF9
892
+ // identity/auth-source \u4E24\u952E\uFF0C\u68C0\u6D4B\u5230\u5E76\u53D1\u5199\u5165\u5373\u653E\u5F03\u672C\u6B21\u5199\u5165\uFF08\u89C1\u843D\u5E93\u6BB5\u6CE8\u91CA\uFF09\u3002
881
893
  // \u8C03\u7528\u5951\u7EA6\uFF082026-10 review\xB7S-3\uFF09\uFF1A\u4EC5\u5728\u5B89\u88C5\u95E8\u786E\u8BA4 standalone \u4E0E header-port \u4E24\u951A
882
894
  // \u5747\u5DF2\u6B66\u88C5\u65F6\u8C03\u7528\u2014\u2014\u8FD9\u4E9B\u5143\u6570\u636E\u952E\u53EA\u88AB standalone \u8D26\u53F7\u6E90\u6D88\u8D39\uFF0C\u4E14\u8D26\u53F7\u94FE\u8981\u6210\u7ACB\u8FD8\u8981\u6C42
883
895
  // \u8BA4\u8BC1\u5934\u8D70\u672C\u5730\u7AEF\u53E3\uFF1B\u4EFB\u4E00\u951A\u6F02\u79FB\uFF08\u7248\u672C\u4E0D\u652F\u6301\uFF09\u65F6\u5199\u5165\u7528\u6237\u5171\u4EAB credentials.json \u65E0
@@ -913,9 +925,15 @@ function ensureAccountMetadata() {
913
925
  }
914
926
  const added = [];
915
927
  const reconciled = [];
928
+ // compare-and-skip \u57FA\u7EBF\uFF082026-10 review\xB7N-3\uFF09\uFF1A\u5168\u90E8 plan \u4E24\u952E\u7684\u8BFB\u65F6\u539F\u59CB\u503C\uFF08\u9996\u89C1
929
+ // \u6355\u83B7\u2014\u2014\u540E\u7EED\u6539\u5199 creds \u4E0D\u5F71\u54CD\u57FA\u7EBF\uFF09\u3002\u5199\u56DE\u524D\u91CD\u8BFB\u78C1\u76D8\u9010\u952E\u6BD4\u5BF9\uFF0C\u68C0\u6D4B\u300C\u5FEB\u7167\u8BFB\u53D6
930
+ // \u2192\u5199\u56DE\u300D\u7A97\u53E3\u5185\u7684 GUI/\u767B\u5F55\u5E76\u53D1\u5199\u5165\uFF0C\u9632\u9648\u65E7\u5FEB\u7167\u8986\u76D6\u6743\u5A01\u65B0\u503C\u3002
931
+ const metaBaseline = Object.create(null);
916
932
  for (const pid of planIds) {
917
933
  const idKey = 'account-provider:' + pid + ':identity';
918
934
  const srcKey = 'account-provider:' + pid + ':auth-source';
935
+ if (metaBaseline[idKey] === undefined) metaBaseline[idKey] = creds[idKey];
936
+ if (metaBaseline[srcKey] === undefined) metaBaseline[srcKey] = creds[srcKey];
919
937
  // host \u6001 plan api-key \u952E\u540D\u6A21\u677F\uFF1Aaccount-provider:coding-plan:<pid>:account:<identity>:api-key
920
938
  const apiKeyPrefix = 'account-provider:coding-plan:' + pid + ':account:';
921
939
  const apiKeySuffix = ':api-key';
@@ -925,13 +943,47 @@ function ensureAccountMetadata() {
925
943
  };
926
944
  let identity = typeof creds[idKey] === 'string' ? creds[idKey] : '';
927
945
  const authSource = typeof creds[srcKey] === 'string' ? creds[srcKey] : '';
928
- // \u6D3E\u751F\u8EAB\u4EFD\u6BCF\u6B21 spawn \u91CD\u7B97\uFF08\u4E0D\u8BFB\u65E2\u6709\u952E\uFF09\uFF1A\u5F53\u524D host \u6001\u952E\u540D\u9996\u547D\u4E2D\uFF08\u4FDD\u6301\u65E2\u6709\u4F18\u5148\u7EA7\uFF09\u3002
946
+ // \u6D3E\u751F\u8EAB\u4EFD\u6BCF\u6B21 spawn \u91CD\u7B97\uFF08\u4E0D\u8BFB\u65E2\u6709\u952E\uFF09\u3002\u591A plan api-key\uFF08\u65E7+\u65B0 account / \u8F6E\u6362
947
+ // \u6B8B\u7559\uFF09\u65F6\u300CObject.keys \u9996\u4E2A\u547D\u4E2D\u300D\u4F1A\u6C38\u4E45\u6307\u5411\u9648\u65E7 account \u4E14 hasPlanApiKey \u6052\u771F\u3001
948
+ // \u6C38\u4E0D\u81EA\u6108\uFF082026-10 review\xB7DG-R2-01\uFF09\u2014\u2014\u6D3E\u751F\u4F18\u5148\u7EA7\uFF1A
949
+ // \u2460 project-key-location:v1:[origin,family,identity,...] \u952E\u5185\u5D4C identity
950
+ // \uFF08ZCode \u767B\u5F55\u5199\u5165\u7684\u786E\u5B9A\u6027\u4FE1\u53F7\uFF1B\u952E\u540D\u5C3E\u6BB5 URL \u7F16\u7801 JSON \u6570\u7EC4\uFF0C\u7B2C 2/3 \u5143\u7D20
951
+ // = family/identity\uFF0Cfamily \u4E0E pid \u524D\u7F00\u5339\u914D\u540C oauth \u5BB6\u65CF\u5224\u636E\uFF09\u5168\u4F53\u53BB\u91CD\u540E
952
+ // \u552F\u4E00\u4E14\u8BE5 identity \u540D\u4E0B\u786E\u6709 plan api-key \u2192 \u7528\u5B83\uFF1B
953
+ // \u2461 \u65E0\u2460\u4FE1\u53F7\u65F6\uFF0C\u8BE5 pid \u540D\u4E0B plan api-key \u952E\u7684 identity \u53BB\u91CD\u540E\u552F\u4E00 \u2192 \u7528\u5B83
954
+ // \uFF08\u5355 account \u5E38\u6001\uFF09\uFF1B
955
+ // \u2462 \u591A\u5019\u9009\u4E14\u65E0\u2460\u786E\u5B9A\u6027\u4FE1\u53F7 \u2192 \u4E0D\u731C\u6D4B\uFF1A\u4E0D\u6D3E\u751F\uFF08\u4FDD\u7559\u65E2\u6709 stored identity\uFF09\u5E76
956
+ // log \u6B67\u4E49\u2014\u2014\u731C\u6D4B\u6539\u5199\u6BD4\u4FDD\u7559\u9648\u65E7\u503C\u66F4\u7CDF\uFF08\u9648\u65E7\u503C\u81F3\u5C11\u662F\u767B\u5F55\u6D41\u7A0B\u5199\u8FC7\u7684\uFF09\u3002
929
957
  let derivedIdentity = '';
958
+ let ambiguousIdentity = false;
959
+ const keyLocPrefix = 'project-key-location:v1:';
960
+ const keyLocIds = [];
930
961
  for (const k of Object.keys(creds)) {
931
- if (k.indexOf(apiKeyPrefix) === 0 && k.slice(-apiKeySuffix.length) === apiKeySuffix) {
962
+ if (k.indexOf(keyLocPrefix) !== 0) continue;
963
+ try {
964
+ const arr = JSON.parse(decodeURIComponent(k.slice(keyLocPrefix.length)));
965
+ if (Array.isArray(arr) && typeof arr[1] === 'string' && typeof arr[2] === 'string'
966
+ && pid.indexOf('account:' + arr[1]) === 0 && keyLocIds.indexOf(arr[2]) === -1) {
967
+ keyLocIds.push(arr[2]);
968
+ }
969
+ } catch (e2) { /* \u5F62\u6001\u5916\u952E\uFF08\u635F\u574F/\u975E\u6570\u7EC4\uFF09\u4E0D\u6784\u6210\u786E\u5B9A\u6027\u4FE1\u53F7 */ }
970
+ }
971
+ if (keyLocIds.length === 1 && hasPlanApiKey(keyLocIds[0])) {
972
+ derivedIdentity = keyLocIds[0];
973
+ } else {
974
+ const apiKeyIds = [];
975
+ for (const k of Object.keys(creds)) {
976
+ if (k.indexOf(apiKeyPrefix) !== 0 || k.slice(-apiKeySuffix.length) !== apiKeySuffix) continue;
932
977
  const mid = k.slice(apiKeyPrefix.length, k.length - apiKeySuffix.length);
933
- if (mid) { try { derivedIdentity = decodeURIComponent(mid); } catch (e2) { derivedIdentity = mid; } break; }
978
+ if (!mid) continue;
979
+ let id; try { id = decodeURIComponent(mid); } catch (e2) { id = mid; }
980
+ if (apiKeyIds.indexOf(id) === -1) apiKeyIds.push(id);
934
981
  }
982
+ if (apiKeyIds.length === 1) derivedIdentity = apiKeyIds[0];
983
+ else if (apiKeyIds.length > 1) ambiguousIdentity = true;
984
+ }
985
+ if (ambiguousIdentity) {
986
+ process.stderr.write('[zcode-launcher] account metadata: multiple plan api-key identities for ' + pid + ', no deterministic signal - keeping stored identity\\n');
935
987
  }
936
988
  // oauth \u5BB6\u65CF\u51ED\u636E\u5B58\u5728\u6027\uFF1A\u626B oauth:<family>:access_token\uFF0Cprovider id \u4EE5
937
989
  // account:<family> \u5F00\u5934\u5373\u7ED1\u5B9A\u8BE5\u5BB6\u65CF\uFF08\u96F6\u5BB6\u65CF\u540D\u5199\u6B7B\uFF09\u3002
@@ -984,6 +1036,24 @@ function ensureAccountMetadata() {
984
1036
  }
985
1037
  }
986
1038
  if (added.length === 0 && reconciled.length === 0) return;
1039
+ // compare-and-skip\uFF082026-10 review\xB7N-3\uFF09\uFF1A\u65E0\u9501 RMW \u7684\u9648\u65E7\u8986\u76D6\u9632\u7EBF\u2014\u2014\u5199\u56DE\u524D\u91CD\u8BFB
1040
+ // \u78C1\u76D8\uFF0C\u4EFB\u4E00 plan \u4E24\u952E\u5F53\u524D\u503C \u2260 \u8BFB\u65F6\u57FA\u7EBF = \u7A97\u53E3\u5185 GUI/\u767B\u5F55\u5DF2\u5B8C\u6210\u6743\u5A01\u5199\u5165 \u2192 \u653E\u5F03
1041
+ // \u672C\u6B21\u5199\u5165\uFF08log \u8BF4\u660E\uFF0C\u4E0B\u6B21 spawn \u5E42\u7B49\u91CD\u7B97\uFF1B\u653E\u5F03\u540E\u7684\u6062\u590D = \u91CD\u65B0\u767B\u5F55/\u914D\u7F6E\u6216\u5220\u9664
1042
+ // \u8BE5\u952E\uFF0CADR-0143 \u5E76\u53D1\u8FB9\u754C\uFF09\u3002\u91CD\u8BFB/\u89E3\u6790\u5931\u8D25 = \u7A97\u53E3\u4E0D\u53EF\u5224\u5B9A\uFF0C\u540C\u6837\u4FDD\u5B88\u653E\u5F03\u3002
1043
+ try {
1044
+ const reread = JSON.parse(readOrig.call(fs, credPath, 'utf8'));
1045
+ if (!reread || typeof reread !== 'object' || Array.isArray(reread)) {
1046
+ throw new Error('credentials reread is not an object');
1047
+ }
1048
+ for (const mk of Object.keys(metaBaseline)) {
1049
+ if ((reread[mk] || '') !== (metaBaseline[mk] || '')) {
1050
+ throw new Error('concurrent write detected on ' + mk);
1051
+ }
1052
+ }
1053
+ } catch (skipErr) {
1054
+ process.stderr.write('[zcode-launcher] account metadata skipped (' + ((skipErr && skipErr.message) || String(skipErr)) + ') - retry on next spawn\\n');
1055
+ return;
1056
+ }
987
1057
  const tmp = credPath + '.taiji-tmp-' + process.pid;
988
1058
  // rename \u6362 inode\u2014\u2014tmp \u4E0D\u5E26 mode \u843D\u76D8\u4F1A\u628A GUI \u4FA7\u6536\u7D27\u8FC7\u7684\u6587\u4EF6\u6743\u9650\u9759\u9ED8\u653E\u5BBD\u4E3A
989
1059
  // \u7F3A\u7701 umask \u503C\uFF0C\u5199\u524D\u8BFB\u65E2\u6709 mode \u968F\u5199\u4F20\u9012\uFF08stat \u5931\u8D25\u56DE\u9000 0600\uFF09\uFF1B\u5199/rename
@@ -1025,7 +1095,7 @@ const CONFIG_PATH = path.join(os.homedir(), '.zcode', 'cli', 'config.json');
1025
1095
 
1026
1096
  // \u5408\u5E76\u5F62\u6001\uFF1A\u771F\u5B9E cli config \u539F\u6837\uFF08model/plugins/mcp/subagents \u7B49 worker \u7EE7\u627F\u9762\u4E0D\u53D8\uFF09
1027
1097
  // + v2 \u7684 provider \u5B57\u5178\u6CE8\u5165\uFF0C\u540C id \u65F6 v2 \u6761\u76EE\u6574\u6761\u4F18\u5148\u2014\u2014GUI \u76F4\u4F20 modelConfig \u65F6
1028
- // v2 \u5373\u6743\u5A01\u6E90\uFF08\u8FDB\u7A0B\u5916\u7B49\u4EF7\u590D\u523B\uFF09\uFF1Bcli config \u4E0D\u5728 GUI \u7BA1\u7406\u9762\u3001\u53EF\u80FD\u6B8B\u7559\u5386\u53F2\u9A8C\u8BC1
1098
+ // v2 \u5373\u6743\u5A01\u6E90\uFF08\u8FDB\u7A0B\u5916\u7B49\u4EF7\u590D\u523B\uFF09\uFF1Bcli config \u4E0D\u5728 GUI \u7BA1\u7406\u4FA7\u3001\u53EF\u80FD\u6B8B\u7559\u5386\u53F2\u9A8C\u8BC1
1029
1099
  // \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
1030
1100
  // 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
1031
1101
  // \u53EF\u7528\u6A21\u578B\uFF1Bplan \u5BB6\u65CF id \u5DF2\u4E0D\u53EF\u7528\uFF0C\u4E0D\u518D\u515C\u5E95\u4F2A\u9020\uFF09\u3002
@@ -1054,12 +1124,16 @@ function injectedConfigText() {
1054
1124
  const v2Providers = v2.provider && typeof v2.provider === 'object' ? v2.provider : {};
1055
1125
  const hasKey = (entry) => entry && typeof entry === 'object'
1056
1126
  && entry.options && typeof entry.options.apiKey === 'string' && entry.options.apiKey !== '';
1057
- const injectable = {};
1127
+ // \u65E0\u539F\u578B\u5B57\u5178\uFF08DG-R2-02\uFF09\uFF1Aprovider id \u503C\u57DF\u542B '__proto__'/'toString' \u7B49\u5371\u9669\u952E
1128
+ //\uFF08runtime \u4FA7\u6709\u610F\u4EA7\u51FA\uFF0CS-17\uFF09\u2014\u2014\u666E\u901A\u5BF9\u8C61\u4E0A\u5BF9 __proto__ \u952E\u8D4B\u503C\u89E6\u53D1\u539F\u578B setter
1129
+ // \u9759\u9ED8\u4E22\u6761\u76EE\u3001\u8BFB toString \u952E\u843D\u5230\u539F\u578B\u65B9\u6CD5\u8BEF\u5224\u5DF2\u5B58\u5728\u3002Object.create(null) \u4F7F
1130
+ // \u952E\u8BFB\u5199\u5747\u843D\u5728 own property\u3002
1131
+ const injectable = Object.create(null);
1058
1132
  for (const [id, entry] of Object.entries(v2Providers)) {
1059
1133
  if (hasKey(entry)) injectable[id] = entry;
1060
1134
  }
1061
1135
  // 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
1062
- // \u7528\uFF0C\u6CE8\u5165\u9762\u7EF4\u6301 v2-only \u73B0\u72B6\uFF1BproviderRules \u7F3A\u5931\u4E0D\u6784\u6210\u51ED\u636E\u9519\u8BEF\uFF09\u3002
1136
+ // \u7528\uFF0C\u6CE8\u5165\u70B9\u7EF4\u6301 v2-only \u73B0\u72B6\uFF1BproviderRules \u7F3A\u5931\u4E0D\u6784\u6210\u51ED\u636E\u9519\u8BEF\uFF09\u3002
1063
1137
  try {
1064
1138
  const pc = JSON.parse(readOrig(PROVIDER_CONFIG_PATH, 'utf8'));
1065
1139
  const pcCfg = pc && typeof pc.config === 'object' && pc.config ? pc.config : {};
@@ -1067,7 +1141,9 @@ function injectedConfigText() {
1067
1141
  const rules = Array.isArray(pcr.providerRules) ? pcr.providerRules : [];
1068
1142
  const mcr = typeof pcCfg.modelConfigRules === 'object' && pcCfg.modelConfigRules ? pcCfg.modelConfigRules : {};
1069
1143
  const modelRules = Array.isArray(mcr.providerModelRules) ? mcr.providerModelRules : [];
1070
- const modelMeta = {};
1144
+ // \u65E0\u539F\u578B\u5B57\u5178\uFF08DG-R2-02 \u540C\u65CF\uFF09\uFF1Aid \u4E3A 'constructor' \u7B49\u539F\u578B\u65B9\u6CD5\u540D\u65F6\u666E\u901A\u5BF9\u8C61\u8BFB\u53D6
1145
+ // \u843D\u5230\u539F\u578B Function\uFF0Cfor...of \u76F4\u63A5 TypeError \u88AB catch \u9759\u9ED8\u541E\u6389\u8BE5 rule \u7684\u5408\u6210
1146
+ const modelMeta = Object.create(null);
1071
1147
  for (const mr of modelRules) {
1072
1148
  if (!mr || typeof mr !== 'object' || typeof mr.providerId !== 'string' || typeof mr.modelId !== 'string') continue;
1073
1149
  (modelMeta[mr.providerId] = modelMeta[mr.providerId] || []).push(mr);
@@ -1078,8 +1154,10 @@ function injectedConfigText() {
1078
1154
  const cfg = rule.config && typeof rule.config === 'object' ? rule.config : {};
1079
1155
  const access = cfg.access && typeof cfg.access === 'object' ? cfg.access : {};
1080
1156
  const api = cfg.api && typeof cfg.api === 'object' ? cfg.api : {};
1081
- // 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
1082
- if (!id || injectable[id] || realProviders[id]) continue;
1157
+ // 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\u3002
1158
+ // realProviders \u662F JSON.parse \u666E\u901A\u5BF9\u8C61\uFF08\u6709\u539F\u578B\uFF09\u2014\u2014id \u4E3A 'toString' \u7B49
1159
+ // \u539F\u578B\u65B9\u6CD5\u540D\u65F6\u76F4\u63A5\u4E0B\u6807\u8BFB\u53D6\u6052 truthy \u8BEF\u5224 real \u5DF2\u6709\uFF0C\u987B own-property \u68C0\u67E5
1160
+ if (!id || injectable[id] || Object.prototype.hasOwnProperty.call(realProviders, id)) continue;
1083
1161
  // hasKey \u7EAA\u5F8B\u540C\u6B3E\uFF1A\u65E0 apiKey \u4E0D\u5408\u6210\uFF1B\u65E0 baseUrl\uFF08endpoint \u7531\u6A21\u677F\u63D0\u4F9B\u7684\u5F62\u6001\uFF09
1084
1162
  // \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
1085
1163
  if (typeof access.apiKey !== 'string' || access.apiKey === '') continue;
@@ -1111,7 +1189,7 @@ function injectedConfigText() {
1111
1189
  models: models,
1112
1190
  };
1113
1191
  }
1114
- } catch { /* provider_config \u7F3A\u5931/\u635F\u574F \u2192 \u8865\u6CE8\u5165\u9762\u9759\u9ED8\u964D\u7EA7\uFF08v2 \u6CE8\u5165\u4E0D\u53D7\u5F71\u54CD\uFF09 */ }
1192
+ } catch { /* provider_config \u7F3A\u5931/\u635F\u574F \u2192 \u8865\u6CE8\u5165\u70B9\u9759\u9ED8\u964D\u7EA7\uFF08v2 \u6CE8\u5165\u4E0D\u53D7\u5F71\u54CD\uFF09 */ }
1115
1193
  if (Object.keys(injectable).length === 0) return null;
1116
1194
  const merged = { ...real };
1117
1195
  merged.provider = { ...realProviders, ...injectable };
@@ -1125,7 +1203,7 @@ function injectedConfigText() {
1125
1203
  // [merge-fn-start]\uFF08\u5BF9\u8D26\u5355\u6D4B\u63D0\u53D6\u951A\u2014\u2014appserver-launcher.test.ts \u6309\u6807\u8BB0\u622A\u53D6\u672C\u51FD\u6570
1126
1204
  // eval \u6267\u884C\uFF1Bpreparer.ts mergePersonalProviderConfig \u4E0E\u672C\u51FD\u6570\u540C\u89C4\u5219\u9501\u6B65\uFF0C\u6539\u4EFB\u4E00\u4FA7
1127
1205
  // \u987B\u540C\u6B65\u53E6\u4E00\u4FA7\u5E76\u8DD1\u5BF9\u8D26\u5355\u6D4B\uFF09
1128
- // merge(\u771F\u5B9E, \u526F\u672C)\u2014\u2014D3 \u6362\u8F68\u7248\u89C4\u5219\uFF1A
1206
+ // merge(\u771F\u5B9E, \u526F\u672C)\u2014\u2014D3 \u5207\u6362\u7248\u89C4\u5219\uFF1A
1129
1207
  // - \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
1130
1208
  // - \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
1131
1209
  // \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
@@ -1145,7 +1223,10 @@ function mergeProviderConfig(realObj, copyObj) {
1145
1223
  };
1146
1224
  const strArr = (v) => (Array.isArray(v) ? v.filter((s) => typeof s === 'string' && s.trim() !== '') : []);
1147
1225
  const mergedRules = [];
1148
- const seen = {};
1226
+ // \u65E0\u539F\u578B\u5B57\u5178\uFF08DG-R2-02\uFF09\uFF1AproviderId \u4E3A '__proto__'/'toString' \u7B49\u5371\u9669\u952E\u65F6\u666E\u901A\u5BF9\u8C61
1227
+ // \u7684 seen \u8BFB\u5199\u90FD\u4F1A\u649E\u539F\u578B\u94FE\uFF08__proto__ \u8D4B\u503C\u88AB setter \u541E / toString \u8BFB\u5230\u539F\u578B\u65B9\u6CD5
1228
+ // \u8BEF\u5224\u5DF2\u89C1 \u2192 \u526F\u672C\u72EC\u6709\u6761\u76EE\u88AB\u4E22\uFF0C\u4E0E TS \u4FA7 Set \u5B9E\u88C5\u5206\u6B67\uFF09\u3002Object.create(null) \u6D88\u9664\u3002
1229
+ const seen = Object.create(null);
1149
1230
  for (const rule of rulesOf(realObj)) {
1150
1231
  mergedRules.push(rule);
1151
1232
  if (isRec(rule) && typeof rule.providerId === 'string' && rule.providerId !== '') seen[rule.providerId] = true;
@@ -1172,7 +1253,7 @@ function mergeProviderConfig(realObj, copyObj) {
1172
1253
  }
1173
1254
  // [merge-fn-end]
1174
1255
 
1175
- // 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
1256
+ // D3 \u5207\u6362\u88C5\u8F7D\u901A\u9053\uFF1A\u547D\u4E2D\u771F\u5B9E provider_config.json \u7684\u8BFB\u53D6\u91CD\u5B9A\u5411\u4E3A merge(\u771F\u5B9E\u5B9E\u65F6
1176
1257
  // \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
1177
1258
  // \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
1178
1259
  // \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
@@ -1233,7 +1314,7 @@ function catalogPatchText(path) {
1233
1314
 
1234
1315
  ${CLI_PATCH_SECTION}
1235
1316
 
1236
- // \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
1317
+ // \u5185\u5EFA provider \u914D\u7F6E\u5B9A\u4F4D\uFF08\u6F02\u79FB\u98CE\u9669\u2461\uFF09\uFF1A3.12.x \u8D77\u914D\u7F6E\u6587\u4EF6\u632A\u4F4D + CLI \u4F9D\u8D56\u5BBF\u4E3B\u6CE8\u5165
1237
1318
  // ZCODE_BUILTIN_PROVIDER_CONFIG_FILE \u76F4\u63A5\u5B9A\u4F4D\uFF1BZCode \u684C\u9762 spawn \u94FE\u81EA\u5E26\u8BE5\u952E\uFF0C
1238
1319
  // \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
1239
1320
  // <plat>-<arch> \u4F18\u5148\uFF0CRust arch \u53D8\u4F53\u5982 darwin-aarch64 \u5B57\u5178\u5E8F\u8DDF\u540E\uFF09\xD7 \u7248\u672C\u76EE\u5F55
@@ -1354,7 +1435,7 @@ if (text !== null || catalogPatch !== null || cliPatchProbe !== null) {
1354
1435
  const cur = injectedConfigText();
1355
1436
  if (cur !== null) return shape(rest, cur);
1356
1437
  }
1357
- // D3 \u6362\u8F68\uFF1ARegistry \u76F4\u8BFB\u771F\u5B9E provider_config.json \u2192 merge(\u771F\u5B9E, \u526F\u672C) \u89C6\u56FE
1438
+ // D3 \u5207\u6362\uFF1ARegistry \u76F4\u8BFB\u771F\u5B9E provider_config.json \u2192 merge(\u771F\u5B9E, \u526F\u672C) \u89C6\u56FE
1358
1439
  if (typeof p === 'string' && p === REAL_PROVIDER_CONFIG_PATH) {
1359
1440
  const cur = mergedProviderConfigText();
1360
1441
  if (cur !== null) return shape(rest, cur);
@@ -1411,8 +1492,8 @@ if (text !== null || catalogPatch !== null || cliPatchProbe !== null) {
1411
1492
 
1412
1493
  // existsSync \u62E6\u622A\uFF1A\u771F\u5B9E bundle \u7684\u7528\u6237 config \u8BFB\u53D6\uFF08loadFileConfig\uFF09\u4EE5 existsSync
1413
1494
  // \u4E3A\u77ED\u8DEF\u95E8\u2014\u2014\u6587\u4EF6\u4E0D\u5B58\u5728\u65F6\u8FD4\u56DE\u7A7A config \u4E14 readFileSync \u4E0D\u88AB\u8C03\u7528\uFF0CGUI-only
1414
- // \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
1415
- // D3 \u6362\u8F68\uFF1A\u771F\u5B9E provider_config.json \u7684\u5B58\u5728\u6027 = \u771F\u5B9E\u6216\u526F\u672C\u4EFB\u4E00\u5B58\u5728\u5373 true
1495
+ // \u767B\u5F55\u5BBF\u4E3B\uFF08cli/config.json \u4ECE\u672A\u88AB CLI \u4FA7\u5199\u8FC7\uFF09\u4E0A\u6CE8\u5165\u53EA\u6302\u5728\u8BFB\u4FA7 = \u4E0D\u53EF\u89C1\u3002
1496
+ // D3 \u5207\u6362\uFF1A\u771F\u5B9E provider_config.json \u7684\u5B58\u5728\u6027 = \u771F\u5B9E\u6216\u526F\u672C\u4EFB\u4E00\u5B58\u5728\u5373 true
1416
1497
  //\uFF08\u771F\u5B9E\u7F3A\u5931 + \u526F\u672C\u5B58\u5728\u65F6 Registry \u7684 existsSync \u95E8\u4E0D\u5F97\u62E6\u6389 merge \u89C6\u56FE\u8BFB\u53D6\uFF09
1417
1498
  fs.__origExistsSync = fs.existsSync;
1418
1499
  fs.existsSync = function (p) {
@@ -1799,7 +1880,7 @@ var AppServerConnection = class {
1799
1880
  killChainPromise;
1800
1881
  generation = 0;
1801
1882
  pushHandlers = /* @__PURE__ */ new Map();
1802
- /** [R4] 连接级崩溃通知面(onClose 订阅者集合;与 pushHandlers 同构的 refCount 形态)。 */
1883
+ /** [R4] 连接级崩溃通知侧(onClose 订阅者集合;与 pushHandlers 同构的 refCount 形态)。 */
1803
1884
  closeHandlers = /* @__PURE__ */ new Set();
1804
1885
  capturedProtocolInfo = null;
1805
1886
  constructor(opts) {
@@ -1819,7 +1900,7 @@ var AppServerConnection = class {
1819
1900
  get pid() {
1820
1901
  return this.child?.pid;
1821
1902
  }
1822
- /** 当前代是否有活进程(关闭原语与重建测试的观测面)。 */
1903
+ /** 当前代是否有活进程(关闭原语与重建测试的观测范围)。 */
1823
1904
  get alive() {
1824
1905
  return this.child !== null;
1825
1906
  }
@@ -2100,7 +2181,7 @@ var AppServerConnection = class {
2100
2181
  return false;
2101
2182
  }
2102
2183
  }
2103
- /** stderr 实时 append 落盘(懒打开 + 尺寸轮转 + 过期清理;失败静默——取证面不
2184
+ /** stderr 实时 append 落盘(懒打开 + 尺寸轮转 + 过期清理;失败静默——取证侧不
2104
2185
  * 能拖垮主通道)。 */
2105
2186
  appendStderrLog(chunk) {
2106
2187
  if (this.stderrStreamFailed) return;
@@ -2286,7 +2367,7 @@ var SessionChannel = class {
2286
2367
  /**
2287
2368
  * [P0-1 U5/D7] dispose 标志:dispose 收割后 closeSession 短路——收割形态下连接
2288
2369
  * 可能仍 alive(close 事件迟到,finalize 未跑、child 非 null),此时发 close
2289
- * request 会写死进程(请求挂到控制面超时)或触发惰性重建(凭空 spawn 无人使用的
2370
+ * request 会写死进程(请求挂到控制范围超时)或触发惰性重建(凭空 spawn 无人使用的
2290
2371
  * 进程,违背 dispose 防泄漏语义);进程随即被引擎杀链回收,会话驻留随之消亡。
2291
2372
  */
2292
2373
  disposed = false;
@@ -2310,10 +2391,10 @@ var SessionChannel = class {
2310
2391
  /**
2311
2392
  * [P0-1 U5/D7] dispose 前收割 + 退订:「退订 = 不会再有事件」在结构上蕴含「在途
2312
2393
  * turn 不应再等」——close 缺失/被吞没形态(onClose 收割主路径未触发)下,在途
2313
- * turn 于退订前经既有 failAllTurns 收敛为明确失败(设计 §3.4 退化路径闭合;
2394
+ * turn 于退订前经既有 failAllTurns 收敛为明确失败(设计 §3.4 退化路径完成;
2314
2395
  * race 窗口在引擎 shutdownRuntimeAndDisposeChannel,量级与 awaitConnFinalized
2315
2396
  * 同源 ZCODE_APPSERVER_HARVEST_GRACE_MS)。幂等:与 onClose 收割先到者赢
2316
- * (turn.fail 的 settled 守卫——已落定 turn 不改写),activeTurns 已空则 no-op。
2397
+ * (turn.fail 的 settled 检查——已落定 turn 不改写),activeTurns 已空则 no-op。
2317
2398
  * 不影响连接自身生命周期(R4)。
2318
2399
  */
2319
2400
  dispose() {
@@ -2385,7 +2466,7 @@ var SessionChannel = class {
2385
2466
  * 原样上抛(调用方决定降级形态——引擎侧 warn 后注入锚失效声明段继续执行,
2386
2467
  * run 不失败,[U3])。
2387
2468
  * 经 conn.request 惰性启动连接(resume 是 run 的**首个**请求——调用时点连接
2388
- * 可能尚未启动,与 readBestEffort 的「已死跳过」判据不同场景,不做 alive 守卫)。
2469
+ * 可能尚未启动,与 readBestEffort 的「已死跳过」判据不同场景,不做 alive 检查)。
2389
2470
  */
2390
2471
  async resumeSession(sessionId) {
2391
2472
  return this.conn.request("session/resume", { sessionId }, { timeoutMs: ZCODE_APPSERVER_TURN_READ_TIMEOUT_MS });
@@ -2534,7 +2615,7 @@ var SessionChannel = class {
2534
2615
  }
2535
2616
  /**
2536
2617
  * 收尾帧判定与吸收(A.2 ⑤ 两种形态:payload.response / stopReason+content 变体)。
2537
- * 命中任一形态则落 finalText/finalUsage 并以 final-frame 宽松终态收口,返回 true。
2618
+ * 命中任一形态则落 finalText/finalUsage 并以 final-frame 宽松终态收尾,返回 true。
2538
2619
  */
2539
2620
  applyFinalFrame(turn, payload) {
2540
2621
  if (typeof payload.response === "string" && payload.response !== "") {
@@ -2611,7 +2692,7 @@ var SessionChannel = class {
2611
2692
  * - "interrupted":用户中断形态,debug(stderr 兜底对 debug no-op——对齐
2612
2693
  * SDK logger CONSOLE_SINK 语义,host/log 反向请求侧仍可观测);
2613
2694
  * - 失败类(isFailedTerminalStatus,与引擎失败分流同口径)与 unknown/未识别
2614
- * status:保守 warn(原样 status 已写入文案)——假成功识破的防御面,理论
2695
+ * status:保守 warn(原样 status 已写入文案)——假成功识破的防御机制,理论
2615
2696
  * 不可达但保留,宁可误报不静默。
2616
2697
  */
2617
2698
  logLateTerminal(turn, status) {
@@ -2652,7 +2733,7 @@ var SessionChannel = class {
2652
2733
  }
2653
2734
  }
2654
2735
  }
2655
- /** 终态后 read(A.2 ⑥)。失败/超时不抛——降级链由 runTurn 收口(read 只兜底不致命)。
2736
+ /** 终态后 read(A.2 ⑥)。失败/超时不抛——降级链由 runTurn 收尾(read 只兜底不致命)。
2656
2737
  * 连接已死时跳过(与 closeSession 同判——对死连接发请求会凭空重建进程)。 */
2657
2738
  async readBestEffort(sessionId) {
2658
2739
  if (!this.conn.alive) return void 0;
@@ -2807,7 +2888,7 @@ function sweepExpiredZcodeSessions(dbPath, opts = {}) {
2807
2888
  return { swept, keptActive };
2808
2889
  } catch (err) {
2809
2890
  logger3.warn(
2810
- `[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)}`,
2891
+ `[zcode-ttl] sweep \u5931\u8D25\uFF08\u8F85\u52A9\u8D44\u6E90\u4FA7\uFF0C\u4E0D\u5F71\u54CD run \u4E3B\u94FE\u8DEF\uFF1B\u4E0B\u4E2A\u8282\u6D41\u7A97\u91CD\u8BD5\uFF09: ${err instanceof Error ? err.message : String(err)}`,
2811
2892
  { dbPath }
2812
2893
  );
2813
2894
  return noOp;
@@ -2885,7 +2966,7 @@ var ZcodeEngine = class {
2885
2966
  permissionMode: "native",
2886
2967
  // [D3-④] 无 turn_end 语义,轮数上限不可兑现(预检 gate 据此同步拒绝)
2887
2968
  maxTurns: false,
2888
- // [subagent-model-switch] zcode 引擎不在模型切换设计范围(协议面引擎中立预留,
2969
+ // [subagent-model-switch] zcode 引擎不在模型切换设计范围(协议范围引擎中立预留,
2889
2970
  // 后续另立项接入)——setModel 通路未接通,unsupported 如实声明
2890
2971
  setModel: "unsupported"
2891
2972
  };
@@ -2953,7 +3034,7 @@ var ZcodeEngine = class {
2953
3034
  * 常驻路径主编排:模型解析(v2 单源校验)→ 惰性连接 + runTurn(事件时序前移:
2954
3035
  * text_delta 流式、终态后 message_end/turn_end)→ schema 仿真重试 → outcome/handle。
2955
3036
  * onHandleReady 在 create 应答后(§3.4 不变量 3;原 onPoolResolved prepare 期
2956
- * 声明已随 [池抽象降级 2026-09-13] poolKey 协议面退役删除)。
3037
+ * 声明已随 [池抽象降级 2026-09-13] poolKey 协议范围退役删除)。
2957
3038
  */
2958
3039
  async runViaAppServer(task, ctx) {
2959
3040
  const startedAt = Date.now();
@@ -2973,7 +3054,7 @@ var ZcodeEngine = class {
2973
3054
  const outcome = this.finalizeOutcome(task, ctx, final, usageAcc, startedAt);
2974
3055
  return { handle: this.appServerHandle(outcome), outcome };
2975
3056
  }
2976
- /** pre-aborted 短路收口:合成中止 outcome + 'shared' 锚定 handle。 */
3057
+ /** pre-aborted 短路收尾:合成中止 outcome + 'shared' 锚定 handle。 */
2977
3058
  abortedAppServerRun(task, ctx, startedAt) {
2978
3059
  const outcome = this.finalizeOutcome(
2979
3060
  task,
@@ -2993,11 +3074,11 @@ var ZcodeEngine = class {
2993
3074
  * 新建会话)。重试轮 prompt 用 basePrompt 原样重跑(失败形态非 schema),文案补
2994
3075
  * 「已自动重试一次」句(retried 标记仅对真实发生的重试生效)。
2995
3076
  *
2996
- * 两次重试一次封顶各自独立(D6 被否①:多次重试/指数退避不做——重跑一轮=整任务
3077
+ * 两次重试一次封顶各自独立(D6 不采用①:多次重试/指数退避不做——重跑一轮=整任务
2997
3078
  * 重算,一次封顶)。组合序:瞬时重试在前、schema 重试在后——瞬时重试轮 parsed 且
2998
3079
  * 校验失败时仍进 schema 重试(末次 attempt 语义,schema 重试编排保持现状不动)。
2999
3080
  *
3000
- * 重试轮是独立会话的独立 LLM 调用:token 计入 outcome.usage 总量;事件面
3081
+ * 重试轮是独立会话的独立 LLM 调用:token 计入 outcome.usage 总量;事件范围
3001
3082
  * text_delta 按实际流出(含失败轮——journal 记录真实流水),message_end/turn_end
3002
3083
  * 只在最终轮终态后合成(不变量 2/5)。
3003
3084
  */
@@ -3044,7 +3125,7 @@ var ZcodeEngine = class {
3044
3125
  }
3045
3126
  /**
3046
3127
  * 单轮常驻执行:runTurn 组合面 + D3 abort 链 + 事件前移(text_delta 实时流出;
3047
- * 终态数据经 read 兜底收口后才 resolve——不变量 1/2)。
3128
+ * 终态数据经 read 兜底收尾后才 resolve——不变量 1/2)。
3048
3129
  *
3049
3130
  * @param opts retried:瞬时重试轮标记——失败文案补「已自动重试一次」句(F-1/F-4)。
3050
3131
  */
@@ -3067,7 +3148,7 @@ var ZcodeEngine = class {
3067
3148
  // 等,真机探针实证每 ~1s 一帧)→ activity 事件(零载荷纯活性信号——宿主无进展
3068
3149
  // 守护对任何事件类型刷新;reducer no-op、不落 journal)。zcode 引擎声明
3069
3150
  // conversation:"cold"(续聊 = session-store 冷恢复重建 + 新 run + resume 锚点,
3070
- // 无热 steering/chat 域接线),run 域是唯一发射面。
3151
+ // 无热 steering/chat 域接线),run 域是唯一发射点。
3071
3152
  onActivity: () => ctx.onEvent?.({ type: "activity" }),
3072
3153
  onSessionCreated: (sessionId) => {
3073
3154
  currentSessionId = sessionId;
@@ -3104,12 +3185,12 @@ var ZcodeEngine = class {
3104
3185
  }
3105
3186
  /**
3106
3187
  * [attemptAppServerTurn 拆分] 失败分流(catch 半边):
3107
- * - signal 已 aborted → 中止终态收口;
3188
+ * - signal 已 aborted → 中止终态收尾;
3108
3189
  * - [P0-1 U4] 连接崩溃收割形态(failAllTurns 的错误,D6 可重试形态)判据:
3109
- * 非 RPC error(服务端无明确应答——有应答即精确错误归类,非瞬时崩溃面)且
3190
+ * 非 RPC error(服务端无明确应答——有应答即精确错误归类,非瞬时崩溃)且
3110
3191
  * conn 不存活。时序可靠性:catch 时刻紧随 onClose 收割,连接重建仅由
3111
3192
  * conn.request 惰性触发——本链路中 runTurn finally 的 closeSession 对死连接
3112
- * 短路(channel 侧 !alive 守卫)、stop 只属 abort 链(已被 signal.aborted
3193
+ * 短路(channel 侧 !alive 检查)、stop 只属 abort 链(已被 signal.aborted
3113
3194
  * 短路)——此刻无 request 可重建,判据可靠;
3114
3195
  * - 其余 → 精确错误终态。
3115
3196
  */
@@ -3131,7 +3212,7 @@ var ZcodeEngine = class {
3131
3212
  async awaitConnFinalized(rt) {
3132
3213
  if (!rt.conn.alive) return;
3133
3214
  await new Promise((resolve2) => {
3134
- const timer = setTimeout(() => finish(), ZCODE_APPSERVER_HARVEST_GRACE_MS);
3215
+ const timer = setTimeout(() => finish(), harvestGraceMs());
3135
3216
  if (typeof timer.unref === "function") timer.unref();
3136
3217
  const off = rt.conn.onClose(() => finish());
3137
3218
  function finish() {
@@ -3145,7 +3226,7 @@ var ZcodeEngine = class {
3145
3226
  * D3 abort 链执行体(用户取消入口,fire-and-forget——与 turn promise 并行推进):
3146
3227
  * stop 帧(超时 ZCODE_APPSERVER_STOP_TIMEOUT_MS)→ grace 窗口内 turn 落定即止
3147
3228
  * (不杀共享进程)→ 超窗 killChain(conn.shutdown 全序:SIGTERM→grace→SIGKILL)。
3148
- * turn 的最终落定由 attempt 主路径 await 收口,本链不直接产出终态。abort 与 create
3229
+ * turn 的最终落定由 attempt 主路径 await 收尾,本链不直接产出终态。abort 与 create
3149
3230
  * 竞态(signal 先到、session 未建立):等会话建立(带上限)再发 stop——否则 stop
3150
3231
  * 永远发不出,直接连坐杀共享进程。
3151
3232
  */
@@ -3194,7 +3275,7 @@ var ZcodeEngine = class {
3194
3275
  * workspace.workspacePath 按任务传递——D10 基线不预设任务级进程 cwd)。
3195
3276
  * spawn 经 fs 拦截 wrapper(appserver-launcher:cli config 读取重定向为
3196
3277
  * 「真实文件 + v2 provider 注入」——CLI 形态 app-server 在共享宿主 HOME 下的
3197
- * 唯一凭据供数通路,机制与漂移面见该文件头注)。
3278
+ * 唯一凭据供数通路,机制与漂移风险见该文件头注)。
3198
3279
  */
3199
3280
  ensureAppServerRuntime() {
3200
3281
  if (this.appserverRuntime !== void 0) return this.appserverRuntime;
@@ -3243,7 +3324,7 @@ var ZcodeEngine = class {
3243
3324
  * [P0-1 U5/D7] grace race 输掉(close 迟到/永不到达——stdio 被孙进程持有排空不
3244
3325
  * 尽等病态形态)时,channel.dispose() 内置的 dispose 收割(failAllTurns 先于退订,
3245
3326
  * SessionChannel.dispose)兜底在途 turn——在途 turn 收敛为「grace 窗口内明确失败」
3246
- * (设计 §3.4 退化路径闭合);race 窗口与 awaitConnFinalized 同源同量级
3327
+ * (设计 §3.4 退化路径完成);race 窗口与 awaitConnFinalized 同源同量级
3247
3328
  * (ZCODE_APPSERVER_HARVEST_GRACE_MS)。正常 close 先到时 onClose 收割先行,
3248
3329
  * dispose 收割幂等 no-op(零回归)。
3249
3330
  */
@@ -3255,14 +3336,14 @@ var ZcodeEngine = class {
3255
3336
  });
3256
3337
  });
3257
3338
  await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
3258
- await Promise.race([harvested, delayResolved(ZCODE_APPSERVER_HARVEST_GRACE_MS, void 0)]);
3339
+ await Promise.race([harvested, delayResolved(harvestGraceMs(), void 0)]);
3259
3340
  rt.channel.dispose();
3260
3341
  }
3261
3342
  /**
3262
- * [R1 D6 主体] 引擎停机面:①fire 全部在途会话的 session/close 帧(不等待
3343
+ * [R1 D6 主体] 引擎停机路径:①fire 全部在途会话的 session/close 帧(不等待
3263
3344
  * 应答——D6① 顺序规定:close 帧必须先于 SIGTERM,否则对面来不及处理即被杀)→
3264
- * ②同步 SIGTERM(conn.shutdown 调用内 killChain 前缀同步执行——同步面在返回
3265
- * Promise 前完成)→ ③grace → SIGKILL(异步面,Promise resolve 于进程退出)。
3345
+ * ②同步 SIGTERM(conn.shutdown 调用内 killChain 前缀同步执行——同步部分在返回
3346
+ * Promise 前完成)→ ③grace → SIGKILL(异步侧,Promise resolve 于进程退出)。
3266
3347
  * 幂等:运行时字段取走即置空,二次调用零副作用;dispose 后首个 run 经
3267
3348
  * ensureAppServerRuntime 自动重建(与崩溃重建同一代码路径,不变量 4)。
3268
3349
  */
@@ -3301,7 +3382,7 @@ var ZcodeEngine = class {
3301
3382
  /** abort 合成终态:exitCode=null(record 正常收尾,不留僵尸)。 */
3302
3383
  applyAbortedOutcome(outcome, task, ctx, final, emit) {
3303
3384
  outcome.exitCode = null;
3304
- outcome.error = isHostTimeoutAbort(ctx) ? (0, import_subagent_engine_sdk12.synthesizeTimeoutOutcome)(task, final.output.stdoutText, ZCODE_ENGINE_ID).error ?? (0, import_subagent_engine_sdk12.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)}`;
3385
+ outcome.error = isHostTimeoutAbort(ctx) ? (0, import_subagent_engine_sdk12.synthesizeTimeoutOutcome)(task, final.output.stdoutText, ZCODE_ENGINE_ID).error ?? (0, import_subagent_engine_sdk12.engineTimeoutDetail)(final.output.stdoutText) : final.abortMessage ?? `engine_run_failed: zcode \u4EFB\u52A1\u88AB\u4E2D\u6B62\uFF08app-server abort \u94FE\u6536\u5C3E\uFF0C\u5BBF\u4E3B\u5408\u6210\u7EC8\u6001\uFF09\u3002\u8F93\u51FA\u5C3E\u90E8: ${final.output.stdoutText.slice(-ZCODE_ERROR_TAIL_CHARS)}`;
3305
3386
  emit({ type: "error", message: outcome.error });
3306
3387
  }
3307
3388
  /**
@@ -3351,7 +3432,7 @@ var ZcodeEngine = class {
3351
3432
  * [engine-ref-translation D1] 派发期模型 ref 翻译(EnginePort 可选成员实装):
3352
3433
  * pi 词汇 ref → zcode 原生 ref(zai-coding-cn/* → account:bigmodel-individual-*
3353
3434
  * 大小写归一,2026-10-16 用户裁决(证据驱动修正目标家族)),其余透传。编排层在 record stamp 前调用——
3354
- * record/上报面呈现实际发给引擎的 ref;翻译失败抛错 = 显式失败(不静默回落)。
3435
+ * record/上报侧呈现实际发给引擎的 ref;翻译失败抛错 = 显式失败(不静默回落)。
3355
3436
  * 幂等:翻译产物(account:/个人 id)再入恒等,引擎内 resolveZcodeModelRef 的
3356
3437
  * 同名翻译分支零冲突。
3357
3438
  */
@@ -3389,9 +3470,9 @@ var ZcodeEngine = class {
3389
3470
  * 权威,无双实现)。校验失败原样抛 ZcodePrepareError,由编排层
3390
3471
  * (engine/model-validation.ts)包装成「引擎与模型不配套」文案。
3391
3472
  *
3392
- * [R4/D6-②] 缺席语义:本实现是进程内形态 + 协议诊断面(宿主 cli 形态消费的是
3473
+ * [R4/D6-②] 缺席语义:本实现是进程内形态 + 协议诊断范围(宿主 cli 形态消费的是
3393
3474
  * RemoteEngine 的 manifest 本地判定,不经本方法——SDK protocol methods.ts 明示
3394
- * validateModel 为诊断面)。缺席 modelRef 时返回空串 canonical(= create 帧省略
3475
+ * validateModel 为诊断范围)。缺席 modelRef 时返回空串 canonical(= create 帧省略
3395
3476
  * model 键、CLI 自身缺省解析——2026-09-29 account 体系迁移后 plan 家族兜底 id
3396
3477
  * 已不可用;record 留痕侧按 R4/D6-② 空串归一为「用户未指定」条件留空)。帧应答
3397
3478
  * 形态维持 {canonicalRef: string}(SDK port-contract 契约必填——「帧字段缺席」
@@ -3408,7 +3489,7 @@ var ZcodeEngine = class {
3408
3489
  * (zcodeSessionDbPath(engineDataDir),tier1 白名单集合见方法体——宿主路径仅
3409
3490
  * 「共享 HOME 时代」存量兼容);旧 records(池时代)的相对路径仍按池目录锚定
3410
3491
  * 解析(read 兼容旧数据,池目录不存在时自然落②级 journal 降级)。[池抽象降级
3411
- * 2026-09-13] 锚定 key 用常量 SHARED_POOL_KEY 承载——协议面 poolKey 已删,池
3492
+ * 2026-09-13] 锚定 key 用常量 SHARED_POOL_KEY 承载——协议范围 poolKey 已删,池
3412
3493
  * 时代真实池 key 不再随 handle 传输;若旧 record 的池目录恰为 shared 布局则①级
3413
3494
  * 仍可达,其余池 key 的旧 record 直接走②级 journal 降级(30 天 TTL 同尺度衰减,
3414
3495
  * 行为安全)。
@@ -3505,8 +3586,8 @@ var ZcodeEngine = class {
3505
3586
  * [U6 / §3.2.6 要点 3] resume 锚 → 历史前缀(interact-resume 的读半段)。
3506
3587
  *
3507
3588
  * 无锚 / 非 zcode 锚形态 → undefined(行为不变)。读通道失败(会话失效 / 控制
3508
- * 面错误)= 锚真失效的权威信号——宿主侧库投影预检查已退役(app-server 落库滞后
3509
- * 于 create 应答,分钟级窗 + 部分行永不落库,预检查系统性误判;resume 走
3589
+ * 面错误)= 锚真失效的权威信号——宿主侧库派生视图预检查已退役(app-server 写盘滞后
3590
+ * 于 create 应答,分钟级窗 + 部分行永不写盘,预检查系统性误判;resume 走
3510
3591
  * app-server resident 内存态才是真实活性判据),因此此处不再静默降级为无前缀
3511
3592
  * 裸跑,而是返回锚失效声明段:让模型知情「延续但无历史」,基于最新消息独立
3512
3593
  * 续推,而非在缺上下文时臆测连续性。用户消息照常执行,不炸轮。
@@ -3600,7 +3681,7 @@ function stopTimeoutMs() {
3600
3681
  return resolveAppserverTimingMs(
3601
3682
  ZCODE_APPSERVER_STOP_TIMEOUT_ENV,
3602
3683
  ZCODE_APPSERVER_STOP_TIMEOUT_MS,
3603
- "stop \u63A7\u5236\u9762\u8D85\u65F6"
3684
+ "stop \u63A7\u5236\u8303\u56F4\u8D85\u65F6"
3604
3685
  );
3605
3686
  }
3606
3687
  function abortGraceMs() {
@@ -3610,6 +3691,13 @@ function abortGraceMs() {
3610
3691
  "abort grace \u7A97\u53E3"
3611
3692
  );
3612
3693
  }
3694
+ function harvestGraceMs() {
3695
+ return resolveAppserverTimingMs(
3696
+ ZCODE_APPSERVER_HARVEST_GRACE_ENV,
3697
+ ZCODE_APPSERVER_HARVEST_GRACE_MS,
3698
+ "\u5D29\u6E83\u6536\u5272\u786E\u8BA4\u7A97\u53E3"
3699
+ );
3700
+ }
3613
3701
  function delayResolved(ms, value) {
3614
3702
  return new Promise((resolve2) => {
3615
3703
  const t = setTimeout(() => resolve2(value), ms);
@@ -3904,10 +3992,10 @@ var EngineProtocolServer = class {
3904
3992
  (0, import_server.writeRunEvent)(this.write, this.activeRuns, runId, event);
3905
3993
  }
3906
3994
  /**
3907
- * 发反向请求并等应答(帧④必须应答)。计时面登记:发出 → clock.started;收到
3995
+ * 发反向请求并等应答(帧④必须应答)。计时侧登记:发出 → clock.started;收到
3908
3996
  * ack 形态应答 → clock.acked(两阶段第一段,等待不计超时);终结 → clock.settled。
3909
- * 应答超时兜底 reject——数据面通道(host/log 等)core 10s 应答,reject 由调用方
3910
- * 吞掉留痕(数据面丢失不拖死主链路)。
3997
+ * 应答超时兜底 reject——数据范围通道(host/log 等)core 10s 应答,reject 由调用方
3998
+ * 吞掉留痕(数据范围丢失不拖死主链路)。
3911
3999
  */
3912
4000
  /**
3913
4001
  * fire-and-forget 反向请求(`void this.reverseRequestInternal(...)` 形态)的
@@ -3922,7 +4010,7 @@ var EngineProtocolServer = class {
3922
4010
  });
3923
4011
  };
3924
4012
  }
3925
- /** 反向请求发送(公开面:main.ts 的 host/log 桥接消费;内部 run 通道同路)。 */
4013
+ /** 反向请求发送(对外公开:main.ts 的 host/log 桥接消费;内部 run 通道同路)。 */
3926
4014
  reverseRequest(method, params) {
3927
4015
  return this.reverseRequestInternal(method, params);
3928
4016
  }
@@ -3939,7 +4027,7 @@ var EngineProtocolServer = class {
3939
4027
  params
3940
4028
  );
3941
4029
  }
3942
- /** 反向请求应答落位(ack 两阶段:{ack:true}/{unsupported:true}/{ok:true} 均先 ack 计时面)。 */
4030
+ /** 反向请求应答落位(ack 两阶段:{ack:true}/{unsupported:true}/{ok:true} 均先 ack 计时侧)。 */
3943
4031
  settleReverse(id, frame) {
3944
4032
  const pending = this.reversePending.get(String(id));
3945
4033
  if (pending === void 0) return;