evolcore 0.0.28 → 0.0.29

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 (183) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/ec-safe-output.js +270 -54
  3. package/dist/agents/baseagent-availability.js +37 -1
  4. package/dist/agents/baseagent.js +19 -9
  5. package/dist/agents/claude-runner.js +171 -13
  6. package/dist/agents/codex/app-server-client.js +6 -2
  7. package/dist/agents/codex/diagnostics.js +4 -3
  8. package/dist/agents/codex-runner.js +396 -95
  9. package/dist/agents/gemini-runner.js +35 -17
  10. package/dist/aun/group/history.js +130 -90
  11. package/dist/aun/group/operations.js +1 -3
  12. package/dist/aun/menu/transport.js +5 -3
  13. package/dist/aun/msg/managed-operation.js +70 -0
  14. package/dist/aun/msg/p2p.js +1 -3
  15. package/dist/aun/msg/upload.js +2 -2
  16. package/dist/aun/outbox.js +1 -37
  17. package/dist/channels/aun.js +249 -89
  18. package/dist/channels/daemon.js +18 -1
  19. package/dist/channels/feishu.js +1 -2
  20. package/dist/cli/agent-command.js +23 -3
  21. package/dist/cli/agent-create.js +68 -2
  22. package/dist/cli/agent.js +12 -2
  23. package/dist/cli/aun-commands.js +87 -13
  24. package/dist/cli/cli-argv.js +26 -0
  25. package/dist/cli/daemon-commands.js +40 -10
  26. package/dist/cli/data-command.js +4 -4
  27. package/dist/cli/handoff-command.js +2 -0
  28. package/dist/cli/index.js +18 -1
  29. package/dist/cli/managed-command-guard.js +10 -0
  30. package/dist/cli/memory-command.js +441 -0
  31. package/dist/cli/restart-monitor.js +62 -6
  32. package/dist/cli/skill-command.js +41 -0
  33. package/dist/cli/task-context.js +2 -23
  34. package/dist/cli/trigger-command.js +9 -1
  35. package/dist/cli/watch-msg-diag.js +209 -0
  36. package/dist/config/config-field-policy.js +52 -3
  37. package/dist/config/config-manager.js +17 -4
  38. package/dist/config/contact-book-store.js +2 -2
  39. package/dist/config/mention-mode.js +7 -2
  40. package/dist/config/merge.js +28 -9
  41. package/dist/config/resolved-config-op.js +1 -1
  42. package/dist/config/runtime-env.js +137 -0
  43. package/dist/config/schema-registry.js +11 -1
  44. package/dist/config-store.js +10 -10
  45. package/dist/core/audit/event-key.js +3 -1
  46. package/dist/core/audit/log-integrity.js +4 -0
  47. package/dist/core/auth/agent-delegation.js +2 -1
  48. package/dist/core/auth/auth-gateway.js +13 -6
  49. package/dist/core/auth/authorization-audit.js +54 -19
  50. package/dist/core/auth/operation-authorizer.js +17 -0
  51. package/dist/core/auth/operation-catalog.js +9 -0
  52. package/dist/core/causation/context.js +5 -1
  53. package/dist/core/command/agent-control.js +1 -2
  54. package/dist/core/command/command-handler.js +51 -23
  55. package/dist/core/command/memory-slash.js +270 -0
  56. package/dist/core/command/menu-catalog.js +1 -0
  57. package/dist/core/command/menu-handler.js +315 -60
  58. package/dist/core/command/menu-protocol.js +42 -4
  59. package/dist/core/command/slash-gate.js +10 -10
  60. package/dist/core/command/slash-handler.js +206 -64
  61. package/dist/core/data-migration.js +282 -505
  62. package/dist/core/event-catalog.js +26 -0
  63. package/dist/core/evolagent-registry.js +6 -0
  64. package/dist/core/evolagent.js +0 -10
  65. package/dist/core/handoff/store.js +1 -20
  66. package/dist/core/inference/text-inference.js +57 -1
  67. package/dist/core/memory/consolidation-context.js +505 -0
  68. package/dist/core/memory/consolidation-ops-records.js +124 -0
  69. package/dist/core/memory/consolidation-trigger.js +394 -0
  70. package/dist/core/memory/index-manager.js +308 -0
  71. package/dist/core/memory/launch-consolidation.js +395 -0
  72. package/dist/core/memory/memory-admin.js +234 -0
  73. package/dist/core/memory/memory-document.js +100 -0
  74. package/dist/core/memory/memory-session-policy.js +41 -0
  75. package/dist/core/memory/memory-signature.js +257 -0
  76. package/dist/core/memory/memory-writer.js +432 -0
  77. package/dist/core/memory/staleness.js +117 -0
  78. package/dist/core/message/auto-mention-gate.js +454 -0
  79. package/dist/core/message/im-renderer.js +69 -16
  80. package/dist/core/message/message-bridge.js +287 -21
  81. package/dist/core/message/message-log.js +1 -0
  82. package/dist/core/message/message-queue.js +114 -27
  83. package/dist/core/message/pending-hints.js +329 -161
  84. package/dist/core/message/response-engine.js +567 -80
  85. package/dist/core/permission/ec-command-parser.js +1029 -80
  86. package/dist/core/permission/protected-paths.js +376 -126
  87. package/dist/core/permission/sandbox-runtime.js +40 -12
  88. package/dist/core/permission/tool-error-code.js +96 -13
  89. package/dist/core/permission/tool-policy.js +276 -23
  90. package/dist/core/role/runtime-policy.js +3 -7
  91. package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
  92. package/dist/core/session/session-fs-store.js +0 -76
  93. package/dist/core/session/session-idle-monitor.js +355 -0
  94. package/dist/core/session/session-manager.js +478 -17
  95. package/dist/core/session/session-mapper.js +33 -16
  96. package/dist/core/session/session-renew.js +217 -155
  97. package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
  98. package/dist/core/skill/global-watcher.js +282 -0
  99. package/dist/core/skill/index-generator.js +84 -0
  100. package/dist/core/skill/startup.js +73 -0
  101. package/dist/core/skill/switch-resolver.js +115 -0
  102. package/dist/core/task/task-activity-store.js +219 -0
  103. package/dist/eck/kit-renderer.js +30 -3
  104. package/dist/eck/manifest-engine.js +26 -4
  105. package/dist/index.js +708 -18
  106. package/dist/ipc.js +564 -2
  107. package/dist/paths.js +30 -9
  108. package/dist/response-system/engines/v1/proactive-flow.js +116 -15
  109. package/dist/response-system/modes/single-session/index.js +3 -0
  110. package/dist/trigger/feedback.js +1 -0
  111. package/dist/trigger/manager.js +0 -10
  112. package/dist/trigger/scheduler.js +11 -0
  113. package/dist/trigger/validation.js +22 -0
  114. package/dist/utils/avatar-upload.js +59 -18
  115. package/dist/utils/error-dict.json +6 -3
  116. package/dist/utils/error-utils.js +27 -0
  117. package/dist/utils/logger.js +32 -10
  118. package/dist/utils/media-cache.js +84 -0
  119. package/dist/utils/msg-diag.js +97 -0
  120. package/dist/utils/welcome.js +37 -8
  121. package/kits/docs/AUTHORING.md +314 -0
  122. package/kits/docs/GUIDE.md +2 -0
  123. package/kits/docs/INDEX.md +2 -0
  124. package/kits/docs/aun/CHEATSHEET.md +4 -0
  125. package/kits/docs/aun/SDK.md +60 -0
  126. package/kits/docs/context-assembly.md +10 -6
  127. package/kits/docs/evolcore/INDEX.md +17 -3
  128. package/kits/docs/evolcore/agent-create.md +24 -0
  129. package/kits/docs/evolcore/agent.md +5 -2
  130. package/kits/docs/evolcore/config.md +1 -1
  131. package/kits/docs/evolcore/ec-capabilities.md +364 -0
  132. package/kits/docs/evolcore/group.md +34 -18
  133. package/kits/docs/evolcore/memory-architecture.md +78 -0
  134. package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
  135. package/kits/docs/evolcore/memory-consolidation.md +263 -0
  136. package/kits/docs/evolcore/memory-tags.md +63 -0
  137. package/kits/docs/evolcore/memory.md +110 -0
  138. package/kits/docs/evolcore/menu-client.md +27 -11
  139. package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
  140. package/kits/docs/evolcore/menu-protocol.md +213 -0
  141. package/kits/docs/evolcore/msg.md +2 -0
  142. package/kits/docs/evolcore/trigger.md +7 -5
  143. package/kits/docs/path-registry.md +6 -8
  144. package/kits/docs/prompt-loading-architecture.md +5 -3
  145. package/kits/docs/venues/group.md +5 -4
  146. package/kits/eck_manifest.json +294 -88
  147. package/kits/eck_manifest.memory.json +257 -0
  148. package/kits/rules/03-identity.md +8 -8
  149. package/kits/rules/05-venue.md +4 -2
  150. package/kits/rules/06-channel.md +1 -1
  151. package/kits/schemas/_meta.json +276 -47
  152. package/kits/schemas/agent-config.schema.12.json +38 -2
  153. package/kits/schemas/agent-config.schema.13.json +591 -0
  154. package/kits/schemas/daemon.schema.6.json +9 -9
  155. package/kits/schemas/defaults.schema.5.json +29 -0
  156. package/kits/schemas/relation-config.schema.8.json +14 -1
  157. package/kits/schemas/relation-config.schema.9.json +129 -0
  158. package/kits/schemas/role-config.schema.1.json +3 -3
  159. package/kits/skills/.switches.json +3 -0
  160. package/kits/skills/ec-skill/SKILL.md +217 -0
  161. package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
  162. package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
  163. package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
  164. package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
  165. package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
  166. package/kits/templates/decision/auto-mention.md +25 -0
  167. package/kits/templates/decision/session-renew.md +22 -0
  168. package/kits/templates/memory-consolidation/forget.md +104 -0
  169. package/kits/templates/memory-consolidation/generalize.md +76 -0
  170. package/kits/templates/memory-consolidation/notes.md +133 -0
  171. package/kits/templates/memory-consolidation/profile.md +58 -0
  172. package/kits/templates/memory-consolidation/promote.md +72 -0
  173. package/kits/templates/memory-consolidation/workflow.md +75 -0
  174. package/kits/templates/system-fragments/bootstrap.md +14 -5
  175. package/kits/templates/system-fragments/commands.md +1 -0
  176. package/kits/templates/system-fragments/group-history.md +6 -0
  177. package/kits/templates/system-fragments/identity.md +0 -3
  178. package/kits/templates/system-fragments/memory.md +79 -0
  179. package/kits/templates/system-fragments/reply.md +1 -0
  180. package/kits/templates/system-fragments/venue.md +1 -1
  181. package/package.json +4 -6
  182. package/dist/trigger/legacy-session-history.js +0 -19
  183. package/kits/templates/system-fragments/group-unread-history.md +0 -7
