evolcore 0.0.25 → 0.0.27

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.
Files changed (304) hide show
  1. package/CHANGELOG.md +632 -565
  2. package/LICENSE +21 -21
  3. package/README.md +455 -455
  4. package/bin/codex-managed-acl.ps1 +203 -160
  5. package/bin/codex-managed-hook.mjs +208 -208
  6. package/bin/ec-safe-output.js +327 -327
  7. package/bin/ec.js +40 -29
  8. package/bin/install-codex-managed-hooks.mjs +582 -555
  9. package/dist/agents/baseagent-availability.js +68 -0
  10. package/dist/agents/baseagent.js +96 -46
  11. package/dist/agents/claude-runner.js +172 -33
  12. package/dist/agents/codex/app-server-client.js +76 -31
  13. package/dist/agents/codex/catalog.js +1 -1
  14. package/dist/agents/codex/diagnostics.js +34 -1
  15. package/dist/agents/codex-runner.js +434 -446
  16. package/dist/agents/ecagent-runner.js +174 -34
  17. package/dist/agents/gemini-runner.js +8 -7
  18. package/dist/agents/runner-types.js +75 -19
  19. package/dist/aun/aid/agentmd.js +151 -36
  20. package/dist/aun/aid/client.js +1 -1
  21. package/dist/aun/aid/control-aid.js +180 -1
  22. package/dist/aun/aid/identity.js +4 -4
  23. package/dist/aun/aid/index.js +1 -1
  24. package/dist/aun/aid/store.js +17 -13
  25. package/dist/aun/group/history.js +303 -0
  26. package/dist/aun/group/identity.js +43 -0
  27. package/dist/aun/group/index.js +5 -0
  28. package/dist/aun/{msg/group.js → group/operations.js} +162 -39
  29. package/dist/aun/{menu-client.js → menu/client.js} +85 -6
  30. package/dist/aun/menu/index.js +2 -0
  31. package/dist/aun/menu/transport.js +49 -0
  32. package/dist/aun/msg/index.js +2 -1
  33. package/dist/aun/msg/managed-operation.js +85 -19
  34. package/dist/aun/msg/mention-schema.js +51 -0
  35. package/dist/aun/msg/p2p.js +19 -9
  36. package/dist/aun/msg/upload.js +43 -80
  37. package/dist/aun/outbox.js +29 -7
  38. package/dist/aun/rpc/client.js +1 -0
  39. package/dist/aun/storage/client.js +39 -50
  40. package/dist/channels/aun.js +842 -318
  41. package/dist/channels/daemon.js +10 -6
  42. package/dist/cli/agent-command.js +212 -89
  43. package/dist/cli/agent-create.js +626 -11
  44. package/dist/cli/agent.js +125 -13
  45. package/dist/cli/aun-commands.js +487 -337
  46. package/dist/cli/bench.js +20 -20
  47. package/dist/cli/config.js +55 -33
  48. package/dist/cli/contact.js +72 -15
  49. package/dist/cli/ctl-command.js +24 -24
  50. package/dist/cli/daemon-commands.js +140 -63
  51. package/dist/cli/diag.js +31 -0
  52. package/dist/cli/fs-command.js +70 -70
  53. package/dist/cli/handoff-command.js +46 -14
  54. package/dist/cli/index.js +91 -79
  55. package/dist/cli/init.js +34 -1
  56. package/dist/cli/link-rules.js +21 -21
  57. package/dist/cli/managed-command-guard.js +5 -1
  58. package/dist/cli/menu-client.js +51 -32
  59. package/dist/cli/model.js +3 -3
  60. package/dist/cli/net-check.js +22 -22
  61. package/dist/cli/queue-command.js +31 -31
  62. package/dist/cli/response.js +32 -32
  63. package/dist/cli/restart-monitor.js +103 -42
  64. package/dist/cli/stats.js +51 -51
  65. package/dist/cli/task-command.js +59 -0
  66. package/dist/cli/task-context.js +97 -49
  67. package/dist/cli/trigger-command.js +74 -64
  68. package/dist/config/builtin-roles.js +11 -0
  69. package/dist/config/config-field-policy.js +11 -0
  70. package/dist/config/config-manager.js +32 -4
  71. package/dist/config/config-operation-service.js +160 -0
  72. package/dist/config/contact-book-store.js +1 -1
  73. package/dist/config/contact-book.js +1 -1
  74. package/dist/config/contact-operation-service.js +97 -8
  75. package/dist/config/peer-role-resolver.js +1 -1
  76. package/dist/config/resolved-config-op.js +30 -7
  77. package/dist/config-store.js +1 -1
  78. package/dist/core/agent-application-service.js +7 -1
  79. package/dist/core/audit/event-key.js +17 -2
  80. package/dist/core/audit/log-integrity.js +34 -8
  81. package/dist/core/auth/agent-delegation.js +48 -14
  82. package/dist/core/auth/auth-gateway.js +2 -0
  83. package/dist/core/auth/authorization-audit.js +53 -49
  84. package/dist/core/auth/operation-authorizer.js +90 -7
  85. package/dist/core/auth/operation-catalog.js +145 -4
  86. package/dist/core/auth/remote-access-store.js +155 -10
  87. package/dist/core/bootstrap-messages.js +66 -7
  88. package/dist/core/bootstrap-service.js +111 -20
  89. package/dist/core/command/agent-control.js +88 -7
  90. package/dist/core/command/agent-storage-menu.js +443 -0
  91. package/dist/core/command/cli-intent-parser.js +19 -0
  92. package/dist/core/command/command-handler.js +159 -65
  93. package/dist/core/command/data-root-resolver.js +273 -0
  94. package/dist/core/command/diag-service.js +118 -0
  95. package/dist/core/command/group-menu.js +7 -2
  96. package/dist/core/command/menu-catalog.js +46 -7
  97. package/dist/core/command/menu-handler.js +392 -100
  98. package/dist/core/command/menu-protocol.js +45 -6
  99. package/dist/core/command/menu-token-store.js +41 -8
  100. package/dist/core/command/role-menu.js +2 -2
  101. package/dist/core/command/slash-gate.js +34 -4
  102. package/dist/core/command/slash-handler.js +123 -43
  103. package/dist/core/event-catalog.js +4 -0
  104. package/dist/core/evolagent-registry.js +28 -2
  105. package/dist/core/group/invite-service.js +924 -0
  106. package/dist/core/handoff/runtime.js +205 -27
  107. package/dist/core/handoff/store.js +193 -4
  108. package/dist/core/interaction-router.js +22 -8
  109. package/dist/core/message/im-renderer.js +146 -34
  110. package/dist/core/message/message-bridge.js +163 -20
  111. package/dist/core/message/message-log.js +5 -3
  112. package/dist/core/message/message-queue.js +99 -41
  113. package/dist/core/message/message-utils.js +1 -0
  114. package/dist/core/message/response-engine.js +755 -144
  115. package/dist/core/model/catalog-service.js +79 -49
  116. package/dist/core/model/context-window-suffix.js +33 -0
  117. package/dist/core/model/model-catalog.js +1 -1
  118. package/dist/core/model/model-permission.js +12 -3
  119. package/dist/core/permission/approval-gateway.js +36 -3
  120. package/dist/core/permission/ec-command-parser.js +112 -41
  121. package/dist/core/permission/protected-paths.js +134 -16
  122. package/dist/core/permission/sandbox-runtime.js +108 -17
  123. package/dist/core/permission/tool-error-code.js +15 -0
  124. package/dist/core/permission/tool-policy.js +9 -8
  125. package/dist/core/relation/peer-identity.js +57 -6
  126. package/dist/core/relation/shared-data.js +106 -0
  127. package/dist/core/role/runtime-policy.js +64 -0
  128. package/dist/core/runtime-lock.js +10 -26
  129. package/dist/core/session/runner-task-history.js +170 -0
  130. package/dist/core/session/session-manager.js +34 -6
  131. package/dist/core/session/session-mapper.js +41 -0
  132. package/dist/core/session/session-turn-coordinator.js +37 -7
  133. package/dist/core/session/session-turns.js +48 -7
  134. package/dist/core/task/task-recovery-service.js +176 -0
  135. package/dist/core/task/task-resume-operation.js +32 -0
  136. package/dist/eck/group-rules-sync.js +5 -3
  137. package/dist/eck/kit-renderer.js +1 -0
  138. package/dist/index.js +588 -217
  139. package/dist/ipc.js +193 -15
  140. package/dist/paths.js +17 -4
  141. package/dist/product.js +2 -0
  142. package/dist/response-system/engines/v1/proactive-flow.js +59 -2
  143. package/dist/stats/billing.js +41 -15
  144. package/dist/stats/db.js +159 -159
  145. package/dist/stats/eck-vars.js +9 -9
  146. package/dist/stats/price-resolver.js +28 -10
  147. package/dist/stats/query.js +139 -139
  148. package/dist/stats/role-budget.js +6 -6
  149. package/dist/stats/writer.js +51 -51
  150. package/dist/trigger/event-source.js +19 -0
  151. package/dist/trigger/feedback.js +233 -40
  152. package/dist/trigger/parser.js +55 -5
  153. package/dist/trigger/patch.js +9 -0
  154. package/dist/trigger/scheduler.js +30 -4
  155. package/dist/trigger/validation.js +20 -3
  156. package/dist/utils/cross-platform.js +30 -1
  157. package/dist/utils/ecweb-utils.js +1 -0
  158. package/dist/utils/error-dict.json +160 -160
  159. package/dist/utils/error-utils.js +61 -2
  160. package/dist/utils/evolcore-version.js +129 -0
  161. package/dist/utils/logger.js +26 -14
  162. package/dist/utils/macos-autostart.js +22 -22
  163. package/dist/utils/model-prices.jsonl +23 -23
  164. package/dist/utils/npm-ops.js +176 -35
  165. package/dist/utils/tool-summary.js +59 -3
  166. package/dist/utils/welcome.js +196 -23
  167. package/ecagent/LICENSE +21 -21
  168. package/ecagent/NOTICE +11 -11
  169. package/ecagent/README.md +61 -61
  170. package/ecagent/dist/harness/agent-harness.js +3 -0
  171. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  172. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  173. package/ecagent/dist/harness/messages.js +7 -7
  174. package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
  175. package/kits/docs/GUIDE.md +20 -20
  176. package/kits/docs/INDEX.md +60 -66
  177. package/kits/docs/aun/CHEATSHEET.md +19 -19
  178. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  179. package/kits/docs/channels/aun.md +69 -56
  180. package/kits/docs/channels/feishu.md +56 -56
  181. package/kits/docs/context-assembly.md +379 -367
  182. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  183. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  184. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  185. package/kits/docs/eck_templates/runtime.template.md +19 -19
  186. package/kits/docs/evolcore/INDEX.md +76 -71
  187. package/kits/docs/evolcore/agent-create.md +202 -165
  188. package/kits/docs/evolcore/agent.md +99 -67
  189. package/kits/docs/evolcore/aid.md +68 -60
  190. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
  191. package/kits/docs/evolcore/config.md +241 -195
  192. package/kits/docs/evolcore/contact.md +75 -68
  193. package/kits/docs/evolcore/ctl.md +46 -46
  194. package/kits/docs/evolcore/event.md +216 -216
  195. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  196. package/kits/docs/evolcore/fs.md +110 -110
  197. package/kits/docs/evolcore/group-collaboration.md +244 -244
  198. package/kits/docs/evolcore/group-fs.md +17 -17
  199. package/kits/docs/evolcore/group-rules.md +251 -251
  200. package/kits/docs/evolcore/group.md +267 -152
  201. package/kits/docs/evolcore/menu-client.md +58 -34
  202. package/kits/docs/evolcore/model.md +50 -50
  203. package/kits/docs/evolcore/msg.md +169 -165
  204. package/kits/docs/evolcore/response.md +75 -75
  205. package/kits/docs/evolcore/rpc.md +37 -37
  206. package/kits/docs/evolcore/self-summary.md +29 -29
  207. package/kits/docs/evolcore/stats.md +83 -83
  208. package/kits/docs/evolcore/storage.md +53 -50
  209. package/kits/docs/evolcore/task.md +14 -0
  210. package/kits/docs/evolcore/trigger.md +623 -575
  211. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  212. package/kits/docs/identity/PATH_OPS.md +16 -16
  213. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  214. package/kits/docs/identity/identity-tools.md +26 -26
  215. package/kits/docs/path-registry.md +62 -62
  216. package/kits/docs/prompt-loading-architecture.md +269 -269
  217. package/kits/docs/venues/aun-group.md +45 -45
  218. package/kits/docs/venues/aun-private.md +10 -10
  219. package/kits/docs/venues/client-desktop.md +10 -10
  220. package/kits/docs/venues/client-mobile.md +10 -10
  221. package/kits/docs/venues/feishu-group.md +13 -13
  222. package/kits/docs/venues/feishu-private.md +9 -9
  223. package/kits/docs/venues/group.md +42 -25
  224. package/kits/docs/venues/private.md +10 -10
  225. package/kits/eck_manifest.auxiliary.json +43 -43
  226. package/kits/eck_manifest.json +217 -190
  227. package/kits/eck_message_manifest.json +74 -74
  228. package/kits/migrations/README-role-config-v4.md +32 -32
  229. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  230. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  231. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  232. package/kits/migrations/rename-config-file.mjs +99 -99
  233. package/kits/rules/01-overview.md +166 -162
  234. package/kits/rules/02-navigation.md +96 -96
  235. package/kits/rules/03-identity.md +36 -36
  236. package/kits/rules/04-relation.md +76 -75
  237. package/kits/rules/05-venue.md +60 -60
  238. package/kits/rules/06-channel.md +65 -63
  239. package/kits/schemas/_meta.json +53 -53
  240. package/kits/schemas/agent-config.schema.1.json +177 -177
  241. package/kits/schemas/agent-config.schema.10.json +395 -395
  242. package/kits/schemas/agent-config.schema.11.json +421 -421
  243. package/kits/schemas/agent-config.schema.12.json +451 -427
  244. package/kits/schemas/agent-config.schema.2.json +239 -239
  245. package/kits/schemas/agent-config.schema.3.json +119 -119
  246. package/kits/schemas/agent-config.schema.4.json +208 -208
  247. package/kits/schemas/agent-config.schema.5.json +326 -326
  248. package/kits/schemas/agent-config.schema.6.json +322 -322
  249. package/kits/schemas/agent-config.schema.7.json +303 -303
  250. package/kits/schemas/agent-config.schema.8.json +304 -304
  251. package/kits/schemas/agent-config.schema.9.json +364 -364
  252. package/kits/schemas/contact-book.schema.1.json +36 -36
  253. package/kits/schemas/contact-book.schema.2.json +43 -43
  254. package/kits/schemas/contact-book.schema.3.json +67 -67
  255. package/kits/schemas/daemon.schema.1.json +91 -91
  256. package/kits/schemas/daemon.schema.2.json +101 -101
  257. package/kits/schemas/daemon.schema.3.json +123 -123
  258. package/kits/schemas/daemon.schema.4.json +136 -136
  259. package/kits/schemas/daemon.schema.5.json +135 -135
  260. package/kits/schemas/daemon.schema.6.json +131 -131
  261. package/kits/schemas/defaults.schema.1.json +81 -81
  262. package/kits/schemas/defaults.schema.2.json +85 -85
  263. package/kits/schemas/defaults.schema.3.json +73 -73
  264. package/kits/schemas/defaults.schema.4.json +107 -107
  265. package/kits/schemas/defaults.schema.5.json +119 -119
  266. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  267. package/kits/schemas/migrations/README.md +30 -30
  268. package/kits/schemas/relation-config.schema.1.json +158 -158
  269. package/kits/schemas/relation-config.schema.2.json +73 -73
  270. package/kits/schemas/relation-config.schema.3.json +50 -50
  271. package/kits/schemas/relation-config.schema.4.json +47 -47
  272. package/kits/schemas/relation-config.schema.5.json +47 -47
  273. package/kits/schemas/relation-config.schema.6.json +46 -46
  274. package/kits/schemas/relation-config.schema.7.json +59 -59
  275. package/kits/schemas/relation-config.schema.8.json +103 -103
  276. package/kits/schemas/role-config.schema.1.json +215 -201
  277. package/kits/schemas/role-registry.schema.1.json +35 -35
  278. package/kits/schemas/single-session.schema.1.json +31 -31
  279. package/kits/schemas/single-session.schema.2.json +57 -57
  280. package/kits/schemas/single-session.schema.3.json +32 -32
  281. package/kits/templates/bootstrap-welcome.md +17 -17
  282. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  283. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  284. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  285. package/kits/templates/message-fragments/inject-default.md +2 -2
  286. package/kits/templates/message-fragments/item.md +2 -2
  287. package/kits/templates/roles/admin.json +92 -92
  288. package/kits/templates/roles/member.json +101 -96
  289. package/kits/templates/roles/owner.json +9 -9
  290. package/kits/templates/roles/visitor.json +90 -85
  291. package/kits/templates/system-fragments/baseagent.md +14 -14
  292. package/kits/templates/system-fragments/bootstrap.md +53 -34
  293. package/kits/templates/system-fragments/channel.md +28 -64
  294. package/kits/templates/system-fragments/commands.md +36 -28
  295. package/kits/templates/system-fragments/group-unread-history.md +7 -0
  296. package/kits/templates/system-fragments/identity.md +13 -13
  297. package/kits/templates/system-fragments/relation.md +19 -19
  298. package/kits/templates/system-fragments/reply.md +56 -0
  299. package/kits/templates/system-fragments/session.md +59 -59
  300. package/kits/templates/system-fragments/venue.md +31 -31
  301. package/package.json +104 -98
  302. package/dist/aun/group-identity.js +0 -9
  303. /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
  304. /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
