@made-by-moonlight/athene-plugin-agent-kimicode 0.9.1
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/LICENSE +22 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +418 -0
- package/dist/index.js.map +1 -0
- package/dist/session-discovery.d.ts +35 -0
- package/dist/session-discovery.d.ts.map +1 -0
- package/dist/session-discovery.js +425 -0
- package/dist/session-discovery.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Composio, Inc.
|
|
4
|
+
Copyright (c) 2026 slievr (Athene fork)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Agent } from "@made-by-moonlight/athene-core";
|
|
2
|
+
import { _resetSessionMatchCache } from "./session-discovery.js";
|
|
3
|
+
export declare const manifest: {
|
|
4
|
+
name: string;
|
|
5
|
+
slot: "agent";
|
|
6
|
+
description: string;
|
|
7
|
+
version: string;
|
|
8
|
+
displayName: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function create(): Agent;
|
|
11
|
+
export { _resetSessionMatchCache };
|
|
12
|
+
export declare function detect(): boolean;
|
|
13
|
+
declare const _default: {
|
|
14
|
+
manifest: {
|
|
15
|
+
name: string;
|
|
16
|
+
slot: "agent";
|
|
17
|
+
description: string;
|
|
18
|
+
version: string;
|
|
19
|
+
displayName: string;
|
|
20
|
+
};
|
|
21
|
+
create: typeof create;
|
|
22
|
+
detect: typeof detect;
|
|
23
|
+
};
|
|
24
|
+
export default _default;
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAWL,KAAK,KAAK,EAWX,MAAM,gCAAgC,CAAC;AAMxC,OAAO,EAIL,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AAsEhC,eAAO,MAAM,QAAQ;;;;;;CAMpB,CAAC;AAkUF,wBAAgB,MAAM,IAAI,KAAK,CAE9B;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAC;AAanC,wBAAgB,MAAM,IAAI,OAAO,CAehC;;;;;;;;;;;;AAED,wBAA0E"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { DEFAULT_READY_THRESHOLD_MS, DEFAULT_ACTIVE_WINDOW_MS, isWindows, shellEscape, normalizeAgentPermissionMode, setupPathWrapperWorkspace, readLastActivityEntry, checkActivityLogState, getActivityFallbackState, recordTerminalActivity, } from "@made-by-moonlight/athene-core";
|
|
2
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { createReadStream, readFileSync } from "node:fs";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { captureKimiBaseline, findKimiSessionMatch, isKimiSessionFile, _resetSessionMatchCache, } from "./session-discovery.js";
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
/** Max chars we keep from a wire.jsonl user-input summary. */
|
|
10
|
+
const SUMMARY_MAX_CHARS = 120;
|
|
11
|
+
/** Max bytes of wire.jsonl we read looking for the first TurnBegin. */
|
|
12
|
+
const SUMMARY_SCAN_BYTE_LIMIT = 1_000_000;
|
|
13
|
+
/**
|
|
14
|
+
* Extract the first user prompt from a session's wire.jsonl as a fallback
|
|
15
|
+
* summary. Stops after the first TurnBegin or after reading ~1 MB (whichever
|
|
16
|
+
* comes first) so we never slurp huge session logs.
|
|
17
|
+
*/
|
|
18
|
+
async function extractKimiSummary(sessionDir) {
|
|
19
|
+
const wirePath = join(sessionDir, "wire.jsonl");
|
|
20
|
+
// Sandbox check: refuse to follow a symlink (or open a socket / FIFO) at
|
|
21
|
+
// wire.jsonl. The sessionDir was already verified, but its children could
|
|
22
|
+
// still be planted as symlinks pointing at /etc/passwd or /dev/zero.
|
|
23
|
+
if (!(await isKimiSessionFile(wirePath)))
|
|
24
|
+
return null;
|
|
25
|
+
let summary = null;
|
|
26
|
+
let stream = null;
|
|
27
|
+
let rl = null;
|
|
28
|
+
try {
|
|
29
|
+
stream = createReadStream(wirePath, { encoding: "utf-8" });
|
|
30
|
+
rl = createInterface({
|
|
31
|
+
input: stream,
|
|
32
|
+
crlfDelay: Infinity,
|
|
33
|
+
});
|
|
34
|
+
let bytes = 0;
|
|
35
|
+
for await (const line of rl) {
|
|
36
|
+
bytes += line.length;
|
|
37
|
+
if (bytes > SUMMARY_SCAN_BYTE_LIMIT)
|
|
38
|
+
break;
|
|
39
|
+
const trimmed = line.trim();
|
|
40
|
+
if (!trimmed)
|
|
41
|
+
continue;
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(trimmed);
|
|
44
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
45
|
+
continue;
|
|
46
|
+
const entry = parsed;
|
|
47
|
+
const message = entry["message"];
|
|
48
|
+
if (!message || typeof message !== "object" || Array.isArray(message))
|
|
49
|
+
continue;
|
|
50
|
+
const msg = message;
|
|
51
|
+
if (msg["type"] !== "TurnBegin")
|
|
52
|
+
continue;
|
|
53
|
+
const payload = msg["payload"];
|
|
54
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
55
|
+
continue;
|
|
56
|
+
const userInput = payload["user_input"];
|
|
57
|
+
if (typeof userInput === "string" && userInput.length > 0) {
|
|
58
|
+
summary =
|
|
59
|
+
userInput.length > SUMMARY_MAX_CHARS
|
|
60
|
+
? userInput.slice(0, SUMMARY_MAX_CHARS) + "..."
|
|
61
|
+
: userInput;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Skip malformed line
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
rl?.close();
|
|
75
|
+
stream?.destroy();
|
|
76
|
+
}
|
|
77
|
+
return summary;
|
|
78
|
+
}
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Plugin Manifest
|
|
81
|
+
// =============================================================================
|
|
82
|
+
export const manifest = {
|
|
83
|
+
name: "kimicode",
|
|
84
|
+
slot: "agent",
|
|
85
|
+
description: "Agent plugin: Kimi Code CLI (MoonshotAI)",
|
|
86
|
+
version: "0.1.0",
|
|
87
|
+
displayName: "Kimi Code",
|
|
88
|
+
};
|
|
89
|
+
// =============================================================================
|
|
90
|
+
// Agent Implementation
|
|
91
|
+
// =============================================================================
|
|
92
|
+
/**
|
|
93
|
+
* Append approval flags — kimi uses `--yolo` (aka `-y`, `--yes`, `--auto-approve`).
|
|
94
|
+
* Suggest/ask modes have no dedicated flag; kimi prompts inline by default.
|
|
95
|
+
*/
|
|
96
|
+
function appendApprovalFlags(parts, permissions) {
|
|
97
|
+
const mode = normalizeAgentPermissionMode(permissions);
|
|
98
|
+
if (mode === "permissionless" || mode === "auto-edit") {
|
|
99
|
+
parts.push("--yolo");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Join command parts and prepend the PowerShell call operator on Windows.
|
|
104
|
+
* Without `& `, PowerShell parses a leading quoted string as an expression
|
|
105
|
+
* and silently does not execute it. Matches agent-codex.
|
|
106
|
+
*/
|
|
107
|
+
function formatLaunchCommand(parts) {
|
|
108
|
+
const cmd = parts.join(" ");
|
|
109
|
+
return isWindows() ? `& ${cmd}` : cmd;
|
|
110
|
+
}
|
|
111
|
+
function createKimicodeAgent() {
|
|
112
|
+
return {
|
|
113
|
+
name: "kimicode",
|
|
114
|
+
processName: "kimi",
|
|
115
|
+
getLaunchCommand(config) {
|
|
116
|
+
const parts = ["kimi"];
|
|
117
|
+
// Explicit --work-dir prevents shell-rc / tmux-hook cwd drift from
|
|
118
|
+
// making our md5(cwd) hash diverge from kimi's.
|
|
119
|
+
//
|
|
120
|
+
// Prefer config.workspacePath (per-session worktree) over
|
|
121
|
+
// projectConfig.path (the original repo root). When the workspace
|
|
122
|
+
// plugin is "worktree", these differ — passing projectConfig.path
|
|
123
|
+
// would either (a) make kimi write to the project root, breaking
|
|
124
|
+
// worktree isolation, or (b) cause md5(cwd) to diverge from
|
|
125
|
+
// session.workspacePath, so getActivityState/getSessionInfo never
|
|
126
|
+
// find this session's bucket. Falls back to projectConfig.path
|
|
127
|
+
// for clone-mode workspaces or older callers that don't plumb it.
|
|
128
|
+
const workDir = config.workspacePath ?? config.projectConfig.path;
|
|
129
|
+
if (workDir) {
|
|
130
|
+
parts.push("--work-dir", shellEscape(workDir));
|
|
131
|
+
}
|
|
132
|
+
appendApprovalFlags(parts, config.permissions);
|
|
133
|
+
if (config.model) {
|
|
134
|
+
parts.push("--model", shellEscape(config.model));
|
|
135
|
+
}
|
|
136
|
+
// Route AO-level subagent selection to kimi's `--agent NAME`
|
|
137
|
+
// (built-in agents: default, okabe, or custom via --agent-file).
|
|
138
|
+
if (config.subagent) {
|
|
139
|
+
parts.push("--agent", shellEscape(config.subagent));
|
|
140
|
+
}
|
|
141
|
+
// kimi's `-p`/`--prompt` is just the prompt string (alias of `--command`).
|
|
142
|
+
// It does NOT switch to print/exit mode — that's the separate `--print`
|
|
143
|
+
// flag, which we never set. Inline delivery is reliable and avoids the
|
|
144
|
+
// post-launch sendMessage() delay.
|
|
145
|
+
//
|
|
146
|
+
// kimi has no documented system-prompt flag. `--agent-file` looked like
|
|
147
|
+
// the closest fit but requires a YAML agent spec — passing AO's plain
|
|
148
|
+
// markdown prompt file makes kimi exit with a YAML parse error. Inline
|
|
149
|
+
// the file contents into --prompt instead. When both are provided, the
|
|
150
|
+
// system instructions come first so the agent reads them before the task.
|
|
151
|
+
let combinedPrompt = config.prompt ?? "";
|
|
152
|
+
if (config.systemPromptFile) {
|
|
153
|
+
const sysContent = readFileSync(config.systemPromptFile, "utf-8");
|
|
154
|
+
combinedPrompt = combinedPrompt
|
|
155
|
+
? `${sysContent}\n\n---\n\n${combinedPrompt}`
|
|
156
|
+
: sysContent;
|
|
157
|
+
}
|
|
158
|
+
if (combinedPrompt) {
|
|
159
|
+
parts.push("--prompt", shellEscape(combinedPrompt));
|
|
160
|
+
}
|
|
161
|
+
return formatLaunchCommand(parts);
|
|
162
|
+
},
|
|
163
|
+
getEnvironment(config) {
|
|
164
|
+
const env = {};
|
|
165
|
+
env["AO_SESSION_ID"] = config.sessionId;
|
|
166
|
+
if (config.issueId) {
|
|
167
|
+
env["AO_ISSUE_ID"] = config.issueId;
|
|
168
|
+
}
|
|
169
|
+
// PATH and GH_PATH are injected by session-manager for all agents.
|
|
170
|
+
return env;
|
|
171
|
+
},
|
|
172
|
+
detectActivity(terminalOutput) {
|
|
173
|
+
if (!terminalOutput.trim())
|
|
174
|
+
return "idle";
|
|
175
|
+
const lines = terminalOutput.trim().split("\n");
|
|
176
|
+
const lastLine = lines[lines.length - 1]?.trim() ?? "";
|
|
177
|
+
const tail = lines.slice(-6).join("\n");
|
|
178
|
+
// Order matters: waiting_input → blocked → idle → active. Actionable
|
|
179
|
+
// states must be checked BEFORE the idle-prompt check, otherwise a
|
|
180
|
+
// confirmation prompt that re-renders `kimi>` on the last line would
|
|
181
|
+
// get classified as idle and the session would sit forever looking
|
|
182
|
+
// quiet. Matches agent-codex / agent-aider ordering.
|
|
183
|
+
// 1. waiting_input — approval / confirmation prompts. Line-anchored
|
|
184
|
+
// where practical to avoid matching narration like "I approve of
|
|
185
|
+
// this approach".
|
|
186
|
+
if (/\(y\)es.*\(n\)o/i.test(tail))
|
|
187
|
+
return "waiting_input";
|
|
188
|
+
if (/\[y\/n\]\s*[?:]?\s*$/im.test(tail))
|
|
189
|
+
return "waiting_input";
|
|
190
|
+
if (/^\s*approve\??\s*$/im.test(tail))
|
|
191
|
+
return "waiting_input";
|
|
192
|
+
if (/\bapproval required\b/i.test(tail))
|
|
193
|
+
return "waiting_input";
|
|
194
|
+
if (/^\s*do you want to (proceed|continue)\?\s*$/im.test(tail))
|
|
195
|
+
return "waiting_input";
|
|
196
|
+
if (/^\s*allow .+\?\s*$/im.test(tail))
|
|
197
|
+
return "waiting_input";
|
|
198
|
+
// 2. blocked — hard errors surfaced to the terminal. Line-anchored to
|
|
199
|
+
// skip narration ("Earlier I failed to connect, then retried").
|
|
200
|
+
if (/^\s*error:/im.test(tail))
|
|
201
|
+
return "blocked";
|
|
202
|
+
if (/^\s*(?:error:\s*)?failed to (connect|authenticate|load)\b/im.test(tail))
|
|
203
|
+
return "blocked";
|
|
204
|
+
// 3. idle — only when nothing actionable is visible and the tail is a
|
|
205
|
+
// bare prompt. Generic shell/REPL prompt…
|
|
206
|
+
if (/^[>$#]\s*$/.test(lastLine))
|
|
207
|
+
return "idle";
|
|
208
|
+
// …or kimi's interactive prompt.
|
|
209
|
+
if (/^kimi[>:]?\s*$/i.test(lastLine))
|
|
210
|
+
return "idle";
|
|
211
|
+
// 4. active — anything else with content is ongoing work.
|
|
212
|
+
return "active";
|
|
213
|
+
},
|
|
214
|
+
async getActivityState(session, readyThresholdMs) {
|
|
215
|
+
const threshold = readyThresholdMs ?? DEFAULT_READY_THRESHOLD_MS;
|
|
216
|
+
const activeWindowMs = Math.min(DEFAULT_ACTIVE_WINDOW_MS, threshold);
|
|
217
|
+
// 1. Process check — always first.
|
|
218
|
+
const exitedAt = new Date();
|
|
219
|
+
if (!session.runtimeHandle)
|
|
220
|
+
return { state: "exited", timestamp: exitedAt };
|
|
221
|
+
const running = await this.isProcessRunning(session.runtimeHandle);
|
|
222
|
+
if (!running)
|
|
223
|
+
return { state: "exited", timestamp: exitedAt };
|
|
224
|
+
if (!session.workspacePath)
|
|
225
|
+
return null;
|
|
226
|
+
// 2. Actionable states (waiting_input / blocked) sourced from the AO
|
|
227
|
+
// activity JSONL written by recordActivity. Kimi's native JSONL format
|
|
228
|
+
// is not publicly documented, so terminal-derived state is our only
|
|
229
|
+
// reliable source for approval/error detection.
|
|
230
|
+
const activityResult = await readLastActivityEntry(session.workspacePath);
|
|
231
|
+
const activityState = checkActivityLogState(activityResult);
|
|
232
|
+
if (activityState)
|
|
233
|
+
return activityState;
|
|
234
|
+
// 3. Native signal — mtime of the freshest live file (context.jsonl /
|
|
235
|
+
// wire.jsonl) inside ~/.kimi/sessions/<md5(cwd)>/<uuid>/. The match
|
|
236
|
+
// already captured the mtime during the scan, so no re-stat here.
|
|
237
|
+
const match = await findKimiSessionMatch(session);
|
|
238
|
+
if (match) {
|
|
239
|
+
const ageMs = Math.max(0, Date.now() - match.mtime.getTime());
|
|
240
|
+
if (ageMs <= activeWindowMs)
|
|
241
|
+
return { state: "active", timestamp: match.mtime };
|
|
242
|
+
if (ageMs <= threshold)
|
|
243
|
+
return { state: "ready", timestamp: match.mtime };
|
|
244
|
+
return { state: "idle", timestamp: match.mtime };
|
|
245
|
+
}
|
|
246
|
+
// 4. JSONL entry fallback (MANDATORY) — uses the last AO activity entry
|
|
247
|
+
// with age-based decay when the native signal is unavailable.
|
|
248
|
+
const fallback = getActivityFallbackState(activityResult, activeWindowMs, threshold);
|
|
249
|
+
if (fallback)
|
|
250
|
+
return fallback;
|
|
251
|
+
// 5. No data available.
|
|
252
|
+
return null;
|
|
253
|
+
},
|
|
254
|
+
async recordActivity(session, terminalOutput) {
|
|
255
|
+
if (!session.workspacePath)
|
|
256
|
+
return;
|
|
257
|
+
await recordTerminalActivity(session.workspacePath, terminalOutput, (output) => this.detectActivity(output));
|
|
258
|
+
},
|
|
259
|
+
async isProcessRunning(handle) {
|
|
260
|
+
try {
|
|
261
|
+
if (handle.runtimeName === "tmux" && handle.id) {
|
|
262
|
+
// tmux + ps are POSIX-only. A stale tmux handle on Windows
|
|
263
|
+
// (e.g. cross-platform session import) would otherwise throw
|
|
264
|
+
// and misclassify a live process as exited.
|
|
265
|
+
if (isWindows())
|
|
266
|
+
return false;
|
|
267
|
+
const { stdout: ttyOut } = await execFileAsync("tmux", ["list-panes", "-t", handle.id, "-F", "#{pane_tty}"], { timeout: 30_000 });
|
|
268
|
+
const ttys = ttyOut
|
|
269
|
+
.trim()
|
|
270
|
+
.split("\n")
|
|
271
|
+
.map((t) => t.trim())
|
|
272
|
+
.filter(Boolean);
|
|
273
|
+
if (ttys.length === 0)
|
|
274
|
+
return false;
|
|
275
|
+
const { stdout: psOut } = await execFileAsync("ps", ["-eo", "pid,tty,args"], {
|
|
276
|
+
timeout: 30_000,
|
|
277
|
+
});
|
|
278
|
+
const ttySet = new Set(ttys.map((t) => t.replace(/^\/dev\//, "")));
|
|
279
|
+
// Only consider argv[0] — this is the executable being run, not
|
|
280
|
+
// arbitrary filenames (e.g. `cat kimi.log`) that happen to contain
|
|
281
|
+
// "kimi". We accept:
|
|
282
|
+
// - argv[0] basename == "kimi" or ".kimi" (dot-prefixed shim)
|
|
283
|
+
// - argv[0] is a python/uv invocation followed by "kimi" as the
|
|
284
|
+
// next token (e.g. `uv run kimi ...`, `python -m kimi ...`).
|
|
285
|
+
const argv0Re = /(?:^|\/)\.?kimi$/;
|
|
286
|
+
const viaRunnerRe = /(?:^|\/)(?:uv|python3?|node)$/;
|
|
287
|
+
for (const line of psOut.split("\n")) {
|
|
288
|
+
const cols = line.trimStart().split(/\s+/);
|
|
289
|
+
if (cols.length < 3 || !ttySet.has(cols[1] ?? ""))
|
|
290
|
+
continue;
|
|
291
|
+
const argv = cols.slice(2);
|
|
292
|
+
const head = argv[0] ?? "";
|
|
293
|
+
if (argv0Re.test(head))
|
|
294
|
+
return true;
|
|
295
|
+
if (!viaRunnerRe.test(head))
|
|
296
|
+
continue;
|
|
297
|
+
// Skip runner-internal flags (`uv run`, `python -m`) and check the
|
|
298
|
+
// next positional argument.
|
|
299
|
+
for (let i = 1; i < argv.length; i++) {
|
|
300
|
+
const tok = argv[i];
|
|
301
|
+
if (!tok || tok.startsWith("-"))
|
|
302
|
+
continue;
|
|
303
|
+
if (tok === "run" || tok === "tool" || tok === "-m")
|
|
304
|
+
continue;
|
|
305
|
+
if (argv0Re.test(tok))
|
|
306
|
+
return true;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
const rawPid = handle.data["pid"];
|
|
313
|
+
const pid = typeof rawPid === "number" ? rawPid : Number(rawPid);
|
|
314
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
315
|
+
try {
|
|
316
|
+
process.kill(pid, 0);
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
if (err instanceof Error && "code" in err && err.code === "EPERM") {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
async getSessionInfo(session) {
|
|
333
|
+
if (!session.workspacePath)
|
|
334
|
+
return null;
|
|
335
|
+
const match = await findKimiSessionMatch(session);
|
|
336
|
+
if (!match)
|
|
337
|
+
return null;
|
|
338
|
+
// Best-effort summary: first user input from wire.jsonl. Kimi does not
|
|
339
|
+
// store a title, model, or cost breakdown on disk.
|
|
340
|
+
const summary = await extractKimiSummary(match.dir);
|
|
341
|
+
return {
|
|
342
|
+
summary,
|
|
343
|
+
summaryIsFallback: true,
|
|
344
|
+
agentSessionId: match.sessionId,
|
|
345
|
+
};
|
|
346
|
+
},
|
|
347
|
+
async getRestoreCommand(session, project) {
|
|
348
|
+
if (!session.workspacePath)
|
|
349
|
+
return null;
|
|
350
|
+
const match = await findKimiSessionMatch(session);
|
|
351
|
+
if (!match)
|
|
352
|
+
return null;
|
|
353
|
+
const configuredModel = typeof project.agentConfig?.model === "string" ? project.agentConfig.model : undefined;
|
|
354
|
+
const parts = ["kimi", "--resume", shellEscape(match.sessionId)];
|
|
355
|
+
appendApprovalFlags(parts, project.agentConfig?.permissions);
|
|
356
|
+
if (configuredModel) {
|
|
357
|
+
parts.push("--model", shellEscape(configuredModel));
|
|
358
|
+
}
|
|
359
|
+
return formatLaunchCommand(parts);
|
|
360
|
+
},
|
|
361
|
+
async setupWorkspaceHooks(workspacePath, _config) {
|
|
362
|
+
await setupPathWrapperWorkspace(workspacePath);
|
|
363
|
+
},
|
|
364
|
+
// Snapshot pre-existing UUIDs BEFORE kimi launches. Capturing in
|
|
365
|
+
// postLaunchSetup races against kimi's own startup writes — kimi may
|
|
366
|
+
// create its UUID directory before postLaunchSetup runs, in which case
|
|
367
|
+
// the freshly-created UUID lands in `preExistingUuids` and gets filtered
|
|
368
|
+
// out forever. Discovery would then return null permanently.
|
|
369
|
+
//
|
|
370
|
+
// No-op on restore — captureKimiBaseline only writes the file when it
|
|
371
|
+
// doesn't already exist, so the original "what was here before AO
|
|
372
|
+
// started" partition stays stable across the session lifetime.
|
|
373
|
+
async preLaunchSetup(workspacePath) {
|
|
374
|
+
await captureKimiBaseline(workspacePath);
|
|
375
|
+
},
|
|
376
|
+
async postLaunchSetup(session) {
|
|
377
|
+
if (!session.workspacePath)
|
|
378
|
+
return;
|
|
379
|
+
await setupPathWrapperWorkspace(session.workspacePath);
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
// =============================================================================
|
|
384
|
+
// Plugin Export
|
|
385
|
+
// =============================================================================
|
|
386
|
+
export function create() {
|
|
387
|
+
return createKimicodeAgent();
|
|
388
|
+
}
|
|
389
|
+
export { _resetSessionMatchCache };
|
|
390
|
+
/** Vendor strings that positively identify MoonshotAI's kimi-cli. Plain "kimi"
|
|
391
|
+
* alone is not enough — it matches unrelated binaries (e.g. a keyboard input
|
|
392
|
+
* manager). `kimi info` on real kimi-cli prints "kimi-cli version: ..." which
|
|
393
|
+
* is a distinct identifier. */
|
|
394
|
+
const KIMI_VENDOR_RE = /kimi[-_](?:cli|code)|moonshot/i;
|
|
395
|
+
/** Keep `kimi info` output capture bounded. Real kimi-cli prints ~80 bytes,
|
|
396
|
+
* but a future release adding plugin lists / telemetry banners could push
|
|
397
|
+
* this higher. 64 KB is well above anything realistic while still guarding
|
|
398
|
+
* against a hostile binary flooding stdout. */
|
|
399
|
+
const DETECT_BUFFER_BYTES = 65_536;
|
|
400
|
+
export function detect() {
|
|
401
|
+
try {
|
|
402
|
+
// Use `kimi info` as the authoritative check — `kimi --version` prints
|
|
403
|
+
// just "kimi, version X.Y.Z" which is too generic to distinguish the
|
|
404
|
+
// MoonshotAI tool from any other binary named "kimi".
|
|
405
|
+
const infoOut = execFileSync("kimi", ["info"], {
|
|
406
|
+
encoding: "utf-8",
|
|
407
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
408
|
+
timeout: 10_000,
|
|
409
|
+
maxBuffer: DETECT_BUFFER_BYTES,
|
|
410
|
+
});
|
|
411
|
+
return KIMI_VENDOR_RE.test(infoOut);
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
export default { manifest, create, detect };
|
|
418
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Session } from "@made-by-moonlight/athene-core";
|
|
2
|
+
/** Kimi stores sessions under ~/.kimi/ (override via KIMI_SHARE_DIR). */
|
|
3
|
+
export declare function kimiShareDir(): string;
|
|
4
|
+
export interface KimiSessionMatch {
|
|
5
|
+
/** Absolute path to the session directory, e.g.
|
|
6
|
+
* ~/.kimi/sessions/<md5(cwd)>/<session-uuid>/ */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** Session UUID (directory basename) — accepted by `kimi --resume <id>`. */
|
|
9
|
+
sessionId: string;
|
|
10
|
+
/** mtime of the newest live-signal file (context.jsonl / wire.jsonl).
|
|
11
|
+
* Captured during the scan so callers don't re-stat. */
|
|
12
|
+
mtime: Date;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Sandbox check for individual files inside a kimi session dir. The dir was
|
|
16
|
+
* already isInsideKimiSessions-verified, but its CHILDREN aren't — a symlink
|
|
17
|
+
* placed at, say, ~/.kimi/sessions/<hash>/<uuid>/wire.jsonl pointing at
|
|
18
|
+
* /etc/passwd or /dev/zero would let stat()/createReadStream() follow it
|
|
19
|
+
* and read or hang on arbitrary files. Reject anything that isn't a regular
|
|
20
|
+
* file (rules out symlinks, sockets, FIFOs, block devices). lstat is used
|
|
21
|
+
* deliberately — stat would follow the symlink before we got the chance.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isKimiSessionFile(filePath: string): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Snapshot existing UUIDs in this workspace's Kimi bucket. Called once by
|
|
26
|
+
* preLaunchSetup; if the baseline file already exists (e.g. on session
|
|
27
|
+
* restore) we leave it alone so the original "what was here before AO
|
|
28
|
+
* started" partition is preserved.
|
|
29
|
+
*/
|
|
30
|
+
export declare function captureKimiBaseline(workspacePath: string): Promise<void>;
|
|
31
|
+
/** Cached wrapper around findKimiSessionMatchUncached. */
|
|
32
|
+
export declare function findKimiSessionMatch(session: Session): Promise<KimiSessionMatch | null>;
|
|
33
|
+
/** @internal Clear the session match cache. Exported for testing only. */
|
|
34
|
+
export declare function _resetSessionMatchCache(): void;
|
|
35
|
+
//# sourceMappingURL=session-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-discovery.d.ts","sourceRoot":"","sources":["../src/session-discovery.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAqC9D,yEAAyE;AACzE,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAoED,MAAM,WAAW,gBAAgB;IAC/B;sDACkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB;6DACyD;IACzD,KAAK,EAAE,IAAI,CAAC;CACb;AA8DD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E;AAiDD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8B9E;AAqLD,0DAA0D;AAC1D,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAqB7F;AAED,0EAA0E;AAC1E,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { lstat, mkdir, readdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, sep } from "node:path";
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// Kimi session-discovery layer
|
|
7
|
+
// =============================================================================
|
|
8
|
+
//
|
|
9
|
+
// Decision table (highest priority first) — used by findKimiSessionMatchUncached
|
|
10
|
+
// to resolve a Session → kimi UUID:
|
|
11
|
+
//
|
|
12
|
+
// priority │ source │ when
|
|
13
|
+
// ─────────┼───────────────────────────────────────────┼──────────────────────
|
|
14
|
+
// 1 │ Pin file (.ao/kimi-session-id.json) │ written once after a
|
|
15
|
+
// │ │ successful match;
|
|
16
|
+
// │ │ then dominant.
|
|
17
|
+
// 2 │ kimi.json soft-pin (`last_session_id`) │ kimi-cli's own
|
|
18
|
+
// │ │ bookkeeping; fallback
|
|
19
|
+
// │ │ when AO pin not yet
|
|
20
|
+
// │ │ written.
|
|
21
|
+
// 3 │ Recency heuristic (live-file mtime) │ filtered by:
|
|
22
|
+
// │ │ - baseline (pre-
|
|
23
|
+
// │ │ existing UUIDs)
|
|
24
|
+
// │ │ - createdAt - 60s
|
|
25
|
+
// │ │ - sandbox check
|
|
26
|
+
// │ │ winner is persisted
|
|
27
|
+
// │ │ to the pin file (1).
|
|
28
|
+
//
|
|
29
|
+
// Files written into the workspace's .ao/ dir:
|
|
30
|
+
// - kimi-baseline.json — UUIDs that existed BEFORE launch
|
|
31
|
+
// - kimi-session-id.json — pinned UUID for this AO session
|
|
32
|
+
//
|
|
33
|
+
// Both are write-once and survive restore.
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Paths and constants
|
|
37
|
+
// =============================================================================
|
|
38
|
+
/** Kimi stores sessions under ~/.kimi/ (override via KIMI_SHARE_DIR). */
|
|
39
|
+
export function kimiShareDir() {
|
|
40
|
+
const override = process.env["KIMI_SHARE_DIR"];
|
|
41
|
+
if (override && override.trim().length > 0)
|
|
42
|
+
return override;
|
|
43
|
+
return join(homedir(), ".kimi");
|
|
44
|
+
}
|
|
45
|
+
const KIMI_BASELINE_FILE = ".ao/kimi-baseline.json";
|
|
46
|
+
const KIMI_PIN_FILE = ".ao/kimi-session-id.json";
|
|
47
|
+
/** Positive-result TTL: a found session is unlikely to change identity within
|
|
48
|
+
* a single refresh cycle. Mirrors agent-codex's SESSION_FILE_CACHE_TTL_MS. */
|
|
49
|
+
const SESSION_MATCH_CACHE_TTL_MS = 30_000;
|
|
50
|
+
/** Negative-result TTL: kept short so a session that appears mid-poll is picked
|
|
51
|
+
* up on the next cycle instead of staying null for the full positive TTL. */
|
|
52
|
+
const SESSION_MATCH_NEGATIVE_TTL_MS = 2_000;
|
|
53
|
+
/** Soft cap on the cache map size — prunes expired entries when exceeded so
|
|
54
|
+
* long-running daemons with many worktrees don't grow unbounded. */
|
|
55
|
+
const SESSION_MATCH_CACHE_MAX_ENTRIES = 256;
|
|
56
|
+
/**
|
|
57
|
+
* Read ~/.kimi/kimi.json — the authoritative workspace-to-session mapping
|
|
58
|
+
* maintained by kimi-cli. Returns null on any I/O or parse error so callers
|
|
59
|
+
* can fall back to the hash-based scan.
|
|
60
|
+
*/
|
|
61
|
+
async function readKimiJson() {
|
|
62
|
+
try {
|
|
63
|
+
const raw = await readFile(join(kimiShareDir(), "kimi.json"), "utf-8");
|
|
64
|
+
const parsed = JSON.parse(raw);
|
|
65
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
66
|
+
return null;
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Find the kimi.json work_dirs entry for a workspace. Matches against the
|
|
75
|
+
* resolved (realpath) workspace path so symlinked worktrees are handled.
|
|
76
|
+
* Returns the entry (including last_session_id when populated) or null.
|
|
77
|
+
*/
|
|
78
|
+
async function findKimiWorkDirEntry(workspacePath) {
|
|
79
|
+
const kimiJson = await readKimiJson();
|
|
80
|
+
if (!kimiJson?.work_dirs || !Array.isArray(kimiJson.work_dirs))
|
|
81
|
+
return null;
|
|
82
|
+
const resolved = await resolveWorkspacePath(workspacePath);
|
|
83
|
+
for (const entry of kimiJson.work_dirs) {
|
|
84
|
+
if (!entry || typeof entry.path !== "string")
|
|
85
|
+
continue;
|
|
86
|
+
const entryResolved = await resolveWorkspacePath(entry.path);
|
|
87
|
+
if (entryResolved === resolved)
|
|
88
|
+
return entry;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/** MD5 hex digest of an absolute workspace path — kimi uses this as the
|
|
93
|
+
* per-workspace bucket under ~/.kimi/sessions/. */
|
|
94
|
+
function kimiWorkspaceHash(workspacePath) {
|
|
95
|
+
return createHash("md5").update(workspacePath).digest("hex");
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolve the workspace path kimi would see as its cwd. kimi's process reads
|
|
99
|
+
* cwd via `os.getcwd()`, which on Linux returns the realpath (symlinks are
|
|
100
|
+
* resolved by the kernel via /proc/self/cwd). If AO hands us a symlinked
|
|
101
|
+
* workspacePath, our MD5 of the symlink won't match kimi's MD5 of the
|
|
102
|
+
* resolved path — session discovery would silently miss every session.
|
|
103
|
+
*
|
|
104
|
+
* realpath() is best-effort: if the path doesn't exist or isn't readable,
|
|
105
|
+
* fall back to the raw string so we don't regress workflows where the
|
|
106
|
+
* workspace is created later or the caller has stricter sandboxing.
|
|
107
|
+
*/
|
|
108
|
+
async function resolveWorkspacePath(workspacePath) {
|
|
109
|
+
try {
|
|
110
|
+
// Stat first because Node's realpath() on Windows silently
|
|
111
|
+
// canonicalizes non-existent paths (e.g. "/workspace/test"
|
|
112
|
+
// becomes "D:\workspace\test") instead of throwing ENOENT
|
|
113
|
+
// like POSIX does. That divergence breaks any caller that
|
|
114
|
+
// hashes the result and expects parity with a separately-
|
|
115
|
+
// computed hash of the raw input.
|
|
116
|
+
await stat(workspacePath);
|
|
117
|
+
return await realpath(workspacePath);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return workspacePath;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Sandbox check — fail closed if a candidate path escapes ~/.kimi/sessions/.
|
|
125
|
+
* Bucket entries in a real kimi install are regular directories, but a
|
|
126
|
+
* symlink placed there (maliciously or accidentally) would let stat() /
|
|
127
|
+
* createReadStream() follow it to arbitrary filesystem locations, potentially
|
|
128
|
+
* hanging on FIFOs/sockets or leaking reads from unrelated files.
|
|
129
|
+
*/
|
|
130
|
+
async function isInsideKimiSessions(candidate) {
|
|
131
|
+
const sessionsRoot = join(kimiShareDir(), "sessions");
|
|
132
|
+
let rootReal;
|
|
133
|
+
let candReal;
|
|
134
|
+
try {
|
|
135
|
+
rootReal = await realpath(sessionsRoot);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
candReal = await realpath(candidate);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
// Use the platform separator. realpath() returns native paths
|
|
147
|
+
// (backslashes on Windows), so a hardcoded "/" check would never
|
|
148
|
+
// match and every session candidate would be rejected.
|
|
149
|
+
const rootWithSep = rootReal.endsWith(sep) ? rootReal : rootReal + sep;
|
|
150
|
+
return candReal === rootReal || candReal.startsWith(rootWithSep);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Sandbox check for individual files inside a kimi session dir. The dir was
|
|
154
|
+
* already isInsideKimiSessions-verified, but its CHILDREN aren't — a symlink
|
|
155
|
+
* placed at, say, ~/.kimi/sessions/<hash>/<uuid>/wire.jsonl pointing at
|
|
156
|
+
* /etc/passwd or /dev/zero would let stat()/createReadStream() follow it
|
|
157
|
+
* and read or hang on arbitrary files. Reject anything that isn't a regular
|
|
158
|
+
* file (rules out symlinks, sockets, FIFOs, block devices). lstat is used
|
|
159
|
+
* deliberately — stat would follow the symlink before we got the chance.
|
|
160
|
+
*/
|
|
161
|
+
export async function isKimiSessionFile(filePath) {
|
|
162
|
+
try {
|
|
163
|
+
const s = await lstat(filePath);
|
|
164
|
+
return s.isFile();
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get the mtime of the freshest live signal inside a Kimi session directory.
|
|
172
|
+
* context.jsonl / wire.jsonl update on every agent turn. Returns null when
|
|
173
|
+
* neither file exists or both are non-regular (symlink/socket/etc) — callers
|
|
174
|
+
* must treat this dir as "not a real session". Probed in parallel to avoid
|
|
175
|
+
* serial filesystem roundtrips.
|
|
176
|
+
*/
|
|
177
|
+
async function getKimiLiveSignalMtime(sessionDir) {
|
|
178
|
+
const stats = await Promise.all(["context.jsonl", "wire.jsonl"].map(async (name) => {
|
|
179
|
+
try {
|
|
180
|
+
const s = await lstat(join(sessionDir, name));
|
|
181
|
+
return s.isFile() ? s : null;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}));
|
|
187
|
+
let newest = null;
|
|
188
|
+
for (const s of stats) {
|
|
189
|
+
if (s && (!newest || s.mtimeMs > newest.getTime()))
|
|
190
|
+
newest = s.mtime;
|
|
191
|
+
}
|
|
192
|
+
return newest;
|
|
193
|
+
}
|
|
194
|
+
async function readKimiBaseline(workspacePath) {
|
|
195
|
+
try {
|
|
196
|
+
const raw = await readFile(join(workspacePath, KIMI_BASELINE_FILE), "utf-8");
|
|
197
|
+
const parsed = JSON.parse(raw);
|
|
198
|
+
if (!Array.isArray(parsed.preExistingUuids))
|
|
199
|
+
return null;
|
|
200
|
+
return new Set(parsed.preExistingUuids);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Snapshot existing UUIDs in this workspace's Kimi bucket. Called once by
|
|
208
|
+
* preLaunchSetup; if the baseline file already exists (e.g. on session
|
|
209
|
+
* restore) we leave it alone so the original "what was here before AO
|
|
210
|
+
* started" partition is preserved.
|
|
211
|
+
*/
|
|
212
|
+
export async function captureKimiBaseline(workspacePath) {
|
|
213
|
+
const baselineFile = join(workspacePath, KIMI_BASELINE_FILE);
|
|
214
|
+
try {
|
|
215
|
+
await stat(baselineFile);
|
|
216
|
+
return; // Already captured — don't overwrite on restore.
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// ENOENT — fall through and capture.
|
|
220
|
+
}
|
|
221
|
+
const resolved = await resolveWorkspacePath(workspacePath);
|
|
222
|
+
const bucket = join(kimiShareDir(), "sessions", kimiWorkspaceHash(resolved));
|
|
223
|
+
let entries = [];
|
|
224
|
+
try {
|
|
225
|
+
entries = await readdir(bucket);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Bucket doesn't exist yet — first kimi launch in this workspace.
|
|
229
|
+
// Empty baseline is correct.
|
|
230
|
+
}
|
|
231
|
+
const baseline = {
|
|
232
|
+
preExistingUuids: entries,
|
|
233
|
+
capturedAt: new Date().toISOString(),
|
|
234
|
+
};
|
|
235
|
+
try {
|
|
236
|
+
await mkdir(join(workspacePath, ".ao"), { recursive: true });
|
|
237
|
+
await writeFile(baselineFile, JSON.stringify(baseline), "utf-8");
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
// Workspace not writable — best-effort. Discovery falls back to the
|
|
241
|
+
// createdAt floor + pinned UUID checks, which already narrow the field.
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async function readKimiSessionPin(workspacePath) {
|
|
245
|
+
try {
|
|
246
|
+
const raw = await readFile(join(workspacePath, KIMI_PIN_FILE), "utf-8");
|
|
247
|
+
const parsed = JSON.parse(raw);
|
|
248
|
+
if (typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0)
|
|
249
|
+
return null;
|
|
250
|
+
return parsed.sessionId;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function writeKimiSessionPin(workspacePath, sessionId) {
|
|
257
|
+
const pin = {
|
|
258
|
+
sessionId,
|
|
259
|
+
pinnedAt: new Date().toISOString(),
|
|
260
|
+
};
|
|
261
|
+
try {
|
|
262
|
+
await mkdir(join(workspacePath, ".ao"), { recursive: true });
|
|
263
|
+
await writeFile(join(workspacePath, KIMI_PIN_FILE), JSON.stringify(pin), "utf-8");
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// Workspace not writable — best-effort. Discovery falls back to the
|
|
267
|
+
// recency heuristic on every call until the pin can be persisted.
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// =============================================================================
|
|
271
|
+
// Discovery — main entry point
|
|
272
|
+
// =============================================================================
|
|
273
|
+
/**
|
|
274
|
+
* Find the Kimi session directory for this workspace. See the decision table
|
|
275
|
+
* at the top of this file for precedence rules.
|
|
276
|
+
*
|
|
277
|
+
* Layout (kimi-cli 1.38):
|
|
278
|
+
* ~/.kimi/sessions/<md5(cwd)>/<session-uuid>/
|
|
279
|
+
* context.jsonl — conversation history
|
|
280
|
+
* wire.jsonl — turn events
|
|
281
|
+
*/
|
|
282
|
+
async function findKimiSessionMatchUncached(session) {
|
|
283
|
+
if (!session.workspacePath)
|
|
284
|
+
return null;
|
|
285
|
+
const resolved = await resolveWorkspacePath(session.workspacePath);
|
|
286
|
+
const bucket = join(kimiShareDir(), "sessions", kimiWorkspaceHash(resolved));
|
|
287
|
+
if (!(await isInsideKimiSessions(bucket)))
|
|
288
|
+
return null;
|
|
289
|
+
let entries;
|
|
290
|
+
try {
|
|
291
|
+
entries = await readdir(bucket);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
// Pin file takes highest priority. Once we've identified a UUID for this
|
|
297
|
+
// AO session (this function writes the pin on first successful match),
|
|
298
|
+
// we always return it — never re-evaluate the recency heuristic.
|
|
299
|
+
const pinnedId = await readKimiSessionPin(session.workspacePath);
|
|
300
|
+
// kimi.json soft-pin: kimi-cli stores `work_dirs[].last_session_id` for
|
|
301
|
+
// each workspace. When populated it's more authoritative than directory
|
|
302
|
+
// mtime — kimi itself wrote it. Used as a tiebreaker when no AO pin yet
|
|
303
|
+
// exists.
|
|
304
|
+
let kimiJsonSessionId = null;
|
|
305
|
+
if (!pinnedId) {
|
|
306
|
+
const workDirEntry = await findKimiWorkDirEntry(session.workspacePath);
|
|
307
|
+
if (workDirEntry &&
|
|
308
|
+
typeof workDirEntry.last_session_id === "string" &&
|
|
309
|
+
workDirEntry.last_session_id.length > 0) {
|
|
310
|
+
kimiJsonSessionId = workDirEntry.last_session_id;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// UUIDs that existed BEFORE this AO session started are partitioned out —
|
|
314
|
+
// they belong to a manual `kimi` run, a sibling AO session, or some other
|
|
315
|
+
// context. Without this, the "freshest in bucket" heuristic would attach
|
|
316
|
+
// to whichever one happened to scroll recently.
|
|
317
|
+
const baseline = await readKimiBaseline(session.workspacePath);
|
|
318
|
+
// Any UUID older than (session.createdAt - grace) is from a prior life.
|
|
319
|
+
const minAgeMs = session.createdAt.getTime() - 60_000;
|
|
320
|
+
let best = null;
|
|
321
|
+
let kimiJsonMatch = null;
|
|
322
|
+
for (const entry of entries) {
|
|
323
|
+
const dir = join(bucket, entry);
|
|
324
|
+
if (!(await isInsideKimiSessions(dir)))
|
|
325
|
+
continue;
|
|
326
|
+
const liveMtime = await getKimiLiveSignalMtime(dir);
|
|
327
|
+
if (!liveMtime)
|
|
328
|
+
continue;
|
|
329
|
+
if (pinnedId) {
|
|
330
|
+
if (entry !== pinnedId)
|
|
331
|
+
continue;
|
|
332
|
+
return { dir, sessionId: entry, mtime: liveMtime };
|
|
333
|
+
}
|
|
334
|
+
// Baseline filter — UUIDs present at launch never count as "ours".
|
|
335
|
+
// Applied BEFORE the kimi.json soft-pin check: kimi.json's
|
|
336
|
+
// last_session_id can lag the live bucket (e.g. a manual `kimi`
|
|
337
|
+
// run earlier left a stale pointer, or AO polls before kimi has
|
|
338
|
+
// updated kimi.json). Without this guard, the soft-pin would
|
|
339
|
+
// capture a baseline UUID and persist it to the AO pin file
|
|
340
|
+
// permanently, with no self-healing path.
|
|
341
|
+
if (baseline?.has(entry))
|
|
342
|
+
continue;
|
|
343
|
+
if (liveMtime.getTime() < minAgeMs)
|
|
344
|
+
continue;
|
|
345
|
+
// kimi.json soft-pin candidate — record it but keep scanning so we
|
|
346
|
+
// can still return a recency winner if the soft-pin UUID has no live
|
|
347
|
+
// files (rare but possible if kimi.json points at a stale entry).
|
|
348
|
+
// Reaches here only after passing the baseline + createdAt filters,
|
|
349
|
+
// so a stale last_session_id pointing at a pre-AO UUID is rejected.
|
|
350
|
+
if (kimiJsonSessionId && entry === kimiJsonSessionId) {
|
|
351
|
+
kimiJsonMatch = { dir, sessionId: entry, mtime: liveMtime };
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const mtimeMs = liveMtime.getTime();
|
|
355
|
+
if (!best || mtimeMs > best.mtimeMs) {
|
|
356
|
+
best = { dir, sessionId: entry, mtime: liveMtime, mtimeMs };
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (pinnedId) {
|
|
360
|
+
// Pin existed but didn't match anything in the bucket — don't silently
|
|
361
|
+
// fall back to a recency guess; that reintroduces the wrong-session bug.
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
if (kimiJsonMatch) {
|
|
365
|
+
await writeKimiSessionPin(session.workspacePath, kimiJsonMatch.sessionId);
|
|
366
|
+
return kimiJsonMatch;
|
|
367
|
+
}
|
|
368
|
+
if (best) {
|
|
369
|
+
// Persist the recency-heuristic winner. Subsequent calls will read the
|
|
370
|
+
// pin file and bypass the heuristic — even if the bucket gains another
|
|
371
|
+
// recently-active UUID later (manual kimi run, sibling AO session).
|
|
372
|
+
await writeKimiSessionPin(session.workspacePath, best.sessionId);
|
|
373
|
+
return { dir: best.dir, sessionId: best.sessionId, mtime: best.mtime };
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
// =============================================================================
|
|
378
|
+
// Cache
|
|
379
|
+
// =============================================================================
|
|
380
|
+
/** Per-workspace cache of the resolved session directory. */
|
|
381
|
+
const sessionMatchCache = new Map();
|
|
382
|
+
/** Prune expired entries; if still over the cap, drop the oldest. */
|
|
383
|
+
function pruneSessionMatchCache(now) {
|
|
384
|
+
for (const [key, entry] of sessionMatchCache) {
|
|
385
|
+
if (entry.expiry <= now)
|
|
386
|
+
sessionMatchCache.delete(key);
|
|
387
|
+
}
|
|
388
|
+
if (sessionMatchCache.size <= SESSION_MATCH_CACHE_MAX_ENTRIES)
|
|
389
|
+
return;
|
|
390
|
+
const sorted = [...sessionMatchCache.entries()].sort((a, b) => a[1].expiry - b[1].expiry);
|
|
391
|
+
const toDrop = sessionMatchCache.size - SESSION_MATCH_CACHE_MAX_ENTRIES;
|
|
392
|
+
for (let i = 0; i < toDrop; i++) {
|
|
393
|
+
const entry = sorted[i];
|
|
394
|
+
if (entry)
|
|
395
|
+
sessionMatchCache.delete(entry[0]);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/** Cached wrapper around findKimiSessionMatchUncached. */
|
|
399
|
+
export async function findKimiSessionMatch(session) {
|
|
400
|
+
const workspacePath = session.workspacePath;
|
|
401
|
+
if (!workspacePath)
|
|
402
|
+
return null;
|
|
403
|
+
// Key on workspace path. The pin is now file-based and persistent, so
|
|
404
|
+
// it cannot drift between calls — workspace path uniquely identifies
|
|
405
|
+
// the session for caching purposes.
|
|
406
|
+
const key = workspacePath;
|
|
407
|
+
const now = Date.now();
|
|
408
|
+
const cached = sessionMatchCache.get(key);
|
|
409
|
+
if (cached && cached.expiry > now)
|
|
410
|
+
return cached.match;
|
|
411
|
+
if (cached)
|
|
412
|
+
sessionMatchCache.delete(key);
|
|
413
|
+
const match = await findKimiSessionMatchUncached(session);
|
|
414
|
+
const ttl = match ? SESSION_MATCH_CACHE_TTL_MS : SESSION_MATCH_NEGATIVE_TTL_MS;
|
|
415
|
+
sessionMatchCache.set(key, { match, expiry: now + ttl });
|
|
416
|
+
if (sessionMatchCache.size > SESSION_MATCH_CACHE_MAX_ENTRIES) {
|
|
417
|
+
pruneSessionMatchCache(now);
|
|
418
|
+
}
|
|
419
|
+
return match;
|
|
420
|
+
}
|
|
421
|
+
/** @internal Clear the session match cache. Exported for testing only. */
|
|
422
|
+
export function _resetSessionMatchCache() {
|
|
423
|
+
sessionMatchCache.clear();
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=session-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-discovery.js","sourceRoot":"","sources":["../src/session-discovery.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@made-by-moonlight/athene-plugin-agent-kimicode",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Agent plugin: Kimi Code CLI (MoonshotAI)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/slievr/Athene.git",
|
|
21
|
+
"directory": "packages/plugins/agent-kimicode"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/slievr/Athene",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/slievr/Athene/issues"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20.0.0"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@made-by-moonlight/athene-core": "0.9.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^25.2.3",
|
|
35
|
+
"typescript": "^5.7.0",
|
|
36
|
+
"vitest": "^3.0.0"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"clean": "rm -rf dist"
|
|
46
|
+
}
|
|
47
|
+
}
|