evolcore 0.0.33 → 0.0.34

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 (129) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/bin/ec.js +3 -2
  3. package/bin/install-codex-managed-hooks.mjs +12 -1
  4. package/dist/agents/baseagent-availability.js +20 -36
  5. package/dist/agents/claude-runner.js +82 -13
  6. package/dist/agents/codex/diagnostics.js +37 -11
  7. package/dist/agents/codex-runner.js +218 -44
  8. package/dist/agents/ecagent-runner.js +73 -20
  9. package/dist/agents/runner-types.js +5 -6
  10. package/dist/aun/aid/identity.js +1 -1
  11. package/dist/channels/aun.js +16 -6
  12. package/dist/channels/daemon.js +6 -3
  13. package/dist/cli/aun-commands.js +3 -2
  14. package/dist/cli/ctl-command.js +3 -1
  15. package/dist/cli/daemon-commands.js +120 -19
  16. package/dist/cli/fs-command.js +2 -1
  17. package/dist/cli/index.js +20 -6
  18. package/dist/cli/integrity-command.js +196 -0
  19. package/dist/cli/memory-command.js +158 -145
  20. package/dist/cli/thread-command.js +5 -3
  21. package/dist/cli/trigger-command.js +6 -1
  22. package/dist/config/config-field-policy.js +4 -0
  23. package/dist/config/config-manager.js +19 -0
  24. package/dist/config/contact-book-store.js +5 -0
  25. package/dist/config/role-service.js +13 -0
  26. package/dist/config/runtime-env.js +16 -0
  27. package/dist/config-store.js +30 -0
  28. package/dist/core/audit/log-integrity.js +3 -1
  29. package/dist/core/auth/agent-delegation.js +108 -35
  30. package/dist/core/auth/delegation-diagnostics.js +414 -0
  31. package/dist/core/auth/operation-authorizer.js +1 -1
  32. package/dist/core/auth/operation-catalog.js +16 -0
  33. package/dist/core/command/agent-control.js +18 -26
  34. package/dist/core/command/command-handler.js +138 -4
  35. package/dist/core/command/daemon-diagnostics.js +1 -0
  36. package/dist/core/command/menu-catalog.js +3 -0
  37. package/dist/core/command/menu-handler.js +22 -7
  38. package/dist/core/command/menu-protocol.js +1 -0
  39. package/dist/core/command/slash-handler.js +2 -0
  40. package/dist/core/daemon-bootstrap.js +4 -7
  41. package/dist/core/file-integrity/read.js +61 -0
  42. package/dist/core/file-integrity/store.js +262 -0
  43. package/dist/core/ipc-registration.js +36 -1
  44. package/dist/core/memory/batch-meta.js +132 -0
  45. package/dist/core/memory/consolidation-context.js +35 -14
  46. package/dist/core/memory/consolidation-ops-records.js +14 -16
  47. package/dist/core/memory/consolidation.js +91 -26
  48. package/dist/core/memory/index-manager.js +19 -28
  49. package/dist/core/memory/memory-admin.js +19 -14
  50. package/dist/core/memory/memory-document.js +53 -9
  51. package/dist/core/memory/memory-writer.js +30 -15
  52. package/dist/core/message/im-renderer.js +20 -4
  53. package/dist/core/message/message-bridge.js +2 -1
  54. package/dist/core/message/response-engine.js +584 -47
  55. package/dist/core/permission/ec-command-parser.js +93 -4
  56. package/dist/core/permission/protected-paths.js +5 -1
  57. package/dist/core/permission/readonly-shell-query.js +43 -5
  58. package/dist/core/permission/session-path-grants.js +334 -0
  59. package/dist/core/permission/tool-canonical.js +226 -0
  60. package/dist/core/permission/tool-policy.js +30 -62
  61. package/dist/core/role/runtime-policy.js +21 -10
  62. package/dist/core/session/session-fs-store.js +123 -8
  63. package/dist/core/session/session-manager.js +75 -5
  64. package/dist/core/skill/index-generator.js +17 -3
  65. package/dist/core/system-session/grants.js +40 -0
  66. package/dist/core/system-session/memory.js +222 -0
  67. package/dist/core/system-session/registry.js +74 -0
  68. package/dist/core/system-session/types.js +14 -0
  69. package/dist/core/thread/thread-service.js +13 -20
  70. package/dist/eck/kit-renderer.js +7 -2
  71. package/dist/eck/manifest-engine.js +24 -4
  72. package/dist/index.js +11 -2
  73. package/dist/ipc.js +51 -40
  74. package/dist/paths.js +70 -0
  75. package/dist/stats/billing.js +20 -8
  76. package/dist/trigger/feedback.js +6 -1
  77. package/dist/trigger/parser.js +8 -1
  78. package/dist/trigger/system-definitions.js +3 -3
  79. package/dist/utils/cross-platform.js +11 -2
  80. package/dist/utils/error-dict.json +18 -4
  81. package/dist/utils/error-utils.js +121 -0
  82. package/dist/utils/instance-registry.js +13 -8
  83. package/dist/utils/log-writer.js +12 -0
  84. package/dist/utils/process-introspect.js +11 -0
  85. package/kits/docs/context-assembly.md +0 -1
  86. package/kits/docs/evolcore/agent-create.md +5 -4
  87. package/kits/docs/evolcore/agent.md +2 -2
  88. package/kits/docs/evolcore/config.md +2 -2
  89. package/kits/docs/evolcore/ctl.md +2 -2
  90. package/kits/docs/evolcore/ec-capabilities.md +1 -1
  91. package/kits/docs/evolcore/fs-architecture.md +4 -5
  92. package/kits/docs/evolcore/fs.md +1 -1
  93. package/kits/docs/evolcore/group-collaboration.md +2 -2
  94. package/kits/docs/evolcore/group.md +10 -10
  95. package/kits/docs/evolcore/memory-architecture.md +4 -5
  96. package/kits/docs/evolcore/memory-consolidation.md +18 -14
  97. package/kits/docs/evolcore/memory-tags.md +3 -1
  98. package/kits/docs/evolcore/memory.md +24 -12
  99. package/kits/docs/evolcore/model.md +16 -0
  100. package/kits/docs/evolcore/msg.md +7 -7
  101. package/kits/docs/evolcore/thread-chain.md +5 -5
  102. package/kits/docs/prompt-loading-architecture.md +0 -1
  103. package/kits/eck_manifest.json +0 -20
  104. package/kits/eck_manifest.memory.json +0 -12
  105. package/kits/rules/01-overview.md +11 -0
  106. package/kits/rules/03-identity.md +6 -3
  107. package/kits/rules/06-channel.md +1 -1
  108. package/kits/rules/07-memory-and-thread.md +12 -15
  109. package/kits/schemas/_meta.json +7 -1
  110. package/kits/schemas/agent-config.schema.13.json +1 -1
  111. package/kits/schemas/daemon.schema.7.json +140 -0
  112. package/kits/skills/ec-group/SKILL.md +4 -4
  113. package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
  114. package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
  115. package/kits/templates/memory-consolidation/forget.md +2 -2
  116. package/kits/templates/memory-consolidation/generalize.md +4 -4
  117. package/kits/templates/memory-consolidation/notes.md +11 -11
  118. package/kits/templates/memory-consolidation/profile.md +3 -3
  119. package/kits/templates/memory-consolidation/promote.md +3 -3
  120. package/kits/templates/memory-consolidation/workflow.md +6 -6
  121. package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
  122. package/kits/templates/system-fragments/bootstrap.md +4 -3
  123. package/kits/templates/system-fragments/channel.md +2 -2
  124. package/kits/templates/system-fragments/memory.md +16 -9
  125. package/kits/templates/system-fragments/reply.md +6 -6
  126. package/kits/templates/system-fragments/session.md +1 -1
  127. package/package.json +4 -3
  128. package/dist/core/memory/memory-session-policy.js +0 -41
  129. package/dist/core/memory/memory-signature.js +0 -257
