@rivus/runtime 0.16.6 → 0.18.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.
package/dist/acp.js CHANGED
@@ -1,4 +1,5 @@
1
- import { _ as createAgentLoopToolExecutionStart, c as toEffectAgentLoop, g as createAgentLoopToolExecutionEnd, h as createAgentLoopThinkingDelta, m as createAgentLoopTextDelta, o as fromEffectAgentLoop, s as toCompatibilityAgentLoopInput, v as createAgentLoopToolExecutionUpdate, y as createAgentLoopTurnStart } from "./chunks/agent-loop.js";
1
+ import { i as isAgentSteeringRequest, o as projectUserInputText } from "./chunks/agent-user-input.js";
2
+ import { c as createAgentLoopToolExecutionEnd, d as createAgentLoopTurnStart, l as createAgentLoopToolExecutionStart, o as createAgentLoopTextDelta, s as createAgentLoopThinkingDelta, t as prepareAgentModelInput, u as createAgentLoopToolExecutionUpdate } from "./chunks/user-input-xml.js";
2
3
  import { Effect, Stream } from "effect";
3
4
  import { randomUUID } from "node:crypto";
4
5
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
@@ -6,11 +7,197 @@ import { dirname } from "node:path";
6
7
  import { Readable, Writable } from "node:stream";
7
8
  import * as acp from "@agentclientprotocol/sdk";
8
9
  import { spawn } from "node:child_process";
