evolcore 0.0.38 → 0.0.40
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 +64 -0
- package/README.md +31 -20
- package/bin/ec-safe-output.js +14 -2
- package/dist/agents/baseagent-availability.js +1 -0
- package/dist/agents/claude-runner.js +32 -46
- package/dist/agents/codex-runner.js +54 -30
- package/dist/agents/ecagent-runner.js +18 -7
- package/dist/agents/gemini-runner.js +1 -2
- package/dist/agents/runner-types.js +28 -6
- package/dist/aun/aid/agentmd.js +21 -0
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +23 -98
- package/dist/aun/aid/ownership-operation.js +35 -12
- package/dist/aun/group/invite-service.js +6 -0
- package/dist/aun/msg/p2p.js +8 -12
- package/dist/channels/aun.js +66 -48
- package/dist/channels/dingtalk.js +8 -2
- package/dist/channels/feishu.js +6 -1
- package/dist/channels/wecom.js +7 -3
- package/dist/cli/agent-create.js +30 -6
- package/dist/cli/agent.js +30 -37
- package/dist/cli/aid-bind-command.js +16 -2
- package/dist/cli/aun-commands.js +31 -23
- package/dist/cli/config.js +15 -18
- package/dist/cli/contact.js +9 -12
- package/dist/cli/ctl-command.js +3 -3
- package/dist/cli/daemon-commands.js +242 -17
- package/dist/cli/diag.js +7 -3
- package/dist/cli/fs-command.js +5 -4
- package/dist/cli/handoff-command.js +11 -11
- package/dist/cli/help.js +18 -0
- package/dist/cli/index.js +25 -13
- package/dist/cli/init-channel.js +2 -1
- package/dist/cli/memory-command.js +47 -27
- package/dist/cli/model.js +5 -4
- package/dist/cli/queue-command.js +3 -3
- package/dist/cli/response.js +5 -4
- package/dist/cli/restart-monitor.js +35 -13
- package/dist/cli/skill-command.js +13 -13
- package/dist/cli/task-command.js +3 -3
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +5 -3
- package/dist/cli/trigger-command.js +3 -3
- package/dist/cli/version.js +6 -2
- package/dist/config/config-field-policy.js +1 -0
- package/dist/contracts/ecweb-service-protocol.js +44 -0
- package/dist/core/agent-runtime.js +6 -0
- package/dist/core/auth/agent-delegation.js +7 -4
- package/dist/core/auth/auth-gateway.js +3 -1
- package/dist/core/auth/authorization-audit.js +5 -1
- package/dist/core/auth/debug-context.js +5 -1
- package/dist/core/auth/operation-catalog.js +17 -1
- package/dist/core/bootstrap-service.js +20 -12
- package/dist/core/command/cli-intent-parser.js +5 -2
- package/dist/core/command/command-handler.js +20 -5
- package/dist/core/command/menu-catalog.js +48 -12
- package/dist/core/command/menu-handler.js +74 -6
- package/dist/core/command/menu-protocol.js +20 -1
- package/dist/core/command/slash-gate.js +17 -3
- package/dist/core/command/slash-handler.js +90 -7
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/interaction-router.js +2 -1
- package/dist/core/ipc-registration.js +26 -7
- package/dist/core/memory/batch-meta.js +2 -0
- package/dist/core/memory/consolidation-context.js +42 -51
- package/dist/core/memory/consolidation-ops-records.js +39 -7
- package/dist/core/memory/consolidation-trigger.js +71 -64
- package/dist/core/memory/consolidation.js +226 -20
- package/dist/core/memory/memory-writer.js +160 -34
- package/dist/core/memory/relation-idle-monitor.js +287 -0
- package/dist/core/memory/relation-sweep.js +206 -0
- package/dist/core/message/aun-ipc-sender.js +5 -0
- package/dist/core/message/message-bridge.js +64 -55
- package/dist/core/message/message-log.js +17 -0
- package/dist/core/message/message-queue.js +6 -0
- package/dist/core/message/outbound.js +70 -0
- package/dist/core/message/peer-mode.js +7 -2
- package/dist/core/message/response-engine.js +341 -73
- package/dist/core/ownership/owner-resolver.js +465 -140
- package/dist/core/permission/ec-command-parser.js +19 -3
- package/dist/core/permission/protected-paths.js +51 -0
- package/dist/core/permission/tool-policy.js +7 -7
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/session/session-manager.js +25 -5
- package/dist/core/system-session/memory.js +3 -18
- package/dist/ecweb/basic-settings-service.js +709 -0
- package/dist/ecweb/providers-service.js +172 -0
- package/dist/index.js +92 -4
- package/dist/ipc.js +78 -11
- package/dist/stats/db.js +33 -0
- package/dist/stats/index.js +2 -2
- package/dist/stats/query.js +246 -0
- package/dist/stats/writer.js +41 -1
- package/dist/trigger/application-service.js +12 -0
- package/dist/trigger/authorization.js +2 -2
- package/dist/trigger/history.js +36 -0
- package/dist/trigger/scheduler.js +3 -0
- package/dist/utils/ecweb-supervisor.js +31 -10
- package/dist/utils/error-utils.js +8 -4
- package/dist/utils/evolcore-version.js +3 -2
- package/dist/utils/host-info.js +52 -0
- package/dist/utils/welcome.js +18 -24
- package/ecagent/dist/harness/agent-harness.js +4 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/agent.md +1 -1
- package/kits/docs/evolcore/aid.md +58 -8
- package/kits/docs/evolcore/config.md +9 -7
- package/kits/docs/evolcore/memory-architecture.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +28 -7
- package/kits/docs/evolcore/memory-tags.md +1 -1
- package/kits/docs/evolcore/memory.md +1 -1
- package/kits/docs/evolcore/menu-client.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +6 -0
- package/kits/docs/evolcore/msg.md +1 -1
- package/kits/eck_manifest.json +3 -3
- package/kits/rules/04-relation.md +3 -2
- package/kits/schemas/agent-config.schema.13.json +5 -1
- package/kits/templates/memory-consolidation/generalize.md +32 -4
- package/kits/templates/memory-consolidation/notes.md +2 -2
- package/kits/templates/memory-consolidation/promote.md +10 -2
- package/kits/templates/system-fragments/baseagent.md +5 -0
- package/kits/templates/system-fragments/memory.md +12 -3
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/{rules/07-memory-and-thread.md → templates/system-fragments/thread.md} +17 -80
- package/package.json +3 -3
|
@@ -22,7 +22,7 @@ import path from 'path';
|
|
|
22
22
|
import { spawn } from 'child_process';
|
|
23
23
|
import { getPackageRoot, agentDir, resolvePaths } from '../paths.js';
|
|
24
24
|
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
25
|
-
import {
|
|
25
|
+
import { daemonUnavailableMessage, queryDaemon } from './help.js';
|
|
26
26
|
import { readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
27
27
|
import { authorizeLocalCommand, TASK_RUNTIME_CONTEXT_ENV } from './task-context.js';
|
|
28
28
|
import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
|
|
@@ -87,17 +87,17 @@ async function cmdSkillRefresh(args) {
|
|
|
87
87
|
process.exitCode = 1;
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
const { response, transportError } = await queryDaemon(resolvePaths().socket, {
|
|
91
|
+
type: 'skill.refresh',
|
|
92
|
+
aid,
|
|
93
|
+
argv,
|
|
94
|
+
...(sessionId ? { sessionId, delegationToken } : {}),
|
|
95
|
+
}, 30_000);
|
|
96
|
+
if (transportError) {
|
|
97
|
+
if (asJson)
|
|
98
|
+
console.log(JSON.stringify({ ok: false, code: 'DAEMON_UNAVAILABLE', error: transportError.message }, null, 2));
|
|
99
|
+
else
|
|
100
|
+
console.error(`❌ 刷新失败 (DAEMON_UNAVAILABLE): ${transportError.message}`);
|
|
101
101
|
process.exitCode = 1;
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
@@ -109,7 +109,7 @@ async function cmdSkillRefresh(args) {
|
|
|
109
109
|
}
|
|
110
110
|
if (!response?.ok) {
|
|
111
111
|
const code = response?.code ?? (response ? 'FAILED' : 'DAEMON_UNAVAILABLE');
|
|
112
|
-
console.error(`❌ 刷新失败 (${code}): ${response?.error ?? '无法连接 evolcore daemon'}`);
|
|
112
|
+
console.error(`❌ 刷新失败 (${code}): ${response?.error ?? daemonUnavailableMessage(transportError, '无法连接 evolcore daemon')}`);
|
|
113
113
|
if (response?.data?.nextStep)
|
|
114
114
|
console.error(` ${response.data.nextStep}`);
|
|
115
115
|
process.exitCode = 1;
|
package/dist/cli/task-command.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolvePaths } from '../paths.js';
|
|
2
|
-
import {
|
|
2
|
+
import { daemonUnavailableMessage, queryDaemon } from './help.js';
|
|
3
3
|
import { readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
4
4
|
import { wantsHelp } from './help.js';
|
|
5
5
|
const TASK_HELP = `用法: ec task resume <sessionId> [--format json]
|
|
@@ -39,14 +39,14 @@ export async function cmdTask(args) {
|
|
|
39
39
|
printError('DELEGATION_REQUIRED', '托管任务缺少 delegation token');
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
-
const result = await
|
|
42
|
+
const { response: result, transportError } = await queryDaemon(resolvePaths().socket, {
|
|
43
43
|
type: 'task.resume',
|
|
44
44
|
argv: ['task', ...args],
|
|
45
45
|
targetSessionId,
|
|
46
46
|
...(sessionId ? { sessionId, delegationToken } : {}),
|
|
47
47
|
}, 10_000);
|
|
48
48
|
if (!result) {
|
|
49
|
-
printError('IPC_UNAVAILABLE', '无法连接 EvolCore 服务;请查询任务状态后再决定是否重试');
|
|
49
|
+
printError('IPC_UNAVAILABLE', daemonUnavailableMessage(transportError, '无法连接 EvolCore 服务;请查询任务状态后再决定是否重试'));
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
if (json)
|
package/dist/cli/task-context.js
CHANGED
|
@@ -47,6 +47,7 @@ function normalizeTaskRuntimeContext(value) {
|
|
|
47
47
|
executionSource: value.executionSource === 'fullaccess-command'
|
|
48
48
|
|| value.executionSource === 'trigger'
|
|
49
49
|
|| value.executionSource === 'agent-policy'
|
|
50
|
+
|| value.executionSource === 'session-policy'
|
|
50
51
|
? value.executionSource : undefined,
|
|
51
52
|
daemonRuntimeEpoch: optionalString(value.daemonRuntimeEpoch),
|
|
52
53
|
threadId: optionalString(value.threadId),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolvePaths } from '../paths.js';
|
|
2
|
-
import {
|
|
2
|
+
import { daemonUnavailableMessage, queryDaemon } from './help.js';
|
|
3
3
|
import { readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
4
4
|
import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
|
|
5
5
|
import { isMainSessionThreadId, resolveThreadRef } from '../core/thread/topic-id.js';
|
|
@@ -91,7 +91,7 @@ async function sendThreadOp(req, commandArgv) {
|
|
|
91
91
|
const cred = readDelegationCredential();
|
|
92
92
|
const { socket } = resolvePaths();
|
|
93
93
|
const invokingSessionId = process.env.EVOLCORE_SESSION_ID;
|
|
94
|
-
const result = await
|
|
94
|
+
const { response: result, transportError } = await queryDaemon(socket, {
|
|
95
95
|
...req,
|
|
96
96
|
type: 'thread.op',
|
|
97
97
|
argv: commandArgv,
|
|
@@ -99,7 +99,9 @@ async function sendThreadOp(req, commandArgv) {
|
|
|
99
99
|
sessionId: invokingSessionId,
|
|
100
100
|
}, 10000);
|
|
101
101
|
if (!result)
|
|
102
|
-
return {
|
|
102
|
+
return {
|
|
103
|
+
ok: false, code: 'DAEMON_UNAVAILABLE', error: daemonUnavailableMessage(transportError, 'daemon 暂时不可用'),
|
|
104
|
+
};
|
|
103
105
|
return result;
|
|
104
106
|
}
|
|
105
107
|
function printResult(data, jsonFormat) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { resolvePaths } from '../paths.js';
|
|
4
|
-
import {
|
|
4
|
+
import { daemonUnavailableMessage, queryDaemon } from './help.js';
|
|
5
5
|
import { isHelpFlag, wantsHelp } from './help.js';
|
|
6
6
|
import { readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
7
7
|
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
@@ -642,9 +642,9 @@ async function request(cmd, timeoutMs = 10_000) {
|
|
|
642
642
|
}
|
|
643
643
|
catch { }
|
|
644
644
|
}
|
|
645
|
-
const res = await
|
|
645
|
+
const { response: res, transportError } = await queryDaemon(resolvePaths().socket, actorSessionId ? { ...cmd, actorSessionId, delegationToken } : delegationToken ? { ...cmd, delegationToken } : { ...cmd, controlToken }, timeoutMs);
|
|
646
646
|
if (!res)
|
|
647
|
-
throw new Error('
|
|
647
|
+
throw new Error(`错误: ${daemonUnavailableMessage(transportError, '无法连接 evolcore 服务')}`);
|
|
648
648
|
if (res.ok === false || res.error)
|
|
649
649
|
throw new Error(res.error || 'trigger command failed');
|
|
650
650
|
return res;
|
package/dist/cli/version.js
CHANGED
|
@@ -6,6 +6,7 @@ import path from 'path';
|
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { MAIN_PACKAGE_NAME, PRODUCT_DISPLAY_NAME, WEB_PACKAGE_NAME } from '../product.js';
|
|
8
8
|
import { getArgValue, wantsHelp } from './help.js';
|
|
9
|
+
import { resolveGlobalPkg } from '../utils/npm-ops.js';
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const PACKAGE_ROOT = path.resolve(__dirname, '../..');
|
|
11
12
|
const RESET = '\x1b[0m';
|
|
@@ -54,8 +55,11 @@ export function handleVersion(args) {
|
|
|
54
55
|
const mainPkg = readPkgJson(PACKAGE_ROOT);
|
|
55
56
|
const mainEntry = path.join(PACKAGE_ROOT, 'dist', 'cli', 'index.js');
|
|
56
57
|
const mainTs = getFileModTime(mainEntry);
|
|
57
|
-
//
|
|
58
|
-
|
|
58
|
+
// EC Web may be in the repository (dev mode) or nested under the globally
|
|
59
|
+
// installed EvolCore package. Use the shared resolver so both layouts report
|
|
60
|
+
// the actual installed package instead of `?`.
|
|
61
|
+
const ecwebInstalled = resolveGlobalPkg(WEB_PACKAGE_NAME);
|
|
62
|
+
const ecwebDir = ecwebInstalled ? path.dirname(ecwebInstalled.path) : path.join(PACKAGE_ROOT, 'ecweb');
|
|
59
63
|
const ecwebPkg = readPkgJson(ecwebDir);
|
|
60
64
|
const ecwebEntry = path.join(ecwebDir, 'dist', 'index.js');
|
|
61
65
|
const ecwebTs = getFileModTime(ecwebEntry);
|
|
@@ -227,6 +227,7 @@ export function resolveConfigFieldRule(fieldPath, scope) {
|
|
|
227
227
|
}
|
|
228
228
|
// 数值参数:整理节奏与容量上限,均 owner-only
|
|
229
229
|
if (field === 'memory.idleHours'
|
|
230
|
+
|| field === 'memory.maxUnconsolidatedHours'
|
|
230
231
|
|| field === 'memory.checkIntervalHours'
|
|
231
232
|
|| field === 'memory.backlogHours'
|
|
232
233
|
|| field === 'memory.indexMaxBytes'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const ECWEB_SERVICE_PROTOCOL = Object.freeze({
|
|
2
|
+
name: 'evolcore.ecweb',
|
|
3
|
+
major: 1,
|
|
4
|
+
minor: 1,
|
|
5
|
+
capabilities: Object.freeze([
|
|
6
|
+
'daemon.status',
|
|
7
|
+
'agent.directory',
|
|
8
|
+
'aun.directory',
|
|
9
|
+
'menu.execute',
|
|
10
|
+
'provider.management',
|
|
11
|
+
'basic-settings',
|
|
12
|
+
'model.catalog',
|
|
13
|
+
'baseagent.snapshot',
|
|
14
|
+
'monitor.snapshot',
|
|
15
|
+
'monitor.subscription',
|
|
16
|
+
'remote-auth',
|
|
17
|
+
'trigger.history',
|
|
18
|
+
]),
|
|
19
|
+
});
|
|
20
|
+
export function validateEcwebClientProtocolRequirements(value) {
|
|
21
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
22
|
+
return 'ECWeb client protocol requirements are missing or malformed';
|
|
23
|
+
}
|
|
24
|
+
const requirements = value;
|
|
25
|
+
if (requirements.name !== ECWEB_SERVICE_PROTOCOL.name) {
|
|
26
|
+
return `Unsupported ECWeb protocol name: ${String(requirements.name ?? 'missing')}`;
|
|
27
|
+
}
|
|
28
|
+
if (requirements.major !== ECWEB_SERVICE_PROTOCOL.major) {
|
|
29
|
+
return `Unsupported ECWeb protocol major version: ${String(requirements.major ?? 'missing')}`;
|
|
30
|
+
}
|
|
31
|
+
if (!Number.isInteger(requirements.minMinor) || requirements.minMinor < 0
|
|
32
|
+
|| requirements.minMinor > ECWEB_SERVICE_PROTOCOL.minor) {
|
|
33
|
+
return `ECWeb requires protocol minor ${String(requirements.minMinor ?? 'missing')}, host supports ${ECWEB_SERVICE_PROTOCOL.minor}`;
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(requirements.requiredCapabilities)
|
|
36
|
+
|| requirements.requiredCapabilities.some(capability => typeof capability !== 'string' || !capability.trim())) {
|
|
37
|
+
return 'ECWeb required capabilities are malformed';
|
|
38
|
+
}
|
|
39
|
+
const supported = new Set(ECWEB_SERVICE_PROTOCOL.capabilities);
|
|
40
|
+
const missing = requirements.requiredCapabilities.filter(capability => !supported.has(capability));
|
|
41
|
+
if (missing.length > 0)
|
|
42
|
+
return `ECWeb requires unsupported capabilities: ${missing.join(', ')}`;
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
@@ -226,6 +226,11 @@ export function createAgentInitializationRuntime({ agentRegistry, agentMap, chan
|
|
|
226
226
|
bindOwner: async (aid, ownerAid) => {
|
|
227
227
|
await ownerResolver.bind(aid, ownerAid);
|
|
228
228
|
},
|
|
229
|
+
ownerBindActive: (aid, ownerAid) => {
|
|
230
|
+
const task = ownerResolver.snapshot(aid).task;
|
|
231
|
+
return task?.operation === 'bind' && task.owner_aid === ownerAid
|
|
232
|
+
&& (task.status === 'pending' || task.status === 'completed');
|
|
233
|
+
},
|
|
229
234
|
checkOwner: async (aid, expectedOwnerAid) => {
|
|
230
235
|
const view = await ownerResolver.refresh(aid, { maxAgeMs: OWNERSHIP_AUTHORIZATION_MAX_AGE_MS });
|
|
231
236
|
if (view.consensusStatus === 'consensus')
|
|
@@ -257,6 +262,7 @@ export function createAgentInitializationRuntime({ agentRegistry, agentMap, chan
|
|
|
257
262
|
const agent = agentRegistry.get(aid);
|
|
258
263
|
return !!agent && agent.config.enabled !== false && agentMap.has(`${aid}::${agent.baseagent}`) && isConnected(aid);
|
|
259
264
|
},
|
|
265
|
+
authorize: actor => actor === 'local-control' || ownerResolver.daemonOwners().includes(actor),
|
|
260
266
|
});
|
|
261
267
|
}
|
|
262
268
|
export function recoverIncompleteAgentInitializations(service, agentsDir = resolvePaths().agentsDir) {
|
|
@@ -440,7 +440,7 @@ export async function authorizeDelegatedAunMsgSend(registry, input) {
|
|
|
440
440
|
catch {
|
|
441
441
|
return { ok: false, code: 'INVALID_DELEGATION', reason: 'Delegation relation key is invalid' };
|
|
442
442
|
}
|
|
443
|
-
await refreshOwnershipForAuthorization(grant.selfAid);
|
|
443
|
+
await refreshOwnershipForAuthorization(grant.selfAid, { allowTransient: true });
|
|
444
444
|
const builtSubject = buildAuthSubject({
|
|
445
445
|
selfAid: grant.selfAid,
|
|
446
446
|
actorId: grant.actorId,
|
|
@@ -503,10 +503,13 @@ function isValidFullAccessExecutionIdentity(identity) {
|
|
|
503
503
|
|| identity.processRole !== 'fullaccess-run'
|
|
504
504
|
|| identity.dataScope !== 'daemon')
|
|
505
505
|
return false;
|
|
506
|
-
if (identity.source === 'fullaccess-command' || identity.source === 'agent-policy') {
|
|
506
|
+
if (identity.source === 'fullaccess-command' || identity.source === 'agent-policy' || identity.source === 'session-policy') {
|
|
507
507
|
return typeof identity.authorizedBy === 'string' && identity.authorizedBy.length > 0
|
|
508
|
-
&& (identity.source
|
|
509
|
-
|| (typeof identity.agentAid === 'string'
|
|
508
|
+
&& (identity.source === 'fullaccess-command'
|
|
509
|
+
|| (typeof identity.agentAid === 'string'
|
|
510
|
+
&& identity.agentAid.length > 0
|
|
511
|
+
&& (identity.source !== 'session-policy'
|
|
512
|
+
|| (typeof identity.sessionId === 'string' && identity.sessionId.length > 0))));
|
|
510
513
|
}
|
|
511
514
|
return identity.source === 'trigger'
|
|
512
515
|
&& typeof identity.authorizedBy === 'string' && identity.authorizedBy.length > 0
|
|
@@ -107,7 +107,8 @@ export async function authorizeFreshOperation(params) {
|
|
|
107
107
|
const configCommand = params.resolvedConfigCommand;
|
|
108
108
|
const changesOwner = configCommand?.kind === 'field' && configCommand.subcommand !== 'get'
|
|
109
109
|
&& ['owner', 'owners'].includes(configCommand.field.split('.')[0]);
|
|
110
|
-
const force = changesOwner ||
|
|
110
|
+
const force = changesOwner || ['ec.aid.bind', 'ec.aid.bind.status', 'ec.aid.unbind', 'ec.aid.transfer']
|
|
111
|
+
.includes(params.intent.operation) || params.intent.operation === 'config.restore';
|
|
111
112
|
const operation = getOperationMeta(params.intent.operation);
|
|
112
113
|
const sensitive = force || params.intent.dangerous || operation?.dangerous || operation?.category !== 'read'
|
|
113
114
|
|| configCommand?.dangerous || subject.processRole === 'fullaccess-run';
|
|
@@ -116,6 +117,7 @@ export async function authorizeFreshOperation(params) {
|
|
|
116
117
|
...(sensitive ? {} : { maxAgeMs: OWNERSHIP_CACHE_MAX_AGE_MS }),
|
|
117
118
|
includeDaemon: subject.isDaemonOwner || subject.processRole === 'fullaccess-run'
|
|
118
119
|
|| params.intent.scope === 'process' || params.intent.scope === 'control' || force,
|
|
120
|
+
allowTransient: !force,
|
|
119
121
|
});
|
|
120
122
|
const current = buildAuthSubject({
|
|
121
123
|
selfAid: subject.selfAid, agentName: subject.agentName, actorId: subject.actorId,
|
|
@@ -25,11 +25,15 @@ export function auditCommandAuthorization(event) {
|
|
|
25
25
|
}
|
|
26
26
|
export function auditFullAccessEvent(input) {
|
|
27
27
|
const configurationEvent = input.event === 'fullaccess.trigger.configured'
|
|
28
|
-
|| input.event === 'fullaccess.trigger.deescalated'
|
|
28
|
+
|| input.event === 'fullaccess.trigger.deescalated'
|
|
29
|
+
|| input.event === 'fullaccess.session-policy.configured'
|
|
30
|
+
|| input.event === 'fullaccess.session-policy.deescalated';
|
|
29
31
|
const executionEvent = input.event === 'fullaccess.execution.started'
|
|
30
32
|
|| input.event === 'fullaccess.execution.ended'
|
|
31
33
|
|| input.event === 'fullaccess.agent-policy.execution.started'
|
|
32
34
|
|| input.event === 'fullaccess.agent-policy.execution.ended'
|
|
35
|
+
|| input.event === 'fullaccess.session-policy.execution.started'
|
|
36
|
+
|| input.event === 'fullaccess.session-policy.execution.ended'
|
|
33
37
|
|| input.event === 'fullaccess.trigger.execution.started'
|
|
34
38
|
|| input.event === 'fullaccess.trigger.execution.ended';
|
|
35
39
|
// The scheduler triggers a run, but the executing subject is the task-bound
|
|
@@ -127,7 +127,11 @@ export function buildDebugTaskContext(input) {
|
|
|
127
127
|
chatType,
|
|
128
128
|
selfAid: session.selfAID,
|
|
129
129
|
peerId,
|
|
130
|
-
|
|
130
|
+
// 系统会话的目标关系在 metadata.peerKey(与 response-engine 的任务上下文同源),
|
|
131
|
+
// 不能从后台伪对端 peerId 推。
|
|
132
|
+
peerKey: meta.systemSession && typeof meta.peerKey === 'string' && meta.peerKey
|
|
133
|
+
? meta.peerKey
|
|
134
|
+
: (channel && peerKeyId ? formatPeerKey(channel, peerKeyId) : undefined),
|
|
131
135
|
peerName: meta.peerName,
|
|
132
136
|
peerType: meta.peerType,
|
|
133
137
|
threadId,
|
|
@@ -323,6 +323,22 @@ const OPERATIONS = [
|
|
|
323
323
|
description: '更新当前主会话或话题的 idle 触发设置',
|
|
324
324
|
sources: ['slash', 'menu', 'ecweb', 'control'],
|
|
325
325
|
},
|
|
326
|
+
{
|
|
327
|
+
id: 'session.fullaccess.current',
|
|
328
|
+
category: 'read',
|
|
329
|
+
dangerous: true,
|
|
330
|
+
defaultScopes: ['relation'],
|
|
331
|
+
description: '查看当前 EC 会话的 fullaccess 状态',
|
|
332
|
+
sources: ['slash', 'menu'],
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
id: 'session.fullaccess.update',
|
|
336
|
+
category: 'dangerous',
|
|
337
|
+
dangerous: true,
|
|
338
|
+
defaultScopes: ['relation'],
|
|
339
|
+
description: '更新当前 EC 会话的 fullaccess 状态',
|
|
340
|
+
sources: ['slash', 'menu'],
|
|
341
|
+
},
|
|
326
342
|
{
|
|
327
343
|
id: 'session.topic.current',
|
|
328
344
|
category: 'read',
|
|
@@ -1123,7 +1139,7 @@ const OPERATIONS = [
|
|
|
1123
1139
|
},
|
|
1124
1140
|
{
|
|
1125
1141
|
id: 'ec.aid.bind', category: 'write-agent', dangerous: true, defaultScopes: ['agent'],
|
|
1126
|
-
description: 'Request a protocol Owner binding using the subject SDK client', sources: ['agent-tool', 'menu.cli'],
|
|
1142
|
+
description: 'Request or decide a protocol Owner binding using the subject SDK client', sources: ['agent-tool', 'menu.cli'],
|
|
1127
1143
|
},
|
|
1128
1144
|
{
|
|
1129
1145
|
id: 'ec.aid.bind.status', category: 'read', dangerous: false, defaultScopes: ['agent'],
|
|
@@ -3,8 +3,8 @@ import { ownerResolver, OWNERSHIP_AUTHORIZATION_MAX_AGE_MS } from './ownership/o
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { randomBytes } from 'crypto';
|
|
6
|
-
import { kitsTemplatesDir,
|
|
7
|
-
import { buildInitialAgentMd, resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
6
|
+
import { kitsTemplatesDir, resolvePaths } from '../paths.js';
|
|
7
|
+
import { buildInitialAgentMd, resolveAgentDisplayName, verifyLocalAgentMd } from '../aun/aid/agentmd.js';
|
|
8
8
|
import { logger } from '../utils/logger.js';
|
|
9
9
|
import { readAgentWelcomeTemplate } from '../utils/welcome.js';
|
|
10
10
|
import { loadAgent, saveAgent } from '../config-store.js';
|
|
@@ -308,7 +308,13 @@ export class BootstrapService {
|
|
|
308
308
|
return false;
|
|
309
309
|
}
|
|
310
310
|
if (starting) {
|
|
311
|
-
|
|
311
|
+
// AUN creation/binding has already published agent.md, and the fresh
|
|
312
|
+
// Owner consensus above requires it. A normal upload here re-signs the
|
|
313
|
+
// approved original while the Owner may still be confirming its hash
|
|
314
|
+
// and ETag. Sending a welcome must not mutate that binding evidence.
|
|
315
|
+
if (channelType !== 'aun') {
|
|
316
|
+
await this.publishAgentMdIfSupported(ctx.adapter, aid, agentName);
|
|
317
|
+
}
|
|
312
318
|
this.setLifecycle(agent, aid, 'bootstrapping');
|
|
313
319
|
lifecycleStarted = true;
|
|
314
320
|
}
|
|
@@ -546,18 +552,20 @@ export class BootstrapService {
|
|
|
546
552
|
async publishAgentMdIfSupported(adapter, aid, fallbackName) {
|
|
547
553
|
if (typeof adapter.uploadAgentMd !== 'function')
|
|
548
554
|
return;
|
|
549
|
-
const
|
|
550
|
-
|
|
555
|
+
const verification = await verifyLocalAgentMd(aid);
|
|
556
|
+
if (verification.status === 'verified') {
|
|
557
|
+
logger.info(`[Bootstrap] Preserving verified local agent.md for ${aid}; upload skipped`);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (verification.status !== 'missing') {
|
|
561
|
+
throw new Error(`Local agent.md cannot be safely republished for ${aid}: ${verification.reason ?? verification.status}`);
|
|
562
|
+
}
|
|
563
|
+
const content = buildInitialAgentMd({
|
|
551
564
|
aid,
|
|
552
565
|
name: fallbackName,
|
|
553
566
|
type: 'agent',
|
|
554
567
|
});
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
logger.info(`[Bootstrap] Published agent.md for ${aid}`);
|
|
558
|
-
}
|
|
559
|
-
catch (e) {
|
|
560
|
-
logger.warn(`[Bootstrap] Failed to publish agent.md for ${aid}: ${e instanceof Error ? e.message : String(e)}`);
|
|
561
|
-
}
|
|
568
|
+
await adapter.uploadAgentMd(content);
|
|
569
|
+
logger.info(`[Bootstrap] Published initial agent.md for ${aid}`);
|
|
562
570
|
}
|
|
563
571
|
}
|
|
@@ -243,11 +243,14 @@ function parseAidCommand(argv, source, defaultSelfAid) {
|
|
|
243
243
|
try {
|
|
244
244
|
const command = parseOwnershipCommand(argv.slice(1));
|
|
245
245
|
const subjectAid = command.subjectAid ?? defaultSelfAid;
|
|
246
|
-
|
|
246
|
+
const isMutation = command.action !== 'status';
|
|
247
|
+
return recognized(isMutation ? 'ec.aid.bind' : 'ec.aid.bind.status', 'agent', source, {
|
|
247
248
|
...(subjectAid ? { self: subjectAid } : {}),
|
|
248
249
|
...(command.subjectAid ? { subjectAid: command.subjectAid } : {}),
|
|
249
250
|
...(command.action === 'bind' ? { ownerAid: command.ownerAid } : {}),
|
|
250
|
-
|
|
251
|
+
...(command.action === 'accept' || command.action === 'reject'
|
|
252
|
+
? { decision: command.action, operationId: command.operationId } : {}),
|
|
253
|
+
}, argv, isMutation);
|
|
251
254
|
}
|
|
252
255
|
catch (error) {
|
|
253
256
|
return raw(source, argv, error instanceof Error ? error.message : 'Invalid aid bind command');
|
|
@@ -1380,6 +1380,7 @@ export class CommandHandler {
|
|
|
1380
1380
|
? {
|
|
1381
1381
|
kind: 'system.error',
|
|
1382
1382
|
text: result.text,
|
|
1383
|
+
...(result.raw !== undefined ? { raw: result.raw } : {}),
|
|
1383
1384
|
subtype: `session.${content.trim().toLowerCase().startsWith('/new') ? 'new' : 'renew'}.error`,
|
|
1384
1385
|
recoverable: true,
|
|
1385
1386
|
}
|
|
@@ -1394,7 +1395,12 @@ export class CommandHandler {
|
|
|
1394
1395
|
catch (error) {
|
|
1395
1396
|
if (error instanceof BaseagentRunnerUnavailableError) {
|
|
1396
1397
|
logger.error(`[CommandHandler] baseagent mismatch blocked: channel=${channel} requested=${error.baseagent} owner=${error.evolagentName} available=${error.availableBaseagents.join(',') || '<none>'}`);
|
|
1397
|
-
return {
|
|
1398
|
+
return {
|
|
1399
|
+
kind: 'command.error',
|
|
1400
|
+
text: this.formatBaseagentUnavailable(error),
|
|
1401
|
+
raw: error.message,
|
|
1402
|
+
reason: error.code,
|
|
1403
|
+
};
|
|
1398
1404
|
}
|
|
1399
1405
|
throw error;
|
|
1400
1406
|
}
|
|
@@ -2196,8 +2202,9 @@ export class CommandHandler {
|
|
|
2196
2202
|
const command = parseOwnershipCommand(argv);
|
|
2197
2203
|
const targetAid = command.subjectAid ?? grant.selfAid;
|
|
2198
2204
|
ownerResolver.assertLocalSubject(targetAid);
|
|
2199
|
-
|
|
2200
|
-
|
|
2205
|
+
const isMutation = command.action !== 'status';
|
|
2206
|
+
if (isMutation && !['request', 'bypass'].includes(grant.permissionMode ?? '')) {
|
|
2207
|
+
return { ok: false, code: 'NOT_ALLOWED', error: 'Binding changes and decisions require a request or bypass task' };
|
|
2201
2208
|
}
|
|
2202
2209
|
await refreshOwnershipForAuthorization(targetAid);
|
|
2203
2210
|
let conversationId;
|
|
@@ -2215,12 +2222,20 @@ export class CommandHandler {
|
|
|
2215
2222
|
if (targetAid !== grant.selfAid && !subject.isDaemonOwner) {
|
|
2216
2223
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'A different local subject requires the current daemon Owner' };
|
|
2217
2224
|
}
|
|
2218
|
-
if (!subject.isDaemonOwner && (
|
|
2225
|
+
if (!subject.isDaemonOwner && (isMutation ? subject.role !== 'owner' : !isManagementRole(subject.role))) {
|
|
2219
2226
|
return { ok: false, code: 'OWNER_REQUIRED', error: 'Ownership binding requires the subject Owner or daemon Owner; first daemon binding uses the local CLI' };
|
|
2220
2227
|
}
|
|
2221
2228
|
const decision = await authorizeFreshOperation({
|
|
2222
2229
|
source: 'agent-tool', subject,
|
|
2223
|
-
intent: {
|
|
2230
|
+
intent: {
|
|
2231
|
+
operation: isMutation ? 'ec.aid.bind' : 'ec.aid.bind.status', scope: 'agent', source: 'agent-tool',
|
|
2232
|
+
args: {
|
|
2233
|
+
self: targetAid,
|
|
2234
|
+
...(command.action === 'bind' ? { ownerAid: command.ownerAid } : {}),
|
|
2235
|
+
...(command.action === 'accept' || command.action === 'reject'
|
|
2236
|
+
? { decision: command.action, operationId: command.operationId } : {}),
|
|
2237
|
+
},
|
|
2238
|
+
},
|
|
2224
2239
|
auditMetadata: { taskId: grant.taskId, messageId: grant.messageId },
|
|
2225
2240
|
});
|
|
2226
2241
|
if (!decision.allow)
|
|
@@ -6,6 +6,7 @@ import { authorizationDenialData, formatAuthorizationDenial } from '../auth/auth
|
|
|
6
6
|
import { ConfigTarget, read as readConfig } from '../../config/config-manager.js';
|
|
7
7
|
import { readRolesConfig } from '../../config/roles.js';
|
|
8
8
|
import { normalizeMenuError } from './menu-protocol.js';
|
|
9
|
+
import { isAgentFullAccessOwner } from '../role/runtime-policy.js';
|
|
9
10
|
import { isValidAid } from '../../aun/aid/validation.js';
|
|
10
11
|
import { DEFAULT_SESSION_LOOP_SECONDS, MAX_SESSION_LOOP_SECONDS, MIN_SESSION_LOOP_SECONDS } from '../session/session-idle-monitor.js';
|
|
11
12
|
const DEFAULT_DIRECTORY = { getGroup: groupInfo, getMembers: groupMembers };
|
|
@@ -79,6 +80,19 @@ const ENTRIES = [
|
|
|
79
80
|
valueSchema: { type: 'boolean' },
|
|
80
81
|
control: 'toggle-with-number',
|
|
81
82
|
},
|
|
83
|
+
{
|
|
84
|
+
id: 'session.fullaccess',
|
|
85
|
+
type: 'setting',
|
|
86
|
+
contexts: ['private_relation'],
|
|
87
|
+
name: 'session',
|
|
88
|
+
label: '当前 EC 会话 fullaccess',
|
|
89
|
+
section: 'relation_behavior',
|
|
90
|
+
order: 6,
|
|
91
|
+
query: { cmd: '/session', args: { view: 'fullaccess' }, operation: 'session.fullaccess.current', scope: 'relation' },
|
|
92
|
+
update: { cmd: '/session', args: { resource: 'fullaccess' }, operation: 'session.fullaccess.update', scope: 'relation' },
|
|
93
|
+
valueSchema: { type: 'boolean' },
|
|
94
|
+
control: 'toggle',
|
|
95
|
+
},
|
|
82
96
|
relation('model.relation', 'model', '模型', 10, '/model', 'model.current', { options: { cmd: '/model', operation: 'model.list', scope: 'relation', mode: 'dynamic' }, update: { cmd: '/model', operation: 'model.use', scope: 'relation' }, control: 'select' }),
|
|
83
97
|
relation('effort.relation', 'effort', '推理强度', 20, '/effort', 'model.effort.current', { options: { cmd: '/effort', operation: 'model.effort.current', scope: 'relation', mode: 'dynamic', dependsOn: ['model.relation'] }, update: { cmd: '/effort', operation: 'model.effort', scope: 'relation' }, control: 'select' }),
|
|
84
98
|
relation('chatmode.relation', 'chatmode', '对话模式', 30, '/chatmode', 'chatmode.current', { options: { cmd: '/chatmode', operation: 'chatmode.current', scope: 'relation', mode: 'static', items: [{ value: 'interactive', label: '交互模式' }, { value: 'proactive', label: '主动模式' }] }, update: { cmd: '/chatmode', operation: 'chatmode.update', scope: 'relation' }, control: 'select' }),
|
|
@@ -290,8 +304,8 @@ export function bindMenuExecutionContextArgs(args, context) {
|
|
|
290
304
|
if (!args && context.kind !== 'group_relation')
|
|
291
305
|
return args;
|
|
292
306
|
const { context: _context, ...bound } = args ?? {};
|
|
293
|
-
const
|
|
294
|
-
if (
|
|
307
|
+
const isSessionScoped = ['loop', 'fullaccess'].includes(String(bound.resource ?? bound.view ?? ''));
|
|
308
|
+
if (isSessionScoped)
|
|
295
309
|
return bound;
|
|
296
310
|
if (context.kind === 'private_relation' || context.kind === 'group_relation') {
|
|
297
311
|
if (bound.scope !== 'role')
|
|
@@ -368,6 +382,17 @@ async function operationView(binding, context, options = {}) {
|
|
|
368
382
|
reason: { code: 'NOT_ALLOWED', message: 'Pairing code issuance requires encrypted DaemonOwner authentication' },
|
|
369
383
|
};
|
|
370
384
|
}
|
|
385
|
+
if (binding.operation.startsWith('session.fullaccess.')) {
|
|
386
|
+
const allowed = context.kind === 'private_relation'
|
|
387
|
+
&& context.subject.chatType === 'private'
|
|
388
|
+
&& context.subject.isDaemonOwner === true
|
|
389
|
+
&& isAgentFullAccessOwner(context.subject.selfAid ?? '', context.subject.principalId);
|
|
390
|
+
return {
|
|
391
|
+
supported: true,
|
|
392
|
+
allowed,
|
|
393
|
+
...(allowed ? {} : { reason: { code: 'FORBIDDEN', message: 'session fullaccess requires the Agent Owner and daemon Owner identity' } }),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
371
396
|
if (binding.operation === 'group.rulespolicy.update' && context.subject.role !== 'owner') {
|
|
372
397
|
return { supported: true, allowed: false, reason: { code: 'PERMISSION_DENIED', message: 'rulesPolicy can only be changed by an Agent owner' } };
|
|
373
398
|
}
|
|
@@ -377,7 +402,7 @@ async function operationView(binding, context, options = {}) {
|
|
|
377
402
|
function wireBinding(binding, context) {
|
|
378
403
|
if (!binding || binding.supported === false)
|
|
379
404
|
return undefined;
|
|
380
|
-
if (binding.operation.startsWith('session.loop.'))
|
|
405
|
+
if (binding.operation.startsWith('session.loop.') || binding.operation.startsWith('session.fullaccess.'))
|
|
381
406
|
return {
|
|
382
407
|
args: context.kind === 'group_relation'
|
|
383
408
|
? { ...(binding.args ?? {}), context: { kind: context.kind, ...(context.groupId ? { groupId: context.groupId } : {}) } }
|
|
@@ -406,10 +431,10 @@ export async function getMenuCatalog(input) {
|
|
|
406
431
|
operations[key] = view;
|
|
407
432
|
}
|
|
408
433
|
entries.push({ id: entry.id, type: entry.type, name: entry.name, label: entry.label, section: entry.section, order: entry.order, target: { scope: context.kind === 'agent' ? 'agent' : context.kind === 'process' ? 'process' : 'relation', ...(context.self ? { self: context.self } : {}), ...(context.peerKey ? { peerKey: context.peerKey } : {}) }, valueSchema: entry.valueSchema ?? { type: 'object' }, operations, wire: { name: entry.name, operations: { ...(wireBinding(entry.query, context) ? { query: wireBinding(entry.query, context) } : {}), ...(wireBinding(entry.options, context) ? { options: wireBinding(entry.options, context) } : {}), ...(wireBinding(entry.update, context) ? { update: wireBinding(entry.update, context) } : {}), ...(wireBinding(entry.action, context) ? { action: wireBinding(entry.action, context) } : {}) } }, ui: { control: entry.control ?? 'action' } });
|
|
409
|
-
if (entry.id === 'session.loop') {
|
|
434
|
+
if (entry.id === 'session.loop' || entry.id === 'session.fullaccess') {
|
|
410
435
|
const loopEntry = entries[entries.length - 1];
|
|
411
|
-
let target = { level: 'relation-main' };
|
|
412
|
-
if (input.resolveLoopTarget) {
|
|
436
|
+
let target = { level: 'relation-main', scope: 'agent' };
|
|
437
|
+
if ((entry.id === 'session.loop' || entry.id === 'session.fullaccess') && input.resolveLoopTarget) {
|
|
413
438
|
try {
|
|
414
439
|
target = await input.resolveLoopTarget(context);
|
|
415
440
|
}
|
|
@@ -420,12 +445,23 @@ export async function getMenuCatalog(input) {
|
|
|
420
445
|
}
|
|
421
446
|
}
|
|
422
447
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
448
|
+
if (entry.id === 'session.loop') {
|
|
449
|
+
loopEntry.target = target;
|
|
450
|
+
loopEntry.label = target.level === 'topic-session' ? '当前话题闲置触发' : '关系主会话闲置触发';
|
|
451
|
+
loopEntry.ui = {
|
|
452
|
+
control: entry.control,
|
|
453
|
+
idleSeconds: { type: 'integer', minimum: MIN_SESSION_LOOP_SECONDS, maximum: MAX_SESSION_LOOP_SECONDS, default: DEFAULT_SESSION_LOOP_SECONDS, unit: 'seconds' },
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
loopEntry.target = {
|
|
458
|
+
level: target.level,
|
|
459
|
+
sessionId: target.sessionId,
|
|
460
|
+
scope: target.level === 'topic-session' ? 'session' : 'agent',
|
|
461
|
+
};
|
|
462
|
+
loopEntry.label = '当前 EC 会话 fullaccess';
|
|
463
|
+
loopEntry.ui = { control: entry.control };
|
|
464
|
+
}
|
|
429
465
|
}
|
|
430
466
|
}
|
|
431
467
|
const policyRevision = hash({ role: context.subject.role, self: context.subject.selfAid, peerKey: context.subject.peerKey, groupMembershipRole: context.groupMembershipRole, baselineRole: context.baselineRole, paircodeAllowed, roles: readRolesConfig(context.subject.selfAid) });
|