evolcore 0.0.2 → 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 (206) hide show
  1. package/CHANGELOG.md +93 -778
  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 +198 -48
  6. package/dist/agents/codex-app-server-client.js +68 -0
  7. package/dist/agents/codex-runner.js +203 -8
  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/aun/outbox.js +8 -0
  14. package/dist/channels/aun.js +241 -116
  15. package/dist/channels/daemon.js +18 -4
  16. package/dist/channels/dingtalk.js +1005 -260
  17. package/dist/channels/feishu.js +184 -56
  18. package/dist/channels/qqbot.js +23 -1
  19. package/dist/channels/wechat.js +303 -155
  20. package/dist/channels/wecom.js +1132 -164
  21. package/dist/cli/agent-command.js +2 -1
  22. package/dist/cli/aun-commands.js +98 -43
  23. package/dist/cli/bench.js +2 -2
  24. package/dist/cli/contact.js +71 -0
  25. package/dist/cli/ctl-command.js +2 -2
  26. package/dist/cli/daemon-commands.js +77 -214
  27. package/dist/cli/handoff-command.js +4 -3
  28. package/dist/cli/help.js +9 -5
  29. package/dist/cli/index.js +132 -116
  30. package/dist/cli/init-channel.js +242 -129
  31. package/dist/cli/init.js +70 -31
  32. package/dist/cli/model.js +2 -1
  33. package/dist/cli/net-check.js +2 -2
  34. package/dist/cli/queue-command.js +30 -6
  35. package/dist/cli/raw-key-input.js +25 -0
  36. package/dist/cli/response.js +5 -6
  37. package/dist/cli/restart-monitor.js +25 -1
  38. package/dist/cli/stats.js +6 -4
  39. package/dist/cli/trigger-command.js +146 -25
  40. package/dist/cli/version.js +6 -1
  41. package/dist/config/access-policy-domain.js +38 -0
  42. package/dist/config/access-policy.js +134 -0
  43. package/dist/config/builtin-role-templates.js +42 -17
  44. package/dist/config/builtin-roles.js +31 -6
  45. package/dist/config/config-field-policy.js +17 -5
  46. package/dist/config/config-manager.js +409 -29
  47. package/dist/config/config-operation-service.js +35 -38
  48. package/dist/config/contact-alias.js +68 -0
  49. package/dist/config/contact-bind-code.js +274 -0
  50. package/dist/config/contact-book-store.js +622 -0
  51. package/dist/config/contact-book-v2-startup.js +35 -0
  52. package/dist/config/contact-book.js +185 -300
  53. package/dist/config/contact-operation-service.js +116 -0
  54. package/dist/config/contact-request-service.js +331 -0
  55. package/dist/config/peer-role-resolver.js +136 -55
  56. package/dist/config/role-ranks.js +18 -0
  57. package/dist/config/role-service.js +16 -19
  58. package/dist/config/role-store.js +16 -5
  59. package/dist/config/roles.js +10 -1
  60. package/dist/config/schema-registry.js +49 -24
  61. package/dist/config-store.js +14 -4
  62. package/dist/core/auth/agent-delegation.js +105 -11
  63. package/dist/core/auth/authorization-audit.js +16 -1
  64. package/dist/core/auth/operation-authorizer.js +10 -0
  65. package/dist/core/auth/operation-catalog.js +105 -1
  66. package/dist/core/auth/trigger-authorization.js +15 -0
  67. package/dist/core/bootstrap-service.js +2 -9
  68. package/dist/core/channel-loader.js +6 -2
  69. package/dist/core/command/command-handler.js +113 -21
  70. package/dist/core/command/connect-menu.js +599 -0
  71. package/dist/core/command/evol-menu-version-gate.js +38 -0
  72. package/dist/core/command/group-menu.js +421 -0
  73. package/dist/core/command/menu-handler.js +382 -100
  74. package/dist/core/command/menu-protocol.js +8 -2
  75. package/dist/core/command/menu-token-store.js +102 -0
  76. package/dist/core/command/role-menu.js +128 -29
  77. package/dist/core/command/slash-gate.js +1 -1
  78. package/dist/core/command/slash-handler.js +186 -49
  79. package/dist/core/daemon-file-cache.js +40 -6
  80. package/dist/core/event-catalog.js +99 -0
  81. package/dist/core/evolagent-registry.js +4 -40
  82. package/dist/core/evolagent.js +20 -9
  83. package/dist/core/handoff/runtime.js +162 -37
  84. package/dist/core/handoff/store.js +46 -2
  85. package/dist/core/handoff/types.js +1 -0
  86. package/dist/core/inference/text-inference.js +16 -74
  87. package/dist/core/message/file-markers.js +7 -0
  88. package/dist/core/message/im-renderer.js +20 -14
  89. package/dist/core/message/inbound-admission.js +124 -0
  90. package/dist/core/message/message-bridge.js +670 -57
  91. package/dist/core/message/message-log.js +1 -0
  92. package/dist/core/message/message-queue.js +195 -10
  93. package/dist/core/message/message-utils.js +8 -2
  94. package/dist/core/message/peer-mode.js +7 -8
  95. package/dist/core/message/response-engine.js +642 -96
  96. package/dist/core/message/send-receipt.js +24 -0
  97. package/dist/core/message/stream-debouncer.js +11 -2
  98. package/dist/core/permission/approval-gateway.js +17 -10
  99. package/dist/core/permission/ec-command-parser.js +101 -46
  100. package/dist/core/permission/tool-policy.js +115 -38
  101. package/dist/core/protected-paths.js +38 -11
  102. package/dist/core/session/session-fs-store.js +44 -3
  103. package/dist/core/session/session-manager.js +86 -8
  104. package/dist/core/session/session-mapper.js +2 -0
  105. package/dist/core/session/session-renew.js +125 -69
  106. package/dist/core/session/session-turn-coordinator.js +5 -1
  107. package/dist/eck/kit-renderer.js +12 -1
  108. package/dist/eck/message-renderer.js +79 -1
  109. package/dist/index.js +285 -94
  110. package/dist/ipc.js +114 -8
  111. package/dist/paths.js +3 -0
  112. package/dist/response-system/config-resolver.js +29 -0
  113. package/dist/response-system/coordinator.js +8 -32
  114. package/dist/response-system/engines/v1/proactive-flow.js +1 -1
  115. package/dist/response-system/index.js +1 -0
  116. package/dist/response-system/modes/single-session/index.js +7 -4
  117. package/dist/stats/billing.js +20 -8
  118. package/dist/trigger/manager.js +3 -0
  119. package/dist/trigger/parser.js +126 -11
  120. package/dist/trigger/patch.js +12 -2
  121. package/dist/trigger/scheduler.js +444 -40
  122. package/dist/trigger/validation.js +13 -0
  123. package/dist/utils/aid-bind.js +43 -29
  124. package/dist/utils/error-dict.json +7 -0
  125. package/dist/utils/evolcore-version.js +21 -0
  126. package/dist/utils/instance-registry.js +14 -7
  127. package/dist/utils/log-writer.js +46 -0
  128. package/dist/utils/logger.js +2 -2
  129. package/dist/utils/media-cache.js +4 -1
  130. package/dist/utils/model-prices.jsonl +6 -3
  131. package/dist/utils/restart-safety.js +31 -0
  132. package/dist/utils/stable-semver.js +21 -0
  133. package/dist/utils/stats.js +33 -9
  134. package/dist/utils/system-memory.js +62 -0
  135. package/kits/docs/INDEX.md +2 -1
  136. package/kits/docs/channels/aun.md +4 -13
  137. package/kits/docs/evolcore/INDEX.md +5 -3
  138. package/kits/docs/evolcore/agent.md +9 -1
  139. package/kits/docs/evolcore/aid.md +5 -2
  140. package/kits/docs/evolcore/config.md +47 -2
  141. package/kits/docs/evolcore/contact.md +61 -0
  142. package/kits/docs/evolcore/fs.md +9 -0
  143. package/kits/docs/evolcore/group-rules.md +46 -4
  144. package/kits/docs/evolcore/group.md +15 -6
  145. package/kits/docs/evolcore/model.md +4 -1
  146. package/kits/docs/evolcore/msg.md +12 -6
  147. package/kits/docs/evolcore/response.md +16 -21
  148. package/kits/docs/evolcore/rpc.md +2 -0
  149. package/kits/docs/evolcore/stats.md +15 -2
  150. package/kits/docs/evolcore/storage.md +1 -0
  151. package/kits/docs/evolcore/trigger.md +42 -10
  152. package/kits/docs/path-registry.md +36 -17
  153. package/kits/eck_manifest.json +12 -0
  154. package/kits/eck_message_manifest.json +12 -1
  155. package/kits/migrations/migrate-contact-book-v2.mjs +754 -0
  156. package/kits/rules/01-overview.md +17 -7
  157. package/kits/rules/02-navigation.md +38 -18
  158. package/kits/rules/03-identity.md +28 -24
  159. package/kits/rules/04-relation.md +44 -28
  160. package/kits/rules/05-venue.md +31 -15
  161. package/kits/rules/06-channel.md +11 -7
  162. package/kits/schemas/_meta.json +21 -7
  163. package/kits/schemas/agent-config.schema.6.json +322 -0
  164. package/kits/schemas/agent-config.schema.7.json +303 -0
  165. package/kits/schemas/agent-config.schema.8.json +304 -0
  166. package/kits/schemas/agent-config.schema.9.json +374 -0
  167. package/kits/schemas/contact-book.schema.2.json +43 -0
  168. package/kits/schemas/contact-book.schema.3.json +67 -0
  169. package/kits/schemas/daemon.schema.1.json +3 -2
  170. package/kits/schemas/daemon.schema.2.json +101 -0
  171. package/kits/schemas/daemon.schema.3.json +123 -0
  172. package/kits/schemas/defaults.schema.2.json +85 -0
  173. package/kits/schemas/defaults.schema.3.json +73 -0
  174. package/kits/schemas/relation-config.schema.5.json +47 -0
  175. package/kits/schemas/relation-config.schema.6.json +46 -0
  176. package/kits/schemas/relation-config.schema.7.json +59 -0
  177. package/kits/schemas/role-config.schema.1.json +1 -0
  178. package/kits/schemas/role-registry.schema.1.json +2 -2
  179. package/kits/schemas/single-session.schema.2.json +57 -0
  180. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
  181. package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
  182. package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
  183. package/kits/templates/roles/admin.json +47 -0
  184. package/kits/templates/roles/member.json +5 -0
  185. package/kits/templates/roles/owner.json +1 -0
  186. package/kits/templates/roles/visitor.json +5 -0
  187. package/kits/templates/system-fragments/channel.md +12 -2
  188. package/kits/templates/system-fragments/commands.md +3 -1
  189. package/kits/templates/system-fragments/identity.md +3 -1
  190. package/kits/templates/system-fragments/relation.md +1 -1
  191. package/kits/templates/system-fragments/session.md +6 -2
  192. package/package.json +7 -5
  193. package/MIGRATION-0.5.0.md +0 -378
  194. package/ROLE_ACCESS_CONTROL.md +0 -174
  195. package/assets/brand/evolcore/README.md +0 -19
  196. package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
  197. package/assets/brand/evolcore/evolcore-app-icon.svg +0 -13
  198. package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
  199. package/assets/brand/evolcore/evolcore-brand-board.svg +0 -126
  200. package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
  201. package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
  202. package/assets/brand/evolcore/evolcore-logo-reverse.svg +0 -14
  203. package/assets/brand/evolcore/evolcore-logo.png +0 -0
  204. package/assets/brand/evolcore/evolcore-logo.svg +0 -14
  205. package/assets/brand/evolcore/evolcore-mark.png +0 -0
  206. package/assets/brand/evolcore/evolcore-mark.svg +0 -10