10
+ //#region src/adapters/acp/runtime/acp-agent-server.ts
11
+ function createAcpAgentServer(options) {
12
+ const sessions = /* @__PURE__ */ new Set();
13
+ const active = /* @__PURE__ */ new Map();
14
+ return acp.agent({ name: options.agentName ?? "rivus" }).onRequest(acp.methods.agent.initialize, ({ params }) => ({
15
+ agentCapabilities: {},
16
+ agentInfo: {
17
+ name: options.agentName ?? "rivus",
18
+ version: "1"
19
+ },
20
+ protocolVersion: params.protocolVersion === acp.PROTOCOL_VERSION ? params.protocolVersion : acp.PROTOCOL_VERSION
21
+ })).onRequest(acp.methods.agent.session.new, () => {
22
+ const sessionId = randomUUID();
23
+ sessions.add(sessionId);
24
+ return { sessionId };
25
+ }).onRequest(acp.methods.agent.session.prompt, async ({ client, params }) => {
26
+ if (!sessions.has(params.sessionId)) throw new Error(`Unknown ACP session: ${params.sessionId}`);
27
+ if (active.has(params.sessionId)) throw new Error(`ACP session is already active: ${params.sessionId}`);
28
+ const controller = new AbortController();
29
+ active.set(params.sessionId, controller);
30
+ const sessionKey = `acp:${params.sessionId}`;
31
+ const releasePermissionBridge = options.permissionBridge?.bind(sessionKey, async (request) => {
32
+ const response = await client.request(acp.methods.client.session.requestPermission, {
33
+ options: request.options.map((option) => ({ ...option })),
34
+ sessionId: params.sessionId,
35
+ toolCall: {
36
+ ...request.toolCall.rawInput === void 0 ? {} : { rawInput: request.toolCall.rawInput },
37
+ ...request.toolCall.title === void 0 ? {} : { title: request.toolCall.title },
38
+ toolCallId: request.toolCall.toolCallId
39
+ }
40
+ });
41
+ return response.outcome.outcome === "selected" ? { optionId: response.outcome.optionId } : void 0;
42
+ });
43
+ try {
44
+ await Effect.runPromise(options.loop.run({
45
+ abortSignal: controller.signal,
46
+ runId: randomUUID(),
47
+ sessionKey,
48
+ text: readPromptText(params.prompt)
49
+ }).pipe(Stream.runForEach((event) => Effect.tryPromise({
50
+ catch: (cause) => cause,
51
+ try: async () => {
52
+ const update = mapAgentLoopEvent(event);
53
+ if (update) await client.notify(acp.methods.client.session.update, {
54
+ sessionId: params.sessionId,
55
+ update
56
+ });
57
+ }
58
+ }))));
59
+ return { stopReason: controller.signal.aborted ? "cancelled" : "end_turn" };
60
+ } finally {
61
+ releasePermissionBridge?.();
62
+ active.delete(params.sessionId);
63
+ }
64
+ }).onNotification(acp.methods.agent.session.cancel, ({ params }) => {
65
+ active.get(params.sessionId)?.abort();
66
+ });
67
+ }
68
+ async function serveAcpAgentOnStdio(app) {
69
+ const stream = acp.ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
70
+ await app.connect(stream).closed;
71
+ }
72
+ function readPromptText(prompt) {
73
+ const parts = prompt.flatMap((content) => content.type === "text" ? [content.text] : []);
74
+ if (parts.length !== prompt.length) throw new Error("Rivus ACP facade currently accepts text prompt content only");
75
+ return parts.join("\n");
76
+ }
77
+ function mapAgentLoopEvent(event) {
78
+ if (event.type === "assistant_text_delta") return {
79
+ content: {
80
+ text: event.delta,
81
+ type: "text"
82
+ },
83
+ sessionUpdate: "agent_message_chunk"
84
+ };
85
+ if (event.type === "assistant_thinking_delta") return {
86
+ content: {
87
+ text: event.delta,
88
+ type: "text"
89
+ },
90
+ sessionUpdate: "agent_thought_chunk"
91
+ };
92
+ if (event.type === "tool_execution_start") return {
93
+ name: event.toolName,
94
+ rawInput: event.input,
95
+ sessionUpdate: "tool_call",
96
+ status: "in_progress",
97
+ title: event.toolName,
98
+ toolCallId: event.toolCallId
99
+ };
100
+ if (event.type === "tool_execution_update") return {
101
+ name: event.toolName,
102
+ rawInput: event.input,
103
+ rawOutput: event.partialResult,
104
+ sessionUpdate: "tool_call_update",
105
+ status: "in_progress",
106
+ title: event.toolName,
107
+ toolCallId: event.toolCallId
108
+ };
109
+ if (event.type === "tool_execution_end") return {
110
+ name: event.toolName,
111
+ rawOutput: event.result,
112
+ sessionUpdate: "tool_call_update",
113
+ status: event.isError ? "failed" : "completed",
114
+ title: event.toolName,
115
+ toolCallId: event.toolCallId
116
+ };
117
+ }
118
+ //#endregion
119
+ //#region src/adapters/acp/runtime/acp-permission-bridge.ts
120
+ function createAcpPermissionBridge() {
121
+ const policies = /* @__PURE__ */ new Map();
122
+ return {
123
+ bind: (sessionKey, policy) => {
124
+ if (policies.has(sessionKey)) throw new Error(`ACP permission bridge is already bound: ${sessionKey}`);
125
+ policies.set(sessionKey, policy);
126
+ return () => {
127
+ if (policies.get(sessionKey) === policy) policies.delete(sessionKey);
128
+ };
129
+ },
130
+ policy: async (request, context) => policies.get(context.sessionKey)?.(request)
131
+ };
132
+ }
133
+ //#endregion
134
+ //#region src/adapters/acp/runtime/acp-session-store.ts
135
+ /**
136
+ * A small deployment-owned store for ACP provider session identities.
137
+ * The file contains no prompts or credentials, only session-key bindings.
138
+ */
139
+ function createJsonAcpSessionStore(options) {
140
+ let recordsPromise;
141
+ let writeChain = Promise.resolve();
142
+ const readRecords = async () => {
143
+ try {
144
+ const raw = await readFile(options.filePath, "utf8");
145
+ const parsed = JSON.parse(raw);
146
+ if (!isRecord$1(parsed)) throw new Error("ACP session store must contain an object");
147
+ const records = /* @__PURE__ */ new Map();
148
+ for (const [sessionKey, value] of Object.entries(parsed)) {
149
+ if (!isRecord$1(value) || typeof value.sessionId !== "string" || value.sessionId.trim() === "") throw new Error(`invalid ACP session record for ${sessionKey}`);
150
+ records.set(sessionKey, { sessionId: value.sessionId });
151
+ }
152
+ return records;
153
+ } catch (error) {
154
+ if (isMissingFile(error)) return /* @__PURE__ */ new Map();
155
+ throw error;
156
+ }
157
+ };
158
+ const records = async () => {
159
+ recordsPromise ??= readRecords();
160
+ return recordsPromise;
161
+ };
162
+ const persist = async (value) => {
163
+ const temporaryPath = `${options.filePath}.tmp-${process.pid}-${Date.now()}`;
164
+ await mkdir(dirname(options.filePath), { recursive: true });
165
+ await writeFile(temporaryPath, `${JSON.stringify(Object.fromEntries(value), null, 2)}\n`, "utf8");
166
+ await rename(temporaryPath, options.filePath);
167
+ };
168
+ return {
169
+ delete: async (sessionKey) => {
170
+ writeChain = writeChain.then(async () => {
171
+ const current = await records();
172
+ if (!current.delete(sessionKey)) return;
173
+ await persist(current);
174
+ });
175
+ await writeChain;
176
+ },
177
+ load: async (sessionKey) => (await records()).get(sessionKey),
178
+ save: async (sessionKey, record) => {
179
+ writeChain = writeChain.then(async () => {
180
+ const current = await records();
181
+ current.set(sessionKey, { sessionId: record.sessionId });
182
+ await persist(current);
183
+ });
184
+ await writeChain;
185
+ }
186
+ };
187
+ }
188
+ function isMissingFile(error) {
189
+ return isRecord$1(error) && error.code === "ENOENT";
190
+ }
191
+ function isRecord$1(value) {
192
+ return value !== null && typeof value === "object" && !Array.isArray(value);
193
+ }
194
+ //#endregion
9
195
  //#region src/adapters/acp/runtime/acp-agent-loop.ts
