@moxt-ai/mobius 0.0.290 → 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.290",
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: {
@@ -68999,7 +69000,7 @@ import { spawnSync } from "node:child_process";
68999
69000
  import { randomUUID as randomUUID7 } from "node:crypto";
69000
69001
  import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
69001
69002
  import { basename as basename4, dirname as dirname7, isAbsolute as isAbsolute11, join as join17 } from "node:path";
69002
- import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
69003
+ import { RequestError as RequestError7 } from "@agentclientprotocol/sdk";
69003
69004
 
69004
69005
  // src/acp/harness/pi/upstream/dist/pi-rpc/process.js
69005
69006
  import { spawn as spawn4 } from "node:child_process";
@@ -69032,6 +69033,13 @@ var PiRpcSpawnError = class extends Error {
69032
69033
  this.cause = opts?.cause;
69033
69034
  }
69034
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
+ };
69035
69043
  var ESC = String.fromCharCode(27);
69036
69044
  var CSI = String.fromCharCode(155);
69037
69045
  var ANSI_ESCAPE_REGEX = new RegExp(`[${ESC}${CSI}][[\\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`, "g");
@@ -69041,6 +69049,10 @@ function stripAnsi(s) {
69041
69049
  var SESSION_STATS_TIMEOUT_MS = 1e3;
69042
69050
  var PiRpcProcess = class _PiRpcProcess {
69043
69051
  child;
69052
+ lifetime = new AbortController();
69053
+ get signal() {
69054
+ return this.lifetime.signal;
69055
+ }
69044
69056
  pending = /* @__PURE__ */ new Map();
69045
69057
  eventHandlers = [];
69046
69058
  preludeLines = [];
@@ -69075,13 +69087,16 @@ var PiRpcProcess = class _PiRpcProcess {
69075
69087
  }
69076
69088
  });
69077
69089
  child.on("exit", (code, signal) => {
69078
- const err = new Error(`pi process exited (code=${code}, signal=${signal})`);
69090
+ const err = new PiRpcProcessError({ code, signal });
69091
+ this.lifetime.abort(err);
69079
69092
  for (const [, p] of this.pending) {
69080
69093
  p.reject(err);
69081
69094
  }
69082
69095
  this.pending.clear();
69083
69096
  });
