@a5c-ai/agent-comm-mux 5.0.1-staging.04ca6ab00d21

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 (263) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/adapter-registry.d.ts +51 -0
  4. package/dist/adapter-registry.d.ts.map +1 -0
  5. package/dist/adapter-registry.js +208 -0
  6. package/dist/adapter-registry.js.map +1 -0
  7. package/dist/adapter-types.d.ts +164 -0
  8. package/dist/adapter-types.d.ts.map +1 -0
  9. package/dist/adapter-types.js +27 -0
  10. package/dist/adapter-types.js.map +1 -0
  11. package/dist/adapter.d.ts +248 -0
  12. package/dist/adapter.d.ts.map +1 -0
  13. package/dist/adapter.js +18 -0
  14. package/dist/adapter.js.map +1 -0
  15. package/dist/atomic-fs.d.ts +33 -0
  16. package/dist/atomic-fs.d.ts.map +1 -0
  17. package/dist/atomic-fs.js +190 -0
  18. package/dist/atomic-fs.js.map +1 -0
  19. package/dist/auth-manager.d.ts +37 -0
  20. package/dist/auth-manager.d.ts.map +1 -0
  21. package/dist/auth-manager.js +94 -0
  22. package/dist/auth-manager.js.map +1 -0
  23. package/dist/auth-types.d.ts +83 -0
  24. package/dist/auth-types.d.ts.map +1 -0
  25. package/dist/auth-types.js +10 -0
  26. package/dist/auth-types.js.map +1 -0
  27. package/dist/automation.d.ts +104 -0
  28. package/dist/automation.d.ts.map +1 -0
  29. package/dist/automation.js +2 -0
  30. package/dist/automation.js.map +1 -0
  31. package/dist/browser.d.ts +5 -0
  32. package/dist/browser.d.ts.map +1 -0
  33. package/dist/browser.js +2 -0
  34. package/dist/browser.js.map +1 -0
  35. package/dist/builtin-hooks.d.ts +24 -0
  36. package/dist/builtin-hooks.d.ts.map +1 -0
  37. package/dist/builtin-hooks.js +80 -0
  38. package/dist/builtin-hooks.js.map +1 -0
  39. package/dist/capabilities.d.ts +251 -0
  40. package/dist/capabilities.d.ts.map +1 -0
  41. package/dist/capabilities.js +7 -0
  42. package/dist/capabilities.js.map +1 -0
  43. package/dist/client.d.ts +118 -0
  44. package/dist/client.d.ts.map +1 -0
  45. package/dist/client.js +316 -0
  46. package/dist/client.js.map +1 -0
  47. package/dist/config-manager.d.ts +85 -0
  48. package/dist/config-manager.d.ts.map +1 -0
  49. package/dist/config-manager.js +202 -0
  50. package/dist/config-manager.js.map +1 -0
  51. package/dist/config-types.d.ts +134 -0
  52. package/dist/config-types.d.ts.map +1 -0
  53. package/dist/config-types.js +10 -0
  54. package/dist/config-types.js.map +1 -0
  55. package/dist/cost-utils.d.ts +31 -0
  56. package/dist/cost-utils.d.ts.map +1 -0
  57. package/dist/cost-utils.js +69 -0
  58. package/dist/cost-utils.js.map +1 -0
  59. package/dist/errors.d.ts +50 -0
  60. package/dist/errors.d.ts.map +1 -0
  61. package/dist/errors.js +85 -0
  62. package/dist/errors.js.map +1 -0
  63. package/dist/events-control.d.ts +91 -0
  64. package/dist/events-control.d.ts.map +1 -0
  65. package/dist/events-control.js +8 -0
  66. package/dist/events-control.js.map +1 -0
  67. package/dist/events.d.ts +285 -0
  68. package/dist/events.d.ts.map +1 -0
  69. package/dist/events.js +8 -0
  70. package/dist/events.js.map +1 -0
  71. package/dist/hook-catalog.d.ts +20 -0
  72. package/dist/hook-catalog.d.ts.map +1 -0
  73. package/dist/hook-catalog.js +63 -0
  74. package/dist/hook-catalog.js.map +1 -0
  75. package/dist/hook-dispatcher.d.ts +24 -0
  76. package/dist/hook-dispatcher.d.ts.map +1 -0
  77. package/dist/hook-dispatcher.js +91 -0
  78. package/dist/hook-dispatcher.js.map +1 -0
  79. package/dist/hook-payload.d.ts +24 -0
  80. package/dist/hook-payload.d.ts.map +1 -0
  81. package/dist/hook-payload.js +62 -0
  82. package/dist/hook-payload.js.map +1 -0
  83. package/dist/hooks.d.ts +59 -0
  84. package/dist/hooks.d.ts.map +1 -0
  85. package/dist/hooks.js +118 -0
  86. package/dist/hooks.js.map +1 -0
  87. package/dist/host-detection.d.ts +67 -0
  88. package/dist/host-detection.d.ts.map +1 -0
  89. package/dist/host-detection.js +147 -0
  90. package/dist/host-detection.js.map +1 -0
  91. package/dist/index.d.ts +88 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +50 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/interaction-channel-impl.d.ts +56 -0
  96. package/dist/interaction-channel-impl.d.ts.map +1 -0
  97. package/dist/interaction-channel-impl.js +159 -0
  98. package/dist/interaction-channel-impl.js.map +1 -0
  99. package/dist/interaction.d.ts +93 -0
  100. package/dist/interaction.d.ts.map +1 -0
  101. package/dist/interaction.js +8 -0
  102. package/dist/interaction.js.map +1 -0
  103. package/dist/invocation.d.ts +100 -0
  104. package/dist/invocation.d.ts.map +1 -0
  105. package/dist/invocation.js +17 -0
  106. package/dist/invocation.js.map +1 -0
  107. package/dist/kanban.d.ts +894 -0
  108. package/dist/kanban.d.ts.map +1 -0
  109. package/dist/kanban.js +1155 -0
  110. package/dist/kanban.js.map +1 -0
  111. package/dist/merge.d.ts +37 -0
  112. package/dist/merge.d.ts.map +1 -0
  113. package/dist/merge.js +102 -0
  114. package/dist/merge.js.map +1 -0
  115. package/dist/model-registry.d.ts +61 -0
  116. package/dist/model-registry.d.ts.map +1 -0
  117. package/dist/model-registry.js +198 -0
  118. package/dist/model-registry.js.map +1 -0
  119. package/dist/plugin-manager-impl.d.ts +48 -0
  120. package/dist/plugin-manager-impl.d.ts.map +1 -0
  121. package/dist/plugin-manager-impl.js +343 -0
  122. package/dist/plugin-manager-impl.js.map +1 -0
  123. package/dist/plugin-manager.d.ts +81 -0
  124. package/dist/plugin-manager.d.ts.map +1 -0
  125. package/dist/plugin-manager.js +7 -0
  126. package/dist/plugin-manager.js.map +1 -0
  127. package/dist/plugin-types.d.ts +114 -0
  128. package/dist/plugin-types.d.ts.map +1 -0
  129. package/dist/plugin-types.js +7 -0
  130. package/dist/plugin-types.js.map +1 -0
  131. package/dist/process-tracker.d.ts +20 -0
  132. package/dist/process-tracker.d.ts.map +1 -0
  133. package/dist/process-tracker.js +96 -0
  134. package/dist/process-tracker.js.map +1 -0
  135. package/dist/profiles.d.ts +99 -0
  136. package/dist/profiles.d.ts.map +1 -0
  137. package/dist/profiles.js +231 -0
  138. package/dist/profiles.js.map +1 -0
  139. package/dist/programmatic-runner.d.ts +4 -0
  140. package/dist/programmatic-runner.d.ts.map +1 -0
  141. package/dist/programmatic-runner.js +110 -0
  142. package/dist/programmatic-runner.js.map +1 -0
  143. package/dist/provider-config.d.ts +40 -0
  144. package/dist/provider-config.d.ts.map +1 -0
  145. package/dist/provider-config.js +67 -0
  146. package/dist/provider-config.js.map +1 -0
  147. package/dist/provider-profiles.d.ts +47 -0
  148. package/dist/provider-profiles.d.ts.map +1 -0
  149. package/dist/provider-profiles.js +117 -0
  150. package/dist/provider-profiles.js.map +1 -0
  151. package/dist/provider-resolver.d.ts +15 -0
  152. package/dist/provider-resolver.d.ts.map +1 -0
  153. package/dist/provider-resolver.js +114 -0
  154. package/dist/provider-resolver.js.map +1 -0
  155. package/dist/provider-support-matrix.d.ts +12 -0
  156. package/dist/provider-support-matrix.d.ts.map +1 -0
  157. package/dist/provider-support-matrix.js +88 -0
  158. package/dist/provider-support-matrix.js.map +1 -0
  159. package/dist/remote-runner.d.ts +4 -0
  160. package/dist/remote-runner.d.ts.map +1 -0
  161. package/dist/remote-runner.js +114 -0
  162. package/dist/remote-runner.js.map +1 -0
  163. package/dist/retry.d.ts +13 -0
  164. package/dist/retry.d.ts.map +1 -0
  165. package/dist/retry.js +17 -0
  166. package/dist/retry.js.map +1 -0
  167. package/dist/run-handle-cost.d.ts +36 -0
  168. package/dist/run-handle-cost.d.ts.map +1 -0
  169. package/dist/run-handle-cost.js +62 -0
  170. package/dist/run-handle-cost.js.map +1 -0
  171. package/dist/run-handle-impl.d.ts +144 -0
  172. package/dist/run-handle-impl.d.ts.map +1 -0
  173. package/dist/run-handle-impl.js +695 -0
  174. package/dist/run-handle-impl.js.map +1 -0
  175. package/dist/run-handle.d.ts +222 -0
  176. package/dist/run-handle.d.ts.map +1 -0
  177. package/dist/run-handle.js +9 -0
  178. package/dist/run-handle.js.map +1 -0
  179. package/dist/run-options-validation.d.ts +23 -0
  180. package/dist/run-options-validation.d.ts.map +1 -0
  181. package/dist/run-options-validation.js +234 -0
  182. package/dist/run-options-validation.js.map +1 -0
  183. package/dist/run-options.d.ts +128 -0
  184. package/dist/run-options.d.ts.map +1 -0
  185. package/dist/run-options.js +329 -0
  186. package/dist/run-options.js.map +1 -0
  187. package/dist/runtime-hook-dispatcher.d.ts +19 -0
  188. package/dist/runtime-hook-dispatcher.d.ts.map +1 -0
  189. package/dist/runtime-hook-dispatcher.js +50 -0
  190. package/dist/runtime-hook-dispatcher.js.map +1 -0
  191. package/dist/runtime-hooks.d.ts +47 -0
  192. package/dist/runtime-hooks.d.ts.map +1 -0
  193. package/dist/runtime-hooks.js +2 -0
  194. package/dist/runtime-hooks.js.map +1 -0
  195. package/dist/schema/__tests__/event-schema.test.d.ts +1 -0
  196. package/dist/schema/__tests__/event-schema.test.d.ts.map +1 -0
  197. package/dist/schema/__tests__/event-schema.test.js +480 -0
  198. package/dist/schema/__tests__/event-schema.test.js.map +1 -0
  199. package/dist/schema/event-schema.d.ts +120 -0
  200. package/dist/schema/event-schema.d.ts.map +1 -0
  201. package/dist/schema/event-schema.js +546 -0
  202. package/dist/schema/event-schema.js.map +1 -0
  203. package/dist/schema/index.d.ts +7 -0
  204. package/dist/schema/index.d.ts.map +1 -0
  205. package/dist/schema/index.js +49 -0
  206. package/dist/schema/index.js.map +1 -0
  207. package/dist/session-manager-helpers.d.ts +33 -0
  208. package/dist/session-manager-helpers.d.ts.map +1 -0
  209. package/dist/session-manager-helpers.js +66 -0
  210. package/dist/session-manager-helpers.js.map +1 -0
  211. package/dist/session-manager.d.ts +70 -0
  212. package/dist/session-manager.d.ts.map +1 -0
  213. package/dist/session-manager.js +343 -0
  214. package/dist/session-manager.js.map +1 -0
  215. package/dist/session-types.d.ts +358 -0
  216. package/dist/session-types.d.ts.map +1 -0
  217. package/dist/session-types.js +10 -0
  218. package/dist/session-types.js.map +1 -0
  219. package/dist/spawn-invocation.d.ts +52 -0
  220. package/dist/spawn-invocation.d.ts.map +1 -0
  221. package/dist/spawn-invocation.js +218 -0
  222. package/dist/spawn-invocation.js.map +1 -0
  223. package/dist/spawn-runner-utils.d.ts +21 -0
  224. package/dist/spawn-runner-utils.d.ts.map +1 -0
  225. package/dist/spawn-runner-utils.js +102 -0
  226. package/dist/spawn-runner-utils.js.map +1 -0
  227. package/dist/spawn-runner.d.ts +21 -0
  228. package/dist/spawn-runner.d.ts.map +1 -0
  229. package/dist/spawn-runner.js +576 -0
  230. package/dist/spawn-runner.js.map +1 -0
  231. package/dist/spawn-runtime-hooks.d.ts +15 -0
  232. package/dist/spawn-runtime-hooks.d.ts.map +1 -0
  233. package/dist/spawn-runtime-hooks.js +136 -0
  234. package/dist/spawn-runtime-hooks.js.map +1 -0
  235. package/dist/state-machine.d.ts +33 -0
  236. package/dist/state-machine.d.ts.map +1 -0
  237. package/dist/state-machine.js +61 -0
  238. package/dist/state-machine.js.map +1 -0
  239. package/dist/storage.d.ts +47 -0
  240. package/dist/storage.d.ts.map +1 -0
  241. package/dist/storage.js +90 -0
  242. package/dist/storage.js.map +1 -0
  243. package/dist/stream-assembler.d.ts +51 -0
  244. package/dist/stream-assembler.d.ts.map +1 -0
  245. package/dist/stream-assembler.js +86 -0
  246. package/dist/stream-assembler.js.map +1 -0
  247. package/dist/tools/classify.d.ts +9 -0
  248. package/dist/tools/classify.d.ts.map +1 -0
  249. package/dist/tools/classify.js +77 -0
  250. package/dist/tools/classify.js.map +1 -0
  251. package/dist/tools/index.d.ts +2 -0
  252. package/dist/tools/index.d.ts.map +1 -0
  253. package/dist/tools/index.js +2 -0
  254. package/dist/tools/index.js.map +1 -0
  255. package/dist/types.d.ts +288 -0
  256. package/dist/types.d.ts.map +1 -0
  257. package/dist/types.js +8 -0
  258. package/dist/types.js.map +1 -0
  259. package/dist/workspaces.d.ts +142 -0
  260. package/dist/workspaces.d.ts.map +1 -0
  261. package/dist/workspaces.js +515 -0
  262. package/dist/workspaces.js.map +1 -0
  263. package/package.json +107 -0