@@ -3,8 +3,11 @@ import { formatPeerKey } from '../relation/peer-identity.js';
3
3
  import { logger } from '../../utils/logger.js';
4
4
  import { isTransientProtocolMessage, messageLogPath } from '../message/message-log.js';
5
5
  import { readAllJsonlLines } from './session-fs-store.js';
6
+ import { ResponseModeRegistry } from '../../response-system/registry.js';
7
+ import { registerBuiltinModes } from '../../response-system/modes/index.js';
8
+ import { resolveEffectiveResponseModeConfig } from '../../response-system/config-resolver.js';
9
+ import { normalizeBaseagent } from '../../agents/baseagent.js';
6
10
  const DEFAULT_AFTER_HOURS = 24;
7
- const DEFAULT_EFFORT = 'low';
8
11
  const DEFAULT_FALLBACK = 'continue';
9
12
  const MAX_CONTEXT_MESSAGES = 40;
10
13
  const MAX_CONTEXT_CHARS = 16_000;
@@ -12,7 +15,7 @@ const MAX_MESSAGE_CHARS = 2_000;
12
15
  const MODEL_TIMEOUT_MS = 10_000;
13
16
  const RECENT_DECISION_TTL_MS = 30_000;
14
17
  const NEW_CONFIDENCE_THRESHOLD = 0.85;
