tinker-agent 2.8.0 → 2.9.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 +39 -1
- package/README.md +64 -10
- package/package.json +4 -3
- package/src/agent/runtime-context-capabilities.ts +19 -0
- package/src/agent/runtime-context-events.ts +127 -0
- package/src/agent/runtime-context-maintenance.ts +780 -0
- package/src/agent/runtime-interactions.ts +291 -0
- package/src/agent/runtime-prompt-scheduler.ts +182 -0
- package/src/agent/runtime-session-contracts.ts +317 -0
- package/src/agent/runtime-session.ts +250 -2130
- package/src/agent/runtime-skills.ts +544 -0
- package/src/cli/runner-dependencies.ts +6 -5
- package/src/context/context-automation-policy.ts +12 -118
- package/src/events/types.ts +12 -0
- package/src/memory/memory-get-tool.ts +1 -1
- package/src/observation/observation-builder.ts +41 -11
- package/src/session/resume-projection.ts +47 -21
- package/src/session/session-history-access.ts +238 -0
- package/src/session/session-store-context-readers.ts +183 -0
- package/src/session/session-store-ledger-writer.ts +315 -0
- package/src/session/session-store-record-writer.ts +318 -0
- package/src/session/session-store-recovery.ts +225 -0
- package/src/session/session-store-revisions.ts +1004 -0
- package/src/session/session-store-sql.ts +40 -0
- package/src/session/session-store-validation.ts +657 -0
- package/src/session/session-store.ts +756 -3186
- package/src/tools/bash-task.ts +20 -2
- package/src/tools/recall.ts +106 -50
- package/src/tools/registry.ts +4 -6
- package/src/tools/task-output-range.ts +146 -0
- package/src/tools/task-output-tool.ts +35 -5
- package/src/tools/task-output.ts +35 -0
- package/src/tools/task-tool-args.ts +34 -0
- package/src/tools/types.ts +9 -0
- package/src/tui/event-store.ts +8 -3
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import {
|
|
3
|
+
activeOverrideManifestHash,
|
|
4
|
+
canonicalSequenceHash,
|
|
5
|
+
renderedMessageHash,
|
|
6
|
+
} from "../context/compiled-context-hash";
|
|
7
|
+
import type {
|
|
8
|
+
StoredContextRevisionV8,
|
|
9
|
+
SwapOverride,
|
|
10
|
+
} from "../context/context-revision";
|
|
11
|
+
import { ContextRevisionCompiler } from "../context/context-revision-compiler";
|
|
12
|
+
import {
|
|
13
|
+
contextSurfaceChangeManifestHash,
|
|
14
|
+
contextSurfaceChanges,
|
|
15
|
+
validateStoredContextSurface,
|
|
16
|
+
} from "../context/context-surface";
|
|
17
|
+
import { SWAP_OBSERVATION_FORMAT } from "../context/context-swap-renderer";
|
|
18
|
+
import { type ProtocolContextView } from "../context/protocol-frame";
|
|
19
|
+
import type { SessionId } from "../ids/runtime-id";
|
|
20
|
+
import { stableJsonStringify } from "../model/model-request-preflight";
|
|
21
|
+
import {
|
|
22
|
+
renderSkillActivationReceipt,
|
|
23
|
+
SKILL_ACTIVATION_RECEIPT_FORMAT,
|
|
24
|
+
SKILL_POLICY_VERSION,
|
|
25
|
+
} from "../skills/skill-context";
|
|
26
|
+
import { sessionWriteError } from "./session-errors";
|
|
27
|
+
import type { SessionStore } from "./session-store";
|
|
28
|
+
import {
|
|
29
|
+
loadMeasuredContextState,
|
|
30
|
+
readRetirementBoundaries,
|
|
31
|
+
requireActiveRevisionId,
|
|
32
|
+
} from "./session-store-context-readers";
|
|
33
|
+
import {
|
|
34
|
+
skillActivationManifestSha256,
|
|
35
|
+
type CommitPrefixRetirementRevisionInput,
|
|
36
|
+
type CommitPrefixRetirementRevisionOptions,
|
|
37
|
+
type CommitSkillsUpdateInput,
|
|
38
|
+
type CommitSkillsUpdateOptions,
|
|
39
|
+
type CommitSurfaceRefreshInput,
|
|
40
|
+
type CommitSurfaceRefreshOptions,
|
|
41
|
+
type CommitSwapRevisionInput,
|
|
42
|
+
type CommitSwapRevisionOptions,
|
|
43
|
+
} from "./session-store-contracts";
|
|
44
|
+
import { decodeStoredSwapOverride } from "./session-store-record-codecs";
|
|
45
|
+
import { insertContextSurface } from "./session-store-record-writer";
|
|
46
|
+
import { requireItem, requireSingleChange, runTransaction } from "./session-store-sql";
|
|
47
|
+
|
|
48
|
+
type RevisionStore = Pick<
|
|
49
|
+
SessionStore,
|
|
50
|
+
| "loadContextSnapshot"
|
|
51
|
+
| "assertContextRevisionBoundary"
|
|
52
|
+
| "assertContextRevisionIdle"
|
|
53
|
+
| "readMeta"
|
|
54
|
+
| "loadSkillActivations"
|
|
55
|
+
>;
|
|
56
|
+
/** Each operation owns its complete validation-and-write transaction. */
|
|
57
|
+
export class SessionStoreRevisions {
|
|
58
|
+
private readonly revisionCompiler = new ContextRevisionCompiler();
|
|
59
|
+
constructor(
|
|
60
|
+
private readonly database: Database,
|
|
61
|
+
private readonly sessionId: SessionId,
|
|
62
|
+
private readonly clock: () => string,
|
|
63
|
+
private readonly store: RevisionStore,
|
|
64
|
+
private readonly requireOpen: () => void,
|
|
65
|
+
private readonly validateAddedOverrides: (
|
|
66
|
+
overrides: readonly SwapOverride[],
|
|
67
|
+
canonical: ProtocolContextView,
|
|
68
|
+
) => void,
|
|
69
|
+
) {}
|
|
70
|
+
|
|
71
|
+
commitSwapRevision(
|
|
72
|
+
input: CommitSwapRevisionInput,
|
|
73
|
+
options: CommitSwapRevisionOptions = {},
|
|
74
|
+
): Extract<StoredContextRevisionV8, { kind: "swap_only" }> {
|
|
75
|
+
this.requireOpen();
|
|
76
|
+
assertCommitSwapRevisionInput(input);
|
|
77
|
+
const now = this.clock();
|
|
78
|
+
try {
|
|
79
|
+
return runTransaction(this.database, () => {
|
|
80
|
+
const snapshot = this.store.loadContextSnapshot();
|
|
81
|
+
const baseRevision = snapshot.revision;
|
|
82
|
+
if (
|
|
83
|
+
baseRevision.revisionId !== input.expectedBaseRevisionId ||
|
|
84
|
+
baseRevision.revisionNumber !== input.expectedBaseRevisionNumber ||
|
|
85
|
+
snapshot.canonical.messages.length !==
|
|
86
|
+
input.expectedCanonicalThroughOrdinal ||
|
|
87
|
+
baseRevision.activeOverrideManifestSha256 !==
|
|
88
|
+
input.expectedBaseActiveOverrideManifestSha256
|
|
89
|
+
) {
|
|
90
|
+
throw new Error("Context revision commit base is stale.");
|
|
91
|
+
}
|
|
92
|
+
this.store.assertContextRevisionBoundary(input.activeTurnId);
|
|
93
|
+
|
|
94
|
+
const active = this.revisionCompiler.compileActive(snapshot);
|
|
95
|
+
const candidateOverrides = [
|
|
96
|
+
...snapshot.activeOverrides,
|
|
97
|
+
...input.addedOverrides,
|
|
98
|
+
];
|
|
99
|
+
if (
|
|
100
|
+
new Set(candidateOverrides.map((override) => override.messageId)).size !==
|
|
101
|
+
candidateOverrides.length ||
|
|
102
|
+
activeOverrideManifestHash(candidateOverrides) !==
|
|
103
|
+
input.nextActiveOverrideManifestSha256
|
|
104
|
+
) {
|
|
105
|
+
throw new Error("Candidate override manifest is invalid.");
|
|
106
|
+
}
|
|
107
|
+
const candidate = this.revisionCompiler.compileProspective({
|
|
108
|
+
active,
|
|
109
|
+
canonical: snapshot.canonical,
|
|
110
|
+
activeOverrides: snapshot.activeOverrides,
|
|
111
|
+
addedOverrides: input.addedOverrides,
|
|
112
|
+
activeSurface: snapshot.surface,
|
|
113
|
+
});
|
|
114
|
+
if (
|
|
115
|
+
canonicalSequenceHash(
|
|
116
|
+
snapshot.canonical,
|
|
117
|
+
input.expectedCanonicalThroughOrdinal,
|
|
118
|
+
) !== input.canonicalSequenceSha256 ||
|
|
119
|
+
renderedMessageHash(
|
|
120
|
+
candidate.entries,
|
|
121
|
+
input.expectedCanonicalThroughOrdinal,
|
|
122
|
+
) !== input.renderedMessageSha256
|
|
123
|
+
) {
|
|
124
|
+
throw new Error("Candidate context revision prefix hash is invalid.");
|
|
125
|
+
}
|
|
126
|
+
this.validateAddedOverrides(input.addedOverrides, snapshot.canonical);
|
|
127
|
+
|
|
128
|
+
const revisionNumber = baseRevision.revisionNumber + 1;
|
|
129
|
+
const activeOverrideCount =
|
|
130
|
+
baseRevision.activeOverrideCount + input.addedOverrides.length;
|
|
131
|
+
options.faultInjector?.("before_revision_insert");
|
|
132
|
+
this.database
|
|
133
|
+
.query(
|
|
134
|
+
`INSERT INTO context_revisions (
|
|
135
|
+
revision_id, session_id, revision_number, parent_revision_id, kind,
|
|
136
|
+
surface_id, surface_sha256, keep_from_ordinal,
|
|
137
|
+
source_through_ordinal, added_override_count,
|
|
138
|
+
active_override_count, active_override_manifest_sha256,
|
|
139
|
+
canonical_sequence_sha256, rendered_message_sha256, policy_version,
|
|
140
|
+
renderer_format, plan_sha256, change_manifest_sha256, created_at
|
|
141
|
+
) VALUES (?, ?, ?, ?, 'swap_only', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`,
|
|
142
|
+
)
|
|
143
|
+
.run(
|
|
144
|
+
input.revisionId,
|
|
145
|
+
this.sessionId,
|
|
146
|
+
revisionNumber,
|
|
147
|
+
baseRevision.revisionId,
|
|
148
|
+
baseRevision.surfaceId,
|
|
149
|
+
baseRevision.surfaceSha256,
|
|
150
|
+
baseRevision.keepFromOrdinal,
|
|
151
|
+
input.expectedCanonicalThroughOrdinal,
|
|
152
|
+
input.addedOverrides.length,
|
|
153
|
+
activeOverrideCount,
|
|
154
|
+
input.nextActiveOverrideManifestSha256,
|
|
155
|
+
input.canonicalSequenceSha256,
|
|
156
|
+
input.renderedMessageSha256,
|
|
157
|
+
input.policyVersion,
|
|
158
|
+
input.rendererFormat,
|
|
159
|
+
input.planHash,
|
|
160
|
+
now,
|
|
161
|
+
);
|
|
162
|
+
options.faultInjector?.("after_revision_insert");
|
|
163
|
+
|
|
164
|
+
for (let index = 0; index < input.addedOverrides.length; index += 1) {
|
|
165
|
+
const override = requireItem(
|
|
166
|
+
input.addedOverrides,
|
|
167
|
+
index,
|
|
168
|
+
"added context override",
|
|
169
|
+
);
|
|
170
|
+
this.database
|
|
171
|
+
.query(
|
|
172
|
+
`INSERT INTO context_overrides (
|
|
173
|
+
introduced_revision_id, session_id, message_id, frame_id, ordinal,
|
|
174
|
+
representation, renderer_format, source, original_content_sha256,
|
|
175
|
+
rendered_content, rendered_content_sha256, original_bytes,
|
|
176
|
+
rendered_bytes, byte_savings, created_at
|
|
177
|
+
) VALUES (?, ?, ?, ?, ?, 'swapped', ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
178
|
+
)
|
|
179
|
+
.run(
|
|
180
|
+
input.revisionId,
|
|
181
|
+
this.sessionId,
|
|
182
|
+
override.messageId,
|
|
183
|
+
override.frameId,
|
|
184
|
+
override.ordinal,
|
|
185
|
+
input.rendererFormat,
|
|
186
|
+
override.source,
|
|
187
|
+
override.originalContentSha256,
|
|
188
|
+
override.renderedContent,
|
|
189
|
+
override.renderedContentSha256,
|
|
190
|
+
override.originalBytes,
|
|
191
|
+
override.renderedBytes,
|
|
192
|
+
override.byteSavings,
|
|
193
|
+
now,
|
|
194
|
+
);
|
|
195
|
+
if (index === 0) {
|
|
196
|
+
options.faultInjector?.("after_first_override_insert");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
options.faultInjector?.("after_overrides_insert");
|
|
200
|
+
|
|
201
|
+
const storedCandidateOverrides = this.database
|
|
202
|
+
.query(
|
|
203
|
+
`SELECT co.* FROM context_overrides co
|
|
204
|
+
JOIN context_revisions cr
|
|
205
|
+
ON cr.revision_id = co.introduced_revision_id
|
|
206
|
+
WHERE cr.revision_number <= ? AND co.ordinal >= ?
|
|
207
|
+
ORDER BY co.ordinal`,
|
|
208
|
+
)
|
|
209
|
+
.all(revisionNumber, baseRevision.keepFromOrdinal)
|
|
210
|
+
.map(decodeStoredSwapOverride);
|
|
211
|
+
if (
|
|
212
|
+
storedCandidateOverrides.length !== activeOverrideCount ||
|
|
213
|
+
activeOverrideManifestHash(storedCandidateOverrides) !==
|
|
214
|
+
input.nextActiveOverrideManifestSha256
|
|
215
|
+
) {
|
|
216
|
+
throw new Error("Stored candidate override readback is invalid.");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.database.query("DELETE FROM context_measurement_state").run();
|
|
220
|
+
options.faultInjector?.("after_measurement_delete");
|
|
221
|
+
const switched = this.database
|
|
222
|
+
.query(
|
|
223
|
+
`UPDATE session_meta
|
|
224
|
+
SET active_revision_id = ?, updated_at = ?
|
|
225
|
+
WHERE singleton = 1 AND active_revision_id = ?`,
|
|
226
|
+
)
|
|
227
|
+
.run(input.revisionId, now, baseRevision.revisionId);
|
|
228
|
+
requireSingleChange(
|
|
229
|
+
this.database,
|
|
230
|
+
switched.changes,
|
|
231
|
+
"activate context revision",
|
|
232
|
+
);
|
|
233
|
+
options.faultInjector?.("after_active_update");
|
|
234
|
+
|
|
235
|
+
const readback = this.store.loadContextSnapshot();
|
|
236
|
+
if (
|
|
237
|
+
readback.revision.kind !== "swap_only" ||
|
|
238
|
+
readback.revision.revisionId !== input.revisionId ||
|
|
239
|
+
loadMeasuredContextState(this.database, this.sessionId) !== undefined
|
|
240
|
+
) {
|
|
241
|
+
throw new Error("Committed context revision readback failed.");
|
|
242
|
+
}
|
|
243
|
+
return readback.revision;
|
|
244
|
+
});
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (
|
|
247
|
+
requireActiveRevisionId(this.store.readMeta()) !== input.expectedBaseRevisionId
|
|
248
|
+
) {
|
|
249
|
+
throw new Error("Failed context revision transaction changed active state.", {
|
|
250
|
+
cause: error,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
throw sessionWriteError("commit_context_revision", this.sessionId, error);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
commitPrefixRetirementRevision(
|
|
258
|
+
input: CommitPrefixRetirementRevisionInput,
|
|
259
|
+
options: CommitPrefixRetirementRevisionOptions = {},
|
|
260
|
+
): Extract<StoredContextRevisionV8, { kind: "prefix_retirement" }> {
|
|
261
|
+
this.requireOpen();
|
|
262
|
+
assertCommitPrefixRetirementRevisionInput(input);
|
|
263
|
+
const now = this.clock();
|
|
264
|
+
try {
|
|
265
|
+
return runTransaction(this.database, () => {
|
|
266
|
+
const snapshot = this.store.loadContextSnapshot();
|
|
267
|
+
const baseRevision = snapshot.revision;
|
|
268
|
+
if (
|
|
269
|
+
baseRevision.revisionId !== input.expectedBaseRevisionId ||
|
|
270
|
+
baseRevision.revisionNumber !== input.expectedBaseRevisionNumber ||
|
|
271
|
+
baseRevision.keepFromOrdinal !== input.expectedBaseKeepFromOrdinal ||
|
|
272
|
+
snapshot.canonical.messages.length !==
|
|
273
|
+
input.expectedCanonicalThroughOrdinal ||
|
|
274
|
+
snapshot.surface.surfaceSha256 !== input.expectedSurfaceSha256 ||
|
|
275
|
+
baseRevision.activeOverrideManifestSha256 !==
|
|
276
|
+
input.expectedBaseActiveOverrideManifestSha256
|
|
277
|
+
) {
|
|
278
|
+
throw new Error("Prefix retirement commit base is stale.");
|
|
279
|
+
}
|
|
280
|
+
this.store.assertContextRevisionBoundary(input.activeTurnId);
|
|
281
|
+
const retirementBoundaries = readRetirementBoundaries(
|
|
282
|
+
this.database,
|
|
283
|
+
snapshot.canonical,
|
|
284
|
+
input.activeTurnId,
|
|
285
|
+
);
|
|
286
|
+
const closedTurns = retirementBoundaries.closedTurns;
|
|
287
|
+
const activeTurns = closedTurns.filter(
|
|
288
|
+
(turn) => turn.firstOrdinal >= baseRevision.keepFromOrdinal,
|
|
289
|
+
);
|
|
290
|
+
const nextBoundary = [
|
|
291
|
+
...activeTurns,
|
|
292
|
+
...(retirementBoundaries.activeTurn === undefined
|
|
293
|
+
? []
|
|
294
|
+
: [retirementBoundaries.activeTurn]),
|
|
295
|
+
].find((turn) => turn.firstOrdinal === input.nextKeepFromOrdinal);
|
|
296
|
+
const retiredTurns = activeTurns.filter(
|
|
297
|
+
(turn) => turn.lastOrdinal < input.nextKeepFromOrdinal,
|
|
298
|
+
);
|
|
299
|
+
if (
|
|
300
|
+
nextBoundary === undefined ||
|
|
301
|
+
input.nextKeepFromOrdinal <= baseRevision.keepFromOrdinal ||
|
|
302
|
+
retiredTurns.length !== input.retiredTurnCount ||
|
|
303
|
+
retiredTurns.reduce((total, turn) => total + turn.frameCount, 0) !==
|
|
304
|
+
input.retiredFrameCount ||
|
|
305
|
+
retiredTurns.reduce((total, turn) => total + turn.messageCount, 0) !==
|
|
306
|
+
input.retiredMessageCount
|
|
307
|
+
) {
|
|
308
|
+
throw new Error("Prefix retirement turn boundary is invalid.");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const active = this.revisionCompiler.compileActive(snapshot);
|
|
312
|
+
const nextActiveOverrides = snapshot.activeOverrides.filter(
|
|
313
|
+
(override) => override.ordinal >= input.nextKeepFromOrdinal,
|
|
314
|
+
);
|
|
315
|
+
const candidate = this.revisionCompiler.compileProspective({
|
|
316
|
+
active,
|
|
317
|
+
canonical: snapshot.canonical,
|
|
318
|
+
activeOverrides: snapshot.activeOverrides,
|
|
319
|
+
addedOverrides: [],
|
|
320
|
+
activeSurface: snapshot.surface,
|
|
321
|
+
keepFromOrdinal: input.nextKeepFromOrdinal,
|
|
322
|
+
});
|
|
323
|
+
if (
|
|
324
|
+
input.retiredThroughOrdinal !== input.nextKeepFromOrdinal - 1 ||
|
|
325
|
+
nextActiveOverrides.length !== input.nextActiveOverrideCount ||
|
|
326
|
+
activeOverrideManifestHash(nextActiveOverrides) !==
|
|
327
|
+
input.nextActiveOverrideManifestSha256 ||
|
|
328
|
+
canonicalSequenceHash(
|
|
329
|
+
snapshot.canonical,
|
|
330
|
+
input.expectedCanonicalThroughOrdinal,
|
|
331
|
+
) !== input.canonicalSequenceSha256 ||
|
|
332
|
+
renderedMessageHash(
|
|
333
|
+
candidate.entries,
|
|
334
|
+
input.expectedCanonicalThroughOrdinal,
|
|
335
|
+
) !== input.renderedMessageSha256
|
|
336
|
+
) {
|
|
337
|
+
throw new Error("Prefix retirement candidate is invalid.");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const revisionNumber = baseRevision.revisionNumber + 1;
|
|
341
|
+
options.faultInjector?.("before_revision_insert");
|
|
342
|
+
this.database
|
|
343
|
+
.query(
|
|
344
|
+
`INSERT INTO context_revisions (
|
|
345
|
+
revision_id, session_id, revision_number, parent_revision_id, kind,
|
|
346
|
+
surface_id, surface_sha256, keep_from_ordinal,
|
|
347
|
+
source_through_ordinal, added_override_count, active_override_count,
|
|
348
|
+
active_override_manifest_sha256, canonical_sequence_sha256,
|
|
349
|
+
rendered_message_sha256, policy_version, renderer_format,
|
|
350
|
+
plan_sha256, change_manifest_sha256, retired_through_ordinal,
|
|
351
|
+
retired_turn_count, retired_frame_count, retired_message_count,
|
|
352
|
+
created_at
|
|
353
|
+
) VALUES (?, ?, ?, ?, 'prefix_retirement', ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, NULL, ?, NULL, ?, ?, ?, ?, ?)`,
|
|
354
|
+
)
|
|
355
|
+
.run(
|
|
356
|
+
input.revisionId,
|
|
357
|
+
this.sessionId,
|
|
358
|
+
revisionNumber,
|
|
359
|
+
baseRevision.revisionId,
|
|
360
|
+
baseRevision.surfaceId,
|
|
361
|
+
baseRevision.surfaceSha256,
|
|
362
|
+
input.nextKeepFromOrdinal,
|
|
363
|
+
input.expectedCanonicalThroughOrdinal,
|
|
364
|
+
input.nextActiveOverrideCount,
|
|
365
|
+
input.nextActiveOverrideManifestSha256,
|
|
366
|
+
input.canonicalSequenceSha256,
|
|
367
|
+
input.renderedMessageSha256,
|
|
368
|
+
input.policyVersion,
|
|
369
|
+
input.planHash,
|
|
370
|
+
input.retiredThroughOrdinal,
|
|
371
|
+
input.retiredTurnCount,
|
|
372
|
+
input.retiredFrameCount,
|
|
373
|
+
input.retiredMessageCount,
|
|
374
|
+
now,
|
|
375
|
+
);
|
|
376
|
+
options.faultInjector?.("after_revision_insert");
|
|
377
|
+
|
|
378
|
+
const storedActiveOverrides = this.database
|
|
379
|
+
.query(
|
|
380
|
+
`SELECT co.* FROM context_overrides co
|
|
381
|
+
JOIN context_revisions introduced
|
|
382
|
+
ON introduced.revision_id = co.introduced_revision_id
|
|
383
|
+
WHERE introduced.revision_number <= ? AND co.ordinal >= ?
|
|
384
|
+
ORDER BY co.ordinal`,
|
|
385
|
+
)
|
|
386
|
+
.all(revisionNumber, input.nextKeepFromOrdinal)
|
|
387
|
+
.map(decodeStoredSwapOverride);
|
|
388
|
+
if (
|
|
389
|
+
storedActiveOverrides.length !== input.nextActiveOverrideCount ||
|
|
390
|
+
activeOverrideManifestHash(storedActiveOverrides) !==
|
|
391
|
+
input.nextActiveOverrideManifestSha256
|
|
392
|
+
) {
|
|
393
|
+
throw new Error("Prefix retirement override readback is invalid.");
|
|
394
|
+
}
|
|
395
|
+
options.faultInjector?.("after_override_readback");
|
|
396
|
+
|
|
397
|
+
this.database.query("DELETE FROM context_measurement_state").run();
|
|
398
|
+
options.faultInjector?.("after_measurement_delete");
|
|
399
|
+
const switched = this.database
|
|
400
|
+
.query(
|
|
401
|
+
`UPDATE session_meta SET active_revision_id = ?, updated_at = ?
|
|
402
|
+
WHERE singleton = 1 AND active_revision_id = ?`,
|
|
403
|
+
)
|
|
404
|
+
.run(input.revisionId, now, baseRevision.revisionId);
|
|
405
|
+
requireSingleChange(
|
|
406
|
+
this.database,
|
|
407
|
+
switched.changes,
|
|
408
|
+
"activate prefix retirement revision",
|
|
409
|
+
);
|
|
410
|
+
options.faultInjector?.("after_active_update");
|
|
411
|
+
|
|
412
|
+
const readback = this.store.loadContextSnapshot();
|
|
413
|
+
if (
|
|
414
|
+
readback.revision.kind !== "prefix_retirement" ||
|
|
415
|
+
readback.revision.revisionId !== input.revisionId ||
|
|
416
|
+
readback.revision.keepFromOrdinal !== input.nextKeepFromOrdinal ||
|
|
417
|
+
loadMeasuredContextState(this.database, this.sessionId) !== undefined
|
|
418
|
+
) {
|
|
419
|
+
throw new Error("Committed prefix retirement readback failed.");
|
|
420
|
+
}
|
|
421
|
+
options.faultInjector?.("after_snapshot_readback");
|
|
422
|
+
return readback.revision;
|
|
423
|
+
});
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (
|
|
426
|
+
requireActiveRevisionId(this.store.readMeta()) !== input.expectedBaseRevisionId
|
|
427
|
+
) {
|
|
428
|
+
throw new Error("Failed prefix retirement changed active state.", {
|
|
429
|
+
cause: error,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
throw sessionWriteError("commit_prefix_retirement", this.sessionId, error);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
commitSurfaceRefresh(
|
|
437
|
+
input: CommitSurfaceRefreshInput,
|
|
438
|
+
options: CommitSurfaceRefreshOptions = {},
|
|
439
|
+
): Extract<StoredContextRevisionV8, { kind: "surface_refresh" }> {
|
|
440
|
+
this.requireOpen();
|
|
441
|
+
assertCommitSurfaceRefreshInput(input);
|
|
442
|
+
const now = this.clock();
|
|
443
|
+
try {
|
|
444
|
+
return runTransaction(this.database, () => {
|
|
445
|
+
const snapshot = this.store.loadContextSnapshot();
|
|
446
|
+
const baseRevision = snapshot.revision;
|
|
447
|
+
if (
|
|
448
|
+
baseRevision.revisionId !== input.expectedBaseRevisionId ||
|
|
449
|
+
baseRevision.revisionNumber !== input.expectedBaseRevisionNumber ||
|
|
450
|
+
snapshot.canonical.messages.length !==
|
|
451
|
+
input.expectedCanonicalThroughOrdinal ||
|
|
452
|
+
baseRevision.activeOverrideManifestSha256 !==
|
|
453
|
+
input.expectedBaseActiveOverrideManifestSha256
|
|
454
|
+
) {
|
|
455
|
+
throw new Error("Context surface refresh base is stale.");
|
|
456
|
+
}
|
|
457
|
+
this.store.assertContextRevisionIdle();
|
|
458
|
+
validateStoredContextSurface(input.surface);
|
|
459
|
+
if (
|
|
460
|
+
input.surface.sessionId !== this.sessionId ||
|
|
461
|
+
input.surface.surfaceId === snapshot.surface.surfaceId ||
|
|
462
|
+
input.surface.surfaceSha256 === snapshot.surface.surfaceSha256
|
|
463
|
+
) {
|
|
464
|
+
throw new Error("Context surface refresh does not introduce a new surface.");
|
|
465
|
+
}
|
|
466
|
+
const actualChanges = contextSurfaceChanges(snapshot.surface, input.surface);
|
|
467
|
+
if (
|
|
468
|
+
stableJsonStringify(actualChanges) !== stableJsonStringify(input.changes) ||
|
|
469
|
+
contextSurfaceChangeManifestHash(actualChanges) !==
|
|
470
|
+
input.changeManifestSha256 ||
|
|
471
|
+
!Object.values(actualChanges).some(Boolean)
|
|
472
|
+
) {
|
|
473
|
+
throw new Error("Context surface refresh change manifest is invalid.");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const active = this.revisionCompiler.compileActive(snapshot);
|
|
477
|
+
const candidate = this.revisionCompiler.compileProspective({
|
|
478
|
+
active,
|
|
479
|
+
canonical: snapshot.canonical,
|
|
480
|
+
activeOverrides: snapshot.activeOverrides,
|
|
481
|
+
addedOverrides: [],
|
|
482
|
+
activeSurface: snapshot.surface,
|
|
483
|
+
surface: input.surface,
|
|
484
|
+
});
|
|
485
|
+
if (
|
|
486
|
+
canonicalSequenceHash(
|
|
487
|
+
snapshot.canonical,
|
|
488
|
+
input.expectedCanonicalThroughOrdinal,
|
|
489
|
+
) !== input.canonicalSequenceSha256 ||
|
|
490
|
+
renderedMessageHash(
|
|
491
|
+
candidate.entries,
|
|
492
|
+
input.expectedCanonicalThroughOrdinal,
|
|
493
|
+
) !== input.renderedMessageSha256
|
|
494
|
+
) {
|
|
495
|
+
throw new Error("Candidate context surface prefix hash is invalid.");
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const revisionNumber = baseRevision.revisionNumber + 1;
|
|
499
|
+
options.faultInjector?.("before_surface_insert");
|
|
500
|
+
insertContextSurface(this.database, input.surface);
|
|
501
|
+
options.faultInjector?.("after_surface_insert");
|
|
502
|
+
this.database
|
|
503
|
+
.query(
|
|
504
|
+
`INSERT INTO context_revisions (
|
|
505
|
+
revision_id, session_id, revision_number, parent_revision_id, kind,
|
|
506
|
+
surface_id, surface_sha256, keep_from_ordinal,
|
|
507
|
+
source_through_ordinal, added_override_count, active_override_count,
|
|
508
|
+
active_override_manifest_sha256, canonical_sequence_sha256,
|
|
509
|
+
rendered_message_sha256, policy_version, renderer_format,
|
|
510
|
+
plan_sha256, change_manifest_sha256, created_at
|
|
511
|
+
) VALUES (?, ?, ?, ?, 'surface_refresh', ?, ?, ?, ?, 0, ?, ?, ?, ?, NULL, NULL, NULL, ?, ?)`,
|
|
512
|
+
)
|
|
513
|
+
.run(
|
|
514
|
+
input.revisionId,
|
|
515
|
+
this.sessionId,
|
|
516
|
+
revisionNumber,
|
|
517
|
+
baseRevision.revisionId,
|
|
518
|
+
input.surface.surfaceId,
|
|
519
|
+
input.surface.surfaceSha256,
|
|
520
|
+
baseRevision.keepFromOrdinal,
|
|
521
|
+
input.expectedCanonicalThroughOrdinal,
|
|
522
|
+
baseRevision.activeOverrideCount,
|
|
523
|
+
baseRevision.activeOverrideManifestSha256,
|
|
524
|
+
input.canonicalSequenceSha256,
|
|
525
|
+
input.renderedMessageSha256,
|
|
526
|
+
input.changeManifestSha256,
|
|
527
|
+
now,
|
|
528
|
+
);
|
|
529
|
+
options.faultInjector?.("after_revision_insert");
|
|
530
|
+
|
|
531
|
+
this.database.query("DELETE FROM context_measurement_state").run();
|
|
532
|
+
options.faultInjector?.("after_measurement_delete");
|
|
533
|
+
const switched = this.database
|
|
534
|
+
.query(
|
|
535
|
+
`UPDATE session_meta SET active_revision_id = ?, updated_at = ?
|
|
536
|
+
WHERE singleton = 1 AND active_revision_id = ?`,
|
|
537
|
+
)
|
|
538
|
+
.run(input.revisionId, now, baseRevision.revisionId);
|
|
539
|
+
requireSingleChange(
|
|
540
|
+
this.database,
|
|
541
|
+
switched.changes,
|
|
542
|
+
"activate context surface revision",
|
|
543
|
+
);
|
|
544
|
+
options.faultInjector?.("after_active_update");
|
|
545
|
+
|
|
546
|
+
const readback = this.store.loadContextSnapshot();
|
|
547
|
+
if (
|
|
548
|
+
readback.revision.kind !== "surface_refresh" ||
|
|
549
|
+
readback.revision.revisionId !== input.revisionId ||
|
|
550
|
+
readback.surface.surfaceId !== input.surface.surfaceId ||
|
|
551
|
+
loadMeasuredContextState(this.database, this.sessionId) !== undefined
|
|
552
|
+
) {
|
|
553
|
+
throw new Error("Committed context surface revision readback failed.");
|
|
554
|
+
}
|
|
555
|
+
return readback.revision;
|
|
556
|
+
});
|
|
557
|
+
} catch (error) {
|
|
558
|
+
if (
|
|
559
|
+
requireActiveRevisionId(this.store.readMeta()) !== input.expectedBaseRevisionId
|
|
560
|
+
) {
|
|
561
|
+
throw new Error("Failed context surface transaction changed active state.", {
|
|
562
|
+
cause: error,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
throw sessionWriteError("commit_context_surface", this.sessionId, error);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
commitSkillsUpdate(
|
|
570
|
+
input: CommitSkillsUpdateInput,
|
|
571
|
+
options: CommitSkillsUpdateOptions = {},
|
|
572
|
+
): Extract<StoredContextRevisionV8, { kind: "skills_update" }> {
|
|
573
|
+
this.requireOpen();
|
|
574
|
+
assertCommitSkillsUpdateInput(input);
|
|
575
|
+
const now = this.clock();
|
|
576
|
+
try {
|
|
577
|
+
return runTransaction(this.database, () => {
|
|
578
|
+
const snapshot = this.store.loadContextSnapshot();
|
|
579
|
+
const baseRevision = snapshot.revision;
|
|
580
|
+
if (
|
|
581
|
+
baseRevision.revisionId !== input.expectedBaseRevisionId ||
|
|
582
|
+
baseRevision.revisionNumber !== input.expectedBaseRevisionNumber ||
|
|
583
|
+
snapshot.canonical.messages.length !==
|
|
584
|
+
input.expectedCanonicalThroughOrdinal ||
|
|
585
|
+
baseRevision.activeOverrideManifestSha256 !==
|
|
586
|
+
input.expectedBaseActiveOverrideManifestSha256
|
|
587
|
+
) {
|
|
588
|
+
throw new Error("Agent Skills update base is stale.");
|
|
589
|
+
}
|
|
590
|
+
this.store.assertContextRevisionIdle();
|
|
591
|
+
validateStoredContextSurface(input.surface);
|
|
592
|
+
|
|
593
|
+
const surfaceChanged =
|
|
594
|
+
input.surface.surfaceSha256 !== snapshot.surface.surfaceSha256;
|
|
595
|
+
const actualChanges = contextSurfaceChanges(snapshot.surface, input.surface);
|
|
596
|
+
if (
|
|
597
|
+
input.surface.sessionId !== this.sessionId ||
|
|
598
|
+
stableJsonStringify(actualChanges) !== stableJsonStringify(input.changes) ||
|
|
599
|
+
contextSurfaceChangeManifestHash(actualChanges) !==
|
|
600
|
+
input.changeManifestSha256 ||
|
|
601
|
+
(surfaceChanged && input.surface.surfaceId === snapshot.surface.surfaceId) ||
|
|
602
|
+
(!surfaceChanged && input.surface.surfaceId !== snapshot.surface.surfaceId) ||
|
|
603
|
+
(!surfaceChanged && Object.values(actualChanges).some(Boolean))
|
|
604
|
+
) {
|
|
605
|
+
throw new Error("Agent Skills surface change manifest is invalid.");
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const unresolved = new Map(
|
|
609
|
+
this.store
|
|
610
|
+
.loadSkillActivations(["pending", "dispatched"])
|
|
611
|
+
.map((entry) => [entry.activationMessageId, entry]),
|
|
612
|
+
);
|
|
613
|
+
if (
|
|
614
|
+
input.settlements.length !== input.addedOverrides.length ||
|
|
615
|
+
input.settlements.length !== unresolved.size ||
|
|
616
|
+
new Set(input.settlements.map((entry) => entry.activationMessageId)).size !==
|
|
617
|
+
input.settlements.length ||
|
|
618
|
+
skillActivationManifestSha256(input.settlements) !==
|
|
619
|
+
input.activationManifestSha256
|
|
620
|
+
) {
|
|
621
|
+
throw new Error("Agent Skills settlement manifest is invalid.");
|
|
622
|
+
}
|
|
623
|
+
const canonicalMessages = new Map(
|
|
624
|
+
snapshot.canonical.messages.map((message) => [message.messageId, message]),
|
|
625
|
+
);
|
|
626
|
+
const providedOverrides = new Map(
|
|
627
|
+
input.addedOverrides.map((override) => [override.messageId, override]),
|
|
628
|
+
);
|
|
629
|
+
for (const settlement of input.settlements) {
|
|
630
|
+
const activation = unresolved.get(settlement.activationMessageId);
|
|
631
|
+
const message = canonicalMessages.get(settlement.activationMessageId);
|
|
632
|
+
const override = providedOverrides.get(settlement.activationMessageId);
|
|
633
|
+
if (
|
|
634
|
+
activation === undefined ||
|
|
635
|
+
message?.role !== "tool" ||
|
|
636
|
+
message.name !== "Skill" ||
|
|
637
|
+
override === undefined ||
|
|
638
|
+
settlement.name !== activation.name ||
|
|
639
|
+
(settlement.state === "promoted" &&
|
|
640
|
+
(activation.state !== "dispatched" ||
|
|
641
|
+
settlement.rejectionReason !== undefined)) ||
|
|
642
|
+
(settlement.state === "rejected" &&
|
|
643
|
+
(settlement.rejectionReason === undefined ||
|
|
644
|
+
settlement.rejectionReason.trim() === "" ||
|
|
645
|
+
settlement.rejectionReason.length > 256))
|
|
646
|
+
) {
|
|
647
|
+
throw new Error(
|
|
648
|
+
`Agent Skill activation ${settlement.activationMessageId} cannot be settled.`,
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
const activeManifest = input.surface.activeSkills.find(
|
|
652
|
+
(entry) =>
|
|
653
|
+
entry.name === activation.name &&
|
|
654
|
+
entry.activationMessageId === activation.activationMessageId,
|
|
655
|
+
);
|
|
656
|
+
if (
|
|
657
|
+
(settlement.state === "promoted" && activeManifest === undefined) ||
|
|
658
|
+
(settlement.state === "rejected" && activeManifest !== undefined)
|
|
659
|
+
) {
|
|
660
|
+
throw new Error("Agent Skills active manifest does not match settlements.");
|
|
661
|
+
}
|
|
662
|
+
const expected = renderSkillActivationReceipt({
|
|
663
|
+
message: {
|
|
664
|
+
messageId: message.messageId,
|
|
665
|
+
frameId: message.frameId,
|
|
666
|
+
ordinal: message.ordinal,
|
|
667
|
+
content: message.displayText,
|
|
668
|
+
contentSha256: message.contentSha256,
|
|
669
|
+
},
|
|
670
|
+
name: activation.name,
|
|
671
|
+
outcome:
|
|
672
|
+
settlement.state === "promoted"
|
|
673
|
+
? "promoted"
|
|
674
|
+
: settlement.rejectionReason === "unavailable"
|
|
675
|
+
? "unavailable"
|
|
676
|
+
: "rejected",
|
|
677
|
+
});
|
|
678
|
+
if (stableJsonStringify(expected) !== stableJsonStringify(override)) {
|
|
679
|
+
throw new Error("Agent Skill activation receipt is not deterministic.");
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const active = this.revisionCompiler.compileActive(snapshot);
|
|
684
|
+
const candidateOverrides = [
|
|
685
|
+
...snapshot.activeOverrides,
|
|
686
|
+
...input.addedOverrides,
|
|
687
|
+
];
|
|
688
|
+
if (
|
|
689
|
+
new Set(candidateOverrides.map((override) => override.messageId)).size !==
|
|
690
|
+
candidateOverrides.length ||
|
|
691
|
+
activeOverrideManifestHash(candidateOverrides) !==
|
|
692
|
+
input.nextActiveOverrideManifestSha256
|
|
693
|
+
) {
|
|
694
|
+
throw new Error("Agent Skills override manifest is invalid.");
|
|
695
|
+
}
|
|
696
|
+
const candidate = this.revisionCompiler.compileProspective({
|
|
697
|
+
active,
|
|
698
|
+
canonical: snapshot.canonical,
|
|
699
|
+
activeOverrides: snapshot.activeOverrides,
|
|
700
|
+
addedOverrides: input.addedOverrides,
|
|
701
|
+
activeSurface: snapshot.surface,
|
|
702
|
+
...(surfaceChanged ? { surface: input.surface } : {}),
|
|
703
|
+
allowCombinedSurfaceAndOverrides: true,
|
|
704
|
+
});
|
|
705
|
+
if (
|
|
706
|
+
canonicalSequenceHash(
|
|
707
|
+
snapshot.canonical,
|
|
708
|
+
input.expectedCanonicalThroughOrdinal,
|
|
709
|
+
) !== input.canonicalSequenceSha256 ||
|
|
710
|
+
renderedMessageHash(
|
|
711
|
+
candidate.entries,
|
|
712
|
+
input.expectedCanonicalThroughOrdinal,
|
|
713
|
+
) !== input.renderedMessageSha256
|
|
714
|
+
) {
|
|
715
|
+
throw new Error("Agent Skills compiled context hashes are invalid.");
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
options.faultInjector?.("before_surface_insert");
|
|
719
|
+
if (surfaceChanged) {
|
|
720
|
+
insertContextSurface(this.database, input.surface);
|
|
721
|
+
}
|
|
722
|
+
options.faultInjector?.("after_surface_insert");
|
|
723
|
+
const revisionNumber = baseRevision.revisionNumber + 1;
|
|
724
|
+
const activeOverrideCount =
|
|
725
|
+
baseRevision.activeOverrideCount + input.addedOverrides.length;
|
|
726
|
+
this.database
|
|
727
|
+
.query(
|
|
728
|
+
`INSERT INTO context_revisions (
|
|
729
|
+
revision_id, session_id, revision_number, parent_revision_id, kind,
|
|
730
|
+
surface_id, surface_sha256, keep_from_ordinal,
|
|
731
|
+
source_through_ordinal, added_override_count, active_override_count,
|
|
732
|
+
active_override_manifest_sha256, canonical_sequence_sha256,
|
|
733
|
+
rendered_message_sha256, policy_version, renderer_format,
|
|
734
|
+
plan_sha256, change_manifest_sha256, activation_manifest_sha256,
|
|
735
|
+
retired_through_ordinal, retired_turn_count, retired_frame_count,
|
|
736
|
+
retired_message_count, created_at
|
|
737
|
+
) VALUES (?, ?, ?, ?, 'skills_update', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, NULL, NULL, NULL, NULL, ?)`,
|
|
738
|
+
)
|
|
739
|
+
.run(
|
|
740
|
+
input.revisionId,
|
|
741
|
+
this.sessionId,
|
|
742
|
+
revisionNumber,
|
|
743
|
+
baseRevision.revisionId,
|
|
744
|
+
input.surface.surfaceId,
|
|
745
|
+
input.surface.surfaceSha256,
|
|
746
|
+
baseRevision.keepFromOrdinal,
|
|
747
|
+
input.expectedCanonicalThroughOrdinal,
|
|
748
|
+
input.addedOverrides.length,
|
|
749
|
+
activeOverrideCount,
|
|
750
|
+
input.nextActiveOverrideManifestSha256,
|
|
751
|
+
input.canonicalSequenceSha256,
|
|
752
|
+
input.renderedMessageSha256,
|
|
753
|
+
SKILL_POLICY_VERSION,
|
|
754
|
+
SKILL_ACTIVATION_RECEIPT_FORMAT,
|
|
755
|
+
input.changeManifestSha256,
|
|
756
|
+
input.activationManifestSha256,
|
|
757
|
+
now,
|
|
758
|
+
);
|
|
759
|
+
options.faultInjector?.("after_revision_insert");
|
|
760
|
+
|
|
761
|
+
for (let index = 0; index < input.addedOverrides.length; index += 1) {
|
|
762
|
+
const override = requireItem(
|
|
763
|
+
input.addedOverrides,
|
|
764
|
+
index,
|
|
765
|
+
"Agent Skill receipt override",
|
|
766
|
+
);
|
|
767
|
+
this.database
|
|
768
|
+
.query(
|
|
769
|
+
`INSERT INTO context_overrides (
|
|
770
|
+
introduced_revision_id, session_id, message_id, frame_id, ordinal,
|
|
771
|
+
representation, renderer_format, source, original_content_sha256,
|
|
772
|
+
rendered_content, rendered_content_sha256, original_bytes,
|
|
773
|
+
rendered_bytes, byte_savings, created_at
|
|
774
|
+
) VALUES (?, ?, ?, ?, ?, 'swapped', ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
775
|
+
)
|
|
776
|
+
.run(
|
|
777
|
+
input.revisionId,
|
|
778
|
+
this.sessionId,
|
|
779
|
+
override.messageId,
|
|
780
|
+
override.frameId,
|
|
781
|
+
override.ordinal,
|
|
782
|
+
SKILL_ACTIVATION_RECEIPT_FORMAT,
|
|
783
|
+
override.source,
|
|
784
|
+
override.originalContentSha256,
|
|
785
|
+
override.renderedContent,
|
|
786
|
+
override.renderedContentSha256,
|
|
787
|
+
override.originalBytes,
|
|
788
|
+
override.renderedBytes,
|
|
789
|
+
override.byteSavings,
|
|
790
|
+
now,
|
|
791
|
+
);
|
|
792
|
+
if (index === 0) {
|
|
793
|
+
options.faultInjector?.("after_first_override_insert");
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
options.faultInjector?.("after_overrides_insert");
|
|
797
|
+
|
|
798
|
+
for (const settlement of input.settlements) {
|
|
799
|
+
const activation = unresolved.get(settlement.activationMessageId)!;
|
|
800
|
+
const updated =
|
|
801
|
+
settlement.state === "promoted"
|
|
802
|
+
? this.database
|
|
803
|
+
.query(
|
|
804
|
+
`UPDATE skill_activations
|
|
805
|
+
SET state = 'promoted', settled_revision_id = ?, updated_at = ?
|
|
806
|
+
WHERE activation_message_id = ? AND state = 'dispatched'`,
|
|
807
|
+
)
|
|
808
|
+
.run(input.revisionId, now, settlement.activationMessageId)
|
|
809
|
+
: this.database
|
|
810
|
+
.query(
|
|
811
|
+
`UPDATE skill_activations
|
|
812
|
+
SET state = 'rejected', settled_revision_id = ?, rejection_reason = ?, updated_at = ?
|
|
813
|
+
WHERE activation_message_id = ? AND state = ?`,
|
|
814
|
+
)
|
|
815
|
+
.run(
|
|
816
|
+
input.revisionId,
|
|
817
|
+
settlement.rejectionReason!,
|
|
818
|
+
now,
|
|
819
|
+
settlement.activationMessageId,
|
|
820
|
+
activation.state,
|
|
821
|
+
);
|
|
822
|
+
requireSingleChange(
|
|
823
|
+
this.database,
|
|
824
|
+
updated.changes,
|
|
825
|
+
`settle Agent Skill activation ${settlement.activationMessageId}`,
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
options.faultInjector?.("after_activations_update");
|
|
829
|
+
|
|
830
|
+
this.database.query("DELETE FROM context_measurement_state").run();
|
|
831
|
+
options.faultInjector?.("after_measurement_delete");
|
|
832
|
+
const switched = this.database
|
|
833
|
+
.query(
|
|
834
|
+
`UPDATE session_meta SET active_revision_id = ?, updated_at = ?
|
|
835
|
+
WHERE singleton = 1 AND active_revision_id = ?`,
|
|
836
|
+
)
|
|
837
|
+
.run(input.revisionId, now, baseRevision.revisionId);
|
|
838
|
+
requireSingleChange(
|
|
839
|
+
this.database,
|
|
840
|
+
switched.changes,
|
|
841
|
+
"activate Agent Skills context revision",
|
|
842
|
+
);
|
|
843
|
+
options.faultInjector?.("after_active_update");
|
|
844
|
+
|
|
845
|
+
const readback = this.store.loadContextSnapshot();
|
|
846
|
+
if (
|
|
847
|
+
readback.revision.kind !== "skills_update" ||
|
|
848
|
+
readback.revision.revisionId !== input.revisionId ||
|
|
849
|
+
readback.surface.surfaceId !== input.surface.surfaceId ||
|
|
850
|
+
loadMeasuredContextState(this.database, this.sessionId) !== undefined ||
|
|
851
|
+
this.store
|
|
852
|
+
.loadSkillActivations(["pending", "dispatched"])
|
|
853
|
+
.some((entry) =>
|
|
854
|
+
input.settlements.some(
|
|
855
|
+
(settlement) =>
|
|
856
|
+
settlement.activationMessageId === entry.activationMessageId,
|
|
857
|
+
),
|
|
858
|
+
)
|
|
859
|
+
) {
|
|
860
|
+
throw new Error("Committed Agent Skills update readback failed.");
|
|
861
|
+
}
|
|
862
|
+
return readback.revision;
|
|
863
|
+
});
|
|
864
|
+
} catch (error) {
|
|
865
|
+
if (
|
|
866
|
+
requireActiveRevisionId(this.store.readMeta()) !== input.expectedBaseRevisionId
|
|
867
|
+
) {
|
|
868
|
+
throw new Error("Failed Agent Skills transaction changed active state.", {
|
|
869
|
+
cause: error,
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
throw sessionWriteError("commit_skills_update", this.sessionId, error);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function assertCommitSwapRevisionInput(input: CommitSwapRevisionInput): void {
|
|
877
|
+
if (
|
|
878
|
+
input.revisionId.trim() === "" ||
|
|
879
|
+
input.expectedBaseRevisionId.trim() === "" ||
|
|
880
|
+
!Number.isSafeInteger(input.expectedBaseRevisionNumber) ||
|
|
881
|
+
input.expectedBaseRevisionNumber < 1 ||
|
|
882
|
+
!Number.isSafeInteger(input.expectedCanonicalThroughOrdinal) ||
|
|
883
|
+
input.expectedCanonicalThroughOrdinal < 1 ||
|
|
884
|
+
input.addedOverrides.length < 1 ||
|
|
885
|
+
input.policyVersion !== "swap-only-v1" ||
|
|
886
|
+
input.rendererFormat !== SWAP_OBSERVATION_FORMAT
|
|
887
|
+
) {
|
|
888
|
+
throw new Error("Commit swap revision input is invalid.");
|
|
889
|
+
}
|
|
890
|
+
for (const [name, hash] of [
|
|
891
|
+
[
|
|
892
|
+
"expectedBaseActiveOverrideManifestSha256",
|
|
893
|
+
input.expectedBaseActiveOverrideManifestSha256,
|
|
894
|
+
],
|
|
895
|
+
["planHash", input.planHash],
|
|
896
|
+
["nextActiveOverrideManifestSha256", input.nextActiveOverrideManifestSha256],
|
|
897
|
+
["canonicalSequenceSha256", input.canonicalSequenceSha256],
|
|
898
|
+
["renderedMessageSha256", input.renderedMessageSha256],
|
|
899
|
+
] as const) {
|
|
900
|
+
if (!/^[0-9a-f]{64}$/.test(hash)) {
|
|
901
|
+
throw new Error(`Commit swap revision ${name} must be a SHA-256 hash.`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
function assertCommitSurfaceRefreshInput(input: CommitSurfaceRefreshInput): void {
|
|
907
|
+
if (
|
|
908
|
+
input.revisionId.trim() === "" ||
|
|
909
|
+
input.expectedBaseRevisionId.trim() === "" ||
|
|
910
|
+
!Number.isSafeInteger(input.expectedBaseRevisionNumber) ||
|
|
911
|
+
input.expectedBaseRevisionNumber < 1 ||
|
|
912
|
+
!Number.isSafeInteger(input.expectedCanonicalThroughOrdinal) ||
|
|
913
|
+
input.expectedCanonicalThroughOrdinal < 1
|
|
914
|
+
) {
|
|
915
|
+
throw new Error("Commit surface refresh input is invalid.");
|
|
916
|
+
}
|
|
917
|
+
for (const [name, hash] of [
|
|
918
|
+
[
|
|
919
|
+
"expectedBaseActiveOverrideManifestSha256",
|
|
920
|
+
input.expectedBaseActiveOverrideManifestSha256,
|
|
921
|
+
],
|
|
922
|
+
["changeManifestSha256", input.changeManifestSha256],
|
|
923
|
+
["canonicalSequenceSha256", input.canonicalSequenceSha256],
|
|
924
|
+
["renderedMessageSha256", input.renderedMessageSha256],
|
|
925
|
+
] as const) {
|
|
926
|
+
if (!/^[0-9a-f]{64}$/.test(hash)) {
|
|
927
|
+
throw new Error(`Commit surface refresh ${name} must be a SHA-256 hash.`);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function assertCommitSkillsUpdateInput(input: CommitSkillsUpdateInput): void {
|
|
933
|
+
if (
|
|
934
|
+
input.revisionId.trim() === "" ||
|
|
935
|
+
input.expectedBaseRevisionId.trim() === "" ||
|
|
936
|
+
!Number.isSafeInteger(input.expectedBaseRevisionNumber) ||
|
|
937
|
+
input.expectedBaseRevisionNumber < 1 ||
|
|
938
|
+
!Number.isSafeInteger(input.expectedCanonicalThroughOrdinal) ||
|
|
939
|
+
input.expectedCanonicalThroughOrdinal < 1 ||
|
|
940
|
+
input.addedOverrides.length < 1 ||
|
|
941
|
+
input.settlements.length !== input.addedOverrides.length
|
|
942
|
+
) {
|
|
943
|
+
throw new Error("Commit Agent Skills update input is invalid.");
|
|
944
|
+
}
|
|
945
|
+
for (const [name, hash] of [
|
|
946
|
+
[
|
|
947
|
+
"expectedBaseActiveOverrideManifestSha256",
|
|
948
|
+
input.expectedBaseActiveOverrideManifestSha256,
|
|
949
|
+
],
|
|
950
|
+
["changeManifestSha256", input.changeManifestSha256],
|
|
951
|
+
["activationManifestSha256", input.activationManifestSha256],
|
|
952
|
+
["nextActiveOverrideManifestSha256", input.nextActiveOverrideManifestSha256],
|
|
953
|
+
["canonicalSequenceSha256", input.canonicalSequenceSha256],
|
|
954
|
+
["renderedMessageSha256", input.renderedMessageSha256],
|
|
955
|
+
] as const) {
|
|
956
|
+
if (!/^[0-9a-f]{64}$/.test(hash)) {
|
|
957
|
+
throw new Error(`Commit Agent Skills update ${name} must be a SHA-256 hash.`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function assertCommitPrefixRetirementRevisionInput(
|
|
963
|
+
input: CommitPrefixRetirementRevisionInput,
|
|
964
|
+
): void {
|
|
965
|
+
if (
|
|
966
|
+
input.revisionId.trim() === "" ||
|
|
967
|
+
input.expectedBaseRevisionId.trim() === "" ||
|
|
968
|
+
input.policyVersion !== "recall-first-retirement-v1" ||
|
|
969
|
+
!Number.isSafeInteger(input.expectedBaseRevisionNumber) ||
|
|
970
|
+
input.expectedBaseRevisionNumber < 1 ||
|
|
971
|
+
!Number.isSafeInteger(input.expectedBaseKeepFromOrdinal) ||
|
|
972
|
+
input.expectedBaseKeepFromOrdinal < 1 ||
|
|
973
|
+
!Number.isSafeInteger(input.expectedCanonicalThroughOrdinal) ||
|
|
974
|
+
input.expectedCanonicalThroughOrdinal < 1 ||
|
|
975
|
+
!Number.isSafeInteger(input.nextKeepFromOrdinal) ||
|
|
976
|
+
input.nextKeepFromOrdinal <= input.expectedBaseKeepFromOrdinal ||
|
|
977
|
+
input.retiredThroughOrdinal !== input.nextKeepFromOrdinal - 1 ||
|
|
978
|
+
!Number.isSafeInteger(input.retiredTurnCount) ||
|
|
979
|
+
input.retiredTurnCount < 1 ||
|
|
980
|
+
!Number.isSafeInteger(input.retiredFrameCount) ||
|
|
981
|
+
input.retiredFrameCount < 1 ||
|
|
982
|
+
!Number.isSafeInteger(input.retiredMessageCount) ||
|
|
983
|
+
input.retiredMessageCount < 1 ||
|
|
984
|
+
!Number.isSafeInteger(input.nextActiveOverrideCount) ||
|
|
985
|
+
input.nextActiveOverrideCount < 0
|
|
986
|
+
) {
|
|
987
|
+
throw new Error("Commit prefix retirement input is invalid.");
|
|
988
|
+
}
|
|
989
|
+
for (const [name, hash] of [
|
|
990
|
+
["expectedSurfaceSha256", input.expectedSurfaceSha256],
|
|
991
|
+
[
|
|
992
|
+
"expectedBaseActiveOverrideManifestSha256",
|
|
993
|
+
input.expectedBaseActiveOverrideManifestSha256,
|
|
994
|
+
],
|
|
995
|
+
["planHash", input.planHash],
|
|
996
|
+
["nextActiveOverrideManifestSha256", input.nextActiveOverrideManifestSha256],
|
|
997
|
+
["canonicalSequenceSha256", input.canonicalSequenceSha256],
|
|
998
|
+
["renderedMessageSha256", input.renderedMessageSha256],
|
|
999
|
+
] as const) {
|
|
1000
|
+
if (!/^[0-9a-f]{64}$/.test(hash)) {
|
|
1001
|
+
throw new Error(`Commit prefix retirement ${name} must be a SHA-256 hash.`);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|