evolcore 0.0.35 → 0.0.37
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 +42 -0
- package/README.md +6 -0
- package/bin/install-codex-managed-hooks.mjs +20 -3
- package/dist/agents/baseagent-availability.js +1 -1
- package/dist/agents/baseagent.js +88 -0
- package/dist/agents/claude-runner.js +59 -12
- package/dist/agents/codex/app-server-client.js +18 -1
- package/dist/agents/codex-runner.js +262 -21
- package/dist/agents/ecagent-runner.js +47 -11
- package/dist/agents/gemini-runner.js +36 -12
- package/dist/aun/aid/client.js +4 -3
- package/dist/aun/aid/control-aid.js +270 -8
- package/dist/aun/aid/identity.js +6 -3
- package/dist/aun/aid/ownership-operation.js +58 -0
- package/dist/aun/aid/store.js +1 -0
- package/dist/aun/group/fs-download.js +67 -2
- package/dist/aun/group/history.js +20 -5
- package/dist/aun/group/operations.js +8 -2
- package/dist/aun/menu/client.js +39 -2
- package/dist/aun/menu/transport.js +1 -1
- package/dist/aun/msg/history.js +43 -4
- package/dist/aun/msg/managed-operation.js +30 -53
- package/dist/aun/msg/p2p.js +51 -15
- package/dist/aun/outbox.js +4 -0
- package/dist/channels/aun.js +260 -73
- package/dist/channels/daemon.js +6 -4
- package/dist/channels/feishu.js +3 -0
- package/dist/cli/agent-command.js +161 -223
- package/dist/cli/agent-create.js +190 -223
- package/dist/cli/agent.js +3 -1
- package/dist/cli/aid-bind-command.js +53 -0
- package/dist/cli/aun-commands.js +384 -91
- package/dist/cli/bench.js +1 -1
- package/dist/cli/cli-argv.js +74 -0
- package/dist/cli/daemon-commands.js +105 -74
- package/dist/cli/diag.js +1 -1
- package/dist/cli/fs-command.js +18 -19
- package/dist/cli/getmeta-command.js +159 -6
- package/dist/cli/index.js +6 -12
- package/dist/cli/init-channel.js +8 -55
- package/dist/cli/init.js +125 -218
- package/dist/cli/queue-command.js +11 -8
- package/dist/cli/restart-monitor.js +177 -85
- package/dist/cli/session-command.js +1 -1
- package/dist/cli/stats.js +7 -5
- package/dist/cli/thread-command.js +103 -30
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg-diag.js +1 -0
- package/dist/config/access-policy.js +2 -2
- package/dist/config/boot-log.js +14 -11
- package/dist/config/config-field-policy.js +9 -1
- package/dist/config/config-manager.js +176 -90
- package/dist/config/config-operation-service.js +7 -14
- package/dist/config/contact-book-store.js +142 -26
- package/dist/config/contact-book.js +4 -2
- package/dist/config/lifecycle.js +1 -1
- package/dist/config/owner-config.js +96 -0
- package/dist/config/peer-role-resolver.js +12 -18
- package/dist/config/providers-config.js +382 -0
- package/dist/config/providers-import.js +74 -0
- package/dist/config/providers-management.js +328 -0
- package/dist/config/providers.js +22 -0
- package/dist/config/resolved-config-op.js +17 -14
- package/dist/config/snapshot.js +28 -16
- package/dist/config-store.js +18 -14
- package/dist/core/agent-ipc-registration.js +21 -63
- package/dist/core/agent-runtime.js +16 -5
- package/dist/core/auth/agent-delegation.js +199 -25
- package/dist/core/auth/auth-gateway.js +44 -2
- package/dist/core/auth/authorization-denial.js +1 -1
- package/dist/core/auth/debug-context.js +2 -1
- package/dist/core/auth/delegation-authorization.js +2 -2
- package/dist/core/auth/delegation-diagnostics.js +4 -1
- package/dist/core/auth/operation-authorizer.js +10 -1
- package/dist/core/auth/operation-catalog.js +19 -1
- package/dist/core/bootstrap-service.js +43 -20
- package/dist/core/channel-loader.js +0 -1
- package/dist/core/channel-runtime.js +30 -4
- package/dist/core/command/agent-control.js +196 -37
- package/dist/core/command/cli-intent-parser.js +60 -4
- package/dist/core/command/command-handler.js +212 -60
- package/dist/core/command/connect-menu.js +1 -0
- package/dist/core/command/diag-service.js +5 -4
- package/dist/core/command/file-search.js +368 -0
- package/dist/core/command/menu-catalog.js +2 -1
- package/dist/core/command/menu-handler.js +292 -107
- package/dist/core/command/menu-protocol.js +23 -0
- package/dist/core/command/role-menu.js +7 -19
- package/dist/core/command/skill-slash.js +130 -25
- package/dist/core/command/slash-handler.js +101 -53
- package/dist/core/command/topic-slash.js +16 -5
- package/dist/core/daemon-bootstrap.js +7 -1
- package/dist/core/daemon-control.js +19 -1
- package/dist/core/daemon-lifecycle.js +2 -1
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/evolagent-registry.js +13 -2
- package/dist/core/evolagent.js +3 -6
- package/dist/core/handoff/ipc-registration.js +2 -2
- package/dist/core/handoff/runtime.js +40 -6
- package/dist/core/handoff/store.js +1 -0
- package/dist/core/inference/text-inference.js +95 -6
- package/dist/core/ipc-registration.js +43 -24
- package/dist/core/memory/consolidation-context.js +4 -1
- package/dist/core/memory/consolidation.js +25 -14
- package/dist/core/message/aun-ipc-sender.js +296 -88
- package/dist/core/message/aun-request-registry.js +226 -0
- package/dist/core/message/create-status.js +33 -4
- package/dist/core/message/message-bridge.js +62 -13
- package/dist/core/message/message-log.js +0 -1
- package/dist/core/message/message-queue.js +4 -1
- package/dist/core/message/message-utils.js +8 -1
- package/dist/core/message/peer-mode.js +17 -9
- package/dist/core/message/response-engine.js +266 -124
- package/dist/core/message/retry-scheduler.js +11 -8
- package/dist/core/model/catalog-service.js +60 -18
- package/dist/core/model/model-catalog.js +112 -9
- package/dist/core/model/model-fallback.js +94 -26
- package/dist/core/ownership/owner-resolver.js +687 -0
- package/dist/core/permission/approval-gateway.js +196 -62
- package/dist/core/permission/ec-command-parser.js +138 -25
- package/dist/core/permission/filesystem-approval-scope.js +141 -0
- package/dist/core/permission/protected-paths.js +14 -1
- package/dist/core/permission/sandbox-runtime.js +171 -18
- package/dist/core/permission/tool-policy.js +13 -2
- package/dist/core/role/runtime-policy.js +3 -4
- package/dist/core/session/session-idle-monitor.js +10 -8
- package/dist/core/session/session-manager.js +72 -45
- package/dist/core/session/session-mapper.js +18 -7
- package/dist/core/session/session-renew.js +5 -4
- package/dist/core/session/session-title.js +2 -1
- package/dist/core/session/session-turn-coordinator.js +4 -2
- package/dist/core/skill/index-generator.js +2 -16
- package/dist/core/task/task-recovery-service.js +15 -2
- package/dist/core/thread/command-parser.js +28 -13
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +11 -8
- package/dist/core/thread/thread-operation.js +79 -44
- package/dist/core/thread/thread-route.js +54 -6
- package/dist/core/thread/thread-service.js +236 -45
- package/dist/core/thread/topic-id.js +248 -0
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/eck/message-renderer.js +12 -2
- package/dist/index.js +50 -27
- package/dist/ipc.js +59 -6
- package/dist/paths.js +1 -0
- package/dist/product.js +1 -0
- package/dist/response-system/engines/v1/proactive-flow.js +10 -3
- package/dist/stats/db.js +8 -0
- package/dist/stats/writer.js +26 -4
- package/dist/trigger/authorization.js +48 -3
- package/dist/trigger/feedback.js +28 -10
- package/dist/trigger/ipc-service.js +86 -10
- package/dist/trigger/local-files.js +55 -23
- package/dist/trigger/scheduler.js +57 -11
- package/dist/trigger/system-definitions.js +1 -0
- package/dist/trigger/validation.js +44 -15
- package/dist/utils/aid-bind.js +17 -297
- package/dist/utils/avatar-upload.js +16 -4
- package/dist/utils/cross-platform.js +115 -36
- package/dist/utils/ecweb-supervisor.js +1 -1
- package/dist/utils/ecweb-utils.js +8 -1
- package/dist/utils/error-utils.js +44 -6
- package/dist/utils/instance-registry.js +12 -2
- package/dist/utils/npm-ops.js +102 -27
- package/dist/utils/package-version-info.js +72 -27
- package/dist/utils/restart-safety.js +50 -0
- package/dist/utils/upgrade-jobs.js +304 -0
- package/dist/utils/windows-autostart.js +17 -10
- package/kits/docs/INDEX.md +5 -0
- package/kits/docs/aun/ABILITY_CALLING.md +239 -0
- package/kits/docs/evolcore/INDEX.md +14 -3
- package/kits/docs/evolcore/agent-create.md +55 -141
- package/kits/docs/evolcore/agent.md +38 -16
- package/kits/docs/evolcore/aid.md +34 -0
- package/kits/docs/evolcore/config.md +20 -1
- package/kits/docs/evolcore/event.md +2 -2
- package/kits/docs/evolcore/fs.md +18 -2
- package/kits/docs/evolcore/group-collaboration.md +13 -246
- package/kits/docs/evolcore/group.md +2 -1
- package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +65 -1
- package/kits/docs/evolcore/msg.md +64 -2
- package/kits/docs/evolcore/skill.md +86 -0
- package/kits/docs/evolcore/stats.md +6 -0
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +145 -51
- package/kits/docs/evolcore/trigger.md +48 -3
- package/kits/eck_manifest.json +21 -0
- package/kits/eck_message_manifest.json +4 -4
- package/kits/migrations/migrate-role-config-v5.mjs +20 -7
- package/kits/rules/01-overview.md +2 -1
- package/kits/rules/03-identity.md +3 -1
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/07-memory-and-thread.md +1 -1
- package/kits/schemas/_meta.json +16 -1
- package/kits/schemas/agent-config.schema.13.json +44 -2
- package/kits/schemas/contact-book.schema.3.json +5 -0
- package/kits/schemas/daemon.schema.6.json +3 -1
- package/kits/schemas/daemon.schema.8.json +329 -0
- package/kits/schemas/defaults.schema.5.json +39 -3
- package/kits/schemas/providers.schema.1.json +76 -0
- package/kits/schemas/relation-config.schema.9.json +5 -1
- package/kits/skills/.switches.json +28 -0
- package/kits/skills/aid-ability/SKILL.md +214 -0
- package/kits/skills/ec-agent-create/SKILL.md +162 -0
- package/kits/skills/ec-group/SKILL.md +2 -1
- package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
- package/kits/skills/ec-identity/SKILL.md +143 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
- package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
- package/kits/skills/ec-trigger/SKILL.md +167 -0
- package/kits/skills/ec-trigger/references/examples.md +153 -0
- package/kits/skills/thread-ops/SKILL.md +108 -420
- package/kits/templates/message-fragments/thread-context.md +1 -2
- package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
- package/kits/templates/roles/member.json +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +1 -0
- package/kits/templates/system-fragments/channel.md +2 -1
- package/kits/templates/system-fragments/commands.md +5 -4
- package/kits/templates/system-fragments/identity.md +2 -0
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -3
package/dist/cli/agent-create.js
CHANGED
|
@@ -4,14 +4,12 @@ import path from 'path';
|
|
|
4
4
|
import readline from 'readline';
|
|
5
5
|
import { parse } from 'yaml';
|
|
6
6
|
import { resolvePaths, aunPath as defaultAunPath, agentConfig, agentDataDir, agentPersonalDir, aidLocalDir, agentMdPath } from '../paths.js';
|
|
7
|
-
import { loadDefaults, loadAgent, saveAgent, ensureAgentDirSkeleton
|
|
7
|
+
import { loadDefaults, loadAgent, saveAgent, ensureAgentDirSkeleton } from '../config-store.js';
|
|
8
8
|
import { ConfigTarget, write as cfgWrite, ensureFile as cfgEnsure, read as cfgRead } from '../config/config-manager.js';
|
|
9
9
|
import { ipcQuery } from '../ipc.js';
|
|
10
10
|
import { CONFIG_SCHEMA_VERSION } from '../types.js';
|
|
11
11
|
import { ensureInitialRoleRegistry } from '../config/role-service.js';
|
|
12
|
-
import { addContact } from '../config/contact-request-service.js';
|
|
13
12
|
import { getCodexAppServerAvailability } from '../agents/codex-runner.js';
|
|
14
|
-
import { resolveEcagentConfig } from '../agents/baseagent.js';
|
|
15
13
|
import { AUTO_BASEAGENT_CANDIDATES, BASEAGENT_CANDIDATES, detectAvailableBaseagents, } from '../agents/baseagent-availability.js';
|
|
16
14
|
import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
|
|
17
15
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
@@ -21,6 +19,19 @@ import { isHClassPath, isLClassPath, isSameOrDescendant } from '../core/permissi
|
|
|
21
19
|
import { MAIN_CLI_BIN } from '../product.js';
|
|
22
20
|
import { uploadAvatarFile, uploadAvatarUrl } from '../utils/avatar-upload.js';
|
|
23
21
|
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
22
|
+
import { logger } from '../utils/logger.js';
|
|
23
|
+
import { creationOwnershipBinding } from '../core/ownership/owner-resolver.js';
|
|
24
|
+
/** Select the creation lifecycle independently of --non-interactive. */
|
|
25
|
+
export function resolveAgentCreateMode(args) {
|
|
26
|
+
if (args.includes('--initialize') && args.includes('--bootstrap')) {
|
|
27
|
+
throw new Error('--initialize and --bootstrap are mutually exclusive');
|
|
28
|
+
}
|
|
29
|
+
for (const flag of ['--initialize', '--bootstrap']) {
|
|
30
|
+
if (args.filter(arg => arg === flag).length > 1)
|
|
31
|
+
throw new Error(`${flag} must be supplied once`);
|
|
32
|
+
}
|
|
33
|
+
return args.includes('--bootstrap') ? 'bootstrap' : 'initialize';
|
|
34
|
+
}
|
|
24
35
|
/** Parse the current Agent's Bootstrap welcome subcommand. */
|
|
25
36
|
export function parseAgentWelcomeArgs(args) {
|
|
26
37
|
const tail = args.at(-2) === '--format' && args.at(-1) === 'json' ? args.slice(0, -2) : args;
|
|
@@ -71,24 +82,17 @@ export function parseAgentTags(raw) {
|
|
|
71
82
|
return [...new Set(tags)];
|
|
72
83
|
}
|
|
73
84
|
export function validateAgentOwnerOptions(options) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (options.owners !== undefined && (!Array.isArray(options.owners) || options.owners.length === 0)) {
|
|
78
|
-
return '--owners must be a non-empty JSON array of AIDs';
|
|
79
|
-
}
|
|
80
|
-
const owners = options.owners ?? (options.owner !== undefined ? [options.owner] : []);
|
|
81
|
-
if (owners.some(owner => typeof owner !== 'string' || !isValidAid(owner))) {
|
|
82
|
-
return 'Every owner must be a valid AID';
|
|
85
|
+
const owner = options.owner?.trim();
|
|
86
|
+
if (owner !== undefined && !isValidAid(owner)) {
|
|
87
|
+
return 'Owner must be a valid AID';
|
|
83
88
|
}
|
|
84
89
|
return null;
|
|
85
90
|
}
|
|
86
91
|
/** Shared by the standalone CLI and the authenticated daemon command path. */
|
|
87
92
|
export function parseAgentOwnerArgs(argv) {
|
|
93
|
+
if (argv.includes('--owners'))
|
|
94
|
+
throw new Error('--owners has been removed; use one --owner <aid>');
|
|
88
95
|
const hasOwner = argv.includes('--owner');
|
|
89
|
-
const hasOwners = argv.includes('--owners');
|
|
90
|
-
if (hasOwner && hasOwners)
|
|
91
|
-
throw new Error('Options --owner and --owners are mutually exclusive');
|
|
92
96
|
const value = (flag) => {
|
|
93
97
|
if (argv.filter(arg => arg === flag).length !== 1)
|
|
94
98
|
throw new Error(`${flag} must be supplied once`);
|
|
@@ -100,71 +104,17 @@ export function parseAgentOwnerArgs(argv) {
|
|
|
100
104
|
const options = {};
|
|
101
105
|
if (hasOwner)
|
|
102
106
|
options.owner = value('--owner');
|
|
103
|
-
if (hasOwners) {
|
|
104
|
-
const raw = value('--owners');
|
|
105
|
-
try {
|
|
106
|
-
options.owners = JSON.parse(raw);
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
throw new Error('--owners must be a non-empty JSON array of AIDs');
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
107
|
const error = validateAgentOwnerOptions(options);
|
|
113
108
|
if (error)
|
|
114
109
|
throw new Error(error);
|
|
115
|
-
if (options.owners)
|
|
116
|
-
options.owners = [...new Set(options.owners)];
|
|
117
110
|
return options;
|
|
118
111
|
}
|
|
119
|
-
function withStaticOwners(config, ownersInput) {
|
|
120
|
-
const values = (ownersInput ?? []).map(value => value.trim()).filter(Boolean);
|
|
121
|
-
if (values.length === 0)
|
|
122
|
-
return config;
|
|
123
|
-
const owners = config.owners ?? [];
|
|
124
|
-
return { ...config, owners: [...new Set([...owners, ...values])] };
|
|
125
|
-
}
|
|
126
|
-
function withStaticOwner(config, owner) {
|
|
127
|
-
return withStaticOwners(config, owner ? [owner] : undefined);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* An Agent owner must be able to receive the new Agent's Bootstrap reply.
|
|
131
|
-
* Contact admission is per local Agent, so materialize the new AID in each
|
|
132
|
-
* locally managed owner's contact book immediately after creation. Human or
|
|
133
|
-
* remote owners have no local contact book and are reported as skipped.
|
|
134
|
-
*/
|
|
135
|
-
export async function syncOwnerContactsAfterCreate(createdAid, owners) {
|
|
136
|
-
const synced = [];
|
|
137
|
-
const skipped = [];
|
|
138
|
-
const errors = [];
|
|
139
|
-
for (const ownerAid of [...new Set((owners ?? []).map(value => value.trim()).filter(Boolean))]) {
|
|
140
|
-
if (ownerAid === createdAid)
|
|
141
|
-
continue;
|
|
142
|
-
const ownerConfig = path.join(resolvePaths().agentsDir, ownerAid, 'config.json');
|
|
143
|
-
if (!fs.existsSync(ownerConfig)) {
|
|
144
|
-
skipped.push(ownerAid);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
try {
|
|
148
|
-
await addContact({
|
|
149
|
-
selfAid: ownerAid,
|
|
150
|
-
primaryId: createdAid,
|
|
151
|
-
actorId: 'agent-create-owner-sync',
|
|
152
|
-
});
|
|
153
|
-
synced.push(ownerAid);
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
errors.push(`${ownerAid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return {
|
|
160
|
-
...(synced.length > 0 ? { ownerContactsSynced: synced } : {}),
|
|
161
|
-
...(skipped.length > 0 ? { ownerContactsSkipped: skipped } : {}),
|
|
162
|
-
...(errors.length > 0 ? { ownerContactSyncErrors: errors } : {}),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
112
|
function pickDefaultBaseagent(available) {
|
|
166
113
|
return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate)) ?? null;
|
|
167
114
|
}
|
|
115
|
+
function pickInteractiveDefaultBaseagent(available) {
|
|
116
|
+
return available.includes('ecagent') ? 'ecagent' : pickDefaultBaseagent(available);
|
|
117
|
+
}
|
|
168
118
|
function readProjectMetadata(project) {
|
|
169
119
|
try {
|
|
170
120
|
const packageJsonPath = path.join(project, 'package.json');
|
|
@@ -196,7 +146,6 @@ export function resolveAgentCreatePlan(options, projectCwd = process.cwd()) {
|
|
|
196
146
|
const project = path.resolve(projectCwd, options.project || '.');
|
|
197
147
|
const metadata = readProjectMetadata(project);
|
|
198
148
|
const defaults = loadDefaults();
|
|
199
|
-
const daemon = loadDaemonConfig();
|
|
200
149
|
const configuredBaseagent = defaults?.active_baseagent;
|
|
201
150
|
const available = detectAvailableBaseagents();
|
|
202
151
|
const defaultBaseagent = configuredBaseagent && available.includes(configuredBaseagent)
|
|
@@ -206,9 +155,9 @@ export function resolveAgentCreatePlan(options, projectCwd = process.cwd()) {
|
|
|
206
155
|
const baseagentSource = options.baseagent
|
|
207
156
|
? 'argument'
|
|
208
157
|
: configuredBaseagent && baseagent === configuredBaseagent ? 'defaults' : 'detected';
|
|
209
|
-
const
|
|
210
|
-
const owner = options.owner?.trim() ||
|
|
211
|
-
const ownerSource =
|
|
158
|
+
const hasExplicitOwner = options.owner !== undefined;
|
|
159
|
+
const owner = options.owner?.trim() || undefined;
|
|
160
|
+
const ownerSource = hasExplicitOwner ? 'argument' : 'none';
|
|
212
161
|
const name = options.name?.trim() || metadata.name || projectSlug(project);
|
|
213
162
|
// description 是可选字段:用户显式传入的必须合规(超长会在校验阶段被拒);
|
|
214
163
|
// 从 package.json 推导出来的超长值直接弃用、回落默认短描述——用户没设过它,
|
|
@@ -224,7 +173,7 @@ export function resolveAgentCreatePlan(options, projectCwd = process.cwd()) {
|
|
|
224
173
|
const type = requestedType ? (normalizeAgentType(requestedType) ?? requestedType) : DEFAULT_AGENT_TYPE;
|
|
225
174
|
const configExists = isValidAid(aid) && fs.existsSync(path.join(resolvePaths().agentsDir, aid, 'config.json'));
|
|
226
175
|
return {
|
|
227
|
-
aid, project, baseagent, owner,
|
|
176
|
+
aid, project, baseagent, owner, avatarFile: options.avatarFile, avatarUrl: options.avatarUrl,
|
|
228
177
|
name, description, type, tags: options.tags,
|
|
229
178
|
force: options.force === true, configExists, baseagentSource, ownerSource,
|
|
230
179
|
nameSource: options.name?.trim() ? 'argument' : metadata.name ? 'package' : 'directory',
|
|
@@ -237,7 +186,7 @@ export function validateAgentCreatePlan(plan) {
|
|
|
237
186
|
if (ownerError)
|
|
238
187
|
return ownerError;
|
|
239
188
|
if (!isValidAid(plan.aid))
|
|
240
|
-
return `Invalid AID "${plan.aid}": must be a valid multi-level domain (e.g. mybot.agentid.
|
|
189
|
+
return `Invalid AID "${plan.aid}": must be a valid multi-level domain (e.g. mybot.agentid.cn)`;
|
|
241
190
|
if (plan.avatarFile !== undefined && plan.avatarUrl !== undefined)
|
|
242
191
|
return '--avatar-file and --avatar-url are mutually exclusive';
|
|
243
192
|
if (plan.configExists && !plan.force)
|
|
@@ -250,6 +199,9 @@ export function validateAgentCreatePlan(plan) {
|
|
|
250
199
|
const typeError = validateAgentType(plan.type);
|
|
251
200
|
if (typeError)
|
|
252
201
|
return `Invalid --type: ${typeError}`;
|
|
202
|
+
if (normalizeAgentType(plan.type) === 'agent' && !plan.owner) {
|
|
203
|
+
return 'An owner AID is required when creating an agent';
|
|
204
|
+
}
|
|
253
205
|
// 只有显式传入的 --description 才会超限(package.json 的超长值在 resolve 阶段
|
|
254
206
|
// 已被弃用)。必须在这里就拒绝:否则 plan 通过、直到 agentmdPut 上传才失败,
|
|
255
207
|
// 而那处是 warn 级不中断创建,最终留下一张 SDK 兜底名片(--type/--name 全部丢失)。
|
|
@@ -265,22 +217,8 @@ export function validateAgentCreatePlan(plan) {
|
|
|
265
217
|
if (!detectAvailableBaseagents().includes(plan.baseagent)) {
|
|
266
218
|
return `${plan.baseagent} is not available in the current environment`;
|
|
267
219
|
}
|
|
268
|
-
if (plan.baseagent === 'ecagent') {
|
|
269
|
-
const reason = ecagentUnavailableReason();
|
|
270
|
-
if (reason)
|
|
271
|
-
return reason;
|
|
272
|
-
}
|
|
273
220
|
return null;
|
|
274
221
|
}
|
|
275
|
-
function ecagentUnavailableReason() {
|
|
276
|
-
try {
|
|
277
|
-
resolveEcagentConfig({ agents: { ecagent: {} } });
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
280
|
-
catch (error) {
|
|
281
|
-
return error instanceof Error ? error.message : String(error);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
222
|
function saveInitialBehavior(aid, baseagent) {
|
|
285
223
|
const behavior = {
|
|
286
224
|
active_baseagent: baseagent,
|
|
@@ -302,7 +240,7 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
302
240
|
const ask = (q) => new Promise(r => rl.question(q, r));
|
|
303
241
|
try {
|
|
304
242
|
const { aidCreate, buildInitialAgentMd, agentmdPut } = await import('../aun/aid/index.js');
|
|
305
|
-
const aidPrompt = opts.suggestedName ? `AID [${opts.suggestedName}]: ` : 'AID (e.g. mybot.agentid.
|
|
243
|
+
const aidPrompt = opts.suggestedName ? `AID [${opts.suggestedName}]: ` : 'AID (e.g. mybot.agentid.cn): ';
|
|
306
244
|
let aid = '';
|
|
307
245
|
while (!aid) {
|
|
308
246
|
const aidInput = (await ask(aidPrompt)).trim();
|
|
@@ -312,7 +250,7 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
312
250
|
continue;
|
|
313
251
|
}
|
|
314
252
|
if (!isValidAid(candidate)) {
|
|
315
|
-
console.log(` ⚠ Invalid AID "${candidate}": must be a valid multi-level domain (e.g. mybot.agentid.
|
|
253
|
+
console.log(` ⚠ Invalid AID "${candidate}": must be a valid multi-level domain (e.g. mybot.agentid.cn)`);
|
|
316
254
|
continue;
|
|
317
255
|
}
|
|
318
256
|
aid = candidate;
|
|
@@ -327,19 +265,6 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
327
265
|
return { ok: false, error: 'Aborted.' };
|
|
328
266
|
}
|
|
329
267
|
console.log(`\nCreating agent: ${aid}\n`);
|
|
330
|
-
let aidCreated = false;
|
|
331
|
-
try {
|
|
332
|
-
const result = await aidCreate(aid);
|
|
333
|
-
try {
|
|
334
|
-
await result.client.close();
|
|
335
|
-
}
|
|
336
|
-
catch { /* ignore */ }
|
|
337
|
-
aidCreated = !result.alreadyExisted;
|
|
338
|
-
console.log(` ✓ AID ${result.alreadyExisted ? 'reused' : 'created'}: ${aid}`);
|
|
339
|
-
}
|
|
340
|
-
catch (e) {
|
|
341
|
-
return { ok: false, error: `AID creation failed: ${e?.message || e}` };
|
|
342
|
-
}
|
|
343
268
|
let suggestedProjectPath = '';
|
|
344
269
|
try {
|
|
345
270
|
suggestedProjectPath = deriveAgentProjectPath(agentProjectRootFromDefaults(loadDefaults(), p.root), aid);
|
|
@@ -366,16 +291,12 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
366
291
|
return { ok: false, error: 'Aborted.' };
|
|
367
292
|
}
|
|
368
293
|
const available = detectAvailableBaseagents();
|
|
369
|
-
if (available.length === 0
|
|
370
|
-
return { ok: false, error: 'No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or
|
|
294
|
+
if (available.length === 0) {
|
|
295
|
+
return { ok: false, error: 'No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or use bundled ecagent.' };
|
|
371
296
|
}
|
|
372
|
-
const defaultBa =
|
|
297
|
+
const defaultBa = pickInteractiveDefaultBaseagent(available);
|
|
373
298
|
let baseagent;
|
|
374
|
-
|
|
375
|
-
console.log(` Baseagent: ${defaultBa}`);
|
|
376
|
-
baseagent = defaultBa;
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
299
|
+
{
|
|
379
300
|
let chosen = null;
|
|
380
301
|
while (chosen === null) {
|
|
381
302
|
const defaultHint = defaultBa ? ` [${defaultBa}]` : '';
|
|
@@ -392,40 +313,19 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
392
313
|
console.log(` ${input} is not available in the current environment. Available: ${available.join('/')}`);
|
|
393
314
|
continue;
|
|
394
315
|
}
|
|
395
|
-
if (input === 'ecagent') {
|
|
396
|
-
const reason = ecagentUnavailableReason();
|
|
397
|
-
if (reason) {
|
|
398
|
-
console.log(` ecagent is unavailable: ${reason}`);
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
316
|
chosen = input;
|
|
403
317
|
}
|
|
404
318
|
baseagent = chosen;
|
|
405
319
|
}
|
|
406
|
-
const daemonCfg = loadDaemonConfig();
|
|
407
|
-
const daemonHasOwner = !!daemonCfg.owners?.length;
|
|
408
320
|
let owner;
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (!ownerInput) {
|
|
415
|
-
owner = defaultOwner;
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
if (ownerInput.toLowerCase() === 'new' || ownerInput.toLowerCase() === 'qr') {
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
if (!isValidAid(ownerInput)) {
|
|
422
|
-
console.log(` ⚠ Invalid Owner AID "${ownerInput}"`);
|
|
423
|
-
continue;
|
|
424
|
-
}
|
|
425
|
-
owner = ownerInput;
|
|
426
|
-
skipQrBinding = true;
|
|
427
|
-
break;
|
|
321
|
+
while (true) {
|
|
322
|
+
const ownerInput = (await ask('Owner AID (required): ')).trim();
|
|
323
|
+
if (!isValidAid(ownerInput)) {
|
|
324
|
+
console.log(` ⚠ Invalid Owner AID "${ownerInput}"`);
|
|
325
|
+
continue;
|
|
428
326
|
}
|
|
327
|
+
owner = ownerInput;
|
|
328
|
+
break;
|
|
429
329
|
}
|
|
430
330
|
const defaultName = aid.split('.')[0];
|
|
431
331
|
const agentName = (await ask(`Display name [${defaultName}]: `)).trim() || defaultName;
|
|
@@ -438,14 +338,28 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
438
338
|
if (tagErrors.length > 0)
|
|
439
339
|
return { ok: false, error: `Invalid tags: ${tagErrors.join('; ')}` };
|
|
440
340
|
}
|
|
341
|
+
let aidCreated = false;
|
|
342
|
+
try {
|
|
343
|
+
// Keep the registration connection independent from Ownership. Its
|
|
344
|
+
// authentication can be short-lived, so only submit binding after the
|
|
345
|
+
// complete agent.md has been published and the daemon has hot-loaded it.
|
|
346
|
+
const result = await aidCreate(aid, { type: 'agent' });
|
|
347
|
+
try {
|
|
348
|
+
await result.client.close();
|
|
349
|
+
}
|
|
350
|
+
catch { /* ignore */ }
|
|
351
|
+
aidCreated = !result.alreadyExisted;
|
|
352
|
+
console.log(` ✓ AID ${result.alreadyExisted ? 'reused' : 'created'}: ${aid}`);
|
|
353
|
+
}
|
|
354
|
+
catch (e) {
|
|
355
|
+
return { ok: false, error: `AID creation failed: ${e?.message || e}` };
|
|
356
|
+
}
|
|
441
357
|
if (ownRl)
|
|
442
358
|
rl.close();
|
|
443
|
-
saveAgent(
|
|
359
|
+
saveAgent({ $schema_version: CONFIG_SCHEMA_VERSION, aid, enabled: true, lifecycle: 'created', channels: [], projects: { defaultPath: projectPath } });
|
|
444
360
|
ensureInitialRoleRegistry(aid);
|
|
445
361
|
saveInitialBehavior(aid, baseagent);
|
|
446
362
|
ensureAgentDirSkeleton(aid);
|
|
447
|
-
if (owner)
|
|
448
|
-
await syncOwnerContactsAfterCreate(aid, [owner]);
|
|
449
363
|
let agentmdUploaded = false;
|
|
450
364
|
try {
|
|
451
365
|
const content = buildInitialAgentMd({ aid, name: agentName, description: agentDescription, tags: agentTags });
|
|
@@ -470,6 +384,13 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
470
384
|
catch (e) {
|
|
471
385
|
console.warn(` ⚠ agent.md generation failed: ${e?.message || e}`);
|
|
472
386
|
}
|
|
387
|
+
if (owner && agentmdUploaded) {
|
|
388
|
+
const config = loadAgent(aid);
|
|
389
|
+
if (config) {
|
|
390
|
+
config.lifecycle = 'ownerpending';
|
|
391
|
+
saveAgent(config);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
473
394
|
let hotLoaded = false;
|
|
474
395
|
let hotLoadError;
|
|
475
396
|
try {
|
|
@@ -480,41 +401,39 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
480
401
|
hotLoadError = ipcResult.error;
|
|
481
402
|
}
|
|
482
403
|
catch { /* daemon not running */ }
|
|
483
|
-
let
|
|
404
|
+
let ownerBindRequestedAid;
|
|
484
405
|
let ownerBindSkipped = false;
|
|
485
406
|
let ownerBindInterrupted = false;
|
|
486
|
-
if (
|
|
407
|
+
if (owner && agentmdUploaded && hotLoaded) {
|
|
487
408
|
try {
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
else
|
|
497
|
-
ownerBindInterrupted = true;
|
|
409
|
+
const bindResult = await ipcQuery(p.socket, {
|
|
410
|
+
type: 'aid.ownership',
|
|
411
|
+
argv: ['aid', 'bind', owner, '--subject', aid],
|
|
412
|
+
}, 30_000);
|
|
413
|
+
if (!bindResult?.ok)
|
|
414
|
+
throw new Error(bindResult?.error || 'Ownership bind failed');
|
|
415
|
+
ownerBindRequestedAid = owner;
|
|
498
416
|
}
|
|
499
|
-
catch {
|
|
500
|
-
|
|
501
|
-
|
|
417
|
+
catch (error) {
|
|
418
|
+
ownerBindInterrupted = true;
|
|
419
|
+
logger.warn(`[agent-create] Ownership bind interrupted for ${aid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
502
420
|
}
|
|
503
421
|
}
|
|
504
|
-
|
|
505
|
-
|
|
422
|
+
else if (owner && agentmdUploaded) {
|
|
423
|
+
ownerBindSkipped = true;
|
|
424
|
+
}
|
|
506
425
|
return {
|
|
507
426
|
ok: true,
|
|
508
427
|
aid,
|
|
428
|
+
ownerAid: owner,
|
|
509
429
|
configPath: toPosix(path.join(agentDirPath, 'config.json')),
|
|
510
430
|
aidCreated,
|
|
511
431
|
agentmdUploaded,
|
|
512
432
|
hotLoaded,
|
|
513
433
|
hotLoadError,
|
|
514
|
-
|
|
434
|
+
ownerBindRequestedAid,
|
|
515
435
|
ownerBindSkipped,
|
|
516
436
|
ownerBindInterrupted,
|
|
517
|
-
...ownerContactSync,
|
|
518
437
|
};
|
|
519
438
|
}
|
|
520
439
|
finally {
|
|
@@ -526,32 +445,6 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
526
445
|
}
|
|
527
446
|
}
|
|
528
447
|
}
|
|
529
|
-
async function promptAgentOwnerManually(aid) {
|
|
530
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
531
|
-
try {
|
|
532
|
-
while (true) {
|
|
533
|
-
const owner = (await new Promise(resolve => rl.question('\n扫码未完成。手动输入 agent owner AID(留空跳过): ', resolve))).trim();
|
|
534
|
-
if (!owner)
|
|
535
|
-
return undefined;
|
|
536
|
-
if (!isValidAid(owner)) {
|
|
537
|
-
console.log(' ⚠ Owner AID 格式无效');
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
const agent = loadAgent(aid);
|
|
541
|
-
if (!agent)
|
|
542
|
-
return undefined;
|
|
543
|
-
saveAgent(withStaticOwner(agent, owner));
|
|
544
|
-
try {
|
|
545
|
-
await ipcQuery(resolvePaths().socket, { type: 'evolagent.reload', name: aid }, 30_000);
|
|
546
|
-
}
|
|
547
|
-
catch { /* daemon not running */ }
|
|
548
|
-
return owner;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
finally {
|
|
552
|
-
rl.close();
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
448
|
export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
556
449
|
const p = resolvePaths();
|
|
557
450
|
const { aidCreate, buildInitialAgentMd, agentmdPut } = await import('../aun/aid/index.js');
|
|
@@ -560,6 +453,10 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
560
453
|
const ownerError = validateAgentOwnerOptions(opts);
|
|
561
454
|
if (ownerError)
|
|
562
455
|
return failValidating(ownerError);
|
|
456
|
+
const ownerAid = opts.owner?.trim();
|
|
457
|
+
const agentType = normalizeAgentType(opts.type ?? DEFAULT_AGENT_TYPE) ?? opts.type ?? DEFAULT_AGENT_TYPE;
|
|
458
|
+
if (agentType === 'agent' && !ownerAid)
|
|
459
|
+
return failValidating('An owner AID is required when creating an agent');
|
|
563
460
|
if (opts.tags !== undefined) {
|
|
564
461
|
const tagErrors = validateAgentTags(opts.tags, { minCount: AGENT_TAG_MIN_COUNT, maxCount: AGENT_TAG_MAX_COUNT });
|
|
565
462
|
if (tagErrors.length > 0)
|
|
@@ -577,7 +474,7 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
577
474
|
}
|
|
578
475
|
}
|
|
579
476
|
if (!isValidAid(opts.aid))
|
|
580
|
-
return failValidating(`Invalid AID "${opts.aid}": must be a valid multi-level domain (e.g. mybot.agentid.
|
|
477
|
+
return failValidating(`Invalid AID "${opts.aid}": must be a valid multi-level domain (e.g. mybot.agentid.cn)`);
|
|
581
478
|
if (hasSuppliedInitialization(opts.aid))
|
|
582
479
|
return failValidating('Use agent initialize --resume for this target');
|
|
583
480
|
const agentDirPath = path.join(p.agentsDir, opts.aid);
|
|
@@ -585,25 +482,20 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
585
482
|
if (configExists && !opts.force)
|
|
586
483
|
return failValidating(`Agent "${opts.aid}" already exists: ${agentDirPath}/config.json (use --force to overwrite)`);
|
|
587
484
|
const available = detectAvailableBaseagents();
|
|
588
|
-
if (available.length === 0
|
|
589
|
-
return failValidating('No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or
|
|
485
|
+
if (available.length === 0)
|
|
486
|
+
return failValidating('No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or use bundled ecagent.');
|
|
590
487
|
let baseagent;
|
|
591
488
|
if (opts.baseagent) {
|
|
592
489
|
if (!BASEAGENT_CANDIDATES.includes(opts.baseagent))
|
|
593
490
|
return failValidating(`Invalid baseagent: ${opts.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})`);
|
|
594
491
|
if (!available.includes(opts.baseagent))
|
|
595
492
|
return failValidating(opts.baseagent === 'codex' ? getCodexAppServerAvailability().reason || `${opts.baseagent} is not available in the current environment` : `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})`);
|
|
596
|
-
if (opts.baseagent === 'ecagent') {
|
|
597
|
-
const reason = ecagentUnavailableReason();
|
|
598
|
-
if (reason)
|
|
599
|
-
return failValidating(reason);
|
|
600
|
-
}
|
|
601
493
|
baseagent = opts.baseagent;
|
|
602
494
|
}
|
|
603
495
|
else {
|
|
604
496
|
const defaultBaseagent = pickDefaultBaseagent(available);
|
|
605
497
|
if (!defaultBaseagent)
|
|
606
|
-
return failValidating('ecagent is optional; pass --baseagent ecagent
|
|
498
|
+
return failValidating('ecagent is optional; pass --baseagent ecagent to select it explicitly');
|
|
607
499
|
baseagent = defaultBaseagent;
|
|
608
500
|
}
|
|
609
501
|
if (!path.isAbsolute(opts.project))
|
|
@@ -619,8 +511,13 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
619
511
|
opts.onPhase?.('validating', 'done');
|
|
620
512
|
opts.onPhase?.('registering_aid', 'begin');
|
|
621
513
|
let aidCreated = false;
|
|
514
|
+
// Do not persist a pending registration ownership intent here. Avatar
|
|
515
|
+
// publication uses an AUN short connection, and the SDK may resume that
|
|
516
|
+
// intent as soon as the short connection is authenticated, before the final
|
|
517
|
+
// agent.md (including avatar) has been published. Submit the bind only after
|
|
518
|
+
// the final card is in place and the Agent has a long-lived channel.
|
|
622
519
|
try {
|
|
623
|
-
const result = await aidCreate(opts.aid);
|
|
520
|
+
const result = await aidCreate(opts.aid, { type: agentType });
|
|
624
521
|
try {
|
|
625
522
|
await result.client.close();
|
|
626
523
|
}
|
|
@@ -643,17 +540,16 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
643
540
|
catch { /* ignore */ }
|
|
644
541
|
}
|
|
645
542
|
opts.onPhase?.('config_saved', 'begin');
|
|
646
|
-
saveAgent(
|
|
543
|
+
saveAgent({ $schema_version: CONFIG_SCHEMA_VERSION, aid: opts.aid, enabled: true, lifecycle: preservedLifecycle, channels: [], projects: { defaultPath: opts.project } });
|
|
647
544
|
ensureInitialRoleRegistry(opts.aid);
|
|
648
545
|
saveInitialBehavior(opts.aid, baseagent);
|
|
649
546
|
ensureAgentDirSkeleton(opts.aid);
|
|
650
|
-
const ownerContactSync = await syncOwnerContactsAfterCreate(opts.aid, opts.owners ?? (opts.owner ? [opts.owner] : undefined));
|
|
651
547
|
opts.onPhase?.('config_saved', 'done');
|
|
652
548
|
opts.onPhase?.('uploading_agentmd', 'begin');
|
|
653
549
|
let agentmdUploaded = false;
|
|
654
550
|
let agentmdError;
|
|
655
551
|
try {
|
|
656
|
-
const content = buildInitialAgentMd({ aid: opts.aid, name: opts.name, description: opts.description, type:
|
|
552
|
+
const content = buildInitialAgentMd({ aid: opts.aid, name: opts.name, description: opts.description, type: agentType, tags: opts.tags });
|
|
657
553
|
const aunPath = process.env.AUN_HOME || defaultAunPath();
|
|
658
554
|
let lastError;
|
|
659
555
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
@@ -679,6 +575,13 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
679
575
|
agentmdError = `generation failed: ${e?.message || e}`;
|
|
680
576
|
opts.onPhase?.('uploading_agentmd', 'warn', agentmdError);
|
|
681
577
|
}
|
|
578
|
+
if (ownerAid && agentmdUploaded) {
|
|
579
|
+
const config = loadAgent(opts.aid);
|
|
580
|
+
if (config) {
|
|
581
|
+
config.lifecycle = 'ownerpending';
|
|
582
|
+
saveAgent(config);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
682
585
|
let avatarUploaded = false;
|
|
683
586
|
if (opts.avatarFile || opts.avatarUrl) {
|
|
684
587
|
opts.onPhase?.('uploading_avatar', 'begin');
|
|
@@ -726,18 +629,57 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
726
629
|
hotLoadError = error instanceof Error ? error.message : String(error);
|
|
727
630
|
opts.onPhase?.('hot_loading', 'warn', hotLoadError);
|
|
728
631
|
}
|
|
632
|
+
let ownerBindRequestedAid;
|
|
633
|
+
let ownerBindSkipped = false;
|
|
634
|
+
let ownerBindInterrupted = false;
|
|
635
|
+
let ownershipBinding;
|
|
636
|
+
if (ownerAid && agentmdUploaded && hotLoaded) {
|
|
637
|
+
try {
|
|
638
|
+
let state;
|
|
639
|
+
if (runtime.bindOwnerAgent) {
|
|
640
|
+
state = await runtime.bindOwnerAgent(opts.aid, ownerAid);
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
const bindResult = await ipcQuery(p.socket, {
|
|
644
|
+
type: 'aid.ownership',
|
|
645
|
+
argv: ['aid', 'bind', ownerAid, '--subject', opts.aid],
|
|
646
|
+
}, 30_000);
|
|
647
|
+
if (!bindResult?.ok)
|
|
648
|
+
throw new Error(bindResult?.error || 'Ownership bind failed');
|
|
649
|
+
state = bindResult.state;
|
|
650
|
+
}
|
|
651
|
+
ownerBindRequestedAid = ownerAid;
|
|
652
|
+
// Reusing an existing identity (including a failed-create retry) cannot
|
|
653
|
+
// prove that an SDK-reused task originated in this creation attempt.
|
|
654
|
+
const binding = aidCreated ? creationOwnershipBinding(state?.task, opts.aid, ownerAid) : undefined;
|
|
655
|
+
if (binding) {
|
|
656
|
+
opts.onOwnershipBinding?.(binding);
|
|
657
|
+
ownershipBinding = binding;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
ownerBindInterrupted = true;
|
|
662
|
+
logger.warn(`[agent-create] Ownership bind interrupted for ${opts.aid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
else if (ownerAid && agentmdUploaded) {
|
|
666
|
+
ownerBindSkipped = true;
|
|
667
|
+
}
|
|
729
668
|
return {
|
|
730
669
|
ok: true,
|
|
731
670
|
aid: opts.aid,
|
|
732
|
-
ownerAid
|
|
671
|
+
ownerAid,
|
|
733
672
|
configPath: toPosix(path.join(agentDirPath, 'config.json')),
|
|
734
673
|
aidCreated,
|
|
735
674
|
agentmdUploaded,
|
|
736
675
|
agentmdError,
|
|
737
676
|
hotLoaded,
|
|
738
677
|
hotLoadError,
|
|
678
|
+
ownerBindRequestedAid,
|
|
679
|
+
ownerBindSkipped,
|
|
680
|
+
ownerBindInterrupted,
|
|
681
|
+
...(ownershipBinding ? { ownershipBinding } : {}),
|
|
739
682
|
...(opts.avatarFile || opts.avatarUrl ? { avatarUploaded } : {}),
|
|
740
|
-
...ownerContactSync,
|
|
741
683
|
};
|
|
742
684
|
}
|
|
743
685
|
// ---- One-shot Agent initialization (kept with Agent creation) ----
|
|
@@ -755,7 +697,7 @@ export function initializationPath(aid) {
|
|
|
755
697
|
}
|
|
756
698
|
export function initializationFingerprint(plan) {
|
|
757
699
|
return createHash('sha256').update(JSON.stringify([
|
|
758
|
-
plan.aid, plan.project, plan.baseagent, plan.
|
|
700
|
+
plan.aid, plan.project, plan.baseagent, plan.ownerAid, plan.name,
|
|
759
701
|
plan.description, plan.type, plan.tags, plan.persona ?? null,
|
|
760
702
|
plan.avatarFile ?? null, plan.avatarUrl ?? null,
|
|
761
703
|
])).digest('hex');
|
|
@@ -824,15 +766,17 @@ function failure(code, error = code) {
|
|
|
824
766
|
throw Object.assign(new Error(error), { code });
|
|
825
767
|
}
|
|
826
768
|
export function validateInitializationPlan(plan) {
|
|
827
|
-
if (!plan.name?.trim() || !plan.description?.trim() || !Array.isArray(plan.tags) || !plan.
|
|
828
|
-
failure('INITIALIZE_FIELDS_REQUIRED', 'Explicit name, description, tags and
|
|
769
|
+
if (!plan.name?.trim() || !plan.description?.trim() || !Array.isArray(plan.tags) || !plan.ownerAid?.trim()) {
|
|
770
|
+
failure('INITIALIZE_FIELDS_REQUIRED', 'Explicit name, description, tags and one Owner AID are required');
|
|
829
771
|
}
|
|
772
|
+
if (!isValidAid(plan.ownerAid))
|
|
773
|
+
failure('INVALID_ARGS', 'Owner must be a valid AID');
|
|
830
774
|
if (plan.type !== 'agent')
|
|
831
775
|
failure('INVALID_ARGS', 'Initialized targets must have type=agent');
|
|
832
776
|
if (plan.avatarFile !== undefined && plan.avatarUrl !== undefined)
|
|
833
777
|
failure('INVALID_ARGS', 'avatar-file and avatar-url are mutually exclusive');
|
|
834
778
|
const error = validateAgentCreatePlan({
|
|
835
|
-
...plan, force: false, configExists: false, baseagentSource: 'argument', ownerSource: 'argument',
|
|
779
|
+
...plan, owner: plan.ownerAid, force: false, configExists: false, baseagentSource: 'argument', ownerSource: 'argument',
|
|
836
780
|
nameSource: 'argument', descriptionSource: 'argument', typeSource: 'argument',
|
|
837
781
|
});
|
|
838
782
|
if (error)
|
|
@@ -931,6 +875,20 @@ export class AgentInitializationService {
|
|
|
931
875
|
}
|
|
932
876
|
return this.status(aid);
|
|
933
877
|
}
|
|
878
|
+
resumeForOwner(aid, ownerAid) {
|
|
879
|
+
if (!ownerAid)
|
|
880
|
+
return;
|
|
881
|
+
try {
|
|
882
|
+
const record = readInitialization(aid);
|
|
883
|
+
if (!record || record.status !== 'in_progress' || record.phase !== 'ownerpending'
|
|
884
|
+
|| this.running.has(aid))
|
|
885
|
+
return;
|
|
886
|
+
this.resume(aid, record.actor);
|
|
887
|
+
}
|
|
888
|
+
catch (error) {
|
|
889
|
+
logger.warn(`[Initialization] Owner-consensus recovery failed for ${aid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
934
892
|
async cancel(aid) {
|
|
935
893
|
const r = readInitialization(aid);
|
|
936
894
|
if (!r || r.status === 'complete')
|
|
@@ -987,7 +945,6 @@ export class AgentInitializationService {
|
|
|
987
945
|
if (r.configSaved && (!config || config.enabled === false))
|
|
988
946
|
failure('INITIALIZE_TARGET_CHANGED');
|
|
989
947
|
if (config && (config.initialization?.operationId !== r.operationId
|
|
990
|
-
|| JSON.stringify(config.owners) !== JSON.stringify(r.plan.owners)
|
|
991
948
|
|| config.active_baseagent !== r.plan.baseagent || config.projects?.defaultPath !== r.plan.project)) {
|
|
992
949
|
failure('INITIALIZE_TARGET_CHANGED');
|
|
993
950
|
}
|
|
@@ -1000,13 +957,13 @@ export class AgentInitializationService {
|
|
|
1000
957
|
async run(r) {
|
|
1001
958
|
const p = r.plan;
|
|
1002
959
|
this.phase(r, 'registering_aid');
|
|
1003
|
-
await this.deps.register(p.aid);
|
|
960
|
+
await this.deps.register(p.aid, p.ownerAid);
|
|
1004
961
|
this.phase(r, 'config_saved');
|
|
1005
962
|
fs.mkdirSync(p.project, { recursive: true });
|
|
1006
963
|
if (!loadAgent(p.aid)) {
|
|
1007
964
|
saveAgent({
|
|
1008
965
|
$schema_version: CONFIG_SCHEMA_VERSION, aid: p.aid, enabled: true, lifecycle: 'created',
|
|
1009
|
-
initialization: { mode: 'supplied', operationId: r.operationId }, channels: [],
|
|
966
|
+
initialization: { mode: 'supplied', operationId: r.operationId }, channels: [],
|
|
1010
967
|
projects: { defaultPath: p.project }, active_baseagent: p.baseagent,
|
|
1011
968
|
baseagents: { [p.baseagent]: {} }, chatmode: { private: 'interactive', group: 'proactive', nothuman: 'proactive' },
|
|
1012
969
|
});
|
|
@@ -1022,7 +979,6 @@ export class AgentInitializationService {
|
|
|
1022
979
|
fs.writeFileSync(file, p.persona, { encoding: 'utf8', mode: 0o600 });
|
|
1023
980
|
}
|
|
1024
981
|
const resolvedAvatarFile = p.avatarFile;
|
|
1025
|
-
await syncOwnerContactsAfterCreate(p.aid, p.owners);
|
|
1026
982
|
this.phase(r, 'uploading_agentmd');
|
|
1027
983
|
let remote;
|
|
1028
984
|
if (r.publicationStarted) {
|
|
@@ -1088,13 +1044,22 @@ export class AgentInitializationService {
|
|
|
1088
1044
|
}
|
|
1089
1045
|
r.published = true;
|
|
1090
1046
|
r.verified = true;
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
saveAgent(config);
|
|
1047
|
+
const currentConfig = loadAgent(p.aid);
|
|
1048
|
+
if (currentConfig.lifecycle !== 'ownerpending') {
|
|
1049
|
+
currentConfig.lifecycle = 'ownerpending';
|
|
1050
|
+
saveAgent(currentConfig);
|
|
1096
1051
|
}
|
|
1052
|
+
this.phase(r, 'hot_loading');
|
|
1097
1053
|
await this.deps.hotLoad(p.aid);
|
|
1054
|
+
if (this.deps.bindOwner)
|
|
1055
|
+
await this.deps.bindOwner(p.aid, p.ownerAid);
|
|
1056
|
+
this.phase(r, 'ownerpending');
|
|
1057
|
+
const ownership = await this.deps.checkOwner(p.aid, p.ownerAid);
|
|
1058
|
+
this.check(r);
|
|
1059
|
+
if (ownership === 'pending')
|
|
1060
|
+
return;
|
|
1061
|
+
if (ownership === 'conflict')
|
|
1062
|
+
failure('INITIALIZE_OWNER_CONFLICT', 'Fresh Ownership consensus does not confirm the requested Owner');
|
|
1098
1063
|
this.phase(r, 'activating');
|
|
1099
1064
|
if (p.persona !== undefined && fs.readFileSync(path.join(agentPersonalDir(p.aid), 'persona.md'), 'utf8') !== p.persona) {
|
|
1100
1065
|
failure('INITIALIZE_PLAN_CONFLICT');
|
|
@@ -1145,9 +1110,11 @@ export function parseInitializationArgs(args) {
|
|
|
1145
1110
|
const resume = args[0] === 'initialize';
|
|
1146
1111
|
if ((!resume && args[0] !== 'new') || !isValidAid(args[1] ?? ''))
|
|
1147
1112
|
throw new Error('Invalid initialization command or AID');
|
|
1113
|
+
if (!resume && resolveAgentCreateMode(args) !== 'initialize')
|
|
1114
|
+
throw new Error('--bootstrap cannot initialize an agent');
|
|
1148
1115
|
const booleans = new Set(resume ? ['--resume'] : ['--initialize', '--non-interactive', '--dry-run']);
|
|
1149
1116
|
const strings = new Set(resume ? ['--wait', '--format'] : [
|
|
1150
|
-
'--project', '--baseagent', '--owner', '--
|
|
1117
|
+
'--project', '--baseagent', '--owner', '--name', '--description', '--tags', '--type', '--avatar-file', '--avatar-url',
|
|
1151
1118
|
'--persona-file', '--persona-text', '--wait', '--format',
|
|
1152
1119
|
]);
|
|
1153
1120
|
const seen = new Set();
|
|
@@ -1164,8 +1131,8 @@ export function parseInitializationArgs(args) {
|
|
|
1164
1131
|
values[flag] = value;
|
|
1165
1132
|
}
|
|
1166
1133
|
}
|
|
1167
|
-
if (!seen.has(
|
|
1168
|
-
throw new Error(
|
|
1134
|
+
if (resume && !seen.has('--resume'))
|
|
1135
|
+
throw new Error('--resume required');
|
|
1169
1136
|
if (values['--format'] !== undefined && values['--format'] !== 'json')
|
|
1170
1137
|
throw new Error('--format must be json');
|
|
1171
1138
|
if (seen.has('--persona-file') && seen.has('--persona-text'))
|
|
@@ -1201,7 +1168,7 @@ export function resolveInitializationPlan(args, context) {
|
|
|
1201
1168
|
name: v['--name'], description: v['--description'], tags: parseAgentTags(v['--tags']), type: v['--type'],
|
|
1202
1169
|
}, context?.projectCwd);
|
|
1203
1170
|
return {
|
|
1204
|
-
aid: p.aid, project: p.project, baseagent: p.baseagent ?? '',
|
|
1171
|
+
aid: p.aid, project: p.project, baseagent: p.baseagent ?? '', ownerAid: p.owner ?? '',
|
|
1205
1172
|
name: p.name, description: p.description, tags: p.tags, type: p.type,
|
|
1206
1173
|
...(persona !== undefined ? { persona } : {}),
|
|
1207
1174
|
...(v['--avatar-file'] !== undefined ? { avatarFile: v['--avatar-file'] } : {}),
|