@moxt-ai/mobius 0.0.356 → 0.0.358

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 (86) hide show
  1. package/LICENSE +202 -0
  2. package/dist/cli.js +414 -290
  3. package/dist/cli.js.map +3 -3
  4. package/dist/engine/acp/adapter/adapter-registry.d.ts +1 -1
  5. package/dist/engine/acp/adapter/adapter-registry.d.ts.map +1 -1
  6. package/dist/engine/acp/adapter/adapter-registry.js +2 -1
  7. package/dist/engine/acp/adapter/adapter-registry.js.map +1 -1
  8. package/dist/engine/acp/adapter/claude/adapter.d.ts +8 -35
  9. package/dist/engine/acp/adapter/claude/adapter.d.ts.map +1 -1
  10. package/dist/engine/acp/adapter/claude/adapter.js +7 -6
  11. package/dist/engine/acp/adapter/claude/adapter.js.map +1 -1
  12. package/dist/engine/acp/adapter/codex/adapter.d.ts +8 -35
  13. package/dist/engine/acp/adapter/codex/adapter.d.ts.map +1 -1
  14. package/dist/engine/acp/adapter/codex/adapter.js +27 -13
  15. package/dist/engine/acp/adapter/codex/adapter.js.map +1 -1
  16. package/dist/engine/acp/adapter/live-sessions.d.ts +1 -1
  17. package/dist/engine/acp/adapter/live-sessions.d.ts.map +1 -1
  18. package/dist/engine/acp/adapter/live-sessions.js +1 -0
  19. package/dist/engine/acp/adapter/live-sessions.js.map +1 -1
  20. package/dist/engine/acp/adapter/native-session-lookup.d.ts +2 -2
  21. package/dist/engine/acp/adapter/native-session-lookup.d.ts.map +1 -1
  22. package/dist/engine/acp/adapter/native-session-lookup.js +4 -4
  23. package/dist/engine/acp/adapter/native-session-lookup.js.map +1 -1
  24. package/dist/engine/acp/adapter/pi/adapter.d.ts +8 -35
  25. package/dist/engine/acp/adapter/pi/adapter.d.ts.map +1 -1
  26. package/dist/engine/acp/adapter/pi/adapter.js +7 -6
  27. package/dist/engine/acp/adapter/pi/adapter.js.map +1 -1
  28. package/dist/engine/acp/adapter/query-processes.d.ts +10 -7
  29. package/dist/engine/acp/adapter/query-processes.d.ts.map +1 -1
  30. package/dist/engine/acp/adapter/query-processes.js +66 -79
  31. package/dist/engine/acp/adapter/query-processes.js.map +1 -1
  32. package/dist/engine/acp/adapter/query-session.d.ts +6 -4
  33. package/dist/engine/acp/adapter/query-session.d.ts.map +1 -1
  34. package/dist/engine/acp/adapter/query-session.js +37 -22
  35. package/dist/engine/acp/adapter/query-session.js.map +1 -1
  36. package/dist/engine/acp/adapter/registry.js +3 -3
  37. package/dist/engine/acp/adapter/registry.js.map +1 -1
  38. package/dist/engine/acp/adapter/session-events.d.ts +1 -0
  39. package/dist/engine/acp/adapter/session-events.d.ts.map +1 -1
  40. package/dist/engine/acp/adapter/session-events.js +60 -56
  41. package/dist/engine/acp/adapter/session-events.js.map +1 -1
  42. package/dist/engine/acp/content/content-externalizer.d.ts +8 -2
  43. package/dist/engine/acp/content/content-externalizer.d.ts.map +1 -1
  44. package/dist/engine/acp/content/content-externalizer.js +23 -6
  45. package/dist/engine/acp/content/content-externalizer.js.map +1 -1
  46. package/dist/engine/acp/harness/codex/agent.d.ts.map +1 -1
  47. package/dist/engine/acp/harness/codex/agent.js +14 -8
  48. package/dist/engine/acp/harness/codex/agent.js.map +1 -1
  49. package/dist/engine/acp/harness/codex/borrowed-agent.d.ts.map +1 -1
  50. package/dist/engine/acp/harness/codex/borrowed-agent.js +8 -2
  51. package/dist/engine/acp/harness/codex/borrowed-agent.js.map +1 -1
  52. package/dist/engine/acp/harness/codex/history.d.ts.map +1 -1
  53. package/dist/engine/acp/harness/codex/history.js +25 -23
  54. package/dist/engine/acp/harness/codex/history.js.map +1 -1
  55. package/dist/engine/acp/native-history/preview-contract.d.ts +6 -13
  56. package/dist/engine/acp/native-history/preview-contract.d.ts.map +1 -1
  57. package/dist/engine/agent/runtime-agents.d.ts +1 -1
  58. package/dist/engine/agent/runtime-agents.d.ts.map +1 -1
  59. package/dist/engine/agent/runtime-agents.js +2 -2
  60. package/dist/engine/agent/runtime-agents.js.map +1 -1
  61. package/dist/engine/runtime/run.d.ts.map +1 -1
  62. package/dist/engine/runtime/run.js +2 -2
  63. package/dist/engine/runtime/run.js.map +1 -1
  64. package/dist/engine/session/content/external-content-store.d.ts +4 -0
  65. package/dist/engine/session/content/external-content-store.d.ts.map +1 -1
  66. package/dist/engine/session/content/external-content-store.js +31 -1
  67. package/dist/engine/session/content/external-content-store.js.map +1 -1
  68. package/dist/engine/session/live/agent-session.d.ts +1 -1
  69. package/dist/engine/session/live/agent-session.d.ts.map +1 -1
  70. package/dist/engine/session/live/agent-session.js +2 -1
  71. package/dist/engine/session/live/agent-session.js.map +1 -1
  72. package/dist/engine/session/manager/session-manager.d.ts.map +1 -1
  73. package/dist/engine/session/manager/session-manager.js +14 -1
  74. package/dist/engine/session/manager/session-manager.js.map +1 -1
  75. package/dist/engine/session/native/native-session-browser.d.ts.map +1 -1
  76. package/dist/engine/session/native/native-session-browser.js +8 -8
  77. package/dist/engine/session/native/native-session-browser.js.map +1 -1
  78. package/dist/engine/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  79. package/dist/engine/session/prompt/prompt-lifecycle.js +6 -1
  80. package/dist/engine/session/prompt/prompt-lifecycle.js.map +1 -1
  81. package/dist/engine/session/turn/execution-receipts.d.ts.map +1 -1
  82. package/dist/engine/session/turn/execution-receipts.js +14 -4
  83. package/dist/engine/session/turn/execution-receipts.js.map +1 -1
  84. package/dist/engine.js +408 -283
  85. package/dist/engine.js.map +4 -4
  86. 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({
@@ -69127,6 +69128,7 @@ var CodexBorrowedAcpAgent = class {
69127
69128
  content,
69128
69129
  clientId: randomUUID7(),
69129
69130
  completion: Promise.withResolvers(),
69131
+ settled: Promise.withResolvers(),
69130
69132
  input: { kind: "awaiting" }
69131
69133
  };
69132
69134
  this.#pending = pending;
@@ -69164,7 +69166,6 @@ var CodexBorrowedAcpAgent = class {
69164
69166
  throw cause;
69165
69167
  } finally {
69166
69168
  signal.removeEventListener("abort", abort);
69167
- this.#pending = null;
69168
69169
  if (outcome === "cancelled") {
69169
69170
  try {
69170
69171
  await this.#removeQueuedInput(pending.clientId);
@@ -69172,6 +69173,8 @@ var CodexBorrowedAcpAgent = class {
69172
69173
  console.error("The cancelled Codex queued input could not be removed", cause);
69173
69174
  }
69174
69175
  }
69176
+ this.#pending = null;
69177
+ pending.settled.resolve();
69175
69178
  }
69176
69179
  };
69177
69180
  #removeQueuedInput = async (clientId) => {
@@ -69231,7 +69234,11 @@ var CodexBorrowedAcpAgent = class {
69231
69234
  );
69232
69235
  };
69233
69236
  cancel = async () => {
69234
- this.#pending?.completion.resolve({ stopReason: "cancelled" });
69237
+ const pending = this.#pending;
69238
+ if (pending !== null) {
69239
+ pending.completion.resolve({ stopReason: "cancelled" });
69240
+ await pending.settled.promise;
69241
+ }
69235
69242
  };
69236
69243
  close = async () => {
69237
69244
  this.#lifecycle = { kind: "closed" };
@@ -70232,40 +70239,42 @@ async function replayCodexHistory(client2, threadId, emit, signal) {
70232
70239
  continue;
70233
70240
  }
70234
70241
  if (item.type === "userMessage") {
70235
- const text2 = item.content.flatMap((content) => {
70242
+ const contents = item.content.flatMap((content) => {
70236
70243
  if (typeof content !== "object" || content === null) {
70237
70244
  return [];
70238
70245
  }
70239
70246
  const type = Reflect.get(content, "type");
70240
- const text3 = Reflect.get(content, "text");
70247
+ const text2 = Reflect.get(content, "text");
70241
70248
  const url2 = Reflect.get(content, "url");
70242
70249
  if (type === "image" && typeof url2 === "string") {
70243
70250
  return [`[@image](${url2})`];
70244
70251
  }
70245
- return type === "text" && typeof text3 === "string" ? [text3] : [];
70246
- }).join("");
70247
- const history = await embedLocalHistoryImages(
70248
- { status: "available", truncated: false, messages: [{ role: "user", text: text2 }] },
70249
- deadline
70250
- );
70251
- if (history.status !== "available") {
70252
- continue;
70253
- }
70254
- const message = history.messages[0];
70255
- for (const image of message?.images ?? []) {
70256
- await emit({
70257
- content: { type: "image", data: image.data, mimeType: image.mimeType },
70258
- messageId: item.id,
70259
- sessionUpdate: "user_message_chunk"
70260
- });
70261
- }
70262
- const restoredText = message?.text ?? "";
70263
- if (restoredText.length > 0) {
70264
- await emit({
70265
- content: { text: restoredText, type: "text" },
70266
- messageId: item.id,
70267
- sessionUpdate: "user_message_chunk"
70268
- });
70252
+ return type === "text" && typeof text2 === "string" ? [text2] : [];
70253
+ });
70254
+ for (const text2 of contents) {
70255
+ const history = await embedLocalHistoryImages(
70256
+ { status: "available", truncated: false, messages: [{ role: "user", text: text2 }] },
70257
+ deadline
70258
+ );
70259
+ if (history.status !== "available") {
70260
+ continue;
70261
+ }
70262
+ const message = history.messages[0];
70263
+ for (const image of message?.images ?? []) {
70264
+ await emit({
70265
+ content: { type: "image", data: image.data, mimeType: image.mimeType },
70266
+ messageId: item.id,
70267
+ sessionUpdate: "user_message_chunk"
70268
+ });
70269
+ }
70270
+ const restoredText = message?.text ?? "";
70271
+ if (restoredText.length > 0) {
70272
+ await emit({
70273
+ content: { text: restoredText, type: "text" },
70274
+ messageId: item.id,
70275
+ sessionUpdate: "user_message_chunk"
70276
+ });
70277
+ }
70269
70278
  }
70270
70279
  } else if (item.type === "agentMessage") {
70271
70280
  await emit(agentMessageUpdate(item.id, item.text));
@@ -70930,16 +70939,19 @@ var CodexAcpAgent = class {
70930
70939
  */
70931
70940
  close = () => {
70932
70941
  if (this.#closePromise === null) {
70933
- this.#appServer.detach();
70934
70942
  this.#closePromise = (async () => {
70935
- await this.#sessionAgent.close();
70936
- const child = this.#appServer.child();
70937
70943
  try {
70938
- const captured = await captureProcessTree(child);
70939
- await stopProcessTree(child, captured);
70944
+ await this.#sessionAgent.close();
70940
70945
  } finally {
70941
- this.#connection?.close();
70942
- this.#onBackgroundActivity?.("none");
70946
+ this.#appServer.detach();
70947
+ const child = this.#appServer.child();
70948
+ try {
70949
+ const captured = await captureProcessTree(child);
70950
+ await stopProcessTree(child, captured);
70951
+ } finally {
70952
+ this.#connection?.close();
70953
+ this.#onBackgroundActivity?.("none");
70954
+ }
70943
70955
  }
70944
70956
  })();
70945
70957
  }
@@ -73327,14 +73339,21 @@ function toToolKind(toolName) {
73327
73339
  }
73328
73340
 
73329
73341
  // src/engine/acp/harness/pi/upstream/dist/acp/translate/pi-messages.js
73330
- function normalizePiMessageText(content) {
73342
+ var userContentSchema = external_exports.discriminatedUnion("type", [
73343
+ external_exports.object({ type: external_exports.literal("text"), text: external_exports.string() }),
73344
+ external_exports.object({ type: external_exports.literal("image"), data: external_exports.string(), mimeType: external_exports.string() })
73345
+ ]);
73346
+ function piUserMessageContent(content) {
73331
73347
  if (typeof content === "string") {
73332
- return content;
73348
+ return content.length === 0 ? [] : [{ type: "text", text: content }];
73333
73349
  }
73334
73350
  if (!Array.isArray(content)) {
73335
- return "";
73351
+ return [];
73336
73352
  }
73337
- return content.map((c) => c?.type === "text" && typeof c.text === "string" ? c.text : "").filter(Boolean).join("");
73353
+ return content.flatMap((value) => {
73354
+ const parsed = userContentSchema.safeParse(value);
73355
+ return parsed.success ? [parsed.data] : [];
73356
+ });
73338
73357
  }
73339
73358
  function normalizePiAssistantText(content) {
73340
73359
  if (!Array.isArray(content)) {
@@ -74427,13 +74446,13 @@ ${JSON.stringify(stats, null, 2)}`;
74427
74446
  const m = entry.message;
74428
74447
  const role = String(m?.role ?? "");
74429
74448
  if (role === "user") {
74430
- const text2 = normalizePiMessageText(m?.content);
74431
- if (text2) {
74449
+ this.conn.startUserMessage();
74450
+ for (const content of piUserMessageContent(m.content)) {
74432
74451
  await this.conn.sessionUpdate({
74433
74452
  sessionId: session.sessionId,
74434
74453
  update: {
74435
74454
  sessionUpdate: "user_message_chunk",
74436
- content: { type: "text", text: text2 }
74455
+ content
74437
74456
  }
74438
74457
  });
74439
74458
  }
@@ -75825,6 +75844,7 @@ var AdapterRegistry = class {
75825
75844
  resolve = (agent) => {
75826
75845
  const existing = this.#adapters.get(agent.id);
75827
75846
  if (existing !== void 0) {
75847
+ existing.refreshAgent(agent);
75828
75848
  return existing;
75829
75849
  }
75830
75850
  const adapter = this.#resolve(agent, this.#dependencies);
@@ -76262,63 +76282,64 @@ var AdapterSessionConnection = class {
76262
76282
  const externalized = await externalizer.update(options.sessionId(), notification.update, signal);
76263
76283
  signal.throwIfAborted();
76264
76284
  const update = externalized.agent;
76265
- const eventUpdate = externalized.event;
76266
- const backgroundTask = update._meta?.["mobius.backgroundTask"];
76267
- const payload = decodeAcpSessionUpdate(
76268
- JSON.stringify(
76269
- normalizeEvent(
76270
- (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") && backgroundTask !== void 0 && typeof eventUpdate === "object" && eventUpdate !== null ? { ...eventUpdate, backgroundTask } : eventUpdate
76285
+ for (const eventUpdate of externalized.events) {
76286
+ const backgroundTask = update._meta?.["mobius.backgroundTask"];
76287
+ const payload = decodeAcpSessionUpdate(
76288
+ JSON.stringify(
76289
+ normalizeEvent(
76290
+ (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") && backgroundTask !== void 0 && typeof eventUpdate === "object" && eventUpdate !== null ? { ...eventUpdate, backgroundTask } : eventUpdate
76291
+ )
76271
76292
  )
76272
- )
76273
- );
76274
- let event;
76275
- switch (update.sessionUpdate) {
76276
- case "available_commands_update":
76277
- event = {
76278
- kind: "commands",
76279
- payload,
76280
- commands: update.availableCommands.slice(0, 500).map((command) => ({
76281
- description: truncate(command.description, 500),
76282
- inputHint: truncate(command.input?.hint ?? "", 255),
76283
- name: truncate(command.name, 128)
76284
- })).filter((command) => command.name.length > 0)
76285
- };
76286
- break;
76287
- case "config_option_update":
76288
- event = {
76289
- kind: "configuration",
76290
- payload,
76291
- configOptions: protocol.normalizeUpdate(options.configOptions(), update.configOptions)
76292
- };
76293
- break;
76294
- case "current_mode_update":
76295
- event = { kind: "mode", payload, modeId: update.currentModeId };
76296
- break;
76297
- case "session_info_update": {
76298
- const changes = [];
76299
- if (Object.hasOwn(update, "title")) {
76300
- changes.push({ kind: "title", value: truncate(update.title ?? "", 500) });
76301
- }
76302
- if (Object.hasOwn(update, "updatedAt")) {
76303
- changes.push({ kind: "updated-at", value: truncate(update.updatedAt ?? "", 128) });
76293
+ );
76294
+ let event;
76295
+ switch (update.sessionUpdate) {
76296
+ case "available_commands_update":
76297
+ event = {
76298
+ kind: "commands",
76299
+ payload,
76300
+ commands: update.availableCommands.slice(0, 500).map((command) => ({
76301
+ description: truncate(command.description, 500),
76302
+ inputHint: truncate(command.input?.hint ?? "", 255),
76303
+ name: truncate(command.name, 128)
76304
+ })).filter((command) => command.name.length > 0)
76305
+ };
76306
+ break;
76307
+ case "config_option_update":
76308
+ event = {
76309
+ kind: "configuration",
76310
+ payload,
76311
+ configOptions: protocol.normalizeUpdate(options.configOptions(), update.configOptions)
76312
+ };
76313
+ break;
76314
+ case "current_mode_update":
76315
+ event = { kind: "mode", payload, modeId: update.currentModeId };
76316
+ break;
76317
+ case "session_info_update": {
76318
+ const changes = [];
76319
+ if (Object.hasOwn(update, "title")) {
76320
+ changes.push({ kind: "title", value: truncate(update.title ?? "", 500) });
76321
+ }
76322
+ if (Object.hasOwn(update, "updatedAt")) {
76323
+ changes.push({ kind: "updated-at", value: truncate(update.updatedAt ?? "", 128) });
76324
+ }
76325
+ event = { kind: "info", payload, changes };
76326
+ break;
76304
76327
  }
76305
- event = { kind: "info", payload, changes };
76306
- break;
76328
+ case "user_message_chunk":
76329
+ case "agent_message_chunk":
76330
+ case "agent_thought_chunk":
76331
+ case "tool_call":
76332
+ case "tool_call_update":
76333
+ case "plan":
76334
+ case "plan_update":
76335
+ case "plan_removed":
76336
+ event = { kind: "content", payload };
76337
+ break;
76338
+ default:
76339
+ event = { kind: "other", payload };
76307
76340
  }
76308
- case "user_message_chunk":
76309
- case "agent_message_chunk":
76310
- case "agent_thought_chunk":
76311
- case "tool_call":
76312
- case "tool_call_update":
76313
- case "plan":
76314
- case "plan_update":
76315
- case "plan_removed":
76316
- event = { kind: "content", payload };
76317
- break;
76318
- default:
76319
- event = { kind: "other", payload };
76341
+ await options.onEvent(event);
76320
76342
  }
76321
- await options.onEvent(event);
76322
76343
  });
76323
76344
  this.events = this.#updates;
76324
76345
  const client2 = {
@@ -76359,6 +76380,9 @@ var AdapterSessionConnection = class {
76359
76380
  get connection() {
76360
76381
  return this.#connection.connection;
76361
76382
  }
76383
+ get termination() {
76384
+ return this.#connection.termination;
76385
+ }
76362
76386
  initialize = (signal) => this.#connection.initialize(signal);
76363
76387
  agentSettingsModelEffort = () => this.#connection.agentSettingsModelEffort();
76364
76388
  race = (operation, exitMessage, signal) => this.#connection.race(operation, exitMessage, signal);
@@ -76472,6 +76496,7 @@ var AcpLiveSessions = class {
76472
76496
  "ACP agent exited while cancelling a prompt",
76473
76497
  bounded
76474
76498
  );
76499
+ return "retained";
76475
76500
  };
76476
76501
  closeSession = async (target, signal) => {
76477
76502
  const session = this.#find(target);
@@ -76683,7 +76708,7 @@ var lookupSchema = external_exports.object({
76683
76708
  maximumEntries: external_exports.number().int().positive().nullable()
76684
76709
  })
76685
76710
  });
76686
- async function findNativeSession(list, query, nativeSessionId, budget) {
76711
+ async function findNativeSession(list, cwd, nativeSessionId, budget, signal) {
76687
76712
  const decoded = lookupSchema.safeParse({ nativeSessionId, budget });
76688
76713
  if (!decoded.success) {
76689
76714
  throw new AcpExecutionError(
@@ -76697,9 +76722,9 @@ async function findNativeSession(list, query, nativeSessionId, budget) {
76697
76722
  let inspected = 0;
76698
76723
  const visited = /* @__PURE__ */ new Set();
76699
76724
  for (let pageNumber = 0; pageNumber < limits.maximumPages; pageNumber += 1) {
76700
- query.signal.throwIfAborted();
76701
- const page = await list(query, cursor);
76702
- query.signal.throwIfAborted();
76725
+ signal.throwIfAborted();
76726
+ const page = await list(cwd, cursor, signal);
76727
+ signal.throwIfAborted();
76703
76728
  inspected += page.sessions.length;
76704
76729
  const found = page.sessions.find((session) => session.sessionId === decoded.data.nativeSessionId);
76705
76730
  if (found !== void 0) {
@@ -76721,6 +76746,9 @@ async function findNativeSession(list, query, nativeSessionId, budget) {
76721
76746
  throw new AcpExecutionError("The native session was not found", {}, "native_session_not_found");
76722
76747
  }
76723
76748
 
76749
+ // src/engine/acp/adapter/query-processes.ts
76750
+ import { randomUUID as randomUUID14 } from "node:crypto";
76751
+
76724
76752
  // src/engine/session/history/session-event-store.ts
76725
76753
  import { randomUUID as randomUUID13 } from "node:crypto";
76726
76754
 
@@ -77429,45 +77457,51 @@ async function initializeAgentConnection(connection, agentId, verifiedVersion, s
77429
77457
 
77430
77458
  // src/engine/acp/adapter/query-session.ts
77431
77459
  var AcpQuerySession = class _AcpQuerySession {
77432
- #options;
77460
+ #cwd;
77461
+ #sessionId;
77433
77462
  #events;
77434
77463
  #connection;
77464
+ #lifecycle = { kind: "open" };
77435
77465
  #capabilities = unsupportedSessionCapabilities();
77436
77466
  #configOptions = [];
77437
- constructor(options, events, connector) {
77438
- this.#options = options;
77467
+ constructor(agent, cwd, sessionId, events, connector) {
77468
+ this.#cwd = cwd;
77469
+ this.#sessionId = sessionId;
77439
77470
  this.#events = events;
77440
77471
  this.#connection = connector.connectQuerySession({
77441
- agent: options.agent,
77442
- cwd: options.cwd,
77443
- sessionId: () => options.sessionId,
77472
+ agent,
77473
+ cwd,
77474
+ sessionId: () => sessionId,
77444
77475
  configOptions: () => this.#configOptions,
77445
77476
  onEvent: async (event) => {
77446
- this.#events.append(options.sessionId, { type: "acp", payload: event.payload });
77477
+ this.#events.append(sessionId, { type: "acp", payload: event.payload });
77447
77478
  if (event.kind === "configuration") {
77448
77479
  this.#configOptions = event.configOptions;
77449
77480
  }
77450
77481
  }
77451
77482
  });
77483
+ void this.#connection.termination.then(() => {
77484
+ this.#lifecycle = { kind: "terminated" };
77485
+ });
77452
77486
  }
77453
- static start = async (options, events, connector) => {
77487
+ static start = async (agent, cwd, sessionId, events, connector, signal) => {
77454
77488
  for (let attempt = 1; attempt <= 2; attempt += 1) {
77455
- await validateAgentRuntime(options.agent, options.signal);
77456
- const runtime = new _AcpQuerySession(options, events, connector);
77489
+ await validateAgentRuntime(agent, signal);
77490
+ const runtime = new _AcpQuerySession(agent, cwd, sessionId, events, connector);
77457
77491
  try {
77458
77492
  const initialized = await initializeAgentConnection(
77459
77493
  runtime.#connection,
77460
- options.agent.id,
77461
- options.agent.agentVersion,
77462
- options.signal
77494
+ agent.id,
77495
+ agent.agentVersion,
77496
+ signal
77463
77497
  );
77464
77498
  runtime.#capabilities = initialized.capabilities;
77465
77499
  return runtime;
77466
77500
  } catch (error62) {
77467
77501
  await runtime.closeProcess();
77468
- options.signal.throwIfAborted();
77502
+ signal.throwIfAborted();
77469
77503
  if (error62 instanceof RetryableAcpInitializationError && attempt === 1) {
77470
- console.warn(`[${options.agent.id}-acp] ACP agent initialization failed; retrying once`);
77504
+ console.warn(`[${agent.id}-acp] ACP agent initialization failed; retrying once`);
77471
77505
  continue;
77472
77506
  }
77473
77507
  if (error62 instanceof AcpExecutionError) {
@@ -77479,7 +77513,18 @@ var AcpQuerySession = class _AcpQuerySession {
77479
77513
  throw new AcpExecutionError("ACP agent initialization failed");
77480
77514
  };
77481
77515
  capabilities = () => this.#capabilities;
77482
- belongsTo = (agentId, cwd, directories) => this.#options.agent.id === agentId && this.#options.cwd === cwd && arraysEqual(this.#options.additionalDirectories, directories);
77516
+ hasTerminated = () => {
77517
+ switch (this.#lifecycle.kind) {
77518
+ case "open":
77519
+ return false;
77520
+ case "terminated":
77521
+ return true;
77522
+ default: {
77523
+ const unreachable3 = this.#lifecycle;
77524
+ return unreachable3;
77525
+ }
77526
+ }
77527
+ };
77483
77528
  listNativeSessions = async (cursor, signal) => {
77484
77529
  if (!this.#capabilities.listSessions) {
77485
77530
  throw new AcpExecutionError(
@@ -77505,7 +77550,7 @@ var AcpQuerySession = class _AcpQuerySession {
77505
77550
  }
77506
77551
  return decoded.data;
77507
77552
  };
77508
- previewEvents = async (nativeSessionId, eventLimit, signal) => {
77553
+ previewEvents = async (nativeSessionId, eventLimit, additionalDirectories, signal) => {
77509
77554
  if (!this.#capabilities.loadSession) {
77510
77555
  throw new AcpExecutionError(
77511
77556
  "This Agent restores context but does not provide message history.",
@@ -77513,14 +77558,14 @@ var AcpQuerySession = class _AcpQuerySession {
77513
77558
  "native_history_unsupported"
77514
77559
  );
77515
77560
  }
77516
- this.#events.reset(this.#options.sessionId);
77561
+ this.#events.reset(this.#sessionId);
77517
77562
  this.#connection.events.select(nativeSessionId);
77518
77563
  await this.#connection.race(
77519
77564
  this.#connection.connection.agent.request(
77520
77565
  methods12.agent.session.load,
77521
77566
  {
77522
- additionalDirectories: [...this.#options.additionalDirectories],
77523
- cwd: this.#options.cwd,
77567
+ additionalDirectories: [...additionalDirectories],
77568
+ cwd: this.#cwd,
77524
77569
  mcpServers: [],
77525
77570
  sessionId: nativeSessionId
77526
77571
  },
@@ -77531,125 +77576,105 @@ var AcpQuerySession = class _AcpQuerySession {
77531
77576
  );
77532
77577
  await this.#connection.events.drained();
77533
77578
  signal.throwIfAborted();
77534
- return this.#events.page(this.#options.sessionId, eventLimit, null);
77579
+ return this.#events.page(this.#sessionId, eventLimit, null);
77535
77580
  };
77536
77581
  closeProcess = async () => await this.#connection.closeProcess();
77582
+ discardEvents = () => this.#events.remove(this.#sessionId);
77537
77583
  };
77538
77584
 
77539
77585
  // src/engine/acp/adapter/query-processes.ts
77540
77586
  var AgentQueryProcesses = class {
77541
77587
  #eventPreviewsBySession = /* @__PURE__ */ new Map();
77542
- #processesByQuery;
77588
+ #processesByAgent = /* @__PURE__ */ new Map();
77589
+ #agent;
77543
77590
  #processes;
77544
77591
  #store = new MemorySessionEventStore();
77545
77592
  #connector;
77546
- constructor(processes, connector) {
77593
+ constructor(agent, processes, connector) {
77594
+ this.#agent = agent;
77547
77595
  this.#processes = processes;
77548
77596
  this.#connector = connector;
77549
- this.#processesByQuery = new RetainedAgentProcesses({
77550
- touch: processes.touch,
77551
- changed: processes.notifyChanged
77552
- });
77553
77597
  }
77554
- retire = async (sessionId, runtime) => {
77555
- const retained = this.#processesByQuery.detach(sessionId, runtime);
77556
- if (retained) {
77557
- this.#store.remove(sessionId);
77598
+ refreshAgent = (agent) => {
77599
+ this.#agent = agent;
77600
+ };
77601
+ retire = async (readers, cwd, runtime) => {
77602
+ readers.detach(cwd, runtime);
77603
+ try {
77604
+ await this.#processes.retire(runtime, () => runtime.closeProcess());
77605
+ } finally {
77606
+ runtime.discardEvents();
77558
77607
  }
77559
- await this.#processes.retire(runtime, () => runtime.closeProcess());
77560
77608
  };
77561
- async #startRetained(options) {
77562
- const runtime = await this.#processes.startRetained(
77563
- options.signal,
77564
- () => AcpQuerySession.start(options, this.#store, this.#connector),
77565
- {
77566
- isBusy: () => this.#processesByQuery.isBusy(options.sessionId),
77567
- retire: (runtime2) => this.retire(options.sessionId, runtime2),
77568
- retireIdle: (runtime2) => this.retire(options.sessionId, runtime2)
77569
- }
77570
- );
77571
- this.#processesByQuery.retain(options.sessionId, runtime);
77572
- return runtime;
77573
- }
77574
- async #startPreparation(options) {
77575
- return await this.#processesByQuery.prepare(options.sessionId, async () => {
77576
- return await this.#startRetained(options);
77577
- });
77578
- }
77579
- async #replacePreparation(existing, options) {
77580
- return await this.#processesByQuery.prepare(options.sessionId, async () => {
77581
- await this.retire(options.sessionId, existing);
77582
- return await this.#startRetained(options);
77583
- });
77584
- }
77585
- #prepare = async (options) => {
77586
- const pending = this.#processesByQuery.pending(options.sessionId);
77609
+ async #ensure(agent, readers, cwd, signal) {
77610
+ signal.throwIfAborted();
77611
+ const pending = readers.pending(cwd);
77587
77612
  if (pending !== void 0) {
77588
- const runtime2 = await waitForSharedOperation(pending, options.signal);
77589
- if (runtime2.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
77590
- this.#processesByQuery.touch(options.sessionId, runtime2);
77591
- return runtime2.capabilities();
77592
- }
77593
- return await this.#prepare(options);
77594
- }
77595
- const existing = this.#processesByQuery.get(options.sessionId);
77596
- if (existing !== void 0) {
77597
- if (existing.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
77598
- this.#processesByQuery.touch(options.sessionId, existing);
77599
- return existing.capabilities();
77600
- }
77601
- const runtime2 = await this.#replacePreparation(existing, options);
77602
- return runtime2.capabilities();
77613
+ return await waitForSharedOperation(pending, signal);
77603
77614
  }
77604
- const runtime = await this.#startPreparation(options);
77605
- return runtime.capabilities();
77606
- };
77607
- async #ensure(options) {
77608
- const existing = this.#processesByQuery.get(options.sessionId);
77609
- if (existing !== void 0) {
77610
- if (!existing.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
77611
- throw new AcpExecutionError(
77612
- "The agent or workspace cannot change after a conversation has started",
77613
- {},
77614
- "session_identity_mismatch"
77615
- );
77616
- }
77617
- this.#processesByQuery.touch(options.sessionId, existing);
77615
+ const existing = readers.get(cwd);
77616
+ if (existing !== void 0 && !existing.hasTerminated()) {
77617
+ readers.touch(cwd, existing);
77618
77618
  return existing;
77619
77619
  }
77620
- await this.#prepare(options);
77621
- const started = this.#processesByQuery.get(options.sessionId);
77622
- if (started === void 0) {
77623
- throw new AcpExecutionError("ACP native session browser did not start");
77624
- }
77625
- return started;
77620
+ return await readers.prepare(cwd, async () => {
77621
+ if (existing !== void 0) {
77622
+ await this.retire(readers, cwd, existing);
77623
+ }
77624
+ const readerId = randomUUID14();
77625
+ const runtime = await this.#processes.startRetained(
77626
+ signal,
77627
+ () => AcpQuerySession.start(agent, cwd, readerId, this.#store, this.#connector, signal),
77628
+ {
77629
+ isBusy: () => readers.isBusy(cwd),
77630
+ retire: (runtime2) => this.retire(readers, cwd, runtime2),
77631
+ retireIdle: (runtime2) => this.retire(readers, cwd, runtime2)
77632
+ }
77633
+ );
77634
+ readers.retain(cwd, runtime);
77635
+ return runtime;
77636
+ });
77626
77637
  }
77627
- #use = async (options, operation) => await this.#processesByQuery.use(options.sessionId, async () => await operation(await this.#ensure(options)));
77628
- capabilities = async (query) => await this.#prepare(query);
77629
- listNativeSessions = async (query, cursor) => await this.#use(query, async (runtime) => await runtime.listNativeSessions(cursor, query.signal));
77630
- previewSessionEvents = async (options, request) => {
77631
- const { nativeSessionId, eventLimit, cursor } = request;
77638
+ #use = async (cwd, signal, operation) => {
77639
+ const agent = this.#agent;
77640
+ let readers = this.#processesByAgent.get(agent);
77641
+ if (readers === void 0) {
77642
+ readers = new RetainedAgentProcesses({
77643
+ touch: this.#processes.touch,
77644
+ changed: this.#processes.notifyChanged
77645
+ });
77646
+ this.#processesByAgent.set(agent, readers);
77647
+ }
77648
+ const current = readers;
77649
+ return await current.use(cwd, async () => await operation(await this.#ensure(agent, current, cwd, signal)));
77650
+ };
77651
+ capabilities = async (cwd, signal) => await this.#use(cwd, signal, async (runtime) => runtime.capabilities());
77652
+ listNativeSessions = async (cwd, cursor, signal) => await this.#use(cwd, signal, async (runtime) => await runtime.listNativeSessions(cursor, signal));
77653
+ previewSessionEvents = async (cwd, additionalDirectories, request, signal) => {
77654
+ signal.throwIfAborted();
77655
+ const agent = this.#agent;
77656
+ const { nativeSessionId, eventLimit, cursor, sessionId } = request;
77632
77657
  if (cursor !== null) {
77633
- const preview = this.#eventPreviewsBySession.get(options.sessionId);
77658
+ const preview = this.#eventPreviewsBySession.get(sessionId);
77634
77659
  if (preview === void 0 || preview.nativeSessionId !== nativeSessionId) {
77635
77660
  throw new Error("The native event cursor is invalid");
77636
77661
  }
77637
- return preview.store.page(options.sessionId, eventLimit, cursor);
77662
+ return preview.store.page(sessionId, eventLimit, cursor);
77638
77663
  }
77639
77664
  const store = new MemorySessionEventStore();
77640
- const previous = this.#eventPreviewsBySession.get(options.sessionId);
77665
+ const previous = this.#eventPreviewsBySession.get(sessionId);
77641
77666
  previous?.store.close();
77642
- this.#eventPreviewsBySession.set(options.sessionId, { nativeSessionId, store });
77667
+ this.#eventPreviewsBySession.set(sessionId, { nativeSessionId, store });
77643
77668
  try {
77644
77669
  return await this.#processes.runTransient(
77645
- options.signal,
77646
- async () => await AcpQuerySession.start(options, store, this.#connector),
77647
- async (runtime) => await runtime.previewEvents(nativeSessionId, eventLimit, options.signal),
77670
+ signal,
77671
+ async () => await AcpQuerySession.start(agent, cwd, sessionId, store, this.#connector, signal),
77672
+ async (runtime) => await runtime.previewEvents(nativeSessionId, eventLimit, additionalDirectories, signal),
77648
77673
  async (runtime) => await runtime.closeProcess()
77649
77674
  );
77650
77675
  } catch (error62) {
77651
- if (this.#eventPreviewsBySession.get(options.sessionId)?.store === store) {
77652
- this.#eventPreviewsBySession.delete(options.sessionId);
77676
+ if (this.#eventPreviewsBySession.get(sessionId)?.store === store) {
77677
+ this.#eventPreviewsBySession.delete(sessionId);
77653
77678
  }
77654
77679
  store.close();
77655
77680
  throw error62;
@@ -77657,8 +77682,11 @@ var AgentQueryProcesses = class {
77657
77682
  };
77658
77683
  close = async () => {
77659
77684
  await Promise.all(
77660
- this.#processesByQuery.entries().map(async ([id, runtime]) => await this.retire(id, runtime))
77685
+ [...this.#processesByAgent.values()].flatMap(
77686
+ (readers) => readers.entries().map(async ([cwd, runtime]) => await this.retire(readers, cwd, runtime))
77687
+ )
77661
77688
  );
77689
+ this.#processesByAgent.clear();
77662
77690
  for (const preview of this.#eventPreviewsBySession.values()) {
77663
77691
  preview.store.close();
77664
77692
  }
@@ -77672,12 +77700,13 @@ var ClaudeAdapter = class {
77672
77700
  #queryProcesses;
77673
77701
  #connector;
77674
77702
  #liveSessions;
77675
- constructor(dependencies) {
77703
+ constructor(agent, dependencies) {
77676
77704
  this.#connector = new AcpSessionConnector(dependencies.contentExternalizer);
77677
77705
  this.#liveSessions = new AcpLiveSessions(dependencies.contentExternalizer);
77678
- this.#queryProcesses = new AgentQueryProcesses(dependencies.processes, this.#connector);
77706
+ this.#queryProcesses = new AgentQueryProcesses(agent, dependencies.processes, this.#connector);
77679
77707
  }
77680
- getCapabilities = async (query) => await this.#queryProcesses.capabilities(query);
77708
+ refreshAgent = (agent) => this.#queryProcesses.refreshAgent(agent);
77709
+ getCapabilities = (cwd, signal) => this.#queryProcesses.capabilities(cwd, signal);
77681
77710
  connectSession = (options) => this.#liveSessions.connectSession(options);
77682
77711
  cancel = (...args) => this.#liveSessions.cancel(...args);
77683
77712
  closeSession = (...args) => this.#liveSessions.closeSession(...args);
@@ -77685,9 +77714,9 @@ var ClaudeAdapter = class {
77685
77714
  steer = (...args) => this.#liveSessions.steer(...args);
77686
77715
  renameSession = (...args) => this.#liveSessions.renameSession(...args);
77687
77716
  configureSession = (...args) => this.#liveSessions.configureSession(...args);
77688
- listSessions = async (query, cursor) => await this.#queryProcesses.listNativeSessions(query, cursor);
77689
- findSession = (query, nativeSessionId, budget) => findNativeSession(this.listSessions, query, nativeSessionId, budget);
77690
- readSessionHistory = async (query, request) => await this.#queryProcesses.previewSessionEvents(query, request);
77717
+ listSessions = (cwd, cursor, signal) => this.#queryProcesses.listNativeSessions(cwd, cursor, signal);
77718
+ findSession = (cwd, nativeSessionId, budget, signal) => findNativeSession(this.listSessions, cwd, nativeSessionId, budget, signal);
77719
+ readSessionHistory = (cwd, additionalDirectories, request, signal) => this.#queryProcesses.previewSessionEvents(cwd, additionalDirectories, request, signal);
77691
77720
  close = async () => await runCleanup(
77692
77721
  () => this.#queryProcesses.close(),
77693
77722
  () => this.#liveSessions.close()
@@ -82992,13 +83021,15 @@ function fitCodexToolEvent(event) {
82992
83021
 
82993
83022
  // src/engine/acp/adapter/codex/adapter.ts
82994
83023
  var CodexAdapter = class {
83024
+ #agent;
82995
83025
  #queryProcesses;
82996
83026
  #connector;
82997
83027
  #liveSessions;
82998
83028
  #dependencies;
82999
83029
  #contentExternalizer;
83000
83030
  #previewCache = null;
83001
- constructor(dependencies) {
83031
+ constructor(agent, dependencies) {
83032
+ this.#agent = agent;
83002
83033
  this.#dependencies = dependencies;
83003
83034
  this.#contentExternalizer = {
83004
83035
  update: async (sessionId, update, signal) => {
@@ -83010,18 +83041,25 @@ var CodexAdapter = class {
83010
83041
  };
83011
83042
  this.#connector = new AcpSessionConnector(this.#contentExternalizer, fitCodexToolEvent);
83012
83043
  this.#liveSessions = new AcpLiveSessions(this.#contentExternalizer, fitCodexToolEvent);
83013
- this.#queryProcesses = new AgentQueryProcesses(dependencies.processes, this.#connector);
83044
+ this.#queryProcesses = new AgentQueryProcesses(agent, dependencies.processes, this.#connector);
83014
83045
  }
83015
- getCapabilities = async (query) => await this.#queryProcesses.capabilities(query);
83046
+ refreshAgent = (agent) => {
83047
+ this.#agent = agent;
83048
+ this.#queryProcesses.refreshAgent(agent);
83049
+ };
83050
+ getCapabilities = (cwd, signal) => this.#queryProcesses.capabilities(cwd, signal);
83016
83051
  connectSession = (options) => this.#liveSessions.connectSession(options);
83017
- cancel = (...args) => this.#liveSessions.cancel(...args);
83052
+ cancel = async (target, signal) => {
83053
+ await this.#liveSessions.closeSession(target, signal);
83054
+ return "closed";
83055
+ };
83018
83056
  closeSession = (...args) => this.#liveSessions.closeSession(...args);
83019
83057
  prompt = (...args) => this.#liveSessions.prompt(...args);
83020
83058
  steer = (...args) => this.#liveSessions.steer(...args);
83021
83059
  renameSession = (...args) => this.#liveSessions.renameSession(...args);
83022
83060
  configureSession = (...args) => this.#liveSessions.configureSession(...args);
83023
- listSessions = async (query, cursor) => await this.#queryProcesses.listNativeSessions(query, cursor);
83024
- findSession = (query, nativeSessionId, budget) => findNativeSession(this.listSessions, query, nativeSessionId, budget);
83061
+ listSessions = (cwd, cursor, signal) => this.#queryProcesses.listNativeSessions(cwd, cursor, signal);
83062
+ findSession = (cwd, nativeSessionId, budget, signal) => findNativeSession(this.listSessions, cwd, nativeSessionId, budget, signal);
83025
83063
  close = async () => {
83026
83064
  await runCleanup(
83027
83065
  () => this.#queryProcesses.close(),
@@ -83029,9 +83067,9 @@ var CodexAdapter = class {
83029
83067
  () => this.#previewCache?.close()
83030
83068
  );
83031
83069
  };
83032
- readSessionHistory = async (query, request) => {
83033
- const { agent, cwd } = query;
83034
- const signal = AbortSignal.any([query.signal, AbortSignal.timeout(6e4)]);
83070
+ readSessionHistory = async (cwd, _additionalDirectories, request, cancellation) => {
83071
+ const agent = this.#agent;
83072
+ const signal = AbortSignal.any([cancellation, AbortSignal.timeout(6e4)]);
83035
83073
  const { sessionId } = request;
83036
83074
  signal.throwIfAborted();
83037
83075
  nativeSessionIdSchema.parse(request.nativeSessionId);
@@ -83084,10 +83122,12 @@ var CodexAdapter = class {
83084
83122
  request.nativeSessionId,
83085
83123
  async (update) => {
83086
83124
  const externalized = await this.#contentExternalizer.update(sessionId, update, signal);
83087
- events.push({
83088
- type: "acp",
83089
- payload: decodeAcpSessionUpdate(JSON.stringify(fitCodexToolEvent(externalized.event)))
83090
- });
83125
+ for (const event of externalized.events) {
83126
+ events.push({
83127
+ type: "acp",
83128
+ payload: decodeAcpSessionUpdate(JSON.stringify(fitCodexToolEvent(event)))
83129
+ });
83130
+ }
83091
83131
  },
83092
83132
  signal
83093
83133
  );
@@ -83105,12 +83145,13 @@ var PiAdapter = class {
83105
83145
  #queryProcesses;
83106
83146
  #connector;
83107
83147
  #liveSessions;
83108
- constructor(dependencies) {
83148
+ constructor(agent, dependencies) {
83109
83149
  this.#connector = new AcpSessionConnector(dependencies.contentExternalizer);
83110
83150
  this.#liveSessions = new AcpLiveSessions(dependencies.contentExternalizer);
83111
- this.#queryProcesses = new AgentQueryProcesses(dependencies.processes, this.#connector);
83151
+ this.#queryProcesses = new AgentQueryProcesses(agent, dependencies.processes, this.#connector);
83112
83152
  }
83113
- getCapabilities = async (query) => await this.#queryProcesses.capabilities(query);
83153
+ refreshAgent = (agent) => this.#queryProcesses.refreshAgent(agent);
83154
+ getCapabilities = (cwd, signal) => this.#queryProcesses.capabilities(cwd, signal);
83114
83155
  connectSession = (options) => this.#liveSessions.connectSession(options);
83115
83156
  cancel = (...args) => this.#liveSessions.cancel(...args);
83116
83157
  closeSession = (...args) => this.#liveSessions.closeSession(...args);
@@ -83118,9 +83159,9 @@ var PiAdapter = class {
83118
83159
  steer = (...args) => this.#liveSessions.steer(...args);
83119
83160
  renameSession = (...args) => this.#liveSessions.renameSession(...args);
83120
83161
  configureSession = (...args) => this.#liveSessions.configureSession(...args);
83121
- listSessions = async (query, cursor) => await this.#queryProcesses.listNativeSessions(query, cursor);
83122
- findSession = (query, nativeSessionId, budget) => findNativeSession(this.listSessions, query, nativeSessionId, budget);
83123
- readSessionHistory = async (query, request) => await this.#queryProcesses.previewSessionEvents(query, request);
83162
+ listSessions = (cwd, cursor, signal) => this.#queryProcesses.listNativeSessions(cwd, cursor, signal);
83163
+ findSession = (cwd, nativeSessionId, budget, signal) => findNativeSession(this.listSessions, cwd, nativeSessionId, budget, signal);
83164
+ readSessionHistory = (cwd, additionalDirectories, request, signal) => this.#queryProcesses.previewSessionEvents(cwd, additionalDirectories, request, signal);
83124
83165
  close = async () => await runCleanup(
83125
83166
  () => this.#queryProcesses.close(),
83126
83167
  () => this.#liveSessions.close()
@@ -83131,11 +83172,11 @@ var PiAdapter = class {
83131
83172
  var harnessAdapters = (agent, dependencies) => {
83132
83173
  switch (agent.id) {
83133
83174
  case "codex":
83134
- return new CodexAdapter(dependencies);
83175
+ return new CodexAdapter(agent, dependencies);
83135
83176
  case "claude":
83136
- return new ClaudeAdapter(dependencies);
83177
+ return new ClaudeAdapter(agent, dependencies);
83137
83178
  case "pi":
83138
- return new PiAdapter(dependencies);
83179
+ return new PiAdapter(agent, dependencies);
83139
83180
  default:
83140
83181
  throw new AcpExecutionError(
83141
83182
  `Agent "${agent.id}" has no registered harness adapter.`,
@@ -83208,8 +83249,18 @@ function isRecord7(value) {
83208
83249
  }
83209
83250
  var AcpContentExternalizer = class {
83210
83251
  #store;
83211
- constructor(store) {
83252
+ #linkedResources;
83253
+ constructor(store, linkedResources = () => []) {
83212
83254
  this.#store = store;
83255
+ this.#linkedResources = linkedResources;
83256
+ }
83257
+ #resourceLinks(text2) {
83258
+ return this.#linkedResources(text2).map((resource) => ({
83259
+ type: "resource_link",
83260
+ uri: resource.uri,
83261
+ name: resource.name,
83262
+ mimeType: resource.mediaType
83263
+ }));
83213
83264
  }
83214
83265
  async #content(sessionId, content, signal) {
83215
83266
  switch (content.type) {
@@ -83361,7 +83412,7 @@ var AcpContentExternalizer = class {
83361
83412
  const hasContent = update.content !== void 0 && update.content !== null || agentContent.length > 0;
83362
83413
  return {
83363
83414
  agent: { ...update, ...meta3, ...output2, ...hasContent ? { content: agentContent } : {} },
83364
- event: { ...update, ...meta3, ...output2, ...hasContent ? { content: eventContent } : {} }
83415
+ events: [{ ...update, ...meta3, ...output2, ...hasContent ? { content: eventContent } : {} }]
83365
83416
  };
83366
83417
  }
83367
83418
  update = async (sessionId, update, signal) => {
@@ -83373,7 +83424,12 @@ var AcpContentExternalizer = class {
83373
83424
  const content = await this.#content(sessionId, update.content, signal);
83374
83425
  return {
83375
83426
  agent: { ...update, content: content.agent },
83376
- event: { ...update, content: content.event }
83427
+ events: [
83428
+ { ...update, content: content.event },
83429
+ // Keep native text intact. Only add cards for exact file URLs whose
83430
+ // original upload metadata is still known; arbitrary context stays text.
83431
+ ...update.sessionUpdate === "user_message_chunk" && update.content.type === "text" ? this.#resourceLinks(update.content.text).map((content2) => ({ ...update, content: content2 })) : []
83432
+ ]
83377
83433
  };
83378
83434
  }
83379
83435
  case "tool_call":
@@ -83381,14 +83437,14 @@ var AcpContentExternalizer = class {
83381
83437
  return await this.#toolUpdate(sessionId, update, signal);
83382
83438
  case "compaction_update": {
83383
83439
  if (update.summary === void 0 || update.summary === null) {
83384
- return { agent: update, event: update };
83440
+ return { agent: update, events: [update] };
83385
83441
  }
83386
83442
  const content = await Promise.all(
83387
83443
  update.summary.map(async (item) => await this.#content(sessionId, item, signal))
83388
83444
  );
83389
83445
  return {
83390
83446
  agent: { ...update, summary: content.map((item) => item.agent) },
83391
- event: { ...update, summary: content.map((item) => item.event) }
83447
+ events: [{ ...update, summary: content.map((item) => item.event) }]
83392
83448
  };
83393
83449
  }
83394
83450
  case "plan":
@@ -83399,16 +83455,16 @@ var AcpContentExternalizer = class {
83399
83455
  case "config_option_update":
83400
83456
  case "session_info_update":
83401
83457
  case "usage_update":
83402
- return { agent: update, event: update };
83458
+ return { agent: update, events: [update] };
83403
83459
  }
83404
83460
  };
83405
83461
  };
83406
83462
 
83407
83463
  // src/engine/agent/runtime-agents.ts
83408
- function createRuntimeAdapterRegistry(adapters, processes, externalize) {
83464
+ function createRuntimeAdapterRegistry(adapters, processes, externalize, linkedResources) {
83409
83465
  return new AdapterRegistry(adapters, {
83410
83466
  processes,
83411
- contentExternalizer: new AcpContentExternalizer(externalize)
83467
+ contentExternalizer: new AcpContentExternalizer(externalize, linkedResources)
83412
83468
  });
83413
83469
  }
83414
83470
  var RuntimeAgentDependencies = class {
@@ -86128,8 +86184,9 @@ var AgentSession = class _AgentSession {
86128
86184
  await this.#interactions.cancelPendingInteractions();
86129
86185
  signal.throwIfAborted();
86130
86186
  if (this.#turn.running) {
86131
- await this.#adapter.cancel(this.#target(), signal);
86187
+ return await this.#adapter.cancel(this.#target(), signal);
86132
86188
  }
86189
+ return "retained";
86133
86190
  };
86134
86191
  respondPermission = async (options) => {
86135
86192
  await this.#interactions.respondPermission(options);
@@ -86770,7 +86827,18 @@ var SessionManager2 = class {
86770
86827
  await this.#executions.cancel(sessionId, async () => {
86771
86828
  const runtime = this.#executionProcesses.get(sessionId);
86772
86829
  if (runtime !== void 0) {
86773
- await runtime.cancel(signal2);
86830
+ const outcome = await runtime.cancel(signal2);
86831
+ switch (outcome) {
86832
+ case "retained":
86833
+ break;
86834
+ case "closed":
86835
+ await this.#stopSessionProcess(sessionId, runtime);
86836
+ break;
86837
+ default: {
86838
+ const unreachable3 = outcome;
86839
+ throw new AcpExecutionError("Unexpected cancellation outcome", { cause: unreachable3 });
86840
+ }
86841
+ }
86774
86842
  }
86775
86843
  });
86776
86844
  });
@@ -87136,7 +87204,7 @@ var SessionManager2 = class {
87136
87204
  };
87137
87205
 
87138
87206
  // src/engine/session/native/native-session-browser.ts
87139
- import { randomUUID as randomUUID14 } from "node:crypto";
87207
+ import { randomUUID as randomUUID15 } from "node:crypto";
87140
87208
 
87141
87209
  // src/engine/session/workspace/local-authorization-error.ts
87142
87210
  var LocalAuthorizationError = class extends OperationError {
@@ -87195,12 +87263,17 @@ var NativeSessionService = class {
87195
87263
  (scoped) => this.#sessions.withNativeSessionOperation(
87196
87264
  scoped,
87197
87265
  nativeSessionId,
87198
- (target) => target.adapter.readSessionHistory(target, {
87199
- nativeSessionId,
87200
- eventLimit,
87201
- cursor,
87202
- sessionId: target.sessionId
87203
- })
87266
+ (target) => target.adapter.readSessionHistory(
87267
+ target.cwd,
87268
+ target.additionalDirectories,
87269
+ {
87270
+ nativeSessionId,
87271
+ eventLimit,
87272
+ cursor,
87273
+ sessionId: target.sessionId
87274
+ },
87275
+ target.signal
87276
+ )
87204
87277
  )
87205
87278
  );
87206
87279
  #execute = async (command, signal) => {
@@ -87231,7 +87304,7 @@ var NativeSessionService = class {
87231
87304
  queuedCancellation: "before-start"
87232
87305
  };
87233
87306
  }
87234
- const capabilities = await options.adapter.getCapabilities(options);
87307
+ const capabilities = await options.adapter.getCapabilities(cwd, signal);
87235
87308
  if (!capabilities.listSessions) {
87236
87309
  throw new AcpExecutionError(
87237
87310
  "This Agent does not support native session listing",
@@ -87246,7 +87319,7 @@ var NativeSessionService = class {
87246
87319
  if (authorized === null) {
87247
87320
  return null;
87248
87321
  }
87249
- const entryId = decodeIdentifier(randomUUID14());
87322
+ const entryId = decodeIdentifier(randomUUID15());
87250
87323
  if (this.#entries.size >= 5e3) {
87251
87324
  this.#entries.clear();
87252
87325
  }
@@ -87285,7 +87358,7 @@ var NativeSessionService = class {
87285
87358
  }
87286
87359
  cursor = known.cursor;
87287
87360
  }
87288
- const page = await options.adapter.listSessions(options, cursor);
87361
+ const page = await options.adapter.listSessions(cwd, cursor, signal);
87289
87362
  const entries = [];
87290
87363
  for (const info of page.sessions) {
87291
87364
  const entry = await register(info);
@@ -87302,7 +87375,7 @@ var NativeSessionService = class {
87302
87375
  "invalid_native_session_response"
87303
87376
  );
87304
87377
  }
87305
- nextCursor = decodeIdentifier(randomUUID14());
87378
+ nextCursor = decodeIdentifier(randomUUID15());
87306
87379
  if (this.#cursors.size >= 500) {
87307
87380
  this.#cursors.clear();
87308
87381
  }
@@ -87320,10 +87393,15 @@ var NativeSessionService = class {
87320
87393
  };
87321
87394
  }
87322
87395
  if (command.action === "find") {
87323
- const found = await options.adapter.findSession(options, command.nativeSessionId, {
87324
- maximumPages: 500,
87325
- maximumEntries: 500
87326
- });
87396
+ const found = await options.adapter.findSession(
87397
+ cwd,
87398
+ command.nativeSessionId,
87399
+ {
87400
+ maximumPages: 500,
87401
+ maximumEntries: 500
87402
+ },
87403
+ signal
87404
+ );
87327
87405
  const entry = await register(found);
87328
87406
  if (entry !== null) {
87329
87407
  return { ...envelope2, action: "find", entry };
@@ -87461,10 +87539,15 @@ var NativeSessionService = class {
87461
87539
  }
87462
87540
  };
87463
87541
  resolveTarget = async (command, options, selected) => {
87464
- const info = await options.adapter.findSession(options, selected.nativeSessionId, {
87465
- maximumPages: 100,
87466
- maximumEntries: null
87467
- }).catch((cause) => {
87542
+ const info = await options.adapter.findSession(
87543
+ options.cwd,
87544
+ selected.nativeSessionId,
87545
+ {
87546
+ maximumPages: 100,
87547
+ maximumEntries: null
87548
+ },
87549
+ options.signal
87550
+ ).catch((cause) => {
87468
87551
  options.signal.throwIfAborted();
87469
87552
  if (cause instanceof AcpExecutionError && cause.code === "native_session_not_found") {
87470
87553
  throw new AcpExecutionError(
@@ -87674,7 +87757,12 @@ var PromptLifecycle = class {
87674
87757
  }
87675
87758
  const recorded = await this.#recordLifecycle(sessionId, {
87676
87759
  type: "lifecycle-metadata",
87677
- payload: { kind: "prompt.completed", commandId, stopReason }
87760
+ payload: {
87761
+ kind: "prompt.completed",
87762
+ commandId,
87763
+ stopReason,
87764
+ ...request.phase === "queued" ? { execution: "not-started" } : {}
87765
+ }
87678
87766
  });
87679
87767
  if (!recorded) {
87680
87768
  this.#sink.publishOperationFailure(
@@ -87728,7 +87816,7 @@ async function settleAbandonedTurn(projection, operations, cancellation) {
87728
87816
  }
87729
87817
 
87730
87818
  // src/engine/session/turn/execution-receipts.ts
87731
- import { createHash as createHash7, randomUUID as randomUUID15 } from "node:crypto";
87819
+ import { createHash as createHash7, randomUUID as randomUUID16 } from "node:crypto";
87732
87820
  var MAX_TERMINAL_RECEIPTS = 1024;
87733
87821
  var ExecutionReceipts = class {
87734
87822
  #executions = /* @__PURE__ */ new Map();
@@ -87841,7 +87929,7 @@ var ExecutionReceipts = class {
87841
87929
  /** Started identities remain authoritative after their detailed receipt is evicted. */
87842
87930
  hasStarted = (commandId, sessionId) => {
87843
87931
  const receipt = this.#executions.get(commandId);
87844
- return receipt === void 0 ? this.#table.identity(commandId)?.sessionId === sessionId : receipt.sessionId === sessionId && receipt.started;
87932
+ return receipt?.sessionId === sessionId && receipt.started || this.#table.identity(commandId)?.sessionId === sessionId;
87845
87933
  };
87846
87934
  /** Reserves an unseen command for cancellation before a delayed prompt can claim its identity. */
87847
87935
  recordCancellationBeforeAdmission = (commandId, sessionId) => {
@@ -87849,9 +87937,16 @@ var ExecutionReceipts = class {
87849
87937
  if (existing !== void 0) {
87850
87938
  return existing.sessionId === sessionId ? this.outcome(commandId) : void 0;
87851
87939
  }
87940
+ const identity2 = this.#table.identity(commandId);
87941
+ if (identity2 !== void 0) {
87942
+ return identity2.sessionId === sessionId ? {
87943
+ type: "lifecycle-metadata",
87944
+ payload: { kind: "prompt.completed", commandId, stopReason: "cancelled" }
87945
+ } : void 0;
87946
+ }
87852
87947
  const outcome = {
87853
87948
  type: "lifecycle-metadata",
87854
- payload: { kind: "prompt.completed", commandId, stopReason: "cancelled" }
87949
+ payload: { kind: "prompt.completed", commandId, stopReason: "cancelled", execution: "not-started" }
87855
87950
  };
87856
87951
  const commandHash = createHash7("sha256").update(
87857
87952
  encodeSessionCancelBeforeStartCommand({
@@ -87862,7 +87957,7 @@ var ExecutionReceipts = class {
87862
87957
  ).digest("hex");
87863
87958
  this.#save(commandId, {
87864
87959
  admissionOrder: this.#nextAdmissionOrder(),
87865
- attemptId: randomUUID15(),
87960
+ attemptId: randomUUID16(),
87866
87961
  commandHash,
87867
87962
  sessionId,
87868
87963
  started: false,
@@ -87944,7 +88039,7 @@ var ExecutionReceipts = class {
87944
88039
  if (identity2 !== void 0) {
87945
88040
  return identity2.sessionId === sessionId && identity2.commandHash === commandHash ? { kind: "expired" } : { kind: "conflict" };
87946
88041
  }
87947
- const attemptId = randomUUID15();
88042
+ const attemptId = randomUUID16();
87948
88043
  this.#save(commandId, {
87949
88044
  admissionOrder: this.#nextAdmissionOrder(),
87950
88045
  attemptId,
@@ -88071,7 +88166,7 @@ var RuntimeTurnActivity = class {
88071
88166
  };
88072
88167
 
88073
88168
  // src/engine/session/turn/active-turn.ts
88074
- import { randomUUID as randomUUID16 } from "node:crypto";
88169
+ import { randomUUID as randomUUID17 } from "node:crypto";
88075
88170
  var ActiveRuntimeTurn = class {
88076
88171
  attemptId;
88077
88172
  command;
@@ -88081,7 +88176,7 @@ var ActiveRuntimeTurn = class {
88081
88176
  #dispatch;
88082
88177
  #sequence = 0;
88083
88178
  #interaction = null;
88084
- #epoch = decodeIdentifier(randomUUID16());
88179
+ #epoch = decodeIdentifier(randomUUID17());
88085
88180
  constructor(command, attemptId) {
88086
88181
  this.attemptId = attemptId;
88087
88182
  this.command = command;
@@ -88644,13 +88739,14 @@ function buildSessionLayer(options) {
88644
88739
  // src/engine/session/content/external-content-store.ts
88645
88740
  import { createHash as createHash8 } from "node:crypto";
88646
88741
  import { extname as extname2, join as join29 } from "node:path";
88742
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
88647
88743
 
88648
88744
  // src/engine/storage/files/scratch-directory.ts
88649
- import { randomUUID as randomUUID17 } from "node:crypto";
88745
+ import { randomUUID as randomUUID18 } from "node:crypto";
88650
88746
  import { lstat as lstat4, mkdir as mkdir5, readdir as readdir6, readFile as readFile6, realpath as realpath7, rename as rename4, rm as rm3, stat as stat6, utimes, writeFile as writeFile2 } from "node:fs/promises";
88651
88747
  import { dirname as dirname11, join as join28 } from "node:path";
88652
88748
  async function atomicWrite(path7, bytes, signal) {
88653
- const temporary = `${path7}.${randomUUID17()}.tmp`;
88749
+ const temporary = `${path7}.${randomUUID18()}.tmp`;
88654
88750
  try {
88655
88751
  await writeFile2(temporary, bytes, { mode: 384, signal });
88656
88752
  signal.throwIfAborted();
@@ -88662,7 +88758,7 @@ async function atomicWrite(path7, bytes, signal) {
88662
88758
  var ScratchDirectory = class {
88663
88759
  #root;
88664
88760
  constructor(parent) {
88665
- this.#root = join28(parent, randomUUID17());
88761
+ this.#root = join28(parent, randomUUID18());
88666
88762
  }
88667
88763
  /** Refreshes this directory's lease and removes sibling directories whose lease is older than `leaseMilliseconds`. */
88668
88764
  refreshLease = async (leaseMilliseconds) => {
@@ -88789,8 +88885,12 @@ var LocalExternalContentStore = class {
88789
88885
  }
88790
88886
  await this.#expire();
88791
88887
  active.throwIfAborted();
88792
- const path7 = await this.#scratch.write(kind, contentFileName(key, metadata.name), bytes, active);
88888
+ const writtenPath = await this.#scratch.write(kind, contentFileName(key, metadata.name), bytes, active);
88889
+ const path7 = await this.#scratch.resolveFile(writtenPath);
88793
88890
  active.throwIfAborted();
88891
+ if (path7 === null) {
88892
+ throw new OperationError("content_unavailable", "The cached content file is unavailable");
88893
+ }
88794
88894
  const retained = this.#entries.get(request);
88795
88895
  this.#entries.set(request, {
88796
88896
  byteLength: bytes.byteLength,
@@ -88831,6 +88931,30 @@ var LocalExternalContentStore = class {
88831
88931
  const bytes = await this.#scratch.read(resolved.path, active);
88832
88932
  return bytes === null ? null : { ...resolved, bytes };
88833
88933
  });
88934
+ /** Metadata for exact, still-cached file URLs. This does not grant content access. */
88935
+ linkedResources = (text2) => {
88936
+ if (!text2.includes("file:///")) {
88937
+ return [];
88938
+ }
88939
+ const resources = [];
88940
+ for (const [request, entry] of this.#entries) {
88941
+ if (!request.startsWith("mobius-content:resource:") || entry.expiresAt <= Date.now()) {
88942
+ continue;
88943
+ }
88944
+ const uri = pathToFileURL3(entry.path).href;
88945
+ for (let index2 = text2.indexOf(uri); index2 >= 0; index2 = text2.indexOf(uri, index2 + uri.length)) {
88946
+ const following = text2[index2 + uri.length];
88947
+ if (following === void 0 || /[\s<>"')\]]/u.test(following)) {
88948
+ resources.push({
88949
+ index: index2,
88950
+ resource: { uri, mediaType: entry.mediaType, name: entry.name, path: entry.path }
88951
+ });
88952
+ break;
88953
+ }
88954
+ }
88955
+ }
88956
+ return resources.sort((left, right) => left.index - right.index).map(({ resource }) => resource);
88957
+ };
88834
88958
  [Symbol.asyncDispose] = async () => {
88835
88959
  clearInterval(this.#expirationTimer);
88836
88960
  this.#lifetime.abort();
@@ -90640,7 +90764,7 @@ function createNativeSessionCommands(context) {
90640
90764
  }
90641
90765
 
90642
90766
  // src/engine/session/prompt/apply-session-configuration.ts
90643
- import { randomUUID as randomUUID18 } from "node:crypto";
90767
+ import { randomUUID as randomUUID19 } from "node:crypto";
90644
90768
  function invalidSessionConfiguration(message) {
90645
90769
  return new AcpExecutionError(message, {}, "invalid_session_configuration");
90646
90770
  }
@@ -90778,7 +90902,7 @@ async function applySessionConfigurationSelection(sessions, options, selection,
90778
90902
  booleanValue: selection.booleanValue,
90779
90903
  configId: selection.configId,
90780
90904
  cwd,
90781
- requestId: `cfg_${randomUUID18()}`,
90905
+ requestId: `cfg_${randomUUID19()}`,
90782
90906
  sessionId,
90783
90907
  signal,
90784
90908
  value: selection.value,
@@ -91068,7 +91192,7 @@ var sessionPromptCommand = {
91068
91192
  // package.json
91069
91193
  var package_default = {
91070
91194
  name: "@moxt-ai/mobius",
91071
- version: "0.0.356",
91195
+ version: "0.0.358",
91072
91196
  description: "Mobius CLI for pairing and running local AI agent services.",
91073
91197
  type: "module",
91074
91198
  bin: {
@@ -91143,8 +91267,8 @@ var package_default = {
91143
91267
  ws: "^8.21.3"
91144
91268
  },
91145
91269
  keywords: [],
91146
- author: "",
91147
- license: "ISC",
91270
+ author: "Moxt",
91271
+ license: "Apache-2.0",
91148
91272
  bugs: {
91149
91273
  url: "https://github.com/moxt-ai/mobius/issues"
91150
91274
  },
@@ -92333,13 +92457,14 @@ async function runRuntime(config2, parentSignal, timer = systemTimer, options =
92333
92457
  const adapters = createRuntimeAdapterRegistry(
92334
92458
  config2.adapters,
92335
92459
  processes,
92336
- (sessionId, content, contentSignal) => externalContent.put({ ...content, sessionId, signal: contentSignal })
92460
+ (sessionId, content, contentSignal) => externalContent.put({ ...content, sessionId, signal: contentSignal }),
92461
+ externalContent.linkedResources
92337
92462
  );
92338
92463
  const resolveAgent = async (agentId, resolveSignal, sessionId) => {
92339
92464
  const catalogAgent = await resolveCatalogAgent(config2, agentId, resolveSignal);
92340
92465
  const environment = sessionId === void 0 ? void 0 : config2.sessionEnvironment?.({ sessionId, agentId });
92341
92466
  const agent = environment === void 0 ? catalogAgent : catalogAgent.withEnvironment({ ...catalogAgent.environment, ...environment });
92342
- return { agent, adapter: adapters.resolve(agent) };
92467
+ return { agent, adapter: adapters.resolve(catalogAgent) };
92343
92468
  };
92344
92469
  const layer = buildSessionLayer({
92345
92470
  processes,