@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/cli.js CHANGED
@@ -5398,7 +5398,7 @@ var program = new Command();
5398
5398
  // package.json
5399
5399
  var package_default = {
5400
5400
  name: "@moxt-ai/mobius",
5401
- version: "0.0.290",
5401
+ version: "0.0.291",
5402
5402
  description: "Mobius CLI for pairing and running local AI agent services.",
5403
5403
  type: "module",
5404
5404
  bin: {
@@ -72214,6 +72214,7 @@ var elicitationFieldSchema = external_exports.object({
72214
72214
  maximumLength: external_exports.string().max(128),
72215
72215
  minimum: external_exports.string().max(128),
72216
72216
  minimumLength: external_exports.string().max(128),
72217
+ multiline: external_exports.boolean().optional(),
72217
72218
  name: external_exports.string().min(1).max(128),
72218
72219
  options: external_exports.array(elicitationOptionSchema).max(200).readonly(),
72219
72220
  pattern: external_exports.string().max(500),
@@ -74237,7 +74238,7 @@ import { spawnSync } from "node:child_process";
74237
74238
  import { randomUUID as randomUUID7 } from "node:crypto";
74238
74239
  import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
74239
74240
  import { basename as basename4, dirname as dirname7, isAbsolute as isAbsolute11, join as join17 } from "node:path";
74240
- import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
74241
+ import { RequestError as RequestError7 } from "@agentclientprotocol/sdk";
74241
74242
 
74242
74243
  // src/acp/harness/pi/upstream/dist/pi-rpc/process.js
74243
74244
  import { spawn as spawn4 } from "node:child_process";
@@ -74270,6 +74271,13 @@ var PiRpcSpawnError = class extends Error {
74270
74271
  this.cause = opts?.cause;
74271
74272
  }
74272
74273
  };
74274
+ var PiRpcProcessError = class extends Error {
74275
+ code = "pi_process_exited";
74276
+ constructor(cause) {
74277
+ super("Pi process stopped unexpectedly.", { cause });
74278
+ this.name = "PiRpcProcessError";
74279
+ }
74280
+ };
74273
74281
  var ESC = String.fromCharCode(27);
74274
74282
  var CSI = String.fromCharCode(155);
74275
74283
  var ANSI_ESCAPE_REGEX = new RegExp(`[${ESC}${CSI}][[\\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`, "g");
@@ -74279,6 +74287,10 @@ function stripAnsi(s) {
74279
74287
  var SESSION_STATS_TIMEOUT_MS = 1e3;
74280
74288
  var PiRpcProcess = class _PiRpcProcess {
74281
74289
  child;
74290
+ lifetime = new AbortController();
74291
+ get signal() {
74292
+ return this.lifetime.signal;
74293
+ }
74282
74294
  pending = /* @__PURE__ */ new Map();
74283
74295
  eventHandlers = [];
74284
74296
  preludeLines = [];
@@ -74313,13 +74325,16 @@ var PiRpcProcess = class _PiRpcProcess {
74313
74325
  }
74314
74326
  });
74315
74327
  child.on("exit", (code, signal) => {
74316
- const err = new Error(`pi process exited (code=${code}, signal=${signal})`);
74328
+ const err = new PiRpcProcessError({ code, signal });
74329
+ this.lifetime.abort(err);
74317
74330
  for (const [, p] of this.pending) {
74318
74331
  p.reject(err);
74319
74332
  }
74320
74333
  this.pending.clear();
74321
74334
  });
74322
- child.on("error", (err) => {
74335
+ child.on("error", (cause) => {
74336
+ const err = new PiRpcProcessError(cause);
74337
+ this.lifetime.abort(err);
74323
74338
  for (const [, p] of this.pending) {
74324
74339
  p.reject(err);
74325
74340
  }
@@ -74401,6 +74416,7 @@ var PiRpcProcess = class _PiRpcProcess {
74401
74416
  };
74402
74417
  }
74403
74418
  dispose(signal = "SIGTERM") {
74419
+ this.lifetime.abort();
74404
74420
  if (this.child.killed) {
74405
74421
  return;
74406
74422
  }
@@ -74409,6 +74425,10 @@ var PiRpcProcess = class _PiRpcProcess {
74409
74425
  } catch {
74410
74426
  }
74411
74427
  }
74428
+ terminateAfterFailure(cause) {
74429
+ this.lifetime.abort(new PiRpcProcessError(cause));
74430
+ this.dispose();
74431
+ }
74412
74432
  /**
74413
74433
  * Human-readable stdout lines emitted before RPC NDJSON begins (e.g. Context/Skills/Extensions info).
74414
74434
  * Themes are typically noisy/less useful for ACP, so callers can filter as needed.
@@ -74429,8 +74449,8 @@ var PiRpcProcess = class _PiRpcProcess {
74429
74449
  throw new Error(`pi steer failed: ${res.error ?? JSON.stringify(res.data)}`);
74430
74450
  }
74431
74451
  }
74432
- async abort() {
74433
- const res = await this.request({ type: "abort" });
74452
+ async abort(opts) {
74453
+ const res = await this.request({ type: "abort" }, opts);
74434
74454
  if (!res.success) {
74435
74455
  throw new Error(`pi abort failed: ${res.error ?? JSON.stringify(res.data)}`);
74436
74456
  }
@@ -75127,7 +75147,176 @@ function getQuietStartup(cwd, environment = process.env) {
75127
75147
  import { randomUUID as randomUUID6 } from "node:crypto";
75128
75148
  import { readFileSync as readFileSync7 } from "node:fs";
75129
75149
  import { isAbsolute as isAbsolute10, resolve as resolvePath } from "node:path";
75150
+ import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
75151
+
75152
+ // src/acp/harness/pi/upstream/dist/acp/extension-questions.js
75130
75153
  import { RequestError as RequestError5 } from "@agentclientprotocol/sdk";
75154
+ var questionBase = {
75155
+ id: external_exports.string().min(1).max(255),
75156
+ title: external_exports.string().max(255),
75157
+ timeout: external_exports.number().int().nonnegative().max(2147483647).optional()
75158
+ };
75159
+ var questionSchema = external_exports.discriminatedUnion("method", [
75160
+ external_exports.object({
75161
+ ...questionBase,
75162
+ method: external_exports.literal("select"),
75163
+ options: external_exports.array(external_exports.string().min(1).max(255)).min(1).max(200).readonly()
75164
+ }),
75165
+ external_exports.object({ ...questionBase, method: external_exports.literal("confirm"), message: external_exports.string().max(2e3) }),
75166
+ external_exports.object({ ...questionBase, method: external_exports.literal("input"), placeholder: external_exports.string().max(500).optional() }),
75167
+ external_exports.object({ ...questionBase, method: external_exports.literal("editor"), prefill: external_exports.string().max(1e4).optional() })
75168
+ ]).readonly();
75169
+ var QUESTION_TIMEOUT_MS = 36e5;
75170
+ var PiQuestionError = class extends RequestError5 {
75171
+ constructor(kind, cause) {
75172
+ const messages = {
75173
+ request_failed: "Pi could not request an answer.",
75174
+ invalid_response: "Pi received an invalid answer.",
75175
+ response_failed: "Pi could not receive the answer."
75176
+ };
75177
+ super(-32603, "Internal error", { code: `pi_question_${kind}`, details: messages[kind] });
75178
+ this.cause = cause;
75179
+ }
75180
+ };
75181
+ function questionField(question) {
75182
+ const title2 = question.title || "Answer";
75183
+ switch (question.method) {
75184
+ case "select":
75185
+ return { type: "string", title: title2, enum: [...question.options] };
75186
+ case "confirm":
75187
+ return {
75188
+ type: "string",
75189
+ title: title2,
75190
+ oneOf: [
75191
+ { const: "yes", title: "Yes" },
75192
+ { const: "no", title: "No" }
75193
+ ]
75194
+ };
75195
+ case "input":
75196
+ return { type: "string", title: title2, description: question.placeholder, maxLength: 1e4 };
75197
+ case "editor":
75198
+ return {
75199
+ type: "string",
75200
+ title: title2,
75201
+ default: question.prefill,
75202
+ maxLength: 1e4,
75203
+ _meta: { "io.mobius/multiline": true }
75204
+ };
75205
+ }
75206
+ }
75207
+ var PiExtensionQuestions = class {
75208
+ sessionId;
75209
+ conn;
75210
+ proc;
75211
+ notify;
75212
+ turn = null;
75213
+ constructor(sessionId, conn, proc, notify) {
75214
+ this.sessionId = sessionId;
75215
+ this.conn = conn;
75216
+ this.proc = proc;
75217
+ this.notify = notify;
75218
+ }
75219
+ startTurn() {
75220
+ this.endTurn();
75221
+ this.turn = { controller: new AbortController(), requestIds: /* @__PURE__ */ new Set() };
75222
+ return this.turn.controller.signal;
75223
+ }
75224
+ endTurn(reason) {
75225
+ this.turn?.controller.abort(reason);
75226
+ this.turn = null;
75227
+ }
75228
+ async handle(event) {
75229
+ if (event.method !== "select" && event.method !== "confirm" && event.method !== "input" && event.method !== "editor") {
75230
+ return;
75231
+ }
75232
+ if (typeof event.id !== "string" || event.id.length === 0 || event.id.length > 255) {
75233
+ return;
75234
+ }
75235
+ const turn = this.turn;
75236
+ if (turn?.requestIds.has(event.id)) {
75237
+ return;
75238
+ }
75239
+ turn?.requestIds.add(event.id);
75240
+ try {
75241
+ await this.answer(event, turn);
75242
+ } catch (cause) {
75243
+ if (!this.proc.signal.aborted) {
75244
+ turn?.controller.abort(new PiQuestionError("response_failed", cause));
75245
+ this.proc.terminateAfterFailure(cause);
75246
+ }
75247
+ }
75248
+ }
75249
+ async answer(event, turn) {
75250
+ const id = typeof event.id === "string" ? event.id : "";
75251
+ const parsed = questionSchema.safeParse(event);
75252
+ if (!parsed.success) {
75253
+ await this.notify("Pi requested a question with unsupported or oversized fields. The question was cancelled without changing its content.");
75254
+ await this.proc.sendExtensionUiResponse({ id, cancelled: true });
75255
+ return;
75256
+ }
75257
+ const question = parsed.data;
75258
+ if (turn === null || turn.controller.signal.aborted) {
75259
+ await this.proc.sendExtensionUiResponse({ id: question.id, cancelled: true });
75260
+ return;
75261
+ }
75262
+ const deadline = new AbortController();
75263
+ const timeout = setTimeout(() => deadline.abort(), question.timeout || QUESTION_TIMEOUT_MS);
75264
+ const signal = AbortSignal.any([turn.controller.signal, deadline.signal]);
75265
+ let response = { id: question.id, cancelled: true };
75266
+ try {
75267
+ const answer = await this.conn.requestElicitation({
75268
+ sessionId: this.sessionId,
75269
+ mode: "form",
75270
+ message: (question.method === "confirm" ? question.message : "") || question.title || "Pi question",
75271
+ requestedSchema: {
75272
+ type: "object",
75273
+ properties: { value: questionField(question) },
75274
+ required: ["value"]
75275
+ }
75276
+ }, signal);
75277
+ const content = answer.action === "accept" ? answer.content : void 0;
75278
+ const value = typeof content === "object" && content !== null ? Reflect.get(content, "value") : void 0;
75279
+ if (!signal.aborted && answer.action === "accept") {
75280
+ if (typeof value !== "string") {
75281
+ throw new PiQuestionError("invalid_response");
75282
+ }
75283
+ switch (question.method) {
75284
+ case "select":
75285
+ if (!question.options.includes(value)) {
75286
+ throw new PiQuestionError("invalid_response");
75287
+ }
75288
+ response = { id: question.id, value };
75289
+ break;
75290
+ case "confirm":
75291
+ if (value !== "yes" && value !== "no") {
75292
+ throw new PiQuestionError("invalid_response");
75293
+ }
75294
+ response = { id: question.id, confirmed: value === "yes" };
75295
+ break;
75296
+ case "input":
75297
+ case "editor":
75298
+ if (value.length > 1e4) {
75299
+ throw new PiQuestionError("invalid_response");
75300
+ }
75301
+ response = { id: question.id, value };
75302
+ break;
75303
+ }
75304
+ } else if (!signal.aborted && answer.action !== "cancel" && answer.action !== "decline") {
75305
+ throw new PiQuestionError("invalid_response");
75306
+ }
75307
+ } catch (cause) {
75308
+ if (!signal.aborted && !(cause instanceof RequestError5 && cause.code === -32800)) {
75309
+ turn.controller.abort(cause instanceof PiQuestionError ? cause : new PiQuestionError("request_failed", cause));
75310
+ }
75311
+ } finally {
75312
+ clearTimeout(timeout);
75313
+ }
75314
+ if (deadline.signal.aborted && signal.reason === deadline.signal.reason) {
75315
+ await this.notify("Pi question timed out; no answer was submitted.");
75316
+ }
75317
+ await this.proc.sendExtensionUiResponse(response);
75318
+ }
75319
+ };
75131
75320
 
75132
75321
  // src/acp/harness/pi/upstream/dist/acp/session-store.js
75133
75322
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "node:fs";
@@ -75396,12 +75585,6 @@ ${stderr}`);
75396
75585
  }
75397
75586
 
75398
75587
  // src/acp/harness/pi/upstream/dist/acp/session.js
75399
- var CONFIRM_PERMISSION_OPTIONS = [
75400
- { optionId: "yes", name: "Yes", kind: "allow_once" },
75401
- { optionId: "no", name: "No", kind: "reject_once" }
75402
- ];
75403
- var EXTENSION_UI_RAW_INPUT_KEYS = ["title", "message", "options", "placeholder", "prefill"];
75404
- var CHOICE_OPTION_PREFIX = "choice-";
75405
75588
  function toUsageUpdate(stats) {
75406
75589
  const used = stats?.contextUsage?.tokens;
75407
75590
  const size = stats?.contextUsage?.contextWindow;
@@ -75522,7 +75705,7 @@ var SessionManager = class {
75522
75705
  return;
75523
75706
  }
75524
75707
  try {
75525
- s.proc.dispose?.();
75708
+ s.dispose();
75526
75709
  } catch {
75527
75710
  }
75528
75711
  this.sessions.delete(sessionId);
@@ -75547,7 +75730,7 @@ var SessionManager = class {
75547
75730
  });
75548
75731
  } catch (e) {
75549
75732
  if (e instanceof PiRpcSpawnError) {
75550
- throw RequestError5.internalError({ code: e.code }, e.message);
75733
+ throw RequestError6.internalError({ code: e.code }, e.message);
75551
75734
  }
75552
75735
  throw e;
75553
75736
  }
@@ -75576,7 +75759,7 @@ var SessionManager = class {
75576
75759
  get(sessionId) {
75577
75760
  const s = this.sessions.get(sessionId);
75578
75761
  if (!s) {
75579
- throw RequestError5.invalidParams(`Unknown sessionId: ${sessionId}`);
75762
+ throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
75580
75763
  }
75581
75764
  return s;
75582
75765
  }
@@ -75609,6 +75792,7 @@ var PiAcpSession = class {
75609
75792
  startupInfoSent = false;
75610
75793
  proc;
75611
75794
  conn;
75795
+ extensionQuestions;
75612
75796
  fileCommands;
75613
75797
  // Used to map abort semantics to ACP stopReason.
75614
75798
  // Applies to the currently running turn.
@@ -75663,8 +75847,38 @@ var PiAcpSession = class {
75663
75847
  this.proc = opts.proc;
75664
75848
  this.conn = opts.conn;
75665
75849
  this.fileCommands = opts.fileCommands ?? [];
75850
+ this.extensionQuestions = new PiExtensionQuestions(this.sessionId, this.conn, this.proc, async (text2) => {
75851
+ this.emit({ sessionUpdate: "agent_message_chunk", content: { type: "text", text: text2 } });
75852
+ await this.flushEmits();
75853
+ });
75854
+ this.proc.signal.addEventListener("abort", () => {
75855
+ const cause = this.proc.signal.reason;
75856
+ if (cause instanceof PiRpcProcessError) {
75857
+ const pending = this.pendingTurn;
75858
+ const reason = pending?.questionSignal.reason;
75859
+ const error62 = reason instanceof RequestError6 ? reason : RequestError6.internalError({ code: cause.code, details: cause.message });
75860
+ if (!(reason instanceof RequestError6)) {
75861
+ error62.cause = cause;
75862
+ }
75863
+ this.extensionQuestions.endTurn(error62);
75864
+ this.pendingTurn = null;
75865
+ void this.flushEmits().then(() => {
75866
+ if (this.cancelRequested) {
75867
+ pending?.resolve("cancelled");
75868
+ } else {
75869
+ pending?.reject(error62);
75870
+ }
75871
+ });
75872
+ } else {
75873
+ this.extensionQuestions.endTurn();
75874
+ }
75875
+ }, { once: true });
75666
75876
  this.proc.onEvent((ev2) => this.handlePiEvent(ev2));
75667
75877
  }
75878
+ dispose() {
75879
+ this.extensionQuestions.endTurn();
75880
+ this.proc.dispose();
75881
+ }
75668
75882
  setStartupInfo(text2) {
75669
75883
  this.startupInfo = text2;
75670
75884
  this.startupInfoSent = false;
@@ -75707,6 +75921,31 @@ var PiAcpSession = class {
75707
75921
  });
75708
75922
  return turnPromise;
75709
75923
  }
75924
+ async compact(customInstructions, timeoutMs) {
75925
+ const signal = this.extensionQuestions.startTurn();
75926
+ try {
75927
+ const result = await this.proc.compact(customInstructions, { signal, timeoutMs });
75928
+ if (signal.aborted && signal.reason instanceof RequestError6) {
75929
+ throw signal.reason;
75930
+ }
75931
+ return result;
75932
+ } catch (cause) {
75933
+ if (signal.aborted && signal.reason instanceof RequestError6) {
75934
+ try {
75935
+ await this.proc.abort({ signal: this.proc.signal, timeoutMs });
75936
+ } catch (abortCause) {
75937
+ if (!this.proc.signal.aborted) {
75938
+ this.proc.terminateAfterFailure(abortCause);
75939
+ }
75940
+ }
75941
+ throw signal.reason;
75942
+ }
75943
+ throw cause;
75944
+ } finally {
75945
+ this.extensionQuestions.endTurn();
75946
+ await this.flushEmits();
75947
+ }
75948
+ }
75710
75949
  async steer(message, images, signal) {
75711
75950
  const target = this.pendingTurn;
75712
75951
  if (target === null) {
@@ -75725,6 +75964,7 @@ var PiAcpSession = class {
75725
75964
  }
75726
75965
  async cancel() {
75727
75966
  this.cancelRequested = true;
75967
+ this.extensionQuestions.endTurn();
75728
75968
  if (this.turnQueue.length) {
75729
75969
  const queued = this.turnQueue.splice(0, this.turnQueue.length);
75730
75970
  for (const t of queued) {
@@ -75739,7 +75979,13 @@ var PiAcpSession = class {
75739
75979
  _meta: { piAcp: { queueDepth: 0, running: Boolean(this.pendingTurn) } }
75740
75980
  });
75741
75981
  }
75742
- await this.proc.abort();
75982
+ try {
75983
+ await this.proc.abort();
75984
+ } catch (cause) {
75985
+ if (!this.proc.signal.aborted) {
75986
+ throw cause;
75987
+ }
75988
+ }
75743
75989
  }
75744
75990
  wasCancelRequested() {
75745
75991
  return this.cancelRequested;
@@ -75772,15 +76018,19 @@ var PiAcpSession = class {
75772
76018
  await this.flushEmits();
75773
76019
  }
75774
76020
  async settleTurn() {
76021
+ const questionSignal = this.pendingTurn?.questionSignal;
76022
+ this.extensionQuestions.endTurn();
75775
76023
  await this.publishContextUsage();
75776
76024
  const retryFailure = this.turnRetryFailure;
75777
76025
  this.turnRetryFailure = null;
75778
- if (retryFailure !== null && !this.cancelRequested) {
76026
+ if (questionSignal?.aborted && questionSignal.reason instanceof RequestError6 && !this.cancelRequested) {
76027
+ this.pendingTurn?.reject(questionSignal.reason);
76028
+ } else if (retryFailure !== null && !this.cancelRequested) {
75779
76029
  this.pendingTurn?.reject(
75780
76030
  // The message rides in `data.details` so clients that unwrap the
75781
76031
  // JSON-RPC internal-error envelope (like Mobius) surface the real
75782
76032
  // model error; `code` keeps the failure machine-readable.
75783
- RequestError5.internalError({
76033
+ RequestError6.internalError({
75784
76034
  code: "pi_retries_exhausted",
75785
76035
  details: `Pi retries exhausted: ${retryFailure}`
75786
76036
  })
@@ -75834,14 +76084,16 @@ var PiAcpSession = class {
75834
76084
  this.bashToolCallIds.delete(toolCallId);
75835
76085
  }
75836
76086
  startTurn(t) {
76087
+ const questionSignal = this.extensionQuestions.startTurn();
75837
76088
  this.cancelRequested = false;
75838
76089
  this.turnRetryFailure = null;
75839
- this.pendingTurn = { resolve: t.resolve, reject: t.reject };
76090
+ this.pendingTurn = { questionSignal, resolve: t.resolve, reject: t.reject };
75840
76091
  this.emit({
75841
76092
  sessionUpdate: "session_info_update",
75842
76093
  _meta: { piAcp: { queueDepth: this.turnQueue.length, running: true } }
75843
76094
  });
75844
76095
  this.proc.prompt(t.message, t.images).catch((err) => {
76096
+ this.extensionQuestions.endTurn();
75845
76097
  void this.flushEmits().finally(() => {
75846
76098
  const authErr = maybeAuthRequiredError(err);
75847
76099
  if (authErr) {
@@ -76080,14 +76332,15 @@ var PiAcpSession = class {
76080
76332
  break;
76081
76333
  }
76082
76334
  case "extension_ui_request": {
76083
- void this.handleExtensionUiRequest(ev2).catch(() => {
76084
- const id = stringProp(ev2, "id");
76085
- if (!id) {
76086
- return;
76087
- }
76088
- void this.proc.sendExtensionUiResponse({ id, cancelled: true }).catch(() => {
76335
+ if (ev2.method === "notify") {
76336
+ this.emit({
76337
+ sessionUpdate: "agent_message_chunk",
76338
+ content: { type: "text", text: stringProp(ev2, "message") ?? "Pi notification" },
76339
+ _meta: { piAcp: { notify: { level: stringProp(ev2, "notifyType") ?? "info" } } }
76089
76340
  });
76090
- });
76341
+ break;
76342
+ }
76343
+ void this.extensionQuestions.handle(ev2);
76091
76344
  break;
76092
76345
  }
76093
76346
  case "auto_retry_start": {
@@ -76140,119 +76393,11 @@ var PiAcpSession = class {
76140
76393
  break;
76141
76394
  }
76142
76395
  }
76143
- async handleExtensionUiRequest(ev2) {
76144
- const id = stringProp(ev2, "id");
76145
- const method = stringProp(ev2, "method");
76146
- if (!id) {
76147
- return;
76148
- }
76149
- if (method === "select") {
76150
- await this.handleExtensionSelect(ev2, id);
76151
- return;
76152
- }
76153
- if (method === "confirm") {
76154
- await this.handleExtensionConfirm(ev2, id);
76155
- return;
76156
- }
76157
- if (method === "input" || method === "editor") {
76158
- this.emit({
76159
- sessionUpdate: "agent_message_chunk",
76160
- content: {
76161
- type: "text",
76162
- text: `Pi ${method} UI request is not supported in ACP yet; cancelling it.`
76163
- }
76164
- });
76165
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
76166
- return;
76167
- }
76168
- if (method === "notify") {
76169
- this.emit({
76170
- sessionUpdate: "agent_message_chunk",
76171
- content: { type: "text", text: stringProp(ev2, "message") ?? "Pi notification" },
76172
- _meta: { piAcp: { notify: { level: stringProp(ev2, "notifyType") ?? "info" } } }
76173
- });
76174
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
76175
- return;
76176
- }
76177
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
76178
- }
76179
- async handleExtensionSelect(ev2, id) {
76180
- const rawOptions = ev2.options;
76181
- const options = Array.isArray(rawOptions) ? rawOptions.map((option) => String(option)) : [];
76182
- if (!options.length) {
76183
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
76184
- return;
76185
- }
76186
- const permissionOptions = options.map((name2, index3) => ({
76187
- optionId: `${CHOICE_OPTION_PREFIX}${index3}`,
76188
- name: name2,
76189
- kind: "allow_once"
76190
- }));
76191
- const selected = await this.requestExtensionPermission(id, ev2, permissionOptions);
76192
- if (selected === null) {
76193
- return;
76194
- }
76195
- const selectedOptionId = selected.outcome.outcome === "selected" ? selected.outcome.optionId : null;
76196
- const index2 = selectedOptionId === null ? null : optionIndex(selectedOptionId);
76197
- const value = index2 === null ? null : options.at(index2) ?? null;
76198
- await this.proc.sendExtensionUiResponse(value === null ? { id, cancelled: true } : { id, value });
76199
- }
76200
- async handleExtensionConfirm(ev2, id) {
76201
- const selected = await this.requestExtensionPermission(id, ev2, CONFIRM_PERMISSION_OPTIONS);
76202
- if (selected === null) {
76203
- return;
76204
- }
76205
- if (selected.outcome.outcome === "cancelled") {
76206
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
76207
- return;
76208
- }
76209
- await this.proc.sendExtensionUiResponse({ id, confirmed: selected.outcome.optionId === "yes" });
76210
- }
76211
- async requestExtensionPermission(id, ev2, options) {
76212
- try {
76213
- return await this.conn.requestPermission({
76214
- sessionId: this.sessionId,
76215
- toolCall: extensionUiToolCall(id, ev2),
76216
- options
76217
- });
76218
- } catch {
76219
- await this.proc.sendExtensionUiResponse({ id, cancelled: true });
76220
- return null;
76221
- }
76222
- }
76223
76396
  };
76224
- function extensionUiToolCall(id, ev2) {
76225
- const method = stringProp(ev2, "method") ?? "ui";
76226
- const title2 = stringProp(ev2, "title") ?? `Pi ${method}`;
76227
- const rawInput = { method };
76228
- for (const key of EXTENSION_UI_RAW_INPUT_KEYS) {
76229
- if (Object.hasOwn(ev2, key)) {
76230
- rawInput[key] = ev2[key];
76231
- }
76232
- }
76233
- return {
76234
- toolCallId: `pi-ui-${id}`,
76235
- title: title2,
76236
- kind: "other",
76237
- status: "pending",
76238
- rawInput
76239
- };
76240
- }
76241
76397
  function stringProp(source, key) {
76242
76398
  const value = source[key];
76243
76399
  return typeof value === "string" ? value : null;
76244
76400
  }
76245
- function optionIndex(optionId) {
76246
- if (!optionId.startsWith(CHOICE_OPTION_PREFIX)) {
76247
- return null;
76248
- }
76249
- const rawIndex = optionId.slice(CHOICE_OPTION_PREFIX.length);
76250
- if (!rawIndex) {
76251
- return null;
76252
- }
76253
- const index2 = Number(rawIndex);
76254
- return Number.isSafeInteger(index2) && index2 >= 0 && String(index2) === rawIndex ? index2 : null;
76255
- }
76256
76401
  function toToolKind(toolName) {
76257
76402
  switch (toolName) {
76258
76403
  case "read":
@@ -76479,7 +76624,7 @@ var PiAcpAgent = class {
76479
76624
  const restorePromise = (async () => {
76480
76625
  const stored = this.findStoredSession(sessionId);
76481
76626
  if (!stored) {
76482
- throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
76627
+ throw RequestError7.invalidParams(`Unknown sessionId: ${sessionId}`);
76483
76628
  }
76484
76629
  const cwd = opts?.cwd ?? stored.cwd;
76485
76630
  let proc;
@@ -76492,7 +76637,7 @@ var PiAcpAgent = class {
76492
76637
  });
76493
76638
  } catch (e) {
76494
76639
  if (e?.name === "PiRpcSpawnError") {
76495
- throw RequestError6.internalError({ code: e?.code }, String(e?.message ?? e));
76640
+ throw RequestError7.internalError({ code: e?.code }, String(e?.message ?? e));
76496
76641
  }
76497
76642
  throw e;
76498
76643
  }
@@ -76552,7 +76697,7 @@ var PiAcpAgent = class {
76552
76697
  }
76553
76698
  async newSession(params) {
76554
76699
  if (!isAbsolute11(params.cwd)) {
76555
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
76700
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
76556
76701
  }
76557
76702
  this.lastSessionCwd = params.cwd;
76558
76703
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -76591,16 +76736,16 @@ var PiAcpAgent = class {
76591
76736
  }
76592
76737
  if (availableModelsErr) {
76593
76738
  this.cleanupFailedNewSession(session.sessionId, state);
76594
- throw RequestError6.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
76739
+ throw RequestError7.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
76595
76740
  }
76596
76741
  const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
76597
76742
  if (rawModelsCount === 0) {
76598
76743
  this.cleanupFailedNewSession(session.sessionId, state);
76599
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76744
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76600
76745
  }
76601
76746
  if (stateErr && maybeAuthRequiredError(stateErr)) {
76602
76747
  this.cleanupFailedNewSession(session.sessionId, state);
76603
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76748
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76604
76749
  }
76605
76750
  let configuration;
76606
76751
  try {
@@ -76610,7 +76755,7 @@ var PiAcpAgent = class {
76610
76755
  configuration = await getSessionConfiguration(session.proc, { state, availableModels });
76611
76756
  } catch (err) {
76612
76757
  this.cleanupFailedNewSession(session.sessionId, state);
76613
- throw maybeAuthRequiredError(err) ?? RequestError6.internalError({}, String(err?.message ?? err));
76758
+ throw maybeAuthRequiredError(err) ?? RequestError7.internalError({}, String(err?.message ?? err));
76614
76759
  }
76615
76760
  const { configOptions, models, modes } = configuration;
76616
76761
  const quietStartup = getQuietStartup(params.cwd, this.environment);
@@ -76695,9 +76840,7 @@ var PiAcpAgent = class {
76695
76840
  }
76696
76841
  });
76697
76842
  try {
76698
- const res = await session.proc.compact(customInstructions, {
76699
- timeoutMs: COMPACT_TIMEOUT_MILLISECONDS2
76700
- });
76843
+ const res = await session.compact(customInstructions, COMPACT_TIMEOUT_MILLISECONDS2);
76701
76844
  const r = res && typeof res === "object" ? res : null;
76702
76845
  const summary = typeof r?.summary === "string" ? r.summary : null;
76703
76846
  if (summary !== null && summary.length > 0) {
@@ -77110,7 +77253,7 @@ ${JSON.stringify(stats, null, 2)}`;
77110
77253
  signal.throwIfAborted();
77111
77254
  const parsed = steerRequestSchema.safeParse(params);
77112
77255
  if (!parsed.success) {
77113
- throw RequestError6.invalidParams("Invalid steering request");
77256
+ throw RequestError7.invalidParams("Invalid steering request");
77114
77257
  }
77115
77258
  const session = this.sessions.maybeGet(parsed.data.sessionId);
77116
77259
  if (session === void 0) {
@@ -77151,11 +77294,11 @@ ${JSON.stringify(stats, null, 2)}`;
77151
77294
  }
77152
77295
  async forkSession(params) {
77153
77296
  if (!isAbsolute11(params.cwd)) {
77154
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77297
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77155
77298
  }
77156
77299
  const source = this.findStoredSession(params.sessionId);
77157
77300
  if (!source) {
77158
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77301
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77159
77302
  }
77160
77303
  this.lastSessionCwd = params.cwd;
77161
77304
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -77217,13 +77360,13 @@ ${JSON.stringify(stats, null, 2)}`;
77217
77360
  }
77218
77361
  async loadSession(params) {
77219
77362
  if (!isAbsolute11(params.cwd)) {
77220
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77363
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77221
77364
  }
77222
77365
  this.sessions.close(params.sessionId);
77223
77366
  this.lastSessionCwd = params.cwd;
77224
77367
  const stored = this.findStoredSession(params.sessionId);
77225
77368
  if (!stored) {
77226
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77369
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77227
77370
  }
77228
77371
  const enableSkillCommands = getEnableSkillCommands(params.cwd, this.environment);
77229
77372
  const session = await this.restoreSession(params.sessionId, {
@@ -77398,7 +77541,7 @@ ${JSON.stringify(stats, null, 2)}`;
77398
77541
  const session = await this.restoreSession(params.sessionId);
77399
77542
  const mode = params.modeId;
77400
77543
  if (typeof mode !== "string" || mode.length === 0) {
77401
- throw RequestError6.invalidParams("Expected nonempty string modeId");
77544
+ throw RequestError7.invalidParams("Expected nonempty string modeId");
77402
77545
  }
77403
77546
  await session.proc.setThinkingLevel(mode);
77404
77547
  await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
@@ -77409,18 +77552,18 @@ ${JSON.stringify(stats, null, 2)}`;
77409
77552
  const configId = String(params.configId);
77410
77553
  let modelChanged = false;
77411
77554
  if (typeof params.value !== "string") {
77412
- throw RequestError6.invalidParams(`Expected string value for config option: ${configId}`);
77555
+ throw RequestError7.invalidParams(`Expected string value for config option: ${configId}`);
77413
77556
  }
77414
77557
  if (configId === MODEL_CONFIG_ID2) {
77415
77558
  await setSessionModel(session.proc, params.value);
77416
77559
  modelChanged = true;
77417
77560
  } else if (configId === THOUGHT_LEVEL_CONFIG_ID) {
77418
77561
  if (params.value.length === 0) {
77419
- throw RequestError6.invalidParams("Expected nonempty thinking level");
77562
+ throw RequestError7.invalidParams("Expected nonempty thinking level");
77420
77563
  }
77421
77564
  await session.proc.setThinkingLevel(params.value);
77422
77565
  } else {
77423
- throw RequestError6.invalidParams(`Unknown config option: ${configId}`);
77566
+ throw RequestError7.invalidParams(`Unknown config option: ${configId}`);
77424
77567
  }
77425
77568
  const configOptions = await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
77426
77569
  if (modelChanged) {
@@ -77576,7 +77719,7 @@ async function setSessionModel(proc, requestedModelId) {
77576
77719
  }
77577
77720
  }
77578
77721
  if (!provider || !modelId) {
77579
- throw RequestError6.invalidParams(`Unknown modelId: ${requestedModelId}`);
77722
+ throw RequestError7.invalidParams(`Unknown modelId: ${requestedModelId}`);
77580
77723
  }
77581
77724
  await proc.setModel(provider, modelId);
77582
77725
  }
@@ -77758,6 +77901,7 @@ function buildStartupInfo(opts) {
77758
77901
  import { methods as methods4 } from "@agentclientprotocol/sdk";
77759
77902
  function createPiAcpClient(connection, onSessionUpdate) {
77760
77903
  return {
77904
+ requestElicitation: async (request, signal) => await connection.client.request(methods4.client.elicitation.create, request, { cancellationSignal: signal }),
77761
77905
  requestPermission: async (request) => await connection.client.request(methods4.client.session.requestPermission, request),
77762
77906
  sessionUpdate: onSessionUpdate
77763
77907
  };
@@ -78234,7 +78378,7 @@ import {
78234
78378
  methods as methods8,
78235
78379
  ndJsonStream as ndJsonStream2,
78236
78380
  PROTOCOL_VERSION as PROTOCOL_VERSION2,
78237
- RequestError as RequestError7
78381
+ RequestError as RequestError8
78238
78382
  } from "@agentclientprotocol/sdk";
78239
78383
 
78240
78384
  // src/acp/connection/create-client.ts
@@ -78408,7 +78552,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
78408
78552
  if (error62 instanceof AgentProbeError) {
78409
78553
  throw error62;
78410
78554
  }
78411
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78555
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78412
78556
  throw new AgentProbeError("authentication_required", "Agent authentication is required", {
78413
78557
  cause: error62
78414
78558
  });
@@ -78484,7 +78628,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
78484
78628
  if (error62 instanceof AgentProbeError) {
78485
78629
  throw error62;
78486
78630
  }
78487
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78631
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78488
78632
  throw new AgentProbeError("authentication_required", "Agent authentication is required", { cause: error62 });
78489
78633
  }
78490
78634
  throw new AgentProbeError("acp_incompatible", "ACP adapter initialization failed", { cause: error62 });
@@ -88652,7 +88796,7 @@ function buildPromptContent(options, capabilities) {
88652
88796
 
88653
88797
  // src/acp/connection/agent-connection.ts
88654
88798
  import { spawn as spawn9 } from "node:child_process";
88655
- import { ndJsonStream as ndJsonStream3, RequestError as RequestError8 } from "@agentclientprotocol/sdk";
88799
+ import { ndJsonStream as ndJsonStream3, RequestError as RequestError9 } from "@agentclientprotocol/sdk";
88656
88800
  function requestErrorMessage(error62) {
88657
88801
  const fallback = truncate2(error62.message, 500);
88658
88802
  if (error62.message !== "Internal error" || !isRecord3(error62.data)) {
@@ -88857,14 +89001,14 @@ var AgentConnection = class {
88857
89001
  ]);
88858
89002
  } catch (error62) {
88859
89003
  signal.throwIfAborted();
88860
- if (error62 instanceof RequestError8 && error62.code === -32e3) {
89004
+ if (error62 instanceof RequestError9 && error62.code === -32e3) {
88861
89005
  throw new AcpExecutionError(
88862
89006
  "Agent authentication is required",
88863
89007
  { cause: error62 },
88864
89008
  "authentication_required"
88865
89009
  );
88866
89010
  }
88867
- if (error62 instanceof RequestError8) {
89011
+ if (error62 instanceof RequestError9) {
88868
89012
  throw new AcpExecutionError(requestErrorMessage(error62), { cause: error62 }, "acp_agent_error");
88869
89013
  }
88870
89014
  throw error62;
@@ -89318,6 +89462,7 @@ function normalizeElicitationFields(requestedSchema) {
89318
89462
  const options = normalizeElicitationOptions(property);
89319
89463
  const normalizedType = type === "string" && options.length > 0 ? "select" : type;
89320
89464
  const defaultValue = property["default"];
89465
+ const metadata = property["_meta"];
89321
89466
  normalized.push({
89322
89467
  defaultBooleanValue: typeof defaultValue === "boolean" ? defaultValue : false,
89323
89468
  defaultNumberValue: typeof defaultValue === "number" && Number.isFinite(defaultValue) ? defaultValue : 0,
@@ -89329,6 +89474,7 @@ function normalizeElicitationFields(requestedSchema) {
89329
89474
  maximumLength: numericString(property["maxLength"] ?? property["maxItems"]),
89330
89475
  minimum: numericString(property["minimum"]),
89331
89476
  minimumLength: numericString(property["minLength"] ?? property["minItems"]),
89477
+ ...normalizedType === "string" && isRecord3(metadata) && metadata["io.mobius/multiline"] === true ? { multiline: true } : {},
89332
89478
  name: truncate2(name2, 128),
89333
89479
  options,
89334
89480
  pattern: truncate2(readString2(property["pattern"]), 500),