@moxt-ai/mobius 0.0.290 → 0.0.292

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.292",
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),
@@ -73017,6 +73018,9 @@ var sessionEventLogPageSchema = external_exports.object({
73017
73018
  nextCursor: external_exports.string().min(1).max(512).nullable(),
73018
73019
  sessionGenerationId: identifierStringSchema,
73019
73020
  tailCursor: external_exports.string().max(512),
73021
+ // The generation-scoped live position covered by this snapshot, including
73022
+ // coalesced and omitted events. Absent only on older Runtime versions.
73023
+ throughPosition: positionSchema.optional(),
73020
73024
  truncated: external_exports.boolean(),
73021
73025
  version: external_exports.literal(2)
73022
73026
  }).readonly();
@@ -74237,7 +74241,7 @@ import { spawnSync } from "node:child_process";
74237
74241
  import { randomUUID as randomUUID7 } from "node:crypto";
74238
74242
  import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
74239
74243
  import { basename as basename4, dirname as dirname7, isAbsolute as isAbsolute11, join as join17 } from "node:path";
74240
- import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
74244
+ import { RequestError as RequestError7 } from "@agentclientprotocol/sdk";
74241
74245
 
74242
74246
  // src/acp/harness/pi/upstream/dist/pi-rpc/process.js
74243
74247
  import { spawn as spawn4 } from "node:child_process";
@@ -74270,6 +74274,13 @@ var PiRpcSpawnError = class extends Error {
74270
74274
  this.cause = opts?.cause;
74271
74275
  }
74272
74276
  };
74277
+ var PiRpcProcessError = class extends Error {
74278
+ code = "pi_process_exited";
74279
+ constructor(cause) {
74280
+ super("Pi process stopped unexpectedly.", { cause });
74281
+ this.name = "PiRpcProcessError";
74282
+ }
74283
+ };
74273
74284
  var ESC = String.fromCharCode(27);
74274
74285
  var CSI = String.fromCharCode(155);
74275
74286
  var ANSI_ESCAPE_REGEX = new RegExp(`[${ESC}${CSI}][[\\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`, "g");
@@ -74279,6 +74290,10 @@ function stripAnsi(s) {
74279
74290
  var SESSION_STATS_TIMEOUT_MS = 1e3;
74280
74291
  var PiRpcProcess = class _PiRpcProcess {
74281
74292
  child;
74293
+ lifetime = new AbortController();
74294
+ get signal() {
74295
+ return this.lifetime.signal;
74296
+ }
74282
74297
  pending = /* @__PURE__ */ new Map();
74283
74298
  eventHandlers = [];
74284
74299
  preludeLines = [];
@@ -74313,13 +74328,16 @@ var PiRpcProcess = class _PiRpcProcess {
74313
74328
  }
74314
74329
  });
74315
74330
  child.on("exit", (code, signal) => {
74316
- const err = new Error(`pi process exited (code=${code}, signal=${signal})`);
74331
+ const err = new PiRpcProcessError({ code, signal });
74332
+ this.lifetime.abort(err);
74317
74333
  for (const [, p] of this.pending) {
74318
74334
  p.reject(err);
74319
74335
  }
74320
74336
  this.pending.clear();
74321
74337
  });
