@moxt-ai/mobius 0.0.355 → 0.0.357

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.
Files changed (59) hide show
  1. package/LICENSE +202 -0
  2. package/dist/cli.js +388 -187
  3. package/dist/cli.js.map +4 -4
  4. package/dist/engine/acp/adapter/codex/adapter.d.ts.map +1 -1
  5. package/dist/engine/acp/adapter/codex/adapter.js +6 -4
  6. package/dist/engine/acp/adapter/codex/adapter.js.map +1 -1
  7. package/dist/engine/acp/adapter/session-events.d.ts.map +1 -1
  8. package/dist/engine/acp/adapter/session-events.js +57 -56
  9. package/dist/engine/acp/adapter/session-events.js.map +1 -1
  10. package/dist/engine/acp/content/content-externalizer.d.ts +8 -2
  11. package/dist/engine/acp/content/content-externalizer.d.ts.map +1 -1
  12. package/dist/engine/acp/content/content-externalizer.js +23 -6
  13. package/dist/engine/acp/content/content-externalizer.js.map +1 -1
  14. package/dist/engine/acp/harness/codex/history.d.ts.map +1 -1
  15. package/dist/engine/acp/harness/codex/history.js +25 -23
  16. package/dist/engine/acp/harness/codex/history.js.map +1 -1
  17. package/dist/engine/agent/runtime-agents.d.ts +1 -1
  18. package/dist/engine/agent/runtime-agents.d.ts.map +1 -1
  19. package/dist/engine/agent/runtime-agents.js +2 -2
  20. package/dist/engine/agent/runtime-agents.js.map +1 -1
  21. package/dist/engine/runtime/command/registry/interactions.d.ts.map +1 -1
  22. package/dist/engine/runtime/command/registry/interactions.js +7 -1
  23. package/dist/engine/runtime/command/registry/interactions.js.map +1 -1
  24. package/dist/engine/runtime/run.d.ts.map +1 -1
  25. package/dist/engine/runtime/run.js +1 -1
  26. package/dist/engine/runtime/run.js.map +1 -1
  27. package/dist/engine/session/content/external-content-store.d.ts +4 -0
  28. package/dist/engine/session/content/external-content-store.d.ts.map +1 -1
  29. package/dist/engine/session/content/external-content-store.js +31 -1
  30. package/dist/engine/session/content/external-content-store.js.map +1 -1
  31. package/dist/engine/session/live/agent-session-restore.d.ts +1 -1
  32. package/dist/engine/session/live/agent-session-restore.d.ts.map +1 -1
  33. package/dist/engine/session/live/agent-session.d.ts +1 -1
  34. package/dist/engine/session/live/agent-session.d.ts.map +1 -1
  35. package/dist/engine/session/live/agent-session.js +23 -2
  36. package/dist/engine/session/live/agent-session.js.map +1 -1
  37. package/dist/engine/session/live/session-configurations.d.ts +1 -1
  38. package/dist/engine/session/live/session-configurations.d.ts.map +1 -1
  39. package/dist/engine/session/live/session-configurations.js +1 -1
  40. package/dist/engine/session/live/session-configurations.js.map +1 -1
  41. package/dist/engine/session/live/session-restore.d.ts.map +1 -1
  42. package/dist/engine/session/live/session-restore.js +7 -1
  43. package/dist/engine/session/live/session-restore.js.map +1 -1
  44. package/dist/engine/session/native/native-session-browser.d.ts.map +1 -1
  45. package/dist/engine/session/native/native-session-browser.js +1 -0
  46. package/dist/engine/session/native/native-session-browser.js.map +1 -1
  47. package/dist/engine/session/prompt/execute-prompt.d.ts.map +1 -1
  48. package/dist/engine/session/prompt/execute-prompt.js +1 -0
  49. package/dist/engine/session/prompt/execute-prompt.js.map +1 -1
  50. package/dist/engine/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  51. package/dist/engine/session/prompt/prompt-lifecycle.js +6 -1
  52. package/dist/engine/session/prompt/prompt-lifecycle.js.map +1 -1
  53. package/dist/engine/session/turn/execution-receipts.d.ts +2 -0
  54. package/dist/engine/session/turn/execution-receipts.d.ts.map +1 -1
  55. package/dist/engine/session/turn/execution-receipts.js +18 -1
  56. package/dist/engine/session/turn/execution-receipts.js.map +1 -1
  57. package/dist/engine.js +375 -186
  58. package/dist/engine.js.map +4 -4
  59. package/package.json +4 -4
