evolcore 0.0.11 → 0.0.13
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 +44 -0
- package/bin/codex-managed-hook.mjs +80 -0
- package/bin/ec-safe-output.js +26 -7
- package/dist/agents/claude-runner.js +86 -7
- package/dist/agents/codex-app-server-client.js +174 -20
- package/dist/agents/codex-runner.js +375 -92
- package/dist/agents/ecagent-runner.js +39 -4
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/control-aid.js +36 -13
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/aid/store.js +13 -7
- package/dist/aun/group-fs-download.js +56 -0
- package/dist/aun/group-identity.js +1 -2
- package/dist/aun/msg/group.js +91 -44
- package/dist/aun/msg/index.js +1 -2
- package/dist/aun/msg/managed-operation.js +865 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/msg/upload.js +16 -1
- package/dist/aun/outbox.js +31 -2
- package/dist/aun/rpc/client.js +66 -0
- package/dist/aun/rpc/index.js +1 -2
- package/dist/aun/storage/{upload.js → client.js} +11 -2
- package/dist/aun/storage/index.js +1 -2
- package/dist/channels/aun.js +655 -240
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +56 -8
- package/dist/cli/agent.js +108 -31
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/cli-argv.js +10 -0
- package/dist/cli/config.js +9 -17
- package/dist/cli/ctl-command.js +1 -1
- package/dist/cli/daemon-commands.js +70 -55
- package/dist/cli/fs-command.js +79 -10
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +30 -2
- package/dist/cli/init.js +77 -8
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +53 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +7 -32
- package/dist/cli/trigger-command.js +11 -0
- package/dist/config/access-policy.js +1 -2
- package/dist/config/builtin-roles.js +9 -0
- package/dist/config/config-manager.js +21 -1
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/mention-mode.js +8 -24
- package/dist/config/peer-role-resolver.js +6 -2
- package/dist/config/role-migration-startup.js +53 -0
- package/dist/config/role-store.js +1 -1
- package/dist/core/auth/agent-delegation.js +1 -2
- package/dist/core/auth/auth-gateway.js +29 -1
- package/dist/core/auth/authorization-audit.js +15 -0
- package/dist/core/auth/operation-authorizer.js +31 -7
- package/dist/core/auth/operation-catalog.js +196 -25
- package/dist/core/bootstrap-messages.js +25 -0
- package/dist/core/bootstrap-service.js +64 -4
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +1 -1
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +650 -42
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +370 -0
- package/dist/core/command/menu-handler.js +230 -51
- package/dist/core/command/menu-protocol.js +94 -2
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +99 -31
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/event-catalog.js +6 -0
- package/dist/core/evolagent.js +4 -2
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +157 -22
- package/dist/core/handoff/store.js +29 -1
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/interaction-router.js +10 -0
- package/dist/core/message/im-renderer.js +19 -0
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +298 -88
- package/dist/core/message/message-log.js +3 -0
- package/dist/core/message/message-queue.js +268 -39
- package/dist/core/message/message-utils.js +81 -4
- package/dist/core/message/response-engine.js +161 -77
- package/dist/core/message/stream-debouncer.js +10 -2
- package/dist/core/permission/approval-gateway.js +1 -0
- package/dist/core/permission/ec-command-parser.js +243 -18
- package/dist/core/permission/index.js +1 -1
- package/dist/core/permission/mode.js +15 -0
- package/dist/core/permission/readonly-shell-query.js +148 -8
- package/dist/core/permission/sandbox-runtime.js +43 -1
- package/dist/core/permission/tool-policy.js +322 -57
- package/dist/core/protected-paths.js +114 -4
- package/dist/core/session/session-manager.js +42 -4
- package/dist/core/session/session-mapper.js +20 -1
- package/dist/eck/detect.js +18 -1
- package/dist/eck/group-rules-sync.js +15 -44
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +541 -180
- package/dist/ipc.js +232 -10
- package/dist/paths.js +3 -0
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/feedback.js +64 -11
- package/dist/trigger/manager.js +21 -1
- package/dist/trigger/parser.js +14 -2
- package/dist/trigger/scheduler.js +1 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/trigger/validation.js +67 -6
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/ecweb-supervisor.js +332 -0
- package/dist/utils/error-utils.js +10 -0
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/evolcore/trigger.md +17 -1
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/migrations/migrate-contact-book-v2.mjs +1 -2
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/role-config.schema.1.json +1 -0
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +27 -0
- package/kits/templates/roles/member.json +34 -1
- package/kits/templates/roles/visitor.json +35 -3
- package/kits/templates/system-fragments/bootstrap.md +1 -1
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +2 -2
- package/dist/aun/msg/payload-type.js +0 -27
- package/dist/aun/rpc/caller.js +0 -42
- package/dist/aun/rpc/connection.js +0 -25
- package/dist/aun/storage/manage.js +0 -10
- package/dist/config/access-policy-domain.js +0 -18
- package/dist/config/config-batch-get.js +0 -11
- package/dist/config/owner-policy.js +0 -4
- package/dist/config/role-config-v4-startup.js +0 -32
- package/dist/config/role-config-v5-startup.js +0 -27
- package/dist/core/auth/trigger-authorization.js +0 -15
- package/dist/core/command/evol-menu-version-gate.js +0 -39
- package/dist/core/interaction-registration.js +0 -10
- package/dist/core/permission/execution-sandbox.js +0 -16
- package/dist/core/relation/peer-key.js +0 -1
- package/dist/core/session/session-key.js +0 -24
- package/dist/eck/baseagent-caps.js +0 -18
- package/dist/eck/rules-loader.js +0 -28
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { resolvePaths } from '../paths.js';
|
|
5
|
+
import { WEB_PACKAGE_NAME } from '../product.js';
|
|
6
|
+
import { resolveGlobalPkg } from './npm-ops.js';
|
|
7
|
+
import { ecwebVersionRequirementError, resolveEcwebLaunchCommand } from './ecweb-utils.js';
|
|
8
|
+
import * as platform from './cross-platform.js';
|
|
9
|
+
import { getProcessStartTime } from './process-introspect.js';
|
|
10
|
+
const INSTANCE_FILE_RE = /^(?:ecweb|watch-web)-\d+\.json$/;
|
|
11
|
+
function sleep(ms) {
|
|
12
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
13
|
+
}
|
|
14
|
+
function isAlive(record) {
|
|
15
|
+
if (!Number.isInteger(record.pid) || record.pid <= 0)
|
|
16
|
+
return false;
|
|
17
|
+
// ECWeb versions before the supervisor used a different Linux boot-time
|
|
18
|
+
// calculation, so their `startedAt` can be off by seconds or minutes even
|
|
19
|
+
// while the PID and HTTP listener are healthy. The instance file is already
|
|
20
|
+
// scoped to this EVOLCORE_HOME; use liveness here and let the HTTP probe be
|
|
21
|
+
// the ownership/health check. PID/start-time validation remains in the
|
|
22
|
+
// explicit kill path below.
|
|
23
|
+
return platform.isProcessRunning(record.pid);
|
|
24
|
+
}
|
|
25
|
+
function readEntries() {
|
|
26
|
+
const dir = resolvePaths().instanceDir;
|
|
27
|
+
let files;
|
|
28
|
+
try {
|
|
29
|
+
files = fs.readdirSync(dir);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const entries = [];
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
if (!INSTANCE_FILE_RE.test(file))
|
|
37
|
+
continue;
|
|
38
|
+
const filePath = path.join(dir, file);
|
|
39
|
+
try {
|
|
40
|
+
const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
41
|
+
if (Number.isInteger(value.pid) && value.pid > 0) {
|
|
42
|
+
entries.push({
|
|
43
|
+
filePath,
|
|
44
|
+
record: {
|
|
45
|
+
pid: value.pid,
|
|
46
|
+
port: Number.isInteger(value.port) ? value.port : 42705,
|
|
47
|
+
...(typeof value.startedAt === 'number' ? { startedAt: value.startedAt } : {}),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// A partially-written instance file is ignored until the next check.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return entries;
|
|
57
|
+
}
|
|
58
|
+
function removeEntry(entry) {
|
|
59
|
+
try {
|
|
60
|
+
fs.unlinkSync(entry.filePath);
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
}
|
|
64
|
+
function removeEntriesForPid(pid) {
|
|
65
|
+
for (const entry of readEntries()) {
|
|
66
|
+
if (entry.record.pid === pid)
|
|
67
|
+
removeEntry(entry);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function probe(port) {
|
|
71
|
+
try {
|
|
72
|
+
const response = await fetch(`http://127.0.0.1:${port}/`, {
|
|
73
|
+
signal: AbortSignal.timeout(1500),
|
|
74
|
+
});
|
|
75
|
+
return response.ok;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Owns the ECWeb process for the lifetime of the daemon.
|
|
83
|
+
*
|
|
84
|
+
* The CLI may have started ECWeb moments earlier (ec start/restart/watch web).
|
|
85
|
+
* We first adopt a healthy registered instance and only spawn after a few
|
|
86
|
+
* seconds of failed discovery. This closes the race between the CLI launcher,
|
|
87
|
+
* ECWeb's own stale-instance cleanup, and daemon startup.
|
|
88
|
+
*/
|
|
89
|
+
export function createEcwebSupervisor(options) {
|
|
90
|
+
const failureThreshold = Math.max(1, options.failureThreshold ?? 3);
|
|
91
|
+
const checkIntervalMs = Math.max(1000, options.checkIntervalMs ?? 5000);
|
|
92
|
+
let active = null;
|
|
93
|
+
let child = null;
|
|
94
|
+
let logFd = null;
|
|
95
|
+
let timer = null;
|
|
96
|
+
let failureCount = 0;
|
|
97
|
+
let startInFlight = null;
|
|
98
|
+
let stopping = false;
|
|
99
|
+
const logFile = path.join(resolvePaths().logs, 'ecweb-daemon.log');
|
|
100
|
+
const setActiveFromEntry = (entry, owned) => {
|
|
101
|
+
active = {
|
|
102
|
+
pid: entry.record.pid,
|
|
103
|
+
port: entry.record.port,
|
|
104
|
+
// Capture the current PID start time rather than trusting the record.
|
|
105
|
+
// Older ECWeb builds could write a clock-skewed `startedAt` value.
|
|
106
|
+
startedAt: getProcessStartTime(entry.record.pid)
|
|
107
|
+
?? (typeof entry.record.startedAt === 'number' ? entry.record.startedAt : null),
|
|
108
|
+
owned,
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const findHealthyInstance = async () => {
|
|
112
|
+
for (const entry of readEntries()) {
|
|
113
|
+
if (!isAlive(entry.record)) {
|
|
114
|
+
removeEntry(entry);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (await probe(entry.record.port))
|
|
118
|
+
return entry;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
};
|
|
122
|
+
const waitForHealthyInstance = async (timeoutMs) => {
|
|
123
|
+
const deadline = Date.now() + timeoutMs;
|
|
124
|
+
do {
|
|
125
|
+
const found = await findHealthyInstance();
|
|
126
|
+
if (found)
|
|
127
|
+
return found;
|
|
128
|
+
if (Date.now() >= deadline)
|
|
129
|
+
break;
|
|
130
|
+
await sleep(250);
|
|
131
|
+
} while (!stopping);
|
|
132
|
+
return null;
|
|
133
|
+
};
|
|
134
|
+
const closeLogStream = () => {
|
|
135
|
+
if (logFd === null)
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
fs.closeSync(logFd);
|
|
139
|
+
}
|
|
140
|
+
catch { }
|
|
141
|
+
logFd = null;
|
|
142
|
+
};
|
|
143
|
+
const killActive = async () => {
|
|
144
|
+
const current = active;
|
|
145
|
+
if (!current) {
|
|
146
|
+
// During the short window between spawn() and ECWeb writing its
|
|
147
|
+
// instance record there is no `active` entry yet, but the daemon still
|
|
148
|
+
// owns the child and must not leave it behind on shutdown.
|
|
149
|
+
const pendingPid = child?.pid;
|
|
150
|
+
if (pendingPid && platform.isProcessRunning(pendingPid)) {
|
|
151
|
+
try {
|
|
152
|
+
platform.killProcess(pendingPid, true);
|
|
153
|
+
}
|
|
154
|
+
catch { }
|
|
155
|
+
await platform.waitForProcessExit(pendingPid, 5000);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const actual = getProcessStartTime(current.pid);
|
|
160
|
+
// Never kill a reused PID. `startedAt` was captured from the live process
|
|
161
|
+
// when it was adopted/spawned, so this remains safe even for legacy records
|
|
162
|
+
// whose on-disk timestamp used a different clock.
|
|
163
|
+
const sameProcess = current.startedAt !== null && actual !== null
|
|
164
|
+
? current.startedAt === actual
|
|
165
|
+
: false;
|
|
166
|
+
if (sameProcess && platform.isProcessRunning(current.pid)) {
|
|
167
|
+
try {
|
|
168
|
+
platform.killProcess(current.pid, true);
|
|
169
|
+
}
|
|
170
|
+
catch { }
|
|
171
|
+
await platform.waitForProcessExit(current.pid, 5000);
|
|
172
|
+
}
|
|
173
|
+
removeEntriesForPid(current.pid);
|
|
174
|
+
active = null;
|
|
175
|
+
};
|
|
176
|
+
const spawnEcweb = async () => {
|
|
177
|
+
const root = resolvePaths().root;
|
|
178
|
+
const localEntry = path.join(root, 'ecweb', 'dist', 'index.js');
|
|
179
|
+
const globalPkg = resolveGlobalPkg(WEB_PACKAGE_NAME);
|
|
180
|
+
let launch = resolveEcwebLaunchCommand(['--home', root, '--port', String(options.port)], {
|
|
181
|
+
installedPkg: globalPkg,
|
|
182
|
+
});
|
|
183
|
+
let cwd = root;
|
|
184
|
+
let version = globalPkg?.version;
|
|
185
|
+
if (!launch && fs.existsSync(localEntry)) {
|
|
186
|
+
launch = {
|
|
187
|
+
command: process.execPath,
|
|
188
|
+
args: [localEntry, '--home', root, '--port', String(options.port)],
|
|
189
|
+
entry: localEntry,
|
|
190
|
+
source: 'package-bin',
|
|
191
|
+
};
|
|
192
|
+
cwd = path.dirname(path.dirname(localEntry));
|
|
193
|
+
try {
|
|
194
|
+
version = JSON.parse(fs.readFileSync(path.join(root, 'ecweb', 'package.json'), 'utf8')).version;
|
|
195
|
+
}
|
|
196
|
+
catch { }
|
|
197
|
+
}
|
|
198
|
+
if (!launch) {
|
|
199
|
+
options.logger.warn(`ECWeb 启动入口不存在,请安装 ${WEB_PACKAGE_NAME}@latest`);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const versionError = ecwebVersionRequirementError(version);
|
|
203
|
+
if (versionError) {
|
|
204
|
+
options.logger.error(`ECWeb 未启动: ${versionError}`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
fs.mkdirSync(resolvePaths().logs, { recursive: true });
|
|
208
|
+
fs.appendFileSync(logFile, `\n# daemon supervisor start ${new Date().toISOString()}\n`);
|
|
209
|
+
// `spawn()` accepts inherited file descriptors, but not a WriteStream
|
|
210
|
+
// whose fd has not been opened yet. Open once and pass the numeric fd to
|
|
211
|
+
// both stdout and stderr; the parent closes its copy after spawning.
|
|
212
|
+
logFd = fs.openSync(logFile, 'a');
|
|
213
|
+
const childLogFd = logFd;
|
|
214
|
+
let spawned;
|
|
215
|
+
try {
|
|
216
|
+
spawned = spawn(launch.command, launch.args, {
|
|
217
|
+
cwd,
|
|
218
|
+
detached: true,
|
|
219
|
+
stdio: ['ignore', childLogFd, childLogFd],
|
|
220
|
+
windowsHide: platform.isWindows,
|
|
221
|
+
env: { ...process.env, EVOLCORE_HOME: root },
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
closeLogStream();
|
|
226
|
+
options.logger.error(`ECWeb 子进程创建失败: ${error?.message || error}`);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
closeLogStream();
|
|
230
|
+
child = spawned;
|
|
231
|
+
spawned.once('error', (error) => {
|
|
232
|
+
options.logger.error(`ECWeb 子进程创建失败: ${error.message}`);
|
|
233
|
+
});
|
|
234
|
+
spawned.once('exit', (code, signal) => {
|
|
235
|
+
options.logger.warn(`ECWeb 进程退出 (PID: ${spawned.pid ?? 'unknown'}, code=${code ?? 'null'}, signal=${signal ?? 'null'})`);
|
|
236
|
+
if (child === spawned)
|
|
237
|
+
child = null;
|
|
238
|
+
if (!stopping) {
|
|
239
|
+
active = null;
|
|
240
|
+
failureCount = failureThreshold;
|
|
241
|
+
}
|
|
242
|
+
closeLogStream();
|
|
243
|
+
});
|
|
244
|
+
spawned.unref();
|
|
245
|
+
if (!spawned.pid) {
|
|
246
|
+
closeLogStream();
|
|
247
|
+
options.logger.warn('ECWeb 启动失败:未取得子进程 PID');
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
options.logger.info(`✓ ECWeb supervisor 已启动 (PID: ${spawned.pid}),日志: ${logFile}`);
|
|
251
|
+
// ECWeb writes its own instance record after binding. Do not fabricate a
|
|
252
|
+
// record here; the watchdog will adopt the real record on its next tick.
|
|
253
|
+
// Returning immediately keeps daemon startup independent of HTTP bind
|
|
254
|
+
// latency while ServiceProxy continues to wait for the instance file.
|
|
255
|
+
};
|
|
256
|
+
const ensureRunning = async () => {
|
|
257
|
+
if (stopping || startInFlight)
|
|
258
|
+
return;
|
|
259
|
+
const healthy = await findHealthyInstance();
|
|
260
|
+
if (healthy) {
|
|
261
|
+
const changed = !active || active.pid !== healthy.record.pid || active.port !== healthy.record.port;
|
|
262
|
+
setActiveFromEntry(healthy, active?.pid === healthy.record.pid ? active.owned : false);
|
|
263
|
+
failureCount = 0;
|
|
264
|
+
if (changed)
|
|
265
|
+
options.logger.info(`✓ ECWeb supervisor 接管现有实例 (PID: ${healthy.record.pid}, 端口: ${healthy.record.port})`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
failureCount += 1;
|
|
269
|
+
if (failureCount < failureThreshold)
|
|
270
|
+
return;
|
|
271
|
+
// A manual `ec watch web` may be between killing the old process and
|
|
272
|
+
// registering the replacement. Re-scan immediately before spawning.
|
|
273
|
+
const replacement = await waitForHealthyInstance(1500);
|
|
274
|
+
if (replacement) {
|
|
275
|
+
setActiveFromEntry(replacement, false);
|
|
276
|
+
failureCount = 0;
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
startInFlight = (async () => {
|
|
280
|
+
await killActive();
|
|
281
|
+
await spawnEcweb();
|
|
282
|
+
failureCount = 0;
|
|
283
|
+
})().finally(() => { startInFlight = null; });
|
|
284
|
+
await startInFlight;
|
|
285
|
+
};
|
|
286
|
+
return {
|
|
287
|
+
async start() {
|
|
288
|
+
if (stopping)
|
|
289
|
+
return;
|
|
290
|
+
// CLI/restart-monitor writes the instance record before the daemon is
|
|
291
|
+
// ready. Give that process enough time to bind before launching another.
|
|
292
|
+
const existing = await waitForHealthyInstance(1_500);
|
|
293
|
+
if (existing) {
|
|
294
|
+
setActiveFromEntry(existing, false);
|
|
295
|
+
options.logger.info(`✓ ECWeb supervisor 接管现有实例 (PID: ${existing.record.pid}, 端口: ${existing.record.port})`);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
failureCount = failureThreshold;
|
|
299
|
+
await ensureRunning();
|
|
300
|
+
}
|
|
301
|
+
timer = setInterval(() => {
|
|
302
|
+
void ensureRunning().catch((error) => {
|
|
303
|
+
options.logger.error(`ECWeb watchdog 检查失败: ${error?.message || error}`);
|
|
304
|
+
});
|
|
305
|
+
}, checkIntervalMs);
|
|
306
|
+
timer.unref?.();
|
|
307
|
+
},
|
|
308
|
+
async stop() {
|
|
309
|
+
if (stopping)
|
|
310
|
+
return;
|
|
311
|
+
stopping = true;
|
|
312
|
+
if (timer)
|
|
313
|
+
clearInterval(timer);
|
|
314
|
+
timer = null;
|
|
315
|
+
if (startInFlight) {
|
|
316
|
+
try {
|
|
317
|
+
await startInFlight;
|
|
318
|
+
}
|
|
319
|
+
catch { }
|
|
320
|
+
}
|
|
321
|
+
await killActive();
|
|
322
|
+
if (child) {
|
|
323
|
+
try {
|
|
324
|
+
child.removeAllListeners();
|
|
325
|
+
}
|
|
326
|
+
catch { }
|
|
327
|
+
child = null;
|
|
328
|
+
}
|
|
329
|
+
closeLogStream();
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { getPackageRoot, resolvePaths } from '../paths.js';
|
|
4
4
|
import { logger } from './logger.js';
|
|
5
|
+
import { isSandboxInitializationFailure } from '../core/permission/sandbox-runtime.js';
|
|
5
6
|
export var ErrorType;
|
|
6
7
|
(function (ErrorType) {
|
|
7
8
|
ErrorType["SDK_TIMEOUT"] = "sdk_timeout";
|
|
@@ -11,6 +12,7 @@ export var ErrorType;
|
|
|
11
12
|
ErrorType["STREAM_ERROR"] = "stream_error";
|
|
12
13
|
ErrorType["CONTEXT_TOO_LONG"] = "context_too_long";
|
|
13
14
|
ErrorType["MODEL_UNAVAILABLE"] = "model_unavailable";
|
|
15
|
+
ErrorType["SANDBOX_INITIALIZATION_FAILED"] = "sandbox_initialization_failed";
|
|
14
16
|
ErrorType["UNKNOWN"] = "unknown";
|
|
15
17
|
})(ErrorType || (ErrorType = {}));
|
|
16
18
|
/**
|
|
@@ -269,6 +271,9 @@ export function classifyError(error) {
|
|
|
269
271
|
|| /api error: 404\b/.test(msg)) {
|
|
270
272
|
return ErrorType.MODEL_UNAVAILABLE;
|
|
271
273
|
}
|
|
274
|
+
if (isSandboxInitializationFailure(error)) {
|
|
275
|
+
return ErrorType.SANDBOX_INITIALIZATION_FAILED;
|
|
276
|
+
}
|
|
272
277
|
if (msg.includes('401') || msg.includes('authentication_error') || msg.includes('unauthorized')) {
|
|
273
278
|
return ErrorType.AUTH_ERROR;
|
|
274
279
|
}
|
|
@@ -328,6 +333,8 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
328
333
|
return `${prefix}工具执行失败,请检查操作或重试`;
|
|
329
334
|
case 'permission_denied':
|
|
330
335
|
return `${prefix}权限被拒绝,操作已取消`;
|
|
336
|
+
case 'sandbox_initialization_failed':
|
|
337
|
+
return `${warnPrefix}只读执行沙箱启动失败(sandbox_initialization_failed),当前任务未执行;请检查 user namespace/seccomp 配置后重试`;
|
|
331
338
|
case 'aborted_streaming':
|
|
332
339
|
case 'aborted_tools':
|
|
333
340
|
return `${prefix}任务已中断`;
|
|
@@ -345,6 +352,9 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
345
352
|
if (msg.includes('CONTEXT_COMPACT_FAILED') || isContextTooLongText(msg)) {
|
|
346
353
|
return `${warnPrefix}上下文过长,自动压缩失败,请手动输入 /compact 重试`;
|
|
347
354
|
}
|
|
355
|
+
if (isSandboxInitializationFailure(error)) {
|
|
356
|
+
return `${warnPrefix}只读执行沙箱启动失败(sandbox_initialization_failed),当前任务未执行;请检查 user namespace/seccomp 配置后重试`;
|
|
357
|
+
}
|
|
348
358
|
if (msg.includes('401') || msg.includes('authentication_error') || msg.toLowerCase().includes('unauthorized')) {
|
|
349
359
|
return `${errPrefix}API Key 无效,请检查密钥配置。使用 /status 查看当前配置`;
|
|
350
360
|
}
|
package/dist/utils/npm-ops.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function npmInstallGlobal(pkg) {
|
|
|
20
20
|
// Windows: run via cmd /c to avoid shell:true deprecation warning on Node 22.
|
|
21
21
|
// Unix: npm directly, no shell needed.
|
|
22
22
|
if (isWindows) {
|
|
23
|
-
cmd = 'cmd';
|
|
23
|
+
cmd = process.env.ComSpec || path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe');
|
|
24
24
|
args = ['/c', 'npm', 'install', '-g', pkg];
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
@@ -28,14 +28,14 @@ export async function npmInstallGlobal(pkg) {
|
|
|
28
28
|
args = ['install', '-g', pkg];
|
|
29
29
|
}
|
|
30
30
|
try {
|
|
31
|
-
await execFileAsync(cmd, args, { timeout: 180000 });
|
|
31
|
+
await execFileAsync(cmd, args, { timeout: 180000, windowsHide: isWindows });
|
|
32
32
|
}
|
|
33
33
|
catch (e) {
|
|
34
34
|
if (e.stderr?.includes('EACCES') || e.message?.includes('EACCES')) {
|
|
35
35
|
if (isWindows) {
|
|
36
36
|
throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
|
|
37
37
|
}
|
|
38
|
-
await execFileAsync('sudo', ['npm', 'install', '-g', pkg], { timeout: 180000 });
|
|
38
|
+
await execFileAsync('sudo', ['npm', 'install', '-g', pkg], { timeout: 180000, windowsHide: false });
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
41
|
throw e;
|
|
@@ -103,7 +103,7 @@ export function resolveGlobalPkg(pkgName) {
|
|
|
103
103
|
// 避免 "args 数组 + shell:true" 组合触发 Node DeprecationWarning,且无注入风险。
|
|
104
104
|
const npmCmd = isWindows ? 'npm.cmd' : 'npm';
|
|
105
105
|
const globalRoot = execSync(`${npmCmd} root -g`, {
|
|
106
|
-
encoding: 'utf-8', timeout: 10000,
|
|
106
|
+
encoding: 'utf-8', timeout: 10000, windowsHide: true,
|
|
107
107
|
}).trim();
|
|
108
108
|
const pkgPath = path.join(globalRoot, ...pkgName.split('/'), 'package.json');
|
|
109
109
|
if (fs.existsSync(pkgPath)) {
|
|
@@ -171,7 +171,7 @@ export async function tryUpgrade() {
|
|
|
171
171
|
/**
|
|
172
172
|
* 通用全局包升级流程:检查 → 比较 → 安装(失败重试一次)。
|
|
173
173
|
* 仅在包已安装时检查升级,未安装则跳过(resolveFn 返回 null)。
|
|
174
|
-
*
|
|
174
|
+
* 用于 EC Web 等真正独立于 EvolCore 的全局包。
|
|
175
175
|
*/
|
|
176
176
|
export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
|
|
177
177
|
if (isLinkedInstall()) {
|
|
@@ -201,10 +201,3 @@ export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
|
|
|
201
201
|
}
|
|
202
202
|
return { status: 'failed', from: localVer, to: remoteVer, error: lastError };
|
|
203
203
|
}
|
|
204
|
-
/**
|
|
205
|
-
* AUN SDK 升级流程:检查 → 比较 → 安装
|
|
206
|
-
* 仅在 SDK 已安装时检查升级,未安装则跳过。
|
|
207
|
-
*/
|
|
208
|
-
export async function tryUpgradeAunSdk(resolveAunCoreSdkPkg, AUN_CORE_SDK_PKG) {
|
|
209
|
-
return tryUpgradeGlobalPkg(resolveAunCoreSdkPkg, AUN_CORE_SDK_PKG);
|
|
210
|
-
}
|
|
@@ -63,6 +63,17 @@ export function summarizeToolInput(toolName, input) {
|
|
|
63
63
|
|| input.url
|
|
64
64
|
|| '';
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Audit-safe tool summary. Keep the useful operation shape while masking
|
|
68
|
+
* credentials commonly embedded in shell commands or tool arguments.
|
|
69
|
+
*/
|
|
70
|
+
export function summarizeToolInputForAudit(toolName, input) {
|
|
71
|
+
return summarizeToolInput(toolName, input)
|
|
72
|
+
.replace(/\b([A-Z0-9_]*(?:TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL)[A-Z0-9_]*)\s*=\s*(?:"[^"]*"|'[^']*'|\S+)/gi, '$1=[REDACTED]')
|
|
73
|
+
.replace(/\b(authorization\s*:\s*Bearer\s+)[^\s"',;]+/gi, '$1[REDACTED]')
|
|
74
|
+
.replace(/\b(api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret|authorization|cookie)\s*([=:])\s*(?:"[^"]*"|'[^']*'|\S+)/gi, '$1$2[REDACTED]')
|
|
75
|
+
.replace(/\bBearer\s+[^\s,;]+/gi, 'Bearer [REDACTED]');
|
|
76
|
+
}
|
|
66
77
|
const EDIT_PREVIEW_MAX_LINES = 14;
|
|
67
78
|
const EDIT_PREVIEW_CONTEXT_LINES = 2;
|
|
68
79
|
const EDIT_PREVIEW_INLINE_GAP_LINES = 4;
|
|
@@ -52,5 +52,5 @@ AUN keystore seed 由 EvolCore 使用固定应用值管理,不再提供配置
|
|
|
52
52
|
- **E2EE 加密**:可选端到端加密,回复默认跟随对端消息加密状态(密文回密文,明文回明文);keystore seed 由 EvolCore 固定管理。
|
|
53
53
|
- **断线重连**:SDK 内置退避策略自动重连。实时连接状态用 `ec watch aid` 查看(`ec aid` 是身份/证书管理,不显示连接状态)。
|
|
54
54
|
- **群 ID 格式**:当前协议规范使用 `g-{slug}.issuer-domain` canonical 形式,输入也可接受本域简写 `g-{slug}` 或兼容形式 `g-{slug}@issuer-domain`。历史会话里可能仍出现 `group.{issuer}/{group_no}` 一类旧格式,运行时需兼容读取。
|
|
55
|
-
-
|
|
55
|
+
- **群 @ 处理策略**:`mention_mode=disabled|mention-only` 是群级配置,只决定哪些群消息进入 Agent LLM 上下文,不影响 AUN 协议层投递;被过滤消息仍应本地存档。
|
|
56
56
|
- **身份体系**:对端以 AID 标识,关系层 peerKey 形如 `aun#alice.aid.pub`。
|
|
@@ -163,6 +163,7 @@ manifest 是 `{ "$schema_version": 1, "sections": [...] }`。每个 section:
|
|
|
163
163
|
|
|
164
164
|
- 覆盖文件含 `"mode": "replace"` → **完全替换**,只用覆盖文件的 sections。
|
|
165
165
|
- 否则 **patch 合并**:以 `id` 为键,覆盖文件里同 id 的字段浅合并进基础 section(`{...base, ...override}`),新 id 追加。
|
|
166
|
+
- 旧 patch 中的 `relation-group-profile` 已废弃并会被忽略;群与私聊 profile 统一通过 `peer-profile` 配置,避免同一文件重复加载。`mode: "replace"` 不做此兼容处理。
|
|
166
167
|
|
|
167
168
|
合并后统一按 `order` 升序排序。改某段行为(如关掉某层、调顺序、换条件)优先用覆盖文件,不动基础 manifest。
|
|
168
169
|
|
|
@@ -295,7 +296,6 @@ coding 场景(无 channel/无身份)下,`chatType`、`channel`、`selfAid`
|
|
|
295
296
|
| 40 | venue-fragment | fragment | chatType≠null | ✓ |
|
|
296
297
|
| 41 | venue-chattype | `$KITS_DOCS/venues/{{chatType}}.md` | chatType≠null | ✗ |
|
|
297
298
|
| 42 | venue-channel-chattype | `$KITS_DOCS/venues/{{channel}}-{{chatType}}.md` | chatType≠null | ✗ |
|
|
298
|
-
| 43 | relation-group-profile | `$RELATIONS_DIR/{{peerKey}}/profile.md` | groupId≠null | ✗ |
|
|
299
299
|
| 44 | venue-client | `$KITS_DOCS/venues/client-{{clientType}}.md` | clientType≠null | ✗ |
|
|
300
300
|
| 50 | channel-layer | fragment | channel≠null | ✓ |
|
|
301
301
|
| 55 | commands | fragment | channel≠null | ✓ |
|
|
@@ -322,7 +322,7 @@ $KITS_DOCS/venues/ 通用环境文档(随包发布,只读
|
|
|
322
322
|
```
|
|
323
323
|
|
|
324
324
|
> **注意**:具体群/私聊实例的 profile 数据落**关系层** `$RELATIONS_DIR/<peerKey>/profile.md`,
|
|
325
|
-
>
|
|
325
|
+
> 统一由 `peer-profile` 段注入,不在 venues 目录;群不会再通过另一个 section 重复加载。
|
|
326
326
|
|
|
327
327
|
## 输出结构
|
|
328
328
|
|
|
@@ -17,9 +17,10 @@ ec agent <aid>
|
|
|
17
17
|
ec agent new [aid]
|
|
18
18
|
|
|
19
19
|
# 创建 agent(非交互式,自动化场景)
|
|
20
|
-
ec agent new <aid> --non-interactive
|
|
21
|
-
[--baseagent claude|codex|gemini]
|
|
22
|
-
[--name "<显示名>"] [--description "<text>"]
|
|
20
|
+
ec agent new <aid> --non-interactive \
|
|
21
|
+
[--project <路径>] [--baseagent claude|codex|gemini|ecagent] \
|
|
22
|
+
[--owner <aid>] [--name "<显示名>"] [--description "<text>"] \
|
|
23
|
+
[--dry-run] [--force]
|
|
23
24
|
|
|
24
25
|
# 启用 / 停用
|
|
25
26
|
ec agent enable <aid>
|
|
@@ -44,9 +45,13 @@ ec agent delete <aid> [--purge]
|
|
|
44
45
|
|
|
45
46
|
## 非交互创建必填项
|
|
46
47
|
|
|
47
|
-
`ec agent new <aid> --non-interactive`
|
|
48
|
-
-
|
|
49
|
-
-
|
|
48
|
+
`ec agent new <aid> --non-interactive` 时,只有 `<aid>` 必填:
|
|
49
|
+
- `--project` 缺省为当前目录,并自动转为绝对路径。
|
|
50
|
+
- `--baseagent` 优先使用 `agents/defaults.json`,否则检测本机可用基座。
|
|
51
|
+
- `--owner` 缺省使用 `daemon.json` 的第一个 owner;未配置时省略。
|
|
52
|
+
- `--name` / `--description` 缺省读取当前项目 `package.json`,再回退到目录名 / 空描述。
|
|
53
|
+
- `--dry-run` 只输出最终创建计划,不注册 AID、不写配置、不上传 `agent.md`。
|
|
54
|
+
- `--force` 覆盖已有 `config.json`;AID 密钥保留。
|
|
50
55
|
|
|
51
56
|
## 头像更新
|
|
52
57
|
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
管理本地 AID 身份(证书、私钥、agent.md 名片),以及远程探测对端 AID。触发词:身份/证书/名片/探测对端/创建身份。
|
|
4
4
|
|
|
5
|
+
## 运行边界
|
|
6
|
+
|
|
7
|
+
本页列出独立终端中 `ec aid` 的完整 CLI 能力。在 EvolCore 托管 Agent 会话中,为防止会话枚举、创建、删除或切换身份,只允许当前 self AID 上的以下精确命令:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ec aid show <当前 self AID> [--format json]
|
|
11
|
+
ec aid lookup <当前 self AID> [--format json]
|
|
12
|
+
ec aid agentmd get <当前 self AID> [--format json]
|
|
13
|
+
# 仅 request / bypass
|
|
14
|
+
ec aid agentmd put <当前 self AID> [--format json]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
仅允许不带选项的基本形式,或在末尾附加一组完整的 `--format json`;不能使用 `--aun-path`、未知选项、管道、重定向或其他 AID。`show`、`lookup` 和 `agentmd get` 是 self-scoped 查询,可在所有 `permissionMode` 执行;`agentmd put` 会发布远端名片,属于写操作,仅允许 `request`、`bypass`,`readonly`、`auto` 和未知模式均拒绝。该身份范围边界早于模式审批,`bypass` 也不能跨越当前 self AID。`show` 会对自身身份做本地签名/验签自检,但不输出私钥;`lookup` 只探测已知的自身 AID;`get` 不改变身份或远端数据,但会下载并验签自身公开名片,同时刷新本地 AID 缓存。`list`、其他 AID 的 `show/lookup/agentmd get/put` 以及 `new/delete` 均拒绝。独立终端不受此托管会话限制。
|
|
18
|
+
|
|
5
19
|
## 子命令
|
|
6
20
|
|
|
7
21
|
```bash
|
|
@@ -132,11 +132,24 @@ Trigger V3 第二阶段使用 `execution.type` 区分执行位置:
|
|
|
132
132
|
```bash
|
|
133
133
|
--target-channel <channelKey>
|
|
134
134
|
--target-channel-id <channelId>
|
|
135
|
+
--target-chat-type <private|group>
|
|
135
136
|
--target-session main
|
|
136
137
|
--target-session thread --target-thread-id <threadId>
|
|
137
138
|
```
|
|
138
139
|
|
|
139
|
-
AUN 渠道由 agent AID 隐式创建,不需要写入 agent 配置的 `channels[]`。CLI 和 Trigger V3.1 definition 均可将 `aun` 用作 `channelKey` 简写;保存前会按 `agentAid` 展开为 `aun#<agentAid>#main
|
|
140
|
+
AUN 渠道由 agent AID 隐式创建,不需要写入 agent 配置的 `channels[]`。CLI 和 Trigger V3.1 definition 均可将 `aun` 用作 `channelKey` 简写;保存前会按 `agentAid` 展开为 `aun#<agentAid>#main`。AUN 目标必须明确设置 `chatType`;`channelId` 在私聊时是对端 ID,在群聊时就是群 ID,不再额外保存 `groupId`。读取缺少 `chatType` 的旧 AUN Trigger definition 时,迁移逻辑会仅在该次迁移中按 `private` 补齐并回写;新建和编辑仍要求显式指定。其他渠道仍需指定完整的实例 channel key。
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"strategy": "target",
|
|
145
|
+
"target": {
|
|
146
|
+
"channelKey": "aun",
|
|
147
|
+
"channelId": "peer.agentid.pub",
|
|
148
|
+
"chatType": "private",
|
|
149
|
+
"session": "main"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
140
153
|
|
|
141
154
|
Trigger V3.1 feedback 是单策略:
|
|
142
155
|
|
|
@@ -307,6 +320,7 @@ ec trigger create --exec script --file ./trigger-dir --enable
|
|
|
307
320
|
"target": {
|
|
308
321
|
"channelKey": "<channelKey>",
|
|
309
322
|
"channelId": "<channelId>",
|
|
323
|
+
"chatType": "private",
|
|
310
324
|
"session": "main"
|
|
311
325
|
}
|
|
312
326
|
},
|
|
@@ -388,6 +402,7 @@ ec trigger create --exec script --file ./trigger-dir --enable
|
|
|
388
402
|
"target": {
|
|
389
403
|
"channelKey": "<channelKey>",
|
|
390
404
|
"channelId": "<channelId>",
|
|
405
|
+
"chatType": "private",
|
|
391
406
|
"session": "main"
|
|
392
407
|
},
|
|
393
408
|
"onReply": {
|
|
@@ -466,6 +481,7 @@ flag 模式不能配置过滤条件。需要根据事件 payload 过滤时,写
|
|
|
466
481
|
"target": {
|
|
467
482
|
"channelKey": "<channelKey>",
|
|
468
483
|
"channelId": "<channelId>",
|
|
484
|
+
"chatType": "private",
|
|
469
485
|
"session": "main"
|
|
470
486
|
}
|
|
471
487
|
},
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
| **vars 粒度** | 会话级(一次构造,整批复用) | item 级(每条叠加 peerName/now/images) |
|
|
70
70
|
| **模板空行** | 删除(stripBlankLines=true,紧凑) | 保留(stripBlankLines=false,消息多段) |
|
|
71
71
|
| **content 注入** | N/A | 哨兵末步字面量注入(防二次解析) |
|
|
72
|
-
| **缓存** |
|
|
72
|
+
| **缓存** | 随包 `kits/` 文件跨 session 缓存到 reload;personal/relations 等运行时文件按 mtime 刷新 | 随包模板缓存到 reload,消息数据不缓存 |
|
|
73
73
|
| **debug 输出** | `eck-debug/context-*.md` `fragments-*.md` `manifest-*.md` `vars-*.json` | `eck-debug/msg-render-*.md` |
|
|
74
74
|
|
|
75
75
|
---
|
|
@@ -109,7 +109,6 @@
|
|
|
109
109
|
| 40 | venue-fragment | chatType≠null | ✓ | 环境层 fragment |
|
|
110
110
|
| 41 | venue-chattype | chatType≠null | ✗ | `venues/{{chatType}}.md` |
|
|
111
111
|
| 42 | venue-channel-chattype | chatType≠null | ✗ | `venues/{{channel}}-{{chatType}}.md` |
|
|
112
|
-
| 43 | venue-group-profile | groupId≠null | ✗ | 群 venue profile.md |
|
|
113
112
|
| 44 | venue-client | clientType≠null | ✗ | `venues/client-{{clientType}}.md` |
|
|
114
113
|
| 50 | channel-layer | channel≠null | ✓ | 渠道层 fragment |
|
|
115
114
|
| 55 | commands | channel≠null | ✓ | 命令集能力卡 |
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## 行为准则
|
|
6
6
|
- `mentionMode: mention-only` — 仅响应明确 @ 自己的消息
|
|
7
7
|
- `mentionMode: disabled` — 所有消息可见,自主判断是否参与。这不是叫你每条都回,而是你可以像真人一样:感兴趣的接话,不感兴趣的就跳过。回得短一点、快一点,让对话流动起来,不要一个人长篇大论堵住别人的空间
|
|
8
|
-
-
|
|
8
|
+
- 查看关系档案(peer-profile)了解本群文化与策略
|
|
9
9
|
- 如果上下文中出现“群规则”,它来自当前群资源空间的本地同步缓存;群规则优先于通用群聊指引
|
|
10
10
|
- 群发言会被多人看到,谨言慎行
|
|
11
11
|
- 具体渠道(AUN/飞书/微信等)的群聊特有指引见对应渠道文档
|
package/kits/eck_manifest.json
CHANGED
|
@@ -127,18 +127,6 @@
|
|
|
127
127
|
] },
|
|
128
128
|
"description": "渠道+场景文档(如 feishu-group)"
|
|
129
129
|
},
|
|
130
|
-
{
|
|
131
|
-
"id": "relation-group-profile",
|
|
132
|
-
"type": "file",
|
|
133
|
-
"file": "$RELATIONS_DIR/{{peerKey}}/profile.md",
|
|
134
|
-
"order": 43,
|
|
135
|
-
"needsInjection": false,
|
|
136
|
-
"when": { "and": [
|
|
137
|
-
{ "var": "groupId", "neq": null },
|
|
138
|
-
{ "var": "lifecycle", "neq": "bootstrapping" }
|
|
139
|
-
] },
|
|
140
|
-
"description": "具体群关系文档"
|
|
141
|
-
},
|
|
142
130
|
{
|
|
143
131
|
"id": "relation-group-rules",
|
|
144
132
|
"type": "file",
|
|
@@ -49,8 +49,7 @@ function isExplicitGroupId(value) {
|
|
|
49
49
|
return (id.startsWith('group.') && id.includes('/'))
|
|
50
50
|
|| /^\d+\.[a-z0-9.-]+$/i.test(id)
|
|
51
51
|
|| /^grp_[a-z0-9_-]+$/i.test(id)
|
|
52
|
-
|| /^g-[a-z0-9_-]+(?:[.@][a-z0-9.-]+)?$/i.test(id)
|
|
53
|
-
|| /(?:^|\.)groupid\.pub$/i.test(id);
|
|
52
|
+
|| /^g-[a-z0-9_-]+(?:[.@][a-z0-9.-]+)?$/i.test(id);
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
function readJson(file) {
|
|
@@ -39,7 +39,7 @@ relations/
|
|
|
39
39
|
|
|
40
40
|
| 条目 | 真实状态 |
|
|
41
41
|
|------|----------|
|
|
42
|
-
| `<peerKey>/profile.md` | manifest
|
|
42
|
+
| `<peerKey>/profile.md` | manifest 通过统一的 `peer-profile` 槽加载(缺失即跳过),群聊和私聊不再重复声明;目前**没有任何代码写它**,可以手写一份供上下文加载 |
|
|
43
43
|
| `_index/name_<name>.json` 名字反查索引 | 目录会被创建,但**无任何读写代码**,磁盘上全是空目录。"已知 name 反查 peerKey" 这条路走不通 |
|
|
44
44
|
| `<peerKey>/history.jsonl` | 关系层无此实现(同名文件都属于 handoff / trigger / CC 自己的历史) |
|
|
45
45
|
| `_trash/` | 只创建目录,没有 merge/split 重定向逻辑 |
|