tinker-agent 2.5.0 → 2.6.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/CHANGELOG.md +19 -1
- package/README.md +30 -7
- package/package.json +1 -1
- package/src/cli/main.ts +2 -0
- package/src/cli/public-config-contract.ts +1 -1
- package/src/cli/tui-runner.tsx +2 -0
- package/src/session/session-clone-helpers.ts +249 -0
- package/src/session/session-compatibility-codec.ts +401 -0
- package/src/session/session-store-contracts.ts +304 -0
- package/src/session/session-store-filesystem.ts +245 -0
- package/src/session/session-store-record-codecs.ts +1064 -0
- package/src/session/session-store-value-codecs.ts +156 -0
- package/src/session/session-store.ts +234 -3023
- package/src/session/session-tool-result-codec.ts +590 -0
- package/src/tools/grep.ts +1 -3
- package/src/tui/app.tsx +2 -0
- package/src/tui/components/prompt-input.tsx +4 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import type { ContextRevisionId, SessionId } from "../ids/runtime-id";
|
|
2
|
+
import {
|
|
3
|
+
createModelContextProfile,
|
|
4
|
+
type ModelContextProfile,
|
|
5
|
+
} from "../model/model-context-profile";
|
|
6
|
+
import {
|
|
7
|
+
MODEL_MESSAGE_PROTOCOL_ADAPTERS,
|
|
8
|
+
type ModelMessageProtocol,
|
|
9
|
+
} from "../model/model-client";
|
|
10
|
+
import { sha256, stableJsonStringify } from "../model/model-request-preflight";
|
|
11
|
+
import { immutableCanonicalClone } from "../context/protocol-frame";
|
|
12
|
+
import type { ProjectInstructionManifest } from "../instructions/project-instructions";
|
|
13
|
+
import {
|
|
14
|
+
IMAGE_INPUT_POLICY,
|
|
15
|
+
IMAGE_INPUT_POLICY_VERSION,
|
|
16
|
+
} from "../image/image-input-policy";
|
|
17
|
+
import type {
|
|
18
|
+
SessionCompatibilityContract,
|
|
19
|
+
StoredSessionMetaV10,
|
|
20
|
+
} from "./session-store-contracts";
|
|
21
|
+
import {
|
|
22
|
+
assertObjectKeys,
|
|
23
|
+
enumFromSql,
|
|
24
|
+
nullableNumberFromSql,
|
|
25
|
+
nullableStringFromSql,
|
|
26
|
+
numberFromJson,
|
|
27
|
+
numberFromSql,
|
|
28
|
+
parseJson,
|
|
29
|
+
recordFromSql,
|
|
30
|
+
sha256FromSql,
|
|
31
|
+
stringFromSql,
|
|
32
|
+
timestampFromSql,
|
|
33
|
+
} from "./session-store-value-codecs";
|
|
34
|
+
|
|
35
|
+
export function createSessionCompatibilityContract(input: {
|
|
36
|
+
modelName: string;
|
|
37
|
+
profileName?: string;
|
|
38
|
+
includeReasoningContent: boolean;
|
|
39
|
+
contextProfile: ModelContextProfile;
|
|
40
|
+
messageProtocol: ModelMessageProtocol;
|
|
41
|
+
inputModalities: readonly ("text" | "image")[];
|
|
42
|
+
toolResultModalities: readonly ("text" | "image")[];
|
|
43
|
+
}): SessionCompatibilityContract {
|
|
44
|
+
if (input.modelName.trim() === "") {
|
|
45
|
+
throw new Error("Session compatibility model name must not be empty.");
|
|
46
|
+
}
|
|
47
|
+
if (input.profileName !== undefined && input.profileName.trim() === "") {
|
|
48
|
+
throw new Error("Session compatibility profile name must not be empty.");
|
|
49
|
+
}
|
|
50
|
+
if (typeof input.includeReasoningContent !== "boolean") {
|
|
51
|
+
throw new Error("Session compatibility reasoning replay flag must be boolean.");
|
|
52
|
+
}
|
|
53
|
+
if (
|
|
54
|
+
!MODEL_MESSAGE_PROTOCOL_ADAPTERS.includes(input.messageProtocol.adapter) ||
|
|
55
|
+
input.messageProtocol.serializationVersion.trim() === ""
|
|
56
|
+
) {
|
|
57
|
+
throw new Error("Session compatibility message protocol is invalid.");
|
|
58
|
+
}
|
|
59
|
+
const inputModalities = normalizeInputModalities(input.inputModalities);
|
|
60
|
+
const toolResultModalities = normalizeInputModalities(input.toolResultModalities);
|
|
61
|
+
if (toolResultModalities.includes("image") && !inputModalities.includes("image")) {
|
|
62
|
+
throw new Error("Session compatibility image tool results require image input.");
|
|
63
|
+
}
|
|
64
|
+
return Object.freeze({
|
|
65
|
+
modelName: input.modelName,
|
|
66
|
+
...(input.profileName === undefined ? {} : { profileName: input.profileName }),
|
|
67
|
+
includeReasoningContent: input.includeReasoningContent,
|
|
68
|
+
contextProfile: Object.freeze(createModelContextProfile(input.contextProfile)),
|
|
69
|
+
messageProtocol: immutableCanonicalClone(input.messageProtocol),
|
|
70
|
+
media: Object.freeze({
|
|
71
|
+
policyVersion: IMAGE_INPUT_POLICY_VERSION,
|
|
72
|
+
policySha256: sha256(
|
|
73
|
+
stableJsonStringify({
|
|
74
|
+
version: IMAGE_INPUT_POLICY_VERSION,
|
|
75
|
+
...IMAGE_INPUT_POLICY,
|
|
76
|
+
}),
|
|
77
|
+
),
|
|
78
|
+
inputModalities,
|
|
79
|
+
toolResultModalities,
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function normalizeSessionCompatibilityContract(
|
|
85
|
+
contract: SessionCompatibilityContract,
|
|
86
|
+
): SessionCompatibilityContract {
|
|
87
|
+
return createSessionCompatibilityContract({
|
|
88
|
+
modelName: contract.modelName,
|
|
89
|
+
...(contract.profileName === undefined
|
|
90
|
+
? {}
|
|
91
|
+
: { profileName: contract.profileName }),
|
|
92
|
+
includeReasoningContent: contract.includeReasoningContent,
|
|
93
|
+
contextProfile: contract.contextProfile,
|
|
94
|
+
messageProtocol: contract.messageProtocol,
|
|
95
|
+
inputModalities: contract.media.inputModalities,
|
|
96
|
+
toolResultModalities: contract.media.toolResultModalities,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function normalizeInputModalities(
|
|
101
|
+
modalities: readonly ("text" | "image")[],
|
|
102
|
+
): readonly ("text" | "image")[] {
|
|
103
|
+
if (
|
|
104
|
+
modalities.length === 0 ||
|
|
105
|
+
modalities.some((value) => value !== "text" && value !== "image") ||
|
|
106
|
+
new Set(modalities).size !== modalities.length ||
|
|
107
|
+
!modalities.includes("text")
|
|
108
|
+
) {
|
|
109
|
+
throw new Error("Session compatibility input modalities are invalid.");
|
|
110
|
+
}
|
|
111
|
+
return Object.freeze(
|
|
112
|
+
modalities.includes("image") ? (["text", "image"] as const) : (["text"] as const),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function decodeMeta(
|
|
117
|
+
value: unknown,
|
|
118
|
+
expectedSessionId: SessionId,
|
|
119
|
+
): StoredSessionMetaV10 {
|
|
120
|
+
const row = recordFromSql(value, "session metadata");
|
|
121
|
+
const sessionId = stringFromSql(row.session_id, "session_id") as SessionId;
|
|
122
|
+
if (sessionId !== expectedSessionId) {
|
|
123
|
+
throw new Error(`Metadata session ID ${sessionId} does not match directory.`);
|
|
124
|
+
}
|
|
125
|
+
const schemaVersion = numberFromSql(row.schema_version, "schema_version");
|
|
126
|
+
if (schemaVersion !== 10) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Session metadata schema version must be 10; received ${schemaVersion}.`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const projectInstructionFile = nullableStringFromSql(
|
|
132
|
+
row.project_instruction_file,
|
|
133
|
+
"project_instruction_file",
|
|
134
|
+
);
|
|
135
|
+
const projectInstructionByteLength = nullableNumberFromSql(
|
|
136
|
+
row.project_instruction_byte_length,
|
|
137
|
+
"project_instruction_byte_length",
|
|
138
|
+
);
|
|
139
|
+
const projectInstructionSha256 = nullableStringFromSql(
|
|
140
|
+
row.project_instruction_sha256,
|
|
141
|
+
"project_instruction_sha256",
|
|
142
|
+
);
|
|
143
|
+
if (
|
|
144
|
+
(projectInstructionFile === null) !== (projectInstructionByteLength === null) ||
|
|
145
|
+
(projectInstructionFile === null) !== (projectInstructionSha256 === null)
|
|
146
|
+
) {
|
|
147
|
+
throw new Error("Project instruction metadata must be entirely set or null.");
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
projectInstructionFile !== null &&
|
|
151
|
+
projectInstructionFile !== "AGENTS.md" &&
|
|
152
|
+
projectInstructionFile !== "CLAUDE.md"
|
|
153
|
+
) {
|
|
154
|
+
throw new Error(`Invalid project instruction file ${projectInstructionFile}.`);
|
|
155
|
+
}
|
|
156
|
+
const projectInstruction: ProjectInstructionManifest | undefined =
|
|
157
|
+
projectInstructionFile === null ||
|
|
158
|
+
projectInstructionByteLength === null ||
|
|
159
|
+
projectInstructionSha256 === null
|
|
160
|
+
? undefined
|
|
161
|
+
: {
|
|
162
|
+
path: projectInstructionFile === "AGENTS.md" ? "AGENTS.md" : "CLAUDE.md",
|
|
163
|
+
byteLength: projectInstructionByteLength,
|
|
164
|
+
sha256: sha256FromSql(projectInstructionSha256, "project_instruction_sha256"),
|
|
165
|
+
};
|
|
166
|
+
const initializationState = enumFromSql(
|
|
167
|
+
row.initialization_state,
|
|
168
|
+
["creating", "ready"] as const,
|
|
169
|
+
"initialization_state",
|
|
170
|
+
);
|
|
171
|
+
const sessionCompatibilityJson = nullableStringFromSql(
|
|
172
|
+
row.session_compatibility_json,
|
|
173
|
+
"session_compatibility_json",
|
|
174
|
+
);
|
|
175
|
+
const sessionCompatibilitySha256 = nullableStringFromSql(
|
|
176
|
+
row.session_compatibility_sha256,
|
|
177
|
+
"session_compatibility_sha256",
|
|
178
|
+
);
|
|
179
|
+
const activeRevisionId = nullableStringFromSql(
|
|
180
|
+
row.active_revision_id,
|
|
181
|
+
"active_revision_id",
|
|
182
|
+
) as ContextRevisionId | null;
|
|
183
|
+
const modelName = stringFromSql(row.model_name, "model_name");
|
|
184
|
+
const storedContract =
|
|
185
|
+
sessionCompatibilityJson === null
|
|
186
|
+
? undefined
|
|
187
|
+
: decodeSessionCompatibilityContract(sessionCompatibilityJson);
|
|
188
|
+
if (
|
|
189
|
+
(sessionCompatibilityJson === null) !== (sessionCompatibilitySha256 === null) ||
|
|
190
|
+
(sessionCompatibilityJson !== null &&
|
|
191
|
+
sha256(sessionCompatibilityJson) !== sessionCompatibilitySha256) ||
|
|
192
|
+
(initializationState === "creating") !== (activeRevisionId === null) ||
|
|
193
|
+
(initializationState === "creating") !== (sessionCompatibilityJson === null) ||
|
|
194
|
+
(storedContract !== undefined &&
|
|
195
|
+
(storedContract.modelName !== modelName ||
|
|
196
|
+
stableJsonStringify(storedContract) !== sessionCompatibilityJson))
|
|
197
|
+
) {
|
|
198
|
+
throw new Error("Session compatibility or initialization metadata is invalid.");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
schemaVersion,
|
|
202
|
+
schemaFingerprint: stringFromSql(row.schema_fingerprint, "schema_fingerprint"),
|
|
203
|
+
initializationState,
|
|
204
|
+
sessionId,
|
|
205
|
+
workspaceRoot: stringFromSql(row.workspace_root, "workspace_root"),
|
|
206
|
+
modelName,
|
|
207
|
+
systemPromptSha256: stringFromSql(row.system_prompt_sha256, "system_prompt_sha256"),
|
|
208
|
+
...(projectInstruction === undefined ? {} : { projectInstruction }),
|
|
209
|
+
sessionCompatibilityJson,
|
|
210
|
+
sessionCompatibilitySha256,
|
|
211
|
+
activeRevisionId,
|
|
212
|
+
nextTurnNumber: numberFromSql(row.next_turn_number, "next_turn_number"),
|
|
213
|
+
nextEventSequence: numberFromSql(row.next_event_sequence, "next_event_sequence"),
|
|
214
|
+
openCount: numberFromSql(row.open_count, "open_count"),
|
|
215
|
+
createdAt: timestampFromSql(row.created_at, "created_at"),
|
|
216
|
+
updatedAt: timestampFromSql(row.updated_at, "updated_at"),
|
|
217
|
+
lastOpenedAt: timestampFromSql(row.last_opened_at, "last_opened_at"),
|
|
218
|
+
lastClosedAt:
|
|
219
|
+
row.last_closed_at === null
|
|
220
|
+
? null
|
|
221
|
+
: timestampFromSql(row.last_closed_at, "last_closed_at"),
|
|
222
|
+
lastCloseReason:
|
|
223
|
+
row.last_close_reason === null
|
|
224
|
+
? null
|
|
225
|
+
: enumFromSql(
|
|
226
|
+
row.last_close_reason,
|
|
227
|
+
[
|
|
228
|
+
"oneshot_complete",
|
|
229
|
+
"tui_exit",
|
|
230
|
+
"session_switch",
|
|
231
|
+
"runner_failed",
|
|
232
|
+
"initialization_failed",
|
|
233
|
+
] as const,
|
|
234
|
+
"last_close_reason",
|
|
235
|
+
),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function compatibilityContractDifferences(
|
|
240
|
+
storedJson: string | null,
|
|
241
|
+
current: SessionCompatibilityContract,
|
|
242
|
+
): string[] {
|
|
243
|
+
if (storedJson === null) {
|
|
244
|
+
return ["sessionCompatibility"];
|
|
245
|
+
}
|
|
246
|
+
const stored = parseJson(storedJson, "session_compatibility_json");
|
|
247
|
+
if (typeof stored !== "object" || stored === null || Array.isArray(stored)) {
|
|
248
|
+
return ["sessionCompatibility"];
|
|
249
|
+
}
|
|
250
|
+
const record = stored as Record<string, unknown>;
|
|
251
|
+
const fields: readonly (keyof SessionCompatibilityContract)[] = [
|
|
252
|
+
"modelName",
|
|
253
|
+
"profileName",
|
|
254
|
+
"includeReasoningContent",
|
|
255
|
+
"contextProfile",
|
|
256
|
+
"messageProtocol",
|
|
257
|
+
"media",
|
|
258
|
+
];
|
|
259
|
+
return fields.filter((key) => {
|
|
260
|
+
const storedValue = record[key];
|
|
261
|
+
const currentValue = current[key];
|
|
262
|
+
if (storedValue === undefined || currentValue === undefined) {
|
|
263
|
+
return storedValue !== currentValue;
|
|
264
|
+
}
|
|
265
|
+
return stableJsonStringify(storedValue) !== stableJsonStringify(currentValue);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function decodeSessionCompatibilityContract(
|
|
270
|
+
json: string,
|
|
271
|
+
): SessionCompatibilityContract {
|
|
272
|
+
const record = recordFromSql(
|
|
273
|
+
parseJson(json, "session_compatibility_json"),
|
|
274
|
+
"session compatibility contract",
|
|
275
|
+
);
|
|
276
|
+
assertObjectKeys(
|
|
277
|
+
record,
|
|
278
|
+
[
|
|
279
|
+
"modelName",
|
|
280
|
+
"profileName",
|
|
281
|
+
"includeReasoningContent",
|
|
282
|
+
"contextProfile",
|
|
283
|
+
"messageProtocol",
|
|
284
|
+
"media",
|
|
285
|
+
],
|
|
286
|
+
[
|
|
287
|
+
"modelName",
|
|
288
|
+
"includeReasoningContent",
|
|
289
|
+
"contextProfile",
|
|
290
|
+
"messageProtocol",
|
|
291
|
+
"media",
|
|
292
|
+
],
|
|
293
|
+
"session compatibility contract",
|
|
294
|
+
);
|
|
295
|
+
const contextProfile = recordFromSql(
|
|
296
|
+
record.contextProfile,
|
|
297
|
+
"session compatibility context profile",
|
|
298
|
+
);
|
|
299
|
+
assertObjectKeys(
|
|
300
|
+
contextProfile,
|
|
301
|
+
["contextWindowTokens", "maxSupportedOutputTokens"],
|
|
302
|
+
["contextWindowTokens", "maxSupportedOutputTokens"],
|
|
303
|
+
"session compatibility context profile",
|
|
304
|
+
);
|
|
305
|
+
const messageProtocol = recordFromSql(
|
|
306
|
+
record.messageProtocol,
|
|
307
|
+
"session compatibility message protocol",
|
|
308
|
+
);
|
|
309
|
+
assertObjectKeys(
|
|
310
|
+
messageProtocol,
|
|
311
|
+
["adapter", "serializationVersion"],
|
|
312
|
+
["adapter", "serializationVersion"],
|
|
313
|
+
"session compatibility message protocol",
|
|
314
|
+
);
|
|
315
|
+
const media = recordFromSql(record.media, "session compatibility media");
|
|
316
|
+
assertObjectKeys(
|
|
317
|
+
media,
|
|
318
|
+
["policyVersion", "policySha256", "inputModalities", "toolResultModalities"],
|
|
319
|
+
["policyVersion", "policySha256", "inputModalities", "toolResultModalities"],
|
|
320
|
+
"session compatibility media",
|
|
321
|
+
);
|
|
322
|
+
const inputModalities = decodeCompatibilityModalities(media.inputModalities, "input");
|
|
323
|
+
const toolResultModalities = decodeCompatibilityModalities(
|
|
324
|
+
media.toolResultModalities,
|
|
325
|
+
"tool result",
|
|
326
|
+
);
|
|
327
|
+
if (toolResultModalities.includes("image") && !inputModalities.includes("image")) {
|
|
328
|
+
throw new Error("Session compatibility image tool results require image input.");
|
|
329
|
+
}
|
|
330
|
+
if (typeof record.includeReasoningContent !== "boolean") {
|
|
331
|
+
throw new Error("Session compatibility reasoning replay flag must be boolean.");
|
|
332
|
+
}
|
|
333
|
+
const modelName = stringFromSql(record.modelName, "compatibility modelName");
|
|
334
|
+
const profileName =
|
|
335
|
+
record.profileName === undefined
|
|
336
|
+
? undefined
|
|
337
|
+
: stringFromSql(record.profileName, "compatibility profileName");
|
|
338
|
+
const context = createModelContextProfile({
|
|
339
|
+
contextWindowTokens: numberFromJson(
|
|
340
|
+
contextProfile.contextWindowTokens,
|
|
341
|
+
"compatibility contextWindowTokens",
|
|
342
|
+
),
|
|
343
|
+
maxSupportedOutputTokens: numberFromJson(
|
|
344
|
+
contextProfile.maxSupportedOutputTokens,
|
|
345
|
+
"compatibility maxSupportedOutputTokens",
|
|
346
|
+
),
|
|
347
|
+
});
|
|
348
|
+
const protocol: ModelMessageProtocol = Object.freeze({
|
|
349
|
+
adapter: enumFromSql(
|
|
350
|
+
messageProtocol.adapter,
|
|
351
|
+
MODEL_MESSAGE_PROTOCOL_ADAPTERS,
|
|
352
|
+
"compatibility message adapter",
|
|
353
|
+
),
|
|
354
|
+
serializationVersion: stringFromSql(
|
|
355
|
+
messageProtocol.serializationVersion,
|
|
356
|
+
"compatibility serializationVersion",
|
|
357
|
+
),
|
|
358
|
+
});
|
|
359
|
+
const policyVersion = stringFromSql(
|
|
360
|
+
media.policyVersion,
|
|
361
|
+
"compatibility image policyVersion",
|
|
362
|
+
);
|
|
363
|
+
const policySha256 = stringFromSql(
|
|
364
|
+
media.policySha256,
|
|
365
|
+
"compatibility image policySha256",
|
|
366
|
+
);
|
|
367
|
+
if (!/^[0-9a-f]{64}$/.test(policySha256)) {
|
|
368
|
+
throw new Error("Session image policy hash is invalid.");
|
|
369
|
+
}
|
|
370
|
+
return Object.freeze({
|
|
371
|
+
modelName,
|
|
372
|
+
...(record.profileName === undefined ? {} : { profileName: profileName! }),
|
|
373
|
+
includeReasoningContent: record.includeReasoningContent,
|
|
374
|
+
contextProfile: Object.freeze(context),
|
|
375
|
+
messageProtocol: protocol,
|
|
376
|
+
media: Object.freeze({
|
|
377
|
+
policyVersion,
|
|
378
|
+
policySha256,
|
|
379
|
+
inputModalities,
|
|
380
|
+
toolResultModalities,
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function decodeCompatibilityModalities(
|
|
386
|
+
value: unknown,
|
|
387
|
+
label: string,
|
|
388
|
+
): readonly ("text" | "image")[] {
|
|
389
|
+
if (!Array.isArray(value)) {
|
|
390
|
+
throw new Error(`Session compatibility ${label} modalities must be an array.`);
|
|
391
|
+
}
|
|
392
|
+
return normalizeInputModalities(
|
|
393
|
+
value.map((modality) =>
|
|
394
|
+
enumFromSql(
|
|
395
|
+
modality,
|
|
396
|
+
["text", "image"] as const,
|
|
397
|
+
`compatibility ${label} modality`,
|
|
398
|
+
),
|
|
399
|
+
),
|
|
400
|
+
);
|
|
401
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ContextRevisionId,
|
|
3
|
+
IterationId,
|
|
4
|
+
MessageId,
|
|
5
|
+
ProtocolFrameId,
|
|
6
|
+
RuntimeIdFactory,
|
|
7
|
+
SessionId,
|
|
8
|
+
ToolCallId,
|
|
9
|
+
TurnId,
|
|
10
|
+
} from "../ids/runtime-id";
|
|
11
|
+
import type { MeasuredContextAnchor } from "../agent/context-meter";
|
|
12
|
+
import type { ModelContextProfile } from "../model/model-context-profile";
|
|
13
|
+
import type { ModelMessageProtocol } from "../model/model-client";
|
|
14
|
+
import { sha256, stableJsonStringify } from "../model/model-request-preflight";
|
|
15
|
+
import type { ProjectInstructionManifest } from "../instructions/project-instructions";
|
|
16
|
+
import type { SkillScope } from "../skills/skill-loader";
|
|
17
|
+
import type {
|
|
18
|
+
ContextSurfaceChanges,
|
|
19
|
+
StoredContextSurfaceV8,
|
|
20
|
+
} from "../context/context-surface";
|
|
21
|
+
import type { SwapOverride } from "../context/context-revision";
|
|
22
|
+
import { SWAP_OBSERVATION_FORMAT } from "../context/context-swap-renderer";
|
|
23
|
+
|
|
24
|
+
export type SessionMediaCompatibility = {
|
|
25
|
+
readonly policyVersion: string;
|
|
26
|
+
readonly policySha256: string;
|
|
27
|
+
readonly inputModalities: readonly ("text" | "image")[];
|
|
28
|
+
readonly toolResultModalities: readonly ("text" | "image")[];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type CompletedTurnMessageSnapshot =
|
|
32
|
+
| {
|
|
33
|
+
readonly ordinal: number;
|
|
34
|
+
readonly role: "user";
|
|
35
|
+
readonly content: string;
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
readonly ordinal: number;
|
|
39
|
+
readonly role: "assistant";
|
|
40
|
+
readonly content: string | null;
|
|
41
|
+
readonly reasoningContent?: string | null;
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
readonly ordinal: number;
|
|
45
|
+
readonly role: "tool";
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly content: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type CompletedTurnSnapshot = {
|
|
51
|
+
readonly messages: readonly CompletedTurnMessageSnapshot[];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type SessionCompatibilityContract = {
|
|
55
|
+
modelName: string;
|
|
56
|
+
profileName?: string;
|
|
57
|
+
includeReasoningContent: boolean;
|
|
58
|
+
contextProfile: ModelContextProfile;
|
|
59
|
+
messageProtocol: ModelMessageProtocol;
|
|
60
|
+
media: SessionMediaCompatibility;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type StoredSessionMetaV10 = {
|
|
64
|
+
schemaVersion: 10;
|
|
65
|
+
schemaFingerprint: string;
|
|
66
|
+
initializationState: "creating" | "ready";
|
|
67
|
+
sessionId: SessionId;
|
|
68
|
+
workspaceRoot: string;
|
|
69
|
+
modelName: string;
|
|
70
|
+
systemPromptSha256: string;
|
|
71
|
+
projectInstruction?: ProjectInstructionManifest;
|
|
72
|
+
sessionCompatibilityJson: string | null;
|
|
73
|
+
sessionCompatibilitySha256: string | null;
|
|
74
|
+
activeRevisionId: ContextRevisionId | null;
|
|
75
|
+
nextTurnNumber: number;
|
|
76
|
+
nextEventSequence: number;
|
|
77
|
+
openCount: number;
|
|
78
|
+
createdAt: string;
|
|
79
|
+
updatedAt: string;
|
|
80
|
+
lastOpenedAt: string;
|
|
81
|
+
lastClosedAt: string | null;
|
|
82
|
+
lastCloseReason:
|
|
83
|
+
| "oneshot_complete"
|
|
84
|
+
| "tui_exit"
|
|
85
|
+
| "session_switch"
|
|
86
|
+
| "runner_failed"
|
|
87
|
+
| "initialization_failed"
|
|
88
|
+
| null;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type SessionCloseReason = NonNullable<StoredSessionMetaV10["lastCloseReason"]>;
|
|
92
|
+
|
|
93
|
+
export type SessionRecoveryResult = {
|
|
94
|
+
recoveredTurnId?: TurnId;
|
|
95
|
+
recoveredFrameId?: ProtocolFrameId;
|
|
96
|
+
syntheticCompletionCount: number;
|
|
97
|
+
recallIndexRebuilt: boolean;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type StoredMeasuredContextState = {
|
|
101
|
+
revisionId: ContextRevisionId;
|
|
102
|
+
anchor: MeasuredContextAnchor;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export type CommitSwapRevisionInput = {
|
|
106
|
+
revisionId: ContextRevisionId;
|
|
107
|
+
expectedBaseRevisionId: ContextRevisionId;
|
|
108
|
+
expectedBaseRevisionNumber: number;
|
|
109
|
+
expectedCanonicalThroughOrdinal: number;
|
|
110
|
+
expectedBaseActiveOverrideManifestSha256: string;
|
|
111
|
+
policyVersion: "swap-only-v1";
|
|
112
|
+
rendererFormat: typeof SWAP_OBSERVATION_FORMAT;
|
|
113
|
+
planHash: string;
|
|
114
|
+
addedOverrides: readonly SwapOverride[];
|
|
115
|
+
nextActiveOverrideManifestSha256: string;
|
|
116
|
+
canonicalSequenceSha256: string;
|
|
117
|
+
renderedMessageSha256: string;
|
|
118
|
+
activeTurnId?: TurnId;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type CommitSwapRevisionFaultStage =
|
|
122
|
+
| "before_revision_insert"
|
|
123
|
+
| "after_revision_insert"
|
|
124
|
+
| "after_first_override_insert"
|
|
125
|
+
| "after_overrides_insert"
|
|
126
|
+
| "after_measurement_delete"
|
|
127
|
+
| "after_active_update";
|
|
128
|
+
|
|
129
|
+
export type CommitSwapRevisionOptions = {
|
|
130
|
+
faultInjector?: (stage: CommitSwapRevisionFaultStage) => void;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type CommitSurfaceRefreshInput = {
|
|
134
|
+
revisionId: ContextRevisionId;
|
|
135
|
+
expectedBaseRevisionId: ContextRevisionId;
|
|
136
|
+
expectedBaseRevisionNumber: number;
|
|
137
|
+
expectedCanonicalThroughOrdinal: number;
|
|
138
|
+
expectedBaseActiveOverrideManifestSha256: string;
|
|
139
|
+
surface: StoredContextSurfaceV8;
|
|
140
|
+
changes: ContextSurfaceChanges;
|
|
141
|
+
changeManifestSha256: string;
|
|
142
|
+
canonicalSequenceSha256: string;
|
|
143
|
+
renderedMessageSha256: string;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type CommitSurfaceRefreshFaultStage =
|
|
147
|
+
| "before_surface_insert"
|
|
148
|
+
| "after_surface_insert"
|
|
149
|
+
| "after_revision_insert"
|
|
150
|
+
| "after_measurement_delete"
|
|
151
|
+
| "after_active_update";
|
|
152
|
+
|
|
153
|
+
export type CommitSurfaceRefreshOptions = {
|
|
154
|
+
faultInjector?: (stage: CommitSurfaceRefreshFaultStage) => void;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export type CommitPrefixRetirementRevisionInput = {
|
|
158
|
+
revisionId: ContextRevisionId;
|
|
159
|
+
expectedBaseRevisionId: ContextRevisionId;
|
|
160
|
+
expectedBaseRevisionNumber: number;
|
|
161
|
+
expectedBaseKeepFromOrdinal: number;
|
|
162
|
+
expectedCanonicalThroughOrdinal: number;
|
|
163
|
+
expectedSurfaceSha256: string;
|
|
164
|
+
expectedBaseActiveOverrideManifestSha256: string;
|
|
165
|
+
policyVersion: "recall-first-retirement-v1";
|
|
166
|
+
planHash: string;
|
|
167
|
+
nextKeepFromOrdinal: number;
|
|
168
|
+
retiredThroughOrdinal: number;
|
|
169
|
+
retiredTurnCount: number;
|
|
170
|
+
retiredFrameCount: number;
|
|
171
|
+
retiredMessageCount: number;
|
|
172
|
+
nextActiveOverrideCount: number;
|
|
173
|
+
nextActiveOverrideManifestSha256: string;
|
|
174
|
+
canonicalSequenceSha256: string;
|
|
175
|
+
renderedMessageSha256: string;
|
|
176
|
+
activeTurnId?: TurnId;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export type CommitPrefixRetirementRevisionFaultStage =
|
|
180
|
+
| "before_revision_insert"
|
|
181
|
+
| "after_revision_insert"
|
|
182
|
+
| "after_override_readback"
|
|
183
|
+
| "after_measurement_delete"
|
|
184
|
+
| "after_active_update"
|
|
185
|
+
| "after_snapshot_readback";
|
|
186
|
+
|
|
187
|
+
export type CommitPrefixRetirementRevisionOptions = {
|
|
188
|
+
faultInjector?: (stage: CommitPrefixRetirementRevisionFaultStage) => void;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export type StoredSkillActivation = {
|
|
192
|
+
readonly activationMessageId: MessageId;
|
|
193
|
+
readonly toolCallId: ToolCallId;
|
|
194
|
+
readonly sessionId: SessionId;
|
|
195
|
+
readonly name: string;
|
|
196
|
+
readonly scope: SkillScope;
|
|
197
|
+
readonly skillFileSha256: string;
|
|
198
|
+
readonly state: "pending" | "dispatched" | "promoted" | "rejected";
|
|
199
|
+
readonly dispatchedIterationId?: IterationId;
|
|
200
|
+
readonly settledRevisionId?: ContextRevisionId;
|
|
201
|
+
readonly rejectionReason?: string;
|
|
202
|
+
readonly createdAt: string;
|
|
203
|
+
readonly updatedAt: string;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export type CommitSkillsUpdateInput = {
|
|
207
|
+
revisionId: ContextRevisionId;
|
|
208
|
+
expectedBaseRevisionId: ContextRevisionId;
|
|
209
|
+
expectedBaseRevisionNumber: number;
|
|
210
|
+
expectedCanonicalThroughOrdinal: number;
|
|
211
|
+
expectedBaseActiveOverrideManifestSha256: string;
|
|
212
|
+
surface: StoredContextSurfaceV8;
|
|
213
|
+
changes: ContextSurfaceChanges;
|
|
214
|
+
changeManifestSha256: string;
|
|
215
|
+
activationManifestSha256: string;
|
|
216
|
+
addedOverrides: readonly SwapOverride[];
|
|
217
|
+
nextActiveOverrideManifestSha256: string;
|
|
218
|
+
settlements: readonly {
|
|
219
|
+
activationMessageId: MessageId;
|
|
220
|
+
name: string;
|
|
221
|
+
state: "promoted" | "rejected";
|
|
222
|
+
rejectionReason?: string;
|
|
223
|
+
}[];
|
|
224
|
+
canonicalSequenceSha256: string;
|
|
225
|
+
renderedMessageSha256: string;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export type CommitSkillsUpdateFaultStage =
|
|
229
|
+
| "before_surface_insert"
|
|
230
|
+
| "after_surface_insert"
|
|
231
|
+
| "after_revision_insert"
|
|
232
|
+
| "after_first_override_insert"
|
|
233
|
+
| "after_overrides_insert"
|
|
234
|
+
| "after_activations_update"
|
|
235
|
+
| "after_measurement_delete"
|
|
236
|
+
| "after_active_update";
|
|
237
|
+
|
|
238
|
+
export type CommitSkillsUpdateOptions = {
|
|
239
|
+
faultInjector?: (stage: CommitSkillsUpdateFaultStage) => void;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export type CloneSessionFaultStage =
|
|
243
|
+
| "after_staging_mkdir"
|
|
244
|
+
| "after_snapshot"
|
|
245
|
+
| "after_trigger_drop"
|
|
246
|
+
| "after_identity_update"
|
|
247
|
+
| "after_revision_hash_rewrite"
|
|
248
|
+
| "after_trigger_reinstall"
|
|
249
|
+
| "after_recall_validation"
|
|
250
|
+
| "after_event_rewrite"
|
|
251
|
+
| "after_observation_render"
|
|
252
|
+
| "after_artifact_validation"
|
|
253
|
+
| "before_publish_rename";
|
|
254
|
+
|
|
255
|
+
export type CloneSessionStoreInput = {
|
|
256
|
+
targetSessionId: SessionId;
|
|
257
|
+
faultInjector?: (stage: CloneSessionFaultStage) => void;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
export function skillActivationManifestSha256(
|
|
261
|
+
settlements: CommitSkillsUpdateInput["settlements"],
|
|
262
|
+
): string {
|
|
263
|
+
return sha256(
|
|
264
|
+
stableJsonStringify(
|
|
265
|
+
[...settlements]
|
|
266
|
+
.sort(
|
|
267
|
+
(left, right) =>
|
|
268
|
+
compareCanonicalText(left.name, right.name) ||
|
|
269
|
+
compareCanonicalText(left.activationMessageId, right.activationMessageId),
|
|
270
|
+
)
|
|
271
|
+
.map((settlement) => ({
|
|
272
|
+
activationMessageId: settlement.activationMessageId,
|
|
273
|
+
name: settlement.name,
|
|
274
|
+
state: settlement.state,
|
|
275
|
+
...(settlement.rejectionReason === undefined
|
|
276
|
+
? {}
|
|
277
|
+
: { rejectionReason: settlement.rejectionReason }),
|
|
278
|
+
})),
|
|
279
|
+
),
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export type CreateNewSessionStoreInput = {
|
|
284
|
+
workspaceRoot: string;
|
|
285
|
+
sessionId: SessionId;
|
|
286
|
+
modelName: string;
|
|
287
|
+
systemPrompt: string;
|
|
288
|
+
projectInstruction?: ProjectInstructionManifest;
|
|
289
|
+
idFactory: RuntimeIdFactory;
|
|
290
|
+
clock?: () => string;
|
|
291
|
+
homeRoot?: string;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export type OpenSessionStoreInput = {
|
|
295
|
+
workspaceRoot: string;
|
|
296
|
+
sessionId: SessionId;
|
|
297
|
+
clock?: () => string;
|
|
298
|
+
allowIncomplete?: boolean;
|
|
299
|
+
homeRoot?: string;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
function compareCanonicalText(left: string, right: string): number {
|
|
303
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
304
|
+
}
|