evolcore 0.0.11 → 0.0.12

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.
Files changed (102) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/codex-managed-hook.mjs +80 -0
  3. package/dist/agents/claude-runner.js +35 -2
  4. package/dist/agents/codex-app-server-client.js +120 -14
  5. package/dist/agents/codex-runner.js +273 -35
  6. package/dist/agents/ecagent-runner.js +36 -4
  7. package/dist/agents/gemini-runner.js +1 -0
  8. package/dist/aun/aid/client.js +11 -21
  9. package/dist/aun/aid/managed-operation.js +107 -0
  10. package/dist/aun/msg/group.js +81 -12
  11. package/dist/aun/msg/managed-operation.js +804 -0
  12. package/dist/aun/msg/mention-schema.js +20 -0
  13. package/dist/aun/msg/p2p.js +7 -0
  14. package/dist/channels/aun.js +288 -108
  15. package/dist/channels/daemon.js +13 -0
  16. package/dist/cli/agent-command.js +56 -8
  17. package/dist/cli/agent.js +170 -6
  18. package/dist/cli/aun-commands.js +270 -69
  19. package/dist/cli/bench.js +12 -3
  20. package/dist/cli/daemon-commands.js +31 -31
  21. package/dist/cli/fs-command.js +60 -0
  22. package/dist/cli/handoff-command.js +3 -0
  23. package/dist/cli/index.js +30 -2
  24. package/dist/cli/managed-command-guard.js +41 -0
  25. package/dist/cli/model.js +11 -4
  26. package/dist/cli/queue-command.js +11 -1
  27. package/dist/cli/response.js +71 -0
  28. package/dist/cli/restart-monitor.js +4 -18
  29. package/dist/cli/trigger-command.js +4 -0
  30. package/dist/config/builtin-roles.js +4 -0
  31. package/dist/config/contact-book-store.js +11 -3
  32. package/dist/config/contact-request-service.js +161 -15
  33. package/dist/config/mention-mode.js +8 -24
  34. package/dist/core/auth/agent-delegation.js +0 -1
  35. package/dist/core/auth/operation-authorizer.js +30 -7
  36. package/dist/core/auth/operation-catalog.js +187 -25
  37. package/dist/core/bootstrap-messages.js +18 -0
  38. package/dist/core/bootstrap-service.js +63 -4
  39. package/dist/core/capability/providers/claude-capability-provider.js +9 -31
  40. package/dist/core/capability/providers/codex-capability-provider.js +4 -15
  41. package/dist/core/capability/skill-discovery.js +55 -0
  42. package/dist/core/channel-loader.js +1 -1
  43. package/dist/core/command/cli-intent-parser.js +9 -0
  44. package/dist/core/command/command-handler.js +467 -6
  45. package/dist/core/command/connect-menu.js +15 -0
  46. package/dist/core/command/group-menu.js +4 -4
  47. package/dist/core/command/menu-catalog.js +366 -0
  48. package/dist/core/command/menu-handler.js +147 -35
  49. package/dist/core/command/menu-protocol.js +71 -0
  50. package/dist/core/command/slash-gate.js +5 -2
  51. package/dist/core/command/slash-handler.js +41 -21
  52. package/dist/core/daemon-file-cache.js +2 -1
  53. package/dist/core/data-migration.js +9 -2
  54. package/dist/core/evolagent.js +4 -2
  55. package/dist/core/handoff/dispatcher.js +5 -2
  56. package/dist/core/handoff/runtime.js +145 -21
  57. package/dist/core/handoff/store.js +24 -0
  58. package/dist/core/handoff/types.js +1 -0
  59. package/dist/core/message/logical-queue-bridge.js +1 -0
  60. package/dist/core/message/mention-schema.js +126 -0
  61. package/dist/core/message/message-bridge.js +236 -78
  62. package/dist/core/message/message-log.js +1 -0
  63. package/dist/core/message/message-queue.js +1 -0
  64. package/dist/core/message/response-engine.js +54 -36
  65. package/dist/core/permission/ec-command-parser.js +156 -10
  66. package/dist/core/permission/sandbox-runtime.js +22 -1
  67. package/dist/core/permission/tool-policy.js +73 -30
  68. package/dist/core/protected-paths.js +7 -2
  69. package/dist/core/session/session-manager.js +21 -2
  70. package/dist/eck/manifest-engine.js +39 -13
  71. package/dist/index.js +384 -98
  72. package/dist/ipc.js +218 -9
  73. package/dist/response-system/coordinator.js +6 -4
  74. package/dist/response-system/engines/v1/proactive-flow.js +26 -8
  75. package/dist/response-system/modes/single-session/index.js +14 -1
  76. package/dist/response-system/selector.js +6 -5
  77. package/dist/trigger/anomaly-store.js +4 -0
  78. package/dist/trigger/script-executor.js +1 -0
  79. package/dist/utils/codex-app-server-registry.js +90 -0
  80. package/dist/utils/codex-cli.js +5 -1
  81. package/dist/utils/cross-platform.js +15 -0
  82. package/dist/utils/npm-ops.js +5 -12
  83. package/dist/utils/tool-summary.js +11 -0
  84. package/kits/docs/channels/aun.md +1 -1
  85. package/kits/docs/context-assembly.md +2 -2
  86. package/kits/docs/evolcore/agent.md +11 -6
  87. package/kits/docs/evolcore/aid.md +14 -0
  88. package/kits/docs/prompt-loading-architecture.md +1 -2
  89. package/kits/docs/venues/group.md +1 -1
  90. package/kits/eck_manifest.json +0 -12
  91. package/kits/rules/04-relation.md +1 -1
  92. package/kits/rules/05-venue.md +2 -2
  93. package/kits/schemas/single-session.schema.1.json +3 -3
  94. package/kits/schemas/single-session.schema.2.json +3 -3
  95. package/kits/templates/roles/admin.json +22 -0
  96. package/kits/templates/roles/member.json +27 -0
  97. package/kits/templates/roles/visitor.json +33 -3
  98. package/kits/templates/system-fragments/bootstrap.md +1 -1
  99. package/kits/templates/system-fragments/channel.md +1 -1
  100. package/kits/templates/system-fragments/session.md +1 -1
  101. package/package.json +2 -2
  102. package/dist/core/command/evol-menu-version-gate.js +0 -39