74322
- child.on("error", (err) => {
74338
+ child.on("error", (cause) => {
74339
+ const err = new PiRpcProcessError(cause);
74340
+ this.lifetime.abort(err);
74323
74341
  for (const [, p] of this.pending) {
74324
74342
  p.reject(err);
74325
74343
  }
@@ -74401,6 +74419,7 @@ var PiRpcProcess = class _PiRpcProcess {
74401
74419
  };
74402
74420
  }
74403
74421
  dispose(signal = "SIGTERM") {
74422
+ this.lifetime.abort();
74404
74423
  if (this.child.killed) {
74405
74424
  return;
74406
74425
  }
@@ -74409,6 +74428,10 @@ var PiRpcProcess = class _PiRpcProcess {
74409
74428
  } catch {
74410
74429
  }
74411
74430
  }
74431
+ terminateAfterFailure(cause) {
74432
+ this.lifetime.abort(new PiRpcProcessError(cause));
74433
+ this.dispose();
74434
+ }
74412
74435
  /**
74413
74436
  * Human-readable stdout lines emitted before RPC NDJSON begins (e.g. Context/Skills/Extensions info).
74414
74437
  * Themes are typically noisy/less useful for ACP, so callers can filter as needed.
@@ -74429,8 +74452,8 @@ var PiRpcProcess = class _PiRpcProcess {
74429
74452
  throw new Error(`pi steer failed: ${res.error ?? JSON.stringify(res.data)}`);
74430
74453
  }
74431
74454
  }
74432
- async abort() {
74433
- const res = await this.request({ type: "abort" });
74455
+ async abort(opts) {
74456
+ const res = await this.request({ type: "abort" }, opts);
74434
74457
  if (!res.success) {
74435
74458
  throw new Error(`pi abort failed: ${res.error ?? JSON.stringify(res.data)}`);
74436
74459
  }
@@ -75127,7 +75150,176 @@ function getQuietStartup(cwd, environment = process.env) {
75127
75150
  import { randomUUID as randomUUID6 } from "node:crypto";
75128
75151
  import { readFileSync as readFileSync7 } from "node:fs";
75129
75152
  import { isAbsolute as isAbsolute10, resolve as resolvePath } from "node:path";
75153
+ import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
75154
+
75155
+ // src/acp/harness/pi/upstream/dist/acp/extension-questions.js
75130
75156
  import { RequestError as RequestError5 } from "@agentclientprotocol/sdk";
75157
+ var questionBase = {
75158
+ id: external_exports.string().min(1).max(255),
75159
+ title: external_exports.string().max(255),
75160
+ timeout: external_exports.number().int().nonnegative().max(2147483647).optional()
75161
+ };
75162
+ var questionSchema = external_exports.discriminatedUnion("method", [
75163
+ external_exports.object({
75164
+ ...questionBase,
75165
+ method: external_exports.literal("select"),
75166
+ options: external_exports.array(external_exports.string().min(1).max(255)).min(1).max(200).readonly()
75167
+ }),
75168
+ external_exports.object({ ...questionBase, method: external_exports.literal("confirm"), message: external_exports.string().max(2e3) }),
75169
+ external_exports.object({ ...questionBase, method: external_exports.literal("input"), placeholder: external_exports.string().max(500).optional() }),
75170
+ external_exports.object({ ...questionBase, method: external_exports.literal("editor"), prefill: external_exports.string().max(1e4).optional() })
75171
+ ]).readonly();
75172
+ var QUESTION_TIMEOUT_MS = 36e5;
75173
+ var PiQuestionError = class extends RequestError5 {
75174
+ constructor(kind, cause) {
75175
+ const messages = {
75176
+ request_failed: "Pi could not request an answer.",
75177
+ invalid_response: "Pi received an invalid answer.",
75178
+ response_failed: "Pi could not receive the answer."
75179
+ };
75180
+ super(-32603, "Internal error", { code: `pi_question_${kind}`, details: messages[kind] });
75181
+ this.cause = cause;
75182
+ }
75183
+ };
75184
+ function questionField(question) {
75185
+ const title2 = question.title || "Answer";
75186
+ switch (question.method) {
75187
+ case "select":
75188
+ return { type: "string", title: title2, enum: [...question.options] };
75189
+ case "confirm":
75190
+ return {
75191
+ type: "string",
75192
+ title: title2,
75193
+ oneOf: [
75194
+ { const: "yes", title: "Yes" },
75195
+ { const: "no", title: "No" }
75196
+ ]
75197
+ };
75198
+ case "input":
75199
+ return { type: "string", title: title2, description: question.placeholder, maxLength: 1e4 };
75200
+ case "editor":
75201
+ return {
75202
+ type: "string",
75203
+ title: title2,
75204
+ default: question.prefill,
75205
+ maxLength: 1e4,
75206
+ _meta: { "io.mobius/multiline": true }
75207
+ };
75208
+ }
75209
+ }
75210
+ var PiExtensionQuestions = class {
75211
+ sessionId;
75212
+ conn;
75213
+ proc;
75214
+ notify;
75215
+ turn = null;
75216
+ constructor(sessionId, conn, proc, notify) {
75217
+ this.sessionId = sessionId;
75218
+ this.conn = conn;
75219
+ this.proc = proc;
75220
+ this.notify = notify;
75221
+ }
75222
+ startTurn() {
75223
+ this.endTurn();
75224
+ this.turn = { controller: new AbortController(), requestIds: /* @__PURE__ */ new Set() };
75225
+ return this.turn.controller.signal;
75226
+ }
75227
+ endTurn(reason) {
75228
+ this.turn?.controller.abort(reason);
75229
+ this.turn = null;
75230
+ }
75231
+ async handle(event) {
75232
+ if (event.method !== "select" && event.method !== "confirm" && event.method !== "input" && event.method !== "editor") {
75233
+ return;
75234
+ }
75235
+ if (typeof event.id !== "string" || event.id.length === 0 || event.id.length > 255) {
75236
+ return;
75237
+ }
75238
+ const turn = this.turn;
75239
+ if (turn?.requestIds.has(event.id)) {
75240
+ return;
75241
+ }
75242
+ turn?.requestIds.add(event.id);
75243
+ try {
75244
+ await this.answer(event, turn);
75245
+ } catch (cause) {
75246
+ if (!this.proc.signal.aborted) {
75247
+ turn?.controller.abort(new PiQuestionError("response_failed", cause));
75248
+ this.proc.terminateAfterFailure(cause);
75249
+ }
75250
+ }
75251
+ }
75252
+ async answer(event, turn) {
75253
+ const id = typeof event.id === "string" ? event.id : "";
75254
+ const parsed = questionSchema.safeParse(event);
75255
+ if (!parsed.success) {
75256
+ await this.notify("Pi requested a question with unsupported or oversized fields. The question was cancelled without changing its content.");
75257
+ await this.proc.sendExtensionUiResponse({ id, cancelled: true });
75258
+ return;
75259
+ }
75260
+ const question = parsed.data;
75261
+ if (turn === null || turn.controller.signal.aborted) {
75262
+ await this.proc.sendExtensionUiResponse({ id: question.id, cancelled: true });
75263
+ return;
75264
+ }
75265
+ const deadline = new AbortController();
75266
+ const timeout = setTimeout(() => deadline.abort(), question.timeout || QUESTION_TIMEOUT_MS);
75267
+ const signal = AbortSignal.any([turn.controller.signal, deadline.signal]);
75268
+ let response = { id: question.id, cancelled: true };
75269
+ try {
75270
+ const answer = await this.conn.requestElicitation({
75271
+ sessionId: this.sessionId,
75272
+ mode: "form",
75273
+ message: (question.method === "confirm" ? question.message : "") || question.title || "Pi question",
75274
+ requestedSchema: {
75275
+ type: "object",
75276
+ properties: { value: questionField(question) },
75277
+ required: ["value"]
75278
+ }
75279
+ }, signal);
75280
+ const content = answer.action === "accept" ? answer.content : void 0;
75281
+ const value = typeof content === "object" && content !== null ? Reflect.get(content, "value") : void 0;
75282
+ if (!signal.aborted && answer.action === "accept") {
75283
+ if (typeof value !== "string") {
75284
+ throw new PiQuestionError("invalid_response");
75285
+ }
75286
+ switch (question.method) {
75287
+ case "select":
75288
+ if (!question.options.includes(value)) {
75289
+ throw new PiQuestionError("invalid_response");
75290
+ }
75291
+ response = { id: question.id, value };
75292
+ break;
75293
+ case "confirm":
75294
+ if (value !== "yes" && value !== "no") {
75295
+ throw new PiQuestionError("invalid_response");
75296
+ }
75297
+ response = { id: question.id, confirmed: value === "yes" };
75298
+ break;
75299
+ case "input":
75300
+ case "editor":
75301
+ if (value.length > 1e4) {
75302
+ throw new PiQuestionError("invalid_response");
75303
+ }
75304
+ response = { id: question.id, value };
75305
+ break;
75306
+ }
75307
+ } else if (!signal.aborted && answer.action !== "cancel" && answer.action !== "decline") {
75308
+ throw new PiQuestionError("invalid_response");
75309
+ }
75310
+ } catch (cause) {
75311
+ if (!signal.aborted && !(cause instanceof RequestError5 && cause.code === -32800)) {
75312
+ turn.controller.abort(cause instanceof PiQuestionError ? cause : new PiQuestionError("request_failed", cause));
75313
+ }
75314
+ } finally {
75315
+ clearTimeout(timeout);
75316
+ }
75317
+ if (deadline.signal.aborted && signal.reason === deadline.signal.reason) {
75318
+ await this.notify("Pi question timed out; no answer was submitted.");
75319
+ }
75320
+ await this.proc.sendExtensionUiResponse(response);
75321
+ }
75322
+ };
75131
75323
 
