evolcore 0.0.25 → 0.0.27
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 +632 -565
- package/LICENSE +21 -21
- package/README.md +455 -455
- package/bin/codex-managed-acl.ps1 +203 -160
- package/bin/codex-managed-hook.mjs +208 -208
- package/bin/ec-safe-output.js +327 -327
- package/bin/ec.js +40 -29
- package/bin/install-codex-managed-hooks.mjs +582 -555
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +172 -33
- package/dist/agents/codex/app-server-client.js +76 -31
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +434 -446
- package/dist/agents/ecagent-runner.js +174 -34
- package/dist/agents/gemini-runner.js +8 -7
- package/dist/agents/runner-types.js +75 -19
- package/dist/aun/aid/agentmd.js +151 -36
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +4 -4
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +17 -13
- package/dist/aun/group/history.js +303 -0
- package/dist/aun/group/identity.js +43 -0
- package/dist/aun/group/index.js +5 -0
- package/dist/aun/{msg/group.js → group/operations.js} +162 -39
- package/dist/aun/{menu-client.js → menu/client.js} +85 -6
- package/dist/aun/menu/index.js +2 -0
- package/dist/aun/menu/transport.js +49 -0
- package/dist/aun/msg/index.js +2 -1
- package/dist/aun/msg/managed-operation.js +85 -19
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +19 -9
- package/dist/aun/msg/upload.js +43 -80
- package/dist/aun/outbox.js +29 -7
- package/dist/aun/rpc/client.js +1 -0
- package/dist/aun/storage/client.js +39 -50
- package/dist/channels/aun.js +842 -318
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +212 -89
- package/dist/cli/agent-create.js +626 -11
- package/dist/cli/agent.js +125 -13
- package/dist/cli/aun-commands.js +487 -337
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +55 -33
- package/dist/cli/contact.js +72 -15
- package/dist/cli/ctl-command.js +24 -24
- package/dist/cli/daemon-commands.js +140 -63
- package/dist/cli/diag.js +31 -0
- package/dist/cli/fs-command.js +70 -70
- package/dist/cli/handoff-command.js +46 -14
- package/dist/cli/index.js +91 -79
- package/dist/cli/init.js +34 -1
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/managed-command-guard.js +5 -1
- package/dist/cli/menu-client.js +51 -32
- package/dist/cli/model.js +3 -3
- package/dist/cli/net-check.js +22 -22
- package/dist/cli/queue-command.js +31 -31
- package/dist/cli/response.js +32 -32
- package/dist/cli/restart-monitor.js +103 -42
- package/dist/cli/stats.js +51 -51
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +74 -64
- package/dist/config/builtin-roles.js +11 -0
- package/dist/config/config-field-policy.js +11 -0
- package/dist/config/config-manager.js +32 -4
- package/dist/config/config-operation-service.js +160 -0
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +97 -8
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +30 -7
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +7 -1
- package/dist/core/audit/event-key.js +17 -2
- package/dist/core/audit/log-integrity.js +34 -8
- package/dist/core/auth/agent-delegation.js +48 -14
- package/dist/core/auth/auth-gateway.js +2 -0
- package/dist/core/auth/authorization-audit.js +53 -49
- package/dist/core/auth/operation-authorizer.js +90 -7
- package/dist/core/auth/operation-catalog.js +145 -4
- package/dist/core/auth/remote-access-store.js +155 -10
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +111 -20
- package/dist/core/command/agent-control.js +88 -7
- package/dist/core/command/agent-storage-menu.js +443 -0
- package/dist/core/command/cli-intent-parser.js +19 -0
- package/dist/core/command/command-handler.js +159 -65
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +46 -7
- package/dist/core/command/menu-handler.js +392 -100
- package/dist/core/command/menu-protocol.js +45 -6
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +123 -43
- package/dist/core/event-catalog.js +4 -0
- package/dist/core/evolagent-registry.js +28 -2
- package/dist/core/group/invite-service.js +924 -0
- package/dist/core/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +146 -34
- package/dist/core/message/message-bridge.js +163 -20
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +99 -41
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +755 -144
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +112 -41
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +108 -17
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +9 -8
- package/dist/core/relation/peer-identity.js +57 -6
- package/dist/core/relation/shared-data.js +106 -0
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/runtime-lock.js +10 -26
- package/dist/core/session/runner-task-history.js +170 -0
- package/dist/core/session/session-manager.js +34 -6
- package/dist/core/session/session-mapper.js +41 -0
- package/dist/core/session/session-turn-coordinator.js +37 -7
- package/dist/core/session/session-turns.js +48 -7
- package/dist/core/task/task-recovery-service.js +176 -0
- package/dist/core/task/task-resume-operation.js +32 -0
- package/dist/eck/group-rules-sync.js +5 -3
- package/dist/eck/kit-renderer.js +1 -0
- package/dist/index.js +588 -217
- package/dist/ipc.js +193 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +2 -0
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- package/dist/stats/price-resolver.js +28 -10
- package/dist/stats/query.js +139 -139
- package/dist/stats/role-budget.js +6 -6
- package/dist/stats/writer.js +51 -51
- package/dist/trigger/event-source.js +19 -0
- package/dist/trigger/feedback.js +233 -40
- package/dist/trigger/parser.js +55 -5
- package/dist/trigger/patch.js +9 -0
- package/dist/trigger/scheduler.js +30 -4
- package/dist/trigger/validation.js +20 -3
- package/dist/utils/cross-platform.js +30 -1
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/error-utils.js +61 -2
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +26 -14
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -23
- package/dist/utils/npm-ops.js +176 -35
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +196 -23
- package/ecagent/LICENSE +21 -21
- package/ecagent/NOTICE +11 -11
- package/ecagent/README.md +61 -61
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
- package/ecagent/dist/harness/compaction/compaction.js +83 -83
- package/ecagent/dist/harness/messages.js +7 -7
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/GUIDE.md +20 -20
- package/kits/docs/INDEX.md +60 -66
- package/kits/docs/aun/CHEATSHEET.md +19 -19
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
- package/kits/docs/channels/aun.md +69 -56
- package/kits/docs/channels/feishu.md +56 -56
- package/kits/docs/context-assembly.md +379 -367
- package/kits/docs/eck_templates/GUIDE.template.md +22 -22
- package/kits/docs/eck_templates/INDEX.template.md +28 -28
- package/kits/docs/eck_templates/path-registry.template.md +33 -33
- package/kits/docs/eck_templates/runtime.template.md +19 -19
- package/kits/docs/evolcore/INDEX.md +76 -71
- package/kits/docs/evolcore/agent-create.md +202 -165
- package/kits/docs/evolcore/agent.md +99 -67
- package/kits/docs/evolcore/aid.md +68 -60
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
- package/kits/docs/evolcore/config.md +241 -195
- package/kits/docs/evolcore/contact.md +75 -68
- package/kits/docs/evolcore/ctl.md +46 -46
- package/kits/docs/evolcore/event.md +216 -216
- package/kits/docs/evolcore/fs-architecture.md +1215 -1215
- package/kits/docs/evolcore/fs.md +110 -110
- package/kits/docs/evolcore/group-collaboration.md +244 -244
- package/kits/docs/evolcore/group-fs.md +17 -17
- package/kits/docs/evolcore/group-rules.md +251 -251
- package/kits/docs/evolcore/group.md +267 -152
- package/kits/docs/evolcore/menu-client.md +58 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +169 -165
- package/kits/docs/evolcore/response.md +75 -75
- package/kits/docs/evolcore/rpc.md +37 -37
- package/kits/docs/evolcore/self-summary.md +29 -29
- package/kits/docs/evolcore/stats.md +83 -83
- package/kits/docs/evolcore/storage.md +53 -50
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +623 -575
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
- package/kits/docs/identity/PATH_OPS.md +16 -16
- package/kits/docs/identity/ROLE_DETAIL.md +25 -25
- package/kits/docs/identity/identity-tools.md +26 -26
- package/kits/docs/path-registry.md +62 -62
- package/kits/docs/prompt-loading-architecture.md +269 -269
- package/kits/docs/venues/aun-group.md +45 -45
- package/kits/docs/venues/aun-private.md +10 -10
- package/kits/docs/venues/client-desktop.md +10 -10
- package/kits/docs/venues/client-mobile.md +10 -10
- package/kits/docs/venues/feishu-group.md +13 -13
- package/kits/docs/venues/feishu-private.md +9 -9
- package/kits/docs/venues/group.md +42 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +217 -190
- package/kits/eck_message_manifest.json +74 -74
- package/kits/migrations/README-role-config-v4.md +32 -32
- package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
- package/kits/migrations/migrate-role-config-v4.mjs +614 -614
- package/kits/migrations/migrate-role-config-v5.mjs +365 -365
- package/kits/migrations/rename-config-file.mjs +99 -99
- package/kits/rules/01-overview.md +166 -162
- package/kits/rules/02-navigation.md +96 -96
- package/kits/rules/03-identity.md +36 -36
- package/kits/rules/04-relation.md +76 -75
- package/kits/rules/05-venue.md +60 -60
- package/kits/rules/06-channel.md +65 -63
- package/kits/schemas/_meta.json +53 -53
- package/kits/schemas/agent-config.schema.1.json +177 -177
- package/kits/schemas/agent-config.schema.10.json +395 -395
- package/kits/schemas/agent-config.schema.11.json +421 -421
- package/kits/schemas/agent-config.schema.12.json +451 -427
- package/kits/schemas/agent-config.schema.2.json +239 -239
- package/kits/schemas/agent-config.schema.3.json +119 -119
- package/kits/schemas/agent-config.schema.4.json +208 -208
- package/kits/schemas/agent-config.schema.5.json +326 -326
- package/kits/schemas/agent-config.schema.6.json +322 -322
- package/kits/schemas/agent-config.schema.7.json +303 -303
- package/kits/schemas/agent-config.schema.8.json +304 -304
- package/kits/schemas/agent-config.schema.9.json +364 -364
- package/kits/schemas/contact-book.schema.1.json +36 -36
- package/kits/schemas/contact-book.schema.2.json +43 -43
- package/kits/schemas/contact-book.schema.3.json +67 -67
- package/kits/schemas/daemon.schema.1.json +91 -91
- package/kits/schemas/daemon.schema.2.json +101 -101
- package/kits/schemas/daemon.schema.3.json +123 -123
- package/kits/schemas/daemon.schema.4.json +136 -136
- package/kits/schemas/daemon.schema.5.json +135 -135
- package/kits/schemas/daemon.schema.6.json +131 -131
- package/kits/schemas/defaults.schema.1.json +81 -81
- package/kits/schemas/defaults.schema.2.json +85 -85
- package/kits/schemas/defaults.schema.3.json +73 -73
- package/kits/schemas/defaults.schema.4.json +107 -107
- package/kits/schemas/defaults.schema.5.json +119 -119
- package/kits/schemas/menu-exec-schema-commands.md +208 -208
- package/kits/schemas/migrations/README.md +30 -30
- package/kits/schemas/relation-config.schema.1.json +158 -158
- package/kits/schemas/relation-config.schema.2.json +73 -73
- package/kits/schemas/relation-config.schema.3.json +50 -50
- package/kits/schemas/relation-config.schema.4.json +47 -47
- package/kits/schemas/relation-config.schema.5.json +47 -47
- package/kits/schemas/relation-config.schema.6.json +46 -46
- package/kits/schemas/relation-config.schema.7.json +59 -59
- package/kits/schemas/relation-config.schema.8.json +103 -103
- package/kits/schemas/role-config.schema.1.json +215 -201
- package/kits/schemas/role-registry.schema.1.json +35 -35
- package/kits/schemas/single-session.schema.1.json +31 -31
- package/kits/schemas/single-session.schema.2.json +57 -57
- package/kits/schemas/single-session.schema.3.json +32 -32
- package/kits/templates/bootstrap-welcome.md +17 -17
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
- package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
- package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
- package/kits/templates/message-fragments/inject-default.md +2 -2
- package/kits/templates/message-fragments/item.md +2 -2
- package/kits/templates/roles/admin.json +92 -92
- package/kits/templates/roles/member.json +101 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +90 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +53 -34
- package/kits/templates/system-fragments/channel.md +28 -64
- package/kits/templates/system-fragments/commands.md +36 -28
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/identity.md +13 -13
- package/kits/templates/system-fragments/relation.md +19 -19
- package/kits/templates/system-fragments/reply.md +56 -0
- package/kits/templates/system-fragments/session.md +59 -59
- package/kits/templates/system-fragments/venue.md +31 -31
- package/package.json +104 -98
- package/dist/aun/group-identity.js +0 -9
- /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
- /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
|
@@ -25,6 +25,7 @@ const GROUP_MANAGEMENT_OPERATIONS = [
|
|
|
25
25
|
['ec.group.list', 'read', ['agent'], 'List groups joined by the current Agent'],
|
|
26
26
|
['ec.group.info', 'read', ['relation', 'agent'], 'Read group information'],
|
|
27
27
|
['ec.group.pull', 'read', ['relation', 'agent'], 'Pull group messages'],
|
|
28
|
+
['ec.group.history', 'read', ['relation', 'agent'], 'Read local group conversation history'],
|
|
28
29
|
['ec.group.members', 'read', ['relation', 'agent'], 'List group members'],
|
|
29
30
|
['ec.group.online', 'read', ['relation', 'agent'], 'List online group members'],
|
|
30
31
|
['ec.group.rules.get', 'read', ['relation', 'agent'], 'Read the published group rules file'],
|
|
@@ -47,6 +48,11 @@ const GROUP_MANAGEMENT_OPERATIONS = [
|
|
|
47
48
|
['ec.group.owner', 'write-agent', ['agent'], 'Transfer group ownership'],
|
|
48
49
|
['ec.group.ban', 'write-agent', ['agent'], 'Read or update the group ban list'],
|
|
49
50
|
['ec.group.unban', 'write-agent', ['agent'], 'Remove a group ban'],
|
|
51
|
+
['ec.group.block', 'write-own', ['relation', 'agent'], 'Block group message reception'],
|
|
52
|
+
['ec.group.unblock', 'write-own', ['relation', 'agent'], 'Unblock group message reception'],
|
|
53
|
+
['ec.group.blocklist', 'read', ['agent'], 'Read the group block list'],
|
|
54
|
+
['ec.group.report-status', 'write-own', ['relation', 'agent'], 'Report the current Agent group status'],
|
|
55
|
+
['ec.group.member-statuses', 'read', ['relation', 'agent'], 'Read reported group member statuses'],
|
|
50
56
|
['ec.group.rules.set', 'write-agent', ['agent'], 'Upload and publish group rules content'],
|
|
51
57
|
['ec.group.rules.publish', 'write-agent', ['agent'], 'Publish the current group rules file'],
|
|
52
58
|
['ec.group.rules.update', 'write-agent', ['agent'], 'Update group join rules'],
|
|
@@ -100,6 +106,13 @@ const HANDOFF_WRITE_OPERATIONS = [{
|
|
|
100
106
|
defaultScopes: ['relation', 'agent'],
|
|
101
107
|
description: 'Return the current handoff result',
|
|
102
108
|
sources: ['agent-tool'],
|
|
109
|
+
}, {
|
|
110
|
+
id: 'ec.handoff.recover',
|
|
111
|
+
category: 'write-agent',
|
|
112
|
+
dangerous: true,
|
|
113
|
+
defaultScopes: ['agent'],
|
|
114
|
+
description: 'Recover an open handoff after its target session changed',
|
|
115
|
+
sources: ['agent-tool'],
|
|
103
116
|
}];
|
|
104
117
|
const QUEUE_OPERATIONS = [['read', 'Read the current Agent message queue'], ['clear', 'Clear the current Agent message queue'], ['cancel', 'Cancel a current Agent queued message'], ['interrupt', 'Interrupt a current Agent session']].map(([kind, description]) => ({
|
|
105
118
|
id: `ec.queue.${kind}`,
|
|
@@ -358,6 +371,86 @@ const OPERATIONS = [
|
|
|
358
371
|
description: '获取文件内容(需路径沙箱检查)',
|
|
359
372
|
sources: ['slash', 'menu'],
|
|
360
373
|
},
|
|
374
|
+
{
|
|
375
|
+
id: 'file.root.agent',
|
|
376
|
+
category: 'read',
|
|
377
|
+
dangerous: false,
|
|
378
|
+
defaultScopes: ['agent'],
|
|
379
|
+
description: '获取 Agent 内部数据根目录句柄',
|
|
380
|
+
sources: ['menu'],
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
id: 'file.root.relation',
|
|
384
|
+
category: 'read',
|
|
385
|
+
dangerous: false,
|
|
386
|
+
defaultScopes: ['relation', 'agent'],
|
|
387
|
+
description: '获取当前或 owner 指定关系的共享数据根目录句柄',
|
|
388
|
+
sources: ['menu'],
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
id: 'file.data.list',
|
|
392
|
+
category: 'read',
|
|
393
|
+
dangerous: false,
|
|
394
|
+
defaultScopes: ['relation', 'agent'],
|
|
395
|
+
description: '列出已授权内部数据根目录',
|
|
396
|
+
sources: ['menu'],
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: 'file.data.query',
|
|
400
|
+
category: 'read',
|
|
401
|
+
dangerous: false,
|
|
402
|
+
defaultScopes: ['relation', 'agent'],
|
|
403
|
+
description: '查询已授权内部数据根文件元信息',
|
|
404
|
+
sources: ['menu'],
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
id: 'file.data.fetch',
|
|
408
|
+
category: 'read',
|
|
409
|
+
dangerous: false,
|
|
410
|
+
defaultScopes: ['relation', 'agent'],
|
|
411
|
+
description: '下载已授权内部数据根文件',
|
|
412
|
+
sources: ['menu'],
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
id: 'file.root.storage',
|
|
416
|
+
category: 'read',
|
|
417
|
+
dangerous: false,
|
|
418
|
+
defaultScopes: ['agent'],
|
|
419
|
+
description: '获取 Agent 自有云存储根句柄',
|
|
420
|
+
sources: ['menu'],
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
id: 'file.storage.list',
|
|
424
|
+
category: 'read',
|
|
425
|
+
dangerous: false,
|
|
426
|
+
defaultScopes: ['agent'],
|
|
427
|
+
description: '列出 Agent 自有云存储目录',
|
|
428
|
+
sources: ['menu'],
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
id: 'file.storage.query',
|
|
432
|
+
category: 'read',
|
|
433
|
+
dangerous: false,
|
|
434
|
+
defaultScopes: ['agent'],
|
|
435
|
+
description: '查询 Agent 自有云存储元信息',
|
|
436
|
+
sources: ['menu'],
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
id: 'file.storage.fetch',
|
|
440
|
+
category: 'read',
|
|
441
|
+
dangerous: false,
|
|
442
|
+
defaultScopes: ['agent'],
|
|
443
|
+
description: '获取 Agent 自有云存储短时下载链接',
|
|
444
|
+
sources: ['menu'],
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
id: 'file.storage.quota',
|
|
448
|
+
category: 'read',
|
|
449
|
+
dangerous: false,
|
|
450
|
+
defaultScopes: ['agent'],
|
|
451
|
+
description: '查询 Agent 自有云存储配额',
|
|
452
|
+
sources: ['menu'],
|
|
453
|
+
},
|
|
361
454
|
// ── Trigger Operations ──
|
|
362
455
|
{
|
|
363
456
|
id: 'trigger.list',
|
|
@@ -446,7 +539,7 @@ const OPERATIONS = [
|
|
|
446
539
|
dangerous: false,
|
|
447
540
|
defaultScopes: ['agent'],
|
|
448
541
|
description: 'Assign a role to a private or group relation',
|
|
449
|
-
sources: ['menu', 'ecweb', 'control'],
|
|
542
|
+
sources: ['menu', 'menu.cli', 'ecweb', 'control', 'agent-tool'],
|
|
450
543
|
},
|
|
451
544
|
{
|
|
452
545
|
id: 'role.revoke',
|
|
@@ -454,7 +547,7 @@ const OPERATIONS = [
|
|
|
454
547
|
dangerous: false,
|
|
455
548
|
defaultScopes: ['agent'],
|
|
456
549
|
description: 'Revoke a private or group relation role assignment',
|
|
457
|
-
sources: ['menu', 'ecweb', 'control'],
|
|
550
|
+
sources: ['menu', 'menu.cli', 'ecweb', 'control', 'agent-tool'],
|
|
458
551
|
},
|
|
459
552
|
{
|
|
460
553
|
id: 'role.policy.read',
|
|
@@ -470,7 +563,7 @@ const OPERATIONS = [
|
|
|
470
563
|
dangerous: false,
|
|
471
564
|
defaultScopes: ['agent'],
|
|
472
565
|
description: 'Read private and group relation role assignments',
|
|
473
|
-
sources: ['menu', 'ecweb', 'control'],
|
|
566
|
+
sources: ['menu', 'menu.cli', 'ecweb', 'control', 'agent-tool'],
|
|
474
567
|
},
|
|
475
568
|
{
|
|
476
569
|
id: 'role.policy.write',
|
|
@@ -485,7 +578,7 @@ const OPERATIONS = [
|
|
|
485
578
|
category: 'read',
|
|
486
579
|
dangerous: false,
|
|
487
580
|
defaultScopes: ['agent'],
|
|
488
|
-
description: 'Read
|
|
581
|
+
description: 'Read Agent contacts, pending requests, and blocked contacts',
|
|
489
582
|
sources: ['agent-tool'],
|
|
490
583
|
},
|
|
491
584
|
{
|
|
@@ -504,6 +597,14 @@ const OPERATIONS = [
|
|
|
504
597
|
description: 'Block or unblock a canonical contact across bound private channels',
|
|
505
598
|
sources: ['agent-tool'],
|
|
506
599
|
},
|
|
600
|
+
{
|
|
601
|
+
id: 'contact.review',
|
|
602
|
+
category: 'write-agent',
|
|
603
|
+
dangerous: false,
|
|
604
|
+
defaultScopes: ['agent'],
|
|
605
|
+
description: 'Review pending Agent contact requests',
|
|
606
|
+
sources: ['agent-tool'],
|
|
607
|
+
},
|
|
507
608
|
{
|
|
508
609
|
id: 'connect.read',
|
|
509
610
|
category: 'read',
|
|
@@ -609,6 +710,14 @@ const OPERATIONS = [
|
|
|
609
710
|
description: '读取或更新当前 Agent 的 persona.md',
|
|
610
711
|
sources: ['agent-tool', 'control'],
|
|
611
712
|
},
|
|
713
|
+
{
|
|
714
|
+
id: 'agent.welcome',
|
|
715
|
+
category: 'write-agent',
|
|
716
|
+
dangerous: false,
|
|
717
|
+
defaultScopes: ['agent'],
|
|
718
|
+
description: '读取或更新当前 Agent 的 Bootstrap welcome.md',
|
|
719
|
+
sources: ['agent-tool', 'control'],
|
|
720
|
+
},
|
|
612
721
|
{
|
|
613
722
|
id: 'agent.create',
|
|
614
723
|
category: 'process',
|
|
@@ -617,6 +726,14 @@ const OPERATIONS = [
|
|
|
617
726
|
description: '创建新 agent',
|
|
618
727
|
sources: ['menu', 'menu.cli', 'agent-tool', 'control'],
|
|
619
728
|
},
|
|
729
|
+
{
|
|
730
|
+
id: 'agent.initialize',
|
|
731
|
+
category: 'process',
|
|
732
|
+
dangerous: true,
|
|
733
|
+
defaultScopes: ['control'],
|
|
734
|
+
description: 'Create and initialize an Agent from an explicit final plan',
|
|
735
|
+
sources: ['menu', 'menu.cli', 'agent-tool', 'control'],
|
|
736
|
+
},
|
|
620
737
|
{
|
|
621
738
|
id: 'agent.enable',
|
|
622
739
|
category: 'process',
|
|
@@ -666,6 +783,14 @@ const OPERATIONS = [
|
|
|
666
783
|
description: '查看系统状态(敏感信息)',
|
|
667
784
|
sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
|
|
668
785
|
},
|
|
786
|
+
{
|
|
787
|
+
id: 'system.paircode',
|
|
788
|
+
category: 'process',
|
|
789
|
+
dangerous: true,
|
|
790
|
+
defaultScopes: ['process'],
|
|
791
|
+
description: '获取 daemon 远程控制配对码',
|
|
792
|
+
sources: ['menu', 'control'],
|
|
793
|
+
},
|
|
669
794
|
{
|
|
670
795
|
id: 'activity.current',
|
|
671
796
|
category: 'read',
|
|
@@ -912,6 +1037,14 @@ const OPERATIONS = [
|
|
|
912
1037
|
...HANDOFF_READ_OPERATIONS,
|
|
913
1038
|
...HANDOFF_WRITE_OPERATIONS,
|
|
914
1039
|
...QUEUE_OPERATIONS,
|
|
1040
|
+
{
|
|
1041
|
+
id: 'ec.task.resume',
|
|
1042
|
+
category: 'write-agent',
|
|
1043
|
+
dangerous: false,
|
|
1044
|
+
defaultScopes: ['agent'],
|
|
1045
|
+
description: 'Resume a paused or abnormally interrupted Agent session task',
|
|
1046
|
+
sources: ['agent-tool', 'control'],
|
|
1047
|
+
},
|
|
915
1048
|
{
|
|
916
1049
|
id: 'ec.version',
|
|
917
1050
|
category: 'read',
|
|
@@ -968,6 +1101,14 @@ const OPERATIONS = [
|
|
|
968
1101
|
description: '通过 ec msg send 发送私聊消息',
|
|
969
1102
|
sources: ['agent-tool'],
|
|
970
1103
|
},
|
|
1104
|
+
{
|
|
1105
|
+
id: 'ec.menu.request',
|
|
1106
|
+
category: 'write-agent',
|
|
1107
|
+
dangerous: false,
|
|
1108
|
+
defaultScopes: ['relation'],
|
|
1109
|
+
description: 'Send an authenticated remote Control Menu request through the current Agent daemon channel',
|
|
1110
|
+
sources: ['agent-tool'],
|
|
1111
|
+
},
|
|
971
1112
|
{
|
|
972
1113
|
id: 'ec.msg.file',
|
|
973
1114
|
category: 'write-own',
|
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { resolvePaths } from '../../paths.js';
|
|
5
5
|
export const REMOTE_ACCESS_TOKEN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
6
|
+
export const DIAGNOSTIC_TOKEN_TTL_MS = 60 * 60 * 1000;
|
|
6
7
|
// 与 owner 绑定二维码(BindService)的默认有效期保持一致:10 分钟。
|
|
7
8
|
export const REMOTE_ACCESS_PAIRING_TTL_MS = 10 * 60 * 1000;
|
|
8
9
|
export function remoteAccessTokenStorePath() {
|
|
@@ -11,13 +12,21 @@ export function remoteAccessTokenStorePath() {
|
|
|
11
12
|
function loadStore() {
|
|
12
13
|
try {
|
|
13
14
|
const value = JSON.parse(fs.readFileSync(remoteAccessTokenStorePath(), 'utf8'));
|
|
14
|
-
if (Array.isArray(value.tokens))
|
|
15
|
-
return
|
|
15
|
+
if (Array.isArray(value.tokens)) {
|
|
16
|
+
return {
|
|
17
|
+
tokens: value.tokens,
|
|
18
|
+
pairingIssuances: Array.isArray(value.pairingIssuances) ? value.pairingIssuances : [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
16
21
|
}
|
|
17
22
|
catch { }
|
|
18
|
-
return { tokens: [] };
|
|
23
|
+
return { tokens: [], pairingIssuances: [] };
|
|
19
24
|
}
|
|
20
25
|
function saveStore(store) {
|
|
26
|
+
// Issuance history contains no credentials. Keep 30 days; live tokens retain
|
|
27
|
+
// their own provenance even after the corresponding history is pruned.
|
|
28
|
+
const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
29
|
+
store.pairingIssuances = store.pairingIssuances.filter(record => record.expiresAt >= cutoff);
|
|
21
30
|
const file = remoteAccessTokenStorePath();
|
|
22
31
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
23
32
|
const temporary = `${file}.${process.pid}.tmp`;
|
|
@@ -42,7 +51,8 @@ function pruneExpired(store, now) {
|
|
|
42
51
|
const before = store.tokens.length;
|
|
43
52
|
store.tokens = store.tokens.filter(record => typeof record?.token === 'string'
|
|
44
53
|
&& typeof record.lastActive === 'number'
|
|
45
|
-
&&
|
|
54
|
+
&& Number.isFinite(record.lastActive)
|
|
55
|
+
&& now - record.lastActive < (record.diagnostic ? DIAGNOSTIC_TOKEN_TTL_MS : REMOTE_ACCESS_TOKEN_TTL_MS));
|
|
46
56
|
return store.tokens.length !== before;
|
|
47
57
|
}
|
|
48
58
|
function validLabel(value) {
|
|
@@ -85,18 +95,105 @@ export class RemoteAccessService {
|
|
|
85
95
|
pairingCode = '';
|
|
86
96
|
pairingExpiresAt = 0;
|
|
87
97
|
pairingAuthorizedBy;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
pairingId = '';
|
|
99
|
+
pairingIssuedAt = 0;
|
|
100
|
+
pairingIssuers = [];
|
|
101
|
+
// Do not recycle consumed codes during their original validity window,
|
|
102
|
+
// including when other sources have issued intervening codes.
|
|
103
|
+
recentPairingCodes = new Map();
|
|
104
|
+
/** Synchronous reservation: concurrent invitations cannot issue two grants. */
|
|
105
|
+
issueDiagnosticToken(controlAid, agentAid, ownerAid, now = Date.now()) {
|
|
106
|
+
const store = loadStore();
|
|
107
|
+
pruneExpired(store, now);
|
|
108
|
+
if (store.tokens.some(record => record.diagnostic?.controlAid === controlAid)) {
|
|
109
|
+
throw Object.assign(new Error('本机已有有效诊断授权,请先执行 /diag stop 或 ec diag stop。'), { code: 'DIAG_ALREADY_ACTIVE' });
|
|
110
|
+
}
|
|
111
|
+
const record = {
|
|
112
|
+
token: crypto.randomBytes(32).toString('hex'), createdAt: now, lastActive: now,
|
|
113
|
+
label: 'diagnostic', authorizedBy: ownerAid,
|
|
114
|
+
diagnostic: { controlAid, agentAid, ownerAid, caseId: crypto.randomUUID() },
|
|
115
|
+
};
|
|
116
|
+
store.tokens.push(record);
|
|
117
|
+
saveStore(store);
|
|
118
|
+
return record;
|
|
119
|
+
}
|
|
120
|
+
getDiagnosticToken(controlAid, now = Date.now()) {
|
|
121
|
+
const store = loadStore();
|
|
122
|
+
if (pruneExpired(store, now))
|
|
123
|
+
saveStore(store);
|
|
124
|
+
return store.tokens.find(record => record.diagnostic?.controlAid === controlAid);
|
|
125
|
+
}
|
|
126
|
+
/** Expected token prevents a failed older send from revoking a newer grant. */
|
|
127
|
+
revokeDiagnosticToken(controlAid, expectedToken) {
|
|
128
|
+
const store = loadStore();
|
|
129
|
+
const pruned = pruneExpired(store, Date.now());
|
|
130
|
+
const index = store.tokens.findIndex(record => record.diagnostic?.controlAid === controlAid
|
|
131
|
+
&& (expectedToken === undefined || record.token === expectedToken));
|
|
132
|
+
if (index < 0) {
|
|
133
|
+
if (pruned)
|
|
134
|
+
saveStore(store);
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
const [record] = store.tokens.splice(index, 1);
|
|
138
|
+
saveStore(store);
|
|
139
|
+
return record;
|
|
140
|
+
}
|
|
141
|
+
getPairingCode(authorizedBy, now = Date.now(), source = 'host-cli') {
|
|
142
|
+
const owner = validOwner(authorizedBy);
|
|
143
|
+
if (!['host-cli', 'pair', 'menu', 'ecweb-local'].includes(source)
|
|
144
|
+
|| ((source === 'pair' || source === 'menu') && !owner)) {
|
|
145
|
+
throw new Error('pairing source requires a valid source and an owner for /pair or Menu');
|
|
146
|
+
}
|
|
147
|
+
const generated = !this.pairingCode || now >= this.pairingExpiresAt;
|
|
148
|
+
if (generated) {
|
|
149
|
+
for (const [code, expiresAt] of this.recentPairingCodes) {
|
|
150
|
+
if (now >= expiresAt)
|
|
151
|
+
this.recentPairingCodes.delete(code);
|
|
152
|
+
}
|
|
153
|
+
// Choose uniformly from unused values without an unbounded retry loop.
|
|
154
|
+
let code = crypto.randomInt(0, 1_000_000 - this.recentPairingCodes.size);
|
|
155
|
+
for (const occupied of [...this.recentPairingCodes.keys()].sort((a, b) => a - b)) {
|
|
156
|
+
if (occupied > code)
|
|
157
|
+
break;
|
|
158
|
+
code++;
|
|
159
|
+
}
|
|
160
|
+
this.pairingCode = String(code).padStart(6, '0');
|
|
161
|
+
this.pairingId = crypto.randomUUID();
|
|
162
|
+
this.pairingIssuedAt = now;
|
|
91
163
|
this.pairingExpiresAt = now + REMOTE_ACCESS_PAIRING_TTL_MS;
|
|
164
|
+
this.recentPairingCodes.set(code, this.pairingExpiresAt);
|
|
92
165
|
this.pairingAuthorizedBy = undefined;
|
|
166
|
+
this.pairingIssuers = [];
|
|
93
167
|
}
|
|
94
|
-
const
|
|
168
|
+
const previousOwner = this.pairingAuthorizedBy;
|
|
95
169
|
if (!this.pairingAuthorizedBy && owner)
|
|
96
170
|
this.pairingAuthorizedBy = owner;
|
|
171
|
+
const issuer = {
|
|
172
|
+
source,
|
|
173
|
+
issuedAt: now,
|
|
174
|
+
...((source === 'pair' || source === 'menu') && owner ? { authorizedBy: owner } : {}),
|
|
175
|
+
};
|
|
176
|
+
const addedIssuer = !this.pairingIssuers.some(candidate => candidate.source === issuer.source
|
|
177
|
+
&& candidate.authorizedBy === issuer.authorizedBy);
|
|
178
|
+
if (addedIssuer) {
|
|
179
|
+
this.pairingIssuers.push(issuer);
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
this.recordPairingIssuance();
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (generated)
|
|
186
|
+
this.clearPairingCode();
|
|
187
|
+
else {
|
|
188
|
+
this.pairingAuthorizedBy = previousOwner;
|
|
189
|
+
if (addedIssuer)
|
|
190
|
+
this.pairingIssuers.pop();
|
|
191
|
+
}
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
97
194
|
return { code: this.pairingCode, expiresAt: this.pairingExpiresAt };
|
|
98
195
|
}
|
|
99
|
-
redeemPairingCode(code, label,
|
|
196
|
+
redeemPairingCode(code, label, now = Date.now()) {
|
|
100
197
|
if (typeof code !== 'string' || !/^\d{6}$/.test(code)) {
|
|
101
198
|
return { ok: false, code: 'INVALID_ARGUMENT', error: 'pairing code must be six digits' };
|
|
102
199
|
}
|
|
@@ -110,17 +207,65 @@ export class RemoteAccessService {
|
|
|
110
207
|
const token = crypto.randomBytes(32).toString('hex');
|
|
111
208
|
const store = loadStore();
|
|
112
209
|
pruneExpired(store, now);
|
|
113
|
-
const owner =
|
|
210
|
+
const owner = this.pairingAuthorizedBy;
|
|
211
|
+
const pairingId = this.pairingId;
|
|
212
|
+
const pairingIssuers = this.pairingIssuers.map(issuer => ({ ...issuer }));
|
|
114
213
|
store.tokens.push({
|
|
115
214
|
token,
|
|
116
215
|
createdAt,
|
|
117
216
|
lastActive: createdAt,
|
|
118
217
|
label: validLabel(label),
|
|
119
218
|
...(owner ? { authorizedBy: owner } : {}),
|
|
219
|
+
pairingId,
|
|
220
|
+
pairingIssuers,
|
|
120
221
|
});
|
|
222
|
+
const issuance = store.pairingIssuances.find(record => record.pairingId === pairingId);
|
|
223
|
+
if (issuance) {
|
|
224
|
+
issuance.redeemedAt = createdAt;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
store.pairingIssuances.push({
|
|
228
|
+
pairingId,
|
|
229
|
+
issuedAt: this.pairingIssuedAt,
|
|
230
|
+
expiresAt: this.pairingExpiresAt,
|
|
231
|
+
issuers: pairingIssuers,
|
|
232
|
+
redeemedAt: createdAt,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
121
235
|
saveStore(store);
|
|
236
|
+
// A pairing code is a one-time capability. Clear it only after the token
|
|
237
|
+
// and provenance record have been atomically saved. No await separates
|
|
238
|
+
// validation, persistence and consumption, so concurrent IPC calls cannot
|
|
239
|
+
// grant the same code twice.
|
|
240
|
+
this.clearPairingCode();
|
|
122
241
|
return { ok: true, token, expiresAt: createdAt + REMOTE_ACCESS_TOKEN_TTL_MS };
|
|
123
242
|
}
|
|
243
|
+
clearPairingCode() {
|
|
244
|
+
this.pairingCode = '';
|
|
245
|
+
this.pairingExpiresAt = 0;
|
|
246
|
+
this.pairingAuthorizedBy = undefined;
|
|
247
|
+
this.pairingId = '';
|
|
248
|
+
this.pairingIssuedAt = 0;
|
|
249
|
+
this.pairingIssuers = [];
|
|
250
|
+
}
|
|
251
|
+
recordPairingIssuance() {
|
|
252
|
+
if (!this.pairingId || !this.pairingCode)
|
|
253
|
+
return;
|
|
254
|
+
const store = loadStore();
|
|
255
|
+
const existing = store.pairingIssuances.find(record => record.pairingId === this.pairingId);
|
|
256
|
+
const record = {
|
|
257
|
+
pairingId: this.pairingId,
|
|
258
|
+
issuedAt: this.pairingIssuedAt,
|
|
259
|
+
expiresAt: this.pairingExpiresAt,
|
|
260
|
+
issuers: this.pairingIssuers.map(issuer => ({ ...issuer })),
|
|
261
|
+
...(existing?.redeemedAt ? { redeemedAt: existing.redeemedAt } : {}),
|
|
262
|
+
};
|
|
263
|
+
if (existing)
|
|
264
|
+
Object.assign(existing, record);
|
|
265
|
+
else
|
|
266
|
+
store.pairingIssuances.push(record);
|
|
267
|
+
saveStore(store);
|
|
268
|
+
}
|
|
124
269
|
issueLocalToken(label, now = Date.now()) {
|
|
125
270
|
const token = crypto.randomBytes(32).toString('hex');
|
|
126
271
|
const store = loadStore();
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import { loadAgent } from '../config-store.js';
|
|
3
|
+
import { resolveAgentLifecycle } from '../config/lifecycle.js';
|
|
2
4
|
import { agentMdPath, resolvePaths } from '../paths.js';
|
|
3
5
|
import { parseAgentDisplayName, resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
4
|
-
import { generateWelcomeMessage } from '../utils/welcome.js';
|
|
6
|
+
import { generateWelcomeMessage, parseAgentWelcomeMetadata, readAgentWelcomeTemplate, renderPostBootstrapWelcomeMessage, } from '../utils/welcome.js';
|
|
5
7
|
import * as outbox from '../aun/outbox.js';
|
|
6
8
|
import { chatDirPath } from './session/session-fs-store.js';
|
|
7
|
-
import { hasMessageLogOperation } from './message/message-log.js';
|
|
9
|
+
import { hasMessageLogOperation, findMessageLogOperationMessageId } from './message/message-log.js';
|
|
8
10
|
import { isDeliveryTarget, sameDeliveryTarget } from './message/message-utils.js';
|
|
9
11
|
export const BOOTSTRAP_MESSAGE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
10
12
|
export const BOOTSTRAP_NOTIFICATION_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
@@ -32,10 +34,27 @@ export function isPostBootstrapWelcomeOperationId(operationId, aid) {
|
|
|
32
34
|
export function renderPostBootstrapWelcome(aid, owner, ownerName) {
|
|
33
35
|
const mdPath = agentMdPath(aid);
|
|
34
36
|
const agentMd = fs.existsSync(mdPath) ? fs.readFileSync(mdPath, 'utf-8') : '';
|
|
35
|
-
const
|
|
37
|
+
const metadata = parseAgentWelcomeMetadata(agentMd);
|
|
38
|
+
const agentName = metadata.name || parseAgentDisplayName(agentMd)
|
|
36
39
|
|| resolveAgentDisplayName(aid)
|
|
37
40
|
|| aid.split('.')[0];
|
|
38
41
|
const fallbackOwnerName = owner.replace(/^@/, '').split('.')[0];
|
|
42
|
+
const template = readAgentWelcomeTemplate(aid);
|
|
43
|
+
if (template) {
|
|
44
|
+
return renderPostBootstrapWelcomeMessage({
|
|
45
|
+
template,
|
|
46
|
+
agentName,
|
|
47
|
+
agentDescription: metadata.description,
|
|
48
|
+
agentAid: aid,
|
|
49
|
+
agentTags: metadata.tags,
|
|
50
|
+
ownerName: ownerName || fallbackOwnerName,
|
|
51
|
+
ownerAid: owner,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const config = loadAgent(aid);
|
|
55
|
+
if (config && resolveAgentLifecycle(config) === 'bootstrapping' && config.initialization?.mode !== 'supplied') {
|
|
56
|
+
throw new Error('WELCOME_REQUIRED: Owner-confirmed welcome.md is required before activation');
|
|
57
|
+
}
|
|
39
58
|
return generateWelcomeMessage({
|
|
40
59
|
channelType: 'aun',
|
|
41
60
|
agentName,
|
|
@@ -49,15 +68,18 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName, deliver
|
|
|
49
68
|
const legacyOperationId = postBootstrapWelcomeOperationId(aid);
|
|
50
69
|
if (hasMessageLogOperation(chatDir, operationId) || hasMessageLogOperation(chatDir, legacyOperationId))
|
|
51
70
|
return;
|
|
52
|
-
const legacy = outbox.findByDedupeKey(aid, legacyOperationId);
|
|
71
|
+
const legacy = outbox.findByDedupeKey(aid, legacyOperationId, { includeTerminal: true });
|
|
53
72
|
if (legacy?.channelId === owner)
|
|
54
73
|
return;
|
|
55
|
-
const existing = outbox.findByDedupeKey(aid, operationId);
|
|
74
|
+
const existing = outbox.findByDedupeKey(aid, operationId, { includeTerminal: true });
|
|
56
75
|
if (existing
|
|
57
76
|
&& existing.channelId === owner
|
|
58
77
|
&& isDeliveryTarget(existing.delivery)
|
|
59
78
|
&& sameDeliveryTarget(existing.delivery, delivery))
|
|
60
79
|
return;
|
|
80
|
+
// Render before touching an existing route record: malformed content must not
|
|
81
|
+
// remove the only durable record of an earlier attempt.
|
|
82
|
+
const text = renderPostBootstrapWelcome(aid, owner, ownerName);
|
|
61
83
|
if (existing)
|
|
62
84
|
outbox.remove(aid, existing.id);
|
|
63
85
|
outbox.enqueue(aid, {
|
|
@@ -66,7 +88,7 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName, deliver
|
|
|
66
88
|
dedupeKey: operationId,
|
|
67
89
|
critical: true,
|
|
68
90
|
type: 'text',
|
|
69
|
-
text
|
|
91
|
+
text,
|
|
70
92
|
ttl: BOOTSTRAP_MESSAGE_TTL_MS,
|
|
71
93
|
context: {
|
|
72
94
|
delivery,
|
|
@@ -79,6 +101,43 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName, deliver
|
|
|
79
101
|
},
|
|
80
102
|
});
|
|
81
103
|
}
|
|
104
|
+
/** Read durable evidence, including failures and already accepted operations. */
|
|
105
|
+
export function postBootstrapWelcomeStatus(aid, owners) {
|
|
106
|
+
const entries = findPostBootstrapWelcomeOutbox(aid, { includeTerminal: true });
|
|
107
|
+
return {
|
|
108
|
+
deliveries: Array.from(new Set(owners)).map(owner => {
|
|
109
|
+
const operationId = postBootstrapWelcomeOperationId(aid, owner);
|
|
110
|
+
const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', owner, aid);
|
|
111
|
+
const loggedId = findMessageLogOperationMessageId(chatDir, operationId)
|
|
112
|
+
?? findMessageLogOperationMessageId(chatDir, postBootstrapWelcomeOperationId(aid));
|
|
113
|
+
const entry = entries.find(candidate => candidate.channelId === owner
|
|
114
|
+
&& candidate.delivery?.chatType === 'private'
|
|
115
|
+
&& (candidate.dedupeKey === operationId || candidate.dedupeKey === postBootstrapWelcomeOperationId(aid)));
|
|
116
|
+
const base = { owner, operationId, ...(entry ? { outboxId: entry.id } : {}) };
|
|
117
|
+
if (loggedId)
|
|
118
|
+
return { ...base, status: 'sent', messageId: loggedId };
|
|
119
|
+
if (!entry)
|
|
120
|
+
return { ...base, status: 'not_prepared' };
|
|
121
|
+
const state = outbox.deliveryState(aid, entry.id);
|
|
122
|
+
if (state.status === 'sent' || state.status === 'failed')
|
|
123
|
+
return { ...base, ...state };
|
|
124
|
+
if (state.status === 'missing')
|
|
125
|
+
return { ...base, status: 'not_prepared' };
|
|
126
|
+
return { ...base, status: 'queued', error: entry.lastError, code: entry.lastErrorCode };
|
|
127
|
+
}),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Supersede unsent setup prompts when the agent becomes active. Keep diagnostic evidence. */
|
|
131
|
+
export function cancelPendingBootstrapPrompts(aid) {
|
|
132
|
+
for (const entry of outbox.findByDedupePrefix(aid, bootstrapInitialMessageOperationPrefix(aid))) {
|
|
133
|
+
if (entry.deliveryReceipt)
|
|
134
|
+
continue;
|
|
135
|
+
outbox.markTerminal(aid, entry.id, {
|
|
136
|
+
code: 'BOOTSTRAP_SUPERSEDED',
|
|
137
|
+
error: 'Bootstrap is complete; the initial prompt is no longer needed',
|
|
138
|
+
}, entry);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
82
141
|
export function hasPendingPostBootstrapWelcomeOutbox(aid) {
|
|
83
142
|
return findPostBootstrapWelcomeOutbox(aid)
|
|
84
143
|
.some(entry => outbox.isDeliveryForChannel(entry.delivery, entry.channelId));
|
|
@@ -92,7 +151,7 @@ export function findPostBootstrapWelcomeOutbox(aid, opts = {}) {
|
|
|
92
151
|
}
|
|
93
152
|
export function pendingPostBootstrapWelcomeOwners(aid) {
|
|
94
153
|
return findPostBootstrapWelcomeOutbox(aid)
|
|
95
|
-
.filter(entry => outbox.isDeliveryForChannel(entry.delivery, entry.channelId))
|
|
154
|
+
.filter(entry => !entry.context?.sessionId && outbox.isDeliveryForChannel(entry.delivery, entry.channelId))
|
|
96
155
|
.map(entry => entry.channelId)
|
|
97
156
|
.filter((owner, index, all) => all.indexOf(owner) === index);
|
|
98
157
|
}
|