tau-coding-agent 0.1.5 → 0.1.6
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/extensions/answer.ts +11 -5
- package/extensions/btw.ts +73 -2
- package/extensions/insights.ts +38 -1
- package/extensions/loop.ts +14 -3
- package/extensions/memory.ts +37 -14
- package/extensions/review/models.ts +1 -1
- package/extensions/review/prompts.ts +4 -4
- package/extensions/review/submit-review-tool.ts +1 -5
- package/extensions/review/submit-triage-tool.ts +1 -0
- package/extensions/sandbox/index.ts +595 -101
- package/extensions/sandbox/macos-sandbox-shell.mjs +202 -0
- package/extensions/usage/anthropic.ts +5 -6
- package/extensions/usage/github-copilot.ts +30 -9
- package/extensions/usage/index.ts +1 -1
- package/extensions/usage/openai-codex.ts +2 -2
- package/extensions/websearch/providers/pi-model.shared.ts +19 -1
- package/package.json +2 -2
- package/skills/oracle/SKILL.md +3 -3
- package/skills/oracle/scripts/oracle +54 -50
package/extensions/answer.ts
CHANGED
|
@@ -95,14 +95,19 @@ interface ModelRequestAuth {
|
|
|
95
95
|
model: Model<Api>;
|
|
96
96
|
apiKey?: string;
|
|
97
97
|
headers?: Record<string, string>;
|
|
98
|
+
env?: Record<string, string>;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
type ModelAuthLookup = {
|
|
101
102
|
find: (provider: string, modelId: string) => Model<Api> | undefined;
|
|
102
|
-
getApiKeyAndHeaders: (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
getApiKeyAndHeaders: (model: Model<Api>) => Promise<
|
|
104
|
+
| {
|
|
105
|
+
ok: true;
|
|
106
|
+
apiKey?: string;
|
|
107
|
+
headers?: Record<string, string>;
|
|
108
|
+
env?: Record<string, string>;
|
|
109
|
+
}
|
|
110
|
+
| { ok: false; error: string }
|
|
106
111
|
>;
|
|
107
112
|
};
|
|
108
113
|
|
|
@@ -149,7 +154,7 @@ async function resolveModelRequestAuth(
|
|
|
149
154
|
model: Model<Api>,
|
|
150
155
|
): Promise<ModelRequestAuth | null> {
|
|
151
156
|
const auth = await modelRegistry.getApiKeyAndHeaders(model);
|
|
152
|
-
return auth.ok ? { model, apiKey: auth.apiKey, headers: auth.headers } : null;
|
|
157
|
+
return auth.ok ? { model, apiKey: auth.apiKey, headers: auth.headers, env: auth.env } : null;
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
/**
|
|
@@ -542,6 +547,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
542
547
|
{
|
|
543
548
|
apiKey: extractionSelection.apiKey,
|
|
544
549
|
headers: extractionSelection.headers,
|
|
550
|
+
env: extractionSelection.env,
|
|
545
551
|
signal: loader.signal,
|
|
546
552
|
},
|
|
547
553
|
);
|
package/extensions/btw.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
InMemoryCredentialStore,
|
|
4
|
+
type AssistantMessage,
|
|
5
|
+
type Provider,
|
|
6
|
+
type ThinkingLevel,
|
|
7
|
+
} from "@earendil-works/pi-ai";
|
|
3
8
|
import {
|
|
4
9
|
DefaultResourceLoader,
|
|
10
|
+
ModelRuntime,
|
|
5
11
|
SessionManager,
|
|
6
12
|
buildSessionContext,
|
|
7
13
|
createAgentSession,
|
|
@@ -59,6 +65,15 @@ type ActiveBtwRequest = {
|
|
|
59
65
|
abort?: () => Promise<void>;
|
|
60
66
|
};
|
|
61
67
|
|
|
68
|
+
type BtwModelRegistry = ExtensionCommandContext["modelRegistry"];
|
|
69
|
+
|
|
70
|
+
type BtwModelRuntimeState = {
|
|
71
|
+
runtime: ModelRuntime;
|
|
72
|
+
providers: Map<string, Provider>;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const btwModelRuntimes = new WeakMap<BtwModelRegistry, Promise<BtwModelRuntimeState>>();
|
|
76
|
+
|
|
62
77
|
export default function btwExtension(pi: ExtensionAPI): void {
|
|
63
78
|
let activeRequest: ActiveBtwRequest | null = null;
|
|
64
79
|
let statusTimer: ReturnType<typeof setInterval> | null = null;
|
|
@@ -227,11 +242,12 @@ async function runBtwRequest(
|
|
|
227
242
|
});
|
|
228
243
|
await resourceLoader.reload();
|
|
229
244
|
|
|
245
|
+
const modelRuntime = await createBtwModelRuntime(snapshot.modelRegistry, snapshot.model.provider);
|
|
230
246
|
const { session } = await createAgentSession({
|
|
231
247
|
cwd: snapshot.cwd,
|
|
232
248
|
model: snapshot.model,
|
|
233
249
|
thinkingLevel: snapshot.thinkingLevel,
|
|
234
|
-
|
|
250
|
+
modelRuntime,
|
|
235
251
|
resourceLoader,
|
|
236
252
|
tools: BTW_TOOL_NAMES,
|
|
237
253
|
sessionManager,
|
|
@@ -269,6 +285,61 @@ async function runBtwRequest(
|
|
|
269
285
|
}
|
|
270
286
|
}
|
|
271
287
|
|
|
288
|
+
async function createBtwModelRuntime(
|
|
289
|
+
modelRegistry: BtwModelRegistry,
|
|
290
|
+
providerId: string,
|
|
291
|
+
): Promise<ModelRuntime> {
|
|
292
|
+
const provider = modelRegistry.getProvider(providerId);
|
|
293
|
+
if (!provider) {
|
|
294
|
+
throw new Error(`Model provider is unavailable: ${providerId}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const state = await getBtwModelRuntimeState(modelRegistry);
|
|
298
|
+
if (state.providers.get(providerId) !== provider) {
|
|
299
|
+
state.runtime.registerNativeProvider(withParentAuth(provider, modelRegistry));
|
|
300
|
+
state.providers.set(providerId, provider);
|
|
301
|
+
}
|
|
302
|
+
return state.runtime;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function getBtwModelRuntimeState(
|
|
306
|
+
modelRegistry: BtwModelRegistry,
|
|
307
|
+
): Promise<BtwModelRuntimeState> {
|
|
308
|
+
let state = btwModelRuntimes.get(modelRegistry);
|
|
309
|
+
if (!state) {
|
|
310
|
+
state = ModelRuntime.create({
|
|
311
|
+
credentials: new InMemoryCredentialStore(),
|
|
312
|
+
modelsPath: null,
|
|
313
|
+
}).then((runtime) => ({ runtime, providers: new Map() }));
|
|
314
|
+
btwModelRuntimes.set(modelRegistry, state);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
return await state;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (btwModelRuntimes.get(modelRegistry) === state) {
|
|
321
|
+
btwModelRuntimes.delete(modelRegistry);
|
|
322
|
+
}
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function withParentAuth(provider: Provider, modelRegistry: BtwModelRegistry): Provider {
|
|
328
|
+
return {
|
|
329
|
+
...provider,
|
|
330
|
+
refreshModels: undefined, // The parent runtime owns model catalog refreshes.
|
|
331
|
+
auth: {
|
|
332
|
+
...provider.auth,
|
|
333
|
+
apiKey: {
|
|
334
|
+
name: `${provider.name} session authentication`,
|
|
335
|
+
async resolve() {
|
|
336
|
+
return modelRegistry.getProviderAuth(provider.id);
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
272
343
|
function seedSessionManager(sessionManager: SessionManager, messages: AgentMessage[]): void {
|
|
273
344
|
type SessionMessageInput = Parameters<SessionManager["appendMessage"]>[0];
|
|
274
345
|
|
package/extensions/insights.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
} from "@earendil-works/pi-tui";
|
|
28
28
|
import { createHash, randomBytes } from "node:crypto";
|
|
29
29
|
import fs from "node:fs/promises";
|
|
30
|
+
import os from "node:os";
|
|
30
31
|
import path from "node:path";
|
|
31
32
|
|
|
32
33
|
// --- Constants ---
|
|
@@ -141,6 +142,7 @@ type ConfiguredModelSelection = {
|
|
|
141
142
|
model: Model<Api>;
|
|
142
143
|
apiKey?: string;
|
|
143
144
|
headers?: Record<string, string>;
|
|
145
|
+
env?: Record<string, string>;
|
|
144
146
|
};
|
|
145
147
|
|
|
146
148
|
type ReadonlySessionManager = Pick<
|
|
@@ -562,9 +564,37 @@ async function runInsightsCommand(
|
|
|
562
564
|
return;
|
|
563
565
|
}
|
|
564
566
|
|
|
567
|
+
let reportPath: string | undefined;
|
|
568
|
+
try {
|
|
569
|
+
reportPath = await saveInsightsReport(result.reportMarkdown, result.generatedAt);
|
|
570
|
+
} catch (error) {
|
|
571
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
572
|
+
ctx.ui.notify(`Failed to save insights report: ${message}`, "warning");
|
|
573
|
+
}
|
|
574
|
+
|
|
565
575
|
await ctx.ui.custom<void>((tui, theme, _kb, done) => {
|
|
566
576
|
return new InsightsReportComponent(result, tui, theme, done);
|
|
567
577
|
});
|
|
578
|
+
|
|
579
|
+
if (reportPath) {
|
|
580
|
+
ctx.ui.notify(`Insights report saved to ${reportPath}`, "info");
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function saveInsightsReport(reportMarkdown: string, generatedAt: string): Promise<string> {
|
|
585
|
+
const timestamp = generatedAt.replace(/[:.]/g, "-");
|
|
586
|
+
const reportPath = path.join(os.tmpdir(), `tau-insights-${timestamp}.md`);
|
|
587
|
+
const reportFile = await fs.open(reportPath, "wx", 0o600);
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
await reportFile.writeFile(`${reportMarkdown.trimEnd()}\n`, "utf8");
|
|
591
|
+
await reportFile.close();
|
|
592
|
+
return reportPath;
|
|
593
|
+
} catch (error) {
|
|
594
|
+
await reportFile.close().catch(() => undefined);
|
|
595
|
+
await fs.rm(reportPath, { force: true }).catch(() => undefined);
|
|
596
|
+
throw error;
|
|
597
|
+
}
|
|
568
598
|
}
|
|
569
599
|
|
|
570
600
|
async function runInsightsPipeline(
|
|
@@ -749,7 +779,12 @@ async function getConfiguredModelSelection(
|
|
|
749
779
|
|
|
750
780
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
751
781
|
if (!auth.ok) return null;
|
|
752
|
-
return {
|
|
782
|
+
return {
|
|
783
|
+
model: ctx.model,
|
|
784
|
+
apiKey: auth.apiKey,
|
|
785
|
+
headers: auth.headers,
|
|
786
|
+
env: auth.env,
|
|
787
|
+
};
|
|
753
788
|
}
|
|
754
789
|
|
|
755
790
|
async function listTargets(
|
|
@@ -1342,6 +1377,7 @@ async function extractFacet(
|
|
|
1342
1377
|
{
|
|
1343
1378
|
apiKey: selection.apiKey,
|
|
1344
1379
|
headers: selection.headers,
|
|
1380
|
+
env: selection.env,
|
|
1345
1381
|
signal,
|
|
1346
1382
|
},
|
|
1347
1383
|
);
|
|
@@ -1514,6 +1550,7 @@ async function synthesizeReport(
|
|
|
1514
1550
|
{
|
|
1515
1551
|
apiKey: selection.apiKey,
|
|
1516
1552
|
headers: selection.headers,
|
|
1553
|
+
env: selection.env,
|
|
1517
1554
|
signal,
|
|
1518
1555
|
},
|
|
1519
1556
|
);
|
package/extensions/loop.ts
CHANGED
|
@@ -54,6 +54,7 @@ type ModelRequestAuth = {
|
|
|
54
54
|
model: Model<Api>;
|
|
55
55
|
apiKey?: string;
|
|
56
56
|
headers?: Record<string, string>;
|
|
57
|
+
env?: Record<string, string>;
|
|
57
58
|
};
|
|
58
59
|
|
|
59
60
|
async function withPromptSignal<T>(pi: ExtensionAPI, run: () => Promise<T>): Promise<T> {
|
|
@@ -137,13 +138,20 @@ async function selectSummaryModel(ctx: ExtensionContext): Promise<ModelRequestAu
|
|
|
137
138
|
|
|
138
139
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(candidate);
|
|
139
140
|
if (auth.ok) {
|
|
140
|
-
return {
|
|
141
|
+
return {
|
|
142
|
+
model: candidate,
|
|
143
|
+
apiKey: auth.apiKey,
|
|
144
|
+
headers: auth.headers,
|
|
145
|
+
env: auth.env,
|
|
146
|
+
};
|
|
141
147
|
}
|
|
142
148
|
}
|
|
143
149
|
}
|
|
144
150
|
|
|
145
151
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
146
|
-
return auth.ok
|
|
152
|
+
return auth.ok
|
|
153
|
+
? { model: ctx.model, apiKey: auth.apiKey, headers: auth.headers, env: auth.env }
|
|
154
|
+
: null;
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
async function summarizeBreakoutCondition(
|
|
@@ -165,7 +173,7 @@ async function summarizeBreakoutCondition(
|
|
|
165
173
|
const response = await complete(
|
|
166
174
|
selection.model,
|
|
167
175
|
{ systemPrompt: SUMMARY_SYSTEM_PROMPT, messages: [userMessage] },
|
|
168
|
-
{ apiKey: selection.apiKey, headers: selection.headers },
|
|
176
|
+
{ apiKey: selection.apiKey, headers: selection.headers, env: selection.env },
|
|
169
177
|
);
|
|
170
178
|
|
|
171
179
|
if (response.stopReason === "aborted" || response.stopReason === "error") {
|
|
@@ -518,6 +526,9 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
518
526
|
auth.headers,
|
|
519
527
|
instructionParts,
|
|
520
528
|
event.signal,
|
|
529
|
+
ctx.thinkingLevel,
|
|
530
|
+
undefined, // Use Pi's default summarization stream.
|
|
531
|
+
auth.env,
|
|
521
532
|
);
|
|
522
533
|
return { compaction };
|
|
523
534
|
} catch (error) {
|
package/extensions/memory.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { complete } from "@earendil-works/pi-ai/compat";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
StringEnum,
|
|
4
|
+
type Api,
|
|
5
|
+
type Model,
|
|
6
|
+
type Usage,
|
|
7
|
+
type UserMessage,
|
|
8
|
+
} from "@earendil-works/pi-ai";
|
|
3
9
|
import {
|
|
4
10
|
defineTool,
|
|
5
11
|
withFileMutationQueue,
|
|
@@ -171,6 +177,11 @@ type DreamResult = {
|
|
|
171
177
|
summaryPath: string | null;
|
|
172
178
|
};
|
|
173
179
|
|
|
180
|
+
type DreamExecution = {
|
|
181
|
+
result: DreamResult;
|
|
182
|
+
usage?: Usage;
|
|
183
|
+
};
|
|
184
|
+
|
|
174
185
|
type AutoDreamStatus = {
|
|
175
186
|
coreLines: number;
|
|
176
187
|
coreChars: number;
|
|
@@ -189,6 +200,7 @@ type ModelSelection = {
|
|
|
189
200
|
model: Model<Api>;
|
|
190
201
|
apiKey?: string;
|
|
191
202
|
headers?: Record<string, string>;
|
|
203
|
+
env?: Record<string, string>;
|
|
192
204
|
};
|
|
193
205
|
|
|
194
206
|
class MemoryReadmeMissingError extends Error {
|
|
@@ -204,12 +216,17 @@ async function toolDream(
|
|
|
204
216
|
cwd: string,
|
|
205
217
|
ctx: ExtensionContext,
|
|
206
218
|
reason?: string,
|
|
207
|
-
): Promise<{
|
|
219
|
+
): Promise<{
|
|
220
|
+
content: Array<{ type: "text"; text: string }>;
|
|
221
|
+
details: DreamResult;
|
|
222
|
+
usage?: Usage;
|
|
223
|
+
}> {
|
|
208
224
|
try {
|
|
209
|
-
const
|
|
225
|
+
const execution = await runMemoryDream(cwd, ctx, reason);
|
|
210
226
|
return {
|
|
211
|
-
content: [{ type: "text", text: result.summary }],
|
|
212
|
-
details: result,
|
|
227
|
+
content: [{ type: "text", text: execution.result.summary }],
|
|
228
|
+
details: execution.result,
|
|
229
|
+
usage: execution.usage,
|
|
213
230
|
};
|
|
214
231
|
} catch (error) {
|
|
215
232
|
if (error instanceof MemoryReadmeMissingError) {
|
|
@@ -369,7 +386,7 @@ async function runMemoryDream(
|
|
|
369
386
|
cwd: string,
|
|
370
387
|
ctx: ExtensionContext,
|
|
371
388
|
reason?: string,
|
|
372
|
-
): Promise<
|
|
389
|
+
): Promise<DreamExecution> {
|
|
373
390
|
const paths = getMemoryPaths(cwd);
|
|
374
391
|
const lockPaths = [
|
|
375
392
|
paths.readmeFile,
|
|
@@ -394,13 +411,15 @@ async function runMemoryDream(
|
|
|
394
411
|
const summary = "Memory dream: nothing to consolidate.";
|
|
395
412
|
notify(ctx, summary, "info");
|
|
396
413
|
return {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
414
|
+
result: {
|
|
415
|
+
summary,
|
|
416
|
+
updatedBlocks: [],
|
|
417
|
+
consumedLogs: 0,
|
|
418
|
+
consumedCompactions: 0,
|
|
419
|
+
lastDreamAt: snapshot.state?.lastDreamAt ?? nowIso(),
|
|
420
|
+
lastDreamedLogAt: snapshot.state?.lastDreamedLogAt ?? null,
|
|
421
|
+
summaryPath: null,
|
|
422
|
+
},
|
|
404
423
|
};
|
|
405
424
|
}
|
|
406
425
|
|
|
@@ -414,6 +433,7 @@ async function runMemoryDream(
|
|
|
414
433
|
{
|
|
415
434
|
apiKey: selection.apiKey,
|
|
416
435
|
headers: selection.headers,
|
|
436
|
+
env: selection.env,
|
|
417
437
|
signal: ctx.signal,
|
|
418
438
|
},
|
|
419
439
|
);
|
|
@@ -464,7 +484,7 @@ async function runMemoryDream(
|
|
|
464
484
|
);
|
|
465
485
|
}
|
|
466
486
|
|
|
467
|
-
|
|
487
|
+
const result = await withMemoryMutationQueue(lockPaths, async () => {
|
|
468
488
|
ensureMemoryInitialized(await pathExists(paths.memoryRoot));
|
|
469
489
|
|
|
470
490
|
const currentReplay = await collectDreamReplay(cwd);
|
|
@@ -512,6 +532,8 @@ async function runMemoryDream(
|
|
|
512
532
|
summaryPath,
|
|
513
533
|
};
|
|
514
534
|
});
|
|
535
|
+
|
|
536
|
+
return { result, usage: response.usage };
|
|
515
537
|
}
|
|
516
538
|
|
|
517
539
|
async function loadMemoryPrompt(cwd: string): Promise<string | undefined> {
|
|
@@ -962,6 +984,7 @@ async function selectDreamModel(ctx: ExtensionContext): Promise<ModelSelection>
|
|
|
962
984
|
model: ctx.model,
|
|
963
985
|
apiKey: auth.apiKey,
|
|
964
986
|
headers: auth.headers,
|
|
987
|
+
env: auth.env,
|
|
965
988
|
};
|
|
966
989
|
}
|
|
967
990
|
|
|
@@ -510,7 +510,7 @@ export async function resolveModels(
|
|
|
510
510
|
requestedModels: string[],
|
|
511
511
|
currentThinkingLevel: ReviewThinkingLevel,
|
|
512
512
|
): Promise<ResolvedReviewModel[]> {
|
|
513
|
-
ctx.modelRegistry.refresh();
|
|
513
|
+
await ctx.modelRegistry.refresh();
|
|
514
514
|
const currentProvider = typeof ctx.model?.provider === "string" ? ctx.model.provider : undefined;
|
|
515
515
|
const currentModelId = ctx.model?.id;
|
|
516
516
|
const allModels = ctx.modelRegistry.getAll();
|
|
@@ -63,7 +63,7 @@ Only flag issues with a concrete exploit path or trust-boundary failure introduc
|
|
|
63
63
|
suffix: " specializing in reuse analysis",
|
|
64
64
|
qualifier: " reuse",
|
|
65
65
|
context: `Review the changes for potential reuse issues, such as:
|
|
66
|
-
1. Search for existing capabilities that could replace newly written code: standard library APIs, native platform features, already-installed dependencies, and existing utilities/helpers.
|
|
66
|
+
1. Search for existing capabilities that could replace newly written code: standard library APIs, native platform features, already-installed dependencies, and existing utilities/helpers. Search for relevant names and behavior, then go beyond string matches by inspecting adjacent files, utility files and directories, and shared modules.
|
|
67
67
|
2. Flag any new function that duplicates existing functionality. Suggest the existing function, API, or feature to use instead.
|
|
68
68
|
3. Flag any inline logic that could use an existing capability — hand-rolled standard-library behavior, string manipulation, manual path handling, custom environment checks, ad-hoc type guards, native platform features, and similar patterns are common candidates.
|
|
69
69
|
4. Flag new dependencies when the standard library, runtime/platform, or an already-installed dependency provides the same capability or behavior.
|
|
@@ -76,7 +76,7 @@ Only flag issues with a concrete exploit path or trust-boundary failure introduc
|
|
|
76
76
|
1. Redundant state: state that duplicates existing state, cached values that could be derived, observers/effects that could be direct calls.
|
|
77
77
|
2. Parameter sprawl: adding new parameters to a function instead of generalizing or restructuring existing ones.
|
|
78
78
|
3. Copy-paste with slight variation: near-duplicate code blocks that should be unified with a shared abstraction.
|
|
79
|
-
4.
|
|
79
|
+
4. Layering and leaky abstractions: logic that lives in the wrong layer, exposes internal details that should be encapsulated, or breaks existing abstraction boundaries.
|
|
80
80
|
5. Stringly-typed code: using raw strings where constants, enums (string unions), or branded types already exist in the codebase.
|
|
81
81
|
6. Simplicity/YAGNI: prefer simple, direct solutions over wrappers, abstractions, configuration, options, extensibility, or scaffolding without clear reuse value or explicit need. Prefer deletion or direct code until the second use appears.
|
|
82
82
|
7. Shrinkage: flag code that preserves behavior with fewer branches, lines, moving parts, or custom helpers. Do not shrink away input validation at trust boundaries, data-loss error handling, security measures, or accessibility basics.
|
|
@@ -141,10 +141,10 @@ export function buildProjectReviewGuidelinesSection(projectGuidelines: string |
|
|
|
141
141
|
export const SUBMIT_TOOL_RETRY_PROMPT = `You did not call {SUBMIT_TOOL} as instructed. You must call that tool exactly once with the final payload. Do not output any text, only call the {SUBMIT_TOOL} when you're done.`;
|
|
142
142
|
|
|
143
143
|
export const REVIEW_OUTPUT_CONTRACT_PROMPT = `Requirements:
|
|
144
|
-
- Never output findings
|
|
144
|
+
- Never output findings as text or write them to files.
|
|
145
145
|
- Always call submit_review exactly once as your final action.
|
|
146
146
|
- If no issues are found, pass an empty array of findings to submit_review.
|
|
147
|
-
-
|
|
147
|
+
- Omit uncertain or speculative findings.`;
|
|
148
148
|
|
|
149
149
|
export const REVIEW_FOCUS_PROMPT = `You are an expert code reviewer{FOCUS_SUFFIX}.
|
|
150
150
|
|
|
@@ -35,11 +35,6 @@ const SUBMIT_REVIEW_PARAMS = Type.Object(
|
|
|
35
35
|
),
|
|
36
36
|
{ description: "Final review findings" },
|
|
37
37
|
),
|
|
38
|
-
note: Type.Optional(
|
|
39
|
-
Type.String({
|
|
40
|
-
description: "Optional note when submitting zero findings or explaining uncertainty.",
|
|
41
|
-
}),
|
|
42
|
-
),
|
|
43
38
|
},
|
|
44
39
|
{ additionalProperties: false },
|
|
45
40
|
);
|
|
@@ -51,6 +46,7 @@ const submitReviewTool = defineTool({
|
|
|
51
46
|
promptSnippet: "Submit the final review findings",
|
|
52
47
|
promptGuidelines: ["Use submit_review exactly once as your final action."],
|
|
53
48
|
parameters: SUBMIT_REVIEW_PARAMS,
|
|
49
|
+
constrainedSampling: { type: "json_schema", strict: "prefer" },
|
|
54
50
|
|
|
55
51
|
async execute(_toolCallId, params) {
|
|
56
52
|
return {
|
|
@@ -44,6 +44,7 @@ const submitTriageTool = defineTool({
|
|
|
44
44
|
promptSnippet: "Submit the final PR feedback triage",
|
|
45
45
|
promptGuidelines: ["Use submit_triage exactly once as your final action."],
|
|
46
46
|
parameters: SUBMIT_TRIAGE_PARAMS,
|
|
47
|
+
constrainedSampling: { type: "json_schema", strict: "prefer" },
|
|
47
48
|
|
|
48
49
|
async execute(_toolCallId, params) {
|
|
49
50
|
return {
|