pybao-cli 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +440 -0
- package/README.zh-CN.md +338 -0
- package/cli-acp.js +82 -0
- package/cli.js +105 -0
- package/dist/REPL-WPV32MTF.js +42 -0
- package/dist/REPL-WPV32MTF.js.map +7 -0
- package/dist/acp-75HO2LBV.js +1357 -0
- package/dist/acp-75HO2LBV.js.map +7 -0
- package/dist/agentsValidate-6Z57ARKC.js +373 -0
- package/dist/agentsValidate-6Z57ARKC.js.map +7 -0
- package/dist/ask-NXXXCGY4.js +125 -0
- package/dist/ask-NXXXCGY4.js.map +7 -0
- package/dist/autoUpdater-PJMGNPUG.js +17 -0
- package/dist/autoUpdater-PJMGNPUG.js.map +7 -0
- package/dist/chunk-27GYWUY2.js +72 -0
- package/dist/chunk-27GYWUY2.js.map +7 -0
- package/dist/chunk-3DFBSQIT.js +23 -0
- package/dist/chunk-3DFBSQIT.js.map +7 -0
- package/dist/chunk-3KNGJX7Q.js +794 -0
- package/dist/chunk-3KNGJX7Q.js.map +7 -0
- package/dist/chunk-3PDD7M4T.js +164 -0
- package/dist/chunk-3PDD7M4T.js.map +7 -0
- package/dist/chunk-3ZNSAB7B.js +515 -0
- package/dist/chunk-3ZNSAB7B.js.map +7 -0
- package/dist/chunk-4SNFQYCY.js +511 -0
- package/dist/chunk-4SNFQYCY.js.map +7 -0
- package/dist/chunk-4XPNRLJG.js +1609 -0
- package/dist/chunk-4XPNRLJG.js.map +7 -0
- package/dist/chunk-5P7HBXTD.js +12 -0
- package/dist/chunk-5P7HBXTD.js.map +7 -0
- package/dist/chunk-6RZIUY5K.js +191 -0
- package/dist/chunk-6RZIUY5K.js.map +7 -0
- package/dist/chunk-6WELHKDA.js +240 -0
- package/dist/chunk-6WELHKDA.js.map +7 -0
- package/dist/chunk-7AAE6EO2.js +145 -0
- package/dist/chunk-7AAE6EO2.js.map +7 -0
- package/dist/chunk-A3BVXXA3.js +47 -0
- package/dist/chunk-A3BVXXA3.js.map +7 -0
- package/dist/chunk-A6PUMROK.js +152 -0
- package/dist/chunk-A6PUMROK.js.map +7 -0
- package/dist/chunk-BH3Y62E3.js +11 -0
- package/dist/chunk-BH3Y62E3.js.map +7 -0
- package/dist/chunk-BJSWTHRM.js +16 -0
- package/dist/chunk-BJSWTHRM.js.map +7 -0
- package/dist/chunk-BQA2EOUU.js +124 -0
- package/dist/chunk-BQA2EOUU.js.map +7 -0
- package/dist/chunk-CZZKRPE2.js +19 -0
- package/dist/chunk-CZZKRPE2.js.map +7 -0
- package/dist/chunk-ERMQRV55.js +24 -0
- package/dist/chunk-ERMQRV55.js.map +7 -0
- package/dist/chunk-HB2P6645.js +34 -0
- package/dist/chunk-HB2P6645.js.map +7 -0
- package/dist/chunk-HIRIJ2LQ.js +1256 -0
- package/dist/chunk-HIRIJ2LQ.js.map +7 -0
- package/dist/chunk-ICTEVBLN.js +735 -0
- package/dist/chunk-ICTEVBLN.js.map +7 -0
- package/dist/chunk-JKGOGSFT.js +128 -0
- package/dist/chunk-JKGOGSFT.js.map +7 -0
- package/dist/chunk-JZDE77EH.js +836 -0
- package/dist/chunk-JZDE77EH.js.map +7 -0
- package/dist/chunk-M624LT6O.js +17 -0
- package/dist/chunk-M624LT6O.js.map +7 -0
- package/dist/chunk-OMELVAJD.js +96 -0
- package/dist/chunk-OMELVAJD.js.map +7 -0
- package/dist/chunk-OUXHGDLH.js +95 -0
- package/dist/chunk-OUXHGDLH.js.map +7 -0
- package/dist/chunk-PCXUZ6AT.js +249 -0
- package/dist/chunk-PCXUZ6AT.js.map +7 -0
- package/dist/chunk-Q24ZGKIE.js +1097 -0
- package/dist/chunk-Q24ZGKIE.js.map +7 -0
- package/dist/chunk-QBHEERCF.js +30254 -0
- package/dist/chunk-QBHEERCF.js.map +7 -0
- package/dist/chunk-QIHB5PYM.js +472 -0
- package/dist/chunk-QIHB5PYM.js.map +7 -0
- package/dist/chunk-RQVLBMP7.js +24 -0
- package/dist/chunk-RQVLBMP7.js.map +7 -0
- package/dist/chunk-SWYJOV5E.js +490 -0
- package/dist/chunk-SWYJOV5E.js.map +7 -0
- package/dist/chunk-T6GVXTNQ.js +21 -0
- package/dist/chunk-T6GVXTNQ.js.map +7 -0
- package/dist/chunk-T7GPUZVK.js +766 -0
- package/dist/chunk-T7GPUZVK.js.map +7 -0
- package/dist/chunk-TXFCNQDE.js +2934 -0
- package/dist/chunk-TXFCNQDE.js.map +7 -0
- package/dist/chunk-UNNVICVU.js +95 -0
- package/dist/chunk-UNNVICVU.js.map +7 -0
- package/dist/chunk-UUNVJZWA.js +515 -0
- package/dist/chunk-UUNVJZWA.js.map +7 -0
- package/dist/chunk-VRGR4ZTQ.js +49 -0
- package/dist/chunk-VRGR4ZTQ.js.map +7 -0
- package/dist/chunk-VTVTEE5N.js +2613 -0
- package/dist/chunk-VTVTEE5N.js.map +7 -0
- package/dist/chunk-WPTPPOYN.js +936 -0
- package/dist/chunk-WPTPPOYN.js.map +7 -0
- package/dist/chunk-XXFY63TM.js +196 -0
- package/dist/chunk-XXFY63TM.js.map +7 -0
- package/dist/chunk-Z3HMXDXP.js +654 -0
- package/dist/chunk-Z3HMXDXP.js.map +7 -0
- package/dist/chunk-ZJGXEWKF.js +138 -0
- package/dist/chunk-ZJGXEWKF.js.map +7 -0
- package/dist/cli-RFYBXM7F.js +3917 -0
- package/dist/cli-RFYBXM7F.js.map +7 -0
- package/dist/commands-YOXMODDO.js +46 -0
- package/dist/commands-YOXMODDO.js.map +7 -0
- package/dist/config-5OPX3H2K.js +81 -0
- package/dist/config-5OPX3H2K.js.map +7 -0
- package/dist/context-THRRBPFP.js +30 -0
- package/dist/context-THRRBPFP.js.map +7 -0
- package/dist/costTracker-ELNBZ2DN.js +19 -0
- package/dist/costTracker-ELNBZ2DN.js.map +7 -0
- package/dist/customCommands-4XOZH44N.js +25 -0
- package/dist/customCommands-4XOZH44N.js.map +7 -0
- package/dist/env-EL4KBHMB.js +22 -0
- package/dist/env-EL4KBHMB.js.map +7 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +7 -0
- package/dist/kodeAgentSessionId-PROTVRBR.js +13 -0
- package/dist/kodeAgentSessionId-PROTVRBR.js.map +7 -0
- package/dist/kodeAgentSessionLoad-UMPV7MC3.js +18 -0
- package/dist/kodeAgentSessionLoad-UMPV7MC3.js.map +7 -0
- package/dist/kodeAgentSessionResume-YJS4FVQM.js +16 -0
- package/dist/kodeAgentSessionResume-YJS4FVQM.js.map +7 -0
- package/dist/kodeAgentStreamJson-3T26CHCP.js +13 -0
- package/dist/kodeAgentStreamJson-3T26CHCP.js.map +7 -0
- package/dist/kodeAgentStreamJsonSession-BZS2VDCY.js +131 -0
- package/dist/kodeAgentStreamJsonSession-BZS2VDCY.js.map +7 -0
- package/dist/kodeAgentStructuredStdio-TNB6U6SP.js +10 -0
- package/dist/kodeAgentStructuredStdio-TNB6U6SP.js.map +7 -0
- package/dist/kodeHooks-VUAWIY2D.js +36 -0
- package/dist/kodeHooks-VUAWIY2D.js.map +7 -0
- package/dist/llm-A3BCM4Q2.js +3118 -0
- package/dist/llm-A3BCM4Q2.js.map +7 -0
- package/dist/llmLazy-ZJSRLZVD.js +15 -0
- package/dist/llmLazy-ZJSRLZVD.js.map +7 -0
- package/dist/loader-HZQBWO74.js +28 -0
- package/dist/loader-HZQBWO74.js.map +7 -0
- package/dist/mcp-XKOJ55B2.js +49 -0
- package/dist/mcp-XKOJ55B2.js.map +7 -0
- package/dist/mentionProcessor-ANYU5MLF.js +211 -0
- package/dist/mentionProcessor-ANYU5MLF.js.map +7 -0
- package/dist/messages-75DL5XBP.js +63 -0
- package/dist/messages-75DL5XBP.js.map +7 -0
- package/dist/model-OPJGJZRC.js +30 -0
- package/dist/model-OPJGJZRC.js.map +7 -0
- package/dist/openai-DT54BAFP.js +29 -0
- package/dist/openai-DT54BAFP.js.map +7 -0
- package/dist/outputStyles-TPFVI52O.js +28 -0
- package/dist/outputStyles-TPFVI52O.js.map +7 -0
- package/dist/package.json +4 -0
- package/dist/pluginRuntime-W74PYSZ4.js +218 -0
- package/dist/pluginRuntime-W74PYSZ4.js.map +7 -0
- package/dist/pluginValidation-FALYRVI2.js +17 -0
- package/dist/pluginValidation-FALYRVI2.js.map +7 -0
- package/dist/prompts-J4TPRMJ3.js +48 -0
- package/dist/prompts-J4TPRMJ3.js.map +7 -0
- package/dist/query-K3QKBVDN.js +50 -0
- package/dist/query-K3QKBVDN.js.map +7 -0
- package/dist/responsesStreaming-HMB74TRD.js +10 -0
- package/dist/responsesStreaming-HMB74TRD.js.map +7 -0
- package/dist/ripgrep-XJGSUBG7.js +17 -0
- package/dist/ripgrep-XJGSUBG7.js.map +7 -0
- package/dist/skillMarketplace-AUGKNCPW.js +37 -0
- package/dist/skillMarketplace-AUGKNCPW.js.map +7 -0
- package/dist/state-DQYRXKTG.js +16 -0
- package/dist/state-DQYRXKTG.js.map +7 -0
- package/dist/theme-MS5HDUBJ.js +14 -0
- package/dist/theme-MS5HDUBJ.js.map +7 -0
- package/dist/toolPermissionContext-GYD5LYFK.js +17 -0
- package/dist/toolPermissionContext-GYD5LYFK.js.map +7 -0
- package/dist/toolPermissionSettings-4MPZVYDR.js +18 -0
- package/dist/toolPermissionSettings-4MPZVYDR.js.map +7 -0
- package/dist/tools-QW6SIJLJ.js +47 -0
- package/dist/tools-QW6SIJLJ.js.map +7 -0
- package/dist/userInput-F2PGBRFU.js +311 -0
- package/dist/userInput-F2PGBRFU.js.map +7 -0
- package/dist/uuid-GYYCQ6QK.js +9 -0
- package/dist/uuid-GYYCQ6QK.js.map +7 -0
- package/dist/yoga.wasm +0 -0
- package/package.json +136 -0
- package/scripts/binary-utils.cjs +62 -0
- package/scripts/cli-acp-wrapper.cjs +82 -0
- package/scripts/cli-wrapper.cjs +105 -0
- package/scripts/postinstall.js +144 -0
- package/yoga.wasm +0 -0
|
@@ -0,0 +1,1357 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { createRequire as __pybCreateRequire } from "node:module";
|
|
3
|
+
const require = __pybCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
ensurePackagedRuntimeEnv
|
|
6
|
+
} from "./chunk-VRGR4ZTQ.js";
|
|
7
|
+
import {
|
|
8
|
+
getTools
|
|
9
|
+
} from "./chunk-VTVTEE5N.js";
|
|
10
|
+
import {
|
|
11
|
+
getCommands,
|
|
12
|
+
getSystemPrompt,
|
|
13
|
+
grantReadPermissionForOriginalDir,
|
|
14
|
+
hasPermissionsToUseTool,
|
|
15
|
+
query
|
|
16
|
+
} from "./chunk-QBHEERCF.js";
|
|
17
|
+
import "./chunk-XXFY63TM.js";
|
|
18
|
+
import {
|
|
19
|
+
getClients
|
|
20
|
+
} from "./chunk-4XPNRLJG.js";
|
|
21
|
+
import "./chunk-QIHB5PYM.js";
|
|
22
|
+
import "./chunk-Q24ZGKIE.js";
|
|
23
|
+
import "./chunk-ZJGXEWKF.js";
|
|
24
|
+
import "./chunk-6WELHKDA.js";
|
|
25
|
+
import "./chunk-5P7HBXTD.js";
|
|
26
|
+
import "./chunk-3DFBSQIT.js";
|
|
27
|
+
import "./chunk-HIRIJ2LQ.js";
|
|
28
|
+
import "./chunk-T6GVXTNQ.js";
|
|
29
|
+
import "./chunk-4SNFQYCY.js";
|
|
30
|
+
import "./chunk-WPTPPOYN.js";
|
|
31
|
+
import "./chunk-HB2P6645.js";
|
|
32
|
+
import "./chunk-3PDD7M4T.js";
|
|
33
|
+
import "./chunk-T7GPUZVK.js";
|
|
34
|
+
import {
|
|
35
|
+
loadToolPermissionContextFromDisk,
|
|
36
|
+
persistToolPermissionUpdateToDisk
|
|
37
|
+
} from "./chunk-6RZIUY5K.js";
|
|
38
|
+
import "./chunk-7AAE6EO2.js";
|
|
39
|
+
import {
|
|
40
|
+
applyToolPermissionContextUpdates
|
|
41
|
+
} from "./chunk-UNNVICVU.js";
|
|
42
|
+
import "./chunk-UUNVJZWA.js";
|
|
43
|
+
import "./chunk-ERMQRV55.js";
|
|
44
|
+
import "./chunk-ICTEVBLN.js";
|
|
45
|
+
import "./chunk-A3BVXXA3.js";
|
|
46
|
+
import "./chunk-BJSWTHRM.js";
|
|
47
|
+
import {
|
|
48
|
+
createAssistantMessage,
|
|
49
|
+
createUserMessage
|
|
50
|
+
} from "./chunk-SWYJOV5E.js";
|
|
51
|
+
import "./chunk-Z3HMXDXP.js";
|
|
52
|
+
import {
|
|
53
|
+
getContext
|
|
54
|
+
} from "./chunk-3ZNSAB7B.js";
|
|
55
|
+
import "./chunk-BQA2EOUU.js";
|
|
56
|
+
import {
|
|
57
|
+
enableConfigs
|
|
58
|
+
} from "./chunk-JZDE77EH.js";
|
|
59
|
+
import "./chunk-RQVLBMP7.js";
|
|
60
|
+
import {
|
|
61
|
+
initDebugLogger
|
|
62
|
+
} from "./chunk-3KNGJX7Q.js";
|
|
63
|
+
import {
|
|
64
|
+
PRODUCT_COMMAND,
|
|
65
|
+
getKodeBaseDir,
|
|
66
|
+
initSentry,
|
|
67
|
+
logError,
|
|
68
|
+
setCwd,
|
|
69
|
+
setOriginalCwd
|
|
70
|
+
} from "./chunk-TXFCNQDE.js";
|
|
71
|
+
import {
|
|
72
|
+
MACRO
|
|
73
|
+
} from "./chunk-A6PUMROK.js";
|
|
74
|
+
import "./chunk-OUXHGDLH.js";
|
|
75
|
+
import "./chunk-BH3Y62E3.js";
|
|
76
|
+
|
|
77
|
+
// src/acp/jsonrpc.ts
|
|
78
|
+
import { format } from "node:util";
|
|
79
|
+
var JsonRpcError = class extends Error {
|
|
80
|
+
code;
|
|
81
|
+
data;
|
|
82
|
+
constructor(code, message, data) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.code = code;
|
|
85
|
+
this.data = data;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var JSONRPC_VERSION = "2.0";
|
|
89
|
+
function isObject(value) {
|
|
90
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
91
|
+
}
|
|
92
|
+
function safeStringify(value) {
|
|
93
|
+
try {
|
|
94
|
+
return JSON.stringify(value);
|
|
95
|
+
} catch {
|
|
96
|
+
return format(value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function makeErrorResponse(id, code, message, data) {
|
|
100
|
+
return {
|
|
101
|
+
jsonrpc: JSONRPC_VERSION,
|
|
102
|
+
id,
|
|
103
|
+
error: data === void 0 ? { code, message } : { code, message, data }
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function normalizeId(id) {
|
|
107
|
+
return typeof id === "string" || typeof id === "number" ? id : null;
|
|
108
|
+
}
|
|
109
|
+
var JsonRpcPeer = class {
|
|
110
|
+
handlers = /* @__PURE__ */ new Map();
|
|
111
|
+
pending = /* @__PURE__ */ new Map();
|
|
112
|
+
nextId = 1;
|
|
113
|
+
sendLine = null;
|
|
114
|
+
setSend(send) {
|
|
115
|
+
this.sendLine = send;
|
|
116
|
+
}
|
|
117
|
+
registerMethod(method, handler) {
|
|
118
|
+
this.handlers.set(method, handler);
|
|
119
|
+
}
|
|
120
|
+
async handleIncoming(payload) {
|
|
121
|
+
if (Array.isArray(payload)) {
|
|
122
|
+
const responses = [];
|
|
123
|
+
for (const item of payload) {
|
|
124
|
+
const r = await this.handleIncomingOne(item);
|
|
125
|
+
if (r) responses.push(r);
|
|
126
|
+
}
|
|
127
|
+
if (responses.length > 0) {
|
|
128
|
+
this.sendRaw(responses);
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const response = await this.handleIncomingOne(payload);
|
|
133
|
+
if (response) this.sendRaw(response);
|
|
134
|
+
}
|
|
135
|
+
async handleIncomingOne(payload) {
|
|
136
|
+
if (!isObject(payload)) {
|
|
137
|
+
return makeErrorResponse(null, -32600, "Invalid Request");
|
|
138
|
+
}
|
|
139
|
+
const jsonrpc = payload.jsonrpc;
|
|
140
|
+
if (jsonrpc !== JSONRPC_VERSION) {
|
|
141
|
+
return makeErrorResponse(normalizeId(payload.id), -32600, "Invalid Request");
|
|
142
|
+
}
|
|
143
|
+
const hasMethod = typeof payload.method === "string" && payload.method.length > 0;
|
|
144
|
+
const hasId = typeof payload.id === "string" || typeof payload.id === "number";
|
|
145
|
+
if (!hasMethod && hasId && ("result" in payload || "error" in payload)) {
|
|
146
|
+
this.handleResponse(payload);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
if (!hasMethod) {
|
|
150
|
+
return makeErrorResponse(normalizeId(payload.id), -32600, "Invalid Request");
|
|
151
|
+
}
|
|
152
|
+
const method = String(payload.method);
|
|
153
|
+
const params = "params" in payload ? payload.params : void 0;
|
|
154
|
+
const id = hasId ? payload.id : null;
|
|
155
|
+
const handler = this.handlers.get(method);
|
|
156
|
+
if (!handler) {
|
|
157
|
+
if (id === null) return null;
|
|
158
|
+
return makeErrorResponse(id, -32601, `Method not found: ${method}`);
|
|
159
|
+
}
|
|
160
|
+
if (id === null) {
|
|
161
|
+
try {
|
|
162
|
+
await handler(params);
|
|
163
|
+
} catch {
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const result = await handler(params);
|
|
169
|
+
return { jsonrpc: JSONRPC_VERSION, id, result: result ?? null };
|
|
170
|
+
} catch (err) {
|
|
171
|
+
if (err instanceof JsonRpcError) {
|
|
172
|
+
return makeErrorResponse(id, err.code, err.message, err.data);
|
|
173
|
+
}
|
|
174
|
+
const message = err instanceof Error ? err.message : safeStringify(err);
|
|
175
|
+
return makeErrorResponse(id, -32603, message);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
handleResponse(msg) {
|
|
179
|
+
const id = normalizeId(msg.id);
|
|
180
|
+
if (id === null) return;
|
|
181
|
+
const pending = this.pending.get(id);
|
|
182
|
+
if (!pending) return;
|
|
183
|
+
this.pending.delete(id);
|
|
184
|
+
if (pending.timeoutId) clearTimeout(pending.timeoutId);
|
|
185
|
+
if (msg && typeof msg === "object" && "error" in msg && msg.error) {
|
|
186
|
+
const e = msg.error;
|
|
187
|
+
const code = typeof e.code === "number" ? e.code : -32603;
|
|
188
|
+
const message = typeof e.message === "string" ? e.message : "Unknown error";
|
|
189
|
+
pending.reject(new JsonRpcError(code, message, e.data));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
pending.resolve(msg.result);
|
|
193
|
+
}
|
|
194
|
+
sendNotification(method, params) {
|
|
195
|
+
this.sendRaw({ jsonrpc: JSONRPC_VERSION, method, ...params !== void 0 ? { params } : {} });
|
|
196
|
+
}
|
|
197
|
+
sendRequest(args) {
|
|
198
|
+
const id = this.nextId++;
|
|
199
|
+
const timeoutMs = args.timeoutMs;
|
|
200
|
+
const p = new Promise((resolve2, reject) => {
|
|
201
|
+
const entry = { resolve: resolve2, reject, abort: args.signal };
|
|
202
|
+
if (timeoutMs && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
203
|
+
entry.timeoutId = setTimeout(() => {
|
|
204
|
+
this.pending.delete(id);
|
|
205
|
+
reject(new JsonRpcError(-32e3, `Request timed out: ${args.method}`));
|
|
206
|
+
}, timeoutMs);
|
|
207
|
+
}
|
|
208
|
+
if (args.signal) {
|
|
209
|
+
const onAbort = () => {
|
|
210
|
+
this.pending.delete(id);
|
|
211
|
+
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
212
|
+
reject(new JsonRpcError(-32e3, `Request aborted: ${args.method}`));
|
|
213
|
+
};
|
|
214
|
+
if (args.signal.aborted) {
|
|
215
|
+
onAbort();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
args.signal.addEventListener("abort", onAbort, { once: true });
|
|
219
|
+
}
|
|
220
|
+
this.pending.set(id, entry);
|
|
221
|
+
});
|
|
222
|
+
this.sendRaw({
|
|
223
|
+
jsonrpc: JSONRPC_VERSION,
|
|
224
|
+
id,
|
|
225
|
+
method: args.method,
|
|
226
|
+
...args.params !== void 0 ? { params: args.params } : {}
|
|
227
|
+
});
|
|
228
|
+
return p;
|
|
229
|
+
}
|
|
230
|
+
sendRaw(obj) {
|
|
231
|
+
const send = this.sendLine;
|
|
232
|
+
if (!send) {
|
|
233
|
+
throw new Error("JsonRpcPeer send() not configured");
|
|
234
|
+
}
|
|
235
|
+
const line = JSON.stringify(obj);
|
|
236
|
+
send(line);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/acp/stdioTransport.ts
|
|
241
|
+
import readline from "node:readline";
|
|
242
|
+
var StdioTransport = class {
|
|
243
|
+
constructor(peer2, opts) {
|
|
244
|
+
this.peer = peer2;
|
|
245
|
+
this.opts = opts;
|
|
246
|
+
}
|
|
247
|
+
rl = null;
|
|
248
|
+
pending = /* @__PURE__ */ new Set();
|
|
249
|
+
start() {
|
|
250
|
+
if (this.rl) return;
|
|
251
|
+
this.peer.setSend(this.opts.writeLine);
|
|
252
|
+
this.rl = readline.createInterface({
|
|
253
|
+
input: process.stdin,
|
|
254
|
+
crlfDelay: Infinity
|
|
255
|
+
});
|
|
256
|
+
this.rl.on("line", (line) => {
|
|
257
|
+
const trimmed = line.trim();
|
|
258
|
+
if (!trimmed) return;
|
|
259
|
+
try {
|
|
260
|
+
const payload = JSON.parse(trimmed);
|
|
261
|
+
const p = this.peer.handleIncoming(payload).catch(() => {
|
|
262
|
+
});
|
|
263
|
+
this.pending.add(p);
|
|
264
|
+
void p.finally(() => this.pending.delete(p));
|
|
265
|
+
} catch (err) {
|
|
266
|
+
this.opts.writeLine(
|
|
267
|
+
JSON.stringify({
|
|
268
|
+
jsonrpc: "2.0",
|
|
269
|
+
id: null,
|
|
270
|
+
error: { code: -32700, message: "Parse error" }
|
|
271
|
+
})
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
this.rl.on("close", () => {
|
|
276
|
+
void (async () => {
|
|
277
|
+
const pending = Array.from(this.pending);
|
|
278
|
+
if (pending.length > 0) {
|
|
279
|
+
await Promise.allSettled(pending);
|
|
280
|
+
}
|
|
281
|
+
process.exit(0);
|
|
282
|
+
})();
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
stop() {
|
|
286
|
+
this.rl?.close();
|
|
287
|
+
this.rl = null;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
// src/acp/stdoutGuard.ts
|
|
292
|
+
import { format as format2 } from "node:util";
|
|
293
|
+
function writeTo(write, chunk, encoding, cb) {
|
|
294
|
+
if (typeof encoding === "function") {
|
|
295
|
+
return write(chunk, void 0, encoding);
|
|
296
|
+
}
|
|
297
|
+
return write(chunk, encoding, cb);
|
|
298
|
+
}
|
|
299
|
+
function installStdoutGuard() {
|
|
300
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
301
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
302
|
+
const originalConsoleLog = console.log.bind(console);
|
|
303
|
+
const originalConsoleInfo = console.info.bind(console);
|
|
304
|
+
const originalConsoleDebug = console.debug.bind(console);
|
|
305
|
+
const originalConsoleWarn = console.warn.bind(console);
|
|
306
|
+
const originalConsoleError = console.error.bind(console);
|
|
307
|
+
const writeAcpLine2 = (line) => {
|
|
308
|
+
writeTo(originalStdoutWrite, `${line}
|
|
309
|
+
`);
|
|
310
|
+
};
|
|
311
|
+
const writeLogToStderr = (...args) => {
|
|
312
|
+
writeTo(originalStderrWrite, `${format2(...args)}
|
|
313
|
+
`);
|
|
314
|
+
};
|
|
315
|
+
console.log = writeLogToStderr;
|
|
316
|
+
console.info = writeLogToStderr;
|
|
317
|
+
console.debug = writeLogToStderr;
|
|
318
|
+
console.warn = writeLogToStderr;
|
|
319
|
+
console.error = writeLogToStderr;
|
|
320
|
+
process.stdout.write = ((chunk, encoding, cb) => {
|
|
321
|
+
return writeTo(originalStderrWrite, chunk, encoding, cb);
|
|
322
|
+
});
|
|
323
|
+
const restore = () => {
|
|
324
|
+
process.stdout.write = originalStdoutWrite;
|
|
325
|
+
console.log = originalConsoleLog;
|
|
326
|
+
console.info = originalConsoleInfo;
|
|
327
|
+
console.debug = originalConsoleDebug;
|
|
328
|
+
console.warn = originalConsoleWarn;
|
|
329
|
+
console.error = originalConsoleError;
|
|
330
|
+
};
|
|
331
|
+
return { writeAcpLine: writeAcpLine2, restore, originalStdoutWrite };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/acp/kodeAcpAgent.ts
|
|
335
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
336
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
337
|
+
import { nanoid } from "nanoid";
|
|
338
|
+
|
|
339
|
+
// src/acp/protocol.ts
|
|
340
|
+
var ACP_PROTOCOL_VERSION = 1;
|
|
341
|
+
|
|
342
|
+
// src/acp/kodeAcpAgent.ts
|
|
343
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
344
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
345
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
346
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
347
|
+
function asJsonObject(value) {
|
|
348
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
349
|
+
try {
|
|
350
|
+
JSON.stringify(value);
|
|
351
|
+
return value;
|
|
352
|
+
} catch {
|
|
353
|
+
return void 0;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function toolKindForName(toolName) {
|
|
357
|
+
switch (toolName) {
|
|
358
|
+
case "Read":
|
|
359
|
+
return "read";
|
|
360
|
+
case "Write":
|
|
361
|
+
case "Edit":
|
|
362
|
+
case "MultiEdit":
|
|
363
|
+
case "NotebookEdit":
|
|
364
|
+
return "edit";
|
|
365
|
+
case "Grep":
|
|
366
|
+
case "Glob":
|
|
367
|
+
return "search";
|
|
368
|
+
case "Bash":
|
|
369
|
+
case "TaskOutput":
|
|
370
|
+
case "KillShell":
|
|
371
|
+
return "execute";
|
|
372
|
+
case "SwitchModel":
|
|
373
|
+
return "switch_mode";
|
|
374
|
+
default:
|
|
375
|
+
return "other";
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function titleForToolCall(toolName, input) {
|
|
379
|
+
if (toolName === "Read" && typeof input.file_path === "string") {
|
|
380
|
+
return `Read ${input.file_path}`;
|
|
381
|
+
}
|
|
382
|
+
if ((toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") && typeof input.file_path === "string") {
|
|
383
|
+
return `${toolName} ${input.file_path}`;
|
|
384
|
+
}
|
|
385
|
+
if (toolName === "Bash" && typeof input.command === "string") {
|
|
386
|
+
const cmd = input.command.trim().replace(/\s+/g, " ");
|
|
387
|
+
const clipped = cmd.length > 120 ? `${cmd.slice(0, 117)}...` : cmd;
|
|
388
|
+
return `Run ${clipped}`;
|
|
389
|
+
}
|
|
390
|
+
return toolName;
|
|
391
|
+
}
|
|
392
|
+
function blocksToText(blocks) {
|
|
393
|
+
const parts = [];
|
|
394
|
+
for (const block of blocks) {
|
|
395
|
+
if (!block || typeof block !== "object") continue;
|
|
396
|
+
switch (block.type) {
|
|
397
|
+
case "text": {
|
|
398
|
+
const text = typeof block.text === "string" ? block.text : "";
|
|
399
|
+
if (text) parts.push(text);
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
case "resource": {
|
|
403
|
+
const resource = block.resource || {};
|
|
404
|
+
const uri = typeof resource.uri === "string" ? resource.uri : "";
|
|
405
|
+
const mimeType = typeof resource.mimeType === "string" && resource.mimeType ? resource.mimeType : "text/plain";
|
|
406
|
+
if (typeof resource.text === "string") {
|
|
407
|
+
parts.push(
|
|
408
|
+
[
|
|
409
|
+
"",
|
|
410
|
+
`@resource ${uri} (${mimeType})`,
|
|
411
|
+
"```",
|
|
412
|
+
resource.text,
|
|
413
|
+
"```"
|
|
414
|
+
].join("\n")
|
|
415
|
+
);
|
|
416
|
+
} else if (typeof resource.blob === "string") {
|
|
417
|
+
parts.push(
|
|
418
|
+
[
|
|
419
|
+
"",
|
|
420
|
+
`@resource ${uri} (${mimeType}) [base64]`,
|
|
421
|
+
resource.blob
|
|
422
|
+
].join("\n")
|
|
423
|
+
);
|
|
424
|
+
} else if (uri) {
|
|
425
|
+
parts.push(`@resource ${uri} (${mimeType})`);
|
|
426
|
+
}
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
case "resource_link": {
|
|
430
|
+
const uri = typeof block.uri === "string" ? block.uri : "";
|
|
431
|
+
const name = typeof block.name === "string" ? block.name : "";
|
|
432
|
+
const title = typeof block.title === "string" ? block.title : "";
|
|
433
|
+
const description = typeof block.description === "string" ? block.description : "";
|
|
434
|
+
parts.push(
|
|
435
|
+
[
|
|
436
|
+
"",
|
|
437
|
+
`@resource_link ${name || uri}`,
|
|
438
|
+
...title ? [title] : [],
|
|
439
|
+
...description ? [description] : [],
|
|
440
|
+
...uri ? [uri] : []
|
|
441
|
+
].join("\n")
|
|
442
|
+
);
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
case "image":
|
|
446
|
+
case "audio": {
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
default:
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return parts.join("\n").trim();
|
|
454
|
+
}
|
|
455
|
+
function extractAssistantText(msg) {
|
|
456
|
+
const blocks = Array.isArray(msg?.message?.content) ? msg.message.content : [];
|
|
457
|
+
const texts = [];
|
|
458
|
+
for (const b of blocks) {
|
|
459
|
+
if (!b || typeof b !== "object") continue;
|
|
460
|
+
if (b.type === "text" && typeof b.text === "string") texts.push(b.text);
|
|
461
|
+
if (b.type === "thinking" && typeof b.thinking === "string") texts.push(b.thinking);
|
|
462
|
+
}
|
|
463
|
+
return texts.join("").trim();
|
|
464
|
+
}
|
|
465
|
+
function extractToolResults(msg) {
|
|
466
|
+
const content = msg?.message?.content;
|
|
467
|
+
const blocks = Array.isArray(content) ? content : [];
|
|
468
|
+
const out = [];
|
|
469
|
+
for (const b of blocks) {
|
|
470
|
+
if (!b || typeof b !== "object") continue;
|
|
471
|
+
if (b.type !== "tool_result") continue;
|
|
472
|
+
const toolUseId = typeof b.tool_use_id === "string" ? b.tool_use_id : "";
|
|
473
|
+
const isError = Boolean(b.is_error);
|
|
474
|
+
const raw = b.content;
|
|
475
|
+
const text = typeof raw === "string" ? raw : Array.isArray(raw) ? raw.filter((x) => x && typeof x === "object" && x.type === "text").map((x) => String(x.text ?? "")).join("") : "";
|
|
476
|
+
if (toolUseId) out.push({ toolUseId, isError, content: text });
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
var ACP_SESSION_STORE_VERSION = 1;
|
|
481
|
+
var MAX_DIFF_FILE_BYTES = 512e3;
|
|
482
|
+
var MAX_DIFF_TEXT_CHARS = 4e5;
|
|
483
|
+
function getProjectDirSlug(cwd) {
|
|
484
|
+
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
485
|
+
}
|
|
486
|
+
function sanitizeSessionId(sessionId) {
|
|
487
|
+
return sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
488
|
+
}
|
|
489
|
+
function getAcpSessionDir(cwd) {
|
|
490
|
+
return join(getKodeBaseDir(), getProjectDirSlug(cwd), "acp-sessions");
|
|
491
|
+
}
|
|
492
|
+
function getAcpSessionFilePath(cwd, sessionId) {
|
|
493
|
+
return join(getAcpSessionDir(cwd), `${sanitizeSessionId(sessionId)}.json`);
|
|
494
|
+
}
|
|
495
|
+
function readTextFileForDiff(filePath) {
|
|
496
|
+
try {
|
|
497
|
+
const stats = statSync(filePath);
|
|
498
|
+
if (!stats.isFile()) return null;
|
|
499
|
+
if (stats.size > MAX_DIFF_FILE_BYTES) return null;
|
|
500
|
+
return readFileSync(filePath, "utf8");
|
|
501
|
+
} catch {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function truncateDiffText(text) {
|
|
506
|
+
if (text.length <= MAX_DIFF_TEXT_CHARS) return text;
|
|
507
|
+
return `${text.slice(0, MAX_DIFF_TEXT_CHARS)}
|
|
508
|
+
|
|
509
|
+
[truncated ${text.length - MAX_DIFF_TEXT_CHARS} chars]`;
|
|
510
|
+
}
|
|
511
|
+
function persistAcpSessionToDisk(session) {
|
|
512
|
+
try {
|
|
513
|
+
const dir = getAcpSessionDir(session.cwd);
|
|
514
|
+
mkdirSync(dir, { recursive: true });
|
|
515
|
+
const payload = {
|
|
516
|
+
version: ACP_SESSION_STORE_VERSION,
|
|
517
|
+
sessionId: session.sessionId,
|
|
518
|
+
cwd: session.cwd,
|
|
519
|
+
mcpServers: session.mcpServers,
|
|
520
|
+
messages: session.messages,
|
|
521
|
+
toolPermissionContext: session.toolPermissionContext,
|
|
522
|
+
readFileTimestamps: session.readFileTimestamps,
|
|
523
|
+
responseState: session.responseState,
|
|
524
|
+
currentModeId: session.currentModeId
|
|
525
|
+
};
|
|
526
|
+
const path = getAcpSessionFilePath(session.cwd, session.sessionId);
|
|
527
|
+
writeFileSync(path, JSON.stringify(payload, null, 2), "utf8");
|
|
528
|
+
} catch (e) {
|
|
529
|
+
logError(e);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
function loadAcpSessionFromDisk(cwd, sessionId) {
|
|
533
|
+
try {
|
|
534
|
+
const path = getAcpSessionFilePath(cwd, sessionId);
|
|
535
|
+
if (!existsSync(path)) return null;
|
|
536
|
+
const raw = readFileSync(path, "utf8");
|
|
537
|
+
const parsed = JSON.parse(raw);
|
|
538
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
539
|
+
if (parsed.sessionId !== sessionId) return null;
|
|
540
|
+
if (typeof parsed.cwd !== "string" || parsed.cwd !== cwd) return null;
|
|
541
|
+
if (!Array.isArray(parsed.messages)) return null;
|
|
542
|
+
return parsed;
|
|
543
|
+
} catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async function connectAcpMcpServers(mcpServers) {
|
|
548
|
+
if (!Array.isArray(mcpServers) || mcpServers.length === 0) return [];
|
|
549
|
+
const rawTimeout = process.env.MCP_CONNECTION_TIMEOUT_MS;
|
|
550
|
+
const parsedTimeout = rawTimeout ? Number.parseInt(rawTimeout, 10) : NaN;
|
|
551
|
+
const timeoutMs = Number.isFinite(parsedTimeout) ? parsedTimeout : 3e4;
|
|
552
|
+
const results = [];
|
|
553
|
+
const connectWithTimeout = async (client, transport2, name) => {
|
|
554
|
+
const connectPromise = client.connect(transport2);
|
|
555
|
+
if (timeoutMs > 0) {
|
|
556
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
557
|
+
const timeoutId = setTimeout(() => {
|
|
558
|
+
reject(new Error(`Connection to MCP server "${name}" timed out after ${timeoutMs}ms`));
|
|
559
|
+
}, timeoutMs);
|
|
560
|
+
connectPromise.then(
|
|
561
|
+
() => clearTimeout(timeoutId),
|
|
562
|
+
() => clearTimeout(timeoutId)
|
|
563
|
+
);
|
|
564
|
+
});
|
|
565
|
+
await Promise.race([connectPromise, timeoutPromise]);
|
|
566
|
+
} else {
|
|
567
|
+
await connectPromise;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
for (const server of mcpServers) {
|
|
571
|
+
const serverType = typeof server?.type === "string" ? String(server.type) : "stdio";
|
|
572
|
+
const name = typeof server?.name === "string" ? String(server.name) : "";
|
|
573
|
+
if (!name) {
|
|
574
|
+
results.push({ name: "<invalid>", type: "failed" });
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
const candidates = [];
|
|
578
|
+
if (serverType === "http" || serverType === "sse") {
|
|
579
|
+
const url = typeof server?.url === "string" ? String(server.url) : "";
|
|
580
|
+
if (!url) {
|
|
581
|
+
results.push({ name, type: "failed" });
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
let parsedUrl;
|
|
585
|
+
try {
|
|
586
|
+
parsedUrl = new URL(url);
|
|
587
|
+
} catch (e) {
|
|
588
|
+
logError(e);
|
|
589
|
+
results.push({ name, type: "failed" });
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const headerList = Array.isArray(server?.headers) ? server.headers : [];
|
|
593
|
+
const headers = {};
|
|
594
|
+
for (const h of headerList) {
|
|
595
|
+
if (!h || typeof h !== "object") continue;
|
|
596
|
+
const k = typeof h.name === "string" ? String(h.name) : "";
|
|
597
|
+
const val = typeof h.value === "string" ? String(h.value) : "";
|
|
598
|
+
if (k) headers[k] = val;
|
|
599
|
+
}
|
|
600
|
+
const requestInit = Object.keys(headers).length > 0 ? { requestInit: { headers } } : {};
|
|
601
|
+
if (serverType === "http") {
|
|
602
|
+
candidates.push(
|
|
603
|
+
{ kind: "http", transport: new StreamableHTTPClientTransport(parsedUrl, requestInit) },
|
|
604
|
+
{ kind: "sse", transport: new SSEClientTransport(parsedUrl, requestInit) }
|
|
605
|
+
);
|
|
606
|
+
} else {
|
|
607
|
+
candidates.push(
|
|
608
|
+
{ kind: "sse", transport: new SSEClientTransport(parsedUrl, requestInit) },
|
|
609
|
+
{ kind: "http", transport: new StreamableHTTPClientTransport(parsedUrl, requestInit) }
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
} else {
|
|
613
|
+
const command = typeof server?.command === "string" ? String(server.command) : "";
|
|
614
|
+
const args = Array.isArray(server?.args) ? server.args.map((a) => String(a)) : [];
|
|
615
|
+
const envList = Array.isArray(server?.env) ? server.env : [];
|
|
616
|
+
if (!command) {
|
|
617
|
+
results.push({ name, type: "failed" });
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const envFromParams = {};
|
|
621
|
+
for (const v of envList) {
|
|
622
|
+
if (!v || typeof v !== "object") continue;
|
|
623
|
+
const k = typeof v.name === "string" ? String(v.name) : "";
|
|
624
|
+
const val = typeof v.value === "string" ? String(v.value) : "";
|
|
625
|
+
if (k) envFromParams[k] = val;
|
|
626
|
+
}
|
|
627
|
+
candidates.push({
|
|
628
|
+
kind: "stdio",
|
|
629
|
+
transport: new StdioClientTransport({
|
|
630
|
+
command,
|
|
631
|
+
args,
|
|
632
|
+
env: { ...process.env, ...envFromParams },
|
|
633
|
+
stderr: "pipe"
|
|
634
|
+
})
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
let lastError;
|
|
638
|
+
for (const candidate of candidates) {
|
|
639
|
+
const client = new Client(
|
|
640
|
+
{ name: PRODUCT_COMMAND, version: MACRO.VERSION || "0.0.0" },
|
|
641
|
+
{ capabilities: {} }
|
|
642
|
+
);
|
|
643
|
+
try {
|
|
644
|
+
await connectWithTimeout(client, candidate.transport, name);
|
|
645
|
+
let capabilities = null;
|
|
646
|
+
try {
|
|
647
|
+
capabilities = client.getServerCapabilities();
|
|
648
|
+
} catch {
|
|
649
|
+
capabilities = null;
|
|
650
|
+
}
|
|
651
|
+
results.push({ name, client, capabilities, type: "connected" });
|
|
652
|
+
lastError = null;
|
|
653
|
+
break;
|
|
654
|
+
} catch (e) {
|
|
655
|
+
lastError = e;
|
|
656
|
+
try {
|
|
657
|
+
await client.close();
|
|
658
|
+
} catch {
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (lastError) {
|
|
663
|
+
logError(lastError);
|
|
664
|
+
results.push({ name, type: "failed" });
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return results;
|
|
668
|
+
}
|
|
669
|
+
function mergeMcpClients(base, extra) {
|
|
670
|
+
const map = /* @__PURE__ */ new Map();
|
|
671
|
+
for (const c of base) map.set(c.name, c);
|
|
672
|
+
for (const c of extra) map.set(c.name, c);
|
|
673
|
+
return Array.from(map.values());
|
|
674
|
+
}
|
|
675
|
+
var KodeAcpAgent = class {
|
|
676
|
+
constructor(peer2) {
|
|
677
|
+
this.peer = peer2;
|
|
678
|
+
this.registerMethods();
|
|
679
|
+
}
|
|
680
|
+
clientCapabilities = {};
|
|
681
|
+
sessions = /* @__PURE__ */ new Map();
|
|
682
|
+
registerMethods() {
|
|
683
|
+
this.peer.registerMethod("initialize", this.handleInitialize.bind(this));
|
|
684
|
+
this.peer.registerMethod("authenticate", this.handleAuthenticate.bind(this));
|
|
685
|
+
this.peer.registerMethod("session/new", this.handleSessionNew.bind(this));
|
|
686
|
+
this.peer.registerMethod("session/load", this.handleSessionLoad.bind(this));
|
|
687
|
+
this.peer.registerMethod("session/prompt", this.handleSessionPrompt.bind(this));
|
|
688
|
+
this.peer.registerMethod("session/set_mode", this.handleSessionSetMode.bind(this));
|
|
689
|
+
this.peer.registerMethod("session/cancel", this.handleSessionCancel.bind(this));
|
|
690
|
+
}
|
|
691
|
+
async handleInitialize(params) {
|
|
692
|
+
const p = params ?? {};
|
|
693
|
+
const protocolVersion = typeof p.protocolVersion === "number" ? p.protocolVersion : ACP_PROTOCOL_VERSION;
|
|
694
|
+
this.clientCapabilities = p.clientCapabilities && typeof p.clientCapabilities === "object" ? p.clientCapabilities : {};
|
|
695
|
+
return {
|
|
696
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
697
|
+
agentCapabilities: {
|
|
698
|
+
loadSession: true,
|
|
699
|
+
promptCapabilities: {
|
|
700
|
+
image: false,
|
|
701
|
+
audio: false,
|
|
702
|
+
embeddedContext: true,
|
|
703
|
+
embeddedContent: true
|
|
704
|
+
},
|
|
705
|
+
mcpCapabilities: {
|
|
706
|
+
http: true,
|
|
707
|
+
sse: true
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
agentInfo: {
|
|
711
|
+
name: "kode",
|
|
712
|
+
title: "Kode",
|
|
713
|
+
version: MACRO.VERSION || "0.0.0"
|
|
714
|
+
},
|
|
715
|
+
authMethods: []
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
async handleAuthenticate(_params) {
|
|
719
|
+
return {};
|
|
720
|
+
}
|
|
721
|
+
async handleSessionNew(params) {
|
|
722
|
+
const p = params ?? {};
|
|
723
|
+
const cwd = typeof p.cwd === "string" ? p.cwd : "";
|
|
724
|
+
if (!cwd) {
|
|
725
|
+
throw new JsonRpcError(-32602, "Missing required param: cwd");
|
|
726
|
+
}
|
|
727
|
+
if (!isAbsolute(cwd)) {
|
|
728
|
+
throw new JsonRpcError(-32602, `cwd must be an absolute path: ${cwd}`);
|
|
729
|
+
}
|
|
730
|
+
setOriginalCwd(cwd);
|
|
731
|
+
await setCwd(cwd);
|
|
732
|
+
grantReadPermissionForOriginalDir();
|
|
733
|
+
const mcpServers = Array.isArray(p.mcpServers) ? p.mcpServers : [];
|
|
734
|
+
const [commands, tools, ctx, systemPrompt, configuredMcpClients] = await Promise.all([
|
|
735
|
+
getCommands(),
|
|
736
|
+
getTools(),
|
|
737
|
+
getContext(),
|
|
738
|
+
getSystemPrompt({ disableSlashCommands: false }),
|
|
739
|
+
getClients().catch(() => [])
|
|
740
|
+
]);
|
|
741
|
+
const acpMcpClients = await connectAcpMcpServers(mcpServers);
|
|
742
|
+
const mcpClients = mergeMcpClients(configuredMcpClients, acpMcpClients);
|
|
743
|
+
const toolPermissionContext = loadToolPermissionContextFromDisk({
|
|
744
|
+
projectDir: cwd,
|
|
745
|
+
includeKodeProjectConfig: true,
|
|
746
|
+
isBypassPermissionsModeAvailable: true
|
|
747
|
+
});
|
|
748
|
+
const sessionId = `sess_${nanoid()}`;
|
|
749
|
+
const session = {
|
|
750
|
+
sessionId,
|
|
751
|
+
cwd,
|
|
752
|
+
mcpServers,
|
|
753
|
+
mcpClients,
|
|
754
|
+
commands,
|
|
755
|
+
tools,
|
|
756
|
+
systemPrompt,
|
|
757
|
+
context: ctx,
|
|
758
|
+
messages: [],
|
|
759
|
+
toolPermissionContext,
|
|
760
|
+
readFileTimestamps: {},
|
|
761
|
+
responseState: {},
|
|
762
|
+
currentModeId: toolPermissionContext.mode ?? "default",
|
|
763
|
+
activeAbortController: null,
|
|
764
|
+
toolCalls: /* @__PURE__ */ new Map()
|
|
765
|
+
};
|
|
766
|
+
this.sessions.set(sessionId, session);
|
|
767
|
+
this.sendAvailableCommands(session);
|
|
768
|
+
this.sendCurrentMode(session);
|
|
769
|
+
persistAcpSessionToDisk(session);
|
|
770
|
+
return {
|
|
771
|
+
sessionId,
|
|
772
|
+
modes: this.getModeState(session)
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
async handleSessionLoad(params) {
|
|
776
|
+
const p = params ?? {};
|
|
777
|
+
const sessionId = typeof p.sessionId === "string" ? p.sessionId : "";
|
|
778
|
+
const cwd = typeof p.cwd === "string" ? p.cwd : "";
|
|
779
|
+
if (!sessionId) throw new JsonRpcError(-32602, "Missing required param: sessionId");
|
|
780
|
+
if (!cwd) throw new JsonRpcError(-32602, "Missing required param: cwd");
|
|
781
|
+
if (!isAbsolute(cwd)) {
|
|
782
|
+
throw new JsonRpcError(-32602, `cwd must be an absolute path: ${cwd}`);
|
|
783
|
+
}
|
|
784
|
+
setOriginalCwd(cwd);
|
|
785
|
+
await setCwd(cwd);
|
|
786
|
+
grantReadPermissionForOriginalDir();
|
|
787
|
+
const persisted = loadAcpSessionFromDisk(cwd, sessionId);
|
|
788
|
+
if (!persisted) {
|
|
789
|
+
throw new JsonRpcError(-32602, `Session not found: ${sessionId}`);
|
|
790
|
+
}
|
|
791
|
+
const mcpServers = Array.isArray(p.mcpServers) ? p.mcpServers : [];
|
|
792
|
+
const [commands, tools, ctx, systemPrompt, configuredMcpClients] = await Promise.all([
|
|
793
|
+
getCommands(),
|
|
794
|
+
getTools(),
|
|
795
|
+
getContext(),
|
|
796
|
+
getSystemPrompt({ disableSlashCommands: false }),
|
|
797
|
+
getClients().catch(() => [])
|
|
798
|
+
]);
|
|
799
|
+
const acpMcpClients = await connectAcpMcpServers(mcpServers);
|
|
800
|
+
const mcpClients = mergeMcpClients(configuredMcpClients, acpMcpClients);
|
|
801
|
+
const toolPermissionContext = loadToolPermissionContextFromDisk({
|
|
802
|
+
projectDir: cwd,
|
|
803
|
+
includeKodeProjectConfig: true,
|
|
804
|
+
isBypassPermissionsModeAvailable: true
|
|
805
|
+
});
|
|
806
|
+
const currentModeId = typeof persisted.currentModeId === "string" && persisted.currentModeId ? persisted.currentModeId : toolPermissionContext.mode ?? "default";
|
|
807
|
+
toolPermissionContext.mode = currentModeId;
|
|
808
|
+
const session = {
|
|
809
|
+
sessionId,
|
|
810
|
+
cwd,
|
|
811
|
+
mcpServers,
|
|
812
|
+
mcpClients,
|
|
813
|
+
commands,
|
|
814
|
+
tools,
|
|
815
|
+
systemPrompt,
|
|
816
|
+
context: ctx,
|
|
817
|
+
messages: Array.isArray(persisted.messages) ? persisted.messages : [],
|
|
818
|
+
toolPermissionContext,
|
|
819
|
+
readFileTimestamps: persisted.readFileTimestamps && typeof persisted.readFileTimestamps === "object" ? persisted.readFileTimestamps : {},
|
|
820
|
+
responseState: persisted.responseState && typeof persisted.responseState === "object" ? persisted.responseState : {},
|
|
821
|
+
currentModeId,
|
|
822
|
+
activeAbortController: null,
|
|
823
|
+
toolCalls: /* @__PURE__ */ new Map()
|
|
824
|
+
};
|
|
825
|
+
this.sessions.set(sessionId, session);
|
|
826
|
+
this.sendAvailableCommands(session);
|
|
827
|
+
this.sendCurrentMode(session);
|
|
828
|
+
this.replayConversation(session);
|
|
829
|
+
return { modes: this.getModeState(session) };
|
|
830
|
+
}
|
|
831
|
+
async handleSessionSetMode(params) {
|
|
832
|
+
const p = params ?? {};
|
|
833
|
+
const sessionId = typeof p.sessionId === "string" ? p.sessionId : "";
|
|
834
|
+
const modeId = typeof p.modeId === "string" ? p.modeId : "";
|
|
835
|
+
const session = this.sessions.get(sessionId);
|
|
836
|
+
if (!session) throw new JsonRpcError(-32602, `Session not found: ${sessionId}`);
|
|
837
|
+
const allowed = new Set(this.getModeState(session).availableModes.map((m) => m.id));
|
|
838
|
+
if (!allowed.has(modeId)) {
|
|
839
|
+
throw new JsonRpcError(-32602, `Unknown modeId: ${modeId}`);
|
|
840
|
+
}
|
|
841
|
+
session.currentModeId = modeId;
|
|
842
|
+
session.toolPermissionContext.mode = modeId;
|
|
843
|
+
this.sendCurrentMode(session);
|
|
844
|
+
persistAcpSessionToDisk(session);
|
|
845
|
+
return {};
|
|
846
|
+
}
|
|
847
|
+
async handleSessionCancel(params) {
|
|
848
|
+
const p = params ?? {};
|
|
849
|
+
const sessionId = typeof p.sessionId === "string" ? p.sessionId : "";
|
|
850
|
+
const session = this.sessions.get(sessionId);
|
|
851
|
+
if (!session) return;
|
|
852
|
+
session.activeAbortController?.abort();
|
|
853
|
+
}
|
|
854
|
+
async handleSessionPrompt(params) {
|
|
855
|
+
const p = params ?? {};
|
|
856
|
+
const sessionId = typeof p.sessionId === "string" ? p.sessionId : "";
|
|
857
|
+
const blocks = Array.isArray(p.prompt) ? p.prompt : Array.isArray(p.content) ? p.content : [];
|
|
858
|
+
const session = this.sessions.get(sessionId);
|
|
859
|
+
if (!session) throw new JsonRpcError(-32602, `Session not found: ${sessionId}`);
|
|
860
|
+
if (session.activeAbortController) {
|
|
861
|
+
throw new JsonRpcError(-32e3, `Session already has an active prompt: ${sessionId}`);
|
|
862
|
+
}
|
|
863
|
+
setOriginalCwd(session.cwd);
|
|
864
|
+
await setCwd(session.cwd);
|
|
865
|
+
grantReadPermissionForOriginalDir();
|
|
866
|
+
const promptText = blocksToText(blocks);
|
|
867
|
+
const userMsg = createUserMessage(promptText);
|
|
868
|
+
const baseMessages = [...session.messages, userMsg];
|
|
869
|
+
session.messages.push(userMsg);
|
|
870
|
+
if (process.env.KODE_ACP_ECHO === "1") {
|
|
871
|
+
await this.handleKodeMessage(session, createAssistantMessage(promptText));
|
|
872
|
+
persistAcpSessionToDisk(session);
|
|
873
|
+
return { stopReason: "end_turn" };
|
|
874
|
+
}
|
|
875
|
+
const abortController = new AbortController();
|
|
876
|
+
session.activeAbortController = abortController;
|
|
877
|
+
const canUseTool = this.createAcpCanUseTool(session);
|
|
878
|
+
const options = {
|
|
879
|
+
commands: session.commands,
|
|
880
|
+
tools: session.tools,
|
|
881
|
+
verbose: false,
|
|
882
|
+
safeMode: false,
|
|
883
|
+
forkNumber: 0,
|
|
884
|
+
messageLogName: session.sessionId,
|
|
885
|
+
maxThinkingTokens: 0,
|
|
886
|
+
persistSession: false,
|
|
887
|
+
toolPermissionContext: session.toolPermissionContext,
|
|
888
|
+
mcpClients: session.mcpClients,
|
|
889
|
+
shouldAvoidPermissionPrompts: false
|
|
890
|
+
};
|
|
891
|
+
let stopReason = "end_turn";
|
|
892
|
+
try {
|
|
893
|
+
for await (const m of query(baseMessages, session.systemPrompt, session.context, canUseTool, {
|
|
894
|
+
options,
|
|
895
|
+
abortController,
|
|
896
|
+
messageId: void 0,
|
|
897
|
+
readFileTimestamps: session.readFileTimestamps,
|
|
898
|
+
setToolJSX: () => {
|
|
899
|
+
},
|
|
900
|
+
agentId: "main",
|
|
901
|
+
responseState: session.responseState
|
|
902
|
+
})) {
|
|
903
|
+
if (abortController.signal.aborted) {
|
|
904
|
+
stopReason = "cancelled";
|
|
905
|
+
}
|
|
906
|
+
await this.handleKodeMessage(session, m);
|
|
907
|
+
}
|
|
908
|
+
if (abortController.signal.aborted) stopReason = "cancelled";
|
|
909
|
+
} catch (err) {
|
|
910
|
+
if (abortController.signal.aborted) {
|
|
911
|
+
stopReason = "cancelled";
|
|
912
|
+
} else {
|
|
913
|
+
logError(err);
|
|
914
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
915
|
+
this.sendAgentMessage(session.sessionId, msg);
|
|
916
|
+
stopReason = "end_turn";
|
|
917
|
+
}
|
|
918
|
+
} finally {
|
|
919
|
+
session.activeAbortController = null;
|
|
920
|
+
persistAcpSessionToDisk(session);
|
|
921
|
+
}
|
|
922
|
+
return { stopReason };
|
|
923
|
+
}
|
|
924
|
+
async handleKodeMessage(session, m) {
|
|
925
|
+
if (!m || typeof m !== "object") return;
|
|
926
|
+
if (m.type === "assistant") {
|
|
927
|
+
session.messages.push(m);
|
|
928
|
+
const blocks = Array.isArray(m.message?.content) ? m.message.content : [];
|
|
929
|
+
for (const b of blocks) {
|
|
930
|
+
if (!b || typeof b !== "object") continue;
|
|
931
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
932
|
+
this.sendAgentMessage(session.sessionId, b.text);
|
|
933
|
+
} else if (b.type === "thinking" && typeof b.thinking === "string") {
|
|
934
|
+
this.sendAgentThought(session.sessionId, b.thinking);
|
|
935
|
+
} else if (b.type === "tool_use") {
|
|
936
|
+
const toolUseId = typeof b.id === "string" ? b.id : "";
|
|
937
|
+
const toolName = typeof b.name === "string" ? b.name : "";
|
|
938
|
+
const input = b.input && typeof b.input === "object" && !Array.isArray(b.input) ? b.input : {};
|
|
939
|
+
if (!toolUseId || !toolName) continue;
|
|
940
|
+
const kind = toolKindForName(toolName);
|
|
941
|
+
const title = titleForToolCall(toolName, input);
|
|
942
|
+
session.toolCalls.set(toolUseId, {
|
|
943
|
+
title,
|
|
944
|
+
kind,
|
|
945
|
+
status: "pending",
|
|
946
|
+
rawInput: asJsonObject(input)
|
|
947
|
+
});
|
|
948
|
+
this.peer.sendNotification("session/update", {
|
|
949
|
+
sessionId: session.sessionId,
|
|
950
|
+
update: {
|
|
951
|
+
sessionUpdate: "tool_call",
|
|
952
|
+
toolCallId: toolUseId,
|
|
953
|
+
title,
|
|
954
|
+
kind,
|
|
955
|
+
status: "pending",
|
|
956
|
+
rawInput: asJsonObject(input)
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
if (m.type === "progress") {
|
|
964
|
+
const toolCallId = m.toolUseID;
|
|
965
|
+
const existing = session.toolCalls.get(toolCallId);
|
|
966
|
+
const title = existing?.title ?? "Tool";
|
|
967
|
+
const kind = existing?.kind ?? "other";
|
|
968
|
+
if (!existing || existing.status === "pending") {
|
|
969
|
+
session.toolCalls.set(toolCallId, {
|
|
970
|
+
title,
|
|
971
|
+
kind,
|
|
972
|
+
status: "in_progress",
|
|
973
|
+
rawInput: existing?.rawInput
|
|
974
|
+
});
|
|
975
|
+
this.sendToolCallUpdate(session.sessionId, {
|
|
976
|
+
toolCallId,
|
|
977
|
+
status: "in_progress"
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
const text = extractAssistantText(m.content);
|
|
981
|
+
if (text) {
|
|
982
|
+
this.sendToolCallUpdate(session.sessionId, {
|
|
983
|
+
toolCallId,
|
|
984
|
+
content: [
|
|
985
|
+
{
|
|
986
|
+
type: "content",
|
|
987
|
+
content: { type: "text", text }
|
|
988
|
+
}
|
|
989
|
+
]
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
if (m.type === "user") {
|
|
995
|
+
const toolResults = extractToolResults(m);
|
|
996
|
+
if (toolResults.length === 0) {
|
|
997
|
+
session.messages.push(m);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
for (const tr of toolResults) {
|
|
1001
|
+
const existing = session.toolCalls.get(tr.toolUseId);
|
|
1002
|
+
const title = existing?.title ?? "Tool";
|
|
1003
|
+
const kind = existing?.kind ?? "other";
|
|
1004
|
+
if (!existing || existing.status === "pending") {
|
|
1005
|
+
session.toolCalls.set(tr.toolUseId, {
|
|
1006
|
+
title,
|
|
1007
|
+
kind,
|
|
1008
|
+
status: "in_progress",
|
|
1009
|
+
rawInput: existing?.rawInput
|
|
1010
|
+
});
|
|
1011
|
+
this.sendToolCallUpdate(session.sessionId, {
|
|
1012
|
+
toolCallId: tr.toolUseId,
|
|
1013
|
+
status: "in_progress"
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
const status = tr.isError ? "failed" : "completed";
|
|
1017
|
+
session.toolCalls.set(tr.toolUseId, {
|
|
1018
|
+
title,
|
|
1019
|
+
kind,
|
|
1020
|
+
status,
|
|
1021
|
+
rawInput: existing?.rawInput
|
|
1022
|
+
});
|
|
1023
|
+
const rawOutput = asJsonObject(m.toolUseResult?.data);
|
|
1024
|
+
const content = [];
|
|
1025
|
+
const diffContent = status === "completed" ? this.buildDiffContentForToolResult(session, tr.toolUseId, rawOutput) : null;
|
|
1026
|
+
if (diffContent) content.push(diffContent);
|
|
1027
|
+
if (tr.content) {
|
|
1028
|
+
content.push({ type: "content", content: { type: "text", text: tr.content } });
|
|
1029
|
+
}
|
|
1030
|
+
this.sendToolCallUpdate(session.sessionId, {
|
|
1031
|
+
toolCallId: tr.toolUseId,
|
|
1032
|
+
status,
|
|
1033
|
+
...content.length > 0 ? { content } : {},
|
|
1034
|
+
...rawOutput ? { rawOutput } : {}
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
session.messages.push(m);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
createAcpCanUseTool(session) {
|
|
1042
|
+
const timeoutMs = (() => {
|
|
1043
|
+
const raw = process.env.KODE_ACP_PERMISSION_TIMEOUT_MS;
|
|
1044
|
+
const parsed = raw ? Number(raw) : NaN;
|
|
1045
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 3e4;
|
|
1046
|
+
})();
|
|
1047
|
+
return async (tool, input, toolUseContext, assistantMessage) => {
|
|
1048
|
+
const toolUseId = typeof toolUseContext?.toolUseId === "string" && toolUseContext.toolUseId ? toolUseContext.toolUseId : `call_${nanoid()}`;
|
|
1049
|
+
const base = await hasPermissionsToUseTool(tool, input, toolUseContext, assistantMessage);
|
|
1050
|
+
if (base.result === true) {
|
|
1051
|
+
this.captureFileSnapshotForTool(session, toolUseId, tool.name, input);
|
|
1052
|
+
return base;
|
|
1053
|
+
}
|
|
1054
|
+
const denied = base;
|
|
1055
|
+
if (denied.shouldPromptUser === false) {
|
|
1056
|
+
return { result: false, message: denied.message };
|
|
1057
|
+
}
|
|
1058
|
+
const title = titleForToolCall(tool.name, input);
|
|
1059
|
+
const kind = toolKindForName(tool.name);
|
|
1060
|
+
if (!session.toolCalls.has(toolUseId)) {
|
|
1061
|
+
session.toolCalls.set(toolUseId, { title, kind, status: "pending", rawInput: asJsonObject(input) });
|
|
1062
|
+
this.peer.sendNotification("session/update", {
|
|
1063
|
+
sessionId: session.sessionId,
|
|
1064
|
+
update: {
|
|
1065
|
+
sessionUpdate: "tool_call",
|
|
1066
|
+
toolCallId: toolUseId,
|
|
1067
|
+
title,
|
|
1068
|
+
kind,
|
|
1069
|
+
status: "pending",
|
|
1070
|
+
rawInput: asJsonObject(input)
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
const options = [
|
|
1075
|
+
{ optionId: "allow_once", name: "Allow once", kind: "allow_once" },
|
|
1076
|
+
{ optionId: "reject_once", name: "Reject", kind: "reject_once" }
|
|
1077
|
+
];
|
|
1078
|
+
if (Array.isArray(denied.suggestions) && denied.suggestions.length > 0) {
|
|
1079
|
+
options.splice(1, 0, {
|
|
1080
|
+
optionId: "allow_always",
|
|
1081
|
+
name: "Allow always (remember)",
|
|
1082
|
+
kind: "allow_always"
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
try {
|
|
1086
|
+
const response = await this.peer.sendRequest({
|
|
1087
|
+
method: "session/request_permission",
|
|
1088
|
+
params: {
|
|
1089
|
+
sessionId: session.sessionId,
|
|
1090
|
+
toolCall: {
|
|
1091
|
+
toolCallId: toolUseId,
|
|
1092
|
+
title,
|
|
1093
|
+
kind,
|
|
1094
|
+
status: "pending",
|
|
1095
|
+
content: [
|
|
1096
|
+
{
|
|
1097
|
+
type: "content",
|
|
1098
|
+
content: { type: "text", text: denied.message }
|
|
1099
|
+
}
|
|
1100
|
+
],
|
|
1101
|
+
rawInput: asJsonObject(input)
|
|
1102
|
+
},
|
|
1103
|
+
options
|
|
1104
|
+
},
|
|
1105
|
+
signal: toolUseContext.abortController.signal,
|
|
1106
|
+
timeoutMs
|
|
1107
|
+
});
|
|
1108
|
+
const outcome = response?.outcome;
|
|
1109
|
+
if (!outcome || outcome.outcome === "cancelled") {
|
|
1110
|
+
toolUseContext.abortController.abort();
|
|
1111
|
+
return { result: false, message: denied.message, shouldPromptUser: false };
|
|
1112
|
+
}
|
|
1113
|
+
if (outcome.outcome === "selected" && outcome.optionId === "allow_once") {
|
|
1114
|
+
this.captureFileSnapshotForTool(session, toolUseId, tool.name, input);
|
|
1115
|
+
return { result: true };
|
|
1116
|
+
}
|
|
1117
|
+
if (outcome.outcome === "selected" && outcome.optionId === "allow_always") {
|
|
1118
|
+
const suggestions = Array.isArray(denied.suggestions) ? denied.suggestions : [];
|
|
1119
|
+
if (suggestions.length > 0) {
|
|
1120
|
+
const next = applyToolPermissionContextUpdates(session.toolPermissionContext, suggestions);
|
|
1121
|
+
session.toolPermissionContext = next;
|
|
1122
|
+
if (toolUseContext?.options) toolUseContext.options.toolPermissionContext = next;
|
|
1123
|
+
for (const update of suggestions) {
|
|
1124
|
+
try {
|
|
1125
|
+
persistToolPermissionUpdateToDisk({ update, projectDir: session.cwd });
|
|
1126
|
+
} catch (e) {
|
|
1127
|
+
logError(e);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
this.captureFileSnapshotForTool(session, toolUseId, tool.name, input);
|
|
1132
|
+
return { result: true };
|
|
1133
|
+
}
|
|
1134
|
+
return { result: false, message: denied.message };
|
|
1135
|
+
} catch (e) {
|
|
1136
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1137
|
+
return { result: false, message: `Permission prompt failed: ${msg}`, shouldPromptUser: false };
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
captureFileSnapshotForTool(session, toolUseId, toolName, input) {
|
|
1142
|
+
if (toolName !== "Write" && toolName !== "MultiEdit") return;
|
|
1143
|
+
const filePath = input && typeof input === "object" ? String(input.file_path ?? "") : "";
|
|
1144
|
+
if (!filePath) return;
|
|
1145
|
+
const absPath = isAbsolute(filePath) ? filePath : resolve(session.cwd, filePath);
|
|
1146
|
+
const oldContent = existsSync(absPath) ? readTextFileForDiff(absPath) : "";
|
|
1147
|
+
if (oldContent === null) return;
|
|
1148
|
+
const existing = session.toolCalls.get(toolUseId);
|
|
1149
|
+
if (existing) {
|
|
1150
|
+
existing.fileSnapshot = { path: absPath, content: oldContent };
|
|
1151
|
+
session.toolCalls.set(toolUseId, existing);
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
session.toolCalls.set(toolUseId, {
|
|
1155
|
+
title: toolName,
|
|
1156
|
+
kind: toolKindForName(toolName),
|
|
1157
|
+
status: "pending",
|
|
1158
|
+
rawInput: asJsonObject(input),
|
|
1159
|
+
fileSnapshot: { path: absPath, content: oldContent }
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
buildDiffContentForToolResult(session, toolUseId, rawOutput) {
|
|
1163
|
+
const existing = session.toolCalls.get(toolUseId);
|
|
1164
|
+
if (!existing || existing.kind !== "edit") return null;
|
|
1165
|
+
const inputFilePath = typeof existing.rawInput?.file_path === "string" ? existing.rawInput.file_path : rawOutput && typeof rawOutput.filePath === "string" ? String(rawOutput.filePath) : "";
|
|
1166
|
+
if (!inputFilePath) return null;
|
|
1167
|
+
const absPath = isAbsolute(inputFilePath) ? inputFilePath : resolve(session.cwd, inputFilePath);
|
|
1168
|
+
const oldText = rawOutput && typeof rawOutput.originalFile === "string" ? String(rawOutput.originalFile) : existing.fileSnapshot && existing.fileSnapshot.path === absPath ? existing.fileSnapshot.content : void 0;
|
|
1169
|
+
if (oldText === void 0) return null;
|
|
1170
|
+
const newTextFromDisk = readTextFileForDiff(absPath);
|
|
1171
|
+
const newTextFromOutput = rawOutput && typeof rawOutput.content === "string" ? String(rawOutput.content) : null;
|
|
1172
|
+
const newText = newTextFromDisk ?? newTextFromOutput;
|
|
1173
|
+
if (newText === null) return null;
|
|
1174
|
+
return {
|
|
1175
|
+
type: "diff",
|
|
1176
|
+
path: absPath,
|
|
1177
|
+
oldText: truncateDiffText(oldText),
|
|
1178
|
+
newText: truncateDiffText(newText)
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
replayConversation(session) {
|
|
1182
|
+
session.toolCalls.clear();
|
|
1183
|
+
for (const m of session.messages) {
|
|
1184
|
+
if (!m || typeof m !== "object") continue;
|
|
1185
|
+
if (m.type === "assistant") {
|
|
1186
|
+
const blocks = Array.isArray(m.message?.content) ? m.message.content : [];
|
|
1187
|
+
for (const b of blocks) {
|
|
1188
|
+
if (!b || typeof b !== "object") continue;
|
|
1189
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
1190
|
+
this.sendAgentMessage(session.sessionId, b.text);
|
|
1191
|
+
} else if (b.type === "thinking" && typeof b.thinking === "string") {
|
|
1192
|
+
this.sendAgentThought(session.sessionId, b.thinking);
|
|
1193
|
+
} else if (b.type === "tool_use") {
|
|
1194
|
+
const toolUseId = typeof b.id === "string" ? b.id : "";
|
|
1195
|
+
const toolName = typeof b.name === "string" ? b.name : "";
|
|
1196
|
+
const input = b.input && typeof b.input === "object" && !Array.isArray(b.input) ? b.input : {};
|
|
1197
|
+
if (!toolUseId || !toolName) continue;
|
|
1198
|
+
if (!session.toolCalls.has(toolUseId)) {
|
|
1199
|
+
const kind = toolKindForName(toolName);
|
|
1200
|
+
const title = titleForToolCall(toolName, input);
|
|
1201
|
+
session.toolCalls.set(toolUseId, {
|
|
1202
|
+
title,
|
|
1203
|
+
kind,
|
|
1204
|
+
status: "pending",
|
|
1205
|
+
rawInput: asJsonObject(input)
|
|
1206
|
+
});
|
|
1207
|
+
this.peer.sendNotification("session/update", {
|
|
1208
|
+
sessionId: session.sessionId,
|
|
1209
|
+
update: {
|
|
1210
|
+
sessionUpdate: "tool_call",
|
|
1211
|
+
toolCallId: toolUseId,
|
|
1212
|
+
title,
|
|
1213
|
+
kind,
|
|
1214
|
+
status: "pending",
|
|
1215
|
+
rawInput: asJsonObject(input)
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
if (m.type === "user") {
|
|
1224
|
+
const content = m?.message?.content;
|
|
1225
|
+
if (typeof content === "string" && content.trim()) {
|
|
1226
|
+
this.sendUserMessage(session.sessionId, content);
|
|
1227
|
+
}
|
|
1228
|
+
const toolResults = extractToolResults(m);
|
|
1229
|
+
if (toolResults.length === 0) continue;
|
|
1230
|
+
for (const tr of toolResults) {
|
|
1231
|
+
const existing = session.toolCalls.get(tr.toolUseId);
|
|
1232
|
+
const title = existing?.title ?? "Tool";
|
|
1233
|
+
const kind = existing?.kind ?? "other";
|
|
1234
|
+
if (!existing) {
|
|
1235
|
+
session.toolCalls.set(tr.toolUseId, { title, kind, status: "pending" });
|
|
1236
|
+
this.peer.sendNotification("session/update", {
|
|
1237
|
+
sessionId: session.sessionId,
|
|
1238
|
+
update: {
|
|
1239
|
+
sessionUpdate: "tool_call",
|
|
1240
|
+
toolCallId: tr.toolUseId,
|
|
1241
|
+
title,
|
|
1242
|
+
kind,
|
|
1243
|
+
status: "pending"
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
const status = tr.isError ? "failed" : "completed";
|
|
1248
|
+
const contentBlocks = [];
|
|
1249
|
+
if (tr.content) {
|
|
1250
|
+
contentBlocks.push({ type: "content", content: { type: "text", text: tr.content } });
|
|
1251
|
+
}
|
|
1252
|
+
const rawOutput = asJsonObject(m.toolUseResult?.data);
|
|
1253
|
+
this.sendToolCallUpdate(session.sessionId, {
|
|
1254
|
+
toolCallId: tr.toolUseId,
|
|
1255
|
+
status,
|
|
1256
|
+
...contentBlocks.length > 0 ? { content: contentBlocks } : {},
|
|
1257
|
+
...rawOutput ? { rawOutput } : {}
|
|
1258
|
+
});
|
|
1259
|
+
session.toolCalls.set(tr.toolUseId, {
|
|
1260
|
+
title,
|
|
1261
|
+
kind,
|
|
1262
|
+
status,
|
|
1263
|
+
rawInput: existing?.rawInput
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
getModeState(session) {
|
|
1270
|
+
const availableModes = [
|
|
1271
|
+
{ id: "default", name: "Default", description: "Normal permissions (prompt when needed)" },
|
|
1272
|
+
{ id: "acceptEdits", name: "Accept Edits", description: "Auto-approve safe file edits" },
|
|
1273
|
+
{ id: "plan", name: "Plan", description: "Read-only planning mode" },
|
|
1274
|
+
{ id: "dontAsk", name: "Don't Ask", description: "Auto-deny permission prompts" },
|
|
1275
|
+
{ id: "bypassPermissions", name: "Bypass", description: "Bypass permission prompts (dangerous)" }
|
|
1276
|
+
];
|
|
1277
|
+
const currentModeId = availableModes.some((m) => m.id === session.currentModeId) ? session.currentModeId : "default";
|
|
1278
|
+
return { currentModeId, availableModes };
|
|
1279
|
+
}
|
|
1280
|
+
sendAvailableCommands(session) {
|
|
1281
|
+
const availableCommands = session.commands.filter((c) => !c.isHidden).map((c) => ({
|
|
1282
|
+
name: c.userFacingName(),
|
|
1283
|
+
description: c.description,
|
|
1284
|
+
...c.argumentHint ? { input: { hint: c.argumentHint } } : {}
|
|
1285
|
+
}));
|
|
1286
|
+
this.peer.sendNotification("session/update", {
|
|
1287
|
+
sessionId: session.sessionId,
|
|
1288
|
+
update: {
|
|
1289
|
+
sessionUpdate: "available_commands_update",
|
|
1290
|
+
availableCommands
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
sendCurrentMode(session) {
|
|
1295
|
+
this.peer.sendNotification("session/update", {
|
|
1296
|
+
sessionId: session.sessionId,
|
|
1297
|
+
update: {
|
|
1298
|
+
sessionUpdate: "current_mode_update",
|
|
1299
|
+
currentModeId: session.currentModeId
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
sendUserMessage(sessionId, text) {
|
|
1304
|
+
if (!text) return;
|
|
1305
|
+
this.peer.sendNotification("session/update", {
|
|
1306
|
+
sessionId,
|
|
1307
|
+
update: {
|
|
1308
|
+
sessionUpdate: "user_message_chunk",
|
|
1309
|
+
content: { type: "text", text }
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
sendAgentMessage(sessionId, text) {
|
|
1314
|
+
if (!text) return;
|
|
1315
|
+
this.peer.sendNotification("session/update", {
|
|
1316
|
+
sessionId,
|
|
1317
|
+
update: {
|
|
1318
|
+
sessionUpdate: "agent_message_chunk",
|
|
1319
|
+
content: { type: "text", text }
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
sendAgentThought(sessionId, text) {
|
|
1324
|
+
if (!text) return;
|
|
1325
|
+
this.peer.sendNotification("session/update", {
|
|
1326
|
+
sessionId,
|
|
1327
|
+
update: {
|
|
1328
|
+
sessionUpdate: "agent_thought_chunk",
|
|
1329
|
+
content: { type: "text", text }
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
sendToolCallUpdate(sessionId, update) {
|
|
1334
|
+
this.peer.sendNotification("session/update", {
|
|
1335
|
+
sessionId,
|
|
1336
|
+
update: {
|
|
1337
|
+
sessionUpdate: "tool_call_update",
|
|
1338
|
+
...update
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
|
|
1344
|
+
// src/entrypoints/acp.ts
|
|
1345
|
+
initSentry();
|
|
1346
|
+
ensurePackagedRuntimeEnv();
|
|
1347
|
+
var { writeAcpLine } = installStdoutGuard();
|
|
1348
|
+
initDebugLogger();
|
|
1349
|
+
try {
|
|
1350
|
+
enableConfigs();
|
|
1351
|
+
} catch (e) {
|
|
1352
|
+
logError(e);
|
|
1353
|
+
}
|
|
1354
|
+
var peer = new JsonRpcPeer();
|
|
1355
|
+
new KodeAcpAgent(peer);
|
|
1356
|
+
var transport = new StdioTransport(peer, { writeLine: writeAcpLine });
|
|
1357
|
+
transport.start();
|