evolcore 0.0.33 → 0.0.35-test.0

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 (152) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/bin/ec.js +3 -2
  3. package/bin/install-codex-managed-hooks.mjs +38 -4
  4. package/dist/agents/baseagent-availability.js +20 -36
  5. package/dist/agents/claude-runner.js +93 -14
  6. package/dist/agents/codex/diagnostics.js +79 -22
  7. package/dist/agents/codex-runner.js +245 -47
  8. package/dist/agents/ecagent-runner.js +90 -27
  9. package/dist/agents/runner-types.js +5 -6
  10. package/dist/aun/aid/agentmd.js +13 -2
  11. package/dist/aun/aid/client.js +1 -1
  12. package/dist/aun/aid/identity.js +1 -1
  13. package/dist/aun/msg/managed-operation.js +16 -0
  14. package/dist/aun/msg/p2p.js +18 -2
  15. package/dist/channels/aun.js +123 -7
  16. package/dist/channels/daemon.js +6 -3
  17. package/dist/cli/aun-commands.js +39 -14
  18. package/dist/cli/ctl-command.js +3 -1
  19. package/dist/cli/daemon-commands.js +130 -23
  20. package/dist/cli/fs-command.js +2 -1
  21. package/dist/cli/getmeta-command.js +211 -0
  22. package/dist/cli/index.js +26 -6
  23. package/dist/cli/integrity-command.js +196 -0
  24. package/dist/cli/managed-command-guard.js +3 -0
  25. package/dist/cli/memory-command.js +200 -156
  26. package/dist/cli/thread-command.js +17 -7
  27. package/dist/cli/trigger-command.js +6 -1
  28. package/dist/config/config-field-policy.js +4 -0
  29. package/dist/config/config-manager.js +19 -0
  30. package/dist/config/contact-book-store.js +5 -0
  31. package/dist/config/resolved-config-op.js +25 -8
  32. package/dist/config/role-service.js +13 -0
  33. package/dist/config/runtime-env.js +16 -0
  34. package/dist/config-store.js +30 -0
  35. package/dist/core/audit/log-integrity.js +3 -1
  36. package/dist/core/auth/agent-delegation.js +108 -35
  37. package/dist/core/auth/delegation-diagnostics.js +414 -0
  38. package/dist/core/auth/operation-authorizer.js +22 -3
  39. package/dist/core/auth/operation-catalog.js +16 -0
  40. package/dist/core/channel-runtime.js +1 -1
  41. package/dist/core/command/agent-control.js +18 -26
  42. package/dist/core/command/command-handler.js +145 -5
  43. package/dist/core/command/daemon-diagnostics.js +1 -0
  44. package/dist/core/command/menu-catalog.js +3 -0
  45. package/dist/core/command/menu-handler.js +24 -7
  46. package/dist/core/command/menu-protocol.js +1 -0
  47. package/dist/core/command/slash-handler.js +2 -0
  48. package/dist/core/command/topic-slash.js +4 -7
  49. package/dist/core/daemon-bootstrap.js +4 -7
  50. package/dist/core/event-catalog.js +14 -0
  51. package/dist/core/file-integrity/read.js +61 -0
  52. package/dist/core/file-integrity/store.js +262 -0
  53. package/dist/core/ipc-registration.js +47 -2
  54. package/dist/core/memory/batch-meta.js +133 -0
  55. package/dist/core/memory/consolidation-context.js +132 -28
  56. package/dist/core/memory/consolidation-ops-records.js +14 -16
  57. package/dist/core/memory/consolidation-trigger.js +15 -7
  58. package/dist/core/memory/consolidation.js +146 -26
  59. package/dist/core/memory/index-manager.js +19 -28
  60. package/dist/core/memory/memory-admin.js +19 -14
  61. package/dist/core/memory/memory-document.js +53 -9
  62. package/dist/core/memory/memory-writer.js +50 -19
  63. package/dist/core/message/aun-ipc-sender.js +19 -1
  64. package/dist/core/message/im-renderer.js +20 -4
  65. package/dist/core/message/message-bridge.js +5 -4
  66. package/dist/core/message/message-log.js +1 -0
  67. package/dist/core/message/peer-mode.js +17 -0
  68. package/dist/core/message/response-engine.js +598 -50
  69. package/dist/core/permission/ec-command-parser.js +169 -44
  70. package/dist/core/permission/protected-paths.js +5 -1
  71. package/dist/core/permission/readonly-shell-query.js +43 -5
  72. package/dist/core/permission/session-path-grants.js +334 -0
  73. package/dist/core/permission/tool-canonical.js +226 -0
  74. package/dist/core/permission/tool-policy.js +60 -62
  75. package/dist/core/relation/peer-identity.js +87 -12
  76. package/dist/core/role/runtime-policy.js +21 -10
  77. package/dist/core/session/session-fs-store.js +123 -8
  78. package/dist/core/session/session-manager.js +131 -6
  79. package/dist/core/session/session-title.js +26 -1
  80. package/dist/core/skill/index-generator.js +17 -3
  81. package/dist/core/system-session/grants.js +40 -0
  82. package/dist/core/system-session/memory.js +289 -0
  83. package/dist/core/system-session/registry.js +74 -0
  84. package/dist/core/system-session/types.js +14 -0
  85. package/dist/core/thread/command-parser.js +6 -1
  86. package/dist/core/thread/ipc-operation.js +33 -16
  87. package/dist/core/thread/thread-operation.js +62 -24
  88. package/dist/core/thread/thread-service.js +258 -51
  89. package/dist/eck/kit-renderer.js +7 -2
  90. package/dist/eck/manifest-engine.js +24 -4
  91. package/dist/index.js +11 -2
  92. package/dist/ipc.js +51 -40
  93. package/dist/paths.js +70 -0
  94. package/dist/stats/billing.js +20 -8
  95. package/dist/trigger/feedback.js +6 -1
  96. package/dist/trigger/parser.js +8 -1
  97. package/dist/trigger/system-definitions.js +3 -3
  98. package/dist/utils/cross-platform.js +17 -5
  99. package/dist/utils/error-dict.json +18 -4
  100. package/dist/utils/error-utils.js +121 -0
  101. package/dist/utils/instance-registry.js +13 -8
  102. package/dist/utils/log-writer.js +12 -0
  103. package/dist/utils/process-introspect.js +11 -0
  104. package/kits/docs/context-assembly.md +0 -1
  105. package/kits/docs/evolcore/agent-create.md +5 -4
  106. package/kits/docs/evolcore/agent.md +2 -2
  107. package/kits/docs/evolcore/config.md +2 -2
  108. package/kits/docs/evolcore/ctl.md +2 -2
  109. package/kits/docs/evolcore/ec-capabilities.md +1 -1
  110. package/kits/docs/evolcore/fs-architecture.md +9 -10
  111. package/kits/docs/evolcore/fs.md +2 -2
  112. package/kits/docs/evolcore/group-collaboration.md +3 -3
  113. package/kits/docs/evolcore/group.md +14 -12
  114. package/kits/docs/evolcore/memory-architecture.md +4 -5
  115. package/kits/docs/evolcore/memory-consolidation.md +19 -16
  116. package/kits/docs/evolcore/memory-tags.md +3 -1
  117. package/kits/docs/evolcore/memory.md +24 -12
  118. package/kits/docs/evolcore/model.md +16 -0
  119. package/kits/docs/evolcore/msg.md +7 -7
  120. package/kits/docs/evolcore/stats.md +1 -1
  121. package/kits/docs/evolcore/thread-chain.md +30 -14
  122. package/kits/docs/prompt-loading-architecture.md +0 -1
  123. package/kits/eck_manifest.auxiliary.json +9 -0
  124. package/kits/eck_manifest.json +20 -20
  125. package/kits/eck_manifest.memory.json +20 -12
  126. package/kits/rules/01-overview.md +11 -0
  127. package/kits/rules/03-identity.md +6 -3
  128. package/kits/rules/06-channel.md +1 -1
  129. package/kits/rules/07-memory-and-thread.md +13 -16
  130. package/kits/schemas/_meta.json +7 -1
  131. package/kits/schemas/agent-config.schema.13.json +1 -1
  132. package/kits/schemas/daemon.schema.7.json +140 -0
  133. package/kits/skills/ec-group/SKILL.md +8 -5
  134. package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
  135. package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
  136. package/kits/skills/thread-ops/SKILL.md +18 -18
  137. package/kits/templates/memory-consolidation/forget.md +2 -2
  138. package/kits/templates/memory-consolidation/generalize.md +4 -4
  139. package/kits/templates/memory-consolidation/notes.md +12 -12
  140. package/kits/templates/memory-consolidation/profile.md +3 -3
  141. package/kits/templates/memory-consolidation/promote.md +3 -3
  142. package/kits/templates/memory-consolidation/workflow.md +42 -7
  143. package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
  144. package/kits/templates/system-fragments/bash-description.md +9 -0
  145. package/kits/templates/system-fragments/bootstrap.md +4 -3
  146. package/kits/templates/system-fragments/channel.md +2 -2
  147. package/kits/templates/system-fragments/memory.md +16 -9
  148. package/kits/templates/system-fragments/reply.md +6 -6
  149. package/kits/templates/system-fragments/session.md +1 -1
  150. package/package.json +4 -3
  151. package/dist/core/memory/memory-session-policy.js +0 -41
  152. package/dist/core/memory/memory-signature.js +0 -257