15
- const JUDGE_BASEAGENT = 'claude';
18
+ const AUXILIARY_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
16
19
  const EXPLICIT_NEW_RE = /^(?:新话题|换个话题|重新开始|新开会话|新会话)[::,,;;。!!\n]/;
17
20
  const EXPLICIT_CONTINUE_RE = /^(?:继续上次|延续上次|继续|接着)[::,,;;。!!\n]/;
18
21
  const NON_CONVERSATION_TYPES = new Set([
@@ -43,41 +46,6 @@ export function selectSessionRenewContext(entries) {
43
46
  const selected = [entries[0], ...entries.slice(-(MAX_CONTEXT_MESSAGES - 1))];
44
47
  return trimContextByChars(selected);
45
48
  }
46
- export function selectLatestHaikuModel(models) {
47
- let selected;
48
- for (const rawModel of models) {
49
- const id = rawModel.trim();
50
- if (!id)
51
- continue;
52
- const leaf = id.toLowerCase().split('/').at(-1) ?? '';
53
- const tokens = leaf.split('-');
54
- const haikuIndex = tokens.indexOf('haiku');
55
- if (haikuIndex < 0)
56
- continue;
57
- const versionTokens = haikuIndex <= 1
58
- ? tokens.slice(haikuIndex + 1)
59
- : tokens.slice(1, haikuIndex);
60
- const versions = versionTokens
61
- .filter(token => /^\d{1,2}$/.test(token))
62
- .map(Number);
63
- const dates = tokens
64
- .filter(token => /^\d{8}$/.test(token))
65
- .map(Number);
66
- const candidate = {
67
- id,
68
- major: versions[0] ?? 0,
69
- minor: versions[1] ?? 0,
70
- date: dates.length > 0 ? Math.max(...dates) : 0,
71
- };
72
- if (!selected
73
- || candidate.major > selected.major
74
- || (candidate.major === selected.major && candidate.minor > selected.minor)
75
- || (candidate.major === selected.major && candidate.minor === selected.minor && candidate.date > selected.date)) {
76
- selected = candidate;
77
- }
78
- }
79
- return selected?.id;
80
- }
81
49
  function trimContextByChars(entries) {
82
50
  if (entries.length === 0)
83
51
  return [];
@@ -100,17 +68,27 @@ export class SessionRenewService {
100
68
  locks = new Map();
101
69
  recentDecisions = new Map();
102
70
  now;
103
- configResolver;
71
+ effectiveConfigResolver;
72
+ eventBus;
73
+ responseModeRegistry = new ResponseModeRegistry();
104
74
  constructor(sessionManager, inferenceProviderResolver, options = {}) {
105
75
  this.sessionManager = sessionManager;
106
76
  this.inferenceProviderResolver = inferenceProviderResolver;
107
77
  this.now = options.now ?? (() => Date.now());
108
- this.configResolver = options.resolveConfig;
78
+ this.effectiveConfigResolver = options.resolveEffectiveConfig;
79
+ this.eventBus = options.eventBus;
80
+ registerBuiltinModes(this.responseModeRegistry);
109
81
  }
110
82
  async resolve(request) {
111
- const config = this.resolveConfig(request);
83
+ const effectiveConfig = this.resolveEffectiveConfig(request);
84
+ const config = effectiveConfig?.session_renew;
112
85
  if (!config || config.enabled !== true)
113
86
  return { session: request.session, renewed: false };
87
+ // Thread / processing sessions are already an explicit routing decision.
88
+ // Do not let a cached or active main session replace them before resolveLocked().
89
+ if (request.session.threadId || request.session.processingState) {
90
+ return { session: request.session, renewed: false };
91
+ }
114
92
  const routeKey = this.routeKey(request);
115
93
  return await this.withLock(routeKey, async () => {
116
94
  const cached = this.recentDecisions.get(routeKey);
@@ -133,12 +111,11 @@ export class SessionRenewService {
133
111
  active.identity = request.session.identity;
134
112
  return { session: active, renewed: true, decision: 'new', source: 'fallback' };
135
113
  }
136
- return await this.resolveLocked(routeKey, request, config);
114
+ return await this.resolveLocked(routeKey, request, config, effectiveConfig);
137
115
  });
138
116
  }
139
- async resolveLocked(routeKey, request, config) {
140
- if (request.session.threadId || request.session.processingState)
141
- return { session: request.session, renewed: false };
117
+ async resolveLocked(routeKey, request, config, effectiveConfig) {
118
+ const evaluationStartedAt = Date.now();
142
119
  const afterHours = validPositiveNumber(config.after_hours) ?? DEFAULT_AFTER_HOURS;
143
120
  const entries = this.readConversationEntries(request.session);
144
121
  const last = entries[entries.length - 1];
@@ -146,7 +123,7 @@ export class SessionRenewService {
146
123
  if (request.isNewSession)
147
124
  return { session: request.session, renewed: false };
148
125
  const idleMs = this.now() - request.session.updatedAt;
149
- return await this.finishDecision(routeKey, request, 'new', 'missing_history', idleMs);
126
+ return await this.finishDecision(routeKey, request, 'new', 'missing_history', idleMs, evaluationStartedAt);
150
127
  }
151
128
  const idleMs = this.now() - last.ts;
152
129
  if (idleMs <= afterHours * 60 * 60 * 1000)
@@ -162,13 +139,22 @@ export class SessionRenewService {
162
139
  source = 'reply';
163
140
  }
164
141
  else {
165
- let judgeModel;
142
+ const auxiliary = this.resolveAuxiliaryConfig(effectiveConfig, request.session.baseagent);
143
+ let judgeMetadata = {
144
+ responseMode: auxiliary.responseMode,
145
+ baseagent: request.session.baseagent,
146
+ model: auxiliary.model,
147
+ effort: auxiliary.effort,
148
+ };
166
149
  try {
167
- const provider = this.inferenceProviderResolver.getTextInferenceProvider(request.channelName, JUDGE_BASEAGENT, request.selfAid);
150
+ if (!auxiliary.model)
151
+ throw new SessionRenewJudgeError('auxiliary_model_missing');
152
+ if (auxiliary.invalidEffort)
153
+ throw new SessionRenewJudgeError('auxiliary_effort_invalid');
154
+ const provider = this.inferenceProviderResolver.getTextInferenceProvider(request.channelName, request.session.baseagent, request.selfAid);
168
155
  if (!provider)
169
- throw new Error(`text inference provider unavailable: ${JUDGE_BASEAGENT}`);
170
- const judged = await this.judgeWithModel(request, config, entries, idleMs, provider);
171
- judgeModel = judged.model;
156
+ throw new SessionRenewJudgeError('provider_unavailable');
157
+ const judged = await this.judgeWithModel(request, entries, idleMs, provider, auxiliary);
172
158
  decision = judged.decision === 'new' && judged.confidence < NEW_CONFIDENCE_THRESHOLD
173
159
  ? 'continue'
174
160
  : judged.decision;
@@ -177,15 +163,19 @@ export class SessionRenewService {
177
163
  catch (error) {
178
164
  decision = config.fallback_action ?? DEFAULT_FALLBACK;
179
165
  source = 'fallback';
166
+ judgeMetadata = { ...judgeMetadata, fallbackReason: sessionRenewFallbackReason(error) };
180
167
  logger.warn(`[SessionRenew] judge failed, using fallback=${decision}: ${error instanceof Error ? error.message : String(error)}`);
181
168
  }
182
- return await this.finishDecision(routeKey, request, decision, source, idleMs, judgeModel);
169
+ return await this.finishDecision(routeKey, request, decision, source, idleMs, evaluationStartedAt, judgeMetadata);
183
170
  }
184
- return await this.finishDecision(routeKey, request, decision, source, idleMs);
171
+ return await this.finishDecision(routeKey, request, decision, source, idleMs, evaluationStartedAt);
185
172
  }
186
- async finishDecision(routeKey, request, decision, source, idleMs, judgeModel) {
173
+ async finishDecision(routeKey, request, decision, source, idleMs, evaluationStartedAt, judge) {
187
174
  const selected = decision === 'new'
188
- ? await this.sessionManager.createRenewedSession(request.session)
175
+ ? await this.sessionManager.createRenewedSession(request.session, {
176
+ source,
177
+ causation: request.causation,
178
+ })
189
179
  : request.session;
190
180
  selected.identity = request.session.identity;
191
181
  this.recentDecisions.set(routeKey, {
@@ -195,12 +185,39 @@ export class SessionRenewService {
195
185
  source,
196
186
  expiresAt: this.now() + RECENT_DECISION_TTL_MS,
197
187
  });
198
- logger.info(`[SessionRenew] session=${request.session.id} selected=${selected.id} decision=${decision} source=${source} idleHours=${(idleMs / 3_600_000).toFixed(2)} baseagent=${judgeModel ? JUDGE_BASEAGENT : 'none'} model=${judgeModel || 'none'}`);
188
+ logger.info(`[SessionRenew] session=${request.session.id} selected=${selected.id} decision=${decision} source=${source}`
189
+ + ` idleHours=${(idleMs / 3_600_000).toFixed(2)}`
190
+ + ` responseMode=${judge?.responseMode ?? 'none'}`
191
+ + ` baseagent=${judge?.baseagent ?? 'none'}`
192
+ + ` model=${judge?.model ?? 'none'}`
193
+ + ` effort=${judge?.effort ?? 'provider-default'}`
194
+ + ` fallbackReason=${judge?.fallbackReason ?? 'none'}`);
195
+ this.eventBus?.publish({
196
+ type: 'session:renew-evaluated',
197
+ candidateSessionId: request.session.id,
198
+ selectedSessionId: selected.id,
199
+ renewed: decision === 'new',
200
+ decision,
201
+ source,
202
+ channel: request.session.channel,
203
+ channelName: request.channelName,
204
+ channelId: request.channelId,
205
+ selfAid: request.selfAid,
206
+ idleHours: Number((idleMs / 3_600_000).toFixed(2)),
207
+ responseMode: judge?.responseMode,
208
+ baseagent: judge?.baseagent,
209
+ model: judge?.model,
210
+ effort: judge?.effort,
211
+ fallbackReason: judge?.fallbackReason,
212
+ durationMs: Math.max(0, Date.now() - evaluationStartedAt),
213
+ timestamp: this.now(),
214
+ causation: request.causation,
215
+ });
199
216
  return { session: selected, renewed: decision === 'new', decision, source };
200
217
  }
201
- resolveConfig(request) {
202
- if (this.configResolver)
203
- return this.configResolver(request);
218
+ resolveEffectiveConfig(request) {
219
+ if (this.effectiveConfigResolver)
220
+ return this.effectiveConfigResolver(request);
204
221
  if (!request.selfAid)
205
222
  return undefined;
206
223
  const peerKeyId = request.chatType === 'group'
@@ -210,7 +227,30 @@ export class SessionRenewService {
210
227
  return resolveEffective({
211
228
  self: request.selfAid,
212
229
  peerKey,
213
- }, { cache: true }).session_renew;
230
+ }, { cache: true, expand: true });
231
+ }
232
+ resolveAuxiliaryConfig(config, baseagent) {
233
+ const resolved = resolveEffectiveResponseModeConfig(this.responseModeRegistry, config);
234
+ const rawModel = resolved.modeConfig.auxiliaryModel;
235
+ const canonicalBaseagent = normalizeBaseagent(baseagent).canonical;
236
+ const selectedModel = typeof rawModel === 'string'
237
+ ? rawModel
238
+ : rawModel && typeof rawModel === 'object' && !Array.isArray(rawModel)
239
+ ? rawModel[canonicalBaseagent]
240
+ : undefined;
241
+ const model = typeof selectedModel === 'string' && selectedModel.trim()
242
+ ? selectedModel.trim()
243
+ : undefined;
244
+ const rawEffort = resolved.modeConfig.auxiliaryEffort;
245
+ const effort = typeof rawEffort === 'string' && AUXILIARY_EFFORTS.has(rawEffort)
246
+ ? rawEffort
247
+ : undefined;
248
+ return {
249
+ responseMode: resolved.mode.id,
250
+ model,
251
+ effort,
252
+ invalidEffort: rawEffort !== undefined && effort === undefined,
253
+ };
214
254
  }
215
255
  readConversationEntries(session) {
216
256
  const entries = readAllJsonlLines(messageLogPath(this.sessionManager.getChatDir(session)));
@@ -218,7 +258,7 @@ export class SessionRenewService {
218
258
  .filter(entry => isSessionRenewConversationEntry(entry, session.id))
219
259
  .sort((left, right) => left.ts - right.ts);
220
260
  }
221
- async judgeWithModel(request, config, entries, idleMs, provider) {
261
+ async judgeWithModel(request, entries, idleMs, provider, auxiliary) {
222
262
  const context = selectSessionRenewContext(entries).map(entry => ({
223
263
  dir: entry.dir,
224
264
  content: entry.content.slice(0, MAX_MESSAGE_CHARS),
@@ -243,20 +283,14 @@ export class SessionRenewService {
243
283
  let timer;
244
284
  try {
245
285
  const operation = (async () => {
246
- if (!provider.listModels)
247
- throw new Error('Claude text inference provider does not expose a model list');
248
- const models = await provider.listModels(controller.signal);
249
- const model = selectLatestHaikuModel(models);
250
- if (!model)
251
- throw new Error('Claude model list contains no Haiku model');
252
286
  const text = await provider.completeText({
253
- model,
254
- effort: config.effort || DEFAULT_EFFORT,
287
+ model: auxiliary.model,
288
+ ...(auxiliary.effort ? { effort: auxiliary.effort } : {}),
255
289
  system: systemPrompt,
256
290
  input: prompt,
257
291
  signal: controller.signal,
258
292
  });
259
- return { model, text };
293
+ return { model: auxiliary.model, text };
260
294
  })();
261
295
  const timeout = new Promise((_resolve, reject) => {
262
296
  timer = setTimeout(() => {
@@ -292,6 +326,28 @@ export class SessionRenewService {
292
326
  }
293
327
  }
294
328
  }
329
+ class SessionRenewJudgeError extends Error {
330
+ code;
331
+ constructor(code) {
332
+ super(code);
333
+ this.code = code;
334
+ this.name = 'SessionRenewJudgeError';
335
+ }
336
+ }
337
+ function sessionRenewFallbackReason(error) {
338
+ if (error instanceof SessionRenewJudgeError)
339
+ return error.code;
340
+ const message = error instanceof Error ? error.message : String(error);
341
+ if (/timed out|abort/i.test(message))
342
+ return 'timeout';
343
+ if (/HTTP \d+/i.test(message))
344
+ return 'http_error';
345
+ if (/no JSON|invalid decision|invalid confidence/i.test(message))
346
+ return 'invalid_response';
347
+ if (/no text|empty/i.test(message))
348
+ return 'empty_response';
349
+ return 'inference_error';
350
+ }
295
351
  function validPositiveNumber(value) {
296
352
  return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : undefined;
297
353
  }
@@ -101,7 +101,11 @@ export class SessionTurnCoordinator {
101
101
  turnId: active.turnId,
102
102
  taskId: active.taskId,
103
103
  generation: active.generation,
104
- status: reason === 'stop' || reason === 'explicit_cancel' ? 'cancelled' : 'superseded',
104
+ status: reason === 'daemon_restart'
105
+ ? 'interrupted'
106
+ : reason === 'stop' || reason === 'explicit_cancel'
107
+ ? 'cancelled'
108
+ : 'superseded',
105
109
  reason,
106
110
  finishedAt: Date.now(),
107
111
  },
@@ -3,6 +3,7 @@ import path from 'path';
3
3
  import { isEckSnapshotsEnabled } from '../config-store.js';
4
4
  import { eckDebugDir } from '../paths.js';
5
5
  import { logger } from '../utils/logger.js';
6
+ import { clearRoleStoreCache } from '../config/role-store.js';
6
7
  import { loadManifest, loadManifestMeta, invalidateManifestCache, evaluateWhen, renderTemplate, renderLoopSection, resolvePathWithDiag, loadSectionFiles, loadChildTemplate, buildPathMappings, shortenPath, } from './manifest-engine.js';
7
8
  const DEFAULT_MANIFEST_FILE = 'eck_manifest.json';
8
9
  // ── Caches ──
@@ -19,9 +20,18 @@ export function loadKitManifest() {
19
20
  const sections = loadManifest(DEFAULT_MANIFEST_FILE);
20
21
  logger.info(`[KitRenderer] Loaded manifest: ${sections.length} sections`);
21
22
  }
23
+ /**
24
+ * kits 统一失效入口:`kits/` 下的任何东西改了,都从这里一次清干净。
25
+ *
26
+ * 必须连带清 role-store 快照:它的签名含角色模板 mtime,而模板内容走 FileCache
27
+ * 的 on-reload(平时不查盘)。若"模板改盘 → 失效前发生一次鉴权读取",快照就会以
28
+ * **新 mtime + 旧内容** 落盘;此后签名不再变化,光清 FileCache 也换不掉它——
29
+ * 授权数据会永久停在旧版本。
30
+ */
22
31
  export function invalidateKitCache() {
23
32
  invalidateManifestCache();
24
33
  _sessionPathCache.clear();
34
+ clearRoleStoreCache();
25
35
  }
26
36
  export function invalidateSessionCache(sessionId) {
27
37
  _sessionPathCache.delete(sessionId);
@@ -203,7 +213,7 @@ const PARAM_DESCRIPTIONS = {
203
213
  sameEgressIp: '对端与本端共享同一出口 IP',
204
214
  groupId: '群组 ID(群聊时)',
205
215
  groupRulesPath: '群规则本地物化文件(relations/<peerKey>/rules.md)',
206
- groupRulesStatus: '群规则同步状态(synced/cached/missing/forbidden/invalid_metadata/file_mismatch/too_large/unreadable/error)',
216
+ groupRulesStatus: '群规则状态(ignored/synced/cached/missing/forbidden/invalid_metadata/file_mismatch/too_large/unreadable/error)',
207
217
  groupRulesError: '群规则同步错误',
208
218
  chatType: '聊天类型(private=私聊 / group=群聊 / null=本地开发)',
209
219
  channel: '渠道类型(aun/feishu/wechat/dingtalk/qqbot/wecom)',
@@ -226,6 +236,7 @@ const PARAM_DESCRIPTIONS = {
226
236
  osInfo: '操作系统及版本(如 Windows 11 Pro (win32 10.0.26200))',
227
237
  threadId: '话题 ID(多话题路由时)',
228
238
  chatMode: '会话模式(interactive=同步交互 / proactive=主动推送)',
239
+ isTriggerTask: '当前任务是否来源于 Trigger(true/false)',
229
240
  proactivePreTool1stMsgChk: 'proactive 前置工具首消息检查(true/false)',
230
241
  proactiveToolUseReminder: 'proactive 工具调用提醒(true/false)',
231
242
  proactiveFirstSendRequired: '当前消息是否要求首次非发送工具前先发送表态',
@@ -51,6 +51,59 @@ function renderOneItem(item, sessionVars, sessionCache, contentSentinel) {
51
51
  const isOwnerHint = item.kind === 'owner-hint';
52
52
  const isHandoff = item.kind === 'handoff';
53
53
  const handoffPreviousSentinel = `\x00ECMSG-HANDOFF-PREV-${randomUUID()}\x00`;
54
+ const requiredReplacements = (item.handoff?.requiredEntries ?? []).map(entry => ({
55
+ entry,
56
+ sentinel: `\x00ECMSG-HANDOFF-REQ-${randomUUID()}\x00`,
57
+ }));
58
+ const contextReplacements = (item.handoff?.contextEntries ?? []).map(entry => ({
59
+ entry,
60
+ sentinel: `\x00ECMSG-HANDOFF-CTX-${randomUUID()}\x00`,
61
+ }));
62
+ const originReplacements = (item.handoff?.originEntries ?? []).map(entry => ({
63
+ entry,
64
+ previousSentinel: `\x00ECMSG-HANDOFF-ORIGIN-PREV-${randomUUID()}\x00`,
65
+ responseSentinel: `\x00ECMSG-HANDOFF-ORIGIN-RESP-${randomUUID()}\x00`,
66
+ }));
67
+ const fallbackRequiredEntries = item.handoff?.returnPolicy === 'required'
68
+ && item.handoff.handoffId
69
+ && item.handoff.previousContent !== undefined
70
+ && requiredReplacements.length === 0
71
+ ? [{
72
+ entry: {
73
+ handoffId: item.handoff.handoffId,
74
+ previousContent: item.handoff.previousContent,
75
+ previousMessageId: item.handoff.previousMessageId,
76
+ },
77
+ sentinel: `\x00ECMSG-HANDOFF-REQ-${randomUUID()}\x00`,
78
+ }]
79
+ : [];
80
+ const fallbackContextEntries = item.handoff?.returnPolicy === 'none'
81
+ && item.handoff.previousContent !== undefined
82
+ && contextReplacements.length === 0
83
+ ? [{
84
+ entry: {
85
+ previousContent: item.handoff.previousContent,
86
+ previousMessageId: item.handoff.previousMessageId,
87
+ },
88
+ sentinel: `\x00ECMSG-HANDOFF-CTX-${randomUUID()}\x00`,
89
+ }]
90
+ : [];
91
+ const allRequiredReplacements = [...requiredReplacements, ...fallbackRequiredEntries];
92
+ const allContextReplacements = [...contextReplacements, ...fallbackContextEntries];
93
+ const fallbackOriginEntries = item.handoff?.kind === 'response_to_origin'
94
+ && item.handoff.previousContent !== undefined
95
+ && originReplacements.length === 0
96
+ ? [{
97
+ entry: {
98
+ previousContent: item.handoff.previousContent,
99
+ responseContent: item.content,
100
+ previousMessageId: item.handoff.previousMessageId,
101
+ },
102
+ previousSentinel: handoffPreviousSentinel,
103
+ responseSentinel: contentSentinel,
104
+ }]
105
+ : [];
106
+ const allOriginReplacements = [...originReplacements, ...fallbackOriginEntries];
54
107
  // item-level vars: session vars overlaid with this message's own sender/timestamp.
55
108
  const itemVars = {
56
109
  ...sessionVars,
@@ -91,6 +144,20 @@ function renderOneItem(item, sessionVars, sessionCache, contentSentinel) {
91
144
  handoffPreviousMessageId: item.handoff?.previousMessageId ?? undefined,
92
145
  handoffPreviousContent: item.handoff ? handoffPreviousSentinel : undefined,
93
146
  handoffReturnPolicy: item.handoff?.returnPolicy,
147
+ handoffRequiredEntries: allRequiredReplacements.map(({ entry, sentinel }) => ({
148
+ handoffId: entry.handoffId,
149
+ previousContent: sentinel,
150
+ previousMessageId: entry.previousMessageId ?? undefined,
151
+ })),
152
+ handoffContextEntries: allContextReplacements.map(({ entry, sentinel }) => ({
153
+ previousContent: sentinel,
154
+ previousMessageId: entry.previousMessageId ?? undefined,
155
+ })),
156
+ handoffOriginEntries: allOriginReplacements.map(({ entry, previousSentinel, responseSentinel }) => ({
157
+ previousContent: previousSentinel,
158
+ responseContent: responseSentinel,
159
+ previousMessageId: entry.previousMessageId ?? undefined,
160
+ })),
94
161
  // content held as a per-call random sentinel, swapped back post-render.
95
162
  // Using a UUID means no real message can collide with it.
96
163
  content: contentSentinel,
@@ -109,9 +176,20 @@ function renderOneItem(item, sessionVars, sessionCache, contentSentinel) {
109
176
  ? renderTemplate(rawContent, itemVars, /* stripBlankLines */ false)
110
177
  : rawContent;
111
178
  // swap the sentinel back to the real message text (literal replace, no parsing).
112
- const withContent = rendered
179
+ let withContent = rendered
113
180
  .split(contentSentinel).join(item.content)
114
181
  .split(handoffPreviousSentinel).join(item.handoff?.previousContent ?? '');
182
+ for (const { entry, sentinel } of allRequiredReplacements) {
183
+ withContent = withContent.split(sentinel).join(entry.previousContent);
184
+ }
185
+ for (const { entry, sentinel } of allContextReplacements) {
186
+ withContent = withContent.split(sentinel).join(entry.previousContent);
187
+ }
188
+ for (const { entry, previousSentinel, responseSentinel } of allOriginReplacements) {
189
+ withContent = withContent
190
+ .split(previousSentinel).join(entry.previousContent)
191
+ .split(responseSentinel).join(entry.responseContent);
192
+ }
115
193
  if (withContent.trim())
116
194
  out.push(withContent.replace(/\s+$/, ''));
117
195
  }