@alpacakit/agents 0.1.0-beta.39 → 0.2.0-beta.3

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 (134) hide show
  1. package/README.md +142 -1
  2. package/dist/agents/builtin.d.ts +2 -2
  3. package/dist/agents/builtin.d.ts.map +1 -1
  4. package/dist/agents/builtin.js +6 -4
  5. package/dist/agents/claude/cli.d.ts +133 -0
  6. package/dist/agents/claude/cli.d.ts.map +1 -0
  7. package/dist/agents/claude/cli.js +182 -0
  8. package/dist/agents/claude/host.d.ts +14 -0
  9. package/dist/agents/claude/host.d.ts.map +1 -0
  10. package/dist/agents/claude/host.js +319 -0
  11. package/dist/agents/{claude.d.ts → claude/index.d.ts} +11 -11
  12. package/dist/agents/claude/index.d.ts.map +1 -0
  13. package/dist/agents/claude/index.js +134 -0
  14. package/dist/agents/claude/session.d.ts +25 -0
  15. package/dist/agents/claude/session.d.ts.map +1 -0
  16. package/dist/agents/claude/session.js +321 -0
  17. package/dist/agents/codex/cli.d.ts +132 -0
  18. package/dist/agents/codex/cli.d.ts.map +1 -0
  19. package/dist/agents/codex/cli.js +110 -0
  20. package/dist/agents/codex/host.d.ts +14 -0
  21. package/dist/agents/codex/host.d.ts.map +1 -0
  22. package/dist/agents/codex/host.js +221 -0
  23. package/dist/agents/codex/index.d.ts +14 -0
  24. package/dist/agents/codex/index.d.ts.map +1 -0
  25. package/dist/agents/codex/index.js +127 -0
  26. package/dist/agents/codex/session.d.ts +21 -0
  27. package/dist/agents/codex/session.d.ts.map +1 -0
  28. package/dist/agents/codex/session.js +390 -0
  29. package/dist/agents/openai-compatible/fetch-engine.d.ts.map +1 -1
  30. package/dist/agents/openai-compatible/fetch-engine.js +3 -2
  31. package/dist/agents/openai-compatible/pi/engine.js +1 -1
  32. package/dist/agents/openai-compatible/pi/{session.d.ts → run.d.ts} +1 -1
  33. package/dist/agents/openai-compatible/pi/run.d.ts.map +1 -0
  34. package/dist/agents/openai-compatible/pi/sandbox.d.ts.map +1 -1
  35. package/dist/auth/login-probe.d.ts +6 -4
  36. package/dist/auth/login-probe.d.ts.map +1 -1
  37. package/dist/auth/login-probe.js +19 -13
  38. package/dist/auth/material.d.ts +1 -1
  39. package/dist/auth/material.d.ts.map +1 -1
  40. package/dist/codex/app-server.d.ts +50 -1
  41. package/dist/codex/app-server.d.ts.map +1 -1
  42. package/dist/codex/app-server.js +140 -66
  43. package/dist/codex/index.d.ts +3 -2
  44. package/dist/codex/index.d.ts.map +1 -1
  45. package/dist/codex/index.js +2 -1
  46. package/dist/codex/protocol.d.ts +101 -0
  47. package/dist/codex/protocol.d.ts.map +1 -1
  48. package/dist/codex/protocol.js +78 -0
  49. package/dist/config/agent-config-validation.d.ts +1 -1
  50. package/dist/config/agent-config-validation.d.ts.map +1 -1
  51. package/dist/config/agent-configs.d.ts.map +1 -1
  52. package/dist/config/agent-configs.js +3 -3
  53. package/dist/config/compose.d.ts.map +1 -1
  54. package/dist/config/config.d.ts +1 -1
  55. package/dist/config/config.d.ts.map +1 -1
  56. package/dist/config/index.d.ts +2 -2
  57. package/dist/config/index.d.ts.map +1 -1
  58. package/dist/config/index.js +1 -1
  59. package/dist/config/model.d.ts +0 -1
  60. package/dist/config/model.d.ts.map +1 -1
  61. package/dist/config/schema.d.ts.map +1 -1
  62. package/dist/config/schema.js +1 -1
  63. package/dist/config/store.d.ts.map +1 -1
  64. package/dist/config/store.js +6 -24
  65. package/dist/config/support.d.ts +0 -1
  66. package/dist/config/support.d.ts.map +1 -1
  67. package/dist/config/support.js +0 -9
  68. package/dist/core/cli-child.d.ts +56 -0
  69. package/dist/core/cli-child.d.ts.map +1 -0
  70. package/dist/core/cli-child.js +105 -0
  71. package/dist/core/fs.d.ts +1 -1
  72. package/dist/core/fs.d.ts.map +1 -1
  73. package/dist/core/fs.js +10 -2
  74. package/dist/core/node-ports.d.ts.map +1 -1
  75. package/dist/core/node-ports.js +1 -5
  76. package/dist/core/session.d.ts +104 -0
  77. package/dist/core/session.d.ts.map +1 -0
  78. package/dist/core/session.js +29 -0
  79. package/dist/fleet/store.d.ts.map +1 -1
  80. package/dist/fleet/store.js +4 -3
  81. package/dist/host/availability.d.ts +2 -1
  82. package/dist/host/availability.d.ts.map +1 -1
  83. package/dist/host/github-dir.d.ts.map +1 -1
  84. package/dist/host/github-dir.js +2 -1
  85. package/dist/host/mcp-list.d.ts.map +1 -1
  86. package/dist/host/mcp-list.js +5 -6
  87. package/dist/host/run-command.d.ts +2 -1
  88. package/dist/host/run-command.d.ts.map +1 -1
  89. package/dist/host/run-command.js +10 -19
  90. package/dist/host/session-files.d.ts +23 -0
  91. package/dist/host/session-files.d.ts.map +1 -0
  92. package/dist/host/session-files.js +92 -0
  93. package/dist/index.d.ts +9 -4
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +5 -2
  96. package/dist/internal/controlled-process/supervisor-runtime.js +27 -1
  97. package/dist/kernel/classify.d.ts +1 -2
  98. package/dist/kernel/classify.d.ts.map +1 -1
  99. package/dist/kernel/classify.js +1 -1
  100. package/dist/kernel/cli-agent.d.ts.map +1 -1
  101. package/dist/kernel/cli-agent.js +26 -85
  102. package/dist/kernel/cli-login.d.ts.map +1 -1
  103. package/dist/kernel/cli-login.js +9 -30
  104. package/dist/kernel/definition.d.ts +11 -2
  105. package/dist/kernel/definition.d.ts.map +1 -1
  106. package/dist/kernel/facets.d.ts +38 -1
  107. package/dist/kernel/facets.d.ts.map +1 -1
  108. package/dist/kernel/facets.js +20 -0
  109. package/dist/kernel/login.d.ts.map +1 -1
  110. package/dist/kernel/login.js +2 -1
  111. package/dist/kernel/options.d.ts +1 -1
  112. package/dist/kernel/options.d.ts.map +1 -1
  113. package/dist/kernel/run.d.ts.map +1 -1
  114. package/dist/kernel/run.js +2 -1
  115. package/dist/kernel/runtime.d.ts +10 -6
  116. package/dist/kernel/runtime.d.ts.map +1 -1
  117. package/dist/kernel/runtime.js +4 -1
  118. package/dist/kernel/session.d.ts +104 -0
  119. package/dist/kernel/session.d.ts.map +1 -0
  120. package/dist/kernel/session.js +314 -0
  121. package/dist/testing/fake-exec.d.ts +10 -5
  122. package/dist/testing/fake-exec.d.ts.map +1 -1
  123. package/dist/testing/fake-exec.js +17 -2
  124. package/dist/testing/index.d.ts +1 -1
  125. package/dist/testing/index.d.ts.map +1 -1
  126. package/dist/testing/index.js +1 -1
  127. package/package.json +2 -2
  128. package/dist/agents/claude.d.ts.map +0 -1
  129. package/dist/agents/claude.js +0 -543
  130. package/dist/agents/codex.d.ts +0 -19
  131. package/dist/agents/codex.d.ts.map +0 -1
  132. package/dist/agents/codex.js +0 -377
  133. package/dist/agents/openai-compatible/pi/session.d.ts.map +0 -1
  134. /package/dist/agents/openai-compatible/pi/{session.js → run.js} +0 -0
