next-issue 2.5.1 → 2.5.2
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/README.md +3 -2
- package/dist/agents.mjs +1 -22
- package/dist/pipeline.mjs +2 -3
- package/dist/prompts.mjs +2 -1
- package/dist/verdict.mjs +13 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,8 +64,9 @@ and exits with code 1. For each issue:
|
|
|
64
64
|
reviewer reads files and runs commands in the worktree, so it can prove a
|
|
65
65
|
finding with the tests or the type check. The harness then throws away every
|
|
66
66
|
change that the reviewer left, so no such change can reach a later commit.
|
|
67
|
-
The reviewer
|
|
68
|
-
finding, each rated `blocking` or `minor`.
|
|
67
|
+
The reviewer ends its last message with its verdict as a json block: a
|
|
68
|
+
summary and one entry per finding, each rated `blocking` or `minor`. A
|
|
69
|
+
reply without a valid block hands the issue to a person. The findings are the only ground
|
|
69
70
|
truth. The reviewer gives no approve or reject field, because such a field
|
|
70
71
|
can contradict the findings. The harness puts the result on the pull
|
|
71
72
|
request.
|
package/dist/agents.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join, sep } from 'node:path';
|
|
2
|
-
import { createAgentSession, DefaultResourceLoader,
|
|
2
|
+
import { createAgentSession, DefaultResourceLoader, getAgentDir, ModelRuntime, SessionManager, } from '@earendil-works/pi-coding-agent';
|
|
3
3
|
import { onStop } from './exec.mjs';
|
|
4
4
|
import { emptyUsage } from './observe.mjs';
|
|
5
5
|
const REVIEWING = {
|
|
@@ -9,7 +9,6 @@ const CODING = {
|
|
|
9
9
|
tools: ['+grep', '+find', '+ls'],
|
|
10
10
|
};
|
|
11
11
|
export { CODING, REVIEWING };
|
|
12
|
-
export const SUBMIT_TOOL = 'submit_result';
|
|
13
12
|
export function isOwnExtension(path) {
|
|
14
13
|
return path.split(sep).includes('pi-next-issue');
|
|
15
14
|
}
|
|
@@ -19,24 +18,6 @@ export async function runAgent(recorder, request) {
|
|
|
19
18
|
const agentDir = runtime.agentDir ?? getAgentDir();
|
|
20
19
|
const started = Date.now();
|
|
21
20
|
log.event('agent.start', { model: request.model, timeoutMs: request.timeoutMs }, 'normal');
|
|
22
|
-
let structured;
|
|
23
|
-
const customTools = [];
|
|
24
|
-
if (request.outputSchema !== undefined) {
|
|
25
|
-
customTools.push(defineTool({
|
|
26
|
-
name: SUBMIT_TOOL,
|
|
27
|
-
label: 'Submit result',
|
|
28
|
-
description: 'Give the result. Call it once, as your last action.',
|
|
29
|
-
parameters: request.outputSchema,
|
|
30
|
-
async execute(_toolCallId, params) {
|
|
31
|
-
structured = params;
|
|
32
|
-
return {
|
|
33
|
-
content: [{ type: 'text', text: 'Result saved.' }],
|
|
34
|
-
details: {},
|
|
35
|
-
terminate: true,
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
21
|
const resourceLoader = new DefaultResourceLoader({
|
|
41
22
|
cwd: request.cwd,
|
|
42
23
|
agentDir,
|
|
@@ -58,7 +39,6 @@ export async function runAgent(recorder, request) {
|
|
|
58
39
|
resourceLoader,
|
|
59
40
|
sessionManager: SessionManager.create(request.cwd, recorder.sessionDir),
|
|
60
41
|
tools: request.profile.tools,
|
|
61
|
-
customTools,
|
|
62
42
|
modelRuntime,
|
|
63
43
|
...(runtime.settingsManager === undefined ? {} : { settingsManager: runtime.settingsManager }),
|
|
64
44
|
...(model === undefined ? {} : { model }),
|
|
@@ -105,7 +85,6 @@ export async function runAgent(recorder, request) {
|
|
|
105
85
|
costUsd: stats.cost,
|
|
106
86
|
model: name,
|
|
107
87
|
sessionId: stats.sessionId,
|
|
108
|
-
structured,
|
|
109
88
|
};
|
|
110
89
|
}
|
|
111
90
|
finally {
|
package/dist/pipeline.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { message } from './observe.mjs';
|
|
|
7
7
|
import { capDiff, fixPrompt, hookPrompt, implementPrompt, parseCommitSubject, parseUnrelated, reviewPrompt, } from './prompts.mjs';
|
|
8
8
|
import { detectSetupCommand } from './setup.mjs';
|
|
9
9
|
import { dropState, readState, resetState, savedIssues, writeState, } from './state.mjs';
|
|
10
|
-
import { blockingFindings, fingerprint, formatFindings, formatVerdict, openFindings,
|
|
10
|
+
import { blockingFindings, fingerprint, formatFindings, formatVerdict, openFindings, parseVerdict, } from './verdict.mjs';
|
|
11
11
|
export const PORTS = {
|
|
12
12
|
addWorktree,
|
|
13
13
|
commentOnIssue,
|
|
@@ -362,12 +362,11 @@ async function reviewRound(job) {
|
|
|
362
362
|
profile: REVIEWING,
|
|
363
363
|
model: config.models.reviewer,
|
|
364
364
|
timeoutMs: config.agentTimeoutMinutes * 60_000,
|
|
365
|
-
outputSchema: VERDICT_SCHEMA,
|
|
366
365
|
}));
|
|
367
366
|
if (await ports.discardChanges(worktree, before)) {
|
|
368
367
|
log.event('review.discard', { round: state.reviewRounds }, 'quiet');
|
|
369
368
|
}
|
|
370
|
-
const verdict =
|
|
369
|
+
const verdict = parseVerdict(review.text);
|
|
371
370
|
if (verdict === undefined) {
|
|
372
371
|
await escalate(job, 'The reviewer gave no verdict.');
|
|
373
372
|
return { done: true, report: await finish(job, 'needs-human', 'no verdict') };
|
package/dist/prompts.mjs
CHANGED
|
@@ -60,7 +60,8 @@ export function reviewPrompt(issue, comments, diff, earlier) {
|
|
|
60
60
|
'The harness discards each change that you make, so a change is only lost work.',
|
|
61
61
|
scope,
|
|
62
62
|
'Mark a finding blocking only for a wrong result, a missing part of the issue, a regression or a broken convention.',
|
|
63
|
-
'
|
|
63
|
+
'End your final message with the result as a json block in a ```json fence, in the form {"summary": "<short summary>", "findings": [{"severity": "blocking" or "minor", "detail": "<what is wrong and where>"}]}.',
|
|
64
|
+
'Give one entry per finding, and an empty list when nothing is wrong. The findings decide the outcome, so a problem that you leave out counts as no problem.',
|
|
64
65
|
].join('\n\n');
|
|
65
66
|
}
|
|
66
67
|
const UNRELATED_RULE = [
|
package/dist/verdict.mjs
CHANGED
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
export const VERDICT_SCHEMA = {
|
|
2
|
-
type: 'object',
|
|
3
|
-
properties: {
|
|
4
|
-
summary: { type: 'string', description: 'Short summary of the review' },
|
|
5
|
-
findings: {
|
|
6
|
-
type: 'array',
|
|
7
|
-
description: 'One entry per problem. Empty when nothing is wrong.',
|
|
8
|
-
items: {
|
|
9
|
-
type: 'object',
|
|
10
|
-
properties: {
|
|
11
|
-
severity: {
|
|
12
|
-
type: 'string',
|
|
13
|
-
enum: ['blocking', 'minor'],
|
|
14
|
-
description: 'blocking for a wrong result, a missing part of the issue, a regression or a broken convention; minor for taste and style',
|
|
15
|
-
},
|
|
16
|
-
detail: { type: 'string', description: 'What is wrong and where' },
|
|
17
|
-
},
|
|
18
|
-
required: ['severity', 'detail'],
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
required: ['summary', 'findings'],
|
|
23
|
-
};
|
|
24
1
|
const SEVERITIES = ['blocking', 'minor'];
|
|
25
2
|
export function readVerdict(value) {
|
|
26
3
|
if (typeof value !== 'object' || value === null) {
|
|
@@ -45,6 +22,19 @@ export function readVerdict(value) {
|
|
|
45
22
|
}
|
|
46
23
|
return { summary, findings: checked };
|
|
47
24
|
}
|
|
25
|
+
const JSON_BLOCK = /```json[^\n]*\n([\s\S]*?)```/g;
|
|
26
|
+
export function parseVerdict(text) {
|
|
27
|
+
const last = [...text.matchAll(JSON_BLOCK)].at(-1);
|
|
28
|
+
if (last === undefined) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
return readVerdict(JSON.parse(last[1]));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
48
38
|
export function blockingFindings(verdict) {
|
|
49
39
|
return verdict.findings.filter((finding) => finding.severity === 'blocking');
|
|
50
40
|
}
|