@asm-agent/coding-agent 0.8.8 → 0.9.0

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 (178) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +2 -7
  3. package/dist/asm-agent-runtime/src/__pycache__/asm_cm_driver.cpython-313.pyc +0 -0
  4. package/dist/asm-agent-runtime/src/__pycache__/phase76_address_head.cpython-313.pyc +0 -0
  5. package/dist/asm-agent-runtime/src/rlm/__init__.py +1 -1
  6. package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-312.pyc +0 -0
  7. package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-313.pyc +0 -0
  8. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-312.pyc +0 -0
  9. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-313.pyc +0 -0
  10. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp.cpython-312.pyc +0 -0
  11. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp_base.cpython-312.pyc +0 -0
  12. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-312.pyc +0 -0
  13. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-313.pyc +0 -0
  14. package/dist/asm-agent-runtime/src/rlm/__pycache__/skill.cpython-313.pyc +0 -0
  15. package/dist/asm-agent-runtime/src/rlm/bash.py +94 -31
  16. package/dist/asm-agent-runtime/src/rlm/repl.md +3 -3
  17. package/dist/asm-agent-runtime/src/rlm/repl.py +46 -45
  18. package/dist/asm-agent-runtime/test/__pycache__/test_bash.cpython-313.pyc +0 -0
  19. package/dist/asm-agent-runtime/test/__pycache__/test_repl.cpython-313.pyc +0 -0
  20. package/dist/asm-agent-runtime/test/test_bash.py +74 -19
  21. package/dist/asm-agent-runtime/test/test_repl.py +67 -3
  22. package/dist/asm-agent-runtime/uv.lock +446 -155
  23. package/dist/bundle/{anthropic-OFECORE7.js → anthropic-ZOZNPFXS.js} +2 -2
  24. package/dist/bundle/{azure-openai-responses-AOVTLR6N.js → azure-openai-responses-TDHCU647.js} +2 -2
  25. package/dist/bundle/{bundled-modules-JQCJDEIY.js → bundled-modules-Q2YODC4B.js} +3 -3
  26. package/dist/bundle/{chunk-M67RGWDQ.js → chunk-AMKLNEAK.js} +25 -44
  27. package/dist/bundle/{chunk-7TB7CGPV.js → chunk-FPY7FFJS.js} +978 -1821
  28. package/dist/bundle/{chunk-JC2TT4MZ.js → chunk-G6SC4YYW.js} +4942 -1916
  29. package/dist/bundle/{chunk-A65OM3TZ.js → chunk-QLHAP3FT.js} +1 -1
  30. package/dist/bundle/{cli-main-FNHDQGQD.js → cli-main-277AG2BR.js} +3 -3
  31. package/dist/bundle/cli.js +1 -1
  32. package/dist/bundle/{google-RVJL6RIQ.js → google-Q3AJWUOY.js} +1 -1
  33. package/dist/bundle/{google-vertex-EZTRRRH6.js → google-vertex-2CSNOUBS.js} +1 -1
  34. package/dist/bundle/{main-IYXIIAJN.js → main-TNXWJXIU.js} +3 -3
  35. package/dist/bundle/{mistral-FSGKSMA7.js → mistral-XRYJGLLG.js} +1 -1
  36. package/dist/bundle/{openai-codex-responses-VM5Y564D.js → openai-codex-responses-C6BRO2BN.js} +2 -2
  37. package/dist/bundle/{openai-completions-X46FOQDN.js → openai-completions-DNXCK2JW.js} +3 -3
  38. package/dist/bundle/{openai-responses-SOQNEPBE.js → openai-responses-BEMWVIXE.js} +2 -2
  39. package/dist/config.d.ts +0 -1
  40. package/dist/config.d.ts.map +1 -1
  41. package/dist/config.js +0 -3
  42. package/dist/config.js.map +1 -1
  43. package/dist/core/agent-session-config.d.ts +1 -2
  44. package/dist/core/agent-session-config.d.ts.map +1 -1
  45. package/dist/core/agent-session-config.js +0 -3
  46. package/dist/core/agent-session-config.js.map +1 -1
  47. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  48. package/dist/core/agent-session-runtime.js +0 -8
  49. package/dist/core/agent-session-runtime.js.map +1 -1
  50. package/dist/core/agent-session-services.d.ts +0 -2
  51. package/dist/core/agent-session-services.d.ts.map +1 -1
  52. package/dist/core/agent-session-services.js +0 -22
  53. package/dist/core/agent-session-services.js.map +1 -1
  54. package/dist/core/agent-session.d.ts +8 -1
  55. package/dist/core/agent-session.d.ts.map +1 -1
  56. package/dist/core/agent-session.js +100 -22
  57. package/dist/core/agent-session.js.map +1 -1
  58. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  59. package/dist/core/kernel/bootstrap.js +1 -1
  60. package/dist/core/kernel/bootstrap.js.map +1 -1
  61. package/dist/core/kernel/repl-manager.d.ts +32 -8
  62. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  63. package/dist/core/kernel/repl-manager.js +401 -102
  64. package/dist/core/kernel/repl-manager.js.map +1 -1
  65. package/dist/core/kernel/shared.d.ts +11 -7
  66. package/dist/core/kernel/shared.d.ts.map +1 -1
  67. package/dist/core/kernel/shared.js +2 -5
  68. package/dist/core/kernel/shared.js.map +1 -1
  69. package/dist/core/mcp/mcp-manager.d.ts +4 -2
  70. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  71. package/dist/core/mcp/mcp-manager.js +9 -7
  72. package/dist/core/mcp/mcp-manager.js.map +1 -1
  73. package/dist/core/prime-inference-auth.d.ts +0 -9
  74. package/dist/core/prime-inference-auth.d.ts.map +1 -1
  75. package/dist/core/prime-inference-auth.js +0 -50
  76. package/dist/core/prime-inference-auth.js.map +1 -1
  77. package/dist/core/provider-display-names.d.ts.map +1 -1
  78. package/dist/core/provider-display-names.js +0 -1
  79. package/dist/core/provider-display-names.js.map +1 -1
  80. package/dist/core/session-manager.d.ts +0 -4
  81. package/dist/core/session-manager.d.ts.map +1 -1
  82. package/dist/core/session-manager.js +0 -22
  83. package/dist/core/session-manager.js.map +1 -1
  84. package/dist/core/settings-manager.d.ts +4 -16
  85. package/dist/core/settings-manager.d.ts.map +1 -1
  86. package/dist/core/settings-manager.js +13 -43
  87. package/dist/core/settings-manager.js.map +1 -1
  88. package/dist/core/slash-commands.d.ts.map +1 -1
  89. package/dist/core/slash-commands.js +0 -5
  90. package/dist/core/slash-commands.js.map +1 -1
  91. package/dist/core/tools/acp-mcp.d.ts +6 -0
  92. package/dist/core/tools/acp-mcp.d.ts.map +1 -0
  93. package/dist/core/tools/acp-mcp.js +86 -0
  94. package/dist/core/tools/acp-mcp.js.map +1 -0
  95. package/dist/core/tools/index.d.ts +1 -0
  96. package/dist/core/tools/index.d.ts.map +1 -1
  97. package/dist/core/tools/index.js +1 -0
  98. package/dist/core/tools/index.js.map +1 -1
  99. package/dist/core/tools/ipython.d.ts.map +1 -1
  100. package/dist/core/tools/ipython.js +9 -4
  101. package/dist/core/tools/ipython.js.map +1 -1
  102. package/dist/main.d.ts.map +1 -1
  103. package/dist/main.js +2 -11
  104. package/dist/main.js.map +1 -1
  105. package/dist/modes/acp/acp-mode.d.ts.map +1 -1
  106. package/dist/modes/acp/acp-mode.js +1 -1
  107. package/dist/modes/acp/acp-mode.js.map +1 -1
  108. package/dist/modes/agent-connection/daemon-agent-connection.d.ts +0 -2
  109. package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
  110. package/dist/modes/agent-connection/daemon-agent-connection.js +0 -2
  111. package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
  112. package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
  113. package/dist/modes/agents-view/agents-view-mode.js +0 -19
  114. package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
  115. package/dist/modes/daemon/daemon-mode.d.ts +1 -10
  116. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  117. package/dist/modes/daemon/daemon-mode.js +134 -274
  118. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  119. package/dist/modes/daemon/daemon-protocol.d.ts +11 -5
  120. package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
  121. package/dist/modes/daemon/daemon-protocol.js +6 -8
  122. package/dist/modes/daemon/daemon-protocol.js.map +1 -1
  123. package/dist/modes/daemon/daemon-supervisor.d.ts +0 -3
  124. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  125. package/dist/modes/daemon/daemon-supervisor.js +18 -52
  126. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  127. package/dist/modes/daemon/daemon-worker-protocol.d.ts +1 -6
  128. package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
  129. package/dist/modes/daemon/daemon-worker-protocol.js +0 -2
  130. package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
  131. package/dist/modes/interactive/auth-flows.d.ts +0 -2
  132. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  133. package/dist/modes/interactive/auth-flows.js +1 -101
  134. package/dist/modes/interactive/auth-flows.js.map +1 -1
  135. package/dist/modes/interactive/components/assistant-message.d.ts +6 -1
  136. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  137. package/dist/modes/interactive/components/assistant-message.js +13 -3
  138. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  139. package/dist/modes/interactive/components/footer.d.ts +1 -1
  140. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  141. package/dist/modes/interactive/components/footer.js +2 -2
  142. package/dist/modes/interactive/components/footer.js.map +1 -1
  143. package/dist/modes/interactive/components/mermaid.d.ts +12 -0
  144. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  145. package/dist/modes/interactive/components/mermaid.js +63 -0
  146. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  147. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  148. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  149. package/dist/modes/interactive/components/settings-selector.js +10 -0
  150. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  151. package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
  152. package/dist/modes/interactive/feature-hints.js +0 -4
  153. package/dist/modes/interactive/feature-hints.js.map +1 -1
  154. package/dist/modes/interactive/interactive-mode.d.ts +1 -7
  155. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  156. package/dist/modes/interactive/interactive-mode.js +23 -279
  157. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  158. package/dist/utils/version-check.d.ts.map +1 -1
  159. package/dist/utils/version-check.js +1 -1
  160. package/dist/utils/version-check.js.map +1 -1
  161. package/docs/settings.md +1 -31
  162. package/docs/usage.md +1 -4
  163. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  164. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  165. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  166. package/examples/extensions/sandbox/package-lock.json +2 -2
  167. package/examples/extensions/sandbox/package.json +1 -1
  168. package/examples/extensions/with-deps/package-lock.json +2 -2
  169. package/examples/extensions/with-deps/package.json +1 -1
  170. package/package.json +6 -5
  171. package/dist/core/agent-traces.d.ts +0 -130
  172. package/dist/core/agent-traces.d.ts.map +0 -1
  173. package/dist/core/agent-traces.js +0 -746
  174. package/dist/core/agent-traces.js.map +0 -1
  175. package/dist/core/telemetry.d.ts +0 -92
  176. package/dist/core/telemetry.d.ts.map +0 -1
  177. package/dist/core/telemetry.js +0 -633
  178. package/dist/core/telemetry.js.map +0 -1