69084
- child.on("error", (err) => {
69097
+ child.on("error", (cause) => {
69098
+ const err = new PiRpcProcessError(cause);
69099
+ this.lifetime.abort(err);
69085
69100
  for (const [, p] of this.pending) {
69086
69101
  p.reject(err);
69087
69102
  }
@@ -69163,6 +69178,7 @@ var PiRpcProcess = class _PiRpcProcess {
69163
69178
  };
69164
69179
  }
69165
69180
  dispose(signal = "SIGTERM") {
69181
+ this.lifetime.abort();
69166
69182
  if (this.child.killed) {
69167
69183
  return;
69168
69184
  }
@@ -69171,6 +69187,10 @@ var PiRpcProcess = class _PiRpcProcess {
69171
69187
  } catch {
69172
69188
  }
69173
69189
  }
69190
+ terminateAfterFailure(cause) {
69191
+ this.lifetime.abort(new PiRpcProcessError(cause));
69192
+ this.dispose();
69193
+ }
69174
69194
  /**
69175
69195
  * Human-readable stdout lines emitted before RPC NDJSON begins (e.g. Context/Skills/Extensions info).
69176
69196
  * Themes are typically noisy/less useful for ACP, so callers can filter as needed.
@@ -69191,8 +69211,8 @@ var PiRpcProcess = class _PiRpcProcess {
69191
69211
  throw new Error(`pi steer failed: ${res.error ?? JSON.stringify(res.data)}`);
69192
69212
  }
69193
69213
  }
69194
- async abort() {
69195
- const res = await this.request({ type: "abort" });
69214
+ async abort(opts) {
69215
+ const res = await this.request({ type: "abort" }, opts);
69196
69216
  if (!res.success) {
69197
69217
  throw new Error(`pi abort failed: ${res.error ?? JSON.stringify(res.data)}`);
69198
69218
  }
@@ -69889,7 +69909,176 @@ function getQuietStartup(cwd, environment = process.env) {
69889
69909
  import { randomUUID as randomUUID6 } from "node:crypto";
69890
69910
  import { readFileSync as readFileSync7 } from "node:fs";
69891
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
69892
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
+ };
69893
70082
 
69894
70083
  // src/acp/harness/pi/upstream/dist/acp/session-store.js
69895
70084
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "node:fs";
@@ -70158,12 +70347,6 @@ ${stderr}`);
70158
70347
  }
70159
70348
 
70160
70349
  // src/acp/harness/pi/upstream/dist/acp/session.js
70161
- var CONFIRM_PERMISSION_OPTIONS = [
70162
- { optionId: "yes", name: "Yes", kind: "allow_once" },
70163
- { optionId: "no", name: "No", kind: "reject_once" }
70164
- ];
70165
- var EXTENSION_UI_RAW_INPUT_KEYS = ["title", "message", "options", "placeholder", "prefill"];
70166
- var CHOICE_OPTION_PREFIX = "choice-";
70167
70350
  function toUsageUpdate(stats) {
70168
70351
  const used = stats?.contextUsage?.tokens;
70169
70352
  const size = stats?.contextUsage?.contextWindow;
@@ -70284,7 +70467,7 @@ var SessionManager = class {
70284
70467
  return;
70285
70468
  }
70286
70469
  try {
70287
- s.proc.dispose?.();
70470
+ s.dispose();
70288
70471
  } catch {
70289
70472
  }
70290
70473
  this.sessions.delete(sessionId);
@@ -70309,7 +70492,7 @@ var SessionManager = class {
70309
70492
  });
70310
70493
  } catch (e) {
70311
70494
  if (e instanceof PiRpcSpawnError) {
70312
- throw RequestError5.internalError({ code: e.code }, e.message);
70495
+ throw RequestError6.internalError({ code: e.code }, e.message);
70313
70496
  }
70314
70497
  throw e;
70315
70498
  }
@@ -70338,7 +70521,7 @@ var SessionManager = class {
70338
70521
  get(sessionId) {
70339
70522
  const s = this.sessions.get(sessionId);
70340
70523
  if (!s) {
70341
- throw RequestError5.invalidParams(`Unknown sessionId: ${sessionId}`);
70524
+ throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
70342
70525
  }
70343
70526
  return s;
70344
70527
  }
@@ -70371,6 +70554,7 @@ var PiAcpSession = class {
70371
70554
  startupInfoSent = false;
70372
70555
  proc;
70373
70556
  conn;
70557
+ extensionQuestions;
70374
70558
  fileCommands;
70375
70559
  // Used to map abort semantics to ACP stopReason.
70376
70560
  // Applies to the currently running turn.
@@ -70425,8 +70609,38 @@ var PiAcpSession = class {
70425
70609
  this.proc = opts.proc;
70426
70610
  this.conn = opts.conn;
70427
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 });
70428
70638
  this.proc.onEvent((ev2) => this.handlePiEvent(ev2));
70429
70639
  }
70640
+ dispose() {
70641
+ this.extensionQuestions.endTurn();
70642
+ this.proc.dispose();
70643
+ }
70430
70644
  setStartupInfo(text2) {
70431
70645
  this.startupInfo = text2;
70432
70646
  this.startupInfoSent = false;
@@ -70469,6 +70683,31 @@ var PiAcpSession = class {
70469
70683
  });
70470
70684
  return turnPromise;
70471
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
+ }
70472
70711
  async steer(message, images, signal) {
70473
70712
  const target = this.pendingTurn;
70474
70713
  if (target === null) {
@@ -70487,6 +70726,7 @@ var PiAcpSession = class {
70487
70726
  }
70488
70727
  async cancel() {
70489
70728
  this.cancelRequested = true;
70729
+ this.extensionQuestions.endTurn();
70490
70730
  if (this.turnQueue.length) {
70491
70731
  const queued = this.turnQueue.splice(0, this.turnQueue.length);
70492
70732
  for (const t of queued) {
@@ -70501,7 +70741,13 @@ var PiAcpSession = class {
70501
70741
  _meta: { piAcp: { queueDepth: 0, running: Boolean(this.pendingTurn) } }
70502
70742
  });
70503
70743
  }
70504
- 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
+ }
70505
70751
  }
70506
70752
  wasCancelRequested() {
70507
70753
  return this.cancelRequested;
@@ -70534,15 +70780,19 @@ var PiAcpSession = class {
70534
70780
  await this.flushEmits();
70535
70781
  }
70536
70782
  async settleTurn() {
70783
+ const questionSignal = this.pendingTurn?.questionSignal;
70784
+ this.extensionQuestions.endTurn();
70537
70785
  await this.publishContextUsage();
70538
70786
  const retryFailure = this.turnRetryFailure;
70539
70787
  this.turnRetryFailure = null;
70540
- 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) {
70541
70791
  this.pendingTurn?.reject(
70542
70792
  // The message rides in `data.details` so clients that unwrap the
70543
70793
  // JSON-RPC internal-error envelope (like Mobius) surface the real
70544
70794
  // model error; `code` keeps the failure machine-readable.
70545
- RequestError5.internalError({
70795
+ RequestError6.internalError({
70546
70796
  code: "pi_retries_exhausted",
70547
70797
  details: `Pi retries exhausted: ${retryFailure}`
70548
70798
  })
@@ -70596,14 +70846,16 @@ var PiAcpSession = class {
70596
70846
  this.bashToolCallIds.delete(toolCallId);
70597
70847
  }
70598
70848
  startTurn(t) {
70849
+ const questionSignal = this.extensionQuestions.startTurn();
70599
70850
  this.cancelRequested = false;
70600
70851
  this.turnRetryFailure = null;
70601
- this.pendingTurn = { resolve: t.resolve, reject: t.reject };
70852
+ this.pendingTurn = { questionSignal, resolve: t.resolve, reject: t.reject };
70602
70853
  this.emit({
70603
70854
  sessionUpdate: "session_info_update",
70604
70855
  _meta: { piAcp: { queueDepth: this.turnQueue.length, running: true } }
70605
70856
  });
70606
70857
  this.proc.prompt(t.message, t.images).catch((err) => {
70858
+ this.extensionQuestions.endTurn();
70607
70859
  void this.flushEmits().finally(() => {
70608
70860
  const authErr = maybeAuthRequiredError(err);
70609
70861
  if (authErr) {
@@ -70842,14 +71094,15 @@ var PiAcpSession = class {
70842
71094
  break;
70843
71095
  }
70844
71096
  case "extension_ui_request": {
70845
- void this.handleExtensionUiRequest(ev2).catch(() => {
70846
- const id = stringProp(ev2, "id");
70847
- if (!id) {
70848
- return;
70849
- }
70850
- 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" } } }
70851
71102
  });
70852
- });
71103
+ break;
71104
+ }
71105
+ void this.extensionQuestions.handle(ev2);
70853
71106
  break;
70854
71107
  }
70855
71108
  case "auto_retry_start": {
@@ -70902,119 +71155,11 @@ var PiAcpSession = class {
70902
71155
  break;
70903
71156
  }
70904
71157
  }
70905
- async handleExtensionUiRequest(ev2) {
70906
- const id = stringProp(ev2, "id");
70907
- const method = stringProp(ev2, "method");
70908
- if (!id) {
70909
- return;
70910
- }
70911
- if (method === "select") {
70912
- await this.handleExtensionSelect(ev2, id);
70913
- return;
70914
- }
70915
- if (method === "confirm") {
70916
- await this.handleExtensionConfirm(ev2, id);
70917
- return;
70918
- }
70919
- if (method === "input" || method === "editor") {
70920
- this.emit({
70921
- sessionUpdate: "agent_message_chunk",
70922
- content: {
70923
- type: "text",
70924
- text: `Pi ${method} UI request is not supported in ACP yet; cancelling it.`
70925
- }
70926
- });
70927
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70928
- return;
70929
- }
70930
- if (method === "notify") {
70931
- this.emit({
70932
- sessionUpdate: "agent_message_chunk",
70933
- content: { type: "text", text: stringProp(ev2, "message") ?? "Pi notification" },
70934
- _meta: { piAcp: { notify: { level: stringProp(ev2, "notifyType") ?? "info" } } }
70935
- });
70936
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70937
- return;
70938
- }
70939
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70940
- }
70941
- async handleExtensionSelect(ev2, id) {
70942
- const rawOptions = ev2.options;
70943
- const options = Array.isArray(rawOptions) ? rawOptions.map((option) => String(option)) : [];
70944
- if (!options.length) {
70945
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70946
- return;
70947
- }
70948
- const permissionOptions = options.map((name2, index3) => ({
70949
- optionId: `${CHOICE_OPTION_PREFIX}${index3}`,
70950
- name: name2,
70951
- kind: "allow_once"
70952
- }));
70953
- const selected = await this.requestExtensionPermission(id, ev2, permissionOptions);
70954
- if (selected === null) {
70955
- return;
70956
- }
70957
- const selectedOptionId = selected.outcome.outcome === "selected" ? selected.outcome.optionId : null;
70958
- const index2 = selectedOptionId === null ? null : optionIndex(selectedOptionId);
70959
- const value = index2 === null ? null : options.at(index2) ?? null;
70960
- await this.proc.sendExtensionUiResponse(value === null ? { id, cancelled: true } : { id, value });
70961
- }
70962
- async handleExtensionConfirm(ev2, id) {
70963
- const selected = await this.requestExtensionPermission(id, ev2, CONFIRM_PERMISSION_OPTIONS);
70964
- if (selected === null) {
70965
- return;
70966
- }
70967
- if (selected.outcome.outcome === "cancelled") {
70968
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70969
- return;
70970
- }
70971
- await this.proc.sendExtensionUiResponse({ id, confirmed: selected.outcome.optionId === "yes" });
70972
- }
70973
- async requestExtensionPermission(id, ev2, options) {
70974
- try {
70975
- return await this.conn.requestPermission({
70976
- sessionId: this.sessionId,
70977
- toolCall: extensionUiToolCall(id, ev2),
70978
- options
70979
- });
70980
- } catch {
70981
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70982
- return null;
70983
- }
70984
- }
70985
71158
  };
70986
- function extensionUiToolCall(id, ev2) {
70987
- const method = stringProp(ev2, "method") ?? "ui";
70988
- const title2 = stringProp(ev2, "title") ?? `Pi ${method}`;
70989
- const rawInput = { method };
70990
- for (const key of EXTENSION_UI_RAW_INPUT_KEYS) {
70991
- if (Object.hasOwn(ev2, key)) {
70992
- rawInput[key] = ev2[key];
70993
- }
70994
- }
70995
- return {
70996
- toolCallId: `pi-ui-${id}`,
70997
- title: title2,
70998
- kind: "other",
70999
- status: "pending",
71000
- rawInput
71001
- };
71002
- }
71003
71159
  function stringProp(source, key) {
71004
71160
  const value = source[key];
71005
71161
  return typeof value === "string" ? value : null;
71006
71162
  }
71007
- function optionIndex(optionId) {
71008
- if (!optionId.startsWith(CHOICE_OPTION_PREFIX)) {
71009
- return null;
71010
- }
71011
- const rawIndex = optionId.slice(CHOICE_OPTION_PREFIX.length);
71012
- if (!rawIndex) {
71013
- return null;
71014
- }
71015
- const index2 = Number(rawIndex);
71016
- return Number.isSafeInteger(index2) && index2 >= 0 && String(index2) === rawIndex ? index2 : null;
71017
- }
71018
71163
  function toToolKind(toolName) {
71019
71164
  switch (toolName) {
71020
71165
  case "read":
@@ -71241,7 +71386,7 @@ var PiAcpAgent = class {
71241
71386
  const restorePromise = (async () => {
71242
71387
  const stored = this.findStoredSession(sessionId);
71243
71388
  if (!stored) {
71244
- throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
71389
+ throw RequestError7.invalidParams(`Unknown sessionId: ${sessionId}`);
71245
71390
  }
71246
71391
  const cwd = opts?.cwd ?? stored.cwd;
71247
71392
  let proc;
@@ -71254,7 +71399,7 @@ var PiAcpAgent = class {
71254
71399
  });
71255
71400
  } catch (e) {
71256
71401
  if (e?.name === "PiRpcSpawnError") {
71257
- throw RequestError6.internalError({ code: e?.code }, String(e?.message ?? e));
71402
+ throw RequestError7.internalError({ code: e?.code }, String(e?.message ?? e));
71258
71403
  }
71259
71404
  throw e;
71260
71405
  }
@@ -71314,7 +71459,7 @@ var PiAcpAgent = class {
71314
71459
  }
71315
71460
  async newSession(params) {
71316
71461
  if (!isAbsolute11(params.cwd)) {
71317
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71462
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71318
71463
  }
71319
71464
  this.lastSessionCwd = params.cwd;
71320
71465
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -71353,16 +71498,16 @@ var PiAcpAgent = class {
71353
71498
  }
71354
71499
  if (availableModelsErr) {
71355
71500
  this.cleanupFailedNewSession(session.sessionId, state);
71356
- throw RequestError6.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
71501
+ throw RequestError7.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
71357
71502
  }
71358
71503
  const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
71359
71504
  if (rawModelsCount === 0) {
71360
71505
  this.cleanupFailedNewSession(session.sessionId, state);
71361
- 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.");
71362
71507
  }
71363
71508
  if (stateErr && maybeAuthRequiredError(stateErr)) {
71364
71509
  this.cleanupFailedNewSession(session.sessionId, state);
71365
- 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.");
71366
71511
  }
71367
71512
  let configuration;
71368
71513
  try {
@@ -71372,7 +71517,7 @@ var PiAcpAgent = class {
71372
71517
  configuration = await getSessionConfiguration(session.proc, { state, availableModels });
71373
71518
  } catch (err) {
71374
71519
  this.cleanupFailedNewSession(session.sessionId, state);
71375
- throw maybeAuthRequiredError(err) ?? RequestError6.internalError({}, String(err?.message ?? err));
71520
+ throw maybeAuthRequiredError(err) ?? RequestError7.internalError({}, String(err?.message ?? err));
71376
71521
  }
71377
71522
  const { configOptions, models, modes } = configuration;
71378
71523
  const quietStartup = getQuietStartup(params.cwd, this.environment);
@@ -71457,9 +71602,7 @@ var PiAcpAgent = class {
71457
71602
  }
71458
71603
  });
71459
71604
  try {
71460
- const res = await session.proc.compact(customInstructions, {
71461
- timeoutMs: COMPACT_TIMEOUT_MILLISECONDS2
71462
- });
71605
+ const res = await session.compact(customInstructions, COMPACT_TIMEOUT_MILLISECONDS2);
71463
71606
  const r = res && typeof res === "object" ? res : null;
71464
71607
  const summary = typeof r?.summary === "string" ? r.summary : null;
71465
71608
  if (summary !== null && summary.length > 0) {
@@ -71872,7 +72015,7 @@ ${JSON.stringify(stats, null, 2)}`;
71872
72015
  signal.throwIfAborted();
71873
72016
  const parsed = steerRequestSchema.safeParse(params);
71874
72017
  if (!parsed.success) {
71875
- throw RequestError6.invalidParams("Invalid steering request");
72018
+ throw RequestError7.invalidParams("Invalid steering request");
71876
72019
  }
71877
72020
  const session = this.sessions.maybeGet(parsed.data.sessionId);
71878
72021
  if (session === void 0) {
@@ -71913,11 +72056,11 @@ ${JSON.stringify(stats, null, 2)}`;
71913
72056
  }
71914
72057
  async forkSession(params) {
71915
72058
  if (!isAbsolute11(params.cwd)) {
71916
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
72059
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71917
72060
  }
71918
72061
  const source = this.findStoredSession(params.sessionId);
71919
72062
  if (!source) {
71920
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
72063
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
71921
72064
  }
71922
72065
  this.lastSessionCwd = params.cwd;
71923
72066
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -71979,13 +72122,13 @@ ${JSON.stringify(stats, null, 2)}`;
71979
72122
  }
71980
72123
  async loadSession(params) {
71981
72124
  if (!isAbsolute11(params.cwd)) {
71982
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
72125
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71983
72126
  }
71984
72127
  this.sessions.close(params.sessionId);
71985
72128
  this.lastSessionCwd = params.cwd;
71986
72129
  const stored = this.findStoredSession(params.sessionId);
71987
72130
  if (!stored) {
71988
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
72131
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
71989
72132
  }
71990
72133
  const enableSkillCommands = getEnableSkillCommands(params.cwd, this.environment);
71991
72134
  const session = await this.restoreSession(params.sessionId, {
@@ -72160,7 +72303,7 @@ ${JSON.stringify(stats, null, 2)}`;
72160
72303
  const session = await this.restoreSession(params.sessionId);
72161
72304
  const mode = params.modeId;
72162
72305
  if (typeof mode !== "string" || mode.length === 0) {
72163
- throw RequestError6.invalidParams("Expected nonempty string modeId");
72306
+ throw RequestError7.invalidParams("Expected nonempty string modeId");
72164
72307
  }
72165
72308
  await session.proc.setThinkingLevel(mode);
72166
72309
  await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
@@ -72171,18 +72314,18 @@ ${JSON.stringify(stats, null, 2)}`;
72171
72314
  const configId = String(params.configId);
72172
72315
  let modelChanged = false;
72173
72316
  if (typeof params.value !== "string") {
72174
- throw RequestError6.invalidParams(`Expected string value for config option: ${configId}`);
72317
+ throw RequestError7.invalidParams(`Expected string value for config option: ${configId}`);
72175
72318
  }
72176
72319
  if (configId === MODEL_CONFIG_ID2) {
72177
72320
  await setSessionModel(session.proc, params.value);
72178
72321
  modelChanged = true;
72179
72322
  } else if (configId === THOUGHT_LEVEL_CONFIG_ID) {
72180
72323
  if (params.value.length === 0) {
72181
- throw RequestError6.invalidParams("Expected nonempty thinking level");
72324
+ throw RequestError7.invalidParams("Expected nonempty thinking level");
72182
72325
  }
72183
72326
  await session.proc.setThinkingLevel(params.value);
72184
72327
  } else {
72185
- throw RequestError6.invalidParams(`Unknown config option: ${configId}`);
72328
+ throw RequestError7.invalidParams(`Unknown config option: ${configId}`);
72186
72329
  }
72187
72330
  const configOptions = await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
72188
72331
  if (modelChanged) {
@@ -72338,7 +72481,7 @@ async function setSessionModel(proc, requestedModelId) {
72338
72481
  }
72339
72482
  }
72340
72483
  if (!provider || !modelId) {
72341
- throw RequestError6.invalidParams(`Unknown modelId: ${requestedModelId}`);
72484
+ throw RequestError7.invalidParams(`Unknown modelId: ${requestedModelId}`);
72342
72485
  }
72343
72486
  await proc.setModel(provider, modelId);
72344
72487
  }
@@ -72520,6 +72663,7 @@ function buildStartupInfo(opts) {
72520
72663
  import { methods as methods4 } from "@agentclientprotocol/sdk";
72521
72664
  function createPiAcpClient(connection, onSessionUpdate) {
72522
72665
  return {
72666
+ requestElicitation: async (request, signal) => await connection.client.request(methods4.client.elicitation.create, request, { cancellationSignal: signal }),
72523
72667
  requestPermission: async (request) => await connection.client.request(methods4.client.session.requestPermission, request),
72524
72668
  sessionUpdate: onSessionUpdate
72525
72669
  };
@@ -72843,7 +72987,7 @@ import {
72843
72987
  methods as methods8,
72844
72988
  ndJsonStream as ndJsonStream2,
72845
72989
  PROTOCOL_VERSION as PROTOCOL_VERSION2,
72846
- RequestError as RequestError7
72990
+ RequestError as RequestError8
72847
72991
  } from "@agentclientprotocol/sdk";
72848
72992
 
72849
72993
  // src/acp/connection/create-client.ts
@@ -73017,7 +73161,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
73017
73161
  if (error62 instanceof AgentProbeError) {
73018
73162
  throw error62;
73019
73163
  }
73020
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73164
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73021
73165
  throw new AgentProbeError("authentication_required", "Agent authentication is required", {
73022
73166
  cause: error62
73023
73167
  });
@@ -73093,7 +73237,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
73093
73237
  if (error62 instanceof AgentProbeError) {
73094
73238
  throw error62;
73095
73239
  }
73096
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73240
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73097
73241
  throw new AgentProbeError("authentication_required", "Agent authentication is required", { cause: error62 });
73098
73242
  }
73099
73243
  throw new AgentProbeError("acp_incompatible", "ACP adapter initialization failed", { cause: error62 });
@@ -81949,7 +82093,7 @@ function buildPromptContent(options, capabilities) {
81949
82093
 
81950
82094
  // src/acp/connection/agent-connection.ts
81951
82095
  import { spawn as spawn7 } from "node:child_process";
81952
- import { ndJsonStream as ndJsonStream3, RequestError as RequestError8 } from "@agentclientprotocol/sdk";
82096
+ import { ndJsonStream as ndJsonStream3, RequestError as RequestError9 } from "@agentclientprotocol/sdk";
81953
82097
  function requestErrorMessage(error62) {
81954
82098
  const fallback = truncate(error62.message, 500);
81955
82099
  if (error62.message !== "Internal error" || !isRecord(error62.data)) {
@@ -82154,14 +82298,14 @@ var AgentConnection = class {
82154
82298
  ]);
82155
82299
  } catch (error62) {
82156
82300
  signal.throwIfAborted();
82157
- if (error62 instanceof RequestError8 && error62.code === -32e3) {
82301
+ if (error62 instanceof RequestError9 && error62.code === -32e3) {
82158
82302
  throw new AcpExecutionError(
82159
82303
  "Agent authentication is required",
82160
82304
  { cause: error62 },
82161
82305
  "authentication_required"
82162
82306
  );
82163
82307
  }
82164
- if (error62 instanceof RequestError8) {
82308
+ if (error62 instanceof RequestError9) {
82165
82309
  throw new AcpExecutionError(requestErrorMessage(error62), { cause: error62 }, "acp_agent_error");
82166
82310
  }
82167
82311
  throw error62;
@@ -82615,6 +82759,7 @@ function normalizeElicitationFields(requestedSchema) {
82615
82759
  const options = normalizeElicitationOptions(property);
82616
82760
  const normalizedType = type === "string" && options.length > 0 ? "select" : type;
82617
82761
  const defaultValue = property["default"];
82762
+ const metadata = property["_meta"];
82618
82763
  normalized.push({
82619
82764
  defaultBooleanValue: typeof defaultValue === "boolean" ? defaultValue : false,
82620
82765
  defaultNumberValue: typeof defaultValue === "number" && Number.isFinite(defaultValue) ? defaultValue : 0,
@@ -82626,6 +82771,7 @@ function normalizeElicitationFields(requestedSchema) {
82626
82771
  maximumLength: numericString(property["maxLength"] ?? property["maxItems"]),
82627
82772
  minimum: numericString(property["minimum"]),
82628
82773
  minimumLength: numericString(property["minLength"] ?? property["minItems"]),
82774
+ ...normalizedType === "string" && isRecord(metadata) && metadata["io.mobius/multiline"] === true ? { multiline: true } : {},
82629
82775
  name: truncate(name2, 128),
82630
82776
  options,
82631
82777
  pattern: truncate(readString(property["pattern"]), 500),