adhdev 0.8.25 → 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) {
@@ -4820,7 +4852,7 @@ async function handleSendChat(h, args) {
4820
4852
  if (isExtensionTransport(transport)) {
4821
4853
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
4822
4854
  try {
4823
- const evalResult = await h.evaluateProviderScript("sendMessage", { MESSAGE: text }, 3e4);
4855
+ const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
4824
4856
  if (evalResult?.result) {
4825
4857
  const parsed = parseMaybeJson(evalResult.result);
4826
4858
  if (didProviderConfirmSend(parsed)) {
@@ -4851,7 +4883,7 @@ async function handleSendChat(h, args) {
4851
4883
  return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
4852
4884
  }
4853
4885
  _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
4854
- const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
4886
+ const sendScript = h.getProviderScript("sendMessage", { message: text });
4855
4887
  if (sendScript) {
4856
4888
  try {
4857
4889
  const result = await targetCdp.evaluate(sendScript, 3e4);
@@ -5859,10 +5891,10 @@ function getCliScriptCommand(payload) {
5859
5891
  }
5860
5892
  const command = payload.command;
5861
5893
  if (!command || typeof command !== "object") return null;
5862
- if (command.type !== "send_message") return null;
5894
+ if (command.type !== "send_message" && command.type !== "pty_write") return null;
5863
5895
  const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
5864
5896
  if (!text) return null;
5865
- return { type: "send_message", text };
5897
+ return { type: command.type, text };
5866
5898
  }
5867
5899
  function applyProviderPatch(h, args, payload) {
5868
5900
  if (!payload || typeof payload !== "object") return;
@@ -5903,6 +5935,8 @@ async function executeProviderScript(h, args, scriptName) {
5903
5935
  const cliCommand = getCliScriptCommand(parsed.payload);
5904
5936
  if (cliCommand?.type === "send_message" && cliCommand.text) {
5905
5937
  await adapter.sendMessage(cliCommand.text);
5938
+ } else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
5939
+ adapter.writeRaw(cliCommand.text + "\r");
5906
5940
  }
5907
5941
  applyProviderPatch(h, args, parsed.payload);
5908
5942
  return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
@@ -6293,9 +6327,26 @@ var init_handler = __esm({
6293
6327
  if (provider?.scripts) {
6294
6328
  const fn = provider.scripts[scriptName];
6295
6329
  if (typeof fn === "function") {
6296
- const firstVal = params ? Object.values(params)[0] : void 0;
6297
- const script = firstVal ? fn(firstVal) : fn();
6298
- 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
+ }
6299
6350
  }
6300
6351
  }
6301
6352
  return null;
@@ -9300,6 +9351,7 @@ var init_cli_provider_instance = __esm({
9300
9351
  this.detectStatusTransition();
9301
9352
  });
9302
9353
  await this.adapter.spawn();
9354
+ this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
9303
9355
  if (this.providerSessionId) {
9304
9356
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
9305
9357
  if (restoredHistory.messages.length > 0) {
@@ -9394,6 +9446,7 @@ var init_cli_provider_instance = __esm({
9394
9446
  this.promoteProviderSessionId(parsedProviderSessionId);
9395
9447
  }
9396
9448
  const runtime = this.adapter.getRuntimeMetadata();
9449
+ this.maybeAppendRuntimeRecoveryMessage(runtime);
9397
9450
  const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
9398
9451
  const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
9399
9452
  if (controlValues) {
@@ -9720,6 +9773,28 @@ ${effect.notification.body || ""}`.trim();
9720
9773
  const pad = (value) => String(value).padStart(2, "0");
9721
9774
  return `${date5.getFullYear()}-${pad(date5.getMonth() + 1)}-${pad(date5.getDate())} ${pad(date5.getHours())}:${pad(date5.getMinutes())}:${pad(date5.getSeconds())}`;
9722
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
+ }
9723
9798
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
9724
9799
  const normalizedContent = String(content || "").trim();
9725
9800
  if (!normalizedContent) return;
@@ -31097,17 +31172,17 @@ function parseMessageTime(value) {
31097
31172
  function getSessionMessageUpdatedAt(session) {
31098
31173
  const lastMessage = session.activeChat?.messages?.at?.(-1);
31099
31174
  if (!lastMessage) return 0;
31100
- return parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt) || 0;
31175
+ return parseMessageTime(lastMessage.receivedAt) || 0;
31101
31176
  }
31102
31177
  function getSessionCompletionMarker(session) {
31103
31178
  const lastMessage = session.activeChat?.messages?.at?.(-1);
31104
31179
  if (!lastMessage) return "";
31105
31180
  const role = typeof lastMessage.role === "string" ? lastMessage.role : "";
31106
- if (role === "user" || role === "human") return "";
31181
+ if (role === "user" || role === "human" || role === "system") return "";
31107
31182
  if (typeof lastMessage._turnKey === "string" && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
31108
31183
  if (typeof lastMessage.id === "string" && lastMessage.id) return `id:${lastMessage.id}`;
31109
31184
  if (typeof lastMessage.index === "number" && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
31110
- const timestamp = parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt);
31185
+ const timestamp = parseMessageTime(lastMessage.receivedAt);
31111
31186
  return timestamp > 0 ? `ts:${timestamp}` : "";
31112
31187
  }
31113
31188
  function getSessionLastUsedAt(session) {
@@ -31124,7 +31199,7 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
31124
31199
  if (status === "generating" || status === "starting") {
31125
31200
  return { unread: false, inboxBucket: "working" };
31126
31201
  }
31127
- 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";
31128
31203
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
31129
31204
  }
31130
31205
  function buildRecentLaunches(recentActivity) {
@@ -31422,6 +31497,25 @@ var init_upgrade_helper = __esm({
31422
31497
  });
31423
31498
 
31424
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
+ }
31425
31519
  var fs9, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter;
31426
31520
  var init_router = __esm({
31427
31521
  "../../oss/packages/daemon-core/src/commands/router.ts"() {
@@ -31525,6 +31619,90 @@ var init_router = __esm({
31525
31619
  return { success: false, error: e.message };
31526
31620
  }
31527
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
+ }
31528
31706
  case "list_saved_sessions": {
31529
31707
  const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
31530
31708
  const kind = args?.kind === "acp" ? "acp" : "cli";
@@ -32071,6 +32249,14 @@ var init_provider_adapter = __esm({
32071
32249
  hasScript(name) {
32072
32250
  return typeof this.provider.scripts?.[name] === "function";
32073
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
+ }
32074
32260
  summarizeRaw(raw) {
32075
32261
  try {
32076
32262
  if (typeof raw === "string") return raw.replace(/\s+/g, " ").trim().slice(0, 240);
@@ -32131,12 +32317,30 @@ var init_provider_adapter = __esm({
32131
32317
  }
32132
32318
  }
32133
32319
  async sendMessage(evaluate, text) {
32134
- const script = this.callScript("sendMessage", text);
32320
+ const params = { message: text };
32321
+ const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
32135
32322
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
32136
32323
  const result = await evaluate(script);
32137
32324
  if (result && typeof result === "string" && result.startsWith("error:")) {
32138
32325
  throw new Error(`[${this.agentName}] sendMessage failed: ${result}`);
32139
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`);
32140
32344
  }
32141
32345
  async resolveAction(evaluate, action, button) {
32142
32346
  const script = this.callScript("resolveAction", { action, button });
@@ -38471,6 +38675,7 @@ var init_session_host_transport = __esm({
38471
38675
  }
38472
38676
  }
38473
38677
  handleEvent(event) {
38678
+ if (!("sessionId" in event)) return;
38474
38679
  if (event.sessionId !== this.options.runtimeId) return;
38475
38680
  if ((event.type === "session_started" || event.type === "session_resumed") && typeof event.pid === "number") {
38476
38681
  this.currentPid = event.pid;
@@ -38546,7 +38751,10 @@ var init_session_host_transport = __esm({
38546
38751
  clientId: client.clientId,
38547
38752
  type: client.type,
38548
38753
  readOnly: client.readOnly
38549
- }))
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
38550
38758
  };
38551
38759
  }
38552
38760
  enqueue(action) {
@@ -39008,6 +39216,7 @@ async function initDaemonComponents(config2) {
39008
39216
  onIdeConnected: () => poller?.start(),
39009
39217
  onStatusChange: config2.onStatusChange,
39010
39218
  onPostChatCommand: config2.onPostChatCommand,
39219
+ sessionHostControl: config2.sessionHostControl,
39011
39220
  getCdpLogFn: config2.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
39012
39221
  });
39013
39222
  poller = new AgentStreamPoller({
@@ -47257,6 +47466,151 @@ var init_session_host = __esm({
47257
47466
  }
47258
47467
  });
47259
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
+
47260
47614
  // src/version.ts
47261
47615
  function resolvePackageVersion(options) {
47262
47616
  const injectedVersion = options?.injectedVersion || "unknown";
@@ -47351,6 +47705,7 @@ var init_adhdev_daemon = __esm({
47351
47705
  init_screenshot_controller();
47352
47706
  init_session_host();
47353
47707
  init_dist();
47708
+ init_session_host_controller();
47354
47709
  os21 = __toESM(require("os"));
47355
47710
  fs17 = __toESM(require("fs"));
47356
47711
  path21 = __toESM(require("path"));
@@ -47358,7 +47713,7 @@ var init_adhdev_daemon = __esm({
47358
47713
  import_ws3 = require("ws");
47359
47714
  import_chalk2 = __toESM(require("chalk"));
47360
47715
  init_version();
47361
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.25" });
47716
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.27" });
47362
47717
  DANGEROUS_PATTERNS = [
47363
47718
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
47364
47719
  /\bsudo\b/i,
@@ -47381,6 +47736,7 @@ var init_adhdev_daemon = __esm({
47381
47736
  statusReporter = null;
47382
47737
  components = null;
47383
47738
  sessionHostEndpoint = null;
47739
+ sessionHostController = null;
47384
47740
  running = false;
47385
47741
  localPort;
47386
47742
  ideType = "unknown";
@@ -47431,6 +47787,11 @@ ${err?.stack || ""}`);
47431
47787
  }
47432
47788
  const sessionHostEndpoint = await ensureSessionHostReady2();
47433
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();
47434
47795
  this.components = await initDaemonComponents({
47435
47796
  providerLogFn: LOG.forComponent("Provider").asLogFn(),
47436
47797
  cliManagerDeps: {
@@ -47468,6 +47829,7 @@ ${err?.stack || ""}`);
47468
47829
  setTimeout(() => this.statusReporter?.throttledReport(), 1e3);
47469
47830
  setTimeout(() => this.statusReporter?.throttledReport(), 3e3);
47470
47831
  },
47832
+ sessionHostControl: this.sessionHostController,
47471
47833
  getCdpLogFn: (ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn(),
47472
47834
  onCdpManagerSetup: (ideType) => {
47473
47835
  if (this.ideType === "unknown") this.ideType = ideType;
@@ -47824,13 +48186,24 @@ ${err?.stack || ""}`);
47824
48186
  serverConnected: this.serverConn?.isConnected() ?? false,
47825
48187
  cdpConnected: (this.components?.cdpManagers.size || 0) > 0,
47826
48188
  localPort: this.localPort,
47827
- cliAgents
48189
+ cliAgents,
48190
+ sessionHostConnected: !!this.sessionHostController
47828
48191
  }
47829
48192
  }));
47830
48193
  } catch (error48) {
47831
48194
  LOG.warn("IPC", `Failed to send welcome: ${error48?.message || error48}`);
47832
48195
  }
47833
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
+ }
47834
48207
  async handleLocalIpcMessage(ws, raw) {
47835
48208
  let msg;
47836
48209
  try {
@@ -47921,6 +48294,11 @@ ${err?.stack || ""}`);
47921
48294
  this.serverConn?.disconnect();
47922
48295
  } catch {
47923
48296
  }
48297
+ try {
48298
+ await this.sessionHostController?.stop();
48299
+ this.sessionHostController = null;
48300
+ } catch {
48301
+ }
47924
48302
  try {
47925
48303
  for (const client of this.localClients) {
47926
48304
  client.close();