@vibes.diy/api-svc 14.3.94 → 14.3.96
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/analytics.d.ts +8 -0
- package/analytics.js +12 -0
- package/analytics.js.map +1 -1
- package/billing.d.ts +1 -0
- package/billing.js +5 -1
- package/billing.js.map +1 -1
- package/create-handler.js +8 -0
- package/create-handler.js.map +1 -1
- package/evento-handler-manifest.js +2 -0
- package/evento-handler-manifest.js.map +1 -1
- package/guest-claim-handoff-token.d.ts +21 -0
- package/guest-claim-handoff-token.js +55 -0
- package/guest-claim-handoff-token.js.map +1 -0
- package/intern/agent-session.d.ts +24 -24
- package/intern/backend-egress.d.ts +1 -0
- package/intern/backend-egress.js +7 -1
- package/intern/backend-egress.js.map +1 -1
- package/intern/builder-close-overlay.d.ts +2 -0
- package/intern/builder-close-overlay.js +16 -0
- package/intern/builder-close-overlay.js.map +1 -0
- package/intern/builder-refusal-fallback.d.ts +21 -0
- package/intern/builder-refusal-fallback.js +23 -0
- package/intern/builder-refusal-fallback.js.map +1 -0
- package/intern/components/vibe-page.js +9 -1
- package/intern/components/vibe-page.js.map +1 -1
- package/intern/fetch-url-tool.d.ts +2 -1
- package/intern/fetch-url-tool.js +1 -0
- package/intern/fetch-url-tool.js.map +1 -1
- package/intern/get-model-defaults.d.ts +2 -1
- package/intern/get-model-defaults.js +15 -6
- package/intern/get-model-defaults.js.map +1 -1
- package/intern/get-vibe-route-hints.d.ts +2 -2
- package/intern/get-vibe-route-hints.js +3 -7
- package/intern/get-vibe-route-hints.js.map +1 -1
- package/intern/guest-mask.d.ts +6 -0
- package/intern/guest-mask.js +12 -0
- package/intern/guest-mask.js.map +1 -1
- package/intern/in-flight-turns.js.map +1 -1
- package/intern/instant-build.d.ts +41 -5
- package/intern/instant-build.js +233 -31
- package/intern/instant-build.js.map +1 -1
- package/intern/load-selected-backend.d.ts +1 -0
- package/intern/load-selected-backend.js +1 -1
- package/intern/load-selected-backend.js.map +1 -1
- package/intern/opens-check.js +3 -0
- package/intern/opens-check.js.map +1 -1
- package/intern/prompt-assembly.js +3 -0
- package/intern/prompt-assembly.js.map +1 -1
- package/intern/save-repair-io.d.ts +1 -0
- package/intern/save-repair-io.js +1 -1
- package/intern/save-repair-io.js.map +1 -1
- package/intern/turn-error-kind.d.ts +2 -0
- package/intern/turn-error-kind.js +18 -0
- package/intern/turn-error-kind.js.map +1 -0
- package/intern/turn-size.d.ts +7 -0
- package/intern/turn-size.js +45 -0
- package/intern/turn-size.js.map +1 -0
- package/intern/work-tools.js +3 -0
- package/intern/work-tools.js.map +1 -1
- package/models.json +42 -20
- package/package.json +13 -13
- package/public/agent-turn-action.js +3 -0
- package/public/agent-turn-action.js.map +1 -1
- package/public/app-settings-snapshot.js +2 -13
- package/public/app-settings-snapshot.js.map +1 -1
- package/public/claim-guest-principal.js +13 -6
- package/public/claim-guest-principal.js.map +1 -1
- package/public/codegen-turn-route.d.ts +2 -1
- package/public/codegen-turn-route.js +1 -0
- package/public/codegen-turn-route.js.map +1 -1
- package/public/ensure-user-settings.js +6 -3
- package/public/ensure-user-settings.js.map +1 -1
- package/public/fork-app.js +1 -4
- package/public/fork-app.js.map +1 -1
- package/public/get-app-by-fsid.js +8 -1
- package/public/get-app-by-fsid.js.map +1 -1
- package/public/handle-prompt-context.d.ts +3 -1
- package/public/handle-prompt-context.js +10 -1
- package/public/handle-prompt-context.js.map +1 -1
- package/public/handler-llm-request.d.ts +3 -1
- package/public/handler-llm-request.js +2 -1
- package/public/handler-llm-request.js.map +1 -1
- package/public/list-app-models.js +7 -6
- package/public/list-app-models.js.map +1 -1
- package/public/llm-dispatch-fallback.d.ts +1 -1
- package/public/mint-guest-claim-handoff.d.ts +3 -0
- package/public/mint-guest-claim-handoff.js +37 -0
- package/public/mint-guest-claim-handoff.js.map +1 -0
- package/public/prompt-chat-section.js +1 -0
- package/public/prompt-chat-section.js.map +1 -1
- package/public/prompt-section-actions.js +4 -0
- package/public/prompt-section-actions.js.map +1 -1
- package/public/prompt-turn-settled.js +8 -0
- package/public/prompt-turn-settled.js.map +1 -1
- package/public/prompt-turn-types.d.ts +2 -0
- package/public/request-flow.js.map +1 -1
- package/public/stream-assembly.d.ts +5 -2
- package/public/stream-assembly.js +104 -5
- package/public/stream-assembly.js.map +1 -1
- package/types.d.ts +2 -0
- package/types.js.map +1 -1
- package/usage-report/codegen-delivery-alert-util.d.ts +37 -1
- package/usage-report/codegen-delivery-alert-util.js +157 -28
- package/usage-report/codegen-delivery-alert-util.js.map +1 -1
- package/usage-report/codegen-delivery-alert.js +17 -3
- package/usage-report/codegen-delivery-alert.js.map +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Result } from "@adviser/cement";
|
|
2
2
|
import { type LLMMessage, type SseUsage } from "@vibes.diy/call-ai-v2";
|
|
3
|
+
import { type InstantRepairModelName } from "@vibes.diy/api-types";
|
|
3
4
|
import type { VibesApiSQLCtx } from "../types.js";
|
|
4
5
|
import { type SynthLook } from "./import-synth.js";
|
|
5
6
|
import { type OpensFinding } from "./opens-check.js";
|
|
@@ -7,7 +8,7 @@ import { type IntactnessDefect, type LineBounds } from "./repair-ladder.js";
|
|
|
7
8
|
import { MercuryCircuitBreaker } from "./mercury-resilience.js";
|
|
8
9
|
export type InstantCandidateName = "mercury" | "haiku";
|
|
9
10
|
export declare const INSTANT_MERCURY_FANOUT = 4;
|
|
10
|
-
export type InstantRepairMode = "router" | "ladder" | "reemit";
|
|
11
|
+
export type InstantRepairMode = "sr" | "router" | "ladder" | "reemit";
|
|
11
12
|
export declare const INSTANT_REPAIR_MODE: InstantRepairMode;
|
|
12
13
|
export declare const INSTANT_LADDER_ROUNDS = 4;
|
|
13
14
|
export declare const INSTANT_THROTTLED_ROUNDS_MAX = 6;
|
|
@@ -23,8 +24,8 @@ export declare const INSTANT_HAIKU_CALL_MS = 90000;
|
|
|
23
24
|
export declare const INSTANT_STAGE_BUDGET_MS = 150000;
|
|
24
25
|
export declare const INSTANT_MAX_ATTEMPTS = 5;
|
|
25
26
|
export declare const INSTANT_MERCURY_BREAKER: MercuryCircuitBreaker;
|
|
26
|
-
export interface
|
|
27
|
-
readonly name:
|
|
27
|
+
export interface InstantCallModel {
|
|
28
|
+
readonly name: string;
|
|
28
29
|
readonly id: string;
|
|
29
30
|
readonly reasoning: {
|
|
30
31
|
readonly effort: "none";
|
|
@@ -33,8 +34,18 @@ export interface InstantModelChoice {
|
|
|
33
34
|
};
|
|
34
35
|
readonly reasoningEffort?: string;
|
|
35
36
|
readonly callMs: number;
|
|
37
|
+
readonly maxTokens?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface InstantModelChoice extends InstantCallModel {
|
|
40
|
+
readonly name: InstantCandidateName;
|
|
36
41
|
readonly maxAttempts: number;
|
|
37
42
|
}
|
|
43
|
+
export interface InstantRepairModel extends InstantCallModel {
|
|
44
|
+
readonly choice: InstantRepairModelName;
|
|
45
|
+
readonly cacheSystem: boolean;
|
|
46
|
+
}
|
|
47
|
+
export declare const INSTANT_REPAIR_MODELS: Readonly<Record<InstantRepairModelName, InstantRepairModel>>;
|
|
48
|
+
export declare function instantRepairModel(setting: unknown): InstantRepairModel;
|
|
38
49
|
export declare function instantModelChoice(name: InstantCandidateName): InstantModelChoice;
|
|
39
50
|
export declare const INSTANT_PARAGRAPH: string;
|
|
40
51
|
export declare function composeInstantBrief(sentence: string): string;
|
|
@@ -70,7 +81,7 @@ export declare function autofixAndCheck(args: {
|
|
|
70
81
|
readonly baseline?: ReadonlySet<string>;
|
|
71
82
|
}): InstantGateResult;
|
|
72
83
|
export interface InstantModelRequest {
|
|
73
|
-
readonly model:
|
|
84
|
+
readonly model: InstantCallModel;
|
|
74
85
|
readonly messages: readonly LLMMessage[];
|
|
75
86
|
readonly signal: AbortSignal;
|
|
76
87
|
}
|
|
@@ -83,6 +94,10 @@ export interface InstantModelReply {
|
|
|
83
94
|
readonly usage?: InstantRoundUsage;
|
|
84
95
|
}
|
|
85
96
|
export type InstantModelCall = (req: InstantModelRequest) => Promise<Result<InstantModelReply>>;
|
|
97
|
+
export declare class InstantPaidReplyError extends Error {
|
|
98
|
+
readonly usage: InstantRoundUsage | undefined;
|
|
99
|
+
constructor(message: string, usage: InstantRoundUsage | undefined);
|
|
100
|
+
}
|
|
86
101
|
export declare function instantModelCallFor(vctx: Pick<VibesApiSQLCtx, "llmRequest" | "params">): InstantModelCall;
|
|
87
102
|
export interface InstantCandidateSpec {
|
|
88
103
|
readonly model: InstantModelChoice;
|
|
@@ -109,6 +124,7 @@ export interface InstantCandidateOutcome {
|
|
|
109
124
|
readonly error?: string;
|
|
110
125
|
readonly repairs: readonly InstantRepairRound[];
|
|
111
126
|
readonly repairsDropped?: number;
|
|
127
|
+
readonly repairModel?: string;
|
|
112
128
|
}
|
|
113
129
|
export interface InstantRepairGap {
|
|
114
130
|
readonly rung: string;
|
|
@@ -116,9 +132,10 @@ export interface InstantRepairGap {
|
|
|
116
132
|
readonly result: string;
|
|
117
133
|
}
|
|
118
134
|
export interface InstantRepairRound {
|
|
119
|
-
readonly kind: "ladder" | "reemit";
|
|
135
|
+
readonly kind: "ladder" | "reemit" | "sr";
|
|
120
136
|
readonly mode: string;
|
|
121
137
|
readonly ms: number;
|
|
138
|
+
readonly model?: string;
|
|
122
139
|
readonly result?: string;
|
|
123
140
|
readonly gaps?: readonly InstantRepairGap[];
|
|
124
141
|
}
|
|
@@ -133,6 +150,22 @@ export interface IntactRefusal {
|
|
|
133
150
|
}
|
|
134
151
|
export declare function repairIntactness(baseline: IntactBaseline, after: ReadonlyMap<string, string>, exempt?: ReadonlySet<string>): IntactRefusal | undefined;
|
|
135
152
|
export declare function intactnessReask(defect: IntactnessDefect): string;
|
|
153
|
+
export declare const SR_NOTE_NO_MATCH = "Your last SEARCH blocks did not match the file; copy the SEARCH lines exactly from the file above.";
|
|
154
|
+
export declare const SR_NOTE_NO_EDITS = "Your last reply had no SEARCH/REPLACE blocks.";
|
|
155
|
+
export declare const SR_NOTE_NO_CHANGE = "Your last edits left the file exactly as it was; change the lines the findings name.";
|
|
156
|
+
export declare function srIntactnessNote(defect: IntactnessDefect): string;
|
|
157
|
+
export declare function composeSrRepairAsk(args: {
|
|
158
|
+
readonly findings: readonly string[];
|
|
159
|
+
readonly paths: readonly string[];
|
|
160
|
+
readonly note?: string;
|
|
161
|
+
}): string;
|
|
162
|
+
export declare function srRepairMessages(args: {
|
|
163
|
+
readonly system: readonly LLMMessage[];
|
|
164
|
+
readonly files: ReadonlyMap<string, string>;
|
|
165
|
+
readonly findings: readonly string[];
|
|
166
|
+
readonly note?: string;
|
|
167
|
+
readonly model: InstantRepairModel;
|
|
168
|
+
}): LLMMessage[];
|
|
136
169
|
export declare function runInstantCandidate(args: {
|
|
137
170
|
readonly call: InstantModelCall;
|
|
138
171
|
readonly spec: InstantCandidateSpec;
|
|
@@ -148,6 +181,7 @@ export declare function runInstantCandidate(args: {
|
|
|
148
181
|
readonly baseline?: ReadonlySet<string>;
|
|
149
182
|
readonly repairMode?: InstantRepairMode;
|
|
150
183
|
readonly ladderRounds?: number;
|
|
184
|
+
readonly repairModel?: InstantRepairModel;
|
|
151
185
|
}): Promise<InstantCandidateOutcome>;
|
|
152
186
|
export interface InstantRaceResult {
|
|
153
187
|
readonly winner?: InstantCandidateOutcome;
|
|
@@ -177,6 +211,7 @@ export interface InstantReceipt {
|
|
|
177
211
|
readonly abortedInFlight?: true;
|
|
178
212
|
readonly repairs?: readonly InstantRepairRound[];
|
|
179
213
|
readonly repairsDropped?: number;
|
|
214
|
+
readonly repairModel?: string;
|
|
180
215
|
}[];
|
|
181
216
|
readonly winner?: string;
|
|
182
217
|
readonly winnerCandidate?: string;
|
|
@@ -192,6 +227,7 @@ export interface InstantAssembly {
|
|
|
192
227
|
readonly instruction: string;
|
|
193
228
|
readonly look: SynthLook;
|
|
194
229
|
readonly base: ReadonlyMap<string, string>;
|
|
230
|
+
readonly repairModel?: InstantRepairModelName;
|
|
195
231
|
}
|
|
196
232
|
export interface InstantBuildOutcome {
|
|
197
233
|
readonly response: Response;
|
package/intern/instant-build.js
CHANGED
|
@@ -3,17 +3,18 @@ import { type } from "arktype";
|
|
|
3
3
|
import { makeBaseSystemPrompt } from "@vibes.diy/prompts";
|
|
4
4
|
import { createBlockStream, isCodeEnd, } from "@vibes.diy/call-ai-v2";
|
|
5
5
|
import { lazyType } from "@vibes.diy/api-types/lazy-type";
|
|
6
|
+
import { instantRepairModelName } from "@vibes.diy/api-types";
|
|
6
7
|
import { createBlockAccumulator, createStreamingResolver, resolveCodeBlocks } from "./prompt-streaming.js";
|
|
7
8
|
import { synthesizeImports } from "./import-synth.js";
|
|
8
9
|
import { checkOpens, collapseOpensFindings, newOpensFindings, opensBaseline, opensFindingKey, OPENS_ADVISORY_CLASSES, } from "./opens-check.js";
|
|
9
10
|
import { fillDefect, gapLadder, identFill, intactnessDefect, mergeGaps, parseModule, regionFill, regionMessages, splice, } from "./repair-ladder.js";
|
|
10
11
|
import { saveRepairFindings } from "./save-repair.js";
|
|
11
|
-
import { composeRepairAsk, renderRepairFiles } from "./save-repair-io.js";
|
|
12
|
+
import { composeRepairAsk, fenceLang, renderRepairFiles } from "./save-repair-io.js";
|
|
12
13
|
import { createPromptAssetFetch, promptsPkgBaseUrl } from "./prompt-asset-fetch.js";
|
|
13
14
|
import { loadActiveSettings, lookOptionForTurn } from "./prompt-assembly.js";
|
|
14
15
|
import { MercuryCircuitBreaker } from "./mercury-resilience.js";
|
|
15
16
|
export const INSTANT_MERCURY_FANOUT = 4;
|
|
16
|
-
export const INSTANT_REPAIR_MODE = "
|
|
17
|
+
export const INSTANT_REPAIR_MODE = "sr";
|
|
17
18
|
export const INSTANT_LADDER_ROUNDS = 4;
|
|
18
19
|
export const INSTANT_THROTTLED_ROUNDS_MAX = 6;
|
|
19
20
|
export const INSTANT_REPAIR_ROWS_MAX = 24;
|
|
@@ -30,6 +31,30 @@ export const INSTANT_HAIKU_CALL_MS = 90_000;
|
|
|
30
31
|
export const INSTANT_STAGE_BUDGET_MS = 150_000;
|
|
31
32
|
export const INSTANT_MAX_ATTEMPTS = 5;
|
|
32
33
|
export const INSTANT_MERCURY_BREAKER = new MercuryCircuitBreaker();
|
|
34
|
+
export const INSTANT_REPAIR_MODELS = {
|
|
35
|
+
deepseek: {
|
|
36
|
+
choice: "deepseek",
|
|
37
|
+
name: "repair:deepseek",
|
|
38
|
+
id: "deepseek/deepseek-v4.1-flash:nitro",
|
|
39
|
+
reasoning: { enabled: false },
|
|
40
|
+
cacheSystem: false,
|
|
41
|
+
callMs: 30_000,
|
|
42
|
+
maxTokens: 16_000,
|
|
43
|
+
},
|
|
44
|
+
haiku: {
|
|
45
|
+
choice: "haiku",
|
|
46
|
+
name: "repair:haiku",
|
|
47
|
+
id: "anthropic/claude-haiku-5.5:nitro",
|
|
48
|
+
reasoning: { effort: "none" },
|
|
49
|
+
reasoningEffort: "none",
|
|
50
|
+
cacheSystem: true,
|
|
51
|
+
callMs: 30_000,
|
|
52
|
+
maxTokens: 16_000,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
export function instantRepairModel(setting) {
|
|
56
|
+
return INSTANT_REPAIR_MODELS[instantRepairModelName(setting)];
|
|
57
|
+
}
|
|
33
58
|
export function instantModelChoice(name) {
|
|
34
59
|
switch (name) {
|
|
35
60
|
case "mercury":
|
|
@@ -187,6 +212,20 @@ export function autofixAndCheck(args) {
|
|
|
187
212
|
notes: fixed.notes,
|
|
188
213
|
};
|
|
189
214
|
}
|
|
215
|
+
export class InstantPaidReplyError extends Error {
|
|
216
|
+
usage;
|
|
217
|
+
constructor(message, usage) {
|
|
218
|
+
super(message);
|
|
219
|
+
this.usage = usage;
|
|
220
|
+
this.name = "InstantPaidReplyError";
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function settledUsage(reply) {
|
|
224
|
+
if (reply.isOk())
|
|
225
|
+
return reply.Ok().usage;
|
|
226
|
+
const error = reply.Err();
|
|
227
|
+
return error instanceof InstantPaidReplyError ? error.usage : undefined;
|
|
228
|
+
}
|
|
190
229
|
const completionBody = lazyType(() => type({
|
|
191
230
|
"model?": "string",
|
|
192
231
|
choices: type({ message: { content: "string | null" }, "finish_reason?": "string | null" }).array(),
|
|
@@ -202,7 +241,7 @@ export function instantModelCallFor(vctx) {
|
|
|
202
241
|
const body = {
|
|
203
242
|
model: req.model.id,
|
|
204
243
|
stream: false,
|
|
205
|
-
max_tokens: INSTANT_MAX_OUTPUT_TOKENS,
|
|
244
|
+
max_tokens: req.model.maxTokens ?? INSTANT_MAX_OUTPUT_TOKENS,
|
|
206
245
|
reasoning: { ...req.model.reasoning },
|
|
207
246
|
messages: [...req.messages],
|
|
208
247
|
};
|
|
@@ -220,28 +259,23 @@ export function instantModelCallFor(vctx) {
|
|
|
220
259
|
const parsed = completionBody(rJson.Ok());
|
|
221
260
|
if (parsed instanceof type.errors)
|
|
222
261
|
return Result.Err(`instant ${req.model.name}: reply: ${parsed.summary}`);
|
|
262
|
+
const u = parsed.usage;
|
|
263
|
+
const servedModel = parsed.model ?? req.model.id;
|
|
264
|
+
const usage = u === undefined
|
|
265
|
+
? undefined
|
|
266
|
+
: {
|
|
267
|
+
prompt_tokens: u.prompt_tokens ?? 0,
|
|
268
|
+
completion_tokens: u.completion_tokens ?? 0,
|
|
269
|
+
total_tokens: u.total_tokens ?? (u.prompt_tokens ?? 0) + (u.completion_tokens ?? 0),
|
|
270
|
+
model: servedModel,
|
|
271
|
+
...(req.model.reasoningEffort === undefined ? {} : { reasoningEffort: req.model.reasoningEffort }),
|
|
272
|
+
...(u.cost === undefined ? {} : { cost: u.cost }),
|
|
273
|
+
};
|
|
223
274
|
const content = parsed.choices[0]?.message.content;
|
|
224
275
|
if (content === undefined || content === null || content.trim().length === 0) {
|
|
225
|
-
return Result.Err(`instant ${req.model.name}: empty reply (finish ${parsed.choices[0]?.finish_reason ?? "unknown"})
|
|
276
|
+
return Result.Err(new InstantPaidReplyError(`instant ${req.model.name}: empty reply (finish ${parsed.choices[0]?.finish_reason ?? "unknown"})`, usage));
|
|
226
277
|
}
|
|
227
|
-
|
|
228
|
-
const servedModel = parsed.model ?? req.model.id;
|
|
229
|
-
return Result.Ok({
|
|
230
|
-
text: content,
|
|
231
|
-
servedModel,
|
|
232
|
-
...(u === undefined
|
|
233
|
-
? {}
|
|
234
|
-
: {
|
|
235
|
-
usage: {
|
|
236
|
-
prompt_tokens: u.prompt_tokens ?? 0,
|
|
237
|
-
completion_tokens: u.completion_tokens ?? 0,
|
|
238
|
-
total_tokens: u.total_tokens ?? (u.prompt_tokens ?? 0) + (u.completion_tokens ?? 0),
|
|
239
|
-
model: servedModel,
|
|
240
|
-
...(req.model.reasoningEffort === undefined ? {} : { reasoningEffort: req.model.reasoningEffort }),
|
|
241
|
-
...(u.cost === undefined ? {} : { cost: u.cost }),
|
|
242
|
-
},
|
|
243
|
-
}),
|
|
244
|
-
});
|
|
278
|
+
return Result.Ok({ text: content, servedModel, ...(usage === undefined ? {} : { usage }) });
|
|
245
279
|
};
|
|
246
280
|
}
|
|
247
281
|
function isThrottle(error) {
|
|
@@ -275,11 +309,13 @@ async function callWithin(call, req, cancel) {
|
|
|
275
309
|
clearTimeout(timer);
|
|
276
310
|
cancel.removeEventListener("abort", cancelled);
|
|
277
311
|
});
|
|
312
|
+
const usage = settledUsage(reply);
|
|
313
|
+
const paid = usage === undefined ? {} : { usage };
|
|
278
314
|
if (cancel.aborted)
|
|
279
|
-
return { kind: "cancelled" };
|
|
315
|
+
return { kind: "cancelled", ...paid };
|
|
280
316
|
if (abort.signal.aborted)
|
|
281
|
-
return { kind: "deadline" };
|
|
282
|
-
return { kind: "reply", reply };
|
|
317
|
+
return { kind: "deadline", ...paid };
|
|
318
|
+
return { kind: "reply", reply, ...paid };
|
|
283
319
|
}
|
|
284
320
|
function pauseUnlessCancelled(ms, cancel) {
|
|
285
321
|
return new Promise((resolve) => {
|
|
@@ -339,6 +375,63 @@ export function intactnessReask(defect) {
|
|
|
339
375
|
function isSourcePath(path) {
|
|
340
376
|
return /\.(jsx?|tsx?|mjs)$/.test(path);
|
|
341
377
|
}
|
|
378
|
+
export const SR_NOTE_NO_MATCH = "Your last SEARCH blocks did not match the file; copy the SEARCH lines exactly from the file above.";
|
|
379
|
+
export const SR_NOTE_NO_EDITS = "Your last reply had no SEARCH/REPLACE blocks.";
|
|
380
|
+
export const SR_NOTE_NO_CHANGE = "Your last edits left the file exactly as it was; change the lines the findings name.";
|
|
381
|
+
export function srIntactnessNote(defect) {
|
|
382
|
+
switch (defect) {
|
|
383
|
+
case "shrank":
|
|
384
|
+
return "Your last edits left part of the app out; keep every line that already worked.";
|
|
385
|
+
case "grew":
|
|
386
|
+
return "Your last edits rewrote far more than the fix needed; change only what the findings require.";
|
|
387
|
+
case "names":
|
|
388
|
+
return "Your last edits dropped functions, components or variables the app still uses; keep every one that already worked.";
|
|
389
|
+
case "loop":
|
|
390
|
+
return "Your last edits repeat the same lines over and over; write each line once.";
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
export function composeSrRepairAsk(args) {
|
|
394
|
+
const names = args.paths.map((path) => path.replace(/^\/+/, ""));
|
|
395
|
+
const first = names[0] ?? "App.jsx";
|
|
396
|
+
const target = names.length > 1 ? `the files above (${names.join(", ")})` : first;
|
|
397
|
+
return (`Opening the app in the browser reported:\n\n${args.findings.slice(0, 6).join("\n")}\n\n` +
|
|
398
|
+
(args.note === undefined ? "" : `${args.note}\n\n`) +
|
|
399
|
+
`Reply with SEARCH/REPLACE edits to ${target}, the same shape as an edit turn, and nothing else:\n\n` +
|
|
400
|
+
`### \`${first}\`\n\`\`\`${fenceLang(first)}\n<<<<<<< SEARCH\n(exact lines from the current file)\n=======\n(the replacement)\n>>>>>>> REPLACE\n\`\`\`\n\n` +
|
|
401
|
+
(names.length > 1 ? "Edit each other file the same way, under its own `### `path`` heading.\n" : "") +
|
|
402
|
+
"Each SEARCH must match the current file exactly, once. Change only what the fix needs; keep everything that already worked.");
|
|
403
|
+
}
|
|
404
|
+
function withoutCache(part) {
|
|
405
|
+
if (part.type === "text")
|
|
406
|
+
return { type: "text", text: part.text };
|
|
407
|
+
if (part.type === "image_url")
|
|
408
|
+
return { type: "image_url", image_url: part.image_url };
|
|
409
|
+
return part;
|
|
410
|
+
}
|
|
411
|
+
export function srRepairMessages(args) {
|
|
412
|
+
const system = args.system.flatMap((message) => message.role === "system" && Array.isArray(message.content)
|
|
413
|
+
? [{ role: "system", content: message.content.map(withoutCache) }]
|
|
414
|
+
: []);
|
|
415
|
+
const last = system[system.length - 1];
|
|
416
|
+
if (args.model.cacheSystem && last !== undefined) {
|
|
417
|
+
const at = last.content.map((part) => part.type).lastIndexOf("text");
|
|
418
|
+
const part = last.content[at];
|
|
419
|
+
if (part !== undefined && part.type === "text") {
|
|
420
|
+
last.content[at] = { type: "text", text: part.text, cache_control: { type: "ephemeral" } };
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const files = renderRepairFiles({ vfs: args.files, intro: "Here is the app." });
|
|
424
|
+
const ask = composeSrRepairAsk({
|
|
425
|
+
findings: args.findings,
|
|
426
|
+
paths: [...args.files.keys()].sort((a, b) => Number(b === "/App.jsx") - Number(a === "/App.jsx")),
|
|
427
|
+
...(args.note === undefined ? {} : { note: args.note }),
|
|
428
|
+
});
|
|
429
|
+
return [
|
|
430
|
+
...system,
|
|
431
|
+
{ role: "assistant", content: [{ type: "text", text: files }] },
|
|
432
|
+
{ role: "user", content: [{ type: "text", text: ask }] },
|
|
433
|
+
];
|
|
434
|
+
}
|
|
342
435
|
async function ladderRound(args) {
|
|
343
436
|
const t0 = args.now();
|
|
344
437
|
const ids = findingIds(args.findings);
|
|
@@ -376,6 +469,8 @@ async function ladderRound(args) {
|
|
|
376
469
|
userFirst: args.model.name === "haiku",
|
|
377
470
|
});
|
|
378
471
|
const answer = await callWithin(args.call, { model: args.model, messages, timeoutMs: args.timeoutMs }, args.cancel);
|
|
472
|
+
if (answer.usage !== undefined)
|
|
473
|
+
usage.push(answer.usage);
|
|
379
474
|
if (answer.kind === "cancelled")
|
|
380
475
|
return { result: "rejected:cancelled", climbs: false };
|
|
381
476
|
if (answer.kind === "deadline")
|
|
@@ -386,8 +481,6 @@ async function ladderRound(args) {
|
|
|
386
481
|
: { result: "rejected:error", climbs: false };
|
|
387
482
|
}
|
|
388
483
|
const reply = answer.reply.Ok();
|
|
389
|
-
if (reply.usage !== undefined)
|
|
390
|
-
usage.push(reply.usage);
|
|
391
484
|
const raw = regionFill(reply.text, group.src.slice(group.gap.start, group.gap.end));
|
|
392
485
|
if (raw === undefined)
|
|
393
486
|
return { result: "rejected:no-fill", climbs: true };
|
|
@@ -465,7 +558,9 @@ export async function runInstantCandidate(args) {
|
|
|
465
558
|
const now = args.now ?? Date.now;
|
|
466
559
|
const random = args.random ?? Math.random;
|
|
467
560
|
const repairMode = args.repairMode ?? INSTANT_REPAIR_MODE;
|
|
468
|
-
|
|
561
|
+
const repairModel = args.repairModel ?? instantRepairModel(undefined);
|
|
562
|
+
let repairModelCalled = false;
|
|
563
|
+
let route = repairMode === "router" ? undefined : repairMode === "sr" ? "reemit" : repairMode;
|
|
469
564
|
const ladderBudget = () => (route === "ladder" ? (args.ladderRounds ?? INSTANT_LADDER_ROUNDS) : 0);
|
|
470
565
|
const modeLabel = () => (repairMode === "router" ? `router:${route ?? "none"}` : repairMode);
|
|
471
566
|
const { model } = args.spec;
|
|
@@ -512,6 +607,7 @@ export async function runInstantCandidate(args) {
|
|
|
512
607
|
findings: firstFindings,
|
|
513
608
|
repairs,
|
|
514
609
|
...(repairsDropped > 0 ? { repairsDropped } : {}),
|
|
610
|
+
...(repairModelCalled ? { repairModel: repairModel.id } : {}),
|
|
515
611
|
...extra,
|
|
516
612
|
};
|
|
517
613
|
}
|
|
@@ -522,7 +618,13 @@ export async function runInstantCandidate(args) {
|
|
|
522
618
|
function exhausted(error) {
|
|
523
619
|
if (overridable === undefined)
|
|
524
620
|
return end("fail", { error });
|
|
525
|
-
record({
|
|
621
|
+
record({
|
|
622
|
+
kind: overridable.kind,
|
|
623
|
+
mode: overridable.kind === "sr" ? "sr" : modeLabel(),
|
|
624
|
+
ms: 0,
|
|
625
|
+
...(overridable.kind === "sr" ? { model: repairModel.id } : {}),
|
|
626
|
+
result: "accepted:gate-overridden",
|
|
627
|
+
});
|
|
526
628
|
return end("pass", { vfs: overridable.gated.vfs, changed: [...new Set([...changed, ...overridable.touched])] });
|
|
527
629
|
}
|
|
528
630
|
function waitWithin(ms) {
|
|
@@ -542,6 +644,101 @@ export async function runInstantCandidate(args) {
|
|
|
542
644
|
messages.push({ role: "user", content: [{ type: "text", text: composeRepairAsk(findings) }] });
|
|
543
645
|
reemitStartedAt = now();
|
|
544
646
|
}
|
|
647
|
+
async function srRepair() {
|
|
648
|
+
repairModelCalled = true;
|
|
649
|
+
let note;
|
|
650
|
+
for (;;) {
|
|
651
|
+
if (args.cancel.aborted)
|
|
652
|
+
return end("cancelled", {});
|
|
653
|
+
const left = args.deadlineAt - now();
|
|
654
|
+
if (left <= 0)
|
|
655
|
+
return exhausted(`stage budget spent after ${attempts} call(s): ${lastError}`);
|
|
656
|
+
if (paidLeft() <= 0)
|
|
657
|
+
return exhausted(`${attempts} calls without a passing answer: ${lastError}`);
|
|
658
|
+
attempts++;
|
|
659
|
+
const startedAt = now();
|
|
660
|
+
const row = (result) => record({ kind: "sr", mode: "sr", model: repairModel.id, ms: now() - startedAt, result });
|
|
661
|
+
const request = srRepairMessages({
|
|
662
|
+
system,
|
|
663
|
+
files: sourceFiles(current),
|
|
664
|
+
findings: liveFindings,
|
|
665
|
+
...(note === undefined ? {} : { note }),
|
|
666
|
+
model: repairModel,
|
|
667
|
+
});
|
|
668
|
+
const answer = await callWithin(args.call, { model: repairModel, messages: request, timeoutMs: Math.min(repairModel.callMs, left) }, args.cancel);
|
|
669
|
+
if (answer.usage !== undefined)
|
|
670
|
+
usage.push(answer.usage);
|
|
671
|
+
if (answer.kind === "cancelled")
|
|
672
|
+
return end("cancelled", { abortedInFlight: true });
|
|
673
|
+
if (answer.kind === "deadline") {
|
|
674
|
+
lastError = `repair call ${attempts} passed its deadline`;
|
|
675
|
+
row("rejected:deadline");
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
if (answer.reply.isErr()) {
|
|
679
|
+
lastError = answer.reply.Err().message;
|
|
680
|
+
providerErrors++;
|
|
681
|
+
if (isThrottle(answer.reply.Err())) {
|
|
682
|
+
throttled++;
|
|
683
|
+
row("rejected:throttled");
|
|
684
|
+
await waitWithin(throttleWaitMs(providerErrors, random));
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
row("rejected:error");
|
|
688
|
+
await waitWithin(Math.min(8_000, 1_000 * 2 ** (providerErrors - 1)));
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
providerErrors = 0;
|
|
692
|
+
const reply = answer.reply.Ok();
|
|
693
|
+
const parsed = await parseCandidateText(reply.text, current);
|
|
694
|
+
if (parsed.isErr()) {
|
|
695
|
+
lastError = "the repair reply held no SEARCH/REPLACE block";
|
|
696
|
+
note = SR_NOTE_NO_EDITS;
|
|
697
|
+
row("rejected:no-edit");
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
const edit = parsed.Ok();
|
|
701
|
+
if (edit.changed.every((path) => edit.vfs.get(path) === current.get(path))) {
|
|
702
|
+
if (edit.applyErrors.length > 0) {
|
|
703
|
+
lastError = edit.applyErrors.slice(0, 3).join("; ");
|
|
704
|
+
note = SR_NOTE_NO_MATCH;
|
|
705
|
+
row("rejected:no-match");
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
note = SR_NOTE_NO_CHANGE;
|
|
709
|
+
row("rejected:no-change");
|
|
710
|
+
}
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
const nextChanged = new Set([...changed, ...edit.changed]);
|
|
714
|
+
const checked = autofixAndCheck({ vfs: edit.vfs, changed: [...nextChanged], look: args.look, baseline });
|
|
715
|
+
const gated = edit.applyErrors.length === 0
|
|
716
|
+
? checked
|
|
717
|
+
: { ...checked, opens: false, findings: [...edit.applyErrors, ...checked.findings] };
|
|
718
|
+
const gutted = preRepair === undefined ? undefined : repairIntactness(preRepair, gated.vfs, repairNames);
|
|
719
|
+
if (gutted !== undefined) {
|
|
720
|
+
if (gated.opens)
|
|
721
|
+
noteOverridable({ gated, ratio: gutted.ratio, touched: [...nextChanged], kind: "sr" });
|
|
722
|
+
lastError = `the repair failed the intactness gate (${gutted.path}: ${gutted.defect})`;
|
|
723
|
+
note = srIntactnessNote(gutted.defect);
|
|
724
|
+
row(`rejected:gutted:${gutted.defect}`);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (gated.opens) {
|
|
728
|
+
row("accepted");
|
|
729
|
+
return end("pass", { vfs: gated.vfs, changed: [...nextChanged] });
|
|
730
|
+
}
|
|
731
|
+
row("rejected:still");
|
|
732
|
+
note = undefined;
|
|
733
|
+
changed = nextChanged;
|
|
734
|
+
current = gated.vfs;
|
|
735
|
+
liveFindings = gated.findings;
|
|
736
|
+
lastError = gated.findings.slice(0, 3).join("; ");
|
|
737
|
+
for (const finding of gated.blocking)
|
|
738
|
+
if (finding.name !== undefined)
|
|
739
|
+
repairNames.add(finding.name);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
545
742
|
for (;;) {
|
|
546
743
|
if (args.cancel.aborted)
|
|
547
744
|
return end("cancelled", {});
|
|
@@ -557,6 +754,8 @@ export async function runInstantCandidate(args) {
|
|
|
557
754
|
}
|
|
558
755
|
attempts++;
|
|
559
756
|
const answer = await callWithin(args.call, { model, messages, timeoutMs: Math.min(model.callMs, left) }, args.cancel);
|
|
757
|
+
if (answer.usage !== undefined)
|
|
758
|
+
usage.push(answer.usage);
|
|
560
759
|
if (answer.kind === "cancelled")
|
|
561
760
|
return end("cancelled", { abortedInFlight: true });
|
|
562
761
|
if (answer.kind === "deadline") {
|
|
@@ -580,8 +779,6 @@ export async function runInstantCandidate(args) {
|
|
|
580
779
|
}
|
|
581
780
|
providerErrors = 0;
|
|
582
781
|
const reply = answer.reply.Ok();
|
|
583
|
-
if (reply.usage !== undefined)
|
|
584
|
-
usage.push(reply.usage);
|
|
585
782
|
const reemitMs = reemitStartedAt === undefined ? undefined : now() - reemitStartedAt;
|
|
586
783
|
reemitStartedAt = undefined;
|
|
587
784
|
const parsed = await parseCandidateText(reply.text, current);
|
|
@@ -629,6 +826,8 @@ export async function runInstantCandidate(args) {
|
|
|
629
826
|
if (finding.name !== undefined)
|
|
630
827
|
repairNames.add(finding.name);
|
|
631
828
|
current = gated.vfs;
|
|
829
|
+
if (repairMode === "sr" && applyErrors === 0)
|
|
830
|
+
return srRepair();
|
|
632
831
|
route ??=
|
|
633
832
|
gated.blocking.some((finding) => INSTANT_REEMIT_CLASSES.has(finding.class)) ||
|
|
634
833
|
gated.unlocated > 0 ||
|
|
@@ -737,6 +936,7 @@ export function instantReceipt(race) {
|
|
|
737
936
|
...(outcome.throttled > 0 ? { throttled: outcome.throttled } : {}),
|
|
738
937
|
...(outcome.repairs.length > 0 ? { repairs: outcome.repairs } : {}),
|
|
739
938
|
...(outcome.repairsDropped === undefined ? {} : { repairsDropped: outcome.repairsDropped }),
|
|
939
|
+
...(outcome.repairModel === undefined ? {} : { repairModel: outcome.repairModel }),
|
|
740
940
|
...(outcome.abortedInFlight ? { abortedInFlight: true } : {}),
|
|
741
941
|
})),
|
|
742
942
|
...(race.winner === undefined ? {} : { winner: race.winner.model, winnerCandidate: race.winner.candidate }),
|
|
@@ -826,6 +1026,7 @@ export async function runInstantBuild(args) {
|
|
|
826
1026
|
const specs = instantSpecs({ assembly, haikuMessages: args.haikuMessages, fanout });
|
|
827
1027
|
const mercuryThrottle = { holdUntil: 0 };
|
|
828
1028
|
const baseline = opensBaseline(sourceFiles(assembly.base));
|
|
1029
|
+
const repairModel = instantRepairModel(assembly.repairModel);
|
|
829
1030
|
let race;
|
|
830
1031
|
try {
|
|
831
1032
|
race = await raceInstantCandidates({
|
|
@@ -841,6 +1042,7 @@ export async function runInstantBuild(args) {
|
|
|
841
1042
|
startedAt,
|
|
842
1043
|
deadlineAt,
|
|
843
1044
|
baseline,
|
|
1045
|
+
repairModel,
|
|
844
1046
|
...(spec.model.name === "mercury" ? { throttle: mercuryThrottle } : {}),
|
|
845
1047
|
}).catch((e) => crashedCandidate(spec, startedAt, e)),
|
|
846
1048
|
});
|