evolcore 0.0.24 → 0.0.26
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.
- package/CHANGELOG.md +59 -0
- package/bin/ec.js +12 -1
- package/bin/install-codex-managed-hooks.mjs +28 -1
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +54 -13
- package/dist/agents/codex/app-server-client.js +13 -5
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +128 -42
- package/dist/agents/ecagent-runner.js +14 -18
- package/dist/agents/gemini-runner.js +24 -9
- package/dist/agents/runner-types.js +25 -17
- package/dist/aun/aid/agentmd.js +135 -10
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/aid/index.js +2 -1
- package/dist/aun/aid/managed-operation.js +53 -1
- package/dist/aun/aid/store.js +17 -13
- package/dist/aun/menu-client.js +74 -0
- package/dist/aun/msg/group.js +1 -1
- package/dist/aun/msg/managed-operation.js +1 -1
- package/dist/aun/msg/p2p.js +6 -1
- package/dist/aun/msg/upload.js +30 -22
- package/dist/aun/storage/client.js +4 -1
- package/dist/aun/storage/index.js +1 -1
- package/dist/channels/aun.js +329 -94
- package/dist/cli/agent-command.js +125 -5
- package/dist/cli/agent-create.js +151 -11
- package/dist/cli/agent.js +119 -6
- package/dist/cli/aun-commands.js +74 -7
- package/dist/cli/config.js +27 -8
- package/dist/cli/contact.js +63 -6
- package/dist/cli/daemon-commands.js +90 -25
- package/dist/cli/diag.js +31 -0
- package/dist/cli/handoff-command.js +35 -3
- package/dist/cli/index.js +7 -1
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +38 -23
- package/dist/cli/restart-monitor.js +57 -10
- package/dist/config/builtin-roles.js +2 -0
- package/dist/config/config-manager.js +15 -0
- package/dist/config/config-operation-service.js +165 -3
- package/dist/config/contact-operation-service.js +96 -7
- package/dist/config/resolved-config-op.js +66 -8
- package/dist/core/agent-application-service.js +2 -1
- package/dist/core/auth/authorization-audit.js +20 -12
- package/dist/core/auth/operation-authorizer.js +87 -8
- package/dist/core/auth/operation-catalog.js +51 -4
- package/dist/core/auth/remote-access-store.js +40 -1
- package/dist/core/bootstrap-service.js +1 -1
- package/dist/core/command/agent-control.js +54 -25
- package/dist/core/command/cli-intent-parser.js +18 -0
- package/dist/core/command/command-handler.js +152 -20
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/menu-catalog.js +13 -3
- package/dist/core/command/menu-handler.js +88 -31
- package/dist/core/command/menu-protocol.js +6 -0
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +77 -12
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +3 -3
- package/dist/core/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/message/im-renderer.js +72 -21
- package/dist/core/message/message-bridge.js +69 -9
- package/dist/core/message/message-queue.js +26 -0
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +48 -23
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/ec-command-parser.js +51 -12
- package/dist/core/permission/sandbox-runtime.js +36 -14
- package/dist/core/permission/tool-policy.js +47 -7
- package/dist/index.js +195 -31
- package/dist/ipc.js +54 -2
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/price-resolver.js +28 -10
- package/dist/utils/avatar-upload.js +282 -23
- package/dist/utils/cross-platform.js +30 -1
- package/dist/utils/error-utils.js +21 -0
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +3 -1
- package/dist/utils/model-prices.jsonl +3 -3
- package/dist/utils/npm-ops.js +176 -35
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +3 -1
- package/kits/docs/evolcore/agent-create.md +30 -11
- package/kits/docs/evolcore/agent.md +18 -8
- package/kits/docs/evolcore/aid.md +6 -4
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
- package/kits/docs/evolcore/config.md +7 -0
- package/kits/docs/evolcore/contact.md +12 -5
- package/kits/docs/evolcore/menu-client.md +11 -3
- package/kits/docs/evolcore/msg.md +1 -0
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/docs/venues/group.md +12 -0
- package/kits/eck_manifest.json +12 -0
- package/kits/rules/03-identity.md +3 -1
- package/kits/templates/message-fragments/handoff-request-to-target.md +3 -3
- package/kits/templates/roles/admin.json +14 -2
- package/kits/templates/roles/member.json +3 -2
- package/kits/templates/roles/visitor.json +4 -3
- package/kits/templates/system-fragments/bootstrap.md +16 -5
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +6 -4
- package/skills/eclink/SKILL.md +0 -84
- package/skills/eclink/agents/openai.yaml +0 -4
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
1
2
|
import fs from 'fs';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { getPackageRoot } from '../paths.js';
|
|
4
5
|
let cachedVersion;
|
|
6
|
+
let cachedFlavor;
|
|
7
|
+
let cachedStartStamp;
|
|
8
|
+
let cachedStartedAtIso;
|
|
5
9
|
export function readInstalledEvolcoreVersion() {
|
|
6
10
|
if (cachedVersion !== undefined)
|
|
7
11
|
return cachedVersion;
|
|
@@ -16,6 +20,131 @@ export function readInstalledEvolcoreVersion() {
|
|
|
16
20
|
cachedVersion = version;
|
|
17
21
|
return version;
|
|
18
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* 区分源码仓库运行与全局安装包。`package.json` 的 `files` 白名单不含 `src/`,
|
|
25
|
+
* 所以 `src/index.ts` 的存在性可以稳定区分两者:同一个版本号在 dev 下跑的是
|
|
26
|
+
* 未发布的工作副本,排查时必须能看出来。
|
|
27
|
+
*/
|
|
28
|
+
export function readEvolcoreRuntimeFlavor() {
|
|
29
|
+
if (cachedFlavor !== undefined)
|
|
30
|
+
return cachedFlavor;
|
|
31
|
+
let flavor;
|
|
32
|
+
try {
|
|
33
|
+
flavor = fs.existsSync(path.join(getPackageRoot(), 'src', 'index.ts')) ? 'dev' : 'install';
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
flavor = 'install';
|
|
37
|
+
}
|
|
38
|
+
cachedFlavor = flavor;
|
|
39
|
+
return flavor;
|
|
40
|
+
}
|
|
41
|
+
function pad2(n) {
|
|
42
|
+
return String(n).padStart(2, '0');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 本进程启动时刻。用 `process.uptime()` 反推而非模块加载时的 `Date.now()`:
|
|
46
|
+
* 后者受 import 链耗时影响,且本模块可能被惰性加载。无 syscall,只算一次。
|
|
47
|
+
*/
|
|
48
|
+
function resolveStartedAt() {
|
|
49
|
+
return new Date(Date.now() - Math.round(process.uptime() * 1000));
|
|
50
|
+
}
|
|
51
|
+
/** 本进程启动时刻的 ISO 串,供结构化字段使用。 */
|
|
52
|
+
export function readEvolcoreStartedAtIso() {
|
|
53
|
+
if (cachedStartedAtIso !== undefined)
|
|
54
|
+
return cachedStartedAtIso;
|
|
55
|
+
cachedStartedAtIso = resolveStartedAt().toISOString();
|
|
56
|
+
return cachedStartedAtIso;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 错误标记里的启动时刻,`MMDD-HHMM` 本地时间。
|
|
60
|
+
*
|
|
61
|
+
* 用本地时间而非 UTC:读的人是对着自己的墙上时钟判断"这进程是我改完之后起的吗"。
|
|
62
|
+
* 精度到分钟——秒级对判断重启无意义,只会占长度。
|
|
63
|
+
*/
|
|
64
|
+
export function readEvolcoreStartStamp() {
|
|
65
|
+
if (cachedStartStamp !== undefined)
|
|
66
|
+
return cachedStartStamp;
|
|
67
|
+
const d = resolveStartedAt();
|
|
68
|
+
cachedStartStamp = `${pad2(d.getMonth() + 1)}${pad2(d.getDate())}-${pad2(d.getHours())}${pad2(d.getMinutes())}`;
|
|
69
|
+
return cachedStartStamp;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 用户可见错误文案的版本标记,形如 `[EC 0.0.24 dev 0915-1230]`。
|
|
73
|
+
*
|
|
74
|
+
* 带启动时刻是因为版本号不足以定位问题:dev 模式下同一个 `0.0.24` 可能是任意一份
|
|
75
|
+
* 工作副本,改完代码不重启就仍在跑旧代码——启动时刻能直接把这种情况暴露出来。
|
|
76
|
+
*/
|
|
77
|
+
export function evolcoreErrorTag() {
|
|
78
|
+
return `[EC ${readInstalledEvolcoreVersion()} ${readEvolcoreRuntimeFlavor()} ${readEvolcoreStartStamp()}]`;
|
|
79
|
+
}
|
|
80
|
+
const ERROR_TAG_PATTERN = /\[EC\s+[^\]]*\]/;
|
|
81
|
+
/**
|
|
82
|
+
* 前导 emoji / 变体选择符 / 空白:标记要插在它们之后。U+FE0F 单独列出,
|
|
83
|
+
* 它跟在 emoji 之后且不属于 Extended_Pictographic,漏掉会把 `⚠️` 拆开。
|
|
84
|
+
*/
|
|
85
|
+
const LEADING_DECORATION = /^(?:\p{Extended_Pictographic}|️|\s)+/u;
|
|
86
|
+
/**
|
|
87
|
+
* 给用户可见的错误文案打版本标记。标记走前缀而非后缀:渠道前端会截断长文案
|
|
88
|
+
* (错误卡片只显示前两行),后缀会被直接截掉。
|
|
89
|
+
*
|
|
90
|
+
* 幂等,且在整串任意位置发现既有标记就跳过——错误文案会被
|
|
91
|
+
* `formatRetryableErrorFinalMessage` 之类的包装器嵌进 `原因:…` 中段,
|
|
92
|
+
* 只检查开头会导致同一个错误被打两次标。
|
|
93
|
+
*/
|
|
94
|
+
export function tagErrorTextWithEvolcoreVersion(text) {
|
|
95
|
+
if (typeof text !== 'string' || !text.trim())
|
|
96
|
+
return text;
|
|
97
|
+
if (ERROR_TAG_PATTERN.test(text))
|
|
98
|
+
return text;
|
|
99
|
+
const decoration = text.match(LEADING_DECORATION)?.[0] ?? '';
|
|
100
|
+
return `${decoration}${evolcoreErrorTag()} ${text.slice(decoration.length)}`;
|
|
101
|
+
}
|
|
19
102
|
export function _resetInstalledEvolcoreVersionForTests() {
|
|
20
103
|
cachedVersion = undefined;
|
|
104
|
+
cachedFlavor = undefined;
|
|
105
|
+
cachedStartStamp = undefined;
|
|
106
|
+
cachedStartedAtIso = undefined;
|
|
107
|
+
}
|
|
108
|
+
function collectFiles(root, current = root, out = []) {
|
|
109
|
+
let entries;
|
|
110
|
+
try {
|
|
111
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
117
|
+
const full = path.join(current, entry.name);
|
|
118
|
+
if (entry.isDirectory())
|
|
119
|
+
collectFiles(root, full, out);
|
|
120
|
+
else if (entry.isFile())
|
|
121
|
+
out.push(path.relative(root, full));
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
/** Hash the complete compiled tree so a changed dependency module changes the ID too. */
|
|
126
|
+
export function computeBuildId(distDir) {
|
|
127
|
+
if (!fs.existsSync(distDir))
|
|
128
|
+
return null;
|
|
129
|
+
try {
|
|
130
|
+
const hash = crypto.createHash('sha256');
|
|
131
|
+
for (const relative of collectFiles(distDir)) {
|
|
132
|
+
const full = path.join(distDir, relative);
|
|
133
|
+
hash.update(relative.replaceAll(path.sep, '/'));
|
|
134
|
+
hash.update('\0');
|
|
135
|
+
hash.update(fs.readFileSync(full));
|
|
136
|
+
hash.update('\0');
|
|
137
|
+
}
|
|
138
|
+
return `sha256:${hash.digest('hex').slice(0, 24)}`;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export function readBuildIdentity(distDir, version) {
|
|
145
|
+
return {
|
|
146
|
+
id: computeBuildId(distDir),
|
|
147
|
+
entry: path.join(distDir, 'index.js'),
|
|
148
|
+
...(version ? { version } : {}),
|
|
149
|
+
};
|
|
21
150
|
}
|
package/dist/utils/logger.js
CHANGED
|
@@ -152,7 +152,9 @@ export function normalizeStructuredLog(data) {
|
|
|
152
152
|
export function localTimestamp() {
|
|
153
153
|
const d = new Date();
|
|
154
154
|
const pad = (n) => String(n).padStart(2, '0');
|
|
155
|
-
|
|
155
|
+
const offset = -d.getTimezoneOffset();
|
|
156
|
+
const zone = `${offset >= 0 ? '+' : '-'}${pad(Math.floor(Math.abs(offset) / 60))}:${pad(Math.abs(offset) % 60)}`;
|
|
157
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${String(d.getMilliseconds()).padStart(3, '0')}${zone}`;
|
|
156
158
|
}
|
|
157
159
|
function log(level, ...args) {
|
|
158
160
|
if (!shouldLog(level))
|
|
@@ -18,6 +18,6 @@
|
|
|
18
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
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
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}
|
|
21
|
+
{"model":"claude-opus-5","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":25,"price_cache_creation":6.25,"price_cache_read":0.5,"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,"billing_fn":"per_token_v1","currency":"USD","price_input":2,"price_output":10,"price_cache_creation":2.5,"price_cache_read":0.2,"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,"billing_fn":"per_token_v1","currency":"USD","price_input":10,"price_output":50,"price_cache_creation":12.5,"price_cache_read":1,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":131072,"supports_cache":true,"supports_vision":true}
|
package/dist/utils/npm-ops.js
CHANGED
|
@@ -8,11 +8,119 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
import path from 'path';
|
|
11
|
-
import { execFile,
|
|
11
|
+
import { execFile, execFileSync } from 'child_process';
|
|
12
12
|
import { promisify } from 'util';
|
|
13
13
|
import { getPackageRoot } from '../paths.js';
|
|
14
14
|
import { isWindows } from './cross-platform.js';
|
|
15
15
|
const execFileAsync = promisify(execFile);
|
|
16
|
+
/** The preferred public registry for update checks. */
|
|
17
|
+
export const DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org';
|
|
18
|
+
function normalizeRegistry(registry = DEFAULT_NPM_REGISTRY) {
|
|
19
|
+
const value = registry.trim();
|
|
20
|
+
return (value || DEFAULT_NPM_REGISTRY).replace(/\/+$/, '');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve npm from the same Node installation that is running EvolCore.
|
|
24
|
+
* This prevents a daemon launched by one NVM installation from querying or
|
|
25
|
+
* mutating another installation selected by the shell's PATH.
|
|
26
|
+
*/
|
|
27
|
+
function resolveNpmInvocation() {
|
|
28
|
+
const nodeDir = path.dirname(process.execPath);
|
|
29
|
+
const sibling = path.join(nodeDir, isWindows ? 'npm.cmd' : 'npm');
|
|
30
|
+
const cliCandidates = [
|
|
31
|
+
path.join(nodeDir, 'node_modules', 'npm', 'bin', 'npm-cli.js'),
|
|
32
|
+
];
|
|
33
|
+
if (fs.existsSync(sibling)) {
|
|
34
|
+
try {
|
|
35
|
+
const target = fs.realpathSync(sibling);
|
|
36
|
+
if (target.endsWith('.js'))
|
|
37
|
+
cliCandidates.unshift(target);
|
|
38
|
+
}
|
|
39
|
+
catch { /* fall through to the known npm layouts */ }
|
|
40
|
+
}
|
|
41
|
+
const npmCli = cliCandidates.find(candidate => fs.existsSync(candidate));
|
|
42
|
+
if (npmCli)
|
|
43
|
+
return { command: process.execPath, argsPrefix: [npmCli] };
|
|
44
|
+
if (isWindows) {
|
|
45
|
+
return {
|
|
46
|
+
command: process.env.ComSpec || path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe'),
|
|
47
|
+
argsPrefix: ['/c', 'npm'],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return { command: fs.existsSync(sibling) ? sibling : 'npm', argsPrefix: [] };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Read the effective registry from the npm belonging to the current Node.
|
|
54
|
+
* An empty value means npm has no explicit registry and uses the public
|
|
55
|
+
* default. A command failure is handled separately by using the public source
|
|
56
|
+
* directly, so a broken npm config cannot leave the upgrade without a retry.
|
|
57
|
+
*/
|
|
58
|
+
async function resolveUpgradeRegistry(signal) {
|
|
59
|
+
const npm = resolveNpmInvocation();
|
|
60
|
+
try {
|
|
61
|
+
const result = await execFileAsync(npm.command, [...npm.argsPrefix, 'config', 'get', 'registry'], {
|
|
62
|
+
timeout: 5000,
|
|
63
|
+
windowsHide: isWindows,
|
|
64
|
+
signal,
|
|
65
|
+
});
|
|
66
|
+
const value = String(result.stdout ?? '').trim();
|
|
67
|
+
if (!value || value === 'undefined' || value === 'null') {
|
|
68
|
+
return { registry: DEFAULT_NPM_REGISTRY, useNpmCli: false };
|
|
69
|
+
}
|
|
70
|
+
return { registry: normalizeRegistry(value), useNpmCli: true };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return { registry: DEFAULT_NPM_REGISTRY, useNpmCli: false };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function checkLatestVersionViaNpm(pkg, timeoutMs, signal, registry) {
|
|
77
|
+
const npm = resolveNpmInvocation();
|
|
78
|
+
try {
|
|
79
|
+
const result = await execFileAsync(npm.command, [
|
|
80
|
+
...npm.argsPrefix,
|
|
81
|
+
'view',
|
|
82
|
+
`${pkg}@latest`,
|
|
83
|
+
'version',
|
|
84
|
+
'--json',
|
|
85
|
+
'--registry',
|
|
86
|
+
registry,
|
|
87
|
+
], {
|
|
88
|
+
timeout: timeoutMs ?? 10000,
|
|
89
|
+
windowsHide: isWindows,
|
|
90
|
+
signal,
|
|
91
|
+
});
|
|
92
|
+
const raw = String(result.stdout ?? '').trim();
|
|
93
|
+
if (!raw)
|
|
94
|
+
return null;
|
|
95
|
+
const parsed = JSON.parse(raw);
|
|
96
|
+
if (typeof parsed === 'string' && parsed)
|
|
97
|
+
return parsed;
|
|
98
|
+
if (Array.isArray(parsed)) {
|
|
99
|
+
const latest = parsed.at(-1);
|
|
100
|
+
return typeof latest === 'string' && latest ? latest : null;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Registry checks can run concurrently, but two package upgrades must never
|
|
109
|
+
// mutate npm's global tree at the same time. A tiny in-process queue keeps the
|
|
110
|
+
// fast path parallel without changing upgrade ordering or failure semantics.
|
|
111
|
+
let globalInstallTail = Promise.resolve();
|
|
112
|
+
async function withGlobalInstallLock(action) {
|
|
113
|
+
const previous = globalInstallTail;
|
|
114
|
+
let release;
|
|
115
|
+
globalInstallTail = new Promise(resolve => { release = resolve; });
|
|
116
|
+
await previous;
|
|
117
|
+
try {
|
|
118
|
+
await action();
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
release();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
16
124
|
// ── npm install -g (shared) ────────────────────────────────────────────────
|
|
17
125
|
/**
|
|
18
126
|
* Can we run sudo without a password prompt?
|
|
@@ -35,20 +143,11 @@ async function canSudoNonInteractively() {
|
|
|
35
143
|
}
|
|
36
144
|
}
|
|
37
145
|
export async function npmInstallGlobal(pkg, options = {}) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// Unix: npm directly, no shell needed.
|
|
42
|
-
if (isWindows) {
|
|
43
|
-
cmd = process.env.ComSpec || path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe');
|
|
44
|
-
args = ['/c', 'npm', 'install', '-g', pkg];
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
cmd = 'npm';
|
|
48
|
-
args = ['install', '-g', pkg];
|
|
49
|
-
}
|
|
146
|
+
const npm = resolveNpmInvocation();
|
|
147
|
+
const registry = options.registry ? normalizeRegistry(options.registry) : undefined;
|
|
148
|
+
const npmArgs = ['install', '-g', pkg, ...(registry ? ['--registry', registry] : [])];
|
|
50
149
|
try {
|
|
51
|
-
await execFileAsync(
|
|
150
|
+
await execFileAsync(npm.command, [...npm.argsPrefix, ...npmArgs], {
|
|
52
151
|
timeout: options.timeoutMs ?? 180000,
|
|
53
152
|
windowsHide: isWindows,
|
|
54
153
|
signal: options.signal,
|
|
@@ -62,9 +161,10 @@ export async function npmInstallGlobal(pkg, options = {}) {
|
|
|
62
161
|
throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
|
|
63
162
|
}
|
|
64
163
|
if (!await canSudoNonInteractively()) {
|
|
65
|
-
|
|
164
|
+
const registryArg = registry ? ` --registry ${registry}` : '';
|
|
165
|
+
throw new Error(`权限不足,且 sudo 需要密码(当前进程无法交互输入)。请使用当前 Node 对应的 npm 手动执行: sudo npm install -g ${pkg}${registryArg}`);
|
|
66
166
|
}
|
|
67
|
-
await execFileAsync('sudo', ['-n',
|
|
167
|
+
await execFileAsync('sudo', ['-n', npm.command, ...npm.argsPrefix, ...npmArgs], {
|
|
68
168
|
timeout: options.timeoutMs ?? 180000,
|
|
69
169
|
windowsHide: false,
|
|
70
170
|
signal: options.signal,
|
|
@@ -132,10 +232,8 @@ export function getLocalVersion() {
|
|
|
132
232
|
*/
|
|
133
233
|
export function resolveGlobalPkg(pkgName) {
|
|
134
234
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const npmCmd = isWindows ? 'npm.cmd' : 'npm';
|
|
138
|
-
const globalRoot = execSync(`${npmCmd} root -g`, {
|
|
235
|
+
const npm = resolveNpmInvocation();
|
|
236
|
+
const globalRoot = execFileSync(npm.command, [...npm.argsPrefix, 'root', '-g'], {
|
|
139
237
|
encoding: 'utf-8', timeout: 10000, windowsHide: true,
|
|
140
238
|
}).trim();
|
|
141
239
|
const pkgPath = path.join(globalRoot, ...pkgName.split('/'), 'package.json');
|
|
@@ -152,7 +250,7 @@ export function resolveGlobalPkg(pkgName) {
|
|
|
152
250
|
* 使用 HTTP fetch 直接查 registry API,不依赖 npm CLI。
|
|
153
251
|
* 超时 10 秒,失败返回 null。
|
|
154
252
|
*/
|
|
155
|
-
|
|
253
|
+
async function fetchLatestVersion(pkg = 'evolcore', timeoutMs = 10000, signal, registry = DEFAULT_NPM_REGISTRY) {
|
|
156
254
|
const controller = new AbortController();
|
|
157
255
|
const abort = () => controller.abort();
|
|
158
256
|
if (signal?.aborted)
|
|
@@ -161,14 +259,14 @@ export async function checkLatestVersion(pkg = 'evolcore', timeoutMs = 10000, si
|
|
|
161
259
|
signal?.addEventListener('abort', abort, { once: true });
|
|
162
260
|
const timer = setTimeout(abort, timeoutMs);
|
|
163
261
|
try {
|
|
164
|
-
const res = await fetch(
|
|
262
|
+
const res = await fetch(`${normalizeRegistry(registry)}/${encodeURIComponent(pkg)}/latest`, {
|
|
165
263
|
signal: controller.signal,
|
|
166
264
|
headers: { 'Accept': 'application/json' },
|
|
167
265
|
});
|
|
168
266
|
if (!res.ok)
|
|
169
267
|
return null;
|
|
170
268
|
const data = await res.json();
|
|
171
|
-
return data.version
|
|
269
|
+
return typeof data.version === 'string' && data.version ? data.version : null;
|
|
172
270
|
}
|
|
173
271
|
catch {
|
|
174
272
|
return null;
|
|
@@ -178,6 +276,31 @@ export async function checkLatestVersion(pkg = 'evolcore', timeoutMs = 10000, si
|
|
|
178
276
|
signal?.removeEventListener('abort', abort);
|
|
179
277
|
}
|
|
180
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Read the current npm registry first. If that read succeeds, npm itself is
|
|
281
|
+
* used for the version query so proxy/CA/auth settings are preserved. If the
|
|
282
|
+
* read fails, retry once against the official HTTP endpoint. The selected
|
|
283
|
+
* registry is returned so installation uses the same source.
|
|
284
|
+
*/
|
|
285
|
+
async function checkLatestVersionWithFallback(pkg, timeoutMs, signal, explicitRegistry) {
|
|
286
|
+
if (explicitRegistry) {
|
|
287
|
+
const registry = normalizeRegistry(explicitRegistry);
|
|
288
|
+
const version = await fetchLatestVersion(pkg, timeoutMs, signal, registry);
|
|
289
|
+
return version ? { version, registry } : null;
|
|
290
|
+
}
|
|
291
|
+
const selection = await resolveUpgradeRegistry(signal);
|
|
292
|
+
const version = selection.useNpmCli
|
|
293
|
+
? await checkLatestVersionViaNpm(pkg, timeoutMs, signal, selection.registry)
|
|
294
|
+
: await fetchLatestVersion(pkg, timeoutMs, signal, selection.registry);
|
|
295
|
+
return version ? { version, registry: selection.registry } : null;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Query the registry selected from the running Node/npm configuration, or the
|
|
299
|
+
* official registry when that configuration cannot be read.
|
|
300
|
+
*/
|
|
301
|
+
export async function checkLatestVersion(pkg = 'evolcore', timeoutMs = 10000, signal, registry) {
|
|
302
|
+
return (await checkLatestVersionWithFallback(pkg, timeoutMs, signal, registry))?.version ?? null;
|
|
303
|
+
}
|
|
181
304
|
/**
|
|
182
305
|
* 完整升级流程:检查 → 比较 → 安装(失败重试一次)
|
|
183
306
|
*/
|
|
@@ -188,12 +311,13 @@ export async function tryUpgrade(options = {}) {
|
|
|
188
311
|
}
|
|
189
312
|
const localVer = getLocalVersion();
|
|
190
313
|
// 查询 registry
|
|
191
|
-
const
|
|
192
|
-
if (!
|
|
314
|
+
const checked = await checkLatestVersionWithFallback('evolcore', options.checkTimeoutMs, options.signal, options.registry);
|
|
315
|
+
if (!checked) {
|
|
193
316
|
return options.signal?.aborted
|
|
194
317
|
? { status: 'failed', from: localVer, error: 'Upgrade cancelled or timed out' }
|
|
195
318
|
: { status: 'skipped', error: 'Failed to check remote version' };
|
|
196
319
|
}
|
|
320
|
+
const { version: remoteVer, registry } = checked;
|
|
197
321
|
// 版本比较
|
|
198
322
|
if (compareVersions(localVer, remoteVer) >= 0) {
|
|
199
323
|
return { status: 'no-update', from: localVer };
|
|
@@ -202,11 +326,19 @@ export async function tryUpgrade(options = {}) {
|
|
|
202
326
|
let lastError;
|
|
203
327
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
204
328
|
try {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
329
|
+
let installedVer;
|
|
330
|
+
await withGlobalInstallLock(async () => {
|
|
331
|
+
await npmInstallGlobal(`evolcore@${remoteVer}`, {
|
|
332
|
+
timeoutMs: options.installTimeoutMs,
|
|
333
|
+
signal: options.signal,
|
|
334
|
+
registry,
|
|
335
|
+
});
|
|
336
|
+
installedVer = getLocalVersion();
|
|
208
337
|
});
|
|
209
|
-
|
|
338
|
+
if (!installedVer || compareVersions(installedVer, remoteVer) < 0) {
|
|
339
|
+
throw new Error(`npm install completed but evolcore is still ${installedVer ?? 'not installed'} (target ${remoteVer})`);
|
|
340
|
+
}
|
|
341
|
+
return { status: 'upgraded', from: localVer, to: installedVer };
|
|
210
342
|
}
|
|
211
343
|
catch (e) {
|
|
212
344
|
lastError = e.stderr || e.message || String(e);
|
|
@@ -230,23 +362,32 @@ export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName, options = {
|
|
|
230
362
|
return { status: 'skipped' }; // not installed, skip
|
|
231
363
|
}
|
|
232
364
|
const localVer = installed.version;
|
|
233
|
-
const
|
|
234
|
-
if (!
|
|
365
|
+
const checked = await checkLatestVersionWithFallback(pkgName, options.checkTimeoutMs, options.signal, options.registry);
|
|
366
|
+
if (!checked) {
|
|
235
367
|
return options.signal?.aborted
|
|
236
368
|
? { status: 'failed', from: localVer, error: 'Upgrade cancelled or timed out' }
|
|
237
369
|
: { status: 'skipped', error: 'Failed to check remote version' };
|
|
238
370
|
}
|
|
371
|
+
const { version: remoteVer, registry } = checked;
|
|
239
372
|
if (compareVersions(localVer, remoteVer) >= 0) {
|
|
240
373
|
return { status: 'no-update', from: localVer };
|
|
241
374
|
}
|
|
242
375
|
let lastError;
|
|
243
376
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
244
377
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
378
|
+
let installedVer;
|
|
379
|
+
await withGlobalInstallLock(async () => {
|
|
380
|
+
await npmInstallGlobal(`${pkgName}@${remoteVer}`, {
|
|
381
|
+
timeoutMs: options.installTimeoutMs,
|
|
382
|
+
signal: options.signal,
|
|
383
|
+
registry,
|
|
384
|
+
});
|
|
385
|
+
installedVer = resolveInstalled()?.version;
|
|
248
386
|
});
|
|
249
|
-
|
|
387
|
+
if (!installedVer || compareVersions(installedVer, remoteVer) < 0) {
|
|
388
|
+
throw new Error(`npm install completed but ${pkgName} is still ${installedVer ?? 'not installed'} (target ${remoteVer})`);
|
|
389
|
+
}
|
|
390
|
+
return { status: 'upgraded', from: localVer, to: installedVer };
|
|
250
391
|
}
|
|
251
392
|
catch (e) {
|
|
252
393
|
lastError = e.stderr || e.message || String(e);
|
|
@@ -290,6 +290,7 @@ coding 场景(无 channel/无身份)下,`chatType`、`channel`、`selfAid`
|
|
|
290
290
|
| 10 | rules | 目录 `$KITS_RULES` | always | ✗ |
|
|
291
291
|
| 20 | identity-layer | fragment | chatType≠null | ✓ |
|
|
292
292
|
| 21 | persona | `$PERSONAL_DIR/persona.md` | chatType≠null | ✗ |
|
|
293
|
+
| 21.5 | personal-profile | `$PERSONAL_DIR/personal.md` | chatType≠null | ✗ |
|
|
293
294
|
| 22 | working-memory | `$PERSONAL_DIR/memory/working.md` | chatType≠null | ✗ |
|
|
294
295
|
| 30 | relation-layer | fragment | chatType∈{private,group} | ✓ |
|
|
295
296
|
| 35 | peer-profile | `$RELATIONS_DIR/{{peerKey}}/profile.md` | peerKey≠null | ✗ |
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
| `ec logs` | 查看日志(`--level` `--module` `--raw` `--no-color`) | 日志/查看日志 |
|
|
25
25
|
| `ec diagnose` | 诊断启动环境 | 诊断/环境诊断 |
|
|
26
26
|
|
|
27
|
+
Windows 上 Codex 托管任务出现 `helper_sandbox_lock_failed`、`SetNamedSecurityInfoW ... 5`、`unelevated ... cannot enforce deny-read` 或 daemon 管道 `EPERM` 时,参见 `codex-windows-sandbox.md`。
|
|
28
|
+
|
|
27
29
|
## 对外通信 / 网络类(连 AUN,以 AID 为发送者)
|
|
28
30
|
|
|
29
31
|
| 命令集 | 用途 | 触发词 | 适用场景 | 文档 |
|
|
@@ -37,7 +39,7 @@
|
|
|
37
39
|
| `ec agent` | EvolAgent 生命周期 | 创建/启停/热重载/改配置 | 管理员(owner/admin) | `agent.md`;完整创建与 Bootstrap 见 `agent-create.md` |
|
|
38
40
|
| `ec contact` | Contact Book 与访问控制 | 申请添加、拉黑/解除拉黑、查拉黑 | Agent 当前私聊可申请添加;管理写操作按角色限制 | `contact.md` |
|
|
39
41
|
| `ec rpc` | 底层 AUN RPC(逃生通道) | 直接调协议方法 | 高级/兜底 | `rpc.md` |
|
|
40
|
-
| `ec menu-client` | 远程 Daemon Menu 请求 | 远程诊断/daemon 日志/监控/原始配置 |
|
|
42
|
+
| `ec menu-client` | 远程 Daemon Menu 请求 | 远程诊断/daemon 日志/监控/原始配置 | 运维主机或具备 `ec.menu.request` 权限的托管 Agent | `menu-client.md` |
|
|
41
43
|
|
|
42
44
|
## 会话自管理类(不连 AUN,操作当前会话)
|
|
43
45
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# 创建 EvolAgent:从创建到 Bootstrap 完成
|
|
2
2
|
|
|
3
|
-
本手册用于创建一个能正常工作的 EvolAgent
|
|
3
|
+
本手册用于创建一个能正常工作的 EvolAgent。创建可以由人类直接发起,也可以由已有 Agent 代办:已有 Agent 负责收集信息、创建配置并观察状态;新 Agent 仍在自己的 Bootstrap 会话中修改名片并执行 `ready`。不要把创建流水线完成误认为 Agent 已激活。
|
|
4
4
|
|
|
5
5
|
### AID 与 Owner 约定
|
|
6
6
|
|
|
7
7
|
- AID 必须是 `{name}.{issuer}`,例如 `helper.agentid.cn`;不要使用四级域名(如 `helper.team.agentid.cn`),也不要把显示名当作 AID。
|
|
8
|
-
- `--owner`
|
|
8
|
+
- `--owner` 保留现有单个 Owner 参数。
|
|
9
|
+
- `--owners` 是有序 Owner 数组;Owner AID 必须使用完整 AID。代办创建时,当前创建 Agent 必须放在第一位,再把它自己的 Owner 放在后面;第一位 Owner 接收新 Agent 的 Bootstrap 首条消息。
|
|
10
|
+
- 通过托管委托创建时,创建者必须显式把原始需求 Owner 写入 `--owners`;若需要创建 Agent 继续代办 Bootstrap,应将创建 Agent 放在 `owners[0]`,原始需求 Owner 放在后续位置。代码不会自动补齐 Owner。
|
|
11
|
+
- `--owner` 与 `--owners` 互斥;未指定 `--owners` 时,现有 `--owner` 行为保持不变。
|
|
12
|
+
- `--handoff <上下文>` 只附着在当前创建 Agent 的任务上,用于把已收集的 Bootstrap 信息注入该 Agent 后续处理新 Agent Bootstrap 首条消息的上下文。它不是发给新 Agent 的消息,也不要求新 Agent 返回或绑定任何 handoff。
|
|
9
13
|
|
|
10
14
|
## 生命周期与完成标准
|
|
11
15
|
|
|
@@ -25,23 +29,31 @@ active
|
|
|
25
29
|
|
|
26
30
|
多个 Agent 必须串行创建和观察。不要并行启动多个 `ec agent new`:证书、热加载和 Bootstrap 会竞争连接与资源,容易造成超时、锁等待或把失败误判为未创建。
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
在人类直接创建时,可以使用交互模式:
|
|
29
33
|
|
|
30
34
|
```bash
|
|
31
35
|
ec agent new helper.agentid.pub
|
|
32
36
|
```
|
|
33
37
|
|
|
34
|
-
在 EvolCore 托管 Agent
|
|
38
|
+
在 EvolCore 托管 Agent 会话等自动化环境中,必须使用非交互模式。代办创建时,先按照 Bootstrap 要求向自己的 Owner 收集显示名 `name`、一到两句话的 `description`、3 到 6 个非空 `tags`,再把结果放入 `--handoff`:
|
|
35
39
|
|
|
36
40
|
```bash
|
|
37
41
|
ec agent new helper.agentid.pub --non-interactive \
|
|
38
42
|
--project /home/user/helper-project \
|
|
39
43
|
--baseagent codex \
|
|
40
|
-
--
|
|
44
|
+
--owners '["helper-creator.agentid.pub","owner.agentid.pub"]' \
|
|
41
45
|
--name "Helper" \
|
|
42
|
-
--description "协助处理项目开发任务"
|
|
46
|
+
--description "协助处理项目开发任务" \
|
|
47
|
+
--tags '["coding","diagnostics","logs"]' \
|
|
48
|
+
--handoff '供当前创建 Agent 使用的 Bootstrap 上下文:已向 Owner 确认 name=Helper;description=协助处理项目开发任务;tags=coding, maintenance, typescript。目标 Agent 不会看到此上下文;创建 Agent 收到其 Bootstrap 首条消息时,以这些信息作为 Owner 回复并完成验收。'
|
|
43
49
|
```
|
|
44
50
|
|
|
51
|
+
Bootstrap 完成前,Owner 可选地提供 persona 内容:直接发送文本,或发送 `.md`/`.txt` 文件。新 Agent 在确认后使用 `ec agent persona --text '<内容>'` 或 `ec agent persona --file '<路径>'` 写入自己的 `$PERSONAL_DIR/persona.md`;未提供 persona 不影响 `ready`。创建者不能借此替代 Bootstrap 中对 `agent.md` 的 Owner 确认。
|
|
52
|
+
|
|
53
|
+
如果新 Agent 向 Agent/AI Owner 发送 Bootstrap 消息时收到“不是联系人”或联系人准入拒绝,应只发送一次 `/request <申请说明>` 申请添加联系人,说明这是新 Agent 的 Bootstrap 首次设定,并等待 Owner 审核;不要反复重试、修改全局联系人策略或联系其它对象。Owner 直接向新 Agent 发消息时,按该入站消息继续 Bootstrap。
|
|
54
|
+
|
|
55
|
+
`--owners` 只用于需要多个 Owner 的创建;普通多 Owner 创建可按实际责任关系排列顺序。使用 `--handoff` 代办创建时,当前创建 Agent 必须是 `owners[0]`。`--handoff` 的内容只在创建 Agent 的当前任务中保存和注入,目标 Agent 不会看到该参数。
|
|
56
|
+
|
|
45
57
|
建议先用 dry-run 检查最终参数:
|
|
46
58
|
|
|
47
59
|
```bash
|
|
@@ -61,6 +73,8 @@ ec agent get helper.agentid.pub lifecycle
|
|
|
61
73
|
ec agent show helper.agentid.pub
|
|
62
74
|
```
|
|
63
75
|
|
|
76
|
+
创建成功后,若 Owner 是本机托管 Agent,创建流程会把新 Agent AID 自动写入该 Owner 的联系人簿;远程或人类 Owner 没有本机联系人簿时跳过。创建者仍应核对首位 Owner 的联系人状态,确保 Bootstrap 回复可达。
|
|
77
|
+
|
|
64
78
|
如果是 menu 创建,`accepted=true` 只表示请求已受理。继续轮询 `createProgress.status` 直到 `ready` 或 `failed`。这里的 `createProgress.status=ready` **只表示后台创建流水线结束,不等于 `lifecycle=active`**。
|
|
65
79
|
|
|
66
80
|
创建者应维护一个最小状态账本(可放在当前任务记录中),每个 Agent 至少记录:`aid`、需求 Owner、项目路径、创建请求是否受理、`createProgress.status`、`lifecycle`、本地 `agent.md` 是否存在/字段是否完整、名片是否已发布、最近错误、下一步和最后检查时间。重启或超时恢复时先按账本逐项核对实际状态,再决定续做,不按“上次命令是否返回”推断结果。
|
|
@@ -75,13 +89,13 @@ ec agent show helper.agentid.pub
|
|
|
75
89
|
|
|
76
90
|
## 3. Owner 与新 Agent 确认名片信息
|
|
77
91
|
|
|
78
|
-
以下步骤发生在**新 Agent 自己的 Bootstrap
|
|
92
|
+
以下步骤发生在**新 Agent 自己的 Bootstrap 会话**中,不由创建者代替执行。若第一位 Owner 是代办创建的 Agent,它可以使用当前任务中注入的 `--handoff` 上下文代替人类回复;这不改变 Bootstrap 的确认规则。
|
|
79
93
|
|
|
80
94
|
新 Agent 先读取系统注入路径指向的本地 `agent.md`,把其中格式有效的字段作为现有候选值:
|
|
81
95
|
|
|
82
96
|
- `name`:简短清晰的显示名
|
|
83
97
|
- `description`:一句到两句话描述职责或定位
|
|
84
|
-
- `tags`:3 到 6
|
|
98
|
+
- `tags`:3 到 6 个非空标签;每个标签只允许 Unicode 字母、数字和连字符(`-`),最长 50 字符
|
|
85
99
|
|
|
86
100
|
交互规则:
|
|
87
101
|
|
|
@@ -131,8 +145,8 @@ ec agent ready helper.agentid.pub
|
|
|
131
145
|
|
|
132
146
|
1. 将 `lifecycle` 切换为 `active`。
|
|
133
147
|
2. 结束当前 Bootstrap 上下文。
|
|
134
|
-
3. 创建全新的主会话。
|
|
135
|
-
4.
|
|
148
|
+
3. 为每位 Owner 创建全新的主会话。
|
|
149
|
+
4. 向全部 Owner 发送正式欢迎消息,并为各 Owner 绑定对应的新会话。
|
|
136
150
|
|
|
137
151
|
新 Agent 在 `ready` 成功后不要从旧 Bootstrap 上下文再输出一段完成说明,也不要继续处理先前夹带的普通任务;后续工作从新主会话开始。
|
|
138
152
|
|
|
@@ -147,10 +161,15 @@ ec agent show helper.agentid.pub
|
|
|
147
161
|
|
|
148
162
|
验证其它 Agent 的生命周期、配置和创建进度统一使用 `ec agent get/show/list`;`ec aid` 只用于身份、证书和名片操作,不要用 `ec aid` 代替 Agent 状态验证。
|
|
149
163
|
|
|
164
|
+
Bootstrap 首条消息只发送给 `owners[0]`;`ready` 成功后,正式欢迎消息发送给配置中的全部 Owner。
|
|
165
|
+
|
|
166
|
+
使用 `--handoff` 代办创建时,创建 Agent 在与新 Agent 的会话中完成验收后,执行 `ec handoff return --content '<验收结果>'`,将结果回流至自己与原需求 Owner 的会话;该步骤不由新 Agent 执行。
|
|
167
|
+
|
|
150
168
|
验收标准:
|
|
151
169
|
|
|
152
|
-
- `lifecycle` 为 `active`。
|
|
170
|
+
- `lifecycle` 为 `active`;`ec agent show` 的 `lifecycle` 字段也必须为 `active`。
|
|
153
171
|
- `agent.md` 已成功签名发布。
|
|
172
|
+
- 本地与远端 `agent.md` 的 `name`、`description`、`tags` 与 Owner 确认值一致;创建流程 skill 必须在发布后复读核验,不以初始上传响应代替。
|
|
154
173
|
- 系统已创建新的主会话并发送正式欢迎消息。
|
|
155
174
|
- 新 Agent 能在新主会话中处理普通任务。
|
|
156
175
|
|