@@ -11,9 +11,10 @@
11
11
  * 整理会话(sessionType=memory)另有 create / update / archive 三个写命令,
12
12
  * 实现见 core/memory/memory-writer.ts,在这里只做 CLI 分发与权限委托。
13
13
  */
14
+ import fs from 'node:fs';
14
15
  import { resolvePaths } from '../paths.js';
15
16
  import { ipcQuery } from '../ipc.js';
16
- import { isHelpFlag, wantsHelp } from './help.js';
17
+ import { isHelpFlag, wantsHelp, getArgValue } from './help.js';
17
18
  import { readDelegationCredential } from '../core/auth/agent-delegation.js';
18
19
  import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
19
20
  import { formatQueueDetails } from '../core/memory/consolidation-queue-format.js';
@@ -28,20 +29,23 @@ const MEMORY_HELP = `用法: ec memory <command> [选项]
28
29
  demote 把记忆降级回上一层(agent→relation,owner→agent)
29
30
  consolidate 触发记忆整理(启动整理会话)
30
31
 
31
- ec memory note --slug <slug> [--tags <tag1,tag2,...>] <content>
32
+ ec memory note --slug <slug> [--tags '<tag1>,<tag2>'] '<content>'
32
33
 
33
34
  选项:
34
35
  --slug <slug> 文件名标识,字母/数字/连字符,长度 1-64(必填)
35
- --tags <...> 标签列表,逗号分隔;取值见 \$KITS_DOCS/evolcore/memory-tags.md
36
+ --tags <...> 标签列表,单引号包住、逗号分隔;取值见 \$KITS_DOCS/evolcore/memory-tags.md
37
+ --speakers <...> 群聊专用:本条 note 涉及的发言人,单引号包住、逗号分隔,
38
+ 如 --speakers '<aid1>,<aid2>';
39
+ 整理会话据此把发言人信息分流写入各自的 relation 记忆
36
40
 
37
- ec memory note update <slug> [--slug <新slug>] [--tags <...>] [<content>]
41
+ ec memory note update <slug> [--slug <新slug>] [--tags '<tag1>,<tag2>'] ['<content>']
38
42
  ec memory note drop <slug>
39
43
  ec memory note list
40
44
 
41
- ec memory create --scope <relation|agent|owner> --slug <slug> --desc <description> \\
42
- --tags <...> [--keywords <...>] [--replaces <path,...>] <content>
45
+ ec memory create --scope <relation|agent|owner> --slug <slug> --desc '<description>' \\
46
+ --tags '<tag1>,<tag2>' [--keywords '<kw1>,<kw2>'] [--replaces '<path1>,<path2>'] '<content>'
43
47
 
44
- ec memory update <path> [--tags <...>] [--touch]
48
+ ec memory update <path> [--tags '<tag1>,<tag2>'] [--touch]
45
49
 
46
50
  ec memory archive <path...>
47
51
 
@@ -51,18 +55,21 @@ ec memory consolidate [--mode <notes|generalize|promote|profile|forget>]
51
55
  --mode 整理模式(默认 notes)
52
56
 
53
57
  公共选项(所有子命令):
58
+ --format json 以 JSON 格式输出:成功时输出 daemon 返回的结果对象,
59
+ 失败时输出 { ok: false, code, error },均写到 stdout
54
60
  --debug <sessionId> 在宿主终端模拟该会话的托管环境(仅开发仓 daemon 支持);
55
61
  会话没有运行中任务时,daemon 从会话文件重建运行时上下文
56
62
 
57
63
  示例:
58
- ec memory note --slug commit-convention --tags "rule,long-term" "commit 消息必须用中文"
59
- ec memory note update commit-convention --tags "rule,done" "commit 消息必须用中文"
64
+ ec memory note --slug commit-convention --tags 'rule,long-term' 'commit 消息必须用中文'
65
+ ec memory note update commit-convention --tags 'rule,done' 'commit 消息必须用中文'
60
66
  ec memory note drop commit-convention
61
67
  ec memory note list
68
+ ec memory note list --format json
62
69
  ec memory consolidate
63
70
  ec memory consolidate --mode promote
64
- ec memory create --scope relation --slug profile --desc "对端画像" --tags "profile" --replaces consolidated/profile.md "(画像正文)"
65
- ec memory update consolidated/commit-convention.md --tags "rule,done"
71
+ ec memory create --scope relation --slug profile --desc '对端画像' --tags 'profile' --replaces consolidated/profile.md '(画像正文)'
72
+ ec memory update consolidated/commit-convention.md --tags 'rule,done'
66
73
  ec memory archive consolidated/obsolete.md`;
