evolcore 0.0.27 → 0.0.29-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 (195) hide show
  1. package/CHANGELOG.md +38 -15
  2. package/bin/ec-safe-output.js +270 -54
  3. package/dist/agents/baseagent-availability.js +37 -1
  4. package/dist/agents/baseagent.js +29 -14
  5. package/dist/agents/claude-runner.js +171 -13
  6. package/dist/agents/codex/app-server-client.js +13 -2
  7. package/dist/agents/codex/catalog.js +7 -1
  8. package/dist/agents/codex/diagnostics.js +4 -3
  9. package/dist/agents/codex-runner.js +444 -106
  10. package/dist/agents/gemini-runner.js +35 -17
  11. package/dist/aun/group/history.js +130 -90
  12. package/dist/aun/group/operations.js +1 -3
  13. package/dist/aun/menu/client.js +1 -0
  14. package/dist/aun/menu/transport.js +5 -3
  15. package/dist/aun/msg/managed-operation.js +70 -0
  16. package/dist/aun/msg/p2p.js +1 -3
  17. package/dist/aun/msg/upload.js +2 -2
  18. package/dist/aun/outbox.js +1 -37
  19. package/dist/channels/aun.js +262 -86
  20. package/dist/channels/daemon.js +18 -1
  21. package/dist/channels/feishu.js +1 -2
  22. package/dist/cli/agent-command.js +23 -3
  23. package/dist/cli/agent-create.js +68 -2
  24. package/dist/cli/agent.js +12 -2
  25. package/dist/cli/aun-commands.js +87 -13
  26. package/dist/cli/cli-argv.js +26 -0
  27. package/dist/cli/daemon-commands.js +116 -149
  28. package/dist/cli/data-command.js +4 -4
  29. package/dist/cli/diag.js +1 -1
  30. package/dist/cli/handoff-command.js +2 -0
  31. package/dist/cli/index.js +18 -1
  32. package/dist/cli/managed-command-guard.js +10 -0
  33. package/dist/cli/memory-command.js +441 -0
  34. package/dist/cli/menu-client.js +4 -0
  35. package/dist/cli/restart-monitor.js +127 -23
  36. package/dist/cli/skill-command.js +41 -0
  37. package/dist/cli/task-context.js +2 -23
  38. package/dist/cli/trigger-command.js +9 -1
  39. package/dist/cli/watch-msg-diag.js +209 -0
  40. package/dist/config/config-field-policy.js +52 -3
  41. package/dist/config/config-manager.js +17 -4
  42. package/dist/config/contact-book-store.js +2 -2
  43. package/dist/config/mention-mode.js +7 -2
  44. package/dist/config/merge.js +28 -9
  45. package/dist/config/resolved-config-op.js +1 -1
  46. package/dist/config/runtime-env.js +137 -0
  47. package/dist/config/schema-registry.js +11 -1
  48. package/dist/config-store.js +10 -10
  49. package/dist/core/audit/event-key.js +3 -1
  50. package/dist/core/audit/log-integrity.js +4 -0
  51. package/dist/core/auth/agent-delegation.js +2 -1
  52. package/dist/core/auth/auth-gateway.js +13 -6
  53. package/dist/core/auth/authorization-audit.js +54 -19
  54. package/dist/core/auth/operation-authorizer.js +17 -0
  55. package/dist/core/auth/operation-catalog.js +9 -0
  56. package/dist/core/causation/context.js +5 -1
  57. package/dist/core/command/agent-control.js +1 -2
  58. package/dist/core/command/command-handler.js +80 -24
  59. package/dist/core/command/diag-service.js +292 -27
  60. package/dist/core/command/memory-slash.js +270 -0
  61. package/dist/core/command/menu-catalog.js +1 -0
  62. package/dist/core/command/menu-handler.js +331 -79
  63. package/dist/core/command/menu-protocol.js +45 -4
  64. package/dist/core/command/slash-gate.js +10 -10
  65. package/dist/core/command/slash-handler.js +230 -79
  66. package/dist/core/data-migration.js +282 -505
  67. package/dist/core/event-catalog.js +26 -0
  68. package/dist/core/evolagent-registry.js +6 -0
  69. package/dist/core/evolagent.js +0 -10
  70. package/dist/core/handoff/store.js +1 -20
  71. package/dist/core/inference/text-inference.js +57 -1
  72. package/dist/core/memory/consolidation-context.js +505 -0
  73. package/dist/core/memory/consolidation-ops-records.js +124 -0
  74. package/dist/core/memory/consolidation-trigger.js +394 -0
  75. package/dist/core/memory/index-manager.js +308 -0
  76. package/dist/core/memory/launch-consolidation.js +395 -0
  77. package/dist/core/memory/memory-admin.js +234 -0
  78. package/dist/core/memory/memory-document.js +100 -0
  79. package/dist/core/memory/memory-session-policy.js +41 -0
  80. package/dist/core/memory/memory-signature.js +257 -0
  81. package/dist/core/memory/memory-writer.js +432 -0
  82. package/dist/core/memory/staleness.js +117 -0
  83. package/dist/core/message/auto-mention-gate.js +454 -0
  84. package/dist/core/message/im-renderer.js +90 -20
  85. package/dist/core/message/message-bridge.js +333 -23
  86. package/dist/core/message/message-log.js +1 -0
  87. package/dist/core/message/message-queue.js +114 -27
  88. package/dist/core/message/pending-hints.js +329 -161
  89. package/dist/core/message/response-engine.js +596 -90
  90. package/dist/core/permission/ec-command-parser.js +1044 -87
  91. package/dist/core/permission/protected-paths.js +376 -126
  92. package/dist/core/permission/sandbox-runtime.js +40 -12
  93. package/dist/core/permission/tool-error-code.js +96 -13
  94. package/dist/core/permission/tool-policy.js +277 -24
  95. package/dist/core/role/runtime-policy.js +3 -7
  96. package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
  97. package/dist/core/session/session-fs-store.js +0 -76
  98. package/dist/core/session/session-idle-monitor.js +355 -0
  99. package/dist/core/session/session-manager.js +482 -17
  100. package/dist/core/session/session-mapper.js +33 -16
  101. package/dist/core/session/session-renew.js +217 -155
  102. package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
  103. package/dist/core/skill/global-watcher.js +282 -0
  104. package/dist/core/skill/index-generator.js +84 -0
  105. package/dist/core/skill/startup.js +73 -0
  106. package/dist/core/skill/switch-resolver.js +115 -0
  107. package/dist/core/task/task-activity-store.js +219 -0
  108. package/dist/eck/kit-renderer.js +30 -3
  109. package/dist/eck/manifest-engine.js +26 -4
  110. package/dist/index.js +722 -171
  111. package/dist/ipc.js +564 -2
  112. package/dist/paths.js +30 -9
  113. package/dist/product.js +1 -0
  114. package/dist/response-system/engines/v1/proactive-flow.js +116 -15
  115. package/dist/response-system/modes/single-session/index.js +3 -0
  116. package/dist/trigger/feedback.js +1 -0
  117. package/dist/trigger/manager.js +0 -10
  118. package/dist/trigger/scheduler.js +11 -0
  119. package/dist/trigger/validation.js +22 -0
  120. package/dist/utils/atomic-write.js +98 -0
  121. package/dist/utils/avatar-upload.js +59 -18
  122. package/dist/utils/cross-platform.js +87 -16
  123. package/dist/utils/error-dict.json +6 -3
  124. package/dist/utils/error-utils.js +27 -0
  125. package/dist/utils/logger.js +32 -10
  126. package/dist/utils/media-cache.js +84 -0
  127. package/dist/utils/msg-diag.js +97 -0
  128. package/dist/utils/npm-ops.js +119 -4
  129. package/dist/utils/package-version-info.js +244 -0
  130. package/dist/utils/welcome.js +37 -8
  131. package/dist/utils/windows-shell-trust.js +37 -0
  132. package/kits/docs/AUTHORING.md +314 -0
  133. package/kits/docs/GUIDE.md +2 -0
  134. package/kits/docs/INDEX.md +2 -0
  135. package/kits/docs/aun/CHEATSHEET.md +4 -0
  136. package/kits/docs/aun/SDK.md +60 -0
  137. package/kits/docs/channels/aun.md +8 -0
  138. package/kits/docs/context-assembly.md +10 -6
  139. package/kits/docs/evolcore/INDEX.md +17 -3
  140. package/kits/docs/evolcore/agent-create.md +24 -0
  141. package/kits/docs/evolcore/agent.md +5 -2
  142. package/kits/docs/evolcore/config.md +1 -1
  143. package/kits/docs/evolcore/ec-capabilities.md +364 -0
  144. package/kits/docs/evolcore/group.md +34 -18
  145. package/kits/docs/evolcore/memory-architecture.md +78 -0
  146. package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
  147. package/kits/docs/evolcore/memory-consolidation.md +263 -0
  148. package/kits/docs/evolcore/memory-tags.md +63 -0
  149. package/kits/docs/evolcore/memory.md +110 -0
  150. package/kits/docs/evolcore/menu-client.md +27 -11
  151. package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
  152. package/kits/docs/evolcore/menu-protocol.md +213 -0
  153. package/kits/docs/evolcore/msg.md +2 -0
  154. package/kits/docs/evolcore/trigger.md +7 -5
  155. package/kits/docs/path-registry.md +6 -8
  156. package/kits/docs/prompt-loading-architecture.md +5 -3
  157. package/kits/docs/venues/group.md +5 -4
  158. package/kits/eck_manifest.json +294 -88
  159. package/kits/eck_manifest.memory.json +257 -0
  160. package/kits/rules/03-identity.md +8 -8
  161. package/kits/rules/05-venue.md +4 -2
  162. package/kits/rules/06-channel.md +1 -1
  163. package/kits/schemas/_meta.json +276 -47
  164. package/kits/schemas/agent-config.schema.12.json +38 -2
  165. package/kits/schemas/agent-config.schema.13.json +591 -0
  166. package/kits/schemas/daemon.schema.6.json +9 -9
  167. package/kits/schemas/defaults.schema.5.json +29 -0
  168. package/kits/schemas/relation-config.schema.8.json +14 -1
  169. package/kits/schemas/relation-config.schema.9.json +129 -0
  170. package/kits/schemas/role-config.schema.1.json +3 -3
  171. package/kits/skills/.switches.json +3 -0
  172. package/kits/skills/ec-skill/SKILL.md +217 -0
  173. package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
  174. package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
  175. package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
  176. package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
  177. package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
  178. package/kits/templates/decision/auto-mention.md +25 -0
  179. package/kits/templates/decision/session-renew.md +22 -0
  180. package/kits/templates/memory-consolidation/forget.md +104 -0
  181. package/kits/templates/memory-consolidation/generalize.md +76 -0
  182. package/kits/templates/memory-consolidation/notes.md +133 -0
  183. package/kits/templates/memory-consolidation/profile.md +58 -0
  184. package/kits/templates/memory-consolidation/promote.md +72 -0
  185. package/kits/templates/memory-consolidation/workflow.md +75 -0
  186. package/kits/templates/system-fragments/bootstrap.md +14 -5
  187. package/kits/templates/system-fragments/commands.md +1 -0
  188. package/kits/templates/system-fragments/group-history.md +6 -0
  189. package/kits/templates/system-fragments/identity.md +0 -3
  190. package/kits/templates/system-fragments/memory.md +79 -0
  191. package/kits/templates/system-fragments/reply.md +1 -0
  192. package/kits/templates/system-fragments/venue.md +1 -1
  193. package/package.json +4 -6
  194. package/dist/trigger/legacy-session-history.js +0 -19
  195. package/kits/templates/system-fragments/group-unread-history.md +0 -7
