aisnitch 0.2.17 → 0.2.19

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.
@@ -41,7 +41,7 @@ var import_commander = require("commander");
41
41
 
42
42
  // src/package-info.ts
43
43
  var AISNITCH_PACKAGE_NAME = "aisnitch";
44
- var AISNITCH_VERSION = "0.2.17";
44
+ var AISNITCH_VERSION = "0.2.19";
45
45
  var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
46
46
 
47
47
  // src/core/events/schema.ts
@@ -78,6 +78,10 @@ var TOOL_NAMES = [
78
78
  "openclaw",
79
79
  "openhands",
80
80
  "kilo",
81
+ "devin",
82
+ "kiro",
83
+ "augment-code",
84
+ "mistral",
81
85
  "unknown"
82
86
  ];
83
87
  var ERROR_TYPES = [
@@ -2459,13 +2463,13 @@ function toConfigPathOptions(options) {
2459
2463
  }
2460
2464
 
2461
2465
  // src/cli/runtime.ts
2462
- var import_node_child_process12 = require("child_process");
2466
+ var import_node_child_process15 = require("child_process");
2463
2467
  var import_node_fs4 = require("fs");
2464
- var import_promises15 = require("fs/promises");
2468
+ var import_promises18 = require("fs/promises");
2465
2469
  var import_node_net4 = require("net");
2466
2470
  var import_node_os5 = require("os");
2467
- var import_node_path16 = require("path");
2468
- var import_node_util11 = require("util");
2471
+ var import_node_path19 = require("path");
2472
+ var import_node_util14 = require("util");
2469
2473
 
2470
2474
  // src/adapters/generic-pty.ts
2471
2475
  var import_node_path5 = require("path");
@@ -2518,23 +2522,27 @@ var PROCESS_NAME_MAP = {
2518
2522
  "tmux: server": "tmux"
2519
2523
  };
2520
2524
  var TOOL_BINARY_MAP = {
2521
- aider: "aider",
2522
- amp: "amp",
2523
- cline: "cline",
2525
+ "aider": "aider",
2526
+ "amp": "amp",
2527
+ "augment-code": "auggie",
2524
2528
  "claude-code": "claude",
2529
+ "cline": "cline",
2530
+ "codex": "codex",
2531
+ "continue": "continue",
2525
2532
  "copilot-cli": "copilot",
2526
- codex: "codex",
2527
- continue: "continue",
2528
- cursor: "cursor",
2533
+ "cursor": "cursor",
2534
+ "devin": "devin",
2529
2535
  "gemini-cli": "gemini",
2530
- goose: "goose",
2531
- kilo: "kilo",
2532
- openclaw: "openclaw",
2533
- opencode: "opencode",
2534
- openhands: "openhands",
2536
+ "goose": "goose",
2537
+ "kilo": "kilo",
2538
+ "kiro": "kiro",
2539
+ "mistral": "mistral",
2540
+ "openhands": "openhands",
2541
+ "openclaw": "openclaw",
2542
+ "opencode": "opencode",
2535
2543
  "qwen-code": "qwen",
2536
- unknown: "unknown",
2537
- windsurf: "windsurf"
2544
+ "unknown": "unknown",
2545
+ "windsurf": "windsurf"
2538
2546
  };
2539
2547
  var ContextDetector = class {
2540
2548
  cache = /* @__PURE__ */ new Map();
@@ -4053,7 +4061,7 @@ var UDSServer = class {
4053
4061
  };
4054
4062
 
4055
4063
  // src/core/engine/pipeline.ts
4056
- var import_node_path13 = require("path");
4064
+ var import_node_path16 = require("path");
4057
4065
  var import_zod4 = require("zod");
4058
4066
 
4059
4067
  // src/adapters/aider.ts
@@ -6697,41 +6705,49 @@ function getString3(payload, key) {
6697
6705
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
6698
6706
  }
6699
6707
 
6700
- // src/adapters/gemini-cli.ts
6708
+ // src/adapters/cursor.ts
6701
6709
  var import_node_child_process7 = require("child_process");
6702
6710
  var import_promises9 = require("fs/promises");
6703
6711
  var import_node_path10 = require("path");
6704
6712
  var import_node_util7 = require("util");
6705
6713
  var import_chokidar5 = require("chokidar");
6706
6714
  var execFile7 = (0, import_node_util7.promisify)(import_node_child_process7.execFile);
6707
- var GEMINI_CODING_TOOLS = /* @__PURE__ */ new Set([
6708
- "edit",
6709
- "replace",
6710
- "write",
6711
- "write_file"
6715
+ var CURSOR_CODING_TOOLS = /* @__PURE__ */ new Set([
6716
+ "Edit",
6717
+ "Write",
6718
+ "MultiEdit",
6719
+ "NotebookEdit",
6720
+ "CreateFile",
6721
+ "DeleteFile",
6722
+ "MoveFile"
6712
6723
  ]);
6713
- var GeminiCLIAdapter = class extends BaseAdapter {
6714
- displayName = "Gemini CLI";
6715
- name = "gemini-cli";
6724
+ var CursorAdapter = class extends BaseAdapter {
6725
+ displayName = "Cursor CLI";
6726
+ name = "cursor";
6716
6727
  strategies = [
6717
- "hooks",
6718
- "log-watch",
6719
- "process-detect"
6728
+ "process-detect",
6729
+ "jsonl-watch"
6720
6730
  ];
6721
6731
  fallbackProcessSessionId = null;
6722
- logsDirectory;
6723
- observedLogMessageIds = /* @__PURE__ */ new Map();
6724
- projectRootCache = /* @__PURE__ */ new Map();
6732
+ logDirectory;
6725
6733
  pollIntervalMs;
6726
6734
  processPoller = null;
6727
6735
  processListCommand;
6736
+ transcriptOffsets = /* @__PURE__ */ new Map();
6737
+ transcriptRemainders = /* @__PURE__ */ new Map();
6728
6738
  watcher = null;
6729
6739
  watcherFactory;
6730
6740
  constructor(options) {
6731
6741
  super(options);
6732
- this.logsDirectory = options.logsDirectory ?? (0, import_node_path10.join)(this.getUserHomeDirectory(), ".gemini", "tmp");
6733
6742
  this.pollIntervalMs = options.pollIntervalMs ?? 5e3;
6734
- this.processListCommand = options.processListCommand ?? (async () => await execFile7("pgrep", ["-lf", "gemini"]).then((result) => result.stdout));
6743
+ this.processListCommand = options.processListCommand ?? (async () => {
6744
+ const results = await Promise.all([
6745
+ execFile7("pgrep", ["-lf", "cursor-agent"]).catch(() => ({ stdout: "" })),
6746
+ execFile7("pgrep", ["-lf", "cursor"]).catch(() => ({ stdout: "" }))
6747
+ ]);
6748
+ return results.map((r) => r.stdout).join("\n");
6749
+ });
6750
+ this.logDirectory = options.logDirectory ?? (0, import_node_path10.join)(this.getUserHomeDirectory(), "Library", "Application Support", "Cursor");
6735
6751
  this.watcherFactory = options.watcherFactory ?? import_chokidar5.watch;
6736
6752
  }
6737
6753
  async start() {
@@ -6739,22 +6755,19 @@ var GeminiCLIAdapter = class extends BaseAdapter {
6739
6755
  return;
6740
6756
  }
6741
6757
  this.setRunning(true);
6742
- await this.seedObservedLogState();
6743
- const logsGlob = (0, import_node_path10.join)(this.logsDirectory, "**", "logs.json");
6744
- this.watcher = this.watcherFactory(logsGlob, {
6745
- awaitWriteFinish: {
6746
- stabilityThreshold: 200
6747
- },
6758
+ await this.seedTranscriptOffsets();
6759
+ const logPatterns = [
6760
+ (0, import_node_path10.join)(this.logDirectory, "logs", "**", "*.jsonl"),
6761
+ (0, import_node_path10.join)(this.logDirectory, "agent", "**", "*.jsonl")
6762
+ ];
6763
+ this.watcher = this.watcherFactory(logPatterns[0], {
6764
+ awaitWriteFinish: { stabilityThreshold: 200 },
6748
6765
  ignoreInitial: true
6749
6766
  });
6750
- this.watcher.on("add", (filePath) => {
6751
- void this.processLogsFile(filePath);
6752
- });
6753
- this.watcher.on("change", (filePath) => {
6754
- void this.processLogsFile(filePath);
6755
- });
6767
+ this.watcher.on("add", (filePath) => void this.processTranscriptUpdate(filePath, true));
6768
+ this.watcher.on("change", (filePath) => void this.processTranscriptUpdate(filePath, false));
6756
6769
  this.watcher.on("error", (error) => {
6757
- logger.warn({ error }, "Gemini logs watcher error");
6770
+ logger.warn({ error }, "Cursor log watcher error");
6758
6771
  });
6759
6772
  this.startProcessPolling();
6760
6773
  }
@@ -6768,8 +6781,8 @@ var GeminiCLIAdapter = class extends BaseAdapter {
6768
6781
  this.processPoller = null;
6769
6782
  }
6770
6783
  this.fallbackProcessSessionId = null;
6771
- this.observedLogMessageIds.clear();
6772
- this.projectRootCache.clear();
6784
+ this.transcriptOffsets.clear();
6785
+ this.transcriptRemainders.clear();
6773
6786
  this.setRunning(false);
6774
6787
  }
6775
6788
  async handleHook(payload) {
@@ -6781,6 +6794,7 @@ var GeminiCLIAdapter = class extends BaseAdapter {
6781
6794
  activeFile: normalizedPayload.data?.activeFile,
6782
6795
  cwd: normalizedPayload.data?.cwd ?? normalizedPayload.cwd,
6783
6796
  pid: normalizedPayload.pid,
6797
+ project: normalizedPayload.data?.project,
6784
6798
  projectPath: normalizedPayload.data?.projectPath,
6785
6799
  sessionId: normalizedPayload.sessionId,
6786
6800
  tool: this.name,
@@ -6790,253 +6804,263 @@ var GeminiCLIAdapter = class extends BaseAdapter {
6790
6804
  return;
6791
6805
  }
6792
6806
  if (!isRecord5(payload)) {
6793
- logger.warn({ payload }, "Gemini hook payload must be an object");
6807
+ logger.warn({ payload }, "Cursor hook payload must be an object");
6794
6808
  return;
6795
6809
  }
6796
- const hookEventName = getString4(payload, "hook_event_name") ?? getString4(payload, "hookEventName");
6797
- if (!hookEventName) {
6798
- logger.warn({ payload }, "Gemini hook payload is missing its event name");
6799
- return;
6800
- }
6801
- const cwd = getString4(payload, "cwd");
6802
- const transcriptPath = getString4(payload, "transcript_path") ?? getString4(payload, "transcriptPath");
6803
6810
  const sessionId = resolveSessionId({
6804
- activeFile: extractGeminiActiveFile(payload),
6805
- cwd,
6806
- projectPath: cwd,
6807
- sessionId: getString4(payload, "session_id") ?? getString4(payload, "sessionId"),
6808
- tool: this.name,
6809
- transcriptPath
6811
+ cwd: getString4(payload, "cwd"),
6812
+ pid: getNumber2(payload, "pid"),
6813
+ projectPath: getString4(payload, "projectPath"),
6814
+ sessionId: getString4(payload, "sessionId") ?? getString4(payload, "session_id"),
6815
+ tool: this.name
6810
6816
  });
6811
6817
  const context = {
6812
- cwd,
6818
+ cwd: getString4(payload, "cwd"),
6819
+ env: this.env ?? process.env,
6813
6820
  hookPayload: payload,
6814
6821
  pid: getNumber2(payload, "pid"),
6815
6822
  sessionId,
6816
- source: "aisnitch://adapters/gemini-cli",
6817
- transcriptPath
6823
+ source: "aisnitch://adapters/cursor"
6818
6824
  };
6825
+ const eventType = getString4(payload, "event") ?? getString4(payload, "type");
6826
+ const toolName = getString4(payload, "tool") ?? getString4(payload, "toolName");
6827
+ const toolInput = extractToolInput(payload);
6819
6828
  const sharedData = {
6820
- activeFile: extractGeminiActiveFile(payload),
6821
- cwd,
6822
- errorMessage: extractGeminiErrorMessage(payload),
6823
- errorType: extractGeminiErrorType(payload),
6824
- model: extractGeminiModel(payload),
6825
- projectPath: cwd,
6829
+ activeFile: toolInput?.filePath,
6830
+ cwd: context.cwd,
6831
+ model: getString4(payload, "model"),
6832
+ projectPath: getString4(payload, "projectPath"),
6826
6833
  raw: payload,
6827
- tokensUsed: extractGeminiTokens(payload),
6828
- toolInput: extractGeminiToolInput(payload),
6829
- toolName: extractGeminiToolName(payload)
6834
+ toolInput,
6835
+ toolName
6830
6836
  };
6831
- switch (hookEventName) {
6837
+ switch (eventType) {
6838
+ case "session_start":
6832
6839
  case "SessionStart": {
6833
- this.fallbackProcessSessionId = null;
6834
6840
  await this.emitStateChange("session.start", sharedData, context);
6835
6841
  await this.emitStateChange("agent.idle", sharedData, context);
6836
6842
  return;
6837
6843
  }
6844
+ case "session_end":
6838
6845
  case "SessionEnd": {
6839
6846
  await this.emitStateChange("session.end", sharedData, context);
6840
6847
  return;
6841
6848
  }
6842
- case "BeforeAgent": {
6849
+ case "task_start":
6850
+ case "TaskStart":
6851
+ case "UserPromptSubmit": {
6843
6852
  await this.emitStateChange("task.start", sharedData, context);
6844
6853
  return;
6845
6854
  }
6846
- case "AfterAgent": {
6847
- if (sharedData.errorMessage) {
6848
- await this.emitStateChange("agent.error", sharedData, context);
6849
- return;
6850
- }
6855
+ case "task_complete":
6856
+ case "TaskComplete":
6857
+ case "TaskCompleted": {
6851
6858
  await this.emitStateChange("task.complete", sharedData, context);
6852
6859
  await this.emitStateChange("agent.idle", sharedData, context);
6853
6860
  return;
6854
6861
  }
6855
- case "BeforeTool": {
6862
+ case "tool_call":
6863
+ case "PreToolUse": {
6856
6864
  await this.emitStateChange("agent.tool_call", sharedData, context);
6857
6865
  return;
6858
6866
  }
6859
- case "AfterTool": {
6860
- if (sharedData.errorMessage) {
6861
- await this.emitStateChange("agent.error", sharedData, context);
6862
- return;
6863
- }
6864
- const emittedType = isGeminiCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
6867
+ case "tool_result":
6868
+ case "PostToolUse": {
6869
+ const emittedType = isCursorCodingTool(toolName) ? "agent.coding" : "agent.tool_call";
6865
6870
  await this.emitStateChange(emittedType, sharedData, context);
6866
6871
  return;
6867
6872
  }
6868
- case "AfterModel": {
6869
- await this.emitStateChange("agent.streaming", sharedData, context);
6873
+ case "thinking":
6874
+ case "Thinking": {
6875
+ await this.emitStateChange("agent.thinking", sharedData, context);
6870
6876
  return;
6871
6877
  }
6872
- case "Notification": {
6873
- await this.emitStateChange("agent.asking_user", sharedData, context);
6878
+ case "streaming":
6879
+ case "Streaming":
6880
+ case "assistant_message": {
6881
+ await this.emitStateChange("agent.streaming", sharedData, context);
6874
6882
  return;
6875
6883
  }
6876
- case "PreCompress": {
6877
- await this.emitStateChange("agent.compact", sharedData, context);
6884
+ case "error":
6885
+ case "Error": {
6886
+ await this.emitStateChange("agent.error", {
6887
+ ...sharedData,
6888
+ errorMessage: getString4(payload, "error") ?? getString4(payload, "message") ?? "Cursor error",
6889
+ errorType: inferCursorErrorType(payload)
6890
+ }, context);
6878
6891
  return;
6879
6892
  }
6880
- default: {
6881
- logger.debug({ hookEventName }, "Gemini hook event ignored by adapter");
6893
+ case "asking_user":
6894
+ case "PermissionRequest":
6895
+ case "Notification": {
6896
+ await this.emitStateChange("agent.asking_user", sharedData, context);
6897
+ return;
6882
6898
  }
6899
+ default:
6900
+ logger.debug({ eventType }, "Cursor hook event ignored by adapter");
6883
6901
  }
6884
6902
  }
6885
- async processLogsFile(filePath) {
6903
+ async processTranscriptUpdate(filePath, readFromStart) {
6886
6904
  let fileContent;
6887
6905
  try {
6888
- fileContent = await (0, import_promises9.readFile)(filePath, "utf8");
6906
+ fileContent = await (0, import_promises9.readFile)(filePath);
6889
6907
  } catch (error) {
6890
- logger.debug({ error, filePath }, "Gemini logs read skipped");
6908
+ logger.debug({ error, filePath }, "Cursor transcript read skipped");
6891
6909
  return;
6892
6910
  }
6893
- let parsedContent;
6911
+ const knownOffset = this.transcriptOffsets.get(filePath);
6912
+ const previousOffset = knownOffset ?? (readFromStart ? 0 : fileContent.byteLength);
6913
+ const safeOffset = previousOffset > fileContent.byteLength ? 0 : previousOffset;
6914
+ const newChunk = fileContent.subarray(safeOffset).toString("utf8");
6915
+ const bufferedChunk = (safeOffset === 0 ? "" : this.transcriptRemainders.get(filePath) ?? "") + newChunk;
6916
+ const lines = bufferedChunk.split(/\r?\n/u);
6917
+ const remainder = bufferedChunk.endsWith("\n") || bufferedChunk.endsWith("\r") ? "" : lines.pop() ?? "";
6918
+ this.transcriptOffsets.set(filePath, fileContent.byteLength);
6919
+ this.transcriptRemainders.set(filePath, remainder);
6920
+ for (const line of lines) {
6921
+ const trimmedLine = line.trim();
6922
+ if (trimmedLine.length === 0) {
6923
+ continue;
6924
+ }
6925
+ await this.processTranscriptLine(trimmedLine, filePath);
6926
+ }
6927
+ }
6928
+ async processTranscriptLine(line, transcriptPath) {
6929
+ let parsedLine;
6894
6930
  try {
6895
- parsedContent = JSON.parse(fileContent);
6931
+ parsedLine = JSON.parse(line);
6896
6932
  } catch (error) {
6897
- logger.warn({ error, filePath }, "Gemini logs file is not valid JSON");
6933
+ logger.warn({ error, transcriptPath }, "Cursor transcript line is not valid JSON");
6898
6934
  return;
6899
6935
  }
6900
- if (!Array.isArray(parsedContent)) {
6901
- logger.warn({ filePath }, "Gemini logs file does not contain an array");
6936
+ if (!isRecord5(parsedLine)) {
6902
6937
  return;
6903
6938
  }
6904
- const knownMessageIds = this.observedLogMessageIds.get(filePath) ?? /* @__PURE__ */ new Set();
6905
- this.observedLogMessageIds.set(filePath, knownMessageIds);
6906
- for (const entry of parsedContent) {
6907
- if (!isRecord5(entry)) {
6908
- continue;
6909
- }
6910
- const messageId = getString4(entry, "messageId") ?? getString4(entry, "message_id");
6911
- if (!messageId || knownMessageIds.has(messageId)) {
6912
- continue;
6913
- }
6914
- knownMessageIds.add(messageId);
6915
- await this.processLogEntry(entry, filePath);
6916
- }
6917
- }
6918
- async processLogEntry(entry, filePath) {
6919
- const messageType = getString4(entry, "type");
6920
6939
  const sessionId = resolveSessionId({
6921
- cwd: await this.readProjectRoot(filePath),
6922
- projectPath: await this.readProjectRoot(filePath),
6923
- sessionId: getString4(entry, "sessionId") ?? `${this.name}:session`,
6924
- tool: this.name
6940
+ sessionId: getString4(parsedLine, "sessionId") ?? getString4(parsedLine, "session_id") ?? (0, import_node_path10.basename)(transcriptPath, ".jsonl"),
6941
+ tool: this.name,
6942
+ transcriptPath
6925
6943
  });
6926
- const projectPath = await this.readProjectRoot(filePath);
6927
6944
  const context = {
6928
- cwd: projectPath,
6929
- hookPayload: entry,
6945
+ env: process.env,
6946
+ hookPayload: parsedLine,
6930
6947
  sessionId,
6931
- source: "aisnitch://adapters/gemini-cli/log-watch"
6948
+ source: "aisnitch://adapters/cursor/log",
6949
+ transcriptPath
6932
6950
  };
6951
+ const eventType = getString4(parsedLine, "type") ?? getString4(parsedLine, "event");
6952
+ const data = getRecord3(parsedLine.data) ?? parsedLine;
6953
+ const toolName = getString4(data, "toolName") ?? getString4(data, "tool");
6954
+ const toolInput = extractToolInput(data);
6933
6955
  const sharedData = {
6934
- cwd: projectPath,
6935
- projectPath,
6936
- raw: entry
6956
+ activeFile: toolInput?.filePath,
6957
+ model: getString4(data, "model"),
6958
+ raw: parsedLine,
6959
+ toolInput,
6960
+ toolName
6937
6961
  };
6938
- await this.ensureObservedSession(sessionId, sharedData, context);
6939
- switch (messageType) {
6940
- case "user": {
6962
+ switch (eventType) {
6963
+ case "session.start":
6964
+ case "session_start": {
6965
+ await this.emitStateChange("session.start", sharedData, context);
6966
+ await this.emitStateChange("agent.idle", sharedData, context);
6967
+ return;
6968
+ }
6969
+ case "session.end":
6970
+ case "session_end": {
6971
+ await this.emitStateChange("session.end", sharedData, context);
6972
+ return;
6973
+ }
6974
+ case "task.start":
6975
+ case "task_start": {
6941
6976
  await this.emitStateChange("task.start", sharedData, context);
6942
6977
  return;
6943
6978
  }
6944
- case "model":
6945
- case "assistant": {
6946
- await this.emitStateChange("agent.streaming", sharedData, context);
6979
+ case "task.complete":
6980
+ case "task_complete": {
6981
+ await this.emitStateChange("task.complete", sharedData, context);
6982
+ await this.emitStateChange("agent.idle", sharedData, context);
6947
6983
  return;
6948
6984
  }
6985
+ case "agent.thinking":
6949
6986
  case "thinking": {
6950
6987
  await this.emitStateChange("agent.thinking", sharedData, context);
6951
6988
  return;
6952
6989
  }
6953
- default: {
6954
- logger.debug({ filePath, messageType }, "Gemini log entry ignored by adapter");
6990
+ case "agent.streaming":
6991
+ case "streaming":
6992
+ case "assistant.message": {
6993
+ await this.emitStateChange("agent.streaming", sharedData, context);
6994
+ return;
6955
6995
  }
6996
+ case "agent.tool_call":
6997
+ case "tool_use": {
6998
+ const emittedType = isCursorCodingTool(toolName) ? "agent.coding" : "agent.tool_call";
6999
+ await this.emitStateChange(emittedType, sharedData, context);
7000
+ return;
7001
+ }
7002
+ case "agent.error":
7003
+ case "error": {
7004
+ await this.emitStateChange("agent.error", {
7005
+ ...sharedData,
7006
+ errorMessage: getString4(data, "error") ?? getString4(data, "message"),
7007
+ errorType: inferCursorErrorType(data)
7008
+ }, context);
7009
+ return;
7010
+ }
7011
+ case "agent.asking_user":
7012
+ case "permission_required": {
7013
+ await this.emitStateChange("agent.asking_user", sharedData, context);
7014
+ return;
7015
+ }
7016
+ default:
7017
+ return;
6956
7018
  }
6957
7019
  }
6958
- async ensureObservedSession(sessionId, data, context) {
6959
- if (this.currentSessionId === sessionId) {
6960
- return;
6961
- }
6962
- await this.emitStateChange("session.start", data, context);
6963
- await this.emitStateChange("agent.idle", data, context);
6964
- }
6965
- async readProjectRoot(filePath) {
6966
- if (this.projectRootCache.has(filePath)) {
6967
- return this.projectRootCache.get(filePath);
6968
- }
6969
- const rootPath = (0, import_node_path10.join)((0, import_node_path10.dirname)(filePath), ".project_root");
7020
+ async seedTranscriptOffsets() {
6970
7021
  try {
6971
- const projectRoot = (await (0, import_promises9.readFile)(rootPath, "utf8")).trim();
6972
- const normalizedRoot = projectRoot.length > 0 ? projectRoot : void 0;
6973
- this.projectRootCache.set(filePath, normalizedRoot);
6974
- return normalizedRoot;
7022
+ const files = await collectFilesRecursively3(
7023
+ (0, import_node_path10.join)(this.logDirectory, "logs"),
7024
+ ".jsonl"
7025
+ );
7026
+ await Promise.all(
7027
+ files.map(async (filePath) => {
7028
+ try {
7029
+ const fileStats = await (0, import_promises9.stat)(filePath);
7030
+ this.transcriptOffsets.set(filePath, fileStats.size);
7031
+ } catch {
7032
+ }
7033
+ })
7034
+ );
6975
7035
  } catch {
6976
- this.projectRootCache.set(filePath, void 0);
6977
- return void 0;
6978
7036
  }
6979
7037
  }
6980
- async seedObservedLogState() {
6981
- const files = await collectFilesRecursively3(this.logsDirectory, "logs.json");
6982
- await Promise.all(
6983
- files.map(async (filePath) => {
6984
- try {
6985
- const fileStats = await (0, import_promises9.stat)(filePath);
6986
- if (!fileStats.isFile()) {
6987
- return;
6988
- }
6989
- const fileContent = await (0, import_promises9.readFile)(filePath, "utf8");
6990
- const parsedContent = JSON.parse(fileContent);
6991
- const messageIds = /* @__PURE__ */ new Set();
6992
- if (Array.isArray(parsedContent)) {
6993
- for (const entry of parsedContent) {
6994
- if (!isRecord5(entry)) {
6995
- continue;
6996
- }
6997
- const messageId = getString4(entry, "messageId") ?? getString4(entry, "message_id");
6998
- if (messageId) {
6999
- messageIds.add(messageId);
7000
- }
7001
- }
7002
- }
7003
- this.observedLogMessageIds.set(filePath, messageIds);
7004
- } catch {
7005
- }
7006
- })
7007
- );
7008
- }
7009
7038
  startProcessPolling() {
7010
7039
  if (this.pollIntervalMs <= 0) {
7011
7040
  return;
7012
7041
  }
7013
7042
  this.processPoller = setInterval(() => {
7014
- void this.pollGeminiProcesses();
7043
+ void this.pollCursorProcesses();
7015
7044
  }, this.pollIntervalMs);
7016
7045
  this.processPoller.unref();
7017
- void this.pollGeminiProcesses();
7046
+ void this.pollCursorProcesses();
7018
7047
  }
7019
- async pollGeminiProcesses() {
7048
+ async pollCursorProcesses() {
7020
7049
  const processes = await listProcesses4(this.processListCommand);
7021
7050
  if (processes.length > 0 && this.getStatus().activeSessions === 0) {
7022
7051
  const processInfo = processes[0];
7023
7052
  if (!processInfo) {
7024
7053
  return;
7025
7054
  }
7026
- const sessionId = `gemini-cli-process-${processInfo.pid}`;
7055
+ const sessionId = `cursor-process-${processInfo.pid}`;
7027
7056
  this.fallbackProcessSessionId = sessionId;
7028
7057
  await this.emitStateChange(
7029
7058
  "session.start",
7030
- {
7031
- raw: {
7032
- process: processInfo,
7033
- source: "process-detect"
7034
- }
7035
- },
7059
+ { raw: { process: processInfo, source: "process-detect" } },
7036
7060
  {
7037
7061
  pid: processInfo.pid,
7038
7062
  sessionId,
7039
- source: "aisnitch://adapters/gemini-cli/process-detect"
7063
+ source: "aisnitch://adapters/cursor/process-detect"
7040
7064
  }
7041
7065
  );
7042
7066
  return;
@@ -7046,32 +7070,25 @@ var GeminiCLIAdapter = class extends BaseAdapter {
7046
7070
  this.fallbackProcessSessionId = null;
7047
7071
  await this.emitStateChange(
7048
7072
  "session.end",
7049
- {
7050
- raw: {
7051
- reason: "process-exit",
7052
- source: "process-detect"
7053
- }
7054
- },
7073
+ { raw: { reason: "process-exit", source: "process-detect" } },
7055
7074
  {
7056
7075
  sessionId,
7057
- source: "aisnitch://adapters/gemini-cli/process-detect"
7076
+ source: "aisnitch://adapters/cursor/process-detect"
7058
7077
  }
7059
7078
  );
7060
7079
  }
7061
7080
  }
7062
7081
  };
7063
- async function collectFilesRecursively3(directoryPath, fileName) {
7082
+ async function collectFilesRecursively3(directoryPath, extension) {
7064
7083
  try {
7065
- const entries = await (0, import_promises9.readdir)(directoryPath, {
7066
- withFileTypes: true
7067
- });
7084
+ const entries = await (0, import_promises9.readdir)(directoryPath, { withFileTypes: true });
7068
7085
  const nestedResults = await Promise.all(
7069
7086
  entries.map(async (entry) => {
7070
7087
  const entryPath = (0, import_node_path10.join)(directoryPath, entry.name);
7071
7088
  if (entry.isDirectory()) {
7072
- return await collectFilesRecursively3(entryPath, fileName);
7089
+ return await collectFilesRecursively3(entryPath, extension);
7073
7090
  }
7074
- return entry.name === fileName ? [entryPath] : [];
7091
+ return entry.name.endsWith(extension) ? [entryPath] : [];
7075
7092
  })
7076
7093
  );
7077
7094
  return nestedResults.flat();
@@ -7082,65 +7099,33 @@ async function collectFilesRecursively3(directoryPath, fileName) {
7082
7099
  throw error;
7083
7100
  }
7084
7101
  }
7085
- function extractGeminiToolName(payload) {
7086
- return getString4(payload, "tool_name") ?? getString4(payload, "toolName");
7087
- }
7088
- function extractGeminiToolInput(payload) {
7089
- const toolInput = getRecord3(payload.tool_input) ?? getRecord3(payload.toolInput);
7090
- if (!toolInput) {
7102
+ function extractToolInput(payload) {
7103
+ if (!payload) {
7091
7104
  return void 0;
7092
7105
  }
7093
- const filePath = getString4(toolInput, "file_path") ?? getString4(toolInput, "filePath") ?? getString4(toolInput, "path");
7106
+ const toolInput = getRecord3(payload.tool_input) ?? getRecord3(payload.toolInput) ?? getRecord3(payload.arguments);
7107
+ const filePath = getString4(toolInput, "filePath") ?? getString4(toolInput, "file_path") ?? getString4(toolInput, "path");
7094
7108
  const command = getString4(toolInput, "command") ?? getString4(toolInput, "cmd");
7095
7109
  if (!filePath && !command) {
7096
7110
  return void 0;
7097
7111
  }
7098
- return {
7099
- command,
7100
- filePath
7101
- };
7102
- }
7103
- function extractGeminiActiveFile(payload) {
7104
- const toolInput = extractGeminiToolInput(payload);
7105
- if (toolInput?.filePath) {
7106
- return toolInput.filePath;
7107
- }
7108
- return getString4(payload, "active_file") ?? getString4(payload, "activeFile") ?? getString4(payload, "file_path");
7112
+ return { command, filePath };
7109
7113
  }
7110
- function extractGeminiErrorMessage(payload) {
7111
- const toolResponse = getRecord3(payload.tool_response) ?? getRecord3(payload.toolResponse);
7112
- const llmResponse = getRecord3(payload.llm_response) ?? getRecord3(payload.llmResponse);
7113
- return getString4(getRecord3(toolResponse?.error), "message") ?? getString4(payload, "reason") ?? getString4(payload, "message") ?? getString4(llmResponse, "error");
7114
+ function isCursorCodingTool(toolName) {
7115
+ return toolName !== void 0 && CURSOR_CODING_TOOLS.has(toolName);
7114
7116
  }
7115
- function extractGeminiErrorType(payload) {
7116
- const rawType = getString4(payload, "error_type") ?? getString4(payload, "errorType") ?? getString4(payload, "stopReason");
7117
- switch (rawType) {
7118
- case "rate_limit":
7119
- return "rate_limit";
7120
- case "max_output_tokens":
7121
- case "context_overflow":
7122
- return "context_overflow";
7123
- case "api_error":
7124
- case "provider_error":
7125
- case "invalid_request":
7126
- return "api_error";
7127
- case "tool_failure":
7128
- return "tool_failure";
7129
- default:
7130
- return void 0;
7117
+ function inferCursorErrorType(payload) {
7118
+ const message = getString4(payload, "error") ?? getString4(payload, "message") ?? "";
7119
+ if (/rate.?limit|quota|credit/i.test(message)) {
7120
+ return "rate_limit";
7131
7121
  }
7132
- }
7133
- function extractGeminiModel(payload) {
7134
- return getString4(getRecord3(payload.llm_request), "model") ?? getString4(payload, "model");
7135
- }
7136
- function extractGeminiTokens(payload) {
7137
- const llmResponse = getRecord3(payload.llm_response) ?? getRecord3(payload.llmResponse);
7138
- const usageMetadata = getRecord3(llmResponse?.usageMetadata);
7139
- const totalTokens = getNumber2(usageMetadata, "totalTokenCount") ?? getNumber2(usageMetadata, "total_tokens");
7140
- return totalTokens;
7141
- }
7142
- function isGeminiCodingTool(toolName) {
7143
- return toolName !== void 0 && GEMINI_CODING_TOOLS.has(toolName);
7122
+ if (/context|token.?limit|too.?long/i.test(message)) {
7123
+ return "context_overflow";
7124
+ }
7125
+ if (/tool|permission|denied/i.test(message)) {
7126
+ return "tool_failure";
7127
+ }
7128
+ return "api_error";
7144
7129
  }
7145
7130
  async function listProcesses4(listCommand) {
7146
7131
  if (process.platform === "win32") {
@@ -7148,13 +7133,9 @@ async function listProcesses4(listCommand) {
7148
7133
  }
7149
7134
  try {
7150
7135
  const stdout = await listCommand();
7151
- return stdout.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).map(parseProcessLine3).filter((processInfo) => processInfo !== null);
7136
+ return stdout.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0 && line.includes("cursor")).map(parseProcessLine3).filter((processInfo) => processInfo !== null);
7152
7137
  } catch (error) {
7153
- const errorCode = isErrnoException4(error) ? String(error.code) : "";
7154
- if (isErrnoException4(error) && (errorCode === "ENOENT" || errorCode === "1")) {
7155
- return [];
7156
- }
7157
- logger.debug({ error }, "Gemini process detection failed");
7138
+ logger.debug({ error }, "Cursor process detection failed");
7158
7139
  return [];
7159
7140
  }
7160
7141
  }
@@ -7183,9 +7164,6 @@ function getRecord3(value) {
7183
7164
  return isRecord5(value) ? value : void 0;
7184
7165
  }
7185
7166
  function getNumber2(payload, key) {
7186
- if (!payload) {
7187
- return void 0;
7188
- }
7189
7167
  const value = payload[key];
7190
7168
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
7191
7169
  }
@@ -7197,70 +7175,54 @@ function getString4(payload, key) {
7197
7175
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
7198
7176
  }
7199
7177
 
7200
- // src/adapters/goose.ts
7178
+ // src/adapters/devin.ts
7201
7179
  var import_node_child_process8 = require("child_process");
7202
7180
  var import_promises10 = require("fs/promises");
7203
7181
  var import_node_path11 = require("path");
7204
7182
  var import_node_util8 = require("util");
7205
7183
  var import_chokidar6 = require("chokidar");
7206
7184
  var execFile8 = (0, import_node_util8.promisify)(import_node_child_process8.execFile);
7207
- var DEFAULT_GOOSED_BASE_URL = "http://127.0.0.1:8080";
7208
- var DEFAULT_SQLITE_QUERY = [
7209
- "SELECT",
7210
- " id,",
7211
- " name,",
7212
- " working_dir,",
7213
- " updated_at,",
7214
- " message_count,",
7215
- " provider_name,",
7216
- " accumulated_total_tokens,",
7217
- " total_tokens,",
7218
- " model_config",
7219
- "FROM sessions",
7220
- "ORDER BY updated_at DESC",
7221
- "LIMIT 24;"
7222
- ].join("\n");
7223
- var MAX_STREAMED_SESSIONS = 6;
7224
- var GOOSE_CODING_TOOL_HINT = /apply|create|delete|edit|move|patch|rename|replace|write/iu;
7225
- var GOOSE_RATE_LIMIT_HINT = /credit|quota|rate limit|exhausted/iu;
7226
- var GooseAdapter = class extends BaseAdapter {
7227
- displayName = "Goose";
7228
- name = "goose";
7185
+ var DEVIN_CODING_TOOLS = /* @__PURE__ */ new Set([
7186
+ "Edit",
7187
+ "Write",
7188
+ "MultiEdit",
7189
+ "Create",
7190
+ "Delete",
7191
+ "Move",
7192
+ "Read",
7193
+ "Grep",
7194
+ "Bash",
7195
+ "WebSearch"
7196
+ ]);
7197
+ var DevinAdapter = class extends BaseAdapter {
7198
+ displayName = "Devin CLI";
7199
+ name = "devin";
7229
7200
  strategies = [
7230
- "api-client",
7231
- "sqlite-watch",
7232
- "process-detect"
7201
+ "process-detect",
7202
+ "hooks",
7203
+ "jsonl-watch"
7233
7204
  ];
7234
- apiPoller = null;
7235
- apiBaseUrl;
7236
- apiKey;
7237
- databasePath;
7238
- databaseWatcher = null;
7239
7205
  fallbackProcessSessionId = null;
7240
- fetchImplementation;
7241
- apiDiscoverySeeded = false;
7242
- observedSessions = /* @__PURE__ */ new Set();
7243
7206
  pollIntervalMs;
7244
7207
  processPoller = null;
7245
7208
  processListCommand;
7246
- sessionEventFingerprints = /* @__PURE__ */ new Map();
7247
- sessionSnapshots = /* @__PURE__ */ new Map();
7248
- sessionStreams = /* @__PURE__ */ new Map();
7249
- sqliteQueryCommand;
7209
+ sessionDirectory;
7210
+ sessionMetadata = /* @__PURE__ */ new Map();
7211
+ transcriptOffsets = /* @__PURE__ */ new Map();
7212
+ transcriptRemainders = /* @__PURE__ */ new Map();
7213
+ watcher = null;
7250
7214
  watcherFactory;
7251
7215
  constructor(options) {
7252
7216
  super(options);
7253
- this.apiBaseUrl = options.apiBaseUrl ?? options.env?.AISNITCH_GOOSE_API_BASE_URL ?? DEFAULT_GOOSED_BASE_URL;
7254
- this.apiKey = options.apiKey ?? options.env?.AISNITCH_GOOSE_API_KEY ?? options.env?.GOOSE_API_KEY;
7255
- this.databasePath = options.databasePath ?? (0, import_node_path11.join)(this.getUserHomeDirectory(), ".config", "goose", "sessions.db");
7256
- this.fetchImplementation = options.fetchImplementation ?? fetch;
7257
7217
  this.pollIntervalMs = options.pollIntervalMs ?? 5e3;
7258
- this.processListCommand = options.processListCommand ?? (async () => await execFile8("pgrep", ["-lf", "goose|goosed"]).then(
7259
- (result) => result.stdout
7260
- ));
7261
- this.sqliteQueryCommand = options.sqliteQueryCommand ?? (async (databasePath, query) => await execFile8("sqlite3", ["-json", databasePath, query]).then(
7262
- (result) => result.stdout
7263
- ));
7218
+ this.processListCommand = options.processListCommand ?? (async () => {
7219
+ const results = await Promise.all([
7220
+ execFile8("pgrep", ["-lf", "devin"]).catch(() => ({ stdout: "" })),
7221
+ execFile8("pgrep", ["-lf", "cognition"]).catch(() => ({ stdout: "" }))
7222
+ ]);
7223
+ return results.map((r) => r.stdout).join("\n");
7224
+ });
7225
+ this.sessionDirectory = options.sessionDirectory ?? (0, import_node_path11.join)(this.getUserHomeDirectory(), ".devin");
7264
7226
  this.watcherFactory = options.watcherFactory ?? import_chokidar6.watch;
7265
7227
  }
7266
7228
  async start() {
@@ -7268,598 +7230,2204 @@ var GooseAdapter = class extends BaseAdapter {
7268
7230
  return;
7269
7231
  }
7270
7232
  this.setRunning(true);
7271
- await this.seedSqliteSessions();
7272
- this.databaseWatcher = this.watcherFactory(this.databasePath, {
7273
- awaitWriteFinish: {
7274
- stabilityThreshold: 200
7275
- },
7276
- ignoreInitial: true
7277
- });
7278
- this.databaseWatcher.on("add", () => {
7279
- void this.pollSqliteSessions(true);
7280
- });
7281
- this.databaseWatcher.on("change", () => {
7282
- void this.pollSqliteSessions(true);
7283
- });
7284
- this.databaseWatcher.on("error", (error) => {
7285
- logger.warn({ error }, "Goose SQLite watcher error");
7233
+ await this.seedTranscriptOffsets();
7234
+ this.watcher = this.watcherFactory(
7235
+ (0, import_node_path11.join)(this.sessionDirectory, "**", "*.jsonl"),
7236
+ {
7237
+ awaitWriteFinish: { stabilityThreshold: 200 },
7238
+ ignoreInitial: true
7239
+ }
7240
+ );
7241
+ this.watcher.on("add", (filePath) => void this.processTranscriptUpdate(filePath, true));
7242
+ this.watcher.on("change", (filePath) => void this.processTranscriptUpdate(filePath, false));
7243
+ this.watcher.on("error", (error) => {
7244
+ logger.warn({ error }, "Devin log watcher error");
7286
7245
  });
7287
- this.startApiPolling();
7288
7246
  this.startProcessPolling();
7289
7247
  }
7290
7248
  async stop() {
7291
- if (this.databaseWatcher !== null) {
7292
- await this.databaseWatcher.close();
7293
- this.databaseWatcher = null;
7294
- }
7295
- if (this.apiPoller !== null) {
7296
- clearInterval(this.apiPoller);
7297
- this.apiPoller = null;
7249
+ if (this.watcher !== null) {
7250
+ await this.watcher.close();
7251
+ this.watcher = null;
7298
7252
  }
7299
7253
  if (this.processPoller !== null) {
7300
7254
  clearInterval(this.processPoller);
7301
7255
  this.processPoller = null;
7302
7256
  }
7303
- for (const streamHandle of this.sessionStreams.values()) {
7304
- streamHandle.abortController.abort();
7305
- void streamHandle.promise.catch(() => void 0);
7306
- }
7307
- this.apiDiscoverySeeded = false;
7308
7257
  this.fallbackProcessSessionId = null;
7309
- this.observedSessions.clear();
7310
- this.sessionEventFingerprints.clear();
7311
- this.sessionSnapshots.clear();
7312
- this.sessionStreams.clear();
7258
+ this.sessionMetadata.clear();
7259
+ this.transcriptOffsets.clear();
7260
+ this.transcriptRemainders.clear();
7313
7261
  this.setRunning(false);
7314
7262
  }
7315
7263
  async handleHook(payload) {
7316
7264
  const normalizedPayload = this.parseNormalizedHookPayload(payload);
7317
- if (normalizedPayload === null) {
7318
- logger.debug({ payload }, "Goose ignores non-normalized hook payloads");
7265
+ if (normalizedPayload !== null) {
7266
+ await this.emitNormalizedPayload({
7267
+ ...normalizedPayload,
7268
+ sessionId: resolveSessionId({
7269
+ activeFile: normalizedPayload.data?.activeFile,
7270
+ cwd: normalizedPayload.data?.cwd ?? normalizedPayload.cwd,
7271
+ pid: normalizedPayload.pid,
7272
+ project: normalizedPayload.data?.project,
7273
+ projectPath: normalizedPayload.data?.projectPath,
7274
+ sessionId: normalizedPayload.sessionId,
7275
+ tool: this.name,
7276
+ transcriptPath: normalizedPayload.transcriptPath
7277
+ })
7278
+ });
7319
7279
  return;
7320
7280
  }
7321
- await this.emitNormalizedPayload({
7322
- ...normalizedPayload,
7323
- sessionId: resolveSessionId({
7324
- activeFile: normalizedPayload.data?.activeFile,
7325
- cwd: normalizedPayload.data?.cwd ?? normalizedPayload.cwd,
7326
- pid: normalizedPayload.pid,
7327
- projectPath: normalizedPayload.data?.projectPath,
7328
- sessionId: normalizedPayload.sessionId,
7329
- tool: this.name,
7330
- transcriptPath: normalizedPayload.transcriptPath
7331
- })
7332
- });
7333
- }
7334
- startApiPolling() {
7335
- if (this.pollIntervalMs <= 0) {
7281
+ if (!isRecord6(payload)) {
7282
+ logger.warn({ payload }, "Devin hook payload must be an object");
7336
7283
  return;
7337
7284
  }
7338
- this.apiPoller = setInterval(() => {
7339
- void this.pollGooseApi(true);
7340
- }, this.pollIntervalMs);
7341
- this.apiPoller.unref();
7342
- void this.pollGooseApi(false);
7285
+ const sessionId = resolveSessionId({
7286
+ cwd: getString5(payload, "cwd"),
7287
+ pid: getNumber3(payload, "pid"),
7288
+ projectPath: getString5(payload, "projectPath"),
7289
+ sessionId: getString5(payload, "sessionId") ?? getString5(payload, "session_id") ?? getString5(payload, "id") ?? getString5(getRecord4(payload.data), "sessionId"),
7290
+ tool: this.name
7291
+ });
7292
+ const context = {
7293
+ cwd: getString5(payload, "cwd"),
7294
+ env: this.env ?? process.env,
7295
+ hookPayload: payload,
7296
+ pid: getNumber3(payload, "pid"),
7297
+ sessionId,
7298
+ source: "aisnitch://adapters/devin"
7299
+ };
7300
+ const eventType = getString5(payload, "event") ?? getString5(payload, "type") ?? getString5(payload, "action");
7301
+ const data = getRecord4(payload.data) ?? payload;
7302
+ const toolName = getString5(data, "tool") ?? getString5(data, "toolName") ?? getString5(data, "name");
7303
+ const toolInput = extractToolInput2(data);
7304
+ const sharedData = {
7305
+ activeFile: toolInput?.filePath,
7306
+ cwd: context.cwd,
7307
+ model: getString5(data, "model") ?? getString5(payload, "model"),
7308
+ projectPath: getString5(data, "projectPath") ?? getString5(payload, "projectPath"),
7309
+ raw: payload,
7310
+ toolInput,
7311
+ toolName
7312
+ };
7313
+ switch (eventType) {
7314
+ case "session.start":
7315
+ case "sessionStart":
7316
+ case "SessionStart": {
7317
+ await this.emitStateChange("session.start", sharedData, context);
7318
+ await this.emitStateChange("agent.idle", sharedData, context);
7319
+ return;
7320
+ }
7321
+ case "session.end":
7322
+ case "sessionEnd":
7323
+ case "SessionEnd": {
7324
+ await this.emitStateChange("session.end", sharedData, context);
7325
+ return;
7326
+ }
7327
+ case "task.start":
7328
+ case "taskStart":
7329
+ case "task_create":
7330
+ case "TaskCreate": {
7331
+ await this.emitStateChange("task.start", sharedData, context);
7332
+ return;
7333
+ }
7334
+ case "task.complete":
7335
+ case "taskComplete":
7336
+ case "TaskComplete":
7337
+ case "task_done": {
7338
+ await this.emitStateChange("task.complete", sharedData, context);
7339
+ await this.emitStateChange("agent.idle", sharedData, context);
7340
+ return;
7341
+ }
7342
+ case "tool.call":
7343
+ case "toolCall":
7344
+ case "ToolCall": {
7345
+ await this.emitStateChange("agent.tool_call", sharedData, context);
7346
+ return;
7347
+ }
7348
+ case "tool.result":
7349
+ case "toolResult":
7350
+ case "ToolResult": {
7351
+ const emittedType = isDevinCodingTool(toolName) ? "agent.coding" : "agent.tool_call";
7352
+ await this.emitStateChange(emittedType, sharedData, context);
7353
+ return;
7354
+ }
7355
+ case "thinking":
7356
+ case "Thinking":
7357
+ case "reasoning": {
7358
+ await this.emitStateChange("agent.thinking", sharedData, context);
7359
+ return;
7360
+ }
7361
+ case "output":
7362
+ case "streaming":
7363
+ case "Output": {
7364
+ await this.emitStateChange("agent.streaming", sharedData, context);
7365
+ return;
7366
+ }
7367
+ case "error":
7368
+ case "Error":
7369
+ case "ErrorOccurred": {
7370
+ await this.emitStateChange("agent.error", {
7371
+ ...sharedData,
7372
+ errorMessage: getString5(data, "error") ?? getString5(payload, "error") ?? "Devin error",
7373
+ errorType: inferDevinErrorType(data)
7374
+ }, context);
7375
+ return;
7376
+ }
7377
+ case "asking_user":
7378
+ case "PermissionRequest":
7379
+ case "user_input_required":
7380
+ case "InputRequired": {
7381
+ await this.emitStateChange("agent.asking_user", sharedData, context);
7382
+ return;
7383
+ }
7384
+ default:
7385
+ logger.debug({ eventType }, "Devin hook event ignored by adapter");
7386
+ }
7343
7387
  }
7344
- startProcessPolling() {
7345
- if (this.pollIntervalMs <= 0) {
7388
+ async processTranscriptUpdate(filePath, readFromStart) {
7389
+ let fileContent;
7390
+ try {
7391
+ fileContent = await (0, import_promises10.readFile)(filePath);
7392
+ } catch (error) {
7393
+ logger.debug({ error, filePath }, "Devin transcript read skipped");
7346
7394
  return;
7347
7395
  }
7348
- this.processPoller = setInterval(() => {
7349
- void this.pollGooseProcesses();
7350
- }, this.pollIntervalMs);
7351
- this.processPoller.unref();
7352
- void this.pollGooseProcesses();
7353
- }
7354
- async seedSqliteSessions() {
7355
- try {
7356
- await (0, import_promises10.stat)(this.databasePath);
7357
- } catch {
7358
- return;
7396
+ const knownOffset = this.transcriptOffsets.get(filePath);
7397
+ const previousOffset = knownOffset ?? (readFromStart ? 0 : fileContent.byteLength);
7398
+ const safeOffset = previousOffset > fileContent.byteLength ? 0 : previousOffset;
7399
+ const newChunk = fileContent.subarray(safeOffset).toString("utf8");
7400
+ const bufferedChunk = (safeOffset === 0 ? "" : this.transcriptRemainders.get(filePath) ?? "") + newChunk;
7401
+ const lines = bufferedChunk.split(/\r?\n/u);
7402
+ const remainder = bufferedChunk.endsWith("\n") || bufferedChunk.endsWith("\r") ? "" : lines.pop() ?? "";
7403
+ this.transcriptOffsets.set(filePath, fileContent.byteLength);
7404
+ this.transcriptRemainders.set(filePath, remainder);
7405
+ for (const line of lines) {
7406
+ const trimmedLine = line.trim();
7407
+ if (trimmedLine.length === 0) {
7408
+ continue;
7409
+ }
7410
+ await this.processTranscriptLine(trimmedLine, filePath);
7359
7411
  }
7360
- const sessions = await this.readSqliteSessions();
7361
- this.syncSessionSnapshots(sessions, false, "sqlite-seed");
7362
- }
7363
- async pollSqliteSessions(emitChanges) {
7364
- const sessions = await this.readSqliteSessions();
7365
- this.syncSessionSnapshots(sessions, emitChanges, "sqlite");
7366
7412
  }
7367
- async readSqliteSessions() {
7368
- let rawOutput;
7413
+ async processTranscriptLine(line, transcriptPath) {
7414
+ let parsedLine;
7369
7415
  try {
7370
- rawOutput = await this.sqliteQueryCommand(
7371
- this.databasePath,
7372
- DEFAULT_SQLITE_QUERY
7373
- );
7416
+ parsedLine = JSON.parse(line);
7374
7417
  } catch (error) {
7375
- logger.debug({ error }, "Goose SQLite session query skipped");
7376
- return [];
7418
+ logger.warn({ error, transcriptPath }, "Devin transcript line is not valid JSON");
7419
+ return;
7377
7420
  }
7378
- let parsedOutput;
7379
- try {
7380
- parsedOutput = JSON.parse(rawOutput);
7381
- } catch (error) {
7382
- logger.warn({ error }, "Goose SQLite JSON output is invalid");
7383
- return [];
7421
+ if (!isRecord6(parsedLine)) {
7422
+ return;
7384
7423
  }
7385
- if (!Array.isArray(parsedOutput)) {
7386
- return [];
7424
+ const eventType = getString5(parsedLine, "type") ?? getString5(parsedLine, "event");
7425
+ const sessionId = resolveSessionId({
7426
+ sessionId: getString5(parsedLine, "sessionId") ?? getString5(parsedLine, "session_id") ?? getString5(parsedLine, "id") ?? (0, import_node_path11.basename)(transcriptPath, ".jsonl"),
7427
+ tool: this.name,
7428
+ transcriptPath
7429
+ });
7430
+ const context = {
7431
+ env: process.env,
7432
+ hookPayload: parsedLine,
7433
+ sessionId,
7434
+ source: "aisnitch://adapters/devin/log",
7435
+ transcriptPath
7436
+ };
7437
+ const data = getRecord4(parsedLine.data) ?? parsedLine;
7438
+ const toolName = getString5(data, "tool") ?? getString5(data, "toolName");
7439
+ const toolInput = extractToolInput2(data);
7440
+ const sharedData = {
7441
+ activeFile: toolInput?.filePath,
7442
+ cwd: getString5(data, "cwd"),
7443
+ model: getString5(data, "model"),
7444
+ raw: parsedLine,
7445
+ toolInput,
7446
+ toolName
7447
+ };
7448
+ switch (eventType) {
7449
+ case "session.start":
7450
+ case "SessionStart": {
7451
+ await this.emitStateChange("session.start", sharedData, context);
7452
+ await this.emitStateChange("agent.idle", sharedData, context);
7453
+ return;
7454
+ }
7455
+ case "session.end":
7456
+ case "SessionEnd": {
7457
+ await this.emitStateChange("session.end", sharedData, context);
7458
+ return;
7459
+ }
7460
+ case "task.start":
7461
+ case "TaskStart": {
7462
+ await this.emitStateChange("task.start", sharedData, context);
7463
+ return;
7464
+ }
7465
+ case "task.complete":
7466
+ case "TaskComplete": {
7467
+ await this.emitStateChange("task.complete", sharedData, context);
7468
+ await this.emitStateChange("agent.idle", sharedData, context);
7469
+ return;
7470
+ }
7471
+ case "thinking":
7472
+ case "Thinking": {
7473
+ await this.emitStateChange("agent.thinking", sharedData, context);
7474
+ return;
7475
+ }
7476
+ case "output":
7477
+ case "assistant_message": {
7478
+ await this.emitStateChange("agent.streaming", sharedData, context);
7479
+ return;
7480
+ }
7481
+ case "tool_use":
7482
+ case "ToolUse": {
7483
+ const emittedType = isDevinCodingTool(toolName) ? "agent.coding" : "agent.tool_call";
7484
+ await this.emitStateChange(emittedType, sharedData, context);
7485
+ return;
7486
+ }
7487
+ case "error":
7488
+ case "Error": {
7489
+ await this.emitStateChange("agent.error", {
7490
+ ...sharedData,
7491
+ errorMessage: getString5(data, "error") ?? getString5(data, "message"),
7492
+ errorType: inferDevinErrorType(data)
7493
+ }, context);
7494
+ return;
7495
+ }
7496
+ case "permission_request":
7497
+ case "asking_user": {
7498
+ await this.emitStateChange("agent.asking_user", sharedData, context);
7499
+ return;
7500
+ }
7501
+ default:
7502
+ return;
7387
7503
  }
7388
- return parsedOutput.map((entry) => normalizeGooseSession(entry)).filter((entry) => entry !== null);
7389
7504
  }
7390
- async pollGooseApi(emitChanges) {
7391
- let statusResponse;
7505
+ async seedTranscriptOffsets() {
7392
7506
  try {
7393
- statusResponse = await this.fetchImplementation(
7394
- new URL("/status", this.apiBaseUrl)
7507
+ const files = await collectFilesRecursively4(this.sessionDirectory, ".jsonl");
7508
+ await Promise.all(
7509
+ files.map(async (filePath) => {
7510
+ try {
7511
+ const fileStats = await (0, import_promises10.stat)(filePath);
7512
+ this.transcriptOffsets.set(filePath, fileStats.size);
7513
+ } catch {
7514
+ }
7515
+ })
7395
7516
  );
7396
- } catch (error) {
7397
- logger.debug({ error }, "Goosed status endpoint is unavailable");
7398
- return;
7517
+ } catch {
7399
7518
  }
7400
- if (!statusResponse.ok) {
7519
+ }
7520
+ startProcessPolling() {
7521
+ if (this.pollIntervalMs <= 0) {
7401
7522
  return;
7402
7523
  }
7403
- let sessionsResponse;
7404
- try {
7405
- sessionsResponse = await this.fetchImplementation(
7406
- new URL("/sessions", this.apiBaseUrl),
7524
+ this.processPoller = setInterval(() => {
7525
+ void this.pollDevinProcesses();
7526
+ }, this.pollIntervalMs);
7527
+ this.processPoller.unref();
7528
+ void this.pollDevinProcesses();
7529
+ }
7530
+ async pollDevinProcesses() {
7531
+ const processes = await listProcesses5(this.processListCommand);
7532
+ if (processes.length > 0 && this.getStatus().activeSessions === 0) {
7533
+ const processInfo = processes[0];
7534
+ if (!processInfo) {
7535
+ return;
7536
+ }
7537
+ const sessionId = `devin-process-${processInfo.pid}`;
7538
+ this.fallbackProcessSessionId = sessionId;
7539
+ await this.emitStateChange(
7540
+ "session.start",
7541
+ { raw: { process: processInfo, source: "process-detect" } },
7407
7542
  {
7408
- headers: this.createApiHeaders()
7543
+ pid: processInfo.pid,
7544
+ sessionId,
7545
+ source: "aisnitch://adapters/devin/process-detect"
7409
7546
  }
7410
7547
  );
7411
- } catch (error) {
7412
- logger.debug({ error }, "Goosed sessions endpoint request failed");
7413
7548
  return;
7414
7549
  }
7415
- if (!sessionsResponse.ok) {
7416
- logger.debug(
7417
- { status: sessionsResponse.status },
7418
- "Goosed sessions endpoint rejected the request"
7550
+ if (processes.length === 0 && this.fallbackProcessSessionId !== null) {
7551
+ const sessionId = this.fallbackProcessSessionId;
7552
+ this.fallbackProcessSessionId = null;
7553
+ await this.emitStateChange(
7554
+ "session.end",
7555
+ { raw: { reason: "process-exit", source: "process-detect" } },
7556
+ {
7557
+ sessionId,
7558
+ source: "aisnitch://adapters/devin/process-detect"
7559
+ }
7419
7560
  );
7420
- return;
7421
- }
7422
- let parsedPayload;
7423
- try {
7424
- parsedPayload = await sessionsResponse.json();
7425
- } catch (error) {
7426
- logger.warn({ error }, "Goosed sessions payload is not valid JSON");
7427
- return;
7428
- }
7429
- const responsePayload = parsedPayload;
7430
- const rawSessions = Array.isArray(responsePayload.sessions) ? responsePayload.sessions : [];
7431
- const sessions = rawSessions.map((entry) => normalizeGooseSession(entry)).filter((entry) => entry !== null);
7432
- this.syncSessionSnapshots(
7433
- sessions,
7434
- emitChanges && this.apiDiscoverySeeded,
7435
- "api"
7436
- );
7437
- this.apiDiscoverySeeded = true;
7438
- }
7439
- syncSessionSnapshots(nextSnapshots, emitChanges, source) {
7440
- for (const snapshot of nextSnapshots) {
7441
- const previousSnapshot = this.sessionSnapshots.get(snapshot.sessionId);
7442
- this.sessionSnapshots.set(snapshot.sessionId, snapshot);
7443
- this.ensureTrackedSessionStream(snapshot);
7444
- if (!emitChanges) {
7445
- continue;
7446
- }
7447
- if (previousSnapshot === void 0) {
7448
- void this.ensureObservedSession(snapshot, source);
7449
- continue;
7450
- }
7451
- if (!didGooseSessionAdvance(previousSnapshot, snapshot)) {
7452
- continue;
7453
- }
7454
- void this.ensureObservedSession(snapshot, source).then(async () => {
7455
- await this.emitStateChange(
7456
- "agent.streaming",
7457
- buildGooseSessionData(snapshot, {
7458
- raw: {
7459
- snapshot,
7460
- source
7461
- }
7462
- }),
7463
- this.createSessionContext(snapshot, `aisnitch://adapters/goose/${source}`)
7464
- );
7465
- });
7466
7561
  }
7467
- this.trimTrackedSessionStreams(nextSnapshots);
7468
7562
  }
7469
- ensureTrackedSessionStream(snapshot) {
7470
- if (this.sessionStreams.has(snapshot.sessionId)) {
7471
- return;
7472
- }
7473
- const abortController = new AbortController();
7474
- const streamPromise = this.consumeSessionEvents(snapshot, abortController).finally(
7475
- () => {
7476
- const activeStream = this.sessionStreams.get(snapshot.sessionId);
7477
- if (activeStream?.abortController === abortController) {
7478
- this.sessionStreams.delete(snapshot.sessionId);
7563
+ };
7564
+ async function collectFilesRecursively4(directoryPath, extension) {
7565
+ try {
7566
+ const entries = await (0, import_promises10.readdir)(directoryPath, { withFileTypes: true });
7567
+ const nestedResults = await Promise.all(
7568
+ entries.map(async (entry) => {
7569
+ const entryPath = (0, import_node_path11.join)(directoryPath, entry.name);
7570
+ if (entry.isDirectory()) {
7571
+ return await collectFilesRecursively4(entryPath, extension);
7479
7572
  }
7480
- }
7481
- );
7482
- this.sessionStreams.set(snapshot.sessionId, {
7483
- abortController,
7484
- promise: streamPromise
7485
- });
7486
- }
7487
- trimTrackedSessionStreams(nextSnapshots) {
7488
- const keepSessionIds = new Set(
7489
- [...nextSnapshots].sort(compareGooseSessionsByRecency).slice(0, MAX_STREAMED_SESSIONS).map((snapshot) => snapshot.sessionId)
7573
+ return entry.name.endsWith(extension) ? [entryPath] : [];
7574
+ })
7490
7575
  );
7491
- for (const [sessionId, streamHandle] of this.sessionStreams) {
7492
- if (keepSessionIds.has(sessionId)) {
7493
- continue;
7494
- }
7495
- streamHandle.abortController.abort();
7496
- this.sessionStreams.delete(sessionId);
7576
+ return nestedResults.flat();
7577
+ } catch (error) {
7578
+ if (isErrnoException5(error) && error.code === "ENOENT") {
7579
+ return [];
7497
7580
  }
7581
+ throw error;
7498
7582
  }
7499
- async consumeSessionEvents(snapshot, abortController) {
7500
- let response;
7501
- try {
7502
- response = await this.fetchImplementation(
7503
- new URL(
7504
- `/sessions/${encodeURIComponent(snapshot.gooseSessionId)}/events`,
7505
- this.apiBaseUrl
7506
- ),
7507
- {
7508
- headers: {
7509
- ...this.createApiHeaders(),
7510
- Accept: "text/event-stream"
7511
- },
7512
- signal: abortController.signal
7513
- }
7514
- );
7515
- } catch (error) {
7516
- if (abortController.signal.aborted) {
7517
- return;
7518
- }
7583
+ }
7584
+ function extractToolInput2(payload) {
7585
+ if (!payload) {
7586
+ return void 0;
7587
+ }
7588
+ const toolInput = getRecord4(payload.tool_input) ?? getRecord4(payload.toolInput) ?? getRecord4(payload.arguments) ?? getRecord4(payload.params);
7589
+ const filePath = getString5(toolInput, "filePath") ?? getString5(toolInput, "file_path") ?? getString5(toolInput, "path") ?? getString5(toolInput, "target");
7590
+ const command = getString5(toolInput, "command") ?? getString5(toolInput, "cmd") ?? getString5(toolInput, "script");
7591
+ if (!filePath && !command) {
7592
+ return void 0;
7593
+ }
7594
+ return { command, filePath };
7595
+ }
7596
+ function isDevinCodingTool(toolName) {
7597
+ return toolName !== void 0 && DEVIN_CODING_TOOLS.has(toolName);
7598
+ }
7599
+ function inferDevinErrorType(payload) {
7600
+ const message = getString5(payload, "error") ?? getString5(payload, "message") ?? "";
7601
+ if (/rate.?limit|quota|credit|api.?key/i.test(message)) {
7602
+ return "rate_limit";
7603
+ }
7604
+ if (/context|token.?limit|too.?long|exceeded/i.test(message)) {
7605
+ return "context_overflow";
7606
+ }
7607
+ if (/tool|permission|denied|access/i.test(message)) {
7608
+ return "tool_failure";
7609
+ }
7610
+ return "api_error";
7611
+ }
7612
+ async function listProcesses5(listCommand) {
7613
+ if (process.platform === "win32") {
7614
+ return [];
7615
+ }
7616
+ try {
7617
+ const stdout = await listCommand();
7618
+ return stdout.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).filter(
7619
+ (line) => line.includes("devin") || line.includes("cognition") || line.includes("swe")
7620
+ ).map(parseProcessLine4).filter((processInfo) => processInfo !== null);
7621
+ } catch (error) {
7622
+ logger.debug({ error }, "Devin process detection failed");
7623
+ return [];
7624
+ }
7625
+ }
7626
+ function parseProcessLine4(line) {
7627
+ const match = line.match(/^(\d+)\s+(.+)$/u);
7628
+ if (!match) {
7629
+ return null;
7630
+ }
7631
+ const pidText = match[1];
7632
+ const command = match[2];
7633
+ if (!pidText || !command) {
7634
+ return null;
7635
+ }
7636
+ return {
7637
+ command,
7638
+ pid: Number.parseInt(pidText, 10)
7639
+ };
7640
+ }
7641
+ function isErrnoException5(error) {
7642
+ return error instanceof Error && "code" in error;
7643
+ }
7644
+ function isRecord6(value) {
7645
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7646
+ }
7647
+ function getRecord4(value) {
7648
+ return isRecord6(value) ? value : void 0;
7649
+ }
7650
+ function getNumber3(payload, key) {
7651
+ const value = payload[key];
7652
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
7653
+ }
7654
+ function getString5(payload, key) {
7655
+ if (!payload) {
7656
+ return void 0;
7657
+ }
7658
+ const value = payload[key];
7659
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
7660
+ }
7661
+
7662
+ // src/adapters/gemini-cli.ts
7663
+ var import_node_child_process9 = require("child_process");
7664
+ var import_promises11 = require("fs/promises");
7665
+ var import_node_path12 = require("path");
7666
+ var import_node_util9 = require("util");
7667
+ var import_chokidar7 = require("chokidar");
7668
+ var execFile9 = (0, import_node_util9.promisify)(import_node_child_process9.execFile);
7669
+ var GEMINI_CODING_TOOLS = /* @__PURE__ */ new Set([
7670
+ "edit",
7671
+ "replace",
7672
+ "write",
7673
+ "write_file"
7674
+ ]);
7675
+ var GeminiCLIAdapter = class extends BaseAdapter {
7676
+ displayName = "Gemini CLI";
7677
+ name = "gemini-cli";
7678
+ strategies = [
7679
+ "hooks",
7680
+ "log-watch",
7681
+ "process-detect"
7682
+ ];
7683
+ fallbackProcessSessionId = null;
7684
+ logsDirectory;
7685
+ observedLogMessageIds = /* @__PURE__ */ new Map();
7686
+ projectRootCache = /* @__PURE__ */ new Map();
7687
+ pollIntervalMs;
7688
+ processPoller = null;
7689
+ processListCommand;
7690
+ watcher = null;
7691
+ watcherFactory;
7692
+ constructor(options) {
7693
+ super(options);
7694
+ this.logsDirectory = options.logsDirectory ?? (0, import_node_path12.join)(this.getUserHomeDirectory(), ".gemini", "tmp");
7695
+ this.pollIntervalMs = options.pollIntervalMs ?? 5e3;
7696
+ this.processListCommand = options.processListCommand ?? (async () => await execFile9("pgrep", ["-lf", "gemini"]).then((result) => result.stdout));
7697
+ this.watcherFactory = options.watcherFactory ?? import_chokidar7.watch;
7698
+ }
7699
+ async start() {
7700
+ if (this.getStatus().running) {
7701
+ return;
7702
+ }
7703
+ this.setRunning(true);
7704
+ await this.seedObservedLogState();
7705
+ const logsGlob = (0, import_node_path12.join)(this.logsDirectory, "**", "logs.json");
7706
+ this.watcher = this.watcherFactory(logsGlob, {
7707
+ awaitWriteFinish: {
7708
+ stabilityThreshold: 200
7709
+ },
7710
+ ignoreInitial: true
7711
+ });
7712
+ this.watcher.on("add", (filePath) => {
7713
+ void this.processLogsFile(filePath);
7714
+ });
7715
+ this.watcher.on("change", (filePath) => {
7716
+ void this.processLogsFile(filePath);
7717
+ });
7718
+ this.watcher.on("error", (error) => {
7719
+ logger.warn({ error }, "Gemini logs watcher error");
7720
+ });
7721
+ this.startProcessPolling();
7722
+ }
7723
+ async stop() {
7724
+ if (this.watcher !== null) {
7725
+ await this.watcher.close();
7726
+ this.watcher = null;
7727
+ }
7728
+ if (this.processPoller !== null) {
7729
+ clearInterval(this.processPoller);
7730
+ this.processPoller = null;
7731
+ }
7732
+ this.fallbackProcessSessionId = null;
7733
+ this.observedLogMessageIds.clear();
7734
+ this.projectRootCache.clear();
7735
+ this.setRunning(false);
7736
+ }
7737
+ async handleHook(payload) {
7738
+ const normalizedPayload = this.parseNormalizedHookPayload(payload);
7739
+ if (normalizedPayload !== null) {
7740
+ await this.emitNormalizedPayload({
7741
+ ...normalizedPayload,
7742
+ sessionId: resolveSessionId({
7743
+ activeFile: normalizedPayload.data?.activeFile,
7744
+ cwd: normalizedPayload.data?.cwd ?? normalizedPayload.cwd,
7745
+ pid: normalizedPayload.pid,
7746
+ projectPath: normalizedPayload.data?.projectPath,
7747
+ sessionId: normalizedPayload.sessionId,
7748
+ tool: this.name,
7749
+ transcriptPath: normalizedPayload.transcriptPath
7750
+ })
7751
+ });
7752
+ return;
7753
+ }
7754
+ if (!isRecord7(payload)) {
7755
+ logger.warn({ payload }, "Gemini hook payload must be an object");
7756
+ return;
7757
+ }
7758
+ const hookEventName = getString6(payload, "hook_event_name") ?? getString6(payload, "hookEventName");
7759
+ if (!hookEventName) {
7760
+ logger.warn({ payload }, "Gemini hook payload is missing its event name");
7761
+ return;
7762
+ }
7763
+ const cwd = getString6(payload, "cwd");
7764
+ const transcriptPath = getString6(payload, "transcript_path") ?? getString6(payload, "transcriptPath");
7765
+ const sessionId = resolveSessionId({
7766
+ activeFile: extractGeminiActiveFile(payload),
7767
+ cwd,
7768
+ projectPath: cwd,
7769
+ sessionId: getString6(payload, "session_id") ?? getString6(payload, "sessionId"),
7770
+ tool: this.name,
7771
+ transcriptPath
7772
+ });
7773
+ const context = {
7774
+ cwd,
7775
+ hookPayload: payload,
7776
+ pid: getNumber4(payload, "pid"),
7777
+ sessionId,
7778
+ source: "aisnitch://adapters/gemini-cli",
7779
+ transcriptPath
7780
+ };
7781
+ const sharedData = {
7782
+ activeFile: extractGeminiActiveFile(payload),
7783
+ cwd,
7784
+ errorMessage: extractGeminiErrorMessage(payload),
7785
+ errorType: extractGeminiErrorType(payload),
7786
+ model: extractGeminiModel(payload),
7787
+ projectPath: cwd,
7788
+ raw: payload,
7789
+ tokensUsed: extractGeminiTokens(payload),
7790
+ toolInput: extractGeminiToolInput(payload),
7791
+ toolName: extractGeminiToolName(payload)
7792
+ };
7793
+ switch (hookEventName) {
7794
+ case "SessionStart": {
7795
+ this.fallbackProcessSessionId = null;
7796
+ await this.emitStateChange("session.start", sharedData, context);
7797
+ await this.emitStateChange("agent.idle", sharedData, context);
7798
+ return;
7799
+ }
7800
+ case "SessionEnd": {
7801
+ await this.emitStateChange("session.end", sharedData, context);
7802
+ return;
7803
+ }
7804
+ case "BeforeAgent": {
7805
+ await this.emitStateChange("task.start", sharedData, context);
7806
+ return;
7807
+ }
7808
+ case "AfterAgent": {
7809
+ if (sharedData.errorMessage) {
7810
+ await this.emitStateChange("agent.error", sharedData, context);
7811
+ return;
7812
+ }
7813
+ await this.emitStateChange("task.complete", sharedData, context);
7814
+ await this.emitStateChange("agent.idle", sharedData, context);
7815
+ return;
7816
+ }
7817
+ case "BeforeTool": {
7818
+ await this.emitStateChange("agent.tool_call", sharedData, context);
7819
+ return;
7820
+ }
7821
+ case "AfterTool": {
7822
+ if (sharedData.errorMessage) {
7823
+ await this.emitStateChange("agent.error", sharedData, context);
7824
+ return;
7825
+ }
7826
+ const emittedType = isGeminiCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
7827
+ await this.emitStateChange(emittedType, sharedData, context);
7828
+ return;
7829
+ }
7830
+ case "AfterModel": {
7831
+ await this.emitStateChange("agent.streaming", sharedData, context);
7832
+ return;
7833
+ }
7834
+ case "Notification": {
7835
+ await this.emitStateChange("agent.asking_user", sharedData, context);
7836
+ return;
7837
+ }
7838
+ case "PreCompress": {
7839
+ await this.emitStateChange("agent.compact", sharedData, context);
7840
+ return;
7841
+ }
7842
+ default: {
7843
+ logger.debug({ hookEventName }, "Gemini hook event ignored by adapter");
7844
+ }
7845
+ }
7846
+ }
7847
+ async processLogsFile(filePath) {
7848
+ let fileContent;
7849
+ try {
7850
+ fileContent = await (0, import_promises11.readFile)(filePath, "utf8");
7851
+ } catch (error) {
7852
+ logger.debug({ error, filePath }, "Gemini logs read skipped");
7853
+ return;
7854
+ }
7855
+ let parsedContent;
7856
+ try {
7857
+ parsedContent = JSON.parse(fileContent);
7858
+ } catch (error) {
7859
+ logger.warn({ error, filePath }, "Gemini logs file is not valid JSON");
7860
+ return;
7861
+ }
7862
+ if (!Array.isArray(parsedContent)) {
7863
+ logger.warn({ filePath }, "Gemini logs file does not contain an array");
7864
+ return;
7865
+ }
7866
+ const knownMessageIds = this.observedLogMessageIds.get(filePath) ?? /* @__PURE__ */ new Set();
7867
+ this.observedLogMessageIds.set(filePath, knownMessageIds);
7868
+ for (const entry of parsedContent) {
7869
+ if (!isRecord7(entry)) {
7870
+ continue;
7871
+ }
7872
+ const messageId = getString6(entry, "messageId") ?? getString6(entry, "message_id");
7873
+ if (!messageId || knownMessageIds.has(messageId)) {
7874
+ continue;
7875
+ }
7876
+ knownMessageIds.add(messageId);
7877
+ await this.processLogEntry(entry, filePath);
7878
+ }
7879
+ }
7880
+ async processLogEntry(entry, filePath) {
7881
+ const messageType = getString6(entry, "type");
7882
+ const sessionId = resolveSessionId({
7883
+ cwd: await this.readProjectRoot(filePath),
7884
+ projectPath: await this.readProjectRoot(filePath),
7885
+ sessionId: getString6(entry, "sessionId") ?? `${this.name}:session`,
7886
+ tool: this.name
7887
+ });
7888
+ const projectPath = await this.readProjectRoot(filePath);
7889
+ const context = {
7890
+ cwd: projectPath,
7891
+ hookPayload: entry,
7892
+ sessionId,
7893
+ source: "aisnitch://adapters/gemini-cli/log-watch"
7894
+ };
7895
+ const sharedData = {
7896
+ cwd: projectPath,
7897
+ projectPath,
7898
+ raw: entry
7899
+ };
7900
+ await this.ensureObservedSession(sessionId, sharedData, context);
7901
+ switch (messageType) {
7902
+ case "user": {
7903
+ await this.emitStateChange("task.start", sharedData, context);
7904
+ return;
7905
+ }
7906
+ case "model":
7907
+ case "assistant": {
7908
+ await this.emitStateChange("agent.streaming", sharedData, context);
7909
+ return;
7910
+ }
7911
+ case "thinking": {
7912
+ await this.emitStateChange("agent.thinking", sharedData, context);
7913
+ return;
7914
+ }
7915
+ default: {
7916
+ logger.debug({ filePath, messageType }, "Gemini log entry ignored by adapter");
7917
+ }
7918
+ }
7919
+ }
7920
+ async ensureObservedSession(sessionId, data, context) {
7921
+ if (this.currentSessionId === sessionId) {
7922
+ return;
7923
+ }
7924
+ await this.emitStateChange("session.start", data, context);
7925
+ await this.emitStateChange("agent.idle", data, context);
7926
+ }
7927
+ async readProjectRoot(filePath) {
7928
+ if (this.projectRootCache.has(filePath)) {
7929
+ return this.projectRootCache.get(filePath);
7930
+ }
7931
+ const rootPath = (0, import_node_path12.join)((0, import_node_path12.dirname)(filePath), ".project_root");
7932
+ try {
7933
+ const projectRoot = (await (0, import_promises11.readFile)(rootPath, "utf8")).trim();
7934
+ const normalizedRoot = projectRoot.length > 0 ? projectRoot : void 0;
7935
+ this.projectRootCache.set(filePath, normalizedRoot);
7936
+ return normalizedRoot;
7937
+ } catch {
7938
+ this.projectRootCache.set(filePath, void 0);
7939
+ return void 0;
7940
+ }
7941
+ }
7942
+ async seedObservedLogState() {
7943
+ const files = await collectFilesRecursively5(this.logsDirectory, "logs.json");
7944
+ await Promise.all(
7945
+ files.map(async (filePath) => {
7946
+ try {
7947
+ const fileStats = await (0, import_promises11.stat)(filePath);
7948
+ if (!fileStats.isFile()) {
7949
+ return;
7950
+ }
7951
+ const fileContent = await (0, import_promises11.readFile)(filePath, "utf8");
7952
+ const parsedContent = JSON.parse(fileContent);
7953
+ const messageIds = /* @__PURE__ */ new Set();
7954
+ if (Array.isArray(parsedContent)) {
7955
+ for (const entry of parsedContent) {
7956
+ if (!isRecord7(entry)) {
7957
+ continue;
7958
+ }
7959
+ const messageId = getString6(entry, "messageId") ?? getString6(entry, "message_id");
7960
+ if (messageId) {
7961
+ messageIds.add(messageId);
7962
+ }
7963
+ }
7964
+ }
7965
+ this.observedLogMessageIds.set(filePath, messageIds);
7966
+ } catch {
7967
+ }
7968
+ })
7969
+ );
7970
+ }
7971
+ startProcessPolling() {
7972
+ if (this.pollIntervalMs <= 0) {
7973
+ return;
7974
+ }
7975
+ this.processPoller = setInterval(() => {
7976
+ void this.pollGeminiProcesses();
7977
+ }, this.pollIntervalMs);
7978
+ this.processPoller.unref();
7979
+ void this.pollGeminiProcesses();
7980
+ }
7981
+ async pollGeminiProcesses() {
7982
+ const processes = await listProcesses6(this.processListCommand);
7983
+ if (processes.length > 0 && this.getStatus().activeSessions === 0) {
7984
+ const processInfo = processes[0];
7985
+ if (!processInfo) {
7986
+ return;
7987
+ }
7988
+ const sessionId = `gemini-cli-process-${processInfo.pid}`;
7989
+ this.fallbackProcessSessionId = sessionId;
7990
+ await this.emitStateChange(
7991
+ "session.start",
7992
+ {
7993
+ raw: {
7994
+ process: processInfo,
7995
+ source: "process-detect"
7996
+ }
7997
+ },
7998
+ {
7999
+ pid: processInfo.pid,
8000
+ sessionId,
8001
+ source: "aisnitch://adapters/gemini-cli/process-detect"
8002
+ }
8003
+ );
8004
+ return;
8005
+ }
8006
+ if (processes.length === 0 && this.fallbackProcessSessionId !== null) {
8007
+ const sessionId = this.fallbackProcessSessionId;
8008
+ this.fallbackProcessSessionId = null;
8009
+ await this.emitStateChange(
8010
+ "session.end",
8011
+ {
8012
+ raw: {
8013
+ reason: "process-exit",
8014
+ source: "process-detect"
8015
+ }
8016
+ },
8017
+ {
8018
+ sessionId,
8019
+ source: "aisnitch://adapters/gemini-cli/process-detect"
8020
+ }
8021
+ );
8022
+ }
8023
+ }
8024
+ };
8025
+ async function collectFilesRecursively5(directoryPath, fileName) {
8026
+ try {
8027
+ const entries = await (0, import_promises11.readdir)(directoryPath, {
8028
+ withFileTypes: true
8029
+ });
8030
+ const nestedResults = await Promise.all(
8031
+ entries.map(async (entry) => {
8032
+ const entryPath = (0, import_node_path12.join)(directoryPath, entry.name);
8033
+ if (entry.isDirectory()) {
8034
+ return await collectFilesRecursively5(entryPath, fileName);
8035
+ }
8036
+ return entry.name === fileName ? [entryPath] : [];
8037
+ })
8038
+ );
8039
+ return nestedResults.flat();
8040
+ } catch (error) {
8041
+ if (isErrnoException6(error) && error.code === "ENOENT") {
8042
+ return [];
8043
+ }
8044
+ throw error;
8045
+ }
8046
+ }
8047
+ function extractGeminiToolName(payload) {
8048
+ return getString6(payload, "tool_name") ?? getString6(payload, "toolName");
8049
+ }
8050
+ function extractGeminiToolInput(payload) {
8051
+ const toolInput = getRecord5(payload.tool_input) ?? getRecord5(payload.toolInput);
8052
+ if (!toolInput) {
8053
+ return void 0;
8054
+ }
8055
+ const filePath = getString6(toolInput, "file_path") ?? getString6(toolInput, "filePath") ?? getString6(toolInput, "path");
8056
+ const command = getString6(toolInput, "command") ?? getString6(toolInput, "cmd");
8057
+ if (!filePath && !command) {
8058
+ return void 0;
8059
+ }
8060
+ return {
8061
+ command,
8062
+ filePath
8063
+ };
8064
+ }
8065
+ function extractGeminiActiveFile(payload) {
8066
+ const toolInput = extractGeminiToolInput(payload);
8067
+ if (toolInput?.filePath) {
8068
+ return toolInput.filePath;
8069
+ }
8070
+ return getString6(payload, "active_file") ?? getString6(payload, "activeFile") ?? getString6(payload, "file_path");
8071
+ }
8072
+ function extractGeminiErrorMessage(payload) {
8073
+ const toolResponse = getRecord5(payload.tool_response) ?? getRecord5(payload.toolResponse);
8074
+ const llmResponse = getRecord5(payload.llm_response) ?? getRecord5(payload.llmResponse);
8075
+ return getString6(getRecord5(toolResponse?.error), "message") ?? getString6(payload, "reason") ?? getString6(payload, "message") ?? getString6(llmResponse, "error");
8076
+ }
8077
+ function extractGeminiErrorType(payload) {
8078
+ const rawType = getString6(payload, "error_type") ?? getString6(payload, "errorType") ?? getString6(payload, "stopReason");
8079
+ switch (rawType) {
8080
+ case "rate_limit":
8081
+ return "rate_limit";
8082
+ case "max_output_tokens":
8083
+ case "context_overflow":
8084
+ return "context_overflow";
8085
+ case "api_error":
8086
+ case "provider_error":
8087
+ case "invalid_request":
8088
+ return "api_error";
8089
+ case "tool_failure":
8090
+ return "tool_failure";
8091
+ default:
8092
+ return void 0;
8093
+ }
8094
+ }
8095
+ function extractGeminiModel(payload) {
8096
+ return getString6(getRecord5(payload.llm_request), "model") ?? getString6(payload, "model");
8097
+ }
8098
+ function extractGeminiTokens(payload) {
8099
+ const llmResponse = getRecord5(payload.llm_response) ?? getRecord5(payload.llmResponse);
8100
+ const usageMetadata = getRecord5(llmResponse?.usageMetadata);
8101
+ const totalTokens = getNumber4(usageMetadata, "totalTokenCount") ?? getNumber4(usageMetadata, "total_tokens");
8102
+ return totalTokens;
8103
+ }
8104
+ function isGeminiCodingTool(toolName) {
8105
+ return toolName !== void 0 && GEMINI_CODING_TOOLS.has(toolName);
8106
+ }
8107
+ async function listProcesses6(listCommand) {
8108
+ if (process.platform === "win32") {
8109
+ return [];
8110
+ }
8111
+ try {
8112
+ const stdout = await listCommand();
8113
+ return stdout.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).map(parseProcessLine5).filter((processInfo) => processInfo !== null);
8114
+ } catch (error) {
8115
+ const errorCode = isErrnoException6(error) ? String(error.code) : "";
8116
+ if (isErrnoException6(error) && (errorCode === "ENOENT" || errorCode === "1")) {
8117
+ return [];
8118
+ }
8119
+ logger.debug({ error }, "Gemini process detection failed");
8120
+ return [];
8121
+ }
8122
+ }
8123
+ function parseProcessLine5(line) {
8124
+ const match = line.match(/^(\d+)\s+(.+)$/u);
8125
+ if (!match) {
8126
+ return null;
8127
+ }
8128
+ const pidText = match[1];
8129
+ const command = match[2];
8130
+ if (!pidText || !command) {
8131
+ return null;
8132
+ }
8133
+ return {
8134
+ command,
8135
+ pid: Number.parseInt(pidText, 10)
8136
+ };
8137
+ }
8138
+ function isErrnoException6(error) {
8139
+ return error instanceof Error && "code" in error;
8140
+ }
8141
+ function isRecord7(value) {
8142
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8143
+ }
8144
+ function getRecord5(value) {
8145
+ return isRecord7(value) ? value : void 0;
8146
+ }
8147
+ function getNumber4(payload, key) {
8148
+ if (!payload) {
8149
+ return void 0;
8150
+ }
8151
+ const value = payload[key];
8152
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8153
+ }
8154
+ function getString6(payload, key) {
8155
+ if (!payload) {
8156
+ return void 0;
8157
+ }
8158
+ const value = payload[key];
8159
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
8160
+ }
8161
+
8162
+ // src/adapters/goose.ts
8163
+ var import_node_child_process10 = require("child_process");
8164
+ var import_promises12 = require("fs/promises");
8165
+ var import_node_path13 = require("path");
8166
+ var import_node_util10 = require("util");
8167
+ var import_chokidar8 = require("chokidar");
8168
+ var execFile10 = (0, import_node_util10.promisify)(import_node_child_process10.execFile);
8169
+ var DEFAULT_GOOSED_BASE_URL = "http://127.0.0.1:8080";
8170
+ var DEFAULT_SQLITE_QUERY = [
8171
+ "SELECT",
8172
+ " id,",
8173
+ " name,",
8174
+ " working_dir,",
8175
+ " updated_at,",
8176
+ " message_count,",
8177
+ " provider_name,",
8178
+ " accumulated_total_tokens,",
8179
+ " total_tokens,",
8180
+ " model_config",
8181
+ "FROM sessions",
8182
+ "ORDER BY updated_at DESC",
8183
+ "LIMIT 24;"
8184
+ ].join("\n");
8185
+ var MAX_STREAMED_SESSIONS = 6;
8186
+ var GOOSE_CODING_TOOL_HINT = /apply|create|delete|edit|move|patch|rename|replace|write/iu;
8187
+ var GOOSE_RATE_LIMIT_HINT = /credit|quota|rate limit|exhausted/iu;
8188
+ var GooseAdapter = class extends BaseAdapter {
8189
+ displayName = "Goose";
8190
+ name = "goose";
8191
+ strategies = [
8192
+ "api-client",
8193
+ "sqlite-watch",
8194
+ "process-detect"
8195
+ ];
8196
+ apiPoller = null;
8197
+ apiBaseUrl;
8198
+ apiKey;
8199
+ databasePath;
8200
+ databaseWatcher = null;
8201
+ fallbackProcessSessionId = null;
8202
+ fetchImplementation;
8203
+ apiDiscoverySeeded = false;
8204
+ observedSessions = /* @__PURE__ */ new Set();
8205
+ pollIntervalMs;
8206
+ processPoller = null;
8207
+ processListCommand;
8208
+ sessionEventFingerprints = /* @__PURE__ */ new Map();
8209
+ sessionSnapshots = /* @__PURE__ */ new Map();
8210
+ sessionStreams = /* @__PURE__ */ new Map();
8211
+ sqliteQueryCommand;
8212
+ watcherFactory;
8213
+ constructor(options) {
8214
+ super(options);
8215
+ this.apiBaseUrl = options.apiBaseUrl ?? options.env?.AISNITCH_GOOSE_API_BASE_URL ?? DEFAULT_GOOSED_BASE_URL;
8216
+ this.apiKey = options.apiKey ?? options.env?.AISNITCH_GOOSE_API_KEY ?? options.env?.GOOSE_API_KEY;
8217
+ this.databasePath = options.databasePath ?? (0, import_node_path13.join)(this.getUserHomeDirectory(), ".config", "goose", "sessions.db");
8218
+ this.fetchImplementation = options.fetchImplementation ?? fetch;
8219
+ this.pollIntervalMs = options.pollIntervalMs ?? 5e3;
8220
+ this.processListCommand = options.processListCommand ?? (async () => await execFile10("pgrep", ["-lf", "goose|goosed"]).then(
8221
+ (result) => result.stdout
8222
+ ));
8223
+ this.sqliteQueryCommand = options.sqliteQueryCommand ?? (async (databasePath, query) => await execFile10("sqlite3", ["-json", databasePath, query]).then(
8224
+ (result) => result.stdout
8225
+ ));
8226
+ this.watcherFactory = options.watcherFactory ?? import_chokidar8.watch;
8227
+ }
8228
+ async start() {
8229
+ if (this.getStatus().running) {
8230
+ return;
8231
+ }
8232
+ this.setRunning(true);
8233
+ await this.seedSqliteSessions();
8234
+ this.databaseWatcher = this.watcherFactory(this.databasePath, {
8235
+ awaitWriteFinish: {
8236
+ stabilityThreshold: 200
8237
+ },
8238
+ ignoreInitial: true
8239
+ });
8240
+ this.databaseWatcher.on("add", () => {
8241
+ void this.pollSqliteSessions(true);
8242
+ });
8243
+ this.databaseWatcher.on("change", () => {
8244
+ void this.pollSqliteSessions(true);
8245
+ });
8246
+ this.databaseWatcher.on("error", (error) => {
8247
+ logger.warn({ error }, "Goose SQLite watcher error");
8248
+ });
8249
+ this.startApiPolling();
8250
+ this.startProcessPolling();
8251
+ }
8252
+ async stop() {
8253
+ if (this.databaseWatcher !== null) {
8254
+ await this.databaseWatcher.close();
8255
+ this.databaseWatcher = null;
8256
+ }
8257
+ if (this.apiPoller !== null) {
8258
+ clearInterval(this.apiPoller);
8259
+ this.apiPoller = null;
8260
+ }
8261
+ if (this.processPoller !== null) {
8262
+ clearInterval(this.processPoller);
8263
+ this.processPoller = null;
8264
+ }
8265
+ for (const streamHandle of this.sessionStreams.values()) {
8266
+ streamHandle.abortController.abort();
8267
+ void streamHandle.promise.catch(() => void 0);
8268
+ }
8269
+ this.apiDiscoverySeeded = false;
8270
+ this.fallbackProcessSessionId = null;
8271
+ this.observedSessions.clear();
8272
+ this.sessionEventFingerprints.clear();
8273
+ this.sessionSnapshots.clear();
8274
+ this.sessionStreams.clear();
8275
+ this.setRunning(false);
8276
+ }
8277
+ async handleHook(payload) {
8278
+ const normalizedPayload = this.parseNormalizedHookPayload(payload);
8279
+ if (normalizedPayload === null) {
8280
+ logger.debug({ payload }, "Goose ignores non-normalized hook payloads");
8281
+ return;
8282
+ }
8283
+ await this.emitNormalizedPayload({
8284
+ ...normalizedPayload,
8285
+ sessionId: resolveSessionId({
8286
+ activeFile: normalizedPayload.data?.activeFile,
8287
+ cwd: normalizedPayload.data?.cwd ?? normalizedPayload.cwd,
8288
+ pid: normalizedPayload.pid,
8289
+ projectPath: normalizedPayload.data?.projectPath,
8290
+ sessionId: normalizedPayload.sessionId,
8291
+ tool: this.name,
8292
+ transcriptPath: normalizedPayload.transcriptPath
8293
+ })
8294
+ });
8295
+ }
8296
+ startApiPolling() {
8297
+ if (this.pollIntervalMs <= 0) {
8298
+ return;
8299
+ }
8300
+ this.apiPoller = setInterval(() => {
8301
+ void this.pollGooseApi(true);
8302
+ }, this.pollIntervalMs);
8303
+ this.apiPoller.unref();
8304
+ void this.pollGooseApi(false);
8305
+ }
8306
+ startProcessPolling() {
8307
+ if (this.pollIntervalMs <= 0) {
8308
+ return;
8309
+ }
8310
+ this.processPoller = setInterval(() => {
8311
+ void this.pollGooseProcesses();
8312
+ }, this.pollIntervalMs);
8313
+ this.processPoller.unref();
8314
+ void this.pollGooseProcesses();
8315
+ }
8316
+ async seedSqliteSessions() {
8317
+ try {
8318
+ await (0, import_promises12.stat)(this.databasePath);
8319
+ } catch {
8320
+ return;
8321
+ }
8322
+ const sessions = await this.readSqliteSessions();
8323
+ this.syncSessionSnapshots(sessions, false, "sqlite-seed");
8324
+ }
8325
+ async pollSqliteSessions(emitChanges) {
8326
+ const sessions = await this.readSqliteSessions();
8327
+ this.syncSessionSnapshots(sessions, emitChanges, "sqlite");
8328
+ }
8329
+ async readSqliteSessions() {
8330
+ let rawOutput;
8331
+ try {
8332
+ rawOutput = await this.sqliteQueryCommand(
8333
+ this.databasePath,
8334
+ DEFAULT_SQLITE_QUERY
8335
+ );
8336
+ } catch (error) {
8337
+ logger.debug({ error }, "Goose SQLite session query skipped");
8338
+ return [];
8339
+ }
8340
+ let parsedOutput;
8341
+ try {
8342
+ parsedOutput = JSON.parse(rawOutput);
8343
+ } catch (error) {
8344
+ logger.warn({ error }, "Goose SQLite JSON output is invalid");
8345
+ return [];
8346
+ }
8347
+ if (!Array.isArray(parsedOutput)) {
8348
+ return [];
8349
+ }
8350
+ return parsedOutput.map((entry) => normalizeGooseSession(entry)).filter((entry) => entry !== null);
8351
+ }
8352
+ async pollGooseApi(emitChanges) {
8353
+ let statusResponse;
8354
+ try {
8355
+ statusResponse = await this.fetchImplementation(
8356
+ new URL("/status", this.apiBaseUrl)
8357
+ );
8358
+ } catch (error) {
8359
+ logger.debug({ error }, "Goosed status endpoint is unavailable");
8360
+ return;
8361
+ }
8362
+ if (!statusResponse.ok) {
8363
+ return;
8364
+ }
8365
+ let sessionsResponse;
8366
+ try {
8367
+ sessionsResponse = await this.fetchImplementation(
8368
+ new URL("/sessions", this.apiBaseUrl),
8369
+ {
8370
+ headers: this.createApiHeaders()
8371
+ }
8372
+ );
8373
+ } catch (error) {
8374
+ logger.debug({ error }, "Goosed sessions endpoint request failed");
8375
+ return;
8376
+ }
8377
+ if (!sessionsResponse.ok) {
8378
+ logger.debug(
8379
+ { status: sessionsResponse.status },
8380
+ "Goosed sessions endpoint rejected the request"
8381
+ );
8382
+ return;
8383
+ }
8384
+ let parsedPayload;
8385
+ try {
8386
+ parsedPayload = await sessionsResponse.json();
8387
+ } catch (error) {
8388
+ logger.warn({ error }, "Goosed sessions payload is not valid JSON");
8389
+ return;
8390
+ }
8391
+ const responsePayload = parsedPayload;
8392
+ const rawSessions = Array.isArray(responsePayload.sessions) ? responsePayload.sessions : [];
8393
+ const sessions = rawSessions.map((entry) => normalizeGooseSession(entry)).filter((entry) => entry !== null);
8394
+ this.syncSessionSnapshots(
8395
+ sessions,
8396
+ emitChanges && this.apiDiscoverySeeded,
8397
+ "api"
8398
+ );
8399
+ this.apiDiscoverySeeded = true;
8400
+ }
8401
+ syncSessionSnapshots(nextSnapshots, emitChanges, source) {
8402
+ for (const snapshot of nextSnapshots) {
8403
+ const previousSnapshot = this.sessionSnapshots.get(snapshot.sessionId);
8404
+ this.sessionSnapshots.set(snapshot.sessionId, snapshot);
8405
+ this.ensureTrackedSessionStream(snapshot);
8406
+ if (!emitChanges) {
8407
+ continue;
8408
+ }
8409
+ if (previousSnapshot === void 0) {
8410
+ void this.ensureObservedSession(snapshot, source);
8411
+ continue;
8412
+ }
8413
+ if (!didGooseSessionAdvance(previousSnapshot, snapshot)) {
8414
+ continue;
8415
+ }
8416
+ void this.ensureObservedSession(snapshot, source).then(async () => {
8417
+ await this.emitStateChange(
8418
+ "agent.streaming",
8419
+ buildGooseSessionData(snapshot, {
8420
+ raw: {
8421
+ snapshot,
8422
+ source
8423
+ }
8424
+ }),
8425
+ this.createSessionContext(snapshot, `aisnitch://adapters/goose/${source}`)
8426
+ );
8427
+ });
8428
+ }
8429
+ this.trimTrackedSessionStreams(nextSnapshots);
8430
+ }
8431
+ ensureTrackedSessionStream(snapshot) {
8432
+ if (this.sessionStreams.has(snapshot.sessionId)) {
8433
+ return;
8434
+ }
8435
+ const abortController = new AbortController();
8436
+ const streamPromise = this.consumeSessionEvents(snapshot, abortController).finally(
8437
+ () => {
8438
+ const activeStream = this.sessionStreams.get(snapshot.sessionId);
8439
+ if (activeStream?.abortController === abortController) {
8440
+ this.sessionStreams.delete(snapshot.sessionId);
8441
+ }
8442
+ }
8443
+ );
8444
+ this.sessionStreams.set(snapshot.sessionId, {
8445
+ abortController,
8446
+ promise: streamPromise
8447
+ });
8448
+ }
8449
+ trimTrackedSessionStreams(nextSnapshots) {
8450
+ const keepSessionIds = new Set(
8451
+ [...nextSnapshots].sort(compareGooseSessionsByRecency).slice(0, MAX_STREAMED_SESSIONS).map((snapshot) => snapshot.sessionId)
8452
+ );
8453
+ for (const [sessionId, streamHandle] of this.sessionStreams) {
8454
+ if (keepSessionIds.has(sessionId)) {
8455
+ continue;
8456
+ }
8457
+ streamHandle.abortController.abort();
8458
+ this.sessionStreams.delete(sessionId);
8459
+ }
8460
+ }
8461
+ async consumeSessionEvents(snapshot, abortController) {
8462
+ let response;
8463
+ try {
8464
+ response = await this.fetchImplementation(
8465
+ new URL(
8466
+ `/sessions/${encodeURIComponent(snapshot.gooseSessionId)}/events`,
8467
+ this.apiBaseUrl
8468
+ ),
8469
+ {
8470
+ headers: {
8471
+ ...this.createApiHeaders(),
8472
+ Accept: "text/event-stream"
8473
+ },
8474
+ signal: abortController.signal
8475
+ }
8476
+ );
8477
+ } catch (error) {
8478
+ if (abortController.signal.aborted) {
8479
+ return;
8480
+ }
7519
8481
  logger.debug({ error, sessionId: snapshot.sessionId }, "Goose SSE request failed");
7520
8482
  return;
7521
8483
  }
7522
- if (!response.ok || response.body === null) {
7523
- logger.debug(
7524
- { sessionId: snapshot.sessionId, status: response.status },
7525
- "Goose SSE stream is unavailable"
7526
- );
8484
+ if (!response.ok || response.body === null) {
8485
+ logger.debug(
8486
+ { sessionId: snapshot.sessionId, status: response.status },
8487
+ "Goose SSE stream is unavailable"
8488
+ );
8489
+ return;
8490
+ }
8491
+ const reader = response.body.getReader();
8492
+ const decoder = new TextDecoder();
8493
+ let buffer = "";
8494
+ try {
8495
+ while (true) {
8496
+ const readResult = await reader.read();
8497
+ if (readResult.done) {
8498
+ break;
8499
+ }
8500
+ buffer += decoder.decode(readResult.value, { stream: true });
8501
+ while (true) {
8502
+ const delimiterIndex = buffer.indexOf("\n\n");
8503
+ if (delimiterIndex === -1) {
8504
+ break;
8505
+ }
8506
+ const frame = buffer.slice(0, delimiterIndex);
8507
+ buffer = buffer.slice(delimiterIndex + 2);
8508
+ await this.processSSEFrame(snapshot, frame);
8509
+ }
8510
+ }
8511
+ } catch (error) {
8512
+ if (!abortController.signal.aborted) {
8513
+ logger.debug(
8514
+ { error, sessionId: snapshot.sessionId },
8515
+ "Goose SSE stream closed unexpectedly"
8516
+ );
8517
+ }
8518
+ } finally {
8519
+ reader.releaseLock();
8520
+ }
8521
+ }
8522
+ async processSSEFrame(snapshot, frame) {
8523
+ const payloadText = frame.split(/\r?\n/u).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
8524
+ if (payloadText.length === 0) {
8525
+ return;
8526
+ }
8527
+ let parsedPayload;
8528
+ try {
8529
+ parsedPayload = JSON.parse(payloadText);
8530
+ } catch (error) {
8531
+ logger.warn({ error }, "Goose SSE frame is not valid JSON");
8532
+ return;
8533
+ }
8534
+ await this.processSessionEvent(snapshot, parsedPayload);
8535
+ }
8536
+ async processSessionEvent(snapshot, payload) {
8537
+ if (!isRecord8(payload)) {
8538
+ return;
8539
+ }
8540
+ const fingerprint = createGooseFingerprint(payload);
8541
+ if (fingerprint && !this.markSessionEventSeen(snapshot.sessionId, fingerprint)) {
8542
+ return;
8543
+ }
8544
+ const eventType = getString7(payload, "type");
8545
+ switch (eventType) {
8546
+ case "Message": {
8547
+ await this.processGooseMessage(snapshot, payload);
8548
+ return;
8549
+ }
8550
+ case "Finish": {
8551
+ await this.ensureObservedSession(snapshot, "sse");
8552
+ await this.emitStateChange(
8553
+ "task.complete",
8554
+ buildGooseSessionData(snapshot, {
8555
+ raw: payload,
8556
+ tokensUsed: extractGooseTokenCount(getRecord6(payload.token_state))
8557
+ }),
8558
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8559
+ );
8560
+ return;
8561
+ }
8562
+ case "Error": {
8563
+ const errorMessage = getString7(payload, "error");
8564
+ await this.ensureObservedSession(snapshot, "sse");
8565
+ await this.emitStateChange(
8566
+ "agent.error",
8567
+ buildGooseSessionData(snapshot, {
8568
+ errorMessage,
8569
+ errorType: inferGooseErrorType(errorMessage),
8570
+ raw: payload
8571
+ }),
8572
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8573
+ );
8574
+ return;
8575
+ }
8576
+ case "Notification": {
8577
+ await this.ensureObservedSession(snapshot, "sse");
8578
+ await this.emitStateChange(
8579
+ "agent.asking_user",
8580
+ buildGooseSessionData(snapshot, {
8581
+ errorMessage: extractLooseString2(payload, ["message"]),
8582
+ raw: payload
8583
+ }),
8584
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8585
+ );
8586
+ return;
8587
+ }
8588
+ default:
8589
+ return;
8590
+ }
8591
+ }
8592
+ async processGooseMessage(snapshot, payload) {
8593
+ const message = getRecord6(payload.message);
8594
+ if (!message) {
8595
+ return;
8596
+ }
8597
+ const role = getString7(message, "role");
8598
+ const content = getRecordArray(message.content);
8599
+ const tokenState = getRecord6(payload.token_state);
8600
+ const tokensUsed = extractGooseTokenCount(tokenState);
8601
+ await this.ensureObservedSession(snapshot, "sse");
8602
+ if (role === "user") {
8603
+ const promptText = content.filter((part) => getString7(part, "type") === "text").map((part) => getString7(part, "text")).filter((part) => typeof part === "string").join("\n").trim();
8604
+ await this.emitStateChange(
8605
+ "task.start",
8606
+ buildGooseSessionData(snapshot, {
8607
+ raw: {
8608
+ message,
8609
+ prompt: promptText,
8610
+ tokenState
8611
+ },
8612
+ tokensUsed
8613
+ }),
8614
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8615
+ );
8616
+ return;
8617
+ }
8618
+ if (role !== "assistant") {
8619
+ return;
8620
+ }
8621
+ for (const part of content) {
8622
+ const partType = getString7(part, "type");
8623
+ switch (partType) {
8624
+ case "thinking": {
8625
+ await this.emitStateChange(
8626
+ "agent.thinking",
8627
+ buildGooseSessionData(snapshot, {
8628
+ raw: {
8629
+ message: {
8630
+ content: [part],
8631
+ role
8632
+ },
8633
+ tokenState
8634
+ },
8635
+ tokensUsed
8636
+ }),
8637
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8638
+ );
8639
+ break;
8640
+ }
8641
+ case "redactedThinking": {
8642
+ await this.emitStateChange(
8643
+ "agent.thinking",
8644
+ buildGooseSessionData(snapshot, {
8645
+ raw: {
8646
+ message: {
8647
+ content: [
8648
+ {
8649
+ thinking: getString7(part, "data"),
8650
+ type: "thinking"
8651
+ }
8652
+ ],
8653
+ role
8654
+ },
8655
+ tokenState
8656
+ },
8657
+ tokensUsed
8658
+ }),
8659
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8660
+ );
8661
+ break;
8662
+ }
8663
+ case "text": {
8664
+ await this.emitStateChange(
8665
+ "agent.streaming",
8666
+ buildGooseSessionData(snapshot, {
8667
+ raw: {
8668
+ message: {
8669
+ content: [part],
8670
+ role
8671
+ },
8672
+ tokenState
8673
+ },
8674
+ tokensUsed
8675
+ }),
8676
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8677
+ );
8678
+ break;
8679
+ }
8680
+ case "toolRequest": {
8681
+ const toolName = extractGooseToolName(part);
8682
+ const toolInput = extractGooseToolInput(part);
8683
+ const activeFile = toolInput?.filePath;
8684
+ const emittedType = isGooseCodingTool(toolName, toolInput) ? "agent.coding" : "agent.tool_call";
8685
+ await this.emitStateChange(
8686
+ emittedType,
8687
+ buildGooseSessionData(snapshot, {
8688
+ activeFile,
8689
+ raw: {
8690
+ message: {
8691
+ content: [part],
8692
+ role
8693
+ },
8694
+ tokenState
8695
+ },
8696
+ toolInput,
8697
+ toolName,
8698
+ tokensUsed
8699
+ }),
8700
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8701
+ );
8702
+ break;
8703
+ }
8704
+ case "toolConfirmationRequest":
8705
+ case "actionRequired": {
8706
+ await this.emitStateChange(
8707
+ "agent.asking_user",
8708
+ buildGooseSessionData(snapshot, {
8709
+ errorMessage: getString7(part, "prompt") ?? extractLooseString2(getRecord6(part.data), ["message"]),
8710
+ raw: {
8711
+ message: {
8712
+ content: [part],
8713
+ role
8714
+ },
8715
+ tokenState
8716
+ },
8717
+ toolInput: extractGooseToolInput(part),
8718
+ toolName: extractGooseToolName(part),
8719
+ tokensUsed
8720
+ }),
8721
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8722
+ );
8723
+ break;
8724
+ }
8725
+ case "systemNotification": {
8726
+ const notificationType = getString7(part, "notificationType");
8727
+ const eventTypeToEmit = notificationType === "thinkingMessage" ? "agent.thinking" : "agent.asking_user";
8728
+ await this.emitStateChange(
8729
+ eventTypeToEmit,
8730
+ buildGooseSessionData(snapshot, {
8731
+ errorMessage: getString7(part, "msg"),
8732
+ errorType: notificationType === "creditsExhausted" ? "rate_limit" : void 0,
8733
+ raw: {
8734
+ message: notificationType === "thinkingMessage" ? {
8735
+ content: [
8736
+ {
8737
+ thinking: getString7(part, "msg"),
8738
+ type: "thinking"
8739
+ }
8740
+ ],
8741
+ role
8742
+ } : {
8743
+ content: [part],
8744
+ role
8745
+ },
8746
+ tokenState
8747
+ },
8748
+ tokensUsed
8749
+ }),
8750
+ this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
8751
+ );
8752
+ break;
8753
+ }
8754
+ default:
8755
+ break;
8756
+ }
8757
+ }
8758
+ }
8759
+ async ensureObservedSession(snapshot, source) {
8760
+ if (this.observedSessions.has(snapshot.sessionId)) {
8761
+ return;
8762
+ }
8763
+ this.observedSessions.add(snapshot.sessionId);
8764
+ const context = this.createSessionContext(
8765
+ snapshot,
8766
+ `aisnitch://adapters/goose/${source}`
8767
+ );
8768
+ const eventData = buildGooseSessionData(snapshot, {
8769
+ raw: {
8770
+ snapshot,
8771
+ source
8772
+ }
8773
+ });
8774
+ await this.emitStateChange("session.start", eventData, context);
8775
+ await this.emitStateChange("agent.idle", eventData, context);
8776
+ }
8777
+ createSessionContext(snapshot, source) {
8778
+ return {
8779
+ cwd: snapshot.workingDir,
8780
+ sessionId: snapshot.sessionId,
8781
+ source
8782
+ };
8783
+ }
8784
+ markSessionEventSeen(sessionId, fingerprint) {
8785
+ const knownFingerprints = this.sessionEventFingerprints.get(sessionId) ?? /* @__PURE__ */ new Set();
8786
+ if (knownFingerprints.has(fingerprint)) {
8787
+ return false;
8788
+ }
8789
+ if (knownFingerprints.size >= 256) {
8790
+ knownFingerprints.clear();
8791
+ }
8792
+ knownFingerprints.add(fingerprint);
8793
+ this.sessionEventFingerprints.set(sessionId, knownFingerprints);
8794
+ return true;
8795
+ }
8796
+ createApiHeaders() {
8797
+ if (!this.apiKey) {
8798
+ return void 0;
8799
+ }
8800
+ return {
8801
+ Authorization: `Bearer ${this.apiKey}`
8802
+ };
8803
+ }
8804
+ async pollGooseProcesses() {
8805
+ const processes = await listProcesses7(this.processListCommand);
8806
+ if (processes.length > 0 && this.getStatus().activeSessions === 0) {
8807
+ const processInfo = processes[0];
8808
+ if (!processInfo) {
8809
+ return;
8810
+ }
8811
+ const sessionId = `goose-process-${processInfo.pid}`;
8812
+ this.fallbackProcessSessionId = sessionId;
8813
+ await this.emitStateChange(
8814
+ "session.start",
8815
+ {
8816
+ raw: {
8817
+ process: processInfo,
8818
+ source: "process-detect"
8819
+ }
8820
+ },
8821
+ {
8822
+ pid: processInfo.pid,
8823
+ sessionId,
8824
+ source: "aisnitch://adapters/goose/process-detect"
8825
+ }
8826
+ );
8827
+ return;
8828
+ }
8829
+ if (processes.length === 0 && this.fallbackProcessSessionId !== null) {
8830
+ const sessionId = this.fallbackProcessSessionId;
8831
+ this.fallbackProcessSessionId = null;
8832
+ await this.emitStateChange(
8833
+ "session.end",
8834
+ {
8835
+ raw: {
8836
+ reason: "process-exit",
8837
+ source: "process-detect"
8838
+ }
8839
+ },
8840
+ {
8841
+ sessionId,
8842
+ source: "aisnitch://adapters/goose/process-detect"
8843
+ }
8844
+ );
8845
+ }
8846
+ }
8847
+ };
8848
+ function normalizeGooseSession(payload) {
8849
+ if (!isRecord8(payload)) {
8850
+ return null;
8851
+ }
8852
+ const gooseSessionId = getString7(payload, "id");
8853
+ if (!gooseSessionId) {
8854
+ return null;
8855
+ }
8856
+ const workingDir = getString7(payload, "working_dir") ?? getString7(payload, "workingDir");
8857
+ const name = getString7(payload, "name");
8858
+ const providerName = getString7(payload, "provider_name") ?? getString7(payload, "providerName");
8859
+ const updatedAt = getString7(payload, "updated_at") ?? getString7(payload, "updatedAt");
8860
+ const messageCount = getNumber5(payload, "message_count") ?? getNumber5(payload, "messageCount");
8861
+ const totalTokens = getNumber5(payload, "accumulated_total_tokens") ?? getNumber5(payload, "total_tokens") ?? getNumber5(payload, "accumulatedTotalTokens") ?? getNumber5(payload, "totalTokens");
8862
+ const modelConfig = getRecord6(payload.model_config) ?? getRecord6(payload.modelConfig);
8863
+ const rawModelConfig = getString7(payload, "model_config");
8864
+ const parsedModelConfig = modelConfig ?? parseJsonRecord2(rawModelConfig);
8865
+ const model = getString7(parsedModelConfig, "model_name") ?? getString7(parsedModelConfig, "modelName");
8866
+ const sessionId = resolveSessionId({
8867
+ cwd: workingDir,
8868
+ projectPath: workingDir,
8869
+ sessionId: gooseSessionId,
8870
+ tool: "goose"
8871
+ });
8872
+ return {
8873
+ gooseSessionId,
8874
+ messageCount,
8875
+ model,
8876
+ name,
8877
+ providerName,
8878
+ sessionId,
8879
+ totalTokens,
8880
+ updatedAt,
8881
+ workingDir
8882
+ };
8883
+ }
8884
+ function buildGooseSessionData(snapshot, overrides = {}) {
8885
+ return {
8886
+ cwd: overrides.cwd ?? snapshot.workingDir,
8887
+ model: overrides.model ?? snapshot.model,
8888
+ project: overrides.project ?? snapshot.name,
8889
+ projectPath: overrides.projectPath ?? snapshot.workingDir,
8890
+ raw: overrides.raw,
8891
+ tokensUsed: overrides.tokensUsed ?? snapshot.totalTokens,
8892
+ ...overrides
8893
+ };
8894
+ }
8895
+ function compareGooseSessionsByRecency(left, right) {
8896
+ return Date.parse(right.updatedAt ?? "") - Date.parse(left.updatedAt ?? "");
8897
+ }
8898
+ function didGooseSessionAdvance(previousSnapshot, nextSnapshot) {
8899
+ return previousSnapshot.updatedAt !== nextSnapshot.updatedAt || previousSnapshot.messageCount !== nextSnapshot.messageCount || previousSnapshot.totalTokens !== nextSnapshot.totalTokens;
8900
+ }
8901
+ function createGooseFingerprint(payload) {
8902
+ const eventType = getString7(payload, "type");
8903
+ if (!eventType) {
8904
+ return null;
8905
+ }
8906
+ if (eventType === "Message") {
8907
+ const message = getRecord6(payload.message);
8908
+ const messageId = getString7(message, "id");
8909
+ const created = getNumber5(message, "created");
8910
+ const role = getString7(message, "role");
8911
+ return [eventType, messageId, created, role].filter((value) => value !== void 0).join(":");
8912
+ }
8913
+ if (eventType === "Finish") {
8914
+ return `${eventType}:${getString7(payload, "reason") ?? "unknown"}`;
8915
+ }
8916
+ if (eventType === "Error") {
8917
+ return `${eventType}:${getString7(payload, "error") ?? "unknown"}`;
8918
+ }
8919
+ return eventType;
8920
+ }
8921
+ function extractGooseTokenCount(tokenState) {
8922
+ return getNumber5(tokenState, "accumulatedTotalTokens") ?? getNumber5(tokenState, "totalTokens");
8923
+ }
8924
+ function extractGooseToolName(payload) {
8925
+ const toolCall = getRecord6(payload.toolCall);
8926
+ return getString7(toolCall, "name") ?? getString7(toolCall, "toolName") ?? getString7(payload, "toolName");
8927
+ }
8928
+ function extractGooseToolInput(payload) {
8929
+ const toolCall = getRecord6(payload.toolCall);
8930
+ const argumentsRecord = getRecord6(toolCall?.arguments) ?? getRecord6(toolCall?.args) ?? getRecord6(toolCall?.input) ?? getRecord6(payload.arguments);
8931
+ const filePath = extractFirstString2(argumentsRecord, [
8932
+ "file",
8933
+ "file_path",
8934
+ "filePath",
8935
+ "path",
8936
+ "target",
8937
+ "target_path",
8938
+ "targetPath"
8939
+ ]);
8940
+ const command = extractFirstString2(argumentsRecord, [
8941
+ "cmd",
8942
+ "command",
8943
+ "script"
8944
+ ]);
8945
+ if (!filePath && !command) {
8946
+ return void 0;
8947
+ }
8948
+ return {
8949
+ command,
8950
+ filePath
8951
+ };
8952
+ }
8953
+ function isGooseCodingTool(toolName, toolInput) {
8954
+ if (toolName && GOOSE_CODING_TOOL_HINT.test(toolName)) {
8955
+ return true;
8956
+ }
8957
+ const filePath = toolInput?.filePath;
8958
+ if (!filePath) {
8959
+ return false;
8960
+ }
8961
+ return !/read|view|glob|grep|list|search/iu.test(toolName ?? "");
8962
+ }
8963
+ function inferGooseErrorType(errorMessage) {
8964
+ if (!errorMessage) {
8965
+ return void 0;
8966
+ }
8967
+ if (GOOSE_RATE_LIMIT_HINT.test(errorMessage)) {
8968
+ return "rate_limit";
8969
+ }
8970
+ if (/context|token limit|too long/iu.test(errorMessage)) {
8971
+ return "context_overflow";
8972
+ }
8973
+ if (/tool/iu.test(errorMessage)) {
8974
+ return "tool_failure";
8975
+ }
8976
+ return "api_error";
8977
+ }
8978
+ async function listProcesses7(processListCommand) {
8979
+ try {
8980
+ const commandOutput = await processListCommand();
8981
+ return commandOutput.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
8982
+ const [pidPart, ...commandParts] = line.split(/\s+/u);
8983
+ const pid = pidPart ? Number.parseInt(pidPart, 10) : Number.NaN;
8984
+ return {
8985
+ command: commandParts.join(" "),
8986
+ pid
8987
+ };
8988
+ }).filter((processInfo) => Number.isInteger(processInfo.pid));
8989
+ } catch (error) {
8990
+ logger.debug({ error }, "Goose process listing skipped");
8991
+ return [];
8992
+ }
8993
+ }
8994
+ function parseJsonRecord2(value) {
8995
+ if (!value) {
8996
+ return void 0;
8997
+ }
8998
+ try {
8999
+ const parsedValue = JSON.parse(value);
9000
+ return getRecord6(parsedValue);
9001
+ } catch {
9002
+ return void 0;
9003
+ }
9004
+ }
9005
+ function extractFirstString2(payload, keys) {
9006
+ if (!payload) {
9007
+ return void 0;
9008
+ }
9009
+ for (const key of keys) {
9010
+ const directValue = getString7(payload, key);
9011
+ if (directValue) {
9012
+ return directValue;
9013
+ }
9014
+ }
9015
+ return void 0;
9016
+ }
9017
+ function extractLooseString2(payload, keys) {
9018
+ if (!payload) {
9019
+ return void 0;
9020
+ }
9021
+ for (const key of keys) {
9022
+ const directValue = getString7(payload, key);
9023
+ if (directValue) {
9024
+ return directValue;
9025
+ }
9026
+ const nestedValue = getString7(getRecord6(payload[key]), "message");
9027
+ if (nestedValue) {
9028
+ return nestedValue;
9029
+ }
9030
+ }
9031
+ return void 0;
9032
+ }
9033
+ function getRecordArray(value) {
9034
+ if (!Array.isArray(value)) {
9035
+ return [];
9036
+ }
9037
+ return value.filter((entry) => isRecord8(entry));
9038
+ }
9039
+ function getRecord6(value) {
9040
+ return isRecord8(value) ? value : void 0;
9041
+ }
9042
+ function isRecord8(value) {
9043
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9044
+ }
9045
+ function getString7(payload, key) {
9046
+ if (!payload) {
9047
+ return void 0;
9048
+ }
9049
+ const value = payload[key];
9050
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
9051
+ }
9052
+ function getNumber5(payload, key) {
9053
+ if (!payload) {
9054
+ return void 0;
9055
+ }
9056
+ const value = payload[key];
9057
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
9058
+ }
9059
+
9060
+ // src/adapters/kilo.ts
9061
+ var import_node_child_process11 = require("child_process");
9062
+ var import_promises13 = require("fs/promises");
9063
+ var import_node_path14 = require("path");
9064
+ var import_node_util11 = require("util");
9065
+ var import_chokidar9 = require("chokidar");
9066
+ var execFile11 = (0, import_node_util11.promisify)(import_node_child_process11.execFile);
9067
+ var KILO_CODING_TOOLS = /* @__PURE__ */ new Set([
9068
+ "Edit",
9069
+ "Write",
9070
+ "MultiEdit",
9071
+ "Create",
9072
+ "Delete",
9073
+ "Move",
9074
+ "ApplyDiff"
9075
+ ]);
9076
+ var KiloAdapter = class extends BaseAdapter {
9077
+ displayName = "Kilo CLI";
9078
+ name = "kilo";
9079
+ strategies = [
9080
+ "process-detect",
9081
+ "jsonl-watch",
9082
+ "hooks"
9083
+ ];
9084
+ fallbackProcessSessionId = null;
9085
+ configDirectory;
9086
+ dataDirectory;
9087
+ pollIntervalMs;
9088
+ processPoller = null;
9089
+ processListCommand;
9090
+ sessionMetadata = /* @__PURE__ */ new Map();
9091
+ transcriptOffsets = /* @__PURE__ */ new Map();
9092
+ transcriptRemainders = /* @__PURE__ */ new Map();
9093
+ watcher = null;
9094
+ watcherFactory;
9095
+ constructor(options) {
9096
+ super(options);
9097
+ this.pollIntervalMs = options.pollIntervalMs ?? 5e3;
9098
+ this.processListCommand = options.processListCommand ?? (async () => {
9099
+ const results = await Promise.all([
9100
+ execFile11("pgrep", ["-lf", "kilo"]).catch(() => ({ stdout: "" })),
9101
+ execFile11("pgrep", ["-lf", "kilocode"]).catch(() => ({ stdout: "" })),
9102
+ execFile11("pgrep", ["-lf", "kilocode"]).catch(() => ({ stdout: "" }))
9103
+ ]);
9104
+ return results.map((r) => r.stdout).join("\n");
9105
+ });
9106
+ this.configDirectory = options.configDirectory ?? (0, import_node_path14.join)(this.getUserHomeDirectory(), ".config", "kilo");
9107
+ this.dataDirectory = options.dataDirectory ?? (0, import_node_path14.join)(this.getUserHomeDirectory(), ".local", "share", "kilo");
9108
+ this.watcherFactory = options.watcherFactory ?? import_chokidar9.watch;
9109
+ }
9110
+ async start() {
9111
+ if (this.getStatus().running) {
9112
+ return;
9113
+ }
9114
+ this.setRunning(true);
9115
+ await this.seedTranscriptOffsets();
9116
+ const _logPatterns = [
9117
+ (0, import_node_path14.join)(this.dataDirectory, "**", "*.jsonl"),
9118
+ (0, import_node_path14.join)(this.configDirectory, "logs", "**", "*.jsonl")
9119
+ ];
9120
+ this.watcher = this.watcherFactory((0, import_node_path14.join)(this.dataDirectory, "**", "*.jsonl"), {
9121
+ awaitWriteFinish: { stabilityThreshold: 200 },
9122
+ ignoreInitial: true
9123
+ });
9124
+ this.watcher.on("add", (filePath) => void this.processTranscriptUpdate(filePath, true));
9125
+ this.watcher.on("change", (filePath) => void this.processTranscriptUpdate(filePath, false));
9126
+ this.watcher.on("error", (error) => {
9127
+ logger.warn({ error }, "Kilo log watcher error");
9128
+ });
9129
+ this.startProcessPolling();
9130
+ }
9131
+ async stop() {
9132
+ if (this.watcher !== null) {
9133
+ await this.watcher.close();
9134
+ this.watcher = null;
9135
+ }
9136
+ if (this.processPoller !== null) {
9137
+ clearInterval(this.processPoller);
9138
+ this.processPoller = null;
9139
+ }
9140
+ this.fallbackProcessSessionId = null;
9141
+ this.sessionMetadata.clear();
9142
+ this.transcriptOffsets.clear();
9143
+ this.transcriptRemainders.clear();
9144
+ this.setRunning(false);
9145
+ }
9146
+ async handleHook(payload) {
9147
+ const normalizedPayload = this.parseNormalizedHookPayload(payload);
9148
+ if (normalizedPayload !== null) {
9149
+ await this.emitNormalizedPayload({
9150
+ ...normalizedPayload,
9151
+ sessionId: resolveSessionId({
9152
+ activeFile: normalizedPayload.data?.activeFile,
9153
+ cwd: normalizedPayload.data?.cwd ?? normalizedPayload.cwd,
9154
+ pid: normalizedPayload.pid,
9155
+ project: normalizedPayload.data?.project,
9156
+ projectPath: normalizedPayload.data?.projectPath,
9157
+ sessionId: normalizedPayload.sessionId,
9158
+ tool: this.name,
9159
+ transcriptPath: normalizedPayload.transcriptPath
9160
+ })
9161
+ });
7527
9162
  return;
7528
9163
  }
7529
- const reader = response.body.getReader();
7530
- const decoder = new TextDecoder();
7531
- let buffer = "";
7532
- try {
7533
- while (true) {
7534
- const readResult = await reader.read();
7535
- if (readResult.done) {
7536
- break;
7537
- }
7538
- buffer += decoder.decode(readResult.value, { stream: true });
7539
- while (true) {
7540
- const delimiterIndex = buffer.indexOf("\n\n");
7541
- if (delimiterIndex === -1) {
7542
- break;
7543
- }
7544
- const frame = buffer.slice(0, delimiterIndex);
7545
- buffer = buffer.slice(delimiterIndex + 2);
7546
- await this.processSSEFrame(snapshot, frame);
7547
- }
9164
+ if (!isRecord9(payload)) {
9165
+ logger.warn({ payload }, "Kilo hook payload must be an object");
9166
+ return;
9167
+ }
9168
+ const sessionId = resolveSessionId({
9169
+ cwd: getString8(payload, "cwd"),
9170
+ pid: getNumber6(payload, "pid"),
9171
+ projectPath: getString8(payload, "projectPath"),
9172
+ sessionId: getString8(payload, "sessionId") ?? getString8(payload, "session_id") ?? getString8(getRecord7(payload.data), "sessionId"),
9173
+ tool: this.name
9174
+ });
9175
+ const context = {
9176
+ cwd: getString8(payload, "cwd"),
9177
+ env: this.env ?? process.env,
9178
+ hookPayload: payload,
9179
+ pid: getNumber6(payload, "pid"),
9180
+ sessionId,
9181
+ source: "aisnitch://adapters/kilo"
9182
+ };
9183
+ const eventType = getString8(payload, "event") ?? getString8(payload, "type") ?? getString8(payload, "method");
9184
+ const data = getRecord7(payload.data) ?? payload;
9185
+ const toolName = getString8(data, "tool") ?? getString8(data, "toolName") ?? getString8(data, "name");
9186
+ const toolInput = extractToolInput3(data);
9187
+ const sharedData = {
9188
+ activeFile: toolInput?.filePath,
9189
+ cwd: context.cwd,
9190
+ model: getString8(data, "model") ?? getString8(payload, "model"),
9191
+ projectPath: getString8(data, "projectPath") ?? getString8(payload, "projectPath"),
9192
+ raw: payload,
9193
+ toolInput,
9194
+ toolName
9195
+ };
9196
+ switch (eventType) {
9197
+ case "session.start":
9198
+ case "sessionStart":
9199
+ case "SessionStart": {
9200
+ await this.emitStateChange("session.start", sharedData, context);
9201
+ await this.emitStateChange("agent.idle", sharedData, context);
9202
+ return;
7548
9203
  }
7549
- } catch (error) {
7550
- if (!abortController.signal.aborted) {
7551
- logger.debug(
7552
- { error, sessionId: snapshot.sessionId },
7553
- "Goose SSE stream closed unexpectedly"
7554
- );
9204
+ case "session.end":
9205
+ case "sessionEnd":
9206
+ case "SessionEnd": {
9207
+ await this.emitStateChange("session.end", sharedData, context);
9208
+ return;
7555
9209
  }
7556
- } finally {
7557
- reader.releaseLock();
9210
+ case "task.start":
9211
+ case "taskStart":
9212
+ case "UserPrompt":
9213
+ case "user_message": {
9214
+ await this.emitStateChange("task.start", sharedData, context);
9215
+ return;
9216
+ }
9217
+ case "task.complete":
9218
+ case "taskComplete":
9219
+ case "TaskComplete": {
9220
+ await this.emitStateChange("task.complete", sharedData, context);
9221
+ await this.emitStateChange("agent.idle", sharedData, context);
9222
+ return;
9223
+ }
9224
+ case "tool.call":
9225
+ case "toolCall":
9226
+ case "PreToolUse": {
9227
+ await this.emitStateChange("agent.tool_call", sharedData, context);
9228
+ return;
9229
+ }
9230
+ case "tool.result":
9231
+ case "toolResult":
9232
+ case "PostToolUse": {
9233
+ const emittedType = isKiloCodingTool(toolName) ? "agent.coding" : "agent.tool_call";
9234
+ await this.emitStateChange(emittedType, sharedData, context);
9235
+ return;
9236
+ }
9237
+ case "thinking":
9238
+ case "Thinking":
9239
+ case "reasoning": {
9240
+ await this.emitStateChange("agent.thinking", sharedData, context);
9241
+ return;
9242
+ }
9243
+ case "streaming":
9244
+ case "Streaming":
9245
+ case "assistant_message": {
9246
+ await this.emitStateChange("agent.streaming", sharedData, context);
9247
+ return;
9248
+ }
9249
+ case "error":
9250
+ case "Error": {
9251
+ await this.emitStateChange("agent.error", {
9252
+ ...sharedData,
9253
+ errorMessage: getString8(data, "error") ?? getString8(payload, "error") ?? "Kilo error",
9254
+ errorType: inferKiloErrorType(data)
9255
+ }, context);
9256
+ return;
9257
+ }
9258
+ case "asking_user":
9259
+ case "PermissionRequest":
9260
+ case "input_required": {
9261
+ await this.emitStateChange("agent.asking_user", sharedData, context);
9262
+ return;
9263
+ }
9264
+ default:
9265
+ logger.debug({ eventType }, "Kilo hook event ignored by adapter");
7558
9266
  }
7559
9267
  }
7560
- async processSSEFrame(snapshot, frame) {
7561
- const payloadText = frame.split(/\r?\n/u).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
7562
- if (payloadText.length === 0) {
7563
- return;
7564
- }
7565
- let parsedPayload;
9268
+ async processTranscriptUpdate(filePath, readFromStart) {
9269
+ let fileContent;
7566
9270
  try {
7567
- parsedPayload = JSON.parse(payloadText);
9271
+ fileContent = await (0, import_promises13.readFile)(filePath);
7568
9272
  } catch (error) {
7569
- logger.warn({ error }, "Goose SSE frame is not valid JSON");
9273
+ logger.debug({ error, filePath }, "Kilo transcript read skipped");
7570
9274
  return;
7571
9275
  }
7572
- await this.processSessionEvent(snapshot, parsedPayload);
9276
+ const knownOffset = this.transcriptOffsets.get(filePath);
9277
+ const previousOffset = knownOffset ?? (readFromStart ? 0 : fileContent.byteLength);
9278
+ const safeOffset = previousOffset > fileContent.byteLength ? 0 : previousOffset;
9279
+ const newChunk = fileContent.subarray(safeOffset).toString("utf8");
9280
+ const bufferedChunk = (safeOffset === 0 ? "" : this.transcriptRemainders.get(filePath) ?? "") + newChunk;
9281
+ const lines = bufferedChunk.split(/\r?\n/u);
9282
+ const remainder = bufferedChunk.endsWith("\n") || bufferedChunk.endsWith("\r") ? "" : lines.pop() ?? "";
9283
+ this.transcriptOffsets.set(filePath, fileContent.byteLength);
9284
+ this.transcriptRemainders.set(filePath, remainder);
9285
+ for (const line of lines) {
9286
+ const trimmedLine = line.trim();
9287
+ if (trimmedLine.length === 0) {
9288
+ continue;
9289
+ }
9290
+ await this.processTranscriptLine(trimmedLine, filePath);
9291
+ }
7573
9292
  }
7574
- async processSessionEvent(snapshot, payload) {
7575
- if (!isRecord6(payload)) {
9293
+ async processTranscriptLine(line, transcriptPath) {
9294
+ let parsedLine;
9295
+ try {
9296
+ parsedLine = JSON.parse(line);
9297
+ } catch (error) {
9298
+ logger.warn({ error, transcriptPath }, "Kilo transcript line is not valid JSON");
7576
9299
  return;
7577
9300
  }
7578
- const fingerprint = createGooseFingerprint(payload);
7579
- if (fingerprint && !this.markSessionEventSeen(snapshot.sessionId, fingerprint)) {
9301
+ if (!isRecord9(parsedLine)) {
7580
9302
  return;
7581
9303
  }
7582
- const eventType = getString5(payload, "type");
7583
- switch (eventType) {
7584
- case "Message": {
7585
- await this.processGooseMessage(snapshot, payload);
9304
+ const acpType = getString8(parsedLine, "type") ?? getString8(parsedLine, "method");
9305
+ const sessionId = resolveSessionId({
9306
+ sessionId: getString8(parsedLine, "sessionId") ?? getString8(parsedLine, "session_id") ?? getString8(parsedLine, "id") ?? (0, import_node_path14.basename)(transcriptPath, ".jsonl"),
9307
+ tool: this.name,
9308
+ transcriptPath
9309
+ });
9310
+ const context = {
9311
+ env: process.env,
9312
+ hookPayload: parsedLine,
9313
+ sessionId,
9314
+ source: "aisnitch://adapters/kilo/log",
9315
+ transcriptPath
9316
+ };
9317
+ const data = getRecord7(parsedLine.data) ?? parsedLine;
9318
+ const toolName = getString8(data, "tool") ?? getString8(data, "toolName");
9319
+ const toolInput = extractToolInput3(data);
9320
+ const sharedData = {
9321
+ activeFile: toolInput?.filePath,
9322
+ cwd: getString8(data, "cwd"),
9323
+ model: getString8(data, "model"),
9324
+ raw: parsedLine,
9325
+ toolInput,
9326
+ toolName
9327
+ };
9328
+ switch (acpType) {
9329
+ case "session.start":
9330
+ case "SessionStart": {
9331
+ await this.emitStateChange("session.start", sharedData, context);
9332
+ await this.emitStateChange("agent.idle", sharedData, context);
7586
9333
  return;
7587
9334
  }
7588
- case "Finish": {
7589
- await this.ensureObservedSession(snapshot, "sse");
7590
- await this.emitStateChange(
7591
- "task.complete",
7592
- buildGooseSessionData(snapshot, {
7593
- raw: payload,
7594
- tokensUsed: extractGooseTokenCount(getRecord4(payload.token_state))
7595
- }),
7596
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7597
- );
9335
+ case "session.end":
9336
+ case "SessionEnd": {
9337
+ await this.emitStateChange("session.end", sharedData, context);
9338
+ return;
9339
+ }
9340
+ case "task.start":
9341
+ case "TaskStart":
9342
+ case "user_message": {
9343
+ await this.emitStateChange("task.start", sharedData, context);
9344
+ return;
9345
+ }
9346
+ case "task.complete":
9347
+ case "TaskComplete": {
9348
+ await this.emitStateChange("task.complete", sharedData, context);
9349
+ await this.emitStateChange("agent.idle", sharedData, context);
9350
+ return;
9351
+ }
9352
+ case "thinking":
9353
+ case "Thinking":
9354
+ case "reasoning": {
9355
+ await this.emitStateChange("agent.thinking", sharedData, context);
9356
+ return;
9357
+ }
9358
+ case "streaming":
9359
+ case "assistant_message": {
9360
+ await this.emitStateChange("agent.streaming", sharedData, context);
9361
+ return;
9362
+ }
9363
+ case "tool_call":
9364
+ case "tool_use": {
9365
+ const emittedType = isKiloCodingTool(toolName) ? "agent.coding" : "agent.tool_call";
9366
+ await this.emitStateChange(emittedType, sharedData, context);
7598
9367
  return;
7599
9368
  }
9369
+ case "error":
7600
9370
  case "Error": {
7601
- const errorMessage = getString5(payload, "error");
7602
- await this.ensureObservedSession(snapshot, "sse");
7603
- await this.emitStateChange(
7604
- "agent.error",
7605
- buildGooseSessionData(snapshot, {
7606
- errorMessage,
7607
- errorType: inferGooseErrorType(errorMessage),
7608
- raw: payload
7609
- }),
7610
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7611
- );
9371
+ await this.emitStateChange("agent.error", {
9372
+ ...sharedData,
9373
+ errorMessage: getString8(data, "error") ?? getString8(data, "message"),
9374
+ errorType: inferKiloErrorType(data)
9375
+ }, context);
7612
9376
  return;
7613
9377
  }
7614
- case "Notification": {
7615
- await this.ensureObservedSession(snapshot, "sse");
7616
- await this.emitStateChange(
7617
- "agent.asking_user",
7618
- buildGooseSessionData(snapshot, {
7619
- errorMessage: extractLooseString2(payload, ["message"]),
7620
- raw: payload
7621
- }),
7622
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7623
- );
9378
+ case "permission_request":
9379
+ case "asking_user": {
9380
+ await this.emitStateChange("agent.asking_user", sharedData, context);
7624
9381
  return;
7625
9382
  }
7626
9383
  default:
9384
+ if (acpType === "ping" || acpType === "pong" || acpType === "heartbeat") {
9385
+ return;
9386
+ }
7627
9387
  return;
7628
9388
  }
7629
9389
  }
7630
- async processGooseMessage(snapshot, payload) {
7631
- const message = getRecord4(payload.message);
7632
- if (!message) {
7633
- return;
7634
- }
7635
- const role = getString5(message, "role");
7636
- const content = getRecordArray(message.content);
7637
- const tokenState = getRecord4(payload.token_state);
7638
- const tokensUsed = extractGooseTokenCount(tokenState);
7639
- await this.ensureObservedSession(snapshot, "sse");
7640
- if (role === "user") {
7641
- const promptText = content.filter((part) => getString5(part, "type") === "text").map((part) => getString5(part, "text")).filter((part) => typeof part === "string").join("\n").trim();
7642
- await this.emitStateChange(
7643
- "task.start",
7644
- buildGooseSessionData(snapshot, {
7645
- raw: {
7646
- message,
7647
- prompt: promptText,
7648
- tokenState
7649
- },
7650
- tokensUsed
7651
- }),
7652
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
9390
+ async seedTranscriptOffsets() {
9391
+ try {
9392
+ const files = await collectFilesRecursively6(this.dataDirectory, ".jsonl");
9393
+ await Promise.all(
9394
+ files.map(async (filePath) => {
9395
+ try {
9396
+ const fileStats = await (0, import_promises13.stat)(filePath);
9397
+ this.transcriptOffsets.set(filePath, fileStats.size);
9398
+ } catch {
9399
+ }
9400
+ })
7653
9401
  );
7654
- return;
7655
- }
7656
- if (role !== "assistant") {
7657
- return;
7658
- }
7659
- for (const part of content) {
7660
- const partType = getString5(part, "type");
7661
- switch (partType) {
7662
- case "thinking": {
7663
- await this.emitStateChange(
7664
- "agent.thinking",
7665
- buildGooseSessionData(snapshot, {
7666
- raw: {
7667
- message: {
7668
- content: [part],
7669
- role
7670
- },
7671
- tokenState
7672
- },
7673
- tokensUsed
7674
- }),
7675
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7676
- );
7677
- break;
7678
- }
7679
- case "redactedThinking": {
7680
- await this.emitStateChange(
7681
- "agent.thinking",
7682
- buildGooseSessionData(snapshot, {
7683
- raw: {
7684
- message: {
7685
- content: [
7686
- {
7687
- thinking: getString5(part, "data"),
7688
- type: "thinking"
7689
- }
7690
- ],
7691
- role
7692
- },
7693
- tokenState
7694
- },
7695
- tokensUsed
7696
- }),
7697
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7698
- );
7699
- break;
7700
- }
7701
- case "text": {
7702
- await this.emitStateChange(
7703
- "agent.streaming",
7704
- buildGooseSessionData(snapshot, {
7705
- raw: {
7706
- message: {
7707
- content: [part],
7708
- role
7709
- },
7710
- tokenState
7711
- },
7712
- tokensUsed
7713
- }),
7714
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7715
- );
7716
- break;
7717
- }
7718
- case "toolRequest": {
7719
- const toolName = extractGooseToolName(part);
7720
- const toolInput = extractGooseToolInput(part);
7721
- const activeFile = toolInput?.filePath;
7722
- const emittedType = isGooseCodingTool(toolName, toolInput) ? "agent.coding" : "agent.tool_call";
7723
- await this.emitStateChange(
7724
- emittedType,
7725
- buildGooseSessionData(snapshot, {
7726
- activeFile,
7727
- raw: {
7728
- message: {
7729
- content: [part],
7730
- role
7731
- },
7732
- tokenState
7733
- },
7734
- toolInput,
7735
- toolName,
7736
- tokensUsed
7737
- }),
7738
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7739
- );
7740
- break;
7741
- }
7742
- case "toolConfirmationRequest":
7743
- case "actionRequired": {
7744
- await this.emitStateChange(
7745
- "agent.asking_user",
7746
- buildGooseSessionData(snapshot, {
7747
- errorMessage: getString5(part, "prompt") ?? extractLooseString2(getRecord4(part.data), ["message"]),
7748
- raw: {
7749
- message: {
7750
- content: [part],
7751
- role
7752
- },
7753
- tokenState
7754
- },
7755
- toolInput: extractGooseToolInput(part),
7756
- toolName: extractGooseToolName(part),
7757
- tokensUsed
7758
- }),
7759
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7760
- );
7761
- break;
7762
- }
7763
- case "systemNotification": {
7764
- const notificationType = getString5(part, "notificationType");
7765
- const eventTypeToEmit = notificationType === "thinkingMessage" ? "agent.thinking" : "agent.asking_user";
7766
- await this.emitStateChange(
7767
- eventTypeToEmit,
7768
- buildGooseSessionData(snapshot, {
7769
- errorMessage: getString5(part, "msg"),
7770
- errorType: notificationType === "creditsExhausted" ? "rate_limit" : void 0,
7771
- raw: {
7772
- message: notificationType === "thinkingMessage" ? {
7773
- content: [
7774
- {
7775
- thinking: getString5(part, "msg"),
7776
- type: "thinking"
7777
- }
7778
- ],
7779
- role
7780
- } : {
7781
- content: [part],
7782
- role
7783
- },
7784
- tokenState
7785
- },
7786
- tokensUsed
7787
- }),
7788
- this.createSessionContext(snapshot, "aisnitch://adapters/goose/sse")
7789
- );
7790
- break;
7791
- }
7792
- default:
7793
- break;
7794
- }
7795
- }
7796
- }
7797
- async ensureObservedSession(snapshot, source) {
7798
- if (this.observedSessions.has(snapshot.sessionId)) {
7799
- return;
7800
- }
7801
- this.observedSessions.add(snapshot.sessionId);
7802
- const context = this.createSessionContext(
7803
- snapshot,
7804
- `aisnitch://adapters/goose/${source}`
7805
- );
7806
- const eventData = buildGooseSessionData(snapshot, {
7807
- raw: {
7808
- snapshot,
7809
- source
7810
- }
7811
- });
7812
- await this.emitStateChange("session.start", eventData, context);
7813
- await this.emitStateChange("agent.idle", eventData, context);
7814
- }
7815
- createSessionContext(snapshot, source) {
7816
- return {
7817
- cwd: snapshot.workingDir,
7818
- sessionId: snapshot.sessionId,
7819
- source
7820
- };
7821
- }
7822
- markSessionEventSeen(sessionId, fingerprint) {
7823
- const knownFingerprints = this.sessionEventFingerprints.get(sessionId) ?? /* @__PURE__ */ new Set();
7824
- if (knownFingerprints.has(fingerprint)) {
7825
- return false;
7826
- }
7827
- if (knownFingerprints.size >= 256) {
7828
- knownFingerprints.clear();
9402
+ } catch {
7829
9403
  }
7830
- knownFingerprints.add(fingerprint);
7831
- this.sessionEventFingerprints.set(sessionId, knownFingerprints);
7832
- return true;
7833
- }
7834
- createApiHeaders() {
7835
- if (!this.apiKey) {
7836
- return void 0;
9404
+ }
9405
+ startProcessPolling() {
9406
+ if (this.pollIntervalMs <= 0) {
9407
+ return;
7837
9408
  }
7838
- return {
7839
- Authorization: `Bearer ${this.apiKey}`
7840
- };
9409
+ this.processPoller = setInterval(() => {
9410
+ void this.pollKiloProcesses();
9411
+ }, this.pollIntervalMs);
9412
+ this.processPoller.unref();
9413
+ void this.pollKiloProcesses();
7841
9414
  }
7842
- async pollGooseProcesses() {
7843
- const processes = await listProcesses5(this.processListCommand);
9415
+ async pollKiloProcesses() {
9416
+ const processes = await listProcesses8(this.processListCommand);
7844
9417
  if (processes.length > 0 && this.getStatus().activeSessions === 0) {
7845
9418
  const processInfo = processes[0];
7846
9419
  if (!processInfo) {
7847
9420
  return;
7848
9421
  }
7849
- const sessionId = `goose-process-${processInfo.pid}`;
9422
+ const sessionId = `kilo-process-${processInfo.pid}`;
7850
9423
  this.fallbackProcessSessionId = sessionId;
7851
9424
  await this.emitStateChange(
7852
9425
  "session.start",
7853
- {
7854
- raw: {
7855
- process: processInfo,
7856
- source: "process-detect"
7857
- }
7858
- },
9426
+ { raw: { process: processInfo, source: "process-detect" } },
7859
9427
  {
7860
9428
  pid: processInfo.pid,
7861
9429
  sessionId,
7862
- source: "aisnitch://adapters/goose/process-detect"
9430
+ source: "aisnitch://adapters/kilo/process-detect"
7863
9431
  }
7864
9432
  );
7865
9433
  return;
@@ -7869,240 +9437,119 @@ var GooseAdapter = class extends BaseAdapter {
7869
9437
  this.fallbackProcessSessionId = null;
7870
9438
  await this.emitStateChange(
7871
9439
  "session.end",
7872
- {
7873
- raw: {
7874
- reason: "process-exit",
7875
- source: "process-detect"
7876
- }
7877
- },
9440
+ { raw: { reason: "process-exit", source: "process-detect" } },
7878
9441
  {
7879
9442
  sessionId,
7880
- source: "aisnitch://adapters/goose/process-detect"
9443
+ source: "aisnitch://adapters/kilo/process-detect"
7881
9444
  }
7882
9445
  );
7883
9446
  }
7884
9447
  }
7885
9448
  };
7886
- function normalizeGooseSession(payload) {
7887
- if (!isRecord6(payload)) {
7888
- return null;
7889
- }
7890
- const gooseSessionId = getString5(payload, "id");
7891
- if (!gooseSessionId) {
7892
- return null;
9449
+ async function collectFilesRecursively6(directoryPath, extension) {
9450
+ try {
9451
+ const entries = await (0, import_promises13.readdir)(directoryPath, { withFileTypes: true });
9452
+ const nestedResults = await Promise.all(
9453
+ entries.map(async (entry) => {
9454
+ const entryPath = (0, import_node_path14.join)(directoryPath, entry.name);
9455
+ if (entry.isDirectory()) {
9456
+ return await collectFilesRecursively6(entryPath, extension);
9457
+ }
9458
+ return entry.name.endsWith(extension) ? [entryPath] : [];
9459
+ })
9460
+ );
9461
+ return nestedResults.flat();
9462
+ } catch (error) {
9463
+ if (isErrnoException7(error) && error.code === "ENOENT") {
9464
+ return [];
9465
+ }
9466
+ throw error;
7893
9467
  }
7894
- const workingDir = getString5(payload, "working_dir") ?? getString5(payload, "workingDir");
7895
- const name = getString5(payload, "name");
7896
- const providerName = getString5(payload, "provider_name") ?? getString5(payload, "providerName");
7897
- const updatedAt = getString5(payload, "updated_at") ?? getString5(payload, "updatedAt");
7898
- const messageCount = getNumber3(payload, "message_count") ?? getNumber3(payload, "messageCount");
7899
- const totalTokens = getNumber3(payload, "accumulated_total_tokens") ?? getNumber3(payload, "total_tokens") ?? getNumber3(payload, "accumulatedTotalTokens") ?? getNumber3(payload, "totalTokens");
7900
- const modelConfig = getRecord4(payload.model_config) ?? getRecord4(payload.modelConfig);
7901
- const rawModelConfig = getString5(payload, "model_config");
7902
- const parsedModelConfig = modelConfig ?? parseJsonRecord2(rawModelConfig);
7903
- const model = getString5(parsedModelConfig, "model_name") ?? getString5(parsedModelConfig, "modelName");
7904
- const sessionId = resolveSessionId({
7905
- cwd: workingDir,
7906
- projectPath: workingDir,
7907
- sessionId: gooseSessionId,
7908
- tool: "goose"
7909
- });
7910
- return {
7911
- gooseSessionId,
7912
- messageCount,
7913
- model,
7914
- name,
7915
- providerName,
7916
- sessionId,
7917
- totalTokens,
7918
- updatedAt,
7919
- workingDir
7920
- };
7921
- }
7922
- function buildGooseSessionData(snapshot, overrides = {}) {
7923
- return {
7924
- cwd: overrides.cwd ?? snapshot.workingDir,
7925
- model: overrides.model ?? snapshot.model,
7926
- project: overrides.project ?? snapshot.name,
7927
- projectPath: overrides.projectPath ?? snapshot.workingDir,
7928
- raw: overrides.raw,
7929
- tokensUsed: overrides.tokensUsed ?? snapshot.totalTokens,
7930
- ...overrides
7931
- };
7932
- }
7933
- function compareGooseSessionsByRecency(left, right) {
7934
- return Date.parse(right.updatedAt ?? "") - Date.parse(left.updatedAt ?? "");
7935
- }
7936
- function didGooseSessionAdvance(previousSnapshot, nextSnapshot) {
7937
- return previousSnapshot.updatedAt !== nextSnapshot.updatedAt || previousSnapshot.messageCount !== nextSnapshot.messageCount || previousSnapshot.totalTokens !== nextSnapshot.totalTokens;
7938
9468
  }
7939
- function createGooseFingerprint(payload) {
7940
- const eventType = getString5(payload, "type");
7941
- if (!eventType) {
7942
- return null;
7943
- }
7944
- if (eventType === "Message") {
7945
- const message = getRecord4(payload.message);
7946
- const messageId = getString5(message, "id");
7947
- const created = getNumber3(message, "created");
7948
- const role = getString5(message, "role");
7949
- return [eventType, messageId, created, role].filter((value) => value !== void 0).join(":");
7950
- }
7951
- if (eventType === "Finish") {
7952
- return `${eventType}:${getString5(payload, "reason") ?? "unknown"}`;
7953
- }
7954
- if (eventType === "Error") {
7955
- return `${eventType}:${getString5(payload, "error") ?? "unknown"}`;
9469
+ function extractToolInput3(payload) {
9470
+ if (!payload) {
9471
+ return void 0;
7956
9472
  }
7957
- return eventType;
7958
- }
7959
- function extractGooseTokenCount(tokenState) {
7960
- return getNumber3(tokenState, "accumulatedTotalTokens") ?? getNumber3(tokenState, "totalTokens");
7961
- }
7962
- function extractGooseToolName(payload) {
7963
- const toolCall = getRecord4(payload.toolCall);
7964
- return getString5(toolCall, "name") ?? getString5(toolCall, "toolName") ?? getString5(payload, "toolName");
7965
- }
7966
- function extractGooseToolInput(payload) {
7967
- const toolCall = getRecord4(payload.toolCall);
7968
- const argumentsRecord = getRecord4(toolCall?.arguments) ?? getRecord4(toolCall?.args) ?? getRecord4(toolCall?.input) ?? getRecord4(payload.arguments);
7969
- const filePath = extractFirstString2(argumentsRecord, [
7970
- "file",
7971
- "file_path",
7972
- "filePath",
7973
- "path",
7974
- "target",
7975
- "target_path",
7976
- "targetPath"
7977
- ]);
7978
- const command = extractFirstString2(argumentsRecord, [
7979
- "cmd",
7980
- "command",
7981
- "script"
7982
- ]);
9473
+ const toolInput = getRecord7(payload.tool_input) ?? getRecord7(payload.toolInput) ?? getRecord7(payload.arguments) ?? getRecord7(payload.params);
9474
+ const filePath = getString8(toolInput, "filePath") ?? getString8(toolInput, "file_path") ?? getString8(toolInput, "path") ?? getString8(toolInput, "target");
9475
+ const command = getString8(toolInput, "command") ?? getString8(toolInput, "cmd") ?? getString8(toolInput, "script");
7983
9476
  if (!filePath && !command) {
7984
9477
  return void 0;
7985
9478
  }
7986
- return {
7987
- command,
7988
- filePath
7989
- };
9479
+ return { command, filePath };
7990
9480
  }
7991
- function isGooseCodingTool(toolName, toolInput) {
7992
- if (toolName && GOOSE_CODING_TOOL_HINT.test(toolName)) {
7993
- return true;
7994
- }
7995
- const filePath = toolInput?.filePath;
7996
- if (!filePath) {
7997
- return false;
7998
- }
7999
- return !/read|view|glob|grep|list|search/iu.test(toolName ?? "");
9481
+ function isKiloCodingTool(toolName) {
9482
+ return toolName !== void 0 && KILO_CODING_TOOLS.has(toolName);
8000
9483
  }
8001
- function inferGooseErrorType(errorMessage) {
8002
- if (!errorMessage) {
8003
- return void 0;
8004
- }
8005
- if (GOOSE_RATE_LIMIT_HINT.test(errorMessage)) {
9484
+ function inferKiloErrorType(payload) {
9485
+ const message = getString8(payload, "error") ?? getString8(payload, "message") ?? "";
9486
+ if (/rate.?limit|quota|credit/i.test(message)) {
8006
9487
  return "rate_limit";
8007
9488
  }
8008
- if (/context|token limit|too long/iu.test(errorMessage)) {
9489
+ if (/context|token.?limit|too.?long/i.test(message)) {
8009
9490
  return "context_overflow";
8010
9491
  }
8011
- if (/tool/iu.test(errorMessage)) {
9492
+ if (/tool|permission|denied/i.test(message)) {
8012
9493
  return "tool_failure";
8013
9494
  }
8014
9495
  return "api_error";
8015
9496
  }
8016
- async function listProcesses5(processListCommand) {
8017
- try {
8018
- const commandOutput = await processListCommand();
8019
- return commandOutput.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
8020
- const [pidPart, ...commandParts] = line.split(/\s+/u);
8021
- const pid = pidPart ? Number.parseInt(pidPart, 10) : Number.NaN;
8022
- return {
8023
- command: commandParts.join(" "),
8024
- pid
8025
- };
8026
- }).filter((processInfo) => Number.isInteger(processInfo.pid));
8027
- } catch (error) {
8028
- logger.debug({ error }, "Goose process listing skipped");
9497
+ async function listProcesses8(listCommand) {
9498
+ if (process.platform === "win32") {
8029
9499
  return [];
8030
9500
  }
8031
- }
8032
- function parseJsonRecord2(value) {
8033
- if (!value) {
8034
- return void 0;
8035
- }
8036
9501
  try {
8037
- const parsedValue = JSON.parse(value);
8038
- return getRecord4(parsedValue);
8039
- } catch {
8040
- return void 0;
8041
- }
8042
- }
8043
- function extractFirstString2(payload, keys) {
8044
- if (!payload) {
8045
- return void 0;
8046
- }
8047
- for (const key of keys) {
8048
- const directValue = getString5(payload, key);
8049
- if (directValue) {
8050
- return directValue;
8051
- }
9502
+ const stdout = await listCommand();
9503
+ return stdout.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).filter((line) => line.includes("kilo") || line.includes("kilocode")).map(parseProcessLine6).filter((processInfo) => processInfo !== null);
9504
+ } catch (error) {
9505
+ logger.debug({ error }, "Kilo process detection failed");
9506
+ return [];
8052
9507
  }
8053
- return void 0;
8054
9508
  }
8055
- function extractLooseString2(payload, keys) {
8056
- if (!payload) {
8057
- return void 0;
8058
- }
8059
- for (const key of keys) {
8060
- const directValue = getString5(payload, key);
8061
- if (directValue) {
8062
- return directValue;
8063
- }
8064
- const nestedValue = getString5(getRecord4(payload[key]), "message");
8065
- if (nestedValue) {
8066
- return nestedValue;
8067
- }
9509
+ function parseProcessLine6(line) {
9510
+ const match = line.match(/^(\d+)\s+(.+)$/u);
9511
+ if (!match) {
9512
+ return null;
8068
9513
  }
8069
- return void 0;
8070
- }
8071
- function getRecordArray(value) {
8072
- if (!Array.isArray(value)) {
8073
- return [];
9514
+ const pidText = match[1];
9515
+ const command = match[2];
9516
+ if (!pidText || !command) {
9517
+ return null;
8074
9518
  }
8075
- return value.filter((entry) => isRecord6(entry));
9519
+ return {
9520
+ command,
9521
+ pid: Number.parseInt(pidText, 10)
9522
+ };
8076
9523
  }
8077
- function getRecord4(value) {
8078
- return isRecord6(value) ? value : void 0;
9524
+ function isErrnoException7(error) {
9525
+ return error instanceof Error && "code" in error;
8079
9526
  }
8080
- function isRecord6(value) {
9527
+ function isRecord9(value) {
8081
9528
  return typeof value === "object" && value !== null && !Array.isArray(value);
8082
9529
  }
8083
- function getString5(payload, key) {
8084
- if (!payload) {
8085
- return void 0;
8086
- }
9530
+ function getRecord7(value) {
9531
+ return isRecord9(value) ? value : void 0;
9532
+ }
9533
+ function getNumber6(payload, key) {
8087
9534
  const value = payload[key];
8088
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
9535
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8089
9536
  }
8090
- function getNumber3(payload, key) {
9537
+ function getString8(payload, key) {
8091
9538
  if (!payload) {
8092
9539
  return void 0;
8093
9540
  }
8094
9541
  const value = payload[key];
8095
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
9542
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
8096
9543
  }
8097
9544
 
8098
9545
  // src/adapters/openclaw.ts
8099
- var import_node_child_process9 = require("child_process");
8100
- var import_promises11 = require("fs/promises");
8101
- var import_node_path12 = require("path");
8102
- var import_node_util9 = require("util");
8103
- var import_chokidar7 = require("chokidar");
9546
+ var import_node_child_process12 = require("child_process");
9547
+ var import_promises14 = require("fs/promises");
9548
+ var import_node_path15 = require("path");
9549
+ var import_node_util12 = require("util");
9550
+ var import_chokidar10 = require("chokidar");
8104
9551
  var import_pid_cwd3 = __toESM(require("pid-cwd"), 1);
8105
- var execFile9 = (0, import_node_util9.promisify)(import_node_child_process9.execFile);
9552
+ var execFile12 = (0, import_node_util12.promisify)(import_node_child_process12.execFile);
8106
9553
  var COMMAND_START_THINKING_DELAY_MS = 2e3;
8107
9554
  var POST_TOOL_THINKING_DELAY_MS = 500;
8108
9555
  var OPENCLAW_CODING_TOOL_HINT = /apply|bash|create|delete|edit|exec|file|patch|replace|shell|write/iu;
@@ -8137,22 +9584,22 @@ var OpenClawAdapter = class extends BaseAdapter {
8137
9584
  fileRemainders = /* @__PURE__ */ new Map();
8138
9585
  constructor(options) {
8139
9586
  super(options);
8140
- const openclawHome = (0, import_node_path12.join)(this.getUserHomeDirectory(), ".openclaw");
8141
- this.agentsDirectory = options.agentsDirectory ?? (0, import_node_path12.join)(openclawHome, "agents");
8142
- this.commandsLogPath = options.commandsLogPath ?? (0, import_node_path12.join)(openclawHome, "logs", "commands.log");
9587
+ const openclawHome = (0, import_node_path15.join)(this.getUserHomeDirectory(), ".openclaw");
9588
+ this.agentsDirectory = options.agentsDirectory ?? (0, import_node_path15.join)(openclawHome, "agents");
9589
+ this.commandsLogPath = options.commandsLogPath ?? (0, import_node_path15.join)(openclawHome, "logs", "commands.log");
8143
9590
  this.cwdResolver = options.cwdResolver ?? (async (pid) => {
8144
9591
  return await (0, import_pid_cwd3.default)(pid);
8145
9592
  });
8146
9593
  this.pollIntervalMs = options.pollIntervalMs ?? 5e3;
8147
- this.processListCommand = options.processListCommand ?? (async () => await execFile9("pgrep", ["-ifl", "openclaw"]).then(
9594
+ this.processListCommand = options.processListCommand ?? (async () => await execFile12("pgrep", ["-ifl", "openclaw"]).then(
8148
9595
  (result) => result.stdout
8149
9596
  ));
8150
- this.watcherFactory = options.watcherFactory ?? import_chokidar7.watch;
9597
+ this.watcherFactory = options.watcherFactory ?? import_chokidar10.watch;
8151
9598
  this.memoryRootGlobs = [
8152
- (0, import_node_path12.join)(openclawHome, "workspace", "MEMORY.md"),
8153
- (0, import_node_path12.join)(openclawHome, "workspace", "memory", "*.md"),
8154
- (0, import_node_path12.join)(openclawHome, "workspace-*", "MEMORY.md"),
8155
- (0, import_node_path12.join)(openclawHome, "workspace-*", "memory", "*.md")
9599
+ (0, import_node_path15.join)(openclawHome, "workspace", "MEMORY.md"),
9600
+ (0, import_node_path15.join)(openclawHome, "workspace", "memory", "*.md"),
9601
+ (0, import_node_path15.join)(openclawHome, "workspace-*", "MEMORY.md"),
9602
+ (0, import_node_path15.join)(openclawHome, "workspace-*", "memory", "*.md")
8156
9603
  ];
8157
9604
  }
8158
9605
  async start() {
@@ -8181,7 +9628,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8181
9628
  logger.warn({ error }, "OpenClaw commands.log watcher error");
8182
9629
  });
8183
9630
  this.transcriptWatcher = this.watcherFactory(
8184
- (0, import_node_path12.join)(this.agentsDirectory, "*", "sessions", "*.jsonl"),
9631
+ (0, import_node_path15.join)(this.agentsDirectory, "*", "sessions", "*.jsonl"),
8185
9632
  {
8186
9633
  awaitWriteFinish: {
8187
9634
  stabilityThreshold: 200
@@ -8263,7 +9710,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8263
9710
  });
8264
9711
  return;
8265
9712
  }
8266
- if (!isRecord7(payload)) {
9713
+ if (!isRecord10(payload)) {
8267
9714
  logger.warn({ payload }, "OpenClaw payload must be an object");
8268
9715
  return;
8269
9716
  }
@@ -8281,7 +9728,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8281
9728
  const sessionId = resolveSessionId({
8282
9729
  activeFile: extractOpenClawActiveFile(payload),
8283
9730
  cwd: extractOpenClawCwd(payload),
8284
- pid: getNumber4(payload, "pid"),
9731
+ pid: getNumber7(payload, "pid"),
8285
9732
  project: extractOpenClawProject(payload),
8286
9733
  projectPath: extractOpenClawCwd(payload),
8287
9734
  sessionId: extractOpenClawSessionKey(payload),
@@ -8291,7 +9738,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8291
9738
  const context = {
8292
9739
  cwd: extractOpenClawCwd(payload),
8293
9740
  hookPayload: payload,
8294
- pid: getNumber4(payload, "pid"),
9741
+ pid: getNumber7(payload, "pid"),
8295
9742
  sessionId,
8296
9743
  source,
8297
9744
  transcriptPath
@@ -8429,24 +9876,24 @@ var OpenClawAdapter = class extends BaseAdapter {
8429
9876
  await this.seedFileOffset(this.commandsLogPath);
8430
9877
  }
8431
9878
  async seedTranscriptOffsets() {
8432
- const files = await collectFilesRecursively4(this.agentsDirectory, ".jsonl");
9879
+ const files = await collectFilesRecursively7(this.agentsDirectory, ".jsonl");
8433
9880
  await Promise.all(files.map(async (filePath) => await this.seedFileOffset(filePath)));
8434
9881
  }
8435
9882
  async seedMemoryOffsets() {
8436
- for (const root of [(0, import_node_path12.join)(this.getUserHomeDirectory(), ".openclaw", "workspace")]) {
9883
+ for (const root of [(0, import_node_path15.join)(this.getUserHomeDirectory(), ".openclaw", "workspace")]) {
8437
9884
  await Promise.all([
8438
- this.seedDirectoryFileOffsets((0, import_node_path12.join)(root, "memory"), ".md"),
8439
- this.seedFileOffset((0, import_node_path12.join)(root, "MEMORY.md"))
9885
+ this.seedDirectoryFileOffsets((0, import_node_path15.join)(root, "memory"), ".md"),
9886
+ this.seedFileOffset((0, import_node_path15.join)(root, "MEMORY.md"))
8440
9887
  ]);
8441
9888
  }
8442
9889
  }
8443
9890
  async seedDirectoryFileOffsets(directory, extension) {
8444
- const files = await collectFilesRecursively4(directory, extension);
9891
+ const files = await collectFilesRecursively7(directory, extension);
8445
9892
  await Promise.all(files.map(async (filePath) => await this.seedFileOffset(filePath)));
8446
9893
  }
8447
9894
  async seedFileOffset(filePath) {
8448
9895
  try {
8449
- const fileStats = await (0, import_promises11.stat)(filePath);
9896
+ const fileStats = await (0, import_promises14.stat)(filePath);
8450
9897
  this.fileOffsets.set(filePath, fileStats.size);
8451
9898
  } catch {
8452
9899
  }
@@ -8478,7 +9925,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8478
9925
  }
8479
9926
  async processTranscriptLine(line, filePath) {
8480
9927
  const rawSessionId = extractOpenClawSessionKey(line) ?? inferOpenClawSessionIdFromTranscriptPath(filePath);
8481
- const cwd = extractOpenClawCwd(line) ?? (0, import_node_path12.dirname)((0, import_node_path12.dirname)(filePath));
9928
+ const cwd = extractOpenClawCwd(line) ?? (0, import_node_path15.dirname)((0, import_node_path15.dirname)(filePath));
8482
9929
  const sessionId = resolveSessionId({
8483
9930
  cwd,
8484
9931
  projectPath: cwd,
@@ -8486,7 +9933,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8486
9933
  tool: this.name,
8487
9934
  transcriptPath: filePath
8488
9935
  });
8489
- const eventFingerprint = getString6(line, "id") ?? `${filePath}:${getString6(line, "timestamp") ?? JSON.stringify(line).slice(0, 120)}`;
9936
+ const eventFingerprint = getString9(line, "id") ?? `${filePath}:${getString9(line, "timestamp") ?? JSON.stringify(line).slice(0, 120)}`;
8490
9937
  const dedupeKey = `${sessionId}:${eventFingerprint}`;
8491
9938
  if (this.observedTranscriptEntries.has(dedupeKey)) {
8492
9939
  return;
@@ -8503,7 +9950,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8503
9950
  };
8504
9951
  const sharedData = {
8505
9952
  cwd,
8506
- project: (0, import_node_path12.basename)(cwd) || cwd,
9953
+ project: (0, import_node_path15.basename)(cwd) || cwd,
8507
9954
  projectPath: cwd,
8508
9955
  raw: line
8509
9956
  };
@@ -8513,11 +9960,11 @@ var OpenClawAdapter = class extends BaseAdapter {
8513
9960
  sessionId,
8514
9961
  transcriptPath: filePath
8515
9962
  });
8516
- if (getString6(line, "type") === "session") {
9963
+ if (getString9(line, "type") === "session") {
8517
9964
  await this.emitOpenClawSessionStart(sharedData, context);
8518
9965
  return;
8519
9966
  }
8520
- if (getString6(line, "type") === "compaction") {
9967
+ if (getString9(line, "type") === "compaction") {
8521
9968
  await this.ensureSessionStarted(sharedData, context);
8522
9969
  await this.emitStateChange("agent.compact", sharedData, context);
8523
9970
  return;
@@ -8572,7 +10019,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8572
10019
  }
8573
10020
  }
8574
10021
  async processMemoryUpdate(filePath, readFromStart) {
8575
- const baseName = (0, import_node_path12.basename)(filePath);
10022
+ const baseName = (0, import_node_path15.basename)(filePath);
8576
10023
  const resolvedSession = this.resolveMemorySession(filePath);
8577
10024
  if (!resolvedSession) {
8578
10025
  return;
@@ -8643,7 +10090,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8643
10090
  );
8644
10091
  }
8645
10092
  resolveMemorySession(filePath) {
8646
- const workspaceDirectory = (0, import_node_path12.basename)(filePath) === "MEMORY.md" ? (0, import_node_path12.dirname)(filePath) : (0, import_node_path12.dirname)((0, import_node_path12.dirname)(filePath));
10093
+ const workspaceDirectory = (0, import_node_path15.basename)(filePath) === "MEMORY.md" ? (0, import_node_path15.dirname)(filePath) : (0, import_node_path15.dirname)((0, import_node_path15.dirname)(filePath));
8647
10094
  for (const snapshot of this.sessionSnapshots.values()) {
8648
10095
  if (snapshot.cwd === workspaceDirectory) {
8649
10096
  return snapshot;
@@ -8652,19 +10099,19 @@ var OpenClawAdapter = class extends BaseAdapter {
8652
10099
  const sessionId = resolveSessionId({
8653
10100
  cwd: workspaceDirectory,
8654
10101
  projectPath: workspaceDirectory,
8655
- sessionId: `openclaw:${(0, import_node_path12.basename)(workspaceDirectory)}:memory`,
10102
+ sessionId: `openclaw:${(0, import_node_path15.basename)(workspaceDirectory)}:memory`,
8656
10103
  tool: this.name
8657
10104
  });
8658
10105
  return {
8659
10106
  cwd: workspaceDirectory,
8660
- project: (0, import_node_path12.basename)(workspaceDirectory) || workspaceDirectory,
10107
+ project: (0, import_node_path15.basename)(workspaceDirectory) || workspaceDirectory,
8661
10108
  sessionId
8662
10109
  };
8663
10110
  }
8664
10111
  async readIncrementalLines(filePath, readFromStart) {
8665
10112
  let fileContent;
8666
10113
  try {
8667
- fileContent = await (0, import_promises11.readFile)(filePath);
10114
+ fileContent = await (0, import_promises14.readFile)(filePath);
8668
10115
  } catch (error) {
8669
10116
  logger.debug({ error, filePath }, "OpenClaw source read skipped");
8670
10117
  return [];
@@ -8708,7 +10155,7 @@ var OpenClawAdapter = class extends BaseAdapter {
8708
10155
  });
8709
10156
  const data = {
8710
10157
  cwd,
8711
- project: cwd ? (0, import_node_path12.basename)(cwd) || cwd : void 0,
10158
+ project: cwd ? (0, import_node_path15.basename)(cwd) || cwd : void 0,
8712
10159
  projectPath: cwd,
8713
10160
  raw: {
8714
10161
  process: processInfo,
@@ -8750,18 +10197,18 @@ var OpenClawAdapter = class extends BaseAdapter {
8750
10197
  }
8751
10198
  }
8752
10199
  };
8753
- async function collectFilesRecursively4(directory, extension) {
10200
+ async function collectFilesRecursively7(directory, extension) {
8754
10201
  try {
8755
- const directoryEntries = await (0, import_promises11.readdir)(directory, {
10202
+ const directoryEntries = await (0, import_promises14.readdir)(directory, {
8756
10203
  withFileTypes: true
8757
10204
  });
8758
10205
  const nestedFiles = await Promise.all(
8759
10206
  directoryEntries.map(async (entry) => {
8760
- const entryPath = (0, import_node_path12.join)(directory, entry.name);
10207
+ const entryPath = (0, import_node_path15.join)(directory, entry.name);
8761
10208
  if (entry.isDirectory()) {
8762
- return await collectFilesRecursively4(entryPath, extension);
10209
+ return await collectFilesRecursively7(entryPath, extension);
8763
10210
  }
8764
- return (0, import_node_path12.extname)(entry.name) === extension ? [entryPath] : [];
10211
+ return (0, import_node_path15.extname)(entry.name) === extension ? [entryPath] : [];
8765
10212
  })
8766
10213
  );
8767
10214
  return nestedFiles.flat();
@@ -8818,12 +10265,12 @@ function buildOpenClawEventData(payload) {
8818
10265
  };
8819
10266
  }
8820
10267
  function extractOpenClawEventName(payload) {
8821
- const explicitEvent = getString6(payload, "event") ?? getString6(payload, "hook_event_name") ?? getString6(payload, "hookEventName");
10268
+ const explicitEvent = getString9(payload, "event") ?? getString9(payload, "hook_event_name") ?? getString9(payload, "hookEventName");
8822
10269
  if (explicitEvent) {
8823
10270
  return explicitEvent;
8824
10271
  }
8825
- const type = getString6(payload, "type");
8826
- const action = getString6(payload, "action");
10272
+ const type = getString9(payload, "type");
10273
+ const action = getString9(payload, "action");
8827
10274
  if (!type) {
8828
10275
  return void 0;
8829
10276
  }
@@ -8836,31 +10283,31 @@ function extractOpenClawEventName(payload) {
8836
10283
  return `${type}:${action}`;
8837
10284
  }
8838
10285
  function extractOpenClawSessionKey(payload) {
8839
- return getString6(payload, "sessionKey") ?? getString6(payload, "sessionId") ?? getString6(payload, "session_id") ?? getString6(getRecord5(payload.context), "sessionKey") ?? getString6(getRecord5(payload.context), "sessionId") ?? getString6(getRecord5(payload.context), "session_id") ?? getString6(getRecord5(payload.context), "sessionEntry");
10286
+ return getString9(payload, "sessionKey") ?? getString9(payload, "sessionId") ?? getString9(payload, "session_id") ?? getString9(getRecord8(payload.context), "sessionKey") ?? getString9(getRecord8(payload.context), "sessionId") ?? getString9(getRecord8(payload.context), "session_id") ?? getString9(getRecord8(payload.context), "sessionEntry");
8840
10287
  }
8841
10288
  function extractOpenClawCwd(payload) {
8842
- const context = getRecord5(payload.context);
8843
- return getString6(payload, "cwd") ?? getString6(payload, "workspaceDir") ?? getString6(context, "workspaceDir") ?? getString6(context, "cwd");
10289
+ const context = getRecord8(payload.context);
10290
+ return getString9(payload, "cwd") ?? getString9(payload, "workspaceDir") ?? getString9(context, "workspaceDir") ?? getString9(context, "cwd");
8844
10291
  }
8845
10292
  function extractOpenClawProject(payload) {
8846
10293
  const cwd = extractOpenClawCwd(payload);
8847
10294
  if (cwd) {
8848
- return (0, import_node_path12.basename)(cwd) || cwd;
10295
+ return (0, import_node_path15.basename)(cwd) || cwd;
8849
10296
  }
8850
- return getString6(payload, "project") ?? getString6(getRecord5(payload.context), "project");
10297
+ return getString9(payload, "project") ?? getString9(getRecord8(payload.context), "project");
8851
10298
  }
8852
10299
  function extractOpenClawActiveFile(payload) {
8853
10300
  const toolInput = extractOpenClawToolInput(payload);
8854
- return getString6(payload, "activeFile") ?? getString6(payload, "filePath") ?? getString6(getRecord5(payload.context), "filePath") ?? toolInput?.filePath;
10301
+ return getString9(payload, "activeFile") ?? getString9(payload, "filePath") ?? getString9(getRecord8(payload.context), "filePath") ?? toolInput?.filePath;
8855
10302
  }
8856
10303
  function extractOpenClawToolName(payload) {
8857
- return getString6(payload, "toolName") ?? getString6(payload, "tool_name") ?? getString6(getRecord5(payload.tool), "name") ?? getString6(getRecord5(payload.data), "toolName") ?? getString6(getRecord5(payload.result), "toolName");
10304
+ return getString9(payload, "toolName") ?? getString9(payload, "tool_name") ?? getString9(getRecord8(payload.tool), "name") ?? getString9(getRecord8(payload.data), "toolName") ?? getString9(getRecord8(payload.result), "toolName");
8858
10305
  }
8859
10306
  function extractOpenClawToolInput(payload) {
8860
- const context = getRecord5(payload.context);
8861
- const argsRecord = getRecord5(payload.toolInput) ?? getRecord5(payload.tool_input) ?? getRecord5(payload.params) ?? getRecord5(payload.arguments) ?? getRecord5(getRecord5(payload.tool)?.params) ?? getRecord5(getRecord5(payload.tool)?.arguments) ?? getRecord5(context?.params) ?? getRecord5(context?.arguments);
8862
- const filePath = getString6(payload, "filePath") ?? getString6(argsRecord, "filePath") ?? getString6(argsRecord, "path");
8863
- const command = getString6(payload, "command") ?? getString6(argsRecord, "command") ?? getString6(argsRecord, "cmd");
10307
+ const context = getRecord8(payload.context);
10308
+ const argsRecord = getRecord8(payload.toolInput) ?? getRecord8(payload.tool_input) ?? getRecord8(payload.params) ?? getRecord8(payload.arguments) ?? getRecord8(getRecord8(payload.tool)?.params) ?? getRecord8(getRecord8(payload.tool)?.arguments) ?? getRecord8(context?.params) ?? getRecord8(context?.arguments);
10309
+ const filePath = getString9(payload, "filePath") ?? getString9(argsRecord, "filePath") ?? getString9(argsRecord, "path");
10310
+ const command = getString9(payload, "command") ?? getString9(argsRecord, "command") ?? getString9(argsRecord, "cmd");
8864
10311
  if (!filePath && !command) {
8865
10312
  return void 0;
8866
10313
  }
@@ -8870,7 +10317,7 @@ function extractOpenClawToolInput(payload) {
8870
10317
  };
8871
10318
  }
8872
10319
  function extractOpenClawErrorMessage(payload) {
8873
- return getString6(payload, "error") ?? getString6(payload, "message") ?? getString6(getRecord5(payload.error), "message") ?? getString6(getRecord5(payload.result), "error");
10320
+ return getString9(payload, "error") ?? getString9(payload, "message") ?? getString9(getRecord8(payload.error), "message") ?? getString9(getRecord8(payload.result), "error");
8874
10321
  }
8875
10322
  function inferOpenClawErrorType(payload) {
8876
10323
  const errorMessage = extractOpenClawErrorMessage(payload);
@@ -8887,10 +10334,10 @@ function inferOpenClawErrorType(payload) {
8887
10334
  return OPENCLAW_ERROR_HINT.test(errorMessage) ? "tool_failure" : "api_error";
8888
10335
  }
8889
10336
  function extractOpenClawModel(payload) {
8890
- return getString6(payload, "model") ?? getString6(getRecord5(payload.context), "model") ?? getString6(getRecord5(payload.sessionEntry), "model");
10337
+ return getString9(payload, "model") ?? getString9(getRecord8(payload.context), "model") ?? getString9(getRecord8(payload.sessionEntry), "model");
8891
10338
  }
8892
10339
  function extractOpenClawTokens(payload) {
8893
- const directTokens = getNumber4(payload, "totalTokens") ?? getNumber4(payload, "tokensUsed") ?? getNumber4(getRecord5(payload.sessionEntry), "totalTokens") ?? getNumber4(getRecord5(payload.stats), "totalTokens");
10340
+ const directTokens = getNumber7(payload, "totalTokens") ?? getNumber7(payload, "tokensUsed") ?? getNumber7(getRecord8(payload.sessionEntry), "totalTokens") ?? getNumber7(getRecord8(payload.stats), "totalTokens");
8894
10341
  return directTokens === void 0 ? void 0 : Math.max(0, directTokens);
8895
10342
  }
8896
10343
  function isOpenClawCodingTool(toolName, toolInput) {
@@ -8899,18 +10346,18 @@ function isOpenClawCodingTool(toolName, toolInput) {
8899
10346
  );
8900
10347
  }
8901
10348
  function inferOpenClawSessionIdFromTranscriptPath(filePath) {
8902
- const fileName = (0, import_node_path12.basename)(filePath, (0, import_node_path12.extname)(filePath));
10349
+ const fileName = (0, import_node_path15.basename)(filePath, (0, import_node_path15.extname)(filePath));
8903
10350
  return fileName.length > 0 ? fileName : void 0;
8904
10351
  }
8905
10352
  function extractOpenClawTranscriptToolObservation(payload) {
8906
- const nestedMessage = getRecord5(payload.message) ?? getRecord5(payload.data);
8907
- const role = getString6(payload, "role") ?? getString6(nestedMessage, "role") ?? getString6(getRecord5(payload.entry), "role");
8908
- const toolName = extractOpenClawToolName(payload) ?? getString6(nestedMessage, "name") ?? getString6(getRecord5(payload.tool), "name");
10353
+ const nestedMessage = getRecord8(payload.message) ?? getRecord8(payload.data);
10354
+ const role = getString9(payload, "role") ?? getString9(nestedMessage, "role") ?? getString9(getRecord8(payload.entry), "role");
10355
+ const toolName = extractOpenClawToolName(payload) ?? getString9(nestedMessage, "name") ?? getString9(getRecord8(payload.tool), "name");
8909
10356
  const toolInput = extractOpenClawToolInput(payload);
8910
- const activeFile = extractOpenClawActiveFile(payload) ?? getString6(nestedMessage, "filePath") ?? toolInput?.filePath;
8911
- const content = getString6(nestedMessage, "content");
10357
+ const activeFile = extractOpenClawActiveFile(payload) ?? getString9(nestedMessage, "filePath") ?? toolInput?.filePath;
10358
+ const content = getString9(nestedMessage, "content");
8912
10359
  const looksLikeToolPayload = Boolean(
8913
- role === "tool" || role === "tool_result" || toolName || getString6(payload, "type") === "tool_result" || content?.includes("tool") || Array.isArray(getRecord5(nestedMessage)?.content)
10360
+ role === "tool" || role === "tool_result" || toolName || getString9(payload, "type") === "tool_result" || content?.includes("tool") || Array.isArray(getRecord8(nestedMessage)?.content)
8914
10361
  );
8915
10362
  if (!looksLikeToolPayload) {
8916
10363
  return null;
@@ -8923,37 +10370,37 @@ function extractOpenClawTranscriptToolObservation(payload) {
8923
10370
  };
8924
10371
  }
8925
10372
  function extractOpenClawTranscriptThinkingText(payload) {
8926
- const directReasoning = getString6(payload, "reasoning") ?? getString6(getRecord5(payload.message), "reasoning") ?? getString6(getRecord5(payload.data), "reasoning");
10373
+ const directReasoning = getString9(payload, "reasoning") ?? getString9(getRecord8(payload.message), "reasoning") ?? getString9(getRecord8(payload.data), "reasoning");
8927
10374
  if (directReasoning) {
8928
10375
  return directReasoning;
8929
10376
  }
8930
- const content = getRecord5(payload.message)?.content;
10377
+ const content = getRecord8(payload.message)?.content;
8931
10378
  if (!Array.isArray(content)) {
8932
10379
  return void 0;
8933
10380
  }
8934
10381
  for (const item of content) {
8935
- const itemRecord = getRecord5(item);
8936
- const itemType = getString6(itemRecord, "type");
10382
+ const itemRecord = getRecord8(item);
10383
+ const itemType = getString9(itemRecord, "type");
8937
10384
  if (itemType === "thinking" || itemType === "reasoning") {
8938
- return getString6(itemRecord, "thinking") ?? getString6(itemRecord, "text") ?? getString6(itemRecord, "content");
10385
+ return getString9(itemRecord, "thinking") ?? getString9(itemRecord, "text") ?? getString9(itemRecord, "content");
8939
10386
  }
8940
10387
  }
8941
10388
  return void 0;
8942
10389
  }
8943
10390
  function extractOpenClawTranscriptStreamingText(payload) {
8944
- const directText = getString6(payload, "text") ?? getString6(getRecord5(payload.message), "text") ?? getString6(getRecord5(payload.data), "text");
10391
+ const directText = getString9(payload, "text") ?? getString9(getRecord8(payload.message), "text") ?? getString9(getRecord8(payload.data), "text");
8945
10392
  if (directText) {
8946
10393
  return directText;
8947
10394
  }
8948
- const content = getRecord5(payload.message)?.content;
10395
+ const content = getRecord8(payload.message)?.content;
8949
10396
  if (!Array.isArray(content)) {
8950
10397
  return void 0;
8951
10398
  }
8952
10399
  for (const item of content) {
8953
- const itemRecord = getRecord5(item);
8954
- const itemType = getString6(itemRecord, "type");
10400
+ const itemRecord = getRecord8(item);
10401
+ const itemType = getString9(itemRecord, "type");
8955
10402
  if (itemType === "text" || itemType === "output_text") {
8956
- return getString6(itemRecord, "text") ?? getString6(itemRecord, "content");
10403
+ return getString9(itemRecord, "text") ?? getString9(itemRecord, "content");
8957
10404
  }
8958
10405
  }
8959
10406
  return void 0;
@@ -8961,30 +10408,30 @@ function extractOpenClawTranscriptStreamingText(payload) {
8961
10408
  function parseJsonRecord3(value) {
8962
10409
  try {
8963
10410
  const parsedValue = JSON.parse(value);
8964
- return getRecord5(parsedValue) ?? null;
10411
+ return getRecord8(parsedValue) ?? null;
8965
10412
  } catch {
8966
10413
  return null;
8967
10414
  }
8968
10415
  }
8969
- function getRecord5(value) {
10416
+ function getRecord8(value) {
8970
10417
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
8971
10418
  }
8972
- function getString6(payload, key) {
10419
+ function getString9(payload, key) {
8973
10420
  const value = payload?.[key];
8974
10421
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
8975
10422
  }
8976
- function getNumber4(payload, key) {
10423
+ function getNumber7(payload, key) {
8977
10424
  const value = payload?.[key];
8978
10425
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8979
10426
  }
8980
- function isRecord7(value) {
10427
+ function isRecord10(value) {
8981
10428
  return typeof value === "object" && value !== null && !Array.isArray(value);
8982
10429
  }
8983
10430
 
8984
10431
  // src/adapters/opencode.ts
8985
- var import_node_child_process10 = require("child_process");
8986
- var import_node_util10 = require("util");
8987
- var execFile10 = (0, import_node_util10.promisify)(import_node_child_process10.execFile);
10432
+ var import_node_child_process13 = require("child_process");
10433
+ var import_node_util13 = require("util");
10434
+ var execFile13 = (0, import_node_util13.promisify)(import_node_child_process13.execFile);
8988
10435
  var OPENCODE_CODING_TOOLS = /* @__PURE__ */ new Set(["edit", "multi_edit", "write"]);
8989
10436
  var OpenCodeAdapter = class extends BaseAdapter {
8990
10437
  displayName = "OpenCode";
@@ -9000,7 +10447,7 @@ var OpenCodeAdapter = class extends BaseAdapter {
9000
10447
  constructor(options) {
9001
10448
  super(options);
9002
10449
  this.pollIntervalMs = options.pollIntervalMs ?? 5e3;
9003
- this.processListCommand = options.processListCommand ?? (async () => await execFile10("pgrep", ["-lf", "opencode"]).then((result) => result.stdout));
10450
+ this.processListCommand = options.processListCommand ?? (async () => await execFile13("pgrep", ["-lf", "opencode"]).then((result) => result.stdout));
9004
10451
  }
9005
10452
  start() {
9006
10453
  if (this.getStatus().running) {
@@ -9036,11 +10483,11 @@ var OpenCodeAdapter = class extends BaseAdapter {
9036
10483
  });
9037
10484
  return;
9038
10485
  }
9039
- if (!isRecord8(payload)) {
10486
+ if (!isRecord11(payload)) {
9040
10487
  logger.warn({ payload }, "OpenCode payload must be an object");
9041
10488
  return;
9042
10489
  }
9043
- const eventType = getString7(payload, "type");
10490
+ const eventType = getString10(payload, "type");
9044
10491
  if (!eventType) {
9045
10492
  logger.warn({ payload }, "OpenCode payload is missing its event type");
9046
10493
  return;
@@ -9048,7 +10495,7 @@ var OpenCodeAdapter = class extends BaseAdapter {
9048
10495
  const sessionId = resolveSessionId({
9049
10496
  activeFile: extractOpenCodeActiveFile(payload),
9050
10497
  cwd: extractOpenCodeCwd(payload),
9051
- pid: getNumber5(payload, "pid"),
10498
+ pid: getNumber8(payload, "pid"),
9052
10499
  project: extractOpenCodeProject(payload),
9053
10500
  sessionId: extractOpenCodeSessionId(payload),
9054
10501
  tool: this.name
@@ -9058,7 +10505,7 @@ var OpenCodeAdapter = class extends BaseAdapter {
9058
10505
  // 📖 Pass process.env so the context detector can detect the terminal
9059
10506
  env: this.env ?? process.env,
9060
10507
  hookPayload: payload,
9061
- pid: getNumber5(payload, "pid"),
10508
+ pid: getNumber8(payload, "pid"),
9062
10509
  sessionId,
9063
10510
  source: "aisnitch://adapters/opencode"
9064
10511
  };
@@ -9068,7 +10515,7 @@ var OpenCodeAdapter = class extends BaseAdapter {
9068
10515
  errorMessage: extractOpenCodeErrorMessage(payload),
9069
10516
  errorType: extractOpenCodeErrorType(payload),
9070
10517
  // 📖 Extract model from payload — OpenCode may send it as "model" or nested in properties
9071
- model: getString7(payload, "model") ?? getString7(getRecord6(payload.properties), "model"),
10518
+ model: getString10(payload, "model") ?? getString10(getRecord9(payload.properties), "model"),
9072
10519
  project: extractOpenCodeProject(payload),
9073
10520
  raw: payload,
9074
10521
  toolInput: extractOpenCodeToolInput(payload),
@@ -9131,7 +10578,7 @@ var OpenCodeAdapter = class extends BaseAdapter {
9131
10578
  void this.pollOpenCodeProcesses();
9132
10579
  }
9133
10580
  async pollOpenCodeProcesses() {
9134
- const processes = await listProcesses6(this.processListCommand);
10581
+ const processes = await listProcesses9(this.processListCommand);
9135
10582
  if (processes.length > 0 && this.getStatus().activeSessions === 0) {
9136
10583
  const processInfo = processes[0];
9137
10584
  if (!processInfo) {
@@ -9174,7 +10621,7 @@ var OpenCodeAdapter = class extends BaseAdapter {
9174
10621
  }
9175
10622
  }
9176
10623
  };
9177
- async function listProcesses6(listCommand) {
10624
+ async function listProcesses9(listCommand) {
9178
10625
  if (process.platform === "win32") {
9179
10626
  return [];
9180
10627
  }
@@ -9205,33 +10652,33 @@ async function listProcesses6(listCommand) {
9205
10652
  }
9206
10653
  }
9207
10654
  function extractOpenCodeSessionId(payload) {
9208
- const directSessionId = getString7(payload, "sessionID") ?? getString7(payload, "sessionId");
10655
+ const directSessionId = getString10(payload, "sessionID") ?? getString10(payload, "sessionId");
9209
10656
  if (directSessionId) {
9210
10657
  return directSessionId;
9211
10658
  }
9212
- const properties = getRecord6(payload.properties);
9213
- return getString7(properties, "sessionID") ?? getString7(properties, "sessionId");
10659
+ const properties = getRecord9(payload.properties);
10660
+ return getString10(properties, "sessionID") ?? getString10(properties, "sessionId");
9214
10661
  }
9215
10662
  function extractOpenCodeCwd(payload) {
9216
- return getString7(payload, "cwd") ?? getString7(getRecord6(payload.properties), "cwd");
10663
+ return getString10(payload, "cwd") ?? getString10(getRecord9(payload.properties), "cwd");
9217
10664
  }
9218
10665
  function extractOpenCodeProject(payload) {
9219
- return getString7(payload, "project") ?? getString7(getRecord6(payload.properties), "project");
10666
+ return getString10(payload, "project") ?? getString10(getRecord9(payload.properties), "project");
9220
10667
  }
9221
10668
  function extractOpenCodeToolName(payload) {
9222
- const tool = getRecord6(payload.tool);
9223
- return getString7(tool, "name") ?? getString7(payload, "tool");
10669
+ const tool = getRecord9(payload.tool);
10670
+ return getString10(tool, "name") ?? getString10(payload, "tool");
9224
10671
  }
9225
10672
  function extractOpenCodeActiveFile(payload) {
9226
- return getString7(payload, "file") ?? getString7(getRecord6(payload.properties), "file") ?? extractOpenCodeToolInput(payload)?.filePath;
10673
+ return getString10(payload, "file") ?? getString10(getRecord9(payload.properties), "file") ?? extractOpenCodeToolInput(payload)?.filePath;
9227
10674
  }
9228
10675
  function extractOpenCodeToolInput(payload) {
9229
- const args = getRecord6(payload.args) ?? getRecord6(getRecord6(payload.output)?.args) ?? getRecord6(getRecord6(payload.properties)?.args);
10676
+ const args = getRecord9(payload.args) ?? getRecord9(getRecord9(payload.output)?.args) ?? getRecord9(getRecord9(payload.properties)?.args);
9230
10677
  if (!args) {
9231
10678
  return void 0;
9232
10679
  }
9233
- const command = getString7(args, "command") ?? getString7(args, "cmd");
9234
- const filePath = getString7(args, "filePath") ?? getString7(args, "file_path") ?? getString7(args, "path");
10680
+ const command = getString10(args, "command") ?? getString10(args, "cmd");
10681
+ const filePath = getString10(args, "filePath") ?? getString10(args, "file_path") ?? getString10(args, "path");
9235
10682
  if (!command && !filePath) {
9236
10683
  return void 0;
9237
10684
  }
@@ -9241,10 +10688,10 @@ function extractOpenCodeToolInput(payload) {
9241
10688
  };
9242
10689
  }
9243
10690
  function extractOpenCodeErrorMessage(payload) {
9244
- return getString7(getRecord6(payload.error), "message") ?? getString7(payload, "message");
10691
+ return getString10(getRecord9(payload.error), "message") ?? getString10(payload, "message");
9245
10692
  }
9246
10693
  function extractOpenCodeErrorType(payload) {
9247
- const rawType = getString7(payload, "errorType") ?? getString7(getRecord6(payload.error), "type");
10694
+ const rawType = getString10(payload, "errorType") ?? getString10(getRecord9(payload.error), "type");
9248
10695
  switch (rawType) {
9249
10696
  case "rate_limit":
9250
10697
  return "rate_limit";
@@ -9262,17 +10709,17 @@ function extractOpenCodeErrorType(payload) {
9262
10709
  function isOpenCodeCodingTool(toolName) {
9263
10710
  return toolName !== void 0 && OPENCODE_CODING_TOOLS.has(toolName);
9264
10711
  }
9265
- function isRecord8(value) {
10712
+ function isRecord11(value) {
9266
10713
  return typeof value === "object" && value !== null && !Array.isArray(value);
9267
10714
  }
9268
- function getRecord6(value) {
9269
- return isRecord8(value) ? value : void 0;
10715
+ function getRecord9(value) {
10716
+ return isRecord11(value) ? value : void 0;
9270
10717
  }
9271
- function getNumber5(payload, key) {
10718
+ function getNumber8(payload, key) {
9272
10719
  const value = payload[key];
9273
10720
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
9274
10721
  }
9275
- function getString7(payload, key) {
10722
+ function getString10(payload, key) {
9276
10723
  if (!payload) {
9277
10724
  return void 0;
9278
10725
  }
@@ -9356,8 +10803,11 @@ function createDefaultAdapters(options) {
9356
10803
  new AiderAdapter(options),
9357
10804
  new ClaudeCodeAdapter(options),
9358
10805
  new CopilotCLIAdapter(options),
10806
+ new CursorAdapter(options),
10807
+ new DevinAdapter(options),
9359
10808
  new GeminiCLIAdapter(options),
9360
10809
  new GooseAdapter(options),
10810
+ new KiloAdapter(options),
9361
10811
  new CodexAdapter(options),
9362
10812
  new OpenClawAdapter(options),
9363
10813
  new OpenCodeAdapter(options)
@@ -9381,7 +10831,7 @@ function getSocketPath(aisnitchHomePath) {
9381
10831
  if (process.platform === "win32") {
9382
10832
  return "\\\\.\\pipe\\aisnitch.sock";
9383
10833
  }
9384
- return (0, import_node_path13.join)(aisnitchHomePath, "aisnitch.sock");
10834
+ return (0, import_node_path16.join)(aisnitchHomePath, "aisnitch.sock");
9385
10835
  }
9386
10836
  var Pipeline = class {
9387
10837
  eventBus = new EventBus();
@@ -9685,7 +11135,7 @@ function formatEventDetail(event) {
9685
11135
  return segments.length > 0 ? segments.join(" | ") : null;
9686
11136
  }
9687
11137
  function getEventDetailSegments(event) {
9688
- const raw = getRecord7(event.data.raw);
11138
+ const raw = getRecord10(event.data.raw);
9689
11139
  const segments = [];
9690
11140
  switch (event.type) {
9691
11141
  case "agent.tool_call":
@@ -9727,7 +11177,7 @@ function getEventDetailSegments(event) {
9727
11177
  break;
9728
11178
  case "agent.asking_user":
9729
11179
  segments.push(
9730
- getString8(raw, "notification_type") ?? getString8(raw, "notificationType") ?? getString8(raw, "type")
11180
+ getString11(raw, "notification_type") ?? getString11(raw, "notificationType") ?? getString11(raw, "type")
9731
11181
  );
9732
11182
  segments.push(event.data.errorMessage ?? extractLooseString3(raw, [
9733
11183
  "message",
@@ -9794,17 +11244,17 @@ function extractContentPart(raw, partType, valueKey) {
9794
11244
  if (!raw) {
9795
11245
  return void 0;
9796
11246
  }
9797
- const message = getRecord7(raw.message);
11247
+ const message = getRecord10(raw.message);
9798
11248
  const content = message?.content ?? raw.content;
9799
11249
  if (!Array.isArray(content)) {
9800
11250
  return void 0;
9801
11251
  }
9802
11252
  for (const part of content) {
9803
- const record = getRecord7(part);
9804
- if (!record || getString8(record, "type") !== partType) {
11253
+ const record = getRecord10(part);
11254
+ if (!record || getString11(record, "type") !== partType) {
9805
11255
  continue;
9806
11256
  }
9807
- const value = getString8(record, valueKey);
11257
+ const value = getString11(record, valueKey);
9808
11258
  if (value) {
9809
11259
  return value;
9810
11260
  }
@@ -9816,12 +11266,12 @@ function extractLooseString3(raw, keys) {
9816
11266
  return void 0;
9817
11267
  }
9818
11268
  for (const key of keys) {
9819
- const directValue = getString8(raw, key);
11269
+ const directValue = getString11(raw, key);
9820
11270
  if (directValue) {
9821
11271
  return directValue;
9822
11272
  }
9823
- const nestedRecord = getRecord7(raw[key]);
9824
- const nestedValue = getString8(nestedRecord, "text") ?? getString8(nestedRecord, "message") ?? getString8(nestedRecord, "content");
11273
+ const nestedRecord = getRecord10(raw[key]);
11274
+ const nestedValue = getString11(nestedRecord, "text") ?? getString11(nestedRecord, "message") ?? getString11(nestedRecord, "content");
9825
11275
  if (nestedValue) {
9826
11276
  return nestedValue;
9827
11277
  }
@@ -9835,13 +11285,13 @@ function truncateSegment(value) {
9835
11285
  }
9836
11286
  return `${normalized.slice(0, DETAIL_SEGMENT_LIMIT - 1)}\u2026`;
9837
11287
  }
9838
- function isRecord9(value) {
11288
+ function isRecord12(value) {
9839
11289
  return typeof value === "object" && value !== null && !Array.isArray(value);
9840
11290
  }
9841
- function getRecord7(value) {
9842
- return isRecord9(value) ? value : void 0;
11291
+ function getRecord10(value) {
11292
+ return isRecord12(value) ? value : void 0;
9843
11293
  }
9844
- function getString8(payload, key) {
11294
+ function getString11(payload, key) {
9845
11295
  if (!payload) {
9846
11296
  return void 0;
9847
11297
  }
@@ -9851,23 +11301,27 @@ function getString8(payload, key) {
9851
11301
 
9852
11302
  // src/tui/theme.ts
9853
11303
  var TOOL_COLORS = {
9854
- aider: "#14b8a6",
9855
- amp: "#fb7185",
11304
+ "aider": "#14b8a6",
11305
+ "amp": "#fb7185",
11306
+ "augment-code": "#c084fc",
9856
11307
  "claude-code": "#f59e0b",
9857
- cline: "#f43f5e",
9858
- codex: "#f97316",
9859
- continue: "#06b6d4",
11308
+ "cline": "#f43f5e",
11309
+ "codex": "#f97316",
11310
+ "continue": "#06b6d4",
9860
11311
  "copilot-cli": "#60a5fa",
9861
- cursor: "#8b5cf6",
11312
+ "cursor": "#8b5cf6",
11313
+ "devin": "#f59e0b",
9862
11314
  "gemini-cli": "#38bdf8",
9863
- goose: "#ec4899",
9864
- kilo: "#84cc16",
9865
- openclaw: "#ef4444",
9866
- opencode: "#10b981",
9867
- openhands: "#facc15",
11315
+ "goose": "#ec4899",
11316
+ "kilo": "#84cc16",
11317
+ "kiro": "#06b6d4",
11318
+ "mistral": "#fb923c",
11319
+ "openhands": "#facc15",
11320
+ "openclaw": "#ef4444",
11321
+ "opencode": "#10b981",
9868
11322
  "qwen-code": "#22c55e",
9869
- unknown: "#94a3b8",
9870
- windsurf: "#c084fc"
11323
+ "unknown": "#94a3b8",
11324
+ "windsurf": "#a855f7"
9871
11325
  };
9872
11326
  var EVENT_COLORS = {
9873
11327
  "agent.asking_user": "#ef4444",
@@ -11621,10 +13075,10 @@ async function renderManagedTui(options) {
11621
13075
 
11622
13076
  // src/cli/pid.ts
11623
13077
  var import_node_fs3 = require("fs");
11624
- var import_promises12 = require("fs/promises");
13078
+ var import_promises15 = require("fs/promises");
11625
13079
  var import_node_net3 = require("net");
11626
13080
  var import_node_os4 = require("os");
11627
- var import_node_path14 = require("path");
13081
+ var import_node_path17 = require("path");
11628
13082
  var import_zod5 = require("zod");
11629
13083
  var DaemonStateSchema = import_zod5.z.strictObject({
11630
13084
  pid: import_zod5.z.number().int().positive(),
@@ -11639,14 +13093,14 @@ function getDefaultSocketPath(options) {
11639
13093
  if (process.platform === "win32") {
11640
13094
  return "\\\\.\\pipe\\aisnitch.sock";
11641
13095
  }
11642
- return (0, import_node_path14.join)(getAISnitchHomePath(options), "aisnitch.sock");
13096
+ return (0, import_node_path17.join)(getAISnitchHomePath(options), "aisnitch.sock");
11643
13097
  }
11644
13098
  async function cleanupSocketPathIfStale(socketPath) {
11645
13099
  if (process.platform === "win32") {
11646
13100
  return false;
11647
13101
  }
11648
13102
  try {
11649
- await (0, import_promises12.access)(socketPath, import_node_fs3.constants.F_OK);
13103
+ await (0, import_promises15.access)(socketPath, import_node_fs3.constants.F_OK);
11650
13104
  } catch (error) {
11651
13105
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
11652
13106
  return false;
@@ -11670,20 +13124,20 @@ async function cleanupSocketPathIfStale(socketPath) {
11670
13124
  if (!staleSocket) {
11671
13125
  return false;
11672
13126
  }
11673
- await (0, import_promises12.rm)(socketPath, { force: true });
13127
+ await (0, import_promises15.rm)(socketPath, { force: true });
11674
13128
  return true;
11675
13129
  }
11676
13130
  function getPidFilePath(options = {}) {
11677
- return (0, import_node_path14.join)(getAISnitchHomePath(options), "aisnitch.pid");
13131
+ return (0, import_node_path17.join)(getAISnitchHomePath(options), "aisnitch.pid");
11678
13132
  }
11679
13133
  function getDaemonStatePath(options = {}) {
11680
- return (0, import_node_path14.join)(getAISnitchHomePath(options), "daemon-state.json");
13134
+ return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon-state.json");
11681
13135
  }
11682
13136
  function getDaemonLogPath(options = {}) {
11683
- return (0, import_node_path14.join)(getAISnitchHomePath(options), "daemon.log");
13137
+ return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon.log");
11684
13138
  }
11685
13139
  function getLaunchAgentPath(options = {}) {
11686
- return (0, import_node_path14.join)(
13140
+ return (0, import_node_path17.join)(
11687
13141
  options.launchAgentHomeDirectory ?? (0, import_node_os4.homedir)(),
11688
13142
  "Library",
11689
13143
  "LaunchAgents",
@@ -11693,13 +13147,13 @@ function getLaunchAgentPath(options = {}) {
11693
13147
  async function writePid(pid, options = {}) {
11694
13148
  await ensureConfigDir(options);
11695
13149
  const pidFilePath = getPidFilePath(options);
11696
- await (0, import_promises12.writeFile)(pidFilePath, `${pid}
13150
+ await (0, import_promises15.writeFile)(pidFilePath, `${pid}
11697
13151
  `, "utf8");
11698
13152
  return pidFilePath;
11699
13153
  }
11700
13154
  async function readPid(options = {}) {
11701
13155
  try {
11702
- const rawPid = await (0, import_promises12.readFile)(getPidFilePath(options), "utf8");
13156
+ const rawPid = await (0, import_promises15.readFile)(getPidFilePath(options), "utf8");
11703
13157
  const parsedPid = Number.parseInt(rawPid.trim(), 10);
11704
13158
  if (!Number.isInteger(parsedPid) || parsedPid <= 0) {
11705
13159
  throw new Error("Invalid PID file contents.");
@@ -11713,13 +13167,13 @@ async function readPid(options = {}) {
11713
13167
  }
11714
13168
  }
11715
13169
  async function removePid(options = {}) {
11716
- await (0, import_promises12.rm)(getPidFilePath(options), { force: true });
13170
+ await (0, import_promises15.rm)(getPidFilePath(options), { force: true });
11717
13171
  }
11718
13172
  async function writeDaemonState(state, options = {}) {
11719
13173
  await ensureConfigDir(options);
11720
13174
  const daemonStatePath = getDaemonStatePath(options);
11721
13175
  const validatedState = DaemonStateSchema.parse(state);
11722
- await (0, import_promises12.writeFile)(
13176
+ await (0, import_promises15.writeFile)(
11723
13177
  daemonStatePath,
11724
13178
  `${JSON.stringify(validatedState, null, 2)}
11725
13179
  `,
@@ -11729,7 +13183,7 @@ async function writeDaemonState(state, options = {}) {
11729
13183
  }
11730
13184
  async function readDaemonState(options = {}) {
11731
13185
  try {
11732
- const rawJson = await (0, import_promises12.readFile)(getDaemonStatePath(options), "utf8");
13186
+ const rawJson = await (0, import_promises15.readFile)(getDaemonStatePath(options), "utf8");
11733
13187
  const parsedJson = JSON.parse(rawJson);
11734
13188
  return DaemonStateSchema.parse(parsedJson);
11735
13189
  } catch (error) {
@@ -11740,7 +13194,7 @@ async function readDaemonState(options = {}) {
11740
13194
  }
11741
13195
  }
11742
13196
  async function removeDaemonState(options = {}) {
11743
- await (0, import_promises12.rm)(getDaemonStatePath(options), { force: true });
13197
+ await (0, import_promises15.rm)(getDaemonStatePath(options), { force: true });
11744
13198
  }
11745
13199
  function isProcessRunning(pid) {
11746
13200
  try {
@@ -11776,13 +13230,13 @@ async function cleanupStaleDaemonFiles(options = {}) {
11776
13230
  }
11777
13231
  async function ensureLaunchAgentDir(options = {}) {
11778
13232
  const launchAgentPath = getLaunchAgentPath(options);
11779
- const directoryPath = (0, import_node_path14.dirname)(launchAgentPath);
11780
- await (0, import_promises12.mkdir)(directoryPath, { recursive: true });
13233
+ const directoryPath = (0, import_node_path17.dirname)(launchAgentPath);
13234
+ await (0, import_promises15.mkdir)(directoryPath, { recursive: true });
11781
13235
  return directoryPath;
11782
13236
  }
11783
13237
  async function getDaemonLogSize(options = {}) {
11784
13238
  try {
11785
- const logStats = await (0, import_promises12.stat)(getDaemonLogPath(options));
13239
+ const logStats = await (0, import_promises15.stat)(getDaemonLogPath(options));
11786
13240
  return logStats.size;
11787
13241
  } catch (error) {
11788
13242
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -11796,16 +13250,16 @@ function getEffectiveCliConfigPath(options = {}) {
11796
13250
  }
11797
13251
 
11798
13252
  // src/cli/auto-update.ts
11799
- var import_node_child_process11 = require("child_process");
11800
- var import_promises13 = require("fs/promises");
11801
- var import_promises14 = require("fs/promises");
11802
- var import_node_path15 = require("path");
13253
+ var import_node_child_process14 = require("child_process");
13254
+ var import_promises16 = require("fs/promises");
13255
+ var import_promises17 = require("fs/promises");
13256
+ var import_node_path18 = require("path");
11803
13257
  var AUTO_UPDATE_STATE_FILE = "auto-update.json";
11804
13258
  var AUTO_UPDATE_LOG_FILE = "auto-update.log";
11805
13259
  function createAutoUpdateController(dependencies = {}) {
11806
13260
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
11807
13261
  const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
11808
- const spawnImplementation = dependencies.spawn ?? import_node_child_process11.spawn;
13262
+ const spawnImplementation = dependencies.spawn ?? import_node_child_process14.spawn;
11809
13263
  return {
11810
13264
  runDetachedUpdate: async (options) => {
11811
13265
  const pathOptions = toPathOptions(options);
@@ -11813,8 +13267,8 @@ function createAutoUpdateController(dependencies = {}) {
11813
13267
  const command = resolveUpdateCommand(options.manager);
11814
13268
  const args = resolveUpdateArgs(options.manager);
11815
13269
  const aisnitchHomePath = getAISnitchHomePath(pathOptions);
11816
- await (0, import_promises13.mkdir)(aisnitchHomePath, { recursive: true });
11817
- const logFilePath = (0, import_node_path15.join)(aisnitchHomePath, AUTO_UPDATE_LOG_FILE);
13270
+ await (0, import_promises16.mkdir)(aisnitchHomePath, { recursive: true });
13271
+ const logFilePath = (0, import_node_path18.join)(aisnitchHomePath, AUTO_UPDATE_LOG_FILE);
11818
13272
  const startedAt = now().toISOString();
11819
13273
  await writeAutoUpdateState(
11820
13274
  {
@@ -11847,7 +13301,7 @@ function createAutoUpdateController(dependencies = {}) {
11847
13301
  });
11848
13302
  combinedLog += `[${now().toISOString()}] finished with code ${exitCode}
11849
13303
  `;
11850
- await (0, import_promises13.writeFile)(logFilePath, combinedLog, "utf8");
13304
+ await (0, import_promises16.writeFile)(logFilePath, combinedLog, "utf8");
11851
13305
  await writeAutoUpdateState(
11852
13306
  {
11853
13307
  attemptedVersion: options.latestVersion,
@@ -11938,7 +13392,7 @@ async function detectInstallManager(options) {
11938
13392
  }
11939
13393
  let resolvedCliPath = options.cliEntryPath;
11940
13394
  try {
11941
- resolvedCliPath = await (0, import_promises14.realpath)(options.cliEntryPath);
13395
+ resolvedCliPath = await (0, import_promises17.realpath)(options.cliEntryPath);
11942
13396
  } catch {
11943
13397
  }
11944
13398
  if (resolvedCliPath.includes("/Cellar/aisnitch/")) {
@@ -12002,7 +13456,7 @@ function resolveUpdateArgs(manager) {
12002
13456
  async function readAutoUpdateState(options) {
12003
13457
  const statePath = getAutoUpdateStatePath(options);
12004
13458
  try {
12005
- const rawJson = await (0, import_promises13.readFile)(statePath, "utf8");
13459
+ const rawJson = await (0, import_promises16.readFile)(statePath, "utf8");
12006
13460
  return JSON.parse(rawJson);
12007
13461
  } catch (error) {
12008
13462
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -12013,8 +13467,8 @@ async function readAutoUpdateState(options) {
12013
13467
  }
12014
13468
  async function writeAutoUpdateState(state, options) {
12015
13469
  const aisnitchHomePath = getAISnitchHomePath(options);
12016
- await (0, import_promises13.mkdir)(aisnitchHomePath, { recursive: true });
12017
- await (0, import_promises13.writeFile)(
13470
+ await (0, import_promises16.mkdir)(aisnitchHomePath, { recursive: true });
13471
+ await (0, import_promises16.writeFile)(
12018
13472
  getAutoUpdateStatePath(options),
12019
13473
  `${JSON.stringify(state, null, 2)}
12020
13474
  `,
@@ -12022,7 +13476,7 @@ async function writeAutoUpdateState(state, options) {
12022
13476
  );
12023
13477
  }
12024
13478
  function getAutoUpdateStatePath(options) {
12025
- return (0, import_node_path15.join)(getAISnitchHomePath(options), AUTO_UPDATE_STATE_FILE);
13479
+ return (0, import_node_path18.join)(getAISnitchHomePath(options), AUTO_UPDATE_STATE_FILE);
12026
13480
  }
12027
13481
  function toPathOptions(options) {
12028
13482
  return {
@@ -12120,7 +13574,7 @@ function flattenValue(lines, currentPath, value) {
12120
13574
  });
12121
13575
  return;
12122
13576
  }
12123
- if (isRecord10(value)) {
13577
+ if (isRecord13(value)) {
12124
13578
  const entries = Object.entries(value).sort(
12125
13579
  ([left], [right]) => left.localeCompare(right)
12126
13580
  );
@@ -12203,17 +13657,17 @@ function parseSocketMessage(data) {
12203
13657
  }
12204
13658
  }
12205
13659
  function isWelcomeMessage(payload) {
12206
- if (!isRecord10(payload)) {
13660
+ if (!isRecord13(payload)) {
12207
13661
  return false;
12208
13662
  }
12209
13663
  return payload.type === "welcome" && typeof payload.version === "string" && Array.isArray(payload.tools);
12210
13664
  }
12211
- function isRecord10(value) {
13665
+ function isRecord13(value) {
12212
13666
  return typeof value === "object" && value !== null && !Array.isArray(value);
12213
13667
  }
12214
13668
 
12215
13669
  // src/cli/runtime.ts
12216
- var execFile11 = (0, import_node_util11.promisify)(import_node_child_process12.execFile);
13670
+ var execFile14 = (0, import_node_util14.promisify)(import_node_child_process15.execFile);
12217
13671
  var DAEMON_READY_TIMEOUT_MS = 4e3;
12218
13672
  var DAEMON_READY_POLL_INTERVAL_MS = 100;
12219
13673
  var DAEMON_STOP_TIMEOUT_MS = 4e3;
@@ -12223,13 +13677,13 @@ function createCliRuntime(dependencies = {}) {
12223
13677
  const output = dependencies.output ?? createProcessOutput();
12224
13678
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
12225
13679
  const renderManagedTuiImplementation = dependencies.renderManagedTui ?? renderManagedTui;
12226
- const spawnImplementation = dependencies.spawn ?? import_node_child_process12.spawn;
13680
+ const spawnImplementation = dependencies.spawn ?? import_node_child_process15.spawn;
12227
13681
  const autoUpdateController = createAutoUpdateController({
12228
13682
  fetch: fetchImplementation,
12229
13683
  spawn: spawnImplementation
12230
13684
  });
12231
13685
  const execFileImplementation = dependencies.execFile ?? (async (file, args) => {
12232
- return await execFile11(file, [...args], {
13686
+ return await execFile14(file, [...args], {
12233
13687
  encoding: "utf8"
12234
13688
  });
12235
13689
  });
@@ -12368,8 +13822,8 @@ function createCliRuntime(dependencies = {}) {
12368
13822
  if (logSize < DAEMON_LOG_MAX_BYTES) {
12369
13823
  return;
12370
13824
  }
12371
- await (0, import_promises15.rm)(backupPath, { force: true });
12372
- await (0, import_promises15.rename)(logFilePath, backupPath);
13825
+ await (0, import_promises18.rm)(backupPath, { force: true });
13826
+ await (0, import_promises18.rename)(logFilePath, backupPath);
12373
13827
  }
12374
13828
  async function waitForDaemonReady(pathOptions) {
12375
13829
  const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
@@ -12404,7 +13858,7 @@ function createCliRuntime(dependencies = {}) {
12404
13858
  }
12405
13859
  async function readDaemonStartupFailure(pathOptions) {
12406
13860
  try {
12407
- const daemonLog = await (0, import_promises15.readFile)(getDaemonLogPath(pathOptions), "utf8");
13861
+ const daemonLog = await (0, import_promises18.readFile)(getDaemonLogPath(pathOptions), "utf8");
12408
13862
  const logLines = daemonLog.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0);
12409
13863
  const lastLine = logLines.at(-1);
12410
13864
  if (!lastLine) {
@@ -12774,7 +14228,7 @@ function createCliRuntime(dependencies = {}) {
12774
14228
  }
12775
14229
  const { config } = await loadEffectiveConfig(options);
12776
14230
  setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
12777
- const ephemeralHomeDirectory = await (0, import_promises15.mkdtemp)((0, import_node_path16.join)((0, import_node_os5.tmpdir)(), "aisnitch-mock-"));
14231
+ const ephemeralHomeDirectory = await (0, import_promises18.mkdtemp)((0, import_node_path19.join)((0, import_node_os5.tmpdir)(), "aisnitch-mock-"));
12778
14232
  const ephemeralPipeline = new Pipeline();
12779
14233
  const status2 = await ephemeralPipeline.start({
12780
14234
  config: {
@@ -12813,7 +14267,7 @@ function createCliRuntime(dependencies = {}) {
12813
14267
  } finally {
12814
14268
  await Promise.resolve(monitorClose());
12815
14269
  await ephemeralPipeline.stop();
12816
- await (0, import_promises15.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
14270
+ await (0, import_promises18.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
12817
14271
  }
12818
14272
  }
12819
14273
  async function install(options) {
@@ -12835,7 +14289,7 @@ function createCliRuntime(dependencies = {}) {
12835
14289
  await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
12836
14290
  } catch {
12837
14291
  }
12838
- await (0, import_promises15.writeFile)(launchAgentPath, plistContents, "utf8");
14292
+ await (0, import_promises18.writeFile)(launchAgentPath, plistContents, "utf8");
12839
14293
  await execFileImplementation("launchctl", ["bootstrap", domainTarget, launchAgentPath]);
12840
14294
  output.stdout(`AISnitch LaunchAgent installed at ${launchAgentPath}
12841
14295
  `);
@@ -12858,7 +14312,7 @@ function createCliRuntime(dependencies = {}) {
12858
14312
  } else {
12859
14313
  const { config } = await loadEffectiveConfig(options);
12860
14314
  setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
12861
- ephemeralHomeDirectory = await (0, import_promises15.mkdtemp)((0, import_node_path16.join)((0, import_node_os5.tmpdir)(), "aisnitch-wrap-"));
14315
+ ephemeralHomeDirectory = await (0, import_promises18.mkdtemp)((0, import_node_path19.join)((0, import_node_os5.tmpdir)(), "aisnitch-wrap-"));
12862
14316
  ephemeralPipeline = new Pipeline();
12863
14317
  await ephemeralPipeline.start({
12864
14318
  config: {
@@ -12913,7 +14367,7 @@ function createCliRuntime(dependencies = {}) {
12913
14367
  await ephemeralPipeline.stop();
12914
14368
  }
12915
14369
  if (ephemeralHomeDirectory !== null) {
12916
- await (0, import_promises15.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
14370
+ await (0, import_promises18.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
12917
14371
  }
12918
14372
  }
12919
14373
  process.exit(wrappedExitCode);
@@ -12933,7 +14387,7 @@ function createCliRuntime(dependencies = {}) {
12933
14387
  },
12934
14388
  pid: process.ppid > 1 ? process.ppid : void 0,
12935
14389
  source: "aisnitch://adapters/aider/notifications-command",
12936
- transcriptPath: (0, import_node_path16.join)(process.cwd(), ".aider.chat.history.md"),
14390
+ transcriptPath: (0, import_node_path19.join)(process.cwd(), ".aider.chat.history.md"),
12937
14391
  type: "agent.idle"
12938
14392
  }),
12939
14393
  headers: {
@@ -12955,7 +14409,7 @@ function createCliRuntime(dependencies = {}) {
12955
14409
  await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
12956
14410
  } catch {
12957
14411
  }
12958
- await (0, import_promises15.rm)(launchAgentPath, { force: true });
14412
+ await (0, import_promises18.rm)(launchAgentPath, { force: true });
12959
14413
  output.stdout(`AISnitch LaunchAgent removed from ${launchAgentPath}
12960
14414
  `);
12961
14415
  }
@@ -13134,7 +14588,7 @@ function joinSocketPath(pathOptions) {
13134
14588
  }
13135
14589
  function resolveCliEntryPath() {
13136
14590
  const cliEntryPath = process.argv[1];
13137
- if (!cliEntryPath || (0, import_node_path16.basename)(cliEntryPath).length === 0) {
14591
+ if (!cliEntryPath || (0, import_node_path19.basename)(cliEntryPath).length === 0) {
13138
14592
  throw new Error("Unable to resolve the AISnitch CLI entry path.");
13139
14593
  }
13140
14594
  return cliEntryPath;