@@ -0,0 +1,314 @@
1
+ /**
2
+ * The session driver (design "Kernel", session plane): as `RunDriver`
3
+ * drives an adapter's generator, `driveSession` drives a host's
4
+ * `SessionProtocol`. The driver owns the conversation's lifecycle — one
5
+ * turn at a time, the approval registry and its lifetime, the event
6
+ * stream, and the end of it all when the child is gone. A protocol only
7
+ * encodes the five verbs for its host and decodes one incoming item into
8
+ * the neutral session signals; it never keeps turn or approval state.
9
+ */
10
+ import { endProcessGracefully, failureEvidence, LineBuffer, appendTail, resolveCommand, } from "../core/cli-child.js";
11
+ import { AGENT_ERROR_CODE, AgentError } from "../core/error.js";
12
+ import { APPROVAL_DECISION, SESSION_EVENT_TYPE, TURN_OUTCOME, } from "../core/session.js";
13
+ import { OUTPUT_CHANNEL } from "../core/values.js";
14
+ import { EventBroadcast } from "./handle.js";
15
+ const EVENT_BASE_COST_BYTES = 64;
16
+ const UNKNOWN_EXIT = { code: null, signal: null };
17
+ /** Both hosts use UUID-shaped conversation ids; anything else never reaches a path. */
18
+ const HOST_SESSION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
19
+ /** Every host takes both answers (Claude allow/deny; Codex accepts `decline` for every request, T53 h2). */
20
+ const APPROVAL_DECISIONS = [
21
+ APPROVAL_DECISION.accept,
22
+ APPROVAL_DECISION.decline,
23
+ ];
24
+ export function assertHostSessionId(id, agentId) {
25
+ if (HOST_SESSION_ID.test(id)) {
26
+ return;
27
+ }
28
+ throw new AgentError({
29
+ code: AGENT_ERROR_CODE.invalidInput,
30
+ classification: AgentError.agentFailure(AGENT_ERROR_CODE.invalidInput),
31
+ message: `Agent "${agentId}" session id is not a UUID`,
32
+ data: { reason: "session id must be a UUID" },
33
+ agentId,
34
+ });
35
+ }
36
+ function sessionEventCost(event) {
37
+ return (EVENT_BASE_COST_BYTES +
38
+ (event.type === SESSION_EVENT_TYPE.message ? event.text.length : 0));
39
+ }
40
+ export function sessionStateError(agentId, reason) {
41
+ return new AgentError({
42
+ code: AGENT_ERROR_CODE.invalidInput,
43
+ classification: AgentError.agentFailure(AGENT_ERROR_CODE.invalidInput),
44
+ message: `Agent "${agentId}" session: ${reason}`,
45
+ data: { reason },
46
+ agentId,
47
+ });
48
+ }
49
+ export function sessionExitedError(agentId, exit, stderrTail) {
50
+ return new AgentError({
51
+ code: AGENT_ERROR_CODE.failed,
52
+ classification: AgentError.agentFailure(AGENT_ERROR_CODE.failed),
53
+ message: `Agent "${agentId}" session process exited`,
54
+ data: failureEvidence({
55
+ stdout: "",
56
+ stderr: stderrTail,
57
+ exitCode: exit?.code ?? null,
58
+ }),
59
+ agentId,
60
+ });
61
+ }
62
+ /**
63
+ * One interactive child held for the life of a Session: stdout is read as
64
+ * NDJSON lines, stdin stays open for incremental writes, and `close()`
65
+ * ends stdin first (both hosts exit on EOF) before escalating to signals.
66
+ */
67
+ export class SessionProcess {
68
+ process;
69
+ clock;
70
+ exitValue = null;
71
+ stderrTail = "";
72
+ constructor(process, clock) {
73
+ this.process = process;
74
+ this.clock = clock;
75
+ void process.exit.then((exit) => {
76
+ this.exitValue = exit;
77
+ }, () => {
78
+ this.exitValue = UNKNOWN_EXIT;
79
+ });
80
+ }
81
+ static async spawn(input) {
82
+ const command = await resolveCommand(input.ctx.exec, input.command, input.ctx.env, input.agentId);
83
+ const process = input.ctx.exec.spawn({
84
+ command,
85
+ args: [...input.args],
86
+ cwd: input.cwd,
87
+ env: input.ctx.env,
88
+ stdin: null,
89
+ keepStdinOpen: true,
90
+ });
91
+ return new SessionProcess(process, input.ctx.clock);
92
+ }
93
+ get exit() {
94
+ return this.process.exit;
95
+ }
96
+ get exited() {
97
+ return this.exitValue;
98
+ }
99
+ get stderr() {
100
+ return this.stderrTail;
101
+ }
102
+ writeLine(line) {
103
+ this.process.writeStdin(`${line}\n`);
104
+ }
105
+ /** Non-empty stdout lines in order; stderr is kept as a tail for errors. */
106
+ async *lines() {
107
+ const buffer = new LineBuffer();
108
+ for await (const chunk of this.process.chunks) {
109
+ if (chunk.channel === OUTPUT_CHANNEL.stderr) {
110
+ this.stderrTail = appendTail(this.stderrTail, chunk.text);
111
+ continue;
112
+ }
113
+ yield* buffer.push(chunk.text);
114
+ }
115
+ yield* buffer.flush();
116
+ }
117
+ close() {
118
+ if (this.exitValue !== null) {
119
+ return Promise.resolve(this.exitValue);
120
+ }
121
+ return endProcessGracefully(this.process, this.clock);
122
+ }
123
+ }
124
+ /**
125
+ * The Session every host gets. Exactly one turn runs at a time: `send`
126
+ * needs idle (including while the host has not yet acknowledged a start),
127
+ * `steer` / `interrupt` need active — both typed `invalid_input`, never a
128
+ * silent queue. Approval requests belong to the turn that raised them and
129
+ * die with it; the child's exit fails the running turn, publishes
130
+ * `exited`, and ends the stream.
131
+ */
132
+ class DrivenSession {
133
+ id;
134
+ agentId;
135
+ protocol;
136
+ logger;
137
+ broadcast = new EventBroadcast(undefined, sessionEventCost);
138
+ approvals = new Map();
139
+ /** Items that arrived while the host had not yet acknowledged the turn. */
140
+ held = [];
141
+ reading;
142
+ active = null;
143
+ starting = false;
144
+ ended = false;
145
+ sink;
146
+ constructor(id, agentId, protocol, logger) {
147
+ this.id = id;
148
+ this.agentId = agentId;
149
+ this.protocol = protocol;
150
+ this.logger = logger;
151
+ const driver = this;
152
+ this.sink = {
153
+ get activeTurnId() {
154
+ return driver.active?.turnId ?? null;
155
+ },
156
+ message: (text) => this.message(text),
157
+ tool: (name) => this.emit({ type: SESSION_EVENT_TYPE.tool, name }),
158
+ approvalRequested: (requestId, summary, payload) => {
159
+ this.approvals.set(requestId, payload);
160
+ this.emit({
161
+ type: SESSION_EVENT_TYPE.approvalRequested,
162
+ requestId,
163
+ summary,
164
+ decisions: APPROVAL_DECISIONS,
165
+ });
166
+ },
167
+ endTurn: (outcome, text) => this.endTurn(outcome, text),
168
+ };
169
+ this.reading = this.read();
170
+ }
171
+ get events() {
172
+ return this.broadcast.events;
173
+ }
174
+ async send(text) {
175
+ this.requireIdle();
176
+ // The slot is taken from here: a second send() during the host's
177
+ // round trip is refused, and items that beat the acknowledgement wait.
178
+ this.starting = true;
179
+ let turnId;
180
+ try {
181
+ turnId = await this.protocol.startTurn(text);
182
+ }
183
+ catch (error) {
184
+ this.starting = false;
185
+ this.held.splice(0);
186
+ throw error;
187
+ }
188
+ const turn = this.beginTurn(turnId);
189
+ for (const item of this.held.splice(0)) {
190
+ this.protocol.decode(item, this.sink);
191
+ }
192
+ return turn;
193
+ }
194
+ async steer(text) {
195
+ await this.protocol.steer(this.requireActive(), text);
196
+ }
197
+ async interrupt() {
198
+ await this.protocol.interrupt(this.requireActive());
199
+ }
200
+ async respond(requestId, decision) {
201
+ this.requireOpen();
202
+ if (!this.approvals.has(requestId)) {
203
+ throw sessionStateError(this.agentId, `no pending approval request "${requestId}"`);
204
+ }
205
+ const payload = this.approvals.get(requestId);
206
+ this.approvals.delete(requestId);
207
+ this.protocol.respond(payload, decision);
208
+ }
209
+ async close() {
210
+ await this.protocol.close();
211
+ await this.reading;
212
+ }
213
+ async read() {
214
+ try {
215
+ for await (const item of this.protocol.incoming) {
216
+ if (this.starting) {
217
+ this.held.push(item);
218
+ }
219
+ else {
220
+ this.protocol.decode(item, this.sink);
221
+ }
222
+ }
223
+ }
224
+ catch (error) {
225
+ this.logger.log("error", "session reader failed", error);
226
+ }
227
+ finally {
228
+ // Whatever happened above, the stream must end so `events` consumers
229
+ // and `close()` are released. A child whose exit rejects (spawn-time
230
+ // `error`) is gone all the same: no code, no signal.
231
+ this.end(await this.protocol.exit.catch(() => UNKNOWN_EXIT), this.protocol.stderrTail);
232
+ }
233
+ }
234
+ emit(event) {
235
+ this.broadcast.push(event);
236
+ }
237
+ message(text) {
238
+ this.active?.messages.push(text);
239
+ this.emit({ type: SESSION_EVENT_TYPE.message, text });
240
+ }
241
+ requireOpen() {
242
+ if (this.ended) {
243
+ throw sessionStateError(this.agentId, "session is closed");
244
+ }
245
+ }
246
+ requireIdle() {
247
+ this.requireOpen();
248
+ if (this.active !== null || this.starting) {
249
+ throw sessionStateError(this.agentId, "a turn is in progress; use steer() or interrupt()");
250
+ }
251
+ }
252
+ requireActive() {
253
+ this.requireOpen();
254
+ if (this.active === null) {
255
+ throw sessionStateError(this.agentId, "no turn is in progress");
256
+ }
257
+ return this.active.turnId;
258
+ }
259
+ beginTurn(turnId) {
260
+ this.starting = false;
261
+ return new Promise((resolve, reject) => {
262
+ this.active = { turnId, messages: [], resolve, reject };
263
+ this.emit({ type: SESSION_EVENT_TYPE.turnStarted, turnId });
264
+ });
265
+ }
266
+ endTurn(outcome, text) {
267
+ const turn = this.active;
268
+ if (turn === null) {
269
+ return;
270
+ }
271
+ this.active = null;
272
+ this.approvals.clear();
273
+ this.emit({
274
+ type: SESSION_EVENT_TYPE.turnEnded,
275
+ turnId: turn.turnId,
276
+ outcome,
277
+ });
278
+ turn.resolve({
279
+ turnId: turn.turnId,
280
+ outcome,
281
+ text: text ?? turn.messages.join("\n"),
282
+ });
283
+ }
284
+ /** The child is gone: fail the running turn, publish `exited`, end the stream. */
285
+ end(exit, stderrTail) {
286
+ if (this.ended) {
287
+ return;
288
+ }
289
+ this.ended = true;
290
+ this.starting = false;
291
+ this.held.splice(0);
292
+ this.approvals.clear();
293
+ const turn = this.active;
294
+ this.active = null;
295
+ if (turn !== null) {
296
+ this.emit({
297
+ type: SESSION_EVENT_TYPE.turnEnded,
298
+ turnId: turn.turnId,
299
+ outcome: TURN_OUTCOME.failed,
300
+ });
301
+ turn.reject(sessionExitedError(this.agentId, exit, stderrTail));
302
+ }
303
+ this.emit({
304
+ type: SESSION_EVENT_TYPE.exited,
305
+ code: exit.code,
306
+ signal: exit.signal,
307
+ });
308
+ this.broadcast.end();
309
+ }
310
+ }
311
+ /** Bind a host protocol to the one Session lifecycle. */
312
+ export function driveSession(input) {
313
+ return new DrivenSession(input.id, input.agentId, input.protocol, input.logger);
314
+ }
@@ -1,4 +1,11 @@
1
1
  import type { Exec, ExecChunk, ExecExit, ExecSpawnOptions, InteractiveExecSpawnOptions } from "../core/ports.js";
