@adhdev/daemon-core 0.8.24 → 0.8.27

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 (34) hide show
  1. package/dist/agent-stream/provider-adapter.d.ts +1 -0
  2. package/dist/boot/daemon-lifecycle.d.ts +2 -0
  3. package/dist/cli-adapters/pty-transport.d.ts +3 -0
  4. package/dist/commands/router.d.ts +24 -0
  5. package/dist/index.js +309 -19
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +309 -19
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/providers/cli-provider-instance.d.ts +1 -0
  10. package/dist/providers/extension-provider-instance.d.ts +7 -0
  11. package/dist/sessions/reconcile.d.ts +22 -0
  12. package/dist/status/normalize.js +14 -2
  13. package/dist/status/normalize.js.map +1 -1
  14. package/dist/status/normalize.mjs +14 -2
  15. package/dist/status/normalize.mjs.map +1 -1
  16. package/dist/status/snapshot.d.ts +0 -2
  17. package/node_modules/@adhdev/session-host-core/dist/index.d.mts +72 -1
  18. package/node_modules/@adhdev/session-host-core/dist/index.d.ts +72 -1
  19. package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
  20. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  21. package/package.json +1 -1
  22. package/src/agent-stream/poller.ts +4 -0
  23. package/src/agent-stream/provider-adapter.ts +30 -1
  24. package/src/boot/daemon-lifecycle.ts +3 -0
  25. package/src/cli-adapters/pty-transport.ts +3 -0
  26. package/src/cli-adapters/session-host-transport.ts +8 -0
  27. package/src/commands/chat-commands.ts +2 -2
  28. package/src/commands/handler.ts +79 -13
  29. package/src/commands/router.ts +132 -0
  30. package/src/providers/cli-provider-instance.ts +29 -1
  31. package/src/providers/extension-provider-instance.ts +24 -1
  32. package/src/sessions/reconcile.ts +85 -0
  33. package/src/status/normalize.ts +19 -2
  34. package/src/status/snapshot.ts +6 -13
