adhdev 0.1.50 → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -4510,16 +4510,16 @@ var init_daemon_commands = __esm({
|
|
|
4510
4510
|
async handleReadChat(args) {
|
|
4511
4511
|
const provider2 = this.getProvider();
|
|
4512
4512
|
const _log = (msg) => console.log(`[read_chat] ${msg}`);
|
|
4513
|
-
if (provider2?.category === "cli") {
|
|
4513
|
+
if (provider2?.category === "cli" || provider2?.category === "acp") {
|
|
4514
4514
|
const adapter = this.getCliAdapter(provider2.type);
|
|
4515
4515
|
if (adapter) {
|
|
4516
|
-
_log(
|
|
4516
|
+
_log(`${provider2.category} adapter: ${adapter.cliType}`);
|
|
4517
4517
|
const status = adapter.getStatus?.();
|
|
4518
4518
|
if (status) {
|
|
4519
4519
|
return { success: true, messages: status.messages || [], status: status.status, activeModal: status.activeModal };
|
|
4520
4520
|
}
|
|
4521
4521
|
}
|
|
4522
|
-
return { success: false, error:
|
|
4522
|
+
return { success: false, error: `${provider2.category} adapter not found` };
|
|
4523
4523
|
}
|
|
4524
4524
|
if (provider2?.category === "extension") {
|
|
4525
4525
|
try {
|
|
@@ -4575,15 +4575,15 @@ var init_daemon_commands = __esm({
|
|
|
4575
4575
|
if (!text) return { success: false, error: "text required" };
|
|
4576
4576
|
const _log = (msg) => console.log(`[send_chat] ${msg}`);
|
|
4577
4577
|
const provider2 = this.getProvider();
|
|
4578
|
-
if (provider2?.category === "cli") {
|
|
4578
|
+
if (provider2?.category === "cli" || provider2?.category === "acp") {
|
|
4579
4579
|
const adapter = this.getCliAdapter(provider2.type);
|
|
4580
4580
|
if (adapter) {
|
|
4581
|
-
_log(
|
|
4581
|
+
_log(`${provider2.category} adapter: ${adapter.cliType}`);
|
|
4582
4582
|
try {
|
|
4583
4583
|
await adapter.sendMessage(text);
|
|
4584
|
-
return { success: true, sent: true, method:
|
|
4584
|
+
return { success: true, sent: true, method: `${provider2.category}-adapter`, targetAgent: adapter.cliType };
|
|
4585
4585
|
} catch (e) {
|
|
4586
|
-
return { success: false, error:
|
|
4586
|
+
return { success: false, error: `${provider2.category} send failed: ${e.message}` };
|
|
4587
4587
|
}
|
|
4588
4588
|
}
|
|
4589
4589
|
}
|
|
@@ -6917,18 +6917,545 @@ var init_cli_provider_instance = __esm({
|
|
|
6917
6917
|
}
|
|
6918
6918
|
});
|
|
6919
6919
|
|
|
6920
|
+
// src/providers/acp-provider-instance.ts
|
|
6921
|
+
var path9, crypto, import_child_process6, import_readline, AcpProviderInstance;
|
|
6922
|
+
var init_acp_provider_instance = __esm({
|
|
6923
|
+
"src/providers/acp-provider-instance.ts"() {
|
|
6924
|
+
"use strict";
|
|
6925
|
+
path9 = __toESM(require("path"));
|
|
6926
|
+
crypto = __toESM(require("crypto"));
|
|
6927
|
+
import_child_process6 = require("child_process");
|
|
6928
|
+
import_readline = require("readline");
|
|
6929
|
+
init_status_monitor();
|
|
6930
|
+
AcpProviderInstance = class {
|
|
6931
|
+
constructor(provider2, workingDir, cliArgs = []) {
|
|
6932
|
+
this.cliArgs = cliArgs;
|
|
6933
|
+
this.type = provider2.type;
|
|
6934
|
+
this.provider = provider2;
|
|
6935
|
+
this.workingDir = workingDir;
|
|
6936
|
+
this.instanceId = `acp_${provider2.type}_${crypto.createHash("md5").update(require("os").hostname() + provider2.type + workingDir).digest("hex").slice(0, 8)}`;
|
|
6937
|
+
this.monitor = new StatusMonitor();
|
|
6938
|
+
}
|
|
6939
|
+
type;
|
|
6940
|
+
category = "acp";
|
|
6941
|
+
provider;
|
|
6942
|
+
context = null;
|
|
6943
|
+
settings = {};
|
|
6944
|
+
events = [];
|
|
6945
|
+
monitor;
|
|
6946
|
+
// Process
|
|
6947
|
+
process = null;
|
|
6948
|
+
readline = null;
|
|
6949
|
+
requestId = 1;
|
|
6950
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
6951
|
+
// State
|
|
6952
|
+
sessionId = null;
|
|
6953
|
+
messages = [];
|
|
6954
|
+
currentStatus = "starting";
|
|
6955
|
+
lastStatus = "starting";
|
|
6956
|
+
generatingStartedAt = 0;
|
|
6957
|
+
agentCapabilities = {};
|
|
6958
|
+
currentModel;
|
|
6959
|
+
currentMode;
|
|
6960
|
+
activeToolCalls = [];
|
|
6961
|
+
stopReason = null;
|
|
6962
|
+
partialContent = "";
|
|
6963
|
+
// Config
|
|
6964
|
+
workingDir;
|
|
6965
|
+
instanceId;
|
|
6966
|
+
// ─── Lifecycle ─────────────────────────────────
|
|
6967
|
+
async init(context) {
|
|
6968
|
+
this.context = context;
|
|
6969
|
+
this.settings = context.settings || {};
|
|
6970
|
+
this.monitor.updateConfig({
|
|
6971
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
6972
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
6973
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
6974
|
+
});
|
|
6975
|
+
await this.spawnAgent();
|
|
6976
|
+
}
|
|
6977
|
+
async onTick() {
|
|
6978
|
+
if (this.process && this.process.exitCode !== null) {
|
|
6979
|
+
this.currentStatus = "stopped";
|
|
6980
|
+
this.detectStatusTransition();
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
getState() {
|
|
6984
|
+
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
6985
|
+
const recentMessages = this.messages.slice(-50).map((m) => ({
|
|
6986
|
+
role: m.role,
|
|
6987
|
+
content: m.content.length > 2e3 ? m.content.slice(0, 2e3) + "\n... (truncated)" : m.content,
|
|
6988
|
+
timestamp: m.timestamp
|
|
6989
|
+
}));
|
|
6990
|
+
if (this.currentStatus === "generating" && this.partialContent) {
|
|
6991
|
+
const cleaned = this.partialContent.trim();
|
|
6992
|
+
if (cleaned) {
|
|
6993
|
+
recentMessages.push({
|
|
6994
|
+
role: "assistant",
|
|
6995
|
+
content: (cleaned.length > 2e3 ? cleaned.slice(0, 2e3) + "..." : cleaned) + "...",
|
|
6996
|
+
timestamp: Date.now()
|
|
6997
|
+
});
|
|
6998
|
+
}
|
|
6999
|
+
}
|
|
7000
|
+
return {
|
|
7001
|
+
type: this.type,
|
|
7002
|
+
name: this.provider.name,
|
|
7003
|
+
category: "acp",
|
|
7004
|
+
status: this.currentStatus,
|
|
7005
|
+
mode: "chat",
|
|
7006
|
+
activeChat: {
|
|
7007
|
+
id: this.sessionId || `${this.type}_${this.workingDir}`,
|
|
7008
|
+
title: `${this.provider.name} \xB7 ${dirName}`,
|
|
7009
|
+
status: this.currentStatus,
|
|
7010
|
+
messages: recentMessages,
|
|
7011
|
+
activeModal: this.currentStatus === "waiting_approval" ? {
|
|
7012
|
+
message: this.activeToolCalls.find((t) => t.status === "running")?.name || "Permission requested",
|
|
7013
|
+
buttons: ["Approve", "Reject"]
|
|
7014
|
+
} : null,
|
|
7015
|
+
inputContent: ""
|
|
7016
|
+
},
|
|
7017
|
+
workingDir: this.workingDir,
|
|
7018
|
+
currentModel: this.currentModel,
|
|
7019
|
+
currentPlan: this.currentMode,
|
|
7020
|
+
instanceId: this.instanceId,
|
|
7021
|
+
lastUpdated: Date.now(),
|
|
7022
|
+
settings: this.settings,
|
|
7023
|
+
pendingEvents: this.flushEvents()
|
|
7024
|
+
};
|
|
7025
|
+
}
|
|
7026
|
+
onEvent(event, data) {
|
|
7027
|
+
if (event === "send_message" && data?.text) {
|
|
7028
|
+
this.sendPrompt(data.text).catch(
|
|
7029
|
+
(e) => console.warn(`[ACP:${this.type}] sendPrompt error:`, e?.message)
|
|
7030
|
+
);
|
|
7031
|
+
} else if (event === "resolve_action") {
|
|
7032
|
+
const action = data?.action || "approve";
|
|
7033
|
+
this.resolvePermission(action === "approve" || action === "accept").catch((e) => console.warn(`[ACP:${this.type}] resolvePermission error:`, e?.message));
|
|
7034
|
+
} else if (event === "cancel") {
|
|
7035
|
+
this.cancelSession().catch(
|
|
7036
|
+
(e) => console.warn(`[ACP:${this.type}] cancel error:`, e?.message)
|
|
7037
|
+
);
|
|
7038
|
+
}
|
|
7039
|
+
}
|
|
7040
|
+
dispose() {
|
|
7041
|
+
if (this.process) {
|
|
7042
|
+
try {
|
|
7043
|
+
this.process.kill("SIGTERM");
|
|
7044
|
+
} catch {
|
|
7045
|
+
}
|
|
7046
|
+
this.process = null;
|
|
7047
|
+
}
|
|
7048
|
+
if (this.readline) {
|
|
7049
|
+
this.readline.close();
|
|
7050
|
+
this.readline = null;
|
|
7051
|
+
}
|
|
7052
|
+
for (const [, req] of this.pendingRequests) {
|
|
7053
|
+
clearTimeout(req.timer);
|
|
7054
|
+
req.reject(new Error("Instance disposed"));
|
|
7055
|
+
}
|
|
7056
|
+
this.pendingRequests.clear();
|
|
7057
|
+
this.monitor.reset();
|
|
7058
|
+
}
|
|
7059
|
+
// ─── ACP Process Management ──────────────────────
|
|
7060
|
+
async spawnAgent() {
|
|
7061
|
+
const spawnConfig = this.provider.spawn;
|
|
7062
|
+
if (!spawnConfig) {
|
|
7063
|
+
throw new Error(`[ACP:${this.type}] No spawn config defined`);
|
|
7064
|
+
}
|
|
7065
|
+
const command = spawnConfig.command;
|
|
7066
|
+
const args = [...spawnConfig.args || [], ...this.cliArgs];
|
|
7067
|
+
const env = { ...process.env, ...spawnConfig.env || {} };
|
|
7068
|
+
console.log(`[ACP:${this.type}] Spawning: ${command} ${args.join(" ")} in ${this.workingDir}`);
|
|
7069
|
+
this.process = (0, import_child_process6.spawn)(command, args, {
|
|
7070
|
+
cwd: this.workingDir,
|
|
7071
|
+
env,
|
|
7072
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
7073
|
+
shell: spawnConfig.shell || false
|
|
7074
|
+
});
|
|
7075
|
+
this.readline = (0, import_readline.createInterface)({ input: this.process.stdout });
|
|
7076
|
+
this.readline.on("line", (line) => {
|
|
7077
|
+
const trimmed = line.trim();
|
|
7078
|
+
if (!trimmed) return;
|
|
7079
|
+
try {
|
|
7080
|
+
const msg = JSON.parse(trimmed);
|
|
7081
|
+
this.handleMessage(msg);
|
|
7082
|
+
} catch (e) {
|
|
7083
|
+
}
|
|
7084
|
+
});
|
|
7085
|
+
this.process.stderr?.on("data", (data) => {
|
|
7086
|
+
const text = data.toString().trim();
|
|
7087
|
+
if (text) {
|
|
7088
|
+
console.log(`[ACP:${this.type}:stderr] ${text.slice(0, 200)}`);
|
|
7089
|
+
}
|
|
7090
|
+
});
|
|
7091
|
+
this.process.on("exit", (code, signal) => {
|
|
7092
|
+
console.log(`[ACP:${this.type}] Process exited: code=${code} signal=${signal}`);
|
|
7093
|
+
this.currentStatus = "stopped";
|
|
7094
|
+
this.detectStatusTransition();
|
|
7095
|
+
});
|
|
7096
|
+
this.process.on("error", (err) => {
|
|
7097
|
+
console.error(`[ACP:${this.type}] Process error:`, err.message);
|
|
7098
|
+
this.currentStatus = "error";
|
|
7099
|
+
this.detectStatusTransition();
|
|
7100
|
+
});
|
|
7101
|
+
await this.initialize();
|
|
7102
|
+
}
|
|
7103
|
+
// ─── ACP Protocol ────────────────────────────────
|
|
7104
|
+
async initialize() {
|
|
7105
|
+
try {
|
|
7106
|
+
const result = await this.sendRequest("initialize", {
|
|
7107
|
+
protocolVersion: "0.1.0",
|
|
7108
|
+
clientInfo: {
|
|
7109
|
+
name: "adhdev",
|
|
7110
|
+
version: "0.1.0"
|
|
7111
|
+
},
|
|
7112
|
+
clientCapabilities: {
|
|
7113
|
+
roots: true
|
|
7114
|
+
},
|
|
7115
|
+
workspaceFolders: [{
|
|
7116
|
+
name: path9.basename(this.workingDir),
|
|
7117
|
+
uri: `file://${this.workingDir}`
|
|
7118
|
+
}]
|
|
7119
|
+
});
|
|
7120
|
+
this.agentCapabilities = result?.capabilities || {};
|
|
7121
|
+
console.log(`[ACP:${this.type}] Initialized. Agent capabilities:`, JSON.stringify(this.agentCapabilities));
|
|
7122
|
+
this.sendNotification("initialized");
|
|
7123
|
+
await this.createSession();
|
|
7124
|
+
} catch (e) {
|
|
7125
|
+
console.error(`[ACP:${this.type}] Initialize failed:`, e?.message);
|
|
7126
|
+
this.currentStatus = "error";
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
async createSession() {
|
|
7130
|
+
try {
|
|
7131
|
+
const result = await this.sendRequest("session/new", {
|
|
7132
|
+
cwd: this.workingDir,
|
|
7133
|
+
mcpServers: []
|
|
7134
|
+
});
|
|
7135
|
+
this.sessionId = result?.sessionId || null;
|
|
7136
|
+
this.currentStatus = "idle";
|
|
7137
|
+
this.messages = [];
|
|
7138
|
+
if (result?.models?.currentModelId) {
|
|
7139
|
+
this.currentModel = result.models.currentModelId;
|
|
7140
|
+
}
|
|
7141
|
+
console.log(`[ACP:${this.type}] Session created: ${this.sessionId}${this.currentModel ? ` (model: ${this.currentModel})` : ""}`);
|
|
7142
|
+
} catch (e) {
|
|
7143
|
+
console.warn(`[ACP:${this.type}] session/new failed:`, e?.message);
|
|
7144
|
+
this.currentStatus = "idle";
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
async sendPrompt(text) {
|
|
7148
|
+
this.messages.push({
|
|
7149
|
+
role: "user",
|
|
7150
|
+
content: text,
|
|
7151
|
+
timestamp: Date.now()
|
|
7152
|
+
});
|
|
7153
|
+
this.currentStatus = "generating";
|
|
7154
|
+
this.partialContent = "";
|
|
7155
|
+
this.detectStatusTransition();
|
|
7156
|
+
try {
|
|
7157
|
+
const result = await this.sendRequest("session/prompt", {
|
|
7158
|
+
sessionId: this.sessionId,
|
|
7159
|
+
prompt: [{ type: "text", text }]
|
|
7160
|
+
}, 3e5);
|
|
7161
|
+
if (result?.stopReason) {
|
|
7162
|
+
this.stopReason = result.stopReason;
|
|
7163
|
+
}
|
|
7164
|
+
if (this.partialContent.trim()) {
|
|
7165
|
+
this.messages.push({
|
|
7166
|
+
role: "assistant",
|
|
7167
|
+
content: this.partialContent.trim(),
|
|
7168
|
+
timestamp: Date.now()
|
|
7169
|
+
});
|
|
7170
|
+
this.partialContent = "";
|
|
7171
|
+
}
|
|
7172
|
+
this.currentStatus = "idle";
|
|
7173
|
+
this.detectStatusTransition();
|
|
7174
|
+
} catch (e) {
|
|
7175
|
+
console.warn(`[ACP:${this.type}] prompt error:`, e?.message);
|
|
7176
|
+
if (this.partialContent.trim()) {
|
|
7177
|
+
this.messages.push({
|
|
7178
|
+
role: "assistant",
|
|
7179
|
+
content: this.partialContent.trim(),
|
|
7180
|
+
timestamp: Date.now()
|
|
7181
|
+
});
|
|
7182
|
+
this.partialContent = "";
|
|
7183
|
+
}
|
|
7184
|
+
this.currentStatus = "idle";
|
|
7185
|
+
this.detectStatusTransition();
|
|
7186
|
+
}
|
|
7187
|
+
}
|
|
7188
|
+
async cancelSession() {
|
|
7189
|
+
this.sendNotification("session/cancel", {
|
|
7190
|
+
sessionId: this.sessionId
|
|
7191
|
+
});
|
|
7192
|
+
this.currentStatus = "idle";
|
|
7193
|
+
this.detectStatusTransition();
|
|
7194
|
+
}
|
|
7195
|
+
permissionResolvers = [];
|
|
7196
|
+
async resolvePermission(approved) {
|
|
7197
|
+
const resolver = this.permissionResolvers.shift();
|
|
7198
|
+
if (resolver) {
|
|
7199
|
+
resolver(approved);
|
|
7200
|
+
}
|
|
7201
|
+
if (this.currentStatus === "waiting_approval") {
|
|
7202
|
+
this.currentStatus = "generating";
|
|
7203
|
+
this.detectStatusTransition();
|
|
7204
|
+
}
|
|
7205
|
+
}
|
|
7206
|
+
// ─── JSON-RPC Transport ──────────────────────────
|
|
7207
|
+
sendRequest(method, params, timeoutMs = 3e4) {
|
|
7208
|
+
return new Promise((resolve6, reject) => {
|
|
7209
|
+
if (!this.process?.stdin?.writable) {
|
|
7210
|
+
reject(new Error("Process stdin not writable"));
|
|
7211
|
+
return;
|
|
7212
|
+
}
|
|
7213
|
+
const id = this.requestId++;
|
|
7214
|
+
const msg = {
|
|
7215
|
+
jsonrpc: "2.0",
|
|
7216
|
+
id,
|
|
7217
|
+
method,
|
|
7218
|
+
params
|
|
7219
|
+
};
|
|
7220
|
+
const timer = setTimeout(() => {
|
|
7221
|
+
this.pendingRequests.delete(id);
|
|
7222
|
+
reject(new Error(`Request ${method} timed out after ${timeoutMs}ms`));
|
|
7223
|
+
}, timeoutMs);
|
|
7224
|
+
this.pendingRequests.set(id, { resolve: resolve6, reject, timer });
|
|
7225
|
+
const line = JSON.stringify(msg) + "\n";
|
|
7226
|
+
this.process.stdin.write(line);
|
|
7227
|
+
});
|
|
7228
|
+
}
|
|
7229
|
+
sendNotification(method, params) {
|
|
7230
|
+
if (!this.process?.stdin?.writable) return;
|
|
7231
|
+
const msg = {
|
|
7232
|
+
jsonrpc: "2.0",
|
|
7233
|
+
method,
|
|
7234
|
+
...params && { params }
|
|
7235
|
+
};
|
|
7236
|
+
const line = JSON.stringify(msg) + "\n";
|
|
7237
|
+
this.process.stdin.write(line);
|
|
7238
|
+
}
|
|
7239
|
+
handleMessage(msg) {
|
|
7240
|
+
if ("id" in msg && msg.id !== void 0 && !("method" in msg && msg.method)) {
|
|
7241
|
+
const pending = this.pendingRequests.get(msg.id);
|
|
7242
|
+
if (pending) {
|
|
7243
|
+
clearTimeout(pending.timer);
|
|
7244
|
+
this.pendingRequests.delete(msg.id);
|
|
7245
|
+
const resp = msg;
|
|
7246
|
+
if (resp.error) {
|
|
7247
|
+
pending.reject(new Error(`${resp.error.message} (${resp.error.code})`));
|
|
7248
|
+
} else {
|
|
7249
|
+
pending.resolve(resp.result);
|
|
7250
|
+
}
|
|
7251
|
+
}
|
|
7252
|
+
return;
|
|
7253
|
+
}
|
|
7254
|
+
if ("method" in msg) {
|
|
7255
|
+
this.handleNotification(msg);
|
|
7256
|
+
if ("id" in msg && msg.id !== void 0) {
|
|
7257
|
+
this.handleAgentRequest(msg);
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
7261
|
+
handleNotification(msg) {
|
|
7262
|
+
switch (msg.method) {
|
|
7263
|
+
case "session/update": {
|
|
7264
|
+
const params = msg.params;
|
|
7265
|
+
this.handleSessionUpdate(params);
|
|
7266
|
+
break;
|
|
7267
|
+
}
|
|
7268
|
+
default:
|
|
7269
|
+
break;
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
handleAgentRequest(msg) {
|
|
7273
|
+
switch (msg.method) {
|
|
7274
|
+
case "fs/readTextFile":
|
|
7275
|
+
case "fs/writeTextFile": {
|
|
7276
|
+
this.sendResponse(msg.id, null, {
|
|
7277
|
+
code: -32601,
|
|
7278
|
+
message: "File system access not supported by ADHDev client"
|
|
7279
|
+
});
|
|
7280
|
+
break;
|
|
7281
|
+
}
|
|
7282
|
+
case "requestPermission": {
|
|
7283
|
+
this.currentStatus = "waiting_approval";
|
|
7284
|
+
this.detectStatusTransition();
|
|
7285
|
+
const promise = new Promise((resolve6) => {
|
|
7286
|
+
this.permissionResolvers.push(resolve6);
|
|
7287
|
+
setTimeout(() => {
|
|
7288
|
+
const idx = this.permissionResolvers.indexOf(resolve6);
|
|
7289
|
+
if (idx >= 0) {
|
|
7290
|
+
this.permissionResolvers.splice(idx, 1);
|
|
7291
|
+
resolve6(false);
|
|
7292
|
+
}
|
|
7293
|
+
}, 3e5);
|
|
7294
|
+
});
|
|
7295
|
+
promise.then((approved) => {
|
|
7296
|
+
this.sendResponse(msg.id, {
|
|
7297
|
+
outcome: approved ? "approved" : "denied"
|
|
7298
|
+
});
|
|
7299
|
+
});
|
|
7300
|
+
break;
|
|
7301
|
+
}
|
|
7302
|
+
case "terminal/create":
|
|
7303
|
+
case "terminal/output":
|
|
7304
|
+
case "terminal/release":
|
|
7305
|
+
case "terminal/kill":
|
|
7306
|
+
case "terminal/waitForExit": {
|
|
7307
|
+
this.sendResponse(msg.id, null, {
|
|
7308
|
+
code: -32601,
|
|
7309
|
+
message: "Terminal not supported by ADHDev client"
|
|
7310
|
+
});
|
|
7311
|
+
break;
|
|
7312
|
+
}
|
|
7313
|
+
default: {
|
|
7314
|
+
this.sendResponse(msg.id, null, {
|
|
7315
|
+
code: -32601,
|
|
7316
|
+
message: `Method ${msg.method} not supported`
|
|
7317
|
+
});
|
|
7318
|
+
}
|
|
7319
|
+
}
|
|
7320
|
+
}
|
|
7321
|
+
sendResponse(id, result, error) {
|
|
7322
|
+
if (!this.process?.stdin?.writable) return;
|
|
7323
|
+
const msg = {
|
|
7324
|
+
jsonrpc: "2.0",
|
|
7325
|
+
id,
|
|
7326
|
+
...error ? { error } : { result }
|
|
7327
|
+
};
|
|
7328
|
+
const line = JSON.stringify(msg) + "\n";
|
|
7329
|
+
this.process.stdin.write(line);
|
|
7330
|
+
}
|
|
7331
|
+
// ─── ACP session/update 처리 ─────────────────────
|
|
7332
|
+
handleSessionUpdate(params) {
|
|
7333
|
+
if (!params) return;
|
|
7334
|
+
if (params.messageDelta) {
|
|
7335
|
+
const delta = params.messageDelta;
|
|
7336
|
+
if (delta.content) {
|
|
7337
|
+
for (const part of Array.isArray(delta.content) ? delta.content : [delta.content]) {
|
|
7338
|
+
if (part.type === "text" && part.text) {
|
|
7339
|
+
this.partialContent += part.text;
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
}
|
|
7343
|
+
this.currentStatus = "generating";
|
|
7344
|
+
}
|
|
7345
|
+
if (params.message) {
|
|
7346
|
+
const m = params.message;
|
|
7347
|
+
let content = "";
|
|
7348
|
+
if (typeof m.content === "string") {
|
|
7349
|
+
content = m.content;
|
|
7350
|
+
} else if (Array.isArray(m.content)) {
|
|
7351
|
+
content = m.content.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
|
|
7352
|
+
}
|
|
7353
|
+
if (content.trim()) {
|
|
7354
|
+
this.messages.push({
|
|
7355
|
+
role: m.role || "assistant",
|
|
7356
|
+
content: content.trim(),
|
|
7357
|
+
timestamp: Date.now()
|
|
7358
|
+
});
|
|
7359
|
+
this.partialContent = "";
|
|
7360
|
+
}
|
|
7361
|
+
}
|
|
7362
|
+
if (params.toolCallUpdate) {
|
|
7363
|
+
const tc = params.toolCallUpdate;
|
|
7364
|
+
const existing = this.activeToolCalls.find((t) => t.id === tc.id);
|
|
7365
|
+
if (existing) {
|
|
7366
|
+
if (tc.status) existing.status = tc.status;
|
|
7367
|
+
if (tc.output) existing.output = tc.output;
|
|
7368
|
+
} else {
|
|
7369
|
+
this.activeToolCalls.push({
|
|
7370
|
+
id: tc.id || `tc_${Date.now()}`,
|
|
7371
|
+
name: tc.name || "unknown",
|
|
7372
|
+
status: tc.status || "running",
|
|
7373
|
+
input: typeof tc.input === "string" ? tc.input : JSON.stringify(tc.input)
|
|
7374
|
+
});
|
|
7375
|
+
}
|
|
7376
|
+
}
|
|
7377
|
+
if (params.stopReason) {
|
|
7378
|
+
this.stopReason = params.stopReason;
|
|
7379
|
+
if (params.stopReason !== "cancelled") {
|
|
7380
|
+
this.currentStatus = "idle";
|
|
7381
|
+
}
|
|
7382
|
+
this.activeToolCalls = [];
|
|
7383
|
+
this.detectStatusTransition();
|
|
7384
|
+
}
|
|
7385
|
+
if (params.model) {
|
|
7386
|
+
this.currentModel = params.model;
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
// ─── 상태 전이 감지 ────────────────────────────
|
|
7390
|
+
detectStatusTransition() {
|
|
7391
|
+
const now = Date.now();
|
|
7392
|
+
const newStatus = this.currentStatus;
|
|
7393
|
+
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
7394
|
+
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
7395
|
+
if (newStatus !== this.lastStatus) {
|
|
7396
|
+
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
7397
|
+
this.generatingStartedAt = now;
|
|
7398
|
+
this.pushEvent({ event: "agent:generating_started", chatTitle, timestamp: now });
|
|
7399
|
+
} else if (newStatus === "waiting_approval") {
|
|
7400
|
+
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
7401
|
+
this.pushEvent({
|
|
7402
|
+
event: "agent:waiting_approval",
|
|
7403
|
+
chatTitle,
|
|
7404
|
+
timestamp: now,
|
|
7405
|
+
modalMessage: this.activeToolCalls.find((t) => t.status === "running")?.name
|
|
7406
|
+
});
|
|
7407
|
+
} else if (newStatus === "idle" && (this.lastStatus === "generating" || this.lastStatus === "waiting_approval")) {
|
|
7408
|
+
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
|
|
7409
|
+
this.pushEvent({ event: "agent:generating_completed", chatTitle, duration, timestamp: now });
|
|
7410
|
+
this.generatingStartedAt = 0;
|
|
7411
|
+
} else if (newStatus === "stopped") {
|
|
7412
|
+
this.pushEvent({ event: "agent:stopped", chatTitle, timestamp: now });
|
|
7413
|
+
}
|
|
7414
|
+
this.lastStatus = newStatus;
|
|
7415
|
+
}
|
|
7416
|
+
const agentKey = `${this.type}:acp`;
|
|
7417
|
+
const monitorEvents = this.monitor.check(agentKey, newStatus, now);
|
|
7418
|
+
for (const me of monitorEvents) {
|
|
7419
|
+
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
7420
|
+
}
|
|
7421
|
+
}
|
|
7422
|
+
pushEvent(event) {
|
|
7423
|
+
this.events.push(event);
|
|
7424
|
+
if (this.events.length > 50) this.events = this.events.slice(-50);
|
|
7425
|
+
}
|
|
7426
|
+
flushEvents() {
|
|
7427
|
+
const events = [...this.events];
|
|
7428
|
+
this.events = [];
|
|
7429
|
+
return events;
|
|
7430
|
+
}
|
|
7431
|
+
// ─── 외부 접근 ─────────────────────────────────
|
|
7432
|
+
get cliType() {
|
|
7433
|
+
return this.type;
|
|
7434
|
+
}
|
|
7435
|
+
get cliName() {
|
|
7436
|
+
return this.provider.name;
|
|
7437
|
+
}
|
|
7438
|
+
/** ACP Agent capabilities (initialize 후 사용 가능) */
|
|
7439
|
+
getCapabilities() {
|
|
7440
|
+
return this.agentCapabilities;
|
|
7441
|
+
}
|
|
7442
|
+
};
|
|
7443
|
+
}
|
|
7444
|
+
});
|
|
7445
|
+
|
|
6920
7446
|
// src/daemon-cli.ts
|
|
6921
|
-
var os10,
|
|
7447
|
+
var os10, path10, import_chalk, DaemonCliManager;
|
|
6922
7448
|
var init_daemon_cli = __esm({
|
|
6923
7449
|
"src/daemon-cli.ts"() {
|
|
6924
7450
|
"use strict";
|
|
6925
7451
|
os10 = __toESM(require("os"));
|
|
6926
|
-
|
|
7452
|
+
path10 = __toESM(require("path"));
|
|
6927
7453
|
import_chalk = __toESM(require("chalk"));
|
|
6928
7454
|
init_provider_cli_adapter();
|
|
6929
7455
|
init_cli_detector();
|
|
6930
7456
|
init_config();
|
|
6931
7457
|
init_cli_provider_instance();
|
|
7458
|
+
init_acp_provider_instance();
|
|
6932
7459
|
DaemonCliManager = class {
|
|
6933
7460
|
adapters = /* @__PURE__ */ new Map();
|
|
6934
7461
|
deps;
|
|
@@ -6963,20 +7490,54 @@ var init_daemon_cli = __esm({
|
|
|
6963
7490
|
// ─── 세션 시작/중지 ──────────────────────────────
|
|
6964
7491
|
async startSession(cliType, workingDir, cliArgs) {
|
|
6965
7492
|
const trimmed = (workingDir || os10.homedir()).trim();
|
|
6966
|
-
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) :
|
|
6967
|
-
const cliInfo = await detectCLI(cliType);
|
|
6968
|
-
if (!cliInfo) throw new Error(`${cliType} not found`);
|
|
6969
|
-
const key = this.getCliKey(cliType, resolvedDir);
|
|
6970
|
-
if (this.adapters.has(key)) {
|
|
6971
|
-
console.log(import_chalk.default.yellow(` \u26A1 CLI ${cliType} already running in ${resolvedDir}`));
|
|
6972
|
-
return;
|
|
6973
|
-
}
|
|
7493
|
+
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path10.resolve(trimmed);
|
|
6974
7494
|
const typeMap = {
|
|
6975
7495
|
"claude-code": "claude-cli",
|
|
6976
7496
|
"codex": "codex-cli"
|
|
6977
7497
|
};
|
|
6978
7498
|
const normalizedType = typeMap[cliType] || cliType;
|
|
6979
7499
|
const provider2 = this.providerLoader.get(normalizedType);
|
|
7500
|
+
const key = this.getCliKey(normalizedType, resolvedDir);
|
|
7501
|
+
if (this.adapters.has(key)) {
|
|
7502
|
+
console.log(import_chalk.default.yellow(` \u26A1 ${cliType} already running in ${resolvedDir}`));
|
|
7503
|
+
return;
|
|
7504
|
+
}
|
|
7505
|
+
if (provider2 && provider2.category === "acp") {
|
|
7506
|
+
const instanceManager2 = this.deps.getInstanceManager();
|
|
7507
|
+
if (!instanceManager2) throw new Error("InstanceManager not available");
|
|
7508
|
+
console.log(import_chalk.default.cyan(` \u{1F50C} Starting ACP agent: ${provider2.name} (${provider2.type}) in ${resolvedDir}`));
|
|
7509
|
+
const acpInstance = new AcpProviderInstance(provider2, resolvedDir, cliArgs);
|
|
7510
|
+
await instanceManager2.addInstance(key, acpInstance, {
|
|
7511
|
+
settings: this.providerLoader.getSettings(normalizedType)
|
|
7512
|
+
});
|
|
7513
|
+
this.adapters.set(key, {
|
|
7514
|
+
cliType: normalizedType,
|
|
7515
|
+
workingDir: resolvedDir,
|
|
7516
|
+
spawn: async () => {
|
|
7517
|
+
},
|
|
7518
|
+
shutdown: () => {
|
|
7519
|
+
instanceManager2.removeInstance(key);
|
|
7520
|
+
},
|
|
7521
|
+
sendMessage: async (text) => {
|
|
7522
|
+
acpInstance.onEvent("send_message", { text });
|
|
7523
|
+
},
|
|
7524
|
+
getStatus: () => ({ status: "idle", messages: [], activeModal: null }),
|
|
7525
|
+
setOnStatusChange: () => {
|
|
7526
|
+
},
|
|
7527
|
+
setOnPtyData: () => {
|
|
7528
|
+
}
|
|
7529
|
+
});
|
|
7530
|
+
console.log(import_chalk.default.green(` \u2713 ACP agent started: ${provider2.name} in ${resolvedDir}`));
|
|
7531
|
+
try {
|
|
7532
|
+
addCliHistory({ cliType: normalizedType, dir: resolvedDir, cliArgs });
|
|
7533
|
+
} catch (e) {
|
|
7534
|
+
console.warn("[ACP] History save failed:", e?.message);
|
|
7535
|
+
}
|
|
7536
|
+
this.deps.onStatusChange();
|
|
7537
|
+
return;
|
|
7538
|
+
}
|
|
7539
|
+
const cliInfo = await detectCLI(cliType);
|
|
7540
|
+
if (!cliInfo) throw new Error(`${cliType} not found`);
|
|
6980
7541
|
console.log(import_chalk.default.yellow(` \u26A1 Starting CLI ${cliType} in ${resolvedDir}...`));
|
|
6981
7542
|
if (provider2) {
|
|
6982
7543
|
console.log(import_chalk.default.cyan(` \u{1F4E6} Using provider: ${provider2.name} (${provider2.type})`));
|
|
@@ -7408,12 +7969,12 @@ var init_extension_provider_instance = __esm({
|
|
|
7408
7969
|
});
|
|
7409
7970
|
|
|
7410
7971
|
// src/providers/ide-provider-instance.ts
|
|
7411
|
-
var os11,
|
|
7972
|
+
var os11, crypto2, IdeProviderInstance;
|
|
7412
7973
|
var init_ide_provider_instance = __esm({
|
|
7413
7974
|
"src/providers/ide-provider-instance.ts"() {
|
|
7414
7975
|
"use strict";
|
|
7415
7976
|
os11 = __toESM(require("os"));
|
|
7416
|
-
|
|
7977
|
+
crypto2 = __toESM(require("crypto"));
|
|
7417
7978
|
init_extension_provider_instance();
|
|
7418
7979
|
init_status_monitor();
|
|
7419
7980
|
IdeProviderInstance = class {
|
|
@@ -7441,7 +8002,7 @@ var init_ide_provider_instance = __esm({
|
|
|
7441
8002
|
constructor(provider2, instanceKey) {
|
|
7442
8003
|
this.type = provider2.type;
|
|
7443
8004
|
this.provider = provider2;
|
|
7444
|
-
this.instanceId = instanceKey ? `${instanceKey}_${
|
|
8005
|
+
this.instanceId = instanceKey ? `${instanceKey}_${crypto2.createHash("md5").update(os11.hostname() + instanceKey).digest("hex").slice(0, 8)}` : `${provider2.type}_${crypto2.createHash("md5").update(os11.hostname() + provider2.type).digest("hex").slice(0, 8)}`;
|
|
7445
8006
|
this.monitor = new StatusMonitor();
|
|
7446
8007
|
}
|
|
7447
8008
|
// ─── Lifecycle ─────────────────────────────────
|
|
@@ -7680,9 +8241,9 @@ __export(adhdev_daemon_exports, {
|
|
|
7680
8241
|
stopDaemon: () => stopDaemon
|
|
7681
8242
|
});
|
|
7682
8243
|
function getDaemonPidFile() {
|
|
7683
|
-
const dir =
|
|
8244
|
+
const dir = path11.join(os12.homedir(), ".adhdev");
|
|
7684
8245
|
if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
|
|
7685
|
-
return
|
|
8246
|
+
return path11.join(dir, "daemon.pid");
|
|
7686
8247
|
}
|
|
7687
8248
|
function writeDaemonPid(pid) {
|
|
7688
8249
|
fs6.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
|
|
@@ -7718,7 +8279,7 @@ function stopDaemon() {
|
|
|
7718
8279
|
return false;
|
|
7719
8280
|
}
|
|
7720
8281
|
}
|
|
7721
|
-
var os12, fs6,
|
|
8282
|
+
var os12, fs6, path11, crypto3, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
|
|
7722
8283
|
var init_adhdev_daemon = __esm({
|
|
7723
8284
|
"src/adhdev-daemon.ts"() {
|
|
7724
8285
|
"use strict";
|
|
@@ -7741,8 +8302,8 @@ var init_adhdev_daemon = __esm({
|
|
|
7741
8302
|
init_daemon_logger();
|
|
7742
8303
|
os12 = __toESM(require("os"));
|
|
7743
8304
|
fs6 = __toESM(require("fs"));
|
|
7744
|
-
|
|
7745
|
-
|
|
8305
|
+
path11 = __toESM(require("path"));
|
|
8306
|
+
crypto3 = __toESM(require("crypto"));
|
|
7746
8307
|
import_chalk2 = __toESM(require("chalk"));
|
|
7747
8308
|
DANGEROUS_PATTERNS = [
|
|
7748
8309
|
/\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
|
|
@@ -7877,7 +8438,7 @@ var init_adhdev_daemon = __esm({
|
|
|
7877
8438
|
this.commandHandler.setAgentStreamManager(this.agentStreamManager);
|
|
7878
8439
|
this.startAgentStreamPolling();
|
|
7879
8440
|
const machineId = os12.hostname().replace(/[^a-zA-Z0-9]/g, "_");
|
|
7880
|
-
const machineHash =
|
|
8441
|
+
const machineHash = crypto3.createHash("md5").update(os12.hostname() + os12.homedir()).digest("hex").slice(0, 8);
|
|
7881
8442
|
const instanceId = `daemon_${machineId}_${machineHash}`;
|
|
7882
8443
|
this.bridge = new CliBridgeConnection({
|
|
7883
8444
|
serverUrl: options.serverUrl || config.serverUrl,
|
|
@@ -9065,16 +9626,16 @@ async function injectTokenToIDE(ide, connectionToken) {
|
|
|
9065
9626
|
try {
|
|
9066
9627
|
const os13 = await import("os");
|
|
9067
9628
|
const fs7 = await import("fs");
|
|
9068
|
-
const
|
|
9629
|
+
const path12 = await import("path");
|
|
9069
9630
|
const platform7 = os13.platform();
|
|
9070
9631
|
const home = os13.homedir();
|
|
9071
9632
|
const getSettingsPath = (appName2) => {
|
|
9072
9633
|
if (platform7 === "darwin") {
|
|
9073
|
-
return
|
|
9634
|
+
return path12.join(home, "Library", "Application Support", appName2, "User", "settings.json");
|
|
9074
9635
|
} else if (platform7 === "win32") {
|
|
9075
|
-
return
|
|
9636
|
+
return path12.join(process.env.APPDATA || path12.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
|
|
9076
9637
|
} else {
|
|
9077
|
-
return
|
|
9638
|
+
return path12.join(home, ".config", appName2, "User", "settings.json");
|
|
9078
9639
|
}
|
|
9079
9640
|
};
|
|
9080
9641
|
const loader = new ProviderLoader({ logFn: () => {
|
|
@@ -9092,7 +9653,7 @@ async function injectTokenToIDE(ide, connectionToken) {
|
|
|
9092
9653
|
settings = {};
|
|
9093
9654
|
}
|
|
9094
9655
|
} else {
|
|
9095
|
-
fs7.mkdirSync(
|
|
9656
|
+
fs7.mkdirSync(path12.dirname(settingsPath), { recursive: true });
|
|
9096
9657
|
}
|
|
9097
9658
|
settings["adhdev.connectionToken"] = connectionToken;
|
|
9098
9659
|
settings["adhdev.autoConnect"] = true;
|
|
@@ -9128,8 +9689,8 @@ async function startDaemonFlow() {
|
|
|
9128
9689
|
const daemon = new AdhdevDaemon2();
|
|
9129
9690
|
const { execSync: execSync6 } = await import("child_process");
|
|
9130
9691
|
const os13 = await import("os");
|
|
9131
|
-
const
|
|
9132
|
-
const logPath =
|
|
9692
|
+
const path12 = await import("path");
|
|
9693
|
+
const logPath = path12.join(os13.homedir(), ".adhdev", "daemon.log");
|
|
9133
9694
|
try {
|
|
9134
9695
|
execSync6(`nohup adhdev daemon > "${logPath}" 2>&1 &`, {
|
|
9135
9696
|
timeout: 3e3,
|
package/package.json
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
* @type {import('../../../../src/providers/contracts').ProviderModule}
|
|
13
13
|
*/
|
|
14
14
|
module.exports = {
|
|
15
|
-
type: 'codex-
|
|
16
|
-
name: 'Codex
|
|
15
|
+
type: 'codex-acp',
|
|
16
|
+
name: 'Codex (ACP)',
|
|
17
17
|
category: 'acp',
|
|
18
18
|
|
|
19
|
-
displayName: 'Codex',
|
|
19
|
+
displayName: 'Codex (ACP)',
|
|
20
20
|
icon: '🤖',
|
|
21
21
|
|
|
22
22
|
// ACP spawn config — uses codex-acp adapter
|