@a-dray/aglib 0.1.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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/dist/agent.d.ts +158 -0
  4. package/dist/agent.js +2 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/content.d.ts +45 -0
  7. package/dist/content.js +11 -0
  8. package/dist/content.js.map +1 -0
  9. package/dist/harness/adapters/acp/index.d.ts +92 -0
  10. package/dist/harness/adapters/acp/index.js +479 -0
  11. package/dist/harness/adapters/acp/index.js.map +1 -0
  12. package/dist/harness/adapters/acp/rpc.d.ts +23 -0
  13. package/dist/harness/adapters/acp/rpc.js +103 -0
  14. package/dist/harness/adapters/acp/rpc.js.map +1 -0
  15. package/dist/harness/adapters/native/compaction.d.ts +16 -0
  16. package/dist/harness/adapters/native/compaction.js +55 -0
  17. package/dist/harness/adapters/native/compaction.js.map +1 -0
  18. package/dist/harness/adapters/native/index.d.ts +2 -0
  19. package/dist/harness/adapters/native/index.js +2 -0
  20. package/dist/harness/adapters/native/index.js.map +1 -0
  21. package/dist/harness/adapters/native/loop.d.ts +25 -0
  22. package/dist/harness/adapters/native/loop.js +153 -0
  23. package/dist/harness/adapters/native/loop.js.map +1 -0
  24. package/dist/harness/harness.d.ts +129 -0
  25. package/dist/harness/harness.js +2 -0
  26. package/dist/harness/harness.js.map +1 -0
  27. package/dist/harness/index.d.ts +3 -0
  28. package/dist/harness/index.js +2 -0
  29. package/dist/harness/index.js.map +1 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +5 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/json.d.ts +4 -0
  34. package/dist/json.js +2 -0
  35. package/dist/json.js.map +1 -0
  36. package/dist/model/adapters/anthropic/index.d.ts +34 -0
  37. package/dist/model/adapters/anthropic/index.js +271 -0
  38. package/dist/model/adapters/anthropic/index.js.map +1 -0
  39. package/dist/model/adapters/fake/index.d.ts +27 -0
  40. package/dist/model/adapters/fake/index.js +37 -0
  41. package/dist/model/adapters/fake/index.js.map +1 -0
  42. package/dist/model/adapters/openai-compatible/index.d.ts +29 -0
  43. package/dist/model/adapters/openai-compatible/index.js +226 -0
  44. package/dist/model/adapters/openai-compatible/index.js.map +1 -0
  45. package/dist/model/conformance.d.ts +210 -0
  46. package/dist/model/conformance.js +427 -0
  47. package/dist/model/conformance.js.map +1 -0
  48. package/dist/model/index.d.ts +2 -0
  49. package/dist/model/index.js +2 -0
  50. package/dist/model/index.js.map +1 -0
  51. package/dist/model/model.d.ts +85 -0
  52. package/dist/model/model.js +8 -0
  53. package/dist/model/model.js.map +1 -0
  54. package/dist/result.d.ts +23 -0
  55. package/dist/result.js +3 -0
  56. package/dist/result.js.map +1 -0
  57. package/dist/run.d.ts +10 -0
  58. package/dist/run.js +477 -0
  59. package/dist/run.js.map +1 -0
  60. package/dist/sandbox/adapters/docker.d.ts +22 -0
  61. package/dist/sandbox/adapters/docker.js +443 -0
  62. package/dist/sandbox/adapters/docker.js.map +1 -0
  63. package/dist/sandbox/adapters/local.d.ts +11 -0
  64. package/dist/sandbox/adapters/local.js +284 -0
  65. package/dist/sandbox/adapters/local.js.map +1 -0
  66. package/dist/sandbox/conformance.d.ts +73 -0
  67. package/dist/sandbox/conformance.js +309 -0
  68. package/dist/sandbox/conformance.js.map +1 -0
  69. package/dist/sandbox/index.d.ts +1 -0
  70. package/dist/sandbox/index.js +2 -0
  71. package/dist/sandbox/index.js.map +1 -0
  72. package/dist/sandbox/sandbox.d.ts +172 -0
  73. package/dist/sandbox/sandbox.js +2 -0
  74. package/dist/sandbox/sandbox.js.map +1 -0
  75. package/dist/session/entry.d.ts +196 -0
  76. package/dist/session/entry.js +2 -0
  77. package/dist/session/entry.js.map +1 -0
  78. package/dist/session/index.d.ts +2 -0
  79. package/dist/session/index.js +2 -0
  80. package/dist/session/index.js.map +1 -0
  81. package/dist/session/log.d.ts +20 -0
  82. package/dist/session/log.js +24 -0
  83. package/dist/session/log.js.map +1 -0
  84. package/dist/session/messages.d.ts +42 -0
  85. package/dist/session/messages.js +99 -0
  86. package/dist/session/messages.js.map +1 -0
  87. package/dist/store/adapters/sqlite.d.ts +20 -0
  88. package/dist/store/adapters/sqlite.js +365 -0
  89. package/dist/store/adapters/sqlite.js.map +1 -0
  90. package/dist/store/conformance.d.ts +66 -0
  91. package/dist/store/conformance.js +580 -0
  92. package/dist/store/conformance.js.map +1 -0
  93. package/dist/store/index.d.ts +1 -0
  94. package/dist/store/index.js +2 -0
  95. package/dist/store/index.js.map +1 -0
  96. package/dist/store/store.d.ts +244 -0
  97. package/dist/store/store.js +2 -0
  98. package/dist/store/store.js.map +1 -0
  99. package/dist/tools/execute.d.ts +24 -0
  100. package/dist/tools/execute.js +105 -0
  101. package/dist/tools/execute.js.map +1 -0
  102. package/dist/tools/index.d.ts +2 -0
  103. package/dist/tools/index.js +2 -0
  104. package/dist/tools/index.js.map +1 -0
  105. package/dist/tools/tool.d.ts +106 -0
  106. package/dist/tools/tool.js +34 -0
  107. package/dist/tools/tool.js.map +1 -0
  108. package/package.json +119 -0
