@alpacakit/agents 0.2.0-beta.2 → 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.
- package/README.md +21 -0
- package/dist/agents/builtin.d.ts +2 -2
- package/dist/agents/builtin.d.ts.map +1 -1
- package/dist/agents/builtin.js +6 -4
- package/dist/agents/claude/cli.d.ts +133 -0
- package/dist/agents/claude/cli.d.ts.map +1 -0
- package/dist/agents/{claude-stream.js → claude/cli.js} +81 -10
- package/dist/agents/claude/host.d.ts +14 -0
- package/dist/agents/claude/host.d.ts.map +1 -0
- package/dist/agents/{claude.js → claude/host.js} +51 -174
- package/dist/agents/{claude.d.ts → claude/index.d.ts} +10 -11
- package/dist/agents/claude/index.d.ts.map +1 -0
- package/dist/agents/claude/index.js +134 -0
- package/dist/agents/{claude-session.d.ts → claude/session.d.ts} +3 -15
- package/dist/agents/claude/session.d.ts.map +1 -0
- package/dist/agents/claude/session.js +321 -0
- package/dist/agents/codex/cli.d.ts +132 -0
- package/dist/agents/codex/cli.d.ts.map +1 -0
- package/dist/agents/codex/cli.js +110 -0
- package/dist/agents/codex/host.d.ts +14 -0
- package/dist/agents/codex/host.d.ts.map +1 -0
- package/dist/agents/codex/host.js +221 -0
- package/dist/agents/codex/index.d.ts +14 -0
- package/dist/agents/codex/index.d.ts.map +1 -0
- package/dist/agents/codex/index.js +127 -0
- package/dist/agents/codex/session.d.ts +21 -0
- package/dist/agents/codex/session.d.ts.map +1 -0
- package/dist/agents/codex/session.js +390 -0
- package/dist/agents/openai-compatible/fetch-engine.d.ts.map +1 -1
- package/dist/agents/openai-compatible/fetch-engine.js +3 -2
- package/dist/agents/openai-compatible/pi/engine.js +1 -1
- package/dist/agents/openai-compatible/pi/{session.d.ts → run.d.ts} +1 -1
- package/dist/agents/openai-compatible/pi/run.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/sandbox.d.ts.map +1 -1
- package/dist/auth/login-probe.d.ts +6 -4
- package/dist/auth/login-probe.d.ts.map +1 -1
- package/dist/auth/login-probe.js +19 -13
- package/dist/auth/material.d.ts +1 -1
- package/dist/auth/material.d.ts.map +1 -1
- package/dist/codex/app-server.d.ts.map +1 -1
- package/dist/codex/app-server.js +13 -42
- package/dist/codex/index.d.ts +1 -1
- package/dist/codex/index.d.ts.map +1 -1
- package/dist/config/agent-config-validation.d.ts +1 -1
- package/dist/config/agent-config-validation.d.ts.map +1 -1
- package/dist/config/agent-configs.d.ts.map +1 -1
- package/dist/config/agent-configs.js +3 -3
- package/dist/config/compose.d.ts.map +1 -1
- package/dist/config/config.d.ts +1 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/model.d.ts +0 -1
- package/dist/config/model.d.ts.map +1 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +1 -1
- package/dist/config/store.d.ts.map +1 -1
- package/dist/config/store.js +6 -24
- package/dist/config/support.d.ts +0 -1
- package/dist/config/support.d.ts.map +1 -1
- package/dist/config/support.js +0 -9
- package/dist/core/cli-child.d.ts +56 -0
- package/dist/core/cli-child.d.ts.map +1 -0
- package/dist/core/cli-child.js +105 -0
- package/dist/core/fs.d.ts +1 -1
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +10 -2
- package/dist/core/node-ports.d.ts.map +1 -1
- package/dist/core/node-ports.js +1 -5
- package/dist/core/session.d.ts +4 -0
- package/dist/core/session.d.ts.map +1 -1
- package/dist/fleet/store.d.ts.map +1 -1
- package/dist/fleet/store.js +4 -3
- package/dist/host/availability.d.ts +2 -1
- package/dist/host/availability.d.ts.map +1 -1
- package/dist/host/github-dir.d.ts.map +1 -1
- package/dist/host/github-dir.js +2 -1
- package/dist/host/mcp-list.d.ts.map +1 -1
- package/dist/host/mcp-list.js +5 -6
- package/dist/host/run-command.d.ts +2 -1
- package/dist/host/run-command.d.ts.map +1 -1
- package/dist/host/run-command.js +10 -19
- package/dist/host/session-files.d.ts +2 -1
- package/dist/host/session-files.d.ts.map +1 -1
- package/dist/host/session-files.js +2 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/kernel/classify.d.ts +1 -2
- package/dist/kernel/classify.d.ts.map +1 -1
- package/dist/kernel/classify.js +1 -1
- package/dist/kernel/cli-agent.d.ts.map +1 -1
- package/dist/kernel/cli-agent.js +26 -85
- package/dist/kernel/cli-login.d.ts.map +1 -1
- package/dist/kernel/cli-login.js +9 -30
- package/dist/kernel/facets.d.ts +1 -1
- package/dist/kernel/facets.d.ts.map +1 -1
- package/dist/kernel/login.d.ts.map +1 -1
- package/dist/kernel/login.js +2 -1
- package/dist/kernel/options.d.ts +1 -1
- package/dist/kernel/options.d.ts.map +1 -1
- package/dist/kernel/run.d.ts.map +1 -1
- package/dist/kernel/run.js +2 -1
- package/dist/kernel/session.d.ts +46 -39
- package/dist/kernel/session.d.ts.map +1 -1
- package/dist/kernel/session.js +128 -69
- package/dist/testing/fake-exec.d.ts +9 -10
- package/dist/testing/fake-exec.d.ts.map +1 -1
- package/dist/testing/fake-exec.js +14 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -1
- package/package.json +2 -2
- package/dist/agents/claude-session.d.ts.map +0 -1
- package/dist/agents/claude-session.js +0 -419
- package/dist/agents/claude-stream.d.ts +0 -65
- package/dist/agents/claude-stream.d.ts.map +0 -1
- package/dist/agents/claude.d.ts.map +0 -1
- package/dist/agents/codex-session.d.ts +0 -52
- package/dist/agents/codex-session.d.ts.map +0 -1
- package/dist/agents/codex-session.js +0 -495
- package/dist/agents/codex.d.ts +0 -20
- package/dist/agents/codex.d.ts.map +0 -1
- package/dist/agents/codex.js +0 -379
- package/dist/agents/openai-compatible/pi/session.d.ts.map +0 -1
- package/dist/core/process-close.d.ts +0 -11
- package/dist/core/process-close.d.ts.map +0 -1
- package/dist/core/process-close.js +0 -27
- /package/dist/agents/openai-compatible/pi/{session.js → run.js} +0 -0
package/dist/kernel/session.js
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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.
|
|
7
9
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { SESSION_EVENT_TYPE, TURN_OUTCOME, } from "../core/session.js";
|
|
11
|
-
import { endProcessGracefully } from "../core/process-close.js";
|
|
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";
|
|
12
13
|
import { OUTPUT_CHANNEL } from "../core/values.js";
|
|
13
14
|
import { EventBroadcast } from "./handle.js";
|
|
14
|
-
const OUTPUT_TAIL_CHARS = 2_000;
|
|
15
15
|
const EVENT_BASE_COST_BYTES = 64;
|
|
16
|
+
const UNKNOWN_EXIT = { code: null, signal: null };
|
|
16
17
|
/** Both hosts use UUID-shaped conversation ids; anything else never reaches a path. */
|
|
17
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
|
+
];
|
|
18
24
|
export function assertHostSessionId(id, agentId) {
|
|
19
25
|
if (HOST_SESSION_ID.test(id)) {
|
|
20
26
|
return;
|
|
@@ -31,12 +37,6 @@ function sessionEventCost(event) {
|
|
|
31
37
|
return (EVENT_BASE_COST_BYTES +
|
|
32
38
|
(event.type === SESSION_EVENT_TYPE.message ? event.text.length : 0));
|
|
33
39
|
}
|
|
34
|
-
function appendTail(current, next) {
|
|
35
|
-
const combined = current + next;
|
|
36
|
-
return combined.length <= OUTPUT_TAIL_CHARS
|
|
37
|
-
? combined
|
|
38
|
-
: combined.slice(-OUTPUT_TAIL_CHARS);
|
|
39
|
-
}
|
|
40
40
|
export function sessionStateError(agentId, reason) {
|
|
41
41
|
return new AgentError({
|
|
42
42
|
code: AGENT_ERROR_CODE.invalidInput,
|
|
@@ -47,16 +47,15 @@ export function sessionStateError(agentId, reason) {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
export function sessionExitedError(agentId, exit, stderrTail) {
|
|
50
|
-
const tail = redactSecrets(stderrTail);
|
|
51
50
|
return new AgentError({
|
|
52
51
|
code: AGENT_ERROR_CODE.failed,
|
|
53
52
|
classification: AgentError.agentFailure(AGENT_ERROR_CODE.failed),
|
|
54
53
|
message: `Agent "${agentId}" session process exited`,
|
|
55
|
-
data: {
|
|
54
|
+
data: failureEvidence({
|
|
55
|
+
stdout: "",
|
|
56
|
+
stderr: stderrTail,
|
|
56
57
|
exitCode: exit?.code ?? null,
|
|
57
|
-
|
|
58
|
-
stderrTail: tail === "" ? null : tail,
|
|
59
|
-
},
|
|
58
|
+
}),
|
|
60
59
|
agentId,
|
|
61
60
|
});
|
|
62
61
|
}
|
|
@@ -76,20 +75,11 @@ export class SessionProcess {
|
|
|
76
75
|
void process.exit.then((exit) => {
|
|
77
76
|
this.exitValue = exit;
|
|
78
77
|
}, () => {
|
|
79
|
-
this.exitValue =
|
|
78
|
+
this.exitValue = UNKNOWN_EXIT;
|
|
80
79
|
});
|
|
81
80
|
}
|
|
82
81
|
static async spawn(input) {
|
|
83
|
-
const command = await input.ctx.exec
|
|
84
|
-
if (command === null) {
|
|
85
|
-
throw new AgentError({
|
|
86
|
-
code: AGENT_ERROR_CODE.notAvailable,
|
|
87
|
-
classification: AgentError.agentFailure(AGENT_ERROR_CODE.notAvailable),
|
|
88
|
-
message: `Command "${input.command}" was not found on PATH`,
|
|
89
|
-
data: { cause: NOT_AVAILABLE_CAUSE.notInstalled },
|
|
90
|
-
agentId: input.agentId,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
82
|
+
const command = await resolveCommand(input.ctx.exec, input.command, input.ctx.env, input.agentId);
|
|
93
83
|
const process = input.ctx.exec.spawn({
|
|
94
84
|
command,
|
|
95
85
|
args: [...input.args],
|
|
@@ -114,26 +104,15 @@ export class SessionProcess {
|
|
|
114
104
|
}
|
|
115
105
|
/** Non-empty stdout lines in order; stderr is kept as a tail for errors. */
|
|
116
106
|
async *lines() {
|
|
117
|
-
|
|
107
|
+
const buffer = new LineBuffer();
|
|
118
108
|
for await (const chunk of this.process.chunks) {
|
|
119
109
|
if (chunk.channel === OUTPUT_CHANNEL.stderr) {
|
|
120
110
|
this.stderrTail = appendTail(this.stderrTail, chunk.text);
|
|
121
111
|
continue;
|
|
122
112
|
}
|
|
123
|
-
|
|
124
|
-
const lines = pending.split(/\r?\n/u);
|
|
125
|
-
pending = lines.pop() ?? "";
|
|
126
|
-
for (const line of lines) {
|
|
127
|
-
const trimmed = line.trim();
|
|
128
|
-
if (trimmed !== "") {
|
|
129
|
-
yield trimmed;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
const last = pending.trim();
|
|
134
|
-
if (last !== "") {
|
|
135
|
-
yield last;
|
|
113
|
+
yield* buffer.push(chunk.text);
|
|
136
114
|
}
|
|
115
|
+
yield* buffer.flush();
|
|
137
116
|
}
|
|
138
117
|
close() {
|
|
139
118
|
if (this.exitValue !== null) {
|
|
@@ -143,36 +122,118 @@ export class SessionProcess {
|
|
|
143
122
|
}
|
|
144
123
|
}
|
|
145
124
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* `invalid_input`, never a
|
|
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.
|
|
149
131
|
*/
|
|
150
|
-
|
|
132
|
+
class DrivenSession {
|
|
133
|
+
id;
|
|
151
134
|
agentId;
|
|
135
|
+
protocol;
|
|
136
|
+
logger;
|
|
152
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;
|
|
153
142
|
active = null;
|
|
154
|
-
/** A turn whose start the host has not acknowledged yet (Codex `turn/start` in flight). */
|
|
155
143
|
starting = false;
|
|
156
144
|
ended = false;
|
|
157
|
-
|
|
145
|
+
sink;
|
|
146
|
+
constructor(id, agentId, protocol, logger) {
|
|
147
|
+
this.id = id;
|
|
158
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();
|
|
159
170
|
}
|
|
160
171
|
get events() {
|
|
161
172
|
return this.broadcast.events;
|
|
162
173
|
}
|
|
163
|
-
|
|
164
|
-
|
|
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);
|
|
165
208
|
}
|
|
166
|
-
|
|
167
|
-
|
|
209
|
+
async close() {
|
|
210
|
+
await this.protocol.close();
|
|
211
|
+
await this.reading;
|
|
168
212
|
}
|
|
169
|
-
|
|
170
|
-
|
|
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
|
+
}
|
|
171
233
|
}
|
|
172
234
|
emit(event) {
|
|
173
235
|
this.broadcast.push(event);
|
|
174
236
|
}
|
|
175
|
-
/** Record a message for the turn's fallback text and publish it. */
|
|
176
237
|
message(text) {
|
|
177
238
|
this.active?.messages.push(text);
|
|
178
239
|
this.emit({ type: SESSION_EVENT_TYPE.message, text });
|
|
@@ -188,14 +249,6 @@ export class SessionState {
|
|
|
188
249
|
throw sessionStateError(this.agentId, "a turn is in progress; use steer() or interrupt()");
|
|
189
250
|
}
|
|
190
251
|
}
|
|
191
|
-
/** Reserve the idle slot while the host is asked to start the turn. */
|
|
192
|
-
markStarting() {
|
|
193
|
-
this.starting = true;
|
|
194
|
-
}
|
|
195
|
-
/** The host refused to start the turn; the slot is idle again. */
|
|
196
|
-
clearStarting() {
|
|
197
|
-
this.starting = false;
|
|
198
|
-
}
|
|
199
252
|
requireActive() {
|
|
200
253
|
this.requireOpen();
|
|
201
254
|
if (this.active === null) {
|
|
@@ -210,13 +263,13 @@ export class SessionState {
|
|
|
210
263
|
this.emit({ type: SESSION_EVENT_TYPE.turnStarted, turnId });
|
|
211
264
|
});
|
|
212
265
|
}
|
|
213
|
-
/** `text === null` falls back to the turn's collected messages. */
|
|
214
266
|
endTurn(outcome, text) {
|
|
215
267
|
const turn = this.active;
|
|
216
268
|
if (turn === null) {
|
|
217
269
|
return;
|
|
218
270
|
}
|
|
219
271
|
this.active = null;
|
|
272
|
+
this.approvals.clear();
|
|
220
273
|
this.emit({
|
|
221
274
|
type: SESSION_EVENT_TYPE.turnEnded,
|
|
222
275
|
turnId: turn.turnId,
|
|
@@ -228,13 +281,15 @@ export class SessionState {
|
|
|
228
281
|
text: text ?? turn.messages.join("\n"),
|
|
229
282
|
});
|
|
230
283
|
}
|
|
231
|
-
/** The
|
|
284
|
+
/** The child is gone: fail the running turn, publish `exited`, end the stream. */
|
|
232
285
|
end(exit, stderrTail) {
|
|
233
286
|
if (this.ended) {
|
|
234
287
|
return;
|
|
235
288
|
}
|
|
236
289
|
this.ended = true;
|
|
237
290
|
this.starting = false;
|
|
291
|
+
this.held.splice(0);
|
|
292
|
+
this.approvals.clear();
|
|
238
293
|
const turn = this.active;
|
|
239
294
|
this.active = null;
|
|
240
295
|
if (turn !== null) {
|
|
@@ -253,3 +308,7 @@ export class SessionState {
|
|
|
253
308
|
this.broadcast.end();
|
|
254
309
|
}
|
|
255
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,17 +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;
|
|
16
19
|
/** Runs when the caller ends stdin — script `controls.exit(...)` here for a child that exits on EOF. */
|
|
17
|
-
onStdinEnd?: (controls:
|
|
18
|
-
stdout(text: string): void;
|
|
19
|
-
stderr(text: string): void;
|
|
20
|
-
exit(exit: ExecExit): void;
|
|
21
|
-
}) => void;
|
|
20
|
+
onStdinEnd?: (controls: FakeExecControls) => void;
|
|
22
21
|
};
|
|
23
22
|
export type FakeExec = Exec & {
|
|
24
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,
|
|
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
|
-
|
|
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);
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/testing/index.js
CHANGED
|
@@ -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.2.0-beta.
|
|
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.2.0-beta.
|
|
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-session.d.ts","sourceRoot":"","sources":["../../src/agents/claude-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAkCH,OAAO,EAGL,KAAK,iBAAiB,EAKvB,MAAM,sBAAsB,CAAC;AAG9B,eAAO,MAAM,sBAAsB;;;;;;;CAOzB,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,OAAO,sBAAsB,CAAC,CAAC;AAEvE,+DAA+D;AAC/D,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;CACrC,CAAC;AA6EF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,cAAc,EAAE,SAAS,GAAG,UAAU,CAAC;CACjD,CAAC;AA4YF,wBAAgB,cAAc,CAC5B,OAAO,EAAE,oBAAoB,GAC5B,iBAAiB,CAAC,wBAAwB,CAAC,CAoB7C"}
|