@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/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),
@@ -22132,6 +22133,9 @@ var sessionEventLogPageSchema = external_exports.object({
22132
22133
  nextCursor: external_exports.string().min(1).max(512).nullable(),
22133
22134
  sessionGenerationId: identifierStringSchema,
22134
22135
  tailCursor: external_exports.string().max(512),
22136
+ // The generation-scoped live position covered by this snapshot, including
22137
+ // coalesced and omitted events. Absent only on older Runtime versions.
22138
+ throughPosition: positionSchema.optional(),
22135
22139
  truncated: external_exports.boolean(),
22136
22140
  version: external_exports.literal(2)
22137
22141
  }).readonly();
@@ -22568,7 +22572,7 @@ import { fetch as fetch3 } from "undici";
22568
22572
  // package.json
22569
22573
  var package_default = {
22570
22574
  name: "@moxt-ai/mobius",
22571
- version: "0.0.290",
22575
+ version: "0.0.292",
22572
22576
  description: "Mobius CLI for pairing and running local AI agent services.",
22573
22577
  type: "module",
22574
22578
  bin: {
@@ -68999,7 +69003,7 @@ import { spawnSync } from "node:child_process";
68999
69003
  import { randomUUID as randomUUID7 } from "node:crypto";
69000
69004
  import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
69001
69005
  import { basename as basename4, dirname as dirname7, isAbsolute as isAbsolute11, join as join17 } from "node:path";
69002
- import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
69006
+ import { RequestError as RequestError7 } from "@agentclientprotocol/sdk";
69003
69007
 
69004
69008
  // src/acp/harness/pi/upstream/dist/pi-rpc/process.js
69005
69009
  import { spawn as spawn4 } from "node:child_process";
@@ -69032,6 +69036,13 @@ var PiRpcSpawnError = class extends Error {
69032
69036
  this.cause = opts?.cause;
69033
69037
  }
69034
69038
  };
69039
+ var PiRpcProcessError = class extends Error {
69040
+ code = "pi_process_exited";
69041
+ constructor(cause) {
69042
+ super("Pi process stopped unexpectedly.", { cause });
69043
+ this.name = "PiRpcProcessError";
69044
+ }
69045
+ };
69035
69046
  var ESC = String.fromCharCode(27);
69036
69047
  var CSI = String.fromCharCode(155);
69037
69048
  var ANSI_ESCAPE_REGEX = new RegExp(`[${ESC}${CSI}][[\\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`, "g");
@@ -69041,6 +69052,10 @@ function stripAnsi(s) {
69041
69052
  var SESSION_STATS_TIMEOUT_MS = 1e3;
69042
69053
  var PiRpcProcess = class _PiRpcProcess {
69043
69054
  child;
69055
+ lifetime = new AbortController();
69056
+ get signal() {
69057
+ return this.lifetime.signal;
69058
+ }
69044
69059
  pending = /* @__PURE__ */ new Map();
69045
69060
  eventHandlers = [];
69046
69061
  preludeLines = [];
@@ -69075,13 +69090,16 @@ var PiRpcProcess = class _PiRpcProcess {
69075
69090
  }
69076
69091
  });
69077
69092
  child.on("exit", (code, signal) => {
69078
- const err = new Error(`pi process exited (code=${code}, signal=${signal})`);
69093
+ const err = new PiRpcProcessError({ code, signal });
69094
+ this.lifetime.abort(err);
69079
69095
  for (const [, p] of this.pending) {
69080
69096
  p.reject(err);
69081
69097
  }
69082
69098
  this.pending.clear();
69083
69099
  });
69084
- child.on("error", (err) => {
69100
+ child.on("error", (cause) => {
69101
+ const err = new PiRpcProcessError(cause);
69102
+ this.lifetime.abort(err);
69085
69103
  for (const [, p] of this.pending) {
69086
69104
  p.reject(err);
69087
69105
  }
@@ -69163,6 +69181,7 @@ var PiRpcProcess = class _PiRpcProcess {
69163
69181
  };
69164
69182
  }
69165
69183
  dispose(signal = "SIGTERM") {
69184
+ this.lifetime.abort();
69166
69185
  if (this.child.killed) {
69167
69186
  return;
69168
69187
  }
@@ -69171,6 +69190,10 @@ var PiRpcProcess = class _PiRpcProcess {
69171
69190
  } catch {
69172
69191
  }
69173
69192
  }
69193
+ terminateAfterFailure(cause) {
69194
+ this.lifetime.abort(new PiRpcProcessError(cause));
69195
+ this.dispose();
69196
+ }
69174
69197
  /**
69175
69198
  * Human-readable stdout lines emitted before RPC NDJSON begins (e.g. Context/Skills/Extensions info).
69176
69199
  * Themes are typically noisy/less useful for ACP, so callers can filter as needed.
@@ -69191,8 +69214,8 @@ var PiRpcProcess = class _PiRpcProcess {
69191
69214
  throw new Error(`pi steer failed: ${res.error ?? JSON.stringify(res.data)}`);
69192
69215
  }
69193
69216
  }
69194
- async abort() {
69195
- const res = await this.request({ type: "abort" });
69217
+ async abort(opts) {
69218
+ const res = await this.request({ type: "abort" }, opts);
69196
69219
  if (!res.success) {
69197
69220
  throw new Error(`pi abort failed: ${res.error ?? JSON.stringify(res.data)}`);
69198
69221
  }
@@ -69889,7 +69912,176 @@ function getQuietStartup(cwd, environment = process.env) {
69889
69912
  import { randomUUID as randomUUID6 } from "node:crypto";
69890
69913
  import { readFileSync as readFileSync7 } from "node:fs";
69891
69914
  import { isAbsolute as isAbsolute10, resolve as resolvePath } from "node:path";
69915
+ import { RequestError as RequestError6 } from "@agentclientprotocol/sdk";
69916
+
69917
+ // src/acp/harness/pi/upstream/dist/acp/extension-questions.js
69892
69918
  import { RequestError as RequestError5 } from "@agentclientprotocol/sdk";
69919
+ var questionBase = {
69920
+ id: external_exports.string().min(1).max(255),
69921
+ title: external_exports.string().max(255),
69922
+ timeout: external_exports.number().int().nonnegative().max(2147483647).optional()
69923
+ };
69924
+ var questionSchema = external_exports.discriminatedUnion("method", [
69925
+ external_exports.object({
69926
+ ...questionBase,
69927
+ method: external_exports.literal("select"),
69928
+ options: external_exports.array(external_exports.string().min(1).max(255)).min(1).max(200).readonly()
69929
+ }),
69930
+ external_exports.object({ ...questionBase, method: external_exports.literal("confirm"), message: external_exports.string().max(2e3) }),
69931
+ external_exports.object({ ...questionBase, method: external_exports.literal("input"), placeholder: external_exports.string().max(500).optional() }),
69932
+ external_exports.object({ ...questionBase, method: external_exports.literal("editor"), prefill: external_exports.string().max(1e4).optional() })
69933
+ ]).readonly();
69934
+ var QUESTION_TIMEOUT_MS = 36e5;
69935
+ var PiQuestionError = class extends RequestError5 {
69936
+ constructor(kind, cause) {
69937
+ const messages = {
69938
+ request_failed: "Pi could not request an answer.",
69939
+ invalid_response: "Pi received an invalid answer.",
69940
+ response_failed: "Pi could not receive the answer."
69941
+ };
69942
+ super(-32603, "Internal error", { code: `pi_question_${kind}`, details: messages[kind] });
69943
+ this.cause = cause;
69944
+ }
69945
+ };
69946
+ function questionField(question) {
69947
+ const title2 = question.title || "Answer";
69948
+ switch (question.method) {
69949
+ case "select":
69950
+ return { type: "string", title: title2, enum: [...question.options] };
69951
+ case "confirm":
69952
+ return {
69953
+ type: "string",
69954
+ title: title2,
69955
+ oneOf: [
69956
+ { const: "yes", title: "Yes" },
69957
+ { const: "no", title: "No" }
69958
+ ]
69959
+ };
69960
+ case "input":
69961
+ return { type: "string", title: title2, description: question.placeholder, maxLength: 1e4 };
69962
+ case "editor":
69963
+ return {
69964
+ type: "string",
69965
+ title: title2,
69966
+ default: question.prefill,
69967
+ maxLength: 1e4,
69968
+ _meta: { "io.mobius/multiline": true }
69969
+ };
69970
+ }
69971
+ }
69972
+ var PiExtensionQuestions = class {
69973
+ sessionId;
69974
+ conn;
69975
+ proc;
69976
+ notify;
69977
+ turn = null;
69978
+ constructor(sessionId, conn, proc, notify) {
69979
+ this.sessionId = sessionId;
69980
+ this.conn = conn;
69981
+ this.proc = proc;
69982
+ this.notify = notify;
69983
+ }
69984
+ startTurn() {
69985
+ this.endTurn();
69986
+ this.turn = { controller: new AbortController(), requestIds: /* @__PURE__ */ new Set() };
69987
+ return this.turn.controller.signal;
69988
+ }
69989
+ endTurn(reason) {
69990
+ this.turn?.controller.abort(reason);
69991
+ this.turn = null;
69992
+ }
69993
+ async handle(event) {
69994
+ if (event.method !== "select" && event.method !== "confirm" && event.method !== "input" && event.method !== "editor") {
69995
+ return;
69996
+ }
69997
+ if (typeof event.id !== "string" || event.id.length === 0 || event.id.length > 255) {
69998
+ return;
69999
+ }
70000
+ const turn = this.turn;
70001
+ if (turn?.requestIds.has(event.id)) {
70002
+ return;
70003
+ }
70004
+ turn?.requestIds.add(event.id);
70005
+ try {
70006
+ await this.answer(event, turn);
70007
+ } catch (cause) {
70008
+ if (!this.proc.signal.aborted) {
70009
+ turn?.controller.abort(new PiQuestionError("response_failed", cause));
70010
+ this.proc.terminateAfterFailure(cause);
70011
+ }
70012
+ }
70013
+ }
70014
+ async answer(event, turn) {
70015
+ const id = typeof event.id === "string" ? event.id : "";
70016
+ const parsed = questionSchema.safeParse(event);
70017
+ if (!parsed.success) {
70018
+ await this.notify("Pi requested a question with unsupported or oversized fields. The question was cancelled without changing its content.");
70019
+ await this.proc.sendExtensionUiResponse({ id, cancelled: true });
70020
+ return;
70021
+ }
70022
+ const question = parsed.data;
70023
+ if (turn === null || turn.controller.signal.aborted) {
70024
+ await this.proc.sendExtensionUiResponse({ id: question.id, cancelled: true });
70025
+ return;
70026
+ }
70027
+ const deadline = new AbortController();
70028
+ const timeout = setTimeout(() => deadline.abort(), question.timeout || QUESTION_TIMEOUT_MS);
70029
+ const signal = AbortSignal.any([turn.controller.signal, deadline.signal]);
70030
+ let response = { id: question.id, cancelled: true };
70031
+ try {
70032
+ const answer = await this.conn.requestElicitation({
70033
+ sessionId: this.sessionId,
70034
+ mode: "form",
70035
+ message: (question.method === "confirm" ? question.message : "") || question.title || "Pi question",
70036
+ requestedSchema: {
70037
+ type: "object",
70038
+ properties: { value: questionField(question) },
70039
+ required: ["value"]
70040
+ }
70041
+ }, signal);
70042
+ const content = answer.action === "accept" ? answer.content : void 0;
70043
+ const value = typeof content === "object" && content !== null ? Reflect.get(content, "value") : void 0;
70044
+ if (!signal.aborted && answer.action === "accept") {
70045
+ if (typeof value !== "string") {
70046
+ throw new PiQuestionError("invalid_response");
70047
+ }
70048
+ switch (question.method) {
70049
+ case "select":
70050
+ if (!question.options.includes(value)) {
70051
+ throw new PiQuestionError("invalid_response");
70052
+ }
70053
+ response = { id: question.id, value };
70054
+ break;
70055
+ case "confirm":
70056
+ if (value !== "yes" && value !== "no") {
70057
+ throw new PiQuestionError("invalid_response");
70058
+ }
70059
+ response = { id: question.id, confirmed: value === "yes" };
70060
+ break;
70061
+ case "input":
70062
+ case "editor":
70063
+ if (value.length > 1e4) {
70064
+ throw new PiQuestionError("invalid_response");
70065
+ }
70066
+ response = { id: question.id, value };
70067
+ break;
70068
+ }
70069
+ } else if (!signal.aborted && answer.action !== "cancel" && answer.action !== "decline") {
70070
+ throw new PiQuestionError("invalid_response");
70071
+ }
70072
+ } catch (cause) {
70073
+ if (!signal.aborted && !(cause instanceof RequestError5 && cause.code === -32800)) {
70074
+ turn.controller.abort(cause instanceof PiQuestionError ? cause : new PiQuestionError("request_failed", cause));
70075
+ }
70076
+ } finally {
70077
+ clearTimeout(timeout);
70078
+ }
70079
+ if (deadline.signal.aborted && signal.reason === deadline.signal.reason) {
70080
+ await this.notify("Pi question timed out; no answer was submitted.");
70081
+ }
70082
+ await this.proc.sendExtensionUiResponse(response);
70083
+ }
70084
+ };
69893
70085
 
69894
70086
  // src/acp/harness/pi/upstream/dist/acp/session-store.js
69895
70087
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "node:fs";
@@ -70158,12 +70350,6 @@ ${stderr}`);
70158
70350
  }
70159
70351
 
70160
70352
  // 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
70353
  function toUsageUpdate(stats) {
70168
70354
  const used = stats?.contextUsage?.tokens;
70169
70355
  const size = stats?.contextUsage?.contextWindow;
@@ -70284,7 +70470,7 @@ var SessionManager = class {
70284
70470
  return;
70285
70471
  }
70286
70472
  try {
70287
- s.proc.dispose?.();
70473
+ s.dispose();
70288
70474
  } catch {
70289
70475
  }
70290
70476
  this.sessions.delete(sessionId);
@@ -70309,7 +70495,7 @@ var SessionManager = class {
70309
70495
  });
70310
70496
  } catch (e) {
70311
70497
  if (e instanceof PiRpcSpawnError) {
70312
- throw RequestError5.internalError({ code: e.code }, e.message);
70498
+ throw RequestError6.internalError({ code: e.code }, e.message);
70313
70499
  }
70314
70500
  throw e;
70315
70501
  }
@@ -70338,7 +70524,7 @@ var SessionManager = class {
70338
70524
  get(sessionId) {
70339
70525
  const s = this.sessions.get(sessionId);
70340
70526
  if (!s) {
70341
- throw RequestError5.invalidParams(`Unknown sessionId: ${sessionId}`);
70527
+ throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
70342
70528
  }
70343
70529
  return s;
70344
70530
  }
@@ -70371,6 +70557,7 @@ var PiAcpSession = class {
70371
70557
  startupInfoSent = false;
70372
70558
  proc;
70373
70559
  conn;
70560
+ extensionQuestions;
70374
70561
  fileCommands;
70375
70562
  // Used to map abort semantics to ACP stopReason.
70376
70563
  // Applies to the currently running turn.
@@ -70425,8 +70612,38 @@ var PiAcpSession = class {
70425
70612
  this.proc = opts.proc;
70426
70613
  this.conn = opts.conn;
70427
70614
  this.fileCommands = opts.fileCommands ?? [];
70615
+ this.extensionQuestions = new PiExtensionQuestions(this.sessionId, this.conn, this.proc, async (text2) => {
70616
+ this.emit({ sessionUpdate: "agent_message_chunk", content: { type: "text", text: text2 } });
70617
+ await this.flushEmits();
70618
+ });
70619
+ this.proc.signal.addEventListener("abort", () => {
70620
+ const cause = this.proc.signal.reason;
70621
+ if (cause instanceof PiRpcProcessError) {
70622
+ const pending = this.pendingTurn;
70623
+ const reason = pending?.questionSignal.reason;
70624
+ const error62 = reason instanceof RequestError6 ? reason : RequestError6.internalError({ code: cause.code, details: cause.message });
70625
+ if (!(reason instanceof RequestError6)) {
70626
+ error62.cause = cause;
70627
+ }
70628
+ this.extensionQuestions.endTurn(error62);
70629
+ this.pendingTurn = null;
70630
+ void this.flushEmits().then(() => {
70631
+ if (this.cancelRequested) {
70632
+ pending?.resolve("cancelled");
70633
+ } else {
70634
+ pending?.reject(error62);
70635
+ }
70636
+ });
70637
+ } else {
70638
+ this.extensionQuestions.endTurn();
70639
+ }
70640
+ }, { once: true });
70428
70641
  this.proc.onEvent((ev2) => this.handlePiEvent(ev2));
70429
70642
  }
70643
+ dispose() {
70644
+ this.extensionQuestions.endTurn();
70645
+ this.proc.dispose();
70646
+ }
70430
70647
  setStartupInfo(text2) {
70431
70648
  this.startupInfo = text2;
70432
70649
  this.startupInfoSent = false;
@@ -70469,6 +70686,31 @@ var PiAcpSession = class {
70469
70686
  });
70470
70687
  return turnPromise;
70471
70688
  }
70689
+ async compact(customInstructions, timeoutMs) {
70690
+ const signal = this.extensionQuestions.startTurn();
70691
+ try {
70692
+ const result = await this.proc.compact(customInstructions, { signal, timeoutMs });
70693
+ if (signal.aborted && signal.reason instanceof RequestError6) {
70694
+ throw signal.reason;
70695
+ }
70696
+ return result;
70697
+ } catch (cause) {
70698
+ if (signal.aborted && signal.reason instanceof RequestError6) {
70699
+ try {
70700
+ await this.proc.abort({ signal: this.proc.signal, timeoutMs });
70701
+ } catch (abortCause) {
70702
+ if (!this.proc.signal.aborted) {
70703
+ this.proc.terminateAfterFailure(abortCause);
70704
+ }
70705
+ }
70706
+ throw signal.reason;
70707
+ }
70708
+ throw cause;
70709
+ } finally {
70710
+ this.extensionQuestions.endTurn();
70711
+ await this.flushEmits();
70712
+ }
70713
+ }
70472
70714
  async steer(message, images, signal) {
70473
70715
  const target = this.pendingTurn;
70474
70716
  if (target === null) {
@@ -70487,6 +70729,7 @@ var PiAcpSession = class {
70487
70729
  }
70488
70730
  async cancel() {
70489
70731
  this.cancelRequested = true;
70732
+ this.extensionQuestions.endTurn();
70490
70733
  if (this.turnQueue.length) {
70491
70734
  const queued = this.turnQueue.splice(0, this.turnQueue.length);
70492
70735
  for (const t of queued) {
@@ -70501,7 +70744,13 @@ var PiAcpSession = class {
70501
70744
  _meta: { piAcp: { queueDepth: 0, running: Boolean(this.pendingTurn) } }
70502
70745
  });
70503
70746
  }
70504
- await this.proc.abort();
70747
+ try {
70748
+ await this.proc.abort();
70749
+ } catch (cause) {
70750
+ if (!this.proc.signal.aborted) {
70751
+ throw cause;
70752
+ }
70753
+ }
70505
70754
  }
70506
70755
  wasCancelRequested() {
70507
70756
  return this.cancelRequested;
@@ -70534,15 +70783,19 @@ var PiAcpSession = class {
70534
70783
  await this.flushEmits();
70535
70784
  }
70536
70785
  async settleTurn() {
70786
+ const questionSignal = this.pendingTurn?.questionSignal;
70787
+ this.extensionQuestions.endTurn();
70537
70788
  await this.publishContextUsage();
70538
70789
  const retryFailure = this.turnRetryFailure;
70539
70790
  this.turnRetryFailure = null;
70540
- if (retryFailure !== null && !this.cancelRequested) {
70791
+ if (questionSignal?.aborted && questionSignal.reason instanceof RequestError6 && !this.cancelRequested) {
70792
+ this.pendingTurn?.reject(questionSignal.reason);
70793
+ } else if (retryFailure !== null && !this.cancelRequested) {
70541
70794
  this.pendingTurn?.reject(
70542
70795
  // The message rides in `data.details` so clients that unwrap the
70543
70796
  // JSON-RPC internal-error envelope (like Mobius) surface the real
70544
70797
  // model error; `code` keeps the failure machine-readable.
70545
- RequestError5.internalError({
70798
+ RequestError6.internalError({
70546
70799
  code: "pi_retries_exhausted",
70547
70800
  details: `Pi retries exhausted: ${retryFailure}`
70548
70801
  })
@@ -70596,14 +70849,16 @@ var PiAcpSession = class {
70596
70849
  this.bashToolCallIds.delete(toolCallId);
70597
70850
  }
70598
70851
  startTurn(t) {
70852
+ const questionSignal = this.extensionQuestions.startTurn();
70599
70853
  this.cancelRequested = false;
70600
70854
  this.turnRetryFailure = null;
70601
- this.pendingTurn = { resolve: t.resolve, reject: t.reject };
70855
+ this.pendingTurn = { questionSignal, resolve: t.resolve, reject: t.reject };
70602
70856
  this.emit({
70603
70857
  sessionUpdate: "session_info_update",
70604
70858
  _meta: { piAcp: { queueDepth: this.turnQueue.length, running: true } }
70605
70859
  });
70606
70860
  this.proc.prompt(t.message, t.images).catch((err) => {
70861
+ this.extensionQuestions.endTurn();
70607
70862
  void this.flushEmits().finally(() => {
70608
70863
  const authErr = maybeAuthRequiredError(err);
70609
70864
  if (authErr) {
@@ -70842,14 +71097,15 @@ var PiAcpSession = class {
70842
71097
  break;
70843
71098
  }
70844
71099
  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(() => {
71100
+ if (ev2.method === "notify") {
71101
+ this.emit({
71102
+ sessionUpdate: "agent_message_chunk",
71103
+ content: { type: "text", text: stringProp(ev2, "message") ?? "Pi notification" },
71104
+ _meta: { piAcp: { notify: { level: stringProp(ev2, "notifyType") ?? "info" } } }
70851
71105
  });
70852
- });
71106
+ break;
71107
+ }
71108
+ void this.extensionQuestions.handle(ev2);
70853
71109
  break;
70854
71110
  }
70855
71111
  case "auto_retry_start": {
@@ -70902,119 +71158,11 @@ var PiAcpSession = class {
70902
71158
  break;
70903
71159
  }
70904
71160
  }
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
71161
  };
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
71162
  function stringProp(source, key) {
71004
71163
  const value = source[key];
71005
71164
  return typeof value === "string" ? value : null;
71006
71165
  }
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
71166
  function toToolKind(toolName) {
71019
71167
  switch (toolName) {
71020
71168
  case "read":
@@ -71241,7 +71389,7 @@ var PiAcpAgent = class {
71241
71389
  const restorePromise = (async () => {
71242
71390
  const stored = this.findStoredSession(sessionId);
71243
71391
  if (!stored) {
71244
- throw RequestError6.invalidParams(`Unknown sessionId: ${sessionId}`);
71392
+ throw RequestError7.invalidParams(`Unknown sessionId: ${sessionId}`);
71245
71393
  }
71246
71394
  const cwd = opts?.cwd ?? stored.cwd;
71247
71395
  let proc;
@@ -71254,7 +71402,7 @@ var PiAcpAgent = class {
71254
71402
  });
71255
71403
  } catch (e) {
71256
71404
  if (e?.name === "PiRpcSpawnError") {
71257
- throw RequestError6.internalError({ code: e?.code }, String(e?.message ?? e));
71405
+ throw RequestError7.internalError({ code: e?.code }, String(e?.message ?? e));
71258
71406
  }
71259
71407
  throw e;
71260
71408
  }
@@ -71314,7 +71462,7 @@ var PiAcpAgent = class {
71314
71462
  }
71315
71463
  async newSession(params) {
71316
71464
  if (!isAbsolute11(params.cwd)) {
71317
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71465
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71318
71466
  }
71319
71467
  this.lastSessionCwd = params.cwd;
71320
71468
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -71353,16 +71501,16 @@ var PiAcpAgent = class {
71353
71501
  }
71354
71502
  if (availableModelsErr) {
71355
71503
  this.cleanupFailedNewSession(session.sessionId, state);
71356
- throw RequestError6.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
71504
+ throw RequestError7.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
71357
71505
  }
71358
71506
  const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
71359
71507
  if (rawModelsCount === 0) {
71360
71508
  this.cleanupFailedNewSession(session.sessionId, state);
71361
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71509
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71362
71510
  }
71363
71511
  if (stateErr && maybeAuthRequiredError(stateErr)) {
71364
71512
  this.cleanupFailedNewSession(session.sessionId, state);
71365
- throw RequestError6.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71513
+ throw RequestError7.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
71366
71514
  }
71367
71515
  let configuration;
71368
71516
  try {
@@ -71372,7 +71520,7 @@ var PiAcpAgent = class {
71372
71520
  configuration = await getSessionConfiguration(session.proc, { state, availableModels });
71373
71521
  } catch (err) {
71374
71522
  this.cleanupFailedNewSession(session.sessionId, state);
71375
- throw maybeAuthRequiredError(err) ?? RequestError6.internalError({}, String(err?.message ?? err));
71523
+ throw maybeAuthRequiredError(err) ?? RequestError7.internalError({}, String(err?.message ?? err));
71376
71524
  }
71377
71525
  const { configOptions, models, modes } = configuration;
71378
71526
  const quietStartup = getQuietStartup(params.cwd, this.environment);
@@ -71457,9 +71605,7 @@ var PiAcpAgent = class {
71457
71605
  }
71458
71606
  });
71459
71607
  try {
71460
- const res = await session.proc.compact(customInstructions, {
71461
- timeoutMs: COMPACT_TIMEOUT_MILLISECONDS2
71462
- });
71608
+ const res = await session.compact(customInstructions, COMPACT_TIMEOUT_MILLISECONDS2);
71463
71609
  const r = res && typeof res === "object" ? res : null;
71464
71610
  const summary = typeof r?.summary === "string" ? r.summary : null;
71465
71611
  if (summary !== null && summary.length > 0) {
@@ -71872,7 +72018,7 @@ ${JSON.stringify(stats, null, 2)}`;
71872
72018
  signal.throwIfAborted();
71873
72019
  const parsed = steerRequestSchema.safeParse(params);
71874
72020
  if (!parsed.success) {
71875
- throw RequestError6.invalidParams("Invalid steering request");
72021
+ throw RequestError7.invalidParams("Invalid steering request");
71876
72022
  }
71877
72023
  const session = this.sessions.maybeGet(parsed.data.sessionId);
71878
72024
  if (session === void 0) {
@@ -71913,11 +72059,11 @@ ${JSON.stringify(stats, null, 2)}`;
71913
72059
  }
71914
72060
  async forkSession(params) {
71915
72061
  if (!isAbsolute11(params.cwd)) {
71916
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
72062
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71917
72063
  }
71918
72064
  const source = this.findStoredSession(params.sessionId);
71919
72065
  if (!source) {
71920
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
72066
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
71921
72067
  }
71922
72068
  this.lastSessionCwd = params.cwd;
71923
72069
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -71979,13 +72125,13 @@ ${JSON.stringify(stats, null, 2)}`;
71979
72125
  }
71980
72126
  async loadSession(params) {
71981
72127
  if (!isAbsolute11(params.cwd)) {
71982
- throw RequestError6.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
72128
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
71983
72129
  }
71984
72130
  this.sessions.close(params.sessionId);
71985
72131
  this.lastSessionCwd = params.cwd;
71986
72132
  const stored = this.findStoredSession(params.sessionId);
71987
72133
  if (!stored) {
71988
- throw RequestError6.invalidParams(`Unknown sessionId: ${params.sessionId}`);
72134
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
71989
72135
  }
71990
72136
  const enableSkillCommands = getEnableSkillCommands(params.cwd, this.environment);
71991
72137
  const session = await this.restoreSession(params.sessionId, {
@@ -72160,7 +72306,7 @@ ${JSON.stringify(stats, null, 2)}`;
72160
72306
  const session = await this.restoreSession(params.sessionId);
72161
72307
  const mode = params.modeId;
72162
72308
  if (typeof mode !== "string" || mode.length === 0) {
72163
- throw RequestError6.invalidParams("Expected nonempty string modeId");
72309
+ throw RequestError7.invalidParams("Expected nonempty string modeId");
72164
72310
  }
72165
72311
  await session.proc.setThinkingLevel(mode);
72166
72312
  await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
@@ -72171,18 +72317,18 @@ ${JSON.stringify(stats, null, 2)}`;
72171
72317
  const configId = String(params.configId);
72172
72318
  let modelChanged = false;
72173
72319
  if (typeof params.value !== "string") {
72174
- throw RequestError6.invalidParams(`Expected string value for config option: ${configId}`);
72320
+ throw RequestError7.invalidParams(`Expected string value for config option: ${configId}`);
72175
72321
  }
72176
72322
  if (configId === MODEL_CONFIG_ID2) {
72177
72323
  await setSessionModel(session.proc, params.value);
72178
72324
  modelChanged = true;
72179
72325
  } else if (configId === THOUGHT_LEVEL_CONFIG_ID) {
72180
72326
  if (params.value.length === 0) {
72181
- throw RequestError6.invalidParams("Expected nonempty thinking level");
72327
+ throw RequestError7.invalidParams("Expected nonempty thinking level");
72182
72328
  }
72183
72329
  await session.proc.setThinkingLevel(params.value);
72184
72330
  } else {
72185
- throw RequestError6.invalidParams(`Unknown config option: ${configId}`);
72331
+ throw RequestError7.invalidParams(`Unknown config option: ${configId}`);
72186
72332
  }
72187
72333
  const configOptions = await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
72188
72334
  if (modelChanged) {
@@ -72338,7 +72484,7 @@ async function setSessionModel(proc, requestedModelId) {
72338
72484
  }
72339
72485
  }
72340
72486
  if (!provider || !modelId) {
72341
- throw RequestError6.invalidParams(`Unknown modelId: ${requestedModelId}`);
72487
+ throw RequestError7.invalidParams(`Unknown modelId: ${requestedModelId}`);
72342
72488
  }
72343
72489
  await proc.setModel(provider, modelId);
72344
72490
  }
@@ -72520,6 +72666,7 @@ function buildStartupInfo(opts) {
72520
72666
  import { methods as methods4 } from "@agentclientprotocol/sdk";
72521
72667
  function createPiAcpClient(connection, onSessionUpdate) {
72522
72668
  return {
72669
+ requestElicitation: async (request, signal) => await connection.client.request(methods4.client.elicitation.create, request, { cancellationSignal: signal }),
72523
72670
  requestPermission: async (request) => await connection.client.request(methods4.client.session.requestPermission, request),
72524
72671
  sessionUpdate: onSessionUpdate
72525
72672
  };
@@ -72843,7 +72990,7 @@ import {
72843
72990
  methods as methods8,
72844
72991
  ndJsonStream as ndJsonStream2,
72845
72992
  PROTOCOL_VERSION as PROTOCOL_VERSION2,
72846
- RequestError as RequestError7
72993
+ RequestError as RequestError8
72847
72994
  } from "@agentclientprotocol/sdk";
72848
72995
 
72849
72996
  // src/acp/connection/create-client.ts
@@ -73017,7 +73164,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
73017
73164
  if (error62 instanceof AgentProbeError) {
73018
73165
  throw error62;
73019
73166
  }
73020
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73167
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73021
73168
  throw new AgentProbeError("authentication_required", "Agent authentication is required", {
73022
73169
  cause: error62
73023
73170
  });
@@ -73093,7 +73240,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
73093
73240
  if (error62 instanceof AgentProbeError) {
73094
73241
  throw error62;
73095
73242
  }
73096
- if (error62 instanceof RequestError7 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73243
+ if (error62 instanceof RequestError8 && error62.code === -32e3 && specification.authenticationCommand().length > 0) {
73097
73244
  throw new AgentProbeError("authentication_required", "Agent authentication is required", { cause: error62 });
73098
73245
  }
73099
73246
  throw new AgentProbeError("acp_incompatible", "ACP adapter initialization failed", { cause: error62 });
@@ -81504,6 +81651,7 @@ var ProjectedSessionEvent = class {
81504
81651
  };
81505
81652
  };
81506
81653
  var SessionEvents = class {
81654
+ #throughPosition = "0";
81507
81655
  #eventIds = [];
81508
81656
  #eventIndexById = /* @__PURE__ */ new Map();
81509
81657
  #cachedItemsByEventId = /* @__PURE__ */ new Map();
@@ -81515,6 +81663,7 @@ var SessionEvents = class {
81515
81663
  }
81516
81664
  apply = (event, resolve18) => {
81517
81665
  const projected = this.#projection.apply(event, resolve18);
81666
+ this.#throughPosition = event.position;
81518
81667
  if (projected.kind === "replace") {
81519
81668
  const current = this.#cachedItemsByEventId.get(projected.replacedEventId);
81520
81669
  if (current === void 0) {
@@ -81528,6 +81677,9 @@ var SessionEvents = class {
81528
81677
  this.#cachedItemsByEventId.set(projected.event.eventId, item);
81529
81678
  this.#eventIds.push(projected.event.eventId);
81530
81679
  };
81680
+ get throughPosition() {
81681
+ return this.#throughPosition;
81682
+ }
81531
81683
  get materializedCount() {
81532
81684
  return this.#cachedItemsByEventId.size;
81533
81685
  }
@@ -81615,6 +81767,7 @@ function eventPage(session, sessionGenerationId, endIndex, eventLimit, read) {
81615
81767
  const startIndex = Math.max(0, endIndex - eventLimit);
81616
81768
  return {
81617
81769
  events: read(startIndex, endIndex),
81770
+ throughPosition: session.throughPosition,
81618
81771
  nextCursor: startIndex === 0 ? null : session.cursorAt(startIndex),
81619
81772
  sessionGenerationId,
81620
81773
  truncated: startIndex > 0
@@ -81949,7 +82102,7 @@ function buildPromptContent(options, capabilities) {
81949
82102
 
81950
82103
  // src/acp/connection/agent-connection.ts
81951
82104
  import { spawn as spawn7 } from "node:child_process";
81952
- import { ndJsonStream as ndJsonStream3, RequestError as RequestError8 } from "@agentclientprotocol/sdk";
82105
+ import { ndJsonStream as ndJsonStream3, RequestError as RequestError9 } from "@agentclientprotocol/sdk";
81953
82106
  function requestErrorMessage(error62) {
81954
82107
  const fallback = truncate(error62.message, 500);
81955
82108
  if (error62.message !== "Internal error" || !isRecord(error62.data)) {
@@ -82154,14 +82307,14 @@ var AgentConnection = class {
82154
82307
  ]);
82155
82308
  } catch (error62) {
82156
82309
  signal.throwIfAborted();
82157
- if (error62 instanceof RequestError8 && error62.code === -32e3) {
82310
+ if (error62 instanceof RequestError9 && error62.code === -32e3) {
82158
82311
  throw new AcpExecutionError(
82159
82312
  "Agent authentication is required",
82160
82313
  { cause: error62 },
82161
82314
  "authentication_required"
82162
82315
  );
82163
82316
  }
82164
- if (error62 instanceof RequestError8) {
82317
+ if (error62 instanceof RequestError9) {
82165
82318
  throw new AcpExecutionError(requestErrorMessage(error62), { cause: error62 }, "acp_agent_error");
82166
82319
  }
82167
82320
  throw error62;
@@ -82615,6 +82768,7 @@ function normalizeElicitationFields(requestedSchema) {
82615
82768
  const options = normalizeElicitationOptions(property);
82616
82769
  const normalizedType = type === "string" && options.length > 0 ? "select" : type;
82617
82770
  const defaultValue = property["default"];
82771
+ const metadata = property["_meta"];
82618
82772
  normalized.push({
82619
82773
  defaultBooleanValue: typeof defaultValue === "boolean" ? defaultValue : false,
82620
82774
  defaultNumberValue: typeof defaultValue === "number" && Number.isFinite(defaultValue) ? defaultValue : 0,
@@ -82626,6 +82780,7 @@ function normalizeElicitationFields(requestedSchema) {
82626
82780
  maximumLength: numericString(property["maxLength"] ?? property["maxItems"]),
82627
82781
  minimum: numericString(property["minimum"]),
82628
82782
  minimumLength: numericString(property["minLength"] ?? property["minItems"]),
82783
+ ...normalizedType === "string" && isRecord(metadata) && metadata["io.mobius/multiline"] === true ? { multiline: true } : {},
82629
82784
  name: truncate(name2, 128),
82630
82785
  options,
82631
82786
  pattern: truncate(readString(property["pattern"]), 500),
@@ -87956,8 +88111,10 @@ async function loadHistoryPage(sessionId, eventLimit, initialCursor, omitReasoni
87956
88111
  let loaded = [];
87957
88112
  let sessionGenerationId = null;
87958
88113
  let sourceNextCursor = null;
88114
+ let throughPosition = null;
87959
88115
  do {
87960
88116
  const replay = await load(eventLimit, cursor);
88117
+ throughPosition ??= replay.throughPosition;
87961
88118
  if (sessionGenerationId !== null && replay.sessionGenerationId !== sessionGenerationId) {
87962
88119
  throw new SessionError("session_reloaded", "The Session reloaded while history was loading");
87963
88120
  }
@@ -87983,7 +88140,7 @@ async function loadHistoryPage(sessionId, eventLimit, initialCursor, omitReasoni
87983
88140
  }
87984
88141
  cursor = sourceNextCursor;
87985
88142
  } while (cursor !== null);
87986
- if (sessionGenerationId === null) {
88143
+ if (sessionGenerationId === null || throughPosition === null) {
87987
88144
  throw new SessionError("session_history_unavailable", "The Session history is unavailable");
87988
88145
  }
87989
88146
  const visible = omitReasoning ? omitAgentReasoning(loaded) : loaded;
@@ -87995,6 +88152,7 @@ async function loadHistoryPage(sessionId, eventLimit, initialCursor, omitReasoni
87995
88152
  nextCursor,
87996
88153
  sessionGenerationId,
87997
88154
  tailCursor: orderedEvents.at(-1)?.event.eventId ?? "",
88155
+ throughPosition,
87998
88156
  truncated: nextCursor !== null,
87999
88157
  version: 2
88000
88158
  },