evolcore 0.0.33 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +12 -1
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +82 -13
- package/dist/agents/codex/diagnostics.js +37 -11
- package/dist/agents/codex-runner.js +218 -44
- package/dist/agents/ecagent-runner.js +73 -20
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/identity.js +1 -1
- package/dist/channels/aun.js +16 -6
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +3 -2
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +120 -19
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/index.js +20 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/memory-command.js +158 -145
- package/dist/cli/thread-command.js +5 -3
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +1 -1
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +138 -4
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +22 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +36 -1
- package/dist/core/memory/batch-meta.js +132 -0
- package/dist/core/memory/consolidation-context.js +35 -14
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation.js +91 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +30 -15
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +2 -1
- package/dist/core/message/response-engine.js +584 -47
- package/dist/core/permission/ec-command-parser.js +93 -4
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +30 -62
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +75 -5
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +222 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/thread-service.js +13 -20
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +11 -2
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +4 -5
- package/kits/docs/evolcore/fs.md +1 -1
- package/kits/docs/evolcore/group-collaboration.md +2 -2
- package/kits/docs/evolcore/group.md +10 -10
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +18 -14
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/thread-chain.md +5 -5
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.json +0 -20
- package/kits/eck_manifest.memory.json +0 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +12 -15
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +4 -4
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +11 -11
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +6 -6
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
// when 求值、模板渲染、路径解析、manifest 加载/缓存原语,避免两套实现漂移。
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import { kitsDir, resolveRoot, getPackageRoot } from '../paths.js';
|
|
6
|
+
import { kitsDir, resolveRoot, getPackageRoot, isDevMode } from '../paths.js';
|
|
7
7
|
import { logger } from '../utils/logger.js';
|
|
8
8
|
import { fileCache } from '../core/daemon-file-cache.js';
|
|
9
|
+
import { readVerifiedText, isShippedPath } from '../core/file-integrity/read.js';
|
|
9
10
|
// ── 限额默认值 ──
|
|
10
11
|
/** 单目录段默认最多文件数 */
|
|
11
12
|
export const DIR_MAX_FILES = 20;
|
|
@@ -365,12 +366,31 @@ function sectionCacheOptions(resolvedPath, vars) {
|
|
|
365
366
|
}
|
|
366
367
|
return { policy: 'mtime', group: 'eck-runtime' };
|
|
367
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* 读取一个 kits 文档,安装模式下先过台账校验。
|
|
371
|
+
*
|
|
372
|
+
* 为什么校验包在 loader 里:这些内容走 FileCache 的 `on-reload` 策略,缓存命中时
|
|
373
|
+
* 不会碰磁盘,校验只有包在这里才与"实际注入的那份内容"对齐;被篡改且恢复不了时
|
|
374
|
+
* 返回 null,条目本身也不会写进缓存。开发模式下 kits 由 agent 直接改写,不校验。
|
|
375
|
+
*/
|
|
376
|
+
function loadKitsDoc(resolved, vars) {
|
|
377
|
+
const opts = sectionCacheOptions(resolved, vars);
|
|
378
|
+
if (!isShippedPath(resolved)) {
|
|
379
|
+
return fileCache.getText(resolved, opts);
|
|
380
|
+
}
|
|
381
|
+
const verify = !isDevMode();
|
|
382
|
+
return fileCache.get(resolved, () => {
|
|
383
|
+
const checked = readVerifiedText(resolved, { verify, tag: 'eck' });
|
|
384
|
+
// 校验不过就不进缓存:返回 null → fileCache 存 null,该段被丢弃
|
|
385
|
+
return checked ? checked.text : null;
|
|
386
|
+
}, opts);
|
|
387
|
+
}
|
|
368
388
|
/** 加载 loop 的 child 子模板文件内容(路径解析 + fileCache)。找不到返回 null。 */
|
|
369
389
|
export function loadChildTemplate(childFile, vars) {
|
|
370
390
|
const resolved = resolvePath(childFile, vars);
|
|
371
391
|
if (!resolved)
|
|
372
392
|
return null;
|
|
373
|
-
return
|
|
393
|
+
return loadKitsDoc(resolved, vars);
|
|
374
394
|
}
|
|
375
395
|
/**
|
|
376
396
|
* 返回 [filePath, rawContent][]。kits 文件跨 session 缓存到 reload;运行时文件
|
|
@@ -401,7 +421,7 @@ function loadFileSection(filePath, vars, sessionCache) {
|
|
|
401
421
|
const resolved = resolvePath(filePath, vars);
|
|
402
422
|
if (!resolved)
|
|
403
423
|
return null;
|
|
404
|
-
const content =
|
|
424
|
+
const content = loadKitsDoc(resolved, vars);
|
|
405
425
|
return content === null ? null : [resolved, content];
|
|
406
426
|
}
|
|
407
427
|
/**
|
|
@@ -435,7 +455,7 @@ function readDirectoryFiles(dirPath, pattern, maxFiles, maxBytes, vars) {
|
|
|
435
455
|
break;
|
|
436
456
|
}
|
|
437
457
|
const fp = path.join(dirPath, f);
|
|
438
|
-
const content =
|
|
458
|
+
const content = loadKitsDoc(fp, vars);
|
|
439
459
|
if (content === null)
|
|
440
460
|
continue;
|
|
441
461
|
// 字节限额(至少加载一个文件,避免单个超大文件导致空目录)
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 【重要】最先加载环境变量,确保后续模块初始化时可用
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { ensureDataDirs, resolvePaths, eckDebugDir, daemonControlDir, daemonQueuePath, daemonMessageDedupPath } from './paths.js';
|
|
4
|
+
import { ensureDataDirs, resolvePaths, eckDebugDir, daemonControlDir, daemonQueuePath, daemonMessageDedupPath, isDevMode } from './paths.js';
|
|
5
5
|
// 立即加载 .env 文件(在其他模块导入之前)
|
|
6
6
|
try {
|
|
7
7
|
dotenv.config({ path: path.join(resolvePaths().root, '.env') });
|
|
@@ -57,6 +57,7 @@ import { StatsCollector } from './utils/stats.js';
|
|
|
57
57
|
import { AidStatsCollector } from './utils/stats.js';
|
|
58
58
|
import { initializeStatsPersistence } from './stats/writer.js';
|
|
59
59
|
import { PermissionGateway } from './core/permission/approval-gateway.js';
|
|
60
|
+
import { releaseSessionPathGrants } from './core/permission/session-path-grants.js';
|
|
60
61
|
import { InteractionRouter } from './core/interaction-router.js';
|
|
61
62
|
import { GroupInviteReviewService } from './aun/group/invite-service.js';
|
|
62
63
|
import { AgentDelegationRegistry, } from './core/auth/agent-delegation.js';
|
|
@@ -138,8 +139,10 @@ async function main() {
|
|
|
138
139
|
}
|
|
139
140
|
// 立即登记自己(让其他并发启动者能看见我)
|
|
140
141
|
const launchedBy = process.env.EVOLCORE_LAUNCHED_BY || 'start';
|
|
142
|
+
// 本次运行模式在这里定下(首次求值时会读走并删除 `ec start --as-mode` 的覆盖文件)
|
|
143
|
+
const devMode = isDevMode();
|
|
141
144
|
writeMain(launchedBy);
|
|
142
|
-
logger.info(`✓ Instance record written: main-${process.pid}.json`);
|
|
145
|
+
logger.info(`✓ Instance record written: main-${process.pid}.json runMode=${devMode ? 'dev' : 'install'}`);
|
|
143
146
|
// post-write 自检:写完 record 后再扫一次,发现并发对手时按 (startedAt, pid) 选赢家
|
|
144
147
|
{
|
|
145
148
|
const verdict = isMainWinner();
|
|
@@ -240,6 +243,12 @@ async function main() {
|
|
|
240
243
|
if (event.nameChanged)
|
|
241
244
|
agentRegistry?.invalidateAgentDisplayCache?.(event.aid);
|
|
242
245
|
});
|
|
246
|
+
// 运行时路径授权以会话为归属:会话被删除后不再有身份去消费它,随之释放。
|
|
247
|
+
// 只挂 session:deleted——session:idle 只表示空闲,会话仍会继续服务。
|
|
248
|
+
eventBus.subscribe('session:deleted', (event) => {
|
|
249
|
+
if ('sessionId' in event && event.sessionId)
|
|
250
|
+
releaseSessionPathGrants(event.sessionId);
|
|
251
|
+
});
|
|
243
252
|
// 统计收集器(近 1 小时滚动统计)
|
|
244
253
|
const statsCollector = new StatsCollector(eventBus);
|
|
245
254
|
// Per-AID 消息统计收集器(累计,供 watch aid 实时展示)
|
package/dist/ipc.js
CHANGED
|
@@ -23,6 +23,7 @@ import { remoteAccessService } from './core/auth/remote-access-store.js';
|
|
|
23
23
|
import { setModelCatalogDaemonOwner } from './core/model/model-catalog.js';
|
|
24
24
|
import { RUNTIME_ENVIRONMENT_NAMES, setRuntimeEnvironmentOverride } from './config/runtime-env.js';
|
|
25
25
|
import { MAIN_CLI_BIN } from './product.js';
|
|
26
|
+
import { delegationDiagnosticMessage, withDelegationDiagnosticContext, } from './core/auth/delegation-diagnostics.js';
|
|
26
27
|
const isWindows = process.platform === 'win32';
|
|
27
28
|
const isNamedPipe = (p) => isWindows && p.toLowerCase().startsWith('\\\\.\\pipe\\');
|
|
28
29
|
const CPU_SAMPLE_INTERVAL_MS = 1000;
|
|
@@ -79,7 +80,7 @@ const LOCAL_CONTROL_COMMANDS = new Set([
|
|
|
79
80
|
]);
|
|
80
81
|
const MEMORY_COMMAND_ENDPOINTS = new Set([
|
|
81
82
|
'memory.note', 'memory.note.update', 'memory.note.drop', 'memory.note.list',
|
|
82
|
-
'memory.create', 'memory.update', 'memory.archive', 'memory.demote', 'memory.consolidate',
|
|
83
|
+
'memory.create', 'memory.update', 'memory.archive', 'memory.demote', 'memory.consolidate',
|
|
83
84
|
]);
|
|
84
85
|
const DELEGATED_ARGV_ENDPOINTS = new Set([
|
|
85
86
|
'agent.op', 'config.op', 'contact.op', 'aid.op', 'group.op', 'msg.op', 'fs.op',
|
|
@@ -194,6 +195,7 @@ export class IpcServer {
|
|
|
194
195
|
debugTaskRuntimeContextProvider;
|
|
195
196
|
debugIssueTokenExecutor;
|
|
196
197
|
taskRuntimeContextAuthorizer;
|
|
198
|
+
systemSessionCommandGate;
|
|
197
199
|
codexPreToolUseExecutor;
|
|
198
200
|
aunMsgSender;
|
|
199
201
|
diagExecutor;
|
|
@@ -218,6 +220,7 @@ export class IpcServer {
|
|
|
218
220
|
wechatContactBindExecutor;
|
|
219
221
|
controlToken;
|
|
220
222
|
daemonRuntimeEpoch;
|
|
223
|
+
delegationFailureRecorder;
|
|
221
224
|
remoteAccessService = remoteAccessService;
|
|
222
225
|
/** Current daemon owner used to bind locally issued ECWeb pairing codes. */
|
|
223
226
|
remoteAccessOwnerProvider;
|
|
@@ -288,6 +291,9 @@ export class IpcServer {
|
|
|
288
291
|
setDaemonRuntimeEpoch(epoch) {
|
|
289
292
|
this.daemonRuntimeEpoch = epoch;
|
|
290
293
|
}
|
|
294
|
+
setDelegationFailureRecorder(recorder) {
|
|
295
|
+
this.delegationFailureRecorder = recorder;
|
|
296
|
+
}
|
|
291
297
|
/** Inject menu.* executor (ECWeb Control proxies menu requests through this) */
|
|
292
298
|
setMenuExecutor(executor) {
|
|
293
299
|
this.menuExecutor = executor;
|
|
@@ -492,6 +498,9 @@ export class IpcServer {
|
|
|
492
498
|
setTaskRuntimeContextAuthorizer(authorizer) {
|
|
493
499
|
this.taskRuntimeContextAuthorizer = authorizer;
|
|
494
500
|
}
|
|
501
|
+
setSystemSessionCommandGate(gate) {
|
|
502
|
+
this.systemSessionCommandGate = gate;
|
|
503
|
+
}
|
|
495
504
|
setCodexPreToolUseExecutor(executor) {
|
|
496
505
|
this.codexPreToolUseExecutor = executor;
|
|
497
506
|
}
|
|
@@ -951,6 +960,27 @@ export class IpcServer {
|
|
|
951
960
|
}
|
|
952
961
|
}
|
|
953
962
|
async handleCommand(cmd, requestSignal, receivedAtMono = performance.now()) {
|
|
963
|
+
return withDelegationDiagnosticContext({
|
|
964
|
+
sessionId: typeof cmd.sessionId === 'string' ? cmd.sessionId : undefined,
|
|
965
|
+
requestId: nextIpcQueryId(cmd),
|
|
966
|
+
correlationId: typeof cmd.correlationId === 'string' ? ipcLogToken(cmd.correlationId) : undefined,
|
|
967
|
+
operation: ipcLogToken(cmd.type),
|
|
968
|
+
}, () => this.dispatchCommand(cmd, requestSignal, receivedAtMono));
|
|
969
|
+
}
|
|
970
|
+
diagnosticRejection(cause, cmd) {
|
|
971
|
+
const failure = this.delegationFailureRecorder?.(cause, typeof (cmd.sessionId ?? cmd.originSessionId ?? cmd.actorSessionId) === 'string'
|
|
972
|
+
? cmd.sessionId ?? cmd.originSessionId ?? cmd.actorSessionId : undefined, typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
973
|
+
const diagnosticCause = failure?.diagnosticCause ?? cause;
|
|
974
|
+
return {
|
|
975
|
+
ok: false,
|
|
976
|
+
code: diagnosticCause === 'missing_token' ? 'DELEGATION_REQUIRED'
|
|
977
|
+
: diagnosticCause === 'daemon_restarted' ? 'DAEMON_RESTARTED' : 'INVALID_DELEGATION',
|
|
978
|
+
error: failure?.reason ?? delegationDiagnosticMessage(diagnosticCause),
|
|
979
|
+
diagnosticCause,
|
|
980
|
+
...(failure?.diagnosticEventId ? { diagnosticEventId: failure.diagnosticEventId } : {}),
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
async dispatchCommand(cmd, requestSignal, receivedAtMono = performance.now()) {
|
|
954
984
|
if (requiresLocalControlToken(cmd) && !this.hasValidControlToken(cmd.controlToken)) {
|
|
955
985
|
return { ok: false, code: 'INVALID_CONTROL_TOKEN', error: 'valid local control token is required' };
|
|
956
986
|
}
|
|
@@ -959,11 +989,7 @@ export class IpcServer {
|
|
|
959
989
|
&& cmd.daemonRuntimeEpoch !== undefined
|
|
960
990
|
&& cmd.daemonRuntimeEpoch !== this.daemonRuntimeEpoch) {
|
|
961
991
|
logger.warn(`[IPC] stale delegation rejected: session=${ipcLogToken(cmd.sessionId ?? cmd.originSessionId ?? cmd.actorSessionId)} type=${ipcLogToken(cmd.type)} cause=daemon_runtime_epoch_mismatch`);
|
|
962
|
-
return
|
|
963
|
-
ok: false,
|
|
964
|
-
code: 'DAEMON_RESTARTED',
|
|
965
|
-
error: 'The task delegation token belongs to a previous daemon runtime; start a new task authorization and do not retry this command in the current task',
|
|
966
|
-
};
|
|
992
|
+
return this.diagnosticRejection('daemon_restarted', cmd);
|
|
967
993
|
}
|
|
968
994
|
if (typeof cmd.delegationToken === 'string'
|
|
969
995
|
&& (DELEGATED_ARGV_ENDPOINTS.has(cmd.type) || (cmd.type === 'aun-msg-send' && cmd.argv !== undefined))) {
|
|
@@ -976,9 +1002,26 @@ export class IpcServer {
|
|
|
976
1002
|
...cmd.argv,
|
|
977
1003
|
]);
|
|
978
1004
|
if (!expectedHash || cmd.delegationCommandHash !== expectedHash) {
|
|
979
|
-
return
|
|
1005
|
+
return this.diagnosticRejection('command_hash_mismatch', cmd);
|
|
980
1006
|
}
|
|
981
1007
|
}
|
|
1008
|
+
// 系统会话统一闸门:带系统会话 delegation 的请求只看模块 checkCommand,拒绝即返回
|
|
1009
|
+
if (this.systemSessionCommandGate
|
|
1010
|
+
&& typeof cmd.delegationToken === 'string' && cmd.delegationToken
|
|
1011
|
+
&& typeof cmd.sessionId === 'string' && cmd.sessionId) {
|
|
1012
|
+
const argv = Array.isArray(cmd.argv) && cmd.argv.every((value) => typeof value === 'string')
|
|
1013
|
+
? cmd.argv
|
|
1014
|
+
: undefined;
|
|
1015
|
+
const gate = await this.systemSessionCommandGate({
|
|
1016
|
+
sessionId: cmd.sessionId,
|
|
1017
|
+
delegationToken: cmd.delegationToken,
|
|
1018
|
+
...(typeof cmd.delegationCommandHash === 'string' ? { delegationCommandHash: cmd.delegationCommandHash } : {}),
|
|
1019
|
+
type: cmd.type,
|
|
1020
|
+
...(argv ? { argv } : {}),
|
|
1021
|
+
});
|
|
1022
|
+
if (gate && !gate.allow)
|
|
1023
|
+
return { ok: false, code: 'NOT_ALLOWED', policyCode: gate.code, error: gate.reason };
|
|
1024
|
+
}
|
|
982
1025
|
if (cmd.argv !== undefined && ['handoff-status', 'handoff-list', 'handoff-trace'].includes(cmd.type)) {
|
|
983
1026
|
try {
|
|
984
1027
|
const { parseList, parseTrace } = await import('./cli/handoff-command.js');
|
|
@@ -1911,37 +1954,6 @@ export class IpcServer {
|
|
|
1911
1954
|
return { ok: false, error: e?.message || String(e) };
|
|
1912
1955
|
}
|
|
1913
1956
|
}
|
|
1914
|
-
case 'memory.feedback': {
|
|
1915
|
-
const { sessionId, text } = cmd;
|
|
1916
|
-
if (!sessionId)
|
|
1917
|
-
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId' };
|
|
1918
|
-
if (typeof text !== 'string' || !text.trim()) {
|
|
1919
|
-
return { ok: false, code: 'INVALID_REQUEST', error: 'missing feedback text' };
|
|
1920
|
-
}
|
|
1921
|
-
try {
|
|
1922
|
-
const taskCtx = await this.resolveMemoryTaskCtx(sessionId, cmd.debug === true);
|
|
1923
|
-
if (!taskCtx)
|
|
1924
|
-
return { ok: false, code: 'SESSION_NOT_FOUND', error: `未找到会话 ${sessionId} 的运行时上下文` };
|
|
1925
|
-
// 只有整理会话可以记录反馈
|
|
1926
|
-
if (taskCtx.sessionType !== 'memory') {
|
|
1927
|
-
return { ok: false, code: 'NOT_MEMORY_SESSION', error: '`ec memory feedback` 只能在整理会话内调用' };
|
|
1928
|
-
}
|
|
1929
|
-
const batchDir = taskCtx.consolidationBatchDir;
|
|
1930
|
-
if (!batchDir) {
|
|
1931
|
-
return { ok: false, code: 'BATCH_DIR_NOT_FOUND', error: '整理会话缺少 consolidationBatchDir' };
|
|
1932
|
-
}
|
|
1933
|
-
const { appendOpsRecord } = await import('./core/memory/consolidation-ops-records.js');
|
|
1934
|
-
appendOpsRecord(batchDir, {
|
|
1935
|
-
ts: new Date().toISOString(),
|
|
1936
|
-
op: 'feedback-set',
|
|
1937
|
-
text,
|
|
1938
|
-
});
|
|
1939
|
-
return { ok: true };
|
|
1940
|
-
}
|
|
1941
|
-
catch (e) {
|
|
1942
|
-
return { ok: false, error: e?.message || String(e) };
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
1957
|
case 'config.op': {
|
|
1946
1958
|
if (!this.configOperationExecutor)
|
|
1947
1959
|
return { ok: false, code: 'NOT_CONFIGURED', error: 'config.op not configured' };
|
|
@@ -2422,8 +2434,7 @@ export class IpcServer {
|
|
|
2422
2434
|
const { rebuildIndex } = await import('./core/memory/index-manager.js');
|
|
2423
2435
|
const mc = this.agentRegistry?.get(taskCtx.selfAid ?? '')?.config?.memory;
|
|
2424
2436
|
const maxBytes = mc?.indexMaxBytes ?? 5 * 1024;
|
|
2425
|
-
const buildResult = await rebuildIndex(dirs.consolidatedDir, path.join(dirs.memoryDir, 'MEMORY.md'), scope,
|
|
2426
|
-
maxBytes);
|
|
2437
|
+
const buildResult = await rebuildIndex(dirs.consolidatedDir, path.join(dirs.memoryDir, 'MEMORY.md'), scope, maxBytes);
|
|
2427
2438
|
// 索引重建后顺手检查这一层有没有超期记忆 —— 有则建触发器拉一个独立的遗忘会话。
|
|
2428
2439
|
await this.maybeTriggerForget(taskCtx, scope, dirs.consolidatedDir, mc, buildResult.verifiedFiles);
|
|
2429
2440
|
}
|
package/dist/paths.js
CHANGED
|
@@ -442,3 +442,73 @@ export function getPackageRoot() {
|
|
|
442
442
|
// the correct OS-native path, regardless of Git Bash or MSYS2 environment.
|
|
443
443
|
return path.resolve(import.meta.dirname, '..');
|
|
444
444
|
}
|
|
445
|
+
// ── FileIntegrityStore 路径 ──
|
|
446
|
+
export function integrityDir() {
|
|
447
|
+
return path.join(resolveRoot(), 'data', 'integrity');
|
|
448
|
+
}
|
|
449
|
+
export function integrityDbPath() {
|
|
450
|
+
return path.join(integrityDir(), 'file-integrity.db');
|
|
451
|
+
}
|
|
452
|
+
export function integritySnapshotDir() {
|
|
453
|
+
return path.join(integrityDir(), 'snapshots');
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* 判断当前是否为开发仓运行模式。
|
|
457
|
+
* 与 ec start 显示的安装类型保持口径一致:包路径包含 node_modules 则为安装模式,否则为开发仓。
|
|
458
|
+
* 调用方据此决定是否跳过 FileIntegrityStore 的 verify 调用。
|
|
459
|
+
*
|
|
460
|
+
* 唯一覆盖来源是 `ec start --as-mode` 写下的一次性文件(见 asModeOverridePath),
|
|
461
|
+
* 由 daemon 启动时读取并立即删除。**不读环境变量**——环境变量会被 restart 之类的
|
|
462
|
+
* 路径静默继承,导致模式在操作者不知情的情况下改变。
|
|
463
|
+
*/
|
|
464
|
+
export function isDevMode() {
|
|
465
|
+
if (!_asModeResolved) {
|
|
466
|
+
_asModeResolved = true;
|
|
467
|
+
_asModeOverride = consumeAsModeOverride();
|
|
468
|
+
}
|
|
469
|
+
if (_asModeOverride)
|
|
470
|
+
return _asModeOverride === 'dev';
|
|
471
|
+
return !getPackageRoot().includes('node_modules');
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* `ec start --as-mode <mode>` 的一次性覆盖文件。
|
|
475
|
+
*
|
|
476
|
+
* 走文件而不是环境变量:环境变量会被 `ec restart`(全量继承 process.env)
|
|
477
|
+
* 静默带进新 daemon,操作者不重启终端就撤不掉。文件由 CLI 在启动前写入,
|
|
478
|
+
* daemon 首次调用 isDevMode() 时读走并删除,只影响这一次启动。
|
|
479
|
+
*/
|
|
480
|
+
export function asModeOverridePath() {
|
|
481
|
+
return path.join(resolveRoot(), 'data', 'daemon', 'as-mode.override');
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* 本次进程的覆盖值。**必须缓存**:文件读一次就删,若每次都去读,
|
|
485
|
+
* 第二次调用起就会悄悄退回按包路径判断——模式在进程运行中途改变是最坏的情况。
|
|
486
|
+
*/
|
|
487
|
+
let _asModeResolved = false;
|
|
488
|
+
let _asModeOverride = null;
|
|
489
|
+
/** 仅供测试:清掉缓存,让下一次 isDevMode() 重新读覆盖文件。 */
|
|
490
|
+
export function _resetAsModeCache() {
|
|
491
|
+
_asModeResolved = false;
|
|
492
|
+
_asModeOverride = null;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* 读出并删除覆盖文件(一次性)。返回 null 表示没有覆盖。
|
|
496
|
+
* 只应由 daemon 启动流程(或 isDevMode 的首次求值)调用一次。
|
|
497
|
+
*/
|
|
498
|
+
export function consumeAsModeOverride() {
|
|
499
|
+
const file = asModeOverridePath();
|
|
500
|
+
if (!fs.existsSync(file))
|
|
501
|
+
return null;
|
|
502
|
+
let value = null;
|
|
503
|
+
try {
|
|
504
|
+
value = fs.readFileSync(file, 'utf-8').trim();
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
value = null;
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
fs.rmSync(file, { force: true });
|
|
511
|
+
}
|
|
512
|
+
catch { /* ignore */ }
|
|
513
|
+
return value === 'dev' || value === 'install' ? value : null;
|
|
514
|
+
}
|
package/dist/stats/billing.js
CHANGED
|
@@ -180,24 +180,36 @@ function loadSpecs(evolcoreHome) {
|
|
|
180
180
|
_specCacheTs = now;
|
|
181
181
|
return _specCache;
|
|
182
182
|
}
|
|
183
|
-
|
|
184
|
-
* 带窗口后缀的 ID 复用基础模型的参数行,但窗口由后缀决定(网关的权威声明)。 */
|
|
185
|
-
export function resolveModelSpec(evolcoreHome, model, ts) {
|
|
183
|
+
export function findModelSpec(evolcoreHome, model, ts = Date.now()) {
|
|
186
184
|
const specs = loadSpecs(evolcoreHome);
|
|
187
|
-
const t = ts ?? Date.now();
|
|
188
185
|
const suffixWindow = parseModelId(model).contextWindow;
|
|
189
186
|
for (const key of modelLookupKeys(evolcoreHome, model)) {
|
|
190
|
-
const candidates = specs.filter(
|
|
187
|
+
const candidates = specs.filter(spec => spec.model === key && spec.effective_from <= ts);
|
|
191
188
|
if (!candidates.length)
|
|
192
189
|
continue;
|
|
193
|
-
const row = candidates.reduce((a, b) => a.effective_from
|
|
190
|
+
const row = candidates.reduce((a, b) => a.effective_from > b.effective_from ? a : b);
|
|
194
191
|
if (suffixWindow === undefined || suffixWindow === row.context_window)
|
|
195
192
|
return row;
|
|
196
193
|
// 后缀声明的窗口覆盖基础行,输入上限按原行的预留比例等比缩放。
|
|
197
194
|
const ratio = row.context_window > 0 ? row.max_input_tokens / row.context_window : 0.9;
|
|
198
195
|
return { ...row, model, context_window: suffixWindow, max_input_tokens: Math.floor(suffixWindow * ratio) };
|
|
199
196
|
}
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
/** 取模型在 ts 时刻的能力参数。找不到时返回 fallback 或默认值。
|
|
200
|
+
* 带窗口后缀的 ID 复用基础模型的参数行,但窗口由后缀决定(网关的权威声明)。 */
|
|
201
|
+
export function resolveModelSpec(evolcoreHome, model, ts, fallback) {
|
|
202
|
+
const spec = findModelSpec(evolcoreHome, model, ts);
|
|
203
|
+
if (spec)
|
|
204
|
+
return spec;
|
|
205
|
+
const suffixWindow = parseModelId(model).contextWindow;
|
|
200
206
|
// 默认值
|
|
201
|
-
const contextWindow = suffixWindow ?? 200000;
|
|
202
|
-
return {
|
|
207
|
+
const contextWindow = suffixWindow ?? fallback?.context_window ?? 200000;
|
|
208
|
+
return {
|
|
209
|
+
model,
|
|
210
|
+
effective_from: 0,
|
|
211
|
+
context_window: contextWindow,
|
|
212
|
+
max_input_tokens: Math.floor(contextWindow * 0.9),
|
|
213
|
+
max_output_tokens: fallback?.max_output_tokens ?? 8192,
|
|
214
|
+
};
|
|
203
215
|
}
|
package/dist/trigger/feedback.js
CHANGED
|
@@ -303,7 +303,12 @@ export class TriggerFeedbackDispatcher {
|
|
|
303
303
|
});
|
|
304
304
|
await binding.adapter.send(envelope, { kind: 'status.error',
|
|
305
305
|
metadata: { errorType: 'orphaned_target_session', message: text } });
|
|
306
|
-
await binding.adapter.send(envelope, {
|
|
306
|
+
await binding.adapter.send(envelope, {
|
|
307
|
+
kind: 'result.error',
|
|
308
|
+
subtype: 'session_unavailable',
|
|
309
|
+
reason: 'orphaned_target_session',
|
|
310
|
+
text,
|
|
311
|
+
});
|
|
307
312
|
};
|
|
308
313
|
const inspect = async () => {
|
|
309
314
|
if (!this.deps.inspectTargetExecution || disposed || delivery.signal.aborted)
|
package/dist/trigger/parser.js
CHANGED
|
@@ -11,7 +11,7 @@ const TRIGGER_UPDATE_FLAGS = new Set([
|
|
|
11
11
|
]);
|
|
12
12
|
const TRIGGER_CREATE_FLAGS = new Set([
|
|
13
13
|
'once', 'delay', 'at', 'cron', 'every', 'event', 'event-filter', 'tz',
|
|
14
|
-
'exec', 'prompt', 'name', 'agent', 'label',
|
|
14
|
+
'exec', 'prompt', 'name', 'description', 'agent', 'label',
|
|
15
15
|
'script-path', 'script-runtime', 'script-args', 'script-timeout', 'execution-timeout',
|
|
16
16
|
'feedback', 'target-channel', 'target-channel-id', 'target-chat-type', 'target-session', 'target-thread-id',
|
|
17
17
|
'trigger-thread', 'baseagent',
|
|
@@ -444,6 +444,9 @@ function commonParsed(flags, opts = {}) {
|
|
|
444
444
|
return { ok: false, error: '--script-args 必须是合法的 JSON' };
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
|
+
const description = optionalFlagString(flags, 'description');
|
|
448
|
+
if ('error' in description)
|
|
449
|
+
return { ok: false, error: description.error };
|
|
447
450
|
const prompt = flags.get('prompt');
|
|
448
451
|
if (prompt === true)
|
|
449
452
|
return { ok: false, error: '--prompt 不能为空;复杂内容建议改用 --prompt-file <path>' };
|
|
@@ -525,6 +528,8 @@ function commonParsed(flags, opts = {}) {
|
|
|
525
528
|
targetThreadId: targetThreadId,
|
|
526
529
|
agentId: flags.has('agent') ? flags.get('agent') : undefined,
|
|
527
530
|
name: flags.has('name') ? flags.get('name') : undefined,
|
|
531
|
+
// create 语义:空字符串等同于「未提供」,不写该字段(与 update 的清除语义不同)。
|
|
532
|
+
description: description.value || undefined,
|
|
528
533
|
prompt: prompt,
|
|
529
534
|
scriptPath: scriptPath,
|
|
530
535
|
scriptRuntime: scriptRuntime,
|
|
@@ -569,6 +574,7 @@ export function parseTriggerSet(args) {
|
|
|
569
574
|
targetThreadId: value.targetThreadId,
|
|
570
575
|
agentId: value.agentId,
|
|
571
576
|
name: value.name,
|
|
577
|
+
description: value.description,
|
|
572
578
|
prompt: value.prompt,
|
|
573
579
|
scriptPath: value.scriptPath,
|
|
574
580
|
scriptRuntime: value.scriptRuntime,
|
|
@@ -615,6 +621,7 @@ export function parseTriggerSetArgv(args, opts = {}) {
|
|
|
615
621
|
targetThreadId: value.targetThreadId,
|
|
616
622
|
agentId: value.agentId,
|
|
617
623
|
name: value.name,
|
|
624
|
+
description: value.description,
|
|
618
625
|
prompt: value.prompt,
|
|
619
626
|
scriptPath: value.scriptPath,
|
|
620
627
|
scriptRuntime: value.scriptRuntime,
|
|
@@ -121,7 +121,7 @@ export function seedTaskRecoveryTrigger(manager, owner) {
|
|
|
121
121
|
fs.rmSync(manager.activePath(definition.id), { force: true });
|
|
122
122
|
}
|
|
123
123
|
const SESSION_IDLE_CHECK_PROMPT = `本会话已闲置一段时间。请依据你的职责执行一次例行巡检:
|
|
124
|
-
1.
|
|
124
|
+
1. 检查当前会话中未完成却停滞的事项。私聊检查自己承诺或正在推进的任务;群聊还需检查成员长时间无进展、被点名未回应、承诺后失联等情况。有,则在既有授权和职责内推进一次,或向相关成员催促一次并说明停滞事项,随后即止;无,则不做任何动作。不要自行新增任务,正常推进或无待办时保持沉默。
|
|
125
125
|
2. 巡检优先基于已有上下文判断,确需补充背景再查询历史,并遵守会话自身的发送规范。
|
|
126
126
|
3. 判定无需任何对外动作时,本轮只输出 [[NOOP]],不发送任何消息、不发布巡检结论。注意:最终对外输出必须是且仅是 [[NOOP]] 这一行——不要在输出它之前先发送任何说明文字或巡检判定,判定过程仅在内部完成。
|
|
127
127
|
(本指令为通用模板,可按具体职责增删检查项。)`;
|
|
@@ -133,7 +133,7 @@ export function seedSessionIdleCheckTrigger(manager, owner) {
|
|
|
133
133
|
agentAid: owner.aid,
|
|
134
134
|
enabled: true,
|
|
135
135
|
name: '__session-idle-check',
|
|
136
|
-
description: 'System trigger: inspect configured idle group sessions.',
|
|
136
|
+
description: 'System trigger: inspect configured idle private and group sessions.',
|
|
137
137
|
createdAt: now,
|
|
138
138
|
updatedAt: now,
|
|
139
139
|
origin: {
|
|
@@ -147,7 +147,7 @@ export function seedSessionIdleCheckTrigger(manager, owner) {
|
|
|
147
147
|
source: {
|
|
148
148
|
type: 'event',
|
|
149
149
|
eventPattern: 'session:idle',
|
|
150
|
-
filter: { match: { chatType: 'group' } },
|
|
150
|
+
filter: { match: { chatType: { $in: ['private', 'group'] } } },
|
|
151
151
|
},
|
|
152
152
|
execution: {
|
|
153
153
|
type: 'target_session',
|
|
@@ -666,6 +666,13 @@ export async function waitForProcessExit(pid, timeoutMs = 10_000, intervalMs = 2
|
|
|
666
666
|
* Returns list of matching PIDs.
|
|
667
667
|
*/
|
|
668
668
|
export function findProcesses(pattern) {
|
|
669
|
+
return findProcessesWithCommandLine(pattern).map(match => match.pid);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Like findProcesses, but keeps the command line returned by the Windows CIM
|
|
673
|
+
* snapshot so callers do not need a second PowerShell launch per candidate.
|
|
674
|
+
*/
|
|
675
|
+
export function findProcessesWithCommandLine(pattern) {
|
|
669
676
|
try {
|
|
670
677
|
if (isWindows) {
|
|
671
678
|
// WMI LIKE is wildcard matching, not JavaScript regex matching. Query a
|
|
@@ -696,13 +703,15 @@ export function findProcesses(pattern) {
|
|
|
696
703
|
const normalized = commandLine.replace(/\\/g, '/');
|
|
697
704
|
return matcherCandidates.some(matcher => matcher.test(commandLine) || matcher.test(normalized));
|
|
698
705
|
})
|
|
699
|
-
.map(row => row.ProcessId);
|
|
706
|
+
.map(row => ({ pid: row.ProcessId, commandLine: row.CommandLine || '' }));
|
|
700
707
|
}
|
|
701
708
|
return [];
|
|
702
709
|
}
|
|
703
710
|
else {
|
|
704
711
|
const output = execFileSync('pgrep', ['-f', pattern], { encoding: 'utf-8' }).trim();
|
|
705
|
-
return output
|
|
712
|
+
return output
|
|
713
|
+
? output.split('\n').map(Number).filter(pid => pid !== process.pid).map(pid => ({ pid }))
|
|
714
|
+
: [];
|
|
706
715
|
}
|
|
707
716
|
}
|
|
708
717
|
catch {
|
|
@@ -85,11 +85,25 @@
|
|
|
85
85
|
"message": "❌ 当前模型不受支持,请正确配置模型后重试"
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
|
-
"id": "
|
|
89
|
-
"match": "api error: 403",
|
|
90
|
-
"action": "
|
|
88
|
+
"id": "claude-client-upgrade-403-cn",
|
|
89
|
+
"match": "api error: 403 请升级claude code 客户端到最新版",
|
|
90
|
+
"action": "stop",
|
|
91
|
+
"type": "api_error",
|
|
92
|
+
"message": "⚠️ Claude Code 客户端版本过旧,请升级到最新版后重试"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "insufficient-credit-403-cn",
|
|
96
|
+
"match": "api error: 403 预扣后为-",
|
|
97
|
+
"action": "stop",
|
|
91
98
|
"type": "api_error",
|
|
92
|
-
"message": "⚠️
|
|
99
|
+
"message": "⚠️ 积分余额不足,请充值积分或升级套餐后重试"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "team-context-limit-403-cn",
|
|
103
|
+
"match": "api error: 403 请求上下文约",
|
|
104
|
+
"action": "stop",
|
|
105
|
+
"type": "context_too_long",
|
|
106
|
+
"message": "⚠️ 请求已超过团队上下文上限,请使用 /compact 压缩上下文后重试,或联系管理员调整上下文配额"
|
|
93
107
|
},
|
|
94
108
|
{
|
|
95
109
|
"id": "api-error-429",
|