2
+ export type FakeExecControls = {
3
+ stdout(text: string): void;
4
+ stderr(text: string): void;
5
+ exit(exit: ExecExit): void;
6
+ /** The child never settled normally: `exit` rejects, output ends. */
7
+ fail(error: Error): void;
8
+ };
2
9
  export type FakeExecScript = {
3
10
  chunks?: ExecChunk[];
4
11
  exitCode?: number;
@@ -8,11 +15,9 @@ export type FakeExecScript = {
8
15
  hang?: boolean;
9
16
  /** Survive SIGTERM so SIGKILL escalation can be observed. */
10
17
  ignoreSigterm?: boolean;
11
- onWrite?: (text: string, controls: {
12
- stdout(text: string): void;
13
- stderr(text: string): void;
14
- exit(exit: ExecExit): void;
15
- }) => void;
18
+ onWrite?: (text: string, controls: FakeExecControls) => void;
19
+ /** Runs when the caller ends stdin — script `controls.exit(...)` here for a child that exits on EOF. */
20
+ onStdinEnd?: (controls: FakeExecControls) => void;
16
21
  };
17
22
  export type FakeExec = Exec & {
18
23
  readonly spawns: (ExecSpawnOptions | InteractiveExecSpawnOptions)[];
@@ -1 +1 @@
1
- {"version":3,"file":"fake-exec.d.ts","sourceRoot":"","sources":["../../src/testing/fake-exec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,gBAAgB,EAEhB,2BAA2B,EAC5B,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6DAA6D;IAC7D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE;QACR,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;KAC5B,KACE,IAAI,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,CAAC,gBAAgB,GAAG,2BAA2B,CAAC,EAAE,CAAC;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;IACjC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvC,CAAC;AAWF,wBAAgB,QAAQ,CAAC,OAAO,GAAE,cAAc,EAAO,GAAG,QAAQ,CAmFjE"}
1
+ {"version":3,"file":"fake-exec.d.ts","sourceRoot":"","sources":["../../src/testing/fake-exec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,gBAAgB,EAEhB,2BAA2B,EAC5B,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,qEAAqE;IACrE,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6DAA6D;IAC7D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC7D,wGAAwG;IACxG,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,CAAC,gBAAgB,GAAG,2BAA2B,CAAC,EAAE,CAAC;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;IACjC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvC,CAAC;AAWF,wBAAgB,QAAQ,CAAC,OAAO,GAAE,cAAc,EAAO,GAAG,QAAQ,CAkGjE"}
@@ -18,9 +18,12 @@ export function fakeExec(scripts = []) {
18
18
  stdinWrites.push(writes);
19
19
  const chunks = new AsyncQueue();
20
20
  let settleExit = () => { };
21
- const exit = new Promise((resolve) => {
21
+ let failExit = () => { };
22
+ const exit = new Promise((resolve, reject) => {
22
23
  settleExit = resolve;
24
+ failExit = reject;
23
25
  });
26
+ void exit.catch(() => { });
24
27
  let settleTermination = () => { };
25
28
  const termination = new Promise((resolve) => {
26
29
  settleTermination = resolve;
@@ -35,10 +38,20 @@ export function fakeExec(scripts = []) {
35
38
  settleTermination();
36
39
  settleExit(value);
37
40
  };
41
+ const fail = (error) => {
42
+ if (finished) {
43
+ return;
44
+ }
45
+ finished = true;
46
+ chunks.end();
47
+ settleTermination();
48
+ failExit(error);
49
+ };
38
50
  const controls = {
39
51
  stdout: (text) => chunks.push({ channel: OUTPUT_CHANNEL.stdout, text }),
40
52
  stderr: (text) => chunks.push({ channel: OUTPUT_CHANNEL.stderr, text }),
41
53
  exit: finish,
54
+ fail,
42
55
  };
43
56
  for (const chunk of script.chunks ?? []) {
44
57
  chunks.push(chunk);
@@ -56,7 +69,9 @@ export function fakeExec(scripts = []) {
56
69
  writes.push(text);
57
70
  script.onWrite?.(text, controls);
58
71
  },
59
- endStdin: () => { },
72
+ endStdin: () => {
73
+ script.onStdinEnd?.(controls);
74
+ },
60
75
  kill: (signal) => {
61
76
  killSignals.push(signal);
62
77
  if (signal === PROCESS_SIGNAL.sigterm &&
@@ -6,6 +6,6 @@
6
6
  export { FAKE_AGENT_BEHAVIOR_KIND, type FakeAgentBehavior, type FakeAgentCall, type FakeAgentDefinition, fakeAgent, } from "./fake-agent.js";
7
7
  export { type FakeClock, fakeClock } from "./fake-clock.js";
8
8
  export { createFakeControlledExec, type FakeControlledExec, type FakeControlledExecScript, } from "./fake-controlled-exec.js";
9
- export { type FakeExec, type FakeExecScript, fakeExec } from "./fake-exec.js";
9
+ export { type FakeExec, type FakeExecControls, type FakeExecScript, fakeExec, } from "./fake-exec.js";
10
10
  export { fakeSecretResolver } from "./fake-secrets.js";
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,SAAS,GACV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,GAC9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,SAAS,GACV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,GAC9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,QAAQ,GACT,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -6,5 +6,5 @@
6
6
  export { FAKE_AGENT_BEHAVIOR_KIND, fakeAgent, } from "./fake-agent.js";
7
7
  export { fakeClock } from "./fake-clock.js";
8
8
  export { createFakeControlledExec, } from "./fake-controlled-exec.js";
9
- export { fakeExec } from "./fake-exec.js";
9
+ export { fakeExec, } from "./fake-exec.js";
10
10
  export { fakeSecretResolver } from "./fake-secrets.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpacakit/agents",
3
- "version": "0.1.0-beta.39",
3
+ "version": "0.2.0-beta.3",
4
4
  "description": "Run AI coding agents programmatically: execute, stream, classify failures, fail over.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "zod": "^4.3.6",
48
- "@alpacakit/core": "0.1.0-beta.39"
48
+ "@alpacakit/core": "0.2.0-beta.3"
49
49
  },
50
50
  "devDependencies": {
51
51
  "typescript": "^5.9.3",
@@ -1 +0,0 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+CH,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/D,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAC7D,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAsK1D;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,cAAc,CA0CvD;AA6TD,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;CAClD,CAAC;AAEF,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,eAAe,CAAC,QAAQ,CAAC,CAgF7E"}