@@ -14,7 +14,7 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
14
14
  import { getLogger } from "@asm-agent/ai";
15
15
  import { createCliSubprocessEnv, createCliSubprocessLaunchSpec } from "../../cli/subprocess-launch.js";
16
16
  import { appendRotatingLog, getCronJobsPath, getDaemonLogPath, getDaemonUpdateRestartManifestPath, getSessionsDir, VERSION, } from "../../config.js";
17
- import { AGENT_FAMILY_REACH_ERROR, AGENT_MESSAGE_SOURCE, AgentSessionMessageRateLimiter, agentFamilyRelationship, assertAgentFamilyReach, assertAgentMessageQueueCapacity, assertAgentSessionNameAvailable, assertDirectAgentMessageTarget, buildAgentFamilyRoster, createAgentSessionMessage, createAgentSessionMessageId, createAgentSessionMessageReceipt, DEFAULT_AGENT_MESSAGE_MAX_CHARS, DEFAULT_AGENT_MESSAGE_MAX_PENDING_PER_SESSION, DEFAULT_AGENT_MESSAGE_RATE_LIMIT_CAPACITY, DEFAULT_AGENT_MESSAGE_RATE_LIMIT_REFILL_MS, formatAgentSessionNameUnavailable, isAgentSessionMessagePrompt, normalizeAgentSessionMessage, sessionNameReservationKey, } from "../../core/agent-messages.js";
17
+ import { AGENT_FAMILY_REACH_ERROR, AGENT_MESSAGE_SOURCE, AgentSessionMessageRateLimiter, agentFamilyRelationship, assertAgentFamilyReach, assertAgentSessionNameAvailable, assertDirectAgentMessageTarget, buildAgentFamilyRoster, createAgentSessionMessage, createAgentSessionMessageId, createAgentSessionMessageReceipt, DEFAULT_AGENT_MESSAGE_MAX_CHARS, DEFAULT_AGENT_MESSAGE_MAX_PENDING_PER_SESSION, DEFAULT_AGENT_MESSAGE_RATE_LIMIT_CAPACITY, DEFAULT_AGENT_MESSAGE_RATE_LIMIT_REFILL_MS, formatAgentSessionNameUnavailable, normalizeAgentSessionMessage, sessionNameReservationKey, } from "../../core/agent-messages.js";
18
18
  import { createAgentObserveMessagePreview, normalizeObserveLimit, normalizeObserveMaxChars, } from "../../core/agent-observe.js";
