evolcore 0.0.3 → 0.0.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.
Files changed (143) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +45 -10
  3. package/bin/ec-safe-output.js +89 -24
  4. package/dist/agents/baseagent.js +46 -0
  5. package/dist/agents/claude-runner.js +1 -31
  6. package/dist/agents/codex-app-server-client.js +68 -0
  7. package/dist/agents/codex-runner.js +157 -5
  8. package/dist/agents/runner-types.js +2 -2
  9. package/dist/aun/aid/agentmd.js +79 -0
  10. package/dist/aun/aid/encryption-seed-policy.js +29 -0
  11. package/dist/aun/aid/index.js +1 -1
  12. package/dist/aun/aid/store.js +2 -5
  13. package/dist/channels/aun.js +220 -112
  14. package/dist/channels/daemon.js +18 -4
  15. package/dist/channels/dingtalk.js +52 -137
  16. package/dist/channels/feishu.js +56 -4
  17. package/dist/channels/qqbot.js +23 -1
  18. package/dist/channels/wechat.js +303 -155
  19. package/dist/channels/wecom.js +383 -7
  20. package/dist/cli/aun-commands.js +11 -11
  21. package/dist/cli/handoff-command.js +2 -1
  22. package/dist/cli/init-channel.js +185 -67
  23. package/dist/cli/init.js +7 -5
  24. package/dist/cli/trigger-command.js +92 -11
  25. package/dist/config/access-policy-domain.js +38 -0
  26. package/dist/config/access-policy.js +134 -0
  27. package/dist/config/builtin-role-templates.js +27 -14
  28. package/dist/config/builtin-roles.js +25 -6
  29. package/dist/config/config-field-policy.js +17 -5
  30. package/dist/config/config-manager.js +198 -22
  31. package/dist/config/config-operation-service.js +35 -38
  32. package/dist/config/contact-bind-code.js +274 -0
  33. package/dist/config/contact-book-store.js +173 -5
  34. package/dist/config/contact-book.js +32 -0
  35. package/dist/config/contact-operation-service.js +9 -3
  36. package/dist/config/contact-request-service.js +331 -0
  37. package/dist/config/peer-role-resolver.js +3 -1
  38. package/dist/config/schema-registry.js +49 -24
  39. package/dist/config-store.js +14 -2
  40. package/dist/core/auth/agent-delegation.js +105 -11
  41. package/dist/core/auth/authorization-audit.js +6 -0
  42. package/dist/core/auth/operation-authorizer.js +8 -0
  43. package/dist/core/auth/operation-catalog.js +51 -3
  44. package/dist/core/auth/trigger-authorization.js +15 -0
  45. package/dist/core/bootstrap-service.js +2 -9
  46. package/dist/core/channel-loader.js +6 -2
  47. package/dist/core/command/command-handler.js +10 -13
  48. package/dist/core/command/connect-menu.js +249 -24
  49. package/dist/core/command/evol-menu-version-gate.js +38 -0
  50. package/dist/core/command/group-menu.js +421 -0
  51. package/dist/core/command/menu-handler.js +269 -85
  52. package/dist/core/command/menu-protocol.js +8 -2
  53. package/dist/core/command/menu-token-store.js +102 -0
  54. package/dist/core/command/slash-gate.js +1 -1
  55. package/dist/core/command/slash-handler.js +158 -31
  56. package/dist/core/daemon-file-cache.js +28 -0
  57. package/dist/core/event-catalog.js +29 -0
  58. package/dist/core/evolagent-registry.js +4 -39
  59. package/dist/core/handoff/runtime.js +162 -37
  60. package/dist/core/handoff/store.js +46 -2
  61. package/dist/core/handoff/types.js +1 -0
  62. package/dist/core/inference/text-inference.js +16 -74
  63. package/dist/core/message/file-markers.js +7 -0
  64. package/dist/core/message/im-renderer.js +18 -14
  65. package/dist/core/message/inbound-admission.js +124 -0
  66. package/dist/core/message/message-bridge.js +616 -74
  67. package/dist/core/message/message-log.js +1 -0
  68. package/dist/core/message/message-queue.js +185 -10
  69. package/dist/core/message/peer-mode.js +7 -8
  70. package/dist/core/message/response-engine.js +381 -79
  71. package/dist/core/permission/approval-gateway.js +17 -10
  72. package/dist/core/permission/ec-command-parser.js +101 -46
  73. package/dist/core/permission/tool-policy.js +69 -24
  74. package/dist/core/protected-paths.js +36 -11
  75. package/dist/core/session/session-manager.js +25 -3
  76. package/dist/core/session/session-mapper.js +2 -0
  77. package/dist/core/session/session-renew.js +125 -69
  78. package/dist/core/session/session-turn-coordinator.js +5 -1
  79. package/dist/eck/kit-renderer.js +12 -1
  80. package/dist/eck/message-renderer.js +79 -1
  81. package/dist/index.js +224 -89
  82. package/dist/ipc.js +81 -4
  83. package/dist/paths.js +3 -0
  84. package/dist/response-system/config-resolver.js +29 -0
  85. package/dist/response-system/coordinator.js +8 -32
  86. package/dist/response-system/engines/v1/proactive-flow.js +1 -1
  87. package/dist/response-system/index.js +1 -0
  88. package/dist/response-system/modes/single-session/index.js +7 -4
  89. package/dist/trigger/parser.js +55 -15
  90. package/dist/trigger/patch.js +4 -1
  91. package/dist/trigger/scheduler.js +441 -39
  92. package/dist/utils/error-dict.json +7 -0
  93. package/dist/utils/evolcore-version.js +21 -0
  94. package/dist/utils/logger.js +2 -2
  95. package/dist/utils/stable-semver.js +21 -0
  96. package/dist/utils/stats.js +33 -9
  97. package/kits/docs/channels/aun.md +4 -13
  98. package/kits/docs/evolcore/INDEX.md +1 -1
  99. package/kits/docs/evolcore/config.md +47 -2
  100. package/kits/docs/evolcore/contact.md +7 -3
  101. package/kits/docs/evolcore/group-rules.md +46 -4
  102. package/kits/docs/evolcore/group.md +4 -4
  103. package/kits/docs/evolcore/msg.md +5 -5
  104. package/kits/docs/evolcore/trigger.md +25 -8
  105. package/kits/docs/path-registry.md +36 -17
  106. package/kits/eck_message_manifest.json +12 -1
  107. package/kits/migrations/migrate-contact-book-v2.mjs +7 -0
  108. package/kits/rules/01-overview.md +17 -7
  109. package/kits/rules/02-navigation.md +38 -18
  110. package/kits/rules/03-identity.md +28 -24
  111. package/kits/rules/04-relation.md +44 -28
  112. package/kits/rules/05-venue.md +31 -15
  113. package/kits/rules/06-channel.md +11 -7
  114. package/kits/schemas/_meta.json +18 -7
  115. package/kits/schemas/agent-config.schema.7.json +303 -0
  116. package/kits/schemas/agent-config.schema.8.json +304 -0
  117. package/kits/schemas/agent-config.schema.9.json +374 -0
  118. package/kits/schemas/contact-book.schema.3.json +67 -0
  119. package/kits/schemas/daemon.schema.1.json +3 -2
  120. package/kits/schemas/daemon.schema.2.json +101 -0
  121. package/kits/schemas/daemon.schema.3.json +123 -0
  122. package/kits/schemas/defaults.schema.2.json +85 -0
  123. package/kits/schemas/defaults.schema.3.json +73 -0
  124. package/kits/schemas/relation-config.schema.6.json +46 -0
  125. package/kits/schemas/relation-config.schema.7.json +59 -0
  126. package/kits/schemas/single-session.schema.2.json +57 -0
  127. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
  128. package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
  129. package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
  130. package/kits/templates/roles/admin.json +46 -0
  131. package/kits/templates/roles/member.json +4 -0
  132. package/kits/templates/roles/visitor.json +4 -0
  133. package/kits/templates/system-fragments/channel.md +12 -2
  134. package/kits/templates/system-fragments/identity.md +3 -1
  135. package/kits/templates/system-fragments/relation.md +1 -1
  136. package/kits/templates/system-fragments/session.md +6 -2
  137. package/package.json +4 -2
  138. package/MIGRATION-0.5.0.md +0 -378
  139. package/ROLE_ACCESS_CONTROL.md +0 -174
  140. package/dist/channels/contact-bind-code.js +0 -134
  141. package/dist/channels/wecom-card.js +0 -101
  142. package/dist/channels/wecom-onboarding.js +0 -82
  143. package/dist/channels/wecom-state.js +0 -191
