luckerr 0.41.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/README.md +267 -0
- package/README.zh-CN.md +237 -0
- package/dashboard/app.css +3022 -0
- package/dashboard/dist/app.js +30137 -0
- package/dashboard/dist/app.js.map +1 -0
- package/dashboard/dist/vendor-hljs.css +10 -0
- package/dashboard/dist/vendor-uplot.css +1 -0
- package/dashboard/index.html +19 -0
- package/data/deepseek-tokenizer.json.gz +0 -0
- package/dist/cli/acp-EOOAI4F5.js +712 -0
- package/dist/cli/acp-EOOAI4F5.js.map +1 -0
- package/dist/cli/chat-7J6GJXL2.js +51 -0
- package/dist/cli/chat-7J6GJXL2.js.map +1 -0
- package/dist/cli/chunk-2425HK6U.js +54 -0
- package/dist/cli/chunk-2425HK6U.js.map +1 -0
- package/dist/cli/chunk-25T6CVUP.js +172 -0
- package/dist/cli/chunk-25T6CVUP.js.map +1 -0
- package/dist/cli/chunk-2UQP6H6T.js +31 -0
- package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
- package/dist/cli/chunk-56OAJILV.js +47 -0
- package/dist/cli/chunk-56OAJILV.js.map +1 -0
- package/dist/cli/chunk-5FTI4KXH.js +150 -0
- package/dist/cli/chunk-5FTI4KXH.js.map +1 -0
- package/dist/cli/chunk-5TWQD73O.js +2846 -0
- package/dist/cli/chunk-5TWQD73O.js.map +1 -0
- package/dist/cli/chunk-653BOCMK.js +40 -0
- package/dist/cli/chunk-653BOCMK.js.map +1 -0
- package/dist/cli/chunk-6ALJTWWQ.js +2663 -0
- package/dist/cli/chunk-6ALJTWWQ.js.map +1 -0
- package/dist/cli/chunk-6DRKA2IL.js +341 -0
- package/dist/cli/chunk-6DRKA2IL.js.map +1 -0
- package/dist/cli/chunk-6LV63NJV.js +634 -0
- package/dist/cli/chunk-6LV63NJV.js.map +1 -0
- package/dist/cli/chunk-74EX7SUH.js +25293 -0
- package/dist/cli/chunk-74EX7SUH.js.map +1 -0
- package/dist/cli/chunk-74U5RKTX.js +60611 -0
- package/dist/cli/chunk-74U5RKTX.js.map +1 -0
- package/dist/cli/chunk-ANJSUESV.js +143 -0
- package/dist/cli/chunk-ANJSUESV.js.map +1 -0
- package/dist/cli/chunk-DB2Z3DKZ.js +54 -0
- package/dist/cli/chunk-DB2Z3DKZ.js.map +1 -0
- package/dist/cli/chunk-DDIH3ZAA.js +400 -0
- package/dist/cli/chunk-DDIH3ZAA.js.map +1 -0
- package/dist/cli/chunk-ELN3Z3B2.js +621 -0
- package/dist/cli/chunk-ELN3Z3B2.js.map +1 -0
- package/dist/cli/chunk-F6BSQJGV.js +200 -0
- package/dist/cli/chunk-F6BSQJGV.js.map +1 -0
- package/dist/cli/chunk-FET2UAG5.js +246 -0
- package/dist/cli/chunk-FET2UAG5.js.map +1 -0
- package/dist/cli/chunk-FFJ342IJ.js +190 -0
- package/dist/cli/chunk-FFJ342IJ.js.map +1 -0
- package/dist/cli/chunk-GB3247B6.js +130 -0
- package/dist/cli/chunk-GB3247B6.js.map +1 -0
- package/dist/cli/chunk-HC2J4U3G.js +373 -0
- package/dist/cli/chunk-HC2J4U3G.js.map +1 -0
- package/dist/cli/chunk-HRUZAIHQ.js +42 -0
- package/dist/cli/chunk-HRUZAIHQ.js.map +1 -0
- package/dist/cli/chunk-J3ZJFUDL.js +308 -0
- package/dist/cli/chunk-J3ZJFUDL.js.map +1 -0
- package/dist/cli/chunk-J5XJHLWM.js +55 -0
- package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
- package/dist/cli/chunk-JFGLMRZ6.js +160 -0
- package/dist/cli/chunk-JFGLMRZ6.js.map +1 -0
- package/dist/cli/chunk-JMBMLOBP.js +26 -0
- package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
- package/dist/cli/chunk-JMWHXZEL.js +551 -0
- package/dist/cli/chunk-JMWHXZEL.js.map +1 -0
- package/dist/cli/chunk-KEQGPJBO.js +209 -0
- package/dist/cli/chunk-KEQGPJBO.js.map +1 -0
- package/dist/cli/chunk-M4K6U37F.js +232 -0
- package/dist/cli/chunk-M4K6U37F.js.map +1 -0
- package/dist/cli/chunk-MIJI2WMN.js +95 -0
- package/dist/cli/chunk-MIJI2WMN.js.map +1 -0
- package/dist/cli/chunk-MPAO3JNR.js +128 -0
- package/dist/cli/chunk-MPAO3JNR.js.map +1 -0
- package/dist/cli/chunk-PZOFBEDC.js +873 -0
- package/dist/cli/chunk-PZOFBEDC.js.map +1 -0
- package/dist/cli/chunk-RAILYQLN.js +46 -0
- package/dist/cli/chunk-RAILYQLN.js.map +1 -0
- package/dist/cli/chunk-RR35VQVT.js +90 -0
- package/dist/cli/chunk-RR35VQVT.js.map +1 -0
- package/dist/cli/chunk-RRA7VPW4.js +417 -0
- package/dist/cli/chunk-RRA7VPW4.js.map +1 -0
- package/dist/cli/chunk-RU36QVN3.js +452 -0
- package/dist/cli/chunk-RU36QVN3.js.map +1 -0
- package/dist/cli/chunk-RUBIINXR.js +1819 -0
- package/dist/cli/chunk-RUBIINXR.js.map +1 -0
- package/dist/cli/chunk-S4XVGLRW.js +499 -0
- package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
- package/dist/cli/chunk-TUK7OWJA.js +51 -0
- package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
- package/dist/cli/chunk-VALDDV76.js +580 -0
- package/dist/cli/chunk-VALDDV76.js.map +1 -0
- package/dist/cli/chunk-WQOGPYGN.js +11390 -0
- package/dist/cli/chunk-WQOGPYGN.js.map +1 -0
- package/dist/cli/chunk-WREKDFXT.js +34320 -0
- package/dist/cli/chunk-WREKDFXT.js.map +1 -0
- package/dist/cli/chunk-Y7XQU2EL.js +270 -0
- package/dist/cli/chunk-Y7XQU2EL.js.map +1 -0
- package/dist/cli/chunk-YBVCZJU4.js +54 -0
- package/dist/cli/chunk-YBVCZJU4.js.map +1 -0
- package/dist/cli/chunk-YLIHDXUQ.js +749 -0
- package/dist/cli/chunk-YLIHDXUQ.js.map +1 -0
- package/dist/cli/chunk-YV5XXFD7.js +767 -0
- package/dist/cli/chunk-YV5XXFD7.js.map +1 -0
- package/dist/cli/chunk-ZRCNIYRQ.js +101 -0
- package/dist/cli/chunk-ZRCNIYRQ.js.map +1 -0
- package/dist/cli/code-CRKVCMFZ.js +155 -0
- package/dist/cli/code-CRKVCMFZ.js.map +1 -0
- package/dist/cli/commands-QLMD3T7B.js +356 -0
- package/dist/cli/commands-QLMD3T7B.js.map +1 -0
- package/dist/cli/commit-53PP32NC.js +293 -0
- package/dist/cli/commit-53PP32NC.js.map +1 -0
- package/dist/cli/desktop-R6W5CLJ5.js +1046 -0
- package/dist/cli/desktop-R6W5CLJ5.js.map +1 -0
- package/dist/cli/devtools-YECO25QO.js +3719 -0
- package/dist/cli/devtools-YECO25QO.js.map +1 -0
- package/dist/cli/diff-LYNRCJZE.js +166 -0
- package/dist/cli/diff-LYNRCJZE.js.map +1 -0
- package/dist/cli/doctor-5IBP4R5J.js +28 -0
- package/dist/cli/doctor-5IBP4R5J.js.map +1 -0
- package/dist/cli/events-QN6KLN2V.js +340 -0
- package/dist/cli/events-QN6KLN2V.js.map +1 -0
- package/dist/cli/index.js +3500 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mcp-FGKEH7RG.js +277 -0
- package/dist/cli/mcp-FGKEH7RG.js.map +1 -0
- package/dist/cli/mcp-browse-YCND4NWT.js +178 -0
- package/dist/cli/mcp-browse-YCND4NWT.js.map +1 -0
- package/dist/cli/mcp-inspect-V34J3VX5.js +143 -0
- package/dist/cli/mcp-inspect-V34J3VX5.js.map +1 -0
- package/dist/cli/package.json +3 -0
- package/dist/cli/prompt-I775PNKT.js +16 -0
- package/dist/cli/prompt-I775PNKT.js.map +1 -0
- package/dist/cli/prune-sessions-KGIIYD3P.js +44 -0
- package/dist/cli/prune-sessions-KGIIYD3P.js.map +1 -0
- package/dist/cli/replay-RDXLUAOE.js +292 -0
- package/dist/cli/replay-RDXLUAOE.js.map +1 -0
- package/dist/cli/run-RCAC2RYW.js +223 -0
- package/dist/cli/run-RCAC2RYW.js.map +1 -0
- package/dist/cli/server-FFU6TLYJ.js +3658 -0
- package/dist/cli/server-FFU6TLYJ.js.map +1 -0
- package/dist/cli/sessions-QT26MQAE.js +107 -0
- package/dist/cli/sessions-QT26MQAE.js.map +1 -0
- package/dist/cli/setup-VV4WKXHV.js +767 -0
- package/dist/cli/setup-VV4WKXHV.js.map +1 -0
- package/dist/cli/stats-JVZPQWAN.js +15 -0
- package/dist/cli/stats-JVZPQWAN.js.map +1 -0
- package/dist/cli/update-KYI3OVJP.js +15 -0
- package/dist/cli/update-KYI3OVJP.js.map +1 -0
- package/dist/cli/version-ANYORXTI.js +34 -0
- package/dist/cli/version-ANYORXTI.js.map +1 -0
- package/dist/index.d.ts +2557 -0
- package/dist/index.js +15000 -0
- package/dist/index.js.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
|
|
3
|
+
import {
|
|
4
|
+
buildCodeToolset
|
|
5
|
+
} from "./chunk-HC2J4U3G.js";
|
|
6
|
+
import {
|
|
7
|
+
Eventizer,
|
|
8
|
+
autoResolveVerdict
|
|
9
|
+
} from "./chunk-RRA7VPW4.js";
|
|
10
|
+
import {
|
|
11
|
+
formatMcpLifecycleEvent,
|
|
12
|
+
formatMcpSlowToast
|
|
13
|
+
} from "./chunk-RAILYQLN.js";
|
|
14
|
+
import {
|
|
15
|
+
buildTransportFromSpec,
|
|
16
|
+
preflightStdioSpec
|
|
17
|
+
} from "./chunk-HRUZAIHQ.js";
|
|
18
|
+
import {
|
|
19
|
+
CacheFirstLoop,
|
|
20
|
+
ImmutablePrefix,
|
|
21
|
+
bridgeMcpTools
|
|
22
|
+
} from "./chunk-WQOGPYGN.js";
|
|
23
|
+
import "./chunk-J3ZJFUDL.js";
|
|
24
|
+
import {
|
|
25
|
+
openTranscriptFile,
|
|
26
|
+
recordFromLoopEvent,
|
|
27
|
+
writeRecord
|
|
28
|
+
} from "./chunk-FFJ342IJ.js";
|
|
29
|
+
import {
|
|
30
|
+
McpClient,
|
|
31
|
+
parseMcpSpec
|
|
32
|
+
} from "./chunk-YV5XXFD7.js";
|
|
33
|
+
import {
|
|
34
|
+
codeSystemPrompt
|
|
35
|
+
} from "./chunk-6LV63NJV.js";
|
|
36
|
+
import {
|
|
37
|
+
canonicalPresetName,
|
|
38
|
+
resolvePreset
|
|
39
|
+
} from "./chunk-2425HK6U.js";
|
|
40
|
+
import "./chunk-F6BSQJGV.js";
|
|
41
|
+
import {
|
|
42
|
+
loadDotenv
|
|
43
|
+
} from "./chunk-2UQP6H6T.js";
|
|
44
|
+
import "./chunk-MIJI2WMN.js";
|
|
45
|
+
import {
|
|
46
|
+
pauseGate
|
|
47
|
+
} from "./chunk-RUBIINXR.js";
|
|
48
|
+
import "./chunk-VALDDV76.js";
|
|
49
|
+
import "./chunk-56OAJILV.js";
|
|
50
|
+
import "./chunk-PZOFBEDC.js";
|
|
51
|
+
import "./chunk-KEQGPJBO.js";
|
|
52
|
+
import "./chunk-S4XVGLRW.js";
|
|
53
|
+
import {
|
|
54
|
+
DeepSeekClient
|
|
55
|
+
} from "./chunk-DDIH3ZAA.js";
|
|
56
|
+
import "./chunk-25T6CVUP.js";
|
|
57
|
+
import {
|
|
58
|
+
timestampSuffix
|
|
59
|
+
} from "./chunk-Y7XQU2EL.js";
|
|
60
|
+
import "./chunk-5TWQD73O.js";
|
|
61
|
+
import {
|
|
62
|
+
loadApiKey,
|
|
63
|
+
loadBaseUrl,
|
|
64
|
+
loadEditMode,
|
|
65
|
+
loadPreset,
|
|
66
|
+
loadReasoningEffort,
|
|
67
|
+
mcpEnvFor,
|
|
68
|
+
readConfig
|
|
69
|
+
} from "./chunk-6ALJTWWQ.js";
|
|
70
|
+
import "./chunk-M4K6U37F.js";
|
|
71
|
+
import "./chunk-ANJSUESV.js";
|
|
72
|
+
import "./chunk-JMWHXZEL.js";
|
|
73
|
+
import {
|
|
74
|
+
VERSION
|
|
75
|
+
} from "./chunk-MPAO3JNR.js";
|
|
76
|
+
import "./chunk-TUK7OWJA.js";
|
|
77
|
+
|
|
78
|
+
// src/cli/commands/acp.ts
|
|
79
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
80
|
+
import { existsSync, statSync } from "fs";
|
|
81
|
+
import { resolve } from "path";
|
|
82
|
+
|
|
83
|
+
// src/acp/dispatch.ts
|
|
84
|
+
var READ_TOOLS = /* @__PURE__ */ new Set([
|
|
85
|
+
"read_file",
|
|
86
|
+
"list_directory",
|
|
87
|
+
"directory_tree",
|
|
88
|
+
"get_file_info",
|
|
89
|
+
"glob"
|
|
90
|
+
]);
|
|
91
|
+
var EDIT_TOOLS = /* @__PURE__ */ new Set([
|
|
92
|
+
"write_file",
|
|
93
|
+
"edit_file",
|
|
94
|
+
"multi_edit",
|
|
95
|
+
"create_directory",
|
|
96
|
+
"delete_file",
|
|
97
|
+
"delete_directory",
|
|
98
|
+
"move_file",
|
|
99
|
+
"copy_file"
|
|
100
|
+
]);
|
|
101
|
+
var SEARCH_TOOLS = /* @__PURE__ */ new Set(["search_content", "search_files"]);
|
|
102
|
+
var EXECUTE_TOOLS = /* @__PURE__ */ new Set(["run_command", "run_background"]);
|
|
103
|
+
function toolKindFor(name) {
|
|
104
|
+
if (READ_TOOLS.has(name)) return "read";
|
|
105
|
+
if (EDIT_TOOLS.has(name)) return "edit";
|
|
106
|
+
if (SEARCH_TOOLS.has(name)) return "search";
|
|
107
|
+
if (EXECUTE_TOOLS.has(name)) return "execute";
|
|
108
|
+
return "other";
|
|
109
|
+
}
|
|
110
|
+
function tryParseJson(raw) {
|
|
111
|
+
if (!raw) return void 0;
|
|
112
|
+
try {
|
|
113
|
+
return JSON.parse(raw);
|
|
114
|
+
} catch {
|
|
115
|
+
return raw;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function dispatchKernelEvent(server, sessionId, ev) {
|
|
119
|
+
switch (ev.type) {
|
|
120
|
+
case "model.delta": {
|
|
121
|
+
if (!ev.text) return;
|
|
122
|
+
const variant = ev.channel === "reasoning" ? "agent_thought_chunk" : "agent_message_chunk";
|
|
123
|
+
emit(server, {
|
|
124
|
+
sessionId,
|
|
125
|
+
update: { sessionUpdate: variant, content: { type: "text", text: ev.text } }
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
case "tool.preparing": {
|
|
130
|
+
emit(server, {
|
|
131
|
+
sessionId,
|
|
132
|
+
update: {
|
|
133
|
+
sessionUpdate: "tool_call",
|
|
134
|
+
toolCallId: ev.callId,
|
|
135
|
+
title: ev.name,
|
|
136
|
+
kind: toolKindFor(ev.name),
|
|
137
|
+
status: "pending"
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
case "tool.intent": {
|
|
143
|
+
emit(server, {
|
|
144
|
+
sessionId,
|
|
145
|
+
update: {
|
|
146
|
+
sessionUpdate: "tool_call_update",
|
|
147
|
+
toolCallId: ev.callId,
|
|
148
|
+
status: "in_progress"
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
const rawInput = tryParseJson(ev.args);
|
|
152
|
+
if (rawInput !== void 0) {
|
|
153
|
+
emit(server, {
|
|
154
|
+
sessionId,
|
|
155
|
+
update: {
|
|
156
|
+
sessionUpdate: "tool_call",
|
|
157
|
+
toolCallId: ev.callId,
|
|
158
|
+
title: ev.name,
|
|
159
|
+
kind: toolKindFor(ev.name),
|
|
160
|
+
status: "in_progress",
|
|
161
|
+
rawInput
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
case "tool.result": {
|
|
168
|
+
emit(server, {
|
|
169
|
+
sessionId,
|
|
170
|
+
update: {
|
|
171
|
+
sessionUpdate: "tool_call_update",
|
|
172
|
+
toolCallId: ev.callId,
|
|
173
|
+
status: ev.ok ? "completed" : "failed",
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: "content",
|
|
177
|
+
content: { type: "text", text: clip(ev.output) }
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
default:
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
var MAX_RESULT_CHARS = 8e3;
|
|
189
|
+
function clip(text) {
|
|
190
|
+
if (text.length <= MAX_RESULT_CHARS) return text;
|
|
191
|
+
return `${text.slice(0, MAX_RESULT_CHARS)}
|
|
192
|
+
\u2026(${text.length - MAX_RESULT_CHARS} more chars truncated)`;
|
|
193
|
+
}
|
|
194
|
+
function emit(server, params) {
|
|
195
|
+
server.sendNotification("session/update", params);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/acp/gates.ts
|
|
199
|
+
var ID_ALLOW_ONCE = "allow_once";
|
|
200
|
+
var ID_ALLOW_ALWAYS = "allow_always";
|
|
201
|
+
var ID_REJECT = "reject";
|
|
202
|
+
var ID_REFINE = "refine";
|
|
203
|
+
var ID_REVISE = "revise";
|
|
204
|
+
var ID_STOP = "stop";
|
|
205
|
+
var ID_CANCEL = "cancel";
|
|
206
|
+
var ID_ACCEPT = "accept";
|
|
207
|
+
function permissionOptionsFor(req) {
|
|
208
|
+
switch (req.kind) {
|
|
209
|
+
case "run_command":
|
|
210
|
+
case "run_background":
|
|
211
|
+
case "path_access":
|
|
212
|
+
return [
|
|
213
|
+
{ optionId: ID_ALLOW_ONCE, name: "Allow once", kind: "allow_once" },
|
|
214
|
+
{ optionId: ID_ALLOW_ALWAYS, name: "Allow always", kind: "allow_always" },
|
|
215
|
+
{ optionId: ID_REJECT, name: "Reject", kind: "reject_once" }
|
|
216
|
+
];
|
|
217
|
+
case "plan_proposed":
|
|
218
|
+
return [
|
|
219
|
+
{ optionId: ID_ALLOW_ONCE, name: "Approve plan", kind: "allow_once" },
|
|
220
|
+
{ optionId: ID_REFINE, name: "Refine", kind: "allow_once" },
|
|
221
|
+
{ optionId: ID_CANCEL, name: "Cancel", kind: "reject_once" }
|
|
222
|
+
];
|
|
223
|
+
case "plan_checkpoint":
|
|
224
|
+
return [
|
|
225
|
+
{ optionId: ID_ALLOW_ONCE, name: "Continue", kind: "allow_once" },
|
|
226
|
+
{ optionId: ID_REVISE, name: "Revise", kind: "allow_once" },
|
|
227
|
+
{ optionId: ID_STOP, name: "Stop", kind: "reject_once" }
|
|
228
|
+
];
|
|
229
|
+
case "plan_revision":
|
|
230
|
+
return [
|
|
231
|
+
{ optionId: ID_ACCEPT, name: "Accept revision", kind: "allow_once" },
|
|
232
|
+
{ optionId: ID_REJECT, name: "Keep original plan", kind: "reject_once" }
|
|
233
|
+
];
|
|
234
|
+
case "choice": {
|
|
235
|
+
const payload = req.payload;
|
|
236
|
+
const opts = (payload.options ?? []).map((o) => ({
|
|
237
|
+
optionId: o.id,
|
|
238
|
+
name: o.title ?? o.id,
|
|
239
|
+
kind: "allow_once"
|
|
240
|
+
}));
|
|
241
|
+
opts.push({ optionId: ID_CANCEL, name: "Cancel", kind: "reject_once" });
|
|
242
|
+
return opts;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function commandPrefix(command) {
|
|
247
|
+
const first = command.trim().split(/\s+/)[0] ?? command.trim();
|
|
248
|
+
return `${first} *`;
|
|
249
|
+
}
|
|
250
|
+
function pathPrefix(p) {
|
|
251
|
+
return p;
|
|
252
|
+
}
|
|
253
|
+
function verdictFor(req, result) {
|
|
254
|
+
const cancelled = result.outcome.outcome === "cancelled";
|
|
255
|
+
const optionId = result.outcome.outcome === "selected" ? result.outcome.optionId : null;
|
|
256
|
+
switch (req.kind) {
|
|
257
|
+
case "run_command":
|
|
258
|
+
case "run_background": {
|
|
259
|
+
if (cancelled || optionId === ID_REJECT) return { type: "deny" };
|
|
260
|
+
if (optionId === ID_ALLOW_ALWAYS) {
|
|
261
|
+
const payload = req.payload;
|
|
262
|
+
return { type: "always_allow", prefix: commandPrefix(payload.command ?? "") };
|
|
263
|
+
}
|
|
264
|
+
return { type: "run_once" };
|
|
265
|
+
}
|
|
266
|
+
case "path_access": {
|
|
267
|
+
if (cancelled || optionId === ID_REJECT) return { type: "deny" };
|
|
268
|
+
if (optionId === ID_ALLOW_ALWAYS) {
|
|
269
|
+
const payload = req.payload;
|
|
270
|
+
return { type: "always_allow", prefix: pathPrefix(payload.allowPrefix) };
|
|
271
|
+
}
|
|
272
|
+
return { type: "run_once" };
|
|
273
|
+
}
|
|
274
|
+
case "plan_proposed": {
|
|
275
|
+
if (cancelled || optionId === ID_CANCEL) return { type: "cancel" };
|
|
276
|
+
if (optionId === ID_REFINE) return { type: "refine" };
|
|
277
|
+
return { type: "approve" };
|
|
278
|
+
}
|
|
279
|
+
case "plan_checkpoint": {
|
|
280
|
+
if (cancelled || optionId === ID_STOP) return { type: "stop" };
|
|
281
|
+
if (optionId === ID_REVISE) return { type: "revise" };
|
|
282
|
+
return { type: "continue" };
|
|
283
|
+
}
|
|
284
|
+
case "plan_revision": {
|
|
285
|
+
if (cancelled) return { type: "cancelled" };
|
|
286
|
+
if (optionId === ID_ACCEPT) return { type: "accepted" };
|
|
287
|
+
return { type: "rejected" };
|
|
288
|
+
}
|
|
289
|
+
case "choice": {
|
|
290
|
+
if (cancelled || optionId === ID_CANCEL || !optionId) return { type: "cancel" };
|
|
291
|
+
return { type: "pick", optionId };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function permissionTitleFor(req) {
|
|
296
|
+
switch (req.kind) {
|
|
297
|
+
case "run_command":
|
|
298
|
+
case "run_background":
|
|
299
|
+
return `Run command \u2014 ${(req.payload.command ?? "").slice(0, 80)}`;
|
|
300
|
+
case "path_access":
|
|
301
|
+
return `Access path \u2014 ${req.payload.path}`;
|
|
302
|
+
case "plan_proposed":
|
|
303
|
+
return "Approve plan";
|
|
304
|
+
case "plan_checkpoint":
|
|
305
|
+
return `Checkpoint \u2014 ${req.payload.title ?? "step complete"}`;
|
|
306
|
+
case "plan_revision":
|
|
307
|
+
return "Approve plan revision";
|
|
308
|
+
case "choice":
|
|
309
|
+
return req.payload.question ?? "Choose an option";
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function permissionKindFor(req) {
|
|
313
|
+
if (req.kind === "run_command" || req.kind === "run_background") return "execute";
|
|
314
|
+
if (req.kind === "path_access") {
|
|
315
|
+
return req.payload.intent === "write" ? "edit" : "other";
|
|
316
|
+
}
|
|
317
|
+
return "other";
|
|
318
|
+
}
|
|
319
|
+
async function requestPermissionForGate(server, sessionId, req) {
|
|
320
|
+
const params = {
|
|
321
|
+
sessionId,
|
|
322
|
+
toolCall: {
|
|
323
|
+
toolCallId: `gate-${req.id}`,
|
|
324
|
+
title: permissionTitleFor(req),
|
|
325
|
+
kind: permissionKindFor(req),
|
|
326
|
+
status: "pending",
|
|
327
|
+
rawInput: req.payload
|
|
328
|
+
},
|
|
329
|
+
options: permissionOptionsFor(req)
|
|
330
|
+
};
|
|
331
|
+
let result;
|
|
332
|
+
try {
|
|
333
|
+
result = await server.sendRequest(
|
|
334
|
+
"session/request_permission",
|
|
335
|
+
params
|
|
336
|
+
);
|
|
337
|
+
} catch {
|
|
338
|
+
result = { outcome: { outcome: "cancelled" } };
|
|
339
|
+
}
|
|
340
|
+
return verdictFor(req, result);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/acp/protocol.ts
|
|
344
|
+
var ACP_PROTOCOL_VERSION = 1;
|
|
345
|
+
var ERR_PARSE = -32700;
|
|
346
|
+
var ERR_METHOD_NOT_FOUND = -32601;
|
|
347
|
+
var ERR_INVALID_PARAMS = -32602;
|
|
348
|
+
var ERR_INTERNAL = -32603;
|
|
349
|
+
function flattenPrompt(blocks) {
|
|
350
|
+
const parts = [];
|
|
351
|
+
for (const b of blocks) {
|
|
352
|
+
if (b.type === "text") parts.push(b.text);
|
|
353
|
+
else if (b.type === "resource" && b.resource.text) parts.push(b.resource.text);
|
|
354
|
+
}
|
|
355
|
+
return parts.join("\n\n").trim();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/acp/server.ts
|
|
359
|
+
import { createInterface } from "readline";
|
|
360
|
+
var AcpServer = class {
|
|
361
|
+
requestHandlers = /* @__PURE__ */ new Map();
|
|
362
|
+
notificationHandlers = /* @__PURE__ */ new Map();
|
|
363
|
+
pending = /* @__PURE__ */ new Map();
|
|
364
|
+
nextOutboundId = 1;
|
|
365
|
+
output;
|
|
366
|
+
rl;
|
|
367
|
+
closed = false;
|
|
368
|
+
constructor(opts = {}) {
|
|
369
|
+
this.output = opts.output ?? process.stdout;
|
|
370
|
+
const input = opts.input ?? process.stdin;
|
|
371
|
+
this.rl = createInterface({ input });
|
|
372
|
+
this.rl.on("line", (line) => {
|
|
373
|
+
void this.handleLine(line);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
onRequest(method, handler) {
|
|
377
|
+
this.requestHandlers.set(method, handler);
|
|
378
|
+
}
|
|
379
|
+
onNotification(method, handler) {
|
|
380
|
+
this.notificationHandlers.set(method, handler);
|
|
381
|
+
}
|
|
382
|
+
sendNotification(method, params) {
|
|
383
|
+
this.write({ jsonrpc: "2.0", method, params });
|
|
384
|
+
}
|
|
385
|
+
/** Send an outbound JSON-RPC request and resolve when the peer responds. */
|
|
386
|
+
sendRequest(method, params) {
|
|
387
|
+
const id = this.nextOutboundId++;
|
|
388
|
+
return new Promise((resolve2, reject) => {
|
|
389
|
+
this.pending.set(id, {
|
|
390
|
+
resolve: resolve2,
|
|
391
|
+
reject
|
|
392
|
+
});
|
|
393
|
+
this.write({ jsonrpc: "2.0", id, method, params });
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
close() {
|
|
397
|
+
if (this.closed) return;
|
|
398
|
+
this.closed = true;
|
|
399
|
+
for (const p of this.pending.values()) p.reject(new Error("server closed"));
|
|
400
|
+
this.pending.clear();
|
|
401
|
+
this.rl.close();
|
|
402
|
+
}
|
|
403
|
+
/** Wait for the input stream to end. */
|
|
404
|
+
done() {
|
|
405
|
+
return new Promise((resolve2) => this.rl.once("close", () => resolve2()));
|
|
406
|
+
}
|
|
407
|
+
write(msg) {
|
|
408
|
+
this.output.write(`${JSON.stringify(msg)}
|
|
409
|
+
`);
|
|
410
|
+
}
|
|
411
|
+
writeError(id, code, message) {
|
|
412
|
+
this.write({ jsonrpc: "2.0", id, error: { code, message } });
|
|
413
|
+
}
|
|
414
|
+
async handleLine(raw) {
|
|
415
|
+
const line = raw.trim();
|
|
416
|
+
if (!line) return;
|
|
417
|
+
let parsed;
|
|
418
|
+
try {
|
|
419
|
+
parsed = JSON.parse(line);
|
|
420
|
+
} catch {
|
|
421
|
+
this.writeError(null, ERR_PARSE, "parse error");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (!parsed || typeof parsed !== "object") {
|
|
425
|
+
this.writeError(null, ERR_PARSE, "expected JSON object");
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const msg = parsed;
|
|
429
|
+
if (typeof msg.method === "string" && msg.id !== void 0) {
|
|
430
|
+
const id = msg.id;
|
|
431
|
+
const handler = this.requestHandlers.get(msg.method);
|
|
432
|
+
if (!handler) {
|
|
433
|
+
this.writeError(id, ERR_METHOD_NOT_FOUND, `method not found: ${msg.method}`);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
const result = await handler(msg.params);
|
|
438
|
+
this.write({ jsonrpc: "2.0", id, result });
|
|
439
|
+
} catch (err) {
|
|
440
|
+
this.writeError(id, ERR_INTERNAL, err.message);
|
|
441
|
+
}
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (typeof msg.method === "string" && msg.id === void 0) {
|
|
445
|
+
const handler = this.notificationHandlers.get(msg.method);
|
|
446
|
+
if (!handler) return;
|
|
447
|
+
try {
|
|
448
|
+
await handler(msg.params);
|
|
449
|
+
} catch {
|
|
450
|
+
}
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (msg.id !== void 0 && msg.method === void 0) {
|
|
454
|
+
const response = parsed;
|
|
455
|
+
const pending = this.pending.get(response.id);
|
|
456
|
+
if (!pending) return;
|
|
457
|
+
this.pending.delete(response.id);
|
|
458
|
+
if (response.error) {
|
|
459
|
+
pending.reject(new Error(response.error.message));
|
|
460
|
+
} else {
|
|
461
|
+
pending.resolve(response.result);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// src/cli/commands/acp.ts
|
|
468
|
+
function resolveMcpPrefix(specName, specCount, globalPrefix) {
|
|
469
|
+
if (specName) return `${specName}_`;
|
|
470
|
+
if (specCount === 1 && globalPrefix) return globalPrefix;
|
|
471
|
+
return "";
|
|
472
|
+
}
|
|
473
|
+
async function loadMcpServers(tools, specs, globalPrefix) {
|
|
474
|
+
const clients = [];
|
|
475
|
+
if (specs.length === 0) return clients;
|
|
476
|
+
const cfg = readConfig();
|
|
477
|
+
const disabledNames = new Set(cfg.mcpDisabled ?? []);
|
|
478
|
+
for (const raw of specs) {
|
|
479
|
+
let label = "anon";
|
|
480
|
+
let mcp;
|
|
481
|
+
try {
|
|
482
|
+
const spec = parseMcpSpec(raw);
|
|
483
|
+
label = spec.name ?? "anon";
|
|
484
|
+
if (spec.name && disabledNames.has(spec.name)) {
|
|
485
|
+
process.stderr.write(`${formatMcpLifecycleEvent({ state: "disabled", name: label })}
|
|
486
|
+
`);
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
process.stderr.write(`${formatMcpLifecycleEvent({ state: "handshake", name: label })}
|
|
490
|
+
`);
|
|
491
|
+
const t0 = Date.now();
|
|
492
|
+
const prefix = resolveMcpPrefix(spec.name, specs.length, globalPrefix);
|
|
493
|
+
if (spec.transport === "stdio") preflightStdioSpec(spec);
|
|
494
|
+
const transport = buildTransportFromSpec(spec, { env: mcpEnvFor(spec.name, cfg) });
|
|
495
|
+
mcp = new McpClient({ transport });
|
|
496
|
+
await mcp.initialize();
|
|
497
|
+
const bridge = await bridgeMcpTools(mcp, {
|
|
498
|
+
registry: tools,
|
|
499
|
+
namePrefix: prefix,
|
|
500
|
+
serverName: label,
|
|
501
|
+
onSlow: (info) => process.stderr.write(
|
|
502
|
+
`${formatMcpSlowToast({ name: info.serverName, p95Ms: info.p95Ms, sampleSize: info.sampleSize })}
|
|
503
|
+
`
|
|
504
|
+
)
|
|
505
|
+
});
|
|
506
|
+
process.stderr.write(
|
|
507
|
+
`${formatMcpLifecycleEvent({
|
|
508
|
+
state: "connected",
|
|
509
|
+
name: label,
|
|
510
|
+
tools: bridge.registeredNames.length,
|
|
511
|
+
ms: Date.now() - t0
|
|
512
|
+
})}
|
|
513
|
+
`
|
|
514
|
+
);
|
|
515
|
+
clients.push(mcp);
|
|
516
|
+
} catch (err) {
|
|
517
|
+
await mcp?.close().catch(() => void 0);
|
|
518
|
+
process.stderr.write(
|
|
519
|
+
`${formatMcpLifecycleEvent({ state: "failed", name: label, reason: err.message })}
|
|
520
|
+
\u2192 run \`luckerr setup\` to remove broken entries from your saved config.
|
|
521
|
+
`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return clients;
|
|
526
|
+
}
|
|
527
|
+
function resolveDir(raw, fallback) {
|
|
528
|
+
if (!raw) return fallback;
|
|
529
|
+
const abs = resolve(raw);
|
|
530
|
+
if (!existsSync(abs) || !statSync(abs).isDirectory()) {
|
|
531
|
+
throw new Error(`workspace directory not found: ${abs}`);
|
|
532
|
+
}
|
|
533
|
+
return abs;
|
|
534
|
+
}
|
|
535
|
+
async function buildSession(opts) {
|
|
536
|
+
const preset = canonicalPresetName(loadPreset());
|
|
537
|
+
const resolved = resolvePreset(preset);
|
|
538
|
+
const model = opts.modelOverride || resolved.model;
|
|
539
|
+
const toolset = await buildCodeToolset({ rootDir: opts.rootDir });
|
|
540
|
+
const mcpClients = await loadMcpServers(toolset.tools, opts.mcpSpecs ?? [], opts.mcpPrefix);
|
|
541
|
+
const system = codeSystemPrompt(opts.rootDir, {
|
|
542
|
+
hasSemanticSearch: toolset.semantic.enabled,
|
|
543
|
+
modelId: model
|
|
544
|
+
});
|
|
545
|
+
const client = new DeepSeekClient({ baseUrl: loadBaseUrl() });
|
|
546
|
+
const prefix = new ImmutablePrefix({ system, toolSpecs: toolset.tools.specs() });
|
|
547
|
+
const loop = new CacheFirstLoop({
|
|
548
|
+
client,
|
|
549
|
+
prefix,
|
|
550
|
+
tools: toolset.tools,
|
|
551
|
+
model,
|
|
552
|
+
budgetUsd: opts.budgetUsd,
|
|
553
|
+
session: `acp-${timestampSuffix()}`
|
|
554
|
+
});
|
|
555
|
+
return {
|
|
556
|
+
id: `sess_${timestampSuffix()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
557
|
+
rootDir: opts.rootDir,
|
|
558
|
+
model,
|
|
559
|
+
toolset,
|
|
560
|
+
mcpClients,
|
|
561
|
+
loop,
|
|
562
|
+
eventizer: new Eventizer(),
|
|
563
|
+
ctx: {
|
|
564
|
+
model,
|
|
565
|
+
prefixHash: prefix.fingerprint,
|
|
566
|
+
reasoningEffort: loadReasoningEffort()
|
|
567
|
+
},
|
|
568
|
+
aborter: null
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
async function acpCommand(opts) {
|
|
572
|
+
loadDotenv();
|
|
573
|
+
if (loadApiKey()) {
|
|
574
|
+
process.env.DEEPSEEK_API_KEY = loadApiKey();
|
|
575
|
+
}
|
|
576
|
+
const defaultDir = resolveDir(opts.dir, process.cwd());
|
|
577
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
578
|
+
const sessionContext = new AsyncLocalStorage();
|
|
579
|
+
const server = new AcpServer();
|
|
580
|
+
let transcriptStream = null;
|
|
581
|
+
if (opts.transcript) {
|
|
582
|
+
const defaultModel = opts.model || resolvePreset(canonicalPresetName(loadPreset())).model;
|
|
583
|
+
transcriptStream = openTranscriptFile(opts.transcript, {
|
|
584
|
+
version: 1,
|
|
585
|
+
source: "luckerr acp",
|
|
586
|
+
model: defaultModel,
|
|
587
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
pauseGate.on((req) => {
|
|
591
|
+
const editMode = opts.yolo ? "yolo" : loadEditMode();
|
|
592
|
+
const auto = autoResolveVerdict(req, editMode);
|
|
593
|
+
if (auto !== null) {
|
|
594
|
+
pauseGate.resolve(req.id, auto);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const activeSessionId = sessionContext.getStore();
|
|
598
|
+
if (!activeSessionId || !sessions.has(activeSessionId)) {
|
|
599
|
+
pauseGate.cancel(req.id);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
void (async () => {
|
|
603
|
+
const verdict = await requestPermissionForGate(server, activeSessionId, req);
|
|
604
|
+
pauseGate.resolve(req.id, verdict);
|
|
605
|
+
})();
|
|
606
|
+
});
|
|
607
|
+
server.onRequest("initialize", (params) => {
|
|
608
|
+
if (!params || typeof params !== "object") {
|
|
609
|
+
throw Object.assign(new Error("initialize: missing params"), { code: ERR_INVALID_PARAMS });
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
613
|
+
agentCapabilities: {
|
|
614
|
+
loadSession: false,
|
|
615
|
+
promptCapabilities: { image: false, audio: false, embeddedContext: true },
|
|
616
|
+
mcpCapabilities: { http: false, sse: false }
|
|
617
|
+
},
|
|
618
|
+
agentInfo: { name: "luckerr", title: "Luckerr", version: VERSION },
|
|
619
|
+
authMethods: []
|
|
620
|
+
};
|
|
621
|
+
});
|
|
622
|
+
server.onRequest("session/new", async (params) => {
|
|
623
|
+
const rootDir = resolveDir(params?.cwd, defaultDir);
|
|
624
|
+
const session = await buildSession({
|
|
625
|
+
rootDir,
|
|
626
|
+
modelOverride: opts.model,
|
|
627
|
+
budgetUsd: opts.budgetUsd,
|
|
628
|
+
mcpSpecs: opts.mcpSpecs,
|
|
629
|
+
mcpPrefix: opts.mcpPrefix
|
|
630
|
+
});
|
|
631
|
+
sessions.set(session.id, session);
|
|
632
|
+
return { sessionId: session.id };
|
|
633
|
+
});
|
|
634
|
+
server.onRequest("session/prompt", async (params) => {
|
|
635
|
+
if (!params?.sessionId) {
|
|
636
|
+
throw Object.assign(new Error("session/prompt: missing sessionId"), {
|
|
637
|
+
code: ERR_INVALID_PARAMS
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
const session = sessions.get(params.sessionId);
|
|
641
|
+
if (!session) {
|
|
642
|
+
throw Object.assign(new Error(`session/prompt: unknown session ${params.sessionId}`), {
|
|
643
|
+
code: ERR_INVALID_PARAMS
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
const text = flattenPrompt(params.prompt);
|
|
647
|
+
if (!text) {
|
|
648
|
+
throw Object.assign(new Error("session/prompt: empty prompt"), { code: ERR_INVALID_PARAMS });
|
|
649
|
+
}
|
|
650
|
+
session.aborter = new AbortController();
|
|
651
|
+
let stopReason = "end_turn";
|
|
652
|
+
try {
|
|
653
|
+
await sessionContext.run(session.id, async () => {
|
|
654
|
+
for await (const ev of session.loop.step(text)) {
|
|
655
|
+
if (session.aborter?.signal.aborted) {
|
|
656
|
+
stopReason = "cancelled";
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
if (transcriptStream) {
|
|
660
|
+
writeRecord(
|
|
661
|
+
transcriptStream,
|
|
662
|
+
recordFromLoopEvent(ev, {
|
|
663
|
+
model: session.ctx.model,
|
|
664
|
+
prefixHash: session.ctx.prefixHash
|
|
665
|
+
})
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
for (const kev of session.eventizer.consume(ev, session.ctx)) {
|
|
669
|
+
dispatchKernelEvent(server, session.id, kev);
|
|
670
|
+
if (kev.type === "error") stopReason = "error";
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
} catch (err) {
|
|
675
|
+
const message = err.message;
|
|
676
|
+
server.sendNotification("session/update", {
|
|
677
|
+
sessionId: session.id,
|
|
678
|
+
update: {
|
|
679
|
+
sessionUpdate: "agent_message_chunk",
|
|
680
|
+
content: { type: "text", text: `
|
|
681
|
+
|
|
682
|
+
[error] ${message}` }
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
stopReason = "error";
|
|
686
|
+
} finally {
|
|
687
|
+
session.aborter = null;
|
|
688
|
+
}
|
|
689
|
+
return { stopReason };
|
|
690
|
+
});
|
|
691
|
+
server.onNotification("session/cancel", (params) => {
|
|
692
|
+
const session = params?.sessionId ? sessions.get(params.sessionId) : void 0;
|
|
693
|
+
session?.aborter?.abort();
|
|
694
|
+
});
|
|
695
|
+
try {
|
|
696
|
+
await server.done();
|
|
697
|
+
} finally {
|
|
698
|
+
transcriptStream?.end();
|
|
699
|
+
const closes = [];
|
|
700
|
+
for (const session of sessions.values()) {
|
|
701
|
+
for (const mcp of session.mcpClients) {
|
|
702
|
+
closes.push(mcp.close().catch(() => void 0));
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
await Promise.all(closes);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
export {
|
|
709
|
+
acpCommand,
|
|
710
|
+
loadMcpServers
|
|
711
|
+
};
|
|
712
|
+
//# sourceMappingURL=acp-EOOAI4F5.js.map
|