@moxt-ai/mobius 0.0.300 → 0.0.301

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 (80) hide show
  1. package/dist/acp/harness/claude/endpoint.d.ts.map +1 -1
  2. package/dist/acp/harness/claude/endpoint.js +2 -0
  3. package/dist/acp/harness/claude/endpoint.js.map +1 -1
  4. package/dist/acp/harness/claude/parent-lifeline.d.ts +3 -0
  5. package/dist/acp/harness/claude/parent-lifeline.d.ts.map +1 -0
  6. package/dist/acp/harness/claude/parent-lifeline.js +73 -0
  7. package/dist/acp/harness/claude/parent-lifeline.js.map +1 -0
  8. package/dist/acp/harness/pi/endpoint.d.ts.map +1 -1
  9. package/dist/acp/harness/pi/endpoint.js +1 -0
  10. package/dist/acp/harness/pi/endpoint.js.map +1 -1
  11. package/dist/cli.js +590 -60
  12. package/dist/cli.js.map +4 -4
  13. package/dist/generated/acp-session-store-migrations.d.ts +3 -0
  14. package/dist/generated/acp-session-store-migrations.d.ts.map +1 -1
  15. package/dist/generated/acp-session-store-migrations.js +5 -1
  16. package/dist/generated/acp-session-store-migrations.js.map +1 -1
  17. package/dist/runtime/command/registry/prompt.d.ts.map +1 -1
  18. package/dist/runtime/command/registry/prompt.js +6 -0
  19. package/dist/runtime/command/registry/prompt.js.map +1 -1
  20. package/dist/runtime/engine.d.ts +1 -0
  21. package/dist/runtime/engine.d.ts.map +1 -1
  22. package/dist/runtime/engine.js +8 -0
  23. package/dist/runtime/engine.js.map +1 -1
  24. package/dist/runtime/run.d.ts +1 -0
  25. package/dist/runtime/run.d.ts.map +1 -1
  26. package/dist/runtime/run.js +3 -0
  27. package/dist/runtime/run.js.map +1 -1
  28. package/dist/runtime.js +555 -53
  29. package/dist/runtime.js.map +4 -4
  30. package/dist/schema/acp-session-store/execution-receipt.d.ts +136 -0
  31. package/dist/schema/acp-session-store/execution-receipt.d.ts.map +1 -0
  32. package/dist/schema/acp-session-store/execution-receipt.js +12 -0
  33. package/dist/schema/acp-session-store/execution-receipt.js.map +1 -0
  34. package/dist/schema/acp-session-store/index.d.ts +1 -0
  35. package/dist/schema/acp-session-store/index.d.ts.map +1 -1
  36. package/dist/schema/acp-session-store/index.js +1 -0
  37. package/dist/schema/acp-session-store/index.js.map +1 -1
  38. package/dist/session/api/session-api.d.ts +4 -0
  39. package/dist/session/api/session-api.d.ts.map +1 -1
  40. package/dist/session/api/session-api.js.map +1 -1
  41. package/dist/session/api/session-layer.d.ts +2 -0
  42. package/dist/session/api/session-layer.d.ts.map +1 -1
  43. package/dist/session/api/session-layer.js +25 -4
  44. package/dist/session/api/session-layer.js.map +1 -1
  45. package/dist/session/history/session-event-store.d.ts +7 -4
  46. package/dist/session/history/session-event-store.d.ts.map +1 -1
  47. package/dist/session/history/session-event-store.js +13 -7
  48. package/dist/session/history/session-event-store.js.map +1 -1
  49. package/dist/session/history/session-history.d.ts +1 -4
  50. package/dist/session/history/session-history.d.ts.map +1 -1
  51. package/dist/session/history/session-history.js +1 -4
  52. package/dist/session/history/session-history.js.map +1 -1
  53. package/dist/session/live/agent-session-restore.d.ts +1 -0
  54. package/dist/session/live/agent-session-restore.d.ts.map +1 -1
  55. package/dist/session/live/agent-session.d.ts +2 -0
  56. package/dist/session/live/agent-session.d.ts.map +1 -1
  57. package/dist/session/live/agent-session.js +15 -4
  58. package/dist/session/live/agent-session.js.map +1 -1
  59. package/dist/session/live/session-restore.d.ts.map +1 -1
  60. package/dist/session/live/session-restore.js +37 -0
  61. package/dist/session/live/session-restore.js.map +1 -1
  62. package/dist/session/manager/session-manager.d.ts.map +1 -1
  63. package/dist/session/manager/session-manager.js +11 -3
  64. package/dist/session/manager/session-manager.js.map +1 -1
  65. package/dist/session/prompt/execute-prompt.d.ts.map +1 -1
  66. package/dist/session/prompt/execute-prompt.js +6 -1
  67. package/dist/session/prompt/execute-prompt.js.map +1 -1
  68. package/dist/session/prompt/prompt-lifecycle.d.ts +1 -0
  69. package/dist/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  70. package/dist/session/prompt/prompt-lifecycle.js +9 -1
  71. package/dist/session/prompt/prompt-lifecycle.js.map +1 -1
  72. package/dist/session/turn/execution-receipts.d.ts +23 -2
  73. package/dist/session/turn/execution-receipts.d.ts.map +1 -1
  74. package/dist/session/turn/execution-receipts.js +161 -10
  75. package/dist/session/turn/execution-receipts.js.map +1 -1
  76. package/dist/storage/session/execution-receipt-table.d.ts +45 -0
  77. package/dist/storage/session/execution-receipt-table.d.ts.map +1 -0
  78. package/dist/storage/session/execution-receipt-table.js +64 -0
  79. package/dist/storage/session/execution-receipt-table.js.map +1 -0
  80. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -2086,16 +2086,28 @@ var init_package = __esm({
2086
2086
  "package.json"() {
2087
2087
  package_default = {
2088
2088
  name: "@moxt-ai/mobius",
2089
- version: "0.0.300",
2089
+ version: "0.0.301",
2090
2090
  description: "Mobius CLI for pairing and running local AI agent services.",
2091
2091
  type: "module",
2092
2092
  bin: {
2093
2093
  mobius: "dist/cli.js"
2094
2094
  },
2095
2095
  exports: {
2096
- "./runtime": "./src/runtime.ts",
2097
- "./process": "./src/util/process/executable.ts",
2098
- "./filesystem": "./src/util/filesystem/private-path.ts"
2096
+ "./runtime": {
2097
+ "mobius-source": "./src/runtime.ts",
2098
+ types: "./dist/runtime.d.ts",
2099
+ default: "./dist/runtime.js"
2100
+ },
2101
+ "./process": {
2102
+ "mobius-source": "./src/util/process/executable.ts",
2103
+ types: "./dist/util/process/executable.d.ts",
2104
+ default: "./dist/util/process/executable.js"
2105
+ },
2106
+ "./filesystem": {
2107
+ "mobius-source": "./src/util/filesystem/private-path.ts",
2108
+ types: "./dist/util/filesystem/private-path.d.ts",
2109
+ default: "./dist/util/filesystem/private-path.js"
2110
+ }
2099
2111
  },
2100
2112
  files: [
2101
2113
  "dist"
@@ -5593,6 +5605,84 @@ var init_commander = __esm({
5593
5605
  }
5594
5606
  });
5595
5607
 
5608
+ // src/acp/harness/claude/parent-lifeline.ts
5609
+ import { spawn } from "node:child_process";
5610
+ import { Duplex } from "node:stream";
5611
+ function spawnClaudeWithParentLifeline(options) {
5612
+ options.signal.throwIfAborted();
5613
+ const child = spawn(process.execPath, ["-e", supervisor, "--", options.command, ...options.args], {
5614
+ cwd: options.cwd,
5615
+ env: options.env,
5616
+ stdio: ["pipe", "pipe", "pipe", "pipe"],
5617
+ windowsHide: true
5618
+ });
5619
+ const lifeline = child.stdio[3];
5620
+ if (child.stdin === null || child.stdout === null || child.stderr === null || !(lifeline instanceof Duplex)) {
5621
+ child.kill("SIGKILL");
5622
+ throw new Error("The Claude process supervisor has no parent lifeline");
5623
+ }
5624
+ let termination = "running";
5625
+ const stop = () => {
5626
+ termination = "requested";
5627
+ lifeline.end();
5628
+ return true;
5629
+ };
5630
+ options.signal.addEventListener("abort", stop, { once: true });
5631
+ if (options.signal.aborted) {
5632
+ stop();
5633
+ }
5634
+ child.once("close", () => options.signal.removeEventListener("abort", stop));
5635
+ child.stderr.pipe(process.stderr, { end: false });
5636
+ return {
5637
+ stdin: child.stdin,
5638
+ stdout: child.stdout,
5639
+ get killed() {
5640
+ return termination === "requested";
5641
+ },
5642
+ get exitCode() {
5643
+ return child.exitCode;
5644
+ },
5645
+ get signalCode() {
5646
+ return child.signalCode;
5647
+ },
5648
+ kill: stop,
5649
+ on: child.on.bind(child),
5650
+ once: child.once.bind(child),
5651
+ off: child.off.bind(child)
5652
+ };
5653
+ }
5654
+ var supervisor;
5655
+ var init_parent_lifeline = __esm({
5656
+ "src/acp/harness/claude/parent-lifeline.ts"() {
5657
+ "use strict";
5658
+ supervisor = `
5659
+ const { spawn, spawnSync } = require("node:child_process");
5660
+ const { Socket } = require("node:net");
5661
+ const [command, ...args] = process.argv.slice(1);
5662
+ const child = spawn(command, args, { stdio: ["pipe", "pipe", "pipe"], detached: process.platform !== "win32", windowsHide: true });
5663
+ const lifeline = new Socket({ fd: 3, readable: true, writable: false });
5664
+ let exited = false;
5665
+ function terminate() {
5666
+ if (exited || child.pid === undefined) return;
5667
+ if (process.platform === "win32") {
5668
+ spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], { timeout: 5000, windowsHide: true, stdio: "ignore" });
5669
+ } else {
5670
+ try { process.kill(-child.pid, "SIGKILL"); }
5671
+ catch (error) { if (error.code !== "ESRCH") throw error; }
5672
+ }
5673
+ }
5674
+ lifeline.on("end", terminate).on("error", terminate).resume();
5675
+ process.on("SIGTERM", terminate).on("SIGINT", terminate);
5676
+ process.stdin.pipe(child.stdin);
5677
+ child.stdin.on("error", () => {});
5678
+ child.stdout.pipe(process.stdout);
5679
+ child.stderr.pipe(process.stderr);
5680
+ child.once("error", (error) => { process.stderr.write(error.message); process.exitCode = 1; lifeline.destroy(); process.stdin.destroy(); });
5681
+ child.once("close", (code) => { exited = true; process.exitCode = code ?? 1; lifeline.destroy(); process.stdin.destroy(); });
5682
+ `;
5683
+ }
5684
+ });
5685
+
5596
5686
  // ../../node_modules/.pnpm/@anthropic-ai+claude-agent-sdk@0.3.257_@anthropic-ai+sdk@0.122.0_zod@4.6.5__@modelconte_b0b90ed00d211c5955785f7de7f8dbf4/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
5597
5687
  import { createRequire as $ne } from "node:module";
5598
5688
  import * as xr from "node:fs/promises";
@@ -66545,7 +66635,7 @@ async function runPromptWithCancellation(agent, params, signal) {
66545
66635
  function createClaudeAcpApp(options = {}) {
66546
66636
  let agent = null;
66547
66637
  const app = acpAgent({ name: "claude-code-acp" }).onConnect((connection) => {
66548
- agent = new ClaudeAcpAgent(new ClientConnection(connection.client, options.onSessionUpdate), options.logger, options.environment);
66638
+ agent = new ClaudeAcpAgent(new ClientConnection(connection.client, options.onSessionUpdate), options.logger, options.environment, options.spawnClaudeCodeProcess);
66549
66639
  options.onConnect?.(connection);
66550
66640
  }).onRequest(methods.agent.initialize, (ctx) => requiredAgent(agent).initialize(ctx.params)).onRequest(methods.agent.session.new, (ctx) => requiredAgent(agent).newSession(ctx.params)).onRequest(methods.agent.session.load, (ctx) => requiredAgent(agent).loadSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.fork, (ctx) => requiredAgent(agent).unstable_forkSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.list, (ctx) => requiredAgent(agent).listSessions(ctx.params, ctx.signal)).onRequest(methods.agent.session.delete, (ctx) => requiredAgent(agent).deleteSession(ctx.params)).onRequest(methods.agent.session.resume, (ctx) => requiredAgent(agent).resumeSession(ctx.params)).onRequest(methods.agent.session.close, (ctx) => requiredAgent(agent).closeSession(ctx.params)).onRequest(methods.agent.session.setMode, (ctx) => requiredAgent(agent).setSessionMode(ctx.params)).onRequest(methods.agent.session.setConfigOption, (ctx) => requiredAgent(agent).setSessionConfigOption(ctx.params)).onRequest(methods.agent.authenticate, (ctx) => requiredAgent(agent).authenticate(ctx.params)).onRequest(methods.agent.providers.list, (ctx) => requiredAgent(agent).unstable_listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => requiredAgent(agent).unstable_setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => requiredAgent(agent).unstable_disableProvider(ctx.params)).onRequest(methods.agent.logout, (ctx) => requiredAgent(agent).logout(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => runPromptWithCancellation(requiredAgent(agent), ctx.params, ctx.signal)).onNotification(methods.agent.session.cancel, (ctx) => requiredAgent(agent).cancel(ctx.params)).onRequest(STEER_METHOD, { parse: parseSteerRequest }, (ctx) => requiredAgent(agent).steer(ctx.params)).onRequest(RENAME_METHOD, { parse: parseRenameRequest }, (ctx) => requiredAgent(agent).renameSession(ctx.params)).onRequest(GOAL_CONTROL_METHOD, { parse: parseGoalRequest }, (ctx) => requiredAgent(agent).goal(ctx.params));
66551
66641
  return { agent: () => agent, app };
@@ -66741,6 +66831,7 @@ var init_acp_agent = __esm({
66741
66831
  };
66742
66832
  mcpAuthentications = /* @__PURE__ */ new WeakMap();
66743
66833
  ClaudeAcpAgent = class {
66834
+ spawnClaudeCodeProcess;
66744
66835
  sessions;
66745
66836
  client;
66746
66837
  clientCapabilities;
@@ -66757,7 +66848,8 @@ var init_acp_agent = __esm({
66757
66848
  * return "cancelled". See {@link DEFAULT_FORCE_CANCEL_GRACE_MS}. Mutable so
66758
66849
  * tests can shrink it. */
66759
66850
  forceCancelGraceMs = DEFAULT_FORCE_CANCEL_GRACE_MS;
66760
- constructor(client2, logger, environment = process.env) {
66851
+ constructor(client2, logger, environment = process.env, spawnClaudeCodeProcess) {
66852
+ this.spawnClaudeCodeProcess = spawnClaudeCodeProcess;
66761
66853
  this.sessions = {};
66762
66854
  this.client = client2;
66763
66855
  this.logger = logger ?? console;
@@ -69334,6 +69426,7 @@ ${message.api_refusal_explanation}` : "";
69334
69426
  supportedDialogKinds: [REFUSAL_FALLBACK_DIALOG_KIND]
69335
69427
  } : {},
69336
69428
  pathToClaudeCodeExecutable: this.environment.CLAUDE_CODE_EXECUTABLE ?? await claudeCliPath(),
69429
+ ...this.spawnClaudeCodeProcess === void 0 ? {} : { spawnClaudeCodeProcess: this.spawnClaudeCodeProcess },
69337
69430
  extraArgs: {
69338
69431
  ...userProvidedOptions?.extraArgs,
69339
69432
  "replay-user-messages": ""
@@ -69609,6 +69702,7 @@ var ClaudeAcpEndpoint;
69609
69702
  var init_endpoint = __esm({
69610
69703
  "src/acp/harness/claude/endpoint.ts"() {
69611
69704
  "use strict";
69705
+ init_parent_lifeline();
69612
69706
  init_lib();
69613
69707
  ClaudeAcpEndpoint = class {
69614
69708
  #agent;
@@ -69622,6 +69716,7 @@ var init_endpoint = __esm({
69622
69716
  this.terminated = termination.promise;
69623
69717
  this.#resolveTermination = termination.resolve;
69624
69718
  const claude = createClaudeAcpApp({
69719
+ spawnClaudeCodeProcess: spawnClaudeWithParentLifeline,
69625
69720
  environment: Object.freeze({
69626
69721
  ...process.env,
69627
69722
  ...options.environment,
@@ -70256,7 +70351,7 @@ var init_platform = __esm({
70256
70351
  });
70257
70352
 
70258
70353
  // src/util/process/executable.ts
70259
- import { spawn as spawn2 } from "node:child_process";
70354
+ import { spawn as spawn3 } from "node:child_process";
70260
70355
  import { stat as stat4 } from "node:fs/promises";
70261
70356
  import { join as join12, resolve as resolve8 } from "node:path";
70262
70357
  async function resolveExecutablePath(requestedPath2, platform2 = process.platform) {
@@ -70422,7 +70517,7 @@ async function runBoundedProcess(options) {
70422
70517
  options.signal.throwIfAborted();
70423
70518
  const outputLimitBytes = options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES;
70424
70519
  const launch = executableLaunch2(options.command, options.arguments);
70425
- const child = spawn2(launch.command, [...launch.arguments], {
70520
+ const child = spawn3(launch.command, [...launch.arguments], {
70426
70521
  cwd: options.cwd,
70427
70522
  ...hostPlatform.spawnOptions,
70428
70523
  env: { ...options.environment },
@@ -71053,7 +71148,7 @@ var init_app_server_protocol = __esm({
71053
71148
  });
71054
71149
 
71055
71150
  // src/acp/harness/codex/app-server-client.ts
71056
- import { spawn as spawn3 } from "node:child_process";
71151
+ import { spawn as spawn4 } from "node:child_process";
71057
71152
  var INITIALIZE_TIMEOUT_MILLISECONDS, MESSAGE_OUTPUT_LIMIT_BYTES, CodexAppServerError, CodexAppServerClient;
71058
71153
  var init_app_server_client = __esm({
71059
71154
  "src/acp/harness/codex/app-server-client.ts"() {
@@ -71085,7 +71180,7 @@ var init_app_server_client = __esm({
71085
71180
  constructor(options) {
71086
71181
  this.#handlers = options.handlers;
71087
71182
  const launch = executableLaunch2(options.command, ["app-server"]);
71088
- this.#child = spawn3(launch.command, [...launch.arguments], {
71183
+ this.#child = spawn4(launch.command, [...launch.arguments], {
71089
71184
  cwd: options.cwd,
71090
71185
  ...platformSpawnOptions,
71091
71186
  env: { ...process.env, ...options.environment },
@@ -71468,12 +71563,12 @@ function presentToolItem(item) {
71468
71563
  };
71469
71564
  }
71470
71565
  if (item.type === "collabAgentToolCall") {
71471
- const spawn10 = item.tool === "spawnAgent";
71566
+ const spawn11 = item.tool === "spawnAgent";
71472
71567
  return {
71473
71568
  content: [],
71474
71569
  kind: "other",
71475
71570
  locations: [],
71476
- rawInput: spawn10 ? {
71571
+ rawInput: spawn11 ? {
71477
71572
  model: item.model,
71478
71573
  prompt: item.prompt,
71479
71574
  reasoningEffort: item.reasoningEffort,
@@ -71481,10 +71576,10 @@ function presentToolItem(item) {
71481
71576
  } : { receiverThreadIds: item.receiverThreadIds, senderThreadId: item.senderThreadId },
71482
71577
  rawOutput: {
71483
71578
  agentsStates: item.agentsStates,
71484
- ...spawn10 ? { receiverThreadIds: item.receiverThreadIds } : {}
71579
+ ...spawn11 ? { receiverThreadIds: item.receiverThreadIds } : {}
71485
71580
  },
71486
71581
  status: collaborationStatus(item.status),
71487
- title: spawn10 ? "Dispatch subagent" : "Wait for subagent"
71582
+ title: spawn11 ? "Dispatch subagent" : "Wait for subagent"
71488
71583
  };
71489
71584
  }
71490
71585
  if (item.type === "subAgentActivity" && item.kind === "started") {
@@ -73624,6 +73719,9 @@ function decodeSessionPromptCommand(encoded) {
73624
73719
  directoryId: readDirectoryIdentifier(value, "directoryId"),
73625
73720
  disposition,
73626
73721
  images,
73722
+ ...value["historyPolicy"] === void 0 ? {} : {
73723
+ historyPolicy: readHistoryPolicy(value)
73724
+ },
73627
73725
  kind: SESSION_PROMPT_KIND,
73628
73726
  prompt,
73629
73727
  resources,
@@ -73632,6 +73730,13 @@ function decodeSessionPromptCommand(encoded) {
73632
73730
  targetCommandId
73633
73731
  };
73634
73732
  }
73733
+ function readHistoryPolicy(value) {
73734
+ const field = value["historyPolicy"];
73735
+ if (field === SessionHistoryPolicy.Eager || field === SessionHistoryPolicy.ExecutionOnly) {
73736
+ return field;
73737
+ }
73738
+ throw new ProtocolMessageError("Invalid value field: historyPolicy");
73739
+ }
73635
73740
  function decodeSessionCancelCommand(encoded) {
73636
73741
  const value = parseJson(encoded);
73637
73742
  assertKind(value, SESSION_CANCEL_KIND);
@@ -73663,7 +73768,7 @@ function encodeSessionCancelCommand(message) {
73663
73768
  function encodeSessionPromptDispositionCommand(message) {
73664
73769
  return JSON.stringify(decodeSessionPromptDispositionCommand(JSON.stringify(message)));
73665
73770
  }
73666
- var SESSION_PROMPT_KIND, SESSION_PROMPT_DISPOSITION_KIND, SESSION_CANCEL_KIND, SessionPromptDisposition, MAX_PROMPT_LENGTH, MAX_PROMPT_ATTACHMENT_LENGTH, promptImageSchema, promptResourceSchema, decodePromptImagesValue, decodePromptResourcesValue;
73771
+ var SESSION_PROMPT_KIND, SESSION_PROMPT_DISPOSITION_KIND, SESSION_CANCEL_KIND, SessionPromptDisposition, SessionHistoryPolicy, MAX_PROMPT_LENGTH, MAX_PROMPT_ATTACHMENT_LENGTH, promptImageSchema, promptResourceSchema, decodePromptImagesValue, decodePromptResourcesValue;
73667
73772
  var init_commands = __esm({
73668
73773
  "../mobius-sdk/src/protocol/session/prompt/commands.ts"() {
73669
73774
  "use strict";
@@ -73679,6 +73784,10 @@ var init_commands = __esm({
73679
73784
  Reject: "reject",
73680
73785
  Steer: "steer"
73681
73786
  });
73787
+ SessionHistoryPolicy = Object.freeze({
73788
+ Eager: "eager",
73789
+ ExecutionOnly: "execution-only"
73790
+ });
73682
73791
  MAX_PROMPT_LENGTH = 1e5;
73683
73792
  MAX_PROMPT_ATTACHMENT_LENGTH = 6e6;
73684
73793
  promptImageSchema = external_exports.object({
@@ -75866,7 +75975,7 @@ var init_command2 = __esm({
75866
75975
  });
75867
75976
 
75868
75977
  // src/acp/harness/pi/upstream/dist/pi-rpc/process.js
75869
- import { spawn as spawn4 } from "node:child_process";
75978
+ import { spawn as spawn5 } from "node:child_process";
75870
75979
  import * as readline from "node:readline";
75871
75980
  function stripAnsi(s) {
75872
75981
  return s.replace(ANSI_ESCAPE_REGEX, "");
@@ -75970,7 +76079,7 @@ var init_process = __esm({
75970
76079
  args.push("--fork", params.origin.sessionPath);
75971
76080
  break;
75972
76081
  }
75973
- const child = spawn4(cmd, args, {
76082
+ const child = spawn5(cmd, args, {
75974
76083
  cwd: params.cwd,
75975
76084
  stdio: "pipe",
75976
76085
  env: params.environment,
@@ -79410,6 +79519,77 @@ ${JSON.stringify(stats, null, 2)}`;
79410
79519
  }, 0);
79411
79520
  return { sessionId: session.sessionId, configOptions, modes };
79412
79521
  }
79522
+ async resumeSession(params) {
79523
+ if (!isAbsolute11(params.cwd)) {
79524
+ throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
79525
+ }
79526
+ this.sessions.close(params.sessionId);
79527
+ this.lastSessionCwd = params.cwd;
79528
+ const stored = this.findStoredSession(params.sessionId);
79529
+ if (!stored) {
79530
+ throw RequestError7.invalidParams(`Unknown sessionId: ${params.sessionId}`);
79531
+ }
79532
+ const session = await this.restoreSession(params.sessionId, {
79533
+ cwd: params.cwd,
79534
+ mcpServers: params.mcpServers
79535
+ });
79536
+ let configuration;
79537
+ try {
79538
+ configuration = await getSessionConfiguration(session.proc);
79539
+ } catch (err) {
79540
+ this.sessions.close(session.sessionId);
79541
+ throw err;
79542
+ }
79543
+ const { configOptions, models, modes } = configuration;
79544
+ const enableSkillCommands = getEnableSkillCommands(params.cwd, this.environment);
79545
+ const fileCommands = loadSlashCommands(params.cwd, this.environment);
79546
+ this.sessions.closeAllExcept?.(session.sessionId);
79547
+ this.store.upsert({
79548
+ sessionId: params.sessionId,
79549
+ cwd: params.cwd,
79550
+ sessionFile: stored.sessionFile
79551
+ });
79552
+ const response = {
79553
+ configOptions,
79554
+ models,
79555
+ modes,
79556
+ _meta: {
79557
+ piAcp: {
79558
+ startupInfo: null
79559
+ }
79560
+ }
79561
+ };
79562
+ setTimeout(() => {
79563
+ void (async () => {
79564
+ await session.publishContextUsage();
79565
+ try {
79566
+ const pi2 = await session.proc.getCommands();
79567
+ const { commands } = toAvailableCommandsFromPiGetCommands(pi2, {
79568
+ enableSkillCommands,
79569
+ includeExtensionCommands: false
79570
+ });
79571
+ await this.conn.sessionUpdate({
79572
+ sessionId: session.sessionId,
79573
+ update: {
79574
+ sessionUpdate: "available_commands_update",
79575
+ availableCommands: mergeCommands(commands, builtinAvailableCommands())
79576
+ }
79577
+ });
79578
+ return;
79579
+ } catch {
79580
+ }
79581
+ await this.conn.sessionUpdate({
79582
+ sessionId: session.sessionId,
79583
+ update: {
79584
+ sessionUpdate: "available_commands_update",
79585
+ availableCommands: mergeCommands(toAvailableCommands(fileCommands), builtinAvailableCommands())
79586
+ }
79587
+ });
79588
+ })().catch(() => {
79589
+ });
79590
+ }, 0);
79591
+ return response;
79592
+ }
79413
79593
  async loadSession(params) {
79414
79594
  if (!isAbsolute11(params.cwd)) {
79415
79595
  throw RequestError7.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
@@ -79689,6 +79869,9 @@ var init_endpoint2 = __esm({
79689
79869
  ).onRequest(
79690
79870
  methods5.agent.session.load,
79691
79871
  async (context) => await piAgentOrThrow(piAgent).loadSession(context.params)
79872
+ ).onRequest(
79873
+ methods5.agent.session.resume,
79874
+ async (context) => await piAgentOrThrow(piAgent).resumeSession(context.params)
79692
79875
  ).onRequest(
79693
79876
  methods5.agent.session.list,
79694
79877
  async (context) => await piAgentOrThrow(piAgent).listSessions(context.params)
@@ -80215,7 +80398,7 @@ var init_create_client = __esm({
80215
80398
  });
80216
80399
 
80217
80400
  // src/agent/discovery/acp-readiness-probe.ts
80218
- import { spawn as spawn5 } from "node:child_process";
80401
+ import { spawn as spawn6 } from "node:child_process";
80219
80402
  import {
80220
80403
  methods as methods8,
80221
80404
  ndJsonStream as ndJsonStream2,
@@ -80372,7 +80555,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
80372
80555
  }
80373
80556
  }
80374
80557
  const launch = executableLaunch2(agent.adapterPath, agent.arguments);
80375
- const child = spawn5(launch.command, [...launch.arguments], {
80558
+ const child = spawn6(launch.command, [...launch.arguments], {
80376
80559
  cwd: defaultWorkspace,
80377
80560
  ...platformSpawnOptions,
80378
80561
  env: { ...environment, ...agent.environment },
@@ -83043,7 +83226,7 @@ var init_worker_lifecycle_log = __esm({
83043
83226
  });
83044
83227
 
83045
83228
  // src/daemon/lifecycle/worker-process.ts
83046
- import { spawn as spawn6 } from "node:child_process";
83229
+ import { spawn as spawn7 } from "node:child_process";
83047
83230
  import { isAbsolute as isAbsolute13, resolve as resolve16 } from "node:path";
83048
83231
  function currentWorkerLaunch(version2) {
83049
83232
  const entry = process.argv[1];
@@ -83103,7 +83286,7 @@ var init_worker_process = __esm({
83103
83286
  constructor(launch, signal, environment) {
83104
83287
  signal.throwIfAborted();
83105
83288
  this.launch = launch;
83106
- this.#child = spawn6(launch.executable, [launch.entry, "__worker"], {
83289
+ this.#child = spawn7(launch.executable, [launch.entry, "__worker"], {
83107
83290
  env: environment,
83108
83291
  windowsHide: true,
83109
83292
  stdio: ["ignore", "pipe", "pipe", "ipc"]
@@ -83436,9 +83619,9 @@ var init_manager2 = __esm({
83436
83619
  });
83437
83620
 
83438
83621
  // src/daemon/lifecycle/manager-client.ts
83439
- import { spawn as spawn7 } from "node:child_process";
83622
+ import { spawn as spawn8 } from "node:child_process";
83440
83623
  async function spawnManager() {
83441
- const child = spawn7(process.execPath, [process.argv[1] ?? "", "__manager"], {
83624
+ const child = spawn8(process.execPath, [process.argv[1] ?? "", "__manager"], {
83442
83625
  detached: true,
83443
83626
  windowsHide: true,
83444
83627
  env: process.env,
@@ -83844,7 +84027,7 @@ var init_history_images = __esm({
83844
84027
  });
83845
84028
 
83846
84029
  // src/acp/native-history/codex/codex-history.ts
83847
- import { spawn as spawn8 } from "node:child_process";
84030
+ import { spawn as spawn9 } from "node:child_process";
83848
84031
  function bounded(value, maximum) {
83849
84032
  return value.slice(0, maximum);
83850
84033
  }
@@ -84220,7 +84403,7 @@ async function readCodexHistoryWithOptions(agent, cwd, nativeSessionId, signal,
84220
84403
  }
84221
84404
  signal.throwIfAborted();
84222
84405
  const launch = executableLaunch2(agent.agentIdentity.realPath, ["app-server"]);
84223
- const child = spawn8(launch.command, [...launch.arguments], {
84406
+ const child = spawn9(launch.command, [...launch.arguments], {
84224
84407
  cwd,
84225
84408
  env: { ...process.env, ...agent.environment },
84226
84409
  ...platformSpawnOptions,
@@ -90233,6 +90416,25 @@ var init_acp_session = __esm({
90233
90416
  }
90234
90417
  });
90235
90418
 
90419
+ // src/schema/acp-session-store/execution-receipt.ts
90420
+ var executionReceipt;
90421
+ var init_execution_receipt = __esm({
90422
+ "src/schema/acp-session-store/execution-receipt.ts"() {
90423
+ "use strict";
90424
+ init_sqlite_core();
90425
+ executionReceipt = sqliteTable("execution_receipt", {
90426
+ commandId: text("command_id").primaryKey(),
90427
+ sessionId: text("session_id").notNull(),
90428
+ commandHash: text("command_hash").notNull(),
90429
+ attemptId: text("attempt_id").notNull(),
90430
+ state: text("state").notNull(),
90431
+ started: integer2("started", { mode: "boolean" }).notNull(),
90432
+ outcome: text("outcome"),
90433
+ updatedAt: text("updated_at").notNull()
90434
+ });
90435
+ }
90436
+ });
90437
+
90236
90438
  // src/schema/acp-session-store/session-live-event-position-allocation.ts
90237
90439
  var sessionLiveEventPositionAllocation;
90238
90440
  var init_session_live_event_position_allocation = __esm({
@@ -90251,6 +90453,7 @@ var init_acp_session_store = __esm({
90251
90453
  "src/schema/acp-session-store/index.ts"() {
90252
90454
  "use strict";
90253
90455
  init_acp_session();
90456
+ init_execution_receipt();
90254
90457
  init_session_live_event_position_allocation();
90255
90458
  }
90256
90459
  });
@@ -90299,6 +90502,71 @@ var init_acp_session_table = __esm({
90299
90502
  }
90300
90503
  });
90301
90504
 
90505
+ // src/storage/session/execution-receipt-table.ts
90506
+ var receiptFields, receiptSchema, ExecutionReceiptTable;
90507
+ var init_execution_receipt_table = __esm({
90508
+ "src/storage/session/execution-receipt-table.ts"() {
90509
+ "use strict";
90510
+ init_drizzle_orm();
90511
+ init_zod();
90512
+ init_acp_session_store();
90513
+ receiptFields = external_exports.object({
90514
+ commandId: external_exports.string(),
90515
+ sessionId: external_exports.string(),
90516
+ commandHash: external_exports.string(),
90517
+ attemptId: external_exports.string(),
90518
+ updatedAt: external_exports.string()
90519
+ });
90520
+ receiptSchema = external_exports.discriminatedUnion("state", [
90521
+ receiptFields.extend({ state: external_exports.literal("dispatching"), started: external_exports.literal(false), outcome: external_exports.null() }),
90522
+ receiptFields.extend({ state: external_exports.literal("running"), started: external_exports.literal(true), outcome: external_exports.string().nullable() }),
90523
+ receiptFields.extend({
90524
+ state: external_exports.enum(["succeeded", "failed", "cancelled"]),
90525
+ started: external_exports.boolean(),
90526
+ outcome: external_exports.string().nullable()
90527
+ })
90528
+ ]).readonly();
90529
+ ExecutionReceiptTable = class {
90530
+ #database;
90531
+ constructor(database) {
90532
+ this.#database = database;
90533
+ }
90534
+ list = () => receiptSchema.array().parse(
90535
+ this.#database.orm.select().from(executionReceipt).orderBy(asc(executionReceipt.updatedAt), asc(executionReceipt.commandId)).all()
90536
+ );
90537
+ save = (receipt) => {
90538
+ this.#database.transaction(() => {
90539
+ this.#database.orm.insert(executionReceipt).values(receipt).onConflictDoUpdate({ target: executionReceipt.commandId, set: receipt }).run();
90540
+ if (receipt.state === "dispatching" || receipt.state === "running") {
90541
+ return;
90542
+ }
90543
+ const retained = this.#database.orm.select({ commandId: executionReceipt.commandId }).from(executionReceipt).where(
90544
+ and(
90545
+ eq2(executionReceipt.sessionId, receipt.sessionId),
90546
+ inArray(executionReceipt.state, ["succeeded", "failed", "cancelled"])
90547
+ )
90548
+ ).orderBy(desc(executionReceipt.updatedAt), desc(executionReceipt.commandId)).limit(1024).all();
90549
+ if (retained.length === 1024) {
90550
+ this.#database.orm.delete(executionReceipt).where(
90551
+ and(
90552
+ eq2(executionReceipt.sessionId, receipt.sessionId),
90553
+ inArray(executionReceipt.state, ["succeeded", "failed", "cancelled"]),
90554
+ notInArray(
90555
+ executionReceipt.commandId,
90556
+ retained.map((row) => row.commandId)
90557
+ )
90558
+ )
90559
+ ).run();
90560
+ }
90561
+ });
90562
+ };
90563
+ remove = (commandId) => {
90564
+ this.#database.orm.delete(executionReceipt).where(eq2(executionReceipt.commandId, commandId)).run();
90565
+ };
90566
+ };
90567
+ }
90568
+ });
90569
+
90302
90570
  // src/storage/session/live-event-position-table.ts
90303
90571
  var allocationRowSchema, LiveEventPositionTable;
90304
90572
  var init_live_event_position_table = __esm({
@@ -90552,6 +90820,10 @@ var init_acp_session_store_migrations = __esm({
90552
90820
  {
90553
90821
  "name": "0006_live_event_position_allocation.sql",
90554
90822
  "sql": "CREATE TABLE `session_live_event_position_allocation` (\n `allocated_position` integer DEFAULT 10000 NOT NULL,\n `session_id` text PRIMARY KEY NOT NULL\n);\n"
90823
+ },
90824
+ {
90825
+ "name": "0007_busy_excalibur.sql",
90826
+ "sql": "CREATE TABLE `execution_receipt` (\n `command_id` text PRIMARY KEY NOT NULL,\n `session_id` text NOT NULL,\n `command_hash` text NOT NULL,\n `attempt_id` text NOT NULL,\n `state` text NOT NULL,\n `started` integer NOT NULL,\n `outcome` text,\n `updated_at` text NOT NULL\n);\n"
90555
90827
  }
90556
90828
  ];
90557
90829
  }
@@ -91170,12 +91442,12 @@ var init_session_event_id_allocator = __esm({
91170
91442
 
91171
91443
  // src/session/history/session-event-store.ts
91172
91444
  import { randomUUID as randomUUID12 } from "node:crypto";
91173
- function liveEvent(sessionId, sessionGenerationId, eventId, position, event) {
91445
+ function liveEvent(sessionId, sessionGenerationId, eventId, position, event, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) {
91174
91446
  return {
91175
91447
  eventId,
91176
91448
  ...event,
91177
91449
  position: position.toString(),
91178
- recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
91450
+ recordedAt,
91179
91451
  sessionGenerationId,
91180
91452
  sessionId
91181
91453
  };
@@ -91336,7 +91608,7 @@ var init_session_event_store = __esm({
91336
91608
  }
91337
91609
  return resolved;
91338
91610
  };
91339
- append = (sessionId, event) => {
91611
+ append = (sessionId, event, recordedAt) => {
91340
91612
  const session = this.#session(sessionId);
91341
91613
  const allocatedPosition = this.#allocatedPositionBySession.get(sessionId) ?? 9999;
91342
91614
  if (allocatedPosition === Number.MAX_SAFE_INTEGER) {
@@ -91349,7 +91621,8 @@ var init_session_event_store = __esm({
91349
91621
  session.sessionGenerationId,
91350
91622
  this.#eventIds.allocate(sessionId, session.sessionGenerationId),
91351
91623
  position,
91352
- event
91624
+ event,
91625
+ recordedAt
91353
91626
  );
91354
91627
  session.apply(recorded, this.#resolver(session));
91355
91628
  return recorded;
@@ -91390,12 +91663,14 @@ var init_session_event_store = __esm({
91390
91663
  };
91391
91664
  };
91392
91665
  CachedSessionEventStore = class {
91666
+ #terminalOutcomes;
91393
91667
  #cacheTable;
91394
91668
  #eventIds;
91395
91669
  #liveEventPositions;
91396
91670
  #sessions = /* @__PURE__ */ new Map();
91397
91671
  #flushSchedule = { kind: "idle" };
91398
- constructor(cacheTable, liveEventPositions, eventIds = new InMemorySessionEventIdAllocator()) {
91672
+ constructor(cacheTable, liveEventPositions, eventIds = new InMemorySessionEventIdAllocator(), terminalOutcomes = () => []) {
91673
+ this.#terminalOutcomes = terminalOutcomes;
91399
91674
  this.#eventIds = eventIds;
91400
91675
  this.#liveEventPositions = liveEventPositions;
91401
91676
  this.#cacheTable = cacheTable;
@@ -91428,7 +91703,7 @@ var init_session_event_store = __esm({
91428
91703
  }
91429
91704
  return resolved;
91430
91705
  };
91431
- append = (sessionId, event) => {
91706
+ append = (sessionId, event, recordedAt) => {
91432
91707
  if (this.#flushSchedule.kind === "closed") {
91433
91708
  throw new Error("The V2 Session event store is closed");
91434
91709
  }
@@ -91439,7 +91714,8 @@ var init_session_event_store = __esm({
91439
91714
  session.sessionGenerationId,
91440
91715
  this.#eventIds.allocate(sessionId, session.sessionGenerationId),
91441
91716
  position,
91442
- event
91717
+ event,
91718
+ recordedAt
91443
91719
  );
91444
91720
  session.apply(recorded, this.#resolver(sessionId, session));
91445
91721
  this.#sessions.set(sessionId, session);
@@ -91528,6 +91804,9 @@ var init_session_event_store = __esm({
91528
91804
  this.#deletePersisted(sessionId, previous?.sessionGenerationId);
91529
91805
  const sessionGenerationId = randomUUID12();
91530
91806
  this.#sessions.set(sessionId, new SessionEvents(sessionGenerationId));
91807
+ for (const { event, recordedAt } of this.#terminalOutcomes(sessionId)) {
91808
+ this.append(sessionId, event, recordedAt);
91809
+ }
91531
91810
  };
91532
91811
  materializedEventCount = (sessionId) => this.#sessions.get(sessionId)?.materializedCount ?? 0;
91533
91812
  close = () => {
@@ -91648,7 +91927,7 @@ var init_prompt_content = __esm({
91648
91927
  });
91649
91928
 
91650
91929
  // src/acp/connection/agent-connection.ts
91651
- import { spawn as spawn9 } from "node:child_process";
91930
+ import { spawn as spawn10 } from "node:child_process";
91652
91931
  import { ndJsonStream as ndJsonStream3, RequestError as RequestError9 } from "@agentclientprotocol/sdk";
91653
91932
  function requestErrorMessage(error62) {
91654
91933
  const fallback = truncate2(error62.message, 500);
@@ -91767,7 +92046,7 @@ var init_agent_connection = __esm({
91767
92046
  return;
91768
92047
  }
91769
92048
  const launch = executableLaunch2(agent.adapterPath, agent.arguments);
91770
- const child = spawn9(launch.command, [...launch.arguments], {
92049
+ const child = spawn10(launch.command, [...launch.arguments], {
91771
92050
  cwd,
91772
92051
  ...platformSpawnOptions,
91773
92052
  env: { ...process.env, ...agent.environment },
@@ -93121,6 +93400,19 @@ var init_agent_turn = __esm({
93121
93400
 
93122
93401
  // src/session/live/session-restore.ts
93123
93402
  import { methods as methods10 } from "@agentclientprotocol/sdk";
93403
+ function hasMethodNotFoundCode(cause) {
93404
+ const visited = /* @__PURE__ */ new Set();
93405
+ let current = cause;
93406
+ while ((current instanceof Error || typeof current === "object" && current !== null) && !visited.has(current)) {
93407
+ visited.add(current);
93408
+ const code = Reflect.get(current, "code");
93409
+ if (code === -32601 || code === "METHOD_NOT_FOUND" || code === "method_not_found") {
93410
+ return true;
93411
+ }
93412
+ current = current instanceof Error ? current.cause : void 0;
93413
+ }
93414
+ return false;
93415
+ }
93124
93416
  async function restoreSession(session, signal, recovery, origin) {
93125
93417
  if (origin !== void 0) {
93126
93418
  session.setSessionTitle(origin.title);
@@ -93187,6 +93479,9 @@ async function restoreSession(session, signal, recovery, origin) {
93187
93479
  }
93188
93480
  session.setPrompted(true);
93189
93481
  session.setRestored(true);
93482
+ if (recovery.kind === "execution-only") {
93483
+ session.startExecutionOnlyCapture();
93484
+ }
93190
93485
  lifecycleResponse2 = forked;
93191
93486
  } else if (stored instanceof FoundStoredSession && stored.session.agentId === session.agentId && stored.session.cwd === session.cwd && arraysEqual(stored.session.additionalDirectories, session.additionalDirectories)) {
93192
93487
  session.selectNativeSession(stored.session.nativeSessionId);
@@ -93205,6 +93500,37 @@ async function restoreSession(session, signal, recovery, origin) {
93205
93500
  "session-load",
93206
93501
  async () => (await session.loadSession(signal, "ACP agent exited while loading a session")).response
93207
93502
  );
93503
+ } else if (recovery.kind === "execution-only") {
93504
+ try {
93505
+ lifecycleResponse2 = await session.metrics.measure(
93506
+ "session-resume",
93507
+ async () => await session.race(
93508
+ session.agentRequest(
93509
+ methods10.agent.session.resume,
93510
+ {
93511
+ additionalDirectories: [...session.additionalDirectories],
93512
+ cwd: session.cwd,
93513
+ mcpServers: [],
93514
+ sessionId: session.nativeSessionId()
93515
+ },
93516
+ { cancellationSignal: signal }
93517
+ ),
93518
+ "ACP agent exited while resuming a session",
93519
+ signal
93520
+ )
93521
+ );
93522
+ } catch (cause) {
93523
+ signal.throwIfAborted();
93524
+ if (cause instanceof AcpExecutionError && !hasMethodNotFoundCode(cause)) {
93525
+ throw cause;
93526
+ }
93527
+ throw new AcpExecutionError(
93528
+ "This Agent cannot continue a persisted session without replaying native history",
93529
+ { cause },
93530
+ "execution_only_unsupported"
93531
+ );
93532
+ }
93533
+ session.startExecutionOnlyCapture();
93208
93534
  } else if (session.capabilities.resumeSession) {
93209
93535
  lifecycleResponse2 = await session.metrics.measure(
93210
93536
  "session-resume",
@@ -93264,6 +93590,9 @@ async function restoreSession(session, signal, recovery, origin) {
93264
93590
  );
93265
93591
  session.selectNativeSession(created.sessionId);
93266
93592
  session.setNativeHistoryOwnership({ kind: "fresh-session" });
93593
+ if (recovery.kind === "execution-only") {
93594
+ session.startExecutionOnlyCapture();
93595
+ }
93267
93596
  lifecycleResponse2 = created;
93268
93597
  }
93269
93598
  session.restrictSessionCapabilities(lifecycleResponse2);
@@ -93579,6 +93908,16 @@ var init_agent_session = __esm({
93579
93908
  }
93580
93909
  await this.#completedHistory(signal, "ACP agent exited while loading history");
93581
93910
  };
93911
+ /** Starts live event capture without asking the native Agent to replay its persisted history. */
93912
+ startExecutionOnlyCapture = () => {
93913
+ if (this.#sessionLoad.kind !== "not-requested") {
93914
+ return;
93915
+ }
93916
+ this.#sessionLoad = {
93917
+ history: new HistoryReplayCapture(this.#store.events, this.#mobiusSessionId),
93918
+ kind: "execution-only"
93919
+ };
93920
+ };
93582
93921
  loadHistoryEvents = async (eventLimit, cursor, signal) => {
93583
93922
  if (!this.#capabilities.loadSession) {
93584
93923
  throw new AcpExecutionError(
@@ -93829,7 +94168,7 @@ var init_agent_session = __esm({
93829
94168
  response: {}
93830
94169
  };
93831
94170
  }
93832
- if (this.#capabilities.loadSession) {
94171
+ if (this.#capabilities.loadSession && this.#sessionLoad.kind !== "execution-only") {
93833
94172
  await this.#completedHistory(options.signal, "ACP agent exited while loading history before a prompt");
93834
94173
  }
93835
94174
  const prompt = this.buildPrompt(options);
@@ -94028,14 +94367,14 @@ var init_agent_session = __esm({
94028
94367
  onElicitationComplete = (notification) => this.#interactions.onElicitationComplete(notification);
94029
94368
  onUpdate = (notification) => this.#updates.receive(notification);
94030
94369
  recordControlEvent = async (event) => {
94031
- if (this.#sessionLoad.kind !== "loaded") {
94370
+ if (this.#sessionLoad.kind !== "loaded" && this.#sessionLoad.kind !== "execution-only") {
94032
94371
  return false;
94033
94372
  }
94034
94373
  await this.recordLifecycleEvent(event);
94035
94374
  return true;
94036
94375
  };
94037
94376
  recordLifecycleEvent = async (event) => {
94038
- if (event.type !== "lifecycle-metadata" || this.#sessionLoad.kind !== "loaded") {
94377
+ if (event.type !== "lifecycle-metadata" || this.#sessionLoad.kind !== "loaded" && this.#sessionLoad.kind !== "execution-only") {
94039
94378
  return;
94040
94379
  }
94041
94380
  const liveEvent2 = this.#sessionLoad.history.recordLifecycle(event);
@@ -94056,7 +94395,7 @@ var init_agent_session = __esm({
94056
94395
  const rawUpdate = decodeAcpSessionUpdate(JSON.stringify(externalized.event));
94057
94396
  if (this.#sessionLoad.kind === "capturing") {
94058
94397
  await this.#sessionLoad.history.record(update, rawUpdate);
94059
- } else if (this.#sessionLoad.kind === "loaded") {
94398
+ } else if (this.#sessionLoad.kind === "loaded" || this.#sessionLoad.kind === "execution-only") {
94060
94399
  const onSessionEvent = this.#getObserver().onSessionEvent;
94061
94400
  await this.#sessionLoad.history.record(update, rawUpdate, async (liveEvent2) => {
94062
94401
  await onSessionEvent?.(liveEvent2);
@@ -95043,13 +95382,14 @@ var init_session_manager = __esm({
95043
95382
  }
95044
95383
  async #replaceExecutionPreparation(existing, options) {
95045
95384
  const desiredConfigOptions = existing.prepared().configOptions;
95385
+ const recovery = options.recovery ?? { kind: "interactive-only" };
95046
95386
  return await this.#executionProcesses.prepare(options.sessionId, async () => {
95047
95387
  await this.#retireSessionProcess(options.sessionId, existing, async () => await existing.close());
95048
95388
  const runtime = await this.#startRetainedSessionProcess(
95049
95389
  options,
95050
95390
  async () => await AgentSession.start(
95051
95391
  options,
95052
- { kind: "interactive-only" },
95392
+ recovery,
95053
95393
  () => this.#observer,
95054
95394
  this.#store,
95055
95395
  unmeasuredOperationMetrics,
@@ -95227,6 +95567,7 @@ var init_session_manager = __esm({
95227
95567
  });
95228
95568
  });
95229
95569
  prepareExecutionSession = async (options) => this.#operations.run(options, async (options2) => {
95570
+ const recovery = options2.recovery ?? { kind: "interactive-only" };
95230
95571
  this.#assertOpen(options2.sessionId);
95231
95572
  const pending = this.#executionProcesses.pending(options2.sessionId);
95232
95573
  if (pending !== void 0) {
@@ -95235,7 +95576,7 @@ var init_session_manager = __esm({
95235
95576
  }
95236
95577
  const existing = this.#executionProcesses.get(options2.sessionId);
95237
95578
  if (existing === void 0) {
95238
- return (await this.#startPreparation(options2, { kind: "interactive-only" })).prepared();
95579
+ return (await this.#startPreparation(options2, recovery)).prepared();
95239
95580
  }
95240
95581
  if (!existing.belongsTo(options2.agent.id, options2.cwd, options2.additionalDirectories)) {
95241
95582
  throw new AcpExecutionError(
@@ -95245,6 +95586,9 @@ var init_session_manager = __esm({
95245
95586
  );
95246
95587
  }
95247
95588
  if (existing.usesEnvironment(options2.agent.environment)) {
95589
+ if (recovery.kind === "execution-only") {
95590
+ existing.startExecutionOnlyCapture();
95591
+ }
95248
95592
  this.#executionProcesses.touch(options2.sessionId, existing);
95249
95593
  return existing.prepared();
95250
95594
  }
@@ -95539,10 +95883,13 @@ var init_session_manager = __esm({
95539
95883
  "session_identity_mismatch"
95540
95884
  );
95541
95885
  }
95886
+ if (options2.recovery?.kind === "execution-only") {
95887
+ existing.startExecutionOnlyCapture();
95888
+ }
95542
95889
  this.#executionProcesses.touch(options2.sessionId, existing);
95543
95890
  return existing;
95544
95891
  }
95545
- await this.prepareSession(options2);
95892
+ await this.#prepareSession(options2, options2.recovery ?? { kind: "interactive-only" }, "reopen");
95546
95893
  const started = this.#executionProcesses.get(options2.sessionId);
95547
95894
  if (started === void 0) {
95548
95895
  throw new AcpExecutionError("ACP session did not start");
@@ -96100,6 +96447,7 @@ var PromptLifecycle;
96100
96447
  var init_prompt_lifecycle = __esm({
96101
96448
  "src/session/prompt/prompt-lifecycle.ts"() {
96102
96449
  "use strict";
96450
+ init_protocol();
96103
96451
  PromptLifecycle = class {
96104
96452
  #sink;
96105
96453
  #terminal = /* @__PURE__ */ new Map();
@@ -96181,6 +96529,13 @@ var init_prompt_lifecycle = __esm({
96181
96529
  }
96182
96530
  this.#queued.delete(commandId);
96183
96531
  };
96532
+ replayOutcome = async (sessionId, event) => {
96533
+ if (event.type !== "lifecycle-metadata" || !("commandId" in event.payload)) {
96534
+ return;
96535
+ }
96536
+ await this.#recordLifecycle(sessionId, event);
96537
+ this.publishTerminal(sessionId, event.payload.commandId);
96538
+ };
96184
96539
  queueChanged = async (sessionId) => {
96185
96540
  await this.#recordLifecycle(sessionId, {
96186
96541
  type: "lifecycle-metadata",
@@ -96214,7 +96569,7 @@ var init_prompt_lifecycle = __esm({
96214
96569
  type: "lifecycle-metadata",
96215
96570
  payload: { kind: "prompt.failed-before-start", commandId, code, message }
96216
96571
  });
96217
- if (!recorded) {
96572
+ if (!recorded || code === SESSION_CLOSED_ERROR_CODE) {
96218
96573
  this.#sink.publishOperationFailure(sessionId, commandId, code, message);
96219
96574
  }
96220
96575
  };
@@ -96239,21 +96594,154 @@ var MAX_TERMINAL_RECEIPTS, ExecutionReceipts;
96239
96594
  var init_execution_receipts = __esm({
96240
96595
  "src/session/turn/execution-receipts.ts"() {
96241
96596
  "use strict";
96597
+ init_protocol();
96598
+ init_zod();
96242
96599
  MAX_TERMINAL_RECEIPTS = 1024;
96243
96600
  ExecutionReceipts = class {
96244
96601
  #executions = /* @__PURE__ */ new Map();
96602
+ #recoveredCommands = /* @__PURE__ */ new Set();
96245
96603
  #terminalCountBySession = /* @__PURE__ */ new Map();
96604
+ #table;
96605
+ #closing = false;
96606
+ #activeCount = 0;
96607
+ #onCount;
96608
+ constructor(table, onCount = () => void 0) {
96609
+ this.#onCount = onCount;
96610
+ this.#table = table;
96611
+ for (const row of table?.list() ?? []) {
96612
+ this.#executions.set(row.commandId, row);
96613
+ this.#recoveredCommands.add(row.commandId);
96614
+ if (!isTerminal(row.state)) {
96615
+ this.#activeCount += 1;
96616
+ }
96617
+ if (isTerminal(row.state)) {
96618
+ this.#terminalCountBySession.set(
96619
+ row.sessionId,
96620
+ (this.#terminalCountBySession.get(row.sessionId) ?? 0) + 1
96621
+ );
96622
+ }
96623
+ }
96624
+ }
96625
+ #save = (commandId, receipt) => {
96626
+ if (!this.#closing) {
96627
+ this.#table?.save({ ...receipt, commandId, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
96628
+ }
96629
+ const previous = this.#executions.get(commandId);
96630
+ const wasActive = previous !== void 0 && !isTerminal(previous.state);
96631
+ if (isTerminal(receipt.state)) {
96632
+ this.#executions.delete(commandId);
96633
+ }
96634
+ this.#executions.set(commandId, receipt);
96635
+ this.#activeCount += Number(!isTerminal(receipt.state)) - Number(wasActive);
96636
+ this.#onCount(this.#activeCount);
96637
+ if (isTerminal(receipt.state) && (previous === void 0 || !isTerminal(previous.state))) {
96638
+ this.#terminalCountBySession.set(
96639
+ receipt.sessionId,
96640
+ (this.#terminalCountBySession.get(receipt.sessionId) ?? 0) + 1
96641
+ );
96642
+ this.pruneTerminalReceipts(receipt.sessionId);
96643
+ }
96644
+ };
96645
+ beginShutdown = () => {
96646
+ this.#closing = true;
96647
+ };
96648
+ outcome = (commandId) => {
96649
+ const receipt = this.#executions.get(commandId);
96650
+ return receipt?.outcome === null || receipt === void 0 ? void 0 : sessionEventPayloadSchema.parse(JSON.parse(receipt.outcome));
96651
+ };
96652
+ recordOutcome = (event) => {
96653
+ if (event.type !== "lifecycle-metadata") {
96654
+ return;
96655
+ }
96656
+ const payload = event.payload;
96657
+ if (payload.kind !== "prompt.completed" && payload.kind !== "prompt.failed" && payload.kind !== "prompt.failed-before-start") {
96658
+ return;
96659
+ }
96660
+ const receipt = this.#executions.get(payload.commandId);
96661
+ if (receipt === void 0) {
96662
+ return;
96663
+ }
96664
+ const sanitized = payload.kind === "prompt.completed" ? payload : { ...payload, message: "The local Agent could not complete the request" };
96665
+ this.#save(payload.commandId, {
96666
+ ...receipt,
96667
+ state: payload.kind === "prompt.completed" ? payload.stopReason === "cancelled" ? "cancelled" : "succeeded" : "failed",
96668
+ outcome: JSON.stringify({ type: "lifecycle-metadata", payload: sanitized, recordedAt: event.recordedAt })
96669
+ });
96670
+ };
96671
+ recover = () => {
96672
+ const recovered = [];
96673
+ for (const [commandId, receipt] of [...this.#executions]) {
96674
+ const event = receipt.outcome === null ? {
96675
+ type: "lifecycle-metadata",
96676
+ payload: receipt.started ? {
96677
+ kind: "prompt.failed",
96678
+ commandId,
96679
+ attemptId: receipt.attemptId,
96680
+ code: "runtime_restarted",
96681
+ message: "The local runtime restarted while this request was running"
96682
+ } : {
96683
+ kind: "prompt.failed-before-start",
96684
+ commandId,
96685
+ code: "runtime_restarted",
96686
+ message: "The local runtime restarted before this request started"
96687
+ }
96688
+ } : sessionEventPayloadSchema.parse(JSON.parse(receipt.outcome));
96689
+ const recordedAt = receipt.outcome === null ? (/* @__PURE__ */ new Date()).toISOString() : external_exports.object({ recordedAt: external_exports.string() }).parse(JSON.parse(receipt.outcome)).recordedAt;
96690
+ recovered.push({ sessionId: receipt.sessionId, event, recordedAt });
96691
+ if (!isTerminal(receipt.state)) {
96692
+ const state = event.type === "lifecycle-metadata" && event.payload.kind === "prompt.completed" ? event.payload.stopReason === "cancelled" ? "cancelled" : "succeeded" : "failed";
96693
+ this.#save(commandId, { ...receipt, state, outcome: JSON.stringify({ ...event, recordedAt }) });
96694
+ }
96695
+ }
96696
+ return recovered;
96697
+ };
96698
+ terminalOutcomes = (sessionId) => [...this.#executions.values()].flatMap((receipt) => {
96699
+ if (receipt.sessionId !== sessionId || receipt.outcome === null) {
96700
+ return [];
96701
+ }
96702
+ const decoded = JSON.parse(receipt.outcome);
96703
+ return [
96704
+ {
96705
+ event: sessionEventPayloadSchema.parse(decoded),
96706
+ recordedAt: external_exports.object({ recordedAt: external_exports.string() }).parse(decoded).recordedAt
96707
+ }
96708
+ ];
96709
+ });
96246
96710
  claimExecution = (commandId, sessionId, commandJson) => {
96247
96711
  const commandHash = createHash7("sha256").update(commandJson).digest("hex");
96248
- const existing = this.#executions.get(commandId);
96712
+ let existing = this.#executions.get(commandId);
96713
+ const previous = this.outcome(commandId);
96714
+ if (existing?.sessionId === sessionId && !existing.started && previous?.type === "lifecycle-metadata" && previous.payload.kind === "prompt.failed-before-start" && previous.payload.code === "runtime_restarted") {
96715
+ this.#executions.delete(commandId);
96716
+ this.#recoveredCommands.delete(commandId);
96717
+ this.#table?.remove(commandId);
96718
+ this.#terminalCountBySession.set(
96719
+ sessionId,
96720
+ Math.max(0, (this.#terminalCountBySession.get(sessionId) ?? 1) - 1)
96721
+ );
96722
+ existing = void 0;
96723
+ }
96249
96724
  if (existing !== void 0) {
96250
96725
  if (existing.sessionId !== sessionId || existing.commandHash !== commandHash) {
96251
96726
  return { kind: "conflict" };
96252
96727
  }
96253
- return { attemptId: existing.attemptId, commandId, kind: "duplicate", state: existing.state };
96728
+ return {
96729
+ attemptId: existing.attemptId,
96730
+ commandId,
96731
+ kind: "duplicate",
96732
+ state: existing.state,
96733
+ outcome: this.#recoveredCommands.has(commandId) ? this.outcome(commandId) : void 0
96734
+ };
96254
96735
  }
96255
96736
  const attemptId = randomUUID14();
96256
- this.#executions.set(commandId, { attemptId, commandHash, sessionId, state: "dispatching" });
96737
+ this.#save(commandId, {
96738
+ attemptId,
96739
+ commandHash,
96740
+ sessionId,
96741
+ state: "dispatching",
96742
+ started: false,
96743
+ outcome: null
96744
+ });
96257
96745
  return { attemptId, commandId, kind: "accepted", state: "dispatching" };
96258
96746
  };
96259
96747
  transitionExecution = (commandId, state) => {
@@ -96268,16 +96756,13 @@ var init_execution_receipts = __esm({
96268
96756
  if (!allowed) {
96269
96757
  throw new Error("The runtime execution transition is invalid");
96270
96758
  }
96271
- this.#executions.set(commandId, { ...current, state });
96272
- if (isTerminal(state)) {
96273
- this.#terminalCountBySession.set(
96274
- current.sessionId,
96275
- (this.#terminalCountBySession.get(current.sessionId) ?? 0) + 1
96276
- );
96277
- this.pruneTerminalReceipts(current.sessionId);
96759
+ if (state === "running") {
96760
+ this.#save(commandId, { ...current, state, started: true });
96761
+ } else if (isTerminal(state)) {
96762
+ this.#save(commandId, { ...current, state });
96278
96763
  }
96279
96764
  };
96280
- /** Receipts disappear when the runtime process exits. */
96765
+ /** Only commands without a terminal result can still be cancelled. */
96281
96766
  findUnsettledAttemptId = (commandId, sessionId) => {
96282
96767
  const receipt = this.#executions.get(commandId);
96283
96768
  return receipt?.sessionId === sessionId && !isTerminal(receipt.state) ? receipt.attemptId : null;
@@ -96288,7 +96773,9 @@ var init_execution_receipts = __esm({
96288
96773
  };
96289
96774
  close = () => {
96290
96775
  this.#executions.clear();
96776
+ this.#recoveredCommands.clear();
96291
96777
  this.#terminalCountBySession.clear();
96778
+ this.#activeCount = 0;
96292
96779
  };
96293
96780
  pruneTerminalReceipts = (sessionId) => {
96294
96781
  if ((this.#terminalCountBySession.get(sessionId) ?? 0) <= MAX_TERMINAL_RECEIPTS) {
@@ -96297,6 +96784,8 @@ var init_execution_receipts = __esm({
96297
96784
  for (const [commandId, receipt] of this.#executions) {
96298
96785
  if (receipt.sessionId === sessionId && isTerminal(receipt.state)) {
96299
96786
  this.#executions.delete(commandId);
96787
+ this.#recoveredCommands.delete(commandId);
96788
+ this.#table?.remove(commandId);
96300
96789
  const remaining = (this.#terminalCountBySession.get(sessionId) ?? 1) - 1;
96301
96790
  if (remaining > 0) {
96302
96791
  this.#terminalCountBySession.set(sessionId, remaining);
@@ -96847,10 +97336,14 @@ function buildSessionLayer(options) {
96847
97336
  const databases = channelSessionDatabasePaths(stateDirectory, channelId);
96848
97337
  const sessionDatabase = openAcpSessionDatabase(databases.sessions);
96849
97338
  let events;
97339
+ let operations;
96850
97340
  try {
97341
+ operations = new ExecutionReceipts(new ExecutionReceiptTable(sessionDatabase), options.onExecutionCountChange);
96851
97342
  events = new CachedSessionEventStore(
96852
97343
  new SessionEventCacheTable(openSessionEventCacheDatabase(databases.events)),
96853
- new SqliteSessionLiveEventPosition(new LiveEventPositionTable(sessionDatabase))
97344
+ new SqliteSessionLiveEventPosition(new LiveEventPositionTable(sessionDatabase)),
97345
+ void 0,
97346
+ (sessionId) => operations.terminalOutcomes(sessionId)
96854
97347
  );
96855
97348
  } catch (error62) {
96856
97349
  sessionDatabase.close();
@@ -96858,11 +97351,13 @@ function buildSessionLayer(options) {
96858
97351
  }
96859
97352
  const sessionStore = new SqliteSessionStore(new AcpSessionTable(sessionDatabase), events);
96860
97353
  const externalContent = new LocalExternalContentStore(stateDirectory);
96861
- const operations = new ExecutionReceipts();
96862
97354
  const activeSessions = new SessionTurns(operations, options.turnActivity);
96863
97355
  const directories = new WorkspaceDirectoryService(bindingId, defaultWorkspace);
96864
97356
  const manager = new SessionManager2({
96865
- observer: new SessionLayerObserver((event) => eventProjection.record(event), sink.publishInfo),
97357
+ observer: new SessionLayerObserver((event) => {
97358
+ operations.recordOutcome(event);
97359
+ eventProjection.record(event);
97360
+ }, sink.publishInfo),
96866
97361
  store: sessionStore,
96867
97362
  adapters: harnessAdapters,
96868
97363
  sessionIdentity: "native",
@@ -96883,7 +97378,15 @@ function buildSessionLayer(options) {
96883
97378
  });
96884
97379
  const eventProjection = new PromptLifecycle(
96885
97380
  sink,
96886
- (sessionId, event) => manager.recordLifecycleEvent(sessionId, event),
97381
+ async (sessionId, event) => {
97382
+ if (await manager.recordLifecycleEvent(sessionId, event)) {
97383
+ return true;
97384
+ }
97385
+ const recorded = events.append(sessionId, event);
97386
+ operations.recordOutcome(recorded);
97387
+ eventProjection.record(recorded);
97388
+ return true;
97389
+ },
96887
97390
  activeSessions.recordSessionEvent
96888
97391
  );
96889
97392
  const nativeSessions = new NativeSessionService(
@@ -96906,7 +97409,13 @@ function buildSessionLayer(options) {
96906
97409
  );
96907
97410
  })
96908
97411
  );
97412
+ const recoveredEvents = operations.recover().map(({ sessionId, event, recordedAt }) => events.append(sessionId, event, recordedAt));
96909
97413
  return {
97414
+ publishRecoveredOutcomes: () => {
97415
+ for (const event of recoveredEvents) {
97416
+ sink.publish(event);
97417
+ }
97418
+ },
96910
97419
  activeSessions,
96911
97420
  directories,
96912
97421
  eventProjection,
@@ -96924,6 +97433,7 @@ var init_session_layer = __esm({
96924
97433
  init_registry();
96925
97434
  init_content_externalizer();
96926
97435
  init_acp_session_table();
97436
+ init_execution_receipt_table();
96927
97437
  init_live_event_position_table();
96928
97438
  init_session_event_cache_table();
96929
97439
  init_local_databases();
@@ -98804,12 +99314,14 @@ async function executePrompt({
98804
99314
  const appliedSettings = command.sessionConfigurations === void 0 ? void 0 : agentForSessionConfiguration(agent, command.sessionConfigurations);
98805
99315
  const executionAgent = appliedSettings?.agent ?? agent;
98806
99316
  const sessionConfigurations = appliedSettings?.sessionConfigurations;
99317
+ const recovery = command.historyPolicy === SessionHistoryPolicy.ExecutionOnly ? { kind: "execution-only" } : { kind: "interactive-only" };
98807
99318
  const preparation = {
98808
99319
  additionalDirectories: resolved.additionalDirectories,
98809
99320
  agent: executionAgent,
98810
99321
  cwd: resolved.cwd,
98811
99322
  sessionId: command.sessionId,
98812
- signal
99323
+ signal,
99324
+ recovery
98813
99325
  };
98814
99326
  let prepared = await sessions.prepareExecutionSession(preparation);
98815
99327
  if (command.sessionConfigurations !== void 0) {
@@ -98878,7 +99390,8 @@ async function executePrompt({
98878
99390
  resources: attachments.resources,
98879
99391
  sessionId: command.sessionId,
98880
99392
  signal,
98881
- targetCommandId: command.targetCommandId
99393
+ targetCommandId: command.targetCommandId,
99394
+ recovery
98882
99395
  });
98883
99396
  stopReason = result.stopReason;
98884
99397
  }
@@ -99076,6 +99589,12 @@ var init_prompt2 = __esm({
99076
99589
  return;
99077
99590
  }
99078
99591
  if (receipt.kind === "duplicate") {
99592
+ const outcome = receipt.outcome;
99593
+ if (outcome !== void 0) {
99594
+ void context.eventProjection.replayOutcome(message.sessionId, outcome).catch((cause) => {
99595
+ console.error("Could not replay the persisted execution outcome", cause);
99596
+ });
99597
+ }
99079
99598
  return;
99080
99599
  }
99081
99600
  const turn = context.sessionTurns.start(message, receipt.attemptId);
@@ -100282,7 +100801,8 @@ async function runRuntime(config2, parentSignal, timer = systemTimer, options =
100282
100801
  resolveAgent: (agentId, resolveSignal) => resolveCatalogAgent(config2, agentId, resolveSignal),
100283
100802
  sink: publisher,
100284
100803
  stateDirectory: config2.localContext.stateDirectory,
100285
- timer
100804
+ timer,
100805
+ onExecutionCountChange: options.onExecutionCountChange ?? (() => void 0)
100286
100806
  });
100287
100807
  const {
100288
100808
  activeSessions,
@@ -100352,6 +100872,7 @@ async function runRuntime(config2, parentSignal, timer = systemTimer, options =
100352
100872
  reportUpgradeAvailability,
100353
100873
  diagnostics,
100354
100874
  onConnected: () => {
100875
+ layer.publishRecoveredOutcomes();
100355
100876
  if (catalogPublished) {
100356
100877
  options.onConnectionStatus?.("online");
100357
100878
  }
@@ -100374,6 +100895,7 @@ async function runRuntime(config2, parentSignal, timer = systemTimer, options =
100374
100895
  }
100375
100896
  } finally {
100376
100897
  shutdown.abort();
100898
+ operations.beginShutdown();
100377
100899
  await activeSessions.close();
100378
100900
  try {
100379
100901
  await catalogPublication;
@@ -100468,6 +100990,10 @@ var init_engine = __esm({
100468
100990
  MobiusRuntimeEngine = class {
100469
100991
  #config;
100470
100992
  #options;
100993
+ #activeTurnCount = 0;
100994
+ get activeTurnCount() {
100995
+ return this.#activeTurnCount;
100996
+ }
100471
100997
  #state = new IdleMobiusRuntimeState();
100472
100998
  constructor(config2, options = {}) {
100473
100999
  validateRuntimeConfiguration(config2);
@@ -100483,11 +101009,15 @@ var init_engine = __esm({
100483
101009
  try {
100484
101010
  await runRuntime(this.#config, signal, void 0, {
100485
101011
  ...this.#options.turnActivity === void 0 ? {} : { turnActivity: this.#options.turnActivity },
101012
+ onExecutionCountChange: (count) => {
101013
+ this.#activeTurnCount = count;
101014
+ },
100486
101015
  ...this.#options.onConnectionStatus === void 0 ? {} : { onConnectionStatus: this.#options.onConnectionStatus },
100487
101016
  ...this.#options.reportUpgradeAvailability === void 0 ? {} : { reportUpgradeAvailability: this.#options.reportUpgradeAvailability },
100488
101017
  ...this.#options.processLogsDirectory === void 0 ? {} : { processLogsDirectory: this.#options.processLogsDirectory }
100489
101018
  });
100490
101019
  } finally {
101020
+ this.#activeTurnCount = 0;
100491
101021
  this.#state = new FinishedMobiusRuntimeState();
100492
101022
  }
100493
101023
  };