codeep 1.2.36 → 1.2.38
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/acp/commands.d.ts +9 -0
- package/dist/acp/commands.js +34 -0
- package/dist/acp/protocol.d.ts +153 -12
- package/dist/acp/protocol.js +1 -1
- package/dist/acp/server.js +169 -40
- package/dist/acp/session.js +16 -6
- package/dist/acp/transport.d.ts +1 -1
- package/dist/config/index.js +15 -7
- package/dist/utils/agent.js +7 -1
- package/package.json +1 -1
package/dist/acp/commands.d.ts
CHANGED
|
@@ -31,6 +31,15 @@ export declare function initWorkspace(workspaceRoot: string): {
|
|
|
31
31
|
history: Message[];
|
|
32
32
|
welcomeText: string;
|
|
33
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Restore a previously saved ACP session by its Zed sessionId.
|
|
36
|
+
* Falls back to initWorkspace if the session cannot be found on disk.
|
|
37
|
+
*/
|
|
38
|
+
export declare function loadWorkspace(workspaceRoot: string, acpSessionId: string): {
|
|
39
|
+
codeepSessionId: string;
|
|
40
|
+
history: Message[];
|
|
41
|
+
welcomeText: string;
|
|
42
|
+
};
|
|
34
43
|
/**
|
|
35
44
|
* Try to handle a slash command. Async because skills and diff/review
|
|
36
45
|
* need to call the AI API or run shell commands.
|
package/dist/acp/commands.js
CHANGED
|
@@ -74,6 +74,40 @@ export function initWorkspace(workspaceRoot) {
|
|
|
74
74
|
}
|
|
75
75
|
return { codeepSessionId, history, welcomeText: lines.join('\n') };
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Restore a previously saved ACP session by its Zed sessionId.
|
|
79
|
+
* Falls back to initWorkspace if the session cannot be found on disk.
|
|
80
|
+
*/
|
|
81
|
+
export function loadWorkspace(workspaceRoot, acpSessionId) {
|
|
82
|
+
// Ensure workspace is set up
|
|
83
|
+
const codeepDir = join(workspaceRoot, '.codeep');
|
|
84
|
+
if (!existsSync(codeepDir)) {
|
|
85
|
+
mkdirSync(codeepDir, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
if (!isManuallyInitializedProject(workspaceRoot)) {
|
|
88
|
+
initializeAsProject(workspaceRoot);
|
|
89
|
+
}
|
|
90
|
+
if (!hasReadPermission(workspaceRoot)) {
|
|
91
|
+
setProjectPermission(workspaceRoot, true, true);
|
|
92
|
+
}
|
|
93
|
+
// Try to load the session that was saved under this ACP session ID
|
|
94
|
+
const loaded = loadSession(acpSessionId, workspaceRoot);
|
|
95
|
+
if (loaded) {
|
|
96
|
+
const history = loaded;
|
|
97
|
+
const provider = getCurrentProvider();
|
|
98
|
+
const model = config.get('model');
|
|
99
|
+
const lines = [
|
|
100
|
+
`**Codeep** • ${provider.name} • \`${model}\``,
|
|
101
|
+
'',
|
|
102
|
+
`**Session restored:** ${acpSessionId} (${history.length} messages)`,
|
|
103
|
+
'',
|
|
104
|
+
...formatSessionPreviewLines(history),
|
|
105
|
+
];
|
|
106
|
+
return { codeepSessionId: acpSessionId, history, welcomeText: lines.join('\n') };
|
|
107
|
+
}
|
|
108
|
+
// Session not found — fall back to initWorkspace behaviour
|
|
109
|
+
return initWorkspace(workspaceRoot);
|
|
110
|
+
}
|
|
77
111
|
// ─── Command dispatch ─────────────────────────────────────────────────────────
|
|
78
112
|
/**
|
|
79
113
|
* Try to handle a slash command. Async because skills and diff/review
|
package/dist/acp/protocol.d.ts
CHANGED
|
@@ -19,15 +19,23 @@ export interface JsonRpcNotification {
|
|
|
19
19
|
params?: unknown;
|
|
20
20
|
}
|
|
21
21
|
export interface InitializeParams {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
protocolVersion?: number;
|
|
23
|
+
clientCapabilities?: {
|
|
24
|
+
fs?: {
|
|
25
|
+
readTextFile?: boolean;
|
|
26
|
+
writeTextFile?: boolean;
|
|
27
|
+
};
|
|
28
|
+
terminal?: boolean;
|
|
29
|
+
};
|
|
30
|
+
clientInfo?: {
|
|
25
31
|
name: string;
|
|
26
|
-
|
|
32
|
+
version: string;
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
35
|
export interface InitializeResult {
|
|
29
36
|
protocolVersion: number;
|
|
30
37
|
agentCapabilities: {
|
|
38
|
+
loadSession?: boolean;
|
|
31
39
|
streaming?: boolean;
|
|
32
40
|
fileEditing?: boolean;
|
|
33
41
|
};
|
|
@@ -37,21 +45,154 @@ export interface InitializeResult {
|
|
|
37
45
|
};
|
|
38
46
|
authMethods: unknown[];
|
|
39
47
|
}
|
|
40
|
-
export interface
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
export interface McpServer {
|
|
49
|
+
name: string;
|
|
50
|
+
command: string;
|
|
51
|
+
args: string[];
|
|
52
|
+
env?: Record<string, string>;
|
|
53
|
+
}
|
|
54
|
+
export interface SessionMode {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
description?: string | null;
|
|
58
|
+
}
|
|
59
|
+
export interface SessionModeState {
|
|
60
|
+
availableModes: SessionMode[];
|
|
61
|
+
currentModeId: string;
|
|
62
|
+
}
|
|
63
|
+
export interface SessionConfigOption {
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
description?: string | null;
|
|
67
|
+
category?: 'mode' | 'model' | 'thought_level' | null;
|
|
68
|
+
type: 'select';
|
|
69
|
+
options?: {
|
|
70
|
+
id: string;
|
|
43
71
|
name: string;
|
|
44
|
-
command: string;
|
|
45
|
-
args: string[];
|
|
46
|
-
env?: Record<string, string>;
|
|
47
72
|
}[];
|
|
73
|
+
currentValue?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface SessionNewParams {
|
|
76
|
+
cwd: string;
|
|
77
|
+
mcpServers?: McpServer[];
|
|
78
|
+
}
|
|
79
|
+
export interface SessionNewResult {
|
|
80
|
+
sessionId: string;
|
|
81
|
+
modes?: SessionModeState | null;
|
|
82
|
+
configOptions?: SessionConfigOption[] | null;
|
|
83
|
+
}
|
|
84
|
+
export interface SessionLoadParams {
|
|
85
|
+
sessionId: string;
|
|
86
|
+
cwd: string;
|
|
87
|
+
mcpServers?: McpServer[];
|
|
88
|
+
}
|
|
89
|
+
export interface SessionLoadResult {
|
|
90
|
+
modes?: SessionModeState | null;
|
|
91
|
+
configOptions?: SessionConfigOption[] | null;
|
|
48
92
|
}
|
|
49
93
|
export interface ContentBlock {
|
|
50
|
-
type: 'text';
|
|
51
|
-
text
|
|
94
|
+
type: 'text' | 'image' | 'audio' | 'resource_link' | 'resource';
|
|
95
|
+
text?: string;
|
|
96
|
+
data?: string;
|
|
97
|
+
mimeType?: string;
|
|
98
|
+
uri?: string;
|
|
99
|
+
name?: string;
|
|
52
100
|
}
|
|
53
101
|
export interface SessionPromptParams {
|
|
54
102
|
sessionId: string;
|
|
55
103
|
prompt: ContentBlock[];
|
|
56
104
|
}
|
|
105
|
+
export interface SessionPromptResult {
|
|
106
|
+
stopReason: 'end_turn' | 'cancelled';
|
|
107
|
+
}
|
|
108
|
+
export interface SessionCancelParams {
|
|
109
|
+
sessionId: string;
|
|
110
|
+
}
|
|
111
|
+
export interface SetSessionModeParams {
|
|
112
|
+
sessionId: string;
|
|
113
|
+
modeId: string;
|
|
114
|
+
}
|
|
115
|
+
export interface SetSessionConfigOptionParams {
|
|
116
|
+
sessionId: string;
|
|
117
|
+
configId: string;
|
|
118
|
+
value: unknown;
|
|
119
|
+
}
|
|
120
|
+
export interface SessionUpdateAgentMessageChunk {
|
|
121
|
+
sessionUpdate: 'agent_message_chunk';
|
|
122
|
+
content: ContentBlock;
|
|
123
|
+
}
|
|
124
|
+
export interface SessionUpdateAgentThoughtChunk {
|
|
125
|
+
sessionUpdate: 'agent_thought_chunk';
|
|
126
|
+
content: ContentBlock;
|
|
127
|
+
}
|
|
128
|
+
export interface SessionUpdateToolCall {
|
|
129
|
+
sessionUpdate: 'tool_call';
|
|
130
|
+
toolCallId: string;
|
|
131
|
+
status: 'pending';
|
|
132
|
+
rawInput: unknown;
|
|
133
|
+
}
|
|
134
|
+
export interface SessionUpdateToolCallUpdate {
|
|
135
|
+
sessionUpdate: 'tool_call_update';
|
|
136
|
+
toolCallId: string;
|
|
137
|
+
status: 'completed' | 'failed';
|
|
138
|
+
rawOutput?: string;
|
|
139
|
+
}
|
|
140
|
+
export interface SessionUpdateAvailableCommands {
|
|
141
|
+
sessionUpdate: 'available_commands_update';
|
|
142
|
+
availableCommands: {
|
|
143
|
+
name: string;
|
|
144
|
+
description: string;
|
|
145
|
+
input?: {
|
|
146
|
+
hint: string;
|
|
147
|
+
} | null;
|
|
148
|
+
}[];
|
|
149
|
+
}
|
|
150
|
+
export interface SessionUpdateCurrentMode {
|
|
151
|
+
sessionUpdate: 'current_mode_update';
|
|
152
|
+
currentModeId: string;
|
|
153
|
+
}
|
|
154
|
+
export type SessionUpdateInner = SessionUpdateAgentMessageChunk | SessionUpdateAgentThoughtChunk | SessionUpdateToolCall | SessionUpdateToolCallUpdate | SessionUpdateAvailableCommands | SessionUpdateCurrentMode;
|
|
155
|
+
export interface SessionUpdateParams {
|
|
156
|
+
sessionId: string;
|
|
157
|
+
update: SessionUpdateInner;
|
|
158
|
+
}
|
|
159
|
+
export type PermissionOptionKind = 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';
|
|
160
|
+
export interface PermissionOption {
|
|
161
|
+
optionId: string;
|
|
162
|
+
name: string;
|
|
163
|
+
kind: PermissionOptionKind;
|
|
164
|
+
}
|
|
165
|
+
export interface RequestPermissionParams {
|
|
166
|
+
sessionId: string;
|
|
167
|
+
toolCall: {
|
|
168
|
+
toolCallId: string;
|
|
169
|
+
toolName: string;
|
|
170
|
+
toolInput: unknown;
|
|
171
|
+
status: 'pending' | 'completed' | 'failed';
|
|
172
|
+
content: unknown[];
|
|
173
|
+
};
|
|
174
|
+
options: PermissionOption[];
|
|
175
|
+
}
|
|
176
|
+
export interface RequestPermissionResult {
|
|
177
|
+
outcome: {
|
|
178
|
+
type: 'cancelled';
|
|
179
|
+
} | {
|
|
180
|
+
type: 'selected';
|
|
181
|
+
optionId: string;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export interface FsReadTextFileParams {
|
|
185
|
+
sessionId: string;
|
|
186
|
+
path: string;
|
|
187
|
+
line?: number;
|
|
188
|
+
limit?: number;
|
|
189
|
+
}
|
|
190
|
+
export interface FsReadTextFileResult {
|
|
191
|
+
content: string;
|
|
192
|
+
}
|
|
193
|
+
export interface FsWriteTextFileParams {
|
|
194
|
+
sessionId: string;
|
|
195
|
+
path: string;
|
|
196
|
+
content: string;
|
|
197
|
+
}
|
|
57
198
|
export type AcpMessage = JsonRpcRequest | JsonRpcResponse | JsonRpcNotification;
|
package/dist/acp/protocol.js
CHANGED
package/dist/acp/server.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import { randomUUID } from 'crypto';
|
|
4
4
|
import { StdioTransport } from './transport.js';
|
|
5
5
|
import { runAgentSession } from './session.js';
|
|
6
|
-
import { initWorkspace, handleCommand } from './commands.js';
|
|
7
|
-
import { autoSaveSession } from '../config/index.js';
|
|
6
|
+
import { initWorkspace, loadWorkspace, handleCommand } from './commands.js';
|
|
7
|
+
import { autoSaveSession, config } from '../config/index.js';
|
|
8
8
|
import { getCurrentVersion } from '../utils/update.js';
|
|
9
|
-
//
|
|
9
|
+
// ─── Slash commands advertised to Zed ────────────────────────────────────────
|
|
10
10
|
const AVAILABLE_COMMANDS = [
|
|
11
11
|
// Configuration
|
|
12
12
|
{ name: 'help', description: 'Show available commands' },
|
|
@@ -49,36 +49,77 @@ const AVAILABLE_COMMANDS = [
|
|
|
49
49
|
{ name: 'build', description: 'Build the project' },
|
|
50
50
|
{ name: 'deploy', description: 'Deploy the project' },
|
|
51
51
|
];
|
|
52
|
+
// ─── Mode definitions ─────────────────────────────────────────────────────────
|
|
53
|
+
const AGENT_MODES = {
|
|
54
|
+
currentModeId: 'auto',
|
|
55
|
+
availableModes: [
|
|
56
|
+
{ id: 'auto', name: 'Auto', description: 'Agent runs automatically without confirmation' },
|
|
57
|
+
{ id: 'manual', name: 'Manual', description: 'Confirm dangerous operations before running' },
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
// ─── Config options ───────────────────────────────────────────────────────────
|
|
61
|
+
function buildConfigOptions() {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
id: 'model',
|
|
65
|
+
name: 'Model',
|
|
66
|
+
description: 'AI model to use',
|
|
67
|
+
category: 'model',
|
|
68
|
+
type: 'select',
|
|
69
|
+
currentValue: config.get('model'),
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
// ─── Server ───────────────────────────────────────────────────────────────────
|
|
52
74
|
export function startAcpServer() {
|
|
53
75
|
const transport = new StdioTransport();
|
|
54
76
|
// ACP sessionId → full AcpSession (includes history + codeep session tracking)
|
|
55
77
|
const sessions = new Map();
|
|
56
78
|
transport.start((msg) => {
|
|
57
|
-
|
|
79
|
+
// Notifications have no id — handle separately
|
|
80
|
+
if (!('id' in msg)) {
|
|
81
|
+
handleNotification(msg);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const req = msg;
|
|
85
|
+
switch (req.method) {
|
|
58
86
|
case 'initialize':
|
|
59
|
-
handleInitialize(
|
|
60
|
-
break;
|
|
61
|
-
case 'initialized':
|
|
62
|
-
// no-op acknowledgment
|
|
87
|
+
handleInitialize(req);
|
|
63
88
|
break;
|
|
89
|
+
case 'initialized': /* no-op acknowledgment */ break;
|
|
64
90
|
case 'session/new':
|
|
65
|
-
handleSessionNew(
|
|
91
|
+
handleSessionNew(req);
|
|
92
|
+
break;
|
|
93
|
+
case 'session/load':
|
|
94
|
+
handleSessionLoad(req);
|
|
66
95
|
break;
|
|
67
96
|
case 'session/prompt':
|
|
68
|
-
handleSessionPrompt(
|
|
97
|
+
handleSessionPrompt(req);
|
|
98
|
+
break;
|
|
99
|
+
case 'session/set_mode':
|
|
100
|
+
handleSetMode(req);
|
|
69
101
|
break;
|
|
70
|
-
case 'session/
|
|
71
|
-
|
|
102
|
+
case 'session/set_config_option':
|
|
103
|
+
handleSetConfigOption(req);
|
|
72
104
|
break;
|
|
73
105
|
default:
|
|
74
|
-
transport.error(
|
|
106
|
+
transport.error(req.id, -32601, `Method not found: ${req.method}`);
|
|
75
107
|
}
|
|
76
108
|
});
|
|
109
|
+
// ── Notification handler (no id, no response) ──────────────────────────────
|
|
110
|
+
function handleNotification(msg) {
|
|
111
|
+
if (msg.method === 'session/cancel') {
|
|
112
|
+
const { sessionId } = (msg.params ?? {});
|
|
113
|
+
sessions.get(sessionId)?.abortController?.abort();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// ── initialize ──────────────────────────────────────────────────────────────
|
|
77
117
|
function handleInitialize(msg) {
|
|
78
118
|
const _params = msg.params;
|
|
79
119
|
const result = {
|
|
80
120
|
protocolVersion: 1,
|
|
81
121
|
agentCapabilities: {
|
|
122
|
+
loadSession: true,
|
|
82
123
|
streaming: true,
|
|
83
124
|
fileEditing: true,
|
|
84
125
|
},
|
|
@@ -90,10 +131,10 @@ export function startAcpServer() {
|
|
|
90
131
|
};
|
|
91
132
|
transport.respond(msg.id, result);
|
|
92
133
|
}
|
|
134
|
+
// ── session/new ─────────────────────────────────────────────────────────────
|
|
93
135
|
function handleSessionNew(msg) {
|
|
94
136
|
const params = msg.params;
|
|
95
137
|
const acpSessionId = randomUUID();
|
|
96
|
-
// Initialise workspace: create .codeep folder, load/create codeep session
|
|
97
138
|
const { codeepSessionId, history, welcomeText } = initWorkspace(params.cwd);
|
|
98
139
|
sessions.set(acpSessionId, {
|
|
99
140
|
sessionId: acpSessionId,
|
|
@@ -102,9 +143,15 @@ export function startAcpServer() {
|
|
|
102
143
|
codeepSessionId,
|
|
103
144
|
addedFiles: new Map(),
|
|
104
145
|
abortController: null,
|
|
146
|
+
currentModeId: 'auto',
|
|
105
147
|
});
|
|
106
|
-
|
|
107
|
-
|
|
148
|
+
const result = {
|
|
149
|
+
sessionId: acpSessionId,
|
|
150
|
+
modes: AGENT_MODES,
|
|
151
|
+
configOptions: buildConfigOptions(),
|
|
152
|
+
};
|
|
153
|
+
transport.respond(msg.id, result);
|
|
154
|
+
// Advertise slash commands
|
|
108
155
|
transport.notify('session/update', {
|
|
109
156
|
sessionId: acpSessionId,
|
|
110
157
|
update: {
|
|
@@ -112,7 +159,7 @@ export function startAcpServer() {
|
|
|
112
159
|
availableCommands: AVAILABLE_COMMANDS,
|
|
113
160
|
},
|
|
114
161
|
});
|
|
115
|
-
//
|
|
162
|
+
// Send welcome message
|
|
116
163
|
transport.notify('session/update', {
|
|
117
164
|
sessionId: acpSessionId,
|
|
118
165
|
update: {
|
|
@@ -121,6 +168,86 @@ export function startAcpServer() {
|
|
|
121
168
|
},
|
|
122
169
|
});
|
|
123
170
|
}
|
|
171
|
+
// ── session/load ────────────────────────────────────────────────────────────
|
|
172
|
+
function handleSessionLoad(msg) {
|
|
173
|
+
const params = msg.params;
|
|
174
|
+
// Try to restore existing Codeep session or fall back to fresh workspace
|
|
175
|
+
const existing = sessions.get(params.sessionId);
|
|
176
|
+
if (existing) {
|
|
177
|
+
// Session already in memory — update cwd if changed
|
|
178
|
+
existing.workspaceRoot = params.cwd;
|
|
179
|
+
const result = {
|
|
180
|
+
modes: AGENT_MODES,
|
|
181
|
+
configOptions: buildConfigOptions(),
|
|
182
|
+
};
|
|
183
|
+
transport.respond(msg.id, result);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// Session not in memory — try to load from disk
|
|
187
|
+
const { codeepSessionId, history, welcomeText } = loadWorkspace(params.cwd, params.sessionId);
|
|
188
|
+
sessions.set(params.sessionId, {
|
|
189
|
+
sessionId: params.sessionId,
|
|
190
|
+
workspaceRoot: params.cwd,
|
|
191
|
+
history,
|
|
192
|
+
codeepSessionId,
|
|
193
|
+
addedFiles: new Map(),
|
|
194
|
+
abortController: null,
|
|
195
|
+
currentModeId: 'auto',
|
|
196
|
+
});
|
|
197
|
+
const result = {
|
|
198
|
+
modes: AGENT_MODES,
|
|
199
|
+
configOptions: buildConfigOptions(),
|
|
200
|
+
};
|
|
201
|
+
transport.respond(msg.id, result);
|
|
202
|
+
// Send restored session welcome
|
|
203
|
+
transport.notify('session/update', {
|
|
204
|
+
sessionId: params.sessionId,
|
|
205
|
+
update: {
|
|
206
|
+
sessionUpdate: 'agent_message_chunk',
|
|
207
|
+
content: { type: 'text', text: welcomeText },
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// ── session/set_mode ────────────────────────────────────────────────────────
|
|
212
|
+
function handleSetMode(msg) {
|
|
213
|
+
const { sessionId, modeId } = msg.params;
|
|
214
|
+
const session = sessions.get(sessionId);
|
|
215
|
+
if (!session) {
|
|
216
|
+
transport.error(msg.id, -32602, `Unknown sessionId: ${sessionId}`);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const validMode = AGENT_MODES.availableModes.find(m => m.id === modeId);
|
|
220
|
+
if (!validMode) {
|
|
221
|
+
transport.error(msg.id, -32602, `Unknown modeId: ${modeId}`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
session.currentModeId = modeId;
|
|
225
|
+
// Map ACP mode to Codeep agentConfirmation setting
|
|
226
|
+
config.set('agentConfirmation', modeId === 'manual' ? 'dangerous' : 'never');
|
|
227
|
+
transport.respond(msg.id, {});
|
|
228
|
+
// Notify Zed of the mode change
|
|
229
|
+
transport.notify('session/update', {
|
|
230
|
+
sessionId,
|
|
231
|
+
update: {
|
|
232
|
+
sessionUpdate: 'current_mode_update',
|
|
233
|
+
currentModeId: modeId,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
// ── session/set_config_option ───────────────────────────────────────────────
|
|
238
|
+
function handleSetConfigOption(msg) {
|
|
239
|
+
const { sessionId, configId, value } = msg.params;
|
|
240
|
+
const session = sessions.get(sessionId);
|
|
241
|
+
if (!session) {
|
|
242
|
+
transport.error(msg.id, -32602, `Unknown sessionId: ${sessionId}`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (configId === 'model' && typeof value === 'string') {
|
|
246
|
+
config.set('model', value);
|
|
247
|
+
}
|
|
248
|
+
transport.respond(msg.id, {});
|
|
249
|
+
}
|
|
250
|
+
// ── session/prompt ──────────────────────────────────────────────────────────
|
|
124
251
|
function handleSessionPrompt(msg) {
|
|
125
252
|
const params = msg.params;
|
|
126
253
|
const session = sessions.get(params.sessionId);
|
|
@@ -131,7 +258,7 @@ export function startAcpServer() {
|
|
|
131
258
|
// Extract text from ContentBlock[]
|
|
132
259
|
const prompt = params.prompt
|
|
133
260
|
.filter((b) => b.type === 'text')
|
|
134
|
-
.map((b) => b.text)
|
|
261
|
+
.map((b) => b.text ?? '')
|
|
135
262
|
.join('\n');
|
|
136
263
|
const abortController = new AbortController();
|
|
137
264
|
session.abortController = abortController;
|
|
@@ -146,19 +273,16 @@ export function startAcpServer() {
|
|
|
146
273
|
},
|
|
147
274
|
});
|
|
148
275
|
};
|
|
149
|
-
// Try slash commands first
|
|
276
|
+
// Try slash commands first
|
|
150
277
|
handleCommand(prompt, session, sendChunk, abortController.signal)
|
|
151
278
|
.then((cmd) => {
|
|
152
279
|
if (cmd.handled) {
|
|
153
|
-
// For streaming commands (skills, diff), chunks were already sent via onChunk.
|
|
154
|
-
// For simple commands, send the response now.
|
|
155
280
|
if (cmd.response)
|
|
156
281
|
sendChunk(cmd.response);
|
|
157
282
|
transport.respond(msg.id, { stopReason: 'end_turn' });
|
|
158
283
|
return;
|
|
159
284
|
}
|
|
160
285
|
// Not a command — run agent loop
|
|
161
|
-
// Prepend any added-files context to the prompt
|
|
162
286
|
let enrichedPrompt = prompt;
|
|
163
287
|
if (session.addedFiles.size > 0) {
|
|
164
288
|
const parts = ['[Attached files]'];
|
|
@@ -182,21 +306,31 @@ export function startAcpServer() {
|
|
|
182
306
|
},
|
|
183
307
|
});
|
|
184
308
|
},
|
|
185
|
-
onToolCall: (toolCallId,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
}
|
|
309
|
+
onToolCall: (toolCallId, toolName, _kind, _title, status, _locations) => {
|
|
310
|
+
if (status === 'running') {
|
|
311
|
+
transport.notify('session/update', {
|
|
312
|
+
sessionId: params.sessionId,
|
|
313
|
+
update: {
|
|
314
|
+
sessionUpdate: 'tool_call',
|
|
315
|
+
toolCallId,
|
|
316
|
+
status: 'pending',
|
|
317
|
+
rawInput: { toolName },
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
transport.notify('session/update', {
|
|
323
|
+
sessionId: params.sessionId,
|
|
324
|
+
update: {
|
|
325
|
+
sessionUpdate: 'tool_call_update',
|
|
326
|
+
toolCallId,
|
|
327
|
+
status: status === 'finished' ? 'completed' : 'failed',
|
|
328
|
+
rawOutput: '',
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
197
332
|
},
|
|
198
333
|
onFileEdit: (uri, newText) => {
|
|
199
|
-
// ACP structured file/edit notification — lets the editor apply changes
|
|
200
334
|
transport.notify('file/edit', {
|
|
201
335
|
uri,
|
|
202
336
|
textChanges: newText
|
|
@@ -230,11 +364,6 @@ export function startAcpServer() {
|
|
|
230
364
|
session.abortController = null;
|
|
231
365
|
});
|
|
232
366
|
}
|
|
233
|
-
function handleSessionCancel(msg) {
|
|
234
|
-
const { sessionId } = msg.params;
|
|
235
|
-
sessions.get(sessionId)?.abortController?.abort();
|
|
236
|
-
transport.respond(msg.id, {});
|
|
237
|
-
}
|
|
238
367
|
// Keep process alive until stdin closes (Zed terminates us)
|
|
239
368
|
return new Promise((resolve) => {
|
|
240
369
|
process.stdin.on('end', resolve);
|
package/dist/acp/session.js
CHANGED
|
@@ -87,13 +87,23 @@ export async function runAgentSession(opts) {
|
|
|
87
87
|
}
|
|
88
88
|
},
|
|
89
89
|
onToolResult: (toolResult, toolCall) => {
|
|
90
|
-
// Find the tracked entry
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
// Find the tracked entry: prefer exact id match, then first FIFO entry for same tool name
|
|
91
|
+
let mapKey;
|
|
92
|
+
if (toolCall.id && toolCallIdMap.has(toolCall.id)) {
|
|
93
|
+
mapKey = toolCall.id;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// FIFO: find oldest pending entry for this tool name
|
|
97
|
+
for (const [k, v] of toolCallIdMap) {
|
|
98
|
+
if (k.startsWith(`${toolCall.tool}_`) && v.toolCallId) {
|
|
99
|
+
mapKey = k;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (mapKey !== undefined) {
|
|
95
105
|
const tracked = toolCallIdMap.get(mapKey);
|
|
96
|
-
if (opts.onToolCall) {
|
|
106
|
+
if (tracked && opts.onToolCall) {
|
|
97
107
|
const status = toolResult.success ? 'finished' : 'error';
|
|
98
108
|
opts.onToolCall(tracked.toolCallId, toolCall.tool, tracked.kind, '', status, tracked.locations);
|
|
99
109
|
}
|
package/dist/acp/transport.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JsonRpcRequest, JsonRpcResponse, JsonRpcNotification } from './protocol.js';
|
|
2
|
-
type MessageHandler = (msg: JsonRpcRequest) => void;
|
|
2
|
+
type MessageHandler = (msg: JsonRpcRequest | JsonRpcNotification) => void;
|
|
3
3
|
export declare class StdioTransport {
|
|
4
4
|
private buffer;
|
|
5
5
|
private handler;
|
package/dist/config/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Conf from 'conf';
|
|
2
2
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync, statSync } from 'fs';
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
|
+
import { randomUUID } from 'crypto';
|
|
4
5
|
import { PROVIDERS, getProvider } from './providers.js';
|
|
5
6
|
import { logSession } from '../utils/logger.js';
|
|
6
7
|
// We'll initialize GLOBAL_SESSIONS_DIR after config is created (to use config.path)
|
|
@@ -389,8 +390,17 @@ export function isConfigured(providerId) {
|
|
|
389
390
|
}
|
|
390
391
|
// Get current provider info
|
|
391
392
|
export function getCurrentProvider() {
|
|
392
|
-
|
|
393
|
-
|
|
393
|
+
let providerId = config.get('provider');
|
|
394
|
+
let provider = getProvider(providerId);
|
|
395
|
+
// If stored provider no longer exists in registry, fall back to first available
|
|
396
|
+
if (!provider) {
|
|
397
|
+
const fallback = Object.keys(PROVIDERS)[0];
|
|
398
|
+
if (fallback) {
|
|
399
|
+
providerId = fallback;
|
|
400
|
+
provider = getProvider(fallback);
|
|
401
|
+
config.set('provider', fallback);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
394
404
|
return {
|
|
395
405
|
id: providerId,
|
|
396
406
|
name: provider?.name || providerId,
|
|
@@ -425,10 +435,8 @@ export function getModelsForCurrentProvider() {
|
|
|
425
435
|
export { PROVIDERS } from './providers.js';
|
|
426
436
|
// Generate unique session ID
|
|
427
437
|
function generateSessionId() {
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
const time = now.toTimeString().split(' ')[0].replace(/:/g, '-');
|
|
431
|
-
return `session-${date}-${time}`;
|
|
438
|
+
const date = new Date().toISOString().split('T')[0];
|
|
439
|
+
return `session-${date}-${randomUUID().slice(0, 8)}`;
|
|
432
440
|
}
|
|
433
441
|
// Get or create current session ID
|
|
434
442
|
export function getCurrentSessionId() {
|
|
@@ -636,7 +644,7 @@ export function getProjectPermission(projectPath) {
|
|
|
636
644
|
if (configPath && existsSync(configPath)) {
|
|
637
645
|
try {
|
|
638
646
|
const data = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
639
|
-
if (data.permission)
|
|
647
|
+
if (data.permission && typeof data.permission === 'object')
|
|
640
648
|
return data.permission;
|
|
641
649
|
}
|
|
642
650
|
catch {
|
package/dist/utils/agent.js
CHANGED
|
@@ -124,6 +124,8 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
124
124
|
let finalResponse = '';
|
|
125
125
|
let result;
|
|
126
126
|
let consecutiveTimeouts = 0;
|
|
127
|
+
let incompleteWorkRetries = 0;
|
|
128
|
+
const maxIncompleteWorkRetries = 2;
|
|
127
129
|
const maxTimeoutRetries = 3;
|
|
128
130
|
const maxConsecutiveTimeouts = 9; // Allow more consecutive timeouts before giving up
|
|
129
131
|
const baseTimeout = config.get('agentApiTimeout');
|
|
@@ -248,9 +250,11 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
248
250
|
const wantsToContinue = continueIndicators.some(indicator => lowerResponse.includes(indicator));
|
|
249
251
|
// Also check if there were tool call parsing failures in this iteration
|
|
250
252
|
// by looking for incomplete actions (e.g., write_file without content)
|
|
251
|
-
const hasIncompleteWork =
|
|
253
|
+
const hasIncompleteWork = wantsToContinue && finalResponse.length < 500
|
|
254
|
+
&& incompleteWorkRetries < maxIncompleteWorkRetries;
|
|
252
255
|
if (hasIncompleteWork) {
|
|
253
256
|
debug('Model wants to continue, prompting for next action');
|
|
257
|
+
incompleteWorkRetries++;
|
|
254
258
|
messages.push({ role: 'assistant', content });
|
|
255
259
|
messages.push({
|
|
256
260
|
role: 'user',
|
|
@@ -258,6 +262,8 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
258
262
|
});
|
|
259
263
|
continue;
|
|
260
264
|
}
|
|
265
|
+
// Reset counter once model produces real output or we give up
|
|
266
|
+
incompleteWorkRetries = 0;
|
|
261
267
|
// Model is done
|
|
262
268
|
debug(`Agent finished at iteration ${iteration}`);
|
|
263
269
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.38",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|