evolcore 0.0.22 → 0.0.24

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 (117) hide show
  1. package/CHANGELOG.md +74 -14
  2. package/README.md +4 -0
  3. package/bin/codex-managed-acl.ps1 +160 -0
  4. package/bin/codex-managed-hook.mjs +127 -10
  5. package/bin/ec-safe-output.js +85 -3
  6. package/bin/install-codex-managed-hooks.mjs +384 -95
  7. package/dist/agents/baseagent-availability.js +48 -0
  8. package/dist/agents/baseagent.js +30 -2
  9. package/dist/agents/claude-runner.js +179 -240
  10. package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +90 -20
  11. package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
  12. package/dist/agents/codex/catalog.js +70 -0
  13. package/dist/agents/codex/diagnostics.js +369 -0
  14. package/dist/agents/codex-runner.js +839 -192
  15. package/dist/agents/ecagent-runner.js +27 -34
  16. package/dist/agents/gemini-runner.js +62 -8
  17. package/dist/agents/runner-types.js +1 -1
  18. package/dist/aun/aid/managed-operation.js +28 -6
  19. package/dist/aun/aid/store.js +43 -0
  20. package/dist/aun/menu-client.js +272 -0
  21. package/dist/aun/msg/group.js +65 -4
  22. package/dist/aun/msg/index.js +1 -1
  23. package/dist/aun/msg/managed-operation.js +57 -5
  24. package/dist/aun/outbox.js +56 -18
  25. package/dist/aun/rpc/client.js +7 -1
  26. package/dist/channels/aun.js +178 -77
  27. package/dist/channels/daemon.js +7 -1
  28. package/dist/channels/dingtalk.js +1 -0
  29. package/dist/channels/feishu.js +10 -4
  30. package/dist/channels/wechat.js +81 -10
  31. package/dist/channels/wecom.js +2 -1
  32. package/dist/cli/agent-command.js +29 -3
  33. package/dist/cli/agent-create.js +538 -0
  34. package/dist/cli/agent.js +26 -675
  35. package/dist/cli/aun-commands.js +108 -24
  36. package/dist/cli/command-log.js +21 -2
  37. package/dist/cli/ctl-command.js +2 -0
  38. package/dist/cli/daemon-commands.js +289 -61
  39. package/dist/cli/index.js +22 -2
  40. package/dist/cli/init.js +2 -10
  41. package/dist/cli/managed-command-guard.js +3 -0
  42. package/dist/cli/menu-client.js +95 -0
  43. package/dist/cli/model.js +23 -8
  44. package/dist/cli/restart-monitor.js +432 -8
  45. package/dist/cli/stats.js +110 -0
  46. package/dist/cli/task-context.js +12 -2
  47. package/dist/cli/trigger-command.js +9 -1
  48. package/dist/config/builtin-roles.js +2 -0
  49. package/dist/config/schema-migration.js +14 -2
  50. package/dist/core/auth/agent-delegation.js +10 -7
  51. package/dist/core/auth/auth-gateway.js +3 -2
  52. package/dist/core/auth/authorization-audit.js +75 -18
  53. package/dist/core/auth/operation-authorizer.js +63 -3
  54. package/dist/core/auth/operation-catalog.js +14 -2
  55. package/dist/core/auth/remote-access-store.js +136 -0
  56. package/dist/core/baseagent-loader.js +4 -4
  57. package/dist/core/bootstrap-messages.js +39 -18
  58. package/dist/core/bootstrap-service.js +32 -26
  59. package/dist/core/capability/capability-manager.js +8 -4
  60. package/dist/core/capability/providers/codex-capability-provider.js +1 -1
  61. package/dist/core/command/agent-control.js +7 -2
  62. package/dist/core/command/command-handler.js +128 -30
  63. package/dist/core/command/daemon-diagnostics.js +448 -0
  64. package/dist/core/command/menu-catalog.js +6 -1
  65. package/dist/core/command/menu-handler.js +410 -98
  66. package/dist/core/command/menu-protocol.js +123 -0
  67. package/dist/core/command/slash-gate.js +4 -6
  68. package/dist/core/command/slash-handler.js +199 -70
  69. package/dist/core/data-migration.js +5 -15
  70. package/dist/core/event-catalog.js +3 -0
  71. package/dist/core/handoff/runtime.js +3 -1
  72. package/dist/core/handoff/store.js +7 -1
  73. package/dist/core/message/im-renderer.js +13 -6
  74. package/dist/core/message/message-bridge.js +5 -36
  75. package/dist/core/message/message-queue.js +20 -15
  76. package/dist/core/message/message-utils.js +6 -3
  77. package/dist/core/message/response-engine.js +147 -94
  78. package/dist/core/model/catalog-service.js +310 -0
  79. package/dist/core/model/model-catalog.js +134 -334
  80. package/dist/core/model/model-diagnostics.js +4 -5
  81. package/dist/core/model/model-fallback.js +9 -16
  82. package/dist/core/model/model-permission.js +5 -2
  83. package/dist/core/permission/approval-gateway.js +183 -32
  84. package/dist/core/permission/protected-paths.js +63 -4
  85. package/dist/core/permission/sandbox-runtime.js +4 -2
  86. package/dist/core/permission/tool-policy.js +257 -31
  87. package/dist/core/runtime-lock.js +13 -8
  88. package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
  89. package/dist/core/session/session-manager.js +49 -13
  90. package/dist/core/session/session-renew.js +1 -0
  91. package/dist/index.js +214 -57
  92. package/dist/ipc.js +191 -102
  93. package/dist/paths.js +23 -1
  94. package/dist/stats/billing.js +18 -4
  95. package/dist/trigger/manager.js +3 -1
  96. package/dist/trigger/parser.js +77 -13
  97. package/dist/trigger/patch.js +104 -1
  98. package/dist/utils/atomic-write.js +26 -2
  99. package/dist/utils/cross-platform.js +436 -1
  100. package/dist/utils/ecweb-supervisor.js +11 -2
  101. package/dist/utils/ecweb-utils.js +8 -26
  102. package/dist/utils/error-utils.js +16 -2
  103. package/dist/utils/model-prices.jsonl +23 -20
  104. package/dist/utils/npm-ops.js +70 -15
  105. package/dist/utils/windows-autostart.js +1 -1
  106. package/kits/docs/evolcore/INDEX.md +1 -0
  107. package/kits/docs/evolcore/agent-create.md +13 -0
  108. package/kits/docs/evolcore/group-collaboration.md +7 -2
  109. package/kits/docs/evolcore/menu-client.md +34 -0
  110. package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
  111. package/kits/migrations/migrate-role-config-v4.mjs +11 -5
  112. package/kits/migrations/migrate-role-config-v5.mjs +8 -2
  113. package/kits/templates/roles/admin.json +2 -0
  114. package/kits/templates/roles/visitor.json +1 -0
  115. package/kits/templates/system-fragments/bootstrap.md +8 -1
  116. package/package.json +16 -4
  117. package/dist/utils/codex-cli.js +0 -43