@@ -3,11 +3,21 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { appendRequestPath, appendV1RequestPath } from '../../agents/request-identity.js';
5
5
  import { atomicReadJson, atomicWriteJson } from '../../utils/atomic-write.js';
6
- export const CATALOG_TTL_MS = 5 * 60_000;
7
- export const CATALOG_STALE_MS = 24 * 60 * 60_000;
6
+ import { parseModelId } from './context-window-suffix.js';
7
+ /** 单页请求的超时(含响应体读取)。多页目录里每页各自享有这个预算。 */
8
+ export const CATALOG_PAGE_TIMEOUT_MS = 4_000;
9
+ /** 整条路由(含全部分页)的总期限,兜住慢网关无限翻页。 */
10
+ export const CATALOG_ROUTE_TIMEOUT_MS = 15_000;
11
+ /**
12
+ * 持久化快照的格式版本。**目录规范化或能力过滤规则变化时必须递增**,
13
+ * 否则旧规则写下的持久化快照与新快照无法区分,会继续被服务。
14
+ * v2:保留上下文窗口后缀(`claude-opus-5[1m]` 不再被折叠成基础 ID)。
15
+ * v3:保留 Codex 上游声明的 max effort,重新获取旧快照中过滤掉的能力。
16
+ */
17
+ export const CATALOG_SNAPSHOT_VERSION = 3;
8
18
  export const TRANSPORT_EFFORTS = {
9
19
  claude: ['low', 'medium', 'high', 'xhigh', 'max'],
10
- codex: ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'],
20
+ codex: ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
11
21
  ecagent: ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
12
22
  gemini: [],
13
23
  };