@@ -415,7 +415,11 @@ function normalizeExecutionAnomaly(value) {
415
415
  : Date.now();
416
416
  const toolName = nonEmptyString(value.toolName);
417
417
  const requestId = nonEmptyString(value.requestId);
418
+ const policyCode = nonEmptyString(value.policyCode);
418
419
  const policy = normalizeApproverPolicy(value.policy);
420
+ const agentAid = nonEmptyString(value.agentAid);
421
+ const sessionId = nonEmptyString(value.sessionId);
422
+ const permissionMode = normalizePermissionMode(value.permissionMode);
419
423
  const summary = nonEmptyString(value.summary);
420
424
  return {
421
425
  code: code,
@@ -425,7 +429,11 @@ function normalizeExecutionAnomaly(value) {
425
429
  effect,
426
430
  ...(toolName ? { toolName } : {}),
427
431
  ...(requestId ? { requestId } : {}),
432
+ ...(policyCode ? { policyCode } : {}),
428
433
  ...(policy ? { policy } : {}),
434
+ ...(agentAid ? { agentAid } : {}),
435
+ ...(sessionId ? { sessionId } : {}),
436
+ ...(permissionMode ? { permissionMode } : {}),
429
437
  ...(summary ? { summary: summary.slice(0, 512) } : {}),
430
438
  };
431
439
  }
@@ -437,3 +445,8 @@ function normalizeApproverPolicy(value) {
437
445
  ? value
438
446
  : undefined;
439
447
  }
448
+ function normalizePermissionMode(value) {
449
+ return value === 'readonly' || value === 'auto' || value === 'request' || value === 'bypass'
450
+ ? value
451
+ : undefined;
452
+ }
@@ -59,6 +59,13 @@ Options:
59
59
  ec agent delete mybot.agentid.pub --purge`);
60
60
  return;
61
61
  }
62
+ if (process.env.EVOLCORE_SESSION_ID && !wantsHelp(args) && !new Set(['get', 'set', 'ready']).has(sub)) {
63
+ if (formatJson)
64
+ console.log(JSON.stringify({ ok: false, code: 'NOT_ALLOWED', error: `agent ${sub} is not available in a managed task` }));
65
+ else
66
+ console.error(`❌ [NOT_ALLOWED] agent ${sub} is not available in a managed task`);
67
+ process.exit(1);
68
+ }
62
69
  const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentRename, agentDelete, agentReady, } = await import('./agent.js');
63
70
  // --- list ---
64
71
  if (sub === 'list') {
@@ -137,17 +144,21 @@ Options:
137
144
  ec agent new <aid> --non-interactive [选项]
138
145
 
139
146
  非交互模式选项:
140
- --baseagent <claude|codex|gemini|ecagent> 默认: PATH 中第一个可用;ecagent 需显式选择
141
- --project <absolute path> 必填
142
- --owner <aid>
143
- --name <display-name>
144
- --description <text>
147
+ --project <path> 默认: 当前目录
148
+ --baseagent <claude|codex|gemini|ecagent>
149
+ 默认: defaults 配置,否则检测本机可用基座
150
+ --owner <aid> 默认: daemon.json 第一个 owner
151
+ --name <display-name> 默认: package.json name,否则目录名
152
+ --description <text> 默认: package.json description,否则留空
153
+ --dry-run 只预览最终参数,不注册 AID、不写文件
145
154
  --force 覆盖已有 config.json
146
155
  --format json 输出 JSON
147
156
 
148
157
  示例:
149
158
  ec agent new mybot.agentid.pub
150
- ec agent new mybot.agentid.pub --non-interactive --project /abs/path --baseagent claude`);
159
+ ec agent new mybot.agentid.pub --non-interactive
160
+ ec agent new mybot.agentid.pub --non-interactive --dry-run --format json
161
+ ec agent new mybot.agentid.pub --non-interactive --project ../project --baseagent claude`);
151
162
  return;
152
163
  }
153
164
  const name = args[1];
@@ -161,15 +172,52 @@ Options:
161
172
  const idx = args.indexOf(flag);
162
173
  return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
163
174
  };
164
- const result = await agentCreateNonInteractive({
175
+ const { resolveAgentCreatePlan, validateAgentCreatePlan } = await import('./agent.js');
176
+ const plan = resolveAgentCreatePlan({
165
177
  aid: name,
178
+ project: getArg('--project'),
166
179
  baseagent: getArg('--baseagent'),
167
- project: getArg('--project') || '',
168
180
  owner: getArg('--owner'),
169
181
  name: getArg('--name'),
170
182
  description: getArg('--description'),
171
183
  force: args.includes('--force'),
172
184
  });
185
+ const validationError = validateAgentCreatePlan(plan);
186
+ if (validationError) {
187
+ const error = { ok: false, error: validationError };
188
+ if (formatJson)
189
+ console.log(JSON.stringify(error));
190
+ else
191
+ console.error(`❌ ${error.error}`);
192
+ process.exitCode = 1;
193
+ return;
194
+ }
195
+ if (args.includes('--dry-run')) {
196
+ const preview = { ok: true, dryRun: true, plan };
197
+ if (formatJson)
198
+ console.log(JSON.stringify(preview, null, 2));
199
+ else {
200
+ console.log('EvolAgent 创建计划(dry-run)');
201
+ console.log(` AID: ${plan.aid}`);
202
+ console.log(` Project: ${plan.project}`);
203
+ console.log(` Baseagent: ${plan.baseagent} (${plan.baseagentSource})`);
204
+ console.log(` Owner: ${plan.owner || '未设置'} (${plan.ownerSource})`);
205
+ console.log(` Name: ${plan.name} (${plan.nameSource})`);
206
+ if (plan.description)
207
+ console.log(` Description: ${plan.description} (${plan.descriptionSource})`);
208
+ console.log(` Existing config: ${plan.configExists ? (plan.force ? '将覆盖' : '是') : '否'}`);
209
+ }
210
+ return;
211
+ }
212
+ const result = await agentCreateNonInteractive({
213
+ aid: plan.aid,
214
+ baseagent: plan.baseagent,
215
+ project: plan.project,
216
+ owner: plan.owner,
217
+ name: plan.name,
218
+ description: plan.description,
219
+ force: args.includes('--force'),
220
+ });
173
221
  if (!result.ok) {
174
222
  if (formatJson) {
175
223
  console.log(JSON.stringify(result));
package/dist/cli/agent.js CHANGED
@@ -2,22 +2,25 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import readline from 'readline';
4
4
  import { resolvePaths, agentMdPath as getAgentMdPathFromPaths, aunPath as defaultAunPath } from '../paths.js';
5
- import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton } from '../config-store.js';
5
+ import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton, loadDaemonConfig } from '../config-store.js';
6
6
  import { ConfigTarget, write as cfgWrite, ensureFile as cfgEnsure, read as cfgRead, initConfigManager } from '../config/config-manager.js';
7
7
  import { resolveConfigCommand } from '../config/resolved-config-op.js';
8
8
  import { executeResolvedConfigCommand } from '../config/config-operation-service.js';
9
9
  import { ipcQuery } from '../ipc.js';
10
10
  import { CONFIG_SCHEMA_VERSION } from '../types.js';
11
11
  import { normalizeAgentLifecycle, withLifecycleForWrite } from '../config/lifecycle.js';
12
- import { preparePostBootstrapWelcomeOutbox } from '../core/bootstrap-messages.js';
12
+ import { bindPostBootstrapWelcomeOutboxSession, hasPendingPostBootstrapWelcomeOutbox, postBootstrapWelcomeOperationId, preparePostBootstrapWelcomeOutbox, } from '../core/bootstrap-messages.js';
13
13
  import { ensureInitialRoleRegistry } from '../config/role-service.js';
14
- import { isValidChannelName } from '../core/channel-loader.js';
14
+ import { formatChannelKey, isValidChannelName } from '../core/channel-loader.js';
15
15
  import { commandExists } from '../utils/cross-platform.js';
16
16
  import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
17
17
  import { resolveEcagentConfig } from '../agents/baseagent.js';
18
18
  import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
19
19
  import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
20
20
  import { readTaskRuntimeContextFromEnv } from './task-context.js';
21
+ import { EventBus } from '../core/event-bus.js';
22
+ import { SessionManager } from '../core/session/session-manager.js';
23
+ import { isValidAid } from '../aun/aid/validation.js';
21
24
  function withStaticOwner(config, owner) {
22
25
  const value = owner?.trim();
23
26
  if (!value)
@@ -43,6 +46,100 @@ function detectAvailableBaseagents() {
43
46
  function pickDefaultBaseagent(available) {
44
47
  return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate)) ?? null;
45
48
  }
49
+ function readProjectMetadata(project) {
50
+ try {
51
+ const packageJsonPath = path.join(project, 'package.json');
52
+ if (!fs.existsSync(packageJsonPath))
53
+ return {};
54
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
55
+ return {
56
+ name: typeof parsed.name === 'string' ? parsed.name.trim() : undefined,
57
+ description: typeof parsed.description === 'string' ? parsed.description.trim() : undefined,
58
+ };
59
+ }
60
+ catch {
61
+ return {};
62
+ }
63
+ }
64
+ function projectSlug(project) {
65
+ const basename = path.basename(project) || 'agent';
66
+ const slug = basename
67
+ .normalize('NFKD')
68
+ .replace(/[^a-zA-Z0-9-]+/g, '-')
69
+ .replace(/^-+|-+$/g, '')
70
+ .toLowerCase()
71
+ .slice(0, 63)
72
+ .replace(/-+$/g, '');
73
+ return slug || 'agent';
74
+ }
75
+ /**
76
+ * Resolve non-interactive creation defaults without performing any network or
77
+ * filesystem mutation. The CLI uses this for both execution and --dry-run.
78
+ */
79
+ export function resolveAgentCreatePlan(options) {
80
+ const aid = options.aid.trim();
81
+ const project = path.resolve(options.project || process.cwd());
82
+ const metadata = readProjectMetadata(project);
83
+ const defaults = loadDefaults();
84
+ const daemon = loadDaemonConfig();
85
+ const configuredBaseagent = defaults?.active_baseagent;
86
+ const available = detectAvailableBaseagents();
87
+ const defaultBaseagent = configuredBaseagent && available.includes(configuredBaseagent)
88
+ ? configuredBaseagent
89
+ : pickDefaultBaseagent(available) ?? undefined;
90
+ const baseagent = options.baseagent || defaultBaseagent;
91
+ const baseagentSource = options.baseagent
92
+ ? 'argument'
93
+ : configuredBaseagent && baseagent === configuredBaseagent
94
+ ? 'defaults'
95
+ : 'detected';
96
+ const owner = options.owner?.trim() || daemon.owners?.[0];
97
+ const ownerSource = options.owner?.trim() ? 'argument' : owner ? 'daemon' : 'none';
98
+ const name = options.name?.trim() || metadata.name || projectSlug(project);
99
+ const description = options.description?.trim() || metadata.description || '';
100
+ const configExists = isValidAid(aid)
101
+ && fs.existsSync(path.join(resolvePaths().agentsDir, aid, 'config.json'));
102
+ return {
103
+ aid,
104
+ project,
105
+ baseagent,
106
+ owner,
107
+ name,
108
+ description,
109
+ force: options.force === true,
110
+ configExists,
111
+ baseagentSource,
112
+ ownerSource,
113
+ nameSource: options.name?.trim() ? 'argument' : metadata.name ? 'package' : 'directory',
114
+ descriptionSource: options.description?.trim() ? 'argument' : metadata.description ? 'package' : 'none',
115
+ };
116
+ }
117
+ export function validateAgentCreatePlan(plan) {
118
+ if (!isValidAid(plan.aid)) {
119
+ return `Invalid AID "${plan.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`;
120
+ }
121
+ if (plan.configExists && !plan.force) {
122
+ return `Agent "${plan.aid}" already exists (use --force to overwrite)`;
123
+ }
124
+ if (!plan.baseagent) {
125
+ return 'No usable baseagent detected. Pass --baseagent or configure a baseagent in agents/defaults.json.';
126
+ }
127
+ if (!BASEAGENT_CANDIDATES.includes(plan.baseagent)) {
128
+ return `Invalid baseagent: ${plan.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})`;
129
+ }
130
+ if (!isBaseagentAvailable(plan.baseagent)) {
131
+ return `${plan.baseagent} is not available in the current environment`;
132
+ }
133
+ if (plan.baseagent === 'ecagent') {
134
+ const reason = ecagentUnavailableReason();
135
+ if (reason)
136
+ return reason;
137
+ }
138
+ if (plan.owner && !isValidAid(plan.owner)) {
139
+ return `Invalid owner: ${plan.owner}`;
140
+ }
141
+ return null;
142
+ }
46
143
  function ecagentUnavailableReason() {
47
144
  try {
48
145
  resolveEcagentConfig({ agents: { ecagent: {} } });
@@ -1099,6 +1196,31 @@ export async function agentRename(aid, name) {
1099
1196
  return { ok: true, aid, name };
1100
1197
  }
1101
1198
  // ==================== agentReady ====================
1199
+ async function ensureOfflinePostBootstrapMainSession(aid, config) {
1200
+ const owner = config.owners?.[0];
1201
+ if (!owner)
1202
+ throw new Error(`Agent "${aid}" has no owner for the post-bootstrap main session`);
1203
+ const p = resolvePaths();
1204
+ const aunName = config.channels?.find(channel => channel.type === 'aun')?.name || 'main';
1205
+ const channel = formatChannelKey({ type: 'aun', selfAID: aid, name: aunName });
1206
+ const manager = new SessionManager(p.sessionsDir, new EventBus());
1207
+ const active = await manager.getActiveSession(channel, owner);
1208
+ const ensured = await manager.ensureNewMainSessionForOperation({
1209
+ channel,
1210
+ channelId: owner,
1211
+ projectPath: active?.projectPath
1212
+ || config.projects?.defaultPath
1213
+ || path.join(p.root, 'projects', aid.split('.')[0]),
1214
+ baseagent: config.active_baseagent || active?.baseagent || 'claude',
1215
+ operationId: postBootstrapWelcomeOperationId(aid),
1216
+ identityMetadata: {
1217
+ ...(active?.metadata ?? {}),
1218
+ channelKey: channel,
1219
+ peerId: owner,
1220
+ },
1221
+ });
1222
+ return ensured.session.id;
1223
+ }
1102
1224
  export async function agentReady(aid) {
1103
1225
  const taskSelfAid = readTaskRuntimeContextFromEnv()?.selfAid;
1104
1226
  if (taskSelfAid && taskSelfAid.replace(/^@/, '') !== aid.replace(/^@/, '')) {
@@ -1109,17 +1231,55 @@ export async function agentReady(aid) {
1109
1231
  };
1110
1232
  }
1111
1233
  const p = resolvePaths();
1234
+ const managedSessionId = process.env.EVOLCORE_SESSION_ID;
1235
+ if (managedSessionId) {
1236
+ const delegationToken = process.env[AGENT_DELEGATION_TOKEN_ENV];
1237
+ if (!delegationToken)
1238
+ return { ok: false, code: 'DELEGATION_REQUIRED', error: `${AGENT_DELEGATION_TOKEN_ENV} is not set for the active task` };
1239
+ const result = await ipcQuery(p.socket, {
1240
+ type: 'evolagent.bootstrapComplete',
1241
+ aid,
1242
+ sessionId: managedSessionId,
1243
+ delegationToken,
1244
+ }, 30_000);
1245
+ if (!result)
1246
+ return { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' };
1247
+ return result.ok
1248
+ ? { ok: true, aid, reloaded: result.reloaded === true }
1249
+ : { ok: false, code: result.code, error: result.error || 'Failed to complete bootstrap' };
1250
+ }
1112
1251
  const config = loadAgent(aid);
1113
1252
  if (!config)
1114
1253
  return { ok: false, error: `Agent "${aid}" not found` };
1115
1254
  try {
1116
- const result = await ipcQuery(p.socket, { type: 'evolagent.bootstrapComplete', aid }, 30_000);
1255
+ let controlToken;
1256
+ try {
1257
+ controlToken = fs.readFileSync(p.controlToken, 'utf8').trim() || undefined;
1258
+ }
1259
+ catch { }
1260
+ const result = await ipcQuery(p.socket, { type: 'evolagent.bootstrapComplete', aid, controlToken }, 30_000);
1117
1261
  if (!result) {
1118
1262
  const offlineConfig = loadAgent(aid) || config;
1119
1263
  const lifecycle = normalizeAgentLifecycle(offlineConfig).lifecycle;
1120
- if (lifecycle === 'active')
1121
- return { ok: true, aid, reloaded: false };
1122
1264
  if (lifecycle !== 'bootstrapping') {
1265
+ if (lifecycle === 'active') {
1266
+ if (hasPendingPostBootstrapWelcomeOutbox(aid)) {
1267
+ try {
1268
+ const sessionId = await ensureOfflinePostBootstrapMainSession(aid, offlineConfig);
1269
+ if (!bindPostBootstrapWelcomeOutboxSession(aid, sessionId)) {
1270
+ throw new Error(`Failed to bind the completion welcome to session ${sessionId}`);
1271
+ }
1272
+ }
1273
+ catch (e) {
1274
+ return {
1275
+ ok: false,
1276
+ code: 'MAIN_SESSION_CREATE_FAILED',
1277
+ error: e instanceof Error ? e.message : 'Failed to recover the post-bootstrap main session',
1278
+ };
1279
+ }
1280
+ }
1281
+ return { ok: true, aid, reloaded: false };
1282
+ }
1123
1283
  return { ok: false, code: 'INVALID_LIFECYCLE', error: `Agent "${aid}" is ${lifecycle}; only a bootstrapping agent can become ready` };
1124
1284
  }
1125
1285
  const owner = offlineConfig.owners?.[0];
@@ -1127,6 +1287,10 @@ export async function agentReady(aid) {
1127
1287
  return { ok: false, code: 'OWNER_REQUIRED', error: `Agent "${aid}" has no owner for the completion welcome` };
1128
1288
  try {
1129
1289
  preparePostBootstrapWelcomeOutbox(aid, owner);
1290
+ const sessionId = await ensureOfflinePostBootstrapMainSession(aid, offlineConfig);
1291
+ if (!bindPostBootstrapWelcomeOutboxSession(aid, sessionId)) {
1292
+ throw new Error(`Failed to bind the completion welcome to session ${sessionId}`);
1293
+ }
1130
1294
  saveAgent(withLifecycleForWrite(offlineConfig, 'active'));
1131
1295
  }
1132
1296
  catch (e) {