evolcore 0.0.35 → 0.0.36
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 +31 -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 +134 -48
- 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 +109 -34
- 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
|
@@ -32,6 +32,9 @@ import { writeRoleDefinition, writeRoleRegistry } from './role-service.js';
|
|
|
32
32
|
import { mutateContactBookTransactionSync } from './contact-book-store.js';
|
|
33
33
|
import { DAEMON_SCHEMA_NAME } from '../product.js';
|
|
34
34
|
import { normalizeDaemonServices } from './daemon-services.js';
|
|
35
|
+
import { assertProviderSelectionWrite, resolveProviderModelOverrides, validateProvidersFile } from './providers-config.js';
|
|
36
|
+
import { assertOwnerConfigRecord, configuredOwnerList, firstConfiguredOwner, normalizeOwnerConfigTree, withOwnerListCompatibility } from './owner-config.js';
|
|
37
|
+
import { getOwners, getPrimaryOwner, ownerResolver } from '../core/ownership/owner-resolver.js';
|
|
35
38
|
// AUN gateway discovery belongs to fastaun. Keep the retired gatewayUrl
|
|
36
39
|
// compatibility handling beside the rest of the configuration read/write
|
|
37
40
|
// policy so all config callers share one implementation.
|
|
@@ -105,25 +108,15 @@ export function shouldFailFastForMissingOwners(env = process.env) {
|
|
|
105
108
|
return value === '1' || value === 'true' || value === 'yes';
|
|
106
109
|
}
|
|
107
110
|
export function validAgentOwnerIds(agent) {
|
|
108
|
-
return
|
|
109
|
-
.map(value => String(value || '').trim())
|
|
110
|
-
.filter(isValidAid))];
|
|
111
|
+
return agent?.aid ? getOwners(agent.aid) : [];
|
|
111
112
|
}
|
|
112
113
|
export function validateAccessPolicyDomain(selfAid, agent) {
|
|
113
|
-
const access = agent.access;
|
|
114
|
-
if (!access)
|
|
115
|
-
return undefined;
|
|
116
|
-
if (access.policyMode !== 'open' && validAgentOwnerIds(agent).length === 0) {
|
|
117
|
-
return {
|
|
118
|
-
code: 'restricted_mode_without_owner',
|
|
119
|
-
message: `Agent ${selfAid} cannot use ${access.policyMode} without an Owner`,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
114
|
return undefined;
|
|
123
115
|
}
|
|
124
116
|
export var ConfigTarget;
|
|
125
117
|
(function (ConfigTarget) {
|
|
126
118
|
ConfigTarget["Process"] = "process";
|
|
119
|
+
ConfigTarget["Providers"] = "providers";
|
|
127
120
|
ConfigTarget["Defaults"] = "defaults";
|
|
128
121
|
ConfigTarget["Agent"] = "agent";
|
|
129
122
|
ConfigTarget["Relation"] = "relation";
|
|
@@ -141,6 +134,7 @@ export function onConfigWrite(listener) {
|
|
|
141
134
|
}
|
|
142
135
|
const TARGET_SCHEMA = {
|
|
143
136
|
[ConfigTarget.Process]: DAEMON_SCHEMA_NAME,
|
|
137
|
+
[ConfigTarget.Providers]: 'providers',
|
|
144
138
|
[ConfigTarget.Defaults]: 'defaults',
|
|
145
139
|
[ConfigTarget.Agent]: 'agent-config',
|
|
146
140
|
[ConfigTarget.Relation]: 'relation-config',
|
|
@@ -175,14 +169,15 @@ export function assertRoleConfigV4Ready() {
|
|
|
175
169
|
const agentsRoot = resolvePaths().agentsDir;
|
|
176
170
|
if (!fs.existsSync(agentsRoot))
|
|
177
171
|
return;
|
|
178
|
-
const
|
|
172
|
+
const migrationTargets = [];
|
|
173
|
+
const degraded = [];
|
|
179
174
|
const inspect = (file, logicalName, minimumVersion) => {
|
|
180
175
|
let value;
|
|
181
176
|
try {
|
|
182
177
|
value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
183
178
|
}
|
|
184
179
|
catch (error) {
|
|
185
|
-
|
|
180
|
+
degraded.push({ file, detail: `invalid JSON: ${error instanceof Error ? error.message : String(error)}` });
|
|
186
181
|
return;
|
|
187
182
|
}
|
|
188
183
|
value = stripLegacyAunGatewayConfig(value, file, message => configWarn(message));
|
|
@@ -190,7 +185,8 @@ export function assertRoleConfigV4Ready() {
|
|
|
190
185
|
? value.$schema_version
|
|
191
186
|
: undefined;
|
|
192
187
|
if (typeof actualVersion !== 'number' || actualVersion < minimumVersion) {
|
|
193
|
-
|
|
188
|
+
// 旧形态才是迁移目标;其余问题只影响该文件对应的 agent/relation。
|
|
189
|
+
migrationTargets.push({
|
|
194
190
|
file,
|
|
195
191
|
detail: `$schema_version must be at least ${minimumVersion}, got ${String(actualVersion)}`,
|
|
196
192
|
});
|
|
@@ -201,7 +197,7 @@ export function assertRoleConfigV4Ready() {
|
|
|
201
197
|
schema = loadSchema(logicalName, actualVersion);
|
|
202
198
|
}
|
|
203
199
|
catch {
|
|
204
|
-
|
|
200
|
+
degraded.push({ file, detail: `unsupported ${logicalName} schema version ${actualVersion}` });
|
|
205
201
|
return;
|
|
206
202
|
}
|
|
207
203
|
if (!schema.validate(value)) {
|
|
@@ -211,7 +207,7 @@ export function assertRoleConfigV4Ready() {
|
|
|
211
207
|
: '';
|
|
212
208
|
return `${error.instancePath || '/'} ${error.message}${extra}`;
|
|
213
209
|
}).join('; ');
|
|
214
|
-
|
|
210
|
+
degraded.push({ file, detail });
|
|
215
211
|
}
|
|
216
212
|
};
|
|
217
213
|
const walkRelationConfigs = (dir) => {
|
|
@@ -239,33 +235,45 @@ export function assertRoleConfigV4Ready() {
|
|
|
239
235
|
inspect(relationFile, 'relation-config', 4);
|
|
240
236
|
}
|
|
241
237
|
}
|
|
242
|
-
if (
|
|
238
|
+
if (degraded.length) {
|
|
239
|
+
configWarn(`[config] ${degraded.length} config issue(s) degraded to warnings; the daemon keeps starting:\n`
|
|
240
|
+
+ degraded.slice(0, 20).map(issue => ` - ${path.relative(root, issue.file)}: ${issue.detail}`).join('\n'));
|
|
241
|
+
}
|
|
242
|
+
if (!migrationTargets.length)
|
|
243
243
|
return;
|
|
244
|
-
const shown =
|
|
244
|
+
const shown = migrationTargets.slice(0, 20)
|
|
245
245
|
.map(issue => `- ${path.relative(root, issue.file)}: ${issue.detail}`)
|
|
246
246
|
.join('\n');
|
|
247
|
-
const omitted =
|
|
247
|
+
const omitted = migrationTargets.length > 20 ? `\n- ... ${migrationTargets.length - 20} more issue(s)` : '';
|
|
248
248
|
const quotedRoot = JSON.stringify(root);
|
|
249
249
|
throw new ConfigError('ROLE_CONFIG_MIGRATION_REQUIRED', `Role config v4 migration is required before daemon startup:\n${shown}${omitted}\n` +
|
|
250
250
|
`Stop the daemon, then run dry-run and review it:\n` +
|
|
251
251
|
`node kits/migrations/migrate-role-config-v4.mjs --home ${quotedRoot}\n` +
|
|
252
252
|
`Apply only after review:\n` +
|
|
253
|
-
`node kits/migrations/migrate-role-config-v4.mjs --apply --home ${quotedRoot}`, { issues });
|
|
253
|
+
`node kits/migrations/migrate-role-config-v4.mjs --apply --home ${quotedRoot}`, { issues: migrationTargets });
|
|
254
254
|
}
|
|
255
|
-
/**
|
|
255
|
+
/**
|
|
256
|
+
* Validate the standalone role domain after the v4 -> v5 migration.
|
|
257
|
+
*
|
|
258
|
+
* 只有「确实需要迁移」的 Agent 才阻止 daemon 启动(仍是旧形态:schema < 5,
|
|
259
|
+
* 或 v5 还内联 roles 定义)。单个 Agent 的形态问题——坏 JSON、未来版本号、
|
|
260
|
+
* 缺/坏 roles/index.json——只告警:这类 Agent 由运行时回退注册表和
|
|
261
|
+
* error-agent 加载路径各自降级,不拖累整个进程。
|
|
262
|
+
*/
|
|
256
263
|
export function assertRoleConfigV5Ready() {
|
|
257
264
|
const root = resolvePaths().root;
|
|
258
265
|
const agentsRoot = resolvePaths().agentsDir;
|
|
259
266
|
if (!fs.existsSync(agentsRoot))
|
|
260
267
|
return;
|
|
261
|
-
const
|
|
268
|
+
const migrationTargets = [];
|
|
269
|
+
const degraded = [];
|
|
262
270
|
const registrySchema = loadSchema('role-registry');
|
|
263
|
-
const readJson = (file) => {
|
|
271
|
+
const readJson = (file, sink) => {
|
|
264
272
|
try {
|
|
265
273
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
266
274
|
}
|
|
267
275
|
catch (error) {
|
|
268
|
-
|
|
276
|
+
sink.push({ file, detail: `invalid JSON: ${error instanceof Error ? error.message : String(error)}` });
|
|
269
277
|
return null;
|
|
270
278
|
}
|
|
271
279
|
};
|
|
@@ -278,46 +286,51 @@ export function assertRoleConfigV5Ready() {
|
|
|
278
286
|
const agentFile = path.join(agentsRoot, aid, 'config.json');
|
|
279
287
|
if (!fs.existsSync(agentFile))
|
|
280
288
|
continue;
|
|
281
|
-
const agent = readJson(agentFile);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
289
|
+
const agent = readJson(agentFile, degraded);
|
|
290
|
+
if (!agent)
|
|
291
|
+
continue;
|
|
292
|
+
const sanitizedAgent = stripLegacyAunGatewayConfig(agent, agentFile, message => configWarn(message));
|
|
285
293
|
const agentVersion = sanitizedAgent?.$schema_version;
|
|
294
|
+
if (typeof agentVersion !== 'number' || agentVersion < 5) {
|
|
295
|
+
migrationTargets.push({
|
|
296
|
+
file: agentFile,
|
|
297
|
+
detail: `agent schema version must be supported and >= 5, got ${String(agentVersion)}`,
|
|
298
|
+
});
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
286
301
|
let agentSchema = null;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
detail: agentSchema
|
|
298
|
-
? schemaErrors(agentSchema)
|
|
299
|
-
: `agent schema version must be supported and >= 5, got ${String(agentVersion)}`,
|
|
300
|
-
});
|
|
302
|
+
try {
|
|
303
|
+
agentSchema = loadSchema('agent-config', agentVersion);
|
|
304
|
+
}
|
|
305
|
+
catch { }
|
|
306
|
+
if (!agentSchema) {
|
|
307
|
+
degraded.push({ file: agentFile, detail: `unsupported agent-config schema version ${agentVersion}` });
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (!agentSchema.validate(sanitizedAgent)) {
|
|
311
|
+
degraded.push({ file: agentFile, detail: schemaErrors(agentSchema) });
|
|
301
312
|
continue;
|
|
302
313
|
}
|
|
303
314
|
const registryFile = agentRolesIndex(aid);
|
|
304
315
|
if (!fs.existsSync(registryFile)) {
|
|
305
|
-
|
|
316
|
+
degraded.push({ file: registryFile, detail: 'roles/index.json is missing (runtime falls back to the builtin role set)' });
|
|
306
317
|
continue;
|
|
307
318
|
}
|
|
308
|
-
const registry = readJson(registryFile);
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
issues.push({ file: registryFile, detail: schemaErrors(registrySchema) });
|
|
312
|
-
continue;
|
|
319
|
+
const registry = readJson(registryFile, degraded);
|
|
320
|
+
if (registry && !registrySchema.validate(registry)) {
|
|
321
|
+
degraded.push({ file: registryFile, detail: schemaErrors(registrySchema) });
|
|
313
322
|
}
|
|
314
323
|
}
|
|
315
|
-
if (
|
|
324
|
+
if (degraded.length) {
|
|
325
|
+
configWarn(`[config] ${degraded.length} agent config issue(s) degraded to warnings; the daemon keeps starting:\n`
|
|
326
|
+
+ degraded.slice(0, 20).map(issue => ` - ${path.relative(root, issue.file)}: ${issue.detail}`).join('\n'));
|
|
327
|
+
}
|
|
328
|
+
if (!migrationTargets.length)
|
|
316
329
|
return;
|
|
317
|
-
const shown =
|
|
330
|
+
const shown = migrationTargets.slice(0, 20).map(issue => `- ${path.relative(root, issue.file)}: ${issue.detail}`).join('\n');
|
|
318
331
|
throw new ConfigError('ROLE_CONFIG_V5_MIGRATION_REQUIRED', `Standalone role config migration is required before daemon startup:\n${shown}\n` +
|
|
319
332
|
`node kits/migrations/migrate-role-config-v5.mjs --home ${JSON.stringify(root)}\n` +
|
|
320
|
-
`node kits/migrations/migrate-role-config-v5.mjs --apply --home ${JSON.stringify(root)}`, { issues });
|
|
333
|
+
`node kits/migrations/migrate-role-config-v5.mjs --apply --home ${JSON.stringify(root)}`, { issues: migrationTargets });
|
|
321
334
|
}
|
|
322
335
|
/** Final startup gate for the contact-book authorization migration. */
|
|
323
336
|
export function assertContactBookV2Ready() {
|
|
@@ -410,7 +423,8 @@ function inspectContactBookDomain(contactFile, agentFile, issues) {
|
|
|
410
423
|
catch {
|
|
411
424
|
return;
|
|
412
425
|
}
|
|
413
|
-
const
|
|
426
|
+
const configuredOwners = getOwners(String(agent?.aid ?? ''));
|
|
427
|
+
const owners = new Set(configuredOwners);
|
|
414
428
|
const configuredChannelKeys = new Set((Array.isArray(agent?.channels) ? agent.channels : [])
|
|
415
429
|
.filter((channel) => channel && typeof channel === 'object' && channel.type !== 'aun')
|
|
416
430
|
.map((channel) => {
|
|
@@ -521,6 +535,10 @@ export function checkSchemaVersionsOnStartup(selfAid) {
|
|
|
521
535
|
read(ConfigTarget.Process);
|
|
522
536
|
}
|
|
523
537
|
catch { }
|
|
538
|
+
try {
|
|
539
|
+
read(ConfigTarget.Providers);
|
|
540
|
+
}
|
|
541
|
+
catch { }
|
|
524
542
|
// 检查 defaults 层
|
|
525
543
|
try {
|
|
526
544
|
read(ConfigTarget.Defaults);
|
|
@@ -545,6 +563,7 @@ function targetPath(target, sel) {
|
|
|
545
563
|
const p = resolvePaths();
|
|
546
564
|
switch (target) {
|
|
547
565
|
case ConfigTarget.Process: return p.daemonConfig;
|
|
566
|
+
case ConfigTarget.Providers: return path.resolve(sel?.providerFile ?? p.providersConfig);
|
|
548
567
|
case ConfigTarget.Defaults: return p.defaultsConfig;
|
|
549
568
|
case ConfigTarget.Agent:
|
|
550
569
|
requireSelf(sel, target);
|
|
@@ -593,6 +612,11 @@ export function read(target, sel, opts = {}) {
|
|
|
593
612
|
else {
|
|
594
613
|
raw = atomicReadJson(file);
|
|
595
614
|
}
|
|
615
|
+
if (target === ConfigTarget.Providers) {
|
|
616
|
+
if (raw === null && !fs.existsSync(file) && !fs.existsSync(`${file}_`))
|
|
617
|
+
return null;
|
|
618
|
+
return normalizeProvidersConfig(migrateIfNeeded(target, raw, file));
|
|
619
|
+
}
|
|
596
620
|
if (raw === null)
|
|
597
621
|
return null;
|
|
598
622
|
raw = stripLegacyAunGatewayConfig(raw, file, message => configWarn(message));
|
|
@@ -641,7 +665,7 @@ function normalizeBaseagentEffortCompat(value) {
|
|
|
641
665
|
return { ...value, baseagents };
|
|
642
666
|
}
|
|
643
667
|
function normalizeAgentConfigForRead(value, aid) {
|
|
644
|
-
const config = foldLegacyProactiveBlock(normalizeAgentLifecycle(value));
|
|
668
|
+
const config = withOwnerListCompatibility(normalizeOwnerConfigTree(foldLegacyProactiveBlock(normalizeAgentLifecycle(value))));
|
|
645
669
|
const mutable = config;
|
|
646
670
|
normalizeShowActivitiesCompat(mutable);
|
|
647
671
|
// AID 校验(如果提供了 aid)
|
|
@@ -678,24 +702,29 @@ function groupFor(target, sel) {
|
|
|
678
702
|
}
|
|
679
703
|
return 'config';
|
|
680
704
|
}
|
|
705
|
+
function normalizeProvidersConfig(value) {
|
|
706
|
+
try {
|
|
707
|
+
return validateProvidersFile(value);
|
|
708
|
+
}
|
|
709
|
+
catch (error) {
|
|
710
|
+
throw new ConfigError('VALIDATION_ERROR', error instanceof Error ? error.message : String(error));
|
|
711
|
+
}
|
|
712
|
+
}
|
|
681
713
|
function normalizeAgentConfigForWrite(value) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
for (const rawOwner of mutable.owners) {
|
|
690
|
-
if (typeof rawOwner !== 'string' || !rawOwner.trim() || !isValidAid(rawOwner.trim())) {
|
|
691
|
-
throw new ConfigError('VALIDATION_ERROR', `Invalid owner AID: ${JSON.stringify(rawOwner)}`);
|
|
692
|
-
}
|
|
693
|
-
const owner = rawOwner.trim();
|
|
694
|
-
if (!owners.includes(owner))
|
|
695
|
-
owners.push(owner);
|
|
714
|
+
try {
|
|
715
|
+
assertOwnerConfigRecord(value, 'agent config', isValidAid);
|
|
716
|
+
for (const [index, channel] of (Array.isArray(value.channels) ? value.channels : []).entries()) {
|
|
717
|
+
const channelType = channel && typeof channel === 'object' && !Array.isArray(channel)
|
|
718
|
+
? String(channel.type || '').trim().toLowerCase()
|
|
719
|
+
: undefined;
|
|
720
|
+
assertOwnerConfigRecord(channel, `channels[${index}]`, channelType === 'aun' ? isValidAid : undefined);
|
|
696
721
|
}
|
|
697
|
-
mutable.owners = owners;
|
|
698
722
|
}
|
|
723
|
+
catch (error) {
|
|
724
|
+
throw new ConfigError('VALIDATION_ERROR', error instanceof Error ? error.message : String(error));
|
|
725
|
+
}
|
|
726
|
+
const mutable = normalizeOwnerConfigTree(normalizeBaseagentEffortCompat({ ...value }));
|
|
727
|
+
normalizeShowActivitiesCompat(mutable);
|
|
699
728
|
// AID 格式校验
|
|
700
729
|
if (mutable.aid && !isValidAid(mutable.aid)) {
|
|
701
730
|
throw new ConfigError('VALIDATION_ERROR', `Invalid aid "${mutable.aid}" (must be a valid multi-level domain like mybot.agentid.pub)`);
|
|
@@ -717,6 +746,12 @@ function normalizeAgentConfigForWrite(value) {
|
|
|
717
746
|
return mutable;
|
|
718
747
|
}
|
|
719
748
|
function normalizeProcessConfigForWrite(value) {
|
|
749
|
+
try {
|
|
750
|
+
assertOwnerConfigRecord(value, 'daemon.json', isValidAid);
|
|
751
|
+
}
|
|
752
|
+
catch (error) {
|
|
753
|
+
throw new ConfigError('VALIDATION_ERROR', error instanceof Error ? error.message : String(error));
|
|
754
|
+
}
|
|
720
755
|
let normalized;
|
|
721
756
|
try {
|
|
722
757
|
normalized = normalizeProcessConfigCompat(normalizeDaemonServices(value));
|
|
@@ -747,7 +782,7 @@ export function normalizeProcessConfigCompat(value) {
|
|
|
747
782
|
const idleMonitor = value.idleMonitor;
|
|
748
783
|
const debug = value.debug;
|
|
749
784
|
let changed = false;
|
|
750
|
-
let normalized = value;
|
|
785
|
+
let normalized = normalizeOwnerConfigTree(value, { projectChannels: false });
|
|
751
786
|
if (Object.prototype.hasOwnProperty.call(normalized, 'fullaccess')) {
|
|
752
787
|
normalized = { ...normalized };
|
|
753
788
|
delete normalized.fullaccess;
|
|
@@ -860,10 +895,24 @@ export function write(target, value, sel, opts = {}) {
|
|
|
860
895
|
? normalizeRelationConfigForWrite(canonicalized)
|
|
861
896
|
: target === ConfigTarget.Process
|
|
862
897
|
? normalizeProcessConfigForWrite(canonicalized)
|
|
863
|
-
:
|
|
898
|
+
: target === ConfigTarget.Providers
|
|
899
|
+
? normalizeProvidersConfig(canonicalized)
|
|
900
|
+
: canonicalized;
|
|
864
901
|
// 1. Schema 校验
|
|
865
902
|
if (!opts.skipValidate) {
|
|
866
903
|
validateOrThrow(schema, normalized, target);
|
|
904
|
+
if (target === ConfigTarget.Defaults || target === ConfigTarget.Agent || target === ConfigTarget.Relation) {
|
|
905
|
+
const previous = atomicReadJson(file) ?? {};
|
|
906
|
+
const inherited = target === ConfigTarget.Defaults ? {}
|
|
907
|
+
: target === ConfigTarget.Agent ? read(ConfigTarget.Defaults, undefined, { cache: false }) ?? {}
|
|
908
|
+
: resolveAgentConfig({ self: sel?.self }, { cache: false });
|
|
909
|
+
try {
|
|
910
|
+
assertProviderSelectionWrite(normalized, previous, inherited, (providerId, serviceType) => resolveProviderModelOverrides(providerId, serviceType, read(ConfigTarget.Providers) ?? { providers: {} }));
|
|
911
|
+
}
|
|
912
|
+
catch (error) {
|
|
913
|
+
throw new ConfigError('VALIDATION_ERROR', error instanceof Error ? error.message : String(error));
|
|
914
|
+
}
|
|
915
|
+
}
|
|
867
916
|
// 1b. responseModeParams 桶内容校验:主 schema 把 responseModeParams 当黑箱,
|
|
868
917
|
// 此处使用“模式桶 schema”逐桶校验内容(桶键 = 已登记的响应模式 id)。
|
|
869
918
|
if (target === ConfigTarget.Defaults || target === ConfigTarget.Agent || target === ConfigTarget.Relation) {
|
|
@@ -876,30 +925,48 @@ export function write(target, value, sel, opts = {}) {
|
|
|
876
925
|
throw new ConfigError(issue.code, issue.message);
|
|
877
926
|
}
|
|
878
927
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
928
|
+
if (target === ConfigTarget.Contact && sel?.self) {
|
|
929
|
+
const contact = { ...normalized, $schema_version: currentVersion('contact-book') };
|
|
930
|
+
mutateContactBookTransactionSync({
|
|
931
|
+
selfAid: sel.self,
|
|
932
|
+
actor: 'contact-config-write',
|
|
933
|
+
mutation: { type: 'replace', contact },
|
|
934
|
+
}, []);
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
if ((target === ConfigTarget.Agent || target === ConfigTarget.Relation) && sel?.self) {
|
|
938
|
+
const syncAid = sel.self;
|
|
939
|
+
if (target === ConfigTarget.Relation && sel.peerKey) {
|
|
940
|
+
ensureRelationSharedDirectory(syncAid, sel.peerKey);
|
|
941
|
+
}
|
|
887
942
|
const before = readTextOrNull(file);
|
|
888
943
|
const after = `${JSON.stringify(normalized, null, 2)}\n`;
|
|
889
944
|
mutateContactBookTransactionSync({
|
|
890
|
-
selfAid:
|
|
891
|
-
actor: 'agent-
|
|
892
|
-
mutation: { type: '
|
|
945
|
+
selfAid: syncAid,
|
|
946
|
+
actor: target === ConfigTarget.Agent ? 'agent-management-role-sync' : 'relation-management-role-sync',
|
|
947
|
+
mutation: { type: 'sync-management-roles' },
|
|
893
948
|
}, [{ file, before, after }]);
|
|
949
|
+
if (target === ConfigTarget.Agent) {
|
|
950
|
+
ownerResolver.setAgentConfiguredOwners(syncAid, configuredOwnerList(normalized));
|
|
951
|
+
}
|
|
894
952
|
finishConfigWrite(target, sel, file);
|
|
895
953
|
return;
|
|
896
954
|
}
|
|
897
955
|
// 2. 写入文件
|
|
898
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
899
|
-
if (target === ConfigTarget.Relation && sel?.self && sel.peerKey) {
|
|
900
|
-
ensureRelationSharedDirectory(sel.self, sel.peerKey);
|
|
901
|
-
}
|
|
956
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, ...(target === ConfigTarget.Providers ? { mode: 0o700 } : {}) });
|
|
902
957
|
atomicWriteJson(file, normalized);
|
|
958
|
+
if (target === ConfigTarget.Providers && process.platform !== 'win32') {
|
|
959
|
+
for (const protectedFile of [file, `${file}_`]) {
|
|
960
|
+
if (fs.existsSync(protectedFile))
|
|
961
|
+
fs.chmodSync(protectedFile, 0o600);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
if (target === ConfigTarget.Agent && sel?.self) {
|
|
965
|
+
ownerResolver.setAgentConfiguredOwners(sel.self, configuredOwnerList(normalized));
|
|
966
|
+
}
|
|
967
|
+
if (target === ConfigTarget.Process && typeof normalized?.aid === 'string') {
|
|
968
|
+
ownerResolver.setConfiguredOwners(normalized.aid, configuredOwnerList(normalized));
|
|
969
|
+
}
|
|
903
970
|
finishConfigWrite(target, sel, file);
|
|
904
971
|
}
|
|
905
972
|
/**
|
|
@@ -1206,6 +1273,8 @@ export function validateConfig(target, value) {
|
|
|
1206
1273
|
try {
|
|
1207
1274
|
if (target === ConfigTarget.Process)
|
|
1208
1275
|
normalized = normalizeProcessConfigCompat(normalizeDaemonServices(withVer));
|
|
1276
|
+
if (target === ConfigTarget.Providers)
|
|
1277
|
+
normalized = normalizeProvidersConfig(withVer);
|
|
1209
1278
|
}
|
|
1210
1279
|
catch (error) {
|
|
1211
1280
|
return [error instanceof Error ? error.message : String(error)];
|
|
@@ -1239,8 +1308,12 @@ export function validateConfig(target, value) {
|
|
|
1239
1308
|
export function validateConfigFile(target, sel) {
|
|
1240
1309
|
const file = targetPath(target, sel);
|
|
1241
1310
|
const value = atomicReadJson(file);
|
|
1242
|
-
if (value === null)
|
|
1311
|
+
if (value === null) {
|
|
1312
|
+
if (target === ConfigTarget.Providers && fs.existsSync(file)) {
|
|
1313
|
+
return { exists: true, errors: validateConfig(target, value) };
|
|
1314
|
+
}
|
|
1243
1315
|
return { exists: false, errors: [] };
|
|
1316
|
+
}
|
|
1244
1317
|
// Persisted pre-removal files remain readable: validate the same sanitized
|
|
1245
1318
|
// view used by runtime reads, while new writes are still rejected by write().
|
|
1246
1319
|
let sanitized;
|
|
@@ -1269,6 +1342,10 @@ export function ensureFile(target, sel) {
|
|
|
1269
1342
|
if (fs.existsSync(file))
|
|
1270
1343
|
return;
|
|
1271
1344
|
const skeleton = configSkeleton(target, sel);
|
|
1345
|
+
if (target === ConfigTarget.Providers) {
|
|
1346
|
+
write(target, skeleton, sel);
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1272
1349
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
1273
1350
|
atomicWriteJson(file, skeleton);
|
|
1274
1351
|
}
|
|
@@ -1323,6 +1400,12 @@ export function resolveAgentConfig(sel, opts = {}) {
|
|
|
1323
1400
|
// gateway credentials and endpoint. Keep the generic dictionary merge
|
|
1324
1401
|
// contract unchanged for every other field.
|
|
1325
1402
|
merged.baseagents = mergeBaseagentBlocks(layers);
|
|
1403
|
+
merged.Services = layers.reduce((selections, layer) => {
|
|
1404
|
+
for (const [serviceType, selection] of Object.entries(layer?.Services ?? {})) {
|
|
1405
|
+
selections[serviceType] = { ...selections[serviceType], ...selection };
|
|
1406
|
+
}
|
|
1407
|
+
return selections;
|
|
1408
|
+
}, {});
|
|
1326
1409
|
return merged;
|
|
1327
1410
|
}
|
|
1328
1411
|
function mergeBaseagentBlocks(layers) {
|
|
@@ -1371,7 +1454,8 @@ export function normalizeDefaultsBehaviorCompat(defaults) {
|
|
|
1371
1454
|
function stripStaticOwnerField(config) {
|
|
1372
1455
|
if (!config)
|
|
1373
1456
|
return config;
|
|
1374
|
-
if (!Object.prototype.hasOwnProperty.call(config, '
|
|
1457
|
+
if (!Object.prototype.hasOwnProperty.call(config, 'owner')
|
|
1458
|
+
&& !Object.prototype.hasOwnProperty.call(config, 'owners')
|
|
1375
1459
|
&& !Object.prototype.hasOwnProperty.call(config, 'admins')
|
|
1376
1460
|
&& !Object.prototype.hasOwnProperty.call(config, 'access')
|
|
1377
1461
|
&& !Object.prototype.hasOwnProperty.call(config, 'groupSlashRequireMentions')
|
|
@@ -1380,7 +1464,7 @@ function stripStaticOwnerField(config) {
|
|
|
1380
1464
|
// fullaccess and groupSlashRequireMentions are Agent-only. Relation/defaults
|
|
1381
1465
|
// files allow forward-compatible unknown properties, so strip it before the
|
|
1382
1466
|
// generic merge chain can treat a manually written value as an override.
|
|
1383
|
-
const { owners: _owners, admins: _admins, access: _access, groupSlashRequireMentions: _groupSlashRequireMentions, fullaccess: _fullaccess, ...rest } = config;
|
|
1467
|
+
const { owner: _owner, owners: _owners, admins: _admins, access: _access, groupSlashRequireMentions: _groupSlashRequireMentions, fullaccess: _fullaccess, ...rest } = config;
|
|
1384
1468
|
return rest;
|
|
1385
1469
|
}
|
|
1386
1470
|
function stripRelationRoleData(config) {
|
|
@@ -1406,7 +1490,8 @@ export function resolveEffective(sel, opts = {}) {
|
|
|
1406
1490
|
lifecycle: config.lifecycle,
|
|
1407
1491
|
initialized: config.initialized,
|
|
1408
1492
|
initialization: config.initialization,
|
|
1409
|
-
|
|
1493
|
+
owner: getPrimaryOwner(sel.self ?? config.aid ?? '') ?? firstConfiguredOwner(config),
|
|
1494
|
+
owners: [...new Set([...getOwners(sel.self ?? config.aid ?? ''), ...configuredOwnerList(config)])],
|
|
1410
1495
|
fullaccess: config.fullaccess,
|
|
1411
1496
|
aun: config.aun,
|
|
1412
1497
|
decision_model: config.decision_model,
|
|
@@ -1422,6 +1507,7 @@ export function resolveEffective(sel, opts = {}) {
|
|
|
1422
1507
|
// Runtime configuration parameters
|
|
1423
1508
|
active_baseagent: config.active_baseagent,
|
|
1424
1509
|
baseagents: config.baseagents,
|
|
1510
|
+
Services: config.Services,
|
|
1425
1511
|
chatmode: config.chatmode,
|
|
1426
1512
|
mentionMode: config.mentionMode,
|
|
1427
1513
|
autoMentionQuietWindowMs: config.autoMentionQuietWindowMs
|
|
@@ -394,6 +394,13 @@ function executeGet(op, options) {
|
|
|
394
394
|
...(note ? { note } : {}),
|
|
395
395
|
};
|
|
396
396
|
}
|
|
397
|
+
// Management fields (e.g. 'owners') are computed by resolveEffective() rather than
|
|
398
|
+
// stored in any config file, so source is always null. op.route is undefined for these
|
|
399
|
+
// cases (routeFieldPath threw but was swallowed because routeRequired=false).
|
|
400
|
+
// Return the effective value directly when it is present.
|
|
401
|
+
if (withSource.value !== undefined && !op.route) {
|
|
402
|
+
return { ok: true, subcommand: 'get', field: op.field, value: withSource.value, scope: op.configScope };
|
|
403
|
+
}
|
|
397
404
|
return getWithSchemaDefault(op, null, note);
|
|
398
405
|
}
|
|
399
406
|
/**
|
|
@@ -455,14 +462,6 @@ function executeSet(op, options) {
|
|
|
455
462
|
return { ok: false, code: 'UNKNOWN_FIELD', error: `Unknown config field: ${op.field}` };
|
|
456
463
|
const selector = selectorFor(op, options);
|
|
457
464
|
const mutation = mutateConfig(op.route.target, selector, existing => {
|
|
458
|
-
if (op.field === 'owners' && op.route.target === ConfigTarget.Agent) {
|
|
459
|
-
const nextOwners = Array.isArray(op.value) ? op.value : [];
|
|
460
|
-
if (nextOwners.length === 0) {
|
|
461
|
-
throw new ConfigError('LAST_OWNER_PROTECTED', 'owners must contain at least one owner');
|
|
462
|
-
}
|
|
463
|
-
setNested(existing, op.field, nextOwners);
|
|
464
|
-
return nextOwners;
|
|
465
|
-
}
|
|
466
465
|
if (op.route.merge === 'list') {
|
|
467
466
|
const previous = getNested(existing, op.field);
|
|
468
467
|
const previousItems = Array.isArray(previous) ? previous : [];
|
|
@@ -506,12 +505,6 @@ function executeUnset(op, options) {
|
|
|
506
505
|
const selector = selectorFor(op, options);
|
|
507
506
|
const target = op.route.target;
|
|
508
507
|
const { value: removed } = mutateConfig(target, selector, existing => {
|
|
509
|
-
if (op.field === 'owners' && target === ConfigTarget.Agent) {
|
|
510
|
-
const current = getNested(existing, op.field);
|
|
511
|
-
if (Array.isArray(current) && current.length > 0) {
|
|
512
|
-
throw new ConfigError('LAST_OWNER_PROTECTED', 'cannot unset owners while an Agent still has owners; use owners replace with another owner');
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
508
|
const parts = op.field.split('.');
|
|
516
509
|
let current = existing;
|
|
517
510
|
for (let i = 0; i < parts.length - 1; i++) {
|