10
196
  function createAcpAgentLoop(options) {
11
197
  return {
12
198
  run: (input) => Stream.fromAsyncIterable(runAcpSession(input, options), (error) => error),
13
- supportsSteering: true
199
+ supportsSteering: true,
200
+ supportsStructuredSteering: true
14
201
  };
15
202
  }
16
203
  async function* runAcpSession(input, options) {
@@ -22,7 +209,11 @@ async function* runAcpSession(input, options) {
22
209
  let abortFailure;
23
210
  let abortSettled = false;
24
211
  let aborted = false;
25
- let currentTurn = startAcpTurn(input.text, session, input, updates, tools, () => {
212
+ let currentTurn = startAcpTurn({
213
+ text: input.text,
214
+ ...input.attachmentAccess ? { attachmentAccess: input.attachmentAccess } : {},
215
+ ...input.userInput ? { userInput: input.userInput } : {}
216
+ }, session, input, updates, tools, () => {
26
217
  wake?.();
27
218
  wake = void 0;
28
219
  });
@@ -40,7 +231,10 @@ async function* runAcpSession(input, options) {
40
231
  });
41
232
  };
42
233
  input.abortSignal.addEventListener("abort", onAbort, { once: true });
43
- const takeSteering = () => input.steering ? Effect.runPromise(input.steering.next()) : void 0;
234
+ const takeSteering = () => {
235
+ if (!input.steering) return;
236
+ return Effect.runPromise(input.steering.nextInput?.() ?? input.steering.next());
237
+ };
44
238
  let steering = takeSteering();
45
239
  try {
46
240
  if (input.abortSignal.aborted) onAbort();
@@ -54,8 +248,8 @@ async function* runAcpSession(input, options) {
54
248
  new Promise((resolve) => {
55
249
  wake = () => resolve({ type: "wake" });
56
250
  }),
57
- ...steering ? [steering.then((text) => ({
58
- text,
251
+ ...steering ? [steering.then((steeringInput) => ({
252
+ steeringInput,
59
253
  type: "steer"
60
254
  }))] : [],
61
255
  currentTurn.promise.then(() => ({ type: "turn_completed" }), () => ({ type: "turn_completed" }))
@@ -64,7 +258,7 @@ async function* runAcpSession(input, options) {
64
258
  await Promise.resolve(session.cancel({ preserveSession: true }));
65
259
  await currentTurn.promise;
66
260
  updates.push(createAgentLoopTurnStart());
67
- currentTurn = startAcpTurn(createSteeringTurnPrompt(signal.text), session, input, updates, tools, () => {
261
+ currentTurn = startAcpTurn(createSteeringTurnPromptInput(signal.steeringInput), session, input, updates, tools, () => {
68
262
  wake?.();
69
263
  wake = void 0;
70
264
  });
@@ -110,14 +304,42 @@ function createSteeringTurnPrompt(text) {
110
304
  text
111
305
  ].join("\n");
112
306
  }
113
- function startAcpTurn(text, session, input, updates, tools, notify) {
307
+ function createSteeringTurnPromptInput(input) {
308
+ if (typeof input === "string") return { text: createSteeringTurnPrompt(input) };
309
+ const userInput = isAgentSteeringRequest(input) ? input.userInput : input;
310
+ return {
311
+ ...isAgentSteeringRequest(input) && input.attachmentAccess ? { attachmentAccess: input.attachmentAccess } : {},
312
+ text: createSteeringTurnPrompt(projectUserInputText(userInput)),
313
+ userInput: {
314
+ ...userInput,
315
+ message: {
316
+ ...userInput.message,
317
+ parts: [{
318
+ type: "text",
319
+ text: "请优先处理这条 steering 消息,并在回答后继续原任务。",
320
+ source: "direct",
321
+ controlEligible: false
322
+ }, ...userInput.message.parts]
323
+ }
324
+ }
325
+ };
326
+ }
327
+ function startAcpTurn(prompt, session, input, updates, tools, notify) {
114
328
  let completed = false;
115
329
  let failure;
116
330
  return {
117
- promise: session.prompt(text, (update) => {
118
- updates.push(...mapAcpSessionUpdate(update, tools));
119
- notify();
120
- }).then(() => void 0).catch((error) => {
331
+ promise: Effect.runPromise(Effect.gen(function* () {
332
+ const prepared = yield* prepareAgentModelInput(toAcpTurnInput(input, prompt), { allowImages: session.supportsImages !== false });
333
+ if (!prepared.hasUsableContent) {
334
+ if (prepared.fallbackText) updates.push(createAgentLoopTextDelta(prepared.fallbackText));
335
+ notify();
336
+ return;
337
+ }
338
+ yield* Effect.promise(() => session.prompt(prepared.text, (update) => {
339
+ updates.push(...mapAcpSessionUpdate(update, tools));
340
+ notify();
341
+ }, prepared.images.length > 0 ? { images: prepared.images } : void 0));
342
+ })).then(() => void 0).catch((error) => {
121
343
  if (!input.abortSignal.aborted) failure = error;
122
344
  }).finally(() => {
123
345
  completed = true;
@@ -131,6 +353,15 @@ function startAcpTurn(text, session, input, updates, tools, notify) {
131
353
  }
132
354
  };
133
355
  }
356
+ function toAcpTurnInput(input, prompt) {
357
+ const { attachmentAccess: _initialAccess, userInput: _initialUserInput, ...runInput } = input;
358
+ return {
359
+ ...runInput,
360
+ text: prompt.text,
361
+ ...prompt.attachmentAccess ? { attachmentAccess: prompt.attachmentAccess } : {},
362
+ ...prompt.userInput ? { userInput: prompt.userInput } : {}
363
+ };
364
+ }
134
365
  function mapAcpSessionUpdate(update, tools) {
135
366
  if (update.sessionUpdate === "agent_message_chunk") {
136
367
  const text = readTextContent(update.content);
@@ -189,137 +420,13 @@ function mapAcpSessionUpdate(update, tools) {
189
420
  })];
190
421
  }
191
422
  function readTextContent(content) {
192
- if (!isRecord$1(content) || content.type !== "text" || typeof content.text !== "string") return void 0;
423
+ if (!isRecord(content) || content.type !== "text" || typeof content.text !== "string") return;
193
424
  return content.text;
194
425
  }
195
- function isRecord$1(value) {
426
+ function isRecord(value) {
196
427
  return typeof value === "object" && value !== null;
197
428
  }
198
429
  //#endregion
199
- //#region src/adapters/acp/runtime/acp-agent-server.ts
200
- function createAcpAgentServer(options) {
201
- const sessions = /* @__PURE__ */ new Set();
202
- const active = /* @__PURE__ */ new Map();
203
- return acp.agent({ name: options.agentName ?? "rivus" }).onRequest(acp.methods.agent.initialize, ({ params }) => ({
204
- agentCapabilities: {},
205
- agentInfo: {
206
- name: options.agentName ?? "rivus",
207
- version: "1"
208
- },
209
- protocolVersion: params.protocolVersion === acp.PROTOCOL_VERSION ? params.protocolVersion : acp.PROTOCOL_VERSION
210
- })).onRequest(acp.methods.agent.session.new, () => {
211
- const sessionId = randomUUID();
212
- sessions.add(sessionId);
213
- return { sessionId };
214
- }).onRequest(acp.methods.agent.session.prompt, async ({ client, params }) => {
215
- if (!sessions.has(params.sessionId)) throw new Error(`Unknown ACP session: ${params.sessionId}`);
216
- if (active.has(params.sessionId)) throw new Error(`ACP session is already active: ${params.sessionId}`);
217
- const controller = new AbortController();
218
- active.set(params.sessionId, controller);
219
- const sessionKey = `acp:${params.sessionId}`;
220
- const releasePermissionBridge = options.permissionBridge?.bind(sessionKey, async (request) => {
221
- const response = await client.request(acp.methods.client.session.requestPermission, {
222
- options: request.options.map((option) => ({ ...option })),
223
- sessionId: params.sessionId,
224
- toolCall: {
225
- ...request.toolCall.rawInput === void 0 ? {} : { rawInput: request.toolCall.rawInput },
226
- ...request.toolCall.title === void 0 ? {} : { title: request.toolCall.title },
227
- toolCallId: request.toolCall.toolCallId
228
- }
229
- });
230
- return response.outcome.outcome === "selected" ? { optionId: response.outcome.optionId } : void 0;
231
- });
232
- try {
233
- await Effect.runPromise(options.loop.run({
234
- abortSignal: controller.signal,
235
- runId: randomUUID(),
236
- sessionKey,
237
- text: readPromptText(params.prompt)
238
- }).pipe(Stream.runForEach((event) => Effect.tryPromise({
239
- catch: (cause) => cause,
240
- try: async () => {
241
- const update = mapAgentLoopEvent(event);
242
- if (update) await client.notify(acp.methods.client.session.update, {
243
- sessionId: params.sessionId,
244
- update
245
- });
246
- }
247
- }))));
248
- return { stopReason: controller.signal.aborted ? "cancelled" : "end_turn" };
249
- } finally {
250
- releasePermissionBridge?.();
251
- active.delete(params.sessionId);
252
- }
253
- }).onNotification(acp.methods.agent.session.cancel, ({ params }) => {
254
- active.get(params.sessionId)?.abort();
255
- });
256
- }
257
- async function serveAcpAgentOnStdio(app) {
258
- const stream = acp.ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
259
- await app.connect(stream).closed;
260
- }
261
- function readPromptText(prompt) {
262
- const parts = prompt.flatMap((content) => content.type === "text" ? [content.text] : []);
263
- if (parts.length !== prompt.length) throw new Error("Rivus ACP facade currently accepts text prompt content only");
264
- return parts.join("\n");
265
- }
266
- function mapAgentLoopEvent(event) {
267
- if (event.type === "assistant_text_delta") return {
268
- content: {
269
- text: event.delta,
270
- type: "text"
271
- },
272
- sessionUpdate: "agent_message_chunk"
273
- };
274
- if (event.type === "assistant_thinking_delta") return {
275
- content: {
276
- text: event.delta,
277
- type: "text"
278
- },
279
- sessionUpdate: "agent_thought_chunk"
280
- };
281
- if (event.type === "tool_execution_start") return {
282
- name: event.toolName,
283
- rawInput: event.input,
284
- sessionUpdate: "tool_call",
285
- status: "in_progress",
286
- title: event.toolName,
287
- toolCallId: event.toolCallId
288
- };
289
- if (event.type === "tool_execution_update") return {
290
- name: event.toolName,
291
- rawInput: event.input,
292
- rawOutput: event.partialResult,
293
- sessionUpdate: "tool_call_update",
294
- status: "in_progress",
295
- title: event.toolName,
296
- toolCallId: event.toolCallId
297
- };
298
- if (event.type === "tool_execution_end") return {
299
- name: event.toolName,
300
- rawOutput: event.result,
301
- sessionUpdate: "tool_call_update",
302
- status: event.isError ? "failed" : "completed",
303
- title: event.toolName,
304
- toolCallId: event.toolCallId
305
- };
306
- }
307
- //#endregion
308
- //#region src/adapters/acp/runtime/acp-permission-bridge.ts
309
- function createAcpPermissionBridge() {
310
- const policies = /* @__PURE__ */ new Map();
311
- return {
312
- bind: (sessionKey, policy) => {
313
- if (policies.has(sessionKey)) throw new Error(`ACP permission bridge is already bound: ${sessionKey}`);
314
- policies.set(sessionKey, policy);
315
- return () => {
316
- if (policies.get(sessionKey) === policy) policies.delete(sessionKey);
317
- };
318
- },
319
- policy: async (request, context) => policies.get(context.sessionKey)?.(request)
320
- };
321
- }
322
- //#endregion
323
430
  //#region src/adapters/acp/runtime/acp-permission-policy.ts
324
431
  async function decideAcpPermission(request, policy) {
325
432
  const selection = await policy?.(request);
@@ -330,67 +437,6 @@ async function decideAcpPermission(request, policy) {
330
437
  };
331
438
  }
332
439
  //#endregion
333
- //#region src/adapters/acp/runtime/acp-session-store.ts
334
- /**
335
- * A small deployment-owned store for ACP provider session identities.
336
- * The file contains no prompts or credentials, only session-key bindings.
337
- */
338
- function createJsonAcpSessionStore(options) {
339
- let recordsPromise;
340
- let writeChain = Promise.resolve();
341
- const readRecords = async () => {
342
- try {
343
- const raw = await readFile(options.filePath, "utf8");
344
- const parsed = JSON.parse(raw);
345
- if (!isRecord(parsed)) throw new Error("ACP session store must contain an object");
346
- const records = /* @__PURE__ */ new Map();
347
- for (const [sessionKey, value] of Object.entries(parsed)) {
348
- if (!isRecord(value) || typeof value.sessionId !== "string" || value.sessionId.trim() === "") throw new Error(`invalid ACP session record for ${sessionKey}`);
349
- records.set(sessionKey, { sessionId: value.sessionId });
350
- }
351
- return records;
352
- } catch (error) {
353
- if (isMissingFile(error)) return /* @__PURE__ */ new Map();
354
- throw error;
355
- }
356
- };
357
- const records = async () => {
358
- recordsPromise ??= readRecords();
359
- return recordsPromise;
360
- };
361
- const persist = async (value) => {
362
- const temporaryPath = `${options.filePath}.tmp-${process.pid}-${Date.now()}`;
363
- await mkdir(dirname(options.filePath), { recursive: true });
364
- await writeFile(temporaryPath, `${JSON.stringify(Object.fromEntries(value), null, 2)}\n`, "utf8");
365
- await rename(temporaryPath, options.filePath);
366
- };
367
- return {
368
- delete: async (sessionKey) => {
369
- writeChain = writeChain.then(async () => {
370
- const current = await records();
371
- if (!current.delete(sessionKey)) return;
372
- await persist(current);
373
- });
374
- await writeChain;
375
- },
376
- load: async (sessionKey) => (await records()).get(sessionKey),
377
- save: async (sessionKey, record) => {
378
- writeChain = writeChain.then(async () => {
379
- const current = await records();
380
- current.set(sessionKey, { sessionId: record.sessionId });
381
- await persist(current);
382
- });
383
- await writeChain;
384
- }
385
- };
386
- }
387
- function isMissingFile(error) {
388
- return isRecord(error) && error.code === "ENOENT";
389
- }
390
- function isRecord(value) {
391
- return value !== null && typeof value === "object" && !Array.isArray(value);
392
- }
393
- //#endregion
394
440
  //#region src/adapters/acp/runtime/acp-stdio-agent-loop.ts
395
441
  async function buildAcpSession(processConnection, options, input) {
396
442
  const mcpServers = createMcpServers(options, input);
@@ -401,7 +447,7 @@ async function buildAcpSession(processConnection, options, input) {
401
447
  mcpServers
402
448
  }).start();
403
449
  await options.sessionStore?.save(input.sessionKey, { sessionId: session.sessionId });
404
- return session;
450
+ return new ActiveAcpSessionTransport(session);
405
451
  }
406
452
  function createMcpServers(options, input) {
407
453
  return (options.mcpServers?.(input) ?? []).map((server) => ({
@@ -504,7 +550,7 @@ function createAcpStdioAgentLoop(options) {
504
550
  current.dispose();
505
551
  processConnection.sessions.delete(input.sessionKey);
506
552
  }
507
- const session = new SdkAcpAgentSession(await buildAcpSession(processConnection, options, input), processConnection.connection.agent, input.sessionKey, options.sessionStore);
553
+ const session = new SdkAcpAgentSession(await buildAcpSession(processConnection, options, input), processConnection.connection.agent, input.sessionKey, options.sessionStore, processConnection.agentCapabilities?.promptCapabilities?.image === true);
508
554
  sessionKeys.set(session.sessionId, input.sessionKey);
509
555
  processConnection.sessions.set(input.sessionKey, session);
510
556
  return session;
@@ -517,12 +563,14 @@ var SdkAcpAgentSession = class {
517
563
  agent;
518
564
  sessionKey;
519
565
  sessionStore;
566
+ supportsImages;
520
567
  reusable = true;
521
- constructor(session, agent, sessionKey, sessionStore) {
568
+ constructor(session, agent, sessionKey, sessionStore, supportsImages = false) {
522
569
  this.session = session;
523
570
  this.agent = agent;
524
571
  this.sessionKey = sessionKey;
525
572
  this.sessionStore = sessionStore;
573
+ this.supportsImages = supportsImages;
526
574
  }
527
575
  get sessionId() {
528
576
  return this.session.sessionId;
@@ -543,8 +591,8 @@ var SdkAcpAgentSession = class {
543
591
  this.session.dispose();
544
592
  await this.sessionStore?.delete(this.sessionKey);
545
593
  }
546
- async prompt(text, onUpdate) {
547
- const failure = this.session.prompt(text).then(() => new Promise(() => void 0), (error) => Promise.reject(error));
594
+ async prompt(text, onUpdate, options) {
595
+ const failure = this.session.prompt(text, options?.images).then(() => new Promise(() => void 0), (error) => Promise.reject(error));
548
596
  for (;;) {
549
597
  const message = await Promise.race([this.session.nextUpdate(), failure]);
550
598
  if (message.kind === "stop") return { stopReason: message.stopReason };
@@ -552,6 +600,40 @@ var SdkAcpAgentSession = class {
552
600
  }
553
601
  }
554
602
  };
603
+ var ActiveAcpSessionTransport = class {
604
+ session;
605
+ sessionId;
606
+ constructor(session) {
607
+ this.session = session;
608
+ this.sessionId = session.sessionId;
609
+ }
610
+ dispose() {
611
+ this.session.dispose();
612
+ }
613
+ nextUpdate() {
614
+ return this.session.nextUpdate().then((message) => {
615
+ if (message.kind === "session_update") return {
616
+ kind: "session_update",
617
+ update: message.update
618
+ };
619
+ return {
620
+ kind: "stop",
621
+ stopReason: message.stopReason
622
+ };
623
+ });
624
+ }
625
+ prompt(text, images) {
626
+ const prompt = [{
627
+ text,
628
+ type: "text"
629
+ }, ...(images ?? []).map(({ data, mimeType }) => ({
630
+ data,
631
+ mimeType,
632
+ type: "image"
633
+ }))];
634
+ return this.session.prompt(prompt).then((response) => ({ stopReason: response.stopReason }));
635
+ }
636
+ };
555
637
  var ResumedAcpSession = class {
556
638
  processConnection;
557
639
  sessionId;
@@ -589,13 +671,18 @@ var ResumedAcpSession = class {
589
671
  resolve
590
672
  }));
591
673
  }
592
- prompt(text) {
674
+ prompt(text, images) {
593
675
  if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error(`ACP session ${this.sessionId} observer disposed`));
676
+ const prompt = [{
677
+ text,
678
+ type: "text"
679
+ }, ...(images ?? []).map(({ data, mimeType }) => ({
680
+ data,
681
+ mimeType,
682
+ type: "image"
683
+ }))];
594
684
  const response = this.processConnection.connection.agent.request(acp.methods.agent.session.prompt, {
595
- prompt: [{
596
- text,
597
- type: "text"
598
- }],
685
+ prompt,
599
686
  sessionId: this.sessionId
600
687
  });
601
688
  response.then((result) => this.enqueue({
@@ -726,43 +813,5 @@ function withTimeout(promise, timeoutMs, message) {
726
813
  });
727
814
  }
728
815
  //#endregion
729
- //#region src/adapters/compatibility/acp/acp-runtime.ts
730
- function createAcpAgentLoop$1(options) {
731
- return fromEffectAgentLoop(createAcpAgentLoop({
732
- ...options.cancellationSettleTimeoutMs === void 0 ? {} : { cancellationSettleTimeoutMs: options.cancellationSettleTimeoutMs },
733
- ...options.disposeSessionAfterRun === void 0 ? {} : { disposeSessionAfterRun: options.disposeSessionAfterRun },
734
- resolveSession: (input) => options.resolveSession(toCompatibilityAgentLoopInput(input))
735
- }));
736
- }
737
- function createAcpAgentServer$1(options) {
738
- return createAcpAgentServer({
739
- ...options.agentName === void 0 ? {} : { agentName: options.agentName },
740
- loop: toEffectAgentLoop(options.loop),
741
- ...options.permissionBridge ? { permissionBridge: options.permissionBridge } : {},
742
- workingDirectory: options.workingDirectory
743
- });
744
- }
745
- function serveAcpAgentOnStdio$1(app) {
746
- return serveAcpAgentOnStdio(app);
747
- }
748
- function createAcpStdioAgentLoop$1(options) {
749
- const handle = createAcpStdioAgentLoop({
750
- ...options.arguments ? { arguments: options.arguments } : {},
751
- ...options.clientName === void 0 ? {} : { clientName: options.clientName },
752
- command: options.command,
753
- ...options.environment ? { environment: options.environment } : {},
754
- ...options.initializationTimeoutMs === void 0 ? {} : { initializationTimeoutMs: options.initializationTimeoutMs },
755
- ...options.mcpServers ? { mcpServers: (input) => options.mcpServers(toCompatibilityAgentLoopInput(input)) } : {},
756
- ...options.onStderr ? { onStderr: options.onStderr } : {},
757
- ...options.permissionPolicy ? { permissionPolicy: options.permissionPolicy } : {},
758
- ...options.sessionStore ? { sessionStore: options.sessionStore } : {},
759
- ...options.terminationTimeoutMs === void 0 ? {} : { terminationTimeoutMs: options.terminationTimeoutMs },
760
- workingDirectory: options.workingDirectory
761
- });
762
- return {
763
- dispose: () => handle.dispose(),
764
- loop: fromEffectAgentLoop(handle.loop)
765
- };
766
- }
767
- //#endregion
768
- export { AcpSessionResumeFailed, AcpSessionResumeUnavailable, createAcpAgentLoop, createAcpAgentServer, createAcpPermissionBridge, createAcpStdioAgentLoop, createJsonAcpSessionStore, createAcpAgentLoop$1 as createLegacyAcpAgentLoop, createAcpAgentServer$1 as createLegacyAcpAgentServer, createAcpStdioAgentLoop$1 as createLegacyAcpStdioAgentLoop, decideAcpPermission, serveAcpAgentOnStdio, serveAcpAgentOnStdio$1 as serveLegacyAcpAgentOnStdio };
816
+ export { createAcpAgentServer, createAcpPermissionBridge, createAcpStdioAgentLoop, createJsonAcpSessionStore, serveAcpAgentOnStdio };
817
+