codeep 2.23.0 → 2.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/utils/agentChat.js +10 -3
- package/dist/utils/headlessReview.js +16 -3
- package/dist/utils/reviewFix.d.ts +18 -0
- package/dist/utils/reviewFix.js +32 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/utils/agentChat.js
CHANGED
|
@@ -259,7 +259,7 @@ export async function summarizeEarlierHistory(history, maxChars = 16000) {
|
|
|
259
259
|
if (dropped.length === 0)
|
|
260
260
|
return '';
|
|
261
261
|
const key = createHash('sha256')
|
|
262
|
-
.update(dropped.map(m => `${m.role}:${m.content}`).join('
|
|
262
|
+
.update(dropped.map(m => `${m.role}:${m.content}`).join('\u0000'))
|
|
263
263
|
.digest('hex');
|
|
264
264
|
const cached = earlierSummaryCache.get(key);
|
|
265
265
|
if (cached)
|
|
@@ -507,7 +507,12 @@ additionalTools, runtime) {
|
|
|
507
507
|
if (errorText.includes('tools') || errorText.includes('function') || response.status === 400) {
|
|
508
508
|
return await agentChatFallback(messages, systemPrompt, onChunk, abortSignal, dynamicTimeout, additionalTools, runtime);
|
|
509
509
|
}
|
|
510
|
-
|
|
510
|
+
// ApiError, not Error: runAgent's retry loop refuses to retry a 4xx by
|
|
511
|
+
// checking `err instanceof ApiError && err.status`. A bare Error made
|
|
512
|
+
// that guard silently inapplicable, so an expired key was retried once
|
|
513
|
+
// per iteration until the budget ran out and the run reported "Exceeded
|
|
514
|
+
// maximum of N iterations" — the one explanation unrelated to the cause.
|
|
515
|
+
throw new ApiError(`API error: ${response.status} - ${errorText}`, response.status);
|
|
511
516
|
}
|
|
512
517
|
if (useStreaming && response.body) {
|
|
513
518
|
if (protocol === 'openai')
|
|
@@ -658,7 +663,9 @@ export async function agentChatFallback(messages, systemPrompt, onChunk, abortSi
|
|
|
658
663
|
});
|
|
659
664
|
if (!response.ok) {
|
|
660
665
|
const error = await response.text();
|
|
661
|
-
|
|
666
|
+
// Same reasoning as the native-tools path: the status has to survive, or
|
|
667
|
+
// the caller cannot tell "your key is wrong" from "the network hiccuped".
|
|
668
|
+
throw new ApiError(`API error: ${response.status} - ${error}`, response.status);
|
|
662
669
|
}
|
|
663
670
|
let content;
|
|
664
671
|
if (onChunk && response.body) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildFixPlan, summariseFixPlan } from './reviewFix.js';
|
|
1
|
+
import { buildFixPlan, summariseFixPlan, describeAgentActivity } from './reviewFix.js';
|
|
2
2
|
// Headless `codeep review` — a non-interactive entry point around the
|
|
3
3
|
// deterministic reviewer in codeReview.ts. No API key, no TUI: it scans, prints
|
|
4
4
|
// a report (markdown or JSON), and exits non-zero when issues at/above a chosen
|
|
@@ -214,6 +214,18 @@ function defaultDeps() {
|
|
|
214
214
|
*/
|
|
215
215
|
async function runFixPlan(plan, context) {
|
|
216
216
|
try {
|
|
217
|
+
// Populate the key cache before anything asks for it. `getApiKey` is
|
|
218
|
+
// synchronous and reads the cache alone — it does not consult the
|
|
219
|
+
// environment — so without this every request goes out with an empty
|
|
220
|
+
// bearer token and the provider answers 401. The AI review path next door
|
|
221
|
+
// has always done this; the fix path never did, which is why a CI run with
|
|
222
|
+
// a perfectly good key in the environment spent its whole iteration budget
|
|
223
|
+
// being rejected.
|
|
224
|
+
const { loadAllApiKeys, isConfigured } = await import('../config/index.js');
|
|
225
|
+
await loadAllApiKeys();
|
|
226
|
+
if (!isConfigured()) {
|
|
227
|
+
return `${summariseFixPlan(plan)} No API key is configured for the current provider, so the fix agent could not start.`;
|
|
228
|
+
}
|
|
217
229
|
const { runAgent } = await import('./agent.js');
|
|
218
230
|
// No cast here. `as never` on this call once hid the fact that
|
|
219
231
|
// personalityOverride did not exist, which would have run the CI fix with
|
|
@@ -230,11 +242,12 @@ async function runFixPlan(plan, context) {
|
|
|
230
242
|
const edited = new Set(result.actions
|
|
231
243
|
.filter(a => a.type === 'write' || a.type === 'edit')
|
|
232
244
|
.map(a => a.target));
|
|
245
|
+
const activity = describeAgentActivity(result.actions);
|
|
233
246
|
if (!result.success) {
|
|
234
|
-
return `${summariseFixPlan(plan)} The run did not finish: ${result.error ?? 'unknown error'}
|
|
247
|
+
return `${summariseFixPlan(plan)} The run did not finish: ${result.error ?? 'unknown error'}. ${activity}`;
|
|
235
248
|
}
|
|
236
249
|
if (edited.size === 0) {
|
|
237
|
-
return `${summariseFixPlan(plan)} Nothing was changed
|
|
250
|
+
return `${summariseFixPlan(plan)} Nothing was changed. ${activity}`;
|
|
238
251
|
}
|
|
239
252
|
return `${summariseFixPlan(plan)} Edited ${edited.size} file${edited.size === 1 ? '' : 's'}: ${[...edited].join(', ')}.`;
|
|
240
253
|
}
|
|
@@ -63,3 +63,21 @@ export declare function buildFixPlan(issues: ReviewIssue[], options?: FixPlanOpt
|
|
|
63
63
|
export declare function formatFixPrompt(issues: ReviewIssue[]): string;
|
|
64
64
|
/** A one-line summary for the pull request body the action opens. */
|
|
65
65
|
export declare function summariseFixPlan(plan: FixPlan): string;
|
|
66
|
+
/**
|
|
67
|
+
* What the agent actually did, in one line.
|
|
68
|
+
*
|
|
69
|
+
* A fix that changes nothing is the hardest outcome to act on, because the
|
|
70
|
+
* summary that reports it — "the run did not finish", "nothing was changed" —
|
|
71
|
+
* says what did not happen and never what did. Debugging one such run through
|
|
72
|
+
* CI cost two releases and forty minutes of guessing at whether the agent
|
|
73
|
+
* could not find the file, could not write, or was being refused a tool.
|
|
74
|
+
*
|
|
75
|
+
* Counting the action log answers that in the message itself. Failures are
|
|
76
|
+
* called out separately from successes, and one failing detail is quoted,
|
|
77
|
+
* because a refusal reason is usually the whole explanation.
|
|
78
|
+
*/
|
|
79
|
+
export declare function describeAgentActivity(actions: {
|
|
80
|
+
type: string;
|
|
81
|
+
result: string;
|
|
82
|
+
details?: string;
|
|
83
|
+
}[]): string;
|
package/dist/utils/reviewFix.js
CHANGED
|
@@ -139,3 +139,35 @@ export function summariseFixPlan(plan) {
|
|
|
139
139
|
].filter(Boolean);
|
|
140
140
|
return `Attempting ${parts.join(' and ')} across ${plan.files.length} file${plan.files.length === 1 ? '' : 's'}.`;
|
|
141
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* What the agent actually did, in one line.
|
|
144
|
+
*
|
|
145
|
+
* A fix that changes nothing is the hardest outcome to act on, because the
|
|
146
|
+
* summary that reports it — "the run did not finish", "nothing was changed" —
|
|
147
|
+
* says what did not happen and never what did. Debugging one such run through
|
|
148
|
+
* CI cost two releases and forty minutes of guessing at whether the agent
|
|
149
|
+
* could not find the file, could not write, or was being refused a tool.
|
|
150
|
+
*
|
|
151
|
+
* Counting the action log answers that in the message itself. Failures are
|
|
152
|
+
* called out separately from successes, and one failing detail is quoted,
|
|
153
|
+
* because a refusal reason is usually the whole explanation.
|
|
154
|
+
*/
|
|
155
|
+
export function describeAgentActivity(actions) {
|
|
156
|
+
if (actions.length === 0)
|
|
157
|
+
return 'It made no tool calls at all.';
|
|
158
|
+
const byType = new Map();
|
|
159
|
+
for (const action of actions) {
|
|
160
|
+
const tally = byType.get(action.type) ?? { ok: 0, failed: 0 };
|
|
161
|
+
if (action.result === 'error')
|
|
162
|
+
tally.failed++;
|
|
163
|
+
else
|
|
164
|
+
tally.ok++;
|
|
165
|
+
byType.set(action.type, tally);
|
|
166
|
+
}
|
|
167
|
+
const parts = [...byType.entries()]
|
|
168
|
+
.sort((a, b) => (b[1].ok + b[1].failed) - (a[1].ok + a[1].failed))
|
|
169
|
+
.map(([type, { ok, failed }]) => (failed ? `${ok + failed} ${type} (${failed} failed)` : `${ok} ${type}`));
|
|
170
|
+
const firstFailure = actions.find(a => a.result === 'error' && a.details);
|
|
171
|
+
const why = firstFailure ? ` First failure: ${firstFailure.details.slice(0, 200)}` : '';
|
|
172
|
+
return `It made ${actions.length} tool call${actions.length === 1 ? '' : 's'}: ${parts.join(', ')}.${why}`;
|
|
173
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "2.
|
|
1
|
+
export declare const VERSION = "2.24.0";
|
package/dist/version.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// AUTO-GENERATED by scripts/gen-version.js — do not edit by hand.
|
|
2
2
|
// Baked from package.json at build time so the bun-compiled binary reports
|
|
3
3
|
// the right version (it has no package.json on disk to read at runtime).
|
|
4
|
-
export const VERSION = '2.
|
|
4
|
+
export const VERSION = '2.24.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
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",
|