evolcore 0.0.37 → 0.0.39
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 +22 -0
- package/dist/agents/codex-runner.js +3 -3
- package/dist/aun/aid/agentmd.js +21 -0
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/ownership-operation.js +34 -9
- package/dist/cli/agent-create.js +7 -2
- package/dist/cli/agent.js +30 -37
- package/dist/cli/aid-bind-command.js +12 -1
- 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/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/init-channel.js +2 -1
- package/dist/cli/memory-command.js +14 -13
- 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/skill-command.js +13 -13
- package/dist/cli/task-command.js +3 -3
- 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/core/auth/operation-catalog.js +1 -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 +13 -4
- package/dist/core/command/menu-handler.js +2 -2
- package/dist/core/ownership/owner-resolver.js +121 -2
- package/dist/core/permission/protected-paths.js +51 -0
- package/dist/utils/npm-ops.js +22 -6
- package/dist/utils/welcome.js +18 -24
- package/kits/docs/evolcore/aid.md +33 -0
- package/package.json +3 -3
|
@@ -117,7 +117,12 @@ export class OwnerResolver {
|
|
|
117
117
|
if (subject.client !== client || subject.generation !== generation)
|
|
118
118
|
return;
|
|
119
119
|
const record = this.record(payload);
|
|
120
|
-
if (!record
|
|
120
|
+
if (!record)
|
|
121
|
+
return;
|
|
122
|
+
this.trackBindingRequest(subjectAid, subject, event, record);
|
|
123
|
+
// When this AID is the prospective owner, the request concerns the
|
|
124
|
+
// remote sub's ownership, not this AID's own Owner authorization.
|
|
125
|
+
if (record.sub_aid !== subjectAid)
|
|
121
126
|
return;
|
|
122
127
|
if (typeof record.operation_id === 'string') {
|
|
123
128
|
const key = JSON.stringify([event, record.operation_id, record.step, record.status,
|
|
@@ -215,6 +220,9 @@ export class OwnerResolver {
|
|
|
215
220
|
clearTimeout(subject.pendingRefreshTimer);
|
|
216
221
|
subject.pendingRefreshTimer = undefined;
|
|
217
222
|
subject.taskEvents.clear();
|
|
223
|
+
subject.requests.clear();
|
|
224
|
+
subject.decisionTasks.clear();
|
|
225
|
+
delete subject.view.decisionTask;
|
|
218
226
|
for (const subscription of subject.subscriptions.splice(0)) {
|
|
219
227
|
try {
|
|
220
228
|
subscription.unsubscribe();
|
|
@@ -275,6 +283,7 @@ export class OwnerResolver {
|
|
|
275
283
|
snapshot(subjectAid) {
|
|
276
284
|
const subject = this.subject(subjectAid);
|
|
277
285
|
const view = JSON.parse(JSON.stringify(subject.view));
|
|
286
|
+
view.requests = [...subject.requests.values()].map(request => ({ ...request }));
|
|
278
287
|
if (view.consensusStatus === 'consensus' && !this.owner(subjectAid)) {
|
|
279
288
|
view.ownerAid = undefined;
|
|
280
289
|
view.synchronization = { status: 'unavailable' };
|
|
@@ -421,6 +430,52 @@ export class OwnerResolver {
|
|
|
421
430
|
subject.binding = undefined;
|
|
422
431
|
}
|
|
423
432
|
}
|
|
433
|
+
async decide(subjectAid, operationId, decision, reason) {
|
|
434
|
+
this.assertLocalSubject(subjectAid);
|
|
435
|
+
const subject = this.subject(subjectAid);
|
|
436
|
+
const client = subject.client;
|
|
437
|
+
if (!client || !subject.view.connected) {
|
|
438
|
+
throw new OwnershipError('SUBJECT_UNAVAILABLE', 'Subject AID SDK client is not ready');
|
|
439
|
+
}
|
|
440
|
+
const request = subject.requests.get(operationId) ?? subject.decisionTasks.get(operationId);
|
|
441
|
+
if (!request) {
|
|
442
|
+
throw new OwnershipError('REQUEST_NOT_FOUND', 'No SDK-verified binding request matches this operation ID; use bind status');
|
|
443
|
+
}
|
|
444
|
+
const generation = subject.generation;
|
|
445
|
+
logger.info(`[ownership] ${decision} requested local=${subjectAid} operationId=${operationId}`);
|
|
446
|
+
try {
|
|
447
|
+
// The SDK revalidates the stored request, decision and current relation
|
|
448
|
+
// before any protocol mutation; this projection is only a discovery aid.
|
|
449
|
+
const result = decision === 'accept'
|
|
450
|
+
? await client.acceptOwnershipRequest(operationId)
|
|
451
|
+
: await client.rejectOwnershipRequest(operationId, reason);
|
|
452
|
+
if (subject.client !== client || subject.generation !== generation) {
|
|
453
|
+
throw new OwnershipError('CLIENT_REPLACED', 'Subject SDK client changed during approval; query status before retrying');
|
|
454
|
+
}
|
|
455
|
+
const task = this.bindingTask(result);
|
|
456
|
+
if (!task || !this.sameBindingTask(task, request)) {
|
|
457
|
+
throw new OwnershipError('TASK_MISMATCH', 'SDK returned a different ownership decision task');
|
|
458
|
+
}
|
|
459
|
+
subject.view.decisionTask = task;
|
|
460
|
+
subject.decisionTasks.set(operationId, task);
|
|
461
|
+
subject.requests.delete(operationId);
|
|
462
|
+
if (request.sub_aid === subjectAid) {
|
|
463
|
+
this.invalidate(subject, 'Binding decision submitted; fresh consensus is required');
|
|
464
|
+
this.schedule(subjectAid, subject);
|
|
465
|
+
}
|
|
466
|
+
logger.info(`[ownership] ${decision} result ${JSON.stringify(task)}`);
|
|
467
|
+
if (task.status === 'error' || task.status === 'stopped' || (decision === 'accept' && task.status === 'rejected')) {
|
|
468
|
+
throw Object.assign(new OwnershipError(task.code ?? 'OWNERSHIP_DECISION_FAILED', task.reason ?? `Ownership task ${task.status}`), { retryable: task.retryable });
|
|
469
|
+
}
|
|
470
|
+
return this.snapshot(subjectAid);
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
logger.warn(`[ownership] ${decision} failed ${JSON.stringify({
|
|
474
|
+
localAid: subjectAid, operationId, error: error instanceof Error ? error.message : String(error),
|
|
475
|
+
})}`);
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
424
479
|
stop() {
|
|
425
480
|
for (const subjectAid of this.subjects.keys())
|
|
426
481
|
this.detachClient(subjectAid);
|
|
@@ -500,7 +555,8 @@ export class OwnerResolver {
|
|
|
500
555
|
}
|
|
501
556
|
const task = saved?.subjectAid === subjectAid ? this.task(saved.task) : undefined;
|
|
502
557
|
subject = {
|
|
503
|
-
generation: 0, epoch: 0, query: 0, subscriptions: [], taskEvents: new Set(),
|
|
558
|
+
generation: 0, epoch: 0, query: 0, subscriptions: [], taskEvents: new Set(),
|
|
559
|
+
requests: new Map(), decisionTasks: new Map(), persistenceError,
|
|
504
560
|
view: {
|
|
505
561
|
subjectAid, consensusStatus: 'unavailable', authorizationStatus: 'unavailable', connected: false, revision: 0,
|
|
506
562
|
task: task?.sub_aid === subjectAid ? task : undefined,
|
|
@@ -639,6 +695,69 @@ export class OwnerResolver {
|
|
|
639
695
|
record(value) {
|
|
640
696
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
641
697
|
}
|
|
698
|
+
trackBindingRequest(localAid, subject, event, record) {
|
|
699
|
+
const task = this.bindingTask(record);
|
|
700
|
+
if (!task || (task.sub_aid !== localAid && task.owner_aid !== localAid))
|
|
701
|
+
return;
|
|
702
|
+
const request = subject.requests.get(task.operation_id);
|
|
703
|
+
const decisionTask = subject.decisionTasks.get(task.operation_id);
|
|
704
|
+
if ((request && !this.sameBindingTask(task, request))
|
|
705
|
+
|| (decisionTask && !this.sameBindingTask(task, decisionTask)))
|
|
706
|
+
return;
|
|
707
|
+
if (event === 'ownership.request') {
|
|
708
|
+
const requester = record.requester_aid;
|
|
709
|
+
const remote = task.sub_aid === localAid ? task.owner_aid : task.sub_aid;
|
|
710
|
+
if (task.status !== 'pending' || task.step !== 'approval' || requester !== remote || decisionTask)
|
|
711
|
+
return;
|
|
712
|
+
subject.requests.set(task.operation_id, {
|
|
713
|
+
...task, requester_aid: remote, local_role: task.sub_aid === localAid ? 'sub' : 'owner',
|
|
714
|
+
});
|
|
715
|
+
logger.info(`[ownership] inbound binding ${JSON.stringify({
|
|
716
|
+
localAid, operationId: task.operation_id, subAid: task.sub_aid, ownerAid: task.owner_aid,
|
|
717
|
+
})}`);
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
const awaitingApproval = task.step === 'approval'
|
|
721
|
+
&& (task.status === 'pending' || (task.status === 'error' && task.retryable === true))
|
|
722
|
+
&& !['ownership.completed', 'ownership.rejected', 'ownership.stopped'].includes(event);
|
|
723
|
+
if (request && awaitingApproval)
|
|
724
|
+
subject.requests.set(task.operation_id, { ...request, ...task });
|
|
725
|
+
else
|
|
726
|
+
subject.requests.delete(task.operation_id);
|
|
727
|
+
}
|
|
728
|
+
const retryableDecision = task.step !== 'approval'
|
|
729
|
+
&& (task.status === 'pending' || (task.status === 'error' && task.retryable === true))
|
|
730
|
+
&& !['ownership.completed', 'ownership.rejected', 'ownership.stopped'].includes(event);
|
|
731
|
+
if (decisionTask || retryableDecision) {
|
|
732
|
+
subject.decisionTasks.set(task.operation_id, task);
|
|
733
|
+
subject.view.decisionTask = task;
|
|
734
|
+
}
|
|
735
|
+
else if (subject.view.decisionTask?.operation_id === task.operation_id) {
|
|
736
|
+
subject.view.decisionTask = task;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
sameBindingTask(left, right) {
|
|
740
|
+
return left.operation_id === right.operation_id && left.sub_aid === right.sub_aid
|
|
741
|
+
&& left.owner_aid === right.owner_aid && left.base_relation_version === right.base_relation_version
|
|
742
|
+
&& left.target_relation_version === right.target_relation_version;
|
|
743
|
+
}
|
|
744
|
+
/** Only public task fields are exposed; raw SDK records remain SDK-owned. */
|
|
745
|
+
bindingTask(value) {
|
|
746
|
+
const task = this.task(value);
|
|
747
|
+
if (!task || !CREATION_BIND_OPERATION_ID.test(task.operation_id) || task.new_owner_aid !== undefined
|
|
748
|
+
|| task.sub_aid === task.owner_aid || !Number.isSafeInteger(task.base_relation_version)
|
|
749
|
+
|| task.base_relation_version < 0 || !Number.isSafeInteger(task.target_relation_version)
|
|
750
|
+
|| task.target_relation_version !== task.base_relation_version + 1)
|
|
751
|
+
return undefined;
|
|
752
|
+
return {
|
|
753
|
+
operation_id: task.operation_id, operation: 'bind', sub_aid: task.sub_aid, owner_aid: task.owner_aid,
|
|
754
|
+
base_relation_version: task.base_relation_version, target_relation_version: task.target_relation_version,
|
|
755
|
+
status: task.status, step: task.step,
|
|
756
|
+
...(typeof task.code === 'string' ? { code: task.code } : {}),
|
|
757
|
+
...(typeof task.reason === 'string' ? { reason: task.reason } : {}),
|
|
758
|
+
...(typeof task.retryable === 'boolean' ? { retryable: task.retryable } : {}),
|
|
759
|
+
};
|
|
760
|
+
}
|
|
642
761
|
task(value) {
|
|
643
762
|
const record = this.record(value);
|
|
644
763
|
if (!record || typeof record.operation_id !== 'string' || !record.operation_id
|
|
@@ -787,6 +787,57 @@ export function compactWindowsCodexFilesystemRules(filesystem, platform = proces
|
|
|
787
787
|
}
|
|
788
788
|
return compacted;
|
|
789
789
|
}
|
|
790
|
+
/** Remove Linux mount targets already hidden by a read-only parent deny. */
|
|
791
|
+
export function compactCodexLinuxFilesystemRules(filesystem, platform = process.platform) {
|
|
792
|
+
if (platform !== 'linux')
|
|
793
|
+
return filesystem;
|
|
794
|
+
const entries = Object.entries(filesystem);
|
|
795
|
+
const denyDirectories = entries
|
|
796
|
+
.filter(([target, access]) => {
|
|
797
|
+
if (access !== 'deny' || CODEX_GLOB_MAGIC.test(target))
|
|
798
|
+
return false;
|
|
799
|
+
try {
|
|
800
|
+
return fs.lstatSync(target).isDirectory();
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
805
|
+
})
|
|
806
|
+
.map(([target]) => target);
|
|
807
|
+
if (denyDirectories.length === 0)
|
|
808
|
+
return filesystem;
|
|
809
|
+
const exceptionTargets = entries.flatMap(([target, access]) => {
|
|
810
|
+
if (access !== 'read' && access !== 'write')
|
|
811
|
+
return [];
|
|
812
|
+
const globIndex = target.search(CODEX_GLOB_MAGIC);
|
|
813
|
+
if (globIndex < 0)
|
|
814
|
+
return [target];
|
|
815
|
+
const prefix = target.slice(0, target.lastIndexOf('/', globIndex));
|
|
816
|
+
return prefix ? [prefix] : [];
|
|
817
|
+
});
|
|
818
|
+
let compacted;
|
|
819
|
+
for (const [target, access] of entries) {
|
|
820
|
+
if (access !== 'deny')
|
|
821
|
+
continue;
|
|
822
|
+
const recursive = target.endsWith('/**');
|
|
823
|
+
const candidate = recursive ? target.slice(0, -3) : target;
|
|
824
|
+
if (CODEX_GLOB_MAGIC.test(candidate))
|
|
825
|
+
continue;
|
|
826
|
+
const covered = denyDirectories.some(parent => {
|
|
827
|
+
if (candidate === parent)
|
|
828
|
+
return recursive && target !== parent;
|
|
829
|
+
return isSameOrDescendant(candidate, parent);
|
|
830
|
+
});
|
|
831
|
+
if (!covered)
|
|
832
|
+
continue;
|
|
833
|
+
const hasException = exceptionTargets.some(exception => isSameOrDescendant(exception, candidate) || isSameOrDescendant(candidate, exception));
|
|
834
|
+
if (hasException)
|
|
835
|
+
continue;
|
|
836
|
+
compacted ??= { ...filesystem };
|
|
837
|
+
delete compacted[target];
|
|
838
|
+
}
|
|
839
|
+
return compacted ?? filesystem;
|
|
840
|
+
}
|
|
790
841
|
/**
|
|
791
842
|
* Codex 0.154 serializes resolved Windows deny-read targets into a base64
|
|
792
843
|
* command-line argument for codex-windows-sandbox-setup.exe. Recursive file
|
package/dist/utils/npm-ops.js
CHANGED
|
@@ -394,20 +394,36 @@ export async function preparePackageCache(target, registry, options = {}) {
|
|
|
394
394
|
const run = (args) => new Promise((resolve, reject) => {
|
|
395
395
|
let failure = null;
|
|
396
396
|
let output = '';
|
|
397
|
+
let errorOutput = '';
|
|
397
398
|
const child = execFile(npm.command, [...npm.argsPrefix, ...args, '--registry', registry, ...npmConfigArgs(options.npmConfig), '--ignore-scripts'], {
|
|
398
399
|
timeout: 180_000, windowsHide: isWindows, signal: options.signal, maxBuffer: 8 * 1024 * 1024,
|
|
399
|
-
}, (error, stdout) => {
|
|
400
|
+
}, (error, stdout, stderr) => {
|
|
401
|
+
failure = error;
|
|
402
|
+
output = String(stdout ?? '');
|
|
403
|
+
errorOutput = String(stderr ?? '');
|
|
404
|
+
});
|
|
400
405
|
child.once('error', error => { failure = error; });
|
|
401
406
|
child.once('close', code => {
|
|
402
|
-
if (failure
|
|
403
|
-
reject(new Error(options.signal?.aborted
|
|
404
|
-
? 'Package cache preparation timed out or was cancelled' : 'npm cache preparation failed; check registry connectivity, credentials, disk space and permissions'));
|
|
405
|
-
else
|
|
407
|
+
if (!failure && code === 0 && !options.signal?.aborted) {
|
|
406
408
|
resolve(output);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (options.signal?.aborted) {
|
|
412
|
+
reject(new Error('Package cache preparation timed out or was cancelled'));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const codeText = failure && 'code' in failure && failure.code ? ` (${String(failure.code)})` : '';
|
|
416
|
+
const detail = (errorOutput || failure?.stderr || failure?.message || '')
|
|
417
|
+
.replace(/\s+/g, ' ').trim().slice(0, 500);
|
|
418
|
+
reject(new Error(`npm cache preparation failed${codeText}${detail ? `: ${detail}` : ''}`));
|
|
407
419
|
});
|
|
408
420
|
});
|
|
409
421
|
options.onPhase?.('downloading');
|
|
410
|
-
|
|
422
|
+
// Revalidate registry metadata before resolving the exact version. A stale
|
|
423
|
+
// install-v1 packument can otherwise report ETARGET even when the registry
|
|
424
|
+
// already contains the selected release. The tarball itself remains cached
|
|
425
|
+
// and is reused when npm can do so.
|
|
426
|
+
await run(['cache', 'add', spec, '--prefer-online']);
|
|
411
427
|
options.onPhase?.('tarball-check');
|
|
412
428
|
const metadata = JSON.parse(await run(['view', spec, 'name', 'version', 'dist', '--json', '--offline']));
|
|
413
429
|
const [packed] = JSON.parse(await run(['pack', spec, '--dry-run', '--json', '--offline']));
|
package/dist/utils/welcome.js
CHANGED
|
@@ -183,36 +183,30 @@ export function parseAgentWelcomeMetadata(content) {
|
|
|
183
183
|
* 生成统一的欢迎消息文本
|
|
184
184
|
*/
|
|
185
185
|
export function generateWelcomeMessage(options) {
|
|
186
|
-
const channelDesc = getChannelDescription(options.channelType);
|
|
187
186
|
const greeting = options.ownerName
|
|
188
187
|
? `👋 你好,${options.ownerName}!`
|
|
189
188
|
: '👋 你好!';
|
|
190
189
|
const agentIntro = options.agentName
|
|
191
|
-
? `我是 ${options.agentName}
|
|
192
|
-
: '
|
|
193
|
-
const channelInfo = `已成功连接到${channelDesc}。`;
|
|
190
|
+
? `我是 ${options.agentName},很高兴认识你。`
|
|
191
|
+
: '我是你的 AI 助手,很高兴认识你。';
|
|
194
192
|
const channelSpecificNote = getChannelSpecificNote(options.channelType);
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
// Owner 才会收到:说明可以随时通过对话调整 Agent 的设定
|
|
194
|
+
const ownerNote = options.includeBindingNote
|
|
195
|
+
? '\n\n🎨 我的名字、性格和说话风格都可以由你来定,想调整时直接告诉我。'
|
|
197
196
|
: '';
|
|
198
|
-
return
|
|
197
|
+
return `${greeting}
|
|
199
198
|
|
|
200
|
-
${
|
|
199
|
+
${agentIntro}有什么需要,直接用平常说话的方式告诉我就行,不用记任何指令。
|
|
201
200
|
|
|
202
|
-
|
|
201
|
+
我可以帮你:
|
|
202
|
+
- 💬 解答问题、出主意、一起梳理思路
|
|
203
|
+
- 📝 写文案、改文档、整理总结
|
|
204
|
+
- ⏰ 定时提醒、到点跟进事情
|
|
205
|
+
- 🧠 记住你的习惯和偏好,下次不用重复交代${channelSpecificNote}${ownerNote}
|
|
203
206
|
|
|
204
|
-
|
|
207
|
+
想看更多玩法,随时发送 /help。
|
|
205
208
|
|
|
206
|
-
|
|
207
|
-
2. **查看状态**:发送 \`/status\` 查看当前会话状态
|
|
208
|
-
3. **会话管理**:发送 \`/session\` 查看和切换会话
|
|
209
|
-
|
|
210
|
-
💡 **提示**:
|
|
211
|
-
- 直接发送消息即可与 Claude/Codex 对话
|
|
212
|
-
- 支持多会话管理,每个会话独立上下文
|
|
213
|
-
- 所有命令以 \`/\` 开头${channelSpecificNote}${bindingNote}
|
|
214
|
-
|
|
215
|
-
现在就可以开始工作了!`;
|
|
209
|
+
那我们从哪儿开始?`;
|
|
216
210
|
}
|
|
217
211
|
export function renderPostBootstrapWelcomeMessage(options) {
|
|
218
212
|
const validation = validateWelcomeTemplate(options.template);
|
|
@@ -306,15 +300,15 @@ function getChannelDescription(channelType) {
|
|
|
306
300
|
function getChannelSpecificNote(channelType) {
|
|
307
301
|
switch (channelType) {
|
|
308
302
|
case 'aun':
|
|
309
|
-
return '
|
|
303
|
+
return '';
|
|
310
304
|
case 'feishu':
|
|
311
|
-
return '\n-
|
|
305
|
+
return '\n- 👥 拉我进群,@我 就能一起讨论';
|
|
312
306
|
case 'wechat':
|
|
313
307
|
return '';
|
|
314
308
|
case 'dingtalk':
|
|
315
|
-
return '\n-
|
|
309
|
+
return '\n- 👥 拉我进群,@我 就能一起讨论';
|
|
316
310
|
case 'qqbot':
|
|
317
|
-
return '\n-
|
|
311
|
+
return '\n- 👥 拉我进群,@我 就能一起讨论';
|
|
318
312
|
case 'wecom':
|
|
319
313
|
return '';
|
|
320
314
|
default:
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
ec aid show <当前 self AID> [--format json]
|
|
13
13
|
ec aid lookup <任意合法 AID> [--format json]
|
|
14
14
|
ec aid agentmd get <任意合法 AID> [--format json]
|
|
15
|
+
ec aid bind status [--subject <本地AID>] [--format json]
|
|
15
16
|
# 仅 request / bypass
|
|
17
|
+
ec aid bind <ownerAid> [--subject <本地AID>] [--format json]
|
|
18
|
+
ec aid bind accept <operationId> [--subject <本地AID>] [--format json]
|
|
19
|
+
ec aid bind reject <operationId> [--reason '<原因>'] [--subject <本地AID>] [--format json]
|
|
16
20
|
ec aid agentmd put <当前 self AID> [--format json]
|
|
17
21
|
ec aid agentmd set <当前 self AID> <name|type|description|version|tags> <value> [--format json]
|
|
18
22
|
ec aid avatar <当前 self AID> (--file <path> | --url <https-url>) [--format json]
|
|
@@ -33,6 +37,7 @@ init 不等待人工审批、不启动 daemon,也不把该目标写入 `daemon
|
|
|
33
37
|
容器入口随后启动 daemon,由 SDK 恢复同一任务,审批事件触发新鲜共识核验后才产生协议 Owner 权限。
|
|
34
38
|
|
|
35
39
|
`bindingStatus` 为 `submitted` 仅表示 SDK 已确认邀约 `message.send` 回执;它不代表 Owner 已审批。
|
|
40
|
+
|
|
36
41
|
`already_bound` 必须经过当前共识核验。旧版本留下的注册意图会由 SDK 恢复原任务,
|
|
37
42
|
初始化复用原 `operation_id`,不重新注册身份或创建第二个绑定任务。
|
|
38
43
|
提交超时返回 `unknown`,错误结果中的 `initialized: true` 表示配置和控制身份仍然保留;
|
|
@@ -40,6 +45,30 @@ init 不等待人工审批、不启动 daemon,也不把该目标写入 `daemon
|
|
|
40
45
|
相同注册意图复用原 operation_id,不同目标返回冲突,`--force` 不覆盖协议 Owner。
|
|
41
46
|
实现与验证说明见 `docs/init-control-aid-owner-registration.md`。
|
|
42
47
|
|
|
48
|
+
## 收到绑定邀约后确认或拒绝
|
|
49
|
+
|
|
50
|
+
先查询接收方本地 AID 的待确认邀约,再按确切的 `operation_id` 作决定:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ec aid bind status --subject <接收方本地AID> --format json
|
|
54
|
+
ec aid bind accept <operationId> --subject <接收方本地AID> --format json
|
|
55
|
+
ec aid bind reject <operationId> --subject <接收方本地AID> --reason '用户拒绝' --format json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`--subject` 始终指定执行审批的本地 AID。省略时,独立 CLI 使用 daemon Control AID,托管会话使用当前 self AID。
|
|
59
|
+
例如 B 请求由 A 担任 Owner,在 A 上确认时应选 `--subject A`。A 的状态中 `requests` 展示 SDK 已验证的
|
|
60
|
+
待确认绑定任务,包括 `operation_id`、`sub_aid`、`owner_aid`、`requester_aid`、本端 `local_role` 和关系版本。
|
|
61
|
+
本端既可以是拟任 Owner,也可以是接受 Owner 绑定的 sub;转移、解绑及未验证请求不通过该入口审批。
|
|
62
|
+
|
|
63
|
+
CLI 经 daemon 已有的 SDK 客户端调用 `acceptOwnershipRequest` / `rejectOwnershipRequest`,不新建连接。
|
|
64
|
+
请求的真实性、当前关系、固定参与方和决定一致性由 SDK 在执行时复核。未处理的邀约保持待审批。
|
|
65
|
+
请求摘要只保存在当前客户端的内存中,daemon 重启或更换客户端后由 SDK 从持久任务重新发出事件恢复。
|
|
66
|
+
|
|
67
|
+
审批回执的 `decisionTask` 是这次提交决定的任务;`pending` 表示仍在推进,`completed` 表示 SDK 已核验任务完成。
|
|
68
|
+
接受回执本身不授予权限,Owner 权限仍由新鲜 Ownership 共识确定。后续 SDK 事件更新当前进程的 `decisionTask`。
|
|
69
|
+
托管会话审批需要当前 Agent Owner 或 daemon Owner,以及 `request` / `bypass` 任务;admin 仅可查询。
|
|
70
|
+
选择另一个本地 AID 需要 daemon Owner。Fullaccess 执行不额外授予协议绑定或审批权限。
|
|
71
|
+
|
|
43
72
|
## Ownership 共识缓存
|
|
44
73
|
|
|
45
74
|
Agent 的首个配置 Owner 可在该 Agent 的 AUN 私聊中发送纯 `/bind`,由 Agent
|
|
@@ -86,6 +115,10 @@ ec aid agentmd put <aid>
|
|
|
86
115
|
ec aid agentmd get <aid>
|
|
87
116
|
ec aid agentmd set <aid> <name|type|description|version|tags> <value>
|
|
88
117
|
ec aid avatar <aid> (--file <path> | --url <https-url>)
|
|
118
|
+
ec aid bind <ownerAid> [--subject <subjectAid>]
|
|
119
|
+
ec aid bind status [--subject <subjectAid>]
|
|
120
|
+
ec aid bind accept <operationId> [--subject <subjectAid>]
|
|
121
|
+
ec aid bind reject <operationId> [--reason '<text>'] [--subject <subjectAid>]
|
|
89
122
|
|
|
90
123
|
# 删除指定本地 AID(无网络注销)
|
|
91
124
|
ec aid delete <aid>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evolcore",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.39",
|
|
4
4
|
"description": "AI Agent gateway connecting Claude, Codex, Gemini, and the bundled ecagent runner to messaging channels with multi-project session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -54,14 +54,14 @@
|
|
|
54
54
|
"prepublishOnly": "npm test"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@agentunion/fastaun": "0.5.
|
|
57
|
+
"@agentunion/fastaun": "0.5.23",
|
|
58
58
|
"@anthropic-ai/claude-agent-sdk": "0.3.300",
|
|
59
59
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
60
60
|
"ajv": "^8.20.0",
|
|
61
61
|
"chokidar": "^5.0.0",
|
|
62
62
|
"cron-parser": "^5.5.0",
|
|
63
63
|
"dotenv": "^17.4.2",
|
|
64
|
-
"ec-web": "0.0.
|
|
64
|
+
"ec-web": "0.0.39",
|
|
65
65
|
"ignore": "7.0.5",
|
|
66
66
|
"image-type": "^6.0.0",
|
|
67
67
|
"partial-json": "0.1.7",
|