sessionpipe-core 0.9.0 → 0.10.0
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/control.js +121 -12
- package/dist/control.js.map +2 -2
- package/dist/index.js +846 -308
- package/dist/index.js.map +4 -4
- package/dist/schema/v1.d.ts +266 -33
- package/dist/schema/v1.d.ts.map +1 -1
- package/dist/schema.js +128 -33
- package/dist/schema.js.map +2 -2
- package/dist/service.js +155 -0
- package/dist/service.js.map +7 -0
- package/dist/src/adapters/acp-control.d.ts +87 -0
- package/dist/src/adapters/acp-control.d.ts.map +1 -0
- package/dist/src/adapters/antigravity-control.d.ts +15 -5
- package/dist/src/adapters/antigravity-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code-control.d.ts +39 -4
- package/dist/src/adapters/claude-code-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code.d.ts.map +1 -1
- package/dist/src/adapters/codex-control.d.ts +23 -7
- package/dist/src/adapters/codex-control.d.ts.map +1 -1
- package/dist/src/adapters/codex.d.ts.map +1 -1
- package/dist/src/adapters/headless.d.ts +4 -1
- package/dist/src/adapters/headless.d.ts.map +1 -1
- package/dist/src/adapters/index.d.ts +7 -1
- package/dist/src/adapters/index.d.ts.map +1 -1
- package/dist/src/control/verify.d.ts +72 -16
- package/dist/src/control/verify.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/launched-by.d.ts +12 -0
- package/dist/src/launched-by.d.ts.map +1 -0
- package/dist/src/paths.d.ts +4 -0
- package/dist/src/paths.d.ts.map +1 -1
- package/dist/src/privacy/filter.d.ts.map +1 -1
- package/dist/src/service/index.d.ts +83 -0
- package/dist/src/service/index.d.ts.map +1 -0
- package/dist/src/version.d.ts +39 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/version.js +75 -0
- package/dist/version.js.map +7 -0
- package/package.json +9 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,525 @@
|
|
|
1
|
-
// sessionpipe-core 0.
|
|
1
|
+
// sessionpipe-core 0.10.0 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __export = (target, all) => {
|
|
4
4
|
for (var name in all)
|
|
5
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
// src/adapters/acp-control.ts
|
|
9
|
+
var acp_control_exports = {};
|
|
10
|
+
__export(acp_control_exports, {
|
|
11
|
+
ACP_AGENTS: () => ACP_AGENTS,
|
|
12
|
+
ACP_HARNESSES: () => ACP_HARNESSES,
|
|
13
|
+
acpAgent: () => acpAgent,
|
|
14
|
+
acpHarnesses: () => acpHarnesses,
|
|
15
|
+
acpOverride: () => acpOverride,
|
|
16
|
+
errorWords: () => errorWords,
|
|
17
|
+
findAcpAgent: () => findAcpAgent,
|
|
18
|
+
permissionChoice: () => permissionChoice,
|
|
19
|
+
startAcp: () => startAcp
|
|
20
|
+
});
|
|
21
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
22
|
+
import os from "node:os";
|
|
23
|
+
import path2 from "node:path";
|
|
24
|
+
|
|
25
|
+
// src/adapters/headless.ts
|
|
26
|
+
import { spawn } from "node:child_process";
|
|
27
|
+
import { accessSync, constants, statSync } from "node:fs";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
30
|
+
var OUT_CAP = 1024 * 1024;
|
|
31
|
+
function isExe(p) {
|
|
32
|
+
try {
|
|
33
|
+
if (!statSync(p).isFile()) return false;
|
|
34
|
+
accessSync(p, constants.X_OK);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function findBinary(o) {
|
|
41
|
+
if (o.override && isExe(o.override)) return o.override;
|
|
42
|
+
for (const d of String(o.env?.PATH || "").split(path.delimiter)) {
|
|
43
|
+
if (!d || !path.isAbsolute(d)) continue;
|
|
44
|
+
for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);
|
|
45
|
+
}
|
|
46
|
+
for (const d of o.dirs) for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function jobEnv(env = process.env) {
|
|
50
|
+
return { ...env, SESSIONPIPE_CONTROL_JOB: "1" };
|
|
51
|
+
}
|
|
52
|
+
function runHeadless(bin, args, o) {
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
let out = [];
|
|
55
|
+
let outLen = 0;
|
|
56
|
+
let tooBig = false;
|
|
57
|
+
let errTail = "";
|
|
58
|
+
let timedOut = false;
|
|
59
|
+
let settled = false;
|
|
60
|
+
let child;
|
|
61
|
+
try {
|
|
62
|
+
child = spawn(bin, args, {
|
|
63
|
+
cwd: o.cwd,
|
|
64
|
+
env: o.env,
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
detached: process.platform !== "win32",
|
|
67
|
+
windowsHide: true
|
|
68
|
+
});
|
|
69
|
+
} catch (e) {
|
|
70
|
+
resolve({ error: e.message });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const kill = (sig) => {
|
|
74
|
+
try {
|
|
75
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
76
|
+
else child.kill(sig);
|
|
77
|
+
} catch {
|
|
78
|
+
try {
|
|
79
|
+
child.kill(sig);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const keep = (line) => {
|
|
85
|
+
if (!o.keep?.(line)) return;
|
|
86
|
+
out = [Buffer.from(line)];
|
|
87
|
+
outLen = line.length;
|
|
88
|
+
};
|
|
89
|
+
let partial = "";
|
|
90
|
+
child.stdout?.on("data", (b) => {
|
|
91
|
+
if (o.onLine) {
|
|
92
|
+
const chunk = partial + b.toString("utf8");
|
|
93
|
+
const lines = chunk.split("\n");
|
|
94
|
+
partial = lines.pop();
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
if (!line.trim()) continue;
|
|
97
|
+
try {
|
|
98
|
+
o.onLine(line);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
keep(line);
|
|
102
|
+
}
|
|
103
|
+
if (partial.length > OUT_CAP) partial = "";
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (tooBig) return;
|
|
107
|
+
if (outLen + b.length > OUT_CAP) {
|
|
108
|
+
tooBig = true;
|
|
109
|
+
out = [];
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
out.push(b);
|
|
113
|
+
outLen += b.length;
|
|
114
|
+
});
|
|
115
|
+
child.stderr?.on("data", (b) => {
|
|
116
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
117
|
+
});
|
|
118
|
+
const timer = setTimeout(() => {
|
|
119
|
+
timedOut = true;
|
|
120
|
+
kill("SIGTERM");
|
|
121
|
+
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
122
|
+
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
123
|
+
const result = (code, signal) => ({
|
|
124
|
+
code,
|
|
125
|
+
signal,
|
|
126
|
+
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
127
|
+
tooBig,
|
|
128
|
+
stderr: errTail,
|
|
129
|
+
timedOut
|
|
130
|
+
});
|
|
131
|
+
const finish = (r) => {
|
|
132
|
+
if (settled) return;
|
|
133
|
+
settled = true;
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
resolve(r);
|
|
136
|
+
};
|
|
137
|
+
child.on("error", (e) => finish({ error: e.message }));
|
|
138
|
+
child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
|
|
139
|
+
child.on("close", (code, signal) => {
|
|
140
|
+
if (o.onLine && partial.trim()) {
|
|
141
|
+
try {
|
|
142
|
+
o.onLine(partial);
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
keep(partial);
|
|
146
|
+
partial = "";
|
|
147
|
+
}
|
|
148
|
+
finish(result(code, signal));
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function stderrTail(stderr, n = 3) {
|
|
153
|
+
return String(stderr || "").trim().split("\n").slice(-n).join(" ").trim();
|
|
154
|
+
}
|
|
155
|
+
function deniedNote(names, count) {
|
|
156
|
+
if (!count) return "";
|
|
157
|
+
return `
|
|
158
|
+
|
|
159
|
+
(Not allowed on this machine, since nobody was there to approve: ${names.join(", ") || `${count} action(s)`}.)`;
|
|
160
|
+
}
|
|
161
|
+
function runFailure(label, res) {
|
|
162
|
+
if (res.error) return `couldn't start ${label}: ${res.error}`;
|
|
163
|
+
if (res.timedOut) return `${label} was still working after 30 minutes, so it was stopped`;
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/adapters/acp-control.ts
|
|
168
|
+
var ACP_AGENTS = [
|
|
169
|
+
{ harness: "gemini-cli", registry: "gemini", label: "Gemini CLI", bin: "gemini", args: ["--acp"] },
|
|
170
|
+
{ harness: "devin", registry: "devin", label: "Devin", bin: "devin", args: ["acp"] },
|
|
171
|
+
{ harness: "opencode", registry: "opencode", label: "OpenCode", bin: "opencode", args: ["acp"] },
|
|
172
|
+
{ harness: "cursor", registry: "cursor", label: "Cursor", bin: "cursor-agent", args: ["acp"] },
|
|
173
|
+
{ harness: "copilot-cli", registry: "github-copilot-cli", label: "Copilot CLI", bin: "copilot", args: ["--acp"] },
|
|
174
|
+
{
|
|
175
|
+
harness: "droid",
|
|
176
|
+
registry: "factory-droid",
|
|
177
|
+
label: "Droid",
|
|
178
|
+
bin: "droid",
|
|
179
|
+
args: ["exec", "--output-format", "acp-daemon"]
|
|
180
|
+
},
|
|
181
|
+
{ harness: "goose", registry: "goose", label: "goose", bin: "goose", args: ["acp"] },
|
|
182
|
+
{ harness: "kilo", registry: "kilo", label: "Kilo", bin: "kilo", args: ["acp"] },
|
|
183
|
+
{
|
|
184
|
+
harness: "qwen-code",
|
|
185
|
+
registry: "qwen-code",
|
|
186
|
+
label: "Qwen Code",
|
|
187
|
+
bin: "qwen",
|
|
188
|
+
args: ["--acp", "--experimental-skills"]
|
|
189
|
+
},
|
|
190
|
+
{ harness: "cline", registry: "cline", label: "Cline", bin: "cline", args: ["--acp"] },
|
|
191
|
+
{ harness: "auggie", registry: "auggie", label: "Auggie", bin: "auggie", args: ["--acp"] },
|
|
192
|
+
{ harness: "kimi", registry: "kimi", label: "Kimi CLI", bin: "kimi", args: ["acp"] },
|
|
193
|
+
{ harness: "amp", registry: "amp-acp", label: "Amp", bin: "amp-acp", args: [] },
|
|
194
|
+
{ harness: "mistral-vibe", registry: "mistral-vibe", label: "Mistral Vibe", bin: "vibe-acp", args: [] },
|
|
195
|
+
{ harness: "bob", registry: "bob", label: "Bob", bin: "bob", args: ["acp"] },
|
|
196
|
+
{ harness: "grok-build", registry: "grok-build", label: "Grok Build", bin: "grok", args: ["agent", "stdio"] },
|
|
197
|
+
{ harness: "qoder", registry: "qoder", label: "Qoder CLI", bin: "qodercli", args: ["--acp"] },
|
|
198
|
+
{ harness: "minimax-code", registry: "minimax-code", label: "MiniMax Code", bin: "mcode", args: ["acp"] },
|
|
199
|
+
{ harness: "codebuddy-code", registry: "codebuddy-code", label: "Codebuddy Code", bin: "codebuddy", args: ["--acp"] },
|
|
200
|
+
{ harness: "nova", registry: "nova", label: "Nova", bin: "nova", args: ["acp"] },
|
|
201
|
+
{ harness: "dimcode", registry: "dimcode", label: "DimCode", bin: "dim", args: ["acp"] },
|
|
202
|
+
{ harness: "dirac", registry: "dirac", label: "Dirac", bin: "dirac", args: ["--acp"] },
|
|
203
|
+
{ harness: "harn", registry: "harn", label: "Harn", bin: "harn", args: ["serve", "acp"] },
|
|
204
|
+
{ harness: "vtcode", registry: "vtcode", label: "VT Code", bin: "vtcode", args: ["acp"] },
|
|
205
|
+
{ harness: "stakpak", registry: "stakpak", label: "Stakpak", bin: "stakpak", args: ["acp"] },
|
|
206
|
+
{ harness: "crow-cli", registry: "crow-cli", label: "crow-cli", bin: "crow-cli", args: ["acp"] },
|
|
207
|
+
{ harness: "corust-agent", registry: "corust-agent", label: "Corust Agent", bin: "corust-agent-acp", args: [] },
|
|
208
|
+
{ harness: "cortex-code", registry: "cortex-code", label: "Cortex Code", bin: "cortex", args: ["acp", "serve"] },
|
|
209
|
+
{ harness: "kimchi", registry: "kimchi", label: "Kimchi", bin: "kimchi", args: ["--mode", "acp"] }
|
|
210
|
+
];
|
|
211
|
+
var ACP_HARNESSES = ACP_AGENTS.map((a) => a.harness);
|
|
212
|
+
var acpAgent = (harness) => ACP_AGENTS.find((a) => a.harness === harness);
|
|
213
|
+
var acpOverride = (harness) => `SESSIONPIPE_ACP_${harness.toUpperCase().replace(/-/g, "_")}`;
|
|
214
|
+
function findAcpAgent(harness, env = process.env, home = os.homedir(), nodeDir = path2.dirname(process.execPath)) {
|
|
215
|
+
const a = acpAgent(harness);
|
|
216
|
+
if (!a) return null;
|
|
217
|
+
const bin = findBinary({
|
|
218
|
+
override: env[acpOverride(harness)],
|
|
219
|
+
names: process.platform === "win32" ? [`${a.bin}.exe`, `${a.bin}.cmd`] : [a.bin],
|
|
220
|
+
env,
|
|
221
|
+
dirs: [
|
|
222
|
+
nodeDir,
|
|
223
|
+
path2.join(home, ".local", "bin"),
|
|
224
|
+
path2.join(home, ".cargo", "bin"),
|
|
225
|
+
"/opt/homebrew/bin",
|
|
226
|
+
"/usr/local/bin"
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
return bin ? { bin, args: a.args } : null;
|
|
230
|
+
}
|
|
231
|
+
function acpHarnesses(env = process.env) {
|
|
232
|
+
return ACP_HARNESSES.filter((h) => findAcpAgent(h, env));
|
|
233
|
+
}
|
|
234
|
+
var obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
235
|
+
var str = (v) => typeof v === "string" ? v : "";
|
|
236
|
+
function permissionChoice(options, mode) {
|
|
237
|
+
const list = (Array.isArray(options) ? options : []).map(obj).filter((o) => !!o && !!str(o.optionId)).map((o) => ({ optionId: str(o.optionId), kind: str(o.kind) }));
|
|
238
|
+
const want = mode === "auto" ? ["allow_once", "allow_always"] : ["reject_once", "reject_always"];
|
|
239
|
+
for (const k of want) {
|
|
240
|
+
const hit = list.find((o) => o.kind === k);
|
|
241
|
+
if (hit) return hit;
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
function errorWords(err) {
|
|
246
|
+
const message = str(err.message);
|
|
247
|
+
const data = str(err.data) || str(obj(err.data)?.message);
|
|
248
|
+
return data && data !== message ? message ? `${message}: ${data}` : data : message;
|
|
249
|
+
}
|
|
250
|
+
function toolName(call, titles) {
|
|
251
|
+
if (!call) return null;
|
|
252
|
+
const t = str(call.title) || titles.get(str(call.toolCallId)) || str(call.name) || str(call.toolCallId);
|
|
253
|
+
return t ? t.slice(0, 80) : null;
|
|
254
|
+
}
|
|
255
|
+
var CLIENT_INFO = { name: "sessionpipe", title: "sessionpipe control", version: "1" };
|
|
256
|
+
function startAcp(o) {
|
|
257
|
+
const now = o.now ?? Date.now;
|
|
258
|
+
const log = o.log ?? (() => {
|
|
259
|
+
});
|
|
260
|
+
const label = o.label;
|
|
261
|
+
let child;
|
|
262
|
+
try {
|
|
263
|
+
child = spawn2(o.bin, o.args, {
|
|
264
|
+
cwd: o.cwd,
|
|
265
|
+
env: jobEnv(o.env),
|
|
266
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
267
|
+
detached: process.platform !== "win32",
|
|
268
|
+
windowsHide: true
|
|
269
|
+
});
|
|
270
|
+
} catch (e) {
|
|
271
|
+
return Promise.resolve({ failed: `couldn't start ${label}: ${e.message}` });
|
|
272
|
+
}
|
|
273
|
+
const kill = (sig) => {
|
|
274
|
+
try {
|
|
275
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
276
|
+
else child.kill(sig);
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
let gone = null;
|
|
281
|
+
let errTail = "";
|
|
282
|
+
let nextId = 1;
|
|
283
|
+
const pending = /* @__PURE__ */ new Map();
|
|
284
|
+
const write = (m) => {
|
|
285
|
+
if (gone) return;
|
|
286
|
+
try {
|
|
287
|
+
child.stdin?.write(`${JSON.stringify(m)}
|
|
288
|
+
`);
|
|
289
|
+
} catch {
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const request = (method, params, timeoutMs) => new Promise((resolve, reject) => {
|
|
293
|
+
if (gone) return reject(new Error(gone));
|
|
294
|
+
const id = nextId++;
|
|
295
|
+
const t = setTimeout(() => {
|
|
296
|
+
pending.delete(id);
|
|
297
|
+
reject(new Error(`${label} didn't answer ${method} in ${Math.round(timeoutMs / 1e3)} s`));
|
|
298
|
+
}, timeoutMs);
|
|
299
|
+
t.unref();
|
|
300
|
+
pending.set(id, {
|
|
301
|
+
resolve: (r) => {
|
|
302
|
+
clearTimeout(t);
|
|
303
|
+
resolve(r);
|
|
304
|
+
},
|
|
305
|
+
reject: (e) => {
|
|
306
|
+
clearTimeout(t);
|
|
307
|
+
reject(e);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
write({ jsonrpc: "2.0", id, method, params });
|
|
311
|
+
});
|
|
312
|
+
const notify = (method, params) => write({ jsonrpc: "2.0", method, params });
|
|
313
|
+
let turn = null;
|
|
314
|
+
const titles = /* @__PURE__ */ new Map();
|
|
315
|
+
const onUpdate = (u) => {
|
|
316
|
+
if (!u) return;
|
|
317
|
+
const kind = str(u.sessionUpdate);
|
|
318
|
+
if (kind === "tool_call" && str(u.toolCallId) && str(u.title)) {
|
|
319
|
+
if (titles.size >= 200) titles.clear();
|
|
320
|
+
titles.set(str(u.toolCallId), str(u.title));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (kind !== "agent_message_chunk" || !turn) return;
|
|
324
|
+
const c = obj(u.content);
|
|
325
|
+
if (c?.type !== "text" || !str(c.text)) return;
|
|
326
|
+
const mid = str(u.messageId) || null;
|
|
327
|
+
if (turn.message !== void 0 && mid !== turn.message && turn.text.trim())
|
|
328
|
+
turn.text = `${turn.text.trimEnd()}
|
|
329
|
+
|
|
330
|
+
`;
|
|
331
|
+
turn.message = mid;
|
|
332
|
+
turn.text += str(c.text);
|
|
333
|
+
o.onText?.(turn.text);
|
|
334
|
+
};
|
|
335
|
+
const onRequest = (id, method, params) => {
|
|
336
|
+
if (method !== "session/request_permission") {
|
|
337
|
+
write({ jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } });
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const pick = permissionChoice(params?.options, o.mode);
|
|
341
|
+
if (!pick?.kind.startsWith("allow")) {
|
|
342
|
+
const name = toolName(obj(params?.toolCall), titles);
|
|
343
|
+
if (turn) {
|
|
344
|
+
turn.count++;
|
|
345
|
+
if (name && !turn.denied.includes(name) && turn.denied.length < 20) turn.denied.push(name);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
write({
|
|
349
|
+
jsonrpc: "2.0",
|
|
350
|
+
id,
|
|
351
|
+
result: { outcome: pick ? { outcome: "selected", optionId: pick.optionId } : { outcome: "cancelled" } }
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
const onLine = (line) => {
|
|
355
|
+
let m;
|
|
356
|
+
try {
|
|
357
|
+
m = obj(JSON.parse(line));
|
|
358
|
+
} catch {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (!m) return;
|
|
362
|
+
const method = str(m.method);
|
|
363
|
+
if (method) {
|
|
364
|
+
if (m.id !== void 0) onRequest(m.id, method, obj(m.params));
|
|
365
|
+
else if (method === "session/update") onUpdate(obj(obj(m.params)?.update));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const p = typeof m.id === "number" ? pending.get(m.id) : void 0;
|
|
369
|
+
if (!p) return;
|
|
370
|
+
pending.delete(m.id);
|
|
371
|
+
const err = obj(m.error);
|
|
372
|
+
if (err) p.reject(new Error(errorWords(err) || `${label} returned error ${String(err.code ?? "?")}`));
|
|
373
|
+
else p.resolve(m.result);
|
|
374
|
+
};
|
|
375
|
+
let partial = "";
|
|
376
|
+
child.stdout?.on("data", (b) => {
|
|
377
|
+
const lines = (partial + b.toString("utf8")).split("\n");
|
|
378
|
+
partial = lines.pop();
|
|
379
|
+
for (const l of lines) if (l.trim()) onLine(l);
|
|
380
|
+
if (partial.length > 4 * 1024 * 1024) partial = "";
|
|
381
|
+
});
|
|
382
|
+
child.stderr?.on("data", (b) => {
|
|
383
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
384
|
+
});
|
|
385
|
+
const left = (why) => {
|
|
386
|
+
if (gone) return;
|
|
387
|
+
gone = why;
|
|
388
|
+
for (const p of pending.values()) p.reject(new Error(why));
|
|
389
|
+
pending.clear();
|
|
390
|
+
};
|
|
391
|
+
child.on("error", (e) => left(`couldn't start ${label}: ${e.message}`));
|
|
392
|
+
const exited = (code, signal) => {
|
|
393
|
+
const tail = errTail.trim().split("\n").slice(-3).join(" ").trim();
|
|
394
|
+
left(`${label} exited (${signal || `code ${code}`})${tail ? `: ${tail}` : ""}`);
|
|
395
|
+
};
|
|
396
|
+
child.on("exit", exited);
|
|
397
|
+
child.on("close", exited);
|
|
398
|
+
let closed = false;
|
|
399
|
+
const close = () => {
|
|
400
|
+
if (closed) return;
|
|
401
|
+
closed = true;
|
|
402
|
+
try {
|
|
403
|
+
child.stdin?.end();
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
const grace = o.closeGraceMs ?? 2e3;
|
|
407
|
+
const t = setTimeout(() => kill("SIGTERM"), grace);
|
|
408
|
+
t.unref();
|
|
409
|
+
const k = setTimeout(() => kill("SIGKILL"), grace * 6);
|
|
410
|
+
k.unref();
|
|
411
|
+
child.once("exit", () => {
|
|
412
|
+
clearTimeout(t);
|
|
413
|
+
clearTimeout(k);
|
|
414
|
+
});
|
|
415
|
+
};
|
|
416
|
+
const open = o.openTimeoutMs ?? 6e4;
|
|
417
|
+
function held(session) {
|
|
418
|
+
let busy = false;
|
|
419
|
+
let lastUsed = now();
|
|
420
|
+
return {
|
|
421
|
+
session,
|
|
422
|
+
mode: "api",
|
|
423
|
+
get busy() {
|
|
424
|
+
return busy;
|
|
425
|
+
},
|
|
426
|
+
get lastUsed() {
|
|
427
|
+
return lastUsed;
|
|
428
|
+
},
|
|
429
|
+
get closed() {
|
|
430
|
+
return closed || !!gone;
|
|
431
|
+
},
|
|
432
|
+
async send(text) {
|
|
433
|
+
if (closed) return { reply: "", error: `${label} was closed` };
|
|
434
|
+
busy = true;
|
|
435
|
+
lastUsed = now();
|
|
436
|
+
const t = { text: "", denied: [], count: 0 };
|
|
437
|
+
turn = t;
|
|
438
|
+
const limit = o.timeoutMs ?? JOB_TIMEOUT_MS;
|
|
439
|
+
let timedOut = false;
|
|
440
|
+
const timer = setTimeout(() => {
|
|
441
|
+
timedOut = true;
|
|
442
|
+
notify("session/cancel", { sessionId: session });
|
|
443
|
+
setTimeout(close, 5e3).unref();
|
|
444
|
+
}, limit);
|
|
445
|
+
timer.unref();
|
|
446
|
+
try {
|
|
447
|
+
const res = obj(
|
|
448
|
+
await request("session/prompt", { sessionId: session, prompt: [{ type: "text", text }] }, limit + 1e4)
|
|
449
|
+
);
|
|
450
|
+
const stop = str(res?.stopReason);
|
|
451
|
+
const words = t.text.trim() || `(${label} finished without a written reply.)`;
|
|
452
|
+
const refused = deniedNote(t.denied, t.count);
|
|
453
|
+
if (stop === "end_turn" || stop === "max_tokens") return { reply: words + refused };
|
|
454
|
+
if (stop === "refusal") return { reply: `${words}
|
|
455
|
+
|
|
456
|
+
(${label} refused the request.)${refused}` };
|
|
457
|
+
if (stop === "cancelled")
|
|
458
|
+
return timedOut ? {
|
|
459
|
+
reply: "",
|
|
460
|
+
error: `${label} was still working after ${Math.round(limit / 6e4)} minutes, so it was stopped`
|
|
461
|
+
} : { reply: `${words}
|
|
462
|
+
|
|
463
|
+
(The turn was cancelled.)${refused}` };
|
|
464
|
+
return { reply: "", error: `${label} ended the turn with ${stop || "no stop reason"}` };
|
|
465
|
+
} catch (e) {
|
|
466
|
+
return { reply: "", error: e.message };
|
|
467
|
+
} finally {
|
|
468
|
+
clearTimeout(timer);
|
|
469
|
+
turn = null;
|
|
470
|
+
busy = false;
|
|
471
|
+
lastUsed = now();
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
async interrupt() {
|
|
475
|
+
notify("session/cancel", { sessionId: session });
|
|
476
|
+
},
|
|
477
|
+
close
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
return (async () => {
|
|
481
|
+
let signIn = "";
|
|
482
|
+
try {
|
|
483
|
+
const init = obj(
|
|
484
|
+
await request(
|
|
485
|
+
"initialize",
|
|
486
|
+
{
|
|
487
|
+
protocolVersion: 1,
|
|
488
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
|
|
489
|
+
clientInfo: CLIENT_INFO
|
|
490
|
+
},
|
|
491
|
+
open
|
|
492
|
+
)
|
|
493
|
+
);
|
|
494
|
+
const caps = obj(init?.agentCapabilities);
|
|
495
|
+
const info = obj(init?.agentInfo);
|
|
496
|
+
log(
|
|
497
|
+
`${label}: ${str(info?.name) || "agent"} ${str(info?.version)} (ACP ${String(init?.protocolVersion ?? "?")})`
|
|
498
|
+
);
|
|
499
|
+
const methods = Array.isArray(init?.authMethods) ? init.authMethods.map(obj) : [];
|
|
500
|
+
if (methods.length) {
|
|
501
|
+
const how = str(methods[0]?.description) || str(methods[0]?.name);
|
|
502
|
+
signIn = ` (if ${label} wants a sign-in, do it at the machine's terminal${how ? `: ${how}` : ""})`;
|
|
503
|
+
}
|
|
504
|
+
if (o.load) {
|
|
505
|
+
if (!caps?.loadSession) {
|
|
506
|
+
close();
|
|
507
|
+
return { unsupported: `${label} can't reopen a session once it has been closed (no session/load)` };
|
|
508
|
+
}
|
|
509
|
+
await request("session/load", { sessionId: o.load, cwd: o.cwd, mcpServers: [] }, open);
|
|
510
|
+
return { held: held(o.load) };
|
|
511
|
+
}
|
|
512
|
+
const res = obj(await request("session/new", { cwd: o.cwd, mcpServers: [] }, open));
|
|
513
|
+
const session = str(res?.sessionId);
|
|
514
|
+
if (!session) throw new Error(`${label} opened a session without naming it`);
|
|
515
|
+
return { held: held(session) };
|
|
516
|
+
} catch (e) {
|
|
517
|
+
close();
|
|
518
|
+
return { failed: `${e.message}${signIn}` };
|
|
519
|
+
}
|
|
520
|
+
})();
|
|
521
|
+
}
|
|
522
|
+
|
|
8
523
|
// src/adapters/antigravity.ts
|
|
9
524
|
import {
|
|
10
525
|
existsSync as existsSync2,
|
|
@@ -12,38 +527,42 @@ import {
|
|
|
12
527
|
readdirSync as readdirSync2,
|
|
13
528
|
readFileSync as readFileSync2,
|
|
14
529
|
realpathSync,
|
|
15
|
-
statSync as
|
|
530
|
+
statSync as statSync3,
|
|
16
531
|
unlinkSync as unlinkSync2,
|
|
17
532
|
writeFileSync as writeFileSync2
|
|
18
533
|
} from "node:fs";
|
|
19
|
-
import
|
|
534
|
+
import path6 from "node:path";
|
|
20
535
|
|
|
21
536
|
// src/paths.ts
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
var homeDir = () =>
|
|
537
|
+
import os2 from "node:os";
|
|
538
|
+
import path3 from "node:path";
|
|
539
|
+
var homeDir = () => os2.homedir();
|
|
25
540
|
function configFile(env = process.env) {
|
|
26
|
-
if (env.SESSIONPIPE_CONFIG) return
|
|
541
|
+
if (env.SESSIONPIPE_CONFIG) return path3.resolve(env.SESSIONPIPE_CONFIG);
|
|
27
542
|
if (process.platform === "win32")
|
|
28
|
-
return
|
|
29
|
-
return
|
|
543
|
+
return path3.join(env.APPDATA || path3.join(homeDir(), "AppData", "Roaming"), "sessionpipe", "config.json");
|
|
544
|
+
return path3.join(env.XDG_CONFIG_HOME || path3.join(homeDir(), ".config"), "sessionpipe", "config.json");
|
|
30
545
|
}
|
|
31
546
|
function stateDir(env = process.env) {
|
|
32
|
-
if (env.SESSIONPIPE_STATE) return
|
|
547
|
+
if (env.SESSIONPIPE_STATE) return path3.resolve(env.SESSIONPIPE_STATE);
|
|
33
548
|
if (process.platform === "win32")
|
|
34
|
-
return
|
|
35
|
-
return
|
|
549
|
+
return path3.join(env.LOCALAPPDATA || path3.join(homeDir(), "AppData", "Local"), "sessionpipe");
|
|
550
|
+
return path3.join(env.XDG_STATE_HOME || path3.join(homeDir(), ".local", "state"), "sessionpipe");
|
|
36
551
|
}
|
|
37
552
|
function tilde(p, home = homeDir()) {
|
|
38
553
|
if (!p) return p;
|
|
39
554
|
if (p === home) return "~";
|
|
40
|
-
return p.startsWith(home +
|
|
555
|
+
return p.startsWith(home + path3.sep) || p.startsWith(`${home}/`) ? `~${p.slice(home.length)}` : p;
|
|
556
|
+
}
|
|
557
|
+
function untilde(p, home = homeDir()) {
|
|
558
|
+
if (p === "~") return home;
|
|
559
|
+
return /^~[\\/]/.test(p) ? path3.join(home, p.slice(2)) : p;
|
|
41
560
|
}
|
|
42
561
|
var safeId = (s) => String(s).replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 160);
|
|
43
562
|
|
|
44
563
|
// src/readers/files.ts
|
|
45
|
-
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from "node:fs";
|
|
46
|
-
import
|
|
564
|
+
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync as statSync2 } from "node:fs";
|
|
565
|
+
import path4 from "node:path";
|
|
47
566
|
function ends(file, headBytes, tailBytes) {
|
|
48
567
|
let fd;
|
|
49
568
|
try {
|
|
@@ -115,14 +634,14 @@ function recentFiles(root, depth, sinceMs, match) {
|
|
|
115
634
|
return;
|
|
116
635
|
}
|
|
117
636
|
for (const e of names) {
|
|
118
|
-
const p =
|
|
637
|
+
const p = path4.join(dir, e.name);
|
|
119
638
|
if (e.isDirectory()) {
|
|
120
639
|
if (d < depth) walk(p, d + 1);
|
|
121
640
|
continue;
|
|
122
641
|
}
|
|
123
642
|
if (!match(e.name)) continue;
|
|
124
643
|
try {
|
|
125
|
-
if (
|
|
644
|
+
if (statSync2(p).mtimeMs >= sinceMs) out.push(p);
|
|
126
645
|
} catch {
|
|
127
646
|
}
|
|
128
647
|
}
|
|
@@ -282,7 +801,7 @@ function readAntigravity(lines, from) {
|
|
|
282
801
|
|
|
283
802
|
// src/adapters/claude-shaped.ts
|
|
284
803
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
285
|
-
import
|
|
804
|
+
import path5 from "node:path";
|
|
286
805
|
var OURS = /(?:sessionpipe[^"\n]*[\\/]hook\.js|[\\/]sessionpipe-hook)"?\s+[a-z][a-z-]*\s+[A-Za-z]+\s*$/;
|
|
287
806
|
var isOurs = (h) => Array.isArray(h?.hooks) && h.hooks.some((x) => typeof x.command === "string" && OURS.test(x.command));
|
|
288
807
|
function readJsonFile(file) {
|
|
@@ -363,7 +882,7 @@ function installClaudeShaped(file, harness, events, cmd, o) {
|
|
|
363
882
|
}
|
|
364
883
|
if (changed) {
|
|
365
884
|
settings2.hooks = hooks;
|
|
366
|
-
mkdirSync(
|
|
885
|
+
mkdirSync(path5.dirname(file), { recursive: true });
|
|
367
886
|
writeFileSync(file, formatJson(settings2, f));
|
|
368
887
|
}
|
|
369
888
|
return { file, changed, created: !f.existed && changed };
|
|
@@ -418,10 +937,10 @@ var HOOK_NAME = "sessionpipe";
|
|
|
418
937
|
var AG_EVENTS = ["PreInvocation", "PostInvocation", "PostToolUse", "Stop"];
|
|
419
938
|
var AG_STALE_PRETOOL = '{"decision":"ask","reason":"sessionpipe: an outdated PreToolUse hook is still loaded. Restart this conversation (or run sessionpipe install) to drop it."}\n';
|
|
420
939
|
var antigravityAnswer = (event) => event === "PreToolUse" ? AG_STALE_PRETOOL : "{}\n";
|
|
421
|
-
var gemini = (env) => env.GEMINI_CLI_HOME ||
|
|
422
|
-
var hooksFile = (env) =>
|
|
423
|
-
var projectsDir = (env) =>
|
|
424
|
-
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) =>
|
|
940
|
+
var gemini = (env) => env.GEMINI_CLI_HOME || path6.join(homeDir(), ".gemini");
|
|
941
|
+
var hooksFile = (env) => path6.join(gemini(env), "config", "hooks.json");
|
|
942
|
+
var projectsDir = (env) => path6.join(gemini(env), "config", "projects");
|
|
943
|
+
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path6.join(gemini(env), d));
|
|
425
944
|
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
426
945
|
var validTool = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
427
946
|
function entry(cmd) {
|
|
@@ -433,7 +952,7 @@ function entry(cmd) {
|
|
|
433
952
|
var antigravity = {
|
|
434
953
|
name: NAME,
|
|
435
954
|
events: AG_EVENTS,
|
|
436
|
-
detect: (env = process.env) => existsSync2(
|
|
955
|
+
detect: (env = process.env) => existsSync2(path6.join(gemini(env), "config")) || dataDirs(env).some((d) => existsSync2(d)),
|
|
437
956
|
configFiles: (env = process.env) => [hooksFile(env)],
|
|
438
957
|
install(cmd, env = process.env) {
|
|
439
958
|
const file = hooksFile(env);
|
|
@@ -443,7 +962,7 @@ var antigravity = {
|
|
|
443
962
|
const e = entry(cmd);
|
|
444
963
|
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
445
964
|
hooks[HOOK_NAME] = e;
|
|
446
|
-
mkdirSync2(
|
|
965
|
+
mkdirSync2(path6.dirname(file), { recursive: true });
|
|
447
966
|
writeFileSync2(file, formatJson(hooks, f));
|
|
448
967
|
return [
|
|
449
968
|
{
|
|
@@ -554,13 +1073,13 @@ var antigravity = {
|
|
|
554
1073
|
for (const data of dataDirs(env)) {
|
|
555
1074
|
let ids = [];
|
|
556
1075
|
try {
|
|
557
|
-
ids = readdirSync2(
|
|
1076
|
+
ids = readdirSync2(path6.join(data, "brain")).filter((n) => /^[0-9a-f-]{36}$/i.test(n));
|
|
558
1077
|
} catch {
|
|
559
1078
|
}
|
|
560
1079
|
for (const id of ids) {
|
|
561
|
-
const f =
|
|
1080
|
+
const f = path6.join(data, "brain", id, ".system_generated", "logs", "transcript.jsonl");
|
|
562
1081
|
try {
|
|
563
|
-
if (
|
|
1082
|
+
if (statSync3(f).mtimeMs < sinceMs) continue;
|
|
564
1083
|
} catch {
|
|
565
1084
|
continue;
|
|
566
1085
|
}
|
|
@@ -588,8 +1107,8 @@ function agPath(p) {
|
|
|
588
1107
|
}
|
|
589
1108
|
if (process.platform === "win32" && /^\/[A-Za-z]:/.test(s)) s = s.slice(1);
|
|
590
1109
|
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s)) return null;
|
|
591
|
-
if (!
|
|
592
|
-
return
|
|
1110
|
+
if (!path6.isAbsolute(s)) return null;
|
|
1111
|
+
return path6.resolve(s);
|
|
593
1112
|
}
|
|
594
1113
|
var agWorkspaces = (paths) => (Array.isArray(paths) ? paths : []).map(agPath).filter((p) => !!p).filter((p, i, a) => a.indexOf(p) === i);
|
|
595
1114
|
function agVariants(p) {
|
|
@@ -614,7 +1133,7 @@ function agProjects(dir) {
|
|
|
614
1133
|
if (!ID_RE.test(id)) continue;
|
|
615
1134
|
let j;
|
|
616
1135
|
try {
|
|
617
|
-
j = JSON.parse(readFileSync2(
|
|
1136
|
+
j = JSON.parse(readFileSync2(path6.join(dir, n), "utf8"));
|
|
618
1137
|
} catch {
|
|
619
1138
|
continue;
|
|
620
1139
|
}
|
|
@@ -637,7 +1156,7 @@ function agMatchProject(projects, workspaces) {
|
|
|
637
1156
|
let bestLen = -1;
|
|
638
1157
|
for (const pr of projects)
|
|
639
1158
|
for (const f of pr.folders) {
|
|
640
|
-
const within = ws.some((w) => w === f || w.startsWith(f.endsWith(
|
|
1159
|
+
const within = ws.some((w) => w === f || w.startsWith(f.endsWith(path6.sep) ? f : f + path6.sep));
|
|
641
1160
|
if (within && f.length > bestLen) {
|
|
642
1161
|
best = pr.id;
|
|
643
1162
|
bestLen = f.length;
|
|
@@ -646,12 +1165,12 @@ function agMatchProject(projects, workspaces) {
|
|
|
646
1165
|
return best;
|
|
647
1166
|
}
|
|
648
1167
|
function antigravityRemoteUrl(transcript, opts) {
|
|
649
|
-
const i = transcript.indexOf(`${
|
|
1168
|
+
const i = transcript.indexOf(`${path6.sep}brain${path6.sep}`);
|
|
650
1169
|
const dirs = (i > 0 ? [transcript.slice(0, i)] : []).concat(dataDirs(opts.env));
|
|
651
1170
|
for (const d of dirs) {
|
|
652
1171
|
try {
|
|
653
1172
|
const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
|
|
654
|
-
readFileSync2(
|
|
1173
|
+
readFileSync2(path6.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
655
1174
|
);
|
|
656
1175
|
if (!m) continue;
|
|
657
1176
|
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
@@ -698,167 +1217,28 @@ __export(antigravity_control_exports, {
|
|
|
698
1217
|
agyError: () => agyError,
|
|
699
1218
|
agyLine: () => agyLine,
|
|
700
1219
|
agyModeFlags: () => agyModeFlags,
|
|
1220
|
+
defaultLevel: () => defaultLevel,
|
|
701
1221
|
findAgy: () => findAgy,
|
|
1222
|
+
offeredLevels: () => offeredLevels,
|
|
702
1223
|
reading: () => reading
|
|
703
1224
|
});
|
|
704
|
-
import
|
|
705
|
-
import
|
|
706
|
-
|
|
707
|
-
// src/adapters/headless.ts
|
|
708
|
-
import { spawn } from "node:child_process";
|
|
709
|
-
import { accessSync, constants, statSync as statSync3 } from "node:fs";
|
|
710
|
-
import path5 from "node:path";
|
|
711
|
-
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
712
|
-
var OUT_CAP = 1024 * 1024;
|
|
713
|
-
function isExe(p) {
|
|
714
|
-
try {
|
|
715
|
-
if (!statSync3(p).isFile()) return false;
|
|
716
|
-
accessSync(p, constants.X_OK);
|
|
717
|
-
return true;
|
|
718
|
-
} catch {
|
|
719
|
-
return false;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
function findBinary(o) {
|
|
723
|
-
if (o.override && isExe(o.override)) return o.override;
|
|
724
|
-
for (const d of String(o.env?.PATH || "").split(path5.delimiter)) {
|
|
725
|
-
if (!d || !path5.isAbsolute(d)) continue;
|
|
726
|
-
for (const n of o.names) if (isExe(path5.join(d, n))) return path5.join(d, n);
|
|
727
|
-
}
|
|
728
|
-
for (const d of o.dirs) for (const n of o.names) if (isExe(path5.join(d, n))) return path5.join(d, n);
|
|
729
|
-
return null;
|
|
730
|
-
}
|
|
731
|
-
function jobEnv(env = process.env) {
|
|
732
|
-
return { ...env, SESSIONPIPE_CONTROL_JOB: "1" };
|
|
733
|
-
}
|
|
734
|
-
function runHeadless(bin, args, o) {
|
|
735
|
-
return new Promise((resolve) => {
|
|
736
|
-
let out = [];
|
|
737
|
-
let outLen = 0;
|
|
738
|
-
let tooBig = false;
|
|
739
|
-
let errTail = "";
|
|
740
|
-
let timedOut = false;
|
|
741
|
-
let settled = false;
|
|
742
|
-
let child;
|
|
743
|
-
try {
|
|
744
|
-
child = spawn(bin, args, {
|
|
745
|
-
cwd: o.cwd,
|
|
746
|
-
env: o.env,
|
|
747
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
748
|
-
detached: process.platform !== "win32",
|
|
749
|
-
windowsHide: true
|
|
750
|
-
});
|
|
751
|
-
} catch (e) {
|
|
752
|
-
resolve({ error: e.message });
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
const kill = (sig) => {
|
|
756
|
-
try {
|
|
757
|
-
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
758
|
-
else child.kill(sig);
|
|
759
|
-
} catch {
|
|
760
|
-
try {
|
|
761
|
-
child.kill(sig);
|
|
762
|
-
} catch {
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
};
|
|
766
|
-
const keep = (line) => {
|
|
767
|
-
if (!o.keep?.(line)) return;
|
|
768
|
-
out = [Buffer.from(line)];
|
|
769
|
-
outLen = line.length;
|
|
770
|
-
};
|
|
771
|
-
let partial = "";
|
|
772
|
-
child.stdout?.on("data", (b) => {
|
|
773
|
-
if (o.onLine) {
|
|
774
|
-
const chunk = partial + b.toString("utf8");
|
|
775
|
-
const lines = chunk.split("\n");
|
|
776
|
-
partial = lines.pop();
|
|
777
|
-
for (const line of lines) {
|
|
778
|
-
if (!line.trim()) continue;
|
|
779
|
-
try {
|
|
780
|
-
o.onLine(line);
|
|
781
|
-
} catch {
|
|
782
|
-
}
|
|
783
|
-
keep(line);
|
|
784
|
-
}
|
|
785
|
-
if (partial.length > OUT_CAP) partial = "";
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
if (tooBig) return;
|
|
789
|
-
if (outLen + b.length > OUT_CAP) {
|
|
790
|
-
tooBig = true;
|
|
791
|
-
out = [];
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
out.push(b);
|
|
795
|
-
outLen += b.length;
|
|
796
|
-
});
|
|
797
|
-
child.stderr?.on("data", (b) => {
|
|
798
|
-
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
799
|
-
});
|
|
800
|
-
const timer = setTimeout(() => {
|
|
801
|
-
timedOut = true;
|
|
802
|
-
kill("SIGTERM");
|
|
803
|
-
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
804
|
-
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
805
|
-
const result = (code, signal) => ({
|
|
806
|
-
code,
|
|
807
|
-
signal,
|
|
808
|
-
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
809
|
-
tooBig,
|
|
810
|
-
stderr: errTail,
|
|
811
|
-
timedOut
|
|
812
|
-
});
|
|
813
|
-
const finish = (r) => {
|
|
814
|
-
if (settled) return;
|
|
815
|
-
settled = true;
|
|
816
|
-
clearTimeout(timer);
|
|
817
|
-
resolve(r);
|
|
818
|
-
};
|
|
819
|
-
child.on("error", (e) => finish({ error: e.message }));
|
|
820
|
-
child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
|
|
821
|
-
child.on("close", (code, signal) => {
|
|
822
|
-
if (o.onLine && partial.trim()) {
|
|
823
|
-
try {
|
|
824
|
-
o.onLine(partial);
|
|
825
|
-
} catch {
|
|
826
|
-
}
|
|
827
|
-
keep(partial);
|
|
828
|
-
partial = "";
|
|
829
|
-
}
|
|
830
|
-
finish(result(code, signal));
|
|
831
|
-
});
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
function stderrTail(stderr, n = 3) {
|
|
835
|
-
return String(stderr || "").trim().split("\n").slice(-n).join(" ").trim();
|
|
836
|
-
}
|
|
837
|
-
function deniedNote(names, count) {
|
|
838
|
-
if (!count) return "";
|
|
839
|
-
return `
|
|
840
|
-
|
|
841
|
-
(Not allowed on this machine, since nobody was there to approve: ${names.join(", ") || `${count} action(s)`}.)`;
|
|
842
|
-
}
|
|
843
|
-
function runFailure(label, res) {
|
|
844
|
-
if (res.error) return `couldn't start ${label}: ${res.error}`;
|
|
845
|
-
if (res.timedOut) return `${label} was still working after 30 minutes, so it was stopped`;
|
|
846
|
-
return null;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
// src/adapters/antigravity-control.ts
|
|
1225
|
+
import os3 from "node:os";
|
|
1226
|
+
import path7 from "node:path";
|
|
850
1227
|
var LABEL = "Antigravity";
|
|
851
|
-
function findAgy(env = process.env, home =
|
|
1228
|
+
function findAgy(env = process.env, home = os3.homedir()) {
|
|
852
1229
|
return findBinary({
|
|
853
1230
|
override: env.SESSIONPIPE_AGY,
|
|
854
1231
|
names: process.platform === "win32" ? ["agy.exe", "agy.cmd"] : ["agy"],
|
|
855
1232
|
env,
|
|
856
|
-
dirs: [
|
|
1233
|
+
dirs: [path7.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
|
|
857
1234
|
});
|
|
858
1235
|
}
|
|
859
|
-
function agyModeFlags(
|
|
860
|
-
|
|
1236
|
+
function agyModeFlags(level) {
|
|
1237
|
+
if (level === "read") throw new Error("Antigravity has no read-only mode");
|
|
1238
|
+
return level === "safe" || level === "inherit" ? [] : ["--mode", "accept-edits"];
|
|
861
1239
|
}
|
|
1240
|
+
var offeredLevels = (mode) => mode === "auto" ? ["safe", "edits"] : ["safe"];
|
|
1241
|
+
var defaultLevel = (mode) => mode === "auto" ? "edits" : mode;
|
|
862
1242
|
function agyArgs(o) {
|
|
863
1243
|
const prompt = o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt;
|
|
864
1244
|
return [
|
|
@@ -867,7 +1247,7 @@ function agyArgs(o) {
|
|
|
867
1247
|
prompt,
|
|
868
1248
|
"--output-format",
|
|
869
1249
|
"stream-json",
|
|
870
|
-
...agyModeFlags(o.
|
|
1250
|
+
...agyModeFlags(o.permission)
|
|
871
1251
|
];
|
|
872
1252
|
}
|
|
873
1253
|
var agyEnv = (env = process.env) => jobEnv(env);
|
|
@@ -879,26 +1259,26 @@ function agyLine(line) {
|
|
|
879
1259
|
return null;
|
|
880
1260
|
}
|
|
881
1261
|
if (!j || typeof j !== "object") return null;
|
|
882
|
-
const
|
|
1262
|
+
const str2 = (v) => typeof v === "string" ? v : "";
|
|
883
1263
|
const out = {};
|
|
884
1264
|
if (j.event === "init") {
|
|
885
|
-
if (
|
|
1265
|
+
if (str2(j.conversation_id)) out.session = str2(j.conversation_id);
|
|
886
1266
|
} else if (j.event === "step_update") {
|
|
887
1267
|
const s = j.step_update ?? {};
|
|
888
|
-
if (
|
|
889
|
-
if (s.step_type === "agent_response" &&
|
|
890
|
-
out.delta = { step: Number(s.step_index ?? 0), text:
|
|
1268
|
+
if (str2(s.conversation_id)) out.session = str2(s.conversation_id);
|
|
1269
|
+
if (s.step_type === "agent_response" && str2(s.text_delta))
|
|
1270
|
+
out.delta = { step: Number(s.step_index ?? 0), text: str2(s.text_delta) };
|
|
891
1271
|
if (s.step_type === "error_message") {
|
|
892
|
-
const why =
|
|
1272
|
+
const why = str2(s.text_delta) || str2(s.error) || str2(s.message);
|
|
893
1273
|
if (why) out.note = why;
|
|
894
1274
|
}
|
|
895
1275
|
} else if (j.event === "result") {
|
|
896
1276
|
const r = j.result ?? {};
|
|
897
|
-
if (
|
|
1277
|
+
if (str2(r.conversation_id)) out.session = str2(r.conversation_id);
|
|
898
1278
|
out.result = {
|
|
899
|
-
status:
|
|
900
|
-
response:
|
|
901
|
-
error:
|
|
1279
|
+
status: str2(r.status),
|
|
1280
|
+
response: str2(r.response),
|
|
1281
|
+
error: str2(r.error),
|
|
902
1282
|
denied: Array.isArray(r.denied_actions) ? r.denied_actions : []
|
|
903
1283
|
};
|
|
904
1284
|
} else return null;
|
|
@@ -970,7 +1350,7 @@ var reading = () => new AgyReading();
|
|
|
970
1350
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
971
1351
|
import { createHash } from "node:crypto";
|
|
972
1352
|
import { existsSync as existsSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync4 } from "node:fs";
|
|
973
|
-
import
|
|
1353
|
+
import path8 from "node:path";
|
|
974
1354
|
|
|
975
1355
|
// src/readers/git.ts
|
|
976
1356
|
import { execFileSync } from "node:child_process";
|
|
@@ -1036,23 +1416,23 @@ function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
|
1036
1416
|
const out = [];
|
|
1037
1417
|
const add = (d) => {
|
|
1038
1418
|
if (!d) return;
|
|
1039
|
-
const r =
|
|
1419
|
+
const r = path8.resolve(d);
|
|
1040
1420
|
if (!out.includes(r)) out.push(r);
|
|
1041
1421
|
};
|
|
1042
|
-
add(
|
|
1422
|
+
add(path8.join(home, ".claude"));
|
|
1043
1423
|
add(env.CLAUDE_CONFIG_DIR);
|
|
1044
1424
|
try {
|
|
1045
1425
|
for (const e of readdirSync3(home, { withFileTypes: true })) {
|
|
1046
1426
|
if (!e.isDirectory() || !/^\.claude[-_.]/.test(e.name)) continue;
|
|
1047
|
-
const d =
|
|
1048
|
-
if (existsSync3(
|
|
1427
|
+
const d = path8.join(home, e.name);
|
|
1428
|
+
if (existsSync3(path8.join(d, "projects")) || existsSync3(path8.join(d, "settings.json"))) add(d);
|
|
1049
1429
|
}
|
|
1050
1430
|
} catch {
|
|
1051
1431
|
}
|
|
1052
1432
|
return out.filter((d, i) => i === 0 || existsSync3(d));
|
|
1053
1433
|
}
|
|
1054
|
-
var settingsIn = (dir) =>
|
|
1055
|
-
var configDirOf = (transcript) =>
|
|
1434
|
+
var settingsIn = (dir) => path8.join(dir, "settings.json");
|
|
1435
|
+
var configDirOf = (transcript) => path8.dirname(path8.dirname(path8.dirname(transcript)));
|
|
1056
1436
|
var validTool2 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1057
1437
|
var endReason = (r) => r === "clear" || r === "logout" || r === "exit" ? r : r === "prompt_input_exit" ? "exit" : "other";
|
|
1058
1438
|
var notifKind = (t) => t === "permission_prompt" ? "permission" : t === "idle_prompt" ? "idle" : t === "elicitation_dialog" ? "elicitation" : "question";
|
|
@@ -1096,14 +1476,28 @@ var claudeCode = {
|
|
|
1096
1476
|
case "SessionStart":
|
|
1097
1477
|
events.push(ev("session.started", { source: pickSource(s.source ?? s.trigger) }));
|
|
1098
1478
|
break;
|
|
1099
|
-
case "UserPromptSubmit":
|
|
1479
|
+
case "UserPromptSubmit": {
|
|
1480
|
+
const prompt = typeof s.prompt === "string" ? s.prompt : void 0;
|
|
1100
1481
|
events.push(
|
|
1101
1482
|
ev("turn.started", {
|
|
1102
1483
|
turn_id,
|
|
1103
|
-
prompt_chars:
|
|
1484
|
+
prompt_chars: prompt === void 0 ? void 0 : [...prompt].length,
|
|
1485
|
+
// Claude Code's hook schema documents `source`
|
|
1486
|
+
// (user | sdk | system | loop_wakeup | schedule_wakeup | poll_event) and says a
|
|
1487
|
+
// payload may omit it while the field rolls out; 2.1.304's payload has no such
|
|
1488
|
+
// key at all. Forwarded verbatim, shape-guarded only: the list is the harness's
|
|
1489
|
+
// to extend.
|
|
1490
|
+
submitted_by: harnessWord(s.source),
|
|
1491
|
+
// One of Claude Code's own prompt wrappers: the prompt is a block the harness
|
|
1492
|
+
// wrote, so it is not the person's words. Absent is unknown, never "the person" —
|
|
1493
|
+
// and `true` is never "no person caused this turn", because a Talk or
|
|
1494
|
+
// `sessionpipe wait` delivery hands the person's own message over inside a
|
|
1495
|
+
// <task-notification> (PROTOCOL.md §4).
|
|
1496
|
+
injected: prompt !== void 0 && injectedBlock(prompt) ? true : void 0
|
|
1104
1497
|
})
|
|
1105
1498
|
);
|
|
1106
1499
|
break;
|
|
1500
|
+
}
|
|
1107
1501
|
case "PreToolUse":
|
|
1108
1502
|
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, agent_id, input: s.tool_input }));
|
|
1109
1503
|
break;
|
|
@@ -1158,7 +1552,13 @@ var claudeCode = {
|
|
|
1158
1552
|
);
|
|
1159
1553
|
break;
|
|
1160
1554
|
case "Stop":
|
|
1161
|
-
events.push(
|
|
1555
|
+
events.push(
|
|
1556
|
+
ev("turn.ended", {
|
|
1557
|
+
turn_id,
|
|
1558
|
+
reason: "stop",
|
|
1559
|
+
background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
|
|
1560
|
+
})
|
|
1561
|
+
);
|
|
1162
1562
|
break;
|
|
1163
1563
|
case "StopFailure":
|
|
1164
1564
|
events.push(
|
|
@@ -1201,6 +1601,8 @@ var claudeCode = {
|
|
|
1201
1601
|
if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
|
|
1202
1602
|
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
1203
1603
|
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
1604
|
+
const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;
|
|
1605
|
+
if (attended === "1" || attended === "0") hints.attended = attended === "1" || !!input.env.SESSIONPIPE_CONTROL_JOB;
|
|
1204
1606
|
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
1205
1607
|
const r = { session, events, hints };
|
|
1206
1608
|
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
@@ -1221,17 +1623,18 @@ var claudeCode = {
|
|
|
1221
1623
|
const cwd = out.cwd ?? session.cwd;
|
|
1222
1624
|
Object.assign(out, gitFacts(cwd), out.branch ? { branch: out.branch } : {});
|
|
1223
1625
|
if (!out.url && typeof hints.desktop_id === "string") out.url = `claude://code/continue/${hints.desktop_id}`;
|
|
1626
|
+
if (typeof hints.attended === "boolean") out.attended = hints.attended || !!out.url;
|
|
1224
1627
|
return out;
|
|
1225
1628
|
},
|
|
1226
1629
|
backfill(sinceMs, env = process.env) {
|
|
1227
1630
|
const rows2 = [];
|
|
1228
1631
|
const files = [];
|
|
1229
1632
|
for (const d of claudeDirs(env))
|
|
1230
|
-
files.push(...recentFiles(
|
|
1633
|
+
files.push(...recentFiles(path8.join(d, "projects"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\.jsonl$/.test(n)));
|
|
1231
1634
|
const noState = { get: () => ({}), save: () => {
|
|
1232
1635
|
} };
|
|
1233
1636
|
for (const f of files) {
|
|
1234
|
-
const id =
|
|
1637
|
+
const id = path8.basename(f, ".jsonl");
|
|
1235
1638
|
const facts = claudeFacts(f, id, {}, noState);
|
|
1236
1639
|
if (!facts.started_at || !facts.last_at) continue;
|
|
1237
1640
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
@@ -1256,6 +1659,9 @@ function notClaudeCode(id, s) {
|
|
|
1256
1659
|
function pickSource(v) {
|
|
1257
1660
|
return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
1258
1661
|
}
|
|
1662
|
+
var harnessWord = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/.test(v) ? v : void 0;
|
|
1663
|
+
var WRAPPER = /^<(?:task-notification|cross-session-message|system-reminder|local-command-stdout|command-[a-z-]+)[\s/>]/i;
|
|
1664
|
+
var injectedBlock = (prompt) => WRAPPER.test(prompt.trim());
|
|
1259
1665
|
function errText(v) {
|
|
1260
1666
|
if (typeof v === "string") return v.slice(0, 2e3);
|
|
1261
1667
|
if (v && typeof v === "object") {
|
|
@@ -1295,12 +1701,12 @@ function claudeLive(dir) {
|
|
|
1295
1701
|
const map = /* @__PURE__ */ new Map();
|
|
1296
1702
|
let names = [];
|
|
1297
1703
|
try {
|
|
1298
|
-
names = readdirSync3(
|
|
1704
|
+
names = readdirSync3(path8.join(dir, "sessions")).filter((n) => n.endsWith(".json"));
|
|
1299
1705
|
} catch {
|
|
1300
1706
|
}
|
|
1301
1707
|
for (const n of names) {
|
|
1302
1708
|
try {
|
|
1303
|
-
const e = registryEntry(JSON.parse(readFileSync3(
|
|
1709
|
+
const e = registryEntry(JSON.parse(readFileSync3(path8.join(dir, "sessions", n), "utf8")));
|
|
1304
1710
|
if (e?.sessionId) map.set(e.sessionId, e);
|
|
1305
1711
|
} catch {
|
|
1306
1712
|
}
|
|
@@ -1311,7 +1717,7 @@ function claudeLive(dir) {
|
|
|
1311
1717
|
function claudeLiveByPid(dir, pid) {
|
|
1312
1718
|
if (!pid) return null;
|
|
1313
1719
|
try {
|
|
1314
|
-
return registryEntry(JSON.parse(readFileSync3(
|
|
1720
|
+
return registryEntry(JSON.parse(readFileSync3(path8.join(dir, "sessions", `${pid}.json`), "utf8")));
|
|
1315
1721
|
} catch {
|
|
1316
1722
|
return null;
|
|
1317
1723
|
}
|
|
@@ -1319,7 +1725,7 @@ function claudeLiveByPid(dir, pid) {
|
|
|
1319
1725
|
function claudePid(env = process.env) {
|
|
1320
1726
|
try {
|
|
1321
1727
|
const dirs = claudeDirs(env);
|
|
1322
|
-
const has = (pid2) => dirs.some((d) => existsSync3(
|
|
1728
|
+
const has = (pid2) => dirs.some((d) => existsSync3(path8.join(d, "sessions", `${pid2}.json`)));
|
|
1323
1729
|
let pid = process.ppid;
|
|
1324
1730
|
if (has(pid)) return pid;
|
|
1325
1731
|
const parent = parentOf();
|
|
@@ -1360,8 +1766,8 @@ function parentOf() {
|
|
|
1360
1766
|
}
|
|
1361
1767
|
var EMAIL_RE = /^[^@\s]{1,64}@[^@\s]{1,189}$/;
|
|
1362
1768
|
function claudeLogin(dir, home = homeDir()) {
|
|
1363
|
-
const files = [
|
|
1364
|
-
if (
|
|
1769
|
+
const files = [path8.join(dir, ".claude.json")];
|
|
1770
|
+
if (path8.resolve(dir) === path8.join(home, ".claude")) files.unshift(path8.join(home, ".claude.json"));
|
|
1365
1771
|
for (const f of files) {
|
|
1366
1772
|
try {
|
|
1367
1773
|
const a = JSON.parse(readFileSync3(f, "utf8")).oauthAccount;
|
|
@@ -1372,7 +1778,27 @@ function claudeLogin(dir, home = homeDir()) {
|
|
|
1372
1778
|
} catch {
|
|
1373
1779
|
}
|
|
1374
1780
|
}
|
|
1375
|
-
return
|
|
1781
|
+
return apiAccount(dir);
|
|
1782
|
+
}
|
|
1783
|
+
var ANTHROPIC_HOST = /(^|\.)anthropic\.com$/i;
|
|
1784
|
+
function apiAccount(dir) {
|
|
1785
|
+
let base;
|
|
1786
|
+
try {
|
|
1787
|
+
base = JSON.parse(readFileSync3(settingsIn(dir), "utf8")).env?.ANTHROPIC_BASE_URL;
|
|
1788
|
+
} catch {
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1791
|
+
if (typeof base !== "string") return null;
|
|
1792
|
+
let u;
|
|
1793
|
+
try {
|
|
1794
|
+
u = new URL(base.trim());
|
|
1795
|
+
} catch {
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
if (u.protocol !== "https:" && u.protocol !== "http:" || !u.hostname || ANTHROPIC_HOST.test(u.hostname))
|
|
1799
|
+
return null;
|
|
1800
|
+
const origin = `${u.protocol}//${u.host}`;
|
|
1801
|
+
return { id: `api-${createHash("sha256").update(origin).digest("hex").slice(0, 16)}`, name: u.host };
|
|
1376
1802
|
}
|
|
1377
1803
|
var accountCache = /* @__PURE__ */ new Map();
|
|
1378
1804
|
function claudeAccount(dir) {
|
|
@@ -1463,7 +1889,7 @@ function claudeFacts(file, sessionId, hints, state) {
|
|
|
1463
1889
|
const H = parseLines(head);
|
|
1464
1890
|
const T = parseLines(tail);
|
|
1465
1891
|
const out = {};
|
|
1466
|
-
const id = sessionId ||
|
|
1892
|
+
const id = sessionId || path8.basename(file, ".jsonl");
|
|
1467
1893
|
const mem = state.get(NAME2, id);
|
|
1468
1894
|
let custom = null;
|
|
1469
1895
|
let ai = null;
|
|
@@ -1547,9 +1973,12 @@ function claudeFacts(file, sessionId, hints, state) {
|
|
|
1547
1973
|
var claude_code_control_exports = {};
|
|
1548
1974
|
__export(claude_code_control_exports, {
|
|
1549
1975
|
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
1976
|
+
ceilingLevel: () => ceilingLevel,
|
|
1977
|
+
claudeArgs: () => claudeArgs,
|
|
1550
1978
|
claudeEnv: () => claudeEnv,
|
|
1551
1979
|
claudeLogin: () => claudeLogin2,
|
|
1552
1980
|
cmpVersion: () => cmpVersion,
|
|
1981
|
+
defaultLevel: () => defaultLevel2,
|
|
1553
1982
|
detectCaps: () => detectCaps,
|
|
1554
1983
|
findClaude: () => findClaude,
|
|
1555
1984
|
findTranscript: () => findTranscript,
|
|
@@ -1558,6 +1987,8 @@ __export(claude_code_control_exports, {
|
|
|
1558
1987
|
loginCommand: () => loginCommand,
|
|
1559
1988
|
loginHint: () => loginHint,
|
|
1560
1989
|
modeFlags: () => modeFlags,
|
|
1990
|
+
offeredLevels: () => offeredLevels2,
|
|
1991
|
+
outputArgs: () => outputArgs,
|
|
1561
1992
|
parseAuthStatus: () => parseAuthStatus,
|
|
1562
1993
|
parseCaps: () => parseCaps,
|
|
1563
1994
|
parseResult: () => parseResult,
|
|
@@ -1567,8 +1998,22 @@ __export(claude_code_control_exports, {
|
|
|
1567
1998
|
});
|
|
1568
1999
|
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
1569
2000
|
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync4, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
1570
|
-
import
|
|
1571
|
-
import
|
|
2001
|
+
import os4 from "node:os";
|
|
2002
|
+
import path9 from "node:path";
|
|
2003
|
+
|
|
2004
|
+
// src/control/verify.ts
|
|
2005
|
+
var GRANT_MAX_MS = 24 * 36e5;
|
|
2006
|
+
var SKEW_MS = 5 * 6e4;
|
|
2007
|
+
var MACHINE_MAX_AGE_MS = 24 * 36e5;
|
|
2008
|
+
var RECEIVER_MAX_AGE_MS = 10 * 6e4;
|
|
2009
|
+
var NONCE_TTL_MS = 25 * 36e5;
|
|
2010
|
+
var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
2011
|
+
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
2012
|
+
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
2013
|
+
var enc = new TextEncoder();
|
|
2014
|
+
var dec = new TextDecoder();
|
|
2015
|
+
|
|
2016
|
+
// src/adapters/claude-code-control.ts
|
|
1572
2017
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1573
2018
|
var LIVE_WRITE_MS = 9e4;
|
|
1574
2019
|
var cmpVersion = (a, b) => {
|
|
@@ -1578,28 +2023,28 @@ var cmpVersion = (a, b) => {
|
|
|
1578
2023
|
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1579
2024
|
return 0;
|
|
1580
2025
|
};
|
|
1581
|
-
function findClaude(env = process.env, home =
|
|
2026
|
+
function findClaude(env = process.env, home = os4.homedir()) {
|
|
1582
2027
|
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
1583
2028
|
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
1584
|
-
for (const d of String(env.PATH || "").split(
|
|
1585
|
-
if (!d || !
|
|
1586
|
-
for (const n of names) if (isExe(
|
|
2029
|
+
for (const d of String(env.PATH || "").split(path9.delimiter)) {
|
|
2030
|
+
if (!d || !path9.isAbsolute(d)) continue;
|
|
2031
|
+
for (const n of names) if (isExe(path9.join(d, n))) return path9.join(d, n);
|
|
1587
2032
|
}
|
|
1588
2033
|
for (const p of [
|
|
1589
|
-
|
|
1590
|
-
|
|
2034
|
+
path9.join(home, ".claude", "local", "claude"),
|
|
2035
|
+
path9.join(home, ".local", "bin", "claude"),
|
|
1591
2036
|
"/opt/homebrew/bin/claude",
|
|
1592
2037
|
"/usr/local/bin/claude"
|
|
1593
2038
|
])
|
|
1594
2039
|
if (isExe(p)) return p;
|
|
1595
|
-
const app =
|
|
2040
|
+
const app = path9.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
1596
2041
|
let versions = [];
|
|
1597
2042
|
try {
|
|
1598
2043
|
versions = readdirSync4(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
|
|
1599
2044
|
} catch {
|
|
1600
2045
|
}
|
|
1601
2046
|
for (const v of versions) {
|
|
1602
|
-
const p =
|
|
2047
|
+
const p = path9.join(app, v, "claude.app", "Contents", "MacOS", "claude");
|
|
1603
2048
|
if (isExe(p)) return p;
|
|
1604
2049
|
}
|
|
1605
2050
|
return null;
|
|
@@ -1625,11 +2070,19 @@ function parseCaps(help) {
|
|
|
1625
2070
|
stream: !!optionBlock(help, "--include-partial-messages")
|
|
1626
2071
|
};
|
|
1627
2072
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
2073
|
+
var LEVEL_MODE = {
|
|
2074
|
+
read: "plan",
|
|
2075
|
+
edits: "acceptEdits",
|
|
2076
|
+
auto: "auto"
|
|
2077
|
+
};
|
|
2078
|
+
function modeFlags(level, caps) {
|
|
2079
|
+
if (level === "inherit") return { args: [] };
|
|
2080
|
+
if (level === "auto" && !caps.modes.includes("auto"))
|
|
2081
|
+
return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
|
|
2082
|
+
if (level !== "safe") {
|
|
2083
|
+
const m = LEVEL_MODE[level];
|
|
2084
|
+
if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };
|
|
2085
|
+
return { args: ["--permission-mode", m] };
|
|
1633
2086
|
}
|
|
1634
2087
|
if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
|
|
1635
2088
|
const args = [];
|
|
@@ -1641,6 +2094,32 @@ function modeFlags(mode, caps) {
|
|
|
1641
2094
|
if (caps.promptsNone) args.push("--permission-prompts", "none");
|
|
1642
2095
|
return { args };
|
|
1643
2096
|
}
|
|
2097
|
+
var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
|
|
2098
|
+
function offeredLevels2(mode, caps) {
|
|
2099
|
+
return START_PERMISSIONS.filter(
|
|
2100
|
+
(l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
var defaultLevel2 = (mode) => mode;
|
|
2104
|
+
function outputArgs(caps) {
|
|
2105
|
+
return caps.stream ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"];
|
|
2106
|
+
}
|
|
2107
|
+
function claudeArgs(o) {
|
|
2108
|
+
const mf = modeFlags(o.level, o.caps);
|
|
2109
|
+
if ("error" in mf) return mf;
|
|
2110
|
+
const where = o.resume ? ["--resume", o.resume, ...o.fork ? ["--fork-session"] : []] : ["--session-id", o.session];
|
|
2111
|
+
return {
|
|
2112
|
+
args: [
|
|
2113
|
+
"-p",
|
|
2114
|
+
...outputArgs(o.caps),
|
|
2115
|
+
...mf.args,
|
|
2116
|
+
...o.model ? ["--model", o.model] : [],
|
|
2117
|
+
...o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(",")}`] : [],
|
|
2118
|
+
...where,
|
|
2119
|
+
o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt
|
|
2120
|
+
]
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
1644
2123
|
function claudeEnv(configDir, env = process.env) {
|
|
1645
2124
|
const e = { ...env };
|
|
1646
2125
|
for (const k of [
|
|
@@ -1654,7 +2133,7 @@ function claudeEnv(configDir, env = process.env) {
|
|
|
1654
2133
|
delete e[k];
|
|
1655
2134
|
e.SESSIONPIPE_CONTROL_JOB = "1";
|
|
1656
2135
|
if (configDir !== void 0) {
|
|
1657
|
-
if (
|
|
2136
|
+
if (path9.resolve(configDir) === path9.resolve(os4.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
|
|
1658
2137
|
else e.CLAUDE_CONFIG_DIR = configDir;
|
|
1659
2138
|
}
|
|
1660
2139
|
return e;
|
|
@@ -1687,12 +2166,12 @@ function claudeLogin2(bin, configDir, env = process.env) {
|
|
|
1687
2166
|
});
|
|
1688
2167
|
}
|
|
1689
2168
|
function loginCommand(configDir) {
|
|
1690
|
-
const home =
|
|
1691
|
-
const isDefault = !configDir ||
|
|
2169
|
+
const home = os4.homedir();
|
|
2170
|
+
const isDefault = !configDir || path9.resolve(configDir) === path9.resolve(home, ".claude");
|
|
1692
2171
|
return isDefault ? "`claude auth login`" : `\`CLAUDE_CONFIG_DIR=${configDir.replace(home, "~")} claude auth login\``;
|
|
1693
2172
|
}
|
|
1694
2173
|
function loginHint(configDir, platform = process.platform) {
|
|
1695
|
-
const shown = configDir ? configDir.replace(
|
|
2174
|
+
const shown = configDir ? configDir.replace(os4.homedir(), "~") : "~/.claude";
|
|
1696
2175
|
const login = loginCommand(configDir);
|
|
1697
2176
|
if (platform === "darwin")
|
|
1698
2177
|
return `Claude Code (${shown}) isn't signed in as this machine's background service sees it. On a Mac its login lives in the login keychain, which a background service can read only while someone is logged in at the screen: log in at the Mac, or run ${login} there.`;
|
|
@@ -1718,13 +2197,13 @@ function findTranscript(session, dirs) {
|
|
|
1718
2197
|
for (const dir of dirs) {
|
|
1719
2198
|
let projects = [];
|
|
1720
2199
|
try {
|
|
1721
|
-
projects = readdirSync4(
|
|
2200
|
+
projects = readdirSync4(path9.join(dir, "projects"), { withFileTypes: true });
|
|
1722
2201
|
} catch {
|
|
1723
2202
|
continue;
|
|
1724
2203
|
}
|
|
1725
2204
|
for (const p of projects) {
|
|
1726
2205
|
if (!p.isDirectory()) continue;
|
|
1727
|
-
const file =
|
|
2206
|
+
const file = path9.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
1728
2207
|
try {
|
|
1729
2208
|
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
1730
2209
|
} catch {
|
|
@@ -1746,7 +2225,7 @@ function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
|
|
|
1746
2225
|
} catch {
|
|
1747
2226
|
return null;
|
|
1748
2227
|
}
|
|
1749
|
-
if (!j || typeof j.cwd !== "string" || !
|
|
2228
|
+
if (!j || typeof j.cwd !== "string" || !path9.isAbsolute(j.cwd)) return null;
|
|
1750
2229
|
if (projectName(j.cwd) === t.project) return j.cwd;
|
|
1751
2230
|
first ??= j.cwd;
|
|
1752
2231
|
count++;
|
|
@@ -1807,13 +2286,13 @@ function pidAlive(pid) {
|
|
|
1807
2286
|
function liveProcess(session, configDir) {
|
|
1808
2287
|
let names = [];
|
|
1809
2288
|
try {
|
|
1810
|
-
names = readdirSync4(
|
|
2289
|
+
names = readdirSync4(path9.join(configDir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
|
|
1811
2290
|
} catch {
|
|
1812
2291
|
return false;
|
|
1813
2292
|
}
|
|
1814
2293
|
for (const n of names) {
|
|
1815
2294
|
try {
|
|
1816
|
-
const j = JSON.parse(readFileSync4(
|
|
2295
|
+
const j = JSON.parse(readFileSync4(path9.join(configDir, "sessions", n), "utf8"));
|
|
1817
2296
|
if (String(j.sessionId) !== session) continue;
|
|
1818
2297
|
if (pidAlive(Number(j.pid ?? n.slice(0, -5)))) return true;
|
|
1819
2298
|
} catch {
|
|
@@ -1862,7 +2341,7 @@ var isUuid = (s) => typeof s === "string" && UUID_RE.test(s);
|
|
|
1862
2341
|
|
|
1863
2342
|
// src/adapters/codex.ts
|
|
1864
2343
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1865
|
-
import
|
|
2344
|
+
import path10 from "node:path";
|
|
1866
2345
|
|
|
1867
2346
|
// src/adapters/codex-config.ts
|
|
1868
2347
|
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?$/m;
|
|
@@ -2007,10 +2486,10 @@ var CODEX_EVENTS = [
|
|
|
2007
2486
|
];
|
|
2008
2487
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
2009
2488
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
2010
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
2011
|
-
var hooksFile2 = (env) =>
|
|
2012
|
-
var configFile2 = (env) =>
|
|
2013
|
-
var sessionsDir = (env) =>
|
|
2489
|
+
var codexHome = (env) => env.CODEX_HOME || path10.join(homeDir(), ".codex");
|
|
2490
|
+
var hooksFile2 = (env) => path10.join(codexHome(env), "hooks.json");
|
|
2491
|
+
var configFile2 = (env) => path10.join(codexHome(env), "config.toml");
|
|
2492
|
+
var sessionsDir = (env) => path10.join(codexHome(env), "sessions");
|
|
2014
2493
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2015
2494
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
2016
2495
|
function installNotify(cmd, env) {
|
|
@@ -2024,7 +2503,7 @@ function installNotify(cmd, env) {
|
|
|
2024
2503
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
2025
2504
|
const n = readNotify(text);
|
|
2026
2505
|
const write = (next) => {
|
|
2027
|
-
mkdirSync3(
|
|
2506
|
+
mkdirSync3(path10.dirname(file), { recursive: true });
|
|
2028
2507
|
writeFileSync3(file, next);
|
|
2029
2508
|
};
|
|
2030
2509
|
if (n && !n.other) {
|
|
@@ -2091,7 +2570,7 @@ function findRollout(threadId, env = process.env) {
|
|
|
2091
2570
|
return;
|
|
2092
2571
|
}
|
|
2093
2572
|
for (const n of names.sort().reverse()) {
|
|
2094
|
-
const p =
|
|
2573
|
+
const p = path10.join(dir, n);
|
|
2095
2574
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
2096
2575
|
best = p;
|
|
2097
2576
|
return;
|
|
@@ -2181,7 +2660,7 @@ var codex = {
|
|
|
2181
2660
|
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2182
2661
|
} catch {
|
|
2183
2662
|
}
|
|
2184
|
-
const id = s.session_id;
|
|
2663
|
+
const id = s.session_id ?? s.thread_id ?? s.conversation_id ?? s["thread-id"] ?? s["session-id"];
|
|
2185
2664
|
if (!id) return null;
|
|
2186
2665
|
if (s.transcript_path === null) return null;
|
|
2187
2666
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
@@ -2276,7 +2755,7 @@ var codex = {
|
|
|
2276
2755
|
backfill(sinceMs, env = process.env) {
|
|
2277
2756
|
const rows2 = [];
|
|
2278
2757
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
2279
|
-
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(
|
|
2758
|
+
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path10.basename(f));
|
|
2280
2759
|
if (!m?.[1]) continue;
|
|
2281
2760
|
if (codexSubagent(rolloutMeta(f))) continue;
|
|
2282
2761
|
const facts = codexFacts(f);
|
|
@@ -2343,38 +2822,71 @@ __export(codex_control_exports, {
|
|
|
2343
2822
|
codexLine: () => codexLine,
|
|
2344
2823
|
codexLogin: () => codexLogin,
|
|
2345
2824
|
codexModeFlags: () => codexModeFlags,
|
|
2825
|
+
codexModels: () => codexModels,
|
|
2826
|
+
defaultLevel: () => defaultLevel3,
|
|
2346
2827
|
findCodex: () => findCodex,
|
|
2347
2828
|
findRollout: () => findRollout,
|
|
2829
|
+
offeredLevels: () => offeredLevels3,
|
|
2348
2830
|
reading: () => reading2
|
|
2349
2831
|
});
|
|
2350
2832
|
import { execFile as execFile2 } from "node:child_process";
|
|
2351
|
-
import
|
|
2352
|
-
import
|
|
2833
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
2834
|
+
import os5 from "node:os";
|
|
2835
|
+
import path11 from "node:path";
|
|
2353
2836
|
var LABEL2 = "Codex";
|
|
2354
|
-
function findCodex(env = process.env, home =
|
|
2837
|
+
function findCodex(env = process.env, home = os5.homedir(), nodeDir = path11.dirname(process.execPath)) {
|
|
2355
2838
|
return findBinary({
|
|
2356
2839
|
override: env.SESSIONPIPE_CODEX,
|
|
2357
2840
|
names: process.platform === "win32" ? ["codex.exe", "codex.cmd"] : ["codex"],
|
|
2358
2841
|
env,
|
|
2359
|
-
dirs: [nodeDir,
|
|
2842
|
+
dirs: [nodeDir, path11.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
|
|
2360
2843
|
});
|
|
2361
2844
|
}
|
|
2362
|
-
function codexModeFlags(
|
|
2845
|
+
function codexModeFlags(level, resume = false) {
|
|
2846
|
+
if (level === "inherit") return [];
|
|
2847
|
+
const sandbox = level === "read" ? "read-only" : "workspace-write";
|
|
2363
2848
|
if (resume)
|
|
2364
|
-
return
|
|
2849
|
+
return level === "auto" ? [
|
|
2365
2850
|
"-c",
|
|
2366
2851
|
'sandbox_mode="workspace-write"',
|
|
2367
2852
|
"-c",
|
|
2368
2853
|
'approval_policy="on-request"',
|
|
2369
2854
|
"-c",
|
|
2370
2855
|
'approvals_reviewer="auto_review"'
|
|
2371
|
-
] : ["-c",
|
|
2372
|
-
return
|
|
2856
|
+
] : ["-c", `sandbox_mode="${sandbox}"`];
|
|
2857
|
+
return level === "auto" ? ["--approve-for-me"] : ["-s", sandbox];
|
|
2373
2858
|
}
|
|
2859
|
+
var offeredLevels3 = (mode) => mode === "auto" ? ["read", "safe", "auto"] : ["read", "safe"];
|
|
2860
|
+
var defaultLevel3 = (mode) => mode;
|
|
2374
2861
|
function codexArgs(o) {
|
|
2375
2862
|
const common = ["--json", "--skip-git-repo-check"];
|
|
2376
|
-
|
|
2377
|
-
|
|
2863
|
+
const model = o.model ? ["-m", o.model] : [];
|
|
2864
|
+
if (o.resume)
|
|
2865
|
+
return ["exec", "resume", ...common, ...codexModeFlags(o.permission, true), ...model, "--", o.resume, o.prompt];
|
|
2866
|
+
return [
|
|
2867
|
+
"exec",
|
|
2868
|
+
...common,
|
|
2869
|
+
...o.cwd ? ["-C", o.cwd] : [],
|
|
2870
|
+
...codexModeFlags(o.permission),
|
|
2871
|
+
...model,
|
|
2872
|
+
"--",
|
|
2873
|
+
o.prompt
|
|
2874
|
+
];
|
|
2875
|
+
}
|
|
2876
|
+
function codexModels(env = process.env, home = os5.homedir()) {
|
|
2877
|
+
try {
|
|
2878
|
+
const file = path11.join(env.CODEX_HOME || path11.join(home, ".codex"), "models_cache.json");
|
|
2879
|
+
const j = JSON.parse(readFileSync6(file, "utf8"));
|
|
2880
|
+
if (!Array.isArray(j.models)) return [];
|
|
2881
|
+
const out = [];
|
|
2882
|
+
for (const m of j.models)
|
|
2883
|
+
if (m && m.visibility === "list" && typeof m.slug === "string" && MODEL_RE.test(m.slug) && m.slug.length <= 100) {
|
|
2884
|
+
if (!out.includes(m.slug)) out.push(m.slug);
|
|
2885
|
+
}
|
|
2886
|
+
return out.slice(0, 50);
|
|
2887
|
+
} catch {
|
|
2888
|
+
return [];
|
|
2889
|
+
}
|
|
2378
2890
|
}
|
|
2379
2891
|
var codexEnv = (env = process.env) => jobEnv(env);
|
|
2380
2892
|
var LOGIN_HINT = "Codex isn't signed in on this machine: run `codex login --device-auth` there.";
|
|
@@ -2401,22 +2913,22 @@ function codexLine(line) {
|
|
|
2401
2913
|
return null;
|
|
2402
2914
|
}
|
|
2403
2915
|
if (!j || typeof j !== "object") return null;
|
|
2404
|
-
const
|
|
2916
|
+
const str2 = (v) => typeof v === "string" ? v : "";
|
|
2405
2917
|
switch (j.type) {
|
|
2406
2918
|
case "thread.started":
|
|
2407
|
-
return
|
|
2919
|
+
return str2(j.thread_id) ? { session: str2(j.thread_id) } : null;
|
|
2408
2920
|
case "item.completed": {
|
|
2409
2921
|
const it = j.item;
|
|
2410
|
-
if (it?.type === "agent_message" &&
|
|
2411
|
-
if (it?.type === "error" &&
|
|
2922
|
+
if (it?.type === "agent_message" && str2(it.text)) return { message: str2(it.text) };
|
|
2923
|
+
if (it?.type === "error" && str2(it.message)) return { warning: str2(it.message) };
|
|
2412
2924
|
return null;
|
|
2413
2925
|
}
|
|
2414
2926
|
case "turn.completed":
|
|
2415
2927
|
return { done: true };
|
|
2416
2928
|
case "turn.failed":
|
|
2417
|
-
return { done: true, failed:
|
|
2929
|
+
return { done: true, failed: str2(j.error?.message) || "Codex reported an error" };
|
|
2418
2930
|
case "error":
|
|
2419
|
-
return
|
|
2931
|
+
return str2(j.message) ? { note: str2(j.message) } : null;
|
|
2420
2932
|
default:
|
|
2421
2933
|
return null;
|
|
2422
2934
|
}
|
|
@@ -2469,7 +2981,7 @@ var reading2 = () => new CodexReading();
|
|
|
2469
2981
|
// src/adapters/devin.ts
|
|
2470
2982
|
import { createHash as createHash2 } from "node:crypto";
|
|
2471
2983
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
2472
|
-
import
|
|
2984
|
+
import path12 from "node:path";
|
|
2473
2985
|
|
|
2474
2986
|
// src/readers/sqlite.ts
|
|
2475
2987
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
@@ -2653,11 +3165,11 @@ function foreignEvent(file) {
|
|
|
2653
3165
|
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
2654
3166
|
}
|
|
2655
3167
|
var homeOf = (env) => env.HOME || homeDir();
|
|
2656
|
-
var appData = (env) => env.APPDATA ||
|
|
2657
|
-
var devinConfigDir = (env = process.env) => process.platform === "win32" ?
|
|
2658
|
-
var configFile3 = (env) =>
|
|
2659
|
-
var devinDataDir = (env = process.env) => process.platform === "win32" ?
|
|
2660
|
-
var sessionsDb = (env) =>
|
|
3168
|
+
var appData = (env) => env.APPDATA || path12.join(homeOf(env), "AppData", "Roaming");
|
|
3169
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path12.join(appData(env), "devin") : path12.join(env.XDG_CONFIG_HOME || path12.join(homeOf(env), ".config"), "devin");
|
|
3170
|
+
var configFile3 = (env) => path12.join(devinConfigDir(env), "config.json");
|
|
3171
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path12.join(appData(env), "devin", "cli") : path12.join(env.XDG_DATA_HOME || path12.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
3172
|
+
var sessionsDb = (env) => path12.join(devinDataDir(env), "sessions.db");
|
|
2661
3173
|
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2662
3174
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2663
3175
|
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
@@ -2829,7 +3341,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
|
2829
3341
|
function sessionFacts(row) {
|
|
2830
3342
|
const out = {};
|
|
2831
3343
|
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2832
|
-
if (typeof row.working_directory === "string" &&
|
|
3344
|
+
if (typeof row.working_directory === "string" && path12.isAbsolute(row.working_directory))
|
|
2833
3345
|
out.cwd = row.working_directory;
|
|
2834
3346
|
const started = Number(row.created_at);
|
|
2835
3347
|
const last = Number(row.last_activity_at);
|
|
@@ -2874,8 +3386,8 @@ function readDevinChain(db, session, fromLine) {
|
|
|
2874
3386
|
}
|
|
2875
3387
|
function devinVersion(env = process.env) {
|
|
2876
3388
|
try {
|
|
2877
|
-
const target = readlinkSync(
|
|
2878
|
-
const name =
|
|
3389
|
+
const target = readlinkSync(path12.join(devinDataDir(env), "_versions", "current"));
|
|
3390
|
+
const name = path12.basename(target);
|
|
2879
3391
|
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2880
3392
|
} catch {
|
|
2881
3393
|
return void 0;
|
|
@@ -2889,8 +3401,8 @@ function devinAccount(env = process.env) {
|
|
|
2889
3401
|
|
|
2890
3402
|
// src/adapters/gemini-cli.ts
|
|
2891
3403
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
2892
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2893
|
-
import
|
|
3404
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
3405
|
+
import path13 from "node:path";
|
|
2894
3406
|
var NAME5 = "gemini-cli";
|
|
2895
3407
|
var GEMINI_EVENTS = [
|
|
2896
3408
|
"SessionStart",
|
|
@@ -2903,14 +3415,14 @@ var GEMINI_EVENTS = [
|
|
|
2903
3415
|
"SessionEnd"
|
|
2904
3416
|
];
|
|
2905
3417
|
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2906
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2907
|
-
var settings = (env) =>
|
|
3418
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path13.join(homeDir(), ".gemini");
|
|
3419
|
+
var settings = (env) => path13.join(geminiDir(env), "settings.json");
|
|
2908
3420
|
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2909
3421
|
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2910
3422
|
function geminiHooksActive(settingsFile) {
|
|
2911
3423
|
let tools;
|
|
2912
3424
|
try {
|
|
2913
|
-
tools = JSON.parse(
|
|
3425
|
+
tools = JSON.parse(readFileSync7(settingsFile, "utf8")).tools;
|
|
2914
3426
|
} catch {
|
|
2915
3427
|
}
|
|
2916
3428
|
if (tools?.enableHooks === true) return { active: true };
|
|
@@ -2940,7 +3452,7 @@ var geminiCli = {
|
|
|
2940
3452
|
name: NAME5,
|
|
2941
3453
|
events: GEMINI_EVENTS,
|
|
2942
3454
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2943
|
-
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(
|
|
3455
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path13.join(geminiDir(env), "tmp")),
|
|
2944
3456
|
configFiles: (env = process.env) => [settings(env)],
|
|
2945
3457
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2946
3458
|
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
@@ -3038,7 +3550,7 @@ var geminiCli = {
|
|
|
3038
3550
|
readTranscript(file, fromLine) {
|
|
3039
3551
|
let text = "";
|
|
3040
3552
|
try {
|
|
3041
|
-
text =
|
|
3553
|
+
text = readFileSync7(file, "utf8");
|
|
3042
3554
|
} catch {
|
|
3043
3555
|
return { turns: [], line: fromLine };
|
|
3044
3556
|
}
|
|
@@ -3076,21 +3588,23 @@ function prune5(o) {
|
|
|
3076
3588
|
// src/adapters/index.ts
|
|
3077
3589
|
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
3078
3590
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
3591
|
+
var HELLO_HARNESSES_MAX = 20;
|
|
3079
3592
|
function drivableHarnesses(env = process.env) {
|
|
3080
3593
|
return [
|
|
3081
3594
|
...findClaude(env) ? ["claude-code"] : [],
|
|
3082
3595
|
...findCodex(env) ? ["codex"] : [],
|
|
3083
|
-
...findAgy(env) ? ["antigravity"] : []
|
|
3084
|
-
|
|
3596
|
+
...findAgy(env) ? ["antigravity"] : [],
|
|
3597
|
+
...acpHarnesses(env)
|
|
3598
|
+
].slice(0, HELLO_HARNESSES_MAX);
|
|
3085
3599
|
}
|
|
3086
3600
|
|
|
3087
3601
|
// src/config.ts
|
|
3088
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as
|
|
3089
|
-
import
|
|
3602
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3603
|
+
import path14 from "node:path";
|
|
3090
3604
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
3091
3605
|
function readConfig(file = configFile()) {
|
|
3092
3606
|
try {
|
|
3093
|
-
const j = JSON.parse(
|
|
3607
|
+
const j = JSON.parse(readFileSync8(file, "utf8"));
|
|
3094
3608
|
return {
|
|
3095
3609
|
...DEFAULT_CONFIG,
|
|
3096
3610
|
...j,
|
|
@@ -3102,7 +3616,7 @@ function readConfig(file = configFile()) {
|
|
|
3102
3616
|
}
|
|
3103
3617
|
}
|
|
3104
3618
|
function writeConfig(c, file = configFile()) {
|
|
3105
|
-
mkdirSync4(
|
|
3619
|
+
mkdirSync4(path14.dirname(file), { recursive: true, mode: 448 });
|
|
3106
3620
|
writeFileSync4(file, `${JSON.stringify(c, null, 2)}
|
|
3107
3621
|
`, { mode: 384 });
|
|
3108
3622
|
if (process.platform !== "win32" && existsSync8(file)) {
|
|
@@ -3167,6 +3681,22 @@ function makeEvent(a, ctx) {
|
|
|
3167
3681
|
};
|
|
3168
3682
|
}
|
|
3169
3683
|
|
|
3684
|
+
// src/launched-by.ts
|
|
3685
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
3686
|
+
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
3687
|
+
function launchedBy(env) {
|
|
3688
|
+
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
3689
|
+
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
3690
|
+
}
|
|
3691
|
+
function readHook(adapter, input) {
|
|
3692
|
+
const r = adapter.fromHook(input);
|
|
3693
|
+
if (r && !r.session.parent_id) {
|
|
3694
|
+
const launcher = launchedBy(input.env);
|
|
3695
|
+
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
3696
|
+
}
|
|
3697
|
+
return r;
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3170
3700
|
// src/outbox.ts
|
|
3171
3701
|
import {
|
|
3172
3702
|
appendFileSync,
|
|
@@ -3175,14 +3705,14 @@ import {
|
|
|
3175
3705
|
mkdirSync as mkdirSync5,
|
|
3176
3706
|
openSync as openSync3,
|
|
3177
3707
|
readdirSync as readdirSync6,
|
|
3178
|
-
readFileSync as
|
|
3708
|
+
readFileSync as readFileSync9,
|
|
3179
3709
|
readSync as readSync3,
|
|
3180
3710
|
renameSync,
|
|
3181
3711
|
statSync as statSync6,
|
|
3182
3712
|
unlinkSync as unlinkSync3,
|
|
3183
3713
|
writeFileSync as writeFileSync5
|
|
3184
3714
|
} from "node:fs";
|
|
3185
|
-
import
|
|
3715
|
+
import path15 from "node:path";
|
|
3186
3716
|
var READ_CHUNK = 1024 * 1024;
|
|
3187
3717
|
var READ_MAX = 8 * 1024 * 1024;
|
|
3188
3718
|
var Outbox = class {
|
|
@@ -3191,13 +3721,13 @@ var Outbox = class {
|
|
|
3191
3721
|
}
|
|
3192
3722
|
root;
|
|
3193
3723
|
dir(harness) {
|
|
3194
|
-
return
|
|
3724
|
+
return path15.join(this.root, "outbox", safeId(harness));
|
|
3195
3725
|
}
|
|
3196
3726
|
file(ref) {
|
|
3197
|
-
return
|
|
3727
|
+
return path15.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
3198
3728
|
}
|
|
3199
3729
|
seqFile(ref) {
|
|
3200
|
-
return
|
|
3730
|
+
return path15.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
3201
3731
|
}
|
|
3202
3732
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
3203
3733
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
@@ -3205,19 +3735,19 @@ var Outbox = class {
|
|
|
3205
3735
|
const f = this.seqFile(ref);
|
|
3206
3736
|
let n = -1;
|
|
3207
3737
|
try {
|
|
3208
|
-
const t =
|
|
3738
|
+
const t = readFileSync9(f, "utf8").trim();
|
|
3209
3739
|
if (/^\d+$/.test(t)) n = Number(t);
|
|
3210
3740
|
} catch {
|
|
3211
3741
|
}
|
|
3212
3742
|
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
3213
|
-
mkdirSync5(
|
|
3743
|
+
mkdirSync5(path15.dirname(f), { recursive: true, mode: 448 });
|
|
3214
3744
|
writeFileSync5(f, String(n + 1), { mode: 384 });
|
|
3215
3745
|
return n;
|
|
3216
3746
|
}
|
|
3217
3747
|
lastSeqInFile(ref) {
|
|
3218
3748
|
const f = this.file(ref);
|
|
3219
3749
|
if (!existsSync9(f)) return -1;
|
|
3220
|
-
const lines =
|
|
3750
|
+
const lines = readFileSync9(f, "utf8").trim().split("\n");
|
|
3221
3751
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3222
3752
|
try {
|
|
3223
3753
|
const j = JSON.parse(lines[i]);
|
|
@@ -3236,10 +3766,10 @@ var Outbox = class {
|
|
|
3236
3766
|
/** Every session file, newest first. */
|
|
3237
3767
|
sessions() {
|
|
3238
3768
|
const out = [];
|
|
3239
|
-
const base =
|
|
3769
|
+
const base = path15.join(this.root, "outbox");
|
|
3240
3770
|
if (!existsSync9(base)) return [];
|
|
3241
3771
|
for (const h of readdirSync6(base)) {
|
|
3242
|
-
const d =
|
|
3772
|
+
const d = path15.join(base, h);
|
|
3243
3773
|
let names = [];
|
|
3244
3774
|
try {
|
|
3245
3775
|
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
@@ -3248,7 +3778,7 @@ var Outbox = class {
|
|
|
3248
3778
|
}
|
|
3249
3779
|
for (const n of names) {
|
|
3250
3780
|
try {
|
|
3251
|
-
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(
|
|
3781
|
+
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path15.join(d, n)).mtimeMs });
|
|
3252
3782
|
} catch {
|
|
3253
3783
|
}
|
|
3254
3784
|
}
|
|
@@ -3336,18 +3866,18 @@ var Cursors = class {
|
|
|
3336
3866
|
root;
|
|
3337
3867
|
sink;
|
|
3338
3868
|
file() {
|
|
3339
|
-
return
|
|
3869
|
+
return path15.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
3340
3870
|
}
|
|
3341
3871
|
read() {
|
|
3342
3872
|
try {
|
|
3343
|
-
return JSON.parse(
|
|
3873
|
+
return JSON.parse(readFileSync9(this.file(), "utf8"));
|
|
3344
3874
|
} catch {
|
|
3345
3875
|
return {};
|
|
3346
3876
|
}
|
|
3347
3877
|
}
|
|
3348
3878
|
write(c) {
|
|
3349
3879
|
const f = this.file();
|
|
3350
|
-
mkdirSync5(
|
|
3880
|
+
mkdirSync5(path15.dirname(f), { recursive: true, mode: 448 });
|
|
3351
3881
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
3352
3882
|
writeFileSync5(tmp, JSON.stringify(c), { mode: 384 });
|
|
3353
3883
|
renameSync(tmp, f);
|
|
@@ -3360,11 +3890,11 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
3360
3890
|
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
3361
3891
|
}
|
|
3362
3892
|
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
3363
|
-
const dir =
|
|
3893
|
+
const dir = path15.join(root, "locks");
|
|
3364
3894
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
3365
|
-
const f =
|
|
3895
|
+
const f = path15.join(dir, `${name}.lock`);
|
|
3366
3896
|
try {
|
|
3367
|
-
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(
|
|
3897
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
|
|
3368
3898
|
else return null;
|
|
3369
3899
|
} catch {
|
|
3370
3900
|
}
|
|
@@ -3612,8 +4142,8 @@ var DATA_ALLOW = {
|
|
|
3612
4142
|
"session.ended": { 0: ["reason"] },
|
|
3613
4143
|
"session.backfill": { 0: ["started_at", "last_at", "turns"] },
|
|
3614
4144
|
"session.forgotten": { 0: [] },
|
|
3615
|
-
"turn.started": { 0: ["turn_id"], 1: ["prompt_chars"] },
|
|
3616
|
-
"turn.ended": { 0: ["turn_id", "reason", "ms"], 1: ["error"] },
|
|
4145
|
+
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
4146
|
+
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
3617
4147
|
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
3618
4148
|
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
3619
4149
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
@@ -3637,7 +4167,9 @@ var SESSION_ALLOW = [
|
|
|
3637
4167
|
"title_source",
|
|
3638
4168
|
"url",
|
|
3639
4169
|
"account_id",
|
|
3640
|
-
"
|
|
4170
|
+
"attended",
|
|
4171
|
+
"parent_id",
|
|
4172
|
+
"launched_by"
|
|
3641
4173
|
];
|
|
3642
4174
|
var MAX_SIDE = 2e4;
|
|
3643
4175
|
var MAX_IO = 64 * 1024;
|
|
@@ -3701,7 +4233,7 @@ function shortId(harness, id) {
|
|
|
3701
4233
|
|
|
3702
4234
|
// src/sinks/file.ts
|
|
3703
4235
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3704
|
-
import
|
|
4236
|
+
import path16 from "node:path";
|
|
3705
4237
|
var FileSink = class {
|
|
3706
4238
|
constructor(name, file, tier, pii) {
|
|
3707
4239
|
this.name = name;
|
|
@@ -3715,7 +4247,7 @@ var FileSink = class {
|
|
|
3715
4247
|
pii;
|
|
3716
4248
|
async send(events) {
|
|
3717
4249
|
try {
|
|
3718
|
-
mkdirSync6(
|
|
4250
|
+
mkdirSync6(path16.dirname(this.file), { recursive: true });
|
|
3719
4251
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3720
4252
|
`);
|
|
3721
4253
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3757,7 +4289,9 @@ export {
|
|
|
3757
4289
|
DEFAULT_CONFIG,
|
|
3758
4290
|
DEVIN_EVENTS,
|
|
3759
4291
|
FileSink,
|
|
4292
|
+
HELLO_HARNESSES_MAX,
|
|
3760
4293
|
JOB_TIMEOUT_MS,
|
|
4294
|
+
LAUNCHED_BY_ENV,
|
|
3761
4295
|
Outbox,
|
|
3762
4296
|
PROTOCOL_VERSION,
|
|
3763
4297
|
REDACTED,
|
|
@@ -3765,6 +4299,7 @@ export {
|
|
|
3765
4299
|
RULESET_SECRETS,
|
|
3766
4300
|
StdoutSink,
|
|
3767
4301
|
TYPE_TIER,
|
|
4302
|
+
acp_control_exports as acpControl,
|
|
3768
4303
|
adapterByName,
|
|
3769
4304
|
antigravity,
|
|
3770
4305
|
antigravityAnswer,
|
|
@@ -3792,6 +4327,7 @@ export {
|
|
|
3792
4327
|
isSecretName,
|
|
3793
4328
|
isUlid,
|
|
3794
4329
|
jobEnv,
|
|
4330
|
+
launchedBy,
|
|
3795
4331
|
machineName,
|
|
3796
4332
|
makeEvent,
|
|
3797
4333
|
notClaudeCode,
|
|
@@ -3803,6 +4339,7 @@ export {
|
|
|
3803
4339
|
readConfig,
|
|
3804
4340
|
readDevinChain,
|
|
3805
4341
|
readGemini,
|
|
4342
|
+
readHook,
|
|
3806
4343
|
redactDeep,
|
|
3807
4344
|
redactSecrets,
|
|
3808
4345
|
reducePii,
|
|
@@ -3816,6 +4353,7 @@ export {
|
|
|
3816
4353
|
takeNamedLock,
|
|
3817
4354
|
tilde,
|
|
3818
4355
|
ulid,
|
|
4356
|
+
untilde,
|
|
3819
4357
|
writeConfig
|
|
3820
4358
|
};
|
|
3821
4359
|
//# sourceMappingURL=index.js.map
|