67
74
  export async function cmdMemory(args) {
68
75
  args = canonicalizeEvolcoreCommandArgv(['ec', 'memory', ...args]).slice(2);
@@ -70,51 +77,69 @@ export async function cmdMemory(args) {
70
77
  console.log(MEMORY_HELP);
71
78
  return;
72
79
  }
80
+ // --format 的取值先于一切校验:后续任何报错都要按它决定输出 JSON 还是文本
81
+ try {
82
+ assertFormatValues(args);
83
+ }
84
+ catch (e) {
85
+ console.error(`❌ ${e.message}`);
86
+ process.exit(1);
87
+ }
88
+ const json = getArgValue(args, '--format') === 'json';
73
89
  const sub = args[0];
74
90
  if (sub === 'note') {
75
- await cmdMemoryNote(args.slice(1));
91
+ await cmdMemoryNote(args.slice(1), json);
76
92
  }
77
93
  else if (sub === 'consolidate') {
78
- await cmdMemoryConsolidate(args.slice(1));
94
+ await cmdMemoryConsolidate(args.slice(1), json);
79
95
  }
80
96
  else if (sub === 'create') {
81
- await cmdMemoryCreate(args.slice(1));
97
+ await cmdMemoryCreate(args.slice(1), json);
82
98
  }
83
99
  else if (sub === 'update') {
84
- await cmdMemoryConsolidatedUpdate(args.slice(1));
100
+ await cmdMemoryConsolidatedUpdate(args.slice(1), json);
85
101
  }
86
102
  else if (sub === 'archive') {
87
- await cmdMemoryArchive(args.slice(1));
103
+ await cmdMemoryArchive(args.slice(1), json);
88
104
  }
89
105
  else if (sub === 'demote') {
90
- await cmdMemoryDemote(args.slice(1));
91
- }
92
- else if (sub === 'feedback') {
93
- await cmdMemoryFeedback(args.slice(1));
106
+ await cmdMemoryDemote(args.slice(1), json);
94
107
  }
95
108
  else {
96
- console.error(`❌ 未知子命令: ${sub}`);
97
- console.error(MEMORY_HELP);
98
- process.exit(1);
109
+ fail(json, 'INVALID_ARGUMENT', `未知子命令: ${sub}`, { text: `❌ 未知子命令: ${sub}\n${MEMORY_HELP}` });
99
110
  }
100
111
  }
112
+ /** 所有子命令都认的公共参数,不用每处声明。 */
113
+ const COMMON_FLAGS = ['--debug', '--format'];
114
+ /** `--format` 只支持 json;每次出现都检查,避免重复给出时 CLI 与 daemon 各取一个值。 */
115
+ function assertFormatValues(args) {
116
+ args.forEach((a, i) => {
117
+ if (a === '--format' && args[i + 1] !== 'json') {
118
+ throw new Error(`--format 仅支持 json: ${args[i + 1] ?? '(缺少取值)'}`);
119
+ }
120
+ });
121
+ }
101
122
  /**
102
- * 解析 `--key value` 与位置参数。未识别的 `--xxx` 一律报错,避免静默忽略。
103
- * `--debug <sessionId>` 是所有子命令的公共参数,不用每处声明。
123
+ * 解析 `--key value` 与位置参数。
124
+ *
125
+ * flag 判定采用**精确匹配已知 flag 集合**,而非 `startsWith('--')`——正文是自由
126
+ * 文本,可能以 `--` 开头(如「--as-session 通道测试」),把它当未知参数拒绝会让
127
+ * 正文受命令行位置影响。不在已知集合里的 token 一律按正文处理。
128
+ *
129
+ * 另支持 POSIX `--` end-of-options:其后所有 token 无条件按正文。
104
130
  */
