evolcore 0.0.10 → 0.0.12
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/README.md +3 -3
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/baseagent.js +4 -0
- package/dist/agents/claude-runner.js +158 -44
- package/dist/agents/codex-app-server-client.js +153 -23
- package/dist/agents/codex-runner.js +331 -43
- package/dist/agents/ecagent-runner.js +53 -6
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/agents/request-identity.js +55 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/outbox.js +28 -31
- package/dist/channels/aun.js +418 -235
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +72 -17
- package/dist/cli/agent.js +249 -22
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +52 -33
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +106 -63
- package/dist/cli/init-cancel.js +208 -0
- package/dist/cli/init-channel.js +343 -195
- package/dist/cli/init.js +21 -9
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +5 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/gateway-config.js +26 -10
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/agent-reload-coordinator.js +53 -0
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +62 -154
- package/dist/core/auth/operation-catalog.js +264 -22
- package/dist/core/bootstrap-messages.js +68 -0
- package/dist/core/bootstrap-service.js +144 -10
- 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 +24 -7
- package/dist/core/command/agent-control.js +14 -11
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- 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 +366 -0
- package/dist/core/command/menu-handler.js +213 -110
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +45 -25
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent-registry.js +125 -35
- package/dist/core/evolagent.js +12 -5
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +38 -4
- 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 +237 -79
- package/dist/core/message/message-log.js +23 -0
- package/dist/core/message/message-queue.js +20 -4
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/model/model-catalog.js +143 -24
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/index.js +1 -0
- package/dist/core/permission/readonly-shell-query.js +532 -0
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/shell-environment.js +46 -0
- package/dist/core/permission/tool-policy.js +292 -111
- package/dist/core/protected-paths.js +17 -9
- package/dist/core/runner-reload-transaction.js +57 -0
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +644 -180
- package/dist/ipc.js +246 -19
- 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/script-executor.js +1 -0
- package/dist/utils/aid-bind.js +3 -8
- 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/log-writer.js +6 -10
- package/dist/utils/logger.js +5 -5
- 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/msg.md +13 -0
- 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/rules/01-overview.md +9 -0
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/agent-config.schema.3.json +1 -1
- package/kits/schemas/agent-config.schema.4.json +1 -1
- package/kits/schemas/relation-config.schema.2.json +1 -1
- package/kits/schemas/role-config.schema.1.json +1 -1
- 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 +44 -0
- package/kits/templates/roles/visitor.json +40 -2
- package/kits/templates/system-fragments/bootstrap.md +12 -6
- package/kits/templates/system-fragments/channel.md +6 -0
- package/kits/templates/system-fragments/session.md +2 -0
- package/package.json +3 -2
- package/skills/eclink/SKILL.md +15 -3
- package/skills/eclink/agents/openai.yaml +3 -3
- package/dist/core/command/evol-menu-version-gate.js +0 -39
|
@@ -9,7 +9,9 @@ import { requestDangerousCommandPermission } from '../core/permission/approval-g
|
|
|
9
9
|
import { normalizePermissionMode } from '../core/permission/mode.js';
|
|
10
10
|
import { checkDangerousCommand, checkReadonly, evaluateToolPreflight } from '../core/permission/tool-policy.js';
|
|
11
11
|
import { logger } from '../utils/logger.js';
|
|
12
|
+
import { summarizeToolInputForAudit } from '../utils/tool-summary.js';
|
|
12
13
|
import { resolveEcagentConfig } from './baseagent.js';
|
|
14
|
+
import { buildModelRequestHeaders } from './request-identity.js';
|
|
13
15
|
const PROVIDER_ID = 'evolcore-gateway';
|
|
14
16
|
const DEFAULT_SYSTEM_PROMPT = `You are ecagent, the coding agent built into EvolCore.
|
|
15
17
|
Work directly in the current project. Inspect relevant files before changing them, keep edits scoped, and verify the result.
|
|
@@ -252,6 +254,7 @@ function createOpenAiTransport(config) {
|
|
|
252
254
|
authorization: `Bearer ${config.apiKey}`,
|
|
253
255
|
'content-type': 'application/json',
|
|
254
256
|
...Object.fromEntries(Object.entries(options?.headers ?? {}).filter((entry) => entry[1] !== null)),
|
|
257
|
+
...config.headers,
|
|
255
258
|
},
|
|
256
259
|
body: JSON.stringify(payload),
|
|
257
260
|
signal: controller.signal,
|
|
@@ -678,7 +681,15 @@ export class EcagentRunner {
|
|
|
678
681
|
onCompactStart;
|
|
679
682
|
modelCache;
|
|
680
683
|
constructor(config, callbacks) {
|
|
681
|
-
this.config =
|
|
684
|
+
this.config = {
|
|
685
|
+
...config,
|
|
686
|
+
headers: buildModelRequestHeaders({
|
|
687
|
+
baseagent: 'ecagent',
|
|
688
|
+
baseUrl: config.baseUrl,
|
|
689
|
+
agentAid: config.evolcoreAgentAid,
|
|
690
|
+
configuredHeaders: config.headers,
|
|
691
|
+
}),
|
|
692
|
+
};
|
|
682
693
|
this.model = config.model;
|
|
683
694
|
this.effort = config.effort;
|
|
684
695
|
this.onSessionIdUpdate = callbacks?.onSessionIdUpdate;
|
|
@@ -714,7 +725,11 @@ export class EcagentRunner {
|
|
|
714
725
|
try {
|
|
715
726
|
const signal = AbortSignal.timeout(MODEL_LIST_TIMEOUT_MS);
|
|
716
727
|
const response = await fetch(`${this.config.baseUrl.replace(/\/+$/, '')}/models`, {
|
|
717
|
-
headers: {
|
|
728
|
+
headers: {
|
|
729
|
+
authorization: `Bearer ${this.config.apiKey}`,
|
|
730
|
+
...this.config.headers,
|
|
731
|
+
},
|
|
732
|
+
signal,
|
|
718
733
|
});
|
|
719
734
|
if (!response.ok)
|
|
720
735
|
throw new Error(`Failed to list ecagent models: HTTP ${response.status}`);
|
|
@@ -943,19 +958,43 @@ export class EcagentRunner {
|
|
|
943
958
|
}
|
|
944
959
|
async authorizeTool(sessionId, projectPath, mode, toolName, input) {
|
|
945
960
|
const permissionContext = this.permissionContexts.get(sessionId);
|
|
961
|
+
const recordBlockedOperation = async (policyCode, operationInput = input) => {
|
|
962
|
+
try {
|
|
963
|
+
await permissionContext?.recordExecutionAnomaly?.({
|
|
964
|
+
code: 'operation_blocked',
|
|
965
|
+
severity: 'warning',
|
|
966
|
+
phase: 'execution',
|
|
967
|
+
occurredAt: Date.now(),
|
|
968
|
+
toolName,
|
|
969
|
+
policyCode,
|
|
970
|
+
summary: summarizeToolInputForAudit(toolName, operationInput).slice(0, 512),
|
|
971
|
+
effect: 'operation_skipped',
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
catch (error) {
|
|
975
|
+
logger.warn(`[EcagentRunner] failed to record blocked operation: session=${sessionId} tool=${toolName} code=${policyCode} error=${error instanceof Error ? error.message : String(error)}`);
|
|
976
|
+
}
|
|
977
|
+
};
|
|
946
978
|
const policy = permissionContext?.policyHook?.(toolName, input);
|
|
947
|
-
if (policy?.block)
|
|
979
|
+
if (policy?.block) {
|
|
980
|
+
await recordBlockedOperation(policy.policyCode ?? 'session_policy_hook');
|
|
948
981
|
return { block: true, reason: policy.reason || '当前会话策略拒绝此工具调用' };
|
|
982
|
+
}
|
|
949
983
|
const preflight = evaluateToolPreflight(toolName, input, {
|
|
950
984
|
sessionId,
|
|
985
|
+
selfAid: permissionContext?.selfAid,
|
|
951
986
|
channel: permissionContext?.channel,
|
|
952
987
|
userId: permissionContext?.userId,
|
|
953
988
|
role: permissionContext?.role,
|
|
954
989
|
permissionMode: mode,
|
|
955
990
|
projectPath,
|
|
956
991
|
});
|
|
957
|
-
if (preflight.behavior === 'deny')
|
|
992
|
+
if (preflight.behavior === 'deny') {
|
|
993
|
+
if (preflight.policyCode) {
|
|
994
|
+
await recordBlockedOperation(preflight.policyCode, input);
|
|
995
|
+
}
|
|
958
996
|
return { block: true, reason: preflight.message };
|
|
997
|
+
}
|
|
959
998
|
if (preflight.behavior === 'allow')
|
|
960
999
|
return {};
|
|
961
1000
|
const checkedInput = preflight.input;
|
|
@@ -963,11 +1002,19 @@ export class EcagentRunner {
|
|
|
963
1002
|
const decision = checkReadonly(toolName, checkedInput, projectPath, {
|
|
964
1003
|
sessionId, channel: permissionContext?.channel, peerId: permissionContext?.userId, role: permissionContext?.role,
|
|
965
1004
|
});
|
|
966
|
-
|
|
1005
|
+
if (decision.behavior === 'deny') {
|
|
1006
|
+
await recordBlockedOperation('readonly_mode', checkedInput);
|
|
1007
|
+
return { block: true, reason: decision.message };
|
|
1008
|
+
}
|
|
1009
|
+
return {};
|
|
967
1010
|
}
|
|
968
1011
|
const dangerous = checkDangerousCommand(toolName, checkedInput);
|
|
969
1012
|
if (mode === 'auto') {
|
|
970
|
-
|
|
1013
|
+
if (dangerous.isDangerous) {
|
|
1014
|
+
await recordBlockedOperation('auto_dangerous_command', checkedInput);
|
|
1015
|
+
return { block: true, reason: `危险操作已由 auto 模式拒绝:${dangerous.reason}` };
|
|
1016
|
+
}
|
|
1017
|
+
return {};
|
|
971
1018
|
}
|
|
972
1019
|
const prompt = permissionContext?.sendPrompt ?? this.sendPromptFn;
|
|
973
1020
|
const decision = await requestDangerousCommandPermission(this.permissionGateway, sessionId, toolName, checkedInput, prompt, permissionContext, `ecagent:${mode}`, mode);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
import { readInstalledEvolcoreVersion } from '../utils/evolcore-version.js';
|
|
3
|
+
export const EVOLCORE_REQUEST_HEADERS = {
|
|
4
|
+
version: 'X-EvolCore-Version',
|
|
5
|
+
agentAid: 'X-EvolCore-Agent-AID',
|
|
6
|
+
baseagent: 'X-EvolCore-Base-Agent',
|
|
7
|
+
};
|
|
8
|
+
const RESERVED_HEADER_NAMES = new Set(Object.values(EVOLCORE_REQUEST_HEADERS).map(name => name.toLowerCase()));
|
|
9
|
+
const warnedReservedHeaders = new Set();
|
|
10
|
+
function officialHostname(baseagent) {
|
|
11
|
+
return baseagent === 'claude' ? 'api.anthropic.com' : 'api.openai.com';
|
|
12
|
+
}
|
|
13
|
+
export function isOfficialModelEndpoint(baseagent, baseUrl) {
|
|
14
|
+
const configured = baseUrl?.trim();
|
|
15
|
+
if (!configured)
|
|
16
|
+
return true;
|
|
17
|
+
try {
|
|
18
|
+
return new URL(configured).hostname.toLowerCase() === officialHostname(baseagent);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Invalid URLs fail at the request layer. Treat them as configured here so
|
|
22
|
+
// identity behavior does not silently change if URL validation is relaxed.
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function configuredHeadersWithoutReserved(baseagent, configuredHeaders) {
|
|
27
|
+
const headers = {};
|
|
28
|
+
for (const [name, value] of Object.entries(configuredHeaders ?? {})) {
|
|
29
|
+
const normalizedName = name.toLowerCase();
|
|
30
|
+
if (!RESERVED_HEADER_NAMES.has(normalizedName)) {
|
|
31
|
+
headers[name] = value;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const warningKey = `${baseagent}:${normalizedName}`;
|
|
35
|
+
if (!warnedReservedHeaders.has(warningKey)) {
|
|
36
|
+
warnedReservedHeaders.add(warningKey);
|
|
37
|
+
logger.warn(`[RequestIdentity] Ignoring configured reserved header ${JSON.stringify(name)} for ${baseagent}; EvolCore manages this header at runtime`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return headers;
|
|
41
|
+
}
|
|
42
|
+
export function buildModelRequestHeaders(input) {
|
|
43
|
+
const headers = configuredHeadersWithoutReserved(input.baseagent, input.configuredHeaders);
|
|
44
|
+
if (isOfficialModelEndpoint(input.baseagent, input.baseUrl))
|
|
45
|
+
return headers;
|
|
46
|
+
headers[EVOLCORE_REQUEST_HEADERS.version] = readInstalledEvolcoreVersion();
|
|
47
|
+
const agentAid = input.agentAid?.trim();
|
|
48
|
+
if (agentAid)
|
|
49
|
+
headers[EVOLCORE_REQUEST_HEADERS.agentAid] = agentAid;
|
|
50
|
+
headers[EVOLCORE_REQUEST_HEADERS.baseagent] = input.baseagent;
|
|
51
|
+
return headers;
|
|
52
|
+
}
|
|
53
|
+
export function _resetRequestIdentityWarningsForTests() {
|
|
54
|
+
warnedReservedHeaders.clear();
|
|
55
|
+
}
|
package/dist/aun/aid/client.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import { execFileSync } from 'child_process';
|
|
5
|
-
import { isWindows } from '../../utils/cross-platform.js';
|
|
6
4
|
/**
|
|
7
5
|
* Suppress SDK console logs (DEBUG/INFO/WARN) in CLI context.
|
|
8
6
|
* Call once at CLI entry point — NOT at module load time, to avoid
|
|
@@ -40,10 +38,9 @@ export function suppressSdkLogs() {
|
|
|
40
38
|
return _origStderrWrite(chunk, encoding, callback);
|
|
41
39
|
};
|
|
42
40
|
}
|
|
43
|
-
// ====================
|
|
44
|
-
export const MIN_AUN_CORE_SDK = [0,
|
|
41
|
+
// ==================== SDK package ====================
|
|
42
|
+
export const MIN_AUN_CORE_SDK = [0, 5, 6];
|
|
45
43
|
export const AUN_CORE_SDK_PKG = '@agentunion/fastaun';
|
|
46
|
-
// ==================== SDK & Environment ====================
|
|
47
44
|
function compareVersion(a, min) {
|
|
48
45
|
const parts = a.split('.').map(n => parseInt(n, 10));
|
|
49
46
|
if (parts.length < 3 || parts.some(isNaN))
|
|
@@ -57,6 +54,7 @@ function compareVersion(a, min) {
|
|
|
57
54
|
export function isAunSdkVersionOk(version) {
|
|
58
55
|
return compareVersion(version, MIN_AUN_CORE_SDK);
|
|
59
56
|
}
|
|
57
|
+
/** Resolve only the SDK bundled in EvolCore's own dependency tree. */
|
|
60
58
|
export function resolveAunCoreSdkPkg() {
|
|
61
59
|
try {
|
|
62
60
|
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -73,28 +71,20 @@ export function resolveAunCoreSdkPkg() {
|
|
|
73
71
|
dir = parent;
|
|
74
72
|
}
|
|
75
73
|
}
|
|
76
|
-
catch { /*
|
|
77
|
-
try {
|
|
78
|
-
const npmCmd = isWindows ? 'npm.cmd' : 'npm';
|
|
79
|
-
const globalRoot = execFileSync(npmCmd, ['root', '-g'], {
|
|
80
|
-
encoding: 'utf-8', timeout: 10000, shell: isWindows,
|
|
81
|
-
}).trim();
|
|
82
|
-
const pkgPath = path.join(globalRoot, AUN_CORE_SDK_PKG, 'package.json');
|
|
83
|
-
if (fs.existsSync(pkgPath)) {
|
|
84
|
-
const data = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
85
|
-
return { version: data.version, path: pkgPath };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
catch { /* not found */ }
|
|
74
|
+
catch { /* invalid or missing package metadata */ }
|
|
89
75
|
return null;
|
|
90
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Compatibility guard retained for callers that explicitly check SDK readiness.
|
|
79
|
+
* The SDK is bundled with EvolCore and must never be installed independently.
|
|
80
|
+
*/
|
|
91
81
|
export async function ensureAunSdk() {
|
|
92
82
|
const installed = resolveAunCoreSdkPkg();
|
|
93
83
|
if (installed && isAunSdkVersionOk(installed.version))
|
|
94
84
|
return;
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
const expected = MIN_AUN_CORE_SDK.join('.');
|
|
86
|
+
const detected = installed?.version ?? 'missing';
|
|
87
|
+
throw new Error(`${AUN_CORE_SDK_PKG} ${detected} is not the bundled SDK expected by EvolCore (>= ${expected}); reinstall or upgrade EvolCore`);
|
|
98
88
|
}
|
|
99
89
|
export function isAunSdkReady() {
|
|
100
90
|
const installed = resolveAunCoreSdkPkg();
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { resolveRoot } from '../../paths.js';
|
|
4
|
+
import { agentmdGet, agentmdPut } from './agentmd.js';
|
|
5
|
+
import { aidLookup, aidShow } from './identity.js';
|
|
6
|
+
import { isValidAid } from './validation.js';
|
|
7
|
+
export class ManagedAidOperationError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
constructor(code, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.name = 'ManagedAidOperationError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function normalizeAid(value) {
|
|
16
|
+
const aid = String(value || '').trim();
|
|
17
|
+
return aid.startsWith('@') ? aid.slice(1) : aid;
|
|
18
|
+
}
|
|
19
|
+
function hasOnlyOptionalJsonFormat(args, aidIndex) {
|
|
20
|
+
const suffix = args.slice(aidIndex + 1);
|
|
21
|
+
return suffix.length === 0 || (suffix.length === 2 && suffix[0] === '--format' && suffix[1] === 'json');
|
|
22
|
+
}
|
|
23
|
+
function parseAid(value) {
|
|
24
|
+
const aid = normalizeAid(value);
|
|
25
|
+
if (!aid)
|
|
26
|
+
return { ok: false, code: 'INVALID_ARGUMENT', reason: 'missing AID' };
|
|
27
|
+
if (!isValidAid(aid))
|
|
28
|
+
return { ok: false, code: 'INVALID_AID', reason: `invalid AID: ${aid}` };
|
|
29
|
+
return { ok: true, aid };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The daemon accepts only the self-scoped AID commands exposed to managed
|
|
33
|
+
* tasks. Parsing here is deliberately narrower than the standalone CLI.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveManagedAidOperation(argv) {
|
|
36
|
+
const args = argv[0] === 'aid' ? argv.slice(1) : argv;
|
|
37
|
+
const subcommand = args[0];
|
|
38
|
+
if ((subcommand === 'show' || subcommand === 'lookup') && hasOnlyOptionalJsonFormat(args, 1)) {
|
|
39
|
+
const parsedAid = parseAid(args[1]);
|
|
40
|
+
if (!parsedAid.ok)
|
|
41
|
+
return parsedAid;
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
command: {
|
|
45
|
+
kind: subcommand,
|
|
46
|
+
aid: parsedAid.aid,
|
|
47
|
+
operationId: `ec.aid.${subcommand}`,
|
|
48
|
+
canonicalArgv: ['aid', subcommand, parsedAid.aid],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (subcommand === 'agentmd' && (args[1] === 'get' || args[1] === 'put') && hasOnlyOptionalJsonFormat(args, 2)) {
|
|
53
|
+
const parsedAid = parseAid(args[2]);
|
|
54
|
+
if (!parsedAid.ok)
|
|
55
|
+
return parsedAid;
|
|
56
|
+
const kind = `agentmd.${args[1]}`;
|
|
57
|
+
return {
|
|
58
|
+
ok: true,
|
|
59
|
+
command: {
|
|
60
|
+
kind,
|
|
61
|
+
aid: parsedAid.aid,
|
|
62
|
+
operationId: `ec.aid.${kind}`,
|
|
63
|
+
canonicalArgv: ['aid', 'agentmd', args[1], parsedAid.aid],
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
code: 'NOT_ALLOWED',
|
|
70
|
+
reason: 'Managed AID operations only allow exact self-scoped show, lookup, agentmd get, or agentmd put commands with an optional --format json suffix',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const defaultDependencies = () => ({
|
|
74
|
+
root: resolveRoot(),
|
|
75
|
+
aidShow,
|
|
76
|
+
aidLookup,
|
|
77
|
+
agentmdGet,
|
|
78
|
+
agentmdPut,
|
|
79
|
+
existsSync: fs.existsSync,
|
|
80
|
+
readFileSync: fs.readFileSync,
|
|
81
|
+
});
|
|
82
|
+
/** Execute a validated AID operation from the daemon-owned host filesystem. */
|
|
83
|
+
export async function executeManagedAidOperation(command, overrides = {}) {
|
|
84
|
+
const deps = { ...defaultDependencies(), ...overrides };
|
|
85
|
+
const aunPath = deps.root;
|
|
86
|
+
switch (command.kind) {
|
|
87
|
+
case 'show':
|
|
88
|
+
return { kind: command.kind, aid: command.aid, value: await deps.aidShow(command.aid, { aunPath }) };
|
|
89
|
+
case 'lookup':
|
|
90
|
+
return { kind: command.kind, aid: command.aid, value: await deps.aidLookup(command.aid) };
|
|
91
|
+
case 'agentmd.get':
|
|
92
|
+
return {
|
|
93
|
+
kind: command.kind,
|
|
94
|
+
aid: command.aid,
|
|
95
|
+
value: await deps.agentmdGet(command.aid, { aunPath, withVerification: true }),
|
|
96
|
+
};
|
|
97
|
+
case 'agentmd.put': {
|
|
98
|
+
const localPath = path.join(aunPath, 'AIDs', command.aid, 'agent.md');
|
|
99
|
+
if (!deps.existsSync(localPath)) {
|
|
100
|
+
throw new ManagedAidOperationError('LOCAL_AGENTMD_MISSING', `local agent.md not found: ${command.aid}`);
|
|
101
|
+
}
|
|
102
|
+
const content = deps.readFileSync(localPath, 'utf-8');
|
|
103
|
+
await deps.agentmdPut(content, { aid: command.aid, aunPath });
|
|
104
|
+
return { kind: command.kind, aid: command.aid, path: localPath };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
package/dist/aun/msg/group.js
CHANGED
|
@@ -13,6 +13,7 @@ import { readBestTaskRuntimeContext } from '../../cli/task-context.js';
|
|
|
13
13
|
import { ipcQuery } from '../../ipc.js';
|
|
14
14
|
import { resolvePaths } from '../../paths.js';
|
|
15
15
|
import { AGENT_DELEGATION_TOKEN_ENV } from '../../core/auth/agent-delegation.js';
|
|
16
|
+
import { normalizeAunMentionEntries } from './mention-schema.js';
|
|
16
17
|
function buildGroupPayload(body) {
|
|
17
18
|
return body.mode === 'text' ? { type: 'text', text: body.text } : { ...body.payload };
|
|
18
19
|
}
|
|
@@ -189,8 +190,18 @@ export async function groupSend(args) {
|
|
|
189
190
|
break;
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
|
-
|
|
193
|
-
payload.mentions
|
|
193
|
+
const payloadMentions = Object.prototype.hasOwnProperty.call(payload, 'mentions')
|
|
194
|
+
? normalizeAunMentionEntries(payload.mentions)
|
|
195
|
+
: undefined;
|
|
196
|
+
if (args.mentions !== undefined) {
|
|
197
|
+
const normalizedMentions = normalizeAunMentionEntries(args.mentions);
|
|
198
|
+
if (normalizedMentions.length > 0)
|
|
199
|
+
payload.mentions = normalizedMentions;
|
|
200
|
+
else if (payloadMentions !== undefined)
|
|
201
|
+
payload.mentions = payloadMentions;
|
|
202
|
+
}
|
|
203
|
+
else if (payloadMentions !== undefined) {
|
|
204
|
+
payload.mentions = payloadMentions;
|
|
194
205
|
}
|
|
195
206
|
applyGroupRoutingPayloadFields(payload, args, runtimeContext);
|
|
196
207
|
const encrypt = resolveAunMessageEncrypt(args.encrypt);
|
|
@@ -305,8 +316,9 @@ export async function groupInfo(args) {
|
|
|
305
316
|
}
|
|
306
317
|
}
|
|
307
318
|
export async function groupList(args) {
|
|
308
|
-
|
|
319
|
+
let conn;
|
|
309
320
|
try {
|
|
321
|
+
conn = await createShortConnection(args.from, { aunPath: args.aunPath, slotId: args.slotId });
|
|
310
322
|
const params = {};
|
|
311
323
|
if (args.size !== undefined)
|
|
312
324
|
params.size = args.size;
|
|
@@ -329,7 +341,8 @@ export async function groupList(args) {
|
|
|
329
341
|
return formatRpcError(e);
|
|
330
342
|
}
|
|
331
343
|
finally {
|
|
332
|
-
|
|
344
|
+
if (conn)
|
|
345
|
+
await conn.close();
|
|
333
346
|
}
|
|
334
347
|
}
|
|
335
348
|
export async function groupUpdate(args) {
|
|
@@ -675,7 +688,7 @@ export async function groupUpdateRules(args) {
|
|
|
675
688
|
}
|
|
676
689
|
}
|
|
677
690
|
const GROUP_RULES_FILE_PATH = '/rules.md';
|
|
678
|
-
const MAX_GROUP_RULES_BYTES = 4 * 1024;
|
|
691
|
+
export const MAX_GROUP_RULES_BYTES = 4 * 1024;
|
|
679
692
|
const MTIME_TOLERANCE_MS = 1;
|
|
680
693
|
export async function groupRulesFileGet(args) {
|
|
681
694
|
return withAunClient(args.from, args, async (client) => {
|
|
@@ -742,13 +755,26 @@ export async function groupRulesFileGet(args) {
|
|
|
742
755
|
}
|
|
743
756
|
export async function groupRulesFileSet(args) {
|
|
744
757
|
return withAunClient(args.from, args, async (client) => {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
758
|
+
if ((args.filePath === undefined) === (args.content === undefined)) {
|
|
759
|
+
throw new Error('规则文件必须且只能提供 filePath 或 content');
|
|
760
|
+
}
|
|
761
|
+
let upload;
|
|
762
|
+
if (args.content !== undefined) {
|
|
763
|
+
const bytes = Buffer.from(args.content, 'utf8');
|
|
764
|
+
if (!args.content || bytes.byteLength > MAX_GROUP_RULES_BYTES || bytes.toString('utf8') !== args.content) {
|
|
765
|
+
throw new Error(`规则内容必须是非空 UTF-8,且不超过 ${MAX_GROUP_RULES_BYTES} bytes`);
|
|
766
|
+
}
|
|
767
|
+
upload = await uploadGroupRulesBytes(client, args.groupId, bytes);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
assertLocalRulesFile(args.filePath);
|
|
771
|
+
upload = await client.group.fs.cp(args.filePath, `${args.groupId}:${GROUP_RULES_FILE_PATH}`, {
|
|
772
|
+
force: true,
|
|
773
|
+
overwrite: true,
|
|
774
|
+
parents: true,
|
|
775
|
+
contentType: 'text/markdown; charset=utf-8',
|
|
776
|
+
});
|
|
777
|
+
}
|
|
752
778
|
const published = await publishCurrentRulesFile(client, args.groupId, args.from);
|
|
753
779
|
return { ...published, upload };
|
|
754
780
|
});
|
|
@@ -984,6 +1010,49 @@ function assertLocalRulesFile(filePath) {
|
|
|
984
1010
|
throw new Error(`规则文件不是有效 UTF-8: ${errorMessage(e)}`);
|
|
985
1011
|
}
|
|
986
1012
|
}
|
|
1013
|
+
async function uploadGroupRulesBytes(client, groupId, bytes) {
|
|
1014
|
+
const remotePath = `${groupId}:${GROUP_RULES_FILE_PATH}`;
|
|
1015
|
+
const base = {
|
|
1016
|
+
path: remotePath,
|
|
1017
|
+
size_bytes: bytes.byteLength,
|
|
1018
|
+
sha256: sha256Hex(bytes),
|
|
1019
|
+
content_type: 'text/markdown; charset=utf-8',
|
|
1020
|
+
force: true,
|
|
1021
|
+
parents: true,
|
|
1022
|
+
};
|
|
1023
|
+
const check = await client.call('group.fs.check_upload', base);
|
|
1024
|
+
if (isRecord(check) && check.within_limit === false) {
|
|
1025
|
+
throw new Error('规则文件超过 group.fs 上传限制');
|
|
1026
|
+
}
|
|
1027
|
+
if (isRecord(check) && (check.instant === true || check.dedup_hit === true || check.skip_upload === true)) {
|
|
1028
|
+
return client.call('group.fs.complete_upload', {
|
|
1029
|
+
...base,
|
|
1030
|
+
skip_blob: true,
|
|
1031
|
+
...(check.session_id !== undefined ? { session_id: check.session_id } : {}),
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
const session = await client.call('group.fs.create_upload_session', base);
|
|
1035
|
+
if (!isRecord(session))
|
|
1036
|
+
throw new Error('group.fs.create_upload_session returned invalid response');
|
|
1037
|
+
const uploadUrl = stringField(session, ['upload_url', 'url']);
|
|
1038
|
+
if (!uploadUrl)
|
|
1039
|
+
throw new Error('group.fs.create_upload_session did not return upload_url');
|
|
1040
|
+
const headers = {};
|
|
1041
|
+
if (isRecord(session.headers)) {
|
|
1042
|
+
for (const [key, value] of Object.entries(session.headers)) {
|
|
1043
|
+
if (value !== undefined && value !== null)
|
|
1044
|
+
headers[key] = String(value);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (!Object.keys(headers).some(key => key.toLowerCase() === 'content-type')) {
|
|
1048
|
+
headers['Content-Type'] = base.content_type;
|
|
1049
|
+
}
|
|
1050
|
+
await client.group.fs.lowlevel.httpPut(uploadUrl, bytes, headers);
|
|
1051
|
+
return client.call('group.fs.complete_upload', {
|
|
1052
|
+
...base,
|
|
1053
|
+
...((session.session_id ?? session.id) !== undefined ? { session_id: session.session_id ?? session.id } : {}),
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
987
1056
|
async function readGroupRulesBytes(client, groupId) {
|
|
988
1057
|
const remoteRef = `${groupId}:${GROUP_RULES_FILE_PATH}`;
|
|
989
1058
|
const ticket = await client.call('group.fs.create_download_ticket', { path: remoteRef });
|