package/dist/ipc.js CHANGED
@@ -9,6 +9,7 @@ import { resolvePaths } from './paths.js';
9
9
  import { getProcessStartTime, isSameOrOlderProcess } from './utils/process-introspect.js';
10
10
  import { classifyProcessTree, ProcessTreeSampler } from './utils/process-tree-stats.js';
11
11
  import { readSystemMemoryUsage } from './utils/system-memory.js';
12
+ import { hashCurrentDelegatedCommand } from './core/auth/agent-delegation.js';
12
13
  const isWindows = process.platform === 'win32';
13
14
  const isNamedPipe = (p) => isWindows && p.startsWith('\\\\.\\pipe\\');
14
15
  export function summarizeMonitorAgents(agents, aids) {
@@ -48,7 +49,10 @@ export class IpcServer {
48
49
  configOperationExecutor;
49
50
  contactOperationExecutor;
50
51
  dingtalkContactBindExecutor;
52
+ feishuContactBindExecutor;
53
+ qqbotContactBindExecutor;
51
54
  wecomContactBindExecutor;
55
+ wechatContactBindExecutor;
52
56
  // CPU 占用追踪:IPC handler 是一次性同步调用,无法在响应里做 200ms 异步采样,
53
57
  // 故用后台 1s interval 累积 process.cpuUsage() 增量,handler 直接读最近值。
54
58
  // procCpuPercent = 本 daemon 进程占单核的百分比(可 >100% 仅当多核,已 clamp 到 100);
@@ -142,10 +146,22 @@ export class IpcServer {
142
146
  setDingtalkContactBindExecutor(executor) {
143
147
  this.dingtalkContactBindExecutor = executor;
144
148
  }
149
+ /** Inject Feishu contact binding-code executor. */
150
+ setFeishuContactBindExecutor(executor) {
151
+ this.feishuContactBindExecutor = executor;
152
+ }
153
+ /** Inject QQ contact binding-code executor. */
154
+ setQQBotContactBindExecutor(executor) {
155
+ this.qqbotContactBindExecutor = executor;
156
+ }
145
157
  /** Inject WeCom contact binding-code executor. */
146
158
  setWecomContactBindExecutor(executor) {
147
159
  this.wecomContactBindExecutor = executor;
148
160
  }
161
+ /** Inject WeChat contact binding-code executor. */
162
+ setWechatContactBindExecutor(executor) {
163
+ this.wechatContactBindExecutor = executor;
164
+ }
149
165
  /** Start the 1s background CPU sampling loop (for monitor-snapshot). Call after start(). */
150
166
  startCpuTracking() {
151
167
  if (this.cpuTimer)
@@ -325,22 +341,72 @@ export class IpcServer {
325
341
  if (!this.dingtalkContactBindExecutor) {
326
342
  return { ok: false, error: 'dingtalk contact bind executor not configured' };
327
343
  }
344
+ if (this.dingtalkContactBindExecutor.isChannelReady
345
+ && !this.dingtalkContactBindExecutor.isChannelReady(cmd.channelName)) {
346
+ return { ok: false, error: 'channel instance is not connected' };
347
+ }
328
348
  return this.dingtalkContactBindExecutor.register({
329
349
  selfAid: cmd.selfAid,
330
350
  channelName: cmd.channelName,
331
351
  primaryId: cmd.primaryId,
332
352
  });
333
353
  }
354
+ case 'feishu.contact-bind.register': {
355
+ if (!this.feishuContactBindExecutor) {
356
+ return { ok: false, error: 'feishu contact bind executor not configured' };
357
+ }
358
+ if (this.feishuContactBindExecutor.isChannelReady
359
+ && !this.feishuContactBindExecutor.isChannelReady(cmd.channelName)) {
360
+ return { ok: false, error: 'channel instance is not connected' };
361
+ }
362
+ return this.feishuContactBindExecutor.register({
363
+ selfAid: cmd.selfAid,
364
+ channelName: cmd.channelName,
365
+ primaryId: cmd.primaryId,
366
+ });
367
+ }
368
+ case 'qqbot.contact-bind.register': {
369
+ if (!this.qqbotContactBindExecutor) {
370
+ return { ok: false, error: 'qqbot contact bind executor not configured' };
371
+ }
372
+ if (this.qqbotContactBindExecutor.isChannelReady
373
+ && !this.qqbotContactBindExecutor.isChannelReady(cmd.channelName)) {
374
+ return { ok: false, error: 'channel instance is not connected' };
375
+ }
376
+ return this.qqbotContactBindExecutor.register({
377
+ selfAid: cmd.selfAid,
378
+ channelName: cmd.channelName,
379
+ primaryId: cmd.primaryId,
380
+ });
381
+ }
334
382
  case 'wecom.contact-bind.register': {
335
383
  if (!this.wecomContactBindExecutor) {
336
384
  return { ok: false, error: 'wecom contact bind executor not configured' };
337
385
  }
386
+ if (this.wecomContactBindExecutor.isChannelReady
387
+ && !this.wecomContactBindExecutor.isChannelReady(cmd.channelName)) {
388
+ return { ok: false, error: 'channel instance is not connected' };
389
+ }
338
390
  return this.wecomContactBindExecutor.register({
339
391
  selfAid: cmd.selfAid,
340
392
  channelName: cmd.channelName,
341
393
  primaryId: cmd.primaryId,
342
394
  });
343
395
  }
396
+ case 'wechat.contact-bind.register': {
397
+ if (!this.wechatContactBindExecutor) {
398
+ return { ok: false, error: 'wechat contact bind executor not configured' };
399
+ }
400
+ if (this.wechatContactBindExecutor.isChannelReady
401
+ && !this.wechatContactBindExecutor.isChannelReady(cmd.channelName)) {
402
+ return { ok: false, error: 'channel instance is not connected' };
403
+ }
404
+ return this.wechatContactBindExecutor.register({
405
+ selfAid: cmd.selfAid,
406
+ channelName: cmd.channelName,
407
+ primaryId: cmd.primaryId,
408
+ });
409
+ }
344
410
  case 'aun-aids': {
345
411
  const aids = this.aunAidProvider ? this.aunAidProvider() : [];
346
412
  return { ok: true, aids };
@@ -457,6 +523,7 @@ export class IpcServer {
457
523
  originSessionId: typeof cmd.originSessionId === 'string' ? cmd.originSessionId : undefined,
458
524
  originMessageId: typeof cmd.originMessageId === 'string' ? cmd.originMessageId : undefined,
459
525
  delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
526
+ delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
460
527
  log,
461
528
  });
462
529
  }
@@ -475,6 +542,7 @@ export class IpcServer {
475
542
  handoffId: typeof cmd.handoffId === 'string' ? cmd.handoffId : undefined,
476
543
  content: cmd.content,
477
544
  delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
545
+ delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
478
546
  });
479
547
  }
480
548
  catch (e) {
@@ -533,7 +601,7 @@ export class IpcServer {
533
601
  const { cmd: slashCmd, sessionId } = cmd;
534
602
  if (!slashCmd || !sessionId)
535
603
  return { ok: false, error: 'missing cmd or sessionId' };
536
- return await this.commandExecutor(slashCmd, sessionId, typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined);
604
+ return await this.commandExecutor(slashCmd, sessionId, typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
537
605
  }
538
606
  case 'config.op': {
539
607
  if (!this.configOperationExecutor)
@@ -545,7 +613,7 @@ export class IpcServer {
545
613
  if (delegationToken !== undefined && typeof delegationToken !== 'string') {
546
614
  return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
547
615
  }
548
- return await this.configOperationExecutor(argv, sessionId, delegationToken);
616
+ return await this.configOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
549
617
  }
550
618
  case 'contact.op': {
551
619
  if (!this.contactOperationExecutor)
@@ -557,7 +625,7 @@ export class IpcServer {
557
625
  if (delegationToken !== undefined && typeof delegationToken !== 'string') {
558
626
  return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
559
627
  }
560
- return await this.contactOperationExecutor(argv, sessionId, delegationToken);
628
+ return await this.contactOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
561
629
  }
562
630
  case 'trigger.list':
563
631
  case 'trigger.show':
@@ -613,6 +681,10 @@ export class IpcServer {
613
681
  return { ok: false, error: `Agent "${name}" not found` };
614
682
  if (!this.agentRegistry.reload)
615
683
  return { ok: false, error: 'EvolAgentRegistry.reload not available' };
684
+ // 'kits' 组(manifest / fragment / schema / 角色模板)走 on-reload 策略,平时不查盘:
685
+ // reload 是它唯一的刷新时机,必须先清再 reload,否则重载出来的 agent 仍用旧模板。
686
+ const { invalidateKitCache } = await import('./eck/kit-renderer.js');
687
+ invalidateKitCache();
616
688
  await this.agentRegistry.reload(name, hooks);
617
689
  return { ok: true, result: `Agent "${name}" reloaded` };
618
690
  }
@@ -745,6 +817,11 @@ export class IpcServer {
745
817
  * Returns null if the service is not running or the socket is unreachable.
746
818
  */
747
819
  export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
820
+ const delegationCommandHash = typeof cmd.delegationToken === 'string'
821
+ && typeof cmd.delegationCommandHash !== 'string'
822
+ ? hashCurrentDelegatedCommand()
823
+ : undefined;
824
+ const request = delegationCommandHash ? { ...cmd, delegationCommandHash } : cmd;
748
825
  return new Promise((resolve) => {
749
826
  const conn = net.connect(socketPath);
750
827
  let buf = '';
@@ -753,7 +830,7 @@ export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
753
830
  resolve(null);
754
831
  }, timeoutMs);
755
832
  conn.on('connect', () => {
756
- conn.write(JSON.stringify(cmd) + '\n');
833
+ conn.write(JSON.stringify(request) + '\n');
757
834
  });
758
835
  conn.on('data', (data) => {
759
836
  buf += data.toString();
package/dist/paths.js CHANGED
@@ -86,6 +86,9 @@ export function agentConfig(aid) {
86
86
  export function agentContactConfig(aid) {
87
87
  return path.join(agentDir(aid), 'contact.json');
88
88
  }
89
+ export function agentContactOperations(aid) {
90
+ return path.join(agentDir(aid), 'contact-operations.jsonl');
91
+ }
89
92
  export function agentPersonalDir(aid) {
90
93
  return path.join(agentDir(aid), 'personal');
91
94
  }
@@ -0,0 +1,29 @@
1
+ import { ResponseModeResolver } from './resolver.js';
2
+ /** Extract mode-specific factory defaults from the registered mode schema. */
3
+ export function responseModeSchemaDefaults(mode) {
4
+ const properties = mode.configSchema?.properties;
5
+ if (!properties)
6
+ return {};
7
+ const defaults = {};
8
+ for (const [key, spec] of Object.entries(properties)) {
9
+ if (spec && typeof spec === 'object' && 'default' in spec) {
10
+ defaults[key] = spec.default;
11
+ }
12
+ }
13
+ return defaults;
14
+ }
15
+ /**
16
+ * Resolve the selected mode and assemble its effective parameter bucket.
17
+ * Response processing and pre-processing consumers such as Session Renew must
18
+ * share this function so they cannot select different mode configurations.
19
+ */
20
+ export function resolveEffectiveResponseModeConfig(registry, config, resolvedChatMode) {
21
+ const resolved = new ResponseModeResolver(registry).resolve(config.responseMode);
22
+ const modeSpecificParams = config.responseModeParams?.[resolved.mode.id] ?? {};
23
+ const modeConfig = {
24
+ ...responseModeSchemaDefaults(resolved.mode),
25
+ ...(resolvedChatMode !== undefined ? { chatmode: resolvedChatMode } : {}),
26
+ ...modeSpecificParams,
27
+ };
28
+ return { ...resolved, modeConfig };
29
+ }
@@ -13,33 +13,13 @@
13
13
  * - 响应模式成为「响应决策的源头」,下游 message-processor 的执行流程原样保留
14
14
  * - 异常不降级(D6):解析/决策失败时回落到安全默认,记 WARN
15
15
  */
16
- import { ResponseModeResolver } from './resolver.js';
17
16
  import { ResponseModeContextBuilder } from './context-builder.js';
18
- /**
19
- * 从模式的 configSchema 提取特有参数出厂默认值:`properties[key].default`。
20
- *
21
- * 模式 schema 是模式特有参数「候选值 + 默认值」的唯一事实源(architecture §五)。
22
- * 宿主组装 modeConfig 时先铺这层默认,再叠用户显式配置——默认来源收敛到 schema,
23
- * flow 不再硬编码 `?? true` 之类的默认。chatMode 无 default(宿主强制注入),跳过。
24
- */
25
- function schemaDefaults(mode) {
26
- const props = mode.configSchema?.properties;
27
- if (!props)
28
- return {};
29
- const out = {};
30
- for (const [key, spec] of Object.entries(props)) {
31
- if (spec && typeof spec === 'object' && 'default' in spec)
32
- out[key] = spec.default;
33
- }
34
- return out;
35
- }
17
+ import { resolveEffectiveResponseModeConfig } from './config-resolver.js';
36
18
  export class ResponseModeCoordinator {
37
19
  registry;
38
- resolver;
39
20
  contextBuilder = new ResponseModeContextBuilder();
40
21
  constructor(registry) {
41
22
  this.registry = registry;
42
- this.resolver = new ResponseModeResolver(registry);
43
23
  }
44
24
  /**
45
25
  * 解析会话该用哪个响应模式 + 构建 context(不调 handleInbound)。
@@ -54,12 +34,11 @@ export class ResponseModeCoordinator {
54
34
  */
55
35
  resolveMode(responseModeId, resolvedChatMode, responseModeParams, contextDeps) {
56
36
  try {
57
- const resolved = this.resolver.resolve(responseModeId);
58
- const mode = resolved.mode;
59
- // 模式特有参数:先铺 schema 出厂默认,再叠用户显式桶(responseModeParams[modeId],显式值优先);
60
- // chatmode 由宿主解析后强制注入(小写,与配置层 chatmode 字典一致)。
61
- const modeSpecificParams = responseModeParams?.[mode.id] ?? {};
62
- const modeConfig = { ...schemaDefaults(mode), chatmode: resolvedChatMode, ...modeSpecificParams };
37
+ const resolved = resolveEffectiveResponseModeConfig(this.registry, {
38
+ responseMode: responseModeId,
39
+ responseModeParams,
40
+ }, resolvedChatMode);
41
+ const { mode, modeConfig } = resolved;
63
42
  contextDeps.logger.debug('[ResponseSystem] resolveMode mode=' + mode.id + ' source=' + resolved.source + ' chatmode=' + (modeConfig.chatmode ?? 'none'));
64
43
  const context = this.contextBuilder.build(mode.id, { ...contextDeps, modeConfig });
65
44
  return { mode, context, source: resolved.source };
@@ -78,11 +57,8 @@ export class ResponseModeCoordinator {
78
57
  async resolveInbound(message, deps, resolvedChatMode) {
79
58
  try {
80
59
  // 解析模式:标量 responseMode(合并后)> 注册表首选。chatMode 不参与选模式。
81
- const resolved = this.resolver.resolve(deps.agentConfig.responseMode);
82
- const mode = resolved.mode;
83
- // 先铺 schema 出厂默认,再叠用户显式桶(显式值优先),chatmode 强制注入。
84
- const modeSpecificParams = deps.agentConfig.responseModeParams?.[mode.id] ?? {};
85
- const modeConfig = { ...schemaDefaults(mode), chatmode: resolvedChatMode, ...modeSpecificParams };
60
+ const resolved = resolveEffectiveResponseModeConfig(this.registry, deps.agentConfig, resolvedChatMode);
61
+ const { mode, modeConfig } = resolved;
86
62
  deps.contextDeps.logger.debug('[ResponseSystem] resolveInbound mode=' + mode.id + ' source=' + resolved.source + ' chatmode=' + (modeConfig.chatmode ?? 'none'));
87
63
  const context = this.contextBuilder.build(mode.id, {
88
64
  ...deps.contextDeps,
@@ -19,7 +19,7 @@ function isHumanFacingMessage(message) {
19
19
  }
20
20
  function buildState(message, cfg) {
21
21
  // 默认值不在此硬编码:候选/出厂默认由模式 schema 声明,宿主组装 modeConfig 时注入
22
- // (coordinator.schemaDefaults)。此处只读注入后的布尔——键缺失即视为关闭。
22
+ // (responseModeSchemaDefaults)。此处只读注入后的布尔——键缺失即视为关闭。
23
23
  const preTool1stMsgChk = cfg.pre_tool_1stmsgchk === true;
24
24
  const toolUseReminder = cfg.tool_use_reminder === true;
25
25
  const humanFacing = isHumanFacingMessage(message);
@@ -4,3 +4,4 @@
4
4
  * 统一导出响应系统的所有公共接口
5
5
  */
6
6
  export { hasAuxiliarySession, hasThreadTracking, hasWorkflow, hasBatchProcessing, hasStatePersistence, } from './extensions.js';
7
+ export { resolveEffectiveResponseModeConfig, responseModeSchemaDefaults, } from './config-resolver.js';
@@ -2,12 +2,15 @@ import { FIFOQueue } from '../../queues/fifo-queue.js';
2
2
  import { flowForChatMode } from '../../engines/v1/index.js';
3
3
  import { loadSchema } from '../../../config/schema-registry.js';
4
4
  /**
5
- * 模式桶 schema —— 候选值与默认值的**唯一事实源**,从 kits/schemas/single-session.schema.1.json
5
+ * 模式桶 schema —— 候选值与默认值的**唯一事实源**,从当前 single-session schema
6
6
  * 读取(随包分发、config schema 子命令可展示、write 时桶专项校验共用同一份)。
7
- * `default` 即运行时出厂默认,由宿主组装 modeConfig 时提取注入(coordinator.schemaDefaults),
7
+ * `default` 即运行时出厂默认,由共享 config resolver 组装 modeConfig 时提取注入,
8
8
  * flow 不再硬编码默认。chatMode 无 default——由宿主按 chatmode 场景表解析后强制注入。
9
+ *
10
+ * 每次现取而非模块初始化时定格:`loadSchema` 自己走 FileCache(读是缓存命中),
11
+ * 定格反而会绕过 invalidateKitCache——schema 改了、registry 已换新,本模式却仍
12
+ * 用旧默认值。
9
13
  */
10
- const CONFIG_SCHEMA = loadSchema('single-session').raw;
11
14
  /**
12
15
  * 单会话响应模式(architecture.md §4.1)—— 合并原 interactive / proactive。
13
16
  *
@@ -21,7 +24,7 @@ export class SingleSessionMode {
21
24
  description = '单会话直接响应,按 chatMode 决定投递方式。';
22
25
  type = 'builtin';
23
26
  applicableScenes = ['private', 'group'];
24
- configSchema = CONFIG_SCHEMA;
27
+ get configSchema() { return loadSchema('single-session').raw; }
25
28
  queue = new FIFOQueue();
26
29
  context;
27
30
  async initialize(context) {
@@ -27,6 +27,34 @@ function parseFlags(args) {
27
27
  }
28
28
  return flags;
29
29
  }
30
+ function promptSyntaxError(args) {
31
+ const match = /--prompt(?:\s+|=|$)/.exec(args);
32
+ if (!match)
33
+ return undefined;
34
+ const rest = args.slice((match.index ?? 0) + match[0].length).trimStart();
35
+ const quote = rest[0];
36
+ if (quote !== '"' && quote !== "'")
37
+ return undefined;
38
+ for (let index = 1; index < rest.length; index++) {
39
+ if (rest[index] === '\\') {
40
+ index += 1;
41
+ continue;
42
+ }
43
+ if (rest[index] === quote)
44
+ return undefined;
45
+ }
46
+ return '--prompt 引号未闭合;复杂内容建议改用 --prompt-file <path>';
47
+ }
48
+ function inlinePromptPresent(args) {
49
+ if (Array.isArray(args))
50
+ return args.some(arg => arg === '--prompt' || arg.startsWith('--prompt='));
51
+ return /--prompt(?:\s+|=|$)/.test(args);
52
+ }
53
+ function withPromptFileHint(error, args, opts = {}) {
54
+ if (!inlinePromptPresent(args) || opts.allowLongPrompt || error.includes('--prompt-file'))
55
+ return error;
56
+ return `${error};复杂内容建议改用 --prompt-file <path>`;
57
+ }
30
58
  export function parseDuration(s) {
31
59
  const m = /^([1-9]\d*)([dhms])$/.exec(s.trim().toLowerCase());
32
60
  if (!m)
@@ -326,9 +354,10 @@ function commonParsed(flags, opts = {}) {
326
354
  }
327
355
  const prompt = flags.get('prompt');
328
356
  if (prompt === true)
329
- return { ok: false, error: '--prompt 不能为空' };
330
- if (typeof prompt === 'string' && prompt.length > 4096)
331
- return { ok: false, error: '--prompt 超过 4096 字符限制' };
357
+ return { ok: false, error: '--prompt 不能为空;复杂内容建议改用 --prompt-file <path>' };
358
+ if (typeof prompt === 'string' && prompt.length > 4096 && !opts.allowLongPrompt) {
359
+ return { ok: false, error: '--prompt 超过 4096 字符限制;复杂内容建议改用 --prompt-file <path>' };
360
+ }
332
361
  const scriptPath = flags.get('script-path');
333
362
  if (scriptPath === true)
334
363
  return { ok: false, error: '--script-path 不能为空' };
@@ -351,7 +380,10 @@ function commonParsed(flags, opts = {}) {
351
380
  if ((targetChannel === undefined) !== (targetChannelId === undefined)) {
352
381
  return { ok: false, error: '--target-channel 与 --target-channel-id 必须同时指定或同时省略' };
353
382
  }
354
- const inferredExecution = execution.value ?? (scriptPath ? 'script' : 'target_session');
383
+ if (!opts.update && !execution.value) {
384
+ return { ok: false, error: '创建 trigger 必须显式指定 --exec script|trigger-session|target-session' };
385
+ }
386
+ const inferredExecution = execution.value;
355
387
  if (baseagent.value && inferredExecution !== 'trigger_session') {
356
388
  return { ok: false, error: '--baseagent 仅适用于 --exec trigger-session' };
357
389
  }
@@ -395,10 +427,13 @@ function commonParsed(flags, opts = {}) {
395
427
  };
396
428
  }
397
429
  export function parseTriggerSet(args) {
430
+ const syntaxError = promptSyntaxError(args);
431
+ if (syntaxError)
432
+ return { ok: false, error: syntaxError };
398
433
  const flags = parseFlags(args);
399
434
  const parsed = commonParsed(flags);
400
435
  if (!parsed.ok)
401
- return parsed;
436
+ return { ok: false, error: withPromptFileHint(parsed.error, args) };
402
437
  const value = parsed.value;
403
438
  return {
404
439
  ok: true,
@@ -431,13 +466,13 @@ export function parseTriggerSet(args) {
431
466
  }
432
467
  /** CLI argv parser. Unlike the slash-command string parser, this preserves
433
468
  * shell-quoted values such as `--prompt "multi word text"` as one token. */
434
- export function parseTriggerSetArgv(args) {
469
+ export function parseTriggerSetArgv(args, opts = {}) {
435
470
  const parsedFlags = flagsFromArgv(args, 'create');
436
471
  if (!parsedFlags.ok)
437
- return parsedFlags;
438
- const parsed = commonParsed(parsedFlags.flags);
472
+ return { ok: false, error: withPromptFileHint(parsedFlags.error, args, opts) };
473
+ const parsed = commonParsed(parsedFlags.flags, opts);
439
474
  if (!parsed.ok)
440
- return parsed;
475
+ return { ok: false, error: withPromptFileHint(parsed.error, args, opts) };
441
476
  const value = parsed.value;
442
477
  return {
443
478
  ok: true,
@@ -491,14 +526,19 @@ export function parseTriggerUpdate(args) {
491
526
  }
492
527
  if (!rest)
493
528
  return { ok: false, error: '至少需要指定一个修改参数(如 --prompt、--delay、--event 等)' };
529
+ const syntaxError = promptSyntaxError(rest);
530
+ if (syntaxError)
531
+ return { ok: false, error: syntaxError };
494
532
  const flags = parseFlags(rest);
495
- return parseTriggerUpdateFlags(nameOrId, flags);
533
+ const parsed = parseTriggerUpdateFlags(nameOrId, flags);
534
+ return parsed.ok ? parsed : { ok: false, error: withPromptFileHint(parsed.error, rest) };
496
535
  }
497
- export function parseTriggerUpdateArgv(nameOrId, args) {
536
+ export function parseTriggerUpdateArgv(nameOrId, args, opts = {}) {
498
537
  const parsedFlags = flagsFromArgv(args, 'update');
499
538
  if (!parsedFlags.ok)
500
- return parsedFlags;
501
- return parseTriggerUpdateFlags(nameOrId, parsedFlags.flags);
539
+ return { ok: false, error: withPromptFileHint(parsedFlags.error, args, opts) };
540
+ const parsed = parseTriggerUpdateFlags(nameOrId, parsedFlags.flags, opts);
541
+ return parsed.ok ? parsed : { ok: false, error: withPromptFileHint(parsed.error, args, opts) };
502
542
  }
503
543
  function flagsFromArgv(args, command) {
504
544
  const flags = new Map();
@@ -523,14 +563,14 @@ function flagsFromArgv(args, command) {
523
563
  }
524
564
  return { ok: true, flags };
525
565
  }
526
- function parseTriggerUpdateFlags(nameOrId, flags) {
566
+ function parseTriggerUpdateFlags(nameOrId, flags, opts = {}) {
527
567
  if (flags.size === 0)
528
568
  return { ok: false, error: '至少需要指定一个修改参数(如 --prompt、--delay、--event 等)' };
529
569
  for (const flag of flags.keys()) {
530
570
  if (!TRIGGER_UPDATE_FLAGS.has(flag))
531
571
  return { ok: false, error: `update 不支持参数 --${flag}` };
532
572
  }
533
- const parsed = commonParsed(flags, { update: true });
573
+ const parsed = commonParsed(flags, { update: true, ...opts });
534
574
  if (!parsed.ok)
535
575
  return parsed;
536
576
  const value = parsed.value;
@@ -13,7 +13,7 @@ const PATCH_KEYS = new Set([
13
13
  'concurrency',
14
14
  'missedPolicy',
15
15
  ]);
16
- export function applyTriggerPatch(definition, input) {
16
+ export function applyTriggerPatch(definition, input, opts = {}) {
17
17
  if (!input || typeof input !== 'object' || Array.isArray(input)) {
18
18
  throw new Error('trigger patch must be an object');
19
19
  }
@@ -30,6 +30,9 @@ export function applyTriggerPatch(definition, input) {
30
30
  updated.name = String(patch.name);
31
31
  applySourcePatch(updated, patch);
32
32
  if (patch.prompt !== undefined) {
33
+ if (opts.fromPromptFile && updated.execution.type === 'script') {
34
+ throw new Error('script 类型 trigger 不支持 --prompt-file;请修改脚本文件后重新创建 trigger');
35
+ }
33
36
  if (updated.execution.type === 'script')
34
37
  updated.feedback.onReply = { template: String(patch.prompt) };
35
38
  else