@earendil-works/pi-durable 1.4.0 → 1.5.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 +186 -0
- package/README.md +181 -84
- package/dist/codemode/codemode.d.ts +4 -4
- package/dist/codemode/codemode.d.ts.map +1 -1
- package/dist/codemode/codemode.js +9 -12
- package/dist/codemode/codemode.js.map +1 -1
- package/dist/codemode/models.js +4 -4
- package/dist/codemode/models.js.map +1 -1
- package/dist/env/index.d.ts +12 -10
- package/dist/env/index.d.ts.map +1 -1
- package/dist/env/index.js.map +1 -1
- package/dist/env/node-watch.d.ts +3 -3
- package/dist/env/node-watch.d.ts.map +1 -1
- package/dist/env/node-watch.js +18 -16
- package/dist/env/node-watch.js.map +1 -1
- package/dist/env/node.d.ts +2 -2
- package/dist/env/node.d.ts.map +1 -1
- package/dist/env/node.js +1 -1
- package/dist/env/node.js.map +1 -1
- package/dist/errors.d.ts +18 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +26 -3
- package/dist/errors.js.map +1 -1
- package/dist/harness/agent.d.ts.map +1 -1
- package/dist/harness/agent.js +10 -2
- package/dist/harness/agent.js.map +1 -1
- package/dist/harness/compaction.d.ts.map +1 -1
- package/dist/harness/compaction.js +19 -2
- package/dist/harness/compaction.js.map +1 -1
- package/dist/harness/context.d.ts +23 -7
- package/dist/harness/context.d.ts.map +1 -1
- package/dist/harness/context.js +71 -25
- package/dist/harness/context.js.map +1 -1
- package/dist/harness/define.d.ts +7 -3
- package/dist/harness/define.d.ts.map +1 -1
- package/dist/harness/define.js +4 -0
- package/dist/harness/define.js.map +1 -1
- package/dist/harness/generation.d.ts +38 -5
- package/dist/harness/generation.d.ts.map +1 -1
- package/dist/harness/generation.js +445 -96
- package/dist/harness/generation.js.map +1 -1
- package/dist/harness/harness.d.ts +2 -0
- package/dist/harness/harness.d.ts.map +1 -1
- package/dist/harness/harness.js +87 -62
- package/dist/harness/harness.js.map +1 -1
- package/dist/harness/inbox.d.ts +5 -1
- package/dist/harness/inbox.d.ts.map +1 -1
- package/dist/harness/inbox.js +4 -1
- package/dist/harness/inbox.js.map +1 -1
- package/dist/harness/json.d.ts +20 -4
- package/dist/harness/json.d.ts.map +1 -1
- package/dist/harness/json.js +23 -5
- package/dist/harness/json.js.map +1 -1
- package/dist/harness/live.d.ts +122 -16
- package/dist/harness/live.d.ts.map +1 -1
- package/dist/harness/live.js +93 -12
- package/dist/harness/live.js.map +1 -1
- package/dist/harness/partial-changes.d.ts +54 -0
- package/dist/harness/partial-changes.d.ts.map +1 -0
- package/dist/harness/partial-changes.js +162 -0
- package/dist/harness/partial-changes.js.map +1 -0
- package/dist/harness/registry-reader.d.ts +11 -0
- package/dist/harness/registry-reader.d.ts.map +1 -0
- package/dist/harness/registry-reader.js +25 -0
- package/dist/harness/registry-reader.js.map +1 -0
- package/dist/harness/scheduler.d.ts +46 -19
- package/dist/harness/scheduler.d.ts.map +1 -1
- package/dist/harness/scheduler.js +973 -376
- package/dist/harness/scheduler.js.map +1 -1
- package/dist/harness/submissions.d.ts +17 -10
- package/dist/harness/submissions.d.ts.map +1 -1
- package/dist/harness/submissions.js +62 -25
- package/dist/harness/submissions.js.map +1 -1
- package/dist/harness/task-graph.d.ts +12 -9
- package/dist/harness/task-graph.d.ts.map +1 -1
- package/dist/harness/task-graph.js +38 -28
- package/dist/harness/task-graph.js.map +1 -1
- package/dist/harness/telemetry.d.ts +11 -11
- package/dist/harness/telemetry.d.ts.map +1 -1
- package/dist/harness/telemetry.js +7 -7
- package/dist/harness/telemetry.js.map +1 -1
- package/dist/harness/tool.d.ts +2 -0
- package/dist/harness/tool.d.ts.map +1 -1
- package/dist/harness/tool.js +171 -55
- package/dist/harness/tool.js.map +1 -1
- package/dist/harness/transcript.d.ts +72 -0
- package/dist/harness/transcript.d.ts.map +1 -0
- package/dist/harness/transcript.js +264 -0
- package/dist/harness/transcript.js.map +1 -0
- package/dist/harness/types.d.ts +218 -59
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/util.d.ts +3 -1
- package/dist/harness/util.d.ts.map +1 -1
- package/dist/harness/util.js +9 -0
- package/dist/harness/util.js.map +1 -1
- package/dist/harness/view.d.ts +43 -22
- package/dist/harness/view.d.ts.map +1 -1
- package/dist/harness/view.js +130 -71
- package/dist/harness/view.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/search.d.ts +19 -3
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +107 -44
- package/dist/search.js.map +1 -1
- package/dist/session/cached-storage.d.ts +71 -0
- package/dist/session/cached-storage.d.ts.map +1 -0
- package/dist/session/cached-storage.js +269 -0
- package/dist/session/cached-storage.js.map +1 -0
- package/dist/session/conversation-tails.d.ts +33 -0
- package/dist/session/conversation-tails.d.ts.map +1 -0
- package/dist/session/conversation-tails.js +101 -0
- package/dist/session/conversation-tails.js.map +1 -0
- package/dist/session/keys.d.ts +68 -0
- package/dist/session/keys.d.ts.map +1 -0
- package/dist/session/keys.js +161 -0
- package/dist/session/keys.js.map +1 -0
- package/dist/session/session.d.ts +167 -26
- package/dist/session/session.d.ts.map +1 -1
- package/dist/session/session.js +1029 -171
- package/dist/session/session.js.map +1 -1
- package/dist/session/transaction.d.ts +81 -3
- package/dist/session/transaction.d.ts.map +1 -1
- package/dist/session/transaction.js +224 -37
- package/dist/session/transaction.js.map +1 -1
- package/dist/session/writer.d.ts +74 -0
- package/dist/session/writer.d.ts.map +1 -0
- package/dist/session/writer.js +294 -0
- package/dist/session/writer.js.map +1 -0
- package/dist/storage/indexeddb.d.ts +10 -2
- package/dist/storage/indexeddb.d.ts.map +1 -1
- package/dist/storage/indexeddb.js +91 -41
- package/dist/storage/indexeddb.js.map +1 -1
- package/dist/storage/jsonl/storage.d.ts +14 -2
- package/dist/storage/jsonl/storage.d.ts.map +1 -1
- package/dist/storage/jsonl/storage.js +88 -24
- package/dist/storage/jsonl/storage.js.map +1 -1
- package/dist/storage/memory.d.ts +7 -2
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +28 -2
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/sqlite/storage.d.ts +14 -2
- package/dist/storage/sqlite/storage.d.ts.map +1 -1
- package/dist/storage/sqlite/storage.js +110 -36
- package/dist/storage/sqlite/storage.js.map +1 -1
- package/dist/storage/store/commit.d.ts +33 -0
- package/dist/storage/store/commit.d.ts.map +1 -0
- package/dist/storage/store/commit.js +286 -0
- package/dist/storage/store/commit.js.map +1 -0
- package/dist/storage/store/documents.d.ts +43 -0
- package/dist/storage/store/documents.d.ts.map +1 -0
- package/dist/storage/store/documents.js +102 -0
- package/dist/storage/store/documents.js.map +1 -0
- package/dist/storage/store/index.d.ts +2 -0
- package/dist/storage/store/index.d.ts.map +1 -0
- package/dist/storage/store/index.js +2 -0
- package/dist/storage/store/index.js.map +1 -0
- package/dist/storage/store/queries.d.ts +49 -0
- package/dist/storage/store/queries.d.ts.map +1 -0
- package/dist/storage/store/queries.js +93 -0
- package/dist/storage/store/queries.js.map +1 -0
- package/dist/storage/store/storage.d.ts +59 -0
- package/dist/storage/store/storage.d.ts.map +1 -0
- package/dist/storage/store/storage.js +438 -0
- package/dist/storage/store/storage.js.map +1 -0
- package/dist/storage/store/tables.d.ts +54 -0
- package/dist/storage/store/tables.d.ts.map +1 -0
- package/dist/storage/store/tables.js +108 -0
- package/dist/storage/store/tables.js.map +1 -0
- package/dist/testing/commit-writes.d.ts +5 -0
- package/dist/testing/commit-writes.d.ts.map +1 -0
- package/dist/testing/commit-writes.js +12 -0
- package/dist/testing/commit-writes.js.map +1 -0
- package/dist/testing/env-conformance.js +7 -7
- package/dist/testing/env-conformance.js.map +1 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/storage-benchmark.d.ts.map +1 -1
- package/dist/testing/storage-benchmark.js +18 -17
- package/dist/testing/storage-benchmark.js.map +1 -1
- package/dist/testing/storage-conformance.d.ts.map +1 -1
- package/dist/testing/storage-conformance.js +354 -69
- package/dist/testing/storage-conformance.js.map +1 -1
- package/dist/tool-search/tool-search.d.ts +6 -12
- package/dist/tool-search/tool-search.d.ts.map +1 -1
- package/dist/tool-search/tool-search.js +7 -7
- package/dist/tool-search/tool-search.js.map +1 -1
- package/dist/tools/bash.d.ts +12 -12
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +16 -15
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +7 -7
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +9 -9
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/index.d.ts +32 -32
- package/dist/tools/read.d.ts +6 -6
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +19 -8
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/write.d.ts +4 -4
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +5 -5
- package/dist/tools/write.js.map +1 -1
- package/dist/types.d.ts +223 -77
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +19 -12
- package/dist/harness/events.d.ts +0 -186
- package/dist/harness/events.d.ts.map +0 -1
- package/dist/harness/events.js +0 -373
- package/dist/harness/events.js.map +0 -1
- package/dist/session/observation.d.ts +0 -42
- package/dist/session/observation.d.ts.map +0 -1
- package/dist/session/observation.js +0 -281
- package/dist/session/observation.js.map +0 -1
package/dist/harness/context.js
CHANGED
|
@@ -4,49 +4,61 @@ const SCAN_PAGE_SIZE = 256;
|
|
|
4
4
|
const EXCLUDED_STOP_REASONS = new Set(["aborted", "error", "deferred"]);
|
|
5
5
|
const MISSING_RESULT_TEXT = "Tool result unavailable: history ends before this call completed.";
|
|
6
6
|
/**
|
|
7
|
-
* Capture the bounds of the current context, or of the context cut off at the visible entry `at
|
|
8
|
-
*
|
|
9
|
-
* them off the
|
|
7
|
+
* Capture the bounds of the current context, or of the context cut off at the visible entry `at`. The newest entry
|
|
8
|
+
* and head marker are known from the Session's commits, so the current bounds usually cost no Storage read. Run this
|
|
9
|
+
* on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan them off the
|
|
10
|
+
* line.
|
|
10
11
|
*/
|
|
11
|
-
export async function captureContextBounds(storage, conversationId, context, at
|
|
12
|
+
export async function captureContextBounds(session, storage, conversationId, context, at,
|
|
13
|
+
/** Set when `at` is known to be visible, as an entry of a range of the conversation is. */
|
|
14
|
+
visible = false) {
|
|
12
15
|
let tail;
|
|
13
16
|
if (at === undefined) {
|
|
14
|
-
tail =
|
|
17
|
+
tail = await session.latestEntryId(conversationId, context);
|
|
15
18
|
if (tail === undefined)
|
|
16
19
|
return undefined;
|
|
17
20
|
}
|
|
18
21
|
else {
|
|
19
|
-
if ((await storage.entry(conversationId, at, context)) === undefined) {
|
|
22
|
+
if (!visible && (await storage.entry(conversationId, at, context)) === undefined) {
|
|
20
23
|
throw new Error(`Entry ${at} is not visible from conversation ${conversationId}`);
|
|
21
24
|
}
|
|
22
25
|
tail = at;
|
|
23
26
|
}
|
|
24
|
-
return { head: await
|
|
27
|
+
return { head: await session.latestHeadMarker(conversationId, tail, context), tail };
|
|
25
28
|
}
|
|
26
29
|
/**
|
|
27
30
|
* `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only
|
|
28
31
|
* entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the
|
|
29
|
-
* read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.
|
|
32
|
+
* read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays. A caller that
|
|
33
|
+
* keeps the range for `advanceRange()` checks `current` again, synchronously where it installs it: a commit can be
|
|
34
|
+
* published between this function's return and the caller's continuation.
|
|
30
35
|
*/
|
|
31
36
|
export async function readContextFrom(session, storage, conversationId, context, at, previous) {
|
|
32
|
-
const
|
|
37
|
+
const visible = at !== undefined && previous !== undefined && rangeEntry(previous, at) !== undefined;
|
|
38
|
+
const bounds = await session.readOnLine((line) => captureContextBounds(session, line, conversationId, context, at, visible));
|
|
33
39
|
if (bounds === undefined)
|
|
34
40
|
return { view: emptyView(), range: undefined };
|
|
35
41
|
freezeJson(bounds.head);
|
|
42
|
+
const current = at === undefined;
|
|
36
43
|
let range;
|
|
37
44
|
if (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {
|
|
38
|
-
|
|
45
|
+
const entries = freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context));
|
|
46
|
+
range = deriveRange(bounds, entries, current);
|
|
39
47
|
}
|
|
40
48
|
else if (bounds.tail === previous.bounds.tail) {
|
|
41
49
|
range = previous;
|
|
42
50
|
}
|
|
43
51
|
else if (bounds.tail < previous.bounds.tail) {
|
|
44
|
-
range = deriveRange(bounds, previous.entries.filter((entry) => entry.id <= bounds.tail));
|
|
52
|
+
range = deriveRange(bounds, previous.entries.filter((entry) => entry.id <= bounds.tail), false);
|
|
45
53
|
}
|
|
46
54
|
else {
|
|
47
55
|
const minEntryId = idFromNumber(previous.bounds.tail + 1);
|
|
48
56
|
const added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);
|
|
49
|
-
range = extendRange(previous, bounds, freezeJson(added));
|
|
57
|
+
range = extendRange(previous, bounds, freezeJson(added), current);
|
|
58
|
+
}
|
|
59
|
+
// Entries committed while the scan ran off the line are not in the range: it is current only if none was.
|
|
60
|
+
if (range.current && session.knownLatestEntryId(conversationId) !== range.bounds.tail) {
|
|
61
|
+
range = { ...range, current: false };
|
|
50
62
|
}
|
|
51
63
|
const { view } = range;
|
|
52
64
|
return {
|
|
@@ -61,7 +73,7 @@ export async function readContextFrom(session, storage, conversationId, context,
|
|
|
61
73
|
}
|
|
62
74
|
/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */
|
|
63
75
|
export async function readContext(session, storage, conversationId, context, at) {
|
|
64
|
-
const bounds = await session.readOnLine(() => captureContextBounds(
|
|
76
|
+
const bounds = await session.readOnLine((line) => captureContextBounds(session, line, conversationId, context, at));
|
|
65
77
|
return deriveContext(storage, conversationId, bounds, context);
|
|
66
78
|
}
|
|
67
79
|
/**
|
|
@@ -73,13 +85,53 @@ export async function readContext(session, storage, conversationId, context, at)
|
|
|
73
85
|
export async function deriveContext(storage, conversationId, bounds, context) {
|
|
74
86
|
if (bounds === undefined)
|
|
75
87
|
return emptyView();
|
|
76
|
-
return deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context)).view;
|
|
88
|
+
return deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context), false).view;
|
|
77
89
|
}
|
|
78
90
|
function emptyView() {
|
|
79
91
|
return { head: undefined, entries: [], contributions: [], messages: [] };
|
|
80
92
|
}
|
|
93
|
+
/** Entry `id` of `range`, by binary search: a range holds its entries in ID order, every one visible from its conversation. */
|
|
94
|
+
export function rangeEntry(range, id) {
|
|
95
|
+
const entries = range.entries;
|
|
96
|
+
let low = 0;
|
|
97
|
+
let high = entries.length - 1;
|
|
98
|
+
while (low <= high) {
|
|
99
|
+
const middle = (low + high) >>> 1;
|
|
100
|
+
const entry = entries[middle];
|
|
101
|
+
if (entry.id === id)
|
|
102
|
+
return entry;
|
|
103
|
+
if (entry.id < id)
|
|
104
|
+
low = middle + 1;
|
|
105
|
+
else
|
|
106
|
+
high = middle - 1;
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* `range` with the entries one commit appended to its conversation, in ID order, without reading Storage: the
|
|
112
|
+
* conversation's own new entries are its newest, and a new head marker's head never lies before the range's start.
|
|
113
|
+
* A range that is not `current` stays as it is; the next read extends it from Storage. `undefined` drops the range.
|
|
114
|
+
*/
|
|
115
|
+
export function advanceRange(range, appended) {
|
|
116
|
+
if (!range.current || appended.length === 0)
|
|
117
|
+
return range;
|
|
118
|
+
const added = freezeJson(appended);
|
|
119
|
+
let head = range.bounds.head;
|
|
120
|
+
for (const entry of added)
|
|
121
|
+
if (entry.head !== undefined)
|
|
122
|
+
head = entry;
|
|
123
|
+
const bounds = { head, tail: added.at(-1).id };
|
|
124
|
+
if (head?.id === range.bounds.head?.id)
|
|
125
|
+
return extendRange(range, bounds, added, true);
|
|
126
|
+
const start = head.head;
|
|
127
|
+
// Heads only move forward: compaction keeps a later entry, a reset starts at itself, an older head is stale.
|
|
128
|
+
if (range.bounds.head !== undefined && start < range.bounds.head.head)
|
|
129
|
+
return undefined;
|
|
130
|
+
const entries = [...range.entries, ...added].filter((entry) => entry.id >= start);
|
|
131
|
+
return deriveRange(bounds, entries, true);
|
|
132
|
+
}
|
|
81
133
|
/** Derive the context view from the entries scanned within `bounds`. */
|
|
82
|
-
function deriveRange(bounds, entries) {
|
|
134
|
+
function deriveRange(bounds, entries, current) {
|
|
83
135
|
const edits = new Map();
|
|
84
136
|
// Edits of every entry in the range count, including older head markers that `selectActive()` drops.
|
|
85
137
|
for (const entry of entries)
|
|
@@ -91,16 +143,16 @@ function deriveRange(bounds, entries) {
|
|
|
91
143
|
const baseline = findBaseline(bounds.head, active);
|
|
92
144
|
const messages = leadWithSystem([...settled, ...orderToolResults(open)], baseline);
|
|
93
145
|
const view = { head: bounds.head, entries: active, contributions, messages };
|
|
94
|
-
return { bounds, entries, view, edited: new Set(edits.keys()), settled, open, baseline };
|
|
146
|
+
return { bounds, entries, view, edited: new Set(edits.keys()), settled, open, baseline, current };
|
|
95
147
|
}
|
|
96
148
|
/**
|
|
97
149
|
* `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and
|
|
98
150
|
* the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.
|
|
99
151
|
*/
|
|
100
|
-
function extendRange(previous, bounds, added) {
|
|
152
|
+
function extendRange(previous, bounds, added, current) {
|
|
101
153
|
const entries = [...previous.entries, ...added];
|
|
102
154
|
if (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {
|
|
103
|
-
return deriveRange(bounds, entries);
|
|
155
|
+
return deriveRange(bounds, entries, current);
|
|
104
156
|
}
|
|
105
157
|
const contributions = added.map((entry) => contribute(entry, undefined));
|
|
106
158
|
const { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);
|
|
@@ -111,7 +163,7 @@ function extendRange(previous, bounds, added) {
|
|
|
111
163
|
contributions: [...previous.view.contributions, ...contributions],
|
|
112
164
|
messages: leadWithSystem([...settled, ...orderToolResults(open)], baseline),
|
|
113
165
|
};
|
|
114
|
-
return { bounds, entries, view, edited: previous.edited, settled, open, baseline };
|
|
166
|
+
return { bounds, entries, view, edited: previous.edited, settled, open, baseline, current };
|
|
115
167
|
}
|
|
116
168
|
/**
|
|
117
169
|
* The baseline among `entries`, oldest first: the system message of the first `pi.system` entry after the head
|
|
@@ -185,12 +237,6 @@ function settle(settled, open) {
|
|
|
185
237
|
return { settled, open };
|
|
186
238
|
return { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };
|
|
187
239
|
}
|
|
188
|
-
/** The raw active entries within captured bounds, without deriving model context. */
|
|
189
|
-
export async function activeEntries(storage, conversationId, bounds, context) {
|
|
190
|
-
if (bounds === undefined)
|
|
191
|
-
return [];
|
|
192
|
-
return selectActive(bounds.head, await scanRange(storage, conversationId, rangeQuery(bounds), context));
|
|
193
|
-
}
|
|
194
240
|
/** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */
|
|
195
241
|
function rangeQuery(bounds) {
|
|
196
242
|
const head = bounds.head;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { SystemEntry } from \"../entries.ts\";\nimport { idFromNumber } from \"../ids.ts\";\nimport type { SessionImpl } from \"../session/session.ts\";\nimport type { ContextEdit, ConversationId, Cursor, EntryId, EntryQuery, EntryRecord, Storage } from \"../types.ts\";\nimport type { ContextView } from \"./types.ts\";\n\nconst SCAN_PAGE_SIZE = 256;\nconst EXCLUDED_STOP_REASONS: ReadonlySet<AssistantMessage[\"stopReason\"]> = new Set([\"aborted\", \"error\", \"deferred\"]);\nconst MISSING_RESULT_TEXT = \"Tool result unavailable: history ends before this call completed.\";\n\n/** Head marker and newest visible entry that fix one committed context range. */\nexport type ContextBounds = {\n\treadonly head: (EntryRecord & { readonly head: EntryId }) | undefined;\n\treadonly tail: EntryId;\n};\n\n/**\n * Capture the bounds of the current context, or of the context cut off at the visible entry `at`, with two O(1)\n * reads. Run this on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan\n * them off the line.\n */\nexport async function captureContextBounds(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextBounds | undefined> {\n\tlet tail: EntryId | undefined;\n\tif (at === undefined) {\n\t\ttail = (await storage.scanEntries({ conversationId }, 1, undefined, context)).items[0]?.id;\n\t\tif (tail === undefined) return undefined;\n\t} else {\n\t\tif ((await storage.entry(conversationId, at, context)) === undefined) {\n\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t}\n\t\ttail = at;\n\t}\n\treturn { head: await storage.findLatestHeadMarker(conversationId, tail, context), tail };\n}\n\n/**\n * One context read: the visible entries it scanned, from the head marker's head, or transcript start, through\n * `bounds.tail`, oldest first, and the view derived from them. Entries at or below the tail never change, so a later\n * read with the same head marker extends both.\n */\nexport type ContextRange = {\n\treadonly bounds: ContextBounds;\n\treadonly entries: readonly EntryRecord[];\n\treadonly view: ContextView;\n\t/** Targets of the edits in `entries`. */\n\treadonly edited: ReadonlySet<EntryId>;\n\t/**\n\t * `view.messages` from the contributed messages before the last assistant message. Tool results are ordered within\n\t * the messages up to the next assistant message, so later entries cannot change these.\n\t */\n\treadonly settled: readonly Message[];\n\t/** Contributed messages from the last assistant message on, before tool result ordering. */\n\treadonly open: readonly Message[];\n\t/** The baseline's system message; see `findBaseline()`. */\n\treadonly baseline: Baseline;\n};\n\n/** A baseline's system message, `null` when the range cannot have one, or `undefined` while a later entry may be one. */\ntype Baseline = Message | null | undefined;\n\n/**\n * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only\n * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the\n * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.\n */\nexport async function readContextFrom(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat: EntryId | undefined,\n\tprevious: ContextRange | undefined,\n): Promise<{ readonly view: ContextView; readonly range: ContextRange | undefined }> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\tif (bounds === undefined) return { view: emptyView(), range: undefined };\n\tfreezeJson(bounds.head);\n\tlet range: ContextRange;\n\tif (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {\n\t\trange = deriveRange(bounds, freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context)));\n\t} else if (bounds.tail === previous.bounds.tail) {\n\t\trange = previous;\n\t} else if (bounds.tail < previous.bounds.tail) {\n\t\trange = deriveRange(\n\t\t\tbounds,\n\t\t\tprevious.entries.filter((entry) => entry.id <= bounds.tail),\n\t\t);\n\t} else {\n\t\tconst minEntryId = idFromNumber<EntryId>(previous.bounds.tail + 1);\n\t\tconst added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);\n\t\trange = extendRange(previous, bounds, freezeJson(added));\n\t}\n\tconst { view } = range;\n\treturn {\n\t\tview: {\n\t\t\t...view,\n\t\t\tentries: [...view.entries],\n\t\t\tcontributions: [...view.contributions],\n\t\t\tmessages: [...view.messages],\n\t\t},\n\t\trange,\n\t};\n}\n\n/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */\nexport async function readContext(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextView> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\treturn deriveContext(storage, conversationId, bounds, context);\n}\n\n/**\n * Derive the active transcript and model context of one conversation within captured bounds.\n *\n * H = newest visible head marker; the range runs from `H.head` (or transcript start) through the tail. Per target,\n * the newest edit in the range wins. Context entries are H followed by the range's non-head entries.\n */\nexport async function deriveContext(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<ContextView> {\n\tif (bounds === undefined) return emptyView();\n\treturn deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context)).view;\n}\n\nfunction emptyView(): ContextView {\n\treturn { head: undefined, entries: [], contributions: [], messages: [] };\n}\n\n/** Derive the context view from the entries scanned within `bounds`. */\nfunction deriveRange(bounds: ContextBounds, entries: readonly EntryRecord[]): ContextRange {\n\tconst edits = new Map<EntryId, ContextEdit>();\n\t// Edits of every entry in the range count, including older head markers that `selectActive()` drops.\n\tfor (const entry of entries) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\tconst active = selectActive(bounds.head, entries);\n\tconst contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));\n\tconst { settled, open } = settle([], contributions.flat());\n\tconst baseline = findBaseline(bounds.head, active);\n\tconst messages = leadWithSystem([...settled, ...orderToolResults(open)], baseline);\n\tconst view = { head: bounds.head, entries: active, contributions, messages };\n\treturn { bounds, entries, view, edited: new Set(edits.keys()), settled, open, baseline };\n}\n\n/**\n * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and\n * the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.\n */\nfunction extendRange(previous: ContextRange, bounds: ContextBounds, added: readonly EntryRecord[]): ContextRange {\n\tconst entries = [...previous.entries, ...added];\n\tif (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {\n\t\treturn deriveRange(bounds, entries);\n\t}\n\tconst contributions = added.map((entry) => contribute(entry, undefined));\n\tconst { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);\n\tconst baseline = previous.baseline !== undefined ? previous.baseline : findBaseline(bounds.head, added);\n\tconst view = {\n\t\thead: bounds.head,\n\t\tentries: [...previous.view.entries, ...added],\n\t\tcontributions: [...previous.view.contributions, ...contributions],\n\t\tmessages: leadWithSystem([...settled, ...orderToolResults(open)], baseline),\n\t};\n\treturn { bounds, entries, view, edited: previous.edited, settled, open, baseline };\n}\n\n/**\n * The baseline among `entries`, oldest first: the system message of the first `pi.system` entry after the head\n * marker, or of the transcript without one, when generation wrote it with every section and tool in effect. After a\n * head marker it always does, and omits the retained `pi.system` entries. Without one it does when it saw no system\n * message before, which holds unless an earlier entry carries one, even if an edit omits it later. An edit that targets\n * the baseline contributes other messages, or none, so none leads; a later entry does not take its place.\n */\nfunction findBaseline(head: ContextBounds[\"head\"], entries: readonly EntryRecord[]): Baseline {\n\tfor (const entry of entries) {\n\t\tif (head !== undefined && entry.id <= head.id) continue;\n\t\tif (SystemEntry.is(entry)) return entry.model?.find((message) => message.role === \"system\") ?? null;\n\t\tif (head === undefined && carriesSystemMessage(entry)) return null;\n\t}\n\treturn undefined;\n}\n\n/** Whether an entry's own messages, or the replacement messages of its edits, include a system message. */\nfunction carriesSystemMessage(entry: EntryRecord): boolean {\n\tconst isSystem = (message: Message) => message.role === \"system\";\n\treturn (\n\t\t(entry.model?.some(isSystem) ?? false) ||\n\t\t(entry.edits?.some((edit) => edit.action === \"replace\" && edit.messages.some(isSystem)) ?? false)\n\t);\n}\n\n/**\n * Move the first system message to the front when only user messages precede it, or when it is the baseline's. A run's\n * input is committed before generation renders the system prompt, so a transcript starts with the input followed by\n * the baseline, and the range after a compaction with the kept history, assistant messages included. Providers treat\n * only a leading system message as the initial prompt and tool set; without it, a later tool change rewrites the\n * request's tool list and invalidates the whole prompt cache. Only the first system message moves, so replaying\n * system messages in order gives the same sections and tools. Any other system message, before an assistant message,\n * stays where it was committed: moving it would change what it means to the model.\n */\nfunction leadWithSystem(messages: Message[], baseline: Baseline): Message[] {\n\tconst index = messages.findIndex((message) => message.role === \"system\");\n\tif (index <= 0) return messages;\n\tconst system = messages[index]!;\n\tif (system !== baseline && messages.slice(0, index).some((message) => message.role !== \"user\")) {\n\t\treturn messages;\n\t}\n\treturn [system, ...messages.slice(0, index), ...messages.slice(index + 1)];\n}\n\n/** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */\nfunction contribute(entry: EntryRecord, edit: ContextEdit | undefined): readonly Message[] {\n\tif (edit?.action === \"omit\") return Object.freeze([]);\n\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\treturn Object.freeze(\n\t\tcontributed.filter((message) => message.role !== \"assistant\" || !EXCLUDED_STOP_REASONS.has(message.stopReason)),\n\t);\n}\n\n/** Objects `freezeJson()` froze with everything in them. `Object.isFrozen()` holds for shallow freezes too. */\nconst deeplyFrozen = new WeakSet<object>();\n\n/** Freeze a JSON value and everything in it. */\nfunction freezeJson<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || deeplyFrozen.has(value)) return value;\n\tfor (const child of Object.values(value)) freezeJson(child);\n\tObject.freeze(value);\n\tdeeplyFrozen.add(value);\n\treturn value;\n}\n\n/**\n * Move the ordered messages before the last assistant message of `open` to `settled`. `orderToolResults()` of a\n * sequence equals the concatenation over its parts when each later part starts with an assistant message.\n */\nfunction settle(\n\tsettled: readonly Message[],\n\topen: readonly Message[],\n): { readonly settled: readonly Message[]; readonly open: readonly Message[] } {\n\tconst last = open.findLastIndex((message) => message.role === \"assistant\");\n\tif (last <= 0) return { settled, open };\n\treturn { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };\n}\n\n/** The raw active entries within captured bounds, without deriving model context. */\nexport async function activeEntries(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<readonly EntryRecord[]> {\n\tif (bounds === undefined) return [];\n\treturn selectActive(bounds.head, await scanRange(storage, conversationId, rangeQuery(bounds), context));\n}\n\n/** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */\nfunction rangeQuery(bounds: ContextBounds): Omit<EntryQuery, \"conversationId\"> {\n\tconst head = bounds.head;\n\treturn head === undefined ? { maxEntryId: bounds.tail } : { minEntryId: head.head, maxEntryId: bounds.tail };\n}\n\n/** Visible entries within `range`, oldest first. */\nasync function scanRange(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\trange: Omit<EntryQuery, \"conversationId\">,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\n\tconst entries: EntryRecord[] = [];\n\tlet cursor: Cursor | undefined;\n\tdo {\n\t\tconst page = await storage.scanEntries({ conversationId, ...range }, SCAN_PAGE_SIZE, cursor, context);\n\t\tentries.push(...page.items);\n\t\tcursor = page.next;\n\t} while (cursor !== undefined);\n\treturn entries.reverse();\n}\n\n/** The head marker followed by the range's non-head entries, or the whole range without a marker. */\nfunction selectActive(head: ContextBounds[\"head\"], range: readonly EntryRecord[]): readonly EntryRecord[] {\n\treturn head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\n}\n\n/**\n * Place each assistant's tool results directly after it in call order. Results are taken from the messages before\n * the next assistant; a missing result is synthesized and unmatched results are dropped.\n */\nexport function orderToolResults(messages: readonly Message[]): Message[] {\n\tconst ordered: Message[] = [];\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index]!;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tordered.push(message);\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst calls = message.content.filter((content): content is ToolCall => content.type === \"toolCall\");\n\t\tif (calls.length === 0) continue;\n\t\tconst results = new Map<string, number>();\n\t\tfor (let next = index + 1; next < messages.length && messages[next]!.role !== \"assistant\"; next++) {\n\t\t\tconst candidate = messages[next]!;\n\t\t\tif (candidate.role === \"toolResult\" && !results.has(candidate.toolCallId)) {\n\t\t\t\tresults.set(candidate.toolCallId, next);\n\t\t\t}\n\t\t}\n\t\tfor (const call of calls) {\n\t\t\tconst resultIndex = results.get(call.id);\n\t\t\tordered.push(resultIndex === undefined ? missingResult(call, message.timestamp) : messages[resultIndex]!);\n\t\t}\n\t}\n\treturn ordered;\n}\n\nfunction missingResult(call: ToolCall, timestamp: number): ToolResultMessage {\n\treturn freezeJson({\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent: [{ type: \"text\", text: MISSING_RESULT_TEXT }],\n\t\tisError: true,\n\t\tdetails: { reason: \"missing_result\" },\n\t\ttimestamp,\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { SystemEntry } from \"../entries.ts\";\nimport { idFromNumber } from \"../ids.ts\";\nimport type { SessionImpl } from \"../session/session.ts\";\nimport type { ContextEdit, ConversationId, Cursor, EntryId, EntryQuery, EntryRecord, Storage } from \"../types.ts\";\nimport type { ContextView } from \"./types.ts\";\n\nconst SCAN_PAGE_SIZE = 256;\nconst EXCLUDED_STOP_REASONS: ReadonlySet<AssistantMessage[\"stopReason\"]> = new Set([\"aborted\", \"error\", \"deferred\"]);\nconst MISSING_RESULT_TEXT = \"Tool result unavailable: history ends before this call completed.\";\n\n/** Head marker and newest visible entry that fix one committed context range. */\nexport type ContextBounds = {\n\treadonly head: (EntryRecord & { readonly head: EntryId }) | undefined;\n\treadonly tail: EntryId;\n};\n\n/**\n * Capture the bounds of the current context, or of the context cut off at the visible entry `at`. The newest entry\n * and head marker are known from the Session's commits, so the current bounds usually cost no Storage read. Run this\n * on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan them off the\n * line.\n */\nexport async function captureContextBounds(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n\t/** Set when `at` is known to be visible, as an entry of a range of the conversation is. */\n\tvisible = false,\n): Promise<ContextBounds | undefined> {\n\tlet tail: EntryId | undefined;\n\tif (at === undefined) {\n\t\ttail = await session.latestEntryId(conversationId, context);\n\t\tif (tail === undefined) return undefined;\n\t} else {\n\t\tif (!visible && (await storage.entry(conversationId, at, context)) === undefined) {\n\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t}\n\t\ttail = at;\n\t}\n\treturn { head: await session.latestHeadMarker(conversationId, tail, context), tail };\n}\n\n/**\n * One context read: the visible entries it scanned, from the head marker's head, or transcript start, through\n * `bounds.tail`, oldest first, and the view derived from them. Entries at or below the tail never change, so a later\n * read with the same head marker extends both.\n */\nexport type ContextRange = {\n\treadonly bounds: ContextBounds;\n\treadonly entries: readonly EntryRecord[];\n\treadonly view: ContextView;\n\t/** Targets of the edits in `entries`. */\n\treadonly edited: ReadonlySet<EntryId>;\n\t/**\n\t * `view.messages` from the contributed messages before the last assistant message. Tool results are ordered within\n\t * the messages up to the next assistant message, so later entries cannot change these.\n\t */\n\treadonly settled: readonly Message[];\n\t/** Contributed messages from the last assistant message on, before tool result ordering. */\n\treadonly open: readonly Message[];\n\t/** The baseline's system message; see `findBaseline()`. */\n\treadonly baseline: Baseline;\n\t/**\n\t * Whether the range reached the conversation's newest entry when it was read, so the entries of later commits extend\n\t * it (`advanceRange()`); a range cut off at an earlier entry is not.\n\t */\n\treadonly current: boolean;\n};\n\n/** A baseline's system message, `null` when the range cannot have one, or `undefined` while a later entry may be one. */\ntype Baseline = Message | null | undefined;\n\n/**\n * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only\n * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the\n * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays. A caller that\n * keeps the range for `advanceRange()` checks `current` again, synchronously where it installs it: a commit can be\n * published between this function's return and the caller's continuation.\n */\nexport async function readContextFrom(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat: EntryId | undefined,\n\tprevious: ContextRange | undefined,\n): Promise<{ readonly view: ContextView; readonly range: ContextRange | undefined }> {\n\tconst visible = at !== undefined && previous !== undefined && rangeEntry(previous, at) !== undefined;\n\tconst bounds = await session.readOnLine((line) =>\n\t\tcaptureContextBounds(session, line, conversationId, context, at, visible),\n\t);\n\tif (bounds === undefined) return { view: emptyView(), range: undefined };\n\tfreezeJson(bounds.head);\n\tconst current = at === undefined;\n\tlet range: ContextRange;\n\tif (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {\n\t\tconst entries = freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context));\n\t\trange = deriveRange(bounds, entries, current);\n\t} else if (bounds.tail === previous.bounds.tail) {\n\t\trange = previous;\n\t} else if (bounds.tail < previous.bounds.tail) {\n\t\trange = deriveRange(\n\t\t\tbounds,\n\t\t\tprevious.entries.filter((entry) => entry.id <= bounds.tail),\n\t\t\tfalse,\n\t\t);\n\t} else {\n\t\tconst minEntryId = idFromNumber<EntryId>(previous.bounds.tail + 1);\n\t\tconst added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);\n\t\trange = extendRange(previous, bounds, freezeJson(added), current);\n\t}\n\t// Entries committed while the scan ran off the line are not in the range: it is current only if none was.\n\tif (range.current && session.knownLatestEntryId(conversationId) !== range.bounds.tail) {\n\t\trange = { ...range, current: false };\n\t}\n\tconst { view } = range;\n\treturn {\n\t\tview: {\n\t\t\t...view,\n\t\t\tentries: [...view.entries],\n\t\t\tcontributions: [...view.contributions],\n\t\t\tmessages: [...view.messages],\n\t\t},\n\t\trange,\n\t};\n}\n\n/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */\nexport async function readContext(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextView> {\n\tconst bounds = await session.readOnLine((line) => captureContextBounds(session, line, conversationId, context, at));\n\treturn deriveContext(storage, conversationId, bounds, context);\n}\n\n/**\n * Derive the active transcript and model context of one conversation within captured bounds.\n *\n * H = newest visible head marker; the range runs from `H.head` (or transcript start) through the tail. Per target,\n * the newest edit in the range wins. Context entries are H followed by the range's non-head entries.\n */\nexport async function deriveContext(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<ContextView> {\n\tif (bounds === undefined) return emptyView();\n\treturn deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context), false).view;\n}\n\nfunction emptyView(): ContextView {\n\treturn { head: undefined, entries: [], contributions: [], messages: [] };\n}\n\n/** Entry `id` of `range`, by binary search: a range holds its entries in ID order, every one visible from its conversation. */\nexport function rangeEntry(range: ContextRange, id: EntryId): EntryRecord | undefined {\n\tconst entries = range.entries;\n\tlet low = 0;\n\tlet high = entries.length - 1;\n\twhile (low <= high) {\n\t\tconst middle = (low + high) >>> 1;\n\t\tconst entry = entries[middle]!;\n\t\tif (entry.id === id) return entry;\n\t\tif (entry.id < id) low = middle + 1;\n\t\telse high = middle - 1;\n\t}\n\treturn undefined;\n}\n\n/**\n * `range` with the entries one commit appended to its conversation, in ID order, without reading Storage: the\n * conversation's own new entries are its newest, and a new head marker's head never lies before the range's start.\n * A range that is not `current` stays as it is; the next read extends it from Storage. `undefined` drops the range.\n */\nexport function advanceRange(range: ContextRange, appended: readonly EntryRecord[]): ContextRange | undefined {\n\tif (!range.current || appended.length === 0) return range;\n\tconst added = freezeJson(appended);\n\tlet head = range.bounds.head;\n\tfor (const entry of added) if (entry.head !== undefined) head = entry as typeof head;\n\tconst bounds = { head, tail: added.at(-1)!.id };\n\tif (head?.id === range.bounds.head?.id) return extendRange(range, bounds, added, true);\n\tconst start = head!.head;\n\t// Heads only move forward: compaction keeps a later entry, a reset starts at itself, an older head is stale.\n\tif (range.bounds.head !== undefined && start < range.bounds.head.head) return undefined;\n\tconst entries = [...range.entries, ...added].filter((entry) => entry.id >= start);\n\treturn deriveRange(bounds, entries, true);\n}\n\n/** Derive the context view from the entries scanned within `bounds`. */\nfunction deriveRange(bounds: ContextBounds, entries: readonly EntryRecord[], current: boolean): ContextRange {\n\tconst edits = new Map<EntryId, ContextEdit>();\n\t// Edits of every entry in the range count, including older head markers that `selectActive()` drops.\n\tfor (const entry of entries) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\tconst active = selectActive(bounds.head, entries);\n\tconst contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));\n\tconst { settled, open } = settle([], contributions.flat());\n\tconst baseline = findBaseline(bounds.head, active);\n\tconst messages = leadWithSystem([...settled, ...orderToolResults(open)], baseline);\n\tconst view = { head: bounds.head, entries: active, contributions, messages };\n\treturn { bounds, entries, view, edited: new Set(edits.keys()), settled, open, baseline, current };\n}\n\n/**\n * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and\n * the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.\n */\nfunction extendRange(\n\tprevious: ContextRange,\n\tbounds: ContextBounds,\n\tadded: readonly EntryRecord[],\n\tcurrent: boolean,\n): ContextRange {\n\tconst entries = [...previous.entries, ...added];\n\tif (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {\n\t\treturn deriveRange(bounds, entries, current);\n\t}\n\tconst contributions = added.map((entry) => contribute(entry, undefined));\n\tconst { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);\n\tconst baseline = previous.baseline !== undefined ? previous.baseline : findBaseline(bounds.head, added);\n\tconst view = {\n\t\thead: bounds.head,\n\t\tentries: [...previous.view.entries, ...added],\n\t\tcontributions: [...previous.view.contributions, ...contributions],\n\t\tmessages: leadWithSystem([...settled, ...orderToolResults(open)], baseline),\n\t};\n\treturn { bounds, entries, view, edited: previous.edited, settled, open, baseline, current };\n}\n\n/**\n * The baseline among `entries`, oldest first: the system message of the first `pi.system` entry after the head\n * marker, or of the transcript without one, when generation wrote it with every section and tool in effect. After a\n * head marker it always does, and omits the retained `pi.system` entries. Without one it does when it saw no system\n * message before, which holds unless an earlier entry carries one, even if an edit omits it later. An edit that targets\n * the baseline contributes other messages, or none, so none leads; a later entry does not take its place.\n */\nfunction findBaseline(head: ContextBounds[\"head\"], entries: readonly EntryRecord[]): Baseline {\n\tfor (const entry of entries) {\n\t\tif (head !== undefined && entry.id <= head.id) continue;\n\t\tif (SystemEntry.is(entry)) return entry.model?.find((message) => message.role === \"system\") ?? null;\n\t\tif (head === undefined && carriesSystemMessage(entry)) return null;\n\t}\n\treturn undefined;\n}\n\n/** Whether an entry's own messages, or the replacement messages of its edits, include a system message. */\nfunction carriesSystemMessage(entry: EntryRecord): boolean {\n\tconst isSystem = (message: Message) => message.role === \"system\";\n\treturn (\n\t\t(entry.model?.some(isSystem) ?? false) ||\n\t\t(entry.edits?.some((edit) => edit.action === \"replace\" && edit.messages.some(isSystem)) ?? false)\n\t);\n}\n\n/**\n * Move the first system message to the front when only user messages precede it, or when it is the baseline's. A run's\n * input is committed before generation renders the system prompt, so a transcript starts with the input followed by\n * the baseline, and the range after a compaction with the kept history, assistant messages included. Providers treat\n * only a leading system message as the initial prompt and tool set; without it, a later tool change rewrites the\n * request's tool list and invalidates the whole prompt cache. Only the first system message moves, so replaying\n * system messages in order gives the same sections and tools. Any other system message, before an assistant message,\n * stays where it was committed: moving it would change what it means to the model.\n */\nfunction leadWithSystem(messages: Message[], baseline: Baseline): Message[] {\n\tconst index = messages.findIndex((message) => message.role === \"system\");\n\tif (index <= 0) return messages;\n\tconst system = messages[index]!;\n\tif (system !== baseline && messages.slice(0, index).some((message) => message.role !== \"user\")) {\n\t\treturn messages;\n\t}\n\treturn [system, ...messages.slice(0, index), ...messages.slice(index + 1)];\n}\n\n/** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */\nfunction contribute(entry: EntryRecord, edit: ContextEdit | undefined): readonly Message[] {\n\tif (edit?.action === \"omit\") return Object.freeze([]);\n\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\treturn Object.freeze(\n\t\tcontributed.filter((message) => message.role !== \"assistant\" || !EXCLUDED_STOP_REASONS.has(message.stopReason)),\n\t);\n}\n\n/** Objects `freezeJson()` froze with everything in them. `Object.isFrozen()` holds for shallow freezes too. */\nconst deeplyFrozen = new WeakSet<object>();\n\n/** Freeze a JSON value and everything in it. */\nfunction freezeJson<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || deeplyFrozen.has(value)) return value;\n\tfor (const child of Object.values(value)) freezeJson(child);\n\tObject.freeze(value);\n\tdeeplyFrozen.add(value);\n\treturn value;\n}\n\n/**\n * Move the ordered messages before the last assistant message of `open` to `settled`. `orderToolResults()` of a\n * sequence equals the concatenation over its parts when each later part starts with an assistant message.\n */\nfunction settle(\n\tsettled: readonly Message[],\n\topen: readonly Message[],\n): { readonly settled: readonly Message[]; readonly open: readonly Message[] } {\n\tconst last = open.findLastIndex((message) => message.role === \"assistant\");\n\tif (last <= 0) return { settled, open };\n\treturn { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };\n}\n\n/** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */\nfunction rangeQuery(bounds: ContextBounds): Omit<EntryQuery, \"conversationId\"> {\n\tconst head = bounds.head;\n\treturn head === undefined ? { maxEntryId: bounds.tail } : { minEntryId: head.head, maxEntryId: bounds.tail };\n}\n\n/** Visible entries within `range`, oldest first. */\nasync function scanRange(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\trange: Omit<EntryQuery, \"conversationId\">,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\n\tconst entries: EntryRecord[] = [];\n\tlet cursor: Cursor | undefined;\n\tdo {\n\t\tconst page = await storage.scanEntries({ conversationId, ...range }, SCAN_PAGE_SIZE, cursor, context);\n\t\tentries.push(...page.items);\n\t\tcursor = page.next;\n\t} while (cursor !== undefined);\n\treturn entries.reverse();\n}\n\n/** The head marker followed by the range's non-head entries, or the whole range without a marker. */\nfunction selectActive(head: ContextBounds[\"head\"], range: readonly EntryRecord[]): readonly EntryRecord[] {\n\treturn head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\n}\n\n/**\n * Place each assistant's tool results directly after it in call order. Results are taken from the messages before\n * the next assistant; a missing result is synthesized and unmatched results are dropped.\n */\nexport function orderToolResults(messages: readonly Message[]): Message[] {\n\tconst ordered: Message[] = [];\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index]!;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tordered.push(message);\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst calls = message.content.filter((content): content is ToolCall => content.type === \"toolCall\");\n\t\tif (calls.length === 0) continue;\n\t\tconst results = new Map<string, number>();\n\t\tfor (let next = index + 1; next < messages.length && messages[next]!.role !== \"assistant\"; next++) {\n\t\t\tconst candidate = messages[next]!;\n\t\t\tif (candidate.role === \"toolResult\" && !results.has(candidate.toolCallId)) {\n\t\t\t\tresults.set(candidate.toolCallId, next);\n\t\t\t}\n\t\t}\n\t\tfor (const call of calls) {\n\t\t\tconst resultIndex = results.get(call.id);\n\t\t\tordered.push(resultIndex === undefined ? missingResult(call, message.timestamp) : messages[resultIndex]!);\n\t\t}\n\t}\n\treturn ordered;\n}\n\nfunction missingResult(call: ToolCall, timestamp: number): ToolResultMessage {\n\treturn freezeJson({\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent: [{ type: \"text\", text: MISSING_RESULT_TEXT }],\n\t\tisError: true,\n\t\tdetails: { reason: \"missing_result\" },\n\t\ttimestamp,\n\t});\n}\n"]}
|
package/dist/harness/define.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import type { JsonValue } from "@earendil-works/chord";
|
|
2
|
-
import type {
|
|
3
|
-
import type { AnyTask, Extension, HookRegistration, HooksOf, PromptSection, ToolRegistration, Wrap } from "./types.ts";
|
|
2
|
+
import type { Task } from "../types.ts";
|
|
3
|
+
import type { AnyTask, Extension, HookRegistration, HooksOf, PromptSection, RunEndTask, ToolRegistration, Wrap } from "./types.ts";
|
|
4
4
|
/** Identity function that types an extension. */
|
|
5
5
|
export declare function defineExtension<Tool extends ToolRegistration = ToolRegistration>(extension: Extension<Tool>): Extension<Tool>;
|
|
6
6
|
/** Identity function that types a tool: `args` from `parameters`, details from what it reports. */
|
|
7
|
-
export declare function defineTool<TParameters extends
|
|
7
|
+
export declare function defineTool<TParameters extends object, TDetails extends JsonValue = JsonValue>(tool: ToolRegistration<TParameters, TDetails>): ToolRegistration<TParameters, TDetails>;
|
|
8
8
|
/** A prompt section; tagged unless `tag` is false. */
|
|
9
9
|
export declare function section<Tool extends ToolRegistration = ToolRegistration>(key: string, render: PromptSection<Tool>["render"], options?: {
|
|
10
10
|
readonly tag?: boolean;
|
|
11
11
|
}): PromptSection<Tool>;
|
|
12
12
|
/** Hook handlers for tasks with `task`'s name. */
|
|
13
13
|
export declare function hook<K extends AnyTask>(task: K, handlers: Partial<HooksOf<K>>): HookRegistration;
|
|
14
|
+
/** A task an `afterRun` hook asks for, its input typed by the task (spec §8.3). */
|
|
15
|
+
export declare function runEndTask<I extends JsonValue, S extends {
|
|
16
|
+
phase: string;
|
|
17
|
+
}, R, H extends object>(task: Task<I, S, R, H>, input: I): RunEndTask;
|
|
14
18
|
/** Wrap the tool named like `tool` wherever the wrapping extension is selected. */
|
|
15
19
|
export declare function wrapTool<Tool extends ToolRegistration>(tool: Tool, wrapper: (tool: Tool) => Tool): Wrap<Tool>;
|
|
16
20
|
/** Wrap the section `key` wherever the wrapping extension is selected. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/harness/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/harness/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EACX,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,IAAI,EACJ,MAAM,YAAY,CAAC;AAEpB,iDAAiD;AACjD,wBAAgB,eAAe,CAAC,IAAI,SAAS,gBAAgB,GAAG,gBAAgB,EAC/E,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,GACxB,SAAS,CAAC,IAAI,CAAC,CAEjB;AAED,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,WAAW,SAAS,MAAM,EAAE,QAAQ,SAAS,SAAS,GAAG,SAAS,EAC5F,IAAI,EAAE,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,GAC3C,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAEzC;AAED,sDAAsD;AACtD,wBAAgB,OAAO,CAAC,IAAI,SAAS,gBAAgB,GAAG,gBAAgB,EACvE,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EACrC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,aAAa,CAAC,IAAI,CAAC,CAErB;AAED,kDAAkD;AAClD,wBAAgB,IAAI,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAEhG;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE,CAAC,SAAS,MAAM,EAC/F,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACtB,KAAK,EAAE,CAAC,GACN,UAAU,CAEZ;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,SAAS,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAE7G;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,IAAI,SAAS,gBAAgB,GAAG,gBAAgB,EAC3E,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,IAAI,CAAC,GAC5D,IAAI,CAAC,IAAI,CAAC,CAEZ"}
|
package/dist/harness/define.js
CHANGED
|
@@ -14,6 +14,10 @@ export function section(key, render, options) {
|
|
|
14
14
|
export function hook(task, handlers) {
|
|
15
15
|
return { task: task.definition.name, handlers };
|
|
16
16
|
}
|
|
17
|
+
/** A task an `afterRun` hook asks for, its input typed by the task (spec §8.3). */
|
|
18
|
+
export function runEndTask(task, input) {
|
|
19
|
+
return { task, input };
|
|
20
|
+
}
|
|
17
21
|
/** Wrap the tool named like `tool` wherever the wrapping extension is selected. */
|
|
18
22
|
export function wrapTool(tool, wrapper) {
|
|
19
23
|
return { tool: tool.name, wrap: wrapper };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/harness/define.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/harness/define.ts"],"names":[],"mappings":"AAaA,iDAAiD;AACjD,MAAM,UAAU,eAAe,CAC9B,SAA0B;IAE1B,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,UAAU,CACzB,IAA6C;IAE7C,OAAO,IAAI,CAAC;AACb,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,OAAO,CACtB,GAAW,EACX,MAAqC,EACrC,OAAoC;IAEpC,OAAO,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;AACzF,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,IAAI,CAAoB,IAAO,EAAE,QAA6B;IAC7E,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;AACjD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,UAAU,CACzB,IAAsB,EACtB,KAAQ;IAER,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAgC,IAAU,EAAE,OAA6B;IAChG,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3C,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAC1B,GAAW,EACX,OAA8D;IAE9D,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import type { JsonValue } from \"@earendil-works/chord\";\nimport type { Task } from \"../types.ts\";\nimport type {\n\tAnyTask,\n\tExtension,\n\tHookRegistration,\n\tHooksOf,\n\tPromptSection,\n\tRunEndTask,\n\tToolRegistration,\n\tWrap,\n} from \"./types.ts\";\n\n/** Identity function that types an extension. */\nexport function defineExtension<Tool extends ToolRegistration = ToolRegistration>(\n\textension: Extension<Tool>,\n): Extension<Tool> {\n\treturn extension;\n}\n\n/** Identity function that types a tool: `args` from `parameters`, details from what it reports. */\nexport function defineTool<TParameters extends object, TDetails extends JsonValue = JsonValue>(\n\ttool: ToolRegistration<TParameters, TDetails>,\n): ToolRegistration<TParameters, TDetails> {\n\treturn tool;\n}\n\n/** A prompt section; tagged unless `tag` is false. */\nexport function section<Tool extends ToolRegistration = ToolRegistration>(\n\tkey: string,\n\trender: PromptSection<Tool>[\"render\"],\n\toptions?: { readonly tag?: boolean },\n): PromptSection<Tool> {\n\treturn options?.tag === undefined ? { key, render } : { key, render, tag: options.tag };\n}\n\n/** Hook handlers for tasks with `task`'s name. */\nexport function hook<K extends AnyTask>(task: K, handlers: Partial<HooksOf<K>>): HookRegistration {\n\treturn { task: task.definition.name, handlers };\n}\n\n/** A task an `afterRun` hook asks for, its input typed by the task (spec §8.3). */\nexport function runEndTask<I extends JsonValue, S extends { phase: string }, R, H extends object>(\n\ttask: Task<I, S, R, H>,\n\tinput: I,\n): RunEndTask {\n\treturn { task, input };\n}\n\n/** Wrap the tool named like `tool` wherever the wrapping extension is selected. */\nexport function wrapTool<Tool extends ToolRegistration>(tool: Tool, wrapper: (tool: Tool) => Tool): Wrap<Tool> {\n\treturn { tool: tool.name, wrap: wrapper };\n}\n\n/** Wrap the section `key` wherever the wrapping extension is selected. */\nexport function wrapSection<Tool extends ToolRegistration = ToolRegistration>(\n\tkey: string,\n\twrapper: (section: PromptSection<Tool>) => PromptSection<Tool>,\n): Wrap<Tool> {\n\treturn { section: key, wrap: wrapper };\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type Draft } from "@earendil-works/chord";
|
|
2
2
|
import type { DeferredHandle, ModelThinkingLevel } from "@earendil-works/pi-ai";
|
|
3
|
-
import type { ConversationId, EntryId, SubmissionId, TaskId, Tx } from "../types.ts";
|
|
3
|
+
import type { ConversationId, EntryId, SubmissionId, Task, TaskId, Tx } from "../types.ts";
|
|
4
4
|
import { type LiveState } from "./live.ts";
|
|
5
5
|
import { type ToolTaskResult } from "./tool.ts";
|
|
6
|
-
import type { CompactionResult, ConversationStreamOptions, GenerationHooks, ModelRef } from "./types.ts";
|
|
6
|
+
import type { CompactionResult, ConversationStreamOptions, GenerationHooks, ModelRef, RunSummary } from "./types.ts";
|
|
7
7
|
export type GenerationInput = Record<string, never>;
|
|
8
8
|
export type GenerationCheckpoint = {
|
|
9
9
|
phase: "prepare";
|
|
@@ -29,11 +29,23 @@ export type GenerationCheckpoint = {
|
|
|
29
29
|
attempt: number;
|
|
30
30
|
compacted?: TaskId<CompactionResult>;
|
|
31
31
|
until: number;
|
|
32
|
+
/**
|
|
33
|
+
* The deferred response a poll was fetching when the host died: the next attempt polls it again instead of
|
|
34
|
+
* sending another request, so the provider's job is neither lost nor paid twice. Absent otherwise.
|
|
35
|
+
*/
|
|
36
|
+
poll?: {
|
|
37
|
+
model: ModelRef;
|
|
38
|
+
thinkingLevel?: ModelThinkingLevel;
|
|
39
|
+
cutoff: EntryId;
|
|
40
|
+
handle: DeferredHandle;
|
|
41
|
+
};
|
|
32
42
|
} | {
|
|
33
43
|
phase: "poll";
|
|
34
44
|
attempt: number;
|
|
35
45
|
compacted?: TaskId<CompactionResult>;
|
|
36
46
|
model: ModelRef;
|
|
47
|
+
/** Absent in checkpoints of earlier versions. */
|
|
48
|
+
thinkingLevel?: ModelThinkingLevel;
|
|
37
49
|
cutoff: EntryId;
|
|
38
50
|
handle: DeferredHandle;
|
|
39
51
|
pollAt: number;
|
|
@@ -46,6 +58,18 @@ export type GenerationCheckpoint = {
|
|
|
46
58
|
tools: TaskId<ToolTaskResult>[];
|
|
47
59
|
/** Calls of a sequential round not started yet, in call order. */
|
|
48
60
|
pending: string[];
|
|
61
|
+
} | {
|
|
62
|
+
/**
|
|
63
|
+
* The run answered: its answer is committed and its inputs settled, and it keeps `pi.live.run` while `afterRun`
|
|
64
|
+
* hooks are asked for tasks (spec §8.3). A crash here asks them again: nothing was created yet.
|
|
65
|
+
*/
|
|
66
|
+
phase: "finish";
|
|
67
|
+
run: RunSummary;
|
|
68
|
+
} | {
|
|
69
|
+
/** Waiting on the tasks `afterRun` hooks asked for, which the generation owns; then the run ends. */
|
|
70
|
+
phase: "drain";
|
|
71
|
+
answer: EntryId;
|
|
72
|
+
tasks: TaskId[];
|
|
49
73
|
};
|
|
50
74
|
export type GenerationResult = {
|
|
51
75
|
entryId: EntryId;
|
|
@@ -54,14 +78,23 @@ export type GenerationResult = {
|
|
|
54
78
|
* Built-in generation task: prepares the positional system prompt and tool loadout, requests or polls the model,
|
|
55
79
|
* retries, and classifies the response. The run's inputs live in `pi.live.run`.
|
|
56
80
|
*/
|
|
57
|
-
export declare const GenerationTask:
|
|
81
|
+
export declare const GenerationTask: Task<GenerationInput, GenerationCheckpoint, GenerationResult, GenerationHooks>;
|
|
58
82
|
/**
|
|
59
83
|
* Append a committed partial left by an interrupted, aborted, faulted, or orphaned attempt as an aborted assistant
|
|
60
84
|
* entry; the caller replaces or removes `generation`.
|
|
61
85
|
*/
|
|
62
86
|
export declare function convertPartial(tx: Tx, live: Draft<LiveState>, conversationId: ConversationId): Promise<void>;
|
|
63
|
-
/**
|
|
64
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Start a run for `inputs`, placed input submissions, whose first entry is `first` (`LiveRun.first`): a new generation
|
|
89
|
+
* takes `pi.live.run`. Removes `runFailure`, unless `keepFailure`: a run started in the commit that ended the failed one
|
|
90
|
+
* keeps its failure.
|
|
91
|
+
*/
|
|
92
|
+
export declare function startRun(tx: Tx, conversationId: ConversationId, live: Draft<LiveState>, run: {
|
|
93
|
+
readonly inputs: SubmissionId[];
|
|
94
|
+
readonly first: EntryId | undefined;
|
|
95
|
+
}, options?: {
|
|
96
|
+
readonly keepFailure?: boolean;
|
|
97
|
+
}): Promise<void>;
|
|
65
98
|
/** Hand run control from `from` to `to`; the run's inputs move with it. */
|
|
66
99
|
export declare function handOver(live: Draft<LiveState>, from: TaskId, to: TaskId): void;
|
|
67
100
|
//# sourceMappingURL=generation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/harness/generation.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,KAAK,EAAkB,MAAM,uBAAuB,CAAC;AAC3F,OAAO,KAAK,EAGX,cAAc,EAGd,kBAAkB,EAGlB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,KAAK,EACX,cAAc,EACd,OAAO,EAEP,YAAY,
|
|
1
|
+
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/harness/generation.ts"],"names":[],"mappings":"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"}
|