package/dist/index.mjs CHANGED
@@ -5116,6 +5116,7 @@ var ExtensionProviderInstance = class {
5116
5116
  currentStatus = "idle";
5117
5117
  agentStreams = [];
5118
5118
  messages = [];
5119
+ prevMessageHashes = /* @__PURE__ */ new Map();
5119
5120
  activeModal = null;
5120
5121
  currentModel = "";
5121
5122
  currentMode = "";
@@ -5181,7 +5182,7 @@ var ExtensionProviderInstance = class {
5181
5182
  onEvent(event, data) {
5182
5183
  if (event === "stream_update") {
5183
5184
  if (data?.streams) this.agentStreams = data.streams;
5184
- if (data?.messages) this.messages = data.messages;
5185
+ if (data?.messages) this.messages = this.assignReceivedAt(data.messages);
5185
5186
  if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
5186
5187
  if (data?.model) this.currentModel = data.model;
5187
5188
  if (data?.mode) this.currentMode = data.mode;
@@ -5207,6 +5208,7 @@ var ExtensionProviderInstance = class {
5207
5208
  dispose() {
5208
5209
  this.agentStreams = [];
5209
5210
  this.messages = [];
5211
+ this.prevMessageHashes.clear();
5210
5212
  this.monitor.reset();
5211
5213
  this.appliedEffectKeys.clear();
5212
5214
  this.runtimeMessages = [];
@@ -5362,6 +5364,23 @@ var ExtensionProviderInstance = class {
5362
5364
  this.chatId || this.instanceId
5363
5365
  );
5364
5366
  }
5367
+ /**
5368
+ * Assign stable receivedAt to extension messages.
5369
+ * Same pattern as IdeProviderInstance.readChat() prevByHash —
5370
+ * preserves first-seen timestamp across polling cycles.
5371
+ */
5372
+ assignReceivedAt(messages) {
5373
+ const now = Date.now();
5374
+ const nextHashes = /* @__PURE__ */ new Map();
5375
+ for (const msg of messages) {
5376
+ const hash = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
5377
+ const prevTime = this.prevMessageHashes.get(hash);
5378
+ msg.receivedAt = prevTime || now;
5379
+ nextHashes.set(hash, msg.receivedAt);
5380
+ }
5381
+ this.prevMessageHashes = nextHashes;
5382
+ return messages;
5383
+ }
5365
5384
  mergeConversationMessages(messages) {
5366
5385
  if (this.runtimeMessages.length === 0) return messages;
5367
5386
  return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
@@ -5418,6 +5437,7 @@ ${effect.notification.body || ""}`.trim();
5418
5437
  }
5419
5438
  this.agentStreams = [];
5420
5439
  this.messages = [];
5440
+ this.prevMessageHashes.clear();
5421
5441
  this.activeModal = null;
5422
5442
  this.currentModel = "";
5423
5443
  this.currentMode = "";
@@ -6403,16 +6423,28 @@ function trimMessageForStatus(message, stringLimit) {
6403
6423
  if (!message || typeof message !== "object") return message;
6404
6424
  return trimStructuredStrings(message, stringLimit);
6405
6425
  }
6426
+ function normalizeMessageTime(message) {
6427
+ if (!message || typeof message !== "object") return message;
6428
+ const msg = message;
6429
+ if (msg.receivedAt == null) {
6430
+ const fallback = msg.timestamp ?? msg.createdAt;
6431
+ if (fallback != null) {
6432
+ const ts2 = typeof fallback === "string" ? Date.parse(fallback) : Number(fallback);
6433
+ if (Number.isFinite(ts2) && ts2 > 0) msg.receivedAt = ts2;
6434
+ }
6435
+ }
6436
+ return msg;
6437
+ }
6406
6438
  function trimMessagesForStatus(messages) {
6407
6439
  if (!Array.isArray(messages) || messages.length === 0) return [];
6408
6440
  const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
6409
6441
  const kept = [];
6410
6442
  let totalBytes = 0;
6411
6443
  for (let i = recent.length - 1; i >= 0; i -= 1) {
6412
- let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);
6444
+ let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT));
6413
6445
  let size = estimateBytes(normalized);
6414
6446
  if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
6415
- normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);
6447
+ normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT));
6416
6448
  size = estimateBytes(normalized);
6417
6449
  }
6418
6450
  if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
@@ -6724,6 +6756,51 @@ function buildSessionEntries(allStates, cdpManagers) {
6724
6756
  return sessions;
6725
6757
  }
6726
6758
 
6759
+ // src/sessions/reconcile.ts
6760
+ function upsertSessionTarget(sessionRegistry, target) {
6761
+ const existing = sessionRegistry.get(target.sessionId);
6762
+ if (existing && existing.parentSessionId === target.parentSessionId && existing.providerType === target.providerType && existing.transport === target.transport && existing.cdpManagerKey === target.cdpManagerKey && existing.instanceKey === target.instanceKey) {
6763
+ return;
6764
+ }
6765
+ sessionRegistry.register(target);
6766
+ }
6767
+ function reconcileIdeRuntimeSessions(instanceManager, sessionRegistry) {
6768
+ if (!instanceManager || !sessionRegistry) return;
6769
+ for (const instanceKey of instanceManager.listInstanceIds()) {
6770
+ if (!instanceKey.startsWith("ide:")) continue;
6771
+ const ideInstance = instanceManager.getInstance(instanceKey);
6772
+ if (!ideInstance || ideInstance.category !== "ide" || typeof ideInstance.getInstanceId !== "function") {
6773
+ continue;
6774
+ }
6775
+ const managerKey = instanceKey.slice(4);
6776
+ const ideType = typeof ideInstance.type === "string" && ideInstance.type.trim() ? ideInstance.type.trim() : managerKey.split("_")[0];
6777
+ const parentSessionId = ideInstance.getInstanceId();
6778
+ if (!parentSessionId) continue;
6779
+ upsertSessionTarget(sessionRegistry, {
6780
+ sessionId: parentSessionId,
6781
+ parentSessionId: null,
6782
+ providerType: ideType,
6783
+ transport: "cdp-page",
6784
+ cdpManagerKey: managerKey,
6785
+ instanceKey
6786
+ });
6787
+ const extensions = ideInstance.getExtensionInstances?.() || [];
6788
+ for (const ext of extensions) {
6789
+ const extType = typeof ext?.type === "string" ? ext.type.trim() : "";
6790
+ const extSessionId = ext?.getInstanceId?.();
6791
+ if (!extType || !extSessionId) continue;
6792
+ upsertSessionTarget(sessionRegistry, {
6793
+ sessionId: extSessionId,
6794
+ parentSessionId,
6795
+ providerType: extType,
6796
+ transport: "cdp-webview",
6797
+ cdpManagerKey: managerKey,
6798
+ instanceKey
6799
+ });
6800
+ }
6801
+ }
6802
+ }
6803
+
6727
6804
  // src/commands/handler.ts
6728
6805
  init_logger();
6729
6806
 
@@ -6990,7 +7067,7 @@ async function handleSendChat(h, args) {
6990
7067
  if (isExtensionTransport(transport)) {
6991
7068
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
6992
7069
  try {
6993
- const evalResult = await h.evaluateProviderScript("sendMessage", { MESSAGE: text }, 3e4);
7070
+ const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
6994
7071
  if (evalResult?.result) {
6995
7072
  const parsed = parseMaybeJson(evalResult.result);
6996
7073
  if (didProviderConfirmSend(parsed)) {
@@ -7021,7 +7098,7 @@ async function handleSendChat(h, args) {
7021
7098
  return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
7022
7099
  }
7023
7100
  _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
7024
- const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
7101
+ const sendScript = h.getProviderScript("sendMessage", { message: text });
7025
7102
  if (sendScript) {
7026
7103
  try {
7027
7104
  const result = await targetCdp.evaluate(sendScript, 3e4);
@@ -8423,9 +8500,26 @@ var DaemonCommandHandler = class {
8423
8500
  if (provider?.scripts) {
8424
8501
  const fn = provider.scripts[scriptName];
8425
8502
  if (typeof fn === "function") {
8426
- const firstVal = params ? Object.values(params)[0] : void 0;
8427
- const script = firstVal ? fn(firstVal) : fn();
8428
- if (script) return script;
8503
+ if (params && Object.keys(params).length > 0) {
8504
+ const firstVal = Object.values(params)[0];
8505
+ if (scriptName === "sendMessage" && typeof firstVal === "string") {
8506
+ const legacyScript = fn(firstVal);
8507
+ if (legacyScript) return legacyScript;
8508
+ }
8509
+ const script = fn(params);
8510
+ if (script) {
8511
+ const likelyLegacyObjectLeak = typeof script === "string" && script.includes("[object Object]") && typeof firstVal === "string";
8512
+ if (!likelyLegacyObjectLeak) return script;
8513
+ }
8514
+ if (firstVal !== void 0) {
8515
+ const legacyScript = fn(firstVal);
8516
+ if (legacyScript) return legacyScript;
8517
+ }
8518
+ if (script) return script;
8519
+ } else {
8520
+ const script = fn();
8521
+ if (script) return script;
8522
+ }
8429
8523
  }
8430
8524
  }
8431
8525
  return null;
@@ -8481,17 +8575,27 @@ var DaemonCommandHandler = class {
8481
8575
  return key.split("_")[0];
8482
8576
  }
8483
8577
  resolveRoute(args) {
8484
- const session = this._ctx.sessionRegistry?.get(args?.targetSessionId);
8485
- const managerKey = this.extractIdeType(args);
8486
- const providerType = args?.agentType || args?.providerType || session?.providerType || this.inferProviderType(managerKey);
8487
- return { session, managerKey, providerType };
8578
+ const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
8579
+ let session = targetSessionId ? this._ctx.sessionRegistry?.get(targetSessionId) : void 0;
8580
+ if (targetSessionId && !session) {
8581
+ reconcileIdeRuntimeSessions(this._ctx.instanceManager, this._ctx.sessionRegistry);
8582
+ session = this._ctx.sessionRegistry?.get(targetSessionId);
8583
+ }
8584
+ const sessionLookupFailed = !!targetSessionId && !session;
8585
+ const managerKey = this.extractIdeType(args, sessionLookupFailed);
8586
+ let providerType;
8587
+ if (!sessionLookupFailed) {
8588
+ providerType = session?.providerType || args?.agentType || args?.providerType || this.inferProviderType(managerKey);
8589
+ }
8590
+ return { session, managerKey, providerType, sessionLookupFailed };
8488
8591
  }
8489
8592
  /** Extract CDP scope key from target session or explicit ideType */
8490
- extractIdeType(args) {
8593
+ extractIdeType(args, sessionLookupFailed = false) {
8491
8594
  if (args?.targetSessionId) {
8492
8595
  const target = this._ctx.sessionRegistry?.get(args.targetSessionId);
8493
8596
  if (target?.cdpManagerKey) return target.cdpManagerKey;
8494
8597
  if (this._ctx.cdpManagers.has(args.targetSessionId)) return args.targetSessionId;
8598
+ if (sessionLookupFailed) return void 0;
8495
8599
  }
8496
8600
  if (args?.ideType) {
8497
8601
  const target = this._ctx.sessionRegistry?.get(args.ideType);
@@ -8538,6 +8642,33 @@ var DaemonCommandHandler = class {
8538
8642
  this._currentRoute = this.resolveRoute(args);
8539
8643
  const startedAt = Date.now();
8540
8644
  this.logCommandStart(cmd, args);
8645
+ const sessionScopedCommands = /* @__PURE__ */ new Set([
8646
+ "read_chat",
8647
+ "send_chat",
8648
+ "list_chats",
8649
+ "new_chat",
8650
+ "switch_chat",
8651
+ "set_mode",
8652
+ "change_model",
8653
+ "set_thought_level",
8654
+ "resolve_action",
8655
+ "focus_session",
8656
+ "pty_input",
8657
+ "pty_resize",
8658
+ "invoke_provider_script",
8659
+ "list_extension_models",
8660
+ "set_extension_model",
8661
+ "list_extension_modes",
8662
+ "set_extension_mode"
8663
+ ]);
8664
+ if (this._currentRoute.sessionLookupFailed && sessionScopedCommands.has(cmd)) {
8665
+ const result2 = {
8666
+ success: false,
8667
+ error: `Live session not found for targetSessionId: ${String(args?.targetSessionId || "").trim() || "unknown"}`
8668
+ };
8669
+ this.logCommandEnd(cmd, result2, startedAt);
8670
+ return result2;
8671
+ }
8541
8672
  let result;
8542
8673
  if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
8543
8674
  const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
@@ -8849,6 +8980,7 @@ var CliProviderInstance = class {
8849
8980
  this.detectStatusTransition();
8850
8981
  });
8851
8982
  await this.adapter.spawn();
8983
+ this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
8852
8984
  if (this.providerSessionId) {
8853
8985
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
8854
8986
  if (restoredHistory.messages.length > 0) {
@@ -8943,6 +9075,7 @@ var CliProviderInstance = class {
8943
9075
  this.promoteProviderSessionId(parsedProviderSessionId);
8944
9076
  }
8945
9077
  const runtime = this.adapter.getRuntimeMetadata();
9078
+ this.maybeAppendRuntimeRecoveryMessage(runtime);
8946
9079
  const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
8947
9080
  const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
8948
9081
  if (controlValues) {
@@ -9269,6 +9402,28 @@ ${effect.notification.body || ""}`.trim();
9269
9402
  const pad = (value) => String(value).padStart(2, "0");
9270
9403
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
9271
9404
  }
9405
+ maybeAppendRuntimeRecoveryMessage(runtime) {
9406
+ if (!runtime?.restoredFromStorage || !runtime.runtimeId) return;
9407
+ const recoveryState = String(runtime.recoveryState || "").trim();
9408
+ if (!recoveryState) return;
9409
+ let content = "";
9410
+ if (recoveryState === "auto_resumed") {
9411
+ content = "Session host restored this CLI after restart and reattached it from a saved snapshot.";
9412
+ } else if (recoveryState === "resume_failed") {
9413
+ const errorSuffix = runtime.recoveryError ? ` Resume failed: ${runtime.recoveryError}` : "";
9414
+ content = `Session host found this CLI after restart, but automatic resume failed.${errorSuffix}`;
9415
+ } else if (recoveryState === "host_restart_interrupted") {
9416
+ content = "Session host found this CLI in interrupted state after restart and is attempting to resume it.";
9417
+ } else if (recoveryState === "orphan_snapshot") {
9418
+ content = "Session host restored the last snapshot for this CLI, but the original runtime was not resumed automatically.";
9419
+ } else {
9420
+ content = `Session host restored this CLI after restart (${recoveryState}).`;
9421
+ }
9422
+ this.appendRuntimeSystemMessage(
9423
+ content,
9424
+ `runtime_recovery:${runtime.runtimeId}:${recoveryState}`
9425
+ );
9426
+ }
9272
9427
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
9273
9428
  const normalizedContent = String(content || "").trim();
9274
9429
  if (!normalizedContent) return;
@@ -12562,17 +12717,17 @@ function parseMessageTime(value) {
12562
12717
  function getSessionMessageUpdatedAt(session) {
12563
12718
  const lastMessage = session.activeChat?.messages?.at?.(-1);
12564
12719
  if (!lastMessage) return 0;
12565
- return parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt) || 0;
12720
+ return parseMessageTime(lastMessage.receivedAt) || 0;
12566
12721
  }
12567
12722
  function getSessionCompletionMarker(session) {
12568
12723
  const lastMessage = session.activeChat?.messages?.at?.(-1);
12569
12724
  if (!lastMessage) return "";
12570
12725
  const role = typeof lastMessage.role === "string" ? lastMessage.role : "";
12571
- if (role === "user" || role === "human") return "";
12726
+ if (role === "user" || role === "human" || role === "system") return "";
12572
12727
  if (typeof lastMessage._turnKey === "string" && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
12573
12728
  if (typeof lastMessage.id === "string" && lastMessage.id) return `id:${lastMessage.id}`;
12574
12729
  if (typeof lastMessage.index === "number" && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
12575
- const timestamp = parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt);
12730
+ const timestamp = parseMessageTime(lastMessage.receivedAt);
12576
12731
  return timestamp > 0 ? `ts:${timestamp}` : "";
12577
12732
  }
12578
12733
  function getSessionLastUsedAt(session) {
@@ -12589,7 +12744,7 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
12589
12744
  if (status === "generating" || status === "starting") {
12590
12745
  return { unread: false, inboxBucket: "working" };
12591
12746
  }
12592
- const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human";
12747
+ const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
12593
12748
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
12594
12749
  }
12595
12750
  function buildRecentLaunches(recentActivity) {
@@ -12874,6 +13029,25 @@ var CHAT_COMMANDS = [
12874
13029
  "change_model"
12875
13030
  ];
12876
13031
  var READ_DEBUG_ENABLED2 = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
13032
+ function toHostedCliRuntimeDescriptor(record) {
13033
+ if (!record || typeof record !== "object") return null;
13034
+ const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
13035
+ const cliType = typeof record.providerType === "string" ? record.providerType : "";
13036
+ const workspace = typeof record.workspace === "string" ? record.workspace : "";
13037
+ if (!runtimeId || !cliType || !workspace) return null;
13038
+ return {
13039
+ runtimeId,
13040
+ runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
13041
+ displayName: typeof record.displayName === "string" ? record.displayName : void 0,
13042
+ workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
13043
+ lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
13044
+ recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
13045
+ cliType,
13046
+ workspace,
13047
+ cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
13048
+ providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
13049
+ };
13050
+ }
12877
13051
  var DaemonCommandRouter = class {
12878
13052
  deps;
12879
13053
  constructor(deps) {
@@ -12947,6 +13121,90 @@ var DaemonCommandRouter = class {
12947
13121
  return { success: false, error: e.message };
12948
13122
  }
12949
13123
  }
13124
+ case "session_host_get_diagnostics": {
13125
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13126
+ const diagnostics = await this.deps.sessionHostControl.getDiagnostics({
13127
+ includeSessions: args?.includeSessions !== false,
13128
+ limit: Number(args?.limit) || void 0
13129
+ });
13130
+ return { success: true, diagnostics };
13131
+ }
13132
+ case "session_host_list_sessions": {
13133
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13134
+ const sessions = await this.deps.sessionHostControl.listSessions();
13135
+ return { success: true, sessions };
13136
+ }
13137
+ case "session_host_stop_session": {
13138
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13139
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13140
+ if (!sessionId) return { success: false, error: "sessionId required" };
13141
+ const record = await this.deps.sessionHostControl.stopSession(sessionId);
13142
+ return { success: true, record };
13143
+ }
13144
+ case "session_host_resume_session": {
13145
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13146
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13147
+ if (!sessionId) return { success: false, error: "sessionId required" };
13148
+ const record = await this.deps.sessionHostControl.resumeSession(sessionId);
13149
+ const hosted = toHostedCliRuntimeDescriptor(record);
13150
+ if (hosted) {
13151
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
13152
+ }
13153
+ return { success: true, record };
13154
+ }
13155
+ case "session_host_restart_session": {
13156
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13157
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13158
+ if (!sessionId) return { success: false, error: "sessionId required" };
13159
+ const record = await this.deps.sessionHostControl.restartSession(sessionId);
13160
+ const hosted = toHostedCliRuntimeDescriptor(record);
13161
+ if (hosted) {
13162
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
13163
+ }
13164
+ return { success: true, record };
13165
+ }
13166
+ case "session_host_send_signal": {
13167
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13168
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13169
+ const signal = typeof args?.signal === "string" ? args.signal : "";
13170
+ if (!sessionId) return { success: false, error: "sessionId required" };
13171
+ if (!signal) return { success: false, error: "signal required" };
13172
+ const record = await this.deps.sessionHostControl.sendSignal(sessionId, signal);
13173
+ return { success: true, record };
13174
+ }
13175
+ case "session_host_force_detach_client": {
13176
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13177
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13178
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13179
+ if (!sessionId) return { success: false, error: "sessionId required" };
13180
+ if (!clientId) return { success: false, error: "clientId required" };
13181
+ const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
13182
+ return { success: true, record };
13183
+ }
13184
+ case "session_host_acquire_write": {
13185
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13186
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13187
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13188
+ const ownerType = args?.ownerType === "agent" ? "agent" : "user";
13189
+ if (!sessionId) return { success: false, error: "sessionId required" };
13190
+ if (!clientId) return { success: false, error: "clientId required" };
13191
+ const record = await this.deps.sessionHostControl.acquireWrite({
13192
+ sessionId,
13193
+ clientId,
13194
+ ownerType,
13195
+ force: args?.force !== false
13196
+ });
13197
+ return { success: true, record };
13198
+ }
13199
+ case "session_host_release_write": {
13200
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13201
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13202
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13203
+ if (!sessionId) return { success: false, error: "sessionId required" };
13204
+ if (!clientId) return { success: false, error: "clientId required" };
13205
+ const record = await this.deps.sessionHostControl.releaseWrite({ sessionId, clientId });
13206
+ return { success: true, record };
13207
+ }
12950
13208
  case "list_saved_sessions": {
12951
13209
  const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
12952
13210
  const kind = args?.kind === "acp" ? "acp" : "cli";
@@ -13475,6 +13733,14 @@ var ProviderStreamAdapter = class {
13475
13733
  hasScript(name) {
13476
13734
  return typeof this.provider.scripts?.[name] === "function";
13477
13735
  }
13736
+ parseMaybeJson(raw) {
13737
+ if (typeof raw !== "string") return raw;
13738
+ try {
13739
+ return JSON.parse(raw);
13740
+ } catch {
13741
+ return raw;
13742
+ }
13743
+ }
13478
13744
  summarizeRaw(raw) {
13479
13745
  try {
13480
13746
  if (typeof raw === "string") return raw.replace(/\s+/g, " ").trim().slice(0, 240);
@@ -13535,12 +13801,30 @@ var ProviderStreamAdapter = class {
13535
13801
  }
13536
13802
  }
13537
13803
  async sendMessage(evaluate, text) {
13538
- const script = this.callScript("sendMessage", text);
13804
+ const params = { message: text };
13805
+ const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
13539
13806
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
13540
13807
  const result = await evaluate(script);
13541
13808
  if (result && typeof result === "string" && result.startsWith("error:")) {
13542
13809
  throw new Error(`[${this.agentName}] sendMessage failed: ${result}`);
13543
13810
  }
13811
+ const parsed = this.parseMaybeJson(result);
13812
+ if (parsed === true) return;
13813
+ if (typeof parsed === "string") {
13814
+ const normalized = parsed.trim().toLowerCase();
13815
+ if (normalized === "ok" || normalized === "sent" || normalized === "success" || normalized === "true") {
13816
+ return;
13817
+ }
13818
+ }
13819
+ if (parsed && typeof parsed === "object") {
13820
+ if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
13821
+ return;
13822
+ }
13823
+ if (typeof parsed.error === "string" && parsed.error.trim()) {
13824
+ throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
13825
+ }
13826
+ }
13827
+ throw new Error(`[${this.agentName}] sendMessage was not confirmed`);
13544
13828
  }
13545
13829
  async resolveAction(evaluate, action, button) {
13546
13830
  const script = this.callScript("resolveAction", { action, button });
@@ -13925,6 +14209,7 @@ var AgentStreamPoller = class {
13925
14209
  sessionRegistry
13926
14210
  } = this.deps;
13927
14211
  if (!agentStreamManager || cdpManagers.size === 0) return;
14212
+ reconcileIdeRuntimeSessions(instanceManager, sessionRegistry);
13928
14213
  for (const [ideType, cdp] of cdpManagers) {
13929
14214
  registerExtensionProviders(providerLoader, cdp, ideType);
13930
14215
  const ideInstance = instanceManager.getInstance(`ide:${ideType}`);
@@ -19792,6 +20077,7 @@ var SessionHostRuntimeTransport = class {
19792
20077
  }
19793
20078
  }
19794
20079
  handleEvent(event) {
20080
+ if (!("sessionId" in event)) return;
19795
20081
  if (event.sessionId !== this.options.runtimeId) return;
19796
20082
  if ((event.type === "session_started" || event.type === "session_resumed") && typeof event.pid === "number") {
19797
20083
  this.currentPid = event.pid;
@@ -19867,7 +20153,10 @@ var SessionHostRuntimeTransport = class {
19867
20153
  clientId: client.clientId,
19868
20154
  type: client.type,
19869
20155
  readOnly: client.readOnly
19870
- }))
20156
+ })),
20157
+ restoredFromStorage: record.meta?.restoredFromStorage === true,
20158
+ recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
20159
+ recoveryError: typeof record.meta?.runtimeRecoveryError === "string" ? String(record.meta.runtimeRecoveryError) : null
19871
20160
  };
19872
20161
  }
19873
20162
  enqueue(action) {
@@ -20314,6 +20603,7 @@ async function initDaemonComponents(config) {
20314
20603
  onIdeConnected: () => poller?.start(),
20315
20604
  onStatusChange: config.onStatusChange,
20316
20605
  onPostChatCommand: config.onPostChatCommand,
20606
+ sessionHostControl: config.sessionHostControl,
20317
20607
  getCdpLogFn: config.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
20318
20608
  });
20319
20609
  poller = new AgentStreamPoller({