@moxt-ai/mobius 0.0.22 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19006,7 +19006,9 @@ var sessionCapabilitiesSchema = external_exports.strictObject({
19006
19006
  imagePrompt: external_exports.boolean(),
19007
19007
  listSessions: external_exports.boolean(),
19008
19008
  loadSession: external_exports.boolean(),
19009
+ promptQueueing: external_exports.boolean().default(false),
19009
19010
  resumeSession: external_exports.boolean(),
19011
+ steering: external_exports.boolean().default(false),
19010
19012
  unstableForkSession: external_exports.boolean().default(false)
19011
19013
  }).readonly();
19012
19014
  var sessionCommandSchema = external_exports.strictObject({
@@ -19348,6 +19350,11 @@ var AGENT_ACTIVITY_LOCATION_LIMIT = 12;
19348
19350
  var AGENT_ACTIVITY_LOCATION_LENGTH_LIMIT = 1024;
19349
19351
  var SESSION_COMPLETED_KIND = "session.completed";
19350
19352
  var SESSION_FAILED_KIND = "session.failed";
19353
+ var SessionPromptDisposition = Object.freeze({
19354
+ Queue: "queue",
19355
+ Reject: "reject",
19356
+ Steer: "steer"
19357
+ });
19351
19358
 
19352
19359
  // ../protocol/src/codecs/messages.ts
19353
19360
  var IDENTIFIER_PATTERN2 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
@@ -19422,6 +19429,19 @@ function readAllowedString(value, key, allowedValues) {
19422
19429
  }
19423
19430
  return field;
19424
19431
  }
19432
+ function readPromptDisposition(value) {
19433
+ const field = value["disposition"];
19434
+ if (field === void 0 || field === SessionPromptDisposition.Reject) {
19435
+ return SessionPromptDisposition.Reject;
19436
+ }
19437
+ if (field === SessionPromptDisposition.Queue) {
19438
+ return SessionPromptDisposition.Queue;
19439
+ }
19440
+ if (field === SessionPromptDisposition.Steer) {
19441
+ return SessionPromptDisposition.Steer;
19442
+ }
19443
+ throw new ProtocolMessageError("Invalid value field: disposition");
19444
+ }
19425
19445
  function readSequence(value, key) {
19426
19446
  const field = value[key];
19427
19447
  if (typeof field !== "number" || !Number.isSafeInteger(field) || field < 0) {
@@ -19546,11 +19566,13 @@ function decodeSessionPromptCommand(encoded) {
19546
19566
  "bindingId",
19547
19567
  "commandId",
19548
19568
  "directoryId",
19569
+ "disposition",
19549
19570
  "images",
19550
19571
  "kind",
19551
19572
  "prompt",
19552
19573
  "resources",
19553
- "sessionId"
19574
+ "sessionId",
19575
+ "targetCommandId"
19554
19576
  ]);
19555
19577
  assertKind(value, SESSION_PROMPT_KIND);
19556
19578
  const prompt = readText(value, "prompt", MAX_PROMPT_LENGTH);
@@ -19563,17 +19585,25 @@ function decodeSessionPromptCommand(encoded) {
19563
19585
  if (prompt.trim().length === 0 && images.length === 0 && resources.length === 0) {
19564
19586
  throw new ProtocolMessageError("Prompt must contain content");
19565
19587
  }
19588
+ const disposition = readPromptDisposition(value);
19589
+ const rawTargetCommandId = value["targetCommandId"];
19590
+ const targetCommandId = rawTargetCommandId === void 0 ? "" : readText(value, "targetCommandId", 128);
19591
+ if (disposition === SessionPromptDisposition.Reject && targetCommandId.length > 0 || disposition !== SessionPromptDisposition.Reject && !IDENTIFIER_PATTERN2.test(targetCommandId)) {
19592
+ throw new ProtocolMessageError("Prompt disposition has an invalid target command");
19593
+ }
19566
19594
  return {
19567
19595
  additionalDirectoryIds: readDirectoryIdentifiers(value, "additionalDirectoryIds", 16),
19568
19596
  agentId: readIdentifier(value, "agentId"),
19569
19597
  bindingId: readIdentifier(value, "bindingId"),
19570
19598
  commandId: readIdentifier(value, "commandId"),
19571
19599
  directoryId: readDirectoryIdentifier(value, "directoryId"),
19600
+ disposition,
19572
19601
  images,
19573
19602
  kind: SESSION_PROMPT_KIND,
19574
19603
  prompt,
19575
19604
  resources,
19576
- sessionId: readIdentifier(value, "sessionId")
19605
+ sessionId: readIdentifier(value, "sessionId"),
19606
+ targetCommandId
19577
19607
  };
19578
19608
  }
19579
19609
  function decodeSessionCancelCommand(encoded) {
@@ -19623,11 +19653,12 @@ function decodeSessionStartedEvent(encoded) {
19623
19653
  }
19624
19654
  function decodeAgentMessageDeltaEvent(encoded) {
19625
19655
  const value = parseJson2(encoded);
19626
- assertExactKeys(value, ["commandId", "kind", "sessionId", "text"]);
19656
+ assertExactKeys(value, ["commandId", "kind", "messageId", "sessionId", "text"]);
19627
19657
  assertKind(value, AGENT_MESSAGE_DELTA_KIND);
19628
19658
  return {
19629
19659
  commandId: readIdentifier(value, "commandId"),
19630
19660
  kind: AGENT_MESSAGE_DELTA_KIND,
19661
+ messageId: value["messageId"] === void 0 ? "" : readText(value, "messageId", 255),
19631
19662
  sessionId: readIdentifier(value, "sessionId"),
19632
19663
  text: readString(value, "text", MAX_EVENT_TEXT_LENGTH)
19633
19664
  };
@@ -20273,6 +20304,8 @@ var sessionHistorySchema = external_exports.discriminatedUnion("status", [
20273
20304
  var NATIVE_SESSION_REQUEST_KIND = "native.session.request";
20274
20305
  var NATIVE_SESSION_RESULT_KIND = "native.session.result";
20275
20306
  var NATIVE_SESSION_CANCEL_KIND = "native.session.cancel";
20307
+ var NATIVE_SESSION_TURN_FORK_REQUEST_KIND = "native.session.turn-fork.request";
20308
+ var NATIVE_SESSION_TURN_FORK_KIND = "native.session.turn-fork";
20276
20309
  var identifier = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/);
20277
20310
  var directoryId = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,2047}$/);
20278
20311
  var envelope = {
@@ -20287,6 +20320,28 @@ var identity = {
20287
20320
  bindingId: identifier,
20288
20321
  directoryId
20289
20322
  };
20323
+ var turnForkIdentity = {
20324
+ additionalDirectoryIds: external_exports.array(directoryId).max(16).readonly(),
20325
+ agentId: identifier,
20326
+ bindingId: identifier,
20327
+ directoryId,
20328
+ requestId: identifier,
20329
+ sessionId: identifier,
20330
+ sourceCommandId: identifier,
20331
+ targetSessionId: identifier
20332
+ };
20333
+ var nativeSessionTurnForkRequestSchema = external_exports.strictObject({
20334
+ ...turnForkIdentity,
20335
+ kind: external_exports.literal(NATIVE_SESSION_TURN_FORK_REQUEST_KIND)
20336
+ }).readonly();
20337
+ var nativeSessionTurnForkCommandSchema = external_exports.strictObject({
20338
+ ...turnForkIdentity,
20339
+ kind: external_exports.literal(NATIVE_SESSION_TURN_FORK_KIND),
20340
+ messageFingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
20341
+ messageId: external_exports.string().min(1).max(255),
20342
+ messageOccurrence: external_exports.number().int().positive(),
20343
+ title: external_exports.string().max(500)
20344
+ }).readonly();
20290
20345
  var nativeSessionCommandSchema = external_exports.discriminatedUnion("action", [
20291
20346
  external_exports.strictObject({
20292
20347
  ...identity,
@@ -20361,6 +20416,7 @@ function decode4(schema, encoded) {
20361
20416
  var decodeNativeSessionCommand = (encoded) => decode4(nativeSessionCommandSchema, encoded);
20362
20417
  var encodeNativeSessionResult = (value) => JSON.stringify(nativeSessionResultSchema.parse(value));
20363
20418
  var decodeNativeSessionCancel = (encoded) => decode4(nativeSessionCancelSchema, encoded);
20419
+ var decodeNativeSessionTurnForkCommand = (encoded) => decode4(nativeSessionTurnForkCommandSchema, encoded);
20364
20420
 
20365
20421
  // ../protocol/src/session-events.ts
20366
20422
  var SESSION_EVENT_KIND = "session.event";
@@ -20449,6 +20505,16 @@ var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSc
20449
20505
  import { realpathSync, statSync } from "node:fs";
20450
20506
 
20451
20507
  // src/acp/session-capabilities.ts
20508
+ function isRecord3(value) {
20509
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20510
+ }
20511
+ function hasTrueChildField(value, childKey, fieldKey) {
20512
+ if (!isRecord3(value)) {
20513
+ return false;
20514
+ }
20515
+ const child = Reflect.get(value, childKey);
20516
+ return isRecord3(child) && Reflect.get(child, fieldKey) === true;
20517
+ }
20452
20518
  function unsupportedSessionCapabilities() {
20453
20519
  return Object.freeze({
20454
20520
  additionalDirectories: false,
@@ -20459,11 +20525,14 @@ function unsupportedSessionCapabilities() {
20459
20525
  imagePrompt: false,
20460
20526
  listSessions: false,
20461
20527
  loadSession: false,
20528
+ promptQueueing: false,
20462
20529
  resumeSession: false,
20530
+ steering: false,
20463
20531
  unstableForkSession: false
20464
20532
  });
20465
20533
  }
20466
- function normalizeAcpCapabilities(capabilities) {
20534
+ function normalizeAcpCapabilities(initialized) {
20535
+ const capabilities = initialized.agentCapabilities ?? {};
20467
20536
  const session = capabilities.sessionCapabilities;
20468
20537
  const prompt = capabilities.promptCapabilities;
20469
20538
  return Object.freeze({
@@ -20475,7 +20544,9 @@ function normalizeAcpCapabilities(capabilities) {
20475
20544
  imagePrompt: prompt?.image === true,
20476
20545
  listSessions: session?.list != null,
20477
20546
  loadSession: capabilities.loadSession === true,
20547
+ promptQueueing: hasTrueChildField(capabilities._meta, "claudeCode", "promptQueueing"),
20478
20548
  resumeSession: session?.resume != null,
20549
+ steering: hasTrueChildField(initialized._meta, "steering", "supported"),
20479
20550
  unstableForkSession: session?.fork != null
20480
20551
  });
20481
20552
  }
@@ -21092,7 +21163,7 @@ import {
21092
21163
 
21093
21164
  // src/acp/activity.ts
21094
21165
  var TRUNCATION_MARKER = "\n\u2026 output truncated by Mobius";
21095
- function isRecord3(value) {
21166
+ function isRecord4(value) {
21096
21167
  return typeof value === "object" && value !== null && !Array.isArray(value);
21097
21168
  }
21098
21169
  var AcpActivity = class _AcpActivity {
@@ -21359,7 +21430,7 @@ function contentPhase(content) {
21359
21430
  return "";
21360
21431
  }
21361
21432
  const codex = metadata["codex"];
21362
- if (!isRecord3(codex)) {
21433
+ if (!isRecord4(codex)) {
21363
21434
  return "";
21364
21435
  }
21365
21436
  const phase = codex["phase"];
@@ -21597,7 +21668,7 @@ var SessionConfigProtocolError = class extends Error {
21597
21668
  this.name = "SessionConfigProtocolError";
21598
21669
  }
21599
21670
  };
21600
- function isRecord4(value) {
21671
+ function isRecord5(value) {
21601
21672
  return typeof value === "object" && value !== null && !Array.isArray(value);
21602
21673
  }
21603
21674
  function readString2(value, fallback = "") {
@@ -21624,7 +21695,7 @@ function normalizeConfigValues(value) {
21624
21695
  }
21625
21696
  const normalized = [];
21626
21697
  for (const entry of value) {
21627
- if (!isRecord4(entry)) {
21698
+ if (!isRecord5(entry)) {
21628
21699
  continue;
21629
21700
  }
21630
21701
  const groupedOptions = entry["options"];
@@ -21632,7 +21703,7 @@ function normalizeConfigValues(value) {
21632
21703
  const groupId = readString2(entry["group"]);
21633
21704
  const groupName = readString2(entry["name"]);
21634
21705
  for (const groupedOption of groupedOptions) {
21635
- if (isRecord4(groupedOption)) {
21706
+ if (isRecord5(groupedOption)) {
21636
21707
  normalized.push(normalizeConfigValue(groupedOption, groupId, groupName));
21637
21708
  }
21638
21709
  }
@@ -21646,7 +21717,7 @@ function normalizeConfigOptions(value, modes) {
21646
21717
  const normalized = [];
21647
21718
  if (Array.isArray(value)) {
21648
21719
  for (const option2 of value) {
21649
- if (!isRecord4(option2)) {
21720
+ if (!isRecord5(option2)) {
21650
21721
  continue;
21651
21722
  }
21652
21723
  const id = truncate2(readString2(option2["id"]), 128);
@@ -21685,12 +21756,12 @@ function normalizeConfigOptions(value, modes) {
21685
21756
  }
21686
21757
  }
21687
21758
  }
21688
- if (isRecord4(modes)) {
21759
+ if (isRecord5(modes)) {
21689
21760
  const values = [];
21690
21761
  const availableModes = modes["availableModes"];
21691
21762
  if (Array.isArray(availableModes)) {
21692
21763
  for (const mode of availableModes) {
21693
- if (!isRecord4(mode)) {
21764
+ if (!isRecord5(mode)) {
21694
21765
  continue;
21695
21766
  }
21696
21767
  const id = truncate2(readString2(mode["id"]), 500);
@@ -21768,7 +21839,7 @@ function ensureReasoningEffortOption(current) {
21768
21839
  return [...current.slice(0, MAXIMUM_CONFIG_OPTIONS - 2), model, unavailable];
21769
21840
  }
21770
21841
  function normalizeStandardLifecycle(response) {
21771
- const lifecycle = isRecord4(response) ? response : {};
21842
+ const lifecycle = isRecord5(response) ? response : {};
21772
21843
  return ensureReasoningEffortOption(normalizeConfigOptions(lifecycle["configOptions"], lifecycle["modes"]));
21773
21844
  }
21774
21845
  function replaceStandardConfigOptions(current, value) {
@@ -21810,7 +21881,7 @@ var StandardSessionConfigProtocol = class {
21810
21881
  "ACP agent exited while changing session configuration",
21811
21882
  change.signal
21812
21883
  );
21813
- if (!isRecord4(response) || !Array.isArray(response["configOptions"])) {
21884
+ if (!isRecord5(response) || !Array.isArray(response["configOptions"])) {
21814
21885
  throw new SessionConfigProtocolError("The Agent returned an invalid session configuration response");
21815
21886
  }
21816
21887
  return replaceStandardConfigOptions(current, response["configOptions"]);
@@ -21824,18 +21895,18 @@ var standardSessionConfigProtocol = new StandardSessionConfigProtocol();
21824
21895
  var LEGACY_MODEL_CONFIG_ID = "grok_build_legacy_model";
21825
21896
  var LEGACY_REASONING_EFFORT_CONFIG_ID = "grok_build_legacy_reasoning_effort";
21826
21897
  function legacyOptions(response) {
21827
- if (!isRecord4(response)) {
21898
+ if (!isRecord5(response)) {
21828
21899
  return [];
21829
21900
  }
21830
21901
  const metadata = response["_meta"];
21831
- if (!isRecord4(metadata)) {
21902
+ if (!isRecord5(metadata)) {
21832
21903
  return [];
21833
21904
  }
21834
21905
  const sessionConfig = metadata["x.ai/sessionConfig"];
21835
- if (!isRecord4(sessionConfig) || !Array.isArray(sessionConfig["options"])) {
21906
+ if (!isRecord5(sessionConfig) || !Array.isArray(sessionConfig["options"])) {
21836
21907
  return [];
21837
21908
  }
21838
- return sessionConfig["options"].slice(0, 500).filter(isRecord4);
21909
+ return sessionConfig["options"].slice(0, 500).filter(isRecord5);
21839
21910
  }
21840
21911
  function legacyValue(option2) {
21841
21912
  const value = readString2(option2["id"]);
@@ -21848,7 +21919,7 @@ function legacyValue(option2) {
21848
21919
  };
21849
21920
  }
21850
21921
  function modelsRecord(response) {
21851
- if (!isRecord4(response) || !isRecord4(response["models"])) {
21922
+ if (!isRecord5(response) || !isRecord5(response["models"])) {
21852
21923
  return {};
21853
21924
  }
21854
21925
  return response["models"];
@@ -21867,7 +21938,7 @@ function legacyModelOptions(response) {
21867
21938
  }
21868
21939
  if (values.length === 0 && Array.isArray(models["availableModels"])) {
21869
21940
  for (const model of models["availableModels"]) {
21870
- if (!isRecord4(model)) {
21941
+ if (!isRecord5(model)) {
21871
21942
  continue;
21872
21943
  }
21873
21944
  const value = readString2(model["modelId"]).slice(0, 500);
@@ -21947,7 +22018,7 @@ function legacyReasoningEffortOptions(response) {
21947
22018
  return [result];
21948
22019
  }
21949
22020
  function normalizeGrokLifecycle(response) {
21950
- const lifecycle = isRecord4(response) ? response : {};
22021
+ const lifecycle = isRecord5(response) ? response : {};
21951
22022
  const standard = normalizeConfigOptions(lifecycle["configOptions"], lifecycle["modes"]);
21952
22023
  const normalized = [...standard];
21953
22024
  if (!normalized.some((option2) => option2.category === "model")) {
@@ -21966,11 +22037,11 @@ function updateCurrentValue2(current, category, value) {
21966
22037
  return current.map((option2) => option2.category === category ? { ...option2, currentValue: value } : option2);
21967
22038
  }
21968
22039
  function validateGrokResponse(response, modelId) {
21969
- if (!isRecord4(response) || !isRecord4(response["_meta"])) {
22040
+ if (!isRecord5(response) || !isRecord5(response["_meta"])) {
21970
22041
  throw new SessionConfigProtocolError("Grok Build returned an invalid session configuration response");
21971
22042
  }
21972
22043
  const model = response["_meta"]["model"];
21973
- const appliedModel = isRecord4(model) ? model["Ok"] : model;
22044
+ const appliedModel = isRecord5(model) ? model["Ok"] : model;
21974
22045
  if (appliedModel !== modelId) {
21975
22046
  throw new SessionConfigProtocolError("Grok Build returned an invalid session configuration response");
21976
22047
  }
@@ -22413,7 +22484,7 @@ function createWritableAcpStream(output2) {
22413
22484
  function truncate3(value, maximumLength) {
22414
22485
  return value.length <= maximumLength ? value : `${value.slice(0, Math.max(0, maximumLength - 1))}\u2026`;
22415
22486
  }
22416
- function isRecord5(value) {
22487
+ function isRecord6(value) {
22417
22488
  return typeof value === "object" && value !== null && !Array.isArray(value);
22418
22489
  }
22419
22490
  function readString4(value, fallback = "") {
@@ -22422,6 +22493,11 @@ function readString4(value, fallback = "") {
22422
22493
  function arraysEqual(left, right) {
22423
22494
  return left.length === right.length && left.every((value, index) => value === right[index]);
22424
22495
  }
22496
+ function recordsEqual(left, right) {
22497
+ const leftEntries = Object.entries(left);
22498
+ const rightEntries = Object.entries(right);
22499
+ return leftEntries.length === rightEntries.length && leftEntries.every(([name, value]) => right[name] === value);
22500
+ }
22425
22501
  function elicitationFieldType(value) {
22426
22502
  if (value === "boolean") {
22427
22503
  return "boolean";
@@ -22442,7 +22518,7 @@ function normalizeElicitationOptions(schema) {
22442
22518
  const oneOf = schema["oneOf"];
22443
22519
  if (Array.isArray(oneOf)) {
22444
22520
  for (const option2 of oneOf) {
22445
- if (isRecord5(option2) && typeof option2["const"] === "string") {
22521
+ if (isRecord6(option2) && typeof option2["const"] === "string") {
22446
22522
  normalized.push({
22447
22523
  description: truncate3(readString4(option2["description"]), 500),
22448
22524
  label: truncate3(readString4(option2["title"], option2["const"]), 255),
@@ -22462,7 +22538,7 @@ function normalizeElicitationOptions(schema) {
22462
22538
  return normalized.slice(0, 200);
22463
22539
  }
22464
22540
  const items = schema["items"];
22465
- if (!isRecord5(items)) {
22541
+ if (!isRecord6(items)) {
22466
22542
  return normalized;
22467
22543
  }
22468
22544
  const anyOf = items["anyOf"];
@@ -22475,7 +22551,7 @@ function numericString(value) {
22475
22551
  return typeof value === "number" && Number.isFinite(value) ? value.toString() : "";
22476
22552
  }
22477
22553
  function normalizeElicitationFields(requestedSchema) {
22478
- if (!isRecord5(requestedSchema) || !isRecord5(requestedSchema["properties"])) {
22554
+ if (!isRecord6(requestedSchema) || !isRecord6(requestedSchema["properties"])) {
22479
22555
  return [];
22480
22556
  }
22481
22557
  const properties = requestedSchema["properties"];
@@ -22485,7 +22561,7 @@ function normalizeElicitationFields(requestedSchema) {
22485
22561
  );
22486
22562
  const normalized = [];
22487
22563
  for (const [name, property] of Object.entries(properties)) {
22488
- if (!isRecord5(property)) {
22564
+ if (!isRecord6(property)) {
22489
22565
  continue;
22490
22566
  }
22491
22567
  const type = elicitationFieldType(property["type"]);
@@ -22576,7 +22652,7 @@ function validateElicitationContent(encoded, requestedSchema) {
22576
22652
  "invalid_elicitation_response"
22577
22653
  );
22578
22654
  }
22579
- if (!isRecord5(decoded) || !isRecord5(requestedSchema) || !isRecord5(requestedSchema["properties"])) {
22655
+ if (!isRecord6(decoded) || !isRecord6(requestedSchema) || !isRecord6(requestedSchema["properties"])) {
22580
22656
  throw new AcpExecutionError(
22581
22657
  "Elicitation response does not match the requested form",
22582
22658
  {},
@@ -22596,7 +22672,7 @@ function validateElicitationContent(encoded, requestedSchema) {
22596
22672
  const validated = {};
22597
22673
  for (const [name, value] of Object.entries(decoded)) {
22598
22674
  const property = properties[name];
22599
- if (!isRecord5(property)) {
22675
+ if (!isRecord6(property)) {
22600
22676
  throw new AcpExecutionError(
22601
22677
  `Elicitation field ${name} was not requested`,
22602
22678
  {},
@@ -22738,6 +22814,7 @@ var AcpProcessSession = class _AcpProcessSession {
22738
22814
  #child;
22739
22815
  #connection;
22740
22816
  #cwd;
22817
+ #environment;
22741
22818
  #getObserver;
22742
22819
  #mobiusSessionId;
22743
22820
  #pendingElicitations = /* @__PURE__ */ new Map();
@@ -22759,7 +22836,9 @@ var AcpProcessSession = class _AcpProcessSession {
22759
22836
  imagePrompt: false,
22760
22837
  listSessions: false,
22761
22838
  loadSession: false,
22839
+ promptQueueing: false,
22762
22840
  resumeSession: false,
22841
+ steering: false,
22763
22842
  unstableForkSession: false
22764
22843
  };
22765
22844
  #commands = [];
@@ -22779,6 +22858,7 @@ var AcpProcessSession = class _AcpProcessSession {
22779
22858
  this.#additionalDirectories = [...additionalDirectories];
22780
22859
  this.#agentId = agent.id;
22781
22860
  this.#cwd = cwd;
22861
+ this.#environment = agent.environment;
22782
22862
  this.#getObserver = getObserver;
22783
22863
  this.#mobiusSessionId = mobiusSessionId;
22784
22864
  this.#sessionConfigProtocol = sessionConfigProtocolForAgent(agent.id);
@@ -22864,7 +22944,18 @@ var AcpProcessSession = class _AcpProcessSession {
22864
22944
  store,
22865
22945
  async (runtime) => await runtime.#initializeAgent(options.signal)
22866
22946
  );
22947
+ static startSessionForkAtTurn = async (options, sourceNativeSessionId, forkPoint, getObserver, store) => await _AcpProcessSession.#start(
22948
+ options,
22949
+ getObserver,
22950
+ store,
22951
+ async (runtime) => await runtime.initialize(
22952
+ options.signal,
22953
+ { kind: "fork", nativeSessionId: sourceNativeSessionId },
22954
+ forkPoint
22955
+ )
22956
+ );
22867
22957
  belongsTo = (agentId, cwd, additionalDirectories) => this.#agentId === agentId && this.#cwd === cwd && arraysEqual(this.#additionalDirectories, additionalDirectories);
22958
+ usesEnvironment = (environment) => recordsEqual(this.#environment, environment);
22868
22959
  canRebind = () => !this.#prompted && !this.#turn.running;
22869
22960
  prepared = () => new AcpPreparedSession(
22870
22961
  this.#agentName,
@@ -22930,15 +23021,14 @@ var AcpProcessSession = class _AcpProcessSession {
22930
23021
  "unsupported_acp_protocol"
22931
23022
  );
22932
23023
  }
22933
- const capabilities = initialized.agentCapabilities ?? {};
22934
- this.#capabilities = normalizeAcpCapabilities(capabilities);
23024
+ this.#capabilities = normalizeAcpCapabilities(initialized);
22935
23025
  this.#agentName = truncate3(initialized.agentInfo?.title ?? initialized.agentInfo?.name ?? this.#agentId, 255);
22936
23026
  this.#agentVersion = truncate3(
22937
23027
  this.#verifiedAgentVersion.length > 0 ? this.#verifiedAgentVersion : initialized.agentInfo?.version ?? "",
22938
23028
  128
22939
23029
  );
22940
23030
  }
22941
- initialize = async (signal, origin) => {
23031
+ initialize = async (signal, origin, forkPoint) => {
22942
23032
  await this.#initializeAgent(signal);
22943
23033
  let stored = origin?.kind === "import" ? new FoundAcpStoredSession(
22944
23034
  new AcpStoredSession(
@@ -22970,6 +23060,13 @@ var AcpProcessSession = class _AcpProcessSession {
22970
23060
  "native_session_unsupported"
22971
23061
  );
22972
23062
  }
23063
+ if (this.#additionalDirectories.length > 0 && !this.#capabilities.additionalDirectories) {
23064
+ throw new AcpExecutionError(
23065
+ "This Agent cannot restore additional working directories",
23066
+ {},
23067
+ "native_session_unsupported"
23068
+ );
23069
+ }
22973
23070
  const forked = await this.race(
22974
23071
  this.#connection.agent.request(
22975
23072
  methods3.agent.session.fork,
@@ -22977,7 +23074,21 @@ var AcpProcessSession = class _AcpProcessSession {
22977
23074
  sessionId: nativeSessionIdSchema.parse(origin.nativeSessionId),
22978
23075
  cwd: this.#cwd,
22979
23076
  additionalDirectories: [...this.#additionalDirectories],
22980
- mcpServers: []
23077
+ mcpServers: [],
23078
+ ...forkPoint === void 0 ? {} : {
23079
+ _meta: {
23080
+ jetbrains: {
23081
+ air: {
23082
+ fork: {
23083
+ version: 1,
23084
+ messageFingerprint: forkPoint.messageFingerprint,
23085
+ messageId: forkPoint.messageId,
23086
+ messageOccurrence: forkPoint.messageOccurrence
23087
+ }
23088
+ }
23089
+ }
23090
+ }
23091
+ }
22981
23092
  },
22982
23093
  { cancellationSignal: signal }
22983
23094
  ),
@@ -23182,10 +23293,7 @@ var AcpProcessSession = class _AcpProcessSession {
23182
23293
  }
23183
23294
  return this.#configOptions;
23184
23295
  };
23185
- prompt = async (options) => {
23186
- if (this.#turn.running) {
23187
- throw new AcpExecutionError("Another prompt is already running in this conversation", {}, "session_busy");
23188
- }
23296
+ buildPrompt = (options) => {
23189
23297
  if (options.images.length > 0 && !this.#capabilities.imagePrompt) {
23190
23298
  throw new AcpExecutionError(
23191
23299
  "The selected agent does not accept image prompts",
@@ -23234,6 +23342,13 @@ var AcpProcessSession = class _AcpProcessSession {
23234
23342
  if (prompt.length === 0) {
23235
23343
  throw new AcpExecutionError("A prompt must contain text or an attachment", {}, "empty_prompt");
23236
23344
  }
23345
+ return prompt;
23346
+ };
23347
+ prompt = async (options) => {
23348
+ if (this.#turn.running) {
23349
+ throw new AcpExecutionError("Another prompt is already running in this conversation", {}, "session_busy");
23350
+ }
23351
+ const prompt = this.buildPrompt(options);
23237
23352
  this.#store.write(
23238
23353
  new AcpStoredSession(
23239
23354
  this.#mobiusSessionId,
@@ -23292,6 +23407,54 @@ var AcpProcessSession = class _AcpProcessSession {
23292
23407
  this.#turn = new IdleAcpTurn();
23293
23408
  }
23294
23409
  };
23410
+ steer = async (options) => {
23411
+ if (!this.#capabilities.steering) {
23412
+ throw new AcpExecutionError(
23413
+ "The selected agent does not support mid-turn steering",
23414
+ {},
23415
+ "steering_unsupported"
23416
+ );
23417
+ }
23418
+ if (!this.#turn.running || this.#turn.commandId !== options.targetCommandId) {
23419
+ throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
23420
+ }
23421
+ const prompt = this.buildPrompt(options);
23422
+ const response = await this.race(
23423
+ this.#connection.agent.request("_session/steering", {
23424
+ _meta: { steering: { idleBehavior: "promptRequired" } },
23425
+ prompt,
23426
+ sessionId: this.#nativeSessionId
23427
+ }),
23428
+ "ACP agent exited during steering",
23429
+ options.signal
23430
+ );
23431
+ if (!isRecord6(response)) {
23432
+ throw new AcpExecutionError(
23433
+ "ACP agent returned an invalid steering response",
23434
+ {},
23435
+ "acp_invalid_steering_response"
23436
+ );
23437
+ }
23438
+ const outcome = response["outcome"];
23439
+ if (outcome === "injected" || outcome === "startedNewTurn") {
23440
+ return outcome;
23441
+ }
23442
+ if (outcome === "promptRequired") {
23443
+ throw new AcpExecutionError(
23444
+ "The target turn finished before steering could be injected",
23445
+ {},
23446
+ "steering_target_unavailable"
23447
+ );
23448
+ }
23449
+ if (outcome === "failed") {
23450
+ throw new AcpExecutionError("The Agent could not inject steering", {}, "steering_failed");
23451
+ }
23452
+ throw new AcpExecutionError(
23453
+ "ACP agent returned an invalid steering response",
23454
+ {},
23455
+ "acp_invalid_steering_response"
23456
+ );
23457
+ };
23295
23458
  cancel = async () => {
23296
23459
  const capturedProcesses = await captureProcessTree(this.#child);
23297
23460
  try {
@@ -23532,7 +23695,7 @@ var AcpProcessSession = class _AcpProcessSession {
23532
23695
  );
23533
23696
  } else {
23534
23697
  await this.#turn.activities.finishNarrative();
23535
- await this.#turn.onText(update.content.text);
23698
+ await this.#turn.onText(update.content.text, truncate3(update.messageId ?? "", 255));
23536
23699
  }
23537
23700
  } else {
23538
23701
  await this.#turn.activities.finishNarrative();
@@ -23758,6 +23921,7 @@ var AcpProcessSession = class _AcpProcessSession {
23758
23921
  };
23759
23922
  };
23760
23923
  var AcpSessionManager = class {
23924
+ #activeCommandIds = /* @__PURE__ */ new Map();
23761
23925
  #nativeOperations = /* @__PURE__ */ new Set();
23762
23926
  #nativeBrowserObserver = new NullAcpSessionObserver();
23763
23927
  #nativeBrowserPreparing = /* @__PURE__ */ new Map();
@@ -23826,6 +23990,25 @@ var AcpSessionManager = class {
23826
23990
  this.#preparing.set(options.sessionId, operation);
23827
23991
  return await operation;
23828
23992
  }
23993
+ async #startSessionForkAtTurnPreparation(options, sourceNativeSessionId, forkPoint) {
23994
+ const operation = (async () => {
23995
+ try {
23996
+ const runtime = await AcpProcessSession.startSessionForkAtTurn(
23997
+ options,
23998
+ sourceNativeSessionId,
23999
+ forkPoint,
24000
+ () => this.#observer,
24001
+ this.#store
24002
+ );
24003
+ this.#sessions.set(options.sessionId, runtime);
24004
+ return runtime;
24005
+ } finally {
24006
+ this.#preparing.delete(options.sessionId);
24007
+ }
24008
+ })();
24009
+ this.#preparing.set(options.sessionId, operation);
24010
+ return await operation;
24011
+ }
23829
24012
  async #replacePreparation(existing, options) {
23830
24013
  const operation = (async () => {
23831
24014
  try {
@@ -23842,6 +24025,58 @@ var AcpSessionManager = class {
23842
24025
  this.#preparing.set(options.sessionId, operation);
23843
24026
  return await operation;
23844
24027
  }
24028
+ async #replaceExecutionPreparation(existing, options) {
24029
+ const desiredConfigOptions = existing.prepared().configOptions;
24030
+ const operation = (async () => {
24031
+ try {
24032
+ this.#sessions.delete(options.sessionId);
24033
+ await existing.close();
24034
+ this.#store.remove(options.sessionId);
24035
+ const runtime = await AcpProcessSession.start(options, () => this.#observer, this.#store);
24036
+ try {
24037
+ for (const desired of desiredConfigOptions) {
24038
+ const current = runtime.prepared().configOptions.find((option2) => option2.id === desired.id);
24039
+ if (current === void 0 || current.type !== desired.type) {
24040
+ continue;
24041
+ }
24042
+ if (desired.type === "boolean") {
24043
+ if (current.booleanValue !== desired.booleanValue) {
24044
+ await runtime.configure({
24045
+ ...options,
24046
+ booleanValue: desired.booleanValue,
24047
+ configId: desired.id,
24048
+ requestId: `execution-prepare-${desired.id}`,
24049
+ value: "",
24050
+ valueType: "boolean"
24051
+ });
24052
+ }
24053
+ continue;
24054
+ }
24055
+ if (current.currentValue !== desired.currentValue && current.values.some((value) => value.value === desired.currentValue)) {
24056
+ await runtime.configure({
24057
+ ...options,
24058
+ booleanValue: false,
24059
+ configId: desired.id,
24060
+ requestId: `execution-prepare-${desired.id}`,
24061
+ value: desired.currentValue,
24062
+ valueType: "select"
24063
+ });
24064
+ }
24065
+ }
24066
+ } catch (error61) {
24067
+ await runtime.close();
24068
+ this.#store.remove(options.sessionId);
24069
+ throw error61;
24070
+ }
24071
+ this.#sessions.set(options.sessionId, runtime);
24072
+ return runtime;
24073
+ } finally {
24074
+ this.#preparing.delete(options.sessionId);
24075
+ }
24076
+ })();
24077
+ this.#preparing.set(options.sessionId, operation);
24078
+ return await operation;
24079
+ }
23845
24080
  prepareSession = async (options) => {
23846
24081
  const stored = this.#store.read(options.sessionId);
23847
24082
  if (stored instanceof DeletedAcpStoredSession) {
@@ -23880,6 +24115,35 @@ var AcpSessionManager = class {
23880
24115
  const runtime = await this.#startPreparation(options);
23881
24116
  return runtime.prepared();
23882
24117
  };
24118
+ prepareExecutionSession = async (options) => {
24119
+ const pending = this.#preparing.get(options.sessionId);
24120
+ if (pending !== void 0) {
24121
+ await pending;
24122
+ return await this.prepareExecutionSession(options);
24123
+ }
24124
+ const existing = this.#sessions.get(options.sessionId);
24125
+ if (existing === void 0) {
24126
+ return (await this.#startPreparation(options)).prepared();
24127
+ }
24128
+ if (!existing.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
24129
+ throw new AcpExecutionError(
24130
+ "The agent or workspace cannot change after a conversation has started",
24131
+ {},
24132
+ "session_identity_mismatch"
24133
+ );
24134
+ }
24135
+ if (existing.usesEnvironment(options.agent.environment)) {
24136
+ return existing.prepared();
24137
+ }
24138
+ if (!existing.canRebind()) {
24139
+ throw new AcpExecutionError(
24140
+ "The execution environment cannot change after a conversation has started",
24141
+ {},
24142
+ "session_identity_mismatch"
24143
+ );
24144
+ }
24145
+ return (await this.#replaceExecutionPreparation(existing, options)).prepared();
24146
+ };
23883
24147
  prepareNativeSessionBrowser = async (options) => {
23884
24148
  const pending = this.#nativeBrowserPreparing.get(options.sessionId);
23885
24149
  if (pending !== void 0) {
@@ -23924,6 +24188,47 @@ var AcpSessionManager = class {
23924
24188
  }
23925
24189
  }
23926
24190
  };
24191
+ steerPrompt = async (options) => {
24192
+ if (this.#activeCommandIds.get(options.sessionId) !== options.targetCommandId) {
24193
+ throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
24194
+ }
24195
+ const pending = this.#preparing.get(options.sessionId);
24196
+ if (pending !== void 0) {
24197
+ await pending;
24198
+ }
24199
+ if (this.#activeCommandIds.get(options.sessionId) !== options.targetCommandId) {
24200
+ throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
24201
+ }
24202
+ const runtime = this.#sessions.get(options.sessionId);
24203
+ if (runtime === void 0 || !runtime.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
24204
+ throw new AcpExecutionError(
24205
+ "The target turn execution context is unavailable",
24206
+ {},
24207
+ "steering_target_unavailable"
24208
+ );
24209
+ }
24210
+ const deadlineController = new AbortController();
24211
+ const deadline = setTimeout(
24212
+ () => deadlineController.abort(
24213
+ new AcpExecutionError("The steering request exceeded its deadline", {}, "steering_timeout")
24214
+ ),
24215
+ options.agent.promptTimeoutMilliseconds
24216
+ );
24217
+ deadline.unref();
24218
+ try {
24219
+ return await runtime.steer({
24220
+ ...options,
24221
+ signal: AbortSignal.any([options.signal, deadlineController.signal])
24222
+ });
24223
+ } catch (error61) {
24224
+ if (error61 instanceof AcpExecutionError) {
24225
+ throw error61;
24226
+ }
24227
+ throw new AcpExecutionError("ACP agent steering failed", { cause: error61 }, "steering_failed");
24228
+ } finally {
24229
+ clearTimeout(deadline);
24230
+ }
24231
+ };
23927
24232
  runPrompt = async (options) => {
23928
24233
  const stored = this.#store.read(options.sessionId);
23929
24234
  if (stored instanceof FoundAcpStoredSession && this.#nativeOperations.has(`${stored.session.agentId}:${stored.session.nativeSessionId}`)) {
@@ -23948,6 +24253,7 @@ var AcpSessionManager = class {
23948
24253
  signal: AbortSignal.any([options.signal, deadlineController.signal])
23949
24254
  };
23950
24255
  this.#activeSessionIds.add(options.sessionId);
24256
+ this.#activeCommandIds.set(options.sessionId, options.commandId);
23951
24257
  try {
23952
24258
  if (this.#cancellationRequests.has(options.sessionId)) {
23953
24259
  return { stopReason: "cancelled" };
@@ -23987,6 +24293,9 @@ var AcpSessionManager = class {
23987
24293
  } finally {
23988
24294
  clearTimeout(deadline);
23989
24295
  this.#activeSessionIds.delete(options.sessionId);
24296
+ if (this.#activeCommandIds.get(options.sessionId) === options.commandId) {
24297
+ this.#activeCommandIds.delete(options.sessionId);
24298
+ }
23990
24299
  this.#cancellationRequests.delete(options.sessionId);
23991
24300
  }
23992
24301
  };
@@ -24011,6 +24320,7 @@ var AcpSessionManager = class {
24011
24320
  this.#nativeBrowserSessions.clear();
24012
24321
  await Promise.all([...sessions, ...nativeBrowserSessions].map(async (session) => await session.close()));
24013
24322
  };
24323
+ hasPreparedSession = (sessionId, agentId, cwd, additionalDirectories) => this.#sessions.get(sessionId)?.belongsTo(agentId, cwd, additionalDirectories) === true;
24014
24324
  ensureSession = async (options) => {
24015
24325
  const existing = this.#sessions.get(options.sessionId);
24016
24326
  if (existing !== void 0) {
@@ -24119,6 +24429,68 @@ var AcpSessionManager = class {
24119
24429
  this.#nativeOperations.delete(key);
24120
24430
  }
24121
24431
  };
24432
+ forkSessionAtTurn = async (options, sourceSessionId, forkPoint) => {
24433
+ if (sourceSessionId === options.sessionId) {
24434
+ throw new AcpExecutionError(
24435
+ "The fork destination must be a new conversation",
24436
+ {},
24437
+ "session_identity_mismatch"
24438
+ );
24439
+ }
24440
+ const source = this.#store.read(sourceSessionId);
24441
+ if (!(source instanceof FoundAcpStoredSession) || !source.session.prompted) {
24442
+ throw new AcpExecutionError(
24443
+ "The source conversation has no resumable native Agent session",
24444
+ {},
24445
+ "native_session_not_found"
24446
+ );
24447
+ }
24448
+ if (source.session.agentId !== options.agent.id || source.session.cwd !== options.cwd || !arraysEqual(source.session.additionalDirectories, options.additionalDirectories)) {
24449
+ throw new AcpExecutionError(
24450
+ "The source conversation execution target does not match the fork request",
24451
+ {},
24452
+ "native_session_target_changed"
24453
+ );
24454
+ }
24455
+ const key = `${options.agent.id}:${source.session.nativeSessionId}`;
24456
+ const linked = this.#store.findNative(options.agent.id, source.session.nativeSessionId);
24457
+ if (this.#nativeOperations.has(key) || linked.some((entry) => this.#activeSessionIds.has(entry.sessionId) || this.#preparing.has(entry.sessionId))) {
24458
+ throw new AcpExecutionError(
24459
+ "Wait for the source conversation's running operation to finish",
24460
+ {},
24461
+ "session_busy"
24462
+ );
24463
+ }
24464
+ if (this.#sessions.has(options.sessionId) || this.#preparing.has(options.sessionId) || this.#store.read(options.sessionId) instanceof FoundAcpStoredSession || this.#store.read(options.sessionId) instanceof DeletedAcpStoredSession) {
24465
+ throw new AcpExecutionError("The destination conversation already exists", {}, "session_identity_mismatch");
24466
+ }
24467
+ this.#nativeOperations.add(key);
24468
+ try {
24469
+ const runtime = await this.#startSessionForkAtTurnPreparation(
24470
+ options,
24471
+ source.session.nativeSessionId,
24472
+ forkPoint
24473
+ );
24474
+ const openedKey = `${options.agent.id}:${runtime.nativeSessionId()}`;
24475
+ this.#nativeOperations.add(openedKey);
24476
+ try {
24477
+ return {
24478
+ history: await runtime.loadHistory(options.signal),
24479
+ prepared: runtime.prepared(),
24480
+ sessionId: options.sessionId
24481
+ };
24482
+ } catch (error61) {
24483
+ this.#sessions.delete(options.sessionId);
24484
+ this.#store.remove(options.sessionId);
24485
+ await runtime.closeProcess();
24486
+ throw error61;
24487
+ } finally {
24488
+ this.#nativeOperations.delete(openedKey);
24489
+ }
24490
+ } finally {
24491
+ this.#nativeOperations.delete(key);
24492
+ }
24493
+ };
24122
24494
  deleteNativeSession = async (options, nativeSessionId) => {
24123
24495
  const runtime = await this.#ensureNativeSessionBrowser(options);
24124
24496
  const key = `${options.agent.id}:${nativeSessionId}`;
@@ -24263,7 +24635,8 @@ var LocalBotGrant = class {
24263
24635
  revocationEpoch;
24264
24636
  workspaceIdentityDigest;
24265
24637
  workspacePath;
24266
- constructor(localGrantId, botId, behaviorRevisionId, behaviorDigest, bindingId, bindingRevision, agent, agentIdentityDigest, workspacePath, workspaceIdentityDigest, allowedDeliveryTargetIds, expiresAt, revocationEpoch) {
24638
+ workspaceRef;
24639
+ constructor(localGrantId, botId, behaviorRevisionId, behaviorDigest, bindingId, bindingRevision, agent, agentIdentityDigest, workspaceRef, workspacePath, workspaceIdentityDigest, allowedDeliveryTargetIds, expiresAt, revocationEpoch) {
24267
24640
  this.localGrantId = localGrantId;
24268
24641
  this.botId = botId;
24269
24642
  this.behaviorRevisionId = behaviorRevisionId;
@@ -24272,6 +24645,7 @@ var LocalBotGrant = class {
24272
24645
  this.bindingRevision = bindingRevision;
24273
24646
  this.agent = agent;
24274
24647
  this.agentIdentityDigest = agentIdentityDigest;
24648
+ this.workspaceRef = workspaceRef;
24275
24649
  this.workspacePath = workspacePath;
24276
24650
  this.workspaceIdentityDigest = workspaceIdentityDigest;
24277
24651
  this.allowedDeliveryTargetIds = Object.freeze([...allowedDeliveryTargetIds]);
@@ -24456,7 +24830,7 @@ var LocalBotGrantStore = class {
24456
24830
  WHERE local_grant_id = ? AND bot_id = ?
24457
24831
  AND behavior_revision_id = ? AND behavior_digest = ?
24458
24832
  AND binding_id = ? AND binding_revision = ?
24459
- AND agent_ref = ? AND workspace_ref = ?
24833
+ AND agent_ref = ?
24460
24834
  AND revoked_at IS NULL AND expires_at > ?`
24461
24835
  ).get(
24462
24836
  command.localGrantId,
@@ -24466,7 +24840,6 @@ var LocalBotGrantStore = class {
24466
24840
  command.bindingId,
24467
24841
  command.bindingRevision,
24468
24842
  command.agentId,
24469
- command.directoryId,
24470
24843
  now
24471
24844
  );
24472
24845
  if (row === void 0) {
@@ -24489,7 +24862,8 @@ var LocalBotGrantStore = class {
24489
24862
  revalidateExecutable(agent.agentIdentity)
24490
24863
  ]);
24491
24864
  signal.throwIfAborted();
24492
- const workspacePath = await directories.resolve(bindingAuthorityId, command.directoryId, signal);
24865
+ const workspaceRef = rowString(row, "workspace_ref");
24866
+ const workspacePath = await directories.resolve(bindingAuthorityId, workspaceRef, signal);
24493
24867
  const currentAgentDigest = agentDigest(agent);
24494
24868
  const currentWorkspaceDigest = await workspaceDigest(workspacePath);
24495
24869
  signal.throwIfAborted();
@@ -24515,6 +24889,7 @@ var LocalBotGrantStore = class {
24515
24889
  command.bindingRevision,
24516
24890
  agent,
24517
24891
  currentAgentDigest,
24892
+ workspaceRef,
24518
24893
  workspacePath,
24519
24894
  currentWorkspaceDigest,
24520
24895
  approvedTargets,
@@ -24683,7 +25058,7 @@ var BotMessageCommandError = class extends Error {
24683
25058
  this.name = "BotMessageCommandError";
24684
25059
  }
24685
25060
  };
24686
- function isRecord6(value) {
25061
+ function isRecord7(value) {
24687
25062
  return typeof value === "object" && value !== null && !Array.isArray(value);
24688
25063
  }
24689
25064
  function requiredEnvironment(environment, name) {
@@ -24734,14 +25109,14 @@ function decodeResponse(encoded) {
24734
25109
  cause
24735
25110
  });
24736
25111
  }
24737
- if (!isRecord6(value) || typeof value["ok"] !== "boolean") {
25112
+ if (!isRecord7(value) || typeof value["ok"] !== "boolean") {
24738
25113
  throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
24739
25114
  }
24740
25115
  if (value["ok"] === true && typeof value["outboundMessageId"] === "string") {
24741
25116
  return value["outboundMessageId"];
24742
25117
  }
24743
25118
  const error61 = value["error"];
24744
- if (value["ok"] === false && isRecord6(error61) && typeof error61["code"] === "string" && typeof error61["message"] === "string") {
25119
+ if (value["ok"] === false && isRecord7(error61) && typeof error61["code"] === "string" && typeof error61["message"] === "string") {
24745
25120
  throw new BotMessageCommandError(error61["code"], error61["message"]);
24746
25121
  }
24747
25122
  throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
@@ -24850,7 +25225,7 @@ var BotOutboundIpcRequest = class {
24850
25225
  this.text = text;
24851
25226
  }
24852
25227
  };
24853
- function isRecord7(value) {
25228
+ function isRecord8(value) {
24854
25229
  return typeof value === "object" && value !== null && !Array.isArray(value);
24855
25230
  }
24856
25231
  function decodeRequest(encoded) {
@@ -24860,7 +25235,7 @@ function decodeRequest(encoded) {
24860
25235
  } catch (cause) {
24861
25236
  throw new LocalBotAuthorizationError("invalid_request", "The local Bot request is invalid", { cause });
24862
25237
  }
24863
- if (!isRecord7(value)) {
25238
+ if (!isRecord8(value)) {
24864
25239
  throw new LocalBotAuthorizationError("invalid_request", "The local Bot request is invalid");
24865
25240
  }
24866
25241
  const keys = Object.keys(value);
@@ -24910,10 +25285,10 @@ async function decodeRelayResponse(response) {
24910
25285
  cause
24911
25286
  });
24912
25287
  }
24913
- if (response.status === 202 && isRecord7(value) && typeof value["outboundMessageId"] === "string") {
25288
+ if (response.status === 202 && isRecord8(value) && typeof value["outboundMessageId"] === "string") {
24914
25289
  return value["outboundMessageId"];
24915
25290
  }
24916
- if (isRecord7(value) && isRecord7(value["error"])) {
25291
+ if (isRecord8(value) && isRecord8(value["error"])) {
24917
25292
  const error61 = value["error"];
24918
25293
  if (typeof error61["code"] === "string" && typeof error61["message"] === "string") {
24919
25294
  throw new LocalBotAuthorizationError(error61["code"], error61["message"]);
@@ -24946,7 +25321,7 @@ var BotOutboundIpcServer = class {
24946
25321
  if (this.#usesFilesystemSocket) {
24947
25322
  await mkdir(dirname3(this.#socketPath), { mode: 448, recursive: true });
24948
25323
  await unlink(this.#socketPath).catch((error61) => {
24949
- if (!isRecord7(error61) || error61["code"] !== "ENOENT") {
25324
+ if (!isRecord8(error61) || error61["code"] !== "ENOENT") {
24950
25325
  throw error61;
24951
25326
  }
24952
25327
  });
@@ -24981,7 +25356,7 @@ var BotOutboundIpcServer = class {
24981
25356
  }
24982
25357
  if (this.#usesFilesystemSocket) {
24983
25358
  await unlink(this.#socketPath).catch((error61) => {
24984
- if (!isRecord7(error61) || error61["code"] !== "ENOENT") {
25359
+ if (!isRecord8(error61) || error61["code"] !== "ENOENT") {
24985
25360
  throw error61;
24986
25361
  }
24987
25362
  });
@@ -25228,7 +25603,7 @@ var ManagedNpmPackageError = class extends Error {
25228
25603
  this.name = "ManagedNpmPackageError";
25229
25604
  }
25230
25605
  };
25231
- function isRecord8(value) {
25606
+ function isRecord9(value) {
25232
25607
  return typeof value === "object" && value !== null && !Array.isArray(value);
25233
25608
  }
25234
25609
  function packagePath(root, packageName) {
@@ -25250,7 +25625,7 @@ async function readJsonRecord(path2, maximumBytes) {
25250
25625
  );
25251
25626
  }
25252
25627
  const value = JSON.parse(await handle.readFile({ encoding: "utf8" }));
25253
- if (!isRecord8(value)) {
25628
+ if (!isRecord9(value)) {
25254
25629
  throw new ManagedNpmPackageError(
25255
25630
  "managed_package_verification_failed",
25256
25631
  "The managed npm package metadata is invalid"
@@ -25276,7 +25651,7 @@ function readBinPath(packageMetadata, executableName) {
25276
25651
  if (typeof bin === "string") {
25277
25652
  return bin;
25278
25653
  }
25279
- if (isRecord8(bin) && typeof bin[executableName] === "string") {
25654
+ if (isRecord9(bin) && typeof bin[executableName] === "string") {
25280
25655
  return bin[executableName];
25281
25656
  }
25282
25657
  throw new ManagedNpmPackageError(
@@ -25292,7 +25667,7 @@ function verifyPackageLockEntry(lock, definition, verifyDependencyClosure) {
25292
25667
  );
25293
25668
  }
25294
25669
  const packages = lock["packages"];
25295
- if (!isRecord8(packages)) {
25670
+ if (!isRecord9(packages)) {
25296
25671
  throw new ManagedNpmPackageError(
25297
25672
  "managed_package_verification_failed",
25298
25673
  "The managed npm package lockfile is invalid"
@@ -25306,7 +25681,7 @@ function verifyPackageLockEntry(lock, definition, verifyDependencyClosure) {
25306
25681
  if (verifyDependencyClosure && normalizedKey.length > 0) {
25307
25682
  let resolved;
25308
25683
  try {
25309
- if (!normalizedKey.split("/").includes("node_modules") || !isRecord8(candidate) || typeof candidate["version"] !== "string" || candidate["version"].length === 0 || typeof candidate["integrity"] !== "string" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(candidate["integrity"]) || typeof candidate["resolved"] !== "string") {
25684
+ if (!normalizedKey.split("/").includes("node_modules") || !isRecord9(candidate) || typeof candidate["version"] !== "string" || candidate["version"].length === 0 || typeof candidate["integrity"] !== "string" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(candidate["integrity"]) || typeof candidate["resolved"] !== "string") {
25310
25685
  throw new Error("Invalid locked dependency");
25311
25686
  }
25312
25687
  resolved = new URL(candidate["resolved"]);
@@ -25329,7 +25704,7 @@ function verifyPackageLockEntry(lock, definition, verifyDependencyClosure) {
25329
25704
  matches += 1;
25330
25705
  }
25331
25706
  }
25332
- if (matches !== 1 || !isRecord8(entry) || entry["integrity"] !== definition.integrity || entry["resolved"] !== definition.tarballUrl || entry["version"] !== definition.version) {
25707
+ if (matches !== 1 || !isRecord9(entry) || entry["integrity"] !== definition.integrity || entry["resolved"] !== definition.tarballUrl || entry["version"] !== definition.version) {
25333
25708
  throw new ManagedNpmPackageError(
25334
25709
  "managed_package_verification_failed",
25335
25710
  "The managed npm package lockfile does not match the pinned artifact"
@@ -25959,7 +26334,7 @@ var ManagedAgentArchiveError = class extends Error {
25959
26334
  this.name = "ManagedAgentArchiveError";
25960
26335
  }
25961
26336
  };
25962
- function isRecord9(value) {
26337
+ function isRecord10(value) {
25963
26338
  return typeof value === "object" && value !== null && !Array.isArray(value);
25964
26339
  }
25965
26340
  function isContainedPath3(root, candidate) {
@@ -26016,7 +26391,7 @@ async function readManifest(path2) {
26016
26391
  );
26017
26392
  }
26018
26393
  const parsed = JSON.parse(await handle.readFile({ encoding: "utf8" }));
26019
- if (!isRecord9(parsed)) {
26394
+ if (!isRecord10(parsed)) {
26020
26395
  throw new ManagedAgentArchiveError(
26021
26396
  "managed_archive_verification_failed",
26022
26397
  "The managed Agent archive manifest is invalid"
@@ -26709,7 +27084,7 @@ async function probeAcpReadiness(specification, agent, environment, defaultWorks
26709
27084
  await specification.verifyAcp(connection, initialized, defaultWorkspace, controller.signal);
26710
27085
  return new VerifiedAcpReadiness(
26711
27086
  normalizedVersion(initialized.agentInfo?.version ?? ""),
26712
- normalizeAcpCapabilities(initialized.agentCapabilities ?? {})
27087
+ normalizeAcpCapabilities(initialized)
26713
27088
  );
26714
27089
  } catch (error61) {
26715
27090
  if (signal.aborted) {
@@ -26904,7 +27279,7 @@ var PairingClientError = class extends Error {
26904
27279
  this.name = "PairingClientError";
26905
27280
  }
26906
27281
  };
26907
- function isRecord10(value) {
27282
+ function isRecord11(value) {
26908
27283
  return typeof value === "object" && value !== null && !Array.isArray(value);
26909
27284
  }
26910
27285
  function hasExactKeys(value, expectedKeys) {
@@ -27020,9 +27395,9 @@ async function readJsonResponse(response) {
27020
27395
  }
27021
27396
  }
27022
27397
  function readRelayError(value) {
27023
- if (isRecord10(value)) {
27398
+ if (isRecord11(value)) {
27024
27399
  const error61 = value["error"];
27025
- if (isRecord10(error61)) {
27400
+ if (isRecord11(error61)) {
27026
27401
  const code = error61["code"];
27027
27402
  const message = error61["message"];
27028
27403
  if (typeof code === "string" && typeof message === "string") {
@@ -27033,7 +27408,7 @@ function readRelayError(value) {
27033
27408
  return new PairingClientError("pairing_failed", "The relay could not pair this machine");
27034
27409
  }
27035
27410
  function decodeClaimResponse(value) {
27036
- if (!isRecord10(value) || !hasExactKeys(value, CLAIM_RESPONSE_KEYS)) {
27411
+ if (!isRecord11(value) || !hasExactKeys(value, CLAIM_RESPONSE_KEYS)) {
27037
27412
  throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
27038
27413
  }
27039
27414
  const channelId = value["channelId"];
@@ -27101,7 +27476,7 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
27101
27476
  return connectionPath;
27102
27477
  }
27103
27478
  function decodeSavedConnection(value, expectedChannelId) {
27104
- if (!isRecord10(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
27479
+ if (!isRecord11(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
27105
27480
  throw new PairingClientError("invalid_connection", "The saved connection is invalid");
27106
27481
  }
27107
27482
  const defaultWorkspace = value["defaultWorkspace"];
@@ -27216,7 +27591,7 @@ async function resumeLocalMachine(arguments_, signal = new AbortController().sig
27216
27591
 
27217
27592
  // src/run-daemon.ts
27218
27593
  import { randomUUID as randomUUID6 } from "node:crypto";
27219
- import { basename as basename3, join as join16 } from "node:path";
27594
+ import { basename as basename4, join as join16 } from "node:path";
27220
27595
  import { styleText } from "node:util";
27221
27596
  import { fetch as fetch6, WebSocket as WebSocket2 } from "undici";
27222
27597
 
@@ -27929,6 +28304,7 @@ var EmbeddedBotContextApplicator = class {
27929
28304
 
27930
28305
  // src/native-sessions/service.ts
27931
28306
  import { randomUUID as randomUUID4 } from "node:crypto";
28307
+ import { basename as basename3 } from "node:path";
27932
28308
 
27933
28309
  // src/native-sessions/codex-history.ts
27934
28310
  import { spawn as spawn4 } from "node:child_process";
@@ -28406,6 +28782,74 @@ var NativeSessionService = class {
28406
28782
  history: opened.history
28407
28783
  };
28408
28784
  };
28785
+ executeTurnFork = async (command, signal) => {
28786
+ signal.throwIfAborted();
28787
+ const cwd = await this.#directories.resolve(this.#config.bindingId, command.directoryId, signal);
28788
+ const additionalDirectories = [];
28789
+ const additionalEntries = [];
28790
+ const resolvedPaths = /* @__PURE__ */ new Set([cwd]);
28791
+ for (const directoryId2 of command.additionalDirectoryIds) {
28792
+ const path2 = await this.#directories.resolve(this.#config.bindingId, directoryId2, signal);
28793
+ if (resolvedPaths.has(path2)) {
28794
+ throw new LocalAuthorizationError("Workspace directories must resolve to distinct locations");
28795
+ }
28796
+ resolvedPaths.add(path2);
28797
+ additionalDirectories.push(path2);
28798
+ additionalEntries.push({ directoryId: directoryId2, name: basename3(path2) });
28799
+ }
28800
+ const listing = await this.#directories.list(this.#config.bindingId, command.directoryId, signal);
28801
+ const opened = await this.#sessions.forkSessionAtTurn(
28802
+ {
28803
+ additionalDirectories,
28804
+ agent: this.#config.catalog.find(command.agentId),
28805
+ cwd,
28806
+ sessionId: command.targetSessionId,
28807
+ signal
28808
+ },
28809
+ command.sessionId,
28810
+ {
28811
+ messageFingerprint: command.messageFingerprint,
28812
+ messageId: command.messageId,
28813
+ messageOccurrence: command.messageOccurrence
28814
+ }
28815
+ );
28816
+ signal.throwIfAborted();
28817
+ const ready = {
28818
+ additionalDirectories: additionalEntries,
28819
+ agentId: command.agentId,
28820
+ agentName: opened.prepared.agentName,
28821
+ agentVersion: opened.prepared.agentVersion,
28822
+ capabilities: opened.prepared.capabilities,
28823
+ commands: opened.prepared.commands,
28824
+ configOptions: opened.prepared.configOptions,
28825
+ directoryId: command.directoryId,
28826
+ directoryLabel: listing.directoryLabel,
28827
+ kind: "session.ready",
28828
+ requestId: command.requestId,
28829
+ restored: true,
28830
+ sessionId: opened.sessionId
28831
+ };
28832
+ this.#audit.record({
28833
+ commandId: command.requestId,
28834
+ details: {
28835
+ agentId: command.agentId,
28836
+ messageId: command.messageId,
28837
+ sourceCommandId: command.sourceCommandId
28838
+ },
28839
+ event: "native_session_turn_forked",
28840
+ sessionId: command.sessionId
28841
+ });
28842
+ return {
28843
+ action: "fork",
28844
+ history: opened.history,
28845
+ kind: "native.session.result",
28846
+ ready,
28847
+ requestId: command.requestId,
28848
+ sessionId: command.sessionId,
28849
+ targetSessionId: command.targetSessionId,
28850
+ title: command.title
28851
+ };
28852
+ };
28409
28853
  authorizeEntry = async (command, options, info) => {
28410
28854
  try {
28411
28855
  const primary = await this.#directories.identify(command.bindingId, info.cwd, options.signal);
@@ -28970,6 +29414,11 @@ var SessionEventOutbox = class {
28970
29414
  }
28971
29415
  this.#database.prepare(`UPDATE relay_turn_receipts SET state = ? WHERE command_id = ?`).run(state, commandId);
28972
29416
  };
29417
+ isTerminalExecution = (commandId, sessionId) => {
29418
+ const row = this.#database.prepare(`SELECT state FROM relay_turn_receipts WHERE command_id = ? AND session_id = ?`).get(commandId, sessionId);
29419
+ const state = typeof row === "object" && row !== null ? Reflect.get(row, "state") : void 0;
29420
+ return state === "succeeded" || state === "failed" || state === "cancelled";
29421
+ };
28973
29422
  acknowledge = (acknowledgement) => {
28974
29423
  this.#database.prepare(
28975
29424
  `DELETE FROM relay_session_event_outbox
@@ -29482,7 +29931,7 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
29482
29931
  additionalDirectories.push(directory);
29483
29932
  additionalDirectoryEntries.push({
29484
29933
  directoryId: additionalDirectoryId,
29485
- name: basename3(directory)
29934
+ name: basename4(directory)
29486
29935
  });
29487
29936
  }
29488
29937
  return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
@@ -29631,68 +30080,96 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
29631
30080
  attemptId
29632
30081
  );
29633
30082
  const attachments = await resolvePromptArtifacts(config2, command, signal);
29634
- const result = await sessions.runPrompt({
29635
- additionalDirectories: resolved.additionalDirectories,
29636
- agent,
29637
- commandId: command.commandId,
29638
- cwd: resolved.cwd,
29639
- images: attachments.images,
29640
- onActivity: (activity) => {
29641
- publisher.recordAttempt(
29642
- command.sessionId,
29643
- encodeAgentActivityEvent({
29644
- activityId: activity.activityId,
29645
- activityType: activity.activityType,
29646
- commandId: command.commandId,
29647
- content: activity.content,
29648
- detail: activity.detail,
29649
- input: activity.input,
29650
- kind: AGENT_ACTIVITY_KIND,
29651
- locations: activity.locations,
29652
- name: activity.name,
29653
- output: activity.output,
29654
- sequence: activity.sequence,
29655
- sessionId: command.sessionId,
29656
- status: activity.status,
29657
- title: activity.title,
29658
- toolKind: activity.toolKind
29659
- }),
29660
- attemptId
29661
- );
29662
- return Promise.resolve();
29663
- },
29664
- onText: (text) => {
29665
- publisher.recordAttempt(
29666
- command.sessionId,
29667
- encodeAgentMessageDeltaEvent({
29668
- commandId: command.commandId,
29669
- kind: AGENT_MESSAGE_DELTA_KIND,
29670
- sessionId: command.sessionId,
29671
- text
29672
- }),
29673
- attemptId
30083
+ let stopReason;
30084
+ if (command.disposition === SessionPromptDisposition.Steer) {
30085
+ await sessions.steerPrompt({
30086
+ additionalDirectories: resolved.additionalDirectories,
30087
+ agent,
30088
+ commandId: command.commandId,
30089
+ cwd: resolved.cwd,
30090
+ images: attachments.images,
30091
+ prompt: command.prompt,
30092
+ resources: attachments.resources,
30093
+ sessionId: command.sessionId,
30094
+ signal,
30095
+ targetCommandId: command.targetCommandId
30096
+ });
30097
+ stopReason = "steered";
30098
+ } else {
30099
+ if (command.disposition === SessionPromptDisposition.Queue && !outbox.isTerminalExecution(command.targetCommandId, command.sessionId)) {
30100
+ throw new AcpExecutionError(
30101
+ "The queued prompt target is unavailable",
30102
+ {},
30103
+ "prompt_queue_target_unavailable"
29674
30104
  );
29675
- return Promise.resolve();
29676
- },
29677
- prompt: command.prompt,
29678
- resources: attachments.resources,
29679
- sessionId: command.sessionId,
29680
- signal
29681
- });
30105
+ }
30106
+ const result = await sessions.runPrompt({
30107
+ additionalDirectories: resolved.additionalDirectories,
30108
+ agent,
30109
+ commandId: command.commandId,
30110
+ cwd: resolved.cwd,
30111
+ disposition: command.disposition,
30112
+ images: attachments.images,
30113
+ onActivity: (activity) => {
30114
+ publisher.recordAttempt(
30115
+ command.sessionId,
30116
+ encodeAgentActivityEvent({
30117
+ activityId: activity.activityId,
30118
+ activityType: activity.activityType,
30119
+ commandId: command.commandId,
30120
+ content: activity.content,
30121
+ detail: activity.detail,
30122
+ input: activity.input,
30123
+ kind: AGENT_ACTIVITY_KIND,
30124
+ locations: activity.locations,
30125
+ name: activity.name,
30126
+ output: activity.output,
30127
+ sequence: activity.sequence,
30128
+ sessionId: command.sessionId,
30129
+ status: activity.status,
30130
+ title: activity.title,
30131
+ toolKind: activity.toolKind
30132
+ }),
30133
+ attemptId
30134
+ );
30135
+ return Promise.resolve();
30136
+ },
30137
+ onText: (text, messageId) => {
30138
+ publisher.recordAttempt(
30139
+ command.sessionId,
30140
+ encodeAgentMessageDeltaEvent({
30141
+ commandId: command.commandId,
30142
+ kind: AGENT_MESSAGE_DELTA_KIND,
30143
+ messageId,
30144
+ sessionId: command.sessionId,
30145
+ text
30146
+ }),
30147
+ attemptId
30148
+ );
30149
+ return Promise.resolve();
30150
+ },
30151
+ prompt: command.prompt,
30152
+ resources: attachments.resources,
30153
+ sessionId: command.sessionId,
30154
+ signal,
30155
+ targetCommandId: command.targetCommandId
30156
+ });
30157
+ stopReason = result.stopReason;
30158
+ }
29682
30159
  publisher.recordAttempt(
29683
30160
  command.sessionId,
29684
30161
  encodeSessionCompletedEvent({
29685
30162
  commandId: command.commandId,
29686
30163
  kind: SESSION_COMPLETED_KIND,
29687
30164
  sessionId: command.sessionId,
29688
- stopReason: result.stopReason
30165
+ stopReason
29689
30166
  }),
29690
30167
  attemptId
29691
30168
  );
29692
- outbox.transitionExecution(command.commandId, result.stopReason === "cancelled" ? "cancelled" : "succeeded");
30169
+ outbox.transitionExecution(command.commandId, stopReason === "cancelled" ? "cancelled" : "succeeded");
29693
30170
  audit.record({
29694
30171
  commandId: command.commandId,
29695
- event: result.stopReason === "cancelled" ? "session_cancelled" : "session_completed",
30172
+ event: stopReason === "cancelled" ? "session_cancelled" : "session_completed",
29696
30173
  sessionId: command.sessionId
29697
30174
  });
29698
30175
  } catch (error61) {
@@ -29736,12 +30213,6 @@ function botExecutionAgent(agent, contextHandle, socketPath) {
29736
30213
  }
29737
30214
  async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, signal) {
29738
30215
  try {
29739
- if (command.additionalDirectoryIds.length > 0) {
29740
- throw new LocalBotAuthorizationError(
29741
- "binding_not_approved",
29742
- "The Bot execution requested unapproved additional directories"
29743
- );
29744
- }
29745
30216
  const deadline = Date.parse(command.executionDeadlineAt);
29746
30217
  const remaining = deadline - Date.now();
29747
30218
  if (!Number.isSafeInteger(deadline) || remaining <= 0) {
@@ -29755,6 +30226,32 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
29755
30226
  Date.now(),
29756
30227
  signal
29757
30228
  );
30229
+ const requestedExecutionTargetOverridden = command.directoryId !== grant.workspaceRef || command.additionalDirectoryIds.length > 0;
30230
+ if (requestedExecutionTargetOverridden && !(command.replyRoute instanceof OwnerBotReplyRoute)) {
30231
+ throw new LocalBotAuthorizationError(
30232
+ "binding_not_approved",
30233
+ "Only a paired owner conversation can override the approved Bot workspace"
30234
+ );
30235
+ }
30236
+ const resolved = await resolveSessionDirectories(
30237
+ directories,
30238
+ config2.bindingId,
30239
+ command.directoryId,
30240
+ command.additionalDirectoryIds,
30241
+ signal
30242
+ );
30243
+ const executionTargetOverridden = resolved.cwd !== grant.workspacePath || resolved.additionalDirectories.length > 0;
30244
+ if (executionTargetOverridden && !sessions.hasPreparedSession(
30245
+ command.sessionId,
30246
+ grant.agent.id,
30247
+ resolved.cwd,
30248
+ resolved.additionalDirectories
30249
+ )) {
30250
+ throw new LocalBotAuthorizationError(
30251
+ "local_approval_required",
30252
+ "Open this Bot conversation in the paired local Web app before using the selected workspace"
30253
+ );
30254
+ }
29758
30255
  const attachments = await resolvePromptArtifacts(config2, command, signal);
29759
30256
  const applied = await applicator.apply(command, grant, attachments.resources, signal);
29760
30257
  const lease = contexts.activate(command);
@@ -29762,6 +30259,13 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
29762
30259
  const deadlineSignal = AbortSignal.timeout(remaining);
29763
30260
  const executionSignal = AbortSignal.any([signal, deadlineSignal]);
29764
30261
  const agent = botExecutionAgent(grant.agent, lease.context.contextHandle, ipc.socketPath);
30262
+ await sessions.prepareExecutionSession({
30263
+ additionalDirectories: resolved.additionalDirectories,
30264
+ agent,
30265
+ cwd: resolved.cwd,
30266
+ sessionId: command.sessionId,
30267
+ signal: executionSignal
30268
+ });
29765
30269
  audit.record({
29766
30270
  commandId: command.commandId,
29767
30271
  details: {
@@ -29786,10 +30290,10 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
29786
30290
  attemptId
29787
30291
  );
29788
30292
  const result = await sessions.runPrompt({
29789
- additionalDirectories: [],
30293
+ additionalDirectories: resolved.additionalDirectories,
29790
30294
  agent,
29791
30295
  commandId: command.commandId,
29792
- cwd: grant.workspacePath,
30296
+ cwd: resolved.cwd,
29793
30297
  images: attachments.images,
29794
30298
  onActivity: (activity) => {
29795
30299
  publisher.recordAttempt(
@@ -29815,12 +30319,13 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
29815
30319
  );
29816
30320
  return Promise.resolve();
29817
30321
  },
29818
- onText: (text) => {
30322
+ onText: (text, messageId) => {
29819
30323
  publisher.recordAttempt(
29820
30324
  command.sessionId,
29821
30325
  encodeAgentMessageDeltaEvent({
29822
30326
  commandId: command.commandId,
29823
30327
  kind: AGENT_MESSAGE_DELTA_KIND,
30328
+ messageId,
29824
30329
  sessionId: command.sessionId,
29825
30330
  text
29826
30331
  }),
@@ -30015,6 +30520,14 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30015
30520
  }
30016
30521
  return;
30017
30522
  }
30523
+ if (kind === BOT_BINDING_APPROVED_KIND) {
30524
+ decodeBotExecutionBindingApprovedEvent(encoded);
30525
+ return;
30526
+ }
30527
+ if (kind === BOT_BINDING_UNAVAILABLE_KIND) {
30528
+ decodeBotExecutionBindingUnavailableEvent(encoded);
30529
+ return;
30530
+ }
30018
30531
  if (kind === BOT_BINDING_APPROVAL_REQUEST_KIND) {
30019
30532
  const request = decodeBotExecutionBindingApprovalRequest(encoded);
30020
30533
  void grants.approve(
@@ -30167,6 +30680,86 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30167
30680
  });
30168
30681
  return;
30169
30682
  }
30683
+ if (kind === NATIVE_SESSION_TURN_FORK_KIND) {
30684
+ const command2 = decodeNativeSessionTurnForkCommand(encoded);
30685
+ const previous = [...nativeRequests.values()].filter(
30686
+ (request) => request.agentId === command2.agentId
30687
+ );
30688
+ if (nativeRequests.has(command2.requestId) || nativeRequests.size >= 4 || previous.some((request) => !request.controller.signal.aborted)) {
30689
+ reportOperationFailure(
30690
+ socket,
30691
+ command2.requestId,
30692
+ command2.sessionId,
30693
+ new AcpExecutionError(
30694
+ "Another native session operation is running",
30695
+ {},
30696
+ "native_session_busy"
30697
+ )
30698
+ );
30699
+ return;
30700
+ }
30701
+ const controller = new AbortController();
30702
+ const operationSignal = AbortSignal.any([connectionController.signal, controller.signal]);
30703
+ const timeout = timer.schedule(
30704
+ () => controller.abort(
30705
+ new AcpExecutionError(
30706
+ "Native session operation timed out",
30707
+ {},
30708
+ "native_session_timeout"
30709
+ )
30710
+ ),
30711
+ 9e4
30712
+ );
30713
+ const completion = (async () => {
30714
+ try {
30715
+ operationSignal.throwIfAborted();
30716
+ if (previous.length > 0) {
30717
+ await new Promise((resolve6, reject2) => {
30718
+ const cancel = () => reject2(operationSignal.reason);
30719
+ operationSignal.addEventListener("abort", cancel, { once: true });
30720
+ void Promise.all(previous.map((request) => request.completion)).then(
30721
+ () => {
30722
+ operationSignal.removeEventListener("abort", cancel);
30723
+ resolve6();
30724
+ },
30725
+ (error61) => {
30726
+ operationSignal.removeEventListener("abort", cancel);
30727
+ reject2(error61);
30728
+ }
30729
+ );
30730
+ });
30731
+ }
30732
+ operationSignal.throwIfAborted();
30733
+ const result = await nativeSessions.executeTurnFork(command2, operationSignal);
30734
+ if (socket.readyState === WebSocket2.OPEN) {
30735
+ send(socket, encodeNativeSessionResult(result));
30736
+ }
30737
+ } catch (error61) {
30738
+ const failure = operationSignal.aborted ? controller.signal.reason ?? new AcpExecutionError(
30739
+ "Native session operation cancelled",
30740
+ {},
30741
+ "native_session_cancelled"
30742
+ ) : error61;
30743
+ audit.record({
30744
+ commandId: command2.requestId,
30745
+ details: { code: operationErrorCode(failure) },
30746
+ event: "native_session_turn_fork_failed",
30747
+ sessionId: command2.sessionId
30748
+ });
30749
+ reportOperationFailure(socket, command2.requestId, command2.sessionId, failure);
30750
+ } finally {
30751
+ timeout.cancel();
30752
+ nativeRequests.delete(command2.requestId);
30753
+ }
30754
+ })();
30755
+ nativeRequests.set(command2.requestId, {
30756
+ agentId: command2.agentId,
30757
+ completion,
30758
+ controller,
30759
+ sessionId: command2.sessionId
30760
+ });
30761
+ return;
30762
+ }
30170
30763
  if (kind === RUNTIME_LIVENESS_READY_KIND) {
30171
30764
  decodeRuntimeLivenessReadyEvent(encoded);
30172
30765
  connectionLiveness.start();
@@ -30320,7 +30913,9 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30320
30913
  }
30321
30914
  const turn = new ActiveDaemonTurn(command, receipt.attemptId);
30322
30915
  activeTurns.set(command.commandId, turn);
30323
- activeAttemptIds.set(command.sessionId, receipt.attemptId);
30916
+ if (command.disposition !== SessionPromptDisposition.Steer) {
30917
+ activeAttemptIds.set(command.sessionId, receipt.attemptId);
30918
+ }
30324
30919
  void executePrompt(
30325
30920
  outbox,
30326
30921
  publisher,