@@ -0,0 +1,219 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { agentTaskActivityDir } from '../../paths.js';
5
+ import { MENU_RESPONSE_MAX_BYTES, MENU_RESPONSE_SAFETY_MARGIN_BYTES } from '../command/menu-protocol.js';
6
+ const LIST_TEXT_BYTES = 1024;
7
+ const LIST_LIMIT = 100;
8
+ const MARKER = '[中间内容已省略]';
9
+ const RESPONSE_DATA_BUDGET = MENU_RESPONSE_MAX_BYTES - MENU_RESPONSE_SAFETY_MARGIN_BYTES - 2 * 1024;
10
+ function safeTaskId(taskId) {
11
+ if (!/^task-[A-Za-z0-9._:-]+$/.test(taskId))
12
+ throw new Error('Invalid taskId');
13
+ return encodeURIComponent(taskId);
14
+ }
15
+ function taskDir(selfAid) {
16
+ return agentTaskActivityDir(selfAid);
17
+ }
18
+ function taskFile(selfAid, taskId) {
19
+ return path.join(taskDir(selfAid), `${safeTaskId(taskId)}.jsonl`);
20
+ }
21
+ function metaLine(meta) {
22
+ return JSON.stringify({ type: 'meta', ...meta });
23
+ }
24
+ function clone(value) {
25
+ return JSON.parse(JSON.stringify(value));
26
+ }
27
+ function utf8Slice(value, maxBytes, fromEnd = false) {
28
+ if (Buffer.byteLength(value, 'utf8') <= maxBytes)
29
+ return value;
30
+ const chars = Array.from(value);
31
+ let used = 0;
32
+ const selected = [];
33
+ const ordered = fromEnd ? chars.reverse() : chars;
34
+ for (const char of ordered) {
35
+ const size = Buffer.byteLength(char, 'utf8');
36
+ if (used + size > maxBytes)
37
+ break;
38
+ selected.push(char);
39
+ used += size;
40
+ }
41
+ return fromEnd ? selected.reverse().join('') : selected.join('');
42
+ }
43
+ function compactText(value, maxBytes) {
44
+ if (Buffer.byteLength(value, 'utf8') <= maxBytes)
45
+ return value;
46
+ const markerBytes = Buffer.byteLength(MARKER, 'utf8');
47
+ const headBytes = Math.min(600, Math.max(0, Math.floor((maxBytes - markerBytes) * 0.75)));
48
+ const tailBytes = Math.max(0, maxBytes - markerBytes - headBytes);
49
+ const paragraphs = value.split(/\n\s*\n/u).map(part => part.trim()).filter(Boolean);
50
+ if (paragraphs.length > 1) {
51
+ const head = utf8Slice(paragraphs.slice(0, 1).join('\n\n'), headBytes);
52
+ const tail = utf8Slice(paragraphs.slice(-1).join('\n\n'), tailBytes, true);
53
+ return `${head}${MARKER}${tail}`;
54
+ }
55
+ return `${utf8Slice(value, headBytes)}${MARKER}${utf8Slice(value, tailBytes, true)}`;
56
+ }
57
+ function walkStrings(value, pathName, visit) {
58
+ if (!value || typeof value !== 'object')
59
+ return;
60
+ for (const [key, child] of Object.entries(value)) {
61
+ const childPath = pathName ? `${pathName}.${key}` : key;
62
+ if (typeof child === 'string')
63
+ visit(value, key, childPath, child);
64
+ else
65
+ walkStrings(child, childPath, visit);
66
+ }
67
+ }
68
+ function projectItem(item, maxTextBytes) {
69
+ const projected = clone(item);
70
+ const truncatedFields = [];
71
+ walkStrings(projected, '', (parent, key, fieldPath, value) => {
72
+ const originalBytes = Buffer.byteLength(value, 'utf8');
73
+ if (originalBytes <= maxTextBytes)
74
+ return;
75
+ const compacted = compactText(value, maxTextBytes);
76
+ parent[key] = compacted;
77
+ truncatedFields.push({ path: fieldPath, originalBytes, returnedBytes: Buffer.byteLength(compacted, 'utf8') });
78
+ });
79
+ return { item: projected, truncatedFields };
80
+ }
81
+ function readJsonl(selfAid, taskId) {
82
+ const file = taskFile(selfAid, taskId);
83
+ if (!fs.existsSync(file))
84
+ return { records: [] };
85
+ const lines = fs.readFileSync(file, 'utf8').split('\n').filter(Boolean);
86
+ let meta;
87
+ const records = [];
88
+ for (const line of lines) {
89
+ try {
90
+ const value = JSON.parse(line);
91
+ if (value.type === 'meta')
92
+ meta = value;
93
+ else if (value.version === 1 && value.activityId && value.taskId && value.item)
94
+ records.push(value);
95
+ }
96
+ catch {
97
+ break;
98
+ }
99
+ }
100
+ return { meta, records };
101
+ }
102
+ function writeAtomic(file, content) {
103
+ const temp = `${file}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`;
104
+ fs.writeFileSync(temp, content, { encoding: 'utf8', mode: 0o600 });
105
+ fs.renameSync(temp, file);
106
+ }
107
+ export class TaskActivityStore {
108
+ selfAid;
109
+ constructor(selfAid) {
110
+ this.selfAid = selfAid;
111
+ }
112
+ ensureMeta(input) {
113
+ const now = Date.now();
114
+ const current = readJsonl(this.selfAid, input.taskId).meta;
115
+ if (current)
116
+ return current;
117
+ const meta = {
118
+ version: 1,
119
+ taskId: input.taskId,
120
+ selfAid: this.selfAid,
121
+ sessionId: input.sessionId,
122
+ channel: input.channel,
123
+ channelId: input.channelId,
124
+ chatType: input.chatType,
125
+ ...(input.groupId ? { groupId: input.groupId } : {}),
126
+ ...(input.peerId ? { peerId: input.peerId } : {}),
127
+ taskStatus: input.taskStatus ?? 'running',
128
+ createdAt: now,
129
+ updatedAt: now,
130
+ };
131
+ fs.mkdirSync(taskDir(this.selfAid), { recursive: true, mode: 0o700 });
132
+ writeAtomic(taskFile(this.selfAid, input.taskId), `${metaLine(meta)}\n`);
133
+ return meta;
134
+ }
135
+ createTask(input) {
136
+ return this.ensureMeta(input);
137
+ }
138
+ append(input) {
139
+ const file = taskFile(this.selfAid, input.taskId);
140
+ const existingLoaded = readJsonl(this.selfAid, input.taskId);
141
+ const existing = input.activityId ? existingLoaded.records.find(record => record.activityId === input.activityId) : undefined;
142
+ if (existing)
143
+ return existing;
144
+ this.ensureMeta(input);
145
+ const loaded = readJsonl(this.selfAid, input.taskId);
146
+ const record = {
147
+ version: 1,
148
+ activityId: input.activityId ?? `activity-${crypto.randomUUID().replace(/-/g, '')}`,
149
+ taskId: input.taskId,
150
+ seq: loaded.records.length,
151
+ createdAt: Date.now(),
152
+ item: clone(input.item),
153
+ };
154
+ const meta = loaded.meta;
155
+ if (!meta)
156
+ throw new Error('Task metadata unavailable');
157
+ const updatedMeta = { ...meta, updatedAt: Date.now() };
158
+ writeAtomic(taskFile(this.selfAid, input.taskId), `${metaLine(updatedMeta)}\n${loaded.records.map(item => JSON.stringify(item)).join('\n')}${loaded.records.length ? '\n' : ''}${JSON.stringify(record)}\n`);
159
+ return record;
160
+ }
161
+ updateStatus(taskId, status) {
162
+ const loaded = readJsonl(this.selfAid, taskId);
163
+ if (!loaded.meta)
164
+ return;
165
+ const meta = { ...loaded.meta, taskStatus: status, updatedAt: Date.now() };
166
+ const lines = [metaLine(meta), ...loaded.records.map(record => JSON.stringify(record))];
167
+ writeAtomic(taskFile(this.selfAid, taskId), `${lines.join('\n')}\n`);
168
+ }
169
+ readList(taskId, offset, limit) {
170
+ const loaded = readJsonl(this.selfAid, taskId);
171
+ if (!loaded.meta)
172
+ throw Object.assign(new Error('Task not found'), { code: 'NOT_FOUND' });
173
+ const boundedLimit = Math.min(Math.max(1, Math.floor(limit)), LIST_LIMIT);
174
+ const safeOffset = Math.max(0, Math.floor(offset));
175
+ const items = [];
176
+ const responseBase = () => ({ view: 'activities', taskId, offset: safeOffset, limit: boundedLimit, total: loaded.records.length, hasMore: false, taskStatus: loaded.meta.taskStatus, updatedAt: loaded.meta.updatedAt, items });
177
+ for (const record of loaded.records.slice(safeOffset, safeOffset + boundedLimit)) {
178
+ const projection = projectItem(record.item, LIST_TEXT_BYTES);
179
+ const candidate = { activityId: record.activityId, seq: record.seq, createdAt: record.createdAt, item: projection.item, truncatedFields: projection.truncatedFields, contentAvailable: true };
180
+ items.push(candidate);
181
+ const candidateResponse = responseBase();
182
+ candidateResponse.hasMore = safeOffset + items.length < loaded.records.length;
183
+ if (Buffer.byteLength(JSON.stringify(candidateResponse), 'utf8') > RESPONSE_DATA_BUDGET) {
184
+ items.pop();
185
+ if (items.length === 0)
186
+ throw Object.assign(new Error('Activity cannot fit in a Menu response'), { code: 'RESPONSE_TOO_LARGE' });
187
+ break;
188
+ }
189
+ }
190
+ return { view: 'activities', taskId, offset: safeOffset, limit: boundedLimit, total: loaded.records.length, hasMore: safeOffset + items.length < loaded.records.length, taskStatus: loaded.meta.taskStatus, updatedAt: loaded.meta.updatedAt, items };
191
+ }
192
+ readDetail(taskId, activityId) {
193
+ const loaded = readJsonl(this.selfAid, taskId);
194
+ if (!loaded.meta)
195
+ throw Object.assign(new Error('Task not found'), { code: 'NOT_FOUND' });
196
+ const record = loaded.records.find(item => item.activityId === activityId);
197
+ if (!record)
198
+ throw Object.assign(new Error('Activity not found'), { code: 'ACTIVITY_NOT_FOUND' });
199
+ let maxBytes = Number.MAX_SAFE_INTEGER;
200
+ let projection = projectItem(record.item, maxBytes);
201
+ const responseBytes = () => Buffer.byteLength(JSON.stringify({
202
+ view: 'activity', taskId, taskStatus: loaded.meta.taskStatus, updatedAt: loaded.meta.updatedAt,
203
+ activity: { activityId: record.activityId, seq: record.seq, createdAt: record.createdAt, item: projection.item, contentMode: projection.truncatedFields.length ? 'compressed' : 'full', truncatedFields: projection.truncatedFields },
204
+ }), 'utf8');
205
+ if (responseBytes() > RESPONSE_DATA_BUDGET)
206
+ maxBytes = Math.floor(RESPONSE_DATA_BUDGET / 2);
207
+ while (responseBytes() > RESPONSE_DATA_BUDGET && maxBytes >= 32) {
208
+ projection = projectItem(record.item, maxBytes);
209
+ maxBytes = Math.floor(maxBytes / 2);
210
+ }
211
+ if (responseBytes() > RESPONSE_DATA_BUDGET) {
212
+ throw Object.assign(new Error('Activity cannot fit in a Menu response'), { code: 'RESPONSE_TOO_LARGE' });
213
+ }
214
+ return { view: 'activity', taskId, taskStatus: loaded.meta.taskStatus, updatedAt: loaded.meta.updatedAt, activity: { activityId: record.activityId, seq: record.seq, createdAt: record.createdAt, item: projection.item, contentMode: projection.truncatedFields.length ? 'compressed' : 'full', truncatedFields: projection.truncatedFields } };
215
+ }
216
+ getMeta(taskId) {
217
+ return readJsonl(this.selfAid, taskId).meta;
218
+ }
219
+ }
@@ -6,7 +6,20 @@ import { logger } from '../utils/logger.js';
6
6
  import { clearRoleStoreCache } from '../config/role-store.js';
