adhdev 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.
package/dist/index.js CHANGED
@@ -2852,6 +2852,7 @@ var init_extension_provider_instance = __esm({
2852
2852
  currentStatus = "idle";
2853
2853
  agentStreams = [];
2854
2854
  messages = [];
2855
+ prevMessageHashes = /* @__PURE__ */ new Map();
2855
2856
  activeModal = null;
2856
2857
  currentModel = "";
2857
2858
  currentMode = "";
@@ -2917,7 +2918,7 @@ var init_extension_provider_instance = __esm({
2917
2918
  onEvent(event, data) {
2918
2919
  if (event === "stream_update") {
2919
2920
  if (data?.streams) this.agentStreams = data.streams;
2920
- if (data?.messages) this.messages = data.messages;
2921
+ if (data?.messages) this.messages = this.assignReceivedAt(data.messages);
2921
2922
  if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
2922
2923
  if (data?.model) this.currentModel = data.model;
2923
2924
  if (data?.mode) this.currentMode = data.mode;
@@ -2943,6 +2944,7 @@ var init_extension_provider_instance = __esm({
2943
2944
  dispose() {
2944
2945
  this.agentStreams = [];
2945
2946
  this.messages = [];
2947
+ this.prevMessageHashes.clear();
2946
2948
  this.monitor.reset();
2947
2949
  this.appliedEffectKeys.clear();
2948
2950
  this.runtimeMessages = [];
@@ -3098,6 +3100,23 @@ var init_extension_provider_instance = __esm({
3098
3100
  this.chatId || this.instanceId
3099
3101
  );
3100
3102
  }
3103
+ /**
3104
+ * Assign stable receivedAt to extension messages.
3105
+ * Same pattern as IdeProviderInstance.readChat() prevByHash —
3106
+ * preserves first-seen timestamp across polling cycles.
3107
+ */
3108
+ assignReceivedAt(messages) {
3109
+ const now = Date.now();
3110
+ const nextHashes = /* @__PURE__ */ new Map();
3111
+ for (const msg of messages) {
3112
+ const hash2 = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
3113
+ const prevTime = this.prevMessageHashes.get(hash2);
3114
+ msg.receivedAt = prevTime || now;
3115
+ nextHashes.set(hash2, msg.receivedAt);
3116
+ }
3117
+ this.prevMessageHashes = nextHashes;
3118
+ return messages;
3119
+ }
3101
3120
  mergeConversationMessages(messages) {
3102
3121
  if (this.runtimeMessages.length === 0) return messages;
3103
3122
  return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
@@ -3154,6 +3173,7 @@ ${effect.notification.body || ""}`.trim();
3154
3173
  }
3155
3174
  this.agentStreams = [];
3156
3175
  this.messages = [];
3176
+ this.prevMessageHashes.clear();
3157
3177
  this.activeModal = null;
3158
3178
  this.currentModel = "";
3159
3179
  this.currentMode = "";
@@ -4161,16 +4181,28 @@ function trimMessageForStatus(message, stringLimit) {
4161
4181
  if (!message || typeof message !== "object") return message;
4162
4182
  return trimStructuredStrings(message, stringLimit);
4163
4183
  }
4184
+ function normalizeMessageTime(message) {
4185
+ if (!message || typeof message !== "object") return message;
4186
+ const msg = message;
4187
+ if (msg.receivedAt == null) {
4188
+ const fallback = msg.timestamp ?? msg.createdAt;
4189
+ if (fallback != null) {
4190
+ const ts2 = typeof fallback === "string" ? Date.parse(fallback) : Number(fallback);
4191
+ if (Number.isFinite(ts2) && ts2 > 0) msg.receivedAt = ts2;
4192
+ }
4193
+ }
4194
+ return msg;
4195
+ }
4164
4196
  function trimMessagesForStatus(messages) {
4165
4197
  if (!Array.isArray(messages) || messages.length === 0) return [];
4166
4198
  const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
4167
4199
  const kept = [];
4168
4200
  let totalBytes = 0;
4169
4201
  for (let i = recent.length - 1; i >= 0; i -= 1) {
4170
- let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);
4202
+ let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT));
4171
4203
  let size = estimateBytes(normalized);
4172
4204
  if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
4173
- normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);
4205
+ normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT));
4174
4206
  size = estimateBytes(normalized);
4175
4207
  }
4176
4208
  if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
@@ -4510,6 +4542,56 @@ var init_builders = __esm({
4510
4542
  }
4511
4543
  });
4512
4544
 
4545
+ // ../../oss/packages/daemon-core/src/sessions/reconcile.ts
4546
+ function upsertSessionTarget(sessionRegistry, target) {
4547
+ const existing = sessionRegistry.get(target.sessionId);
4548
+ if (existing && existing.parentSessionId === target.parentSessionId && existing.providerType === target.providerType && existing.transport === target.transport && existing.cdpManagerKey === target.cdpManagerKey && existing.instanceKey === target.instanceKey) {
4549
+ return;
4550
+ }
4551
+ sessionRegistry.register(target);
4552
+ }
4553
+ function reconcileIdeRuntimeSessions(instanceManager, sessionRegistry) {
4554
+ if (!instanceManager || !sessionRegistry) return;
4555
+ for (const instanceKey of instanceManager.listInstanceIds()) {
4556
+ if (!instanceKey.startsWith("ide:")) continue;
4557
+ const ideInstance = instanceManager.getInstance(instanceKey);
4558
+ if (!ideInstance || ideInstance.category !== "ide" || typeof ideInstance.getInstanceId !== "function") {
4559
+ continue;
4560
+ }
4561
+ const managerKey = instanceKey.slice(4);
4562
+ const ideType = typeof ideInstance.type === "string" && ideInstance.type.trim() ? ideInstance.type.trim() : managerKey.split("_")[0];
4563
+ const parentSessionId = ideInstance.getInstanceId();
4564
+ if (!parentSessionId) continue;
4565
+ upsertSessionTarget(sessionRegistry, {
4566
+ sessionId: parentSessionId,
4567
+ parentSessionId: null,
4568
+ providerType: ideType,
4569
+ transport: "cdp-page",
4570
+ cdpManagerKey: managerKey,
4571
+ instanceKey
4572
+ });
4573
+ const extensions = ideInstance.getExtensionInstances?.() || [];
4574
+ for (const ext of extensions) {
4575
+ const extType = typeof ext?.type === "string" ? ext.type.trim() : "";
4576
+ const extSessionId = ext?.getInstanceId?.();
4577
+ if (!extType || !extSessionId) continue;
4578
+ upsertSessionTarget(sessionRegistry, {
4579
+ sessionId: extSessionId,
4580
+ parentSessionId,
4581
+ providerType: extType,
4582
+ transport: "cdp-webview",
4583
+ cdpManagerKey: managerKey,
4584
+ instanceKey
4585
+ });
4586
+ }
4587
+ }
4588
+ }
4589
+ var init_reconcile = __esm({
4590
+ "../../oss/packages/daemon-core/src/sessions/reconcile.ts"() {
4591
+ "use strict";
4592
+ }
4593
+ });
4594
+
4513
4595
  // ../../oss/packages/daemon-core/src/commands/chat-commands.ts
4514
4596
  function getCurrentProviderType(h, fallback = "") {
4515
4597
  return h.currentSession?.providerType || h.currentProviderType || fallback;
@@ -4770,7 +4852,7 @@ async function handleSendChat(h, args) {
4770
4852
  if (isExtensionTransport(transport)) {
4771
4853
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
4772
4854
  try {
4773
- const evalResult = await h.evaluateProviderScript("sendMessage", { MESSAGE: text }, 3e4);
4855
+ const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
4774
4856
  if (evalResult?.result) {
4775
4857
  const parsed = parseMaybeJson(evalResult.result);
4776
4858
  if (didProviderConfirmSend(parsed)) {
@@ -4801,7 +4883,7 @@ async function handleSendChat(h, args) {
4801
4883
  return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
4802
4884
  }
4803
4885
  _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
4804
- const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
4886
+ const sendScript = h.getProviderScript("sendMessage", { message: text });
4805
4887
  if (sendScript) {
4806
4888
  try {
4807
4889
  const result = await targetCdp.evaluate(sendScript, 3e4);
@@ -5809,10 +5891,10 @@ function getCliScriptCommand(payload) {
5809
5891
  }
5810
5892
  const command = payload.command;
5811
5893
  if (!command || typeof command !== "object") return null;
5812
- if (command.type !== "send_message") return null;
5894
+ if (command.type !== "send_message" && command.type !== "pty_write") return null;
5813
5895
  const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
5814
5896
  if (!text) return null;
5815
- return { type: "send_message", text };
5897
+ return { type: command.type, text };
5816
5898
  }
5817
5899
  function applyProviderPatch(h, args, payload) {
5818
5900
  if (!payload || typeof payload !== "object") return;
@@ -5853,6 +5935,8 @@ async function executeProviderScript(h, args, scriptName) {
5853
5935
  const cliCommand = getCliScriptCommand(parsed.payload);
5854
5936
  if (cliCommand?.type === "send_message" && cliCommand.text) {
5855
5937
  await adapter.sendMessage(cliCommand.text);
5938
+ } else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
5939
+ adapter.writeRaw(cliCommand.text + "\r");
5856
5940
  }
5857
5941
  applyProviderPatch(h, args, parsed.payload);
5858
5942
  return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
@@ -6166,6 +6250,7 @@ var init_handler = __esm({
6166
6250
  init_devtools();
6167
6251
  init_builders();
6168
6252
  init_chat_history();
6253
+ init_reconcile();
6169
6254
  init_logger();
6170
6255
  init_chat_commands();
6171
6256
  init_cdp_commands();
@@ -6242,9 +6327,26 @@ var init_handler = __esm({
6242
6327
  if (provider?.scripts) {
6243
6328
  const fn = provider.scripts[scriptName];
6244
6329
  if (typeof fn === "function") {
6245
- const firstVal = params ? Object.values(params)[0] : void 0;
6246
- const script = firstVal ? fn(firstVal) : fn();
6247
- if (script) return script;
6330
+ if (params && Object.keys(params).length > 0) {
6331
+ const firstVal = Object.values(params)[0];
6332
+ if (scriptName === "sendMessage" && typeof firstVal === "string") {
6333
+ const legacyScript = fn(firstVal);
6334
+ if (legacyScript) return legacyScript;
6335
+ }
6336
+ const script = fn(params);
6337
+ if (script) {
6338
+ const likelyLegacyObjectLeak = typeof script === "string" && script.includes("[object Object]") && typeof firstVal === "string";
6339
+ if (!likelyLegacyObjectLeak) return script;
6340
+ }
6341
+ if (firstVal !== void 0) {
6342
+ const legacyScript = fn(firstVal);
6343
+ if (legacyScript) return legacyScript;
6344
+ }
6345
+ if (script) return script;
6346
+ } else {
6347
+ const script = fn();
6348
+ if (script) return script;
6349
+ }
6248
6350
  }
6249
6351
  }
6250
6352
  return null;
@@ -6300,17 +6402,27 @@ var init_handler = __esm({
6300
6402
  return key.split("_")[0];
6301
6403
  }
6302
6404
  resolveRoute(args) {
6303
- const session = this._ctx.sessionRegistry?.get(args?.targetSessionId);
6304
- const managerKey = this.extractIdeType(args);
6305
- const providerType = args?.agentType || args?.providerType || session?.providerType || this.inferProviderType(managerKey);
6306
- return { session, managerKey, providerType };
6405
+ const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
6406
+ let session = targetSessionId ? this._ctx.sessionRegistry?.get(targetSessionId) : void 0;
6407
+ if (targetSessionId && !session) {
6408
+ reconcileIdeRuntimeSessions(this._ctx.instanceManager, this._ctx.sessionRegistry);
6409
+ session = this._ctx.sessionRegistry?.get(targetSessionId);
6410
+ }
6411
+ const sessionLookupFailed = !!targetSessionId && !session;
6412
+ const managerKey = this.extractIdeType(args, sessionLookupFailed);
6413
+ let providerType;
6414
+ if (!sessionLookupFailed) {
6415
+ providerType = session?.providerType || args?.agentType || args?.providerType || this.inferProviderType(managerKey);
6416
+ }
6417
+ return { session, managerKey, providerType, sessionLookupFailed };
6307
6418
  }
6308
6419
  /** Extract CDP scope key from target session or explicit ideType */
6309
- extractIdeType(args) {
6420
+ extractIdeType(args, sessionLookupFailed = false) {
6310
6421
  if (args?.targetSessionId) {
6311
6422
  const target = this._ctx.sessionRegistry?.get(args.targetSessionId);
6312
6423
  if (target?.cdpManagerKey) return target.cdpManagerKey;
6313
6424
  if (this._ctx.cdpManagers.has(args.targetSessionId)) return args.targetSessionId;
6425
+ if (sessionLookupFailed) return void 0;
6314
6426
  }
6315
6427
  if (args?.ideType) {
6316
6428
  const target = this._ctx.sessionRegistry?.get(args.ideType);
@@ -6357,6 +6469,33 @@ var init_handler = __esm({
6357
6469
  this._currentRoute = this.resolveRoute(args);
6358
6470
  const startedAt = Date.now();
6359
6471
  this.logCommandStart(cmd, args);
6472
+ const sessionScopedCommands = /* @__PURE__ */ new Set([
6473
+ "read_chat",
6474
+ "send_chat",
6475
+ "list_chats",
6476
+ "new_chat",
6477
+ "switch_chat",
6478
+ "set_mode",
6479
+ "change_model",
6480
+ "set_thought_level",
6481
+ "resolve_action",
6482
+ "focus_session",
6483
+ "pty_input",
6484
+ "pty_resize",
6485
+ "invoke_provider_script",
6486
+ "list_extension_models",
6487
+ "set_extension_model",
6488
+ "list_extension_modes",
6489
+ "set_extension_mode"
6490
+ ]);
6491
+ if (this._currentRoute.sessionLookupFailed && sessionScopedCommands.has(cmd)) {
6492
+ const result2 = {
6493
+ success: false,
6494
+ error: `Live session not found for targetSessionId: ${String(args?.targetSessionId || "").trim() || "unknown"}`
6495
+ };
6496
+ this.logCommandEnd(cmd, result2, startedAt);
6497
+ return result2;
6498
+ }
6360
6499
  let result;
6361
6500
  if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
6362
6501
  const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
@@ -9212,6 +9351,7 @@ var init_cli_provider_instance = __esm({
9212
9351
  this.detectStatusTransition();
9213
9352
  });
9214
9353
  await this.adapter.spawn();
9354
+ this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
9215
9355
  if (this.providerSessionId) {
9216
9356
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
9217
9357
  if (restoredHistory.messages.length > 0) {
@@ -9306,6 +9446,7 @@ var init_cli_provider_instance = __esm({
9306
9446
  this.promoteProviderSessionId(parsedProviderSessionId);
9307
9447
  }
9308
9448
  const runtime = this.adapter.getRuntimeMetadata();
9449
+ this.maybeAppendRuntimeRecoveryMessage(runtime);
9309
9450
  const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
9310
9451
  const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
9311
9452
  if (controlValues) {
@@ -9632,6 +9773,28 @@ ${effect.notification.body || ""}`.trim();
9632
9773
  const pad = (value) => String(value).padStart(2, "0");
9633
9774
  return `${date5.getFullYear()}-${pad(date5.getMonth() + 1)}-${pad(date5.getDate())} ${pad(date5.getHours())}:${pad(date5.getMinutes())}:${pad(date5.getSeconds())}`;
9634
9775
  }
9776
+ maybeAppendRuntimeRecoveryMessage(runtime) {
9777
+ if (!runtime?.restoredFromStorage || !runtime.runtimeId) return;
9778
+ const recoveryState = String(runtime.recoveryState || "").trim();
9779
+ if (!recoveryState) return;
9780
+ let content = "";
9781
+ if (recoveryState === "auto_resumed") {
9782
+ content = "Session host restored this CLI after restart and reattached it from a saved snapshot.";
9783
+ } else if (recoveryState === "resume_failed") {
9784
+ const errorSuffix = runtime.recoveryError ? ` Resume failed: ${runtime.recoveryError}` : "";
9785
+ content = `Session host found this CLI after restart, but automatic resume failed.${errorSuffix}`;
9786
+ } else if (recoveryState === "host_restart_interrupted") {
9787
+ content = "Session host found this CLI in interrupted state after restart and is attempting to resume it.";
9788
+ } else if (recoveryState === "orphan_snapshot") {
9789
+ content = "Session host restored the last snapshot for this CLI, but the original runtime was not resumed automatically.";
9790
+ } else {
9791
+ content = `Session host restored this CLI after restart (${recoveryState}).`;
9792
+ }
9793
+ this.appendRuntimeSystemMessage(
9794
+ content,
9795
+ `runtime_recovery:${runtime.runtimeId}:${recoveryState}`
9796
+ );
9797
+ }
9635
9798
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
9636
9799
  const normalizedContent = String(content || "").trim();
9637
9800
  if (!normalizedContent) return;
@@ -31009,17 +31172,17 @@ function parseMessageTime(value) {
31009
31172
  function getSessionMessageUpdatedAt(session) {
31010
31173
  const lastMessage = session.activeChat?.messages?.at?.(-1);
31011
31174
  if (!lastMessage) return 0;
31012
- return parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt) || 0;
31175
+ return parseMessageTime(lastMessage.receivedAt) || 0;
31013
31176
  }
31014
31177
  function getSessionCompletionMarker(session) {
31015
31178
  const lastMessage = session.activeChat?.messages?.at?.(-1);
31016
31179
  if (!lastMessage) return "";
31017
31180
  const role = typeof lastMessage.role === "string" ? lastMessage.role : "";
31018
- if (role === "user" || role === "human") return "";
31181
+ if (role === "user" || role === "human" || role === "system") return "";
31019
31182
  if (typeof lastMessage._turnKey === "string" && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
31020
31183
  if (typeof lastMessage.id === "string" && lastMessage.id) return `id:${lastMessage.id}`;
31021
31184
  if (typeof lastMessage.index === "number" && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
31022
- const timestamp = parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt);
31185
+ const timestamp = parseMessageTime(lastMessage.receivedAt);
31023
31186
  return timestamp > 0 ? `ts:${timestamp}` : "";
31024
31187
  }
31025
31188
  function getSessionLastUsedAt(session) {
@@ -31036,7 +31199,7 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
31036
31199
  if (status === "generating" || status === "starting") {
31037
31200
  return { unread: false, inboxBucket: "working" };
31038
31201
  }
31039
- const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human";
31202
+ const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
31040
31203
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
31041
31204
  }
31042
31205
  function buildRecentLaunches(recentActivity) {
@@ -31334,6 +31497,25 @@ var init_upgrade_helper = __esm({
31334
31497
  });
31335
31498
 
31336
31499
  // ../../oss/packages/daemon-core/src/commands/router.ts
31500
+ function toHostedCliRuntimeDescriptor(record2) {
31501
+ if (!record2 || typeof record2 !== "object") return null;
31502
+ const runtimeId = typeof record2.sessionId === "string" ? record2.sessionId : "";
31503
+ const cliType = typeof record2.providerType === "string" ? record2.providerType : "";
31504
+ const workspace = typeof record2.workspace === "string" ? record2.workspace : "";
31505
+ if (!runtimeId || !cliType || !workspace) return null;
31506
+ return {
31507
+ runtimeId,
31508
+ runtimeKey: typeof record2.runtimeKey === "string" ? record2.runtimeKey : void 0,
31509
+ displayName: typeof record2.displayName === "string" ? record2.displayName : void 0,
31510
+ workspaceLabel: typeof record2.workspaceLabel === "string" ? record2.workspaceLabel : void 0,
31511
+ lifecycle: typeof record2.lifecycle === "string" ? record2.lifecycle : void 0,
31512
+ recoveryState: typeof record2.meta?.runtimeRecoveryState === "string" ? String(record2.meta.runtimeRecoveryState) : null,
31513
+ cliType,
31514
+ workspace,
31515
+ cliArgs: Array.isArray(record2.meta?.cliArgs) ? record2.meta.cliArgs : [],
31516
+ providerSessionId: typeof record2.meta?.providerSessionId === "string" ? String(record2.meta.providerSessionId) : void 0
31517
+ };
31518
+ }
31337
31519
  var fs9, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter;
31338
31520
  var init_router = __esm({
31339
31521
  "../../oss/packages/daemon-core/src/commands/router.ts"() {
@@ -31437,6 +31619,90 @@ var init_router = __esm({
31437
31619
  return { success: false, error: e.message };
31438
31620
  }
31439
31621
  }
31622
+ case "session_host_get_diagnostics": {
31623
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31624
+ const diagnostics = await this.deps.sessionHostControl.getDiagnostics({
31625
+ includeSessions: args?.includeSessions !== false,
31626
+ limit: Number(args?.limit) || void 0
31627
+ });
31628
+ return { success: true, diagnostics };
31629
+ }
31630
+ case "session_host_list_sessions": {
31631
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31632
+ const sessions = await this.deps.sessionHostControl.listSessions();
31633
+ return { success: true, sessions };
31634
+ }
31635
+ case "session_host_stop_session": {
31636
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31637
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
31638
+ if (!sessionId) return { success: false, error: "sessionId required" };
31639
+ const record2 = await this.deps.sessionHostControl.stopSession(sessionId);
31640
+ return { success: true, record: record2 };
31641
+ }
31642
+ case "session_host_resume_session": {
31643
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31644
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
31645
+ if (!sessionId) return { success: false, error: "sessionId required" };
31646
+ const record2 = await this.deps.sessionHostControl.resumeSession(sessionId);
31647
+ const hosted = toHostedCliRuntimeDescriptor(record2);
31648
+ if (hosted) {
31649
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
31650
+ }
31651
+ return { success: true, record: record2 };
31652
+ }
31653
+ case "session_host_restart_session": {
31654
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31655
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
31656
+ if (!sessionId) return { success: false, error: "sessionId required" };
31657
+ const record2 = await this.deps.sessionHostControl.restartSession(sessionId);
31658
+ const hosted = toHostedCliRuntimeDescriptor(record2);
31659
+ if (hosted) {
31660
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
31661
+ }
31662
+ return { success: true, record: record2 };
31663
+ }
31664
+ case "session_host_send_signal": {
31665
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31666
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
31667
+ const signal = typeof args?.signal === "string" ? args.signal : "";
31668
+ if (!sessionId) return { success: false, error: "sessionId required" };
31669
+ if (!signal) return { success: false, error: "signal required" };
31670
+ const record2 = await this.deps.sessionHostControl.sendSignal(sessionId, signal);
31671
+ return { success: true, record: record2 };
31672
+ }
31673
+ case "session_host_force_detach_client": {
31674
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31675
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
31676
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
31677
+ if (!sessionId) return { success: false, error: "sessionId required" };
31678
+ if (!clientId) return { success: false, error: "clientId required" };
31679
+ const record2 = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
31680
+ return { success: true, record: record2 };
31681
+ }
31682
+ case "session_host_acquire_write": {
31683
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31684
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
31685
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
31686
+ const ownerType = args?.ownerType === "agent" ? "agent" : "user";
31687
+ if (!sessionId) return { success: false, error: "sessionId required" };
31688
+ if (!clientId) return { success: false, error: "clientId required" };
31689
+ const record2 = await this.deps.sessionHostControl.acquireWrite({
31690
+ sessionId,
31691
+ clientId,
31692
+ ownerType,
31693
+ force: args?.force !== false
31694
+ });
31695
+ return { success: true, record: record2 };
31696
+ }
31697
+ case "session_host_release_write": {
31698
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31699
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
31700
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
31701
+ if (!sessionId) return { success: false, error: "sessionId required" };
31702
+ if (!clientId) return { success: false, error: "clientId required" };
31703
+ const record2 = await this.deps.sessionHostControl.releaseWrite({ sessionId, clientId });
31704
+ return { success: true, record: record2 };
31705
+ }
31440
31706
  case "list_saved_sessions": {
31441
31707
  const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
31442
31708
  const kind = args?.kind === "acp" ? "acp" : "cli";
@@ -31983,6 +32249,14 @@ var init_provider_adapter = __esm({
31983
32249
  hasScript(name) {
31984
32250
  return typeof this.provider.scripts?.[name] === "function";
31985
32251
  }
32252
+ parseMaybeJson(raw) {
32253
+ if (typeof raw !== "string") return raw;
32254
+ try {
32255
+ return JSON.parse(raw);
32256
+ } catch {
32257
+ return raw;
32258
+ }
32259
+ }
31986
32260
  summarizeRaw(raw) {
31987
32261
  try {
31988
32262
  if (typeof raw === "string") return raw.replace(/\s+/g, " ").trim().slice(0, 240);
@@ -32043,12 +32317,30 @@ var init_provider_adapter = __esm({
32043
32317
  }
32044
32318
  }
32045
32319
  async sendMessage(evaluate, text) {
32046
- const script = this.callScript("sendMessage", text);
32320
+ const params = { message: text };
32321
+ const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
32047
32322
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
32048
32323
  const result = await evaluate(script);
32049
32324
  if (result && typeof result === "string" && result.startsWith("error:")) {
32050
32325
  throw new Error(`[${this.agentName}] sendMessage failed: ${result}`);
32051
32326
  }
32327
+ const parsed = this.parseMaybeJson(result);
32328
+ if (parsed === true) return;
32329
+ if (typeof parsed === "string") {
32330
+ const normalized = parsed.trim().toLowerCase();
32331
+ if (normalized === "ok" || normalized === "sent" || normalized === "success" || normalized === "true") {
32332
+ return;
32333
+ }
32334
+ }
32335
+ if (parsed && typeof parsed === "object") {
32336
+ if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
32337
+ return;
32338
+ }
32339
+ if (typeof parsed.error === "string" && parsed.error.trim()) {
32340
+ throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
32341
+ }
32342
+ }
32343
+ throw new Error(`[${this.agentName}] sendMessage was not confirmed`);
32052
32344
  }
32053
32345
  async resolveAction(evaluate, action, button) {
32054
32346
  const script = this.callScript("resolveAction", { action, button });
@@ -32415,6 +32707,7 @@ var init_poller = __esm({
32415
32707
  "../../oss/packages/daemon-core/src/agent-stream/poller.ts"() {
32416
32708
  "use strict";
32417
32709
  init_setup();
32710
+ init_reconcile();
32418
32711
  init_logger();
32419
32712
  AgentStreamPoller = class {
32420
32713
  deps;
@@ -32454,6 +32747,7 @@ var init_poller = __esm({
32454
32747
  sessionRegistry
32455
32748
  } = this.deps;
32456
32749
  if (!agentStreamManager || cdpManagers.size === 0) return;
32750
+ reconcileIdeRuntimeSessions(instanceManager, sessionRegistry);
32457
32751
  for (const [ideType, cdp] of cdpManagers) {
32458
32752
  registerExtensionProviders(providerLoader, cdp, ideType);
32459
32753
  const ideInstance = instanceManager.getInstance(`ide:${ideType}`);
@@ -38381,6 +38675,7 @@ var init_session_host_transport = __esm({
38381
38675
  }
38382
38676
  }
38383
38677
  handleEvent(event) {
38678
+ if (!("sessionId" in event)) return;
38384
38679
  if (event.sessionId !== this.options.runtimeId) return;
38385
38680
  if ((event.type === "session_started" || event.type === "session_resumed") && typeof event.pid === "number") {
38386
38681
  this.currentPid = event.pid;
@@ -38456,7 +38751,10 @@ var init_session_host_transport = __esm({
38456
38751
  clientId: client.clientId,
38457
38752
  type: client.type,
38458
38753
  readOnly: client.readOnly
38459
- }))
38754
+ })),
38755
+ restoredFromStorage: record2.meta?.restoredFromStorage === true,
38756
+ recoveryState: typeof record2.meta?.runtimeRecoveryState === "string" ? String(record2.meta.runtimeRecoveryState) : null,
38757
+ recoveryError: typeof record2.meta?.runtimeRecoveryError === "string" ? String(record2.meta.runtimeRecoveryError) : null
38460
38758
  };
38461
38759
  }
38462
38760
  enqueue(action) {
@@ -38918,6 +39216,7 @@ async function initDaemonComponents(config2) {
38918
39216
  onIdeConnected: () => poller?.start(),
38919
39217
  onStatusChange: config2.onStatusChange,
38920
39218
  onPostChatCommand: config2.onPostChatCommand,
39219
+ sessionHostControl: config2.sessionHostControl,
38921
39220
  getCdpLogFn: config2.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
38922
39221
  });
38923
39222
  poller = new AgentStreamPoller({
@@ -47167,6 +47466,151 @@ var init_session_host = __esm({
47167
47466
  }
47168
47467
  });
47169
47468
 
47469
+ // src/session-host-controller.ts
47470
+ var SessionHostController;
47471
+ var init_session_host_controller = __esm({
47472
+ "src/session-host-controller.ts"() {
47473
+ "use strict";
47474
+ init_src();
47475
+ init_dist();
47476
+ SessionHostController = class {
47477
+ constructor(endpoint, onEvent) {
47478
+ this.onEvent = onEvent;
47479
+ this.client = new SessionHostClient({ endpoint });
47480
+ }
47481
+ client;
47482
+ reconnectTimer = null;
47483
+ unsubscribe = null;
47484
+ started = false;
47485
+ async start() {
47486
+ if (this.started) return;
47487
+ this.started = true;
47488
+ this.unsubscribe = this.client.onEvent((event) => this.handleEvent(event));
47489
+ await this.ensureConnected();
47490
+ this.reconnectTimer = setInterval(() => {
47491
+ void this.ensureConnected();
47492
+ }, 2e3);
47493
+ }
47494
+ async stop() {
47495
+ this.started = false;
47496
+ if (this.reconnectTimer) {
47497
+ clearInterval(this.reconnectTimer);
47498
+ this.reconnectTimer = null;
47499
+ }
47500
+ try {
47501
+ this.unsubscribe?.();
47502
+ this.unsubscribe = null;
47503
+ } catch {
47504
+ }
47505
+ await this.client.close().catch(() => {
47506
+ });
47507
+ }
47508
+ async getDiagnostics(payload = {}) {
47509
+ return this.request({
47510
+ type: "get_host_diagnostics",
47511
+ payload
47512
+ });
47513
+ }
47514
+ async listSessions() {
47515
+ return this.request({
47516
+ type: "list_sessions",
47517
+ payload: {}
47518
+ });
47519
+ }
47520
+ async stopSession(sessionId) {
47521
+ return this.request({
47522
+ type: "stop_session",
47523
+ payload: { sessionId }
47524
+ });
47525
+ }
47526
+ async resumeSession(sessionId) {
47527
+ return this.request({
47528
+ type: "resume_session",
47529
+ payload: { sessionId }
47530
+ });
47531
+ }
47532
+ async restartSession(sessionId) {
47533
+ return this.request({
47534
+ type: "restart_session",
47535
+ payload: { sessionId }
47536
+ });
47537
+ }
47538
+ async sendSignal(sessionId, signal) {
47539
+ return this.request({
47540
+ type: "send_signal",
47541
+ payload: { sessionId, signal }
47542
+ });
47543
+ }
47544
+ async forceDetachClient(sessionId, clientId) {
47545
+ return this.request({
47546
+ type: "force_detach_client",
47547
+ payload: { sessionId, clientId }
47548
+ });
47549
+ }
47550
+ async acquireWrite(payload) {
47551
+ return this.request({
47552
+ type: "acquire_write",
47553
+ payload
47554
+ });
47555
+ }
47556
+ async releaseWrite(payload) {
47557
+ return this.request({
47558
+ type: "release_write",
47559
+ payload
47560
+ });
47561
+ }
47562
+ async request(request) {
47563
+ await this.ensureConnected();
47564
+ const response = await this.client.request(request);
47565
+ if (!response.success) {
47566
+ throw new Error(response.error || `Session host request failed: ${request.type}`);
47567
+ }
47568
+ return response.result ?? null;
47569
+ }
47570
+ async ensureConnected() {
47571
+ try {
47572
+ await this.client.connect();
47573
+ } catch (error48) {
47574
+ if (!this.started) return;
47575
+ LOG.debug("SessionHost", `connect failed: ${error48?.message || error48}`);
47576
+ }
47577
+ }
47578
+ handleEvent(event) {
47579
+ if (event.type === "host_log") {
47580
+ const line = event.entry.sessionId ? `${event.entry.message} (session=${event.entry.sessionId})` : event.entry.message;
47581
+ switch (event.entry.level) {
47582
+ case "debug":
47583
+ LOG.debug("SessionHost", line);
47584
+ break;
47585
+ case "warn":
47586
+ LOG.warn("SessionHost", line);
47587
+ break;
47588
+ case "error":
47589
+ LOG.error("SessionHost", line);
47590
+ break;
47591
+ default:
47592
+ LOG.info("SessionHost", line);
47593
+ break;
47594
+ }
47595
+ } else if (event.type === "request_trace") {
47596
+ const line = `${event.trace.type} ${event.trace.success ? "ok" : "failed"} ${event.trace.durationMs}ms` + (event.trace.sessionId ? ` session=${event.trace.sessionId}` : "") + (event.trace.error ? ` error=${event.trace.error}` : "");
47597
+ if (event.trace.success) LOG.debug("SessionHost", line);
47598
+ else LOG.warn("SessionHost", line);
47599
+ } else if (event.type === "runtime_transition") {
47600
+ const line = `${event.transition.action} ${event.transition.success === false ? "failed" : "ok"}` + (event.transition.lifecycle ? ` lifecycle=${event.transition.lifecycle}` : "") + (event.transition.detail ? ` detail=${event.transition.detail}` : "") + (event.transition.error ? ` error=${event.transition.error}` : "");
47601
+ if (event.transition.success === false) LOG.warn("SessionHost", `[${event.transition.sessionId}] ${line}`);
47602
+ else LOG.info("SessionHost", `[${event.transition.sessionId}] ${line}`);
47603
+ }
47604
+ try {
47605
+ this.onEvent?.(event);
47606
+ } catch (error48) {
47607
+ LOG.warn("SessionHost", `event callback failed: ${error48?.message || error48}`);
47608
+ }
47609
+ }
47610
+ };
47611
+ }
47612
+ });
47613
+
47170
47614
  // src/version.ts
47171
47615
  function resolvePackageVersion(options) {
47172
47616
  const injectedVersion = options?.injectedVersion || "unknown";
@@ -47261,6 +47705,7 @@ var init_adhdev_daemon = __esm({
47261
47705
  init_screenshot_controller();
47262
47706
  init_session_host();
47263
47707
  init_dist();
47708
+ init_session_host_controller();
47264
47709
  os21 = __toESM(require("os"));
47265
47710
  fs17 = __toESM(require("fs"));
47266
47711
  path21 = __toESM(require("path"));
@@ -47268,7 +47713,7 @@ var init_adhdev_daemon = __esm({
47268
47713
  import_ws3 = require("ws");
47269
47714
  import_chalk2 = __toESM(require("chalk"));
47270
47715
  init_version();
47271
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.24" });
47716
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.27" });
47272
47717
  DANGEROUS_PATTERNS = [
47273
47718
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
47274
47719
  /\bsudo\b/i,
@@ -47291,6 +47736,7 @@ var init_adhdev_daemon = __esm({
47291
47736
  statusReporter = null;
47292
47737
  components = null;
47293
47738
  sessionHostEndpoint = null;
47739
+ sessionHostController = null;
47294
47740
  running = false;
47295
47741
  localPort;
47296
47742
  ideType = "unknown";
@@ -47341,6 +47787,11 @@ ${err?.stack || ""}`);
47341
47787
  }
47342
47788
  const sessionHostEndpoint = await ensureSessionHostReady2();
47343
47789
  this.sessionHostEndpoint = sessionHostEndpoint;
47790
+ this.sessionHostController = new SessionHostController(
47791
+ sessionHostEndpoint,
47792
+ (event) => this.broadcastLocalIpcMessage("daemon:session_host_event", event)
47793
+ );
47794
+ await this.sessionHostController.start();
47344
47795
  this.components = await initDaemonComponents({
47345
47796
  providerLogFn: LOG.forComponent("Provider").asLogFn(),
47346
47797
  cliManagerDeps: {
@@ -47378,6 +47829,7 @@ ${err?.stack || ""}`);
47378
47829
  setTimeout(() => this.statusReporter?.throttledReport(), 1e3);
47379
47830
  setTimeout(() => this.statusReporter?.throttledReport(), 3e3);
47380
47831
  },
47832
+ sessionHostControl: this.sessionHostController,
47381
47833
  getCdpLogFn: (ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn(),
47382
47834
  onCdpManagerSetup: (ideType) => {
47383
47835
  if (this.ideType === "unknown") this.ideType = ideType;
@@ -47734,13 +48186,24 @@ ${err?.stack || ""}`);
47734
48186
  serverConnected: this.serverConn?.isConnected() ?? false,
47735
48187
  cdpConnected: (this.components?.cdpManagers.size || 0) > 0,
47736
48188
  localPort: this.localPort,
47737
- cliAgents
48189
+ cliAgents,
48190
+ sessionHostConnected: !!this.sessionHostController
47738
48191
  }
47739
48192
  }));
47740
48193
  } catch (error48) {
47741
48194
  LOG.warn("IPC", `Failed to send welcome: ${error48?.message || error48}`);
47742
48195
  }
47743
48196
  }
48197
+ broadcastLocalIpcMessage(type, payload) {
48198
+ const message = JSON.stringify({ type, payload });
48199
+ for (const client of this.localClients) {
48200
+ if (client.readyState !== import_ws3.WebSocket.OPEN) continue;
48201
+ try {
48202
+ client.send(message);
48203
+ } catch {
48204
+ }
48205
+ }
48206
+ }
47744
48207
  async handleLocalIpcMessage(ws, raw) {
47745
48208
  let msg;
47746
48209
  try {
@@ -47831,6 +48294,11 @@ ${err?.stack || ""}`);
47831
48294
  this.serverConn?.disconnect();
47832
48295
  } catch {
47833
48296
  }
48297
+ try {
48298
+ await this.sessionHostController?.stop();
48299
+ this.sessionHostController = null;
48300
+ } catch {
48301
+ }
47834
48302
  try {
47835
48303
  for (const client of this.localClients) {
47836
48304
  client.close();