@@ -4,6 +4,7 @@ import { compareVersions, resolveGlobalPkg } from './npm-ops.js';
4
4
  import { isWindows as platformIsWindows, resolveCommandPath } from './cross-platform.js';
5
5
  import { MIN_ECWEB_VERSION, WEB_CLI_BIN, WEB_PACKAGE_NAME } from '../product.js';
6
6
  import { resolvePaths } from '../paths.js';
7
+ import { ipcQuery } from '../ipc.js';
7
8
  /** EC Web owns the session-log reader, so older builds cannot safely serve a newer daemon. */
8
9
  export function isEcwebVersionSupported(version) {
9
10
  const normalized = version?.trim();
@@ -65,34 +66,15 @@ export function resolveEcwebLaunchCommand(ecwebArgs, opts = {}) {
65
66
  }
66
67
  return { command: commandPath, args: ecwebArgs, source: 'command' };
67
68
  }
68
- /**
69
- * ECWeb 配对码取码 helper(共享给 daemon 和 CLI)。
70
- *
71
- * 配对码是 ecweb 进程自己生成并持有的内部状态。daemon/CLI 通过 ecweb 的
72
- * localhost-only HTTP 接口 GET /api/pair-code 取当前码(远程访问被 ecweb 403 拒绝)。
73
- */
74
- /** 经 localhost 拉取 ecweb 当前配对码(仅本机可取)。失败返回 null。
75
- * authorizedBy 仅由 daemon 的 AUN 控制通道传入,用于把配对码换出的 token
76
- * 绑定到实际批准的 daemon-owner;普通 CLI 取码不携带该字段。 */
77
- export async function fetchEcwebPairCode(port, authorizedBy) {
69
+ export async function fetchRemotePairingCode(authorizedBy) {
78
70
  try {
79
- const headers = {};
80
- const owner = authorizedBy?.trim();
81
- if (owner)
82
- headers['x-evolcore-pair-authorized-by'] = owner;
83
- try {
84
- const controlToken = fs.readFileSync(resolvePaths().controlToken, 'utf8').trim();
85
- if (controlToken)
86
- headers['x-evolcore-control-token'] = controlToken;
87
- }
88
- catch { }
89
- const resp = await fetch(`http://127.0.0.1:${port}/api/pair-code`, {
90
- headers,
91
- signal: AbortSignal.timeout(2000),
92
- });
93
- if (!resp.ok)
71
+ const response = await ipcQuery(resolvePaths().socket, {
72
+ type: 'remote-auth.pair-code',
73
+ ...(authorizedBy?.trim() ? { authorizedBy: authorizedBy.trim() } : {}),
74
+ }, 2_000);
75
+ if (!response?.ok || typeof response.code !== 'string' || typeof response.expiresAt !== 'number')
94
76
  return null;
95
- return await resp.json();
77
+ return { code: response.code, expiresAt: response.expiresAt };
96
78
  }
97
79
  catch {
98
80
  return null;
@@ -38,6 +38,7 @@ export var ErrorType;
38
38
  ErrorType["STREAM_ERROR"] = "stream_error";
39
39
  ErrorType["CONTEXT_TOO_LONG"] = "context_too_long";
40
40
  ErrorType["MODEL_UNAVAILABLE"] = "model_unavailable";
41
+ ErrorType["SESSION_UNAVAILABLE"] = "session_unavailable";
41
42
  ErrorType["SANDBOX_INITIALIZATION_FAILED"] = "sandbox_initialization_failed";
42
43
  ErrorType["CLAUDE_STARTUP_ARGUMENTS_TOO_LONG"] = "claude_startup_arguments_too_long";
43
44
  ErrorType["UNKNOWN"] = "unknown";
@@ -278,6 +279,9 @@ function hasRetryableHttpStatus(text) {
278
279
  }
279
280
  export function classifyError(error) {
280
281
  const msg = (error?.message || '').toLowerCase();
282
+ if (error?.code === 'TOPIC_BACKEND_UNAVAILABLE') {
283
+ return ErrorType.SESSION_UNAVAILABLE;
284
+ }
281
285
  // Startup-size failures are deterministic local failures and must win over
282
286
  // dictionary rules that would otherwise turn them into generic retries.
283
287
  if (isClaudeStartupArgumentsTooLong(error)) {
@@ -380,13 +384,23 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
380
384
  }
381
385
  // 回退到原有的错误消息匹配逻辑
382
386
  const msg = error?.message || String(error);
387
+ const warnPrefix = includeEmoji ? '⚠️ ' : '';
388
+ const errPrefix = includeEmoji ? '❌ ' : '';
389
+ // Codex app-server reports this when another app-server still owns the
390
+ // thread writer. Surface the handoff condition instead of hiding it behind
391
+ // the generic "processing failed" message.
392
+ if (error?.code === 'THREAD_ACTIVE_WRITER' || /already has an active writer/i.test(msg)) {
393
+ return `${warnPrefix}Codex 会话正在由另一个执行通道占用,请稍后重试(THREAD_ACTIVE_WRITER)`;
394
+ }
395
+ if (error?.code === 'TOPIC_BACKEND_UNAVAILABLE'
396
+ || /bound topic backend is unavailable/i.test(msg)) {
397
+ return `${warnPrefix}当前会话已失效,请发送 /renew。`;
398
+ }
383
399
  // 字典优先 — 命中且有自定义消息则直接返回
384
400
  const rule = matchErrorRule(msg.toLowerCase());
385
401
  if (rule?.message)
386
402
  return rule.message;
387
403
  // 内置兜底规则(结构性错误)
388
- const warnPrefix = includeEmoji ? '⚠️ ' : '';
389
- const errPrefix = includeEmoji ? '❌ ' : '';
390
404
  if (msg.includes('CONTEXT_COMPACT_FAILED') || isContextTooLongText(msg)) {
391
405
  const detail = msg.match(/^CONTEXT_COMPACT_FAILED:\s*(.+)$/)?.[1]?.trim();
392
406
  return detail
@@ -1,20 +1,23 @@
1
- {"model":"claude-opus-4-8","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5}
2
- {"model":"claude-opus-4-7","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5}
3
- {"model":"claude-opus-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5}
4
- {"model":"claude-sonnet-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":3,"price_output":15,"price_cache_creation":3.75,"price_cache_read":0.3}
5
- {"model":"claude-haiku-4-5-20251001","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":0.8,"price_output":4,"price_cache_creation":1,"price_cache_read":0.08}
6
- {"model":"gpt-5.6-sol","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":12.5,"price_cache_read":1}]}
7
- {"model":"gpt-5.6-terra","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":6.25,"price_cache_read":0.5}]}
8
- {"model":"gpt-5.6-luna","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1,"tiers":[{"up_to_input_tokens":272000,"price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1},{"up_to_input_tokens":null,"price_input":2,"price_output":9,"price_cache_creation":2.5,"price_cache_read":0.2}]}
9
- {"model":"gpt-5.5","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":0,"price_cache_read":1}]}
10
- {"model":"gpt-5.4","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":0,"price_cache_read":0.5}]}
11
- {"model":"gpt-5.3-codex","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1.75,"price_output":14,"price_cache_creation":0,"price_cache_read":0.175}
12
- {"model":"deepseek-v4-pro","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8}
13
- {"model":"deepseek-v4-flash","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.1,"price_cache_miss":0.5,"price_output":2}
14
- {"model":"deepseek-v3.2","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8}
15
- {"model":"kimi-k2.6","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0}
16
- {"model":"kimi-k2.5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0}
17
- {"model":"glm-5.1","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0}
18
- {"model":"glm-5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0}
19
- {"model":"glm-4.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0}
20
- {"model":"MiniMax-M2.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0}
1
+ {"model":"claude-opus-4-8","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":32768,"supports_cache":true,"supports_vision":true}
2
+ {"model":"claude-opus-4-7","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":32768,"supports_cache":true,"supports_vision":true}
3
+ {"model":"claude-opus-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":32768,"supports_cache":true,"supports_vision":true}
4
+ {"model":"claude-sonnet-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":3,"price_output":15,"price_cache_creation":3.75,"price_cache_read":0.3,"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384,"supports_cache":true,"supports_vision":true}
5
+ {"model":"claude-haiku-4-5-20251001","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":0.8,"price_output":4,"price_cache_creation":1,"price_cache_read":0.08,"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384,"supports_cache":true,"supports_vision":false}
6
+ {"model":"gpt-5.6-sol","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":12.5,"price_cache_read":1}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
7
+ {"model":"gpt-5.6-terra","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":6.25,"price_cache_read":0.5}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
8
+ {"model":"gpt-5.6-luna","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1,"tiers":[{"up_to_input_tokens":272000,"price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1},{"up_to_input_tokens":null,"price_input":2,"price_output":9,"price_cache_creation":2.5,"price_cache_read":0.2}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
9
+ {"model":"gpt-5.5","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":0,"price_cache_read":1}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
10
+ {"model":"gpt-5.4","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":0,"price_cache_read":0.5}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
11
+ {"model":"gpt-5.3-codex","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1.75,"price_output":14,"price_cache_creation":0,"price_cache_read":0.175,"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
12
+ {"model":"deepseek-v4-pro","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8,"context_window":1000000,"max_input_tokens":950000,"max_output_tokens":8192,"supports_cache":true}
13
+ {"model":"deepseek-v4-flash","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.1,"price_cache_miss":0.5,"price_output":2,"context_window":1000000,"max_input_tokens":950000,"max_output_tokens":8192,"supports_cache":true}
14
+ {"model":"deepseek-v3.2","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192,"supports_cache":true}
15
+ {"model":"kimi-k2.6","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
16
+ {"model":"kimi-k2.5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
17
+ {"model":"glm-5.1","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
18
+ {"model":"glm-5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
19
+ {"model":"glm-4.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
20
+ {"model":"MiniMax-M2.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
21
+ {"model":"claude-opus-5","effective_from":0,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":131072,"supports_cache":true,"supports_vision":true}
22
+ {"model":"claude-sonnet-5","effective_from":0,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":131072,"supports_cache":true,"supports_vision":true}
23
+ {"model":"claude-fable-5","effective_from":0,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":131072,"supports_cache":true,"supports_vision":true}
@@ -14,7 +14,27 @@ import { getPackageRoot } from '../paths.js';
14
14
  import { isWindows } from './cross-platform.js';
15
15
  const execFileAsync = promisify(execFile);
16
16
  // ── npm install -g (shared) ────────────────────────────────────────────────
17
- export async function npmInstallGlobal(pkg) {
17
+ /**
18
+ * Can we run sudo without a password prompt?
19
+ *
20
+ * `sudo -n` never prompts: it exits non-zero instead when credentials are
21
+ * needed. That matters because upgrades run inside a detached monitor with no
22
+ * tty — a plain `sudo` there would block until the 180s timeout rather than
23
+ * fail. NOPASSWD rules and an unexpired credential cache both pass this check,
24
+ * so the cases where sudo can actually succeed are preserved.
25
+ */
26
+ async function canSudoNonInteractively() {
27
+ if (isWindows)
28
+ return false;
29
+ try {
30
+ await execFileAsync('sudo', ['-n', 'true'], { timeout: 5000 });
31
+ return true;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ export async function npmInstallGlobal(pkg, options = {}) {
18
38
  let cmd;
19
39
  let args;
20
40
  // Windows: run via cmd /c to avoid shell:true deprecation warning on Node 22.
@@ -28,14 +48,27 @@ export async function npmInstallGlobal(pkg) {
28
48
  args = ['install', '-g', pkg];
29
49
  }
30
50
  try {
31
- await execFileAsync(cmd, args, { timeout: 180000, windowsHide: isWindows });
51
+ await execFileAsync(cmd, args, {
52
+ timeout: options.timeoutMs ?? 180000,
53
+ windowsHide: isWindows,
54
+ signal: options.signal,
55
+ });
32
56
  }
33
57
  catch (e) {
58
+ if (options.signal?.aborted)
59
+ throw e;
34
60
  if (e.stderr?.includes('EACCES') || e.message?.includes('EACCES')) {
35
61
  if (isWindows) {
36
62
  throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
37
63
  }
38
- await execFileAsync('sudo', ['npm', 'install', '-g', pkg], { timeout: 180000, windowsHide: false });
64
+ if (!await canSudoNonInteractively()) {
65
+ throw new Error(`权限不足,且 sudo 需要密码(当前进程无法交互输入)。请手动执行: sudo npm install -g ${pkg}`);
66
+ }
67
+ await execFileAsync('sudo', ['-n', 'npm', 'install', '-g', pkg], {
68
+ timeout: options.timeoutMs ?? 180000,
69
+ windowsHide: false,
70
+ signal: options.signal,
71
+ });
39
72
  }
40
73
  else {
41
74
  throw e;
@@ -119,15 +152,19 @@ export function resolveGlobalPkg(pkgName) {
119
152
  * 使用 HTTP fetch 直接查 registry API,不依赖 npm CLI。
120
153
  * 超时 10 秒,失败返回 null。
121
154
  */
122
- export async function checkLatestVersion(pkg = 'evolcore') {
155
+ export async function checkLatestVersion(pkg = 'evolcore', timeoutMs = 10000, signal) {
156
+ const controller = new AbortController();
157
+ const abort = () => controller.abort();
158
+ if (signal?.aborted)
159
+ abort();
160
+ else
161
+ signal?.addEventListener('abort', abort, { once: true });
162
+ const timer = setTimeout(abort, timeoutMs);
123
163
  try {
124
- const controller = new AbortController();
125
- const timer = setTimeout(() => controller.abort(), 10000);
126
164
  const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`, {
127
165
  signal: controller.signal,
128
166
  headers: { 'Accept': 'application/json' },
129
167
  });
130
- clearTimeout(timer);
131
168
  if (!res.ok)
132
169
  return null;
133
170
  const data = await res.json();
@@ -136,20 +173,26 @@ export async function checkLatestVersion(pkg = 'evolcore') {
136
173
  catch {
137
174
  return null;
138
175
  }
176
+ finally {
177
+ clearTimeout(timer);
178
+ signal?.removeEventListener('abort', abort);
179
+ }
139
180
  }
140
181
  /**
141
182
  * 完整升级流程:检查 → 比较 → 安装(失败重试一次)
142
183
  */
143
- export async function tryUpgrade() {
184
+ export async function tryUpgrade(options = {}) {
144
185
  // 开发模式跳过
145
186
  if (isLinkedInstall()) {
146
187
  return { status: 'skipped' };
147
188
  }
148
189
  const localVer = getLocalVersion();
149
190
  // 查询 registry
150
- const remoteVer = await checkLatestVersion();
191
+ const remoteVer = await checkLatestVersion('evolcore', options.checkTimeoutMs, options.signal);
151
192
  if (!remoteVer) {
152
- return { status: 'skipped', error: 'Failed to check remote version' };
193
+ return options.signal?.aborted
194
+ ? { status: 'failed', from: localVer, error: 'Upgrade cancelled or timed out' }
195
+ : { status: 'skipped', error: 'Failed to check remote version' };
153
196
  }
154
197
  // 版本比较
155
198
  if (compareVersions(localVer, remoteVer) >= 0) {
@@ -159,11 +202,16 @@ export async function tryUpgrade() {
159
202
  let lastError;
160
203
  for (let attempt = 0; attempt < 2; attempt++) {
161
204
  try {
162
- await npmInstallGlobal('evolcore@latest');
205
+ await npmInstallGlobal('evolcore@latest', {
206
+ timeoutMs: options.installTimeoutMs,
207
+ signal: options.signal,
208
+ });
163
209
  return { status: 'upgraded', from: localVer, to: remoteVer };
164
210
  }
165
211
  catch (e) {
166
212
  lastError = e.stderr || e.message || String(e);
213
+ if (options.signal?.aborted)
214
+ break;
167
215
  }
168
216
  }
169
217
  return { status: 'failed', from: localVer, to: remoteVer, error: lastError };
@@ -173,7 +221,7 @@ export async function tryUpgrade() {
173
221
  * 仅在包已安装时检查升级,未安装则跳过(resolveFn 返回 null)。
174
222
  * 用于 EC Web 等真正独立于 EvolCore 的全局包。
175
223
  */
176
- export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
224
+ export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName, options = {}) {
177
225
  if (isLinkedInstall()) {
178
226
  return { status: 'skipped' };
179
227
  }
@@ -182,9 +230,11 @@ export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
182
230
  return { status: 'skipped' }; // not installed, skip
183
231
  }
184
232
  const localVer = installed.version;
185
- const remoteVer = await checkLatestVersion(pkgName);
233
+ const remoteVer = await checkLatestVersion(pkgName, options.checkTimeoutMs, options.signal);
186
234
  if (!remoteVer) {
187
- return { status: 'skipped', error: 'Failed to check remote version' };
235
+ return options.signal?.aborted
236
+ ? { status: 'failed', from: localVer, error: 'Upgrade cancelled or timed out' }
237
+ : { status: 'skipped', error: 'Failed to check remote version' };
188
238
  }
189
239
  if (compareVersions(localVer, remoteVer) >= 0) {
190
240
  return { status: 'no-update', from: localVer };
@@ -192,11 +242,16 @@ export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
192
242
  let lastError;
193
243
  for (let attempt = 0; attempt < 2; attempt++) {
194
244
  try {
195
- await npmInstallGlobal(`${pkgName}@latest`);
245
+ await npmInstallGlobal(`${pkgName}@latest`, {
246
+ timeoutMs: options.installTimeoutMs,
247
+ signal: options.signal,
248
+ });
196
249
  return { status: 'upgraded', from: localVer, to: remoteVer };
197
250
  }
198
251
  catch (e) {
199
252
  lastError = e.stderr || e.message || String(e);
253
+ if (options.signal?.aborted)
254
+ break;
200
255
  }
201
256
  }
202
257
  return { status: 'failed', from: localVer, to: remoteVer, error: lastError };
@@ -693,7 +693,7 @@ export function configureWindowsAutostart(enabled, runtimeRoot) {
693
693
  '/TN', WINDOWS_AUTOSTART_TASK_NAME,
694
694
  '/SC', 'ONLOGON',
695
695
  '/TR', taskCommand(scriptPath),
696
- '/RL', 'LIMITED',
696
+ '/RL', 'HIGHEST',
697
697
  '/F',
698
698
  ];
699
699
  const result = runSchtasks(args);
@@ -37,6 +37,7 @@
37
37
  | `ec agent` | EvolAgent 生命周期 | 创建/启停/热重载/改配置 | 管理员(owner/admin) | `agent.md`;完整创建与 Bootstrap 见 `agent-create.md` |
38
38
  | `ec contact` | Contact Book 与访问控制 | 申请添加、拉黑/解除拉黑、查拉黑 | Agent 当前私聊可申请添加;管理写操作按角色限制 | `contact.md` |
39
39
  | `ec rpc` | 底层 AUN RPC(逃生通道) | 直接调协议方法 | 高级/兜底 | `rpc.md` |
40
+ | `ec menu-client` | 远程 Daemon Menu 请求 | 远程诊断/daemon 日志/监控/原始配置 | 非托管 eclink/运维主机 | `menu-client.md` |
40
41
 
41
42
  ## 会话自管理类(不连 AUN,操作当前会话)
42
43
 
@@ -2,6 +2,11 @@
2
2
 
3
3
  本手册用于创建一个能正常工作的 EvolAgent。创建者负责创建和观察状态;新 Agent 在自己的 Bootstrap 会话中完成首次设定。不要把创建流水线完成误认为 Agent 已激活。
4
4
 
5
+ ### AID 与 Owner 约定
6
+
7
+ - AID 必须是 `{name}.{issuer}`,例如 `helper.agentid.cn`;不要使用四级域名(如 `helper.team.agentid.cn`),也不要把显示名当作 AID。
8
+ - `--owner` 填写需求提出者或实际责任 Owner 的完整 AID,而不是默认填写当前执行者。创建者只是代办创建流程时,仍应保留需求提出者的 Owner 权限。
9
+
5
10
  ## 生命周期与完成标准
6
11
 
7
12
  ```text
@@ -18,6 +23,8 @@ active
18
23
 
19
24
  ## 1. 创建者创建 Agent
20
25
 
26
+ 多个 Agent 必须串行创建和观察。不要并行启动多个 `ec agent new`:证书、热加载和 Bootstrap 会竞争连接与资源,容易造成超时、锁等待或把失败误判为未创建。
27
+
21
28
  在独立终端可以使用交互模式:
22
29
 
23
30
  ```bash
@@ -56,6 +63,8 @@ ec agent show helper.agentid.pub
56
63
 
57
64
  如果是 menu 创建,`accepted=true` 只表示请求已受理。继续轮询 `createProgress.status` 直到 `ready` 或 `failed`。这里的 `createProgress.status=ready` **只表示后台创建流水线结束,不等于 `lifecycle=active`**。
58
65
 
66
+ 创建者应维护一个最小状态账本(可放在当前任务记录中),每个 Agent 至少记录:`aid`、需求 Owner、项目路径、创建请求是否受理、`createProgress.status`、`lifecycle`、本地 `agent.md` 是否存在/字段是否完整、名片是否已发布、最近错误、下一步和最后检查时间。重启或超时恢复时先按账本逐项核对实际状态,再决定续做,不按“上次命令是否返回”推断结果。
67
+
59
68
  ## 2. 触发 Bootstrap
60
69
 
61
70
  当任一已启用 channel 可以联系 Owner 时,系统会向 Owner 发送固定的 Bootstrap 首条消息,并把生命周期从 `created` 切换为 `bootstrapping`。
@@ -136,6 +145,8 @@ ec agent get helper.agentid.pub lifecycle
136
145
  ec agent show helper.agentid.pub
137
146
  ```
138
147
 
148
+ 验证其它 Agent 的生命周期、配置和创建进度统一使用 `ec agent get/show/list`;`ec aid` 只用于身份、证书和名片操作,不要用 `ec aid` 代替 Agent 状态验证。
149
+
139
150
  验收标准:
140
151
 
141
152
  - `lifecycle` 为 `active`。
@@ -144,3 +155,5 @@ ec agent show helper.agentid.pub
144
155
  - 新 Agent 能在新主会话中处理普通任务。
145
156
 
146
157
  如果仍为 `created`,检查 channel 是否已能联系 Owner,必要时由 Owner 主动发送首条消息。如果仍为 `bootstrapping`,回到新 Agent 的 Bootstrap 会话完成字段确认、名片发布和 `ready`,不要由创建者直接绕过引导。
158
+
159
+ 失败重试前必须先执行状态检查:若本地 `agent.md` 已存在且远端名片已发布,只向该 Agent 发送 `ready`/续做指令,让它在自己的 Bootstrap 会话中完成收尾;不要重复创建、重复改名片或重复发布。仅在确认创建记录不存在且名片未发布时,才重新发起创建。
@@ -21,6 +21,7 @@
21
21
 
22
22
  - `<self-aid>` 是当前 Agent 的 AID。托管 Agent 会话必须使用自己的 AID,不能代替其他 Agent 发起管理操作。
23
23
  - 创建群的 Agent 自动成为群 owner。
24
+ - 需求发起者应设置为群 admin(若需求发起者就是创建者,则其已是 owner,无需另设)。
24
25
  - owner/admin 负责邀请成员、设置角色、修改入群规则和发布 `/rules.md`。
25
26
  - 普通 member 可以读取当前群信息、读取规则并参与协作;需要管理群成员或设置时,应请 owner/admin 操作。
26
27
  - `dissolve` 和 `owner` 转让属于 owner 级操作,临时协作结束通常不需要解散群。
@@ -36,13 +37,13 @@
36
37
  - 分工:谁负责协调、执行、复核和最终决策。
37
38
  - 验收:完成条件、截止时间、交付路径和汇报频率。
38
39
 
39
- 使用 Owner 明确提供或已经可靠取得的完整 AID;不要从显示名猜测 AID,也不要邀请尚未确认的对象。如果参与者、可见性或入群方式不明确,先向 Owner 补齐这些信息再创建群。
40
+ 使用 Owner 明确提供或已经可靠取得的完整 AID;AID 必须是 `{name}.{issuer}`,例如 `coordinator.agentid.cn`,不要使用四级域名或从显示名猜测 AID,也不要邀请尚未确认的对象。如果参与者、可见性或入群方式不明确,先向 Owner 补齐这些信息再创建群。
40
41
 
41
42
  如果已经有合适的群,先用 `ec group list` 和 `ec group info` 检查,不要为每个任务重复建群。
42
43
 
43
44
  ## 2. 创建私有协作群
44
45
 
45
- 推荐默认使用私有群和 `invite_only`,先建群再按名单邀请:
46
+ 群名称必填。创建前根据群名称确定一个简短、唯一的 group AID(同样使用 `{name}.{issuer}`,不要四级域名),并给出一句群简介;名称、AID 和简介应能体现协作目标。推荐默认使用私有群和 `invite_only`,先建群再按名单邀请:
46
47
 
47
48
  ```bash
48
49
  ec group create coordinator.agentid.pub "Project Alpha 协作" \
@@ -63,6 +64,8 @@ ec group rules coordinator.agentid.pub <group-id> --format json
63
64
 
64
65
  ## 3. 邀请成员并设置角色
65
66
 
67
+ 先确认所有待协作 Agent 的 `lifecycle` 都是 `active`(使用 `ec agent get/show`,不要用 `ec aid`)。在此之前不要邀请成员,也不要发送或分派任务;Bootstrap 未完成的 Agent 不能稳定接收协作工作。
68
+
66
69
  一次邀请多个成员:
67
70
 
68
71
  ```bash
@@ -100,6 +103,8 @@ ec group rules coordinator.agentid.pub <group-id> \
100
103
 
101
104
  ### 4.2 编写并发布 `/rules.md`
102
105
 
106
+ 团队群必须先发布一份最小 `/rules.md`。至少明确四类职责:协调(拆解任务、同步状态、最终汇总)、侦察(收集资料和事实)、分析(比较方案、识别风险)、策略(做出取舍和升级决策)。可再补充具体成员映射、交付格式和升级路径。
107
+
103
108
  在当前项目中准备一个不超过 4KB 的 UTF-8 Markdown 文件,例如 `project-alpha-rules.md`:
104
109
 
105
110
  ```markdown
@@ -0,0 +1,34 @@
1
+ # ec menu-client — 远程 Daemon Menu 客户端
2
+
3
+ 向另一台主机的 EvolCore Control AID 发送一个标准 `menu.*` 请求,并在同一 AUN 短连接中完成发送、拉取、匹配和 ACK。适合非托管主机上的 eclink/运维工具调用;托管 Agent 会话不应使用它绕过当前 daemon。
4
+
5
+ ## 用法
6
+
7
+ ```bash
8
+ ec menu-client request <from-aid> <daemon-aid> \
9
+ --request-file "$TMPDIR/menu-request.json" \
10
+ --timeout 15s \
11
+ --encrypt \
12
+ --format json
13
+ ```
14
+
15
+ - `--request-file` 与 `--request-json` 二选一,内容是现有 Menu request;缺少 `id` 时自动生成 UUID。
16
+ - 默认使用 E2EE,并发送 `client_name=ec-menu-client` 和本机 EvolCore `client_version`。
17
+ - 默认 slot 是 `evolcore-menu-client`;`--app <slot>` 可覆盖,但会切换消费游标,调用方须自行串行化。
18
+ - 默认授权是远端 `daemon.json.owners` 对发送 AID 的校验。
19
+ - `--remote-token <token>` 使用 daemon 签发的远程访问 token,权限与 daemon-owner 一致。CLI 审计日志不会记录 token 明文。
20
+ - `--ecweb-token <token>` 是 `--remote-token` 的兼容别名。
21
+ - `menu.query` / `menu.options` 等待响应超时时自动重发一次相同 request id;写操作不自动重试。
22
+ - 成功时 stdout 是完整 `menu.response` JSON。远端业务错误仍在响应的 `error` 字段中;本地传输/超时/ACK 错误退出码非 0。
23
+
24
+ ## 常用请求
25
+
26
+ ```json
27
+ {"type":"menu.query","id":"status-1","name":"system","args":{"view":"status"}}
28
+ {"type":"menu.query","id":"monitor-1","name":"system","args":{"view":"monitor"}}
29
+ {"type":"menu.query","id":"logs-1","name":"system","args":{"view":"logs","source":"daemon","limit":200}}
30
+ {"type":"menu.options","id":"config-options-1","name":"config","args":{"option":"rawFile"}}
31
+ {"type":"menu.query","id":"config-1","name":"config","args":{"view":"rawFile","resource":"daemon"}}
32
+ ```
33
+
34
+ 先用 `menu.list` 的 `args.view=catalog`、`context.kind=process` 获取目标 daemon 实际支持的能力和 wire 参数,不要在适配器中另建一套协议字段。
@@ -531,7 +531,15 @@ function planMigration(home) {
531
531
  function assertDaemonStopped(home) {
532
532
  const candidates = [];
533
533
  const pidFile = path.join(home, 'daemon.pid');
534
- if (fs.existsSync(pidFile)) candidates.push({ pid: Number(fs.readFileSync(pidFile, 'utf8').trim()), source: pidFile });
534
+ try {
535
+ // daemon.pid is a retired legacy marker. Ignore directory/symlink residue
536
+ // so a sandbox-created directory cannot turn migration into EISDIR.
537
+ if (fs.lstatSync(pidFile).isFile()) {
538
+ candidates.push({ pid: Number(fs.readFileSync(pidFile, 'utf8').trim()), source: pidFile });
539
+ }
540
+ } catch (error) {
541
+ if (error?.code !== 'ENOENT') throw error;
542
+ }
535
543
  const instanceDir = path.join(home, 'data', 'instance');
536
544
  if (fs.existsSync(instanceDir)) {
537
545
  for (const name of fs.readdirSync(instanceDir)) {
@@ -452,11 +452,17 @@ function changeRecord(home, file, kind, beforeText, after) {
452
452
  function assertDaemonStopped(home) {
453
453
  const candidates = [];
454
454
  const legacyPidFile = path.join(home, 'daemon.pid');
455
- if (fs.existsSync(legacyPidFile)) {
456
- candidates.push({
457
- pid: Number(fs.readFileSync(legacyPidFile, 'utf8').trim()),
458
- source: legacyPidFile,
459
- });
455
+ try {
456
+ // daemon.pid is a retired legacy marker. Ignore directory/symlink residue
457
+ // so a sandbox-created directory cannot turn migration into EISDIR.
458
+ if (fs.lstatSync(legacyPidFile).isFile()) {
459
+ candidates.push({
460
+ pid: Number(fs.readFileSync(legacyPidFile, 'utf8').trim()),
461
+ source: legacyPidFile,
462
+ });
463
+ }
464
+ } catch (error) {
465
+ if (error?.code !== 'ENOENT') throw error;
460
466
  }
461
467
 
462
468
  const instanceDir = path.join(home, 'data', 'instance');
@@ -200,8 +200,14 @@ function planMigration(home) {
200
200
  function assertDaemonStopped(home) {
201
201
  const candidates = [];
202
202
  const pidFile = path.join(home, 'daemon.pid');
203
- if (fs.existsSync(pidFile)) {
204
- candidates.push({ pid: Number(fs.readFileSync(pidFile, 'utf8').trim()), source: pidFile });
203
+ try {
204
+ // daemon.pid is a retired legacy marker. Ignore directory/symlink residue
205
+ // so a sandbox-created directory cannot turn migration into EISDIR.
206
+ if (fs.lstatSync(pidFile).isFile()) {
207
+ candidates.push({ pid: Number(fs.readFileSync(pidFile, 'utf8').trim()), source: pidFile });
208
+ }
209
+ } catch (error) {
210
+ if (error?.code !== 'ENOENT') throw error;
205
211
  }
206
212
  const instanceDir = path.join(home, 'data', 'instance');
207
213
  if (fs.existsSync(instanceDir)) {
@@ -58,6 +58,8 @@
58
58
  "scopes": ["agent"],
59
59
  "constraints": { "targetCurrentAgentOnly": true, "requireAgentAdmin": true }
60
60
  },
61
+ "ec.aid.lookup": { "allow": true, "scopes": ["agent"] },
62
+ "ec.aid.agentmd.get": { "allow": true, "scopes": ["agent"] },
61
63
  "ec.aid.agentmd.put": {
62
64
  "allow": true,
63
65
  "dangerous": true,
@@ -21,6 +21,7 @@
21
21
  "model.list": { "allow": true, "scopes": ["relation"], "constraints": { "ownPeerOnly": true, "targetCurrentAgentOnly": true } },
22
22
  "model.current": { "allow": true, "scopes": ["relation"], "constraints": { "ownPeerOnly": true, "targetCurrentAgentOnly": true } },
23
23
  "model.info": { "allow": true, "scopes": ["relation"], "constraints": { "ownPeerOnly": true, "targetCurrentAgentOnly": true } },
24
+ "model.effort.current": { "allow": true, "scopes": ["relation"], "constraints": { "ownPeerOnly": true, "targetCurrentAgentOnly": true } },
24
25
  "model.use": { "allow": false },
25
26
  "model.effort": { "allow": false },
26
27
  "model.reset": { "allow": false },
@@ -2,7 +2,7 @@
2
2
  phase: bootstrapping
3
3
 
4
4
  这是 agent 生命周期的强制门禁。你正在进行首次自我设定;在完成前,唯一目标是和 owner 确认并写入基础名片信息,不处理普通任务。
5
- 整个过程由你根据当前对话自主推进,不存在需要查询或维护的额外 bootstrap 子状态。
5
+ Bootstrap 不另设隐藏状态机;恢复依据是可核对的生命周期和名片状态。每次重启、超时恢复或收到“继续”时,先检查自身 `lifecycle` 以及本地 `agent.md` 是否存在、字段是否完整,再决定续做步骤,不按旧上下文推断尚未完成。
6
6
  系统已经在进入本阶段时向 owner 发送过固定的首条引导消息。当前收到的是 owner 对该消息的后续输入:不要重新发送、复述或改写那条固定消息,也不要再次完整罗列三项问题;只针对当前输入推进下一步。
7
7
 
8
8
  需要确认的字段:
@@ -17,6 +17,13 @@ phase: bootstrapping
17
17
  - `ec agent ready {{selfAid}}`
18
18
  - 除这些 bootstrap 操作及向 owner 回复外,不得调用工具或执行命令。
19
19
 
20
+ 恢复时遵循以下幂等规则:
21
+ - `lifecycle=active`:Bootstrap 已完成,不重复改名片、不重复发布、不重复执行 `ready`,也不处理本轮夹带的普通任务。
22
+ - `lifecycle=created`:尚未进入 Bootstrap,只等待 channel/owner 首条消息;不重复创建 Agent。
23
+ - `lifecycle=bootstrapping` 且本地 `agent.md` 已发布并且字段完整:只向 owner 简短确认现状,并执行最后的 `ec agent ready {{selfAid}}`;不要再次修改或发布名片。
24
+ - `lifecycle=bootstrapping` 但名片未发布或字段不完整:从缺失的确认步骤继续。发布失败时先说明错误,修正后再试,不盲目重试。
25
+ - Bootstrap 期间不执行、不分析、不规划普通任务;普通任务只能留到 `active` 后的新主会话。
26
+
20
27
  每轮处理规则:
21
28
  1. 首次需要候选值时,读取本地 agent.md 的 YAML frontmatter。已有且格式有效的 `name`、`description`、`tags` 是现有候选值:简短回显并询问保持还是修改,不要求重新输入。空值、占位值或格式无效的字段视为缺失;`tags` 必须为 3 到 6 个非空标签。
22
29
  2. 只把 owner 明确提供、修改、选择或确认的 `name`、`description`、`tags` 当作候选信息。普通任务内容、示例值和顺带提及的词语都不是候选信息,不得据此推断字段。