@@ -18,6 +18,7 @@ import { auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
18
18
  import { resolveEcagentConfig } from './baseagent.js';
19
19
  import { buildModelRequestHeaders } from './request-identity.js';
20
20
  import { appendRequestPath } from './request-identity.js';
21
+ import { resolveModelSpec } from '../stats/billing.js';
21
22
  import { getManagedTaskTempDir } from '../cli/task-context.js';
22
23
  import { unsettledInterruptError, withInterruptDeadline, hasTrustedFullAccessContext } from './runner-types.js';
23
24
  const PROVIDER_ID = 'evolcore-gateway';
@@ -436,18 +437,23 @@ async function consumeOpenAiStream(output, body, model) {
436
437
  throw new Error('[network] Gateway stream ended without a terminal event');
437
438
  }
438
439
  function createModel(id, baseUrl) {
439
- const isClaude = id.toLowerCase().startsWith('claude-');
440
+ const modelId = stripContextWindowSuffix(id);
441
+ const isClaude = modelId.toLowerCase().startsWith('claude-');
442
+ const fallback = isClaude
443
+ ? { context_window: 200_000, max_output_tokens: 8_192 }
444
+ : { context_window: 128_000, max_output_tokens: 16_384 };
445
+ const spec = resolveModelSpec(resolveRoot(), id, undefined, fallback);
440
446
  return {
441
- id,
442
- name: id,
447
+ id: modelId,
448
+ name: modelId,
443
449
  api: 'openai-completions',
444
450
  provider: PROVIDER_ID,
445
451
  baseUrl,
446
452
  reasoning: true,
447
453
  input: ['text', 'image'],
448
454
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
449
- contextWindow: isClaude ? 200_000 : 128_000,
450
- maxTokens: isClaude ? 8_192 : 16_384,
455
+ contextWindow: spec.context_window,
456
+ maxTokens: spec.max_output_tokens,
451
457
  compat: {
452
458
  maxTokensField: 'max_tokens',
453
459
  supportsUsageInStreaming: true,
@@ -464,7 +470,11 @@ function isWithin(root, candidate) {
464
470
  const relative = path.relative(root, candidate);
465
471
  return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
466
472
  }
467
- export function resolveEcagentToolPath(projectPath, candidate, forWrite = false, bootstrapAgentMdPath, fullaccess = false, personalMdPath) {
473
+ /** 路径是否落在任一显式声明的根之下(含根本身)。 */
474
+ function isWithinAny(candidate, roots) {
475
+ return roots.some(root => isWithin(path.resolve(root), candidate));
476
+ }
477
+ export function resolveEcagentToolPath(projectPath, candidate, forWrite = false, bootstrapAgentMdPath, fullaccess = false, personalMdPath, sessionGrants) {
468
478
  const root = fs.realpathSync(projectPath);
469
479
  const resolved = path.resolve(root, candidate || '.');
470
480
  const allowedBootstrapFile = bootstrapAgentMdPath
@@ -472,7 +482,15 @@ export function resolveEcagentToolPath(projectPath, candidate, forWrite = false,
472
482
  : undefined;
473
483
  const allowedPersonalFile = personalMdPath ? path.resolve(personalMdPath) : undefined;
474
484
  const isManagedIdentityFile = resolved === allowedBootstrapFile || resolved === allowedPersonalFile;
475
- if (!fullaccess && !isWithin(root, resolved) && !isManagedIdentityFile) {
485
+ // 系统会话:模块声明的可读根整体放行,可写文件逐个精确匹配(不含目录)。
486
+ // 声明之外、项目之外的路径仍然拒绝。声明里的路径可能在多个根之间借符号链接互串,
487
+ // 所以放行前统一按真实路径复核,确认真实位置仍落在同一个被授予的边界内。
488
+ // 写只看可写文件(逐个精确匹配),绝不由可读根推导;读只看可读根。
489
+ const sessionWrites = forWrite && (sessionGrants?.writeFiles ?? [])
490
+ .some(file => path.resolve(file) === resolved);
491
+ const sessionReads = !forWrite && isWithinAny(resolved, sessionGrants?.readRoots ?? []);
492
+ const isSessionTarget = sessionReads || sessionWrites;
493
+ if (!fullaccess && !isWithin(root, resolved) && !isManagedIdentityFile && !isSessionTarget) {
476
494
  throw new Error(`Path is outside the project: ${candidate}`);
477
495
  }
478
496
  if (isManagedIdentityFile) {
@@ -494,7 +512,34 @@ export function resolveEcagentToolPath(projectPath, candidate, forWrite = false,
494
512
  while (!fs.existsSync(cursor) && cursor !== path.dirname(cursor))
495
513
  cursor = path.dirname(cursor);
496
514
  const canonical = fs.realpathSync(cursor);
497
- if (!fullaccess && !isWithin(root, canonical))
515
+ if (fullaccess)
516
+ return resolved;
517
+ // 系统会话的写入目标与判定层一致:必须是普通文件,链接一律拒绝。
518
+ // 链接会让 resolved 与真实位置分离,放行等于把洞开到声明之外。
519
+ if (sessionWrites && fs.existsSync(resolved) && fs.lstatSync(resolved).isSymbolicLink()) {
520
+ throw new Error(`Refusing to write through a symbolic link: ${candidate}`);
521
+ }
522
+ // 可写文件尚未创建时,以父目录的真实路径为准;已存在时以文件自身的真实路径为准。
523
+ const canonicalSelf = fs.existsSync(resolved) ? fs.realpathSync(resolved) : canonical;
524
+ if (isSessionTarget) {
525
+ const grantedRoots = [
526
+ ...(sessionReads ? sessionGrants?.readRoots ?? [] : []),
527
+ ...(sessionWrites ? (sessionGrants?.writeFiles ?? []).filter(file => path.resolve(file) === resolved) : []),
528
+ ];
529
+ const canonicalRoots = grantedRoots.flatMap(granted => {
530
+ const inside = fs.existsSync(granted) ? [fs.realpathSync(granted)] : [];
531
+ // 根自身可能尚未创建,或者位于尚未创建的上层目录中;先归到存在的祖先再比。
532
+ let ancestor = granted;
533
+ while (!fs.existsSync(ancestor) && ancestor !== path.dirname(ancestor))
534
+ ancestor = path.dirname(ancestor);
535
+ return [...inside, fs.realpathSync(ancestor)];
536
+ });
537
+ if (!canonicalRoots.some(granted => isWithin(granted, canonical) && isWithin(granted, canonicalSelf))) {
538
+ throw new Error(`Path resolves outside its declared grant: ${candidate}`);
539
+ }
540
+ return resolved;
541
+ }
542
+ if (!isWithin(root, canonical))
498
543
  throw new Error(`Path resolves outside the project: ${candidate}`);
499
544
  return resolved;
500
545
  }
@@ -547,7 +592,7 @@ async function walkFiles(root, signal) {
547
592
  function textResult(text, details = {}) {
548
593
  return { content: [{ type: 'text', text: truncate(text) }], details };
549
594
  }
550
- function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullaccess = false, personalMdPath) {
595
+ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullaccess = false, personalMdPath, sessionGrants) {
551
596
  const read = {
552
597
  name: 'Read', label: 'Read', description: 'Read a UTF-8 text file from the project.',
553
598
  parameters: Type.Object({
@@ -556,7 +601,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
556
601
  limit: Type.Optional(Type.Number({ minimum: 1 })),
557
602
  }),
558
603
  async execute(_id, params, signal) {
559
- const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess, personalMdPath);
604
+ const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess, personalMdPath, sessionGrants);
560
605
  const stat = await fs.promises.stat(file);
561
606
  if (stat.size > MAX_READ_BYTES)
562
607
  throw new Error(`File exceeds ${MAX_READ_BYTES} byte read limit`);
@@ -573,7 +618,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
573
618
  name: 'Glob', label: 'Glob', description: 'Find project files matching a glob pattern.',
574
619
  parameters: Type.Object({ pattern: Type.String(), path: Type.Optional(Type.String()) }),
575
620
  async execute(_id, params, signal) {
576
- const root = resolveEcagentToolPath(projectPath, params.path ?? '.', false, undefined, fullaccess);
621
+ const root = resolveEcagentToolPath(projectPath, params.path ?? '.', false, undefined, fullaccess, undefined, sessionGrants);
577
622
  const matcher = globRegex(params.pattern.replaceAll('\\', '/'));
578
623
  const files = (await walkFiles(root, signal))
579
624
  .map(file => path.relative(root, file).replaceAll(path.sep, '/'))
@@ -589,7 +634,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
589
634
  case_insensitive: Type.Optional(Type.Boolean()), literal: Type.Optional(Type.Boolean()),
590
635
  }),
591
636
  async execute(_id, params, signal) {
592
- const root = resolveEcagentToolPath(projectPath, params.path ?? '.', false, undefined, fullaccess);
637
+ const root = resolveEcagentToolPath(projectPath, params.path ?? '.', false, undefined, fullaccess, undefined, sessionGrants);
593
638
  const files = (await fs.promises.stat(root)).isFile() ? [root] : await walkFiles(root, signal);
594
639
  const fileMatcher = params.glob ? globRegex(params.glob.replaceAll('\\', '/')) : undefined;
595
640
  const flags = params.case_insensitive ? 'i' : '';
@@ -622,7 +667,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
622
667
  name: 'Write', label: 'Write', description: 'Create or completely overwrite a project file.',
623
668
  parameters: Type.Object({ file_path: Type.String(), content: Type.String() }), executionMode: 'sequential',
624
669
  async execute(_id, params, signal) {
625
- const file = resolveEcagentToolPath(projectPath, params.file_path, true, bootstrapAgentMdPath, fullaccess, personalMdPath);
670
+ const file = resolveEcagentToolPath(projectPath, params.file_path, true, bootstrapAgentMdPath, fullaccess, personalMdPath, sessionGrants);
626
671
  await fs.promises.mkdir(path.dirname(file), { recursive: true });
627
672
  await fs.promises.writeFile(file, params.content, { encoding: 'utf8', signal });
628
673
  return textResult(`Wrote ${Buffer.byteLength(params.content)} bytes to ${path.relative(projectPath, file)}`, { file });
@@ -635,7 +680,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
635
680
  replace_all: Type.Optional(Type.Boolean()),
636
681
  }), executionMode: 'sequential',
637
682
  async execute(_id, params, signal) {
638
- const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess, personalMdPath);
683
+ const file = resolveEcagentToolPath(projectPath, params.file_path, true, bootstrapAgentMdPath, fullaccess, personalMdPath, sessionGrants);
639
684
  const value = await fs.promises.readFile(file, { encoding: 'utf8', signal });
640
685
  if (!params.old_string)
641
686
  throw new Error('old_string must not be empty');
@@ -724,7 +769,7 @@ export class EcagentRunner {
724
769
  configuredHeaders: config.headers,
725
770
  }),
726
771
  };
727
- this.model = stripContextWindowSuffix(config.model);
772
+ this.model = config.model;
728
773
  this.effort = config.effort;
729
774
  this.agentAid = runtime?.agentAid;
730
775
  this.onSessionIdUpdate = callbacks?.onSessionIdUpdate;
@@ -734,8 +779,8 @@ export class EcagentRunner {
734
779
  throw new Error('ecagent requires an EvolCore Agent AID');
735
780
  return agentSessionsDir(this.agentAid);
736
781
  }
737
- setModel(model) { this.model = stripContextWindowSuffix(model); }
738
- getModel() { return this.model; }
782
+ setModel(model) { this.model = model; }
783
+ getModel() { return stripContextWindowSuffix(this.model); }
739
784
  resolveModelId(model) { return model; }
740
785
  setEffort(effort) { this.effort = effort; }
741
786
  getEffort() { return this.effort; }
@@ -885,7 +930,7 @@ export class EcagentRunner {
885
930
  models,
886
931
  model,
887
932
  thinkingLevel: normalizeThinkingLevel(effort),
888
- tools: createTools(nodeEnv, request.projectPath, request.runtimeEnv, request.modelOverride?.bootstrapAgentMdPath, fullaccess, request.modelOverride?.personalMdPath),
933
+ tools: createTools(nodeEnv, request.projectPath, request.runtimeEnv, request.modelOverride?.bootstrapAgentMdPath, fullaccess, request.modelOverride?.personalMdPath, this.permissionContexts.get(request.sessionId)?.systemSessionGrants),
889
934
  systemPrompt: [DEFAULT_SYSTEM_PROMPT, request.systemPromptAppend].filter(Boolean).join('\n\n'),
890
935
  streamOptions: {
891
936
  timeoutMs: this.config.timeoutMs,
@@ -1212,6 +1257,14 @@ export class EcagentRunner {
1212
1257
  logger.warn(`[EcagentRunner] failed to record blocked operation: session=${sessionId} tool=${toolName} code=${policyCode} error=${error instanceof Error ? error.message : String(error)}`);
1213
1258
  }
1214
1259
  };
1260
+ // 系统会话:模块判定即最终结论,不进入 preflight / permissionMode 分支
1261
+ if (permissionContext?.systemSessionCheck) {
1262
+ const decision = permissionContext.systemSessionCheck(toolName, input);
1263
+ if (decision.allow)
1264
+ return {};
1265
+ await recordBlockedOperation(decision.code);
1266
+ return { block: true, reason: decision.reason };
1267
+ }
1215
1268
  const policy = permissionContext?.policyHook?.(toolName, input, { turn });
1216
1269
  if (policy?.block) {
1217
1270
  await recordBlockedOperation(policy.policyCode ?? 'session_policy_hook');
@@ -1239,7 +1292,7 @@ export class EcagentRunner {
1239
1292
  const checkedInput = preflight.input;
1240
1293
  if (mode === 'readonly') {
1241
1294
  const decision = checkReadonly(toolName, checkedInput, projectPath, {
1242
- sessionId, managedTempDir: permissionContext?.managedTempDir, channel: permissionContext?.channel, peerId: permissionContext?.userId, role: permissionContext?.role,
1295
+ sessionId, managedTempDir: permissionContext?.managedTempDir, grantedReadRoots: permissionContext?.grantedReadRoots, channel: permissionContext?.channel, peerId: permissionContext?.userId, role: permissionContext?.role,
1243
1296
  allowReadonlySourceDiagnostics: permissionContext?.allowReadonlySourceDiagnostics === true,
1244
1297
  });
1245
1298
  if (decision.behavior === 'deny') {
@@ -1351,7 +1404,7 @@ export class EcagentRunner {
1351
1404
  ? cached
1352
1405
  : await this.openExistingRuntime(agentSessionId, cwd);
1353
1406
  this.onCompactStart?.(sessionId);
1354
- const compactModel = stripContextWindowSuffix(modelOverride?.model || this.model);
1407
+ const compactModel = modelOverride?.model || this.model;
1355
1408
  const compactEffort = modelOverride?.effortMode === 'model_default'
1356
1409
  ? undefined
1357
1410
  : modelOverride?.effort ?? this.effort;
@@ -136,10 +136,9 @@ export function isOneMillionContextModel(model) {
136
136
  return ONE_M_CONTEXT_MODEL_PREFIXES.some(prefix => base === prefix || base.startsWith(`${prefix}-`));
137
137
  }
138
138
  /**
139
- * EvolCore's conservative local context-window catalog: 1M models = 1,000,000;
140
- * every other model falls back to 200,000 until provider metadata is available.
141
- * This is an operational guardrail, not a claim about an upstream provider's
142
- * advertised capacity.
139
+ * Hardcoded context-window fallback for models missing from the parameter table:
140
+ * known 1M models use 1,000,000; every other model falls back to 200,000.
141
+ * This is an operational guardrail, not a claim about advertised capacity.
143
142
  */
144
143
  export function realContextWindowForModel(model) {
145
144
  return isOneMillionContextModel(model) ? 1000000 : 200000;
@@ -159,8 +158,8 @@ export function autoCompactWindowForModel(_model) {
159
158
  * at the right point for small-context models (200k/128k), where EvolCore's
160
159
  * fixed 650k threshold would never trigger.
161
160
  */
162
- export function sdkAutoCompactWindowForModel(model) {
163
- const contextWindow = realContextWindowForModel(model);
161
+ export function sdkAutoCompactWindowForModel(model, resolvedContextWindow) {
162
+ const contextWindow = resolvedContextWindow ?? realContextWindowForModel(model);
164
163
  const reserve = Math.min(10_000, Math.floor(contextWindow * 0.05));
165
164
  return Math.min(650_000, Math.max(1, contextWindow - reserve));
166
165
  }
@@ -224,7 +224,7 @@ export async function aidCreate(aid, opts) {
224
224
  // 新注册流程:AIDStore.register → downloadCaRoot → load → authenticate
225
225
  const store = await getAidStore({ slotId: SLOT.cli, aunPath });
226
226
  try {
227
- const regResult = await store.register(aid);
227
+ const regResult = await store.register(aid, { type: 'agent' });
228
228
  if (!regResult.ok) {
229
229
  const e = new Error(regResult.error.message);
230
230
  e.code = regResult.error.code;
@@ -557,6 +557,11 @@ function setIfDefined(target, key, value) {
557
557
  if (value !== undefined)
558
558
  target[key] = value;
559
559
  }
560
+ function appendRawErrorText(text, raw) {
561
+ if (typeof raw !== 'string' || !raw.trim() || text.includes(raw))
562
+ return text;
563
+ return `${text}\n原因:${raw}`;
564
+ }
560
565
  /**
561
566
  * 把 AUNChannel 投递的 opts 映射成渠道无关的 InboundMessage。
562
567
  *
@@ -6415,6 +6420,7 @@ export class AUNChannelPlugin {
6415
6420
  text: payload.text,
6416
6421
  severity: 'info',
6417
6422
  };
6423
+ setIfDefined(noticePayload, 'raw', payload.raw);
6418
6424
  setIfDefined(noticePayload, 'subtype', payload.subtype);
6419
6425
  await channel.sendReliableStructured(channelId, noticePayload, replyCtx, payload.text);
6420
6426
  return;
@@ -6424,10 +6430,14 @@ export class AUNChannelPlugin {
6424
6430
  type: payload.recoverable === true ? 'notice' : 'error',
6425
6431
  ...taskBase(),
6426
6432
  subtype: payload.subtype,
6427
- text: payload.text,
6433
+ text: payload.recoverable === true
6434
+ ? payload.text
6435
+ : appendRawErrorText(payload.text, payload.raw),
6436
+ ...(payload.raw !== undefined ? { raw: payload.raw } : {}),
6437
+ ...(payload.reason !== undefined ? { reason: payload.reason } : {}),
6428
6438
  ...(payload.recoverable === true
6429
6439
  ? { severity: 'warn' }
6430
- : { message: payload.text, user_message: payload.text, recoverable: false }),
6440
+ : { recoverable: false }),
6431
6441
  };
6432
6442
  await channel.sendReliableStructured(channelId, errorPayload, { ...(replyCtx ?? {}), suppressPeerMention: true }, payload.text);
6433
6443
  return;
@@ -6436,11 +6446,11 @@ export class AUNChannelPlugin {
6436
6446
  const errorPayload = {
6437
6447
  type: 'error',
6438
6448
  ...taskBase(),
6439
- reason: payload.reason,
6440
- text: payload.text,
6441
- message: payload.text,
6442
- user_message: payload.text,
6449
+ text: appendRawErrorText(payload.text, payload.raw),
6443
6450
  };
6451
+ setIfDefined(errorPayload, 'subtype', payload.subtype);
6452
+ setIfDefined(errorPayload, 'reason', payload.reason);
6453
+ setIfDefined(errorPayload, 'raw', payload.raw);
6444
6454
  await channel.sendReliableStructured(channelId, errorPayload, { ...(replyCtx ?? {}), suppressPeerMention: true }, payload.text);
6445
6455
  return;
6446
6456
  }
@@ -2,6 +2,7 @@ import { appendExpireEntry, recordNextLaunchCleanup, cleanupPreviousByKind, runE
2
2
  import { ExecutionTimeoutError } from '../utils/error-utils.js';
3
3
  import { normalizeExecutionMode } from '../trigger/validation.js';
4
4
  import { resolvePeerRoleDetail } from '../config/peer-role-resolver.js';
5
+ import { assertSystemSessionLaunchable } from '../core/system-session/registry.js';
5
6
  const DEFAULT_WATCHDOG_MS = 11 * 60 * 1000;
6
7
  export class DaemonChannel {
7
8
  sessionManager;
@@ -253,6 +254,9 @@ export class DaemonChannel {
253
254
  // 记忆整理会话:把会话原型与整理模式写进 metadata,供渲染层选 manifest / 手册,
254
255
  // 供 ipc 侧校验写命令的 --scope。
255
256
  const { memoryMode, memoryScope, consolidationBatchDir, consolidationChatDir, consolidationMemoryDir } = trigger.execution;
257
+ // 系统会话:类型 / subtype / runner 校验不过直接拒绝(Gemini 不支持),不降级成普通会话
258
+ if (memoryMode)
259
+ assertSystemSessionLaunchable({ type: 'memory', subtype: memoryMode }, baseagent);
256
260
  // 反馈目标与触发方式(consolidation.ts 在 normalize 后补挂的字段):
257
261
  // 收尾时据此投递反馈,并判断是否该反馈(定时触发不反馈)。
258
262
  const { feedbackOrigin, consolidationTriggeredBy } = trigger.execution;
@@ -260,7 +264,7 @@ export class DaemonChannel {
260
264
  channelKey: this.channelKey,
261
265
  peerId: `trigger:${trigger.id}`,
262
266
  peerName: trigger.name,
263
- ...(memoryMode ? { sessionType: 'memory', memoryMode } : {}),
267
+ ...(memoryMode ? { systemSession: { type: 'memory', subtype: memoryMode }, sessionType: 'memory', memoryMode } : {}),
264
268
  ...(memoryScope ? { memoryScope } : {}),
265
269
  ...(consolidationBatchDir ? { consolidationBatchDir } : {}),
266
270
  ...(consolidationChatDir ? { consolidationChatDir } : {}),
@@ -570,8 +574,6 @@ export class DaemonChannel {
570
574
  peerId: `bg:${spec.kind}`,
571
575
  peerName: spec.kind,
572
576
  peerType: 'system',
573
- bgKind: spec.kind,
574
- bgInstanceId: spec.instanceId,
575
577
  ...(spec.metadata ?? {}),
576
578
  }, spec.kind, undefined, 'private', spec.baseagent, spec.agentAid, this.channelName, 'system');
577
579
  const promise = new Promise((resolve) => {
@@ -586,6 +588,7 @@ export class DaemonChannel {
586
588
  terminated: false,
587
589
  cleanupPolicy,
588
590
  agentAid: spec.agentAid,
591
+ runtimeDir: spec.runtimeDir,
589
592
  watchdogTimer: setTimeout(() => {
590
593
  this.finishBgSession(runId, slot, 'error', 'bg-session watchdog timeout');
591
594
  }, this.watchdogMs),
@@ -2173,7 +2173,8 @@ Options:
2173
2173
  无值时默认 5m;Trigger 名:check-<目标threadId>-from-<来源threadId>-<唯一后缀>
2174
2174
  取消/调整用返回的 trigger_id(trig_…),show/update/cancel 只认 id
2175
2175
  --trigger-prompt <text> 自定义复查 Trigger prompt 中的用户段(仅 --check-every)
2176
- --mention <aid> 发送时 @ 某个成员(可多次,或用逗号分隔多个 aid)
2176
+ --mention <...> 发送时 @ 某个成员。写法固定为单引号包住、逗号分隔多个 aid
2177
+ (如 --mention 'alice.agentid.pub,bob.agentid.pub'),可多次传入
2177
2178
  --mention-all 发送时 @ 所有人
2178
2179
  --content-type <mime> 显式覆盖 MIME(仅 --file 模式)
2179
2180
  --as <type> 附件类型:image|video|voice|file(仅 --file 模式)
@@ -2187,7 +2188,7 @@ Options:
2187
2188
  格式示例(.example.invalid 是保留的虚构域,以下 AID 均不是真实地址):
2188
2189
  ec group create sender.example.invalid "Dev Team" --visibility private
2189
2190
  ec group send sender.example.invalid 12345.example.invalid "hello team"
2190
- ec group send sender.example.invalid 12345.example.invalid "@member 看下 PR" --mention member.example.invalid
2191
+ ec group send sender.example.invalid 12345.example.invalid '@member 看下 PR' --mention 'member.example.invalid'
2191
2192
  ec group send sender.example.invalid 12345.example.invalid --file ./arch.png
2192
2193
  ec group send sender.example.invalid g-12345.example.invalid "进度?" --thread thread-xxx --check-every 30m
2193
2194
  ec group rules sender.example.invalid 12345.example.invalid set ./rules.md
@@ -25,7 +25,9 @@ const CTL_HELP = `用法: ec ctl <command> [args...]
25
25
  ec ctl name "新会话名称"
26
26
  ec ctl send "任务已完成"
27
27
  ec ctl file report.md
28
- ec ctl queue --full`;
28
+ ec ctl queue --full
29
+ ec ctl path list
30
+ ec ctl path grant "D:/shared/data" --access read`;
29
31
  export async function cmdCtl(args) {
30
32
  if (args.length === 0) {
31
33
  console.error(CTL_HELP);