@@ -22,7 +32,7 @@ export const catalogHash = (value) => createHash('sha256').update(stable(value))
22
32
  export function catalogContextKey(context) {
23
33
  const headers = Object.fromEntries(Object.entries(context.headers ?? {}).map(([k, v]) => [k.toLowerCase(), v]));
24
34
  return catalogHash({ scope: context.scope, baseagent: context.baseagent, provider: context.provider,
25
- baseUrl: context.baseUrl?.replace(/\/+$/, ''), apiKey: context.apiKey,
35
+ baseUrl: context.baseUrl?.replace(/\/+$/, ''), apiKey: context.apiKey, authType: context.authType,
26
36
  headers, queryParams: context.queryParams ?? {}, configUnavailable: context.configUnavailable });
27
37
  }
28
38
  const strings = (value) => Array.isArray(value)
@@ -40,37 +50,45 @@ export function parseModelList(json, _url) {
40
50
  const out = new Map();
41
51
  const items = rows(json);
42
52
  for (const item of items) {
43
- const rawId = typeof item === 'string' ? item : item?.id ?? item?.slug ?? item?.name ?? item?.model;
44
- if (typeof rawId !== 'string' || !rawId.trim())
45
- continue;
46
- // 规范化模型 ID:去掉 Claude Code SDK 的上下文窗口后缀 [1m]/[200k]
47
- // 这些后缀只在传给 claude baseagent 时由 applyContextWindow 添加
48
- const id = rawId.trim().replace(/\[(?:1m|200k)\]$/i, '');
49
- if (out.has(id))
50
- continue;
51
- const rawLevels = item?.reasoningLevels ?? item?.supportedReasoningEfforts ?? item?.supported_reasoning_levels;
52
- const levels = Array.isArray(rawLevels) ? strings(rawLevels.map(v => typeof v === 'string' ? v : v?.reasoningEffort ?? v?.effort)) : undefined;
53
- const owner = item?.owned_by ?? item?.owner ?? item?.provider;
54
- out.set(id, {
55
- id, verification: 'listed',
56
- ...(typeof owner === 'string' ? { owned_by: owner } : {}),
57
- ...(typeof item?.created === 'number' ? { created: item.created } : {}),
58
- ...(typeof (item?.displayName ?? item?.display_name) === 'string' ? { displayName: item.displayName ?? item.display_name } : {}),
59
- ...(levels ? { reasoningLevels: levels } : {}),
60
- ...(typeof (item?.defaultReasoningEffort ?? item?.default_reasoning_level) === 'string' ? { defaultReasoningLevel: item.defaultReasoningEffort ?? item.default_reasoning_level } : {}),
61
- ...(strings(item?.protocols ?? item?.supported_protocols) ? { protocols: strings(item.protocols ?? item.supported_protocols) } : {}),
62
- ...(typeof item?.capabilities?.tool_calling === 'boolean' ? { supportsTools: item.capabilities.tool_calling } : {}),
63
- ...(typeof (item?.context_window ?? item?.contextWindow) === 'number' ? { contextWindow: item.context_window ?? item.contextWindow } : {}),
64
- ...(typeof item?.max_output_tokens === 'number' ? { maxOutputTokens: item.max_output_tokens } : {}),
65
- });
53
+ // 单个畸形条目只跳过自己:一条读不懂的记录不该让整份目录退化到 builtin。
54
+ try {
55
+ const rawId = typeof item === 'string' ? item : item?.id ?? item?.slug ?? item?.name ?? item?.model;
56
+ if (typeof rawId !== 'string' || !rawId.trim())
57
+ continue;
58
+ // 上下文窗口后缀 [1m]/[200k] 是模型身份的一部分:网关把不同窗口作为独立条目
59
+ // 下发,两个变体必须各自成为一条可选条目。仅规范化后缀大小写以便去重。
60
+ const parsed = parseModelId(rawId.trim());
61
+ const id = `${parsed.base}${parsed.suffix ?? ''}`;
62
+ if (out.has(id))
63
+ continue;
64
+ const rawLevels = item?.reasoningLevels ?? item?.supportedReasoningEfforts ?? item?.supported_reasoning_levels;
65
+ const levels = Array.isArray(rawLevels) ? strings(rawLevels.map(v => typeof v === 'string' ? v : v?.reasoningEffort ?? v?.effort)) : undefined;
66
+ const owner = item?.owned_by ?? item?.owner ?? item?.provider;
67
+ out.set(id, {
68
+ id, verification: 'listed',
69
+ ...(typeof owner === 'string' ? { owned_by: owner } : {}),
70
+ ...(typeof item?.created === 'number' ? { created: item.created } : {}),
71
+ ...(typeof (item?.displayName ?? item?.display_name) === 'string' ? { displayName: item.displayName ?? item.display_name } : {}),
72
+ ...(levels ? { reasoningLevels: levels } : {}),
73
+ ...(typeof (item?.defaultReasoningEffort ?? item?.default_reasoning_level) === 'string' ? { defaultReasoningLevel: item.defaultReasoningEffort ?? item.default_reasoning_level } : {}),
74
+ ...(strings(item?.protocols ?? item?.supported_protocols) ? { protocols: strings(item.protocols ?? item.supported_protocols) } : {}),
75
+ ...(typeof item?.capabilities?.tool_calling === 'boolean' ? { supportsTools: item.capabilities.tool_calling } : {}),
76
+ // 网关显式给出的 context_window 优先;缺失时用后缀声明的窗口兜底。
77
+ ...(typeof (item?.context_window ?? item?.contextWindow) === 'number' ? { contextWindow: item.context_window ?? item.contextWindow }
78
+ : parsed.contextWindow !== undefined ? { contextWindow: parsed.contextWindow } : {}),
79
+ ...(typeof item?.max_output_tokens === 'number' ? { maxOutputTokens: item.max_output_tokens } : {}),
80
+ });
81
+ }
82
+ catch { /* Skip the offending entry; the rest of the page stays usable. */ }
66
83
  }
84
+ // 全部条目都无法解析仍视为不可信响应:避免把一个格式不符的 200 当成空目录。
67
85
  if (items.length && !out.size)
68
86
  throw new Error('Model list contains no valid IDs');
69
87
  return [...out.values()];
70
88
  }