105
- function parseArgs(args, known, throwOnUnknown = false) {
131
+ function parseArgs(args, known) {
132
+ assertFormatValues(args);
106
133
  const flags = new Map();
107
134
  const positional = [];
108
135
  for (let i = 0; i < args.length; i++) {
109
136
  const a = args[i];
110
- if (a.startsWith('--')) {
111
- if (a !== '--debug' && !known.includes(a)) {
112
- if (throwOnUnknown)
113
- throw new Error(`未知参数: ${a}`);
114
- console.error(`❌ 未知参数: ${a}`);
115
- process.exit(1);
116
- }
117
- if (i + 1 >= args.length || args[i + 1].startsWith('--')) {
137
+ if (a === '--') {
138
+ positional.push(...args.slice(i + 1));
139
+ break;
140
+ }
141
+ if (COMMON_FLAGS.includes(a) || known.includes(a)) {
142
+ if (i + 1 >= args.length || args[i + 1] === '--' || isKnownFlagToken(args[i + 1], known)) {
118
143
  // --touch 这类布尔开关不带值
119
144
  flags.set(a, '');
120
145
  continue;
@@ -127,6 +152,9 @@ function parseArgs(args, known, throwOnUnknown = false) {
127
152
  }
128
153
  return { flags, positional };
129
154
  }
155
+ function isKnownFlagToken(token, known) {
156
+ return COMMON_FLAGS.includes(token) || known.includes(token);
157
+ }
130
158
  function parseTags(raw) {
131
159
  if (raw === undefined)
132
160
  return undefined;
@@ -141,11 +169,15 @@ export function resolveMemoryCommandArgv(argv) {
141
169
  const known = {
142
170
  note: ['--slug', '--tags', '--speakers'], 'note.update': ['--slug', '--tags'],
143
171
  'note.drop': [], 'note.list': [], create: ['--scope', '--slug', '--desc', '--tags', '--keywords', '--replaces', '--peer'],
144
- update: ['--tags', '--touch'], archive: [], demote: ['--scope'], consolidate: ['--mode'], feedback: [],
172
+ update: ['--tags', '--touch'], archive: [], demote: ['--scope'], consolidate: ['--mode'],
145
173
  };
174
+ // feedback 已移除:旧会话/旧文档可能还在调用,给一句能照做的说明而不是泛化的非法命令。
175
+ if (kind === 'feedback') {
176
+ throw new Error('`ec memory feedback` 已移除:整理反馈由会话直接写入任务书给出的反馈文件');
177
+ }
146
178
  if (!Object.prototype.hasOwnProperty.call(known, kind))
147
179
  throw new Error('Invalid memory command');
148
- const { flags, positional } = parseArgs(argv.slice(noteSub ? 3 : 2), known[kind], true);
180
+ const { flags, positional } = parseArgs(argv.slice(noteSub ? 3 : 2), known[kind]);
149
181
  const required = (value, name) => {
150
182
  if (!value)
151
183
  throw new Error(`${name} is required`);
@@ -201,124 +233,162 @@ export function resolveMemoryCommandArgv(argv) {
201
233
  throw new Error('Invalid consolidation mode');
202
234
  return { type, mode };
203
235
  }
204
- return { type, text: required(content, 'feedback') };
236
+ throw new Error(`Unhandled memory command kind: ${kind}`);
205
237
  }
206
238
  /** 各子命令的一行完整用法:缺参报错时附上,模型照抄即可重试。 */
207
239
  const USAGE = {
208
- note: 'ec memory note --slug <slug> [--tags <tag1,tag2>] [--speakers <aid1,aid2>] "<正文>"',
209
- noteUpdate: 'ec memory note update <slug> [--slug <新slug>] [--tags <...>] ["<新正文>"]',
240
+ note: "ec memory note --slug <slug> [--tags '<tag1>,<tag2>'] [--speakers '<aid1>,<aid2>'] '<正文>'",
241
+ noteUpdate: "ec memory note update <slug> [--slug <新slug>] [--tags '<tag1>,<tag2>'] ['<新正文>']",
210
242
  noteDrop: 'ec memory note drop <slug>',
211
- create: 'ec memory create --scope <relation|agent|owner> --slug <slug> --desc "<描述>" --tags <tag1,tag2> [--keywords <...>] [--replaces <路径,...>] [--peer <peerKey>] "<正文>"',
212
- update: 'ec memory update <路径> [--tags <tag1,tag2>] [--touch](至少给一项)',
243
+ create: "ec memory create --scope <relation|agent|owner> --slug <slug> --desc '<描述>' --tags '<tag1>,<tag2>' [--keywords '<kw1>,<kw2>'] [--replaces '<路径1>,<路径2>'] [--peer <peerKey>] '<正文>'",
244
+ update: "ec memory update <路径> [--tags '<tag1>,<tag2>'] [--touch](至少给一项)",
213
245
  archive: 'ec memory archive <路径...>',
214
246
  };
215
- function failWithUsage(message, usage) {
216
- console.error(`❌ ${message}`);
217
- console.error(`用法: ${usage}`);
247
+ /**
248
+ * 统一失败出口。JSON 模式与 `ec fs` / `ec response` 一致:stdout 输出 `{ ok: false, code, error }`;
249
+ * 文本模式默认输出 `❌ <message>`(有 usage 时再加一行用法);`text` 给出时整段原样输出,保留各处既有措辞。
250
+ */
251
+ function fail(json, code, message, opts = {}) {
252
+ if (json) {
253
+ console.log(JSON.stringify({ ok: false, code, error: message, ...(opts.usage ? { usage: opts.usage } : {}) }, null, 2));
254
+ }
255
+ else {
256
+ console.error(opts.text ?? (opts.usage ? `❌ ${message}\n用法: ${opts.usage}` : `❌ ${message}`));
257
+ }
218
258
  process.exit(1);
219
259
  }
260
+ function failWithUsage(json, message, usage) {
261
+ fail(json, 'INVALID_ARGUMENT', message, { usage });
262
+ }
263
+ /** CLI 侧解析:参数错误按输出格式报错退出。 */
264
+ function parseCliArgs(args, known, json) {
265
+ try {
266
+ return parseArgs(args, known);
267
+ }
268
+ catch (e) {
269
+ return fail(json, 'INVALID_ARGUMENT', e.message);
270
+ }
271
+ }
220
272
  /**
221
273
  * 取当前会话。`--debug <sessionId>` 在宿主终端模拟托管环境(仅开发仓 daemon 认);
222
274
  * 否则读 EVOLCORE_SESSION_ID。
223
275
  */
224
- function requireManagedSession(cmd, flags) {
225
- const debugSessionId = flags.get('--debug');
226
- if (flags.has('--debug') && !debugSessionId) {
227
- console.error(`❌ --debug 需要提供 sessionId(如 --debug meta_20260910_1789013149542)`);
228
- process.exit(1);
229
- }
230
- const sessionId = debugSessionId || process.env.EVOLCORE_SESSION_ID;
276
+ function requireManagedSession(cmd, flags, json) {
277
+ const explicitDebugSessionId = flags.get('--debug');
278
+ if (flags.has('--debug') && !explicitDebugSessionId) {
279
+ fail(json, 'INVALID_ARGUMENT', '--debug 需要提供 sessionId(如 --debug meta_20260910_1789013149542)');
280
+ }
281
+ // `ec … --as-session <id>` 由 debug-mode.ts 在进程启动时注入会话环境,
282
+ // 与 `--debug <id>` 是同一种调试身份。判据:注入了 delegation token、
283
+ // 但来自从属会话的 daemon runtime epoch,这是 debug-issue-token 的固定形态
284
+ // (真正的托管任务两者都有)。两者都必须走 daemon 的 debug 分支:
285
+ // 该分支只认本地 control token,不接受 task delegation。
286
+ const asSessionDebug = !explicitDebugSessionId
287
+ && !!process.env.EVOLCORE_SESSION_ID
288
+ && !!readDelegationCredential()
289
+ && !process.env.EVOLCORE_DAEMON_RUNTIME_EPOCH;
290
+ const debugMode = !!explicitDebugSessionId || asSessionDebug;
291
+ const sessionId = explicitDebugSessionId || process.env.EVOLCORE_SESSION_ID;
231
292
  if (!sessionId) {
232
- console.error(`错误: EVOLCORE_SESSION_ID 未设置(${cmd} 仅在 evolcore 托管环境中可用;调试可加 --debug <sessionId>)`);
233
- process.exit(1);
293
+ const message = `EVOLCORE_SESSION_ID 未设置(${cmd} 仅在 evolcore 托管环境中可用;调试可加 --debug <sessionId> 或 --as-session <sessionId>)`;
294
+ fail(json, 'NO_SESSION', message, { text: `错误: ${message}` });
295
+ }
296
+ if (explicitDebugSessionId)
297
+ console.error(`[debug] 模拟会话 ${explicitDebugSessionId}`);
298
+ else if (asSessionDebug)
299
+ console.error(`[debug] --as-session 会话 ${sessionId}`);
300
+ let controlToken;
301
+ if (debugMode) {
302
+ try {
303
+ controlToken = fs.readFileSync(resolvePaths().controlToken, 'utf8').trim() || undefined;
304
+ }
305
+ catch { /* 交给 daemon 报错 */ }
234
306
  }
235
- if (debugSessionId)
236
- console.error(`[debug] 模拟会话 ${debugSessionId}`);
237
307
  return {
238
308
  sessionId,
239
309
  socketPath: resolvePaths().socket,
240
- delegationToken: readDelegationCredential(),
241
- ...(debugSessionId ? { debug: true } : {}),
310
+ // debug 身份必须用 control token;带 delegationToken 会被 daemon 直接拒绝
311
+ ...(debugMode ? { controlToken, debug: true } : { delegationToken: readDelegationCredential() }),
242
312
  };
243
313
  }
244
314
  // ── note ───────────────────────────────────────────────────────────────────
245
- async function cmdMemoryNote(args) {
315
+ async function cmdMemoryNote(args, json) {
246
316
  if (wantsHelp(args)) {
247
317
  console.log(MEMORY_HELP);
248
318
  return;
249
319
  }
250
320
  const sub = args[0];
251
321
  if (sub === 'update')
252
- return cmdMemoryNoteUpdate(args.slice(1));
322
+ return cmdMemoryNoteUpdate(args.slice(1), json);
253
323
  if (sub === 'drop')
254
- return cmdMemoryNoteDrop(args.slice(1));
324
+ return cmdMemoryNoteDrop(args.slice(1), json);
255
325
  if (sub === 'list')
256
- return cmdMemoryNoteList(args.slice(1));
257
- return cmdMemoryNoteCreate(args);
326
+ return cmdMemoryNoteList(args.slice(1), json);
327
+ return cmdMemoryNoteCreate(args, json);
258
328
  }
259
- async function cmdMemoryNoteCreate(args) {
260
- const { flags, positional } = parseArgs(args, ['--slug', '--tags', '--speakers']);
329
+ async function cmdMemoryNoteCreate(args, json) {
330
+ const { flags, positional } = parseCliArgs(args, ['--slug', '--tags', '--speakers'], json);
261
331
  const content = positional.join(' ').trim();
262
332
  const slug = flags.get('--slug');
263
333
  const tags = parseTags(flags.get('--tags'));
264
334
  const speakers = parseTags(flags.get('--speakers'));
265
335
  if (!slug)
266
- failWithUsage('必须提供 --slug', USAGE.note);
336
+ failWithUsage(json, '必须提供 --slug', USAGE.note);
267
337
  if (!content)
268
- failWithUsage('必须提供记忆内容(content)', USAGE.note);
269
- const { socketPath, ...session } = requireManagedSession('ec memory note', flags);
338
+ failWithUsage(json, '必须提供记忆内容(content)', USAGE.note);
339
+ const { socketPath, ...session } = requireManagedSession('ec memory note', flags, json);
270
340
  const result = await ipcQuery(socketPath, {
271
341
  type: 'memory.note', argv: ['memory', 'note', ...args], ...session, slug, content, tags,
272
342
  speakers: speakers && speakers.length > 0 ? speakers : undefined,
273
343
  }, 15_000);
274
- reportResult(result, (r) => {
344
+ reportResult(result, json, (r) => {
275
345
  if (r.filePath)
276
346
  console.log(r.filePath);
277
347
  });
278
348
  }
279
- async function cmdMemoryNoteUpdate(args) {
280
- const { flags, positional } = parseArgs(args, ['--slug', '--tags']);
349
+ async function cmdMemoryNoteUpdate(args, json) {
350
+ const { flags, positional } = parseCliArgs(args, ['--slug', '--tags'], json);
281
351
  const slug = positional[0];
282
352
  const content = positional.slice(1).join(' ').trim();
283
353
  const newSlug = flags.get('--slug');
284
354
  const tagsRaw = flags.get('--tags');
285
355
  const tags = parseTags(tagsRaw);
286
356
  if (!slug)
287
- failWithUsage('必须提供要修改的 slug', USAGE.noteUpdate);
357
+ failWithUsage(json, '必须提供要修改的 slug', USAGE.noteUpdate);
288
358
  if (!newSlug && tagsRaw === undefined && !content) {
289
- failWithUsage('至少提供一项要改的内容(--slug / --tags / 正文)', USAGE.noteUpdate);
359
+ failWithUsage(json, '至少提供一项要改的内容(--slug / --tags / 正文)', USAGE.noteUpdate);
290
360
  }
291
- const { socketPath, ...session } = requireManagedSession('ec memory note update', flags);
361
+ const { socketPath, ...session } = requireManagedSession('ec memory note update', flags, json);
292
362
  const result = await ipcQuery(socketPath, {
293
363
  type: 'memory.note.update', argv: ['memory', 'note', 'update', ...args], ...session, slug,
294
364
  newSlug, tags, tagsProvided: tagsRaw !== undefined,
295
365
  content: content || undefined,
296
366
  }, 15_000);
297
- reportResult(result, (r) => {
367
+ reportResult(result, json, (r) => {
298
368
  if (r.filePath)
299
369
  console.log(r.filePath);
300
370
  });
301
371
  }
302
- async function cmdMemoryNoteDrop(args) {
303
- const { flags, positional } = parseArgs(args, []);
372
+ async function cmdMemoryNoteDrop(args, json) {
373
+ const { flags, positional } = parseCliArgs(args, [], json);
304
374
  const slug = positional[0];
305
375
  if (!slug)
306
- failWithUsage('必须提供要撤销的 slug', USAGE.noteDrop);
307
- const { socketPath, ...session } = requireManagedSession('ec memory note drop', flags);
376
+ failWithUsage(json, '必须提供要撤销的 slug', USAGE.noteDrop);
377
+ const { socketPath, ...session } = requireManagedSession('ec memory note drop', flags, json);
308
378
  const result = await ipcQuery(socketPath, {
309
379
  type: 'memory.note.drop', argv: ['memory', 'note', 'drop', ...args], ...session, slug,
310
380
  }, 15_000);
311
- reportResult(result, (r) => {
381
+ reportResult(result, json, (r) => {
312
382
  console.log(`✓ 已撤销: ${r.filePath ?? slug}`);
313
383
  });
314
384
  }
315
- async function cmdMemoryNoteList(args) {
316
- const { flags } = parseArgs(args, []);
317
- const { socketPath, ...session } = requireManagedSession('ec memory note list', flags);
385
+ async function cmdMemoryNoteList(args, json) {
386
+ const { flags } = parseCliArgs(args, [], json);
387
+ const { socketPath, ...session } = requireManagedSession('ec memory note list', flags, json);
318
388
  const result = await ipcQuery(socketPath, {
319
389
  type: 'memory.note.list', argv: ['memory', 'note', 'list', ...args], ...session,
320
390
  }, 15_000);
321
- reportResult(result, (r) => {
391
+ reportResult(result, json, (r) => {
322
392
  const notes = r.notes ?? [];
323
393
  if (notes.length === 0) {
324
394
  console.log('(没有待整理的 note)');
@@ -333,10 +403,10 @@ async function cmdMemoryNoteList(args) {
333
403
  });
334
404
  }
335
405
  // ── create / update / archive(仅整理会话) ─────────────────────────────────
336
- async function cmdMemoryCreate(args) {
337
- const { flags, positional } = parseArgs(args, [
406
+ async function cmdMemoryCreate(args, json) {
407
+ const { flags, positional } = parseCliArgs(args, [
338
408
  '--scope', '--slug', '--desc', '--tags', '--keywords', '--replaces', '--peer',
339
- ]);
409
+ ], json);
340
410
  const content = positional.join(' ').trim();
341
411
  const scope = flags.get('--scope');
342
412
  const slug = flags.get('--slug');
@@ -346,20 +416,20 @@ async function cmdMemoryCreate(args) {
346
416
  const replacesRaw = flags.get('--replaces');
347
417
  const targetPeerKey = flags.get('--peer');
348
418
  if (!scope || !['agent', 'relation', 'owner'].includes(scope)) {
349
- failWithUsage('--scope 必须是 agent / relation / owner 之一', USAGE.create);
419
+ failWithUsage(json, '--scope 必须是 agent / relation / owner 之一', USAGE.create);
350
420
  }
351
421
  if (!slug)
352
- failWithUsage('必须提供 --slug', USAGE.create);
422
+ failWithUsage(json, '必须提供 --slug', USAGE.create);
353
423
  if (!desc)
354
- failWithUsage('必须提供 --desc', USAGE.create);
424
+ failWithUsage(json, '必须提供 --desc', USAGE.create);
355
425
  if (tagsRaw === undefined)
356
- failWithUsage('必须提供 --tags', USAGE.create);
426
+ failWithUsage(json, '必须提供 --tags', USAGE.create);
357
427
  if (!content)
358
- failWithUsage('必须提供记忆内容(content)', USAGE.create);
428
+ failWithUsage(json, '必须提供记忆内容(content)', USAGE.create);
359
429
  if (targetPeerKey !== undefined && scope !== 'relation') {
360
- failWithUsage('--peer 只在 --scope relation 时有效', USAGE.create);
430
+ failWithUsage(json, '--peer 只在 --scope relation 时有效', USAGE.create);
361
431
  }
362
- const { socketPath, ...session } = requireManagedSession('ec memory create', flags);
432
+ const { socketPath, ...session } = requireManagedSession('ec memory create', flags, json);
363
433
  const result = await ipcQuery(socketPath, {
364
434
  type: 'memory.create', argv: ['memory', 'create', ...args], ...session, scope, slug, description: desc,
365
435
  content,
@@ -368,7 +438,7 @@ async function cmdMemoryCreate(args) {
368
438
  replaces: replacesRaw ? replacesRaw.split(',').map(s => s.trim()).filter(Boolean) : undefined,
369
439
  targetPeerKey: targetPeerKey ?? undefined,
370
440
  }, 15_000);
371
- reportResult(result, (r) => {
441
+ reportResult(result, json, (r) => {
372
442
  if (r.filePath)
373
443
  console.log(r.filePath);
374
444
  if (Array.isArray(r.archived) && r.archived.length > 0) {
@@ -376,33 +446,33 @@ async function cmdMemoryCreate(args) {
376
446
  }
377
447
  });
378
448
  }
379
- async function cmdMemoryConsolidatedUpdate(args) {
380
- const { flags, positional } = parseArgs(args, ['--tags', '--touch']);
449
+ async function cmdMemoryConsolidatedUpdate(args, json) {
450
+ const { flags, positional } = parseCliArgs(args, ['--tags', '--touch'], json);
381
451
  const filePath = positional[0];
382
452
  const tagsRaw = flags.get('--tags');
383
453
  const touch = flags.has('--touch');
384
454
  if (!filePath)
385
- failWithUsage('必须提供要修改的记忆文件路径', USAGE.update);
455
+ failWithUsage(json, '必须提供要修改的记忆文件路径', USAGE.update);
386
456
  if (tagsRaw === undefined && !touch)
387
- failWithUsage('至少提供 --tags 或 --touch', USAGE.update);
388
- const { socketPath, ...session } = requireManagedSession('ec memory update', flags);
457
+ failWithUsage(json, '至少提供 --tags 或 --touch', USAGE.update);
458
+ const { socketPath, ...session } = requireManagedSession('ec memory update', flags, json);
389
459
  const result = await ipcQuery(socketPath, {
390
460
  type: 'memory.update', argv: ['memory', 'update', ...args], ...session, filePath,
391
461
  tags: parseTags(tagsRaw), touch,
392
462
  }, 15_000);
393
- reportResult(result, () => {
463
+ reportResult(result, json, () => {
394
464
  console.log(`✓ 已更新: ${filePath}`);
395
465
  });
396
466
  }
397
- async function cmdMemoryArchive(args) {
398
- const { flags, positional } = parseArgs(args, []);
467
+ async function cmdMemoryArchive(args, json) {
468
+ const { flags, positional } = parseCliArgs(args, [], json);
399
469
  if (positional.length === 0)
400
- failWithUsage('必须提供至少一个要归档的文件路径', USAGE.archive);
401
- const { socketPath, ...session } = requireManagedSession('ec memory archive', flags);
470
+ failWithUsage(json, '必须提供至少一个要归档的文件路径', USAGE.archive);
471
+ const { socketPath, ...session } = requireManagedSession('ec memory archive', flags, json);
402
472
  const result = await ipcQuery(socketPath, {
403
473
  type: 'memory.archive', argv: ['memory', 'archive', ...args], ...session, filePaths: positional,
404
474
  }, 15_000);
405
- reportResult(result, (r) => {
475
+ reportResult(result, json, (r) => {
406
476
  const archived = r.archived ?? positional;
407
477
  console.log(`✓ 已归档 ${archived.length} 个文件:`);
408
478
  for (const f of archived)
@@ -410,27 +480,26 @@ async function cmdMemoryArchive(args) {
410
480
  });
411
481
  }
412
482
  // ── consolidate ────────────────────────────────────────────────────────────
413
- async function cmdMemoryDemote(args) {
483
+ async function cmdMemoryDemote(args, json) {
414
484
  if (wantsHelp(args)) {
415
485
  console.log(MEMORY_HELP);
416
486
  return;
417
487
  }
418
- const { flags, positional } = parseArgs(args, ['--scope']);
488
+ const { flags, positional } = parseCliArgs(args, ['--scope'], json);
419
489
  const slug = positional[0];
420
490
  if (!slug) {
421
- console.error('用法: ec memory demote <slug> [--scope agent|owner]');
422
- process.exit(1);
491
+ const usage = 'ec memory demote <slug> [--scope agent|owner]';
492
+ fail(json, 'INVALID_ARGUMENT', '必须提供要降级的 slug', { usage, text: `用法: ${usage}` });
423
493
  }
424
494
  const scope = flags.get('--scope') ?? 'agent';
425
495
  if (scope !== 'agent' && scope !== 'owner') {
426
- console.error('--scope 必须是 agent 或 owner');
427
- process.exit(1);
496
+ fail(json, 'INVALID_ARGUMENT', '--scope 必须是 agent 或 owner', { text: '--scope 必须是 agent 或 owner' });
428
497
  }
429
- const { socketPath, ...session } = requireManagedSession('ec memory demote', flags);
498
+ const { socketPath, ...session } = requireManagedSession('ec memory demote', flags, json);
430
499
  const result = await ipcQuery(socketPath, {
431
500
  type: 'memory.demote', argv: ['memory', 'demote', ...args], ...session, slug, scope,
432
501
  }, 15_000);
433
- reportResult(result, (r) => {
502
+ reportResult(result, json, (r) => {
434
503
  if (r.conflictArchived) {
435
504
  console.log(`⚠ 目标层已有同名文件,当前记忆已归档至:`);
436
505
  console.log(` ${r.conflictArchived}`);
@@ -441,28 +510,28 @@ async function cmdMemoryDemote(args) {
441
510
  }
442
511
  });
443
512
  }
444
- async function cmdMemoryConsolidate(args) {
513
+ async function cmdMemoryConsolidate(args, json) {
445
514
  if (wantsHelp(args)) {
446
515
  console.log(MEMORY_HELP);
447
516
  return;
448
517
  }
449
518
  // 先于通用解析处理已废弃的 --scope,给出可操作的提示而不是「未知参数」
450
519
  if (args.includes('--scope')) {
451
- console.error('❌ `--scope` 已废弃。作用域由模式唯一决定,请改用 `--mode <notes|generalize|promote|profile|forget>`');
452
- process.exit(1);
520
+ fail(json, 'INVALID_ARGUMENT', '`--scope` 已废弃。作用域由模式唯一决定,请改用 `--mode <notes|generalize|promote|profile|forget>`');
453
521
  }
454
- const { flags } = parseArgs(args, ['--mode']);
522
+ const { flags } = parseCliArgs(args, ['--mode'], json);
455
523
  const mode = (flags.get('--mode') ?? 'notes');
456
524
  if (!MEMORY_MODES.includes(mode)) {
457
- console.error(`❌ --mode 必须是 ${MEMORY_MODES.join(' / ')} 之一`);
458
- process.exit(1);
525
+ fail(json, 'INVALID_ARGUMENT', `--mode 必须是 ${MEMORY_MODES.join(' / ')} 之一`);
459
526
  }
460
- const { socketPath, ...session } = requireManagedSession('ec memory consolidate', flags);
461
- console.log(`正在触发记忆整理(mode=${mode})...`);
527
+ const { socketPath, ...session } = requireManagedSession('ec memory consolidate', flags, json);
528
+ // JSON 模式下 stdout 只留结果对象
529
+ if (!json)
530
+ console.log(`正在触发记忆整理(mode=${mode})...`);
462
531
  const result = await ipcQuery(socketPath, {
463
532
  type: 'memory.consolidate', argv: ['memory', 'consolidate', ...args], ...session, mode,
464
533
  }, 30_000);
465
- reportResult(result, (r) => {
534
+ reportResult(result, json, (r) => {
466
535
  if (r.queued) {
467
536
  console.log(`⏳ 整理请求已入队(${mode} 模式),前一个整理会话结束后自动启动`);
468
537
  for (const line of formatQueueDetails(r.queued))
@@ -480,43 +549,18 @@ async function cmdMemoryConsolidate(args) {
480
549
  console.log('整理会话在后台继续运行。');
481
550
  });
482
551
  }
483
- // ── feedback(整理会话反馈)─────────────────────────────────────────────────
484
- /**
485
- * 整理会话用 `ec memory feedback "<文本>"` 记录本次整理的反馈内容。
486
- * 文本写入批次目录的 ops.jsonl(op=feedback-set),收尾时据此投递回发起会话。
487
- * 未调用则不写,收尾回退到默认统计模板。
488
- */
489
- async function cmdMemoryFeedback(args) {
490
- if (wantsHelp(args)) {
491
- console.log(MEMORY_HELP);
492
- return;
493
- }
494
- const { flags, positional } = parseArgs(args, []);
495
- const text = positional.join(' ').trim();
496
- if (!text) {
497
- console.error('用法: ec memory feedback "<反馈文本>"');
498
- process.exit(1);
499
- }
500
- const { socketPath, ...session } = requireManagedSession('ec memory feedback', flags);
501
- const result = await ipcQuery(socketPath, {
502
- type: 'memory.feedback', argv: ['memory', 'feedback', ...args], ...session, text,
503
- }, 15_000);
504
- reportResult(result, () => {
505
- console.log('✓ 已记录反馈内容');
506
- });
507
- }
508
552
  // ── 结果输出 ───────────────────────────────────────────────────────────────
509
- function reportResult(result, ok) {
553
+ function reportResult(result, json, ok) {
510
554
  if (!result) {
511
- console.error('错误: 无法连接 evolcore 服务');
512
- process.exit(1);
555
+ fail(json, 'DAEMON_UNAVAILABLE', '无法连接 evolcore 服务', { text: '错误: 无法连接 evolcore 服务' });
513
556
  }
514
557
  const r = result;
515
- if (r.ok) {
516
- ok(r);
517
- }
518
- else {
519
- console.error(r.error || '操作失败');
520
- process.exit(1);
558
+ if (!r.ok) {
559
+ const message = r.error || '操作失败';
560
+ fail(json, r.code || 'MEMORY_OPERATION_FAILED', message, { text: message });
521
561
  }
562
+ if (json)
563
+ console.log(JSON.stringify(r, null, 2));
564
+ else
565
+ ok(r);
522
566
  }