7
7
  import { resolveAgentLifecycle } from '../config/lifecycle.js';
8
8
  import { loadManifest, loadManifestMeta, invalidateManifestCache, evaluateWhen, renderTemplate, renderLoopSection, resolvePathWithDiag, loadSectionFiles, loadChildTemplate, buildPathMappings, shortenPath, } from './manifest-engine.js';
9
- const DEFAULT_MANIFEST_FILE = 'eck_manifest.json';
9
+ export const DEFAULT_MANIFEST_FILE = 'eck_manifest.json';
10
+ /**
11
+ * 会话原型(sessionType)→ manifest 文件的内置映射。
12
+ * 核心会话类型不依赖 agent config 即可找到自己的 manifest;config.sessionManifests 仅作可选覆盖。
13
+ */
14
+ export const BUILTIN_SESSION_MANIFESTS = {
15
+ main: DEFAULT_MANIFEST_FILE,
16
+ memory: 'eck_manifest.memory.json',
17
+ auxiliary: 'eck_manifest.auxiliary.json',
18
+ };
19
+ /** 选择顺序:config 覆盖 → 内置映射 → 主 manifest。 */
20
+ export function resolveSessionManifest(sessionType, overrides) {
21
+ return overrides?.[sessionType] ?? BUILTIN_SESSION_MANIFESTS[sessionType] ?? DEFAULT_MANIFEST_FILE;
22
+ }
10
23
  // ── Caches ──
