@moxt-ai/mobius 0.0.289 → 0.0.291

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/runtime.js CHANGED
@@ -21329,6 +21329,7 @@ var elicitationFieldSchema = external_exports.object({
21329
21329
  maximumLength: external_exports.string().max(128),
21330
21330
  minimum: external_exports.string().max(128),
21331
21331
  minimumLength: external_exports.string().max(128),
21332
+ multiline: external_exports.boolean().optional(),
21332
21333
  name: external_exports.string().min(1).max(128),
21333
21334
  options: external_exports.array(elicitationOptionSchema).max(200).readonly(),
21334
21335
  pattern: external_exports.string().max(500),
@@ -22568,7 +22569,7 @@ import { fetch as fetch3 } from "undici";
22568
22569
  // package.json
22569
22570
  var package_default = {
22570
22571
  name: "@moxt-ai/mobius",
22571
- version: "0.0.289",
22572
+ version: "0.0.291",
22572
22573
  description: "Mobius CLI for pairing and running local AI agent services.",
22573
22574
  type: "module",
22574
22575
  bin: {
@@ -67418,7 +67419,7 @@ var CodexBorrowedAcpAgent = class {
67418
67419
  const text2 = previous !== void 0 && value.startsWith(previous) ? value.slice(previous.length) : value;
67419
67420
  if (text2.length > 0) {
67420
67421
  await this.#emit({
67421
- sessionUpdate: item.type === "agentMessage" && item.phase !== "commentary" ? "agent_message_chunk" : "agent_thought_chunk",
67422
+ sessionUpdate: item.type === "agentMessage" ? "agent_message_chunk" : "agent_thought_chunk",
67422
67423
  messageId: key,
67423
67424
  content: { type: "text", text: text2 }
67424
67425
  });
@@ -67646,11 +67647,11 @@ function steeringPrompt(prompt) {
67646
67647
  }
67647
67648
  return blocks;
67648
67649
  }
67649
- function agentMessageUpdate(messageId, text2, phase) {
67650
+ function agentMessageUpdate(messageId, text2) {
67650
67651
  return {
67651
67652
  content: { text: text2, type: "text" },
67652
67653
  messageId,
67653
- sessionUpdate: phase === "commentary" ? "agent_thought_chunk" : "agent_message_chunk"
67654
+ sessionUpdate: "agent_message_chunk"
67654
67655
  };
67655
67656
  }
67656
67657
  function userInputSchema(questions) {
@@ -67899,10 +67900,9 @@ var CodexOwnedAcpAgent = class {
67899
67900
  if (method === "item/agentMessage/delta") {
67900
67901
  const decoded = reasoningTextDeltaParamsSchema.safeParse(params);
67901
67902
  if (decoded.success && decoded.data.threadId === this.#context.threadId() && this.#turn !== null && decoded.data.turnId === this.#turn.turn.id && decoded.data.delta.length > 0) {
67902
- const phase = this.#agentMessages.get(decoded.data.itemId)?.phase;
67903
- this.#agentMessages.set(decoded.data.itemId, { phase, kind: "streaming" });
67903
+ this.#agentMessages.set(decoded.data.itemId, "streaming");
67904
67904
  await this.#enqueue(async () => {
67905
- await this.#context.emit(agentMessageUpdate(decoded.data.itemId, decoded.data.delta, phase));
67905
+ await this.#context.emit(agentMessageUpdate(decoded.data.itemId, decoded.data.delta));
67906
67906
  });
67907
67907
  }
67908
67908
  return;
@@ -67916,16 +67916,16 @@ var CodexOwnedAcpAgent = class {
67916
67916
  }
67917
67917
  if (item.type === "agentMessage") {
67918
67918
  if (method === "item/started") {
67919
- this.#agentMessages.set(item.id, { phase: item.phase, kind: "started" });
67919
+ this.#agentMessages.set(item.id, "started");
67920
67920
  return;
67921
67921
  }
67922
67922
  const message = this.#agentMessages.get(item.id);
67923
67923
  this.#agentMessages.delete(item.id);
67924
- if (message?.kind === "streaming") {
67924
+ if (message === "streaming") {
67925
67925
  return;
67926
67926
  }
67927
67927
  await this.#enqueue(async () => {
67928
- await this.#context.emit(agentMessageUpdate(item.id, item.text, item.phase));
67928
+ await this.#context.emit(agentMessageUpdate(item.id, item.text));
67929
67929
  });
67930
67930
  return;
67931
67931
  }
@@ -68922,7 +68922,7 @@ var CodexAcpAgent = class {
68922
68922
  continue;
68923
68923
  }
68924
68924
  if (item.type === "agentMessage") {
68925
- await this.#emit(agentMessageUpdate(item.id, item.text, item.phase));
68925
+ await this.#emit(agentMessageUpdate(item.id, item.text));
68926
68926
  continue;
68927
68927
  }
68928
68928
  if (item.type === "contextCompaction") {
@@ -69000,7 +69000,7 @@ import { spawnSync } from "node:child_process";
69000
69000
  import { randomUUID as randomUUID7 } from "node:crypto";
69001
69001
  import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
69002
69002
  import { basename as basename4, dirname as dirname7, isAbsolute as isAbsolute11, join as join17 } from "node:path";
69003
- import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
69003
+ import { RequestError as RequestError7 } from "@agentclientprotocol/sdk";
69004
69004
 
69005
69005
  // src/acp/harness/pi/upstream/dist/pi-rpc/process.js
69006
69006
  import { spawn as spawn4 } from "node:child_process";
@@ -69033,6 +69033,13 @@ var PiRpcSpawnError = class extends Error {
69033
69033
  this.cause = opts?.cause;
69034
69034
  }
69035
69035
  };
69036
+ var PiRpcProcessError = class extends Error {
69037
+ code = "pi_process_exited";
69038
+ constructor(cause) {
69039
+ super("Pi process stopped unexpectedly.", { cause });
69040
+ this.name = "PiRpcProcessError";
69041
+ }
69042
+ };
69036
69043
  var ESC = String.fromCharCode(27);
69037
69044
  var CSI = String.fromCharCode(155);
69038
69045
  var ANSI_ESCAPE_REGEX = new RegExp(`[${ESC}${CSI}][[\\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`, "g");
@@ -69042,6 +69049,10 @@ function stripAnsi(s) {
69042
69049
  var SESSION_STATS_TIMEOUT_MS = 1e3;
69043
69050
  var PiRpcProcess = class _PiRpcProcess {
69044
69051
  child;
69052
+ lifetime = new AbortController();
69053
+ get signal() {
69054
+ return this.lifetime.signal;
69055
+ }
69045
69056
  pending = /* @__PURE__ */ new Map();
69046
69057
  eventHandlers = [];
69047
69058
  preludeLines = [];
@@ -69076,13 +69087,16 @@ var PiRpcProcess = class _PiRpcProcess {
69076
69087
  }
69077
69088
  });
69078
69089
  child.on("exit", (code, signal) => {
69079
- const err = new Error(`pi process exited (code=${code}, signal=${signal})`);
69090
+ const err = new PiRpcProcessError({ code, signal });
69091
+ this.lifetime.abort(err);
69080
69092
  for (const [, p] of this.pending) {
69081
69093
  p.reject(err);
69082
69094
  }
69083
69095
  this.pending.clear();
69084
69096
  });
69085
- child.on("error", (err) => {
69097
+ child.on("error", (cause) => {
69098
+ const err = new PiRpcProcessError(cause);
69099
+ this.lifetime.abort(err);
69086
69100
  for (const [, p] of this.pending) {
69087
69101
  p.reject(err);
69088
69102
  }
@@ -69164,6 +69178,7 @@ var PiRpcProcess = class _PiRpcProcess {
69164
69178
  };
69165
69179
  }
69166
69180
  dispose(signal = "SIGTERM") {
69181
+ this.lifetime.abort();
69167
69182
  if (this.child.killed) {
69168
69183
  return;
69169
69184
  }
@@ -69172,6 +69187,10 @@ var PiRpcProcess = class _PiRpcProcess {
69172
69187
  } catch {
69173
69188
  }
69174
69189
  }
69190
+ terminateAfterFailure(cause) {
69191
+ this.lifetime.abort(new PiRpcProcessError(cause));
69192
+ this.dispose();
69193
+ }
69175
69194
  /**
69176
69195
  * Human-readable stdout lines emitted before RPC NDJSON begins (e.g. Context/Skills/Extensions info).
69177
69196
  * Themes are typically noisy/less useful for ACP, so callers can filter as needed.
@@ -69192,8 +69211,8 @@ var PiRpcProcess = class _PiRpcProcess {
69192
69211
  throw new Error(`pi steer failed: ${res.error ?? JSON.stringify(res.data)}`);
69193
69212
  }
69194
69213
  }
69195
- async abort() {
69196
- const res = await this.request({ type: "abort" });
69214
+ async abort(opts) {
69215
+ const res = await this.request({ type: "abort" }, opts);
69197
69216
  if (!res.success) {
69198
69217
  throw new Error(`pi abort failed: ${res.error ?? JSON.stringify(res.data)}`);
69199
69218
  }
@@ -69890,7 +69909,176 @@ function getQuietStartup(cwd, environment = process.env) {
69890
69909
  import { randomUUID as randomUUID6 } from "node:crypto";
69891
69910
  import { readFileSync as readFileSync7 } from "node:fs";
69892
69911
  import { isAbsolute as isAbsolute10, resolve as resolvePath } from "node:path";
69912
+ import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
69913
+
69914
+ // src/acp/harness/pi/upstream/dist/acp/extension-questions.js
69893
69915
  import { RequestError as RequestError5 } from "@agentclientprotocol/sdk";
69916
+ var questionBase = {
69917
+ id: external_exports.string().min(1).max(255),
69918
+ title: external_exports.string().max(255),
69919
+ timeout: external_exports.number().int().nonnegative().max(2147483647).optional()
69920
+ };
69921
+ var questionSchema = external_exports.discriminatedUnion("method", [
69922
+ external_exports.object({
69923
+ ...questionBase,
69924
+ method: external_exports.literal("select"),
69925
+ options: external_exports.array(external_exports.string().min(1).max(255)).min(1).max(200).readonly()
69926
+ }),
69927
+ external_exports.object({ ...questionBase, method: external_exports.literal("confirm"), message: external_exports.string().max(2e3) }),
69928
+ external_exports.object({ ...questionBase, method: external_exports.literal("input"), placeholder: external_exports.string().max(500).optional() }),
69929
+ external_exports.object({ ...questionBase, method: external_exports.literal("editor"), prefill: external_exports.string().max(1e4).optional() })
69930
+ ]).readonly();
69931
+ var QUESTION_TIMEOUT_MS = 36e5;
69932
+ var PiQuestionError = class extends RequestError5 {
69933
+ constructor(kind, cause) {
69934
+ const messages = {
69935
+ request_failed: "Pi could not request an answer.",
69936
+ invalid_response: "Pi received an invalid answer.",
69937
+ response_failed: "Pi could not receive the answer."
69938
+ };
69939
+ super(-32603, "Internal error", { code: `pi_question_${kind}`, details: messages[kind] });
69940
+ this.cause = cause;
69941
+ }
69942
+ };
69943
+ function questionField(question) {
69944
+ const title2 = question.title || "Answer";
69945
+ switch (question.method) {
69946
+ case "select":
69947
+ return { type: "string", title: title2, enum: [...question.options] };
69948
+ case "confirm":
69949
+ return {
69950
+ type: "string",
69951
+ title: title2,
69952
+ oneOf: [
69953
+ { const: "yes", title: "Yes" },
69954
+ { const: "no", title: "No" }
69955
+ ]
69956
+ };
69957
+ case "input":
69958
+ return { type: "string", title: title2, description: question.placeholder, maxLength: 1e4 };
69959
+ case "editor":
69960
+ return {
69961
+ type: "string",
69962
+ title: title2,
69963
+ default: question.prefill,
69964
+ maxLength: 1e4,
69965
+ _meta: { "io.mobius/multiline": true }
69966
+ };
69967
+ }
69968
+ }
69969
+ var PiExtensionQuestions = class {
69970
+ sessionId;
69971
+ conn;
69972
+ proc;
69973
+ notify;
69974
+ turn = null;
69975
+ constructor(sessionId, conn, proc, notify) {
69976
+ this.sessionId = sessionId;
69977
+ this.conn = conn;
69978
+ this.proc = proc;
69979
+ this.notify = notify;
69980
+ }
69981
+ startTurn() {
69982
+ this.endTurn();
69983
+ this.turn = { controller: new AbortController(), requestIds: /* @__PURE__ */ new Set() };
69984
+ return this.turn.controller.signal;
69985
+ }
69986
+ endTurn(reason) {
69987
+ this.turn?.controller.abort(reason);
69988
+ this.turn = null;
69989
+ }
69990
+ async handle(event) {
69991
+ if (event.method !== "select" && event.method !== "confirm" && event.method !== "input" && event.method !== "editor") {
69992
+ return;
69993
+ }
69994
+ if (typeof event.id !== "string" || event.id.length === 0 || event.id.length > 255) {
69995
+ return;
69996
+ }
69997
+ const turn = this.turn;
69998
+ if (turn?.requestIds.has(event.id)) {
69999
+ return;
70000
+ }
70001
+ turn?.requestIds.add(event.id);
70002
+ try {
70003
+ await this.answer(event, turn);
70004
+ } catch (cause) {
70005
+ if (!this.proc.signal.aborted) {
70006
+ turn?.controller.abort(new PiQuestionError("response_failed", cause));
70007
+ this.proc.terminateAfterFailure(cause);
70008
+ }
70009
+ }
70010
+ }
70011
+ async answer(event, turn) {
70012
+ const id = typeof event.id === "string" ? event.id : "";
70013
+ const parsed = questionSchema.safeParse(event);
70014
+ if (!parsed.success) {
70015
+ await this.notify("Pi requested a question with unsupported or oversized fields. The question was cancelled without changing its content.");
70016
+ await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70017
+ return;
70018
+ }
70019
+ const question = parsed.data;
70020
+ if (turn === null || turn.controller.signal.aborted) {
70021
+ await this.proc.sendExtensionUiResponse({ id: question.id, cancelled: true });
70022
+ return;
70023
+ }
70024
+ const deadline = new AbortController();
70025
+ const timeout = setTimeout(() => deadline.abort(), question.timeout || QUESTION_TIMEOUT_MS);
70026
+ const signal = AbortSignal.any([turn.controller.signal, deadline.signal]);
70027
+ let response = { id: question.id, cancelled: true };
70028
+ try {
70029
+ const answer = await this.conn.requestElicitation({
70030
+ sessionId: this.sessionId,
70031
+ mode: "form",
70032
+ message: (question.method === "confirm" ? question.message : "") || question.title || "Pi question",
70033
+ requestedSchema: {
70034
+ type: "object",
70035
+ properties: { value: questionField(question) },
70036
+ required: ["value"]
70037
+ }
70038
+ }, signal);
70039
+ const content = answer.action === "accept" ? answer.content : void 0;
70040
+ const value = typeof content === "object" && content !== null ? Reflect.get(content, "value") : void 0;
70041
+ if (!signal.aborted && answer.action === "accept") {
70042
+ if (typeof value !== "string") {
70043
+ throw new PiQuestionError("invalid_response");
70044
+ }
70045
+ switch (question.method) {
70046
+ case "select":
70047
+ if (!question.options.includes(value)) {
70048
+ throw new PiQuestionError("invalid_response");
70049
+ }
70050
+ response = { id: question.id, value };
70051
+ break;
70052
+ case "confirm":
70053
+ if (value !== "yes" && value !== "no") {
70054
+ throw new PiQuestionError("invalid_response");
70055
+ }
70056
+ response = { id: question.id, confirmed: value === "yes" };
70057
+ break;
70058
+ case "input":
70059
+ case "editor":
70060
+ if (value.length > 1e4) {
70061
+ throw new PiQuestionError("invalid_response");
70062
+ }
70063
+ response = { id: question.id, value };
70064
+ break;
70065
+ }
70066
+ } else if (!signal.aborted && answer.action !== "cancel" && answer.action !== "decline") {
70067
+ throw new PiQuestionError("invalid_response");
70068
+ }
70069
+ } catch (cause) {
70070
+ if (!signal.aborted && !(cause instanceof RequestError5 && cause.code === -32800)) {
70071
+ turn.controller.abort(cause instanceof PiQuestionError ? cause : new PiQuestionError("request_failed", cause));
70072
+ }
70073
+ } finally {
70074
+ clearTimeout(timeout);
70075
+ }
70076
+ if (deadline.signal.aborted && signal.reason === deadline.signal.reason) {
70077
+ await this.notify("Pi question timed out; no answer was submitted.");
70078
+ }
70079
+ await this.proc.sendExtensionUiResponse(response);
70080
+ }
70081
+ };
69894
70082
 
69895
70083
  // src/acp/harness/pi/upstream/dist/acp/session-store.js
69896
70084
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "node:fs";
@@ -70159,12 +70347,6 @@ ${stderr}`);
70159
70347
  }
70160
70348
 
70161
70349
  // src/acp/harness/pi/upstream/dist/acp/session.js
70162
- var CONFIRM_PERMISSION_OPTIONS = [
70163
- { optionId: "yes", name: "Yes", kind: "allow_once" },
70164
- { optionId: "no", name: "No", kind: "reject_once" }
70165
- ];
70166
- var EXTENSION_UI_RAW_INPUT_KEYS = ["title", "message", "options", "placeholder", "prefill"];
70167
- var CHOICE_OPTION_PREFIX = "choice-";
70168
70350
  function toUsageUpdate(stats) {
70169
70351
  const used = stats?.contextUsage?.tokens;
70170
70352
  const size = stats?.contextUsage?.contextWindow;
@@ -70285,7 +70467,7 @@ var SessionManager = class {
70285
70467
  return;
70286
70468
  }
70287
70469
  try {
70288
- s.proc.dispose?.();
70470
+ s.dispose();
70289
70471
  } catch {
70290
70472
  }
70291
70473
  this.sessions.delete(sessionId);
@@ -70310,7 +70492,7 @@ var SessionManager = class {
70310
70492
  });
70311
70493
  } catch (e) {
70312
70494
  if (e instanceof PiRpcSpawnError) {
70313
- throw RequestError5.internalError({ code: e.code }, e.message);
70495
+ throw RequestError6.internalError({ code: e.code }, e.message);
70314
70496
  }
70315
70497
  throw e;
70316
70498
  }
@@ -70339,7 +70521,7 @@ var SessionManager = class {
70339
70521
  get(sessionId) {
70340
70522
  const s = this.sessions.get(sessionId);
70341
70523
  if (!s) {
70342
- throw RequestError5.invalidParams(`Unknown sessionId: ${sessionId}`);
70524
+ throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
70343
70525
  }
70344
70526
  return s;
70345
70527
  }
@@ -70372,6 +70554,7 @@ var PiAcpSession = class {
70372
70554
  startupInfoSent = false;
70373
70555
  proc;
70374
70556
  conn;
70557
+ extensionQuestions;
70375
70558
  fileCommands;
70376
70559
  // Used to map abort semantics to ACP stopReason.
70377
70560
  // Applies to the currently running turn.
@@ -70426,8 +70609,38 @@ var PiAcpSession = class {
70426
70609
  this.proc = opts.proc;
70427
70610
  this.conn = opts.conn;
70428
70611
  this.fileCommands = opts.fileCommands ?? [];
70612
+ this.extensionQuestions = new PiExtensionQuestions(this.sessionId, this.conn, this.proc, async (text2) => {
70613
+ this.emit({ sessionUpdate: "agent_message_chunk", content: { type: "text", text: text2 } });
70614
+ await this.flushEmits();
70615
+ });
70616
+ this.proc.signal.addEventListener("abort", () => {
70617
+ const cause = this.proc.signal.reason;
70618
+ if (cause instanceof PiRpcProcessError) {
70619
+ const pending = this.pendingTurn;
70620
+ const reason = pending?.questionSignal.reason;
70621
+ const error62 = reason instanceof RequestError6 ? reason : RequestError6.internalError({ code: cause.code, details: cause.message });
70622
+ if (!(reason instanceof RequestError6)) {
70623
+ error62.cause = cause;
70624
+ }
70625
+ this.extensionQuestions.endTurn(error62);
70626
+ this.pendingTurn = null;
70627
+ void this.flushEmits().then(() => {
70628
+ if (this.cancelRequested) {
70629
+ pending?.resolve("cancelled");
70630
+ } else {
70631
+ pending?.reject(error62);
70632
+ }
70633
+ });
70634
+ } else {
70635
+ this.extensionQuestions.endTurn();
70636
+ }
70637
+ }, { once: true });
70429
70638
  this.proc.onEvent((ev2) => this.handlePiEvent(ev2));
70430
70639
  }
70640
+ dispose() {
70641
+ this.extensionQuestions.endTurn();
70642
+ this.proc.dispose();
70643
+ }
70431
70644
  setStartupInfo(text2) {
70432
70645
  this.startupInfo = text2;
70433
70646
  this.startupInfoSent = false;
@@ -70470,6 +70683,31 @@ var PiAcpSession = class {
70470
70683
  });
70471
70684
  return turnPromise;
70472
70685
  }
70686
+ async compact(customInstructions, timeoutMs) {
70687
+ const signal = this.extensionQuestions.startTurn();
70688
+ try {
70689
+ const result = await this.proc.compact(customInstructions, { signal, timeoutMs });
70690
+ if (signal.aborted && signal.reason instanceof RequestError6) {
70691
+ throw signal.reason;
70692
+ }
70693
+ return result;
70694
+ } catch (cause) {
70695
+ if (signal.aborted && signal.reason instanceof RequestError6) {
70696
+ try {
70697
+ await this.proc.abort({ signal: this.proc.signal, timeoutMs });
70698
+ } catch (abortCause) {
70699
+ if (!this.proc.signal.aborted) {
70700
+ this.proc.terminateAfterFailure(abortCause);
70701
+ }
70702
+ }
70703
+ throw signal.reason;
70704
+ }
70705
+ throw cause;
70706
+ } finally {
70707
+ this.extensionQuestions.endTurn();
70708
+ await this.flushEmits();
70709
+ }
70710
+ }
70473
70711
  async steer(message, images, signal) {
70474
70712
  const target = this.pendingTurn;
70475
70713
  if (target === null) {
@@ -70488,6 +70726,7 @@ var PiAcpSession = class {
70488
70726
  }
70489
70727
  async cancel() {
70490
70728
  this.cancelRequested = true;
70729
+ this.extensionQuestions.endTurn();
70491
70730
  if (this.turnQueue.length) {
70492
70731
  const queued = this.turnQueue.splice(0, this.turnQueue.length);
70493
70732
  for (const t of queued) {
@@ -70502,7 +70741,13 @@ var PiAcpSession = class {
70502
70741
  _meta: { piAcp: { queueDepth: 0, running: Boolean(this.pendingTurn) } }
70503
70742
  });
70504
70743
  }
70505
- await this.proc.abort();
70744
+ try {
70745
+ await this.proc.abort();
70746
+ } catch (cause) {
70747
+ if (!this.proc.signal.aborted) {
70748
+ throw cause;
70749
+ }
70750
+ }
70506
70751
  }
70507
70752
  wasCancelRequested() {
70508
70753
  return this.cancelRequested;
@@ -70535,15 +70780,19 @@ var PiAcpSession = class {
70535
70780
  await this.flushEmits();
70536
70781
  }
70537
70782
  async settleTurn() {
70783
+ const questionSignal = this.pendingTurn?.questionSignal;
70784
+ this.extensionQuestions.endTurn();
70538
70785
  await this.publishContextUsage();
70539
70786
  const retryFailure = this.turnRetryFailure;
70540
70787
  this.turnRetryFailure = null;
70541
- if (retryFailure !== null && !this.cancelRequested) {
70788
+ if (questionSignal?.aborted && questionSignal.reason instanceof RequestError6 && !this.cancelRequested) {
70789
+ this.pendingTurn?.reject(questionSignal.reason);
70790
+ } else if (retryFailure !== null && !this.cancelRequested) {
70542
70791
  this.pendingTurn?.reject(
70543
70792
  // The message rides in `data.details` so clients that unwrap the
70544
70793
  // JSON-RPC internal-error envelope (like Mobius) surface the real
70545
70794
  // model error; `code` keeps the failure machine-readable.
70546
- RequestError5.internalError({
70795
+ RequestError6.internalError({
70547
70796
  code: "pi_retries_exhausted",
70548
70797
  details: `Pi retries exhausted: ${retryFailure}`
70549
70798
  })
@@ -70597,14 +70846,16 @@ var PiAcpSession = class {
70597
70846
  this.bashToolCallIds.delete(toolCallId);
70598
70847
  }
70599
70848
  startTurn(t) {
70849
+ const questionSignal = this.extensionQuestions.startTurn();
70600
70850
  this.cancelRequested = false;
70601
70851
  this.turnRetryFailure = null;
70602
- this.pendingTurn = { resolve: t.resolve, reject: t.reject };
70852
+ this.pendingTurn = { questionSignal, resolve: t.resolve, reject: t.reject };
70603
70853
  this.emit({
70604
70854
  sessionUpdate: "session_info_update",
70605
70855
  _meta: { piAcp: { queueDepth: this.turnQueue.length, running: true } }
70606
70856
  });
70607
70857
  this.proc.prompt(t.message, t.images).catch((err) => {
70858
+ this.extensionQuestions.endTurn();
70608
70859
  void this.flushEmits().finally(() => {
70609
70860
  const authErr = maybeAuthRequiredError(err);
70610
70861
  if (authErr) {
@@ -70843,14 +71094,15 @@ var PiAcpSession = class {
70843
71094
  break;
70844
71095
  }
70845
71096
  case "extension_ui_request": {
70846
- void this.handleExtensionUiRequest(ev2).catch(() => {
70847
- const id = stringProp(ev2, "id");
70848
- if (!id) {
70849
- return;
70850
- }
70851
- void this.proc.sendExtensionUiResponse({ id, cancelled: true }).catch(() => {
71097
+ if (ev2.method === "notify") {
71098
+ this.emit({
71099
+ sessionUpdate: "agent_message_chunk",
71100
+ content: { type: "text", text: stringProp(ev2, "message") ?? "Pi notification" },
71101
+ _meta: { piAcp: { notify: { level: stringProp(ev2, "notifyType") ?? "info" } } }
70852
71102
  });
70853
- });
71103
+ break;
71104
+ }
71105
+ void this.extensionQuestions.handle(ev2);
70854
71106
  break;
70855
71107
  }
70856
71108
  case "auto_retry_start": {
@@ -70903,119 +71155,11 @@ var PiAcpSession = class {
70903
71155
  break;
70904
71156
  }
70905
71157
  }
70906
- async handleExtensionUiRequest(ev2) {
70907
- const id = stringProp(ev2, "id");
70908
- const method = stringProp(ev2, "method");
70909
- if (!id) {
70910
- return;
70911
- }
70912
- if (method === "select") {
70913
- await this.handleExtensionSelect(ev2, id);
70914
- return;
70915
- }
70916
- if (method === "confirm") {
70917
- await this.handleExtensionConfirm(ev2, id);
70918
- return;
70919
- }
70920
- if (method === "input" || method === "editor") {
70921
- this.emit({
70922
- sessionUpdate: "agent_message_chunk",
70923
- content: {
70924
- type: "text",
70925
- text: `Pi ${method} UI request is not supported in ACP yet; cancelling it.`
70926
- }
70927
- });
70928
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70929
- return;
70930
- }
70931
- if (method === "notify") {
70932
- this.emit({
70933
- sessionUpdate: "agent_message_chunk",
70934
- content: { type: "text", text: stringProp(ev2, "message") ?? "Pi notification" },
70935
- _meta: { piAcp: { notify: { level: stringProp(ev2, "notifyType") ?? "info" } } }
70936
- });
70937
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70938
- return;
70939
- }
70940
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70941
- }
70942
- async handleExtensionSelect(ev2, id) {
70943
- const rawOptions = ev2.options;
70944
- const options = Array.isArray(rawOptions) ? rawOptions.map((option) => String(option)) : [];
70945
- if (!options.length) {
70946
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70947
- return;
70948
- }
70949
- const permissionOptions = options.map((name2, index3) => ({
70950
- optionId: `${CHOICE_OPTION_PREFIX}${index3}`,
70951
- name: name2,
70952
- kind: "allow_once"
70953
- }));
70954
- const selected = await this.requestExtensionPermission(id, ev2, permissionOptions);
70955
- if (selected === null) {
70956
- return;
70957
- }
70958
- const selectedOptionId = selected.outcome.outcome === "selected" ? selected.outcome.optionId : null;
70959
- const index2 = selectedOptionId === null ? null : optionIndex(selectedOptionId);
70960
- const value = index2 === null ? null : options.at(index2) ?? null;
70961
- await this.proc.sendExtensionUiResponse(value === null ? { id, cancelled: true } : { id, value });
70962
- }
70963
- async handleExtensionConfirm(ev2, id) {
70964
- const selected = await this.requestExtensionPermission(id, ev2, CONFIRM_PERMISSION_OPTIONS);
70965
- if (selected === null) {
70966
- return;
70967
- }
70968
- if (selected.outcome.outcome === "cancelled") {
70969
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70970
- return;
70971
- }
70972
- await this.proc.sendExtensionUiResponse({ id, confirmed: selected.outcome.optionId === "yes" });
70973
- }
70974
- async requestExtensionPermission(id, ev2, options) {
70975
- try {
70976
- return await this.conn.requestPermission({
70977
- sessionId: this.sessionId,
70978
- toolCall: extensionUiToolCall(id, ev2),
70979
- options
70980
- });
70981
- } catch {
70982
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70983
- return null;
70984
- }
70985
- }
70986
71158
  };
70987
- function extensionUiToolCall(id, ev2) {
70988
- const method = stringProp(ev2, "method") ?? "ui";
70989
- const title2 = stringProp(ev2, "title") ?? `Pi ${method}`;
70990
- const rawInput = { method };
70991
- for (const key of EXTENSION_UI_RAW_INPUT_KEYS) {
70992
- if (Object.hasOwn(ev2, key)) {
70993
- rawInput[key] = ev2[key];
70994
- }
70995
- }
70996
- return {
70997
- toolCallId: `pi-ui-${id}`,
70998
- title: title2,
70999
- kind: "other",
71000
- status: "pending",
71001
- rawInput
71002
- };
71003
- }
71004
71159
  function stringProp(source, key) {
71005
71160
  const value = source[key];
71006
71161
  return typeof value === "string" ? value : null;
71007
71162
  }
71008
- function optionIndex(optionId) {
71009
- if (!optionId.startsWith(CHOICE_OPTION_PREFIX)) {
71010
- return null;
71011
- }
71012
- const rawIndex = optionId.slice(CHOICE_OPTION_PREFIX.length);
71013
- if (!rawIndex) {
71014
- return null;
71015
- }
71016
- const index2 = Number(rawIndex);
71017
- return Number.isSafeInteger(index2) && index2 >= 0 && String(index2) === rawIndex ? index2 : null;
71018
- }
71019
71163
  function toToolKind(toolName) {
71020
71164
  switch (toolName) {
71021
71165
  case "read":
@@ -71242,7 +71386,7 @@ var PiAcpAgent = class {
71242
71386
  const restorePromise = (async () => {
71243
71387
  const stored = this.findStoredSession(sessionId);
71244
71388
  if (!stored) {
71245
- throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
71389
+ throw RequestError7.invalidParams(`Unknown sessionId: ${sessionId}`);
71246
71390
  }
71247
71391
  const cwd = opts?.cwd ?? stored.cwd;
71248
71392
  let proc;
@@ -71255,7 +71399,7 @@ var PiAcpAgent = class {
71255
71399
  });
71256
71400
  } catch (e) {
71257
71401
  if (e?.name === "PiRpcSpawnError") {
71258
- throw RequestError6.internalError({ code: e?.code }, String(e?.message ?? e));
71402
+ throw RequestError7.internalError({ code: e?.code }, String(e?.message ?? e));
71259
71403
  }
71260
71404
  throw e;
71261
71405
  }
@@ -71315,7 +71459,7 @@ var PiAcpAgent = class {
71315
71459
  }
71316
71460
  async newSession(params) {
71317
71461
  if (!isAbsolute11(params.cwd)) {
71318
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71462
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71319
71463
  }
71320
71464
  this.lastSessionCwd = params.cwd;
71321
71465
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -71354,16 +71498,16 @@ var PiAcpAgent = class {
71354
71498
  }
71355
71499
  if (availableModelsErr) {
71356
71500
  this.cleanupFailedNewSession(session.sessionId, state);
71357
- throw RequestError6.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
71501
+ throw RequestError7.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
71358
71502
  }
71359
71503
  const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
71360
71504
  if (rawModelsCount === 0) {
71361
71505
  this.cleanupFailedNewSession(session.sessionId, state);
71362
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71506
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71363
71507
  }
71364
71508
  if (stateErr && maybeAuthRequiredError(stateErr)) {
71365
71509
  this.cleanupFailedNewSession(session.sessionId, state);
71366
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71510
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71367
71511
  }
71368
71512
  let configuration;
71369
71513
  try {
@@ -71373,7 +71517,7 @@ var PiAcpAgent = class {
71373
71517
  configuration = await getSessionConfiguration(session.proc, { state, availableModels });
71374
71518
  } catch (err) {
71375
71519
  this.cleanupFailedNewSession(session.sessionId, state);
71376
- throw maybeAuthRequiredError(err) ?? RequestError6.internalError({}, String(err?.message ?? err));
71520
+ throw maybeAuthRequiredError(err) ?? RequestError7.internalError({}, String(err?.message ?? err));
71377
71521
  }
71378
71522
  const { configOptions, models, modes } = configuration;
71379
71523
  const quietStartup = getQuietStartup(params.cwd, this.environment);
@@ -71458,9 +71602,7 @@ var PiAcpAgent = class {
71458
71602
  }
71459
71603
  });
71460
71604
  try {
71461
- const res = await session.proc.compact(customInstructions, {
71462
- timeoutMs: COMPACT_TIMEOUT_MILLISECONDS2
71463
- });
71605
+ const res = await session.compact(customInstructions, COMPACT_TIMEOUT_MILLISECONDS2);
71464
71606
  const r = res && typeof res === "object" ? res : null;
71465
71607
  const summary = typeof r?.summary === "string" ? r.summary : null;
71466
71608
  if (summary !== null && summary.length > 0) {
@@ -71873,7 +72015,7 @@ ${JSON.stringify(stats, null, 2)}`;
71873
72015
  signal.throwIfAborted();
71874
72016
  const parsed = steerRequestSchema.safeParse(params);
71875
72017
  if (!parsed.success) {
71876
- throw RequestError6.invalidParams("Invalid steering request");
72018
+ throw RequestError7.invalidParams("Invalid steering request");
71877
72019
  }
71878
72020
  const session = this.sessions.maybeGet(parsed.data.sessionId);
71879
72021
  if (session === void 0) {
@@ -71914,11 +72056,11 @@ ${JSON.stringify(stats, null, 2)}`;
71914
72056
  }
71915
72057
  async forkSession(params) {
71916
72058
  if (!isAbsolute11(params.cwd)) {
71917
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
72059
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71918
72060
  }
71919
72061
  const source = this.findStoredSession(params.sessionId);
71920
72062
  if (!source) {
71921
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
72063
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
71922
72064
  }
71923
72065
  this.lastSessionCwd = params.cwd;
71924
72066
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -71980,13 +72122,13 @@ ${JSON.stringify(stats, null, 2)}`;
71980
72122
  }
71981
72123
  async loadSession(params) {
71982
72124
  if (!isAbsolute11(params.cwd)) {
71983
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
72125
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71984
72126
  }
71985
72127
  this.sessions.close(params.sessionId);
71986
72128
  this.lastSessionCwd = params.cwd;
71987
72129
  const stored = this.findStoredSession(params.sessionId);
71988
72130
  if (!stored) {
71989
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
72131
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
71990
72132
  }
71991
72133
  const enableSkillCommands = getEnableSkillCommands(params.cwd, this.environment);
71992
72134
  const session = await this.restoreSession(params.sessionId, {
@@ -72161,7 +72303,7 @@ ${JSON.stringify(stats, null, 2)}`;
72161
72303
  const session = await this.restoreSession(params.sessionId);
72162
72304
  const mode = params.modeId;
72163
72305
  if (typeof mode !== "string" || mode.length === 0) {
72164
- throw RequestError6.invalidParams("Expected nonempty string modeId");
72306
+ throw RequestError7.invalidParams("Expected nonempty string modeId");
72165
72307
  }
72166
72308
  await session.proc.setThinkingLevel(mode);
72167
72309
  await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
@@ -72172,18 +72314,18 @@ ${JSON.stringify(stats, null, 2)}`;
72172
72314
  const configId = String(params.configId);
72173
72315
  let modelChanged = false;
72174
72316
  if (typeof params.value !== "string") {
72175
- throw RequestError6.invalidParams(`Expected string value for config option: ${configId}`);
72317
+ throw RequestError7.invalidParams(`Expected string value for config option: ${configId}`);
72176
72318
  }
72177
72319
  if (configId === MODEL_CONFIG_ID2) {
72178
72320
  await setSessionModel(session.proc, params.value);
72179
72321
  modelChanged = true;
72180
72322
  } else if (configId === THOUGHT_LEVEL_CONFIG_ID) {
72181
72323
  if (params.value.length === 0) {
72182
- throw RequestError6.invalidParams("Expected nonempty thinking level");
72324
+ throw RequestError7.invalidParams("Expected nonempty thinking level");
72183
72325
  }
72184
72326
  await session.proc.setThinkingLevel(params.value);
72185
72327
  } else {
72186
- throw RequestError6.invalidParams(`Unknown config option: ${configId}`);
72328
+ throw RequestError7.invalidParams(`Unknown config option: ${configId}`);
72187
72329
  }
72188
72330
  const configOptions = await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
72189
72331
  if (modelChanged) {
@@ -72339,7 +72481,7 @@ async function setSessionModel(proc, requestedModelId) {
72339
72481
  }
72340
72482
  }
72341
72483
  if (!provider || !modelId) {
72342
- throw RequestError6.invalidParams(`Unknown modelId: ${requestedModelId}`);
72484
+ throw RequestError7.invalidParams(`Unknown modelId: ${requestedModelId}`);
72343
72485
  }
72344
72486
  await proc.setModel(provider, modelId);
72345
72487
  }
@@ -72521,6 +72663,7 @@ function buildStartupInfo(opts) {
72521
72663
  import { methods as methods4 } from "@agentclientprotocol/sdk";
72522
72664
  function createPiAcpClient(connection, onSessionUpdate) {
72523
72665
  return {
72666
+ requestElicitation: async (request, signal) => await connection.client.request(methods4.client.elicitation.create, request, { cancellationSignal: signal }),
72524
72667
  requestPermission: async (request) => await connection.client.request(methods4.client.session.requestPermission, request),
72525
72668
  sessionUpdate: onSessionUpdate
72526
72669
  };
@@ -72844,7 +72987,7 @@ import {
72844
72987
  methods as methods8,
72845
72988
  ndJsonStream as ndJsonStream2,
72846
72989
  PROTOCOL_VERSION as PROTOCOL_VERSION2,
72847
- RequestError as RequestError7
72990
+ RequestError as RequestError8
72848
72991
  } from "@agentclientprotocol/sdk";
72849
72992
 
72850
72993
  // src/acp/connection/create-client.ts
@@ -73018,7 +73161,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
73018
73161
  if (error62 instanceof AgentProbeError) {
73019
73162
  throw error62;
73020
73163
  }
73021
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73164
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73022
73165
  throw new AgentProbeError("authentication_required", "Agent authentication is required", {
73023
73166
  cause: error62
73024
73167
  });
@@ -73094,7 +73237,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
73094
73237
  if (error62 instanceof AgentProbeError) {
73095
73238
  throw error62;
73096
73239
  }
73097
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73240
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73098
73241
  throw new AgentProbeError("authentication_required", "Agent authentication is required", { cause: error62 });
73099
73242
  }
73100
73243
  throw new AgentProbeError("acp_incompatible", "ACP adapter initialization failed", { cause: error62 });
@@ -81950,7 +82093,7 @@ function buildPromptContent(options, capabilities) {
81950
82093
 
81951
82094
  // src/acp/connection/agent-connection.ts
81952
82095
  import { spawn as spawn7 } from "node:child_process";
81953
- import { ndJsonStream as ndJsonStream3, RequestError as RequestError8 } from "@agentclientprotocol/sdk";
82096
+ import { ndJsonStream as ndJsonStream3, RequestError as RequestError9 } from "@agentclientprotocol/sdk";
81954
82097
  function requestErrorMessage(error62) {
81955
82098
  const fallback = truncate(error62.message, 500);
81956
82099
  if (error62.message !== "Internal error" || !isRecord(error62.data)) {
@@ -82155,14 +82298,14 @@ var AgentConnection = class {
82155
82298
  ]);
82156
82299
  } catch (error62) {
82157
82300
  signal.throwIfAborted();
82158
- if (error62 instanceof RequestError8 && error62.code === -32e3) {
82301
+ if (error62 instanceof RequestError9 && error62.code === -32e3) {
82159
82302
  throw new AcpExecutionError(
82160
82303
  "Agent authentication is required",
82161
82304
  { cause: error62 },
82162
82305
  "authentication_required"
82163
82306
  );
82164
82307
  }
82165
- if (error62 instanceof RequestError8) {
82308
+ if (error62 instanceof RequestError9) {
82166
82309
  throw new AcpExecutionError(requestErrorMessage(error62), { cause: error62 }, "acp_agent_error");
82167
82310
  }
82168
82311
  throw error62;
@@ -82616,6 +82759,7 @@ function normalizeElicitationFields(requestedSchema) {
82616
82759
  const options = normalizeElicitationOptions(property);
82617
82760
  const normalizedType = type === "string" && options.length > 0 ? "select" : type;
82618
82761
  const defaultValue = property["default"];
82762
+ const metadata = property["_meta"];
82619
82763
  normalized.push({
82620
82764
  defaultBooleanValue: typeof defaultValue === "boolean" ? defaultValue : false,
82621
82765
  defaultNumberValue: typeof defaultValue === "number" && Number.isFinite(defaultValue) ? defaultValue : 0,
@@ -82627,6 +82771,7 @@ function normalizeElicitationFields(requestedSchema) {
82627
82771
  maximumLength: numericString(property["maxLength"] ?? property["maxItems"]),
82628
82772
  minimum: numericString(property["minimum"]),
82629
82773
  minimumLength: numericString(property["minLength"] ?? property["minItems"]),
82774
+ ...normalizedType === "string" && isRecord(metadata) && metadata["io.mobius/multiline"] === true ? { multiline: true } : {},
82630
82775
  name: truncate(name2, 128),
82631
82776
  options,
82632
82777
  pattern: truncate(readString(property["pattern"]), 500),