75132
75324
  // src/acp/harness/pi/upstream/dist/acp/session-store.js
75133
75325
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "node:fs";
@@ -75396,12 +75588,6 @@ ${stderr}`);
75396
75588
  }
75397
75589
 
75398
75590
  // 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
75591
  function toUsageUpdate(stats) {
75406
75592
  const used = stats?.contextUsage?.tokens;
75407
75593
  const size = stats?.contextUsage?.contextWindow;
@@ -75522,7 +75708,7 @@ var SessionManager = class {
75522
75708
  return;
75523
75709
  }
75524
75710
  try {
75525
- s.proc.dispose?.();
75711
+ s.dispose();
75526
75712
  } catch {
75527
75713
  }
75528
75714
  this.sessions.delete(sessionId);
@@ -75547,7 +75733,7 @@ var SessionManager = class {
75547
75733
  });
75548
75734
  } catch (e) {
75549
75735
  if (e instanceof PiRpcSpawnError) {
75550
- throw RequestError5.internalError({ code: e.code }, e.message);
75736
+ throw RequestError6.internalError({ code: e.code }, e.message);
75551
75737
  }
75552
75738
  throw e;
75553
75739
  }
@@ -75576,7 +75762,7 @@ var SessionManager = class {
75576
75762
  get(sessionId) {
75577
75763
  const s = this.sessions.get(sessionId);
75578
75764
  if (!s) {
75579
- throw RequestError5.invalidParams(`Unknown sessionId: ${sessionId}`);
75765
+ throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
75580
75766
  }
75581
75767
  return s;
75582
75768
  }
@@ -75609,6 +75795,7 @@ var PiAcpSession = class {
75609
75795
  startupInfoSent = false;
75610
75796
  proc;
75611
75797
  conn;
75798
+ extensionQuestions;
75612
75799
  fileCommands;
75613
75800
  // Used to map abort semantics to ACP stopReason.
75614
75801
  // Applies to the currently running turn.
@@ -75663,8 +75850,38 @@ var PiAcpSession = class {
75663
75850
  this.proc = opts.proc;
75664
75851
  this.conn = opts.conn;
75665
75852
  this.fileCommands = opts.fileCommands ?? [];
75853
+ this.extensionQuestions = new PiExtensionQuestions(this.sessionId, this.conn, this.proc, async (text2) => {
75854
+ this.emit({ sessionUpdate: "agent_message_chunk", content: { type: "text", text: text2 } });
75855
+ await this.flushEmits();
75856
+ });
75857
+ this.proc.signal.addEventListener("abort", () => {
75858
+ const cause = this.proc.signal.reason;
75859
+ if (cause instanceof PiRpcProcessError) {
75860
+ const pending = this.pendingTurn;
75861
+ const reason = pending?.questionSignal.reason;
75862
+ const error62 = reason instanceof RequestError6 ? reason : RequestError6.internalError({ code: cause.code, details: cause.message });
75863
+ if (!(reason instanceof RequestError6)) {
75864
+ error62.cause = cause;
75865
+ }
75866
+ this.extensionQuestions.endTurn(error62);
75867
+ this.pendingTurn = null;
75868
+ void this.flushEmits().then(() => {
75869
+ if (this.cancelRequested) {
75870
+ pending?.resolve("cancelled");
75871
+ } else {
75872
+ pending?.reject(error62);
75873
+ }
75874
+ });
75875
+ } else {
75876
+ this.extensionQuestions.endTurn();
75877
+ }
75878
+ }, { once: true });
75666
75879
  this.proc.onEvent((ev2) => this.handlePiEvent(ev2));
75667
75880
  }
75881
+ dispose() {
75882
+ this.extensionQuestions.endTurn();
75883
+ this.proc.dispose();
75884
+ }
75668
75885
  setStartupInfo(text2) {
75669
75886
  this.startupInfo = text2;
75670
75887
  this.startupInfoSent = false;
@@ -75707,6 +75924,31 @@ var PiAcpSession = class {
75707
75924
  });
75708
75925
  return turnPromise;
75709
75926
  }
75927
+ async compact(customInstructions, timeoutMs) {
75928
+ const signal = this.extensionQuestions.startTurn();
75929
+ try {
75930
+ const result = await this.proc.compact(customInstructions, { signal, timeoutMs });
75931
+ if (signal.aborted && signal.reason instanceof RequestError6) {
75932
+ throw signal.reason;
75933
+ }
75934
+ return result;
75935
+ } catch (cause) {
75936
+ if (signal.aborted && signal.reason instanceof RequestError6) {
75937
+ try {
75938
+ await this.proc.abort({ signal: this.proc.signal, timeoutMs });
75939
+ } catch (abortCause) {
75940
+ if (!this.proc.signal.aborted) {
75941
+ this.proc.terminateAfterFailure(abortCause);
75942
+ }
75943
+ }
75944
+ throw signal.reason;
75945
+ }
75946
+ throw cause;
75947
+ } finally {
75948
+ this.extensionQuestions.endTurn();
75949
+ await this.flushEmits();
75950
+ }
75951
+ }
75710
75952
  async steer(message, images, signal) {
75711
75953
  const target = this.pendingTurn;
75712
75954
  if (target === null) {
@@ -75725,6 +75967,7 @@ var PiAcpSession = class {
75725
75967
  }
75726
75968
  async cancel() {
75727
75969
  this.cancelRequested = true;
75970
+ this.extensionQuestions.endTurn();
75728
75971
  if (this.turnQueue.length) {
75729
75972
  const queued = this.turnQueue.splice(0, this.turnQueue.length);
75730
75973
  for (const t of queued) {
@@ -75739,7 +75982,13 @@ var PiAcpSession = class {
75739
75982
  _meta: { piAcp: { queueDepth: 0, running: Boolean(this.pendingTurn) } }
75740
75983
  });
75741
75984
  }
75742
- await this.proc.abort();
75985
+ try {
75986
+ await this.proc.abort();
75987
+ } catch (cause) {
75988
+ if (!this.proc.signal.aborted) {
75989
+ throw cause;
75990
+ }
75991
+ }
75743
75992
  }
75744
75993
  wasCancelRequested() {
75745
75994
  return this.cancelRequested;
@@ -75772,15 +76021,19 @@ var PiAcpSession = class {
75772
76021
  await this.flushEmits();
75773
76022
  }
75774
76023
  async settleTurn() {
76024
+ const questionSignal = this.pendingTurn?.questionSignal;
76025
+ this.extensionQuestions.endTurn();
75775
76026
  await this.publishContextUsage();
75776
76027
  const retryFailure = this.turnRetryFailure;
75777
76028
  this.turnRetryFailure = null;
75778
- if (retryFailure !== null && !this.cancelRequested) {
76029
+ if (questionSignal?.aborted && questionSignal.reason instanceof RequestError6 && !this.cancelRequested) {
76030
+ this.pendingTurn?.reject(questionSignal.reason);
76031
+ } else if (retryFailure !== null && !this.cancelRequested) {
75779
76032
  this.pendingTurn?.reject(
75780
76033
  // The message rides in `data.details` so clients that unwrap the
75781
76034
  // JSON-RPC internal-error envelope (like Mobius) surface the real
75782
76035
  // model error; `code` keeps the failure machine-readable.
75783
- RequestError5.internalError({
76036
+ RequestError6.internalError({
75784
76037
  code: "pi_retries_exhausted",
75785
76038
  details: `Pi retries exhausted: ${retryFailure}`
75786
76039
  })
@@ -75834,14 +76087,16 @@ var PiAcpSession = class {
75834
76087
  this.bashToolCallIds.delete(toolCallId);
75835
76088
  }
75836
76089
  startTurn(t) {
76090
+ const questionSignal = this.extensionQuestions.startTurn();
75837
76091
  this.cancelRequested = false;
75838
76092
  this.turnRetryFailure = null;
75839
- this.pendingTurn = { resolve: t.resolve, reject: t.reject };
76093
+ this.pendingTurn = { questionSignal, resolve: t.resolve, reject: t.reject };
75840
76094
  this.emit({
75841
76095
  sessionUpdate: "session_info_update",
75842
76096
  _meta: { piAcp: { queueDepth: this.turnQueue.length, running: true } }
75843
76097
  });
75844
76098
  this.proc.prompt(t.message, t.images).catch((err) => {
76099
+ this.extensionQuestions.endTurn();
75845
76100
  void this.flushEmits().finally(() => {
75846
76101
  const authErr = maybeAuthRequiredError(err);
75847
76102
  if (authErr) {
@@ -76080,14 +76335,15 @@ var PiAcpSession = class {
76080
76335
  break;
76081
76336
  }
76082
76337
  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(() => {
76338
+ if (ev2.method === "notify") {
76339
+ this.emit({
76340
+ sessionUpdate: "agent_message_chunk",
76341
+ content: { type: "text", text: stringProp(ev2, "message") ?? "Pi notification" },
76342
+ _meta: { piAcp: { notify: { level: stringProp(ev2, "notifyType") ?? "info" } } }
76089
76343
  });
76090
- });
76344
+ break;
76345
+ }
76346
+ void this.extensionQuestions.handle(ev2);
76091
76347
  break;
76092
76348
  }
76093
76349
  case "auto_retry_start": {
@@ -76140,119 +76396,11 @@ var PiAcpSession = class {
76140
76396
  break;
76141
76397
  }
76142
76398
  }
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
76399
  };
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
76400
  function stringProp(source, key) {
76242
76401
  const value = source[key];
76243
76402
  return typeof value === "string" ? value : null;
76244
76403
  }
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
76404
  function toToolKind(toolName) {
76257
76405
  switch (toolName) {
76258
76406
  case "read":
@@ -76479,7 +76627,7 @@ var PiAcpAgent = class {
76479
76627
  const restorePromise = (async () => {
76480
76628
  const stored = this.findStoredSession(sessionId);
76481
76629
  if (!stored) {
76482
- throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
76630
+ throw RequestError7.invalidParams(`Unknown sessionId: ${sessionId}`);
76483
76631
  }
76484
76632
  const cwd = opts?.cwd ?? stored.cwd;
76485
76633
  let proc;
@@ -76492,7 +76640,7 @@ var PiAcpAgent = class {
76492
76640
  });
76493
76641
  } catch (e) {
76494
76642
  if (e?.name === "PiRpcSpawnError") {
76495
- throw RequestError6.internalError({ code: e?.code }, String(e?.message ?? e));
76643
+ throw RequestError7.internalError({ code: e?.code }, String(e?.message ?? e));
76496
76644
  }
76497
76645
  throw e;
76498
76646
  }
@@ -76552,7 +76700,7 @@ var PiAcpAgent = class {
76552
76700
  }
76553
76701
  async newSession(params) {
76554
76702
  if (!isAbsolute11(params.cwd)) {
76555
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
76703
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
76556
76704
  }
76557
76705
  this.lastSessionCwd = params.cwd;
76558
76706
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -76591,16 +76739,16 @@ var PiAcpAgent = class {
76591
76739
  }
76592
76740
  if (availableModelsErr) {
76593
76741
  this.cleanupFailedNewSession(session.sessionId, state);
76594
- throw RequestError6.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
76742
+ throw RequestError7.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
76595
76743
  }
76596
76744
  const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
76597
76745
  if (rawModelsCount === 0) {
76598
76746
  this.cleanupFailedNewSession(session.sessionId, state);
76599
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76747
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76600
76748
  }
76601
76749
  if (stateErr && maybeAuthRequiredError(stateErr)) {
76602
76750
  this.cleanupFailedNewSession(session.sessionId, state);
76603
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76751
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
76604
76752
  }
76605
76753
  let configuration;
76606
76754
  try {
@@ -76610,7 +76758,7 @@ var PiAcpAgent = class {
76610
76758
  configuration = await getSessionConfiguration(session.proc, { state, availableModels });
76611
76759
  } catch (err) {
76612
76760
  this.cleanupFailedNewSession(session.sessionId, state);
76613
- throw maybeAuthRequiredError(err) ?? RequestError6.internalError({}, String(err?.message ?? err));
76761
+ throw maybeAuthRequiredError(err) ?? RequestError7.internalError({}, String(err?.message ?? err));
76614
76762
  }
76615
76763
  const { configOptions, models, modes } = configuration;
76616
76764
  const quietStartup = getQuietStartup(params.cwd, this.environment);
@@ -76695,9 +76843,7 @@ var PiAcpAgent = class {
76695
76843
  }
76696
76844
  });
76697
76845
  try {
76698
- const res = await session.proc.compact(customInstructions, {
76699
- timeoutMs: COMPACT_TIMEOUT_MILLISECONDS2
76700
- });
76846
+ const res = await session.compact(customInstructions, COMPACT_TIMEOUT_MILLISECONDS2);
76701
76847
  const r = res && typeof res === "object" ? res : null;
76702
76848
  const summary = typeof r?.summary === "string" ? r.summary : null;
76703
76849
  if (summary !== null && summary.length > 0) {
@@ -77110,7 +77256,7 @@ ${JSON.stringify(stats, null, 2)}`;
77110
77256
  signal.throwIfAborted();
