evolcore 0.0.38 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/README.md +31 -20
- package/bin/ec-safe-output.js +14 -2
- package/dist/agents/baseagent-availability.js +1 -0
- package/dist/agents/claude-runner.js +32 -46
- package/dist/agents/codex-runner.js +54 -30
- package/dist/agents/ecagent-runner.js +18 -7
- package/dist/agents/gemini-runner.js +1 -2
- package/dist/agents/runner-types.js +28 -6
- package/dist/aun/aid/agentmd.js +21 -0
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +23 -98
- package/dist/aun/aid/ownership-operation.js +35 -12
- package/dist/aun/group/invite-service.js +6 -0
- package/dist/aun/msg/p2p.js +8 -12
- package/dist/channels/aun.js +66 -48
- package/dist/channels/dingtalk.js +8 -2
- package/dist/channels/feishu.js +6 -1
- package/dist/channels/wecom.js +7 -3
- package/dist/cli/agent-create.js +30 -6
- package/dist/cli/agent.js +30 -37
- package/dist/cli/aid-bind-command.js +16 -2
- package/dist/cli/aun-commands.js +31 -23
- package/dist/cli/config.js +15 -18
- package/dist/cli/contact.js +9 -12
- package/dist/cli/ctl-command.js +3 -3
- package/dist/cli/daemon-commands.js +242 -17
- package/dist/cli/diag.js +7 -3
- package/dist/cli/fs-command.js +5 -4
- package/dist/cli/handoff-command.js +11 -11
- package/dist/cli/help.js +18 -0
- package/dist/cli/index.js +25 -13
- package/dist/cli/init-channel.js +2 -1
- package/dist/cli/memory-command.js +47 -27
- package/dist/cli/model.js +5 -4
- package/dist/cli/queue-command.js +3 -3
- package/dist/cli/response.js +5 -4
- package/dist/cli/restart-monitor.js +35 -13
- package/dist/cli/skill-command.js +13 -13
- package/dist/cli/task-command.js +3 -3
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +5 -3
- package/dist/cli/trigger-command.js +3 -3
- package/dist/cli/version.js +6 -2
- package/dist/config/config-field-policy.js +1 -0
- package/dist/contracts/ecweb-service-protocol.js +44 -0
- package/dist/core/agent-runtime.js +6 -0
- package/dist/core/auth/agent-delegation.js +7 -4
- package/dist/core/auth/auth-gateway.js +3 -1
- package/dist/core/auth/authorization-audit.js +5 -1
- package/dist/core/auth/debug-context.js +5 -1
- package/dist/core/auth/operation-catalog.js +17 -1
- package/dist/core/bootstrap-service.js +20 -12
- package/dist/core/command/cli-intent-parser.js +5 -2
- package/dist/core/command/command-handler.js +20 -5
- package/dist/core/command/menu-catalog.js +48 -12
- package/dist/core/command/menu-handler.js +74 -6
- package/dist/core/command/menu-protocol.js +20 -1
- package/dist/core/command/slash-gate.js +17 -3
- package/dist/core/command/slash-handler.js +90 -7
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/interaction-router.js +2 -1
- package/dist/core/ipc-registration.js +26 -7
- package/dist/core/memory/batch-meta.js +2 -0
- package/dist/core/memory/consolidation-context.js +42 -51
- package/dist/core/memory/consolidation-ops-records.js +39 -7
- package/dist/core/memory/consolidation-trigger.js +71 -64
- package/dist/core/memory/consolidation.js +226 -20
- package/dist/core/memory/memory-writer.js +160 -34
- package/dist/core/memory/relation-idle-monitor.js +287 -0
- package/dist/core/memory/relation-sweep.js +206 -0
- package/dist/core/message/aun-ipc-sender.js +5 -0
- package/dist/core/message/message-bridge.js +64 -55
- package/dist/core/message/message-log.js +17 -0
- package/dist/core/message/message-queue.js +6 -0
- package/dist/core/message/outbound.js +70 -0
- package/dist/core/message/peer-mode.js +7 -2
- package/dist/core/message/response-engine.js +341 -73
- package/dist/core/ownership/owner-resolver.js +465 -140
- package/dist/core/permission/ec-command-parser.js +19 -3
- package/dist/core/permission/protected-paths.js +51 -0
- package/dist/core/permission/tool-policy.js +7 -7
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/session/session-manager.js +25 -5
- package/dist/core/system-session/memory.js +3 -18
- package/dist/ecweb/basic-settings-service.js +709 -0
- package/dist/ecweb/providers-service.js +172 -0
- package/dist/index.js +92 -4
- package/dist/ipc.js +78 -11
- package/dist/stats/db.js +33 -0
- package/dist/stats/index.js +2 -2
- package/dist/stats/query.js +246 -0
- package/dist/stats/writer.js +41 -1
- package/dist/trigger/application-service.js +12 -0
- package/dist/trigger/authorization.js +2 -2
- package/dist/trigger/history.js +36 -0
- package/dist/trigger/scheduler.js +3 -0
- package/dist/utils/ecweb-supervisor.js +31 -10
- package/dist/utils/error-utils.js +8 -4
- package/dist/utils/evolcore-version.js +3 -2
- package/dist/utils/host-info.js +52 -0
- package/dist/utils/welcome.js +18 -24
- package/ecagent/dist/harness/agent-harness.js +4 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/agent.md +1 -1
- package/kits/docs/evolcore/aid.md +58 -8
- package/kits/docs/evolcore/config.md +9 -7
- package/kits/docs/evolcore/memory-architecture.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +28 -7
- package/kits/docs/evolcore/memory-tags.md +1 -1
- package/kits/docs/evolcore/memory.md +1 -1
- package/kits/docs/evolcore/menu-client.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +6 -0
- package/kits/docs/evolcore/msg.md +1 -1
- package/kits/eck_manifest.json +3 -3
- package/kits/rules/04-relation.md +3 -2
- package/kits/schemas/agent-config.schema.13.json +5 -1
- package/kits/templates/memory-consolidation/generalize.md +32 -4
- package/kits/templates/memory-consolidation/notes.md +2 -2
- package/kits/templates/memory-consolidation/promote.md +10 -2
- package/kits/templates/system-fragments/baseagent.md +5 -0
- package/kits/templates/system-fragments/memory.md +12 -3
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/{rules/07-memory-and-thread.md → templates/system-fragments/thread.md} +17 -80
- package/package.json +3 -3
|
@@ -136,24 +136,27 @@ function openAiMessages(context) {
|
|
|
136
136
|
continue;
|
|
137
137
|
}
|
|
138
138
|
if (message.role === 'assistant') {
|
|
139
|
-
const
|
|
139
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
140
|
+
const text = content
|
|
140
141
|
.filter((block) => block.type === 'text')
|
|
141
142
|
.map(block => block.text)
|
|
142
143
|
.join('');
|
|
143
|
-
const thinking =
|
|
144
|
+
const thinking = content
|
|
144
145
|
.filter((block) => block.type === 'thinking')
|
|
145
146
|
.map((block) => block.thinking)
|
|
146
147
|
.join('');
|
|
147
|
-
const toolCalls =
|
|
148
|
+
const toolCalls = content
|
|
148
149
|
.filter((block) => block.type === 'toolCall')
|
|
149
150
|
.map((block) => ({
|
|
150
151
|
id: block.id,
|
|
151
152
|
type: 'function',
|
|
152
153
|
function: { name: block.name, arguments: JSON.stringify(block.arguments ?? {}) },
|
|
153
154
|
}));
|
|
155
|
+
if (!text && !thinking && toolCalls.length === 0)
|
|
156
|
+
continue;
|
|
154
157
|
messages.push({
|
|
155
158
|
role: 'assistant',
|
|
156
|
-
content: text ||
|
|
159
|
+
content: text || [],
|
|
157
160
|
...(thinking ? { reasoning_content: thinking } : {}),
|
|
158
161
|
...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
|
|
159
162
|
});
|
|
@@ -1383,15 +1386,23 @@ export class EcagentRunner {
|
|
|
1383
1386
|
harness?.abort(),
|
|
1384
1387
|
...runs.map(run => run.done),
|
|
1385
1388
|
]), 'ecagent');
|
|
1389
|
+
for (const run of runs) {
|
|
1390
|
+
if (run.cancelled)
|
|
1391
|
+
run.interruptError = undefined;
|
|
1392
|
+
}
|
|
1386
1393
|
}
|
|
1387
1394
|
catch (error) {
|
|
1395
|
+
for (const run of runs) {
|
|
1396
|
+
if (run.cancelled && run.interruptError === undefined)
|
|
1397
|
+
run.interruptError = error;
|
|
1398
|
+
}
|
|
1388
1399
|
logger.warn(`[EcagentRunner] Interrupted execution still requires quiescence: session=${sessionKey} error=${String(error)}`);
|
|
1389
1400
|
}
|
|
1390
1401
|
}
|
|
1391
1402
|
assertInterruptSettled(sessionId) {
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1403
|
+
const unsettled = [...(this.sessionRuns.get(sessionId) ?? [])].find(run => run.cancelled);
|
|
1404
|
+
if (unsettled)
|
|
1405
|
+
throw unsettledInterruptError('ecagent', unsettled.interruptError);
|
|
1395
1406
|
}
|
|
1396
1407
|
injectUserMessage(sessionId, text) {
|
|
1397
1408
|
const harness = this.runtimes.get(sessionId)?.harness;
|
|
@@ -760,8 +760,7 @@ export class GeminiRunner {
|
|
|
760
760
|
if (context?.approvalInteractionPolicy !== 'deny')
|
|
761
761
|
return;
|
|
762
762
|
if (isGeminiUnattendedInteraction(toolName)) {
|
|
763
|
-
|
|
764
|
-
return;
|
|
763
|
+
throw new Error('无人值守任务不能自动回答用户问题');
|
|
765
764
|
}
|
|
766
765
|
const lifecycleRequestId = requestId ?? `gemini-approval-${crypto.randomUUID()}`;
|
|
767
766
|
const auditSummary = `${toolName} runtime authorization denied by Gemini headless policy`;
|
|
@@ -34,7 +34,23 @@ export function parseAsyncUserQuestions(value) {
|
|
|
34
34
|
|| question.options.length > 20 || question.options.some((option) => typeof option !== 'string' || !option.trim() || option.length > 4_096))) {
|
|
35
35
|
throw new Error('Invalid asynchronous question options');
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
if (question.blocking != null && typeof question.blocking !== 'boolean') {
|
|
38
|
+
throw new Error('Invalid asynchronous question blocking flag');
|
|
39
|
+
}
|
|
40
|
+
if (question.recommendedOption != null
|
|
41
|
+
&& (typeof question.recommendedOption !== 'string' || !question.recommendedOption.trim())) {
|
|
42
|
+
throw new Error('Invalid asynchronous question recommended option');
|
|
43
|
+
}
|
|
44
|
+
if (question.recommendedOption != null && question.options
|
|
45
|
+
&& !question.options.includes(question.recommendedOption)) {
|
|
46
|
+
throw new Error('Asynchronous question recommended option is not in options');
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
title: question.title,
|
|
50
|
+
...(question.options ? { options: [...question.options] } : {}),
|
|
51
|
+
blocking: question.blocking !== false,
|
|
52
|
+
...(question.recommendedOption ? { recommendedOption: question.recommendedOption } : {}),
|
|
53
|
+
};
|
|
38
54
|
});
|
|
39
55
|
}
|
|
40
56
|
export function hasTrustedFullAccessContext(context) {
|
|
@@ -44,10 +60,13 @@ export function hasTrustedFullAccessContext(context) {
|
|
|
44
60
|
|| authorization.processRole !== 'fullaccess-run'
|
|
45
61
|
|| authorization.dataScope !== 'daemon')
|
|
46
62
|
return false;
|
|
47
|
-
if (authorization.source === 'fullaccess-command' || authorization.source === 'agent-policy') {
|
|
63
|
+
if (authorization.source === 'fullaccess-command' || authorization.source === 'agent-policy' || authorization.source === 'session-policy') {
|
|
48
64
|
return typeof authorization.authorizedBy === 'string' && authorization.authorizedBy.length > 0
|
|
49
|
-
&& (authorization.source
|
|
50
|
-
|| (typeof authorization.agentAid === 'string'
|
|
65
|
+
&& (authorization.source === 'fullaccess-command'
|
|
66
|
+
|| (typeof authorization.agentAid === 'string'
|
|
67
|
+
&& authorization.agentAid.length > 0
|
|
68
|
+
&& (authorization.source !== 'session-policy'
|
|
69
|
+
|| (typeof authorization.sessionId === 'string' && authorization.sessionId.length > 0))));
|
|
51
70
|
}
|
|
52
71
|
return authorization.source === 'trigger'
|
|
53
72
|
&& typeof authorization.authorizedBy === 'string' && authorization.authorizedBy.length > 0
|
|
@@ -75,10 +94,13 @@ export async function withInterruptDeadline(operation, label, timeoutMs = 5_000)
|
|
|
75
94
|
clearTimeout(timer);
|
|
76
95
|
}
|
|
77
96
|
}
|
|
78
|
-
export function unsettledInterruptError(runner) {
|
|
79
|
-
|
|
97
|
+
export function unsettledInterruptError(runner, cause) {
|
|
98
|
+
const error = Object.assign(new Error(`${runner} 的上一次执行尚未确认停止,本次任务未启动;旧执行结束后可在原会话继续。`), {
|
|
80
99
|
code: 'RUNNER_INTERRUPT_UNCONFIRMED',
|
|
81
100
|
});
|
|
101
|
+
if (cause !== undefined)
|
|
102
|
+
error.cause = cause;
|
|
103
|
+
return error;
|
|
82
104
|
}
|
|
83
105
|
// ── 类型守卫 ──
|
|
84
106
|
export function hasModelSwitcher(agent) {
|
package/dist/aun/aid/agentmd.js
CHANGED
|
@@ -538,6 +538,27 @@ function verifyLocal(store, aid, content) {
|
|
|
538
538
|
return { status: 'invalid', reason: `certificate not available: ${String(e?.message || e).slice(0, 100)}` };
|
|
539
539
|
}
|
|
540
540
|
}
|
|
541
|
+
/** Verify a locally stored agent.md without contacting the AUN gateway. */
|
|
542
|
+
export async function verifyLocalAgentMd(aid, opts = {}) {
|
|
543
|
+
const aunPath = resolveAgentMdAunPath(opts);
|
|
544
|
+
const localPath = agentMdPath(aid, aunPath);
|
|
545
|
+
if (!fs.existsSync(localPath))
|
|
546
|
+
return { status: 'missing' };
|
|
547
|
+
const store = opts.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
|
|
548
|
+
const ownStore = !opts.store;
|
|
549
|
+
try {
|
|
550
|
+
const content = fs.readFileSync(localPath, 'utf-8');
|
|
551
|
+
const result = verifyLocal(store, aid, content);
|
|
552
|
+
return { status: result.status, ...(result.reason ? { reason: result.reason } : {}) };
|
|
553
|
+
}
|
|
554
|
+
finally {
|
|
555
|
+
if (ownStore)
|
|
556
|
+
try {
|
|
557
|
+
store.close();
|
|
558
|
+
}
|
|
559
|
+
catch { /* ignore */ }
|
|
560
|
+
}
|
|
561
|
+
}
|
|
541
562
|
export async function agentmdGet(aid, opts) {
|
|
542
563
|
const aunPath = resolveAgentMdAunPath(opts);
|
|
543
564
|
const store = opts?.store ?? await getAidStore({ slotId: SLOT.cli, aunPath });
|
package/dist/aun/aid/client.js
CHANGED
|
@@ -39,7 +39,7 @@ export function suppressSdkLogs() {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
// ==================== SDK package ====================
|
|
42
|
-
export const MIN_AUN_CORE_SDK = [0, 5,
|
|
42
|
+
export const MIN_AUN_CORE_SDK = [0, 5, 25];
|
|
43
43
|
export const AUN_CORE_SDK_PKG = '@agentunion/fastaun';
|
|
44
44
|
function compareVersion(a, min) {
|
|
45
45
|
const parts = a.split('.').map(n => parseInt(n, 10));
|
|
@@ -8,7 +8,6 @@ import { getAidStore, loadClient, SLOT } from './store.js';
|
|
|
8
8
|
import { LocalTokenStore, validateAgentMdDocument, verifyAgentMdUpload, } from '@agentunion/fastaun';
|
|
9
9
|
import { AUN_KEYSTORE_ENCRYPTION_SEED } from './encryption-seed-policy.js';
|
|
10
10
|
import { isValidAid } from './validation.js';
|
|
11
|
-
import { OwnerResolver } from '../../core/ownership/owner-resolver.js';
|
|
12
11
|
import { agentMdPath, resolvePaths } from '../../paths.js';
|
|
13
12
|
import { PeerIdentityCache } from '../../core/relation/peer-identity.js';
|
|
14
13
|
import { agentmdPut, buildInitialAgentMd, invalidateAgentDisplayName, stripAgentMdSignature } from './agentmd.js';
|
|
@@ -331,7 +330,6 @@ export async function submitControlAidRegistration(aid, owner, options = {}) {
|
|
|
331
330
|
let connecting = false;
|
|
332
331
|
let sentOperationIds = new Set();
|
|
333
332
|
let invitationObserver;
|
|
334
|
-
const resolver = new OwnerResolver();
|
|
335
333
|
async function bounded(operation, timeoutMs = deadline - Date.now()) {
|
|
336
334
|
let timer;
|
|
337
335
|
try {
|
|
@@ -346,27 +344,6 @@ export async function submitControlAidRegistration(aid, owner, options = {}) {
|
|
|
346
344
|
clearTimeout(timer);
|
|
347
345
|
}
|
|
348
346
|
}
|
|
349
|
-
function readTask() {
|
|
350
|
-
const raw = records.loadOwnershipTask(aid, operationId)?.task;
|
|
351
|
-
if (!raw)
|
|
352
|
-
return;
|
|
353
|
-
if (raw.operation_id !== operationId || raw.operation !== 'bind' || raw.sub_aid !== aid
|
|
354
|
-
|| raw.owner_aid !== ownerAid || raw.initiator_aid !== aid || raw.new_owner_aid !== undefined
|
|
355
|
-
|| !['pending', 'completed', 'rejected', 'stopped', 'error'].includes(String(raw.status))
|
|
356
|
-
|| typeof raw.step !== 'string' || !Number.isSafeInteger(raw.base_relation_version)
|
|
357
|
-
|| raw.target_relation_version !== Number(raw.base_relation_version) + 1) {
|
|
358
|
-
throw Object.assign(new Error('SDK registration task does not match its original intent'), { code: 'TASK_MISMATCH' });
|
|
359
|
-
}
|
|
360
|
-
task = {
|
|
361
|
-
operation_id: operationId, operation: 'bind', sub_aid: aid, owner_aid: ownerAid,
|
|
362
|
-
base_relation_version: raw.base_relation_version,
|
|
363
|
-
target_relation_version: raw.target_relation_version,
|
|
364
|
-
status: raw.status, step: raw.step,
|
|
365
|
-
...(typeof raw.code === 'string' ? { code: raw.code } : {}),
|
|
366
|
-
...(typeof raw.reason === 'string' ? { reason: raw.reason } : {}),
|
|
367
|
-
...(typeof raw.retryable === 'boolean' ? { retryable: raw.retryable } : {}),
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
347
|
function result(status, error) {
|
|
371
348
|
return { status, ...(operationId ? { operationId } : {}), ...(task ? { task } : {}), ...(error ? { error } : {}) };
|
|
372
349
|
}
|
|
@@ -374,10 +351,8 @@ export async function submitControlAidRegistration(aid, owner, options = {}) {
|
|
|
374
351
|
records = new LocalTokenStore(resolvePaths().root, { encryptionSeed: AUN_KEYSTORE_ENCRYPTION_SEED });
|
|
375
352
|
const intent = records.loadMetadata(aid)?.registration_intent;
|
|
376
353
|
const registrationIntentOwner = typeof intent?.owner === 'string' ? intent.owner.toLowerCase() : undefined;
|
|
377
|
-
// Preserve an existing SDK registration task and let the same SDK client
|
|
378
|
-
// resume it during connect before OwnerResolver checks/binds the subject.
|
|
379
354
|
if (intent && Object.prototype.hasOwnProperty.call(intent, 'owner')) {
|
|
380
|
-
if (!registrationIntentOwner) {
|
|
355
|
+
if (!registrationIntentOwner || !isValidAid(registrationIntentOwner)) {
|
|
381
356
|
return result('unknown', {
|
|
382
357
|
code: 'REGISTRATION_INTENT_PRESENT',
|
|
383
358
|
message: 'Control AID has an invalid SDK registration Owner intent',
|
|
@@ -386,93 +361,44 @@ export async function submitControlAidRegistration(aid, owner, options = {}) {
|
|
|
386
361
|
if (registrationIntentOwner !== ownerAid) {
|
|
387
362
|
return result('failed', { code: 'OWNER_INTENT_CONFLICT', message: 'Control AID was registered for a different Owner' });
|
|
388
363
|
}
|
|
389
|
-
if (typeof intent.operation_id === 'string' && REGISTRATION_OPERATION_ID.test(intent.operation_id)) {
|
|
390
|
-
operationId = intent.operation_id;
|
|
391
|
-
readTask();
|
|
392
|
-
}
|
|
393
|
-
if (!operationId) {
|
|
394
|
-
return result('unknown', {
|
|
395
|
-
code: 'REGISTRATION_INTENT_PRESENT',
|
|
396
|
-
message: 'Control AID has an SDK registration intent without a valid operation ID',
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
364
|
}
|
|
400
365
|
if (options.agentMdError) {
|
|
401
366
|
return result('failed', { code: 'CONTROL_AGENTMD_NOT_READY', message: options.agentMdError });
|
|
402
367
|
}
|
|
403
|
-
store = await bounded(getAidStore({ slotId:
|
|
404
|
-
const ownershipTasks = records.listOwnershipTasks(aid).map(snapshot => snapshot.task);
|
|
405
|
-
const conflictingTask = ownershipTasks.find(candidate => candidate.sub_aid === aid
|
|
406
|
-
&& (candidate.status === 'pending' || candidate.status === 'error' && candidate.retryable === true)
|
|
407
|
-
&& !(candidate.operation === 'bind' && candidate.owner_aid === ownerAid
|
|
408
|
-
&& candidate.initiator_aid === aid && candidate.new_owner_aid === undefined));
|
|
409
|
-
if (conflictingTask) {
|
|
410
|
-
return result('failed', { code: 'BIND_CONFLICT', message: 'A different active ownership task must be resolved first' });
|
|
411
|
-
}
|
|
368
|
+
store = await bounded(getAidStore({ slotId: `ownership-${crypto.randomUUID()}` }));
|
|
412
369
|
client = await bounded(loadClient(store, aid));
|
|
413
370
|
connecting = true;
|
|
414
371
|
sentOperationIds = new Set();
|
|
415
372
|
invitationObserver = observeInvitationSend(client, ownerAid, sentOperationIds);
|
|
416
373
|
await bounded(client.connect({
|
|
417
|
-
connection_kind: '
|
|
374
|
+
connection_kind: 'short', short_ttl_ms: 30_000, auto_reconnect: false, background_sync: false,
|
|
418
375
|
connect_timeout: 10, call_timeout: 10,
|
|
419
376
|
}));
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
377
|
+
task = await bounded(client.bindOwneraid(ownerAid));
|
|
378
|
+
if (task.operation !== 'bind' || task.sub_aid !== aid || task.owner_aid !== ownerAid
|
|
379
|
+
|| task.new_owner_aid !== undefined || !REGISTRATION_OPERATION_ID.test(task.operation_id)
|
|
380
|
+
|| !Number.isSafeInteger(task.base_relation_version)
|
|
381
|
+
|| task.target_relation_version !== task.base_relation_version + 1) {
|
|
382
|
+
throw Object.assign(new Error('SDK returned a binding result for a different subject or Owner'), { code: 'TASK_MISMATCH' });
|
|
383
|
+
}
|
|
384
|
+
operationId = task.operation_id;
|
|
385
|
+
if (task.status === 'completed') {
|
|
386
|
+
let consensus;
|
|
387
|
+
try {
|
|
388
|
+
consensus = await bounded(client.checkOwnershipConsensus(aid));
|
|
425
389
|
}
|
|
426
|
-
|
|
390
|
+
catch (error) {
|
|
427
391
|
return result('unknown', {
|
|
428
|
-
code: '
|
|
429
|
-
message:
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
if (task?.status === 'completed') {
|
|
433
|
-
const consensus = await bounded(client.checkOwnershipConsensus(aid));
|
|
434
|
-
if (consensus.status === 'consensus' && consensus.sub_aid === aid && consensus.owner_aid === ownerAid) {
|
|
435
|
-
return result('already_bound');
|
|
436
|
-
}
|
|
437
|
-
return result('failed', {
|
|
438
|
-
code: 'CONSENSUS_REQUIRED',
|
|
439
|
-
message: 'A completed binding task has no current verified Owner consensus',
|
|
392
|
+
code: 'BIND_SUBMIT_UNKNOWN',
|
|
393
|
+
message: error instanceof Error ? error.message : String(error),
|
|
440
394
|
});
|
|
441
395
|
}
|
|
442
|
-
if (task && (['rejected', 'stopped', 'error'].includes(task.status) || task.code)) {
|
|
443
|
-
return result('failed', { code: task.code ?? 'BIND_SUBMIT_FAILED', message: task.reason ?? `Binding task ${task.status}` });
|
|
444
|
-
}
|
|
445
|
-
if (sentOperationIds.has(operationId))
|
|
446
|
-
return result('submitted');
|
|
447
|
-
return result('unknown', {
|
|
448
|
-
code: 'BIND_SUBMIT_UNKNOWN',
|
|
449
|
-
message: 'SDK is recovering the original registration task; no matching message.send acknowledgement was observed',
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
// Reuse the same service as agent-create, including its fresh consensus
|
|
453
|
-
// check, explicit SDK bind call, and persistent task projection.
|
|
454
|
-
resolver.attachClient(aid, client, true);
|
|
455
|
-
const view = await bounded(resolver.bind(aid, ownerAid));
|
|
456
|
-
if (view.consensusStatus === 'consensus' && view.ownerAid === ownerAid) {
|
|
457
|
-
return result('already_bound');
|
|
458
|
-
}
|
|
459
|
-
const returnedTask = view.task;
|
|
460
|
-
if (!returnedTask || returnedTask.operation !== 'bind' || returnedTask.sub_aid !== aid
|
|
461
|
-
|| returnedTask.owner_aid !== ownerAid || returnedTask.new_owner_aid !== undefined
|
|
462
|
-
|| typeof returnedTask.operation_id !== 'string' || !REGISTRATION_OPERATION_ID.test(returnedTask.operation_id)) {
|
|
463
|
-
throw Object.assign(new Error('SDK returned a binding task for a different subject or Owner'), { code: 'TASK_MISMATCH' });
|
|
464
|
-
}
|
|
465
|
-
operationId = returnedTask.operation_id;
|
|
466
|
-
readTask();
|
|
467
|
-
if (!task) {
|
|
468
|
-
throw Object.assign(new Error('SDK binding task was not persisted'), { code: 'BIND_TASK_NOT_PERSISTED' });
|
|
469
|
-
}
|
|
470
|
-
if (task.status === 'completed') {
|
|
471
|
-
const consensus = await bounded(client.checkOwnershipConsensus(aid));
|
|
472
396
|
if (consensus.status === 'consensus' && consensus.sub_aid === aid && consensus.owner_aid === ownerAid) {
|
|
473
397
|
return result('already_bound');
|
|
474
398
|
}
|
|
475
|
-
|
|
399
|
+
if (sentOperationIds.has(operationId))
|
|
400
|
+
return result('submitted');
|
|
401
|
+
return result('unknown', { code: 'BIND_SUBMIT_UNKNOWN', message: 'SDK completed the local bind without a message.send acknowledgement' });
|
|
476
402
|
}
|
|
477
403
|
if (['rejected', 'stopped', 'error'].includes(task.status)) {
|
|
478
404
|
return result('failed', { code: task.code ?? 'BIND_SUBMIT_FAILED', message: task.reason ?? `Binding task ${task.status}` });
|
|
@@ -489,15 +415,14 @@ export async function submitControlAidRegistration(aid, owner, options = {}) {
|
|
|
489
415
|
const details = error;
|
|
490
416
|
const code = typeof details.stringCode === 'string' ? details.stringCode
|
|
491
417
|
: typeof details.code === 'string' ? details.code : undefined;
|
|
492
|
-
|
|
493
|
-
|
|
418
|
+
operationId ??= sentOperationIds.values().next().value;
|
|
419
|
+
const uncertain = connecting && (code === 'BIND_SUBMIT_TIMEOUT' || !code || !!operationId);
|
|
494
420
|
return result(uncertain ? 'unknown' : 'failed', {
|
|
495
421
|
code: uncertain ? 'BIND_SUBMIT_UNKNOWN' : code ?? 'BIND_SUBMIT_FAILED',
|
|
496
422
|
message: details.message ?? String(error),
|
|
497
423
|
});
|
|
498
424
|
}
|
|
499
425
|
finally {
|
|
500
|
-
resolver.stop();
|
|
501
426
|
invitationObserver?.restore();
|
|
502
427
|
if (client) {
|
|
503
428
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isValidAid } from './validation.js';
|
|
2
2
|
import { ownerResolver, OwnershipError } from '../../core/ownership/owner-resolver.js';
|
|
3
3
|
import { logger } from '../../utils/logger.js';
|
|
4
|
+
import { MAIN_CLI_BIN } from '../../product.js';
|
|
5
|
+
const OWNERSHIP_OPERATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4
6
|
export function ownershipFailure(error) {
|
|
5
7
|
const details = error && typeof error === 'object' ? error : {};
|
|
6
8
|
return {
|
|
@@ -14,34 +16,55 @@ export function ownershipFailure(error) {
|
|
|
14
16
|
export function parseOwnershipCommand(argv) {
|
|
15
17
|
const args = argv[0] === 'aid' ? argv.slice(1) : [...argv];
|
|
16
18
|
if (args[0] !== 'bind' || !args[1]) {
|
|
17
|
-
throw new OwnershipError('INVALID_ARGUMENT',
|
|
19
|
+
throw new OwnershipError('INVALID_ARGUMENT', `Use ${MAIN_CLI_BIN} aid bind <ownerAid>, status, accept, or reject`);
|
|
18
20
|
}
|
|
19
|
-
const action =
|
|
21
|
+
const action = ['status', 'accept', 'reject'].includes(args[1]) ? args[1] : 'bind';
|
|
20
22
|
if (action === 'bind' && !isValidAid(args[1]))
|
|
21
23
|
throw new OwnershipError('INVALID_AID', 'A valid owner AID is required');
|
|
24
|
+
if ((action === 'accept' || action === 'reject') && !OWNERSHIP_OPERATION_ID.test(args[2] ?? '')) {
|
|
25
|
+
throw new OwnershipError('INVALID_OPERATION', 'A valid ownership operation ID is required');
|
|
26
|
+
}
|
|
22
27
|
let subjectAid;
|
|
28
|
+
let reason;
|
|
23
29
|
let formatSeen = false;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
const positionalEnd = action === 'bind' || action === 'status' ? 2 : 3;
|
|
31
|
+
for (let index = positionalEnd; index < args.length; index += 2) {
|
|
32
|
+
const flag = args[index];
|
|
33
|
+
const value = args[index + 1];
|
|
34
|
+
if (flag === '--subject' && !subjectAid && isValidAid(value ?? '')) {
|
|
35
|
+
subjectAid = value;
|
|
36
|
+
}
|
|
37
|
+
else if (flag === '--format' && value === 'json' && !formatSeen) {
|
|
28
38
|
formatSeen = true;
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
}
|
|
40
|
+
else if (flag === '--reason' && action === 'reject' && reason === undefined
|
|
41
|
+
&& value !== undefined && !value.startsWith('--')) {
|
|
42
|
+
reason = value;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new OwnershipError('INVALID_ARGUMENT', 'Supported options are --subject <subjectAid>, --reason <text>, and --format json');
|
|
46
|
+
}
|
|
31
47
|
}
|
|
32
|
-
|
|
48
|
+
if (action === 'status')
|
|
49
|
+
return { action, subjectAid };
|
|
50
|
+
if (action === 'accept')
|
|
51
|
+
return { action, operationId: args[2], subjectAid };
|
|
52
|
+
if (action === 'reject')
|
|
53
|
+
return { action, operationId: args[2], ...(reason !== undefined ? { reason } : {}), subjectAid };
|
|
54
|
+
return { action, subjectAid, ownerAid: args[1] };
|
|
33
55
|
}
|
|
34
56
|
export async function executeOwnershipCommand(command, defaultSubjectAid) {
|
|
35
57
|
const subjectAid = command.subjectAid ?? defaultSubjectAid;
|
|
36
58
|
if (!subjectAid)
|
|
37
59
|
throw new OwnershipError('SUBJECT_REQUIRED', 'No local subject AID is available');
|
|
38
60
|
if (command.action === 'status') {
|
|
39
|
-
ownerResolver.
|
|
40
|
-
|
|
61
|
+
return ownerResolver.status(subjectAid);
|
|
62
|
+
}
|
|
63
|
+
if (command.action === 'accept' || command.action === 'reject') {
|
|
64
|
+
return ownerResolver.decide(subjectAid, command.operationId, command.action, command.action === 'reject' ? command.reason : undefined);
|
|
41
65
|
}
|
|
42
66
|
logger.info(`[ownership] bind requested subject=${subjectAid} owner=${command.ownerAid}`);
|
|
43
67
|
try {
|
|
44
|
-
ownerResolver.assertLocalSubject(subjectAid);
|
|
45
68
|
const result = await ownerResolver.bind(subjectAid, command.ownerAid);
|
|
46
69
|
const task = result.task;
|
|
47
70
|
const log = task && ['rejected', 'stopped', 'error'].includes(task.status) ? logger.warn : logger.info;
|
|
@@ -372,6 +372,12 @@ export class GroupInviteReviewService {
|
|
|
372
372
|
if (!runtime)
|
|
373
373
|
return Promise.reject(new Error('邀请审核服务未就绪'));
|
|
374
374
|
const task = this.serial(selfAid, async () => {
|
|
375
|
+
if (!this.active(runtime)) {
|
|
376
|
+
runtime.ready = false;
|
|
377
|
+
runtime.failures = 0;
|
|
378
|
+
runtime.nextSync = Date.now() + 15 * 60_000;
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
375
381
|
try {
|
|
376
382
|
await this.sync(runtime);
|
|
377
383
|
runtime.failures = 0;
|
package/dist/aun/msg/p2p.js
CHANGED
|
@@ -10,6 +10,7 @@ import { resolvePaths } from '../../paths.js';
|
|
|
10
10
|
import { ipcQuery } from '../../ipc.js';
|
|
11
11
|
import { readDelegationCredential } from '../../core/auth/agent-delegation.js';
|
|
12
12
|
import { loadDaemonConfig } from '../../config-store.js';
|
|
13
|
+
import { formatPeerKey } from '../../core/relation/peer-identity.js';
|
|
13
14
|
import { normalizeAunMentionEntries, withAunAttention } from './mention-schema.js';
|
|
14
15
|
import { isTopicThreadId } from '../../core/thread/topic-id.js';
|
|
15
16
|
const DAEMON_SEND_TIMEOUT_GRACE_MS = 10_000;
|
|
@@ -60,9 +61,6 @@ function buildSimplePayload(body) {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
|
-
function isRawPayloadBody(body) {
|
|
64
|
-
return body.mode === 'payload';
|
|
65
|
-
}
|
|
66
64
|
function messageLogContent(body) {
|
|
67
65
|
return body.mode === 'text' ? body.text
|
|
68
66
|
: body.mode === 'link' ? `[link] ${body.url}`
|
|
@@ -134,7 +132,7 @@ async function tryDaemonMsgSend(args, payload, runtimeContext, file) {
|
|
|
134
132
|
if (args.slotId || args.aunPath) {
|
|
135
133
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Task sends cannot override the daemon AUN connection' };
|
|
136
134
|
}
|
|
137
|
-
const thread =
|
|
135
|
+
const thread = msgSendThreadId(args, payload, runtimeContext);
|
|
138
136
|
const response = await ipcQuery(resolvePaths().socket, {
|
|
139
137
|
type: 'aun-msg-send',
|
|
140
138
|
aid: args.from,
|
|
@@ -254,8 +252,7 @@ export async function msgSend(args) {
|
|
|
254
252
|
if (args.body.mode !== 'payload' && Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
|
|
255
253
|
payload.mentions = normalizeAunMentionEntries(payload.mentions);
|
|
256
254
|
}
|
|
257
|
-
|
|
258
|
-
applyRoutingPayloadFields(payload, args, runtimeContext, false);
|
|
255
|
+
applyRoutingPayloadFields(payload, args, runtimeContext, false);
|
|
259
256
|
const daemonResult = await tryDaemonMsgSend(args, payload, runtimeContext);
|
|
260
257
|
if (daemonResult)
|
|
261
258
|
return daemonMsgSendResult(args, payload, runtimeContext, daemonResult);
|
|
@@ -327,7 +324,10 @@ export async function msgSend(args) {
|
|
|
327
324
|
}).effectiveRole;
|
|
328
325
|
}
|
|
329
326
|
catch { /* 角色解析失败时按未知处理(proactive) */ }
|
|
330
|
-
const chatmode = chatmodeForPeerIdentity(peerIdentity, peerRole
|
|
327
|
+
const chatmode = chatmodeForPeerIdentity(peerIdentity, peerRole, {
|
|
328
|
+
self: args.from,
|
|
329
|
+
peerKey: formatPeerKey('aun', args.to),
|
|
330
|
+
});
|
|
331
331
|
// 3. 构建 payload
|
|
332
332
|
if (!payload) {
|
|
333
333
|
payload = buildSimplePayload(args.body);
|
|
@@ -336,11 +336,7 @@ export async function msgSend(args) {
|
|
|
336
336
|
if (args.body.mode !== 'payload')
|
|
337
337
|
payload.chatmode = chatmode;
|
|
338
338
|
// 5. 写入 payload.thread_id/ref_message_id(如果指定)
|
|
339
|
-
|
|
340
|
-
if (!isRawPayloadBody(args.body))
|
|
341
|
-
applyRoutingPayloadFields(payload, args, runtimeContext, false);
|
|
342
|
-
applyRoutingPayloadFields(payload, args, runtimeContext);
|
|
343
|
-
}
|
|
339
|
+
applyRoutingPayloadFields(payload, args, runtimeContext, args.body.mode !== 'payload');
|
|
344
340
|
if (args.body.mode !== 'payload' && Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
|
|
345
341
|
payload.mentions = normalizeAunMentionEntries(payload.mentions);
|
|
346
342
|
}
|