@@ -4,6 +4,7 @@ import { resolvePaths } from '../../paths.js';
4
4
  import crypto from 'node:crypto';
5
5
  import { buildAuthorizationEventKey, buildToolLifecycleEventKey, normalizeToolCorrelation } from '../audit/event-key.js';
6
6
  import { normalizeExecutionPermissionMode } from '../permission/mode.js';
7
+ import { normalizeToolErrorClassification } from '../permission/tool-error-code.js';
7
8
  export function auditCommandAuthorization(event) {
8
9
  const shouldAudit = event.source === 'menu.cli' ||
9
10
  event.decision === 'deny' ||
@@ -101,6 +102,13 @@ export function auditFullAccessEvent(input) {
101
102
  * denials otherwise only reach `logger.warn` and `TriggerExecutionAnomaly`.
102
103
  */
103
104
  export function auditToolPreflightDenial(input) {
105
+ const classification = normalizeToolErrorClassification({
106
+ errorCode: 'POLICY_DENIED',
107
+ decisionSource: 'policy',
108
+ policyCode: input.policyCode,
109
+ executed: false,
110
+ executionState: 'blocked',
111
+ });
104
112
  auditCommandAuthorization({
105
113
  ts: Date.now(),
106
114
  eventKey: buildToolLifecycleEventKey(input),
@@ -114,7 +122,8 @@ export function auditToolPreflightDenial(input) {
114
122
  executed: false,
115
123
  executionState: 'blocked',
116
124
  decisionSource: 'policy',
117
- errorCode: 'POLICY_DENIED',
125
+ errorCode: classification.errorCode,
126
+ ...(classification.errorDetailCode ? { errorDetailCode: classification.errorDetailCode } : {}),
118
127
  toolName: input.toolName,
119
128
  policyCode: input.policyCode,
120
129
  protectionClass: input.protectionClass ?? protectionClassForPolicy(input.policyCode),
@@ -158,6 +167,7 @@ export function auditCodexApprovalDecision(input) {
158
167
  errorCode: input.decision === 'deny'
159
168
  ? input.decisionSource === 'approval' ? 'USER_DENIED' : input.decisionSource === 'policy' ? 'POLICY_DENIED' : infrastructureErrorCode(input.policyCode)
160
169
  : undefined,
170
+ ...(input.errorDetailCode ? { errorDetailCode: input.errorDetailCode } : {}),
161
171
  sessionId: input.sessionId ?? 'unknown',
162
172
  agentAid: input.agentAid ?? 'unknown',
163
173
  agentName: input.agentName,
@@ -313,14 +323,25 @@ function infrastructureErrorCode(policyCode) {
313
323
  }
314
324
  function buildAuditRecord(event) {
315
325
  const executed = event.executed ?? false;
316
- const decisionSource = event.decisionSource ?? (event.decision === 'deny' ? 'policy' : event.decision === 'pending' ? 'approval' : 'command');
317
- const errorCode = event.errorCode ?? (event.decision === 'deny'
318
- ? decisionSource === 'approval'
319
- ? 'USER_DENIED'
320
- : decisionSource === 'policy'
321
- ? 'POLICY_DENIED'
322
- : 'EXECUTION_FAILED'
323
- : undefined);
326
+ const roleDenial = event.decision === 'deny'
327
+ && (event.errorCode === 'ROLE_DENIED'
328
+ || event.code === 'ROLE_ACCESS_DENIED'
329
+ || event.policyCode === 'ROLE_ACCESS_DENIED');
330
+ const decisionSource = event.decisionSource
331
+ ?? (roleDenial
332
+ ? 'role'
333
+ : event.decision === 'deny' ? 'policy' : event.decision === 'pending' ? 'approval' : 'command');
334
+ const executionFailed = executed
335
+ && (event.executionState === 'failed' || (event.exitCode !== undefined && event.exitCode !== 0));
336
+ const rawErrorCode = event.errorCode ?? (event.decision === 'deny'
337
+ ? decisionSource === 'role'
338
+ ? 'ROLE_DENIED'
339
+ : decisionSource === 'approval'
340
+ ? 'USER_DENIED'
341
+ : decisionSource === 'policy'
342
+ ? 'POLICY_DENIED'
343
+ : 'EXECUTION_FAILED'
344
+ : executionFailed ? 'EXECUTION_FAILED' : undefined);
324
345
  const executionState = event.executionState
325
346
  ?? (event.decision === 'deny'
326
347
  ? 'blocked'
@@ -329,25 +350,35 @@ function buildAuditRecord(event) {
329
350
  : executed
330
351
  ? (event.exitCode === undefined || event.exitCode === 0 ? 'completed' : 'failed')
331
352
  : 'authorized');
353
+ const classification = rawErrorCode !== undefined
354
+ ? normalizeToolErrorClassification({
355
+ errorCode: rawErrorCode,
356
+ errorDetailCode: event.errorDetailCode,
357
+ decisionSource,
358
+ executed,
359
+ executionState,
360
+ exitCode: event.exitCode,
361
+ policyCode: event.policyCode,
362
+ })
363
+ : undefined;
364
+ const errorCode = classification?.errorCode;
332
365
  const ecwebRequestId = event.source === 'ecweb' ? event.ecwebRequestId : undefined;
333
366
  const requestId = event.requestId ?? ecwebRequestId;
334
367
  const correlationId = event.correlationId ?? event.callId ?? requestId;
335
- const lifecycleId = event.callId ?? correlationId ?? requestId;
336
368
  return {
369
+ type: 'authorization:audit',
337
370
  ts: event.ts,
338
371
  eventKey: event.eventKey ?? (ecwebRequestId
339
372
  ? `ecweb:${crypto.createHash('sha256').update(JSON.stringify([
340
373
  ecwebRequestId, event.targetAgentAid ?? event.selfAid, event.operation,
341
374
  ])).digest('hex').slice(0, 20)}`
342
- : lifecycleId
343
- ? buildAuthorizationEventKey({
344
- sessionId: event.sessionId,
345
- callId: event.callId,
346
- correlationId,
347
- requestId,
348
- operation: event.operation,
349
- })
350
- : undefined),
375
+ : buildAuthorizationEventKey({
376
+ sessionId: event.sessionId,
377
+ callId: event.callId,
378
+ correlationId,
379
+ requestId,
380
+ operation: event.operation,
381
+ })),
351
382
  eventPhase: event.eventPhase,
352
383
  callId: event.callId,
353
384
  correlationId,
@@ -360,6 +391,9 @@ function buildAuditRecord(event) {
360
391
  toolName: event.toolName,
361
392
  approvalMethod: event.approvalMethod,
362
393
  errorCode,
394
+ errorDetailCode: classification?.errorDetailCode,
395
+ classificationMissing: classification?.classificationMissing || event.classificationMissing,
396
+ classificationConflict: classification?.classificationConflict || event.classificationConflict,
363
397
  policyCode: event.policyCode ?? event.code,
364
398
  protectionClass: event.protectionClass,
365
399
  matchedPath: event.matchedPath,
@@ -459,6 +493,7 @@ function logAuditEvent(record) {
459
493
  record.correlationId ? `correlation=${record.correlationId}` : null,
460
494
  record.eventKey ? `eventKey=${record.eventKey}` : null,
461
495
  record.eventPhase ? `eventPhase=${record.eventPhase}` : null,
496
+ record.sessionScope ? `sessionScope=${record.sessionScope}` : null,
462
497
  record.sessionId ? `session=${record.sessionId}` : null,
463
498
  record.agentName ? `agentName=${JSON.stringify(record.agentName)}` : null,
464
499
  record.agentAid ? `agent=${record.agentAid}` : null,
@@ -41,6 +41,10 @@ const DAEMON_SERVICE_OPERATIONS = new Set([
41
41
  'system.restart',
42
42
  'system.upgrade',
43
43
  ]);
44
+ const DAEMON_OWNER_MODEL_OPTION_READ_OPERATIONS = new Set([
45
+ 'model.list',
46
+ 'model.effort.current',
47
+ ]);
44
48
  export function authorizeCommand(ctx) {
45
49
  return authorizeCommandInternal(ctx);
46
50
  }
@@ -186,6 +190,19 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
186
190
  matchedRule: 'daemon-service',
187
191
  };
188
192
  }
193
+ if (ctx.processRole === 'daemon-owner'
194
+ && ctx.fromControlChannel
195
+ && (intent.scope === 'relation' || intent.scope === 'agent')
196
+ && DAEMON_OWNER_MODEL_OPTION_READ_OPERATIONS.has(operation)) {
197
+ return {
198
+ allow: true,
199
+ operation,
200
+ scope: intent.scope,
201
+ role: 'daemon-owner',
202
+ dangerous: opMeta.dangerous,
203
+ matchedRule: 'daemon-owner-model-options-read',
204
+ };
205
+ }
189
206
  if (requiresDaemonOwner(ctx, resolvedConfigCommand)) {
190
207
  if (!hasDaemonOwnerRole(ctx)) {
191
208
  const reasonCode = daemonOwnerDenialReasonCode(ctx);
@@ -53,6 +53,7 @@ const GROUP_MANAGEMENT_OPERATIONS = [
53
53
  ['ec.group.blocklist', 'read', ['agent'], 'Read the group block list'],
54
54
  ['ec.group.report-status', 'write-own', ['relation', 'agent'], 'Report the current Agent group status'],
55
55
  ['ec.group.member-statuses', 'read', ['relation', 'agent'], 'Read reported group member statuses'],
56
+ ['ec.group.mention', 'write-own', ['relation', 'agent'], 'Read or update the group mention mode'],
56
57
  ['ec.group.rules.set', 'write-agent', ['agent'], 'Upload and publish group rules content'],
57
58
  ['ec.group.rules.publish', 'write-agent', ['agent'], 'Publish the current group rules file'],
58
59
  ['ec.group.rules.update', 'write-agent', ['agent'], 'Update group join rules'],
@@ -799,6 +800,14 @@ const OPERATIONS = [
799
800
  description: '查看当前中间活动显示策略',
800
801
  sources: ['menu'],
801
802
  },
803
+ {
804
+ id: 'task.activity.read',
805
+ category: 'read',
806
+ dangerous: false,
807
+ defaultScopes: ['relation'],
808
+ description: '读取当前关系内任务的 Activity 列表和详情',
809
+ sources: ['menu'],
810
+ },
802
811
  {
803
812
  id: 'activity.update',
804
813
  category: 'write-agent',
@@ -1,6 +1,10 @@
1
1
  import crypto from 'crypto';
2
2
  import { CAUSATION_VERSION, MAX_TRIGGER_CAUSATION_DEPTH, } from './types.js';
3
- const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
3
+ // Trigger identifiers may be daemon-owned and conventionally begin with
4
+ // underscores (for example, `__session-idle-check`). Internal trace/span IDs
5
+ // are still generated with alphabetic prefixes, so accepting `_` here only
6
+ // broadens validation for persisted/propagated trigger and run IDs.
7
+ const ID_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._:-]{0,127}$/;
4
8
  function newId(prefix) {
5
9
  return `${prefix}_${crypto.randomUUID().replace(/-/g, '')}`;
6
10
  }
@@ -750,8 +750,7 @@ export async function execAgentQuery(args, service, subject) {
750
750
  canConfigure: subject?.ecwebManagement === true
751
751
  && subject?.isDaemonOwner === true
752
752
  && subject.canApprovePersistentFullAccess !== false
753
- && subject.chatType !== 'group'
754
- && fullaccessStatus.hostEnabled,
753
+ && subject.chatType !== 'group',
755
754
  },
756
755
  };
757
756
  // 叠加构建进度(create 受理后、ready 前可见;ready 后文件仍在,可反映软失败 warn)
@@ -1,11 +1,12 @@
1
1
  import { BaseagentRunnerUnavailableError } from '../../agents/runner-types.js';
2
2
  import { loadedBaseagentIds } from '../../agents/baseagent-availability.js';
3
+ import { closeDiagnosticSession, diagnosticTokenHash, isDiagnosticSessionActive } from './diag-service.js';
3
4
  import { parseMenuClientArgs } from '../../cli/menu-client.js';
4
5
  import { ensureSessionRuntimeDir } from '../../cli/task-context.js';
5
6
  import { renderCommandCardAsText } from '../interaction-router.js';
6
7
  import { buildEnvelope, deliveryTargetFromSession, isInteractionSendAccepted, replyContextFromSession, sendInteractionPayload } from '../message/message-utils.js';
7
8
  import { resolvePaths, getPackageRoot } from '../../paths.js';
8
- import { isFullAccessEnabled, loadDaemonConfig } from '../../config-store.js';
9
+ import { loadDaemonConfig } from '../../config-store.js';
9
10
  import { logger } from '../../utils/logger.js';
10
11
  import { resolvePeerRoleDetail, roleToSessionIdentity } from '../../config/peer-role-resolver.js';
11
12
  import { isManagementRole } from '../../config/builtin-roles.js';
@@ -19,7 +20,7 @@ import { applyTriggerPatch, replaceEditableTriggerDefinition } from '../../trigg
19
20
  import { definitionRevision } from '../../trigger/validation.js';
20
21
  import { tryParseChannelKey } from '../channel-loader.js';
21
22
  import { displaySessionTitle } from '../session/session-title.js';
22
- import { buildSessionTurnList } from '../session/session-turns.js';
23
+ import { buildTranscriptTurns } from '../session/session-transcript.js';
23
24
  import { resolveConfigCommand } from '../../config/resolved-config-op.js';
24
25
  import { AUTHENTICATED_CONFIG_ROLE_ENV, executeResolvedConfigCommandAsync } from '../../config/config-operation-service.js';
25
26
  import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
@@ -27,6 +28,7 @@ import { auditCommandAuthorization, auditFullAccessEvent, hashArgv } from '../au
27
28
  import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
28
29
  import { hasTrustedFullAccessDelegation, trustedExecutionIdentityInput } from '../auth/agent-delegation.js';
29
30
  import { executeManagedAidOperation, ManagedAidOperationError, managedAidShowViewForRole, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
31
+ import { MAX_AVATAR_SOURCE_BYTES } from '../../utils/avatar-upload.js';
30
32
  import { executeManagedGroupOperation, isManagedGroupRelationRead, isManagedGroupRelationWrite, ManagedGroupOperationError, resolveManagedGroupOperation, executeManagedMsgOperation, ManagedMsgOperationError, resolveManagedMsgOperation, executeManagedFsOperation, ManagedFsOperationError, resolveManagedFsOperation, } from '../../aun/msg/managed-operation.js';
31
33
  import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
32
34
  import { isHClassPath, isLClassPath, isSameOrDescendant } from '../permission/protected-paths.js';
@@ -37,7 +39,7 @@ import { parsePeerKey } from '../relation/peer-identity.js';
37
39
  import { isQuickCommand } from './slash-gate.js';
38
40
  import { handleSlashCommand } from './slash-handler.js';
39
41
  import { resolveChatMode } from '../message/peer-mode.js';
40
- import { execMenuAction as menuExecMenuAction, execMenuForEcweb as menuExecMenuForEcweb, execMenuForControl as menuExecMenuForControl, execMenuQuery as menuExecMenuQuery, execMenuUpdate as menuExecMenuUpdate, getMenuItems as menuGetMenuItems, getSubMenuItems as menuGetSubMenuItems, } from './menu-handler.js';
42
+ import { execMenuAction as menuExecMenuAction, execMenuForEcweb as menuExecMenuForEcweb, execMenuForControl as menuExecMenuForControl, execMenuQuery as menuExecMenuQuery, execMenuUpdate as menuExecMenuUpdate, getMenuItems as menuGetMenuItems, getSubMenuItems as menuGetSubMenuItems, buildTopicForkSourcePayload, } from './menu-handler.js';
41
43
  import { getMenuCatalog, execMenuValueBatch, validateMenuValueBatchArgs } from './menu-catalog.js';
42
44
  export { isProcessLevelOwner } from './menu-handler.js';
43
45
  const CLI_EXEC_TIMEOUT_MS = 15_000;
@@ -123,6 +125,7 @@ export class CommandHandler {
123
125
  legacySingleRunnerMode;
124
126
  primaryRunnerKey;
125
127
  agentRegistry;
128
+ memoryConsolidationLauncher;
126
129
  agentApplicationService;
127
130
  triggerSchedulerResolver;
128
131
  triggerSchedulerFallback;
@@ -166,7 +169,34 @@ export class CommandHandler {
166
169
  throw new Error('Menu request file must be a regular file of at most 128 KiB');
167
170
  return fs.readFileSync(target, 'utf8');
168
171
  });
169
- return { ok: true, response: await send(options) };
172
+ const binding = session.metadata?.diagnostic;
173
+ if (binding || session.metadata?.diagnosticStatus === 'cancelled') {
174
+ if (!isDiagnosticSessionActive(session, this.sessionManager.getChatDir(session))) {
175
+ return { ok: false, code: 'DIAG_CLOSED', error: '诊断授权已结束,禁止旧会话继续访问' };
176
+ }
177
+ if (binding && (options.targetAid !== binding.controlAid
178
+ || diagnosticTokenHash(options.remoteAccessToken ?? options.ecwebToken ?? '') !== binding.tokenHash)) {
179
+ return { ok: false, code: 'DIAG_SCOPE_MISMATCH', error: '诊断请求必须使用本会话绑定的主机和 Token' };
180
+ }
181
+ }
182
+ const guardedOptions = binding
183
+ ? {
184
+ ...options,
185
+ beforeSend: () => {
186
+ if (!isDiagnosticSessionActive(session, this.sessionManager.getChatDir(session))) {
187
+ const error = new Error('诊断授权已结束,禁止旧会话继续访问');
188
+ error.code = 'DIAG_CLOSED';
189
+ throw error;
190
+ }
191
+ },
192
+ }
193
+ : options;
194
+ const response = await send(guardedOptions);
195
+ if (binding && response?.error?.code === 'REMOTE_ACCESS_TOKEN_INVALID') {
196
+ closeDiagnosticSession(session, this.sessionManager.getChatDir(session));
197
+ this.messageQueue?.clearBySession(session.id);
198
+ }
199
+ return { ok: true, response };
170
200
  }
171
201
  catch (error) {
172
202
  return { ok: false, code: error.code ?? 'MENU_REQUEST_FAILED', error: error.message };
@@ -482,8 +512,6 @@ export class CommandHandler {
482
512
  if (!mode)
483
513
  return undefined;
484
514
  if (mode === 'fullaccess') {
485
- if (!isFullAccessEnabled())
486
- return 'fullaccess 功能未启用';
487
515
  return canApprovePersistentFullAccess ? undefined : '无权限:只有 daemon-owner 可以配置 fullaccess Trigger';
488
516
  }
489
517
  if (!role) {
@@ -901,6 +929,10 @@ export class CommandHandler {
901
929
  setStatsCollector(collector) {
902
930
  this.statsCollector = collector;
903
931
  }
932
+ /** 注入记忆整理入口(daemon 的统一整理队列),供 /memory consolidate 使用。返回 null 表示已入队。 */
933
+ setMemoryConsolidationLauncher(launcher) {
934
+ this.memoryConsolidationLauncher = launcher;
935
+ }
904
936
  registerAdapter(adapter) {
905
937
  this.adapters.set(adapter.channelName, adapter);
906
938
  }
@@ -1017,8 +1049,12 @@ export class CommandHandler {
1017
1049
  const item = {
1018
1050
  value: s.threadId,
1019
1051
  label: displayName,
1020
- lable: s.lable || null,
1052
+ topicLabel: s.label || null,
1053
+ topicStatus: s.topicStatus ?? 'pending',
1021
1054
  };
1055
+ const forkSource = buildTopicForkSourcePayload(s);
1056
+ if (forkSource)
1057
+ item.forkSource = forkSource;
1022
1058
  if (s.agentSessionId) {
1023
1059
  item.agentSessionId = s.agentSessionId;
1024
1060
  const fileInfo = this.sessionManager.getSessionFileInfo(s.projectPath, s.agentSessionId, s.baseagent, s.selfAID);
@@ -1332,8 +1368,8 @@ export class CommandHandler {
1332
1368
  }
1333
1369
  if (sub.startsWith('enable ')) {
1334
1370
  const { definition } = extractAndFindTrigger(sub, 'enable ', 'enable');
1335
- if (definition.execution.permissionMode === 'fullaccess' && (!isDaemonOwner || !isFullAccessEnabled())) {
1336
- return '❌ 只有 daemon-owner 可以启用 fullaccess Trigger,且 daemon.json.fullaccess.enabled 必须为 true';
1371
+ if (definition.execution.permissionMode === 'fullaccess' && !isDaemonOwner) {
1372
+ return '❌ 只有 daemon-owner 可以启用 fullaccess Trigger';
1337
1373
  }
1338
1374
  if (definition.execution.permissionMode === 'fullaccess' && !canApprovePersistentFullAccess) {
1339
1375
  return '❌ 当前认证通道不能批准持久 fullaccess Trigger';
@@ -1543,9 +1579,7 @@ export class CommandHandler {
1543
1579
  return {
1544
1580
  ok: false,
1545
1581
  error: permissionError,
1546
- code: /fullaccess 功能未启用/.test(permissionError) ? 'NOT_SUPPORTED'
1547
- : /无权限|只有 daemon-owner/.test(permissionError) ? 'FORBIDDEN'
1548
- : undefined,
1582
+ code: /无权限|只有 daemon-owner/.test(permissionError) ? 'FORBIDDEN' : undefined,
1549
1583
  };
1550
1584
  }
1551
1585
  const runtimeFieldError = this.validateTriggerRuntimeFields(updated, triggerRole.role, triggerRole.baseagent);
@@ -1631,7 +1665,7 @@ export class CommandHandler {
1631
1665
  async handleRewindList(session, agent) {
1632
1666
  try {
1633
1667
  const messages = await agent.getSessionMessages(session.agentSessionId, session.projectPath);
1634
- const turns = buildSessionTurnList(messages);
1668
+ const turns = buildTranscriptTurns(messages);
1635
1669
  if (turns.length === 0) {
1636
1670
  return '📋 当前会话暂无对话记录';
1637
1671
  }
@@ -1651,8 +1685,11 @@ export class CommandHandler {
1651
1685
  }
1652
1686
  async handleRewind(session, agent, turnNum, mode) {
1653
1687
  try {
1688
+ if (session.threadId && turnNum === 1) {
1689
+ return '❌ 话题会话暂不支持 /rewind 1,仅开放 N>1';
1690
+ }
1654
1691
  const messages = await agent.getSessionMessages(session.agentSessionId, session.projectPath);
1655
- const turns = buildSessionTurnList(messages);
1692
+ const turns = buildTranscriptTurns(messages);
1656
1693
  if (turnNum < 1 || turnNum > turns.length) {
1657
1694
  return `❌ 轮次超出范围,当前共 ${turns.length} 轮`;
1658
1695
  }
@@ -1688,19 +1725,38 @@ export class CommandHandler {
1688
1725
  if (mode === 'chat' || mode === 'all') {
1689
1726
  const discarded = turns.length - turnNum + 1;
1690
1727
  if (agent.rollbackSessionTurns) {
1691
- const ok = await agent.rollbackSessionTurns(session.agentSessionId, session.projectPath, discarded);
1728
+ const ok = await agent.rollbackSessionTurns(session.agentSessionId, session.projectPath, discarded, rewindTarget.assistantUuid);
1692
1729
  if (!ok)
1693
1730
  return '❌ 对话回退失败';
1694
- const meta = { ...(session.metadata || {}) };
1695
- delete meta.resumeAt;
1696
- await this.sessionManager.updateSession(session.id, { metadata: meta });
1731
+ if (session.threadId) {
1732
+ await this.sessionManager.clearTopicResumeAt({
1733
+ sessionId: session.id,
1734
+ expectedThreadId: session.threadId,
1735
+ expectedAgentSessionId: session.agentSessionId,
1736
+ });
1737
+ }
1738
+ else {
1739
+ const meta = { ...(session.metadata || {}) };
1740
+ delete meta.resumeAt;
1741
+ await this.sessionManager.updateSession(session.id, { metadata: meta });
1742
+ }
1697
1743
  }
1698
1744
  else if (keepTarget) {
1699
- const meta = { ...(session.metadata || {}), resumeAt: keepTarget.assistantUuid };
1700
- await this.sessionManager.updateSession(session.id, { metadata: meta });
1745
+ if (session.threadId) {
1746
+ await this.sessionManager.setTopicResumeAt({
1747
+ sessionId: session.id,
1748
+ expectedThreadId: session.threadId,
1749
+ expectedAgentSessionId: session.agentSessionId,
1750
+ resumeAt: keepTarget.assistantUuid,
1751
+ });
1752
+ }
1753
+ else {
1754
+ const meta = { ...(session.metadata || {}), resumeAt: keepTarget.assistantUuid };
1755
+ await this.sessionManager.updateSession(session.id, { metadata: meta });
1756
+ }
1701
1757
  }
1702
1758
  else {
1703
- // N=1:撤销全部对话,清空 session 从头开始
1759
+ // N=1:撤销全部对话,清空主会话 session 从头开始
1704
1760
  const meta = { ...(session.metadata || {}) };
1705
1761
  delete meta.resumeAt;
1706
1762
  await this.sessionManager.updateSession(session.id, {
@@ -2102,15 +2158,15 @@ export class CommandHandler {
2102
2158
  if (realManagedTempPath && isSameOrDescendant(realPath, realManagedTempPath) && stat.nlink > 1) {
2103
2159
  return { ok: false, code: 'INVALID_AVATAR_PATH', error: 'Avatar source cannot be a hard link inside managed TMPDIR' };
2104
2160
  }
2105
- if (stat.size < 1 || stat.size > 500 * 1024) {
2106
- return { ok: false, code: 'INVALID_AVATAR_FILE', error: 'Avatar source must be between 1 byte and 500KB' };
2161
+ if (stat.size < 1 || stat.size > MAX_AVATAR_SOURCE_BYTES) {
2162
+ return { ok: false, code: 'INVALID_AVATAR_FILE', error: 'Avatar source must be between 1 byte and 10 MiB' };
2107
2163
  }
2108
2164
  // Open the user-supplied node with no-follow semantics after checking
2109
2165
  // its canonical ancestors; the bytes passed to the uploader come from
2110
2166
  // this descriptor rather than from a second path lookup.
2111
2167
  fd = fs.openSync(resolvedPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
2112
2168
  const finalStat = fs.fstatSync(fd);
2113
- if (!finalStat.isFile() || finalStat.size < 1 || finalStat.size > 500 * 1024 || finalStat.nlink !== stat.nlink) {
2169
+ if (!finalStat.isFile() || finalStat.size < 1 || finalStat.size > MAX_AVATAR_SOURCE_BYTES || finalStat.nlink !== stat.nlink) {
2114
2170
  return { ok: false, code: 'INVALID_AVATAR_FILE', error: 'Avatar source changed during validation' };
2115
2171
  }
2116
2172
  avatarBuffer = fs.readFileSync(fd);