77111
77257
  const parsed = steerRequestSchema.safeParse(params);
77112
77258
  if (!parsed.success) {
77113
- throw RequestError6.invalidParams("Invalid steering request");
77259
+ throw RequestError7.invalidParams("Invalid steering request");
77114
77260
  }
77115
77261
  const session = this.sessions.maybeGet(parsed.data.sessionId);
77116
77262
  if (session === void 0) {
@@ -77151,11 +77297,11 @@ ${JSON.stringify(stats, null, 2)}`;
77151
77297
  }
77152
77298
  async forkSession(params) {
77153
77299
  if (!isAbsolute11(params.cwd)) {
77154
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77300
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77155
77301
  }
77156
77302
  const source = this.findStoredSession(params.sessionId);
77157
77303
  if (!source) {
77158
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77304
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77159
77305
  }
77160
77306
  this.lastSessionCwd = params.cwd;
77161
77307
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -77217,13 +77363,13 @@ ${JSON.stringify(stats, null, 2)}`;
77217
77363
  }
77218
77364
  async loadSession(params) {
77219
77365
  if (!isAbsolute11(params.cwd)) {
77220
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77366
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
77221
77367
  }
77222
77368
  this.sessions.close(params.sessionId);
77223
77369
  this.lastSessionCwd = params.cwd;
77224
77370
  const stored = this.findStoredSession(params.sessionId);
77225
77371
  if (!stored) {
77226
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77372
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
77227
77373
  }
77228
77374
  const enableSkillCommands = getEnableSkillCommands(params.cwd, this.environment);
77229
77375
  const session = await this.restoreSession(params.sessionId, {
@@ -77398,7 +77544,7 @@ ${JSON.stringify(stats, null, 2)}`;
77398
77544
  const session = await this.restoreSession(params.sessionId);
77399
77545
  const mode = params.modeId;
77400
77546
  if (typeof mode !== "string" || mode.length === 0) {
77401
- throw RequestError6.invalidParams("Expected nonempty string modeId");
77547
+ throw RequestError7.invalidParams("Expected nonempty string modeId");
77402
77548
  }
77403
77549
  await session.proc.setThinkingLevel(mode);
77404
77550
  await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
@@ -77409,18 +77555,18 @@ ${JSON.stringify(stats, null, 2)}`;
77409
77555
  const configId = String(params.configId);
77410
77556
  let modelChanged = false;
77411
77557
  if (typeof params.value !== "string") {
77412
- throw RequestError6.invalidParams(`Expected string value for config option: ${configId}`);
77558
+ throw RequestError7.invalidParams(`Expected string value for config option: ${configId}`);
77413
77559
  }
77414
77560
  if (configId === MODEL_CONFIG_ID2) {
77415
77561
  await setSessionModel(session.proc, params.value);
77416
77562
  modelChanged = true;
77417
77563
  } else if (configId === THOUGHT_LEVEL_CONFIG_ID) {
77418
77564
  if (params.value.length === 0) {
77419
- throw RequestError6.invalidParams("Expected nonempty thinking level");
77565
+ throw RequestError7.invalidParams("Expected nonempty thinking level");
77420
77566
  }
77421
77567
  await session.proc.setThinkingLevel(params.value);
77422
77568
  } else {
77423
- throw RequestError6.invalidParams(`Unknown config option: ${configId}`);
77569
+ throw RequestError7.invalidParams(`Unknown config option: ${configId}`);
77424
77570
  }
77425
77571
  const configOptions = await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
77426
77572
  if (modelChanged) {
@@ -77576,7 +77722,7 @@ async function setSessionModel(proc, requestedModelId) {
77576
77722
  }
77577
77723
  }
77578
77724
  if (!provider || !modelId) {
77579
- throw RequestError6.invalidParams(`Unknown modelId: ${requestedModelId}`);
77725
+ throw RequestError7.invalidParams(`Unknown modelId: ${requestedModelId}`);
77580
77726
  }
77581
77727
  await proc.setModel(provider, modelId);
77582
77728
  }
@@ -77758,6 +77904,7 @@ function buildStartupInfo(opts) {
77758
77904
  import { methods as methods4 } from "@agentclientprotocol/sdk";
77759
77905
  function createPiAcpClient(connection, onSessionUpdate) {
77760
77906
  return {
77907
+ requestElicitation: async (request, signal) => await connection.client.request(methods4.client.elicitation.create, request, { cancellationSignal: signal }),
77761
77908
  requestPermission: async (request) => await connection.client.request(methods4.client.session.requestPermission, request),
77762
77909
  sessionUpdate: onSessionUpdate
77763
77910
  };
@@ -78234,7 +78381,7 @@ import {
78234
78381
  methods as methods8,
78235
78382
  ndJsonStream as ndJsonStream2,
78236
78383
  PROTOCOL_VERSION as PROTOCOL_VERSION2,
78237
- RequestError as RequestError7
78384
+ RequestError as RequestError8
78238
78385
  } from "@agentclientprotocol/sdk";
78239
78386
 
78240
78387
  // src/acp/connection/create-client.ts
@@ -78408,7 +78555,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
78408
78555
  if (error62 instanceof AgentProbeError) {
78409
78556
  throw error62;
78410
78557
  }
78411
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78558
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78412
78559
  throw new AgentProbeError("authentication_required", "Agent authentication is required", {
78413
78560
  cause: error62
78414
78561
  });
@@ -78484,7 +78631,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
78484
78631
  if (error62 instanceof AgentProbeError) {
78485
78632
  throw error62;
78486
78633
  }
78487
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78634
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
78488
78635
  throw new AgentProbeError("authentication_required", "Agent authentication is required", { cause: error62 });
78489
78636
  }
78490
78637
  throw new AgentProbeError("acp_incompatible", "ACP adapter initialization failed", { cause: error62 });
@@ -88207,6 +88354,7 @@ var ProjectedSessionEvent = class {
88207
88354
  };
88208
88355
  };
88209
88356
  var SessionEvents = class {
88357
+ #throughPosition = "0";
88210
88358
  #eventIds = [];
88211
88359
  #eventIndexById = /* @__PURE__ */ new Map();
88212
88360
  #cachedItemsByEventId = /* @__PURE__ */ new Map();
@@ -88218,6 +88366,7 @@ var SessionEvents = class {
88218
88366
  }
88219
88367
  apply = (event, resolve20) => {
88220
88368
  const projected = this.#projection.apply(event, resolve20);
88369
+ this.#throughPosition = event.position;
88221
88370
  if (projected.kind === "replace") {
88222
88371
  const current = this.#cachedItemsByEventId.get(projected.replacedEventId);
88223
88372
  if (current === void 0) {
@@ -88231,6 +88380,9 @@ var SessionEvents = class {
88231
88380
  this.#cachedItemsByEventId.set(projected.event.eventId, item);
88232
88381
  this.#eventIds.push(projected.event.eventId);
88233
88382
  };
88383
+ get throughPosition() {
88384
+ return this.#throughPosition;
88385
+ }
88234
88386
  get materializedCount() {
88235
88387
  return this.#cachedItemsByEventId.size;
88236
88388
  }
@@ -88318,6 +88470,7 @@ function eventPage(session, sessionGenerationId, endIndex, eventLimit, read) {
88318
88470
  const startIndex = Math.max(0, endIndex - eventLimit);
88319
88471
  return {
88320
88472
  events: read(startIndex, endIndex),
88473
+ throughPosition: session.throughPosition,
88321
88474
  nextCursor: startIndex === 0 ? null : session.cursorAt(startIndex),
88322
88475
  sessionGenerationId,
88323
88476
  truncated: startIndex > 0
@@ -88652,7 +88805,7 @@ function buildPromptContent(options, capabilities) {
88652
88805
 
88653
88806
  // src/acp/connection/agent-connection.ts
88654
88807
  import { spawn as spawn9 } from "node:child_process";
88655
- import { ndJsonStream as ndJsonStream3, RequestError as RequestError8 } from "@agentclientprotocol/sdk";
88808
+ import { ndJsonStream as ndJsonStream3, RequestError as RequestError9 } from "@agentclientprotocol/sdk";
88656
88809
  function requestErrorMessage(error62) {
88657
88810
  const fallback = truncate2(error62.message, 500);
88658
88811
  if (error62.message !== "Internal error" || !isRecord3(error62.data)) {
@@ -88857,14 +89010,14 @@ var AgentConnection = class {
88857
89010
  ]);
88858
89011
  } catch (error62) {
88859
89012
  signal.throwIfAborted();
88860
- if (error62 instanceof RequestError8 && error62.code === -32e3) {
89013
+ if (error62 instanceof RequestError9 && error62.code === -32e3) {
88861
89014
  throw new AcpExecutionError(
88862
89015
  "Agent authentication is required",
88863
89016
  { cause: error62 },
88864
89017
  "authentication_required"
88865
89018
  );
88866
89019
  }
88867
- if (error62 instanceof RequestError8) {
89020
+ if (error62 instanceof RequestError9) {
88868
89021
  throw new AcpExecutionError(requestErrorMessage(error62), { cause: error62 }, "acp_agent_error");
88869
89022
  }
88870
89023
  throw error62;
@@ -89318,6 +89471,7 @@ function normalizeElicitationFields(requestedSchema) {
89318
89471
  const options = normalizeElicitationOptions(property);
89319
89472
  const normalizedType = type === "string" && options.length > 0 ? "select" : type;
89320
89473
  const defaultValue = property["default"];
89474
+ const metadata = property["_meta"];
89321
89475
  normalized.push({
89322
89476
  defaultBooleanValue: typeof defaultValue === "boolean" ? defaultValue : false,
89323
89477
  defaultNumberValue: typeof defaultValue === "number" && Number.isFinite(defaultValue) ? defaultValue : 0,
@@ -89329,6 +89483,7 @@ function normalizeElicitationFields(requestedSchema) {
89329
89483
  maximumLength: numericString(property["maxLength"] ?? property["maxItems"]),
89330
89484
  minimum: numericString(property["minimum"]),
89331
89485
  minimumLength: numericString(property["minLength"] ?? property["minItems"]),
89486
+ ...normalizedType === "string" && isRecord3(metadata) && metadata["io.mobius/multiline"] === true ? { multiline: true } : {},
89332
89487
  name: truncate2(name2, 128),
89333
89488
  options,
89334
89489
  pattern: truncate2(readString2(property["pattern"]), 500),
@@ -94571,8 +94726,10 @@ async function loadHistoryPage(sessionId, eventLimit, initialCursor, omitReasoni
94571
94726
  let loaded = [];
94572
94727
  let sessionGenerationId = null;
94573
94728
  let sourceNextCursor = null;
94729
+ let throughPosition = null;
94574
94730
  do {
94575
94731
  const replay = await load(eventLimit, cursor);
94732
+ throughPosition ??= replay.throughPosition;
94576
94733
  if (sessionGenerationId !== null && replay.sessionGenerationId !== sessionGenerationId) {
94577
94734
  throw new SessionError("session_reloaded", "The Session reloaded while history was loading");
94578
94735
  }
@@ -94598,7 +94755,7 @@ async function loadHistoryPage(sessionId, eventLimit, initialCursor, omitReasoni
94598
94755
  }
94599
94756
  cursor = sourceNextCursor;
94600
94757
  } while (cursor !== null);
94601
- if (sessionGenerationId === null) {
94758
+ if (sessionGenerationId === null || throughPosition === null) {
94602
94759
  throw new SessionError("session_history_unavailable", "The Session history is unavailable");
94603
94760
  }
94604
94761
  const visible = omitReasoning ? omitAgentReasoning(loaded) : loaded;
@@ -94610,6 +94767,7 @@ async function loadHistoryPage(sessionId, eventLimit, initialCursor, omitReasoni
94610
94767
  nextCursor,
94611
94768
  sessionGenerationId,
94612
94769
  tailCursor: orderedEvents.at(-1)?.event.eventId ?? "",
94770
+ throughPosition,
94613
94771
  truncated: nextCursor !== null,
94614
94772
  version: 2
94615
94773
  },