package/dist/engine.js CHANGED
@@ -20720,6 +20720,7 @@ var lifecycleMetadataSchema = external_exports.discriminatedUnion("kind", [
20720
20720
  external_exports.object({
20721
20721
  ...lifecycleBase,
20722
20722
  kind: external_exports.literal("prompt.completed"),
20723
+ execution: external_exports.literal("not-started").optional(),
20723
20724
  stopReason: shortString
20724
20725
  }).readonly(),
20725
20726
  external_exports.object({
@@ -21441,7 +21442,9 @@ var nativeSessionResultSchema = external_exports.discriminatedUnion("action", [
21441
21442
  // unrecognized delivery from a newer peer reads as absent instead of failing the snapshot.
21442
21443
  command: commandDeliverySchema.optional().catch(void 0),
21443
21444
  // Absent from older Runtimes and while the last started command is still running.
21444
- lastTurn: lastTurnOutcomeSchema.optional().catch(void 0)
21445
+ lastTurn: lastTurnOutcomeSchema.optional().catch(void 0),
21446
+ // Runtime-owned support; older Relays strip this field before exposing the snapshot.
21447
+ queuedCancellation: external_exports.literal("before-start").optional().catch(void 0)
21445
21448
  }).readonly(),
21446
21449
  external_exports.object({
21447
21450
  ...resultEnvelope,
@@ -70230,40 +70233,42 @@ async function replayCodexHistory(client2, threadId, emit, signal) {
70230
70233
  continue;
70231
70234
  }
70232
70235
  if (item.type === "userMessage") {
70233
- const text2 = item.content.flatMap((content) => {
70236
+ const contents = item.content.flatMap((content) => {
70234
70237
  if (typeof content !== "object" || content === null) {
70235
70238
  return [];
70236
70239
  }
70237
70240
  const type = Reflect.get(content, "type");
70238
- const text3 = Reflect.get(content, "text");
70241
+ const text2 = Reflect.get(content, "text");
70239
70242
  const url2 = Reflect.get(content, "url");
70240
70243
  if (type === "image" && typeof url2 === "string") {
70241
70244
  return [`[@image](${url2})`];
70242
70245
  }
70243
- return type === "text" && typeof text3 === "string" ? [text3] : [];
70244
- }).join("");
70245
- const history = await embedLocalHistoryImages(
70246
- { status: "available", truncated: false, messages: [{ role: "user", text: text2 }] },
70247
- deadline
70248
- );
70249
- if (history.status !== "available") {
70250
- continue;
70251
- }
70252
- const message = history.messages[0];
70253
- for (const image of message?.images ?? []) {
70254
- await emit({
70255
- content: { type: "image", data: image.data, mimeType: image.mimeType },
70256
- messageId: item.id,
70257
- sessionUpdate: "user_message_chunk"
70258
- });
70259
- }
70260
- const restoredText = message?.text ?? "";
70261
- if (restoredText.length > 0) {
70262
- await emit({
70263
- content: { text: restoredText, type: "text" },
70264
- messageId: item.id,
70265
- sessionUpdate: "user_message_chunk"
70266
- });
70246
+ return type === "text" && typeof text2 === "string" ? [text2] : [];
70247
+ });
70248
+ for (const text2 of contents) {
70249
+ const history = await embedLocalHistoryImages(
70250
+ { status: "available", truncated: false, messages: [{ role: "user", text: text2 }] },
70251
+ deadline
70252
+ );
70253
+ if (history.status !== "available") {
70254
+ continue;
70255
+ }
70256
+ const message = history.messages[0];
70257
+ for (const image of message?.images ?? []) {
70258
+ await emit({
70259
+ content: { type: "image", data: image.data, mimeType: image.mimeType },
70260
+ messageId: item.id,
70261
+ sessionUpdate: "user_message_chunk"
70262
+ });
70263
+ }
70264
+ const restoredText = message?.text ?? "";
70265
+ if (restoredText.length > 0) {
70266
+ await emit({
70267
+ content: { text: restoredText, type: "text" },
70268
+ messageId: item.id,
70269
+ sessionUpdate: "user_message_chunk"
70270
+ });
70271
+ }
70267
70272
  }
70268
70273
  } else if (item.type === "agentMessage") {
70269
70274
  await emit(agentMessageUpdate(item.id, item.text));
@@ -71070,9 +71075,9 @@ import {
71070
71075
  // src/engine/acp/harness/pi/upstream/dist/acp/agent.js
71071
71076
  import { spawnSync } from "node:child_process";
71072
71077
  import { randomUUID as randomUUID11 } from "node:crypto";
71073
- import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
71078
+ import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync9, realpathSync as realpathSync3, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
71074
71079
  import { basename as basename5, dirname as dirname8, isAbsolute as isAbsolute12, join as join20 } from "node:path";
71075
- import { RequestError as RequestError10 } from "@agentclientprotocol/sdk";
71080
+ import { RequestError as RequestError11 } from "@agentclientprotocol/sdk";
71076
71081
 
71077
71082
  // src/engine/acp/harness/pi/upstream/dist/pi-rpc/process.js
71078
71083
  import { spawn as spawn5 } from "node:child_process";
@@ -71585,8 +71590,62 @@ function toAvailableCommandsFromPiGetCommands(data, opts) {
71585
71590
  return { commands: out2, raw: commandsRaw };
71586
71591
  }
71587
71592
 
71593
+ // src/engine/acp/harness/pi/upstream/dist/acp/pi-history.js
71594
+ import { readFileSync as readFileSync3 } from "node:fs";
71595
+ import { RequestError as RequestError8 } from "@agentclientprotocol/sdk";
71596
+ var entrySchema = external_exports.object({
71597
+ type: external_exports.string(),
71598
+ id: external_exports.string(),
71599
+ parentId: external_exports.string().nullable()
71600
+ }).passthrough();
71601
+ var messageSchema = external_exports.object({
71602
+ role: external_exports.enum(["user", "assistant", "toolResult"]),
71603
+ content: external_exports.unknown()
71604
+ }).passthrough();
71605
+ function readPiHistory(sessionFile) {
71606
+ try {
71607
+ const entries = /* @__PURE__ */ new Map();
71608
+ let leaf = null;
71609
+ for (const line of readFileSync3(sessionFile, "utf8").split(/\r?\n/u)) {
71610
+ if (!line.trim()) {
71611
+ continue;
71612
+ }
71613
+ const value = JSON.parse(line);
71614
+ if (external_exports.object({ type: external_exports.literal("session") }).safeParse(value).success) {
71615
+ continue;
71616
+ }
71617
+ const entry = entrySchema.parse(value);
71618
+ entries.set(entry.id, entry);
71619
+ leaf = entry.id;
71620
+ }
71621
+ const history = [];
71622
+ const visited = /* @__PURE__ */ new Set();
71623
+ while (leaf !== null) {
71624
+ const entry = entries.get(leaf);
71625
+ if (entry === void 0 || visited.has(leaf)) {
71626
+ throw new Error("Invalid Pi history branch");
71627
+ }
71628
+ visited.add(leaf);
71629
+ if (entry.type === "message") {
71630
+ const { role } = external_exports.object({ role: external_exports.string() }).parse(entry.message);
71631
+ if (role === "user" || role === "assistant" || role === "toolResult") {
71632
+ history.push({ type: "message", message: messageSchema.parse(entry.message) });
71633
+ }
71634
+ } else if (entry.type === "compaction") {
71635
+ history.push({ type: "compaction", id: entry.id, summary: external_exports.string().parse(entry.summary) });
71636
+ }
71637
+ leaf = entry.parentId;
71638
+ }
71639
+ return history.reverse();
71640
+ } catch (cause) {
71641
+ const error62 = RequestError8.internalError({ code: "PI_HISTORY_READ_FAILED" }, "Unable to read Pi history");
71642
+ error62.cause = cause;
71643
+ throw error62;
71644
+ }
71645
+ }
71646
+
71588
71647
  // src/engine/acp/harness/pi/upstream/dist/acp/pi-sessions.js
71589
- import { closeSync as closeSync2, existsSync as existsSync4, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync3, readSync as readSync2, statSync as statSync2 } from "node:fs";
71648
+ import { closeSync as closeSync2, existsSync as existsSync4, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync4, readSync as readSync2, statSync as statSync2 } from "node:fs";
71590
71649
  import { isAbsolute as isAbsolute10, join as join17, resolve as resolve10 } from "node:path";
71591
71650
  var DEFAULT_TAIL_BYTES = 256 * 1024;
71592
71651
  var DEFAULT_HEAD_BYTES = 64 * 1024;
@@ -71600,7 +71659,7 @@ function readSessionDirFromSettings(agentDir) {
71600
71659
  if (!existsSync4(settingsPath)) {
71601
71660
  return null;
71602
71661
  }
71603
- const raw = readFileSync3(settingsPath, "utf8");
71662
+ const raw = readFileSync4(settingsPath, "utf8");
71604
71663
  const data = JSON.parse(raw);
71605
71664
  if (!data || typeof data !== "object" || Array.isArray(data)) {
71606
71665
  return null;
@@ -71816,7 +71875,7 @@ function pickUpdatedAtFromTail(tail) {
71816
71875
  }
71817
71876
  function pickFallbackTitleFromHead(path7) {
71818
71877
  try {
71819
- const raw = readFileSync3(path7, { encoding: "utf8" });
71878
+ const raw = readFileSync4(path7, { encoding: "utf8" });
71820
71879
  const lines = raw.split(/\r?\n/);
71821
71880
  for (const line0 of lines) {
71822
71881
  const line = line0.trim();
@@ -71912,7 +71971,7 @@ function findPiSession(sessionId, environment = process.env) {
71912
71971
  }
71913
71972
 
71914
71973
  // src/engine/acp/harness/pi/upstream/dist/acp/pi-settings.js
71915
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
71974
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
71916
71975
  import { join as join18, resolve as resolve11 } from "node:path";
71917
71976
  function isObject2(x) {
71918
71977
  return Boolean(x) && typeof x === "object" && !Array.isArray(x);
@@ -71934,7 +71993,7 @@ function readJsonFile2(path7) {
71934
71993
  if (!existsSync5(path7)) {
71935
71994
  return {};
71936
71995
  }
71937
- const raw = readFileSync4(path7, "utf-8");
71996
+ const raw = readFileSync5(path7, "utf-8");
71938
71997
  const data = JSON.parse(raw);
71939
71998
  return isObject2(data) ? data : {};
71940
71999
  } catch {
@@ -71979,12 +72038,12 @@ function getQuietStartup(cwd, environment = process.env) {
71979
72038
 
71980
72039
  // src/engine/acp/harness/pi/upstream/dist/acp/session.js
71981
72040
  import { randomUUID as randomUUID10 } from "node:crypto";
71982
- import { readFileSync as readFileSync7 } from "node:fs";
72041
+ import { readFileSync as readFileSync8 } from "node:fs";
71983
72042
  import { isAbsolute as isAbsolute11, resolve as resolvePath } from "node:path";
71984
- import { RequestError as RequestError9 } from "@agentclientprotocol/sdk";
72043
+ import { RequestError as RequestError10 } from "@agentclientprotocol/sdk";
71985
72044
 
71986
72045
  // src/engine/acp/harness/pi/upstream/dist/acp/extension-questions.js
71987
- import { RequestError as RequestError8 } from "@agentclientprotocol/sdk";
72046
+ import { RequestError as RequestError9 } from "@agentclientprotocol/sdk";
71988
72047
  var questionBase = {
71989
72048
  id: external_exports.string().min(1).max(255),
71990
72049
  title: external_exports.string().max(255),
@@ -72001,7 +72060,7 @@ var questionSchema = external_exports.discriminatedUnion("method", [
72001
72060
  external_exports.object({ ...questionBase, method: external_exports.literal("editor"), prefill: external_exports.string().max(1e4).optional() })
72002
72061
  ]).readonly();
72003
72062
  var QUESTION_TIMEOUT_MS = 36e5;
72004
- var PiQuestionError = class extends RequestError8 {
72063
+ var PiQuestionError = class extends RequestError9 {
72005
72064
  constructor(kind, cause) {
72006
72065
  const messages = {
72007
72066
  request_failed: "Pi could not request an answer.",
@@ -72139,7 +72198,7 @@ var PiExtensionQuestions = class {
72139
72198
  throw new PiQuestionError("invalid_response");
72140
72199
  }
72141
72200
  } catch (cause) {
72142
- if (!signal.aborted && !(cause instanceof RequestError8 && cause.code === -32800)) {
72201
+ if (!signal.aborted && !(cause instanceof RequestError9 && cause.code === -32800)) {
72143
72202
  turn.controller.abort(cause instanceof PiQuestionError ? cause : new PiQuestionError("request_failed", cause));
72144
72203
  }
72145
72204
  } finally {
@@ -72153,14 +72212,14 @@ var PiExtensionQuestions = class {
72153
72212
  };
72154
72213
 
72155
72214
  // src/engine/acp/harness/pi/upstream/dist/acp/session-store.js
72156
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "node:fs";
72215
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync } from "node:fs";
72157
72216
  import { dirname as dirname7 } from "node:path";
72158
72217
  function ensureParentDir(path7) {
72159
72218
  mkdirSync2(dirname7(path7), { recursive: true });
72160
72219
  }
72161
72220
  function loadFile(path7) {
72162
72221
  try {
72163
- const raw = readFileSync5(path7, "utf-8");
72222
+ const raw = readFileSync6(path7, "utf-8");
72164
72223
  const parsed = JSON.parse(raw);
72165
72224
  if (parsed?.version !== 1 || typeof parsed.sessions !== "object" || !parsed.sessions) {
72166
72225
  return { version: 1, sessions: {} };
@@ -72205,7 +72264,7 @@ var SessionStore = class {
72205
72264
  };
72206
72265
 
72207
72266
  // src/engine/acp/harness/pi/upstream/dist/acp/slash-commands.js
72208
- import { existsSync as existsSync6, readdirSync as readdirSync5, readFileSync as readFileSync6 } from "node:fs";
72267
+ import { existsSync as existsSync6, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
72209
72268
  import { join as join19, resolve as resolve12 } from "node:path";
72210
72269
  function parseFrontmatter(content) {
72211
72270
  const frontmatter = {};
@@ -72244,7 +72303,7 @@ function loadCommandsFromDir(dir, source, subdir = "") {
72244
72303
  continue;
72245
72304
  }
72246
72305
  try {
72247
- const rawContent = readFileSync6(fullPath, "utf-8");
72306
+ const rawContent = readFileSync7(fullPath, "utf-8");
72248
72307
  const { frontmatter, content } = parseFrontmatter(rawContent);
72249
72308
  const name2 = entry.name.slice(0, -3);
72250
72309
  const sourceStr = source === "user" ? subdir ? `(user:${subdir})` : "(user)" : subdir ? `(project:${subdir})` : "(project)";
@@ -72419,6 +72478,16 @@ ${stderr}`);
72419
72478
  }
72420
72479
 
72421
72480
  // src/engine/acp/harness/pi/upstream/dist/acp/session.js
72481
+ var nativeUserMessageSchema = external_exports.object({
72482
+ role: external_exports.literal("user"),
72483
+ content: external_exports.union([
72484
+ external_exports.string().transform((text2) => [{ type: "text", text: text2 }]),
72485
+ external_exports.array(external_exports.discriminatedUnion("type", [
72486
+ external_exports.object({ type: external_exports.literal("text"), text: external_exports.string() }),
72487
+ external_exports.object({ type: external_exports.literal("image"), data: external_exports.string(), mimeType: external_exports.string() })
72488
+ ]))
72489
+ ])
72490
+ });
72422
72491
  function toUsageUpdate(stats) {
72423
72492
  const used = stats?.contextUsage?.tokens;
72424
72493
  const size = stats?.contextUsage?.contextWindow;
@@ -72564,7 +72633,7 @@ var SessionManager = class {
72564
72633
  });
72565
72634
  } catch (e) {
72566
72635
  if (e instanceof PiRpcSpawnError) {
72567
- throw RequestError9.internalError({ code: e.code }, e.message);
72636
+ throw RequestError10.internalError({ code: e.code }, e.message);
72568
72637
  }
72569
72638
  throw e;
72570
72639
  }
@@ -72593,7 +72662,7 @@ var SessionManager = class {
72593
72662
  get(sessionId) {
72594
72663
  const s = this.sessions.get(sessionId);
72595
72664
  if (!s) {
72596
- throw RequestError9.invalidParams(`Unknown sessionId: ${sessionId}`);
72665
+ throw RequestError10.invalidParams(`Unknown sessionId: ${sessionId}`);
72597
72666
  }
72598
72667
  return s;
72599
72668
  }
@@ -72693,8 +72762,8 @@ var PiAcpSession = class {
72693
72762
  if (cause instanceof PiRpcProcessError) {
72694
72763
  const pending = this.pendingTurn;
72695
72764
  const reason = pending?.questionSignal.reason;
72696
- const error62 = reason instanceof RequestError9 ? reason : RequestError9.internalError({ code: cause.code, details: cause.message });
72697
- if (!(reason instanceof RequestError9)) {
72765
+ const error62 = reason instanceof RequestError10 ? reason : RequestError10.internalError({ code: cause.code, details: cause.message });
72766
+ if (!(reason instanceof RequestError10)) {
72698
72767
  error62.cause = cause;
72699
72768
  }
72700
72769
  this.extensionQuestions.endTurn(error62);
@@ -72762,12 +72831,12 @@ var PiAcpSession = class {
72762
72831
  const signal = this.extensionQuestions.startTurn();
72763
72832
  try {
72764
72833
  const result = await this.proc.compact(customInstructions, { signal, timeoutMs });
72765
- if (signal.aborted && signal.reason instanceof RequestError9) {
72834
+ if (signal.aborted && signal.reason instanceof RequestError10) {
72766
72835
  throw signal.reason;
72767
72836
  }
72768
72837
  return result;
72769
72838
  } catch (cause) {
72770
- if (signal.aborted && signal.reason instanceof RequestError9) {
72839
+ if (signal.aborted && signal.reason instanceof RequestError10) {
72771
72840
  try {
72772
72841
  await this.proc.abort({ signal: this.proc.signal, timeoutMs });
72773
72842
  } catch (abortCause) {
@@ -72860,14 +72929,14 @@ var PiAcpSession = class {
72860
72929
  await this.publishContextUsage();
72861
72930
  const retryFailure = this.turnRetryFailure;
72862
72931
  this.turnRetryFailure = null;
72863
- if (questionSignal?.aborted && questionSignal.reason instanceof RequestError9 && !this.cancelRequested) {
72932
+ if (questionSignal?.aborted && questionSignal.reason instanceof RequestError10 && !this.cancelRequested) {
72864
72933
  this.pendingTurn?.reject(questionSignal.reason);
72865
72934
  } else if (retryFailure !== null && !this.cancelRequested) {
72866
72935
  this.pendingTurn?.reject(
72867
72936
  // The message rides in `data.details` so clients that unwrap the
72868
72937
  // JSON-RPC internal-error envelope (like Mobius) surface the real
72869
72938
  // model error; `code` keeps the failure machine-readable.
72870
- RequestError9.internalError({
72939
+ RequestError10.internalError({
72871
72940
  code: "pi_retries_exhausted",
72872
72941
  details: `Pi retries exhausted: ${retryFailure}`
72873
72942
  })
@@ -72951,6 +73020,17 @@ var PiAcpSession = class {
72951
73020
  handlePiEvent(ev2) {
72952
73021
  const type = String(ev2.type ?? "");
72953
73022
  switch (type) {
73023
+ case "message_start": {
73024
+ const message = nativeUserMessageSchema.safeParse(ev2.message);
73025
+ if (!message.success) {
73026
+ break;
73027
+ }
73028
+ this.lastEmit = this.lastEmit.then(() => this.conn.startUserMessage());
73029
+ for (const content of message.data.content) {
73030
+ this.emit({ sessionUpdate: "user_message_chunk", content });
73031
+ }
73032
+ break;
73033
+ }
72954
73034
  case "message_update": {
72955
73035
  const ame2 = ev2.assistantMessageEvent;
72956
73036
  if (this.turnRetryFailure !== null && (ame2?.type === "text_delta" || ame2?.type === "thinking_delta" || ame2?.type === "toolcall_start")) {
@@ -73057,7 +73137,7 @@ var PiAcpSession = class {
73057
73137
  if (p) {
73058
73138
  try {
73059
73139
  const abs = isAbsolute11(p) ? p : resolvePath(this.cwd, p);
73060
- snapshotOldText = readFileSync7(abs, "utf8");
73140
+ snapshotOldText = readFileSync8(abs, "utf8");
73061
73141
  this.fileSnapshots.set(toolCallId, { path: p, oldText: snapshotOldText });
73062
73142
  if (toolName === "edit") {
73063
73143
  for (const needle of getEditOldTexts(args)) {
@@ -73140,7 +73220,7 @@ var PiAcpSession = class {
73140
73220
  if (!isError && snapshot) {
73141
73221
  try {
73142
73222
  const abs = isAbsolute11(snapshot.path) ? snapshot.path : resolvePath(this.cwd, snapshot.path);
73143
- const newText = readFileSync7(abs, "utf8");
73223
+ const newText = readFileSync8(abs, "utf8");
73144
73224
  if (snapshot.oldText === null || newText !== snapshot.oldText) {
73145
73225
  hasStructuredDiff = true;
73146
73226
  content = [
@@ -73250,14 +73330,21 @@ function toToolKind(toolName) {
73250
73330
  }
73251
73331
 
73252
73332
  // src/engine/acp/harness/pi/upstream/dist/acp/translate/pi-messages.js
73253
- function normalizePiMessageText(content) {
73333
+ var userContentSchema = external_exports.discriminatedUnion("type", [
73334
+ external_exports.object({ type: external_exports.literal("text"), text: external_exports.string() }),
73335
+ external_exports.object({ type: external_exports.literal("image"), data: external_exports.string(), mimeType: external_exports.string() })
73336
+ ]);
73337
+ function piUserMessageContent(content) {
73254
73338
  if (typeof content === "string") {
73255
- return content;
73339
+ return content.length === 0 ? [] : [{ type: "text", text: content }];
73256
73340
  }
73257
73341
  if (!Array.isArray(content)) {
73258
- return "";
73342
+ return [];
73259
73343
  }
73260
- return content.map((c) => c?.type === "text" && typeof c.text === "string" ? c.text : "").filter(Boolean).join("");
73344
+ return content.flatMap((value) => {
73345
+ const parsed = userContentSchema.safeParse(value);
73346
+ return parsed.success ? [parsed.data] : [];
73347
+ });
73261
73348
  }
73262
73349
  function normalizePiAssistantText(content) {
73263
73350
  if (!Array.isArray(content)) {
@@ -73462,7 +73549,7 @@ var PiAcpAgent = class {
73462
73549
  const restorePromise = (async () => {
73463
73550
  const stored = this.findStoredSession(sessionId);
73464
73551
  if (!stored) {
73465
- throw RequestError10.invalidParams(`Unknown sessionId: ${sessionId}`);
73552
+ throw RequestError11.invalidParams(`Unknown sessionId: ${sessionId}`);
73466
73553
  }
73467
73554
  const cwd = opts?.cwd ?? stored.cwd;
73468
73555
  let proc;
@@ -73475,7 +73562,7 @@ var PiAcpAgent = class {
73475
73562
  });
73476
73563
  } catch (e) {
73477
73564
  if (e?.name === "PiRpcSpawnError") {
73478
- throw RequestError10.internalError({ code: e?.code }, String(e?.message ?? e));
73565
+ throw RequestError11.internalError({ code: e?.code }, String(e?.message ?? e));
73479
73566
  }
73480
73567
  throw e;
73481
73568
  }
@@ -73535,7 +73622,7 @@ var PiAcpAgent = class {
73535
73622
  }
73536
73623
  async newSession(params) {
73537
73624
  if (!isAbsolute12(params.cwd)) {
73538
- throw RequestError10.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
73625
+ throw RequestError11.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
73539
73626
  }
73540
73627
  this.lastSessionCwd = params.cwd;
73541
73628
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -73574,16 +73661,16 @@ var PiAcpAgent = class {
73574
73661
  }
73575
73662
  if (availableModelsErr) {
73576
73663
  this.cleanupFailedNewSession(session.sessionId, state);
73577
- throw RequestError10.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
73664
+ throw RequestError11.internalError({}, String(availableModelsErr?.message ?? availableModelsErr));
73578
73665
  }
73579
73666
  const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
73580
73667
  if (rawModelsCount === 0) {
73581
73668
  this.cleanupFailedNewSession(session.sessionId, state);
73582
- throw RequestError10.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
73669
+ throw RequestError11.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
73583
73670
  }
73584
73671
  if (stateErr && maybeAuthRequiredError(stateErr)) {
73585
73672
  this.cleanupFailedNewSession(session.sessionId, state);
73586
- throw RequestError10.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
73673
+ throw RequestError11.authRequired({ authMethods: getAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
73587
73674
  }
73588
73675
  let configuration;
73589
73676
  try {
@@ -73593,7 +73680,7 @@ var PiAcpAgent = class {
73593
73680
  configuration = await getSessionConfiguration(session.proc, { state, availableModels });
73594
73681
  } catch (err) {
73595
73682
  this.cleanupFailedNewSession(session.sessionId, state);
73596
- throw maybeAuthRequiredError(err) ?? RequestError10.internalError({}, String(err?.message ?? err));
73683
+ throw maybeAuthRequiredError(err) ?? RequestError11.internalError({}, String(err?.message ?? err));
73597
73684
  }
73598
73685
  const { configOptions, models, modes } = configuration;
73599
73686
  const quietStartup = getQuietStartup(params.cwd, this.environment);
@@ -73673,16 +73760,6 @@ var PiAcpAgent = class {
73673
73760
  }
73674
73761
  async promptSubmission(params, submission) {
73675
73762
  const session = await this.restoreSession(params.sessionId);
73676
- this.conn.startUserMessage();
73677
- for (const content of params.prompt) {
73678
- if (submission.aborted) {
73679
- return { stopReason: "cancelled" };
73680
- }
73681
- await this.conn.sessionUpdate({
73682
- sessionId: params.sessionId,
73683
- update: { sessionUpdate: "user_message_chunk", content }
73684
- });
73685
- }
73686
73763
  if (submission.aborted) {
73687
73764
  return { stopReason: "cancelled" };
73688
73765
  }
@@ -73693,6 +73770,9 @@ var PiAcpAgent = class {
73693
73770
  const cmd = space === -1 ? trimmed.slice(1) : trimmed.slice(1, space);
73694
73771
  const argsString = space === -1 ? "" : trimmed.slice(space + 1);
73695
73772
  const args = parseCommandArgs(argsString);
73773
+ if (builtinAvailableCommands().some((command) => command.name === cmd)) {
73774
+ this.conn.startUserMessage();
73775
+ }
73696
73776
  if (cmd === "compact") {
73697
73777
  const customInstructions = args.join(" ").trim() || void 0;
73698
73778
  const compactionId = randomUUID11();
@@ -73954,7 +74034,7 @@ ${JSON.stringify(stats, null, 2)}`;
73954
74034
  }
73955
74035
  let text2 = "";
73956
74036
  try {
73957
- text2 = readFileSync8(changelogPath, "utf-8");
74037
+ text2 = readFileSync9(changelogPath, "utf-8");
73958
74038
  } catch (e) {
73959
74039
  await this.conn.sessionUpdate({
73960
74040
  sessionId: session.sessionId,
@@ -73998,7 +74078,7 @@ ${JSON.stringify(stats, null, 2)}`;
73998
74078
  return { stopReason: "end_turn" };
73999
74079
  }
74000
74080
  try {
74001
- const raw = readFileSync8(sessionFile, "utf-8");
74081
+ const raw = readFileSync9(sessionFile, "utf-8");
74002
74082
  if (raw.trim().length === 0) {
74003
74083
  await this.conn.sessionUpdate({
74004
74084
  sessionId: session.sessionId,
@@ -74121,7 +74201,7 @@ ${JSON.stringify(stats, null, 2)}`;
74121
74201
  signal.throwIfAborted();
74122
74202
  const parsed = steerRequestSchema.safeParse(params);
74123
74203
  if (!parsed.success) {
74124
- throw RequestError10.invalidParams("Invalid steering request");
74204
+ throw RequestError11.invalidParams("Invalid steering request");
74125
74205
  }
74126
74206
  const session = this.sessions.maybeGet(parsed.data.sessionId);
74127
74207
  if (session === void 0) {
@@ -74133,14 +74213,6 @@ ${JSON.stringify(stats, null, 2)}`;
74133
74213
  case "idle":
74134
74214
  return { outcome: "promptRequired", reason: "noRunningTurn" };
74135
74215
  case "injected": {
74136
- this.conn.startUserMessage();
74137
- for (const content of parsed.data.prompt) {
74138
- signal.throwIfAborted();
74139
- await this.conn.sessionUpdate({
74140
- sessionId: parsed.data.sessionId,
74141
- update: { sessionUpdate: "user_message_chunk", content }
74142
- });
74143
- }
74144
74216
  return { outcome: "injected" };
74145
74217
  }
74146
74218
  }
@@ -74174,11 +74246,11 @@ ${JSON.stringify(stats, null, 2)}`;
74174
74246
  }
74175
74247
  async forkSession(params) {
74176
74248
  if (!isAbsolute12(params.cwd)) {
74177
- throw RequestError10.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
74249
+ throw RequestError11.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
74178
74250
  }
74179
74251
  const source = this.findStoredSession(params.sessionId);
74180
74252
  if (!source) {
74181
- throw RequestError10.invalidParams(`Unknown sessionId: ${params.sessionId}`);
74253
+ throw RequestError11.invalidParams(`Unknown sessionId: ${params.sessionId}`);
74182
74254
  }
74183
74255
  this.lastSessionCwd = params.cwd;
74184
74256
  const fileCommands = loadSlashCommands(params.cwd, this.environment);
@@ -74240,13 +74312,13 @@ ${JSON.stringify(stats, null, 2)}`;
74240
74312
  }
74241
74313
  async resumeSession(params) {
74242
74314
  if (!isAbsolute12(params.cwd)) {
74243
- throw RequestError10.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
74315
+ throw RequestError11.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
74244
74316
  }
74245
74317
  this.sessions.close(params.sessionId);
74246
74318
  this.lastSessionCwd = params.cwd;
74247
74319
  const stored = this.findStoredSession(params.sessionId);
74248
74320
  if (!stored) {
74249
- throw RequestError10.invalidParams(`Unknown sessionId: ${params.sessionId}`);
74321
+ throw RequestError11.invalidParams(`Unknown sessionId: ${params.sessionId}`);
74250
74322
  }
74251
74323
  const session = await this.restoreSession(params.sessionId, {
74252
74324
  cwd: params.cwd,
@@ -74311,13 +74383,13 @@ ${JSON.stringify(stats, null, 2)}`;
74311
74383
  }
74312
74384
  async loadSession(params) {
74313
74385
  if (!isAbsolute12(params.cwd)) {
74314
- throw RequestError10.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
74386
+ throw RequestError11.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
74315
74387
  }
74316
74388
  this.sessions.close(params.sessionId);
74317
74389
  this.lastSessionCwd = params.cwd;
74318
74390
  const stored = this.findStoredSession(params.sessionId);
74319
74391
  if (!stored) {
74320
- throw RequestError10.invalidParams(`Unknown sessionId: ${params.sessionId}`);
74392
+ throw RequestError11.invalidParams(`Unknown sessionId: ${params.sessionId}`);
74321
74393
  }
74322
74394
  const enableSkillCommands = getEnableSkillCommands(params.cwd, this.environment);
74323
74395
  const session = await this.restoreSession(params.sessionId, {
@@ -74340,18 +74412,38 @@ ${JSON.stringify(stats, null, 2)}`;
74340
74412
  cwd: params.cwd,
74341
74413
  sessionFile: stored.sessionFile
74342
74414
  });
74343
- const data = await proc.getMessages();
74344
- const messages = Array.isArray(data?.messages) ? data.messages : [];
74345
- for (const m of messages) {
74415
+ for (const entry of readPiHistory(stored.sessionFile)) {
74416
+ if (entry.type === "compaction") {
74417
+ await this.conn.sessionUpdate({
74418
+ sessionId: session.sessionId,
74419
+ update: {
74420
+ sessionUpdate: "compaction_update",
74421
+ compactionId: entry.id,
74422
+ status: "completed"
74423
+ }
74424
+ });
74425
+ if (entry.summary) {
74426
+ await this.conn.sessionUpdate({
74427
+ sessionId: session.sessionId,
74428
+ update: {
74429
+ sessionUpdate: "compaction_summary_chunk",
74430
+ compactionId: entry.id,
74431
+ content: { type: "text", text: entry.summary }
74432
+ }
74433
+ });
74434
+ }
74435
+ continue;
74436
+ }
74437
+ const m = entry.message;
74346
74438
  const role = String(m?.role ?? "");
74347
74439
  if (role === "user") {
74348
- const text2 = normalizePiMessageText(m?.content);
74349
- if (text2) {
74440
+ this.conn.startUserMessage();
74441
+ for (const content of piUserMessageContent(m.content)) {
74350
74442
  await this.conn.sessionUpdate({
74351
74443
  sessionId: session.sessionId,
74352
74444
  update: {
74353
74445
  sessionUpdate: "user_message_chunk",
74354
- content: { type: "text", text: text2 }
74446
+ content
74355
74447
  }
74356
74448
  });
74357
74449
  }
@@ -74492,7 +74584,7 @@ ${JSON.stringify(stats, null, 2)}`;
74492
74584
  const session = await this.restoreSession(params.sessionId);
74493
74585
  const mode = params.modeId;
74494
74586
  if (typeof mode !== "string" || mode.length === 0) {
74495
- throw RequestError10.invalidParams("Expected nonempty string modeId");
74587
+ throw RequestError11.invalidParams("Expected nonempty string modeId");
74496
74588
  }
74497
74589
  await session.proc.setThinkingLevel(mode);
74498
74590
  await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
@@ -74503,18 +74595,18 @@ ${JSON.stringify(stats, null, 2)}`;
74503
74595
  const configId = String(params.configId);
74504
74596
  let modelChanged = false;
74505
74597
  if (typeof params.value !== "string") {
74506
- throw RequestError10.invalidParams(`Expected string value for config option: ${configId}`);
74598
+ throw RequestError11.invalidParams(`Expected string value for config option: ${configId}`);
74507
74599
  }
74508
74600
  if (configId === MODEL_CONFIG_ID2) {
74509
74601
  await setSessionModel(session.proc, params.value);
74510
74602
  modelChanged = true;
74511
74603
  } else if (configId === THOUGHT_LEVEL_CONFIG_ID) {
74512
74604
  if (params.value.length === 0) {
74513
- throw RequestError10.invalidParams("Expected nonempty thinking level");
74605
+ throw RequestError11.invalidParams("Expected nonempty thinking level");
74514
74606
  }
74515
74607
  await session.proc.setThinkingLevel(params.value);
74516
74608
  } else {
74517
- throw RequestError10.invalidParams(`Unknown config option: ${configId}`);
74609
+ throw RequestError11.invalidParams(`Unknown config option: ${configId}`);
74518
74610
  }
74519
74611
  const configOptions = await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
74520
74612
  if (modelChanged) {
@@ -74670,7 +74762,7 @@ async function setSessionModel(proc, requestedModelId) {
74670
74762
  }
74671
74763
  }
74672
74764
  if (!provider || !modelId) {
74673
- throw RequestError10.invalidParams(`Unknown modelId: ${requestedModelId}`);
74765
+ throw RequestError11.invalidParams(`Unknown modelId: ${requestedModelId}`);
74674
74766
  }
74675
74767
  await proc.setModel(provider, modelId);
74676
74768
  }
@@ -74824,7 +74916,7 @@ function buildStartupInfo(opts) {
74824
74916
  ];
74825
74917
  for (const settingsPath of settingsPaths) {
74826
74918
  try {
74827
- const settings = JSON.parse(readFileSync8(settingsPath, "utf-8"));
74919
+ const settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
74828
74920
  const pkgs = Array.isArray(settings?.packages) ? settings.packages : [];
74829
74921
  for (const pkg of pkgs) {
74830
74922
  const s = String(pkg);
@@ -76180,63 +76272,64 @@ var AdapterSessionConnection = class {
76180
76272
  const externalized = await externalizer.update(options.sessionId(), notification.update, signal);
76181
76273
  signal.throwIfAborted();
76182
76274
  const update = externalized.agent;
76183
- const eventUpdate = externalized.event;
76184
- const backgroundTask = update._meta?.["mobius.backgroundTask"];
76185
- const payload = decodeAcpSessionUpdate(
76186
- JSON.stringify(
76187
- normalizeEvent(
76188
- (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") && backgroundTask !== void 0 && typeof eventUpdate === "object" && eventUpdate !== null ? { ...eventUpdate, backgroundTask } : eventUpdate
76275
+ for (const eventUpdate of externalized.events) {
76276
+ const backgroundTask = update._meta?.["mobius.backgroundTask"];
76277
+ const payload = decodeAcpSessionUpdate(
76278
+ JSON.stringify(
76279
+ normalizeEvent(
76280
+ (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") && backgroundTask !== void 0 && typeof eventUpdate === "object" && eventUpdate !== null ? { ...eventUpdate, backgroundTask } : eventUpdate
76281
+ )
76189
76282
  )
76190
- )
76191
- );
76192
- let event;
76193
- switch (update.sessionUpdate) {
76194
- case "available_commands_update":
76195
- event = {
76196
- kind: "commands",
76197
- payload,
76198
- commands: update.availableCommands.slice(0, 500).map((command) => ({
76199
- description: truncate(command.description, 500),
76200
- inputHint: truncate(command.input?.hint ?? "", 255),
76201
- name: truncate(command.name, 128)
76202
- })).filter((command) => command.name.length > 0)
76203
- };
76204
- break;
76205
- case "config_option_update":
76206
- event = {
76207
- kind: "configuration",
76208
- payload,
76209
- configOptions: protocol.normalizeUpdate(options.configOptions(), update.configOptions)
76210
- };
76211
- break;
76212
- case "current_mode_update":
76213
- event = { kind: "mode", payload, modeId: update.currentModeId };
76214
- break;
76215
- case "session_info_update": {
76216
- const changes = [];
76217
- if (Object.hasOwn(update, "title")) {
76218
- changes.push({ kind: "title", value: truncate(update.title ?? "", 500) });
76219
- }
76220
- if (Object.hasOwn(update, "updatedAt")) {
76221
- changes.push({ kind: "updated-at", value: truncate(update.updatedAt ?? "", 128) });
76283
+ );
76284
+ let event;
76285
+ switch (update.sessionUpdate) {
76286
+ case "available_commands_update":
76287
+ event = {
76288
+ kind: "commands",
76289
+ payload,
76290
+ commands: update.availableCommands.slice(0, 500).map((command) => ({
76291
+ description: truncate(command.description, 500),
76292
+ inputHint: truncate(command.input?.hint ?? "", 255),
76293
+ name: truncate(command.name, 128)
76294
+ })).filter((command) => command.name.length > 0)
76295
+ };
76296
+ break;
76297
+ case "config_option_update":
76298
+ event = {
76299
+ kind: "configuration",
76300
+ payload,
76301
+ configOptions: protocol.normalizeUpdate(options.configOptions(), update.configOptions)
76302
+ };
76303
+ break;
76304
+ case "current_mode_update":
76305
+ event = { kind: "mode", payload, modeId: update.currentModeId };
76306
+ break;
76307
+ case "session_info_update": {
76308
+ const changes = [];
76309
+ if (Object.hasOwn(update, "title")) {
76310
+ changes.push({ kind: "title", value: truncate(update.title ?? "", 500) });
76311
+ }
76312
+ if (Object.hasOwn(update, "updatedAt")) {
76313
+ changes.push({ kind: "updated-at", value: truncate(update.updatedAt ?? "", 128) });
76314
+ }
76315
+ event = { kind: "info", payload, changes };
76316
+ break;
76222
76317
  }
76223
- event = { kind: "info", payload, changes };
76224
- break;
76318
+ case "user_message_chunk":
76319
+ case "agent_message_chunk":
76320
+ case "agent_thought_chunk":
76321
+ case "tool_call":
76322
+ case "tool_call_update":
76323
+ case "plan":
76324
+ case "plan_update":
76325
+ case "plan_removed":
76326
+ event = { kind: "content", payload };
76327
+ break;
76328
+ default:
76329
+ event = { kind: "other", payload };
76225
76330
  }
76226
- case "user_message_chunk":
76227
- case "agent_message_chunk":
76228
- case "agent_thought_chunk":
76229
- case "tool_call":
76230
- case "tool_call_update":
76231
- case "plan":
76232
- case "plan_update":
76233
- case "plan_removed":
76234
- event = { kind: "content", payload };
76235
- break;
76236
- default:
76237
- event = { kind: "other", payload };
76331
+ await options.onEvent(event);
76238
76332
  }
76239
- await options.onEvent(event);
76240
76333
  });
76241
76334
  this.events = this.#updates;
76242
76335
  const client2 = {
@@ -83002,10 +83095,12 @@ var CodexAdapter = class {
83002
83095
  request.nativeSessionId,
83003
83096
  async (update) => {
83004
83097
  const externalized = await this.#contentExternalizer.update(sessionId, update, signal);
83005
- events.push({
83006
- type: "acp",
83007
- payload: decodeAcpSessionUpdate(JSON.stringify(fitCodexToolEvent(externalized.event)))
83008
- });
83098
+ for (const event of externalized.events) {
83099
+ events.push({
83100
+ type: "acp",
83101
+ payload: decodeAcpSessionUpdate(JSON.stringify(fitCodexToolEvent(event)))
83102
+ });
83103
+ }
83009
83104
  },
83010
83105
  signal
83011
83106
  );
@@ -83126,8 +83221,18 @@ function isRecord7(value) {
83126
83221
  }
83127
83222
  var AcpContentExternalizer = class {
83128
83223
  #store;
83129
- constructor(store) {
83224
+ #linkedResources;
83225
+ constructor(store, linkedResources = () => []) {
83130
83226
  this.#store = store;
83227
+ this.#linkedResources = linkedResources;
83228
+ }
83229
+ #resourceLinks(text2) {
83230
+ return this.#linkedResources(text2).map((resource) => ({
83231
+ type: "resource_link",
83232
+ uri: resource.uri,
83233
+ name: resource.name,
83234
+ mimeType: resource.mediaType
83235
+ }));
83131
83236
  }
83132
83237
  async #content(sessionId, content, signal) {
83133
83238
  switch (content.type) {
@@ -83279,7 +83384,7 @@ var AcpContentExternalizer = class {
83279
83384
  const hasContent = update.content !== void 0 && update.content !== null || agentContent.length > 0;
83280
83385
  return {
83281
83386
  agent: { ...update, ...meta3, ...output2, ...hasContent ? { content: agentContent } : {} },
83282
- event: { ...update, ...meta3, ...output2, ...hasContent ? { content: eventContent } : {} }
83387
+ events: [{ ...update, ...meta3, ...output2, ...hasContent ? { content: eventContent } : {} }]
83283
83388
  };
83284
83389
  }
83285
83390
  update = async (sessionId, update, signal) => {
@@ -83291,7 +83396,12 @@ var AcpContentExternalizer = class {
83291
83396
  const content = await this.#content(sessionId, update.content, signal);
83292
83397
  return {
83293
83398
  agent: { ...update, content: content.agent },
83294
- event: { ...update, content: content.event }
83399
+ events: [
83400
+ { ...update, content: content.event },
83401
+ // Keep native text intact. Only add cards for exact file URLs whose
83402
+ // original upload metadata is still known; arbitrary context stays text.
83403
+ ...update.sessionUpdate === "user_message_chunk" && update.content.type === "text" ? this.#resourceLinks(update.content.text).map((content2) => ({ ...update, content: content2 })) : []
83404
+ ]
83295
83405
  };
83296
83406
  }
83297
83407
  case "tool_call":
@@ -83299,14 +83409,14 @@ var AcpContentExternalizer = class {
83299
83409
  return await this.#toolUpdate(sessionId, update, signal);
83300
83410
  case "compaction_update": {
83301
83411
  if (update.summary === void 0 || update.summary === null) {
83302
- return { agent: update, event: update };
83412
+ return { agent: update, events: [update] };
83303
83413
  }
83304
83414
  const content = await Promise.all(
83305
83415
  update.summary.map(async (item) => await this.#content(sessionId, item, signal))
83306
83416
  );
83307
83417
  return {
83308
83418
  agent: { ...update, summary: content.map((item) => item.agent) },
83309
- event: { ...update, summary: content.map((item) => item.event) }
83419
+ events: [{ ...update, summary: content.map((item) => item.event) }]
83310
83420
  };
83311
83421
  }
83312
83422
  case "plan":
@@ -83317,16 +83427,16 @@ var AcpContentExternalizer = class {
83317
83427
  case "config_option_update":
83318
83428
  case "session_info_update":
83319
83429
  case "usage_update":
83320
- return { agent: update, event: update };
83430
+ return { agent: update, events: [update] };
83321
83431
  }
83322
83432
  };
83323
83433
  };
83324
83434
 
83325
83435
  // src/engine/agent/runtime-agents.ts
83326
- function createRuntimeAdapterRegistry(adapters, processes, externalize) {
83436
+ function createRuntimeAdapterRegistry(adapters, processes, externalize, linkedResources) {
83327
83437
  return new AdapterRegistry(adapters, {
83328
83438
  processes,
83329
- contentExternalizer: new AcpContentExternalizer(externalize)
83439
+ contentExternalizer: new AcpContentExternalizer(externalize, linkedResources)
83330
83440
  });
83331
83441
  }
83332
83442
  var RuntimeAgentDependencies = class {
@@ -83392,7 +83502,7 @@ function describeEnvironmentProxyRoute(target) {
83392
83502
  import { join as join26 } from "node:path";
83393
83503
 
83394
83504
  // src/engine/storage/logs/bounded-log.ts
83395
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
83505
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync10, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
83396
83506
  import { join as join24 } from "node:path";
83397
83507
  var BoundedLineLog = class {
83398
83508
  #path;
@@ -83433,7 +83543,7 @@ var BoundedLineLog = class {
83433
83543
  appendFileSync2(this.#path, encoded, { encoding: "utf8", mode: 384 });
83434
83544
  return;
83435
83545
  }
83436
- const lines = `${existingBytes === 0 ? "" : readFileSync9(this.#path, "utf8")}${encoded}`.split("\n").filter((candidate) => candidate.length > 0);
83546
+ const lines = `${existingBytes === 0 ? "" : readFileSync10(this.#path, "utf8")}${encoded}`.split("\n").filter((candidate) => candidate.length > 0);
83437
83547
  let totalBytes = lines.reduce((total, candidate) => total + Buffer.byteLength(candidate) + 1, 0);
83438
83548
  while (totalBytes > this.#maximumBytes && lines.length > 0) {
83439
83549
  const oldest = lines.shift();
@@ -83452,7 +83562,7 @@ var BoundedLineLog = class {
83452
83562
  };
83453
83563
 
83454
83564
  // src/engine/storage/logs/log-files.ts
83455
- import { readFileSync as readFileSync10 } from "node:fs";
83565
+ import { readFileSync as readFileSync11 } from "node:fs";
83456
83566
  import { join as join25 } from "node:path";
83457
83567
  var DAEMON_LOG_FILENAME = "daemon.log";
83458
83568
  var WORKER_LIFECYCLE_LOG_FILENAME = "worker-lifecycle.log";
@@ -83471,7 +83581,7 @@ function processLogPaths(dataDirectory) {
83471
83581
  }
83472
83582
  function readLogText(path7) {
83473
83583
  try {
83474
- return readFileSync10(path7, "utf8");
83584
+ return readFileSync11(path7, "utf8");
83475
83585
  } catch {
83476
83586
  return null;
83477
83587
  }
@@ -85112,7 +85222,7 @@ var SessionConfigurations = class {
85112
85222
  restore = async (stored, options, apply, signal) => {
85113
85223
  this.#selected = stored;
85114
85224
  for (const selection of this.#selected) {
85115
- const current = options.find(
85225
+ const current = options().find(
85116
85226
  (option) => option.id === selection.configId && option.type === selection.type
85117
85227
  );
85118
85228
  if (current === void 0) {
@@ -85304,6 +85414,7 @@ async function restoreSession(session, signal, recovery, origin) {
85304
85414
  session.store.remove(session.sessionId);
85305
85415
  stored = session.metrics.measureSync("session-state-read", () => session.store.read(session.sessionId));
85306
85416
  }
85417
+ const forkSource = origin?.kind === "fork" ? session.store.findNative(session.agentId, origin.nativeSessionId)[0] : void 0;
85307
85418
  let lifecycleResponse;
85308
85419
  if (origin?.kind === "fork") {
85309
85420
  if (!session.capabilities.unstableForkSession) {
@@ -85465,7 +85576,9 @@ async function restoreSession(session, signal, recovery, origin) {
85465
85576
  }
85466
85577
  session.restrictSessionCapabilities(lifecycleResponse);
85467
85578
  session.setConfigOptions(session.sessionConfigProtocol.normalizeLifecycle(lifecycleResponse));
85468
- if (stored instanceof FoundStoredSession && session.restored) {
85579
+ if (forkSource !== void 0) {
85580
+ await session.restoreStoredConfigurations(forkSource, signal, "fork");
85581
+ } else if (stored instanceof FoundStoredSession && session.restored) {
85469
85582
  await session.restoreStoredConfigurations(stored.session, signal);
85470
85583
  }
85471
85584
  await session.drained();
@@ -85677,13 +85790,31 @@ var AgentSession = class _AgentSession {
85677
85790
  this.writeStoredSession((/* @__PURE__ */ new Date()).toISOString());
85678
85791
  }
85679
85792
  };
85680
- restoreStoredConfigurations = async (session, signal) => {
85793
+ restoreStoredConfigurations = async (session, signal, intent = "restore") => {
85794
+ const configurations = intent === "fork" ? session.configurations.toSorted(
85795
+ (left, right) => Number(right.configId === "model") - Number(left.configId === "model")
85796
+ ) : session.configurations;
85681
85797
  await this.#sessionConfigurations.restore(
85682
- session.configurations,
85683
- this.#configOptions,
85798
+ configurations,
85799
+ () => this.#configOptions,
85684
85800
  this.#applyConfiguration,
85685
85801
  signal
85686
85802
  );
85803
+ if (intent === "fork") {
85804
+ for (const selection of session.configurations) {
85805
+ if (selection.configId !== "mode") {
85806
+ continue;
85807
+ }
85808
+ const current = this.#configOptions.find((option) => option.id === selection.configId);
85809
+ if (current?.type !== selection.type || (selection.type === "select" ? current.currentValue !== selection.value : current.booleanValue !== selection.booleanValue)) {
85810
+ throw new AcpExecutionError(
85811
+ "The Agent could not preserve the source Session mode for this Fork",
85812
+ {},
85813
+ "invalid_session_configuration"
85814
+ );
85815
+ }
85816
+ }
85817
+ }
85687
85818
  };
85688
85819
  restrictSessionCapabilities = (response) => {
85689
85820
  this.#capabilities = restrictAcpSessionCapabilities(this.#capabilities, response);
@@ -87124,7 +87255,8 @@ var NativeSessionService = class {
87124
87255
  requestId: command.requestId,
87125
87256
  sessionId: command.sessionId,
87126
87257
  targetSessionId: command.targetSessionId,
87127
- ...await this.#activeTurn(command, options)
87258
+ ...await this.#activeTurn(command, options),
87259
+ queuedCancellation: "before-start"
87128
87260
  };
87129
87261
  }
87130
87262
  const capabilities = await options.adapter.getCapabilities(options);
@@ -87570,7 +87702,12 @@ var PromptLifecycle = class {
87570
87702
  }
87571
87703
  const recorded = await this.#recordLifecycle(sessionId, {
87572
87704
  type: "lifecycle-metadata",
87573
- payload: { kind: "prompt.completed", commandId, stopReason }
87705
+ payload: {
87706
+ kind: "prompt.completed",
87707
+ commandId,
87708
+ stopReason,
87709
+ ...request.phase === "queued" ? { execution: "not-started" } : {}
87710
+ }
87574
87711
  });
87575
87712
  if (!recorded) {
87576
87713
  this.#sink.publishOperationFailure(
@@ -87734,15 +87871,27 @@ var ExecutionReceipts = class {
87734
87871
  return void 0;
87735
87872
  }
87736
87873
  };
87874
+ /** Started identities remain authoritative after their detailed receipt is evicted. */
87875
+ hasStarted = (commandId, sessionId) => {
87876
+ const receipt = this.#executions.get(commandId);
87877
+ return receipt?.sessionId === sessionId && receipt.started || this.#table.identity(commandId)?.sessionId === sessionId;
87878
+ };
87737
87879
  /** Reserves an unseen command for cancellation before a delayed prompt can claim its identity. */
87738
87880
  recordCancellationBeforeAdmission = (commandId, sessionId) => {
87739
87881
  const existing = this.#executions.get(commandId);
87740
87882
  if (existing !== void 0) {
87741
87883
  return existing.sessionId === sessionId ? this.outcome(commandId) : void 0;
87742
87884
  }
87885
+ const identity2 = this.#table.identity(commandId);
87886
+ if (identity2 !== void 0) {
87887
+ return identity2.sessionId === sessionId ? {
87888
+ type: "lifecycle-metadata",
87889
+ payload: { kind: "prompt.completed", commandId, stopReason: "cancelled" }
87890
+ } : void 0;
87891
+ }
87743
87892
  const outcome = {
87744
87893
  type: "lifecycle-metadata",
87745
- payload: { kind: "prompt.completed", commandId, stopReason: "cancelled" }
87894
+ payload: { kind: "prompt.completed", commandId, stopReason: "cancelled", execution: "not-started" }
87746
87895
  };
87747
87896
  const commandHash = createHash7("sha256").update(
87748
87897
  encodeSessionCancelBeforeStartCommand({
@@ -88535,6 +88684,7 @@ function buildSessionLayer(options) {
88535
88684
  // src/engine/session/content/external-content-store.ts
88536
88685
  import { createHash as createHash8 } from "node:crypto";
88537
88686
  import { extname as extname2, join as join29 } from "node:path";
88687
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
88538
88688
 
88539
88689
  // src/engine/storage/files/scratch-directory.ts
88540
88690
  import { randomUUID as randomUUID17 } from "node:crypto";
@@ -88680,8 +88830,12 @@ var LocalExternalContentStore = class {
88680
88830
  }
88681
88831
  await this.#expire();
88682
88832
  active.throwIfAborted();
88683
- const path7 = await this.#scratch.write(kind, contentFileName(key, metadata.name), bytes, active);
88833
+ const writtenPath = await this.#scratch.write(kind, contentFileName(key, metadata.name), bytes, active);
88834
+ const path7 = await this.#scratch.resolveFile(writtenPath);
88684
88835
  active.throwIfAborted();
88836
+ if (path7 === null) {
88837
+ throw new OperationError("content_unavailable", "The cached content file is unavailable");
88838
+ }
88685
88839
  const retained = this.#entries.get(request);
88686
88840
  this.#entries.set(request, {
88687
88841
  byteLength: bytes.byteLength,
@@ -88722,6 +88876,30 @@ var LocalExternalContentStore = class {
88722
88876
  const bytes = await this.#scratch.read(resolved.path, active);
88723
88877
  return bytes === null ? null : { ...resolved, bytes };
88724
88878
  });
88879
+ /** Metadata for exact, still-cached file URLs. This does not grant content access. */
88880
+ linkedResources = (text2) => {
88881
+ if (!text2.includes("file:///")) {
88882
+ return [];
88883
+ }
88884
+ const resources = [];
88885
+ for (const [request, entry] of this.#entries) {
88886
+ if (!request.startsWith("mobius-content:resource:") || entry.expiresAt <= Date.now()) {
88887
+ continue;
88888
+ }
88889
+ const uri = pathToFileURL3(entry.path).href;
88890
+ for (let index2 = text2.indexOf(uri); index2 >= 0; index2 = text2.indexOf(uri, index2 + uri.length)) {
88891
+ const following = text2[index2 + uri.length];
88892
+ if (following === void 0 || /[\s<>"')\]]/u.test(following)) {
88893
+ resources.push({
88894
+ index: index2,
88895
+ resource: { uri, mediaType: entry.mediaType, name: entry.name, path: entry.path }
88896
+ });
88897
+ break;
88898
+ }
88899
+ }
88900
+ }
88901
+ return resources.sort((left, right) => left.index - right.index).map(({ resource }) => resource);
88902
+ };
88725
88903
  [Symbol.asyncDispose] = async () => {
88726
88904
  clearInterval(this.#expirationTimer);
88727
88905
  this.#lifetime.abort();
@@ -90292,10 +90470,19 @@ var sessionCancelBeforeStartCommand = {
90292
90470
  if (message.kind !== SESSION_CANCEL_BEFORE_START_KIND) {
90293
90471
  throw new Error("Command kind mismatch");
90294
90472
  }
90473
+ if (operations.hasStarted(message.commandId, message.sessionId)) {
90474
+ reportOperationFailure(
90475
+ socket,
90476
+ message.commandId,
90477
+ message.sessionId,
90478
+ new SessionError("turn_already_started", "The message already started and could not be withdrawn")
90479
+ );
90480
+ return;
90481
+ }
90295
90482
  const turn = sessionTurns.get(message.commandId, message.sessionId);
90296
90483
  if (turn === void 0) {
90297
90484
  const outcome = operations.recordCancellationBeforeAdmission(message.commandId, message.sessionId);
90298
- if (outcome !== void 0) {
90485
+ if (outcome?.type === "lifecycle-metadata" && outcome.payload.kind === "prompt.completed" && outcome.payload.stopReason === "cancelled") {
90299
90486
  void eventProjection.replayOutcome(message.sessionId, outcome).catch((error62) => {
90300
90487
  reportOperationFailure(socket, message.commandId, message.sessionId, error62);
90301
90488
  });
@@ -90767,6 +90954,7 @@ async function executePrompt({
90767
90954
  );
90768
90955
  prepared = await sessions.prepareExecutionSession(preparation);
90769
90956
  }
90957
+ signal.throwIfAborted();
90770
90958
  operations.markExecutionStarted(command.commandId);
90771
90959
  if (availability.disposition === SessionPromptDisposition.Steer) {
90772
90960
  if (!recordSteeringPrompt()) {
@@ -90949,7 +91137,7 @@ var sessionPromptCommand = {
90949
91137
  // package.json
90950
91138
  var package_default = {
90951
91139
  name: "@moxt-ai/mobius",
90952
- version: "0.0.355",
91140
+ version: "0.0.357",
90953
91141
  description: "Mobius CLI for pairing and running local AI agent services.",
90954
91142
  type: "module",
90955
91143
  bin: {
@@ -91024,8 +91212,8 @@ var package_default = {
91024
91212
  ws: "^8.21.3"
91025
91213
  },
91026
91214
  keywords: [],
91027
- author: "",
91028
- license: "ISC",
91215
+ author: "Moxt",
91216
+ license: "Apache-2.0",
91029
91217
  bugs: {
91030
91218
  url: "https://github.com/moxt-ai/mobius/issues"
91031
91219
  },
@@ -92214,7 +92402,8 @@ async function runRuntime(config2, parentSignal, timer = systemTimer, options =
92214
92402
  const adapters = createRuntimeAdapterRegistry(
92215
92403
  config2.adapters,
92216
92404
  processes,
92217
- (sessionId, content, contentSignal) => externalContent.put({ ...content, sessionId, signal: contentSignal })
92405
+ (sessionId, content, contentSignal) => externalContent.put({ ...content, sessionId, signal: contentSignal }),
92406
+ externalContent.linkedResources
92218
92407
  );
92219
92408
  const resolveAgent = async (agentId, resolveSignal, sessionId) => {
92220
92409
  const catalogAgent = await resolveCatalogAgent(config2, agentId, resolveSignal);