@phosphorco/bb-identity 0.3.1 → 0.4.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/EDITING.md +131 -0
- package/NATIVE-SOURCES.md +222 -0
- package/README.md +14 -0
- package/dist/client-entry-runtime.js +128 -36
- package/dist/editing-runtime.js +1845 -0
- package/dist/native-runtime.js +476 -0
- package/dist/native-testing-runtime.js +11 -0
- package/dist/react-entry-runtime.js +253 -51
- package/dist/state-controller-runtime.js +96 -11
- package/dist/state-runtime.js +95 -11
- package/editing.d.ts +137 -0
- package/native-testing.d.ts +15 -0
- package/native.d.ts +100 -0
- package/package.json +20 -3
- package/react.d.ts +16 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// native-runtime.ts
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
// model-runtime.ts
|
|
6
|
+
var ID_MAX_LENGTH = 256;
|
|
7
|
+
var ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@/-]*$/;
|
|
8
|
+
function failure(code, message, retry = "never") {
|
|
9
|
+
return { code, message, retry };
|
|
10
|
+
}
|
|
11
|
+
function ok(value) {
|
|
12
|
+
return { ok: true, value };
|
|
13
|
+
}
|
|
14
|
+
function err(code, message, retry = "never") {
|
|
15
|
+
return { ok: false, error: failure(code, message, retry) };
|
|
16
|
+
}
|
|
17
|
+
function isRecord(value) {
|
|
18
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
function isBoundedString(value, maximum = ID_MAX_LENGTH) {
|
|
21
|
+
return typeof value === "string" && value.length > 0 && value.length <= maximum;
|
|
22
|
+
}
|
|
23
|
+
function idCodec(kind) {
|
|
24
|
+
return {
|
|
25
|
+
decode(input) {
|
|
26
|
+
const valid = kind === "server-session" ? isBoundedString(input, 4096) && !/[\u0000-\u0020\u007f]/.test(input) : isBoundedString(input) && ID_PATTERN.test(input);
|
|
27
|
+
if (!valid) {
|
|
28
|
+
return err("invalid-input", `Invalid ${kind} identifier.`);
|
|
29
|
+
}
|
|
30
|
+
return ok(input);
|
|
31
|
+
},
|
|
32
|
+
encode(value) {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
var identityKeyCodec = {
|
|
38
|
+
decode(input) {
|
|
39
|
+
if (!isBoundedString(input, 512) || /[\u0000-\u001f\u007f]/.test(input)) {
|
|
40
|
+
return err("invalid-input", "Invalid identity key.");
|
|
41
|
+
}
|
|
42
|
+
return ok(input);
|
|
43
|
+
},
|
|
44
|
+
encode(value) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function decodePresentation(value) {
|
|
49
|
+
return isRecord(value) && isBoundedString(value.displayName, 512) && (value.handle === null || isBoundedString(value.handle, 512)) && (value.avatarUrl === null || isBoundedString(value.avatarUrl, 2048));
|
|
50
|
+
}
|
|
51
|
+
function decodeIdentity(value) {
|
|
52
|
+
if (!isRecord(value) || !identityKeyCodec.decode(value.key).ok)
|
|
53
|
+
return false;
|
|
54
|
+
if (value.kind === "person")
|
|
55
|
+
return isBoundedString(value.issuer, 512) && isBoundedString(value.subject, 512);
|
|
56
|
+
if (value.kind === "default-user")
|
|
57
|
+
return isBoundedString(value.instanceId);
|
|
58
|
+
if (value.kind === "machine") {
|
|
59
|
+
return isBoundedString(value.instanceId) && (value.hostId === null || isBoundedString(value.hostId, 512));
|
|
60
|
+
}
|
|
61
|
+
return value.kind === "external" && isBoundedString(value.pluginId) && isBoundedString(value.subject, 512);
|
|
62
|
+
}
|
|
63
|
+
function isMachineReference(value) {
|
|
64
|
+
return isRecord(value) && value.kind === "machine" && decodeIdentity(value);
|
|
65
|
+
}
|
|
66
|
+
function isActorSnapshot(value) {
|
|
67
|
+
if (!isRecord(value) || !decodeIdentity(value.identity) || !decodePresentation(value.presentation))
|
|
68
|
+
return false;
|
|
69
|
+
const evidence = value.evidence;
|
|
70
|
+
if (evidence !== "provider-verified" && evidence !== "local-user" && evidence !== "upstream-default" && evidence !== "integration-asserted" && evidence !== "legacy" && evidence !== "machine")
|
|
71
|
+
return false;
|
|
72
|
+
return isMachineReference(value.identity) ? evidence === "machine" : evidence !== "machine";
|
|
73
|
+
}
|
|
74
|
+
var externalPersonSnapshotCodec = {
|
|
75
|
+
decode(value) {
|
|
76
|
+
if (!isRecord(value) || Object.keys(value).some((key) => !["identity", "presentation", "evidence"].includes(key)) || value.evidence !== "integration-asserted" || !isActorSnapshot(value) || !isRecord(value.identity) || value.identity.kind !== "person" || Object.keys(value.identity).some((key) => !["kind", "key", "issuer", "subject"].includes(key))) {
|
|
77
|
+
return err("invalid-input", "External person attribution requires a canonical person and integration-asserted evidence.");
|
|
78
|
+
}
|
|
79
|
+
return ok(value);
|
|
80
|
+
},
|
|
81
|
+
encode(value) {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// native-runtime.ts
|
|
87
|
+
var NATIVE_SOURCE_LIMITS = Object.freeze({ events: 2000, rows: 2000, bytes: 2097152, depth: 8, historyReads: 16, identifier: 256, dataDepth: 64 });
|
|
88
|
+
var coverage = (reasons, empty = false) => ({ status: reasons.length ? empty ? "unknown" : "partial" : "known", reasons: [...new Set(reasons)] });
|
|
89
|
+
var unknownLineage = () => ({ status: "unknown", reason: "not-established-by-native-source" });
|
|
90
|
+
var validId = (v) => typeof v === "string" && v.trim().length > 0 && v.length <= NATIVE_SOURCE_LIMITS.identifier;
|
|
91
|
+
var presentation = z.object({ displayName: z.string().min(1).max(512), handle: z.string().max(512).nullable(), avatarUrl: z.string().max(2048).nullable() });
|
|
92
|
+
var nativeActor = z.discriminatedUnion("evidence", [
|
|
93
|
+
z.object({ evidence: z.literal("provider-verified"), identity: z.object({ kind: z.literal("person"), key: z.string().min(1).max(2048), issuer: z.string().min(1).max(512), subject: z.string().min(1).max(512) }), presentation }),
|
|
94
|
+
z.object({ evidence: z.literal("machine"), identity: z.object({ kind: z.literal("machine"), key: z.string().min(1).max(2048), instanceId: z.string().min(1).max(256), hostId: z.string().max(256).nullable() }), presentation })
|
|
95
|
+
]);
|
|
96
|
+
var nativeMention = z.object({ identity: z.object({ kind: z.literal("person"), key: z.string().min(1).max(2048), issuer: z.string().min(1).max(512), subject: z.string().min(1).max(512) }), presentation, source: z.object({ pluginId: z.string().min(1).max(256), generation: z.string().min(1).max(256) }) });
|
|
97
|
+
function decodeSourceActors(value) {
|
|
98
|
+
if (value === undefined)
|
|
99
|
+
return ok(undefined);
|
|
100
|
+
if (!Array.isArray(value) || value.length > 256)
|
|
101
|
+
return err("invalid-input", "Malformed native actor snapshot.");
|
|
102
|
+
const actors = [];
|
|
103
|
+
for (const actor of value) {
|
|
104
|
+
if (actor === null) {
|
|
105
|
+
actors.push(null);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const external = externalPersonSnapshotCodec.decode(actor);
|
|
109
|
+
if (external.ok) {
|
|
110
|
+
actors.push(external.value);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const native = nativeActor.safeParse(actor);
|
|
114
|
+
if (!native.success)
|
|
115
|
+
return err("invalid-input", "Malformed native actor snapshot.");
|
|
116
|
+
actors.push(native.data);
|
|
117
|
+
}
|
|
118
|
+
return ok(actors);
|
|
119
|
+
}
|
|
120
|
+
var seq = (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
|
|
121
|
+
var turn = (e) => e.scope.kind === "turn" ? e.scope.turnId : null;
|
|
122
|
+
function boundedSnapshot(input) {
|
|
123
|
+
let bytes = 0;
|
|
124
|
+
const active = new Set;
|
|
125
|
+
const encoder = new TextEncoder;
|
|
126
|
+
function add(text) {
|
|
127
|
+
if (text.length > NATIVE_SOURCE_LIMITS.bytes - bytes)
|
|
128
|
+
throw new Error("bytes");
|
|
129
|
+
bytes += encoder.encode(text).byteLength;
|
|
130
|
+
if (bytes > NATIVE_SOURCE_LIMITS.bytes)
|
|
131
|
+
throw new Error("bytes");
|
|
132
|
+
}
|
|
133
|
+
function copy(value, depth) {
|
|
134
|
+
if (depth > NATIVE_SOURCE_LIMITS.dataDepth)
|
|
135
|
+
throw new Error("data depth");
|
|
136
|
+
if (value === null || value === undefined) {
|
|
137
|
+
add("null");
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === "string") {
|
|
141
|
+
add(value);
|
|
142
|
+
add('""');
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
if (typeof value === "boolean") {
|
|
146
|
+
add(value ? "true" : "false");
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
150
|
+
add(String(value));
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
if (typeof value !== "object")
|
|
154
|
+
throw new Error("non-data value");
|
|
155
|
+
const array = Array.isArray(value);
|
|
156
|
+
if (!array && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
|
|
157
|
+
throw new Error("non-data prototype");
|
|
158
|
+
if (active.has(value))
|
|
159
|
+
throw new Error("cycle");
|
|
160
|
+
active.add(value);
|
|
161
|
+
add(array ? "[]" : "{}");
|
|
162
|
+
const result = array ? [] : {};
|
|
163
|
+
const keys = Reflect.ownKeys(value);
|
|
164
|
+
if (array && (value.length > NATIVE_SOURCE_LIMITS.bytes || keys.length !== value.length + 1))
|
|
165
|
+
throw new Error("non-dense array");
|
|
166
|
+
for (const key of keys) {
|
|
167
|
+
if (typeof key !== "string")
|
|
168
|
+
throw new Error("symbol key");
|
|
169
|
+
if (array && key === "length")
|
|
170
|
+
continue;
|
|
171
|
+
if (array && !/^(0|[1-9][0-9]*)$/.test(key))
|
|
172
|
+
throw new Error("non-data array property");
|
|
173
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
174
|
+
if (!descriptor || !("value" in descriptor))
|
|
175
|
+
throw new Error("accessor");
|
|
176
|
+
add(key);
|
|
177
|
+
add(":,");
|
|
178
|
+
Object.defineProperty(result, key, { value: copy(descriptor.value, depth + 1), enumerable: true, configurable: true, writable: true });
|
|
179
|
+
}
|
|
180
|
+
active.delete(value);
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
const stable = copy(input, 0);
|
|
184
|
+
if (encoder.encode(JSON.stringify(stable)).byteLength > NATIVE_SOURCE_LIMITS.bytes)
|
|
185
|
+
throw new Error("serialized bytes");
|
|
186
|
+
return stable;
|
|
187
|
+
}
|
|
188
|
+
function assistantItem(e) {
|
|
189
|
+
if (e.type !== "item/agentMessage/delta" && !(e.type === "item/completed" && e.data.item.type === "agentMessage"))
|
|
190
|
+
return null;
|
|
191
|
+
const completed = e.type === "item/completed" && e.data.item.type === "agentMessage" ? e.data.item : null;
|
|
192
|
+
const id = completed ? completed.id : e.type === "item/agentMessage/delta" ? e.data.itemId : null;
|
|
193
|
+
const text = completed ? completed.text : e.type === "item/agentMessage/delta" ? e.data.delta : null;
|
|
194
|
+
const parent = completed ? completed.parentToolCallId : e.type === "item/agentMessage/delta" ? e.data.parentToolCallId : undefined;
|
|
195
|
+
const phase = completed?.phase;
|
|
196
|
+
if (!validId(id) || typeof text !== "string" || text.length === 0 || parent !== undefined && !validId(parent) || phase !== undefined && phase !== null && phase !== "commentary" && phase !== "final_answer")
|
|
197
|
+
return null;
|
|
198
|
+
return { id, parentToolCallId: parent ?? null, phase: phase ?? null };
|
|
199
|
+
}
|
|
200
|
+
var sameItem = (a, b) => a.id === b.id && a.parentToolCallId === b.parentToolCallId;
|
|
201
|
+
function validInputGroups(r) {
|
|
202
|
+
const data = r.data;
|
|
203
|
+
if (!validId(data.requestId) || !Array.isArray(data.input))
|
|
204
|
+
return false;
|
|
205
|
+
const groups = data.inputGroups ?? [data.input];
|
|
206
|
+
if (!Array.isArray(groups) || groups.length === 0 || groups.length > 256)
|
|
207
|
+
return false;
|
|
208
|
+
return groups.every((group) => Array.isArray(group) && group.every((part) => {
|
|
209
|
+
if (!part || typeof part !== "object" || part.visibility !== undefined && part.visibility !== "agent-only")
|
|
210
|
+
return false;
|
|
211
|
+
if (part.type === "text")
|
|
212
|
+
return typeof part.text === "string";
|
|
213
|
+
if (part.type === "image")
|
|
214
|
+
return typeof part.url === "string";
|
|
215
|
+
if (part.type === "localImage" || part.type === "localFile")
|
|
216
|
+
return typeof part.path === "string";
|
|
217
|
+
return false;
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
function validateReference(s) {
|
|
221
|
+
if (!s || !validId(s.threadId))
|
|
222
|
+
return false;
|
|
223
|
+
const keys = Object.keys(s).sort().join(",");
|
|
224
|
+
const expected = s.kind === "event" ? "eventId,kind,threadId" : s.kind === "request-group" ? "eventId,groupIndex,kind,requestId,threadId" : s.kind === "assistant-item" ? "itemId,kind,scope,terminalEventId,threadId" : "kind,scope,terminalEventId,threadId";
|
|
225
|
+
if (keys !== expected)
|
|
226
|
+
return false;
|
|
227
|
+
if ((s.kind === "assistant-item" || s.kind === "assistant-terminal") && (!s.scope || Object.keys(s.scope).join(",") !== "turnId"))
|
|
228
|
+
return false;
|
|
229
|
+
if (s.kind === "event")
|
|
230
|
+
return validId(s.eventId);
|
|
231
|
+
if (s.kind === "request-group")
|
|
232
|
+
return validId(s.eventId) && validId(s.requestId) && seq(s.groupIndex);
|
|
233
|
+
return (s.kind === "assistant-item" || s.kind === "assistant-terminal") && validId(s.terminalEventId) && validId(s.scope?.turnId) && (s.kind !== "assistant-item" || validId(s.itemId));
|
|
234
|
+
}
|
|
235
|
+
function normalize(snapshot) {
|
|
236
|
+
try {
|
|
237
|
+
const { source, events, timeline } = boundedSnapshot(snapshot);
|
|
238
|
+
if (!validateReference(source) || !Array.isArray(events) || events.length > NATIVE_SOURCE_LIMITS.events)
|
|
239
|
+
return err("invalid-input", "Invalid or oversized native source snapshot.");
|
|
240
|
+
const ids = new Set, sequences = new Set;
|
|
241
|
+
for (const e of events) {
|
|
242
|
+
if (e.threadId !== source.threadId || !validId(e.id) || !seq(e.seq) || !e.scope || !["thread", "turn"].includes(e.scope.kind) || e.scope.kind === "turn" && !validId(e.scope.turnId) || ids.has(e.id) || sequences.has(e.seq))
|
|
243
|
+
return err("invalid-input", "Native events must have unique ids/sequences in the requested thread.");
|
|
244
|
+
ids.add(e.id);
|
|
245
|
+
sequences.add(e.seq);
|
|
246
|
+
}
|
|
247
|
+
const byId = new Map(events.map((e) => [e.id, e]));
|
|
248
|
+
let sourceReasons = [], attributionReasons = [], executionReasons = [];
|
|
249
|
+
let author = null, latestEditor = null;
|
|
250
|
+
let mentionedTargets = [], dispatch = null;
|
|
251
|
+
let terminal = null, origin = "unknown";
|
|
252
|
+
const text = [], inputs = [];
|
|
253
|
+
let request;
|
|
254
|
+
let groupIndex;
|
|
255
|
+
let boundarySeq = Number.MAX_SAFE_INTEGER, targetTurn = null;
|
|
256
|
+
if (source.kind === "event" || source.kind === "request-group") {
|
|
257
|
+
const event = byId.get(source.eventId);
|
|
258
|
+
if (!event)
|
|
259
|
+
sourceReasons.push("missing-source-event");
|
|
260
|
+
else if (event.type === "client/turn/requested") {
|
|
261
|
+
request = event;
|
|
262
|
+
if (source.kind === "request-group") {
|
|
263
|
+
if (event.data.requestId !== source.requestId)
|
|
264
|
+
return err("invalid-input", "Native request id does not match the source event.");
|
|
265
|
+
groupIndex = source.groupIndex;
|
|
266
|
+
} else
|
|
267
|
+
groupIndex = (event.data.inputGroups?.length ?? 1) === 1 ? 0 : undefined;
|
|
268
|
+
} else
|
|
269
|
+
sourceReasons.push("unsupported-event-source");
|
|
270
|
+
} else {
|
|
271
|
+
const event = byId.get(source.terminalEventId);
|
|
272
|
+
if (!event)
|
|
273
|
+
sourceReasons.push("missing-terminal-event");
|
|
274
|
+
else if (event.type !== "turn/completed" || turn(event) !== source.scope.turnId)
|
|
275
|
+
return err("invalid-input", "Terminal event and scope.turnId must match exactly.");
|
|
276
|
+
else {
|
|
277
|
+
if (!["completed", "failed", "interrupted"].includes(event.data.status))
|
|
278
|
+
return err("invalid-input", "Invalid terminal status.");
|
|
279
|
+
boundarySeq = event.seq;
|
|
280
|
+
targetTurn = source.scope.turnId;
|
|
281
|
+
origin = "assistant";
|
|
282
|
+
terminal = { terminalEventId: event.id, scope: { turnId: targetTurn }, status: event.data.status };
|
|
283
|
+
collectAssistant({ source, timeline, events, terminalSeq: event.seq, targetTurn, text, reasons: sourceReasons });
|
|
284
|
+
}
|
|
285
|
+
attributionReasons.push("assistant-has-no-native-person-author");
|
|
286
|
+
}
|
|
287
|
+
if (request) {
|
|
288
|
+
const data = request.data;
|
|
289
|
+
const groups = data.inputGroups ?? [data.input];
|
|
290
|
+
if (!validInputGroups(request) || !["user", "agent", "system"].includes(data.initiator) || !["spawn", "tell"].includes(data.source) || data.senderThreadId !== null && !validId(data.senderThreadId) || groups.length === 0 || groups.length > 256 || groups.some((g) => !Array.isArray(g)))
|
|
291
|
+
return err("invalid-input", "Malformed native request data.");
|
|
292
|
+
const authors = decodeSourceActors(data.p6rAuthors), editors = decodeSourceActors(data.p6rEditors);
|
|
293
|
+
if (!authors.ok)
|
|
294
|
+
return authors;
|
|
295
|
+
if (!editors.ok)
|
|
296
|
+
return editors;
|
|
297
|
+
if (data.p6rMentionedTargets && (data.p6rMentionedTargets.length > 256 || data.p6rMentionedTargets.some((m) => !nativeMention.safeParse(m).success)))
|
|
298
|
+
return err("invalid-input", "Malformed native mention target.");
|
|
299
|
+
dispatch = { initiator: data.initiator, source: data.source, senderThreadId: data.senderThreadId };
|
|
300
|
+
origin = data.initiator;
|
|
301
|
+
mentionedTargets = data.p6rMentionedTargets ?? [];
|
|
302
|
+
if (!data.p6rMentionedTargets)
|
|
303
|
+
attributionReasons.push("mentions-not-recorded");
|
|
304
|
+
if (groupIndex === undefined)
|
|
305
|
+
sourceReasons.push("request-group-required");
|
|
306
|
+
else if (!groups[groupIndex])
|
|
307
|
+
return err("invalid-input", "Native input group is out of range.");
|
|
308
|
+
else {
|
|
309
|
+
if (authors.value?.length === groups.length)
|
|
310
|
+
author = authors.value[groupIndex] ?? null;
|
|
311
|
+
else
|
|
312
|
+
attributionReasons.push("authors-not-group-aligned");
|
|
313
|
+
if (editors.value?.length === groups.length)
|
|
314
|
+
latestEditor = editors.value[groupIndex] ?? null;
|
|
315
|
+
else
|
|
316
|
+
attributionReasons.push("editors-not-group-aligned");
|
|
317
|
+
const group = groups[groupIndex];
|
|
318
|
+
const visible = group.filter((p) => p.type === "text" && p.visibility !== "agent-only");
|
|
319
|
+
text.push({ text: visible.map((p) => p.type === "text" ? p.text : "").join(`
|
|
320
|
+
`), parentToolCallId: null, basis: "request-input", eventIds: [request.id], sourceSeqStart: request.seq, sourceSeqEnd: request.seq, itemId: null, timelineRowId: null, phase: null });
|
|
321
|
+
if (group.some((p) => p.type !== "text"))
|
|
322
|
+
sourceReasons.push("non-text-input-not-expanded");
|
|
323
|
+
if (group.some((p) => p.visibility === "agent-only"))
|
|
324
|
+
sourceReasons.push("agent-only-input-excluded");
|
|
325
|
+
}
|
|
326
|
+
if (!author)
|
|
327
|
+
attributionReasons.push("author-not-recorded");
|
|
328
|
+
}
|
|
329
|
+
if (!author && attributionReasons.length === 0)
|
|
330
|
+
attributionReasons.push("author-not-established");
|
|
331
|
+
const accepted = (request || terminal ? events : []).filter((e) => e.type === "turn/input/accepted" && e.seq <= boundarySeq && (targetTurn === null || turn(e) === targetTurn)).sort((a, b) => a.seq - b.seq);
|
|
332
|
+
for (const e of accepted) {
|
|
333
|
+
if (e.type !== "turn/input/accepted")
|
|
334
|
+
continue;
|
|
335
|
+
if (request && e.data.clientRequestId !== request.data.requestId)
|
|
336
|
+
continue;
|
|
337
|
+
const candidates = events.filter((r2) => r2.type === "client/turn/requested" && r2.data.requestId === e.data.clientRequestId && r2.seq < e.seq);
|
|
338
|
+
const t = turn(e);
|
|
339
|
+
if (candidates.length !== 1 || !t || request && candidates[0]?.id !== request.id || !validId(e.data.clientRequestId) || !validInputGroups(candidates[0])) {
|
|
340
|
+
executionReasons.push("unresolved-accepted-request");
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const r = candidates[0];
|
|
344
|
+
inputs.push({ requestEventId: r.id, requestId: r.data.requestId, acceptedEventId: e.id, turnId: t, groupCount: r.data.inputGroups?.length ?? 1 });
|
|
345
|
+
}
|
|
346
|
+
if (!inputs.length)
|
|
347
|
+
executionReasons.push("no-exact-accepted-input-link");
|
|
348
|
+
executionReasons.push("bounded-snapshot-causal-coverage");
|
|
349
|
+
return ok(structuredClone({ source, basis: "public-sdk-native", origin, author, latestEditor, mentionedTargets, dispatch, terminal, text, inputs, initiatingPerson: unknownLineage(), carriedPerson: unknownLineage(), agentId: null, delegation: unknownLineage(), coverage: { source: coverage(sourceReasons, text.length === 0), attribution: coverage(attributionReasons, author === null), execution: coverage(executionReasons, inputs.length === 0), personalLineage: coverage(["not-established-by-native-source"], true) }, history: [] }));
|
|
350
|
+
} catch {
|
|
351
|
+
return err("invalid-input", "Malformed native source snapshot.");
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function collectAssistant(args) {
|
|
355
|
+
const { source, timeline, events, terminalSeq, targetTurn, text, reasons } = args;
|
|
356
|
+
if (!timeline) {
|
|
357
|
+
reasons.push("public-timeline-required");
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (!seq(timeline.maxSeq) || timeline.maxSeq < terminalSeq || !validId(timeline.timelinePage.historySnapshot)) {
|
|
361
|
+
reasons.push("projection-does-not-cover-terminal");
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (timeline.timelinePage.olderCursor)
|
|
365
|
+
reasons.push("projection-page-incomplete");
|
|
366
|
+
if (timeline.timelinePage.olderRowUpdates?.length)
|
|
367
|
+
reasons.push("projection-row-updates-unapplied");
|
|
368
|
+
if (timeline.delta)
|
|
369
|
+
reasons.push("projection-delta-requires-materialized-rows");
|
|
370
|
+
const flat = [];
|
|
371
|
+
function walk(rows, depth) {
|
|
372
|
+
if (depth > NATIVE_SOURCE_LIMITS.depth)
|
|
373
|
+
throw new Error("depth");
|
|
374
|
+
for (const row of rows) {
|
|
375
|
+
if (flat.length >= NATIVE_SOURCE_LIMITS.rows)
|
|
376
|
+
throw new Error("rows");
|
|
377
|
+
flat.push(row);
|
|
378
|
+
if (row.kind === "turn") {
|
|
379
|
+
if (row.children === null && row.turnId === targetTurn)
|
|
380
|
+
reasons.push("projection-children-unavailable");
|
|
381
|
+
if (row.children)
|
|
382
|
+
walk(row.children, depth + 1);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
walk(timeline.rows, 0);
|
|
387
|
+
const rowIds = new Set;
|
|
388
|
+
for (const row of flat) {
|
|
389
|
+
if (row.kind !== "conversation" || row.role !== "assistant" || row.turnId !== targetTurn)
|
|
390
|
+
continue;
|
|
391
|
+
if (typeof row.text !== "string")
|
|
392
|
+
throw new Error("invalid projection text");
|
|
393
|
+
if (!validId(row.id) || row.threadId !== source.threadId || !seq(row.sourceSeqStart) || !seq(row.sourceSeqEnd) || row.sourceSeqStart > row.sourceSeqEnd || rowIds.has(row.id)) {
|
|
394
|
+
reasons.push("invalid-projection-row");
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
rowIds.add(row.id);
|
|
398
|
+
if (row.sourceSeqStart > terminalSeq)
|
|
399
|
+
continue;
|
|
400
|
+
if (row.sourceSeqEnd > terminalSeq) {
|
|
401
|
+
reasons.push("projection-row-crosses-terminal");
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
const start = events.find((e) => e.seq === row.sourceSeqStart);
|
|
405
|
+
const end = events.find((e) => e.seq === row.sourceSeqEnd);
|
|
406
|
+
const startItem = start && assistantItem(start), endItem = end && assistantItem(end);
|
|
407
|
+
if (!start || !end || !startItem || !endItem || !sameItem(startItem, endItem) || turn(start) !== targetTurn || turn(end) !== targetTurn) {
|
|
408
|
+
reasons.push("projection-source-join-unresolved");
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (source.kind === "assistant-item") {
|
|
412
|
+
if (startItem.id !== source.itemId)
|
|
413
|
+
continue;
|
|
414
|
+
const parents = new Set(events.filter((e) => e.seq <= terminalSeq && turn(e) === targetTurn && assistantItem(e)?.id === source.itemId).map((e) => assistantItem(e).parentToolCallId));
|
|
415
|
+
if (parents.size > 1) {
|
|
416
|
+
reasons.push("assistant-item-parent-ambiguous");
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const matching = events.filter((e) => e.seq >= row.sourceSeqStart && e.seq <= row.sourceSeqEnd && turn(e) === targetTurn && (() => {
|
|
421
|
+
const item = assistantItem(e);
|
|
422
|
+
return item !== null && sameItem(item, startItem);
|
|
423
|
+
})()).sort((a, b) => a.seq - b.seq);
|
|
424
|
+
if (!row.text.trim()) {
|
|
425
|
+
reasons.push("empty-or-deferred-assistant-row");
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
text.push({ text: row.text, parentToolCallId: startItem.parentToolCallId, basis: "public-timeline", eventIds: matching.map((e) => e.id), sourceSeqStart: row.sourceSeqStart, sourceSeqEnd: row.sourceSeqEnd, itemId: startItem.id, timelineRowId: row.id, phase: matching.map(assistantItem).find((i) => i?.phase)?.phase ?? null });
|
|
429
|
+
}
|
|
430
|
+
if (text.length === 0)
|
|
431
|
+
reasons.push("no-retained-assistant-text-in-snapshot");
|
|
432
|
+
}
|
|
433
|
+
function createNativeSourceAdapter(options = {}) {
|
|
434
|
+
return {
|
|
435
|
+
resolve: normalize,
|
|
436
|
+
async enrich(evidence, readOptions) {
|
|
437
|
+
if (!options.history)
|
|
438
|
+
return ok(evidence);
|
|
439
|
+
const thread = idCodec("thread").decode(evidence.source.threadId);
|
|
440
|
+
if (!thread.ok)
|
|
441
|
+
return thread;
|
|
442
|
+
const history = [];
|
|
443
|
+
const attempted = new Set;
|
|
444
|
+
const eventIds = evidence.origin === "assistant" ? evidence.inputs.map((i) => i.requestEventId) : evidence.text.flatMap((t) => t.eventIds);
|
|
445
|
+
for (const eventId of [...new Set(eventIds)].slice(0, NATIVE_SOURCE_LIMITS.historyReads)) {
|
|
446
|
+
if (readOptions?.signal?.aborted)
|
|
447
|
+
return err("cancelled", "Native evidence enrichment cancelled.");
|
|
448
|
+
const result = await options.history.contributions({ kind: "native-event", threadId: thread.value, eventId, limit: 100 }, readOptions);
|
|
449
|
+
history.push({ kind: "contributions", eventId, result });
|
|
450
|
+
if (!result.ok || result.value.status !== "known" && result.value.status !== "partial")
|
|
451
|
+
continue;
|
|
452
|
+
for (const contribution of result.value.items) {
|
|
453
|
+
if (history.length >= NATIVE_SOURCE_LIMITS.historyReads)
|
|
454
|
+
break;
|
|
455
|
+
if (contribution.reference.threadId !== thread.value)
|
|
456
|
+
continue;
|
|
457
|
+
const key = JSON.stringify([contribution.reference.threadId, contribution.reference.contributionId]);
|
|
458
|
+
if (attempted.has(key))
|
|
459
|
+
continue;
|
|
460
|
+
attempted.add(key);
|
|
461
|
+
if (readOptions?.signal?.aborted)
|
|
462
|
+
return err("cancelled", "Native evidence enrichment cancelled.");
|
|
463
|
+
const attempts = await options.history.attempts({ kind: "contribution", reference: contribution.reference, limit: 50 }, readOptions);
|
|
464
|
+
history.push({ kind: "attempts", contribution: contribution.reference, result: attempts });
|
|
465
|
+
}
|
|
466
|
+
if (history.length >= NATIVE_SOURCE_LIMITS.historyReads)
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
return ok({ ...evidence, history });
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
export {
|
|
474
|
+
createNativeSourceAdapter,
|
|
475
|
+
NATIVE_SOURCE_LIMITS
|
|
476
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// native-testing-runtime.ts
|
|
3
|
+
function defineNativeSourceConformance(options) {
|
|
4
|
+
if (options.cases.length === 0)
|
|
5
|
+
throw new Error("Native source conformance requires explicit semantic cases.");
|
|
6
|
+
for (const fixture of options.cases)
|
|
7
|
+
options.test(`native source: ${fixture.name}`, async () => fixture.verify(await options.resolve(fixture.snapshot)));
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
defineNativeSourceConformance
|
|
11
|
+
};
|