19
19
  import { rlmChildLabel } from "../../core/agent-session.js";
20
20
  import { mergeAgentSessionRuntimeConfig } from "../../core/agent-session-config.js";
@@ -245,13 +245,6 @@ export class AgentDaemon {
245
245
  agentDir;
246
246
  cronScheduler;
247
247
  agentMessageRateLimiter = new AgentSessionMessageRateLimiter();
248
- remoteAgentPeers = new Map();
249
- agentMessagePendingReservations = new Map();
250
- agentMessageTargetLocks = new Map();
251
- agentMessageAcceptingTargets = new Set();
252
- // Refcount of prompts in preflight (accepted but not yet streaming); >0 makes
253
- // concurrent agent messages queue instead of racing agent.prompt.
254
- agentMessagePreparingTargets = new Map();
255
248
  asmCmDriver = new AsmCmDriver();
256
249
  phase76AddressHead = new Phase76AddressHeadDriver();
257
250
  asmRuntimePersistence = new AsmRuntimePersistence();
@@ -1368,16 +1361,10 @@ export class AgentDaemon {
1368
1361
  return "skipped";
1369
1362
  }
1370
1363
  const session = state.runtime.session;
1371
- const isAgentMessagePromptInProgress = this.agentMessageAcceptingTargets.has(state.activeSessionId) ||
1372
- this.agentMessagePreparingTargets.has(state.activeSessionId);
1373
- if (isHeartbeatCronJob(runnableJob) && isAgentMessagePromptInProgress) {
1374
- return "skipped";
1375
- }
1376
1364
  if (shouldDeferHeartbeatCronJob(runnableJob, session)) {
1377
1365
  return "skipped";
1378
1366
  }
1379
- const shouldQueueCronPrompt = isAgentMessagePromptInProgress ||
1380
- session.isStreaming ||
1367
+ const shouldQueueCronPrompt = session.isStreaming ||
1381
1368
  session.isCompacting ||
1382
1369
  session.isRetrying ||
1383
1370
  session.isBashRunning ||
@@ -1395,104 +1382,44 @@ export class AgentDaemon {
1395
1382
  const current = requirePersistedJob ? this.getRunnableCronJob(job.id) : runnableJob;
1396
1383
  return current && this.isCronJobRunnableForState(current, state, requirePersistedJob) ? current : undefined;
1397
1384
  };
1398
- if (isHeartbeatCronJob(runnableJob)) {
1399
- const streamingBehavior = resolveHeartbeatStreamingBehavior(runnableJob.deliveryMode);
1400
- const didPrompt = await this.promptHeartbeatWithAgentMessagePreparingGuard(state, runnableJob, {
1401
- streamingBehavior,
1402
- followUpQueueKey: `heartbeat:${runnableJob.id}`,
1403
- source: "rpc",
1404
- }, getRunnableJob);
1405
- return didPrompt ? undefined : "skipped";
1385
+ const current = getRunnableJob();
1386
+ if (!current) {
1387
+ return "skipped";
1406
1388
  }
1407
- const canPrompt = () => getRunnableJob() !== undefined;
1408
- const didPrompt = await this.promptWithAgentMessagePreparingGuard(state, runnableJob.prompt, {
1409
- streamingBehavior: "followUp",
1410
- source: "rpc",
1411
- }, canPrompt, false);
1412
- return didPrompt ? undefined : "skipped";
1413
- }
1414
- getRunnableCronJob(jobId) {
1415
- return this.cronStore.getClaimedJob(jobId) ?? this.cronStore.getDueJob(jobId);
1416
- }
1417
- // Wraps session.prompt so the target counts as "preparing" until the turn
1418
- // starts streaming (or the prompt settles); concurrent agent messages queue
1419
- // instead of racing agent.prompt during the preflight awaits. Refcounted so
1420
- // overlapping prompts each hold their own registration.
1421
- async promptWithAgentMessagePreparingGuard(state, message, options, canPrompt, waitForCompletion = true) {
1422
- return this.withAgentMessagePreparingGuard(state, (session, clearPreparing) => {
1423
- const prompt = options?.agentMessageId !== undefined && options.expandPromptTemplates === false
1424
- ? session.acceptAgentMessagePrompt.bind(session)
1425
- : waitForCompletion
1426
- ? (session.promptAndWait ?? session.prompt).bind(session)
1427
- : (session.promptUntilAccepted ?? session.prompt).bind(session);
1428
- return prompt(message, {
1429
- ...options,
1430
- preflightResult: (didSucceed, queued) => {
1431
- if (didSucceed && session.isStreaming) {
1432
- clearPreparing();
1433
- }
1434
- options?.preflightResult?.(didSucceed, queued);
1435
- },
1436
- });
1437
- }, canPrompt, options?.signal);
1438
- }
1439
- async promptHeartbeatWithAgentMessagePreparingGuard(state, job, options, getPromptJob) {
1440
- let promptJob = job;
1441
- return this.withAgentMessagePreparingGuard(state, (session, clearPreparing) => session.promptHeartbeat(promptJob, {
1442
- ...options,
1443
- preflightResult: (didSucceed) => {
1444
- if (didSucceed && session.isStreaming) {
1445
- clearPreparing();
1446
- }
1447
- options?.preflightResult?.(didSucceed);
1448
- },
1449
- }), () => {
1450
- if (!getPromptJob) {
1451
- return true;
1452
- }
1453
- const current = getPromptJob();
1454
- if (!current) {
1455
- return false;
1456
- }
1457
- promptJob = current;
1458
- return true;
1459
- });
1460
- }
1461
- async withAgentMessagePreparingGuard(state, run, canRun, signal) {
1462
- const activeSessionId = state.activeSessionId;
1463
- this.agentMessagePreparingTargets.set(activeSessionId, (this.agentMessagePreparingTargets.get(activeSessionId) ?? 0) + 1);
1464
- let cleared = false;
1465
- const clearPreparing = () => {
1466
- if (cleared) {
1467
- return;
1468
- }
1469
- cleared = true;
1470
- const next = (this.agentMessagePreparingTargets.get(activeSessionId) ?? 1) - 1;
1471
- if (next <= 0) {
1472
- this.agentMessagePreparingTargets.delete(activeSessionId);
1473
- }
1474
- else {
1475
- this.agentMessagePreparingTargets.set(activeSessionId, next);
1389
+ // Re-check after the session admission fence wait: the job may have been cancelled, completed, or updated meanwhile.
1390
+ const unrunnableAtAdmission = new Error("Cron job became unrunnable before admission");
1391
+ const admissionCommitted = () => {
1392
+ const refreshed = getRunnableJob();
1393
+ if (!refreshed || refreshed.prompt !== current.prompt || refreshed.deliveryMode !== current.deliveryMode) {
1394
+ throw unrunnableAtAdmission;
1476
1395
  }
1477
1396
  };
1478
1397
  try {
1479
- const targetLock = this.agentMessageTargetLocks.get(activeSessionId);
1480
- if (targetLock)
1481
- await waitForPromptAdmission(targetLock.catch(() => undefined), signal);
1482
- if (this.sessions.get(activeSessionId) !== state || this.closingSessions.has(activeSessionId)) {
1483
- throw new Error("Target session is closing before prompt delivery");
1484
- }
1485
- if (canRun && !canRun()) {
1486
- return false;
1398
+ if (isHeartbeatCronJob(current)) {
1399
+ await session.promptHeartbeat(current, {
1400
+ streamingBehavior: resolveHeartbeatStreamingBehavior(current.deliveryMode),
1401
+ followUpQueueKey: `heartbeat:${current.id}`,
1402
+ source: "rpc",
1403
+ admissionCommitted,
1404
+ });
1405
+ return;
1487
1406
  }
1488
- const session = state.runtime.session;
1489
- await run(session, clearPreparing);
1490
- return true;
1407
+ await session.promptUntilAccepted(current.prompt, {
1408
+ streamingBehavior: "followUp",
1409
+ source: "rpc",
1410
+ admissionCommitted,
1411
+ });
1491
1412
  }
1492
- finally {
1493
- clearPreparing();
1413
+ catch (error) {
1414
+ if (error === unrunnableAtAdmission) {
1415
+ return "skipped";
1416
+ }
1417
+ throw error;
1494
1418
  }
1495
1419
  }
1420
+ getRunnableCronJob(jobId) {
1421
+ return this.cronStore.getClaimedJob(jobId) ?? this.cronStore.getDueJob(jobId);
1422
+ }
1496
1423
  createCronJobForState(state, schedule, prompt) {
1497
1424
  const session = state.runtime.session;
1498
1425
  const sessionFile = session.sessionFile;
@@ -2150,11 +2077,7 @@ export class AgentDaemon {
2150
2077
  .filter((job) => job.activeSessionId === state.activeSessionId &&
2151
2078
  job.status !== "cancelled" &&
2152
2079
  job.status !== "completed");
2153
- const hasPendingAdmission = this.agentMessageAcceptingTargets.has(state.activeSessionId) ||
2154
- this.agentMessagePreparingTargets.has(state.activeSessionId) ||
2155
- this.agentMessageTargetLocks.has(state.activeSessionId) ||
2156
- [...this.promptAdmissions.values()].some((admission) => admission.activeSessionId === state.activeSessionId && admission.status !== "cancelled") ||
2157
- state.runtime.session.hasPendingAdmissionWaiters;
2080
+ const hasPendingAdmission = [...this.promptAdmissions.values()].some((admission) => admission.activeSessionId === state.activeSessionId && admission.status !== "cancelled") || state.runtime.session.hasPendingAdmissionWaiters;
2158
2081
  return {
2159
2082
  isSessionActive: summary.isSessionActive || summary.hasRunningRlmChildren === true || hasPendingAdmission,
2160
2083
  attachedClients: state.clients.size + state.pendingAttaches,
@@ -3022,13 +2945,6 @@ export class AgentDaemon {
3022
2945
  this.write(client, success(command.id, "detach"));
3023
2946
  return;
3024
2947
  }
3025
- case "worker_sync_agent_peers":
3026
- this.remoteAgentPeers.clear();
3027
- for (const peer of command.peers) {
3028
- this.remoteAgentPeers.set(peer.activeSessionId, peer);
3029
- }
3030
- this.writeWorkerSuccess(client, command);
3031
- return;
3032
2948
  case "worker_archive_and_shutdown": {
3033
2949
  for (const state of [...this.sessions.values()]) {
3034
2950
  await this.closeSession(state, "killed");
@@ -3103,6 +3019,12 @@ export class AgentDaemon {
3103
3019
  this.writeWorkerSuccess(client, command);
3104
3020
  return;
3105
3021
  }
3022
+ default: {
3023
+ // Legacy commands from older supervisors (e.g. worker_sync_agent_peers) must fail fast, not time out.
3024
+ const unknown = command;
3025
+ this.write(client, failure(unknown.id, unknown.type, `Unknown worker command: ${unknown.type}`));
3026
+ return;
3027
+ }
3106
3028
  }
3107
3029
  }
3108
3030
  catch (error) {
@@ -3453,7 +3375,7 @@ export class AgentDaemon {
3453
3375
  };
3454
3376
  if (command.type === "prompt_and_wait") {
3455
3377
  try {
3456
- await this.promptWithAgentMessagePreparingGuard(state, promptMessage, {
3378
+ await state.runtime.session.promptAndWait(promptMessage, {
3457
3379
  ...options,
3458
3380
  preflightResult: (didSucceed) => {
3459
3381
  if (didSucceed)
@@ -3474,7 +3396,10 @@ export class AgentDaemon {
3474
3396
  responseSent = true;
3475
3397
  this.write(client, success(command.id, "prompt"));
3476
3398
  };
3477
- void this.promptWithAgentMessagePreparingGuard(state, promptMessage, {
3399
+ const prompt = command.agentMessageId !== undefined && command.expandPromptTemplates === false
3400
+ ? state.runtime.session.acceptAgentMessagePrompt.bind(state.runtime.session)
3401
+ : state.runtime.session.promptUntilAccepted.bind(state.runtime.session);
3402
+ void prompt(promptMessage, {
3478
3403
  ...options,
3479
3404
  agentMessageId: command.agentMessageId,
3480
3405
  customMessage: command.customMessage,
@@ -3487,7 +3412,7 @@ export class AgentDaemon {
3487
3412
  preflightRejected = true;
3488
3413
  }
3489
3414
  },
3490
- }, undefined, false)
3415
+ })
3491
3416
  .then(() => {
3492
3417
  if (preflightRejected) {
3493
3418
  const error = new Error("Prompt was not accepted by the session.");
@@ -3610,10 +3535,8 @@ export class AgentDaemon {
3610
3535
  }
3611
3536
  case "agent_messages_clear": {
3612
3537
  const state = this.getSessionState(command.activeSessionId);
3613
- const cleared = await this.withAgentMessageTargetLock(state.activeSessionId, async () => {
3614
- this.agentMessageRateLimiter.clearMatching((key) => key.endsWith(`->${state.activeSessionId}`));
3615
- return state.runtime.session.clearQueuedUserMessagesMatching(isAgentSessionMessagePrompt);
3616
- });
3538
+ this.agentMessageRateLimiter.clearMatching((key) => key.endsWith(`->${state.activeSessionId}`));
3539
+ const cleared = state.runtime.session.clearQueuedAgentMessages();
3617
3540
  return success(command.id, "agent_messages_clear", cleared);
3618
3541
  }
3619
3542
  case "abort": {
@@ -4538,7 +4461,29 @@ export class AgentDaemon {
4538
4461
  ...(session.sessionFile ? { sessionPath: session.sessionFile } : {}),
4539
4462
  };
4540
4463
  }
4541
- async createAgentMessageListResult(current) {
4464
+ async listSupervisorAgentPeers() {
4465
+ const supervisorSocketPath = this.supervisorSocketPathFromEnv();
4466
+ if (!this.options.worker || !supervisorSocketPath)
4467
+ return [];
4468
+ const client = new DaemonClient(supervisorSocketPath);
4469
+ try {
4470
+ await client.connect(1000);
4471
+ await client.waitForHello(1000);
4472
+ const response = await client.request({ type: "list_agent_peers", workerToken: this.options.worker.authenticationToken }, 5000);
4473
+ if (!response.success)
4474
+ throw deserializeDaemonError(response);
4475
+ // SAFETY: The authenticated supervisor constructs the peer response.
4476
+ return response.data.peers;
4477
+ }
4478
+ catch {
4479
+ return [];
4480
+ }
4481
+ finally {
4482
+ client.close();
4483
+ }
4484
+ }
4485
+ async createAgentMessageListResult(current, peers) {
4486
+ peers ??= await this.listSupervisorAgentPeers();
4542
4487
  const localAgents = this.listTargetableSessionStates(current).map((state) => this.createAgentMessageAgentSummary(state));
4543
4488
  for (const passive of await this.listPassiveRlmSubagents()) {
4544
4489
  const { entry, info } = passive;
@@ -4568,23 +4513,20 @@ export class AgentDaemon {
4568
4513
  });
4569
4514
  }
4570
4515
  const localIds = new Set(localAgents.map((agent) => agent.activeSessionId));
4516
+ const remoteAgents = peers.filter((peer) => !localIds.has(peer.activeSessionId) && !this.closingSessions.has(peer.activeSessionId));
4571
4517
  return {
4572
4518
  current: this.createAgentSessionMessageEndpoint(current),
4573
- agents: [
4574
- ...localAgents,
4575
- ...[...this.remoteAgentPeers.values()].filter((peer) => peer.status !== "inactive" &&
4576
- !localIds.has(peer.activeSessionId) &&
4577
- !this.closingSessions.has(peer.activeSessionId)),
4578
- ],
4519
+ agents: [...localAgents, ...remoteAgents],
4579
4520
  };
4580
4521
  }
4581
4522
  async createAgentFamilyCatalog(currentState) {
4582
4523
  const current = currentState ?? [...this.sessions.values()].find((state) => !this.bindingSessions.has(state.activeSessionId));
4583
- const listed = current ? await this.createAgentMessageListResult(current) : { agents: [] };
4584
- const remotePeers = new Set(this.remoteAgentPeers.values());
4524
+ const remotePeers = current ? await this.listSupervisorAgentPeers() : [];
4525
+ const listed = current ? await this.createAgentMessageListResult(current, remotePeers) : { agents: [] };
4526
+ const remotePeerSet = new Set(remotePeers);
4585
4527
  const localAgents = current
4586
- ? [this.createAgentMessageAgentSummary(current), ...listed.agents.filter((agent) => !remotePeers.has(agent))]
4587
- : listed.agents.filter((agent) => !remotePeers.has(agent));
4528
+ ? [this.createAgentMessageAgentSummary(current), ...listed.agents.filter((agent) => !remotePeerSet.has(agent))]
4529
+ : listed.agents;
4588
4530
  const activePaths = new Set(localAgents.flatMap((agent) => (agent.sessionPath ? [canonicalSessionPath(agent.sessionPath)] : [])));
4589
4531
  const savedRoots = (await SessionManager.listAll(undefined, this.options.defaultSessionConfig.sessionDir))
4590
4532
  .filter((info) => (info.rlmDepth ?? (info.parentSessionPath ? -1 : 0)) === 0 &&
@@ -4618,7 +4560,7 @@ export class AgentDaemon {
4618
4560
  ...(agent.sessionPath ? { sessionPath: canonicalSessionPath(agent.sessionPath) } : {}),
4619
4561
  });
4620
4562
  };
4621
- for (const peer of this.remoteAgentPeers.values())
4563
+ for (const peer of remotePeers)
4622
4564
  addAgent(peer);
4623
4565
  for (const agent of localAgents)
4624
4566
  addAgent(agent);
@@ -4752,50 +4694,11 @@ export class AgentDaemon {
4752
4694
  return `cli:${this.socketPath}`;
4753
4695
  }
4754
4696
  async clearQueuedAgentSessionMessagesForState(state) {
4755
- return this.withAgentMessageTargetLock(state.activeSessionId, async () => state.runtime.session.clearQueuedUserMessagesMatching(isAgentSessionMessagePrompt));
4697
+ return state.runtime.session.clearQueuedAgentMessages();
4756
4698
  }
4757
4699
  async clearQueuedAgentSessionMessagesForAllStates() {
4758
4700
  await Promise.all([...this.sessions.values()].map((state) => this.clearQueuedAgentSessionMessagesForState(state)));
4759
4701
  }
4760
- reserveAgentMessageQueueSlot(targetState) {
4761
- const activeSessionId = targetState.activeSessionId;
4762
- const reserved = this.agentMessagePendingReservations.get(activeSessionId) ?? 0;
4763
- assertAgentMessageQueueCapacity(targetState.runtime.session.unfinishedActionCount + reserved, DEFAULT_AGENT_MESSAGE_MAX_PENDING_PER_SESSION);
4764
- this.agentMessagePendingReservations.set(activeSessionId, reserved + 1);
4765
- let released = false;
4766
- return () => {
4767
- if (released) {
4768
- return;
4769
- }
4770
- released = true;
4771
- const next = (this.agentMessagePendingReservations.get(activeSessionId) ?? 1) - 1;
4772
- if (next <= 0) {
4773
- this.agentMessagePendingReservations.delete(activeSessionId);
4774
- }
4775
- else {
4776
- this.agentMessagePendingReservations.set(activeSessionId, next);
4777
- }
4778
- };
4779
- }
4780
- async withAgentMessageTargetLock(activeSessionId, run) {
4781
- const previous = this.agentMessageTargetLocks.get(activeSessionId) ?? Promise.resolve();
4782
- let releaseCurrent = () => { };
4783
- const current = new Promise((resolve) => {
4784
- releaseCurrent = resolve;
4785
- });
4786
- const next = previous.catch(() => undefined).then(() => current);
4787
- this.agentMessageTargetLocks.set(activeSessionId, next);
4788
- await previous.catch(() => undefined);
4789
- try {
4790
- return await run();
4791
- }
4792
- finally {
4793
- releaseCurrent();
4794
- if (this.agentMessageTargetLocks.get(activeSessionId) === next) {
4795
- this.agentMessageTargetLocks.delete(activeSessionId);
4796
- }
4797
- }
4798
- }
4799
4702
  agentFamilyEntry(state) {
4800
4703
  const metadata = state.runtime.metadata;
4801
4704
  const depth = state.runtime.session.rlmDepth ?? 0;
@@ -4946,12 +4849,10 @@ export class AgentDaemon {
4946
4849
  if (options.origin === "agent" && options.fromState) {
4947
4850
  this.assertAgentFamilyReachable(options.fromState, targetState);
4948
4851
  }
4949
- const releaseQueueSlot = this.reserveAgentMessageQueueSlot(targetState);
4950
4852
  const senderKey = options.senderKey ?? options.fromState?.activeSessionId ?? `client:${options.clientId ?? "unknown"}`;
4951
4853
  const rateLimitKey = `${senderKey}->${targetState.activeSessionId}`;
4952
4854
  const rateLimit = this.agentMessageRateLimiter.tryConsume(rateLimitKey);
4953
4855
  if (!rateLimit.ok) {
4954
- releaseQueueSlot();
4955
4856
  throw new Error(`Agent messaging rate limit exceeded; retry after ${rateLimit.retryAfterMs}ms`);
4956
4857
  }
4957
4858
  const payload = {
@@ -4964,29 +4865,13 @@ export class AgentDaemon {
4964
4865
  target: this.createAgentSessionMessageEndpoint(targetState),
4965
4866
  };
4966
4867
  try {
4967
- const { status } = await this.withAgentMessageTargetLock(targetState.activeSessionId, async () => {
4968
- if (this.agentMessagesPaused) {
4969
- throw new Error("Agent messaging is paused");
4970
- }
4971
- if (!this.sessions.has(targetState.activeSessionId) ||
4972
- this.closingSessions.has(targetState.activeSessionId)) {
4973
- throw new Error("Target session is closing before agent message delivery");
4974
- }
4975
- if (targetState.runtime.session.sessionId !== payload.target.sessionId) {
4976
- throw new Error("Target session changed before agent message delivery");
4977
- }
4978
- return this.acceptAgentSessionMessage(targetState, payload, releaseQueueSlot);
4979
- });
4868
+ const { status } = await this.acceptAgentSessionMessage(targetState, payload);
4980
4869
  return createAgentSessionMessageReceipt(payload, status);
4981
4870
  }
4982
4871
  catch (error) {
4983
4872
  this.agentMessageRateLimiter.refund(rateLimitKey);
4984
4873
  throw error;
4985
4874
  }
4986
- finally {
4987
- // Error-path backstop; success paths release inside acceptAgentSessionMessage.
4988
- releaseQueueSlot();
4989
- }
4990
4875
  }
4991
4876
  async sendRemoteAgentSessionMessage(fromState, targetSelector, message) {
4992
4877
  const supervisorSocketPath = this.supervisorSocketPathFromEnv();
@@ -4994,100 +4879,75 @@ export class AgentDaemon {
4994
4879
  throw new Error(`Unknown active session: ${targetSelector}`);
4995
4880
  }
4996
4881
  const deadline = Date.now() + 30_000;
4882
+ let client;
4997
4883
  let lastError;
4998
4884
  while (Date.now() < deadline && !this.shuttingDown) {
4999
- const client = new DaemonClient(supervisorSocketPath);
5000
- let receivedResponse = false;
4885
+ const candidate = new DaemonClient(supervisorSocketPath);
5001
4886
  try {
5002
- await client.connect(1000);
5003
- await client.waitForHello(1000);
5004
- const response = await client.request({
5005
- type: "send_message",
5006
- targetActiveSessionId: targetSelector,
5007
- message,
5008
- fromActiveSessionId: fromState.activeSessionId,
5009
- agentOrigin: true,
5010
- }, 30_000);
5011
- receivedResponse = true;
5012
- if (!response.success) {
5013
- throw deserializeDaemonError(response);
5014
- }
5015
- if (!response.data || typeof response.data !== "object") {
5016
- throw new Error("Supervisor returned an invalid agent-message receipt");
5017
- }
5018
- return response.data;
4887
+ await candidate.connect(1000);
4888
+ await candidate.waitForHello(1000);
4889
+ client = candidate;
4890
+ break;
5019
4891
  }
5020
4892
  catch (error) {
5021
4893
  lastError = error;
5022
- if (receivedResponse) {
5023
- throw error;
5024
- }
5025
- }
5026
- finally {
5027
- client.close();
4894
+ candidate.close();
5028
4895
  }
5029
4896
  await new Promise((resolveDelay) => setTimeout(resolveDelay, 250));
5030
4897
  }
5031
- throw lastError instanceof Error ? lastError : new Error(`Unknown active session: ${targetSelector}`);
5032
- }
5033
- async acceptAgentSessionMessage(targetState, payload, releaseReservation) {
5034
- const session = targetState.runtime.session;
5035
- const reserved = this.agentMessagePendingReservations.get(targetState.activeSessionId) ?? 0;
5036
- const otherReservations = Math.max(0, reserved - 1);
5037
- assertAgentMessageQueueCapacity(session.unfinishedActionCount + otherReservations, DEFAULT_AGENT_MESSAGE_MAX_PENDING_PER_SESSION);
5038
- const shouldQueue = this.agentMessageAcceptingTargets.has(targetState.activeSessionId) ||
5039
- this.agentMessagePreparingTargets.has(targetState.activeSessionId) ||
5040
- session.isStreaming ||
5041
- session.isCompacting ||
5042
- session.isRetrying ||
5043
- session.isBashRunning ||
5044
- session.unfinishedActionCount > 0;
5045
- const streamingBehavior = "steer";
5046
- const message = createAgentSessionMessage(payload);
5047
- const prompt = message.content;
5048
- if (shouldQueue) {
5049
- const didQueue = await session.queueAgentMessagePrompt(prompt, streamingBehavior, message);
5050
- if (!didQueue) {
5051
- throw new Error("Agent message was not queued");
5052
- }
5053
- // The queued message now counts in unfinishedActionCount.
5054
- releaseReservation();
5055
- // Do not await delivery: a queued message delivers only when the target's
5056
- // turn progresses, and the sender is blocked inside its own turn — awaiting
5057
- // here deadlocks mutual sends between busy sessions.
5058
- return { status: "queued" };
5059
- }
5060
- this.agentMessageAcceptingTargets.add(targetState.activeSessionId);
5061
- let preflightFailed = false;
5062
- let preflightQueued = false;
4898
+ if (!client) {
4899
+ throw lastError instanceof Error ? lastError : new Error(`Unknown active session: ${targetSelector}`);
4900
+ }
5063
4901
  try {
5064
- const promptOptions = {
5065
- expandPromptTemplates: false,
5066
- streamingBehavior,
5067
- queueIfBusy: true,
5068
- preflightResult: (didSucceed, didQueue) => {
5069
- preflightFailed = !didSucceed;
5070
- preflightQueued = didSucceed && didQueue === true;
5071
- },
5072
- };
5073
- if (typeof session.acceptAgentMessagePrompt === "function") {
5074
- await session.acceptAgentMessagePrompt(prompt, {
5075
- ...promptOptions,
5076
- customMessage: message,
5077
- });
5078
- }
5079
- else {
5080
- await session.prompt(prompt, promptOptions);
4902
+ const response = await client.request({
4903
+ type: "send_message",
4904
+ targetActiveSessionId: targetSelector,
4905
+ message,
4906
+ fromActiveSessionId: fromState.activeSessionId,
4907
+ agentOrigin: true,
4908
+ }, 30_000);
4909
+ if (!response.success) {
4910
+ throw deserializeDaemonError(response);
5081
4911
  }
5082
- if (preflightFailed) {
5083
- throw new Error("Agent message was not accepted");
4912
+ if (!response.data || typeof response.data !== "object") {
4913
+ throw new Error("Supervisor returned an invalid agent-message receipt");
5084
4914
  }
5085
- releaseReservation();
5086
- return { status: preflightQueued ? "queued" : "delivered" };
4915
+ return response.data;
5087
4916
  }
5088
4917
  finally {
5089
- this.agentMessageAcceptingTargets.delete(targetState.activeSessionId);
4918
+ client.close();
4919
+ }
4920
+ }
4921
+ async acceptAgentSessionMessage(targetState, payload) {
4922
+ const message = createAgentSessionMessage(payload);
4923
+ let preflightFailed = false;
4924
+ let preflightQueued = false;
4925
+ await targetState.runtime.session.acceptAgentMessagePrompt(message.content, {
4926
+ expandPromptTemplates: false,
4927
+ streamingBehavior: "steer",
4928
+ queueIfBusy: true,
4929
+ customMessage: message,
4930
+ admissionCommitted: () => {
4931
+ if (this.agentMessagesPaused) {
4932
+ throw new Error("Agent messaging is paused");
4933
+ }
4934
+ if (!this.sessions.has(targetState.activeSessionId) ||
4935
+ this.closingSessions.has(targetState.activeSessionId)) {
4936
+ throw new Error("Target session is closing before agent message delivery");
4937
+ }
4938
+ if (targetState.runtime.session.sessionId !== payload.target.sessionId) {
4939
+ throw new Error("Target session changed before agent message delivery");
4940
+ }
4941
+ },
4942
+ preflightResult: (didSucceed, didQueue) => {
4943
+ preflightFailed = !didSucceed;
4944
+ preflightQueued = didSucceed && didQueue === true;
4945
+ },
4946
+ });
4947
+ if (preflightFailed) {
4948
+ throw new Error("Agent message was not accepted");
5090
4949
  }
4950
+ return { status: preflightQueued ? "queued" : "delivered" };
5091
4951
  }
5092
4952
  detachClientFromSession(client, state) {
5093
4953
  const acpMcpOwner = this.acpMcpOwners.get(state.activeSessionId);