@@ -0,0 +1,479 @@
1
+ import { textOf } from "../../../content.js";
2
+ import { err, ok } from "../../../result.js";
3
+ import { createRpc } from "./rpc.js";
4
+ const PROTOCOL_VERSION = 1;
5
+ /**
6
+ * Runs a foreign coding agent over the Agent Client Protocol.
7
+ *
8
+ * The adapter is not a remote control. It does three things the raw protocol
9
+ * does not: it normalises whatever the agent did into this package's entries,
10
+ * so one durable log describes a Claude Code turn and a native-loop turn
11
+ * identically; it puts the agent's own tools under the application's decision
12
+ * function, one call at a time; and it hands the agent tool servers of ours, so
13
+ * agents can dispatch and message each other.
14
+ */
15
+ export function createAcpHarness(options) {
16
+ return {
17
+ id: options.id,
18
+ // Its tools are its own. We gate them and we record them; we do not claim
19
+ // to have validated arguments we never had a schema for.
20
+ toolUse: "harness",
21
+ // The agent owns its context. Our entries describe what it did, and cannot
22
+ // by themselves put it back mid-turn.
23
+ recovery: "none",
24
+ run: (context) => runTurn(options, context),
25
+ };
26
+ }
27
+ async function runTurn(options, context) {
28
+ const started = await options.sandbox.spawn({
29
+ command: options.agent.command,
30
+ cwd: options.sandbox.root,
31
+ ...(options.agent.env ? { env: options.agent.env } : {}),
32
+ });
33
+ if (!started.ok)
34
+ return { status: "failed", error: started.error };
35
+ const rpc = createRpc(started.value);
36
+ const turn = createTurn(options, context, rpc);
37
+ try {
38
+ return await turn.run();
39
+ }
40
+ finally {
41
+ rpc.close();
42
+ }
43
+ }
44
+ /** Cumulative state for one activation. Discarded with the process that produced it. */
45
+ function createTurn(options, context, rpc) {
46
+ const sandbox = options.sandbox;
47
+ const terminals = new Map();
48
+ const calls = new Map();
49
+ let text = "";
50
+ let sessionId = "";
51
+ let terminalCount = 0;
52
+ /**
53
+ * Whether an update describes this activation, or the last one.
54
+ *
55
+ * `session/load` replays the whole conversation back as `session/update`
56
+ * notifications — measured against `codex-acp`, which answers a load with the
57
+ * user message and the agent message of every turn before it. Those are the
58
+ * same words this log already committed when they were first said, so
59
+ * treating them as output would append the entire history again on every
60
+ * resumed activation, and prepend it to whatever the new turn actually says.
61
+ * Nothing before the prompt belongs to the run the prompt starts.
62
+ */
63
+ let live = false;
64
+ return { run };
65
+ async function run() {
66
+ serveFiles();
67
+ serveTerminals();
68
+ servePermission();
69
+ rpc.onNotify("session/update", (params) => { void receive(params); });
70
+ const ready = await rpc.request("initialize", {
71
+ protocolVersion: PROTOCOL_VERSION,
72
+ clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: true },
73
+ clientInfo: { name: "aglib", version: "0" },
74
+ });
75
+ if (!ready.ok)
76
+ return { status: "failed", error: ready.error };
77
+ const capabilities = field(ready.value, "agentCapabilities");
78
+ const opened = await openSession(truthy(field(capabilities, "loadSession")));
79
+ if (!opened.ok)
80
+ return { status: "failed", error: opened.error };
81
+ sessionId = opened.value;
82
+ options.onSession?.(sessionId);
83
+ const configured = await configure(opened.value, opened.opened ?? undefined);
84
+ if (!configured.ok)
85
+ return { status: "failed", error: configured.error };
86
+ const cancel = () => rpc.notify("session/cancel", { sessionId });
87
+ context.signal.addEventListener("abort", cancel, { once: true });
88
+ // Anything the agent replayed while opening has already been dispatched:
89
+ // one stdio stream, read in order, so a load's echo is behind us here.
90
+ live = true;
91
+ const answered = await rpc.request("session/prompt", {
92
+ sessionId,
93
+ prompt: [{ type: "text", text: inputFor(context) }],
94
+ });
95
+ context.signal.removeEventListener("abort", cancel);
96
+ for (const terminal of terminals.values())
97
+ terminal.process.kill();
98
+ if (!answered.ok)
99
+ return { status: "failed", error: answered.error };
100
+ await flushText();
101
+ return outcome(String(field(answered.value, "stopReason") ?? "end_turn"));
102
+ }
103
+ async function openSession(canLoad) {
104
+ const parameters = {
105
+ cwd: sandbox.root,
106
+ mcpServers: (options.mcpServers ?? []).map((server) => ({
107
+ type: "stdio", name: server.name, command: server.command,
108
+ args: [...(server.args ?? [])], env: [...(server.env ?? [])],
109
+ })),
110
+ };
111
+ // The whole answer is carried out, not one field of it: what an agent
112
+ // publishes about a session has changed shape once already.
113
+ if (options.resume && canLoad) {
114
+ const loaded = await rpc.request("session/load", { ...parameters, sessionId: options.resume });
115
+ if (loaded.ok)
116
+ return { ...ok(options.resume), opened: loaded.value };
117
+ }
118
+ const created = await rpc.request("session/new", parameters);
119
+ if (!created.ok)
120
+ return { ...created, opened: null };
121
+ const id = field(created.value, "sessionId");
122
+ if (typeof id !== "string") {
123
+ return { ...err({ code: "protocol", message: "The agent opened no session.", retryable: false }), opened: null };
124
+ }
125
+ return { ...ok(id), opened: created.value };
126
+ }
127
+ /**
128
+ * Read what the agent lets us change, report it, and set what was asked for.
129
+ *
130
+ * Two shapes, because the protocol changed one into the other: `models` used
131
+ * to be its own field on the session, and is now one entry in `configOptions`
132
+ * carrying `category: "model"`. Both are read, so an agent on either version
133
+ * is understood, and neither is required.
134
+ *
135
+ * A resumed session reads this from the `session/load` answer, and an agent
136
+ * that answers a load without republishing is one whose options we do not
137
+ * currently know. Setting one from what a previous session said would be
138
+ * asserting a capability this connection never claimed — so a request for an
139
+ * option that is not published fails, naming what is. Measured against
140
+ * `claude-agent-acp`, which does republish on load.
141
+ */
142
+ async function configure(id, opened) {
143
+ const { options: published, legacyModel } = readOptions(opened);
144
+ if (published.length)
145
+ options.onConfig?.(published);
146
+ if (options.mode) {
147
+ const set = await rpc.request("session/set_mode", { sessionId: id, modeId: options.mode });
148
+ if (!set.ok)
149
+ return set;
150
+ }
151
+ const wanted = { ...options.select };
152
+ if (options.model) {
153
+ const selector = published.find((option) => option.category === "model");
154
+ if (!selector) {
155
+ return err({
156
+ code: "unsupported",
157
+ message: `${options.id} publishes no model selector, so '${options.model}' cannot be chosen over the protocol. Point it at a model through its environment instead.`,
158
+ retryable: false,
159
+ });
160
+ }
161
+ wanted[selector.id] = options.model;
162
+ }
163
+ for (const [option, value] of Object.entries(wanted)) {
164
+ const published_ = published.find((candidate) => candidate.id === option);
165
+ if (!published_) {
166
+ return err({
167
+ code: "unsupported",
168
+ message: `${options.id} offers no option '${option}'. It offers: ${published.map((o) => o.id).join(", ") || "none"}.`,
169
+ retryable: false,
170
+ });
171
+ }
172
+ if (published_.current === value)
173
+ continue;
174
+ if (published_.choices && !published_.choices.some((choice) => choice.id === value)) {
175
+ return err({
176
+ code: "unsupported",
177
+ message: `${options.id} does not offer '${String(value)}' for ${published_.name}. It offers: ${published_.choices.map((c) => c.id).join(", ")}.`,
178
+ retryable: false,
179
+ });
180
+ }
181
+ const legacy = published_.id === legacyModel;
182
+ const set = await rpc.request(legacy ? "session/set_model" : "session/set_config_option", legacy
183
+ ? { sessionId: id, modelId: value }
184
+ : { sessionId: id, configId: published_.id, value, ...(typeof value === "boolean" ? { type: "boolean" } : {}) });
185
+ if (!set.ok)
186
+ return set;
187
+ }
188
+ return ok(undefined);
189
+ }
190
+ // ---- Normalising what the agent did into entries -------------------------
191
+ /**
192
+ * A tool call becomes entries when it finishes, in one commit: the assistant
193
+ * turn that asked for it, the start, and the result. Committing the pieces as
194
+ * they arrive would leave half-states in a log whose whole purpose is that it
195
+ * can be replayed, and a live viewer is served by `emit` instead.
196
+ */
197
+ async function receive(params) {
198
+ if (!live)
199
+ return;
200
+ const update = field(params, "update");
201
+ const kind = String(field(update, "sessionUpdate") ?? "");
202
+ if (kind === "agent_message_chunk") {
203
+ const chunk = String(field(field(update, "content"), "text") ?? "");
204
+ text += chunk;
205
+ context.emit({ type: "text.delta", text: chunk });
206
+ return;
207
+ }
208
+ if (kind === "agent_thought_chunk") {
209
+ context.emit({ type: "reasoning.delta", text: String(field(field(update, "content"), "text") ?? "") });
210
+ return;
211
+ }
212
+ if (kind === "usage_update") {
213
+ // Dropped deliberately. This protocol reports a spend figure and no token
214
+ // counts, and `Usage` carries tokens and no money, so there is nothing
215
+ // here that could be recorded without inventing it. An agent behind this
216
+ // harness is unaccounted for, and the capability table says so.
217
+ return;
218
+ }
219
+ if (kind !== "tool_call" && kind !== "tool_call_update")
220
+ return;
221
+ const callId = String(field(update, "toolCallId") ?? "");
222
+ if (!callId)
223
+ return;
224
+ const existing = calls.get(callId) ?? { name: "", input: null };
225
+ const name = nameOf(update);
226
+ const raw = field(update, "rawInput");
227
+ calls.set(callId, {
228
+ name: name || existing.name || "tool",
229
+ input: raw === undefined || raw === null ? existing.input : raw,
230
+ ...(existing.output === undefined ? {} : { output: existing.output }),
231
+ });
232
+ context.emit({ type: "tool.progress", callId, data: update ?? null });
233
+ const status = String(field(update, "status") ?? "");
234
+ if (status !== "completed" && status !== "failed")
235
+ return;
236
+ const call = calls.get(callId);
237
+ calls.delete(callId);
238
+ const result = resultOf(update, status === "failed");
239
+ await context.commit(assistantAsking(call, callId).concat({ type: "tool.started", runId: context.runId, callId }, { type: "tool.finished", runId: context.runId, callId, result }));
240
+ }
241
+ function assistantAsking(call, callId) {
242
+ const asked = { callId, name: call.name, arguments: JSON.stringify(call.input ?? {}) };
243
+ const said = text;
244
+ text = "";
245
+ return [{ type: "assistant", runId: context.runId, content: said, calls: [asked] }];
246
+ }
247
+ async function flushText() {
248
+ if (!text)
249
+ return;
250
+ const said = text;
251
+ text = "";
252
+ await context.commit([{ type: "assistant", runId: context.runId, content: said }]);
253
+ }
254
+ function outcome(stopReason) {
255
+ if (stopReason === "cancelled")
256
+ return { status: "cancelled" };
257
+ if (stopReason === "max_tokens" || stopReason === "max_turn_requests") {
258
+ return {
259
+ status: "failed",
260
+ error: { code: "limit", message: `${options.id} stopped: ${stopReason}.`, retryable: false },
261
+ };
262
+ }
263
+ return { status: "completed", output: lastSaid(context) };
264
+ }
265
+ // ---- Serving the agent's requests from the sandbox ----------------------
266
+ function serveFiles() {
267
+ rpc.on("fs/read_text_file", async (params) => {
268
+ const read = await sandbox.readFile({ path: String(field(params, "path") ?? "") });
269
+ return read.ok ? ok({ content: read.value }) : read;
270
+ });
271
+ rpc.on("fs/write_text_file", async (params) => {
272
+ const written = await sandbox.writeFile({
273
+ path: String(field(params, "path") ?? ""),
274
+ content: String(field(params, "content") ?? ""),
275
+ });
276
+ return written.ok ? ok(null) : written;
277
+ });
278
+ }
279
+ /**
280
+ * The protocol's terminal, served from the sandbox.
281
+ *
282
+ * Worth knowing before relying on it: an agent uses this only if it chooses
283
+ * to, and the one measured here does not. `@agentclientprotocol/claude-agent-acp`
284
+ * calls exactly four client methods — `fs/read_text_file`,
285
+ * `fs/write_text_file`, `session/request_permission`, `session/update` — and
286
+ * emits a terminal block for display while running the command itself. So
287
+ * these handlers are correct and, for that agent, never reached. Containment
288
+ * comes from starting the agent inside the sandbox, not from serving this.
289
+ */
290
+ function serveTerminals() {
291
+ rpc.on("terminal/create", async (params) => {
292
+ const argv = ["/bin/sh", "-lc", [field(params, "command"), ...(asArray(field(params, "args")))].filter(Boolean).join(" ")];
293
+ const cwd = field(params, "cwd");
294
+ const spawned = await sandbox.spawn({
295
+ command: argv,
296
+ ...(typeof cwd === "string" ? { cwd } : {}),
297
+ });
298
+ if (!spawned.ok)
299
+ return spawned;
300
+ const id = `terminal-${++terminalCount}`;
301
+ let collected = "";
302
+ const drain = async (stream) => {
303
+ const decoder = new TextDecoder();
304
+ for await (const chunk of stream) {
305
+ collected += decoder.decode(chunk, { stream: true });
306
+ }
307
+ };
308
+ void drain(spawned.value.stdout);
309
+ void drain(spawned.value.stderr);
310
+ terminals.set(id, { process: spawned.value, output: () => collected, exit: spawned.value.exited });
311
+ return ok({ terminalId: id });
312
+ });
313
+ rpc.on("terminal/output", async (params) => {
314
+ const terminal = terminals.get(String(field(params, "terminalId") ?? ""));
315
+ if (!terminal)
316
+ return err({ code: "not-found", message: "No such terminal.", retryable: false });
317
+ const finished = await Promise.race([terminal.exit, Promise.resolve(undefined)]);
318
+ return ok({
319
+ output: terminal.output(),
320
+ truncated: false,
321
+ ...(finished === undefined ? {} : { exitStatus: { exitCode: finished, signal: null } }),
322
+ });
323
+ });
324
+ rpc.on("terminal/wait_for_exit", async (params) => {
325
+ const terminal = terminals.get(String(field(params, "terminalId") ?? ""));
326
+ if (!terminal)
327
+ return err({ code: "not-found", message: "No such terminal.", retryable: false });
328
+ return ok({ exitCode: await terminal.exit, signal: null });
329
+ });
330
+ rpc.on("terminal/kill", async (params) => {
331
+ terminals.get(String(field(params, "terminalId") ?? ""))?.process.kill();
332
+ return ok(null);
333
+ });
334
+ rpc.on("terminal/release", async (params) => {
335
+ const id = String(field(params, "terminalId") ?? "");
336
+ terminals.get(id)?.process.kill();
337
+ terminals.delete(id);
338
+ return ok(null);
339
+ });
340
+ }
341
+ /**
342
+ * The agent's own tools, one call at a time, through the application's rule.
343
+ *
344
+ * The agent is blocked on this reply, so the answer is yes or no and nothing
345
+ * else. That constraint is why the permission model has two outcomes rather
346
+ * than three: a rule that could park a call would be honourable in our own
347
+ * loop and unhonourable here, which is the ragged surface the two-outcome
348
+ * shape avoids.
349
+ */
350
+ function servePermission() {
351
+ rpc.on("session/request_permission", async (params) => {
352
+ const call = field(params, "toolCall");
353
+ const callId = String(field(call, "toolCallId") ?? "");
354
+ const options_ = asArray(field(params, "options"));
355
+ const pick = (...kinds) => {
356
+ for (const kind of kinds) {
357
+ const found = options_.find((option) => String(field(option, "kind") ?? "") === kind);
358
+ if (found)
359
+ return String(field(found, "optionId") ?? "");
360
+ }
361
+ return undefined;
362
+ };
363
+ const selected = (optionId) => optionId === undefined
364
+ ? ok({ outcome: { outcome: "cancelled" } })
365
+ : ok({ outcome: { outcome: "selected", optionId } });
366
+ if (!options.decide)
367
+ return selected(pick("allow_once", "allow_always"));
368
+ const spec = {
369
+ name: nameOf(call) || "tool",
370
+ description: String(field(call, "title") ?? ""),
371
+ parameters: {},
372
+ };
373
+ const decided = await options.decide({
374
+ tool: spec,
375
+ input: (field(call, "rawInput") ?? {}),
376
+ sessionId: context.sessionId,
377
+ runId: context.runId,
378
+ callId,
379
+ });
380
+ if (decided.action === "execute")
381
+ return selected(pick("allow_once", "allow_always"));
382
+ return selected(pick("reject_once", "reject_always"));
383
+ });
384
+ }
385
+ }
386
+ /**
387
+ * What the agent published, from either shape the protocol has used.
388
+ *
389
+ * `configOptions` is current. `models` is what the same fact was called before
390
+ * it was generalised, and is normalised into an option categorised "model" so
391
+ * a caller never has to know which version answered.
392
+ */
393
+ function readOptions(opened) {
394
+ const found = [];
395
+ for (const raw of asArray(field(opened, "configOptions"))) {
396
+ const id = field(raw, "id");
397
+ if (typeof id !== "string")
398
+ continue;
399
+ const choices = asArray(field(raw, "options")).flatMap((choice) => {
400
+ const value = field(choice, "id") ?? field(choice, "value");
401
+ return typeof value === "string"
402
+ ? [{ id: value, name: String(field(choice, "name") ?? value) }]
403
+ : [];
404
+ });
405
+ const current = field(raw, "currentValue");
406
+ found.push({
407
+ id,
408
+ name: String(field(raw, "name") ?? id),
409
+ category: String(field(raw, "category") ?? ""),
410
+ ...(typeof field(raw, "description") === "string" ? { description: String(field(raw, "description")) } : {}),
411
+ current: typeof current === "boolean" ? current : String(current ?? ""),
412
+ ...(choices.length ? { choices } : {}),
413
+ });
414
+ }
415
+ const models = field(opened, "models");
416
+ const available = asArray(field(models, "availableModels"));
417
+ if (available.length && !found.some((option) => option.category === "model")) {
418
+ found.push({
419
+ id: "model",
420
+ name: "Model",
421
+ category: "model",
422
+ current: String(field(models, "currentModelId") ?? ""),
423
+ choices: available.flatMap((model) => {
424
+ const id = field(model, "modelId");
425
+ return typeof id === "string" ? [{ id, name: String(field(model, "name") ?? id) }] : [];
426
+ }),
427
+ });
428
+ // An agent that published its models this way answers `session/set_model`
429
+ // and has never heard of `session/set_config_option`. Both shapes carry
430
+ // choices, so the option alone cannot say which method it takes: where it
431
+ // was read is the only thing that distinguishes them.
432
+ return { options: found, legacyModel: "model" };
433
+ }
434
+ return { options: found, legacyModel: null };
435
+ }
436
+ // ---- Reading the protocol's loosely-typed payloads --------------------------
437
+ const field = (value, key) => value !== null && typeof value === "object" && !Array.isArray(value)
438
+ ? value[key]
439
+ : undefined;
440
+ const asArray = (value) => (Array.isArray(value) ? value : []);
441
+ const truthy = (value) => value === true;
442
+ /** The agent's real tool name when it publishes one, else the protocol's coarse kind. */
443
+ const nameOf = (update) => {
444
+ const meta = field(update, "_meta");
445
+ for (const vendor of Object.values((meta ?? {}))) {
446
+ const named = field(vendor, "toolName");
447
+ if (typeof named === "string")
448
+ return named;
449
+ }
450
+ return String(field(update, "kind") ?? "");
451
+ };
452
+ function resultOf(update, failed) {
453
+ const raw = field(update, "rawOutput");
454
+ const blocks = asArray(field(update, "content"))
455
+ .map((entry) => field(field(entry, "content"), "text"))
456
+ .filter((value) => typeof value === "string");
457
+ const content = blocks.length ? blocks.join("\n") : typeof raw === "string" ? raw : JSON.stringify(raw ?? null);
458
+ return {
459
+ content,
460
+ ...(raw === undefined ? {} : { details: raw }),
461
+ ...(failed ? { isError: true } : {}),
462
+ };
463
+ }
464
+ /** This activation's input: the entries `runAgent` committed before calling us. */
465
+ const inputFor = (context) => context.entries()
466
+ .filter((entry) => entry.type === "run.started" && entry.runId === context.runId)
467
+ .map((entry) => textOf(entry.input))
468
+ .join("\n\n");
469
+ /** The agent's final word, read back from the log it was just committed to. */
470
+ function lastSaid(context) {
471
+ const entries = context.entries();
472
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
473
+ const entry = entries[index];
474
+ if (entry.type === "assistant" && entry.runId === context.runId)
475
+ return textOf(entry.content);
476
+ }
477
+ return "";
478
+ }
479
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/harness/adapters/acp/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Harness, HarnessContext, HarnessResult } from \"../../harness.js\";\nimport type { Entry, ToolCall, ToolResult } from \"../../../session/entry.js\";\nimport type { Sandbox, SandboxProcess } from \"../../../sandbox/sandbox.js\";\nimport type { Decide, ToolSpec } from \"../../../tools/tool.js\";\nimport type { JsonValue } from \"../../../json.js\";\nimport { textOf } from \"../../../content.js\";\nimport { err, ok, type Failure } from \"../../../result.js\";\nimport { createRpc, type Rpc } from \"./rpc.js\";\n\n/** An agent process: argv and the environment that selects its provider and model. */\nexport interface AcpAgent {\n command: readonly string[];\n env?: Readonly<Record<string, string>>;\n}\n\n/** A tool server the foreign agent connects to itself. This is how it gets ours. */\nexport interface AcpMcpServer {\n name: string;\n command: string;\n args?: readonly string[];\n env?: readonly { name: string; value: string }[];\n}\n\n/**\n * Something the agent lets a client change about a session.\n *\n * The protocol used to name models specifically; it now publishes a list of\n * options and gives each a category, so a client can tell a model selector\n * from a reasoning level without knowing the agent. We report them rather than\n * interpret them: the ids and the choices are the agent's, and a service that\n * mapped them onto its own three levels would be guessing at another product's\n * vocabulary.\n */\nexport interface AcpConfigOption {\n id: string;\n name: string;\n /** \"model\", \"thought_level\", \"mode\", \"model_config\", or whatever the agent says. */\n category: string;\n description?: string;\n current: string | boolean;\n /** Absent for a boolean option. */\n choices?: readonly { id: string; name: string }[];\n}\n\nexport interface AcpHarnessOptions {\n /** Names this harness in the log and in the UI. */\n id: string;\n agent: AcpAgent;\n /**\n * Where the agent runs. Not merely where its file requests are served: the\n * process itself is started here, because a coding agent runs its own shell\n * and only delegates the calls it chooses to.\n */\n sandbox: Sandbox;\n mcpServers?: readonly AcpMcpServer[];\n /**\n * What to set before prompting, by option id. Applied only where the agent\n * published that option and, for a select, that value — otherwise the run\n * fails naming what it does offer, rather than quietly running something else.\n */\n select?: Readonly<Record<string, string | boolean>>;\n /**\n * Requested model, matched against whichever option the agent categorised as\n * its model selector. A convenience over `select` for the one option every\n * agent has, and it fails the same way.\n */\n model?: string;\n /** What the agent published. The caller persists it so a session can offer the agent's own choices. */\n onConfig?(options: readonly AcpConfigOption[]): void;\n /** Agent-defined mode. Choosing one that asks before acting is what routes its own tools through `decide`. */\n mode?: string;\n /**\n * Applied to the agent's own tools, per call, before they run.\n *\n * The protocol carries a title and the raw arguments for these, but no\n * schema, so `spec.parameters` is `{}` and `spec.description` is the agent's\n * own title for the call. That is what we actually have.\n */\n decide?: Decide;\n /** The agent's previous session id, so this activation continues its context. */\n resume?: string;\n /** Called with the agent's session id, so the caller can persist it for the next activation. */\n onSession?(sessionId: string): void;\n}\n\nconst PROTOCOL_VERSION = 1;\n\n/**\n * Runs a foreign coding agent over the Agent Client Protocol.\n *\n * The adapter is not a remote control. It does three things the raw protocol\n * does not: it normalises whatever the agent did into this package's entries,\n * so one durable log describes a Claude Code turn and a native-loop turn\n * identically; it puts the agent's own tools under the application's decision\n * function, one call at a time; and it hands the agent tool servers of ours, so\n * agents can dispatch and message each other.\n */\nexport function createAcpHarness(options: AcpHarnessOptions): Harness {\n return {\n id: options.id,\n // Its tools are its own. We gate them and we record them; we do not claim\n // to have validated arguments we never had a schema for.\n toolUse: \"harness\",\n // The agent owns its context. Our entries describe what it did, and cannot\n // by themselves put it back mid-turn.\n recovery: \"none\",\n run: (context) => runTurn(options, context),\n };\n}\n\nasync function runTurn(options: AcpHarnessOptions, context: HarnessContext): Promise<HarnessResult> {\n const started = await options.sandbox.spawn({\n command: options.agent.command,\n cwd: options.sandbox.root,\n ...(options.agent.env ? { env: options.agent.env } : {}),\n });\n if (!started.ok) return { status: \"failed\", error: started.error };\n\n const rpc = createRpc(started.value);\n const turn = createTurn(options, context, rpc);\n try {\n return await turn.run();\n } finally {\n rpc.close();\n }\n}\n\n/** Cumulative state for one activation. Discarded with the process that produced it. */\nfunction createTurn(options: AcpHarnessOptions, context: HarnessContext, rpc: Rpc) {\n const sandbox = options.sandbox;\n const terminals = new Map<string, { process: SandboxProcess; output: () => string; exit: Promise<number> }>();\n const calls = new Map<string, { name: string; input: JsonValue; output?: JsonValue; failed?: boolean }>();\n let text = \"\";\n let sessionId = \"\";\n let terminalCount = 0;\n /**\n * Whether an update describes this activation, or the last one.\n *\n * `session/load` replays the whole conversation back as `session/update`\n * notifications — measured against `codex-acp`, which answers a load with the\n * user message and the agent message of every turn before it. Those are the\n * same words this log already committed when they were first said, so\n * treating them as output would append the entire history again on every\n * resumed activation, and prepend it to whatever the new turn actually says.\n * Nothing before the prompt belongs to the run the prompt starts.\n */\n let live = false;\n\n return { run };\n\n async function run(): Promise<HarnessResult> {\n serveFiles();\n serveTerminals();\n servePermission();\n rpc.onNotify(\"session/update\", (params) => { void receive(params); });\n\n const ready = await rpc.request(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: true },\n clientInfo: { name: \"aglib\", version: \"0\" },\n });\n if (!ready.ok) return { status: \"failed\", error: ready.error };\n const capabilities = field(ready.value, \"agentCapabilities\");\n\n const opened = await openSession(truthy(field(capabilities, \"loadSession\")));\n if (!opened.ok) return { status: \"failed\", error: opened.error };\n sessionId = opened.value;\n options.onSession?.(sessionId);\n\n const configured = await configure(opened.value, opened.opened ?? undefined);\n if (!configured.ok) return { status: \"failed\", error: configured.error };\n\n const cancel = () => rpc.notify(\"session/cancel\", { sessionId });\n context.signal.addEventListener(\"abort\", cancel, { once: true });\n\n // Anything the agent replayed while opening has already been dispatched:\n // one stdio stream, read in order, so a load's echo is behind us here.\n live = true;\n const answered = await rpc.request(\"session/prompt\", {\n sessionId,\n prompt: [{ type: \"text\", text: inputFor(context) }],\n });\n context.signal.removeEventListener(\"abort\", cancel);\n for (const terminal of terminals.values()) terminal.process.kill();\n\n if (!answered.ok) return { status: \"failed\", error: answered.error };\n await flushText();\n return outcome(String(field(answered.value, \"stopReason\") ?? \"end_turn\"));\n }\n\n async function openSession(canLoad: boolean) {\n const parameters = {\n cwd: sandbox.root,\n mcpServers: (options.mcpServers ?? []).map((server) => ({\n type: \"stdio\", name: server.name, command: server.command,\n args: [...(server.args ?? [])], env: [...(server.env ?? [])],\n })),\n } as unknown as JsonValue;\n\n // The whole answer is carried out, not one field of it: what an agent\n // publishes about a session has changed shape once already.\n if (options.resume && canLoad) {\n const loaded = await rpc.request(\"session/load\", { ...(parameters as object), sessionId: options.resume } as JsonValue);\n if (loaded.ok) return { ...ok(options.resume), opened: loaded.value };\n }\n const created = await rpc.request(\"session/new\", parameters);\n if (!created.ok) return { ...created, opened: null };\n const id = field(created.value, \"sessionId\");\n if (typeof id !== \"string\") {\n return { ...err<Failure>({ code: \"protocol\", message: \"The agent opened no session.\", retryable: false }), opened: null };\n }\n return { ...ok(id), opened: created.value };\n }\n\n /**\n * Read what the agent lets us change, report it, and set what was asked for.\n *\n * Two shapes, because the protocol changed one into the other: `models` used\n * to be its own field on the session, and is now one entry in `configOptions`\n * carrying `category: \"model\"`. Both are read, so an agent on either version\n * is understood, and neither is required.\n *\n * A resumed session reads this from the `session/load` answer, and an agent\n * that answers a load without republishing is one whose options we do not\n * currently know. Setting one from what a previous session said would be\n * asserting a capability this connection never claimed — so a request for an\n * option that is not published fails, naming what is. Measured against\n * `claude-agent-acp`, which does republish on load.\n */\n async function configure(id: string, opened: JsonValue | undefined) {\n const { options: published, legacyModel } = readOptions(opened);\n if (published.length) options.onConfig?.(published);\n\n if (options.mode) {\n const set = await rpc.request(\"session/set_mode\", { sessionId: id, modeId: options.mode });\n if (!set.ok) return set;\n }\n\n const wanted: Record<string, string | boolean> = { ...options.select };\n if (options.model) {\n const selector = published.find((option) => option.category === \"model\");\n if (!selector) {\n return err<Failure>({\n code: \"unsupported\",\n message: `${options.id} publishes no model selector, so '${options.model}' cannot be chosen over the protocol. Point it at a model through its environment instead.`,\n retryable: false,\n });\n }\n wanted[selector.id] = options.model;\n }\n\n for (const [option, value] of Object.entries(wanted)) {\n const published_ = published.find((candidate) => candidate.id === option);\n if (!published_) {\n return err<Failure>({\n code: \"unsupported\",\n message: `${options.id} offers no option '${option}'. It offers: ${published.map((o) => o.id).join(\", \") || \"none\"}.`,\n retryable: false,\n });\n }\n if (published_.current === value) continue;\n if (published_.choices && !published_.choices.some((choice) => choice.id === value)) {\n return err<Failure>({\n code: \"unsupported\",\n message: `${options.id} does not offer '${String(value)}' for ${published_.name}. It offers: ${published_.choices.map((c) => c.id).join(\", \")}.`,\n retryable: false,\n });\n }\n const legacy = published_.id === legacyModel;\n const set = await rpc.request(\n legacy ? \"session/set_model\" : \"session/set_config_option\",\n legacy\n ? { sessionId: id, modelId: value }\n : { sessionId: id, configId: published_.id, value, ...(typeof value === \"boolean\" ? { type: \"boolean\" } : {}) },\n );\n if (!set.ok) return set;\n }\n return ok(undefined);\n }\n\n // ---- Normalising what the agent did into entries -------------------------\n\n /**\n * A tool call becomes entries when it finishes, in one commit: the assistant\n * turn that asked for it, the start, and the result. Committing the pieces as\n * they arrive would leave half-states in a log whose whole purpose is that it\n * can be replayed, and a live viewer is served by `emit` instead.\n */\n async function receive(params: JsonValue): Promise<void> {\n if (!live) return;\n const update = field(params, \"update\");\n const kind = String(field(update, \"sessionUpdate\") ?? \"\");\n\n if (kind === \"agent_message_chunk\") {\n const chunk = String(field(field(update, \"content\"), \"text\") ?? \"\");\n text += chunk;\n context.emit({ type: \"text.delta\", text: chunk });\n return;\n }\n if (kind === \"agent_thought_chunk\") {\n context.emit({ type: \"reasoning.delta\", text: String(field(field(update, \"content\"), \"text\") ?? \"\") });\n return;\n }\n if (kind === \"usage_update\") {\n // Dropped deliberately. This protocol reports a spend figure and no token\n // counts, and `Usage` carries tokens and no money, so there is nothing\n // here that could be recorded without inventing it. An agent behind this\n // harness is unaccounted for, and the capability table says so.\n return;\n }\n if (kind !== \"tool_call\" && kind !== \"tool_call_update\") return;\n\n const callId = String(field(update, \"toolCallId\") ?? \"\");\n if (!callId) return;\n const existing = calls.get(callId) ?? { name: \"\", input: null };\n const name = nameOf(update);\n const raw = field(update, \"rawInput\");\n calls.set(callId, {\n name: name || existing.name || \"tool\",\n input: raw === undefined || raw === null ? existing.input : raw,\n ...(existing.output === undefined ? {} : { output: existing.output }),\n });\n context.emit({ type: \"tool.progress\", callId, data: update ?? null });\n\n const status = String(field(update, \"status\") ?? \"\");\n if (status !== \"completed\" && status !== \"failed\") return;\n\n const call = calls.get(callId)!;\n calls.delete(callId);\n const result = resultOf(update, status === \"failed\");\n await context.commit(assistantAsking(call, callId).concat(\n { type: \"tool.started\", runId: context.runId, callId },\n { type: \"tool.finished\", runId: context.runId, callId, result },\n ));\n }\n\n function assistantAsking(call: { name: string; input: JsonValue }, callId: string): Entry[] {\n const asked: ToolCall = { callId, name: call.name, arguments: JSON.stringify(call.input ?? {}) };\n const said = text;\n text = \"\";\n return [{ type: \"assistant\", runId: context.runId, content: said, calls: [asked] }];\n }\n\n async function flushText(): Promise<void> {\n if (!text) return;\n const said = text;\n text = \"\";\n await context.commit([{ type: \"assistant\", runId: context.runId, content: said }]);\n }\n\n function outcome(stopReason: string): HarnessResult {\n if (stopReason === \"cancelled\") return { status: \"cancelled\" };\n if (stopReason === \"max_tokens\" || stopReason === \"max_turn_requests\") {\n return {\n status: \"failed\",\n error: { code: \"limit\", message: `${options.id} stopped: ${stopReason}.`, retryable: false },\n };\n }\n return { status: \"completed\", output: lastSaid(context) };\n }\n\n // ---- Serving the agent's requests from the sandbox ----------------------\n\n function serveFiles(): void {\n rpc.on(\"fs/read_text_file\", async (params) => {\n const read = await sandbox.readFile({ path: String(field(params, \"path\") ?? \"\") });\n return read.ok ? ok({ content: read.value }) : read;\n });\n rpc.on(\"fs/write_text_file\", async (params) => {\n const written = await sandbox.writeFile({\n path: String(field(params, \"path\") ?? \"\"),\n content: String(field(params, \"content\") ?? \"\"),\n });\n return written.ok ? ok(null) : written;\n });\n }\n\n /**\n * The protocol's terminal, served from the sandbox.\n *\n * Worth knowing before relying on it: an agent uses this only if it chooses\n * to, and the one measured here does not. `@agentclientprotocol/claude-agent-acp`\n * calls exactly four client methods — `fs/read_text_file`,\n * `fs/write_text_file`, `session/request_permission`, `session/update` — and\n * emits a terminal block for display while running the command itself. So\n * these handlers are correct and, for that agent, never reached. Containment\n * comes from starting the agent inside the sandbox, not from serving this.\n */\n function serveTerminals(): void {\n rpc.on(\"terminal/create\", async (params) => {\n const argv = [\"/bin/sh\", \"-lc\", [field(params, \"command\"), ...(asArray(field(params, \"args\")))].filter(Boolean).join(\" \")];\n const cwd = field(params, \"cwd\");\n const spawned = await sandbox.spawn({\n command: argv,\n ...(typeof cwd === \"string\" ? { cwd } : {}),\n });\n if (!spawned.ok) return spawned;\n const id = `terminal-${++terminalCount}`;\n let collected = \"\";\n const drain = async (stream: ReadableStream<Uint8Array>) => {\n const decoder = new TextDecoder();\n for await (const chunk of stream as unknown as AsyncIterable<Uint8Array>) {\n collected += decoder.decode(chunk, { stream: true });\n }\n };\n void drain(spawned.value.stdout);\n void drain(spawned.value.stderr);\n terminals.set(id, { process: spawned.value, output: () => collected, exit: spawned.value.exited });\n return ok({ terminalId: id });\n });\n\n rpc.on(\"terminal/output\", async (params) => {\n const terminal = terminals.get(String(field(params, \"terminalId\") ?? \"\"));\n if (!terminal) return err<Failure>({ code: \"not-found\", message: \"No such terminal.\", retryable: false });\n const finished = await Promise.race([terminal.exit, Promise.resolve(undefined)]);\n return ok({\n output: terminal.output(),\n truncated: false,\n ...(finished === undefined ? {} : { exitStatus: { exitCode: finished, signal: null } }),\n });\n });\n\n rpc.on(\"terminal/wait_for_exit\", async (params) => {\n const terminal = terminals.get(String(field(params, \"terminalId\") ?? \"\"));\n if (!terminal) return err<Failure>({ code: \"not-found\", message: \"No such terminal.\", retryable: false });\n return ok({ exitCode: await terminal.exit, signal: null });\n });\n\n rpc.on(\"terminal/kill\", async (params) => {\n terminals.get(String(field(params, \"terminalId\") ?? \"\"))?.process.kill();\n return ok(null);\n });\n\n rpc.on(\"terminal/release\", async (params) => {\n const id = String(field(params, \"terminalId\") ?? \"\");\n terminals.get(id)?.process.kill();\n terminals.delete(id);\n return ok(null);\n });\n }\n\n /**\n * The agent's own tools, one call at a time, through the application's rule.\n *\n * The agent is blocked on this reply, so the answer is yes or no and nothing\n * else. That constraint is why the permission model has two outcomes rather\n * than three: a rule that could park a call would be honourable in our own\n * loop and unhonourable here, which is the ragged surface the two-outcome\n * shape avoids.\n */\n function servePermission(): void {\n rpc.on(\"session/request_permission\", async (params) => {\n const call = field(params, \"toolCall\");\n const callId = String(field(call, \"toolCallId\") ?? \"\");\n const options_ = asArray(field(params, \"options\"));\n const pick = (...kinds: string[]) => {\n for (const kind of kinds) {\n const found = options_.find((option) => String(field(option, \"kind\") ?? \"\") === kind);\n if (found) return String(field(found, \"optionId\") ?? \"\");\n }\n return undefined;\n };\n const selected = (optionId: string | undefined) =>\n optionId === undefined\n ? ok({ outcome: { outcome: \"cancelled\" } })\n : ok({ outcome: { outcome: \"selected\", optionId } });\n\n if (!options.decide) return selected(pick(\"allow_once\", \"allow_always\"));\n\n const spec: ToolSpec = {\n name: nameOf(call) || \"tool\",\n description: String(field(call, \"title\") ?? \"\"),\n parameters: {},\n };\n const decided = await options.decide({\n tool: spec,\n input: (field(call, \"rawInput\") ?? {}) as JsonValue,\n sessionId: context.sessionId,\n runId: context.runId,\n callId,\n });\n if (decided.action === \"execute\") return selected(pick(\"allow_once\", \"allow_always\"));\n return selected(pick(\"reject_once\", \"reject_always\"));\n });\n }\n}\n\n/**\n * What the agent published, from either shape the protocol has used.\n *\n * `configOptions` is current. `models` is what the same fact was called before\n * it was generalised, and is normalised into an option categorised \"model\" so\n * a caller never has to know which version answered.\n */\nfunction readOptions(\n opened: JsonValue | undefined,\n): { options: readonly AcpConfigOption[]; legacyModel: string | null } {\n const found: AcpConfigOption[] = [];\n\n for (const raw of asArray(field(opened, \"configOptions\"))) {\n const id = field(raw, \"id\");\n if (typeof id !== \"string\") continue;\n const choices = asArray(field(raw, \"options\")).flatMap((choice) => {\n const value = field(choice, \"id\") ?? field(choice, \"value\");\n return typeof value === \"string\"\n ? [{ id: value, name: String(field(choice, \"name\") ?? value) }]\n : [];\n });\n const current = field(raw, \"currentValue\");\n found.push({\n id,\n name: String(field(raw, \"name\") ?? id),\n category: String(field(raw, \"category\") ?? \"\"),\n ...(typeof field(raw, \"description\") === \"string\" ? { description: String(field(raw, \"description\")) } : {}),\n current: typeof current === \"boolean\" ? current : String(current ?? \"\"),\n ...(choices.length ? { choices } : {}),\n });\n }\n\n const models = field(opened, \"models\");\n const available = asArray(field(models, \"availableModels\"));\n if (available.length && !found.some((option) => option.category === \"model\")) {\n found.push({\n id: \"model\",\n name: \"Model\",\n category: \"model\",\n current: String(field(models, \"currentModelId\") ?? \"\"),\n choices: available.flatMap((model) => {\n const id = field(model, \"modelId\");\n return typeof id === \"string\" ? [{ id, name: String(field(model, \"name\") ?? id) }] : [];\n }),\n });\n // An agent that published its models this way answers `session/set_model`\n // and has never heard of `session/set_config_option`. Both shapes carry\n // choices, so the option alone cannot say which method it takes: where it\n // was read is the only thing that distinguishes them.\n return { options: found, legacyModel: \"model\" };\n }\n return { options: found, legacyModel: null };\n}\n\n// ---- Reading the protocol's loosely-typed payloads --------------------------\n\nconst field = (value: JsonValue | undefined, key: string): JsonValue | undefined =>\n value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, JsonValue>)[key]\n : undefined;\n\nconst asArray = (value: JsonValue | undefined): JsonValue[] => (Array.isArray(value) ? value : []);\n\nconst truthy = (value: JsonValue | undefined): boolean => value === true;\n\n/** The agent's real tool name when it publishes one, else the protocol's coarse kind. */\nconst nameOf = (update: JsonValue | undefined): string => {\n const meta = field(update, \"_meta\");\n for (const vendor of Object.values((meta ?? {}) as Record<string, JsonValue>)) {\n const named = field(vendor, \"toolName\");\n if (typeof named === \"string\") return named;\n }\n return String(field(update, \"kind\") ?? \"\");\n};\n\nfunction resultOf(update: JsonValue | undefined, failed: boolean): ToolResult {\n const raw = field(update, \"rawOutput\");\n const blocks = asArray(field(update, \"content\"))\n .map((entry) => field(field(entry, \"content\"), \"text\"))\n .filter((value): value is string => typeof value === \"string\");\n const content = blocks.length ? blocks.join(\"\\n\") : typeof raw === \"string\" ? raw : JSON.stringify(raw ?? null);\n return {\n content,\n ...(raw === undefined ? {} : { details: raw }),\n ...(failed ? { isError: true } : {}),\n };\n}\n\n/** This activation's input: the entries `runAgent` committed before calling us. */\nconst inputFor = (context: HarnessContext): string =>\n context.entries()\n .filter((entry) => entry.type === \"run.started\" && entry.runId === context.runId)\n .map((entry) => textOf((entry as Extract<Entry, { type: \"run.started\" }>).input))\n .join(\"\\n\\n\");\n\n/** The agent's final word, read back from the log it was just committed to. */\nfunction lastSaid(context: HarnessContext): string {\n const entries = context.entries();\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index]!;\n if (entry.type === \"assistant\" && entry.runId === context.runId) return textOf(entry.content);\n }\n return \"\";\n}\n\n"]}
@@ -0,0 +1,23 @@
1
+ import type { SandboxProcess } from "../../../sandbox/sandbox.js";
2
+ import type { JsonValue } from "../../../json.js";
3
+ import { type Failure, type Result } from "../../../result.js";
4
+ /** Handles a request the agent sends back to us. Rejecting is a JSON-RPC error. */
5
+ export type RpcHandler = (params: JsonValue) => Promise<Result<JsonValue, Failure>>;
6
+ export interface Rpc {
7
+ request(method: string, params: JsonValue): Promise<Result<JsonValue, Failure>>;
8
+ notify(method: string, params: JsonValue): void;
9
+ /** Register a method the agent may call on us. Unregistered methods answer "method not found". */
10
+ on(method: string, handler: RpcHandler): void;
11
+ onNotify(method: string, handler: (params: JsonValue) => void): void;
12
+ /** Resolves when the process ends; carries whatever it wrote to stderr. */
13
+ readonly closed: Promise<string>;
14
+ close(): void;
15
+ }
16
+ /**
17
+ * JSON-RPC 2.0 over one process's stdio, newline-delimited.
18
+ *
19
+ * Bidirectional on purpose: the protocol this speaks inverts control for files
20
+ * and terminals, so the agent is as much a caller as a callee and the two
21
+ * directions are the same machinery.
22
+ */
23
+ export declare function createRpc(child: SandboxProcess): Rpc;