@@ -0,0 +1,695 @@
1
+ /**
2
+ * RunHandleImpl — concrete implementation of RunHandle.
3
+ *
4
+ * Implements:
5
+ * - Typed EventEmitter dispatch with per-type handler maps
6
+ * - AsyncIterator fan-out with shared replay buffer (high-water-mark bounded)
7
+ * - State machine (via state-machine.ts)
8
+ * - Thenable contract (lazy internal promise, resolves to RunResult)
9
+ * - Control method guards (RUN_NOT_ACTIVE, INVALID_STATE_TRANSITION)
10
+ * - Interaction channel wiring
11
+ */
12
+ import { AgentMuxError } from './errors.js';
13
+ import { assertTransition, isTerminal } from './state-machine.js';
14
+ import { InteractionChannelImpl } from './interaction-channel-impl.js';
15
+ import { accumulateTokenUsage, accumulateCost, buildTokenUsageSummary, } from './run-handle-cost.js';
16
+ import { createComponentLogger, telemetry } from '@a5c-ai/agent-mux-observability';
17
+ /** Default high-water mark for the shared event buffer. */
18
+ const DEFAULT_HWM = 1000;
19
+ // ---------------------------------------------------------------------------
20
+ // RunHandleImpl
21
+ // ---------------------------------------------------------------------------
22
+ export class RunHandleImpl {
23
+ // ── Identity ─────────────────────────────────────────────────────────────
24
+ runId;
25
+ agent;
26
+ model;
27
+ // ── State machine ─────────────────────────────────────────────────────────
28
+ _state = 'spawned';
29
+ // ── EventEmitter handler map ──────────────────────────────────────────────
30
+ /** Map from event type -> array of handlers. */
31
+ _handlers = new Map();
32
+ // ── AsyncIterator fan-out ─────────────────────────────────────────────────
33
+ /** Shared ordered buffer of all events emitted so far. */
34
+ _buffer = [];
35
+ /** Per-iterator tracking state. */
36
+ _iterators = new Set();
37
+ /** When true, the run has ended and all iterators should drain and stop. */
38
+ _done = false;
39
+ /** High-water mark for the buffer. */
40
+ _hwm;
41
+ // ── Thenable / result promise (lazy, per spec §2) ─────────────────────────
42
+ _resultPromise = null;
43
+ _resolveResult = null;
44
+ /** Queued result if complete() is called before the promise is created. */
45
+ _pendingResult = null;
46
+ // ── Run result accumulators ───────────────────────────────────────────────
47
+ _text = '';
48
+ _sessionId;
49
+ _cost = null;
50
+ _startTime = Date.now();
51
+ _tokenUsage = { inputTokens: 0, outputTokens: 0, thinkingTokens: 0, cachedTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
52
+ _turnCount = 0;
53
+ _exitCode = null;
54
+ _signal = null;
55
+ _runError = null;
56
+ _collectedEvents = [];
57
+ _collectEvents;
58
+ _tags;
59
+ // ── Interaction channel ───────────────────────────────────────────────────
60
+ interaction;
61
+ /** Logger instance for this run. */
62
+ logger;
63
+ /** OpenTelemetry span for this run. */
64
+ _runSpan;
65
+ /** Map of active tool call spans by toolCallId. */
66
+ _toolSpans = new Map();
67
+ /** Map of active subagent spans by subagentId. */
68
+ _subagentSpans = new Map();
69
+ /** Bound runtime input transport used by send()/queue()/steer(). */
70
+ _inputTransport = null;
71
+ /** Bound interaction response transport used by approval/input dispatch. */
72
+ _interactionTransport = null;
73
+ /** Deferred prompts waiting for a matching run boundary. */
74
+ _deferredPrompts = [];
75
+ /** Serializes deferred prompt delivery. */
76
+ _deferredDeliveryChain = Promise.resolve();
77
+ /** Monotonic counter for deferred prompt bookkeeping. */
78
+ _deferredPromptSeq = 0;
79
+ // ── Constructor ───────────────────────────────────────────────────────────
80
+ constructor(options) {
81
+ this.runId = options.runId;
82
+ this.agent = options.agent;
83
+ this.model = options.model;
84
+ this._hwm = options.bufferHighWaterMark ?? DEFAULT_HWM;
85
+ this._collectEvents = options.collectEvents ?? false;
86
+ this._tags = options.tags ?? [];
87
+ this.interaction = new InteractionChannelImpl(options.approvalMode ?? 'prompt');
88
+ // Initialize logger for this run
89
+ this.logger = createComponentLogger(this.agent);
90
+ // Start OpenTelemetry span for this run
91
+ this._runSpan = telemetry.startRunSpan(this.runId, this.agent, this.model);
92
+ // Wire up the interaction channel's response dispatcher.
93
+ this.interaction.setDispatch(async (id, response) => {
94
+ await this._handleInteractionResponse(id, response);
95
+ });
96
+ // Result promise is created lazily per spec §2.
97
+ }
98
+ // ── State machine ─────────────────────────────────────────────────────────
99
+ /** Current run state. */
100
+ get state() {
101
+ return this._state;
102
+ }
103
+ /**
104
+ * Transition to a new state.
105
+ * Validates the transition and updates the internal state.
106
+ * Does NOT emit a state-change event — callers should emit the appropriate event then call this.
107
+ */
108
+ transitionTo(next) {
109
+ assertTransition(this._state, next);
110
+ this._state = next;
111
+ }
112
+ // ── Event dispatch ────────────────────────────────────────────────────────
113
+ /**
114
+ * Emit an event. Called by the adapter or test harness.
115
+ *
116
+ * 1. Accumulates relevant fields (text, token usage, cost, session ID).
117
+ * 2. Appends to the shared buffer (with HWM enforcement).
118
+ * 3. Wakes up waiting async iterators.
119
+ * 4. Dispatches to registered EventEmitter handlers.
120
+ */
121
+ emit(event) {
122
+ // Log important events
123
+ this._logEvent(event);
124
+ // Accumulate run result fields.
125
+ this._accumulate(event);
126
+ // Collect if requested.
127
+ if (this._collectEvents) {
128
+ this._collectedEvents.push(event);
129
+ }
130
+ // Append to shared buffer with HWM enforcement.
131
+ if (this._buffer.length >= this._hwm) {
132
+ // Drop oldest events already consumed by all iterators per spec §10.3.
133
+ let minPosition = this._buffer.length;
134
+ for (const iter of this._iterators) {
135
+ if (iter.position < minPosition)
136
+ minPosition = iter.position;
137
+ }
138
+ if (minPosition > 0) {
139
+ // Evict events already consumed by all iterators.
140
+ this._buffer.splice(0, minPosition);
141
+ for (const iter of this._iterators) {
142
+ iter.position -= minPosition;
143
+ }
144
+ }
145
+ else if (this._buffer.length >= this._hwm) {
146
+ // All iterators at position 0 — must drop oldest anyway.
147
+ this._buffer.shift();
148
+ // No position adjustment needed since they haven't consumed it.
149
+ }
150
+ this._emitDebugEvent('warn', `Event buffer overflow: events dropped (HWM=${this._hwm})`);
151
+ }
152
+ this._buffer.push(event);
153
+ // Wake waiting iterators.
154
+ for (const iter of this._iterators) {
155
+ if (iter.waiting !== null) {
156
+ const resolve = iter.waiting;
157
+ iter.waiting = null;
158
+ iter.position++;
159
+ resolve({ value: event, done: false });
160
+ }
161
+ }
162
+ // Dispatch to EventEmitter handlers.
163
+ this._dispatchHandlers(event);
164
+ // Deferred prompt delivery runs after consumers have observed the
165
+ // boundary event that triggered it.
166
+ this._triggerDeferredPromptDelivery(event);
167
+ }
168
+ /**
169
+ * Log important events for observability.
170
+ */
171
+ _logEvent(event) {
172
+ switch (event.type) {
173
+ case 'tool_call_start':
174
+ this.logger.toolCallStart({
175
+ runId: this.runId,
176
+ toolName: event.toolName,
177
+ toolCallId: event.toolCallId,
178
+ args: event.inputAccumulated,
179
+ });
180
+ // Start tool call span
181
+ const toolSpan = telemetry.startToolCallSpan(event.toolName, event.toolCallId, this._runSpan);
182
+ this._toolSpans.set(event.toolCallId, toolSpan);
183
+ break;
184
+ case 'tool_call_ready':
185
+ // Tool call is ready to be executed (or result is pending)
186
+ break;
187
+ case 'tool_result':
188
+ {
189
+ const span = this._toolSpans.get(event.toolCallId);
190
+ const duration = event.durationMs || 0;
191
+ this.logger.toolCallComplete({
192
+ runId: this.runId,
193
+ toolName: event.toolName,
194
+ toolCallId: event.toolCallId,
195
+ duration,
196
+ result: event.result,
197
+ });
198
+ if (span) {
199
+ telemetry.endSpanSuccess(span, {
200
+ 'tool.duration_ms': duration,
201
+ });
202
+ this._toolSpans.delete(event.toolCallId);
203
+ }
204
+ telemetry.recordToolCall(event.toolName, duration, true);
205
+ }
206
+ break;
207
+ case 'tool_error':
208
+ {
209
+ const span = this._toolSpans.get(event.toolCallId);
210
+ const duration = event.durationMs || 0;
211
+ this.logger.toolCallComplete({
212
+ runId: this.runId,
213
+ toolName: event.toolName,
214
+ toolCallId: event.toolCallId,
215
+ duration,
216
+ result: event.error,
217
+ });
218
+ if (span) {
219
+ telemetry.endSpanError(span, event.error);
220
+ this._toolSpans.delete(event.toolCallId);
221
+ }
222
+ telemetry.recordToolCall(event.toolName, duration, false);
223
+ }
224
+ break;
225
+ case 'subagent_spawn':
226
+ {
227
+ const subagentSpan = telemetry.startSubagentSpan(event.subagentId, event.agentName, this._runSpan);
228
+ this._subagentSpans.set(event.subagentId, subagentSpan);
229
+ }
230
+ break;
231
+ case 'subagent_result':
232
+ {
233
+ const span = this._subagentSpans.get(event.subagentId);
234
+ if (span) {
235
+ telemetry.endSpanSuccess(span);
236
+ this._subagentSpans.delete(event.subagentId);
237
+ }
238
+ }
239
+ break;
240
+ case 'subagent_error':
241
+ {
242
+ const span = this._subagentSpans.get(event.subagentId);
243
+ if (span) {
244
+ telemetry.endSpanError(span, event.error);
245
+ this._subagentSpans.delete(event.subagentId);
246
+ }
247
+ }
248
+ break;
249
+ case 'error':
250
+ this.logger.error({
251
+ runId: this.runId,
252
+ agent: this.agent,
253
+ error: {
254
+ code: event.code,
255
+ message: event.message,
256
+ recoverable: event.recoverable,
257
+ },
258
+ }, 'Agent error occurred');
259
+ break;
260
+ case 'session_start':
261
+ this.logger.session('Session started', {
262
+ runId: this.runId,
263
+ sessionId: event.sessionId,
264
+ action: 'create',
265
+ });
266
+ break;
267
+ case 'session_resume':
268
+ this.logger.session('Session resumed', {
269
+ runId: this.runId,
270
+ sessionId: event.sessionId,
271
+ action: 'resume',
272
+ });
273
+ break;
274
+ case 'cost':
275
+ this.logger.debug({
276
+ runId: this.runId,
277
+ cost: event.cost,
278
+ }, 'Cost update received');
279
+ break;
280
+ // Log other important events as debug
281
+ default:
282
+ if (['approval_request', 'input_required'].includes(event.type)) {
283
+ this.logger.debug({ runId: this.runId, eventType: event.type }, 'Interaction event');
284
+ }
285
+ break;
286
+ }
287
+ }
288
+ /**
289
+ * Signal that the run has ended with the given exit information.
290
+ * Resolves the result promise and terminates all async iterators.
291
+ */
292
+ complete(exitReason, exitCode, signal) {
293
+ this._exitCode = exitCode;
294
+ this._signal = signal;
295
+ this._done = true;
296
+ this.interaction.terminate();
297
+ // Log run completion
298
+ const duration = Date.now() - this._startTime;
299
+ const cost = this._cost ? {
300
+ totalUsd: this._cost.totalUsd,
301
+ inputTokens: this._tokenUsage.inputTokens,
302
+ outputTokens: this._tokenUsage.outputTokens,
303
+ thinkingTokens: this._tokenUsage.thinkingTokens,
304
+ } : undefined;
305
+ if (exitReason === 'completed') {
306
+ this.logger.runComplete({
307
+ runId: this.runId,
308
+ agent: this.agent,
309
+ duration,
310
+ cost,
311
+ });
312
+ telemetry.recordRunComplete(this.agent, this.model, duration, cost);
313
+ telemetry.endSpanSuccess(this._runSpan, {
314
+ exitReason,
315
+ durationMs: duration,
316
+ ...(cost || {}),
317
+ });
318
+ }
319
+ else {
320
+ const error = this._runError || { message: `Run failed with reason: ${exitReason}` };
321
+ this.logger.runError({
322
+ runId: this.runId,
323
+ agent: this.agent,
324
+ error,
325
+ });
326
+ telemetry.recordRunError(this.agent, this.model, error.message, cost);
327
+ telemetry.endSpanError(this._runSpan, error.message, {
328
+ exitReason,
329
+ durationMs: duration,
330
+ ...(cost || {}),
331
+ });
332
+ }
333
+ // Wake all waiting iterators with done = true.
334
+ for (const iter of this._iterators) {
335
+ if (iter.waiting !== null) {
336
+ const resolve = iter.waiting;
337
+ iter.waiting = null;
338
+ resolve({ value: undefined, done: true });
339
+ }
340
+ }
341
+ // Resolve the result promise (or queue for lazy creation).
342
+ const result = this._buildResult(exitReason);
343
+ if (this._resolveResult) {
344
+ this._resolveResult(result);
345
+ }
346
+ else {
347
+ this._pendingResult = result;
348
+ }
349
+ }
350
+ // ── AsyncIterable ─────────────────────────────────────────────────────────
351
+ [Symbol.asyncIterator]() {
352
+ const iterState = {
353
+ position: 0,
354
+ waiting: null,
355
+ };
356
+ this._iterators.add(iterState);
357
+ const self = this;
358
+ return {
359
+ next() {
360
+ // Replay buffered events that this iterator hasn't consumed yet.
361
+ if (iterState.position < self._buffer.length) {
362
+ const event = self._buffer[iterState.position++];
363
+ return Promise.resolve({ value: event, done: false });
364
+ }
365
+ // If the run is done and we've consumed all buffered events, terminate.
366
+ if (self._done) {
367
+ self._iterators.delete(iterState);
368
+ return Promise.resolve({ value: undefined, done: true });
369
+ }
370
+ // Wait for the next event.
371
+ return new Promise((resolve) => {
372
+ iterState.waiting = resolve;
373
+ });
374
+ },
375
+ return() {
376
+ iterState.waiting = null;
377
+ self._iterators.delete(iterState);
378
+ return Promise.resolve({ value: undefined, done: true });
379
+ },
380
+ };
381
+ }
382
+ // ── EventEmitter ──────────────────────────────────────────────────────────
383
+ on(type, handler) {
384
+ const list = this._handlers.get(type) ?? [];
385
+ list.push(handler);
386
+ this._handlers.set(type, list);
387
+ return this;
388
+ }
389
+ off(type, handler) {
390
+ const list = this._handlers.get(type);
391
+ if (!list)
392
+ return this;
393
+ const idx = list.indexOf(handler);
394
+ if (idx !== -1)
395
+ list.splice(idx, 1);
396
+ return this;
397
+ }
398
+ once(type, handler) {
399
+ const wrapper = (event) => {
400
+ this.off(type, wrapper);
401
+ handler(event);
402
+ };
403
+ return this.on(type, wrapper);
404
+ }
405
+ // ── Thenable ──────────────────────────────────────────────────────────────
406
+ /** Lazily create the result promise per spec §2. */
407
+ _ensureResultPromise() {
408
+ if (!this._resultPromise) {
409
+ if (this._pendingResult) {
410
+ // complete() was called before anyone accessed the promise.
411
+ this._resultPromise = Promise.resolve(this._pendingResult);
412
+ }
413
+ else {
414
+ this._resultPromise = new Promise((resolve) => {
415
+ this._resolveResult = resolve;
416
+ });
417
+ }
418
+ }
419
+ return this._resultPromise;
420
+ }
421
+ get then() {
422
+ const p = this._ensureResultPromise();
423
+ return p.then.bind(p);
424
+ }
425
+ get catch() {
426
+ const p = this._ensureResultPromise();
427
+ return p.catch.bind(p);
428
+ }
429
+ get finally() {
430
+ const p = this._ensureResultPromise();
431
+ return p.finally.bind(p);
432
+ }
433
+ result() {
434
+ return this._ensureResultPromise();
435
+ }
436
+ /** @internal Bind the active runtime input transport. */
437
+ bindInputTransport(writer) {
438
+ this._inputTransport = writer;
439
+ }
440
+ /** @internal Bind the active runtime interaction transport. */
441
+ bindInteractionTransport(writer) {
442
+ this._interactionTransport = writer;
443
+ }
444
+ // ── Interaction methods ───────────────────────────────────────────────────
445
+ async send(text) {
446
+ this._assertActive();
447
+ if (!text) {
448
+ throw new AgentMuxError('VALIDATION_ERROR', 'send() requires non-empty text', false);
449
+ }
450
+ await this._sendNow(text);
451
+ }
452
+ async queue(prompt, options) {
453
+ await this._enqueueDeferredPrompt('queue', prompt, options?.when ?? 'next-turn');
454
+ }
455
+ async approve(detail) {
456
+ this._assertActive();
457
+ const pending = this.interaction.pending.filter((p) => p.type === 'approval');
458
+ if (pending.length === 0) {
459
+ throw new AgentMuxError('NO_PENDING_INTERACTION', 'No pending approval interaction', false);
460
+ }
461
+ await this.interaction.respond(pending[0].id, { type: 'approve', detail });
462
+ }
463
+ async deny(reason) {
464
+ this._assertActive();
465
+ const pending = this.interaction.pending.filter((p) => p.type === 'approval');
466
+ if (pending.length === 0) {
467
+ throw new AgentMuxError('NO_PENDING_INTERACTION', 'No pending approval interaction', false);
468
+ }
469
+ await this.interaction.respond(pending[0].id, { type: 'deny', reason });
470
+ }
471
+ async continue(prompt) {
472
+ this._assertActive();
473
+ if (!prompt) {
474
+ throw new AgentMuxError('VALIDATION_ERROR', 'continue() requires non-empty prompt', false);
475
+ }
476
+ // Semantically equivalent to send() — adapters may differentiate later.
477
+ await this.send(prompt);
478
+ }
479
+ async steer(prompt, options) {
480
+ await this._enqueueDeferredPrompt('steer', prompt, options?.when ?? 'after-response');
481
+ }
482
+ // ── Control methods ───────────────────────────────────────────────────────
483
+ async interrupt() {
484
+ this._assertActive();
485
+ this.transitionTo('interrupted');
486
+ }
487
+ async abort() {
488
+ if (isTerminal(this._state)) {
489
+ // No-op for already-terminated runs.
490
+ return;
491
+ }
492
+ this.transitionTo('aborted');
493
+ }
494
+ async pause() {
495
+ this._assertActive();
496
+ this.transitionTo('paused');
497
+ }
498
+ async resume() {
499
+ this._assertActive();
500
+ this.transitionTo('running');
501
+ }
502
+ // ── Private helpers ───────────────────────────────────────────────────────
503
+ _assertActive() {
504
+ if (isTerminal(this._state)) {
505
+ throw new AgentMuxError('RUN_NOT_ACTIVE', `Run ${this.runId} is not active (state: ${this._state})`, false);
506
+ }
507
+ }
508
+ _accumulate(event) {
509
+ switch (event.type) {
510
+ case 'text_delta':
511
+ this._text += event.delta;
512
+ break;
513
+ case 'session_start':
514
+ this._sessionId = event.sessionId;
515
+ break;
516
+ case 'token_usage':
517
+ accumulateTokenUsage(this._tokenUsage, event);
518
+ break;
519
+ case 'cost':
520
+ this._cost = accumulateCost(this._cost, event);
521
+ break;
522
+ case 'turn_start':
523
+ this._turnCount = event.turnIndex + 1;
524
+ break;
525
+ case 'crash':
526
+ this._runError = {
527
+ code: 'AGENT_CRASH',
528
+ message: event.stderr || 'Agent crashed',
529
+ stderr: event.stderr,
530
+ recoverable: false,
531
+ };
532
+ break;
533
+ case 'error':
534
+ this._runError = {
535
+ code: event.code,
536
+ message: event.message,
537
+ stderr: '',
538
+ recoverable: event.recoverable,
539
+ };
540
+ break;
541
+ case 'approval_request':
542
+ this.interaction.handleApprovalRequest(event);
543
+ break;
544
+ case 'input_required':
545
+ this.interaction.handleInputRequired(event);
546
+ break;
547
+ }
548
+ }
549
+ _dispatchHandlers(event) {
550
+ const list = this._handlers.get(event.type);
551
+ if (!list || list.length === 0)
552
+ return;
553
+ // Invoke handlers in registration order; catch errors and emit debug events per spec §10.4.
554
+ for (const handler of list.slice()) {
555
+ try {
556
+ handler(event);
557
+ }
558
+ catch (err) {
559
+ // Per spec: errors in handlers are caught, logged via debug event,
560
+ // and do not prevent subsequent handlers from executing.
561
+ this._emitDebugEvent('warn', `EventEmitter handler threw: ${err instanceof Error ? err.message : String(err)}`);
562
+ }
563
+ }
564
+ }
565
+ /** Emit a debug event without recursing through _dispatchHandlers for debug handlers. */
566
+ _emitDebugEvent(level, message) {
567
+ const debugEvent = {
568
+ type: 'debug',
569
+ runId: this.runId,
570
+ agent: this.agent,
571
+ timestamp: Date.now(),
572
+ level,
573
+ message,
574
+ };
575
+ // Append to buffer and collected events, but dispatch to debug handlers
576
+ // directly to avoid infinite recursion.
577
+ if (this._collectEvents) {
578
+ this._collectedEvents.push(debugEvent);
579
+ }
580
+ this._buffer.push(debugEvent);
581
+ // Wake waiting iterators for the debug event.
582
+ for (const iter of this._iterators) {
583
+ if (iter.waiting !== null) {
584
+ const resolve = iter.waiting;
585
+ iter.waiting = null;
586
+ iter.position++;
587
+ resolve({ value: debugEvent, done: false });
588
+ }
589
+ }
590
+ // Dispatch only to 'debug' handlers (not the handler that just threw).
591
+ const debugHandlers = this._handlers.get('debug');
592
+ if (debugHandlers && debugHandlers.length > 0) {
593
+ for (const h of debugHandlers.slice()) {
594
+ try {
595
+ h(debugEvent);
596
+ }
597
+ catch {
598
+ // Swallow errors in debug handlers to prevent infinite recursion.
599
+ }
600
+ }
601
+ }
602
+ }
603
+ _buildResult(exitReason) {
604
+ const durationMs = Date.now() - this._startTime;
605
+ const tokenUsage = buildTokenUsageSummary(this._tokenUsage);
606
+ return {
607
+ runId: this.runId,
608
+ agent: this.agent,
609
+ model: this.model,
610
+ sessionId: this._sessionId,
611
+ text: this._text,
612
+ cost: this._cost,
613
+ durationMs,
614
+ exitCode: this._exitCode,
615
+ signal: this._signal,
616
+ exitReason,
617
+ tokenUsage,
618
+ turnCount: this._turnCount,
619
+ error: this._runError,
620
+ events: this._collectEvents ? [...this._collectedEvents] : [],
621
+ tags: [...this._tags],
622
+ };
623
+ }
624
+ async _handleInteractionResponse(_id, _response) {
625
+ const writer = this._interactionTransport;
626
+ if (!writer) {
627
+ return;
628
+ }
629
+ await writer(_id, _response);
630
+ }
631
+ async _sendNow(text) {
632
+ const writer = this._inputTransport;
633
+ if (!writer) {
634
+ throw new AgentMuxError('STDIN_NOT_AVAILABLE', 'Agent stdin is not available', false);
635
+ }
636
+ await writer(text);
637
+ }
638
+ async _enqueueDeferredPrompt(mode, prompt, when) {
639
+ this._assertActive();
640
+ if (!prompt) {
641
+ const method = mode === 'queue' ? 'queue()' : 'steer()';
642
+ throw new AgentMuxError('VALIDATION_ERROR', `${method} requires non-empty prompt`, false);
643
+ }
644
+ if (!this._inputTransport) {
645
+ throw new AgentMuxError('STDIN_NOT_AVAILABLE', 'Agent stdin is not available', false);
646
+ }
647
+ this._deferredPrompts.push({
648
+ id: `deferred-${++this._deferredPromptSeq}`,
649
+ mode,
650
+ prompt,
651
+ when,
652
+ });
653
+ }
654
+ _triggerDeferredPromptDelivery(event) {
655
+ if (this._deferredPrompts.length === 0 || !this._inputTransport) {
656
+ return;
657
+ }
658
+ const boundaries = this._boundariesForEvent(event);
659
+ if (boundaries.length === 0) {
660
+ return;
661
+ }
662
+ const deliverNow = this._deferredPrompts.filter((entry) => boundaries.includes(entry.when));
663
+ if (deliverNow.length === 0) {
664
+ return;
665
+ }
666
+ const pending = this._deferredPrompts.filter((entry) => !boundaries.includes(entry.when));
667
+ this._deferredPrompts.splice(0, this._deferredPrompts.length, ...pending);
668
+ this._deferredDeliveryChain = this._deferredDeliveryChain
669
+ .then(async () => {
670
+ for (const entry of deliverNow) {
671
+ try {
672
+ await this._sendNow(entry.prompt);
673
+ }
674
+ catch (err) {
675
+ this._emitDebugEvent('warn', `Deferred ${entry.mode} delivery failed: ${err instanceof Error ? err.message : String(err)}`);
676
+ }
677
+ }
678
+ })
679
+ .catch(() => { });
680
+ }
681
+ _boundariesForEvent(event) {
682
+ switch (event.type) {
683
+ case 'tool_result':
684
+ case 'tool_error':
685
+ return ['after-tool'];
686
+ case 'message_stop':
687
+ return ['after-response', 'next-turn'];
688
+ case 'turn_end':
689
+ return ['next-turn'];
690
+ default:
691
+ return [];
692
+ }
693
+ }
694
+ }
695
+ //# sourceMappingURL=run-handle-impl.js.map