aisnitch 0.2.17 → 0.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.cjs +2643 -1189
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +2620 -1166
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +2623 -1163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -3
- package/dist/index.d.ts +127 -3
- package/dist/index.js +2610 -1153
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.cjs
CHANGED
|
@@ -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.
|
|
44
|
+
var AISNITCH_VERSION = "0.2.18";
|
|
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
|
|
2466
|
+
var import_node_child_process15 = require("child_process");
|
|
2463
2467
|
var import_node_fs4 = require("fs");
|
|
2464
|
-
var
|
|
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
|
|
2468
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
2527
|
-
|
|
2528
|
-
cursor: "cursor",
|
|
2533
|
+
"cursor": "cursor",
|
|
2534
|
+
"devin": "devin",
|
|
2529
2535
|
"gemini-cli": "gemini",
|
|
2530
|
-
goose: "goose",
|
|
2531
|
-
kilo: "kilo",
|
|
2532
|
-
|
|
2533
|
-
|
|
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
|
|
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/
|
|
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
|
|
6708
|
-
"
|
|
6709
|
-
"
|
|
6710
|
-
"
|
|
6711
|
-
"
|
|
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
|
|
6714
|
-
displayName = "
|
|
6715
|
-
name = "
|
|
6724
|
+
var CursorAdapter = class extends BaseAdapter {
|
|
6725
|
+
displayName = "Cursor CLI";
|
|
6726
|
+
name = "cursor";
|
|
6716
6727
|
strategies = [
|
|
6717
|
-
"
|
|
6718
|
-
"
|
|
6719
|
-
"process-detect"
|
|
6728
|
+
"process-detect",
|
|
6729
|
+
"jsonl-watch"
|
|
6720
6730
|
];
|
|
6721
6731
|
fallbackProcessSessionId = null;
|
|
6722
|
-
|
|
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 () =>
|
|
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.
|
|
6743
|
-
const
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
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
|
-
|
|
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 }, "
|
|
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.
|
|
6772
|
-
this.
|
|
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 }, "
|
|
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
|
-
|
|
6805
|
-
|
|
6806
|
-
projectPath:
|
|
6807
|
-
sessionId: getString4(payload, "
|
|
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/
|
|
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:
|
|
6821
|
-
cwd,
|
|
6822
|
-
|
|
6823
|
-
|
|
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
|
-
|
|
6828
|
-
|
|
6829
|
-
toolName: extractGeminiToolName(payload)
|
|
6834
|
+
toolInput,
|
|
6835
|
+
toolName
|
|
6830
6836
|
};
|
|
6831
|
-
switch (
|
|
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 "
|
|
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 "
|
|
6847
|
-
|
|
6848
|
-
|
|
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 "
|
|
6862
|
+
case "tool_call":
|
|
6863
|
+
case "PreToolUse": {
|
|
6856
6864
|
await this.emitStateChange("agent.tool_call", sharedData, context);
|
|
6857
6865
|
return;
|
|
6858
6866
|
}
|
|
6859
|
-
case "
|
|
6860
|
-
|
|
6861
|
-
|
|
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 "
|
|
6869
|
-
|
|
6873
|
+
case "thinking":
|
|
6874
|
+
case "Thinking": {
|
|
6875
|
+
await this.emitStateChange("agent.thinking", sharedData, context);
|
|
6870
6876
|
return;
|
|
6871
6877
|
}
|
|
6872
|
-
case "
|
|
6873
|
-
|
|
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 "
|
|
6877
|
-
|
|
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
|
-
|
|
6881
|
-
|
|
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
|
|
6903
|
+
async processTranscriptUpdate(filePath, readFromStart) {
|
|
6886
6904
|
let fileContent;
|
|
6887
6905
|
try {
|
|
6888
|
-
fileContent = await (0, import_promises9.readFile)(filePath
|
|
6906
|
+
fileContent = await (0, import_promises9.readFile)(filePath);
|
|
6889
6907
|
} catch (error) {
|
|
6890
|
-
logger.debug({ error, filePath }, "
|
|
6908
|
+
logger.debug({ error, filePath }, "Cursor transcript read skipped");
|
|
6891
6909
|
return;
|
|
6892
6910
|
}
|
|
6893
|
-
|
|
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
|
-
|
|
6931
|
+
parsedLine = JSON.parse(line);
|
|
6896
6932
|
} catch (error) {
|
|
6897
|
-
logger.warn({ error,
|
|
6933
|
+
logger.warn({ error, transcriptPath }, "Cursor transcript line is not valid JSON");
|
|
6898
6934
|
return;
|
|
6899
6935
|
}
|
|
6900
|
-
if (!
|
|
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
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
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
|
-
|
|
6929
|
-
hookPayload:
|
|
6945
|
+
env: process.env,
|
|
6946
|
+
hookPayload: parsedLine,
|
|
6930
6947
|
sessionId,
|
|
6931
|
-
source: "aisnitch://adapters/
|
|
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
|
-
|
|
6935
|
-
|
|
6936
|
-
raw:
|
|
6956
|
+
activeFile: toolInput?.filePath,
|
|
6957
|
+
model: getString4(data, "model"),
|
|
6958
|
+
raw: parsedLine,
|
|
6959
|
+
toolInput,
|
|
6960
|
+
toolName
|
|
6937
6961
|
};
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
case "
|
|
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 "
|
|
6945
|
-
case "
|
|
6946
|
-
await this.emitStateChange("
|
|
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
|
-
|
|
6954
|
-
|
|
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
|
|
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
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
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.
|
|
7043
|
+
void this.pollCursorProcesses();
|
|
7015
7044
|
}, this.pollIntervalMs);
|
|
7016
7045
|
this.processPoller.unref();
|
|
7017
|
-
void this.
|
|
7046
|
+
void this.pollCursorProcesses();
|
|
7018
7047
|
}
|
|
7019
|
-
async
|
|
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 = `
|
|
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/
|
|
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/
|
|
7076
|
+
source: "aisnitch://adapters/cursor/process-detect"
|
|
7058
7077
|
}
|
|
7059
7078
|
);
|
|
7060
7079
|
}
|
|
7061
7080
|
}
|
|
7062
7081
|
};
|
|
7063
|
-
async function collectFilesRecursively3(directoryPath,
|
|
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,
|
|
7089
|
+
return await collectFilesRecursively3(entryPath, extension);
|
|
7073
7090
|
}
|
|
7074
|
-
return entry.name
|
|
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
|
|
7086
|
-
|
|
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
|
|
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
|
|
7111
|
-
|
|
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
|
|
7116
|
-
const
|
|
7117
|
-
|
|
7118
|
-
|
|
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
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
|
7208
|
-
|
|
7209
|
-
"
|
|
7210
|
-
"
|
|
7211
|
-
"
|
|
7212
|
-
"
|
|
7213
|
-
"
|
|
7214
|
-
"
|
|
7215
|
-
"
|
|
7216
|
-
"
|
|
7217
|
-
"
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
"
|
|
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
|
-
"
|
|
7231
|
-
"
|
|
7232
|
-
"
|
|
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
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
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 () =>
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
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.
|
|
7272
|
-
this.
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
this.
|
|
7282
|
-
|
|
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.
|
|
7292
|
-
await this.
|
|
7293
|
-
this.
|
|
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.
|
|
7310
|
-
this.
|
|
7311
|
-
this.
|
|
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
|
|
7318
|
-
|
|
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
|
-
|
|
7322
|
-
|
|
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
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
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
|
-
|
|
7345
|
-
|
|
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
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
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
|
|
7368
|
-
let
|
|
7413
|
+
async processTranscriptLine(line, transcriptPath) {
|
|
7414
|
+
let parsedLine;
|
|
7369
7415
|
try {
|
|
7370
|
-
|
|
7371
|
-
this.databasePath,
|
|
7372
|
-
DEFAULT_SQLITE_QUERY
|
|
7373
|
-
);
|
|
7416
|
+
parsedLine = JSON.parse(line);
|
|
7374
7417
|
} catch (error) {
|
|
7375
|
-
logger.
|
|
7376
|
-
return
|
|
7418
|
+
logger.warn({ error, transcriptPath }, "Devin transcript line is not valid JSON");
|
|
7419
|
+
return;
|
|
7377
7420
|
}
|
|
7378
|
-
|
|
7379
|
-
|
|
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
|
-
|
|
7386
|
-
|
|
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
|
|
7391
|
-
let statusResponse;
|
|
7505
|
+
async seedTranscriptOffsets() {
|
|
7392
7506
|
try {
|
|
7393
|
-
|
|
7394
|
-
|
|
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
|
|
7397
|
-
logger.debug({ error }, "Goosed status endpoint is unavailable");
|
|
7398
|
-
return;
|
|
7517
|
+
} catch {
|
|
7399
7518
|
}
|
|
7400
|
-
|
|
7519
|
+
}
|
|
7520
|
+
startProcessPolling() {
|
|
7521
|
+
if (this.pollIntervalMs <= 0) {
|
|
7401
7522
|
return;
|
|
7402
7523
|
}
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
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
|
-
|
|
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 (
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
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
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
}
|
|
7473
|
-
const
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
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
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
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
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
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
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
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
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
);
|
|
9204
|
+
case "session.end":
|
|
9205
|
+
case "sessionEnd":
|
|
9206
|
+
case "SessionEnd": {
|
|
9207
|
+
await this.emitStateChange("session.end", sharedData, context);
|
|
9208
|
+
return;
|
|
7555
9209
|
}
|
|
7556
|
-
|
|
7557
|
-
|
|
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
|
|
7561
|
-
|
|
7562
|
-
if (payloadText.length === 0) {
|
|
7563
|
-
return;
|
|
7564
|
-
}
|
|
7565
|
-
let parsedPayload;
|
|
9268
|
+
async processTranscriptUpdate(filePath, readFromStart) {
|
|
9269
|
+
let fileContent;
|
|
7566
9270
|
try {
|
|
7567
|
-
|
|
9271
|
+
fileContent = await (0, import_promises13.readFile)(filePath);
|
|
7568
9272
|
} catch (error) {
|
|
7569
|
-
logger.
|
|
9273
|
+
logger.debug({ error, filePath }, "Kilo transcript read skipped");
|
|
7570
9274
|
return;
|
|
7571
9275
|
}
|
|
7572
|
-
|
|
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
|
|
7575
|
-
|
|
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
|
-
|
|
7579
|
-
if (fingerprint && !this.markSessionEventSeen(snapshot.sessionId, fingerprint)) {
|
|
9301
|
+
if (!isRecord9(parsedLine)) {
|
|
7580
9302
|
return;
|
|
7581
9303
|
}
|
|
7582
|
-
const
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
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 "
|
|
7589
|
-
|
|
7590
|
-
await this.emitStateChange(
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
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
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
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 "
|
|
7615
|
-
|
|
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
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
createApiHeaders() {
|
|
7835
|
-
if (!this.apiKey) {
|
|
7836
|
-
return void 0;
|
|
9404
|
+
}
|
|
9405
|
+
startProcessPolling() {
|
|
9406
|
+
if (this.pollIntervalMs <= 0) {
|
|
9407
|
+
return;
|
|
7837
9408
|
}
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
};
|
|
9409
|
+
this.processPoller = setInterval(() => {
|
|
9410
|
+
void this.pollKiloProcesses();
|
|
9411
|
+
}, this.pollIntervalMs);
|
|
9412
|
+
this.processPoller.unref();
|
|
9413
|
+
void this.pollKiloProcesses();
|
|
7841
9414
|
}
|
|
7842
|
-
async
|
|
7843
|
-
const processes = await
|
|
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 = `
|
|
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/
|
|
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/
|
|
9443
|
+
source: "aisnitch://adapters/kilo/process-detect"
|
|
7881
9444
|
}
|
|
7882
9445
|
);
|
|
7883
9446
|
}
|
|
7884
9447
|
}
|
|
7885
9448
|
};
|
|
7886
|
-
function
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
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
|
|
7940
|
-
|
|
7941
|
-
|
|
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
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
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
|
|
7992
|
-
|
|
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
|
|
8002
|
-
|
|
8003
|
-
|
|
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
|
|
9489
|
+
if (/context|token.?limit|too.?long/i.test(message)) {
|
|
8009
9490
|
return "context_overflow";
|
|
8010
9491
|
}
|
|
8011
|
-
if (/tool/
|
|
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
|
|
8017
|
-
|
|
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
|
|
8038
|
-
return
|
|
8039
|
-
} catch {
|
|
8040
|
-
|
|
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
|
|
8056
|
-
|
|
8057
|
-
|
|
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
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
return [];
|
|
9514
|
+
const pidText = match[1];
|
|
9515
|
+
const command = match[2];
|
|
9516
|
+
if (!pidText || !command) {
|
|
9517
|
+
return null;
|
|
8074
9518
|
}
|
|
8075
|
-
return
|
|
9519
|
+
return {
|
|
9520
|
+
command,
|
|
9521
|
+
pid: Number.parseInt(pidText, 10)
|
|
9522
|
+
};
|
|
8076
9523
|
}
|
|
8077
|
-
function
|
|
8078
|
-
return
|
|
9524
|
+
function isErrnoException7(error) {
|
|
9525
|
+
return error instanceof Error && "code" in error;
|
|
8079
9526
|
}
|
|
8080
|
-
function
|
|
9527
|
+
function isRecord9(value) {
|
|
8081
9528
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8082
9529
|
}
|
|
8083
|
-
function
|
|
8084
|
-
|
|
8085
|
-
|
|
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 === "
|
|
9535
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
8089
9536
|
}
|
|
8090
|
-
function
|
|
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 === "
|
|
9542
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
8096
9543
|
}
|
|
8097
9544
|
|
|
8098
9545
|
// src/adapters/openclaw.ts
|
|
8099
|
-
var
|
|
8100
|
-
var
|
|
8101
|
-
var
|
|
8102
|
-
var
|
|
8103
|
-
var
|
|
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
|
|
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,
|
|
8141
|
-
this.agentsDirectory = options.agentsDirectory ?? (0,
|
|
8142
|
-
this.commandsLogPath = options.commandsLogPath ?? (0,
|
|
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
|
|
9594
|
+
this.processListCommand = options.processListCommand ?? (async () => await execFile12("pgrep", ["-ifl", "openclaw"]).then(
|
|
8148
9595
|
(result) => result.stdout
|
|
8149
9596
|
));
|
|
8150
|
-
this.watcherFactory = options.watcherFactory ??
|
|
9597
|
+
this.watcherFactory = options.watcherFactory ?? import_chokidar10.watch;
|
|
8151
9598
|
this.memoryRootGlobs = [
|
|
8152
|
-
(0,
|
|
8153
|
-
(0,
|
|
8154
|
-
(0,
|
|
8155
|
-
(0,
|
|
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,
|
|
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 (!
|
|
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:
|
|
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:
|
|
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
|
|
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,
|
|
9883
|
+
for (const root of [(0, import_node_path15.join)(this.getUserHomeDirectory(), ".openclaw", "workspace")]) {
|
|
8437
9884
|
await Promise.all([
|
|
8438
|
-
this.seedDirectoryFileOffsets((0,
|
|
8439
|
-
this.seedFileOffset((0,
|
|
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
|
|
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,
|
|
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,
|
|
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 =
|
|
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,
|
|
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 (
|
|
9963
|
+
if (getString9(line, "type") === "session") {
|
|
8517
9964
|
await this.emitOpenClawSessionStart(sharedData, context);
|
|
8518
9965
|
return;
|
|
8519
9966
|
}
|
|
8520
|
-
if (
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
10200
|
+
async function collectFilesRecursively7(directory, extension) {
|
|
8754
10201
|
try {
|
|
8755
|
-
const directoryEntries = await (0,
|
|
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,
|
|
10207
|
+
const entryPath = (0, import_node_path15.join)(directory, entry.name);
|
|
8761
10208
|
if (entry.isDirectory()) {
|
|
8762
|
-
return await
|
|
10209
|
+
return await collectFilesRecursively7(entryPath, extension);
|
|
8763
10210
|
}
|
|
8764
|
-
return (0,
|
|
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 =
|
|
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 =
|
|
8826
|
-
const 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
|
|
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 =
|
|
8843
|
-
return
|
|
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,
|
|
10295
|
+
return (0, import_node_path15.basename)(cwd) || cwd;
|
|
8849
10296
|
}
|
|
8850
|
-
return
|
|
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
|
|
10301
|
+
return getString9(payload, "activeFile") ?? getString9(payload, "filePath") ?? getString9(getRecord8(payload.context), "filePath") ?? toolInput?.filePath;
|
|
8855
10302
|
}
|
|
8856
10303
|
function extractOpenClawToolName(payload) {
|
|
8857
|
-
return
|
|
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 =
|
|
8861
|
-
const argsRecord =
|
|
8862
|
-
const filePath =
|
|
8863
|
-
const command =
|
|
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
|
|
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
|
|
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 =
|
|
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,
|
|
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 =
|
|
8907
|
-
const role =
|
|
8908
|
-
const toolName = extractOpenClawToolName(payload) ??
|
|
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) ??
|
|
8911
|
-
const 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 ||
|
|
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 =
|
|
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 =
|
|
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 =
|
|
8936
|
-
const itemType =
|
|
10382
|
+
const itemRecord = getRecord8(item);
|
|
10383
|
+
const itemType = getString9(itemRecord, "type");
|
|
8937
10384
|
if (itemType === "thinking" || itemType === "reasoning") {
|
|
8938
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
8954
|
-
const itemType =
|
|
10400
|
+
const itemRecord = getRecord8(item);
|
|
10401
|
+
const itemType = getString9(itemRecord, "type");
|
|
8955
10402
|
if (itemType === "text" || itemType === "output_text") {
|
|
8956
|
-
return
|
|
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
|
|
10411
|
+
return getRecord8(parsedValue) ?? null;
|
|
8965
10412
|
} catch {
|
|
8966
10413
|
return null;
|
|
8967
10414
|
}
|
|
8968
10415
|
}
|
|
8969
|
-
function
|
|
10416
|
+
function getRecord8(value) {
|
|
8970
10417
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
8971
10418
|
}
|
|
8972
|
-
function
|
|
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
|
|
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
|
|
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
|
|
8986
|
-
var
|
|
8987
|
-
var
|
|
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
|
|
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 (!
|
|
10486
|
+
if (!isRecord11(payload)) {
|
|
9040
10487
|
logger.warn({ payload }, "OpenCode payload must be an object");
|
|
9041
10488
|
return;
|
|
9042
10489
|
}
|
|
9043
|
-
const eventType =
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
10655
|
+
const directSessionId = getString10(payload, "sessionID") ?? getString10(payload, "sessionId");
|
|
9209
10656
|
if (directSessionId) {
|
|
9210
10657
|
return directSessionId;
|
|
9211
10658
|
}
|
|
9212
|
-
const properties =
|
|
9213
|
-
return
|
|
10659
|
+
const properties = getRecord9(payload.properties);
|
|
10660
|
+
return getString10(properties, "sessionID") ?? getString10(properties, "sessionId");
|
|
9214
10661
|
}
|
|
9215
10662
|
function extractOpenCodeCwd(payload) {
|
|
9216
|
-
return
|
|
10663
|
+
return getString10(payload, "cwd") ?? getString10(getRecord9(payload.properties), "cwd");
|
|
9217
10664
|
}
|
|
9218
10665
|
function extractOpenCodeProject(payload) {
|
|
9219
|
-
return
|
|
10666
|
+
return getString10(payload, "project") ?? getString10(getRecord9(payload.properties), "project");
|
|
9220
10667
|
}
|
|
9221
10668
|
function extractOpenCodeToolName(payload) {
|
|
9222
|
-
const tool =
|
|
9223
|
-
return
|
|
10669
|
+
const tool = getRecord9(payload.tool);
|
|
10670
|
+
return getString10(tool, "name") ?? getString10(payload, "tool");
|
|
9224
10671
|
}
|
|
9225
10672
|
function extractOpenCodeActiveFile(payload) {
|
|
9226
|
-
return
|
|
10673
|
+
return getString10(payload, "file") ?? getString10(getRecord9(payload.properties), "file") ?? extractOpenCodeToolInput(payload)?.filePath;
|
|
9227
10674
|
}
|
|
9228
10675
|
function extractOpenCodeToolInput(payload) {
|
|
9229
|
-
const 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 =
|
|
9234
|
-
const filePath =
|
|
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
|
|
10691
|
+
return getString10(getRecord9(payload.error), "message") ?? getString10(payload, "message");
|
|
9245
10692
|
}
|
|
9246
10693
|
function extractOpenCodeErrorType(payload) {
|
|
9247
|
-
const rawType =
|
|
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
|
|
10712
|
+
function isRecord11(value) {
|
|
9266
10713
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9267
10714
|
}
|
|
9268
|
-
function
|
|
9269
|
-
return
|
|
10715
|
+
function getRecord9(value) {
|
|
10716
|
+
return isRecord11(value) ? value : void 0;
|
|
9270
10717
|
}
|
|
9271
|
-
function
|
|
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
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
9804
|
-
if (!record ||
|
|
11253
|
+
const record = getRecord10(part);
|
|
11254
|
+
if (!record || getString11(record, "type") !== partType) {
|
|
9805
11255
|
continue;
|
|
9806
11256
|
}
|
|
9807
|
-
const value =
|
|
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 =
|
|
11269
|
+
const directValue = getString11(raw, key);
|
|
9820
11270
|
if (directValue) {
|
|
9821
11271
|
return directValue;
|
|
9822
11272
|
}
|
|
9823
|
-
const nestedRecord =
|
|
9824
|
-
const nestedValue =
|
|
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
|
|
11288
|
+
function isRecord12(value) {
|
|
9839
11289
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9840
11290
|
}
|
|
9841
|
-
function
|
|
9842
|
-
return
|
|
11291
|
+
function getRecord10(value) {
|
|
11292
|
+
return isRecord12(value) ? value : void 0;
|
|
9843
11293
|
}
|
|
9844
|
-
function
|
|
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
|
-
|
|
9866
|
-
|
|
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: "#
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
13127
|
+
await (0, import_promises15.rm)(socketPath, { force: true });
|
|
11674
13128
|
return true;
|
|
11675
13129
|
}
|
|
11676
13130
|
function getPidFilePath(options = {}) {
|
|
11677
|
-
return (0,
|
|
13131
|
+
return (0, import_node_path17.join)(getAISnitchHomePath(options), "aisnitch.pid");
|
|
11678
13132
|
}
|
|
11679
13133
|
function getDaemonStatePath(options = {}) {
|
|
11680
|
-
return (0,
|
|
13134
|
+
return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon-state.json");
|
|
11681
13135
|
}
|
|
11682
13136
|
function getDaemonLogPath(options = {}) {
|
|
11683
|
-
return (0,
|
|
13137
|
+
return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon.log");
|
|
11684
13138
|
}
|
|
11685
13139
|
function getLaunchAgentPath(options = {}) {
|
|
11686
|
-
return (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
11780
|
-
await (0,
|
|
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,
|
|
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
|
|
11800
|
-
var
|
|
11801
|
-
var
|
|
11802
|
-
var
|
|
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 ??
|
|
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,
|
|
11817
|
-
const logFilePath = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
12017
|
-
await (0,
|
|
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,
|
|
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 (
|
|
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 (!
|
|
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
|
|
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
|
|
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 ??
|
|
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
|
|
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,
|
|
12372
|
-
await (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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;
|