codeep 1.2.36 → 1.2.37
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 +154 -12
- package/dist/acp/protocol.js +1 -1
- package/dist/acp/server.js +157 -53
- 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,155 @@ 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 type ToolCallState = 'running' | 'finished' | 'error';
|
|
121
|
+
export interface SessionUpdateContentChunk {
|
|
122
|
+
type: 'content_chunk';
|
|
123
|
+
sessionId: string;
|
|
124
|
+
content: ContentBlock;
|
|
125
|
+
}
|
|
126
|
+
export interface SessionUpdateToolCall {
|
|
127
|
+
type: 'tool_call';
|
|
128
|
+
sessionId: string;
|
|
129
|
+
toolCallId: string;
|
|
130
|
+
toolName: string;
|
|
131
|
+
toolInput: unknown;
|
|
132
|
+
state: ToolCallState;
|
|
133
|
+
content?: {
|
|
134
|
+
type: 'text';
|
|
135
|
+
text: string;
|
|
136
|
+
}[];
|
|
137
|
+
}
|
|
138
|
+
export interface SessionUpdateThoughtChunk {
|
|
139
|
+
type: 'agent_thought_chunk';
|
|
140
|
+
sessionId: string;
|
|
141
|
+
content: ContentBlock;
|
|
142
|
+
}
|
|
143
|
+
export interface SessionUpdateAvailableCommands {
|
|
144
|
+
type: 'available_commands_update';
|
|
145
|
+
sessionId: string;
|
|
146
|
+
availableCommands: {
|
|
147
|
+
name: string;
|
|
148
|
+
description: string;
|
|
149
|
+
input?: {
|
|
150
|
+
hint: string;
|
|
151
|
+
};
|
|
152
|
+
}[];
|
|
153
|
+
}
|
|
154
|
+
export interface SessionUpdateCurrentMode {
|
|
155
|
+
type: 'current_mode_update';
|
|
156
|
+
sessionId: string;
|
|
157
|
+
currentModeId: string;
|
|
158
|
+
}
|
|
159
|
+
export type SessionUpdateParams = SessionUpdateContentChunk | SessionUpdateToolCall | SessionUpdateThoughtChunk | SessionUpdateAvailableCommands | SessionUpdateCurrentMode;
|
|
160
|
+
export type PermissionOptionKind = 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';
|
|
161
|
+
export interface PermissionOption {
|
|
162
|
+
optionId: string;
|
|
163
|
+
name: string;
|
|
164
|
+
kind: PermissionOptionKind;
|
|
165
|
+
}
|
|
166
|
+
export interface RequestPermissionParams {
|
|
167
|
+
sessionId: string;
|
|
168
|
+
toolCall: {
|
|
169
|
+
toolCallId: string;
|
|
170
|
+
toolName: string;
|
|
171
|
+
toolInput: unknown;
|
|
172
|
+
state: ToolCallState;
|
|
173
|
+
content: unknown[];
|
|
174
|
+
};
|
|
175
|
+
options: PermissionOption[];
|
|
176
|
+
}
|
|
177
|
+
export interface RequestPermissionResult {
|
|
178
|
+
outcome: {
|
|
179
|
+
type: 'cancelled';
|
|
180
|
+
} | {
|
|
181
|
+
type: 'selected';
|
|
182
|
+
optionId: string;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
export interface FsReadTextFileParams {
|
|
186
|
+
sessionId: string;
|
|
187
|
+
path: string;
|
|
188
|
+
line?: number;
|
|
189
|
+
limit?: number;
|
|
190
|
+
}
|
|
191
|
+
export interface FsReadTextFileResult {
|
|
192
|
+
content: string;
|
|
193
|
+
}
|
|
194
|
+
export interface FsWriteTextFileParams {
|
|
195
|
+
sessionId: string;
|
|
196
|
+
path: string;
|
|
197
|
+
content: string;
|
|
198
|
+
}
|
|
57
199
|
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,25 +143,103 @@ 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', {
|
|
156
|
+
type: 'available_commands_update',
|
|
109
157
|
sessionId: acpSessionId,
|
|
110
|
-
|
|
111
|
-
sessionUpdate: 'available_commands_update',
|
|
112
|
-
availableCommands: AVAILABLE_COMMANDS,
|
|
113
|
-
},
|
|
158
|
+
availableCommands: AVAILABLE_COMMANDS,
|
|
114
159
|
});
|
|
115
|
-
//
|
|
160
|
+
// Send welcome message
|
|
116
161
|
transport.notify('session/update', {
|
|
162
|
+
type: 'content_chunk',
|
|
117
163
|
sessionId: acpSessionId,
|
|
118
|
-
|
|
119
|
-
sessionUpdate: 'agent_message_chunk',
|
|
120
|
-
content: { type: 'text', text: welcomeText },
|
|
121
|
-
},
|
|
164
|
+
content: { type: 'text', text: welcomeText },
|
|
122
165
|
});
|
|
123
166
|
}
|
|
167
|
+
// ── session/load ────────────────────────────────────────────────────────────
|
|
168
|
+
function handleSessionLoad(msg) {
|
|
169
|
+
const params = msg.params;
|
|
170
|
+
// Try to restore existing Codeep session or fall back to fresh workspace
|
|
171
|
+
const existing = sessions.get(params.sessionId);
|
|
172
|
+
if (existing) {
|
|
173
|
+
// Session already in memory — update cwd if changed
|
|
174
|
+
existing.workspaceRoot = params.cwd;
|
|
175
|
+
const result = {
|
|
176
|
+
modes: AGENT_MODES,
|
|
177
|
+
configOptions: buildConfigOptions(),
|
|
178
|
+
};
|
|
179
|
+
transport.respond(msg.id, result);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Session not in memory — try to load from disk
|
|
183
|
+
const { codeepSessionId, history, welcomeText } = loadWorkspace(params.cwd, params.sessionId);
|
|
184
|
+
sessions.set(params.sessionId, {
|
|
185
|
+
sessionId: params.sessionId,
|
|
186
|
+
workspaceRoot: params.cwd,
|
|
187
|
+
history,
|
|
188
|
+
codeepSessionId,
|
|
189
|
+
addedFiles: new Map(),
|
|
190
|
+
abortController: null,
|
|
191
|
+
currentModeId: 'auto',
|
|
192
|
+
});
|
|
193
|
+
const result = {
|
|
194
|
+
modes: AGENT_MODES,
|
|
195
|
+
configOptions: buildConfigOptions(),
|
|
196
|
+
};
|
|
197
|
+
transport.respond(msg.id, result);
|
|
198
|
+
// Send restored session welcome
|
|
199
|
+
transport.notify('session/update', {
|
|
200
|
+
type: 'content_chunk',
|
|
201
|
+
sessionId: params.sessionId,
|
|
202
|
+
content: { type: 'text', text: welcomeText },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
// ── session/set_mode ────────────────────────────────────────────────────────
|
|
206
|
+
function handleSetMode(msg) {
|
|
207
|
+
const { sessionId, modeId } = msg.params;
|
|
208
|
+
const session = sessions.get(sessionId);
|
|
209
|
+
if (!session) {
|
|
210
|
+
transport.error(msg.id, -32602, `Unknown sessionId: ${sessionId}`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const validMode = AGENT_MODES.availableModes.find(m => m.id === modeId);
|
|
214
|
+
if (!validMode) {
|
|
215
|
+
transport.error(msg.id, -32602, `Unknown modeId: ${modeId}`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
session.currentModeId = modeId;
|
|
219
|
+
// Map ACP mode to Codeep agentConfirmation setting
|
|
220
|
+
config.set('agentConfirmation', modeId === 'manual' ? 'dangerous' : 'never');
|
|
221
|
+
transport.respond(msg.id, {});
|
|
222
|
+
// Notify Zed of the mode change
|
|
223
|
+
transport.notify('session/update', {
|
|
224
|
+
type: 'current_mode_update',
|
|
225
|
+
sessionId,
|
|
226
|
+
currentModeId: modeId,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// ── session/set_config_option ───────────────────────────────────────────────
|
|
230
|
+
function handleSetConfigOption(msg) {
|
|
231
|
+
const { sessionId, configId, value } = msg.params;
|
|
232
|
+
const session = sessions.get(sessionId);
|
|
233
|
+
if (!session) {
|
|
234
|
+
transport.error(msg.id, -32602, `Unknown sessionId: ${sessionId}`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (configId === 'model' && typeof value === 'string') {
|
|
238
|
+
config.set('model', value);
|
|
239
|
+
}
|
|
240
|
+
transport.respond(msg.id, {});
|
|
241
|
+
}
|
|
242
|
+
// ── session/prompt ──────────────────────────────────────────────────────────
|
|
124
243
|
function handleSessionPrompt(msg) {
|
|
125
244
|
const params = msg.params;
|
|
126
245
|
const session = sessions.get(params.sessionId);
|
|
@@ -131,7 +250,7 @@ export function startAcpServer() {
|
|
|
131
250
|
// Extract text from ContentBlock[]
|
|
132
251
|
const prompt = params.prompt
|
|
133
252
|
.filter((b) => b.type === 'text')
|
|
134
|
-
.map((b) => b.text)
|
|
253
|
+
.map((b) => b.text ?? '')
|
|
135
254
|
.join('\n');
|
|
136
255
|
const abortController = new AbortController();
|
|
137
256
|
session.abortController = abortController;
|
|
@@ -139,26 +258,21 @@ export function startAcpServer() {
|
|
|
139
258
|
const sendChunk = (text) => {
|
|
140
259
|
agentResponseChunks.push(text);
|
|
141
260
|
transport.notify('session/update', {
|
|
261
|
+
type: 'content_chunk',
|
|
142
262
|
sessionId: params.sessionId,
|
|
143
|
-
|
|
144
|
-
sessionUpdate: 'agent_message_chunk',
|
|
145
|
-
content: { type: 'text', text },
|
|
146
|
-
},
|
|
263
|
+
content: { type: 'text', text },
|
|
147
264
|
});
|
|
148
265
|
};
|
|
149
|
-
// Try slash commands first
|
|
266
|
+
// Try slash commands first
|
|
150
267
|
handleCommand(prompt, session, sendChunk, abortController.signal)
|
|
151
268
|
.then((cmd) => {
|
|
152
269
|
if (cmd.handled) {
|
|
153
|
-
// For streaming commands (skills, diff), chunks were already sent via onChunk.
|
|
154
|
-
// For simple commands, send the response now.
|
|
155
270
|
if (cmd.response)
|
|
156
271
|
sendChunk(cmd.response);
|
|
157
272
|
transport.respond(msg.id, { stopReason: 'end_turn' });
|
|
158
273
|
return;
|
|
159
274
|
}
|
|
160
275
|
// Not a command — run agent loop
|
|
161
|
-
// Prepend any added-files context to the prompt
|
|
162
276
|
let enrichedPrompt = prompt;
|
|
163
277
|
if (session.addedFiles.size > 0) {
|
|
164
278
|
const parts = ['[Attached files]'];
|
|
@@ -175,28 +289,23 @@ export function startAcpServer() {
|
|
|
175
289
|
onChunk: sendChunk,
|
|
176
290
|
onThought: (text) => {
|
|
177
291
|
transport.notify('session/update', {
|
|
292
|
+
type: 'agent_thought_chunk',
|
|
178
293
|
sessionId: params.sessionId,
|
|
179
|
-
|
|
180
|
-
sessionUpdate: 'agent_thought_chunk',
|
|
181
|
-
content: { type: 'text', text },
|
|
182
|
-
},
|
|
294
|
+
content: { type: 'text', text },
|
|
183
295
|
});
|
|
184
296
|
},
|
|
185
|
-
onToolCall: (toolCallId,
|
|
297
|
+
onToolCall: (toolCallId, toolName, _kind, _title, status, _locations) => {
|
|
186
298
|
transport.notify('session/update', {
|
|
299
|
+
type: 'tool_call',
|
|
187
300
|
sessionId: params.sessionId,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
status,
|
|
194
|
-
...(locations?.length ? { locations: locations.map(uri => ({ uri })) } : {}),
|
|
195
|
-
},
|
|
301
|
+
toolCallId,
|
|
302
|
+
toolName,
|
|
303
|
+
toolInput: {},
|
|
304
|
+
state: status === 'running' ? 'running' : status === 'finished' ? 'finished' : 'error',
|
|
305
|
+
content: [],
|
|
196
306
|
});
|
|
197
307
|
},
|
|
198
308
|
onFileEdit: (uri, newText) => {
|
|
199
|
-
// ACP structured file/edit notification — lets the editor apply changes
|
|
200
309
|
transport.notify('file/edit', {
|
|
201
310
|
uri,
|
|
202
311
|
textChanges: newText
|
|
@@ -230,11 +339,6 @@ export function startAcpServer() {
|
|
|
230
339
|
session.abortController = null;
|
|
231
340
|
});
|
|
232
341
|
}
|
|
233
|
-
function handleSessionCancel(msg) {
|
|
234
|
-
const { sessionId } = msg.params;
|
|
235
|
-
sessions.get(sessionId)?.abortController?.abort();
|
|
236
|
-
transport.respond(msg.id, {});
|
|
237
|
-
}
|
|
238
342
|
// Keep process alive until stdin closes (Zed terminates us)
|
|
239
343
|
return new Promise((resolve) => {
|
|
240
344
|
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.37",
|
|
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",
|