71
89
  const BUILTIN_IDS = {
72
90
  codex: ['gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex', 'gpt-5.2'],
73
- claude: ['claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'deepseek-v4-pro', 'deepseek-v4-flash', 'kimi-k2.6', 'kimi-k2.5', 'glm-5.1', 'glm-5', 'glm-4.7', 'MiniMax-M2.7'],
91
+ claude: ['claude-opus-5', 'claude-sonnet-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'deepseek-v4-pro', 'deepseek-v4-flash', 'kimi-k2.6', 'kimi-k2.5', 'glm-5.1', 'glm-5', 'glm-4.7', 'MiniMax-M2.7'],
74
92
  ecagent: ['claude-sonnet-4-6'],
75
93
  gemini: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite'],
76
94
  };
@@ -89,9 +107,14 @@ export function deriveAliases(models, baseagent) {
89
107
  if (!match)
90
108
  continue;
91
109
  const [, family, major, minor = '0'] = match;
110
+ // 同版本的多个窗口变体:取窗口最大的一条,别名才不会随网关下发顺序漂移。
111
+ const window = parseModelId(id).contextWindow ?? 0;
92
112
  const current = best[family];
93
- if (!current || +major > current.major || (+major === current.major && +minor > current.minor))
94
- best[family] = { id, major: +major, minor: +minor };
113
+ const better = !current || +major > current.major
114
+ || (+major === current.major && +minor > current.minor)
115
+ || (+major === current.major && +minor === current.minor && window > current.window);
116
+ if (better)
117
+ best[family] = { id, major: +major, minor: +minor, window };
95
118
  }
96
119
  return Object.fromEntries(Object.entries(best).map(([family, info]) => [family, info.id]));
97
120
  }
@@ -133,12 +156,17 @@ async function gatewayModels(context, fetcher) {
133
156
  const models = [];
134
157
  const seen = new Set();
135
158
  let next = url;
136
- // Bound the whole route, including response bodies and pagination.
137
- const signal = AbortSignal.timeout(4000);
159
+ // 每页各自计时,避免多页目录被一个覆盖全程的超时提前掐断;
160
+ // 同时用总期限兜住整条路由,防止慢网关无限翻页。
161
+ const deadline = Date.now() + CATALOG_ROUTE_TIMEOUT_MS;
138
162
  for (let page = 0; page < 100; page++) {
139
163
  if (seen.has(next))
140
164
  throw new GatewayError();
141
165
  seen.add(next);
166
+ const remaining = deadline - Date.now();
167
+ if (remaining <= 0)
168
+ throw new GatewayError();
169
+ const signal = AbortSignal.timeout(Math.min(CATALOG_PAGE_TIMEOUT_MS, remaining));
142
170
  const headers = new Headers(context.headers);
143
171
  if (context.apiKey && !headers.has('authorization')) {
144
172
  headers.set('authorization', `Bearer ${context.apiKey}`);
@@ -176,7 +204,7 @@ async function gatewayModels(context, fetcher) {
176
204
  /** One instance per daemon. Offline clients use the same persistence format. */
177
205
  export class ModelCatalogService {
178
206
  options;
179
- cache = new Map();
207
+ latest = new Map();
180
208
  successes = new Map();
181
209
  inflight = new Map();
182
210
  constructor(options = {}) {
@@ -192,41 +220,43 @@ export class ModelCatalogService {
192
220
  try {
193
221
  const file = this.file(context);
194
222
  const saved = file ? atomicReadJson(file) : undefined;
195
- if (saved?.version === 1 && saved.snapshot && Array.isArray(saved.snapshot.models) && ['gateway', 'native'].includes(saved.snapshot.source))
223
+ // 版本不匹配的快照按冷启动处理:其 ID 可能出自已废弃的规范化规则。
224
+ if (saved?.version === CATALOG_SNAPSHOT_VERSION && saved.snapshot && Array.isArray(saved.snapshot.models) && ['gateway', 'native'].includes(saved.snapshot.source))
196
225
  snapshot = saved.snapshot;
197
226
  }
198
227
  catch { /* A missing/corrupt cache is a cold load. */ }
199
228
  if (snapshot)
200
229
  this.successes.set(key, snapshot);
201
230
  }
202
- return snapshot && this.now() - Date.parse(snapshot.fetchedAt) <= CATALOG_STALE_MS ? snapshot : undefined;
231
+ return snapshot;
203
232
  }
204
233
  peek(context) {
205
- return structuredClone(this.cache.get(this.key(context))?.snapshot ?? this.previous(context) ?? this.make(context, 'builtin', 'fallback', this.legacyBuiltins(context), [{ code: 'BUILTIN', message: '内置候选,未经当前连接验证' }]));
234
+ return structuredClone(this.latest.get(this.key(context)) ?? this.previous(context) ?? this.make(context, 'builtin', 'fallback', this.legacyBuiltins(context), [{ code: 'BUILTIN', message: '内置候选,未经当前连接验证' }]));
206
235
  }
207
236
  async get(context, refresh = false) {
208
237
  const key = this.key(context);
209
- const cached = this.cache.get(key);
210
- if (!refresh && cached && cached.retryAt > this.now())
211
- return structuredClone(cached.snapshot);
212
- const previous = this.previous(context);
213
- if (!refresh && previous && this.now() - Date.parse(previous.fetchedAt) < CATALOG_TTL_MS && !cached) {
214
- this.cache.set(key, { snapshot: previous, retryAt: Date.parse(previous.fetchedAt) + CATALOG_TTL_MS });
215
- return structuredClone(previous);
216
- }
238
+ const previous = this.latest.get(key) ?? this.previous(context);
217
239
  let pending = this.inflight.get(key);
218
240
  if (!pending) {
219
241
  pending = this.load(context, previous).then(snapshot => {
220
- this.cache.set(key, { snapshot, retryAt: this.now() + (snapshot.freshness === 'fresh' ? CATALOG_TTL_MS : 30_000) });
242
+ // Keep any usable first result (including builtin/native fallback), but
243
+ // never let a failed refresh replace the last usable snapshot.
244
+ if (snapshot.freshness !== 'stale')
245
+ this.latest.set(key, snapshot);
221
246
  return snapshot;
222
247
  }).finally(() => this.inflight.delete(key));
223
248
  this.inflight.set(key, pending);
224
249
  }
225
- if (!refresh && previous)
226
- return structuredClone({ ...previous, freshness: 'stale', diagnostics: [...(cached?.snapshot.diagnostics ?? []), { code: 'STALE_CACHE', message: '显示同一连接的最近成功目录,正在刷新' }] });
250
+ if (!refresh && previous) {
251
+ // Serve the last successful catalog immediately on every ordinary read,
252
+ // while each read also asks the shared background job to refresh it.
253
+ // Explicit refresh callers still await that same in-flight request.
254
+ void pending.catch(() => undefined);
255
+ return structuredClone(previous);
256
+ }
227
257
  return structuredClone(await pending);
228
258
  }
229
- clear() { this.cache.clear(); this.successes.clear(); }
259
+ clear() { this.latest.clear(); this.successes.clear(); }
230
260
  make(context, source, freshness, models, diagnostics) {
231
261
  const entries = adapt(models, context.baseagent);
232
262
  const fetchedAt = new Date(this.now()).toISOString();
@@ -241,7 +271,7 @@ export class ModelCatalogService {
241
271
  const file = this.file(context);
242
272
  if (file) {
243
273
  fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
244
- atomicWriteJson(file, { version: 1, snapshot });
274
+ atomicWriteJson(file, { version: CATALOG_SNAPSHOT_VERSION, snapshot });
245
275
  }
246
276
  }
247
277
  catch { /* Persistence failure does not discard a live result. */ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Claude Code SDK 的上下文窗口后缀(如 `claude-opus-5[1m]`)。
3
+ *
4
+ * 网关会把同一模型的不同窗口作为独立条目下发,所以后缀是**模型身份的一部分**,
5
+ * 目录与选择界面必须原样保留——两个变体各成一条可选条目。
6
+ * 只有按基础 ID 归档的场景(查价、计费、能力参数表)才剥离后缀。
7
+ */
8
+ /** 后缀 token → 该后缀声明的上下文窗口。 */
9
+ export const CONTEXT_WINDOW_SUFFIX_TOKENS = {
10
+ '1m': 1_000_000,
11
+ '200k': 200_000,
12
+ };
13
+ const SUFFIX_RE = /\[(1m|200k)\]$/i;
14
+ /** 拆分模型 ID 的基础部分与上下文窗口后缀。 */
15
+ export function parseModelId(modelId) {
16
+ const match = SUFFIX_RE.exec(modelId);
17
+ if (!match)
18
+ return { base: modelId };
19
+ const token = match[1].toLowerCase();
20
+ return {
21
+ base: modelId.slice(0, match.index),
22
+ suffix: `[${token}]`,
23
+ contextWindow: CONTEXT_WINDOW_SUFFIX_TOKENS[token],
24
+ };
25
+ }
26
+ /** 去掉上下文窗口后缀。仅用于查价/计费/能力表这类按基础 ID 归档的场景。 */
27
+ export function stripContextWindowSuffix(modelId) {
28
+ return modelId.replace(SUFFIX_RE, '');
29
+ }
30
+ /** 该 ID 是否已带网关显式给出的上下文窗口后缀。 */
31
+ export function hasContextWindowSuffix(modelId) {
32
+ return SUFFIX_RE.test(modelId);
33
+ }
@@ -88,7 +88,7 @@ export function resolveCatalogContext(self, ba) {
88
88
  : baseagent === 'claude' ? resolveAnthropicConfig(cfg, block.claude)
89
89
  : baseagent === 'ecagent' ? resolveEcagentConfig(cfg, block.ecagent) : undefined;
90
90
  if (resolved) {
91
- Object.assign(context, { baseUrl: resolved.baseUrl, apiKey: resolved.apiKey, queryParams: resolved.queryParams,
91
+ Object.assign(context, { baseUrl: resolved.baseUrl, apiKey: resolved.apiKey, authType: 'authType' in resolved ? resolved.authType : undefined, queryParams: resolved.queryParams,
92
92
  provider: 'provider' in resolved ? resolved.provider : undefined,
93
93
  headers: buildModelRequestHeaders({ baseagent: baseagent, baseUrl: resolved.baseUrl, agentAid: self, configuredHeaders: resolved.headers }) });
94
94
  }
@@ -1,10 +1,19 @@
1
1
  import { getFieldPermission } from '../../config/roles.js';
2
+ import { stripContextWindowSuffix } from './context-window-suffix.js';
2
3
  export function isModelAllowedByPatterns(model, allowedModels) {
3
4
  if (allowedModels.includes('*'))
4
5
  return true;
5
- return allowedModels.some(pattern => pattern.endsWith('*')
6
- ? model.startsWith(pattern.slice(0, -1))
7
- : model === pattern);
6
+ // 窗口变体是同一模型的不同上下文窗口:白名单写基础 ID(`claude-opus-5`)即同时
7
+ // 授权其窗口变体(`claude-opus-5[1m]`)。反向不成立——白名单显式写了某个变体时,
8
+ // 只授权那一个变体,其它变体和裸 ID 仍被拒绝。
9
+ const candidates = new Set([model, stripContextWindowSuffix(model)]);
10
+ return allowedModels.some(pattern => {
11
+ if (pattern.endsWith('*')) {
12
+ const prefix = pattern.slice(0, -1);
13
+ return [...candidates].some(candidate => candidate.startsWith(prefix));
14
+ }
15
+ return candidates.has(pattern);
16
+ });
8
17
  }
9
18
  function normalizeBaseagent(baseagent) {
10
19
  const normalized = typeof baseagent === 'string' ? baseagent.trim() : '';
@@ -178,6 +178,35 @@ function approvalCodeBlock(value) {
178
178
  const fence = '`'.repeat(Math.max(3, longestRun + 1));
179
179
  return `${fence}text\n${value}\n${fence}`;
180
180
  }
181
+ function normalizeAidForApprovalRouting(value) {
182
+ const normalized = value?.trim().replace(/^@/, '');
183
+ return normalized || undefined;
184
+ }
185
+ function preferredOriginOwner(context, approval, owners) {
186
+ const originChannel = (approval?.originChannel ?? context.channel ?? '').split('#', 1)[0].toLowerCase();
187
+ if (context.chatType !== 'private' || originChannel !== 'aun')
188
+ return undefined;
189
+ const originPeerId = normalizeAidForApprovalRouting(approval?.originPeerId ?? context.userId);
190
+ if (!originPeerId)
191
+ return undefined;
192
+ return owners.find(owner => normalizeAidForApprovalRouting(owner) === originPeerId);
193
+ }
194
+ /**
195
+ * Owner approvals normally belong to the Owner's private main session. The
196
+ * one exception is an AUN private conversation whose peer is itself a
197
+ * configured Owner: preserve that conversation's topic so the request stays
198
+ * with the Owner's current work.
199
+ */
200
+ function ownerApprovalThreadId(context, approval, approverOperatorId) {
201
+ const originChannel = (approval.originChannel ?? context.channel ?? '').split('#', 1)[0].toLowerCase();
202
+ const originPeerId = normalizeAidForApprovalRouting(approval.originPeerId ?? context.userId);
203
+ const approverId = normalizeAidForApprovalRouting(approverOperatorId);
204
+ const isOwnerPrivateConversation = context.chatType === 'private'
205
+ && originChannel === 'aun'
206
+ && !!originPeerId
207
+ && originPeerId === approverId;
208
+ return isOwnerPrivateConversation ? (approval.originThreadId ?? '') : '';
209
+ }
181
210
  export function planApprovalRoute(challenge, context) {
182
211
  if (!challenge.grantable) {
183
212
  return { kind: 'unavailable', reason: 'challenge_not_grantable' };
@@ -235,9 +264,12 @@ export function planApprovalRoute(challenge, context) {
235
264
  approverPrincipalId: actor.principalId,
236
265
  };
237
266
  }
238
- const handoffCandidates = challenge.approverPolicy === 'agent_manager'
239
- ? Array.from(new Set([...admins, ...owners]))
240
- : owners;
267
+ const preferredOwner = preferredOriginOwner(context ?? {}, routing, owners);
268
+ const handoffCandidates = preferredOwner
269
+ ? [preferredOwner]
270
+ : challenge.approverPolicy === 'agent_manager'
271
+ ? Array.from(new Set([...admins, ...owners]))
272
+ : owners;
241
273
  if (handoffCandidates.length === 0) {
242
274
  return {
243
275
  kind: 'unavailable',
@@ -777,6 +809,7 @@ export class PermissionGateway {
777
809
  }
778
810
  const ownerReplyContext = {
779
811
  delivery: { chatType: 'private' },
812
+ threadId: ownerApprovalThreadId(context, approval, route.approverOperatorId),
780
813
  metadata: {
781
814
  source: 'handoff',
782
815
  chatmode: 'interactive',