Skip to content
Source
Sign inSign up

ReplayRuntimeInstructions.ts

250 lines9.8 KB

1import type { ProviderDriverKind, ProviderReplayTranscript } from "@t3tools/contracts";
2​
3import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions";
4​
5export function materializeReplayTranscriptRuntimeInstructions(
6 transcript: ProviderReplayTranscript,
7 runtime: { readonly driver: ProviderDriverKind; readonly model: string },
8): ProviderReplayTranscript {
9 const harness =
10 runtime.driver === "cursor"
11 ? "Cursor"
12 : runtime.driver === "grok"
13 ? "Grok"
14 : runtime.driver === "kiro"
15 ? "Kiro"
16 : runtime.driver === "acpRegistry"
17 ? "acpRegistry"
18 : runtime.driver === "fx"
19 ? "fx"
20 : undefined;
21 if (harness === undefined) return transcript;
22 const instructions = buildRuntimeInstructions({ harness, model: runtime.model });
23 // The notes name the model, so each prompt expects the ones for its session's current model.
24 const sessionModels = new Map<unknown, string>();
25 const notesFor = (sessionId: unknown) => {
26 const model = sessionModels.get(sessionId) ?? runtime.model;
27 return model === runtime.model ? instructions : buildRuntimeInstructions({ harness, model });
28 };
29 // Grok's flavor sets `supportsCompaction`; Kiro compacts through its own request instead.
30 const compactsThroughPrompt = runtime.driver === "grok";
31 // Like the adapter: notes go out until a prompt carrying that text succeeds, and a new runtime
32 // or compaction resets that. Not mirrored, as no recording has them: Copilot compaction events,
33 // child-session compactions, and ACP v2 prompts that settle through an idle `state_update`.
34 const deliveredNotes = new Map<unknown, string>();
35 // A prompt's success records delivery only if no compaction completed after it was sent.
36 const compactionCounts = new Map<unknown, number>();
37 const completedCompactionIds = new Set<string>();
38 // A turn that completes with a compaction still open projects it as completed.
39 const openCompactions = new Map<unknown, Set<unknown>>();
40 const markCompacted = (sessionId: unknown, compactionId?: unknown) => {
41 if (typeof compactionId === "string") {
42 const key = `${String(sessionId)}\u0000${compactionId}`;
43 if (completedCompactionIds.has(key)) return;
44 completedCompactionIds.add(key);
45 }
46 deliveredNotes.delete(sessionId);
47 compactionCounts.set(sessionId, (compactionCounts.get(sessionId) ?? 0) + 1);
48 };
49 let pendingPrompt:
50 | {
51 readonly sessionId: unknown;
52 /** The runtime notes the prompt carried, if any. */
53 readonly notes: string | undefined;
54 readonly compact: boolean;
55 readonly compactionsAtSend: number;
56 }
57 | undefined;
58 let pendingKiroCompact: { readonly sessionId: unknown } | undefined;
59​
60 return {
61 ...transcript,
62 entries: transcript.entries.map((entry) => {
63 if (entry.type === "emit_inbound") {
64 const frame = asFrame(entry.frame);
65 if (frame?.kind === "response" && frame.method === "session/prompt" && pendingPrompt) {
66 const stopReason = isRecord(frame.result) ? frame.result.stopReason : undefined;
67 const delivered =
68 frame.error === undefined &&
69 stopReason !== "cancelled" &&
70 stopReason !== "refusal" &&
71 stopReason !== "error";
72 const open = openCompactions.get(pendingPrompt.sessionId);
73 if (delivered && open !== undefined && open.size > 0) {
74 openCompactions.delete(pendingPrompt.sessionId);
75 markCompacted(pendingPrompt.sessionId);
76 }
77 if (delivered && pendingPrompt.compact) {
78 markCompacted(pendingPrompt.sessionId);
79 } else if (
80 delivered &&
81 pendingPrompt.notes !== undefined &&
82 (compactionCounts.get(pendingPrompt.sessionId) ?? 0) === pendingPrompt.compactionsAtSend
83 ) {
84 deliveredNotes.set(pendingPrompt.sessionId, pendingPrompt.notes);
85 }
86 pendingPrompt = undefined;
87 }
88 if (
89 frame?.kind === "response" &&
90 frame.method === "_kiro/session/compact" &&
91 pendingKiroCompact
92 ) {
93 // Kiro's adapter accepts only `{ success: true }`.
94 if (isRecord(frame.result) && frame.result.success === true) {
95 markCompacted(pendingKiroCompact.sessionId);
96 }
97 pendingKiroCompact = undefined;
98 }
99 const update =
100 frame?.method === "session/update" && isRecord(frame.params)
101 ? frame.params.update
102 : undefined;
103 if (
104 isRecord(update) &&
105 update.sessionUpdate === "compaction_update" &&
106 isRecord(frame?.params)
107 ) {
108 const sessionId = frame.params.sessionId;
109 const open = openCompactions.get(sessionId) ?? new Set<unknown>();
110 if (update.status === "in_progress") {
111 openCompactions.set(sessionId, open.add(update.compactionId));
112 } else {
113 open.delete(update.compactionId);
114 }
115 if (update.status === "completed") markCompacted(sessionId, update.compactionId);
116 }
117 return entry;
118 }
119 if (entry.type !== "expect_outbound") return entry;
120 const frame = entry.frame;
121 if (typeof frame !== "object" || frame === null) return entry;
122 if (
123 runtime.driver === "cursor" &&
124 "type" in frame &&
125 frame.type === "run.start" &&
126 "message" in frame &&
127 typeof frame.message === "string"
128 ) {
129 return frame.message.endsWith(instructions)
130 ? entry
131 : { ...entry, frame: { ...frame, message: `${frame.message}\n\n${instructions}` } };
132 }
133 if (
134 "method" in frame &&
135 (frame.method === "session/close" || frame.method === "initialize")
136 ) {
137 // A closed or restarted session comes back in a new runtime, which resends the instructions.
138 deliveredNotes.clear();
139 completedCompactionIds.clear();
140 openCompactions.clear();
141 pendingPrompt = undefined;
142 pendingKiroCompact = undefined;
143 return entry;
144 }
145 const params = "params" in frame && isRecord(frame.params) ? frame.params : undefined;
146 const selectedModel =
147 "method" in frame && frame.method === "session/set_config_option"
148 ? params?.configId === "model"
149 ? params.value
150 : undefined
151 : "method" in frame && frame.method === "session/set_model"
152 ? params?.modelId
153 : undefined;
154 if (typeof selectedModel === "string") {
155 sessionModels.set(params?.sessionId, selectedModel);
156 return entry;
157 }
158 if (
159 "method" in frame &&
160 frame.method === "_kiro/session/compact" &&
161 "params" in frame &&
162 typeof frame.params === "object" &&
163 frame.params !== null &&
164 "sessionId" in frame.params
165 ) {
166 // Once it succeeds, Kiro's native compaction makes the next prompt resend them.
167 pendingKiroCompact = { sessionId: frame.params.sessionId };
168 return entry;
169 }
170 if (
171 "method" in frame &&
172 frame.method === "session/prompt" &&
173 "params" in frame &&
174 typeof frame.params === "object" &&
175 frame.params !== null &&
176 "prompt" in frame.params &&
177 Array.isArray(frame.params.prompt)
178 ) {
179 const promptSessionId = "sessionId" in frame.params ? frame.params.sessionId : undefined;
180 const prompt: ReadonlyArray<unknown> = frame.params.prompt;
181 const firstText = promptPartText(prompt[0])?.trim();
182 // Like the adapter, native slash commands go out without instructions. Where the agent
183 // compacts through the prompt, a successful `/compact` makes the next prompt resend them.
184 if (firstText?.startsWith("/") === true) {
185 pendingPrompt = {
186 sessionId: promptSessionId,
187 notes: undefined,
188 compact:
189 compactsThroughPrompt && prompt.length === 1 && /^\/compact(?:\s|$)/.test(firstText),
190 compactionsAtSend: compactionCounts.get(promptSessionId) ?? 0,
191 };
192 return entry;
193 }
194 const lastText = promptPartText(prompt.at(-1));
195 const expected = notesFor(promptSessionId);
196 // A recording that switched models mid-thread already carries the
197 // instructions for the model each prompt ran on.
198 const recordedNotes =
199 lastText === expected || lastText?.startsWith("<runtime_info>") === true
200 ? lastText
201 : undefined;
202 const resend =
203 recordedNotes === undefined && deliveredNotes.get(promptSessionId) !== expected;
204 pendingPrompt = {
205 sessionId: promptSessionId,
206 // Recorders normalize the notes they keep, so a recorded block stands for this model's.
207 notes: recordedNotes !== undefined || resend ? expected : undefined,
208 compact: false,
209 compactionsAtSend: compactionCounts.get(promptSessionId) ?? 0,
210 };
211 if (!resend) return entry;
212 return {
213 ...entry,
214 frame: {
215 ...frame,
216 params: { ...frame.params, prompt: [...prompt, { type: "text", text: expected }] },
217 },
218 };
219 }
220 return entry;
221 }),
222 };
224​
225function promptPartText(part: unknown): string | undefined {
226 return typeof part === "object" &&
227 part !== null &&
228 "type" in part &&
229 part.type === "text" &&
230 "text" in part &&
231 typeof part.text === "string"
232 ? part.text
233 : undefined;
235​
236type ReplayFrame = {
237 readonly kind?: unknown;
238 readonly method?: unknown;
239 readonly params?: unknown;
240 readonly result?: unknown;
241 readonly error?: unknown;
242};
243​
244function isRecord(value: unknown): value is Record<string, unknown> {
245 return typeof value === "object" && value !== null;
247​
248function asFrame(value: unknown): ReplayFrame | undefined {
249 return isRecord(value) ? value : undefined;