11
24
  const _sessionPathCache = new Map();
12
25
  function getSessionCache(sessionId) {
@@ -77,11 +90,17 @@ export function renderKitSections(ctx, manifestFile = DEFAULT_MANIFEST_FILE) {
77
90
  const skippedByCap = []; // 因总闸超限未加载的 section id
78
91
  for (const section of sections) {
79
92
  const rawPath = section.type === 'file' ? (section.file ?? '') : (section.path ?? '');
93
+ if (section.type === 'file' && !section.file) {
94
+ // 字段写错(如写成 path)时引擎会丢段。用真实字段名记账,别让诊断显示成
95
+ // "路径为空"——那会误导排查方向。加载期另有 warnInvalidSections 告警。
96
+ logger.warn(`[KitRenderer] section "${section.id}" 是 type=file 但缺少 file 字段,将被跳过`);
97
+ }
80
98
  const diag = {
81
99
  id: section.id, description: section.description, type: section.type, rawPath,
82
100
  pattern: section.pattern, needsInjection: section.needsInjection, when: section.when,
83
101
  enabled: section.enabled !== false, whenPassed: false, resolvedPath: null,
84
102
  resolveStatus: 'no-path', fileCount: 0, used: false, injected: false,
103
+ optional: section.optional,
85
104
  };
86
105
  // 总闸已触发:后续命中的段一律不加载,只记录 id
87
106
  if (capReached) {
@@ -203,6 +222,7 @@ const PARAM_DESCRIPTIONS = {
203
222
  EVOLCORE_HOME: '用户数据根目录',
204
223
  PACKAGE_ROOT: 'evolcore 包根目录',
205
224
  CURRENT_PROJECT: '当前项目完整路径',
225
+ AGENT_DIR: '当前 agent 数据根目录',
206
226
  PERSONAL_DIR: '当前 agent 个人数据目录',
207
227
  RELATIONS_DIR: '当前 agent 关系数据目录',
208
228
  VENUES_DIR: '当前 agent 环境数据目录',
@@ -227,7 +247,7 @@ const PARAM_DESCRIPTIONS = {
227
247
  chatType: '聊天类型(private=私聊 / group=群聊 / null=本地开发)',
228
248
  channel: '渠道类型(aun/feishu/wechat/dingtalk/qqbot/wecom)',
229
249
  venueUid: '场所唯一标识(预留)',
230
- mentionMode: '群 @ 处理模式(mention-only=被@才响应 / disabled=所有消息都响应)',
250
+ mentionMode: '群 @ 处理模式(mention-only=被@才响应 / disabled=所有消息都响应 / auto=按上下文判断)',
231
251
  groupHistoryAvailable: '本地群历史读取能力是否可用',
232
252
  clientType: '客户端类型(desktop/web/mobile)',
233
253
  permissionMode: '权限模式(readonly/auto/request/bypass;旧值会安全迁移)',
@@ -306,7 +326,14 @@ function formatManifestDiagnostics(ctx, diagnostics) {
306
326
  };
307
327
  const used = diagnostics.filter(d => d.used).length;
308
328
  const skippedWhen = diagnostics.filter(d => d.resolveStatus === 'skipped-when').length;
309
- const errors = diagnostics.filter(d => d.resolveStatus === 'unresolved-vars' || d.resolveStatus === 'not-exist').length;
329
+ // optional=true 的段 not-exist 不算错误(Q1 决策:空集索引缺失是正常态)
330
+ const errors = diagnostics.filter(d => {
331
+ if (d.resolveStatus === 'unresolved-vars')
332
+ return true;
333
+ if (d.resolveStatus === 'not-exist')
334
+ return d.optional !== true;
335
+ return false;
336
+ }).length;
310
337
  const lines = [];
311
338
  lines.push(`# ECK Manifest Diagnostics`);
312
339
  lines.push('');
@@ -46,11 +46,11 @@ function loadAndMergeManifest(filename) {
46
46
  return [];
47
47
  }
48
48
  if (!fs.existsSync(eckPath))
49
- return sortSections(base.sections);
49
+ return sortSections(warnInvalidSections(base.sections, filename));
50
50
  try {
51
51
  const override = JSON.parse(fs.readFileSync(eckPath, 'utf-8'));
52
52
  if (override.mode === 'replace')
53
- return sortSections(override.sections);
53
+ return sortSections(warnInvalidSections(override.sections, filename));
54
54
  const merged = new Map();
55
55
  for (const s of base.sections)
56
56
  merged.set(s.id, { ...s });
@@ -62,13 +62,35 @@ function loadAndMergeManifest(filename) {
62
62
  const existing = merged.get(s.id);
63
63
  merged.set(s.id, existing ? { ...existing, ...s } : s);
64
64
  }
65
- return sortSections([...merged.values()]);
65
+ return sortSections(warnInvalidSections([...merged.values()], filename));
66
66
  }
67
67
  catch (err) {
68
68
  logger.warn(`[ManifestEngine] Failed to load eck override for ${filename}, using kits only: ${err}`);
69
- return sortSections(base.sections);
69
+ return sortSections(warnInvalidSections(base.sections, filename));
70
70
  }
71
71
  }
72
+ /**
73
+ * 校验段声明的路径字段是否可被引擎读取。
74
+ *
75
+ * `type=file` 只认 `file`,`type=directory` 只认 `path`(见 loadSectionFiles)。
76
+ * 字段名写错(例如把 file 段写成 path)时引擎原样丢段——可选段还是静默的,
77
+ * 排查极难。这里在加载期统一告警,把「配错了」与「文件不存在」区分开。
78
+ */
79
+ function warnInvalidSections(sections, filename) {
80
+ for (const s of sections) {
81
+ if (s.enabled === false)
82
+ continue;
83
+ if (s.type === 'file' && !s.file) {
84
+ const wrong = s.path ? `(疑似误用 path="${s.path}",file 段须写 file 字段)` : '';
85
+ logger.warn(`[ManifestEngine] ${filename}: section "${s.id}" 是 type=file 但缺少 file 字段,该段会被跳过${wrong}`);
86
+ }
87
+ else if (s.type === 'directory' && !s.path) {
88
+ const wrong = s.file ? `(疑似误用 file="${s.file}",directory 段须写 path 字段)` : '';
89
+ logger.warn(`[ManifestEngine] ${filename}: section "${s.id}" 是 type=directory 但缺少 path 字段,该段会被跳过${wrong}`);
90
+ }
91
+ }
92
+ return sections;
93
+ }
72
94
  function sortSections(sections) {
73
95
  return sections.slice().sort((a, b) => a.order - b.order);
74
96
  }