evolcore 0.0.23 → 0.0.25
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 +583 -516
- package/LICENSE +21 -21
- package/README.md +455 -453
- package/bin/codex-managed-acl.ps1 +160 -155
- package/bin/codex-managed-hook.mjs +209 -92
- package/bin/ec-safe-output.js +327 -245
- package/bin/ec.js +29 -29
- package/bin/install-codex-managed-hooks.mjs +555 -544
- package/dist/agents/baseagent-availability.js +4 -4
- package/dist/agents/baseagent.js +29 -1
- package/dist/agents/claude-runner.js +152 -14
- package/dist/agents/codex/app-server-client.js +18 -8
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +215 -0
- package/dist/agents/codex-runner.js +445 -63
- package/dist/agents/ecagent-runner.js +31 -16
- package/dist/agents/gemini-runner.js +82 -4
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/agentmd.js +56 -4
- package/dist/aun/aid/index.js +1 -0
- package/dist/aun/aid/managed-operation.js +80 -6
- package/dist/aun/aid/store.js +41 -0
- package/dist/aun/msg/managed-operation.js +6 -1
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +4 -5
- package/dist/aun/storage/client.js +4 -1
- package/dist/aun/storage/index.js +1 -1
- package/dist/channels/aun.js +147 -76
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +214 -89
- package/dist/cli/agent-create.js +576 -0
- package/dist/cli/agent.js +129 -676
- package/dist/cli/aun-commands.js +419 -313
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +20 -20
- package/dist/cli/contact.js +10 -10
- package/dist/cli/ctl-command.js +22 -20
- package/dist/cli/daemon-commands.js +163 -32
- package/dist/cli/fs-command.js +67 -67
- package/dist/cli/handoff-command.js +7 -7
- package/dist/cli/index.js +79 -79
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +11 -11
- package/dist/cli/net-check.js +22 -22
- package/dist/cli/queue-command.js +29 -29
- package/dist/cli/response.js +30 -30
- package/dist/cli/restart-monitor.js +462 -38
- package/dist/cli/stats.js +161 -51
- package/dist/cli/trigger-command.js +56 -56
- package/dist/config/builtin-roles.js +3 -0
- package/dist/config/config-manager.js +15 -0
- package/dist/config/config-operation-service.js +20 -3
- package/dist/config/resolved-config-op.js +39 -1
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +72 -19
- package/dist/core/auth/operation-authorizer.js +125 -7
- package/dist/core/auth/operation-catalog.js +26 -2
- package/dist/core/auth/remote-access-store.js +3 -2
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +60 -26
- package/dist/core/command/cli-intent-parser.js +18 -0
- package/dist/core/command/command-handler.js +173 -36
- package/dist/core/command/menu-catalog.js +4 -1
- package/dist/core/command/menu-handler.js +315 -75
- package/dist/core/command/menu-protocol.js +7 -2
- package/dist/core/command/slash-gate.js +1 -3
- package/dist/core/command/slash-handler.js +166 -57
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +8 -0
- package/dist/core/evolagent.js +3 -3
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +85 -27
- package/dist/core/model/catalog-service.js +3 -1
- package/dist/core/model/model-catalog.js +10 -0
- package/dist/core/model/model-permission.js +4 -1
- package/dist/core/permission/approval-gateway.js +169 -33
- package/dist/core/permission/tool-policy.js +56 -10
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +327 -134
- package/dist/ipc.js +68 -21
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- package/dist/stats/query.js +139 -139
- package/dist/stats/role-budget.js +6 -6
- package/dist/stats/writer.js +51 -51
- package/dist/utils/atomic-write.js +26 -2
- package/dist/utils/avatar-upload.js +282 -23
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/error-utils.js +11 -4
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +24 -1
- package/dist/utils/welcome.js +22 -22
- package/dist/utils/windows-autostart.js +1 -1
- package/ecagent/LICENSE +21 -21
- package/ecagent/NOTICE +11 -11
- package/ecagent/README.md +61 -61
- package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
- package/ecagent/dist/harness/compaction/compaction.js +83 -83
- package/ecagent/dist/harness/messages.js +7 -7
- package/kits/docs/GUIDE.md +20 -20
- package/kits/docs/INDEX.md +66 -66
- package/kits/docs/aun/CHEATSHEET.md +19 -19
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
- package/kits/docs/channels/aun.md +56 -56
- package/kits/docs/channels/feishu.md +56 -56
- package/kits/docs/context-assembly.md +365 -364
- package/kits/docs/eck_templates/GUIDE.template.md +22 -22
- package/kits/docs/eck_templates/INDEX.template.md +28 -28
- package/kits/docs/eck_templates/path-registry.template.md +33 -33
- package/kits/docs/eck_templates/runtime.template.md +19 -19
- package/kits/docs/evolcore/INDEX.md +71 -69
- package/kits/docs/evolcore/agent-create.md +170 -156
- package/kits/docs/evolcore/agent.md +83 -73
- package/kits/docs/evolcore/aid.md +64 -62
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
- package/kits/docs/evolcore/config.md +195 -195
- package/kits/docs/evolcore/contact.md +68 -68
- package/kits/docs/evolcore/ctl.md +46 -46
- package/kits/docs/evolcore/event.md +216 -216
- package/kits/docs/evolcore/fs-architecture.md +1215 -1215
- package/kits/docs/evolcore/fs.md +110 -110
- package/kits/docs/evolcore/group-collaboration.md +243 -243
- package/kits/docs/evolcore/group-fs.md +17 -17
- package/kits/docs/evolcore/group-rules.md +251 -251
- package/kits/docs/evolcore/group.md +152 -152
- package/kits/docs/evolcore/menu-client.md +34 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +165 -165
- package/kits/docs/evolcore/response.md +75 -75
- package/kits/docs/evolcore/rpc.md +37 -37
- package/kits/docs/evolcore/self-summary.md +29 -29
- package/kits/docs/evolcore/stats.md +83 -83
- package/kits/docs/evolcore/storage.md +50 -50
- package/kits/docs/evolcore/trigger.md +575 -575
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
- package/kits/docs/identity/PATH_OPS.md +16 -16
- package/kits/docs/identity/ROLE_DETAIL.md +25 -25
- package/kits/docs/identity/identity-tools.md +26 -26
- package/kits/docs/path-registry.md +62 -62
- package/kits/docs/prompt-loading-architecture.md +270 -269
- package/kits/docs/venues/aun-group.md +45 -45
- package/kits/docs/venues/aun-private.md +10 -10
- package/kits/docs/venues/client-desktop.md +10 -10
- package/kits/docs/venues/client-mobile.md +10 -10
- package/kits/docs/venues/feishu-group.md +13 -13
- package/kits/docs/venues/feishu-private.md +9 -9
- package/kits/docs/venues/group.md +25 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +195 -183
- package/kits/eck_message_manifest.json +74 -74
- package/kits/migrations/README-role-config-v4.md +32 -32
- package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
- package/kits/migrations/migrate-role-config-v4.mjs +614 -614
- package/kits/migrations/migrate-role-config-v5.mjs +365 -365
- package/kits/migrations/rename-config-file.mjs +99 -99
- package/kits/rules/01-overview.md +162 -162
- package/kits/rules/02-navigation.md +96 -96
- package/kits/rules/03-identity.md +39 -37
- package/kits/rules/04-relation.md +75 -75
- package/kits/rules/05-venue.md +60 -60
- package/kits/rules/06-channel.md +63 -63
- package/kits/schemas/_meta.json +53 -53
- package/kits/schemas/agent-config.schema.1.json +177 -177
- package/kits/schemas/agent-config.schema.10.json +395 -395
- package/kits/schemas/agent-config.schema.11.json +421 -421
- package/kits/schemas/agent-config.schema.12.json +427 -427
- package/kits/schemas/agent-config.schema.2.json +239 -239
- package/kits/schemas/agent-config.schema.3.json +119 -119
- package/kits/schemas/agent-config.schema.4.json +208 -208
- package/kits/schemas/agent-config.schema.5.json +326 -326
- package/kits/schemas/agent-config.schema.6.json +322 -322
- package/kits/schemas/agent-config.schema.7.json +303 -303
- package/kits/schemas/agent-config.schema.8.json +304 -304
- package/kits/schemas/agent-config.schema.9.json +364 -364
- package/kits/schemas/contact-book.schema.1.json +36 -36
- package/kits/schemas/contact-book.schema.2.json +43 -43
- package/kits/schemas/contact-book.schema.3.json +67 -67
- package/kits/schemas/daemon.schema.1.json +91 -91
- package/kits/schemas/daemon.schema.2.json +101 -101
- package/kits/schemas/daemon.schema.3.json +123 -123
- package/kits/schemas/daemon.schema.4.json +136 -136
- package/kits/schemas/daemon.schema.5.json +135 -135
- package/kits/schemas/daemon.schema.6.json +131 -131
- package/kits/schemas/defaults.schema.1.json +81 -81
- package/kits/schemas/defaults.schema.2.json +85 -85
- package/kits/schemas/defaults.schema.3.json +73 -73
- package/kits/schemas/defaults.schema.4.json +107 -107
- package/kits/schemas/defaults.schema.5.json +119 -119
- package/kits/schemas/menu-exec-schema-commands.md +208 -208
- package/kits/schemas/migrations/README.md +30 -30
- package/kits/schemas/relation-config.schema.1.json +158 -158
- package/kits/schemas/relation-config.schema.2.json +73 -73
- package/kits/schemas/relation-config.schema.3.json +50 -50
- package/kits/schemas/relation-config.schema.4.json +47 -47
- package/kits/schemas/relation-config.schema.5.json +47 -47
- package/kits/schemas/relation-config.schema.6.json +46 -46
- package/kits/schemas/relation-config.schema.7.json +59 -59
- package/kits/schemas/relation-config.schema.8.json +103 -103
- package/kits/schemas/role-config.schema.1.json +201 -201
- package/kits/schemas/role-registry.schema.1.json +35 -35
- package/kits/schemas/single-session.schema.1.json +31 -31
- package/kits/schemas/single-session.schema.2.json +57 -57
- package/kits/schemas/single-session.schema.3.json +32 -32
- package/kits/templates/bootstrap-welcome.md +17 -17
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
- package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
- package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
- package/kits/templates/message-fragments/inject-default.md +2 -2
- package/kits/templates/message-fragments/item.md +2 -2
- package/kits/templates/roles/admin.json +96 -82
- package/kits/templates/roles/member.json +97 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +86 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +38 -29
- package/kits/templates/system-fragments/channel.md +64 -64
- package/kits/templates/system-fragments/commands.md +28 -28
- package/kits/templates/system-fragments/identity.md +13 -13
- package/kits/templates/system-fragments/relation.md +19 -19
- package/kits/templates/system-fragments/session.md +59 -59
- package/kits/templates/system-fragments/venue.md +31 -31
- package/package.json +98 -94
- package/AGENTS.md +0 -62
- package/dist/utils/codex-cli.js +0 -43
- package/skills/eclink/SKILL.md +0 -84
- package/skills/eclink/agents/openai.yaml +0 -4
|
@@ -18,14 +18,21 @@ import { appendRequestPath } from './request-identity.js';
|
|
|
18
18
|
import { getManagedTaskTempDir } from '../cli/task-context.js';
|
|
19
19
|
import { hasTrustedFullAccessContext } from './runner-types.js';
|
|
20
20
|
const PROVIDER_ID = 'evolcore-gateway';
|
|
21
|
-
const DEFAULT_SYSTEM_PROMPT = `You are ecagent, the coding agent built into EvolCore.
|
|
22
|
-
Work directly in the current project. Inspect relevant files before changing them, keep edits scoped, and verify the result.
|
|
21
|
+
const DEFAULT_SYSTEM_PROMPT = `You are ecagent, the coding agent built into EvolCore.
|
|
22
|
+
Work directly in the current project. Inspect relevant files before changing them, keep edits scoped, and verify the result.
|
|
23
23
|
Use the provided tools for filesystem and shell work. Never claim a tool action succeeded unless its result confirms success.`;
|
|
24
24
|
const MAX_READ_BYTES = 1024 * 1024;
|
|
25
25
|
const MAX_TOOL_OUTPUT = 200_000;
|
|
26
26
|
const MAX_SCAN_FILES = 5_000;
|
|
27
27
|
const MAX_SEARCH_RESULTS = 500;
|
|
28
28
|
const DEFAULT_BASH_TIMEOUT_MS = 120_000;
|
|
29
|
+
/**
|
|
30
|
+
* 规范化模型 ID:去掉 Claude Code SDK 专有的上下文窗口后缀 [1m]/[200k]
|
|
31
|
+
* Ecagent/OpenAI API 不认识这些后缀,必须传入标准模型 ID
|
|
32
|
+
*/
|
|
33
|
+
function stripContextWindowSuffix(modelId) {
|
|
34
|
+
return modelId.replace(/\[(?:1m|200k)\]$/i, '');
|
|
35
|
+
}
|
|
29
36
|
class AsyncEventQueue {
|
|
30
37
|
values = [];
|
|
31
38
|
waiters = [];
|
|
@@ -461,26 +468,27 @@ function isWithin(root, candidate) {
|
|
|
461
468
|
const relative = path.relative(root, candidate);
|
|
462
469
|
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
463
470
|
}
|
|
464
|
-
export function resolveEcagentToolPath(projectPath, candidate, forWrite = false, bootstrapAgentMdPath, fullaccess = false) {
|
|
471
|
+
export function resolveEcagentToolPath(projectPath, candidate, forWrite = false, bootstrapAgentMdPath, fullaccess = false, personalMdPath) {
|
|
465
472
|
const root = fs.realpathSync(projectPath);
|
|
466
473
|
const resolved = path.resolve(root, candidate || '.');
|
|
467
474
|
const allowedBootstrapFile = bootstrapAgentMdPath
|
|
468
475
|
? path.resolve(bootstrapAgentMdPath)
|
|
469
476
|
: undefined;
|
|
470
|
-
const
|
|
471
|
-
|
|
477
|
+
const allowedPersonalFile = personalMdPath ? path.resolve(personalMdPath) : undefined;
|
|
478
|
+
const isManagedIdentityFile = resolved === allowedBootstrapFile || resolved === allowedPersonalFile;
|
|
479
|
+
if (!fullaccess && !isWithin(root, resolved) && !isManagedIdentityFile) {
|
|
472
480
|
throw new Error(`Path is outside the project: ${candidate}`);
|
|
473
481
|
}
|
|
474
|
-
if (
|
|
482
|
+
if (isManagedIdentityFile) {
|
|
475
483
|
const parent = path.dirname(resolved);
|
|
476
484
|
const canonicalParent = fs.realpathSync(parent);
|
|
477
485
|
if (!isWithin(parent, canonicalParent) || !isWithin(canonicalParent, parent)) {
|
|
478
|
-
throw new Error(`
|
|
486
|
+
throw new Error(`Managed identity file parent resolves outside its declared path: ${candidate}`);
|
|
479
487
|
}
|
|
480
488
|
if (fs.existsSync(resolved)) {
|
|
481
489
|
const canonicalFile = fs.realpathSync(resolved);
|
|
482
490
|
if (!isWithin(resolved, canonicalFile) || !isWithin(canonicalFile, resolved)) {
|
|
483
|
-
throw new Error(`
|
|
491
|
+
throw new Error(`Managed identity file resolves outside its declared path: ${candidate}`);
|
|
484
492
|
}
|
|
485
493
|
}
|
|
486
494
|
return resolved;
|
|
@@ -543,7 +551,7 @@ async function walkFiles(root, signal) {
|
|
|
543
551
|
function textResult(text, details = {}) {
|
|
544
552
|
return { content: [{ type: 'text', text: truncate(text) }], details };
|
|
545
553
|
}
|
|
546
|
-
function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullaccess = false) {
|
|
554
|
+
function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullaccess = false, personalMdPath) {
|
|
547
555
|
const read = {
|
|
548
556
|
name: 'Read', label: 'Read', description: 'Read a UTF-8 text file from the project.',
|
|
549
557
|
parameters: Type.Object({
|
|
@@ -552,7 +560,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
|
|
|
552
560
|
limit: Type.Optional(Type.Number({ minimum: 1 })),
|
|
553
561
|
}),
|
|
554
562
|
async execute(_id, params, signal) {
|
|
555
|
-
const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess);
|
|
563
|
+
const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess, personalMdPath);
|
|
556
564
|
const stat = await fs.promises.stat(file);
|
|
557
565
|
if (stat.size > MAX_READ_BYTES)
|
|
558
566
|
throw new Error(`File exceeds ${MAX_READ_BYTES} byte read limit`);
|
|
@@ -618,7 +626,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
|
|
|
618
626
|
name: 'Write', label: 'Write', description: 'Create or completely overwrite a project file.',
|
|
619
627
|
parameters: Type.Object({ file_path: Type.String(), content: Type.String() }), executionMode: 'sequential',
|
|
620
628
|
async execute(_id, params, signal) {
|
|
621
|
-
const file = resolveEcagentToolPath(projectPath, params.file_path, true, bootstrapAgentMdPath, fullaccess);
|
|
629
|
+
const file = resolveEcagentToolPath(projectPath, params.file_path, true, bootstrapAgentMdPath, fullaccess, personalMdPath);
|
|
622
630
|
await fs.promises.mkdir(path.dirname(file), { recursive: true });
|
|
623
631
|
await fs.promises.writeFile(file, params.content, { encoding: 'utf8', signal });
|
|
624
632
|
return textResult(`Wrote ${Buffer.byteLength(params.content)} bytes to ${path.relative(projectPath, file)}`, { file });
|
|
@@ -631,7 +639,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
|
|
|
631
639
|
replace_all: Type.Optional(Type.Boolean()),
|
|
632
640
|
}), executionMode: 'sequential',
|
|
633
641
|
async execute(_id, params, signal) {
|
|
634
|
-
const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess);
|
|
642
|
+
const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess, personalMdPath);
|
|
635
643
|
const value = await fs.promises.readFile(file, { encoding: 'utf8', signal });
|
|
636
644
|
if (!params.old_string)
|
|
637
645
|
throw new Error('old_string must not be empty');
|
|
@@ -714,7 +722,7 @@ export class EcagentRunner {
|
|
|
714
722
|
configuredHeaders: config.headers,
|
|
715
723
|
}),
|
|
716
724
|
};
|
|
717
|
-
this.model = config.model;
|
|
725
|
+
this.model = stripContextWindowSuffix(config.model);
|
|
718
726
|
this.effort = config.effort;
|
|
719
727
|
this.agentAid = runtime?.agentAid;
|
|
720
728
|
this.onSessionIdUpdate = callbacks?.onSessionIdUpdate;
|
|
@@ -724,7 +732,7 @@ export class EcagentRunner {
|
|
|
724
732
|
throw new Error('ecagent requires an EvolCore Agent AID');
|
|
725
733
|
return agentSessionsDir(this.agentAid);
|
|
726
734
|
}
|
|
727
|
-
setModel(model) { this.model = model; }
|
|
735
|
+
setModel(model) { this.model = stripContextWindowSuffix(model); }
|
|
728
736
|
getModel() { return this.model; }
|
|
729
737
|
resolveModelId(model) { return model; }
|
|
730
738
|
setEffort(effort) { this.effort = effort; }
|
|
@@ -851,7 +859,7 @@ export class EcagentRunner {
|
|
|
851
859
|
models,
|
|
852
860
|
model,
|
|
853
861
|
thinkingLevel: normalizeThinkingLevel(effort),
|
|
854
|
-
tools: createTools(nodeEnv, request.projectPath, request.runtimeEnv, request.modelOverride?.bootstrapAgentMdPath, fullaccess),
|
|
862
|
+
tools: createTools(nodeEnv, request.projectPath, request.runtimeEnv, request.modelOverride?.bootstrapAgentMdPath, fullaccess, request.modelOverride?.personalMdPath),
|
|
855
863
|
systemPrompt: [DEFAULT_SYSTEM_PROMPT, request.systemPromptAppend].filter(Boolean).join('\n\n'),
|
|
856
864
|
streamOptions: {
|
|
857
865
|
timeoutMs: this.config.timeoutMs,
|
|
@@ -995,6 +1003,13 @@ export class EcagentRunner {
|
|
|
995
1003
|
return runtime;
|
|
996
1004
|
}
|
|
997
1005
|
mapHarnessEvent(queue, event, stats, model, effort) {
|
|
1006
|
+
if (event.type === 'message_update'
|
|
1007
|
+
&& (event.assistantMessageEvent.type === 'thinking_start'
|
|
1008
|
+
|| event.assistantMessageEvent.type === 'thinking_delta'
|
|
1009
|
+
|| event.assistantMessageEvent.type === 'thinking_end')) {
|
|
1010
|
+
queue.push({ type: 'heartbeat', source: 'reasoning', turn: stats.numTurns + 1 });
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
998
1013
|
if (event.type === 'message_update' && event.assistantMessageEvent.type === 'text_delta') {
|
|
999
1014
|
if (!stats.firstTextAt)
|
|
1000
1015
|
stats.firstTextAt = Date.now();
|
|
@@ -1202,7 +1217,7 @@ export class EcagentRunner {
|
|
|
1202
1217
|
? cached
|
|
1203
1218
|
: await this.openExistingRuntime(agentSessionId, cwd);
|
|
1204
1219
|
this.onCompactStart?.(sessionId);
|
|
1205
|
-
const compactModel = modelOverride?.model || this.model;
|
|
1220
|
+
const compactModel = stripContextWindowSuffix(modelOverride?.model || this.model);
|
|
1206
1221
|
const compactEffort = modelOverride?.effortMode === 'model_default'
|
|
1207
1222
|
? undefined
|
|
1208
1223
|
: modelOverride?.effort ?? this.effort;
|
|
@@ -24,6 +24,7 @@ import { normalizeExecutionPermissionMode, normalizePermissionMode } from '../co
|
|
|
24
24
|
import { workspaceContainsHClassPaths } from '../core/permission/protected-paths.js';
|
|
25
25
|
import { buildBubblewrapCommand } from '../core/permission/sandbox-runtime.js';
|
|
26
26
|
import { getManagedTaskTempDir } from '../cli/task-context.js';
|
|
27
|
+
import { auditPermissionLifecycle } from '../core/auth/authorization-audit.js';
|
|
27
28
|
// Strip ANSI escape codes from Gemini CLI text output.
|
|
28
29
|
// Gemini embeds raw terminal colors from tool stdout (e.g. vitest, npm)
|
|
29
30
|
// into its assistant text, unlike Claude SDK which strips them internally.
|
|
@@ -32,6 +33,13 @@ const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\].*?(?:\x1b\\|\x07)/g;
|
|
|
32
33
|
function stripAnsi(text) {
|
|
33
34
|
return text.replace(ANSI_RE, '');
|
|
34
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 规范化模型 ID:去掉 Claude Code SDK 专有的上下文窗口后缀 [1m]/[200k]
|
|
38
|
+
* Gemini API 不认识这些后缀,必须传入标准模型 ID
|
|
39
|
+
*/
|
|
40
|
+
function stripContextWindowSuffix(modelId) {
|
|
41
|
+
return modelId.replace(/\[(?:1m|200k)\]$/i, '');
|
|
42
|
+
}
|
|
35
43
|
// ── MIME → 扩展名映射 ──
|
|
36
44
|
const MIME_EXT = {
|
|
37
45
|
'image/png': '.png',
|
|
@@ -139,6 +147,7 @@ const GEMINI_EC_COMMAND_PATTERNS = [
|
|
|
139
147
|
// quoted arguments.
|
|
140
148
|
String.raw `\x00"command":"[ ]*ec[ ]+(?:[^"\x00;&|$()<>\x60\r\n\\]|\\(?:\\|"))*"\x00`,
|
|
141
149
|
];
|
|
150
|
+
const GEMINI_BOOTSTRAP_PERSONA_COMMAND_PATTERN = String.raw `^\{\x00"command":"[ ]*ec[ ]+agent[ ]+persona(?:[ ]+show|[ ]+--(?:text|file)[ ]+(?:[^"\x00;&|$()<>\x60\r\n\\]|\\(?:\\|"))+)[ ]*"\x00\}$`;
|
|
142
151
|
export function resolveGeminiPermissionProfile(value, bootstrap = false) {
|
|
143
152
|
if (value === 'fullaccess')
|
|
144
153
|
return { mode: 'fullaccess', approvalMode: 'yolo' };
|
|
@@ -178,6 +187,9 @@ function geminiExactShellCommandPattern(command) {
|
|
|
178
187
|
// is_background or additional_permissions during bootstrap.
|
|
179
188
|
return `^\\{\\x00"command":"[ ]*${escapeGeminiArgsRegex(command)}[ ]*"\\x00\\}$`;
|
|
180
189
|
}
|
|
190
|
+
function geminiManagedIdentityPathPattern(filePath) {
|
|
191
|
+
return geminiJsonFieldEqualsPattern(['file_path'], path.resolve(filePath));
|
|
192
|
+
}
|
|
181
193
|
function appendGeminiPolicyRule(lines, toolName, decision, priority, argsPattern) {
|
|
182
194
|
lines.push('[[rule]]');
|
|
183
195
|
lines.push(`toolName = ${Array.isArray(toolName) ? JSON.stringify(toolName) : JSON.stringify(toolName)}`);
|
|
@@ -186,7 +198,7 @@ function appendGeminiPolicyRule(lines, toolName, decision, priority, argsPattern
|
|
|
186
198
|
lines.push(`decision = "${decision}"`);
|
|
187
199
|
lines.push(`priority = ${priority}`, '');
|
|
188
200
|
}
|
|
189
|
-
export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath) {
|
|
201
|
+
export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath, personalMdPath) {
|
|
190
202
|
const lines = [];
|
|
191
203
|
if (bootstrapAgentMdPath) {
|
|
192
204
|
const bootstrap = normalizeGeminiBootstrapAgentMdPath(bootstrapAgentMdPath);
|
|
@@ -204,6 +216,12 @@ export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath) {
|
|
|
204
216
|
appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_FILE_TOOLS, 'allow', 990, writePathPattern);
|
|
205
217
|
appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, geminiExactShellCommandPattern(`ec aid agentmd put ${bootstrap.aid}`));
|
|
206
218
|
appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, geminiExactShellCommandPattern(`ec agent ready ${bootstrap.aid}`));
|
|
219
|
+
appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, GEMINI_BOOTSTRAP_PERSONA_COMMAND_PATTERN);
|
|
220
|
+
if (personalMdPath) {
|
|
221
|
+
const personalPathPattern = geminiManagedIdentityPathPattern(personalMdPath);
|
|
222
|
+
appendGeminiPolicyRule(lines, 'read_file', 'allow', 989, personalPathPattern);
|
|
223
|
+
appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_FILE_TOOLS, 'allow', 989, personalPathPattern);
|
|
224
|
+
}
|
|
207
225
|
// These are Gemini's non-I/O task bookkeeping tools. They do not expose
|
|
208
226
|
// project files, shell, network, MCP, or user-interaction capabilities.
|
|
209
227
|
appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_LIFECYCLE_TOOLS, 'allow', 950);
|
|
@@ -223,6 +241,11 @@ export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath) {
|
|
|
223
241
|
for (const pattern of GEMINI_L_CLASS_PATTERNS) {
|
|
224
242
|
appendGeminiPolicyRule(lines, GEMINI_L_CLASS_WRITE_TOOLS, 'deny', 997, pattern);
|
|
225
243
|
}
|
|
244
|
+
if (personalMdPath) {
|
|
245
|
+
const personalPathPattern = geminiManagedIdentityPathPattern(personalMdPath);
|
|
246
|
+
appendGeminiPolicyRule(lines, 'read_file', 'allow', 989, personalPathPattern);
|
|
247
|
+
appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_FILE_TOOLS, 'allow', 989, personalPathPattern);
|
|
248
|
+
}
|
|
226
249
|
if (profile.mode === 'readonly') {
|
|
227
250
|
appendGeminiPolicyRule(lines, GEMINI_READONLY_TOOLS, 'allow', 950);
|
|
228
251
|
appendGeminiPolicyRule(lines, '*', 'deny', 900);
|
|
@@ -286,11 +309,11 @@ export class GeminiRunner {
|
|
|
286
309
|
permissionContexts = new Map();
|
|
287
310
|
constructor(config, callbacks) {
|
|
288
311
|
this.resolved = resolveGoogleConfig(config);
|
|
289
|
-
this.model = this.resolved.model;
|
|
312
|
+
this.model = stripContextWindowSuffix(this.resolved.model);
|
|
290
313
|
this.onSessionIdUpdate = callbacks.onSessionIdUpdate;
|
|
291
314
|
}
|
|
292
315
|
// ── ModelSwitcher ──
|
|
293
|
-
setModel(model) { this.model = model; }
|
|
316
|
+
setModel(model) { this.model = stripContextWindowSuffix(model); }
|
|
294
317
|
getModel() { return this.model; }
|
|
295
318
|
async listModels() { const catalog = await getCatalog(undefined, 'gemini'); assertCatalogSelectable(catalog); return catalog.models.map(m => m.id); }
|
|
296
319
|
// ── Effort (not applicable) ──
|
|
@@ -357,6 +380,9 @@ export class GeminiRunner {
|
|
|
357
380
|
const bootstrapAgentMdPath = modelOverride?.bootstrapAgentMdPath
|
|
358
381
|
? normalizeGeminiBootstrapAgentMdPath(modelOverride.bootstrapAgentMdPath).filePath
|
|
359
382
|
: undefined;
|
|
383
|
+
const personalMdPath = modelOverride?.personalMdPath
|
|
384
|
+
? path.resolve(modelOverride.personalMdPath)
|
|
385
|
+
: undefined;
|
|
360
386
|
const permissionProfile = resolveGeminiPermissionProfile(requestedPermissionMode, !!bootstrapAgentMdPath);
|
|
361
387
|
const callModel = modelOverride?.model || this.model;
|
|
362
388
|
const managedTaskTmpDir = getManagedTaskTempDir(runtimeEnv);
|
|
@@ -374,6 +400,7 @@ export class GeminiRunner {
|
|
|
374
400
|
...(runtimeDir ? [runtimeDir] : []),
|
|
375
401
|
...(runtimeLockDir ? [runtimeLockDir] : []),
|
|
376
402
|
...(bootstrapAgentMdPath ? [bootstrapAgentMdPath] : []),
|
|
403
|
+
...(personalMdPath ? [personalMdPath] : []),
|
|
377
404
|
],
|
|
378
405
|
});
|
|
379
406
|
if (executionPermissionMode !== 'fullaccess' && !sandboxProbe && workspaceContainsHClassPaths(projectPath)) {
|
|
@@ -402,7 +429,7 @@ export class GeminiRunner {
|
|
|
402
429
|
}
|
|
403
430
|
else {
|
|
404
431
|
const policyPath = path.join(managedTmpDir, `evolcore-gemini-permission-${crypto.randomUUID()}.toml`);
|
|
405
|
-
fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile, bootstrapAgentMdPath), { mode: 0o600, flag: 'wx' });
|
|
432
|
+
fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile, bootstrapAgentMdPath, personalMdPath), { mode: 0o600, flag: 'wx' });
|
|
406
433
|
tempFiles.push(policyPath);
|
|
407
434
|
args.push(...buildGeminiPermissionArgs(permissionProfile, policyPath, !!sandboxProbe));
|
|
408
435
|
}
|
|
@@ -444,6 +471,7 @@ export class GeminiRunner {
|
|
|
444
471
|
...(runtimeDir ? [runtimeDir] : []),
|
|
445
472
|
...(runtimeLockDir ? [runtimeLockDir] : []),
|
|
446
473
|
...(bootstrapAgentMdPath ? [bootstrapAgentMdPath] : []),
|
|
474
|
+
...(personalMdPath ? [personalMdPath] : []),
|
|
447
475
|
],
|
|
448
476
|
readonlyPaths: tempFiles,
|
|
449
477
|
});
|
|
@@ -687,6 +715,56 @@ export class GeminiRunner {
|
|
|
687
715
|
logger.info(`[GeminiRunner] interaction.auto_resolved kind=${normalizeGeminiToolName(toolName)} session=${sessionId}`);
|
|
688
716
|
return;
|
|
689
717
|
}
|
|
718
|
+
const lifecycleRequestId = requestId ?? `gemini-approval-${crypto.randomUUID()}`;
|
|
719
|
+
const auditSummary = `${toolName} runtime authorization denied by Gemini headless policy`;
|
|
720
|
+
auditPermissionLifecycle({
|
|
721
|
+
phase: 'request',
|
|
722
|
+
requestId: lifecycleRequestId,
|
|
723
|
+
sessionId,
|
|
724
|
+
toolName,
|
|
725
|
+
decision: 'pending',
|
|
726
|
+
decisionSource: 'approval',
|
|
727
|
+
permissionMode: context.permissionMode,
|
|
728
|
+
agentAid: context.selfAid,
|
|
729
|
+
agentName: context.agentName,
|
|
730
|
+
role: context.role,
|
|
731
|
+
taskId: context.taskId,
|
|
732
|
+
summary: auditSummary,
|
|
733
|
+
});
|
|
734
|
+
auditPermissionLifecycle({
|
|
735
|
+
phase: 'response',
|
|
736
|
+
requestId: lifecycleRequestId,
|
|
737
|
+
sessionId,
|
|
738
|
+
toolName,
|
|
739
|
+
decision: 'deny',
|
|
740
|
+
decisionSource: 'policy',
|
|
741
|
+
policyCode: 'permission_mode_denied',
|
|
742
|
+
reason: auditSummary,
|
|
743
|
+
permissionMode: context.permissionMode,
|
|
744
|
+
agentAid: context.selfAid,
|
|
745
|
+
agentName: context.agentName,
|
|
746
|
+
role: context.role,
|
|
747
|
+
taskId: context.taskId,
|
|
748
|
+
summary: auditSummary,
|
|
749
|
+
response: 'deny',
|
|
750
|
+
});
|
|
751
|
+
auditPermissionLifecycle({
|
|
752
|
+
phase: 'final',
|
|
753
|
+
requestId: lifecycleRequestId,
|
|
754
|
+
sessionId,
|
|
755
|
+
toolName,
|
|
756
|
+
decision: 'deny',
|
|
757
|
+
decisionSource: 'policy',
|
|
758
|
+
policyCode: 'permission_mode_denied',
|
|
759
|
+
reason: auditSummary,
|
|
760
|
+
permissionMode: context.permissionMode,
|
|
761
|
+
agentAid: context.selfAid,
|
|
762
|
+
agentName: context.agentName,
|
|
763
|
+
role: context.role,
|
|
764
|
+
taskId: context.taskId,
|
|
765
|
+
summary: auditSummary,
|
|
766
|
+
response: 'deny',
|
|
767
|
+
});
|
|
690
768
|
try {
|
|
691
769
|
await context.recordExecutionAnomaly?.({
|
|
692
770
|
code: 'runtime_permission_denied',
|
|
@@ -63,7 +63,7 @@ export function usageForContext(usage) {
|
|
|
63
63
|
return lastIteration ?? usage;
|
|
64
64
|
}
|
|
65
65
|
/** Models whose base ID uses a 1M context window when sent to the SDK with [1m]. */
|
|
66
|
-
export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-4-8', 'claude-sonnet-4-6'];
|
|
66
|
+
export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-5', 'claude-opus-4-8', 'claude-sonnet-4-6'];
|
|
67
67
|
const ONE_M_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet'];
|
|
68
68
|
const CLAUDE_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet', 'haiku'];
|
|
69
69
|
export function isClaudeContextUsageModel(model) {
|
package/dist/aun/aid/agentmd.js
CHANGED
|
@@ -4,6 +4,51 @@ import { parseDocument } from 'yaml';
|
|
|
4
4
|
import { getAidStore, loadAid, SLOT } from './store.js';
|
|
5
5
|
import { agentMdPath, aidLocalDir, resolveRoot } from '../../paths.js';
|
|
6
6
|
export const DEFAULT_AGENT_DESCRIPTION = 'EvolCore AI agent.';
|
|
7
|
+
export const AGENT_TAG_MIN_COUNT = 3;
|
|
8
|
+
export const AGENT_TAG_MAX_COUNT = 6;
|
|
9
|
+
export const AGENT_TAG_MAX_LENGTH = 50;
|
|
10
|
+
const AGENT_TAG_PATTERN = /^[\p{L}\p{N}]+$/u;
|
|
11
|
+
/** Return a human-readable validation error for one tag, or null when valid. */
|
|
12
|
+
export function validateAgentTag(tag) {
|
|
13
|
+
if (typeof tag !== 'string' || !tag.trim())
|
|
14
|
+
return 'must be a non-empty string';
|
|
15
|
+
const normalized = tag.trim();
|
|
16
|
+
if (Array.from(normalized).length > AGENT_TAG_MAX_LENGTH) {
|
|
17
|
+
return `must be at most ${AGENT_TAG_MAX_LENGTH} characters`;
|
|
18
|
+
}
|
|
19
|
+
if (!AGENT_TAG_PATTERN.test(normalized)) {
|
|
20
|
+
return 'must contain only Unicode letters and numbers';
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validate an agent.md tags value. Count constraints are optional because
|
|
26
|
+
* existing published agent.md files may contain fewer than three tags.
|
|
27
|
+
*/
|
|
28
|
+
export function validateAgentTags(tags, options = {}) {
|
|
29
|
+
if (!Array.isArray(tags))
|
|
30
|
+
return ['must be an array of strings'];
|
|
31
|
+
const errors = [];
|
|
32
|
+
const normalized = new Set();
|
|
33
|
+
tags.forEach((tag, index) => {
|
|
34
|
+
const error = validateAgentTag(tag);
|
|
35
|
+
if (error) {
|
|
36
|
+
errors.push(`tag ${index + 1} ${error}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const value = tag.trim();
|
|
40
|
+
normalized.add(value);
|
|
41
|
+
});
|
|
42
|
+
const minCount = options.minCount;
|
|
43
|
+
const maxCount = options.maxCount;
|
|
44
|
+
if (minCount !== undefined && normalized.size < minCount) {
|
|
45
|
+
errors.push(`must contain at least ${minCount} unique tags`);
|
|
46
|
+
}
|
|
47
|
+
if (maxCount !== undefined && normalized.size > maxCount) {
|
|
48
|
+
errors.push(`must contain at most ${maxCount} unique tags`);
|
|
49
|
+
}
|
|
50
|
+
return errors;
|
|
51
|
+
}
|
|
7
52
|
const displayNameCache = new Map();
|
|
8
53
|
const displayNamePending = new Map();
|
|
9
54
|
const displayNameRevision = new Map();
|
|
@@ -87,7 +132,12 @@ export function buildInitialAgentMd(opts) {
|
|
|
87
132
|
const agentName = opts.name?.trim() || opts.aid.split('.')[0];
|
|
88
133
|
const agentType = opts.type || 'ai';
|
|
89
134
|
const description = opts.description?.trim() || DEFAULT_AGENT_DESCRIPTION;
|
|
90
|
-
|
|
135
|
+
const rawTags = opts.tags?.length ? opts.tags : ['evolcore'];
|
|
136
|
+
const tagErrors = validateAgentTags(rawTags);
|
|
137
|
+
if (tagErrors.length > 0)
|
|
138
|
+
throw new Error(`Invalid agent.md tags: ${tagErrors.join('; ')}`);
|
|
139
|
+
const tags = [...new Set(rawTags.map(tag => tag.trim()))];
|
|
140
|
+
return `---\naid: ${yamlDoubleQuote(opts.aid)}\nname: ${yamlDoubleQuote(agentName)}\ntype: ${yamlDoubleQuote(agentType)}\nversion: "1.0.0"\ndescription: ${yamlDoubleQuote(description)}\ntags:\n${tags.map(tag => ` - ${yamlDoubleQuote(tag)}\n`).join('')}---\n`;
|
|
91
141
|
}
|
|
92
142
|
function ensureTrailingNewline(content) {
|
|
93
143
|
return content.endsWith('\n') ? content : `${content}\n`;
|
|
@@ -145,9 +195,11 @@ export function validateAgentMdFrontmatter(content, expectedAid) {
|
|
|
145
195
|
if (description && description.length > 100) {
|
|
146
196
|
errors.push('agent.md frontmatter description must be at most 100 characters');
|
|
147
197
|
}
|
|
148
|
-
if (values.tags !== undefined
|
|
149
|
-
|
|
150
|
-
|
|
198
|
+
if (values.tags !== undefined) {
|
|
199
|
+
const tagErrors = validateAgentTags(values.tags);
|
|
200
|
+
if (tagErrors.length > 0) {
|
|
201
|
+
errors.push(`agent.md frontmatter tags ${tagErrors.join('; ')}`);
|
|
202
|
+
}
|
|
151
203
|
}
|
|
152
204
|
return { valid: errors.length === 0, errors };
|
|
153
205
|
}
|
package/dist/aun/aid/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { AGENT_TAG_MIN_COUNT, AGENT_TAG_MAX_COUNT, AGENT_TAG_MAX_LENGTH, validateAgentTag, validateAgentTags, } from './agentmd.js';
|
|
1
2
|
export { isValidAid, aidList, aidListVerified, aidCreate, aidShow, aidDelete, aidLookup, verifySignAbility, probePkiRecoverability, appendAidLifecycle, readAidLifecycle } from './identity.js';
|
|
2
3
|
export { DEFAULT_AGENT_DESCRIPTION, buildInitialAgentMd, validateAgentMdFrontmatter, agentmdGet, agentmdPut, agentmdSync, invalidateAgentDisplayName, parseAgentDisplayName, refreshAgentDisplayName, resolveAgentDisplayName, stripAgentMdSignature, updateAgentMdFrontmatterAvatar, updateAgentMdFrontmatterName, } from './agentmd.js';
|
|
3
4
|
export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, suppressSdkLogs, } from './client.js';
|
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { resolveRoot } from '../../paths.js';
|
|
4
4
|
import { agentmdGet, agentmdPut } from './agentmd.js';
|
|
5
|
+
import { uploadAvatarBuffer, uploadAvatarUrl } from '../../utils/avatar-upload.js';
|
|
5
6
|
import { aidLookup, aidShow } from './identity.js';
|
|
6
7
|
import { isValidAid } from './validation.js';
|
|
7
8
|
export class ManagedAidOperationError extends Error {
|
|
@@ -28,13 +29,42 @@ function parseAid(value) {
|
|
|
28
29
|
return { ok: false, code: 'INVALID_AID', reason: `invalid AID: ${aid}` };
|
|
29
30
|
return { ok: true, aid };
|
|
30
31
|
}
|
|
32
|
+
function parseAvatar(args) {
|
|
33
|
+
const aidResult = parseAid(args[1]);
|
|
34
|
+
if (!aidResult.ok)
|
|
35
|
+
return aidResult;
|
|
36
|
+
const sourceFlag = args[2];
|
|
37
|
+
const sourceValue = args[3];
|
|
38
|
+
if (sourceFlag !== '--file' && sourceFlag !== '--url') {
|
|
39
|
+
return { ok: false, code: 'INVALID_ARGUMENT', reason: 'avatar requires exactly one of --file <path> or --url <https-url>' };
|
|
40
|
+
}
|
|
41
|
+
if (!sourceValue || sourceValue.startsWith('-')) {
|
|
42
|
+
return { ok: false, code: 'INVALID_ARGUMENT', reason: `${sourceFlag} requires a non-empty value` };
|
|
43
|
+
}
|
|
44
|
+
if (args.length !== 4 && !(args.length === 6 && args[4] === '--format' && args[5] === 'json')) {
|
|
45
|
+
return { ok: false, code: 'NOT_ALLOWED', reason: 'avatar only supports an optional --format json suffix' };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
command: {
|
|
50
|
+
kind: 'avatar',
|
|
51
|
+
aid: aidResult.aid,
|
|
52
|
+
...(sourceFlag === '--file' ? { filePath: sourceValue } : { url: sourceValue }),
|
|
53
|
+
operationId: 'ec.aid.avatar',
|
|
54
|
+
canonicalArgv: ['aid', 'avatar', aidResult.aid, sourceFlag, sourceValue],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
31
58
|
/**
|
|
32
|
-
* The daemon accepts only the
|
|
33
|
-
* tasks.
|
|
59
|
+
* The daemon accepts only the narrowly scoped AID commands exposed to managed
|
|
60
|
+
* tasks. Public lookup/get may name any valid AID; local show/publish remain
|
|
61
|
+
* subject to the self-AID boundary enforced by the command handler.
|
|
34
62
|
*/
|
|
35
63
|
export function resolveManagedAidOperation(argv) {
|
|
36
64
|
const args = argv[0] === 'aid' ? argv.slice(1) : argv;
|
|
37
65
|
const subcommand = args[0];
|
|
66
|
+
if (subcommand === 'avatar')
|
|
67
|
+
return parseAvatar(args);
|
|
38
68
|
if ((subcommand === 'show' || subcommand === 'lookup') && hasOnlyOptionalJsonFormat(args, 1)) {
|
|
39
69
|
const parsedAid = parseAid(args[1]);
|
|
40
70
|
if (!parsedAid.ok)
|
|
@@ -67,7 +97,21 @@ export function resolveManagedAidOperation(argv) {
|
|
|
67
97
|
return {
|
|
68
98
|
ok: false,
|
|
69
99
|
code: 'NOT_ALLOWED',
|
|
70
|
-
reason: 'Managed AID operations only allow exact
|
|
100
|
+
reason: 'Managed AID operations only allow exact show, lookup, agentmd get, agentmd put, or avatar commands with supported options',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function managedAidShowViewForRole(role) {
|
|
104
|
+
return role === 'owner' || role === 'admin' ? 'full' : 'redacted';
|
|
105
|
+
}
|
|
106
|
+
export function redactAidShowResult(value) {
|
|
107
|
+
return {
|
|
108
|
+
aid: value.aid,
|
|
109
|
+
redacted: true,
|
|
110
|
+
hasAgentMd: value.hasAgentMd,
|
|
111
|
+
certExpiresAt: value.certExpiresAt,
|
|
112
|
+
certSubject: value.certSubject,
|
|
113
|
+
certExpired: value.certExpired,
|
|
114
|
+
agentMdSignature: value.agentMdSignature,
|
|
71
115
|
};
|
|
72
116
|
}
|
|
73
117
|
const defaultDependencies = () => ({
|
|
@@ -78,14 +122,23 @@ const defaultDependencies = () => ({
|
|
|
78
122
|
agentmdPut,
|
|
79
123
|
existsSync: fs.existsSync,
|
|
80
124
|
readFileSync: fs.readFileSync,
|
|
125
|
+
uploadAvatarBuffer,
|
|
126
|
+
uploadAvatarUrl,
|
|
81
127
|
});
|
|
82
128
|
/** Execute a validated AID operation from the daemon-owned host filesystem. */
|
|
83
|
-
export async function executeManagedAidOperation(command,
|
|
129
|
+
export async function executeManagedAidOperation(command, options = {}) {
|
|
130
|
+
const { showView = 'full', ...overrides } = options;
|
|
84
131
|
const deps = { ...defaultDependencies(), ...overrides };
|
|
85
132
|
const aunPath = deps.root;
|
|
86
133
|
switch (command.kind) {
|
|
87
|
-
case 'show':
|
|
88
|
-
|
|
134
|
+
case 'show': {
|
|
135
|
+
const value = await deps.aidShow(command.aid, { aunPath });
|
|
136
|
+
return {
|
|
137
|
+
kind: command.kind,
|
|
138
|
+
aid: command.aid,
|
|
139
|
+
value: showView === 'redacted' ? redactAidShowResult(value) : value,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
89
142
|
case 'lookup':
|
|
90
143
|
return { kind: command.kind, aid: command.aid, value: await deps.aidLookup(command.aid) };
|
|
91
144
|
case 'agentmd.get':
|
|
@@ -103,5 +156,26 @@ export async function executeManagedAidOperation(command, overrides = {}) {
|
|
|
103
156
|
await deps.agentmdPut(content, { aid: command.aid, aunPath });
|
|
104
157
|
return { kind: command.kind, aid: command.aid, path: localPath };
|
|
105
158
|
}
|
|
159
|
+
case 'avatar': {
|
|
160
|
+
const result = command.url
|
|
161
|
+
? await deps.uploadAvatarUrl(command.aid, command.url, { aunPath })
|
|
162
|
+
: await deps.uploadAvatarBuffer(command.aid, options.avatarBuffer ?? deps.readFileSync(command.filePath), { aunPath });
|
|
163
|
+
if (!result.ok) {
|
|
164
|
+
throw new ManagedAidOperationError(result.code ?? 'UPLOAD_FAILED', result.error ?? 'avatar upload failed');
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
kind: 'avatar',
|
|
168
|
+
aid: command.aid,
|
|
169
|
+
avatar: result.publicUrl,
|
|
170
|
+
objectKey: result.objectKey,
|
|
171
|
+
sha256: result.sha256,
|
|
172
|
+
mime: result.mime,
|
|
173
|
+
width: result.width,
|
|
174
|
+
height: result.height,
|
|
175
|
+
aspectRatio: result.aspectRatio,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
default:
|
|
179
|
+
throw new ManagedAidOperationError('NOT_ALLOWED', `Unsupported managed AID operation: ${command.kind}`);
|
|
106
180
|
}
|
|
107
181
|
}
|
package/dist/aun/aid/store.js
CHANGED
|
@@ -7,6 +7,7 @@ import { AUN_KEYSTORE_ENCRYPTION_SEED } from './encryption-seed-policy.js';
|
|
|
7
7
|
// pages so a large backlog is drained without monopolizing the sync loop.
|
|
8
8
|
const FULL_GROUP_PULL_MAX_PAGES = 10;
|
|
9
9
|
const GROUP_PULL_PAGINATION_PATCH = Symbol('evolcore.groupPullPaginationPatch');
|
|
10
|
+
const RELIABLE_MESSAGE_DELIVERY_PATCH = Symbol('evolcore.reliableMessageDeliveryPatch');
|
|
10
11
|
/**
|
|
11
12
|
* Slot 命名约定(基于 fastaun 0.4.3 隔离键语义)。
|
|
12
13
|
*
|
|
@@ -106,6 +107,46 @@ export function enableFullGroupPullPagination(client) {
|
|
|
106
107
|
target._pullGroupV2 = wrappedPull;
|
|
107
108
|
return true;
|
|
108
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Make SDK pull delivery wait until EvolCore's application handlers finish.
|
|
112
|
+
*
|
|
113
|
+
* fastaun 0.5.13 publishes message events with dispatcher.enqueue(), then
|
|
114
|
+
* advances its sequence tracker immediately. That is a transport watermark,
|
|
115
|
+
* so a daemon restart can acknowledge a message before the application has
|
|
116
|
+
* durably claimed it. The SDK keeps _publishAppEvent private, but the public
|
|
117
|
+
* client is intentionally adapted here in one place: preserve the SDK's
|
|
118
|
+
* normal publication path and wait for its dispatcher queue for pulled
|
|
119
|
+
* message events before the pull operation commits the sequence.
|
|
120
|
+
*/
|
|
121
|
+
export function enableReliableMessageDelivery(client) {
|
|
122
|
+
const target = client;
|
|
123
|
+
if (target[RELIABLE_MESSAGE_DELIVERY_PATCH])
|
|
124
|
+
return false;
|
|
125
|
+
const publish = target._publishAppEvent;
|
|
126
|
+
const dispatcher = target._dispatcher;
|
|
127
|
+
if (typeof publish !== 'function' || !dispatcher || typeof dispatcher.flush !== 'function')
|
|
128
|
+
return false;
|
|
129
|
+
const wrappedPublish = async function (event, payload, source, ...rest) {
|
|
130
|
+
const result = await publish.call(this, event, payload, source, ...rest);
|
|
131
|
+
// Pull/recovery paths must not advance SeqTracker until the application
|
|
132
|
+
// handlers have completed. Push delivery has no pull commit to gate and
|
|
133
|
+
// remains asynchronous as intended by the SDK.
|
|
134
|
+
const reliable = (event === 'message.received' || event === 'group.message_created')
|
|
135
|
+
&& source !== 'push'
|
|
136
|
+
&& source !== 'inline-push';
|
|
137
|
+
if (reliable)
|
|
138
|
+
await dispatcher.flush();
|
|
139
|
+
return result;
|
|
140
|
+
};
|
|
141
|
+
Object.defineProperty(target, RELIABLE_MESSAGE_DELIVERY_PATCH, {
|
|
142
|
+
configurable: false,
|
|
143
|
+
enumerable: false,
|
|
144
|
+
value: true,
|
|
145
|
+
writable: false,
|
|
146
|
+
});
|
|
147
|
+
target._publishAppEvent = wrappedPublish;
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
109
150
|
/** 加载本地 AID 值对象(用于离线签名/验签,无需连接)。load 失败抛 AidLoadError。 */
|
|
110
151
|
export function loadAid(store, aid) {
|
|
111
152
|
const r = store.load(aid);
|
|
@@ -797,7 +797,12 @@ export function resolveManagedFsOperation(argv) {
|
|
|
797
797
|
const destination = remoteOrError(positionals[1]);
|
|
798
798
|
if (!('groupId' in destination))
|
|
799
799
|
return destination;
|
|
800
|
-
|
|
800
|
+
// Windows absolute paths contain a drive-letter colon, but are local
|
|
801
|
+
// upload sources rather than managed group references.
|
|
802
|
+
const isWindowsAbsolutePath = /^[A-Za-z]:[\\/]/.test(positionals[0]);
|
|
803
|
+
const source = positionals[0].includes(':') && !isWindowsAbsolutePath
|
|
804
|
+
? remoteOrError(positionals[0])
|
|
805
|
+
: undefined;
|
|
801
806
|
if (source && !('groupId' in source))
|
|
802
807
|
return source;
|
|
803
808
|
if (source && source.groupId !== destination.groupId)
|