@hypercarrier/rarebit 0.1.0-alpha.1

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.
Files changed (50) hide show
  1. package/AGENTS.md +14 -0
  2. package/CHANGELOG.md +10 -0
  3. package/LICENSE +21 -0
  4. package/README.md +142 -0
  5. package/RUNBOOK.md +36 -0
  6. package/SECURITY.md +11 -0
  7. package/VISUAL-LANGUAGE.md +89 -0
  8. package/bin/rarebit.mjs +20 -0
  9. package/package.json +96 -0
  10. package/schemas/rarebit.schema.json +618 -0
  11. package/scripts/e2e-recall-pi083.mjs +199 -0
  12. package/scripts/e2e-summary-cache.mjs +257 -0
  13. package/scripts/verify-package.mjs +81 -0
  14. package/src/automatic-summary-policy.mjs +210 -0
  15. package/src/extension.mjs +516 -0
  16. package/src/herdr-activity.mjs +111 -0
  17. package/src/index.mjs +13 -0
  18. package/src/lifecycle.mjs +322 -0
  19. package/src/rarebit-activity.mjs +48 -0
  20. package/src/rarebit-artifact-state.mjs +839 -0
  21. package/src/rarebit-cli.mjs +305 -0
  22. package/src/rarebit-command.mjs +281 -0
  23. package/src/rarebit-core.mjs +525 -0
  24. package/src/rarebit-model.mjs +226 -0
  25. package/src/rarebit-recall.mjs +182 -0
  26. package/src/rarebit-service.mjs +579 -0
  27. package/src/rarebit-session.mjs +227 -0
  28. package/src/rarebit-settings.mjs +79 -0
  29. package/src/rarebit-store.mjs +771 -0
  30. package/src/rarebit-visual-language.mjs +110 -0
  31. package/src/types.d.ts +601 -0
  32. package/test/automatic-summary-policy.test.mjs +476 -0
  33. package/test/extension-activity.test.mjs +73 -0
  34. package/test/fixtures/branch-mixed.json +117 -0
  35. package/test/fixtures/conformance-corpus.v1.json +306 -0
  36. package/test/fixtures/truncated-mixed.jsonl +2 -0
  37. package/test/herdr-activity.test.mjs +93 -0
  38. package/test/lifecycle-contract-independent.test.mjs +627 -0
  39. package/test/rarebit-activity.test.mjs +82 -0
  40. package/test/rarebit-artifact-state.test.mjs +440 -0
  41. package/test/rarebit-cli.test.mjs +222 -0
  42. package/test/rarebit-command.test.mjs +127 -0
  43. package/test/rarebit-core.test.mjs +461 -0
  44. package/test/rarebit-cross-consumer-conformance.test.mjs +150 -0
  45. package/test/rarebit-independent-contract.test.mjs +317 -0
  46. package/test/rarebit-recall.test.mjs +434 -0
  47. package/test/rarebit-sidecar-protocol.test.mjs +538 -0
  48. package/test/rarebit-visual-language.test.mjs +75 -0
  49. package/test/summary-attention-independent.test.mjs +172 -0
  50. package/test/types-history.ts +13 -0
@@ -0,0 +1,839 @@
1
+ import {
2
+ RAREBIT_SESSION_STATUS_REASONS,
3
+ RAREBIT_SUMMARY_RECEIPT_LIFECYCLE_BOUNDARIES,
4
+ sha256,
5
+ } from "./rarebit-core.mjs";
6
+
7
+ const TERMINAL_STATUSES = new Set([
8
+ "ok",
9
+ "ineligible",
10
+ "inhibited",
11
+ "unavailable_overflow",
12
+ "failure",
13
+ ]);
14
+ const AVAILABILITY = new Set(["available", "missing", "unreadable"]);
15
+ const EXPECTATIONS = new Set(["owner_request", "agent_settled", "snapshot"]);
16
+
17
+ function timestamp(value) {
18
+ const result = Date.parse(value);
19
+ return Number.isFinite(result) ? result : Number.NEGATIVE_INFINITY;
20
+ }
21
+
22
+ function nullableString(value) {
23
+ return typeof value === "string" ? value : null;
24
+ }
25
+
26
+ function modelRef(value) {
27
+ return value &&
28
+ typeof value.provider === "string" &&
29
+ typeof value.id === "string"
30
+ ? { provider: value.provider, id: value.id }
31
+ : null;
32
+ }
33
+
34
+ function ref(record) {
35
+ if (!record) return null;
36
+ return {
37
+ jobId: nullableString(record.jobId),
38
+ sessionId: nullableString(record.sessionId),
39
+ branchLeafId: nullableString(record.branch?.leafId),
40
+ selectionManifestHash: nullableString(record.selection?.manifestHash),
41
+ selectorVersion: nullableString(record.selection?.selectorVersion),
42
+ lifecycleBoundary: nullableString(record.lifecycleBoundary),
43
+ promptVersion: nullableString(record.promptVersion),
44
+ model: modelRef(record.model),
45
+ observedAt: nullableString(record.observedAt),
46
+ schemaVersion: Number.isInteger(record.schemaVersion)
47
+ ? record.schemaVersion
48
+ : null,
49
+ implementationVersion: nullableString(record.implementationVersion),
50
+ };
51
+ }
52
+
53
+ function projection(record) {
54
+ if (record.status === "ok")
55
+ return {
56
+ status: record.sessionStatus,
57
+ reason: record.statusReason,
58
+ assessmentRef: ref(record),
59
+ };
60
+ if (record.status === "ineligible")
61
+ return {
62
+ status: "ineligible",
63
+ reason: "intrinsic_policy",
64
+ assessmentRef: ref(record),
65
+ };
66
+ const reason = {
67
+ inhibited: "inhibited",
68
+ unavailable_overflow: "overflow",
69
+ failure: "synthesis_failure",
70
+ }[record.status];
71
+ return { status: "error", reason, assessmentRef: ref(record) };
72
+ }
73
+
74
+ function isOccurrence(value) {
75
+ return (
76
+ value &&
77
+ typeof value === "object" &&
78
+ typeof value.occurrenceId === "string" &&
79
+ value.occurrenceId &&
80
+ (typeof value.sourceEntryId === "string" || value.sourceEntryId === null) &&
81
+ Number.isInteger(value.order) &&
82
+ (value.role === "user" || value.role === "assistant") &&
83
+ ["user", "stop", "continuation"].includes(value.outcome) &&
84
+ typeof value.contentHash === "string" &&
85
+ value.contentHash
86
+ );
87
+ }
88
+
89
+ function plainObject(value) {
90
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
91
+ }
92
+
93
+ function exactKeys(value, required, optional = []) {
94
+ if (!plainObject(value)) return false;
95
+ const allowed = new Set([...required, ...optional]);
96
+ return (
97
+ required.every((key) => Object.hasOwn(value, key)) &&
98
+ Object.keys(value).every((key) => allowed.has(key))
99
+ );
100
+ }
101
+
102
+ function nonEmptyString(value) {
103
+ return typeof value === "string" && value.length > 0;
104
+ }
105
+
106
+ function sha256String(value) {
107
+ return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
108
+ }
109
+
110
+ function validSelection(selection, { request = false, compact = false } = {}) {
111
+ if (compact) {
112
+ if (
113
+ !exactKeys(selection, [
114
+ "manifestHash",
115
+ "selectorVersion",
116
+ "occurrenceCount",
117
+ "uniquePayloadCount",
118
+ "latestUserSourceEntryId",
119
+ ]) ||
120
+ !sha256String(selection.manifestHash) ||
121
+ !nonEmptyString(selection.selectorVersion) ||
122
+ !Number.isInteger(selection.occurrenceCount) ||
123
+ selection.occurrenceCount < 0 ||
124
+ !Number.isInteger(selection.uniquePayloadCount) ||
125
+ selection.uniquePayloadCount < 0 ||
126
+ selection.uniquePayloadCount > selection.occurrenceCount ||
127
+ !(
128
+ selection.latestUserSourceEntryId === null ||
129
+ nonEmptyString(selection.latestUserSourceEntryId)
130
+ )
131
+ )
132
+ return false;
133
+ return !request || nonEmptyString(selection.latestUserSourceEntryId);
134
+ }
135
+ if (
136
+ !selection ||
137
+ !sha256String(selection.manifestHash) ||
138
+ !nonEmptyString(
139
+ selection.selectorVersion ?? selection.manifest?.selectorVersion,
140
+ )
141
+ )
142
+ return false;
143
+ const occurrences = selection.occurrences;
144
+ if (!Array.isArray(occurrences) || !occurrences.every(isOccurrence))
145
+ return false;
146
+ const ids = new Set();
147
+ for (const occurrence of occurrences) {
148
+ if (!request && occurrence.sourceEntryId === null) continue;
149
+ if (
150
+ !nonEmptyString(occurrence.sourceEntryId) ||
151
+ ids.has(occurrence.sourceEntryId)
152
+ )
153
+ return false;
154
+ ids.add(occurrence.sourceEntryId);
155
+ }
156
+ if (!request) return true;
157
+ const last = occurrences.at(-1);
158
+ return last?.role === "user" && last.outcome === "user";
159
+ }
160
+
161
+ function validBranch(branch) {
162
+ return (
163
+ exactKeys(branch, ["leafId", "entryCount", "pathHash"]) &&
164
+ (branch.leafId === null || nonEmptyString(branch.leafId)) &&
165
+ Number.isInteger(branch.entryCount) &&
166
+ branch.entryCount >= 0 &&
167
+ sha256String(branch.pathHash)
168
+ );
169
+ }
170
+
171
+ function validModel(model) {
172
+ return (
173
+ model === null ||
174
+ (exactKeys(model, ["provider", "id"]) &&
175
+ nonEmptyString(model.provider) &&
176
+ nonEmptyString(model.id))
177
+ );
178
+ }
179
+
180
+ function validModelProvenance(value) {
181
+ return (
182
+ exactKeys(value, ["source", "status"], ["settingsKey"]) &&
183
+ nonEmptyString(value.source) &&
184
+ nonEmptyString(value.status) &&
185
+ (!Object.hasOwn(value, "settingsKey") || nonEmptyString(value.settingsKey))
186
+ );
187
+ }
188
+
189
+ function validError(value) {
190
+ return (
191
+ exactKeys(value, ["name", "message"]) &&
192
+ nonEmptyString(value.name) &&
193
+ typeof value.message === "string"
194
+ );
195
+ }
196
+
197
+ function validOverflow(value) {
198
+ return (
199
+ exactKeys(value, ["promptChars", "maxPromptChars", "strategy"]) &&
200
+ Number.isInteger(value.promptChars) &&
201
+ value.promptChars >= 0 &&
202
+ Number.isInteger(value.maxPromptChars) &&
203
+ value.maxPromptChars > 0 &&
204
+ ["none", "fixed_contract_exceeds_limit"].includes(value.strategy)
205
+ );
206
+ }
207
+
208
+ function validInputCoveragePolicy(value) {
209
+ return (
210
+ exactKeys(value, ["strategy", "maxPromptChars"]) &&
211
+ [
212
+ "complete_or_explicit_overflow",
213
+ "newest_suffix_with_explicit_omission",
214
+ ].includes(value.strategy) &&
215
+ Number.isInteger(value.maxPromptChars) &&
216
+ value.maxPromptChars > 0
217
+ );
218
+ }
219
+
220
+ function validInputCoverage(value) {
221
+ return (
222
+ exactKeys(value, [
223
+ "totalMessageCount",
224
+ "includedMessageCount",
225
+ "omittedMessageCount",
226
+ "omittedTextChars",
227
+ "promptChars",
228
+ "complete",
229
+ ]) &&
230
+ [
231
+ value.totalMessageCount,
232
+ value.includedMessageCount,
233
+ value.omittedMessageCount,
234
+ value.omittedTextChars,
235
+ value.promptChars,
236
+ ].every((item) => Number.isInteger(item) && item >= 0) &&
237
+ value.includedMessageCount + value.omittedMessageCount ===
238
+ value.totalMessageCount &&
239
+ typeof value.complete === "boolean" &&
240
+ value.complete ===
241
+ (value.omittedMessageCount === 0 && value.omittedTextChars === 0)
242
+ );
243
+ }
244
+
245
+ function nullableStringValue(value) {
246
+ return value === null || typeof value === "string";
247
+ }
248
+
249
+ function nullableNonNegative(value) {
250
+ return value === null || (Number.isFinite(value) && value >= 0);
251
+ }
252
+
253
+ function validSynthesisReceipt(value) {
254
+ const providerKeys = [
255
+ "responseProvider",
256
+ "responseProviderSource",
257
+ "responseModel",
258
+ "responseModelSource",
259
+ "responseId",
260
+ "responseIdSource",
261
+ "requestId",
262
+ "requestIdSource",
263
+ ];
264
+ const usageKeys = [
265
+ "inputTokens",
266
+ "outputTokens",
267
+ "totalTokens",
268
+ "cacheReadTokens",
269
+ "cacheWriteTokens",
270
+ "reasoningTokens",
271
+ "estimatedCostUsd",
272
+ ];
273
+ return (
274
+ exactKeys(value, [
275
+ "schemaVersion",
276
+ "kind",
277
+ "outcome",
278
+ "requestedModel",
279
+ "timing",
280
+ "provider",
281
+ "usage",
282
+ ]) &&
283
+ value.schemaVersion === 1 &&
284
+ value.kind === "rarebit_model_synthesis" &&
285
+ nonEmptyString(value.outcome) &&
286
+ validModel(value.requestedModel) &&
287
+ exactKeys(value.timing, [
288
+ "startedAt",
289
+ "completedAt",
290
+ "durationMs",
291
+ "provenance",
292
+ ]) &&
293
+ nullableStringValue(value.timing.startedAt) &&
294
+ nullableStringValue(value.timing.completedAt) &&
295
+ nullableNonNegative(value.timing.durationMs) &&
296
+ value.timing.provenance === "local_monotonic_clock" &&
297
+ exactKeys(value.provider, providerKeys) &&
298
+ providerKeys.every((key) => nullableStringValue(value.provider[key])) &&
299
+ exactKeys(value.usage, ["availability", ...usageKeys, "provenance"]) &&
300
+ ["unavailable", "partial", "reported"].includes(value.usage.availability) &&
301
+ usageKeys.every((key) => nullableNonNegative(value.usage[key])) &&
302
+ exactKeys(value.usage.provenance, usageKeys) &&
303
+ usageKeys.every((key) => nullableStringValue(value.usage.provenance[key]))
304
+ );
305
+ }
306
+
307
+ function validAutomaticSummaryPolicy(value) {
308
+ return (
309
+ exactKeys(value, [
310
+ "contractVersion",
311
+ "decision",
312
+ "queryStatus",
313
+ "queryId",
314
+ "provider",
315
+ "reason",
316
+ "observedAt",
317
+ "validUntil",
318
+ "queriedAt",
319
+ "provenance",
320
+ ]) &&
321
+ value.contractVersion === "rarebit-automatic-summary-policy/1" &&
322
+ value.decision === "inhibit" &&
323
+ value.queryStatus === "inhibited" &&
324
+ [
325
+ value.queryId,
326
+ value.provider,
327
+ value.reason,
328
+ value.observedAt,
329
+ value.validUntil,
330
+ value.queriedAt,
331
+ ].every(nonEmptyString) &&
332
+ exactKeys(value.provenance, ["identity", "generation", "association"]) &&
333
+ [
334
+ value.provenance.identity,
335
+ value.provenance.generation,
336
+ value.provenance.association,
337
+ ].every(nonEmptyString)
338
+ );
339
+ }
340
+
341
+ /** Validate the exact schema-v4 Summary receipt without consulting prose. */
342
+ export function validateRarebitArtifactReceipt(record) {
343
+ const common = [
344
+ "schemaVersion",
345
+ "type",
346
+ "status",
347
+ "jobId",
348
+ "sessionId",
349
+ "branch",
350
+ "observedAt",
351
+ "selection",
352
+ "lifecycleBoundary",
353
+ "implementationVersion",
354
+ "synthesisMode",
355
+ "inputCoveragePolicy",
356
+ "promptVersion",
357
+ "model",
358
+ "modelProvenance",
359
+ ];
360
+ const hasInputCoverage = Object.hasOwn(record ?? {}, "inputCoverage");
361
+ const variantKeys = {
362
+ ok: [
363
+ ...(hasInputCoverage ? ["inputCoverage"] : []),
364
+ "summary",
365
+ "sessionStatus",
366
+ "statusReason",
367
+ "synthesis",
368
+ ],
369
+ ineligible: [],
370
+ inhibited: ["automaticSummaryPolicy"],
371
+ unavailable_overflow: ["overflow"],
372
+ failure: ["retryable", "error"],
373
+ };
374
+ if (
375
+ !plainObject(record) ||
376
+ !TERMINAL_STATUSES.has(record.status) ||
377
+ !exactKeys(record, [...common, ...variantKeys[record.status]]) ||
378
+ record.type !== "rarebit_summary" ||
379
+ record.schemaVersion !== 4 ||
380
+ !sha256String(record.jobId) ||
381
+ !nonEmptyString(record.sessionId) ||
382
+ ![
383
+ "hc-rarebit-summary-v4",
384
+ "hc-rarebit-summary-v5",
385
+ "hc-rarebit-summary-v6",
386
+ ].includes(
387
+ record.implementationVersion,
388
+ ) ||
389
+ !nonEmptyString(record.promptVersion) ||
390
+ !validBranch(record.branch) ||
391
+ !validSelection(record.selection, {
392
+ request: record.lifecycleBoundary === "owner_request",
393
+ compact: true,
394
+ }) ||
395
+ !RAREBIT_SUMMARY_RECEIPT_LIFECYCLE_BOUNDARIES.includes(
396
+ record.lifecycleBoundary,
397
+ ) ||
398
+ !nonEmptyString(record.observedAt) ||
399
+ !Number.isFinite(Date.parse(record.observedAt)) ||
400
+ !["forced", "automatic"].includes(record.synthesisMode) ||
401
+ !validInputCoveragePolicy(record.inputCoveragePolicy) ||
402
+ !validModel(record.model) ||
403
+ !validModelProvenance(record.modelProvenance)
404
+ )
405
+ return { valid: false, reason: "malformed" };
406
+
407
+ if (
408
+ (hasInputCoverage && !validInputCoverage(record.inputCoverage)) ||
409
+ (record.status === "ok" &&
410
+ ["hc-rarebit-summary-v5", "hc-rarebit-summary-v6"].includes(
411
+ record.implementationVersion,
412
+ ) &&
413
+ !hasInputCoverage)
414
+ )
415
+ return { valid: false, reason: "malformed" };
416
+
417
+ if (
418
+ record.status === "inhibited" &&
419
+ !validAutomaticSummaryPolicy(record.automaticSummaryPolicy)
420
+ )
421
+ return { valid: false, reason: "malformed" };
422
+ if (record.status === "failure") {
423
+ if (typeof record.retryable !== "boolean" || !validError(record.error))
424
+ return { valid: false, reason: "malformed" };
425
+ return { valid: true, record };
426
+ }
427
+ if (record.status === "unavailable_overflow") {
428
+ if (!validOverflow(record.overflow))
429
+ return { valid: false, reason: "malformed" };
430
+ return { valid: true, record };
431
+ }
432
+ if (record.status !== "ok") return { valid: true, record };
433
+ if (
434
+ typeof record.summary !== "string" ||
435
+ !record.summary.trim() ||
436
+ !validSynthesisReceipt(record.synthesis)
437
+ )
438
+ return { valid: false, reason: "malformed" };
439
+ const legal = RAREBIT_SESSION_STATUS_REASONS[record.sessionStatus]?.includes(
440
+ record.statusReason,
441
+ );
442
+ const owner = record.lifecycleBoundary === "owner_request";
443
+ if (
444
+ !legal ||
445
+ (owner &&
446
+ (record.sessionStatus !== "user_requested" ||
447
+ record.statusReason !== "owner_request_recorded")) ||
448
+ (!owner && record.sessionStatus === "user_requested")
449
+ )
450
+ return { valid: false, reason: "malformed" };
451
+ return { valid: true, record };
452
+ }
453
+
454
+ export function validateRarebitTitleReceipt(record) {
455
+ const common = [
456
+ "schemaVersion",
457
+ "type",
458
+ "status",
459
+ "jobId",
460
+ "implementationVersion",
461
+ "sessionId",
462
+ "branch",
463
+ "selectionManifestHash",
464
+ "titleEvidence",
465
+ "promptVersion",
466
+ "model",
467
+ "modelProvenance",
468
+ "applicationMode",
469
+ "priorTitle",
470
+ "title",
471
+ "observedAt",
472
+ ];
473
+ const resultKeys =
474
+ record?.status === "failure" ? ["retryable", "error"] : ["synthesis"];
475
+ if (
476
+ !plainObject(record) ||
477
+ ![
478
+ "proposal",
479
+ "applied",
480
+ "skipped_session_changed",
481
+ "skipped_title_changed",
482
+ "skipped_existing_title",
483
+ "failure",
484
+ ].includes(record.status) ||
485
+ !exactKeys(record, [...common, ...resultKeys]) ||
486
+ record.schemaVersion !== 4 ||
487
+ record.type !== "rarebit_title" ||
488
+ !sha256String(record.jobId) ||
489
+ record.implementationVersion !== "hc-rarebit-title-v4" ||
490
+ !nonEmptyString(record.sessionId) ||
491
+ !validBranch(record.branch) ||
492
+ !sha256String(record.selectionManifestHash) ||
493
+ !exactKeys(record.titleEvidence, ["provenance", "sourceEntryId"]) ||
494
+ !nonEmptyString(record.titleEvidence.provenance) ||
495
+ !(
496
+ record.titleEvidence.sourceEntryId === null ||
497
+ nonEmptyString(record.titleEvidence.sourceEntryId)
498
+ ) ||
499
+ !nonEmptyString(record.promptVersion) ||
500
+ !validModel(record.model) ||
501
+ !validModelProvenance(record.modelProvenance) ||
502
+ !["apply", "proposal"].includes(record.applicationMode) ||
503
+ !(record.priorTitle === null || typeof record.priorTitle === "string") ||
504
+ !nonEmptyString(record.observedAt) ||
505
+ !Number.isFinite(Date.parse(record.observedAt))
506
+ )
507
+ return { valid: false, reason: "malformed" };
508
+ if (record.status === "failure") {
509
+ if (
510
+ record.title !== null ||
511
+ typeof record.retryable !== "boolean" ||
512
+ !validError(record.error)
513
+ )
514
+ return { valid: false, reason: "malformed" };
515
+ return { valid: true, record };
516
+ }
517
+ const titleExpected =
518
+ record.status === "proposal" || record.status === "applied";
519
+ if (
520
+ (titleExpected ? !nonEmptyString(record.title) : record.title !== null) ||
521
+ !validSynthesisReceipt(record.synthesis)
522
+ )
523
+ return { valid: false, reason: "malformed" };
524
+ return { valid: true, record };
525
+ }
526
+
527
+ function prefixManifest(selection, count) {
528
+ if (
529
+ !Array.isArray(selection?.occurrences) ||
530
+ !Array.isArray(selection?.payloads)
531
+ )
532
+ return null;
533
+ const occurrences = selection.occurrences
534
+ .slice(0, count)
535
+ .map(({ text, ...value }) => value);
536
+ const ids = new Set(occurrences.map((occurrence) => occurrence.occurrenceId));
537
+ const payloads = selection.payloads
538
+ .map(({ text, ...payload }) => ({
539
+ ...payload,
540
+ occurrenceIds: payload.occurrenceIds.filter((id) => ids.has(id)),
541
+ }))
542
+ .filter((payload) => payload.occurrenceIds.length);
543
+ return sha256({
544
+ selectorVersion:
545
+ selection.selectorVersion ?? selection.manifest?.selectorVersion,
546
+ occurrences,
547
+ payloads,
548
+ });
549
+ }
550
+
551
+ export function exactSelectionApplies(receiptSelection, selection) {
552
+ return (
553
+ receiptSelection?.manifestHash === selection?.manifestHash &&
554
+ receiptSelection?.selectorVersion ===
555
+ (selection?.selectorVersion ?? selection?.manifest?.selectorVersion)
556
+ );
557
+ }
558
+ export function requestPrefixApplies(receiptSelection, selection) {
559
+ if (
560
+ receiptSelection?.selectorVersion !==
561
+ (selection?.selectorVersion ?? selection?.manifest?.selectorVersion)
562
+ )
563
+ return false;
564
+ const count = receiptSelection?.occurrenceCount;
565
+ return (
566
+ Number.isInteger(count) &&
567
+ count <= selection.occurrences.length &&
568
+ prefixManifest(selection, count) === receiptSelection.manifestHash &&
569
+ selection.occurrences
570
+ .filter(
571
+ (occurrence) =>
572
+ occurrence.role === "user" && occurrence.outcome === "user",
573
+ )
574
+ .at(-1)?.sourceEntryId === receiptSelection.latestUserSourceEntryId
575
+ );
576
+ }
577
+
578
+ function newerThan(left, right) {
579
+ if (!right) return true;
580
+ const leftAt = timestamp(left.record.observedAt);
581
+ const rightAt = timestamp(right.record.observedAt);
582
+ return leftAt > rightAt || (leftAt === rightAt && left.index > right.index);
583
+ }
584
+
585
+ function newest(records) {
586
+ return records.reduce(
587
+ (best, candidate) =>
588
+ !best || newerThan(candidate, best) ? candidate : best,
589
+ null,
590
+ );
591
+ }
592
+
593
+ function retry(reason, deadlineExpired) {
594
+ return { recommended: !deadlineExpired, reason, deadlineExpired };
595
+ }
596
+
597
+ function result({
598
+ syncState,
599
+ projection: projected = null,
600
+ applicability = "none",
601
+ nativeRef = null,
602
+ receiptRef = null,
603
+ retry: retryValue = null,
604
+ } = {}) {
605
+ return {
606
+ syncState,
607
+ projection: projected,
608
+ applicability,
609
+ nativeRef,
610
+ receiptRef,
611
+ retry: retryValue,
612
+ };
613
+ }
614
+
615
+ function terminalError(reason, input, receipt = null) {
616
+ return result({
617
+ syncState: "terminal_error",
618
+ projection: { status: "error", reason, assessmentRef: ref(receipt) },
619
+ nativeRef: nativeReference(input.native),
620
+ receiptRef: ref(receipt),
621
+ retry: retry(reason, true),
622
+ });
623
+ }
624
+
625
+ function nativeReference(native) {
626
+ return {
627
+ availability: native?.availability ?? "missing",
628
+ sessionId: native?.sessionId ?? null,
629
+ selectionManifestHash: native?.selection?.manifestHash ?? null,
630
+ };
631
+ }
632
+
633
+ /**
634
+ * Compose native active-branch evidence and append-only materialization history.
635
+ * The result is deliberately a producer projection: consumers do not walk
636
+ * selection ancestry, inspect receipt prose, or infer artifact identity.
637
+ */
638
+ export function projectRarebitArtifactState({
639
+ native = { availability: "missing" },
640
+ materialization = { availability: "missing", records: [] },
641
+ expectation = "snapshot",
642
+ deadlineExpired = false,
643
+ } = {}) {
644
+ if (
645
+ !AVAILABILITY.has(native.availability) ||
646
+ !AVAILABILITY.has(materialization.availability)
647
+ )
648
+ throw new TypeError(
649
+ "native and materialization availability must be available, missing, or unreadable",
650
+ );
651
+ if (!EXPECTATIONS.has(expectation))
652
+ throw new TypeError("Unsupported Rarebit artifact expectation");
653
+ const records = Array.isArray(materialization.records)
654
+ ? materialization.records
655
+ : [];
656
+ const parsed = records.map((record, index) => ({
657
+ record,
658
+ index,
659
+ ...validateRarebitArtifactReceipt(record),
660
+ }));
661
+ const valid = parsed.filter((candidate) => candidate.valid);
662
+ const invalidReason = parsed.some(
663
+ ({ record, valid: isValid }) =>
664
+ !isValid &&
665
+ record?.type === "rarebit_summary" &&
666
+ record.schemaVersion !== 4,
667
+ )
668
+ ? "unsupported"
669
+ : parsed.some(
670
+ ({ record, valid: isValid }) =>
671
+ !isValid && record?.type === "rarebit_summary",
672
+ )
673
+ ? "malformed"
674
+ : null;
675
+ const sidecarSessionIds = new Set(
676
+ records
677
+ .filter(
678
+ (record) =>
679
+ record?.type === "rarebit_summary" &&
680
+ typeof record.sessionId === "string" &&
681
+ record.sessionId,
682
+ )
683
+ .map((record) => record.sessionId),
684
+ );
685
+ if (sidecarSessionIds.size > 1)
686
+ return terminalError("session_conflict", { native });
687
+ const sidecarSessionId = [...sidecarSessionIds][0] ?? null;
688
+
689
+ const materializationUnreadable =
690
+ materialization.availability === "unreadable";
691
+
692
+ const nativeAvailable = native.availability === "available";
693
+ if (
694
+ nativeAvailable &&
695
+ (!native.sessionId || !validSelection(native.selection))
696
+ )
697
+ return terminalError("native_malformed", { native });
698
+ if (
699
+ nativeAvailable &&
700
+ sidecarSessionId &&
701
+ sidecarSessionId !== native.sessionId
702
+ )
703
+ return terminalError("session_conflict", { native });
704
+
705
+ if (!nativeAvailable) {
706
+ const candidates = valid.filter(({ record }) =>
707
+ expectation === "owner_request"
708
+ ? record.lifecycleBoundary === "owner_request"
709
+ : expectation === "agent_settled"
710
+ ? record.lifecycleBoundary === "agent_settled"
711
+ : true,
712
+ );
713
+ const chosen = newest(candidates);
714
+ const opposing = newest(
715
+ valid.filter(({ record }) =>
716
+ expectation === "owner_request"
717
+ ? record.lifecycleBoundary === "agent_settled"
718
+ : expectation === "agent_settled"
719
+ ? record.lifecycleBoundary === "owner_request"
720
+ : false,
721
+ ),
722
+ );
723
+ if (chosen && (expectation === "snapshot" || newerThan(chosen, opposing))) {
724
+ const state =
725
+ chosen.record.lifecycleBoundary === "owner_request"
726
+ ? "request_source_pending"
727
+ : "assessment_source_pending";
728
+ return result({
729
+ syncState: state,
730
+ projection: projection(chosen.record),
731
+ applicability:
732
+ chosen.record.lifecycleBoundary === "owner_request"
733
+ ? "request_cut"
734
+ : "materialization_only",
735
+ nativeRef: nativeReference(native),
736
+ receiptRef: ref(chosen.record),
737
+ retry: retry("native_source_pending", deadlineExpired),
738
+ });
739
+ }
740
+ if (!deadlineExpired)
741
+ return result({
742
+ syncState: "awaiting_artifacts",
743
+ nativeRef: nativeReference(native),
744
+ retry: retry(
745
+ materializationUnreadable
746
+ ? "materialization_unreadable"
747
+ : native.availability === "unreadable"
748
+ ? "native_unreadable"
749
+ : "native_missing",
750
+ false,
751
+ ),
752
+ });
753
+ return terminalError(
754
+ invalidReason ??
755
+ (materializationUnreadable
756
+ ? "materialization_unreadable"
757
+ : native.availability === "unreadable"
758
+ ? "native_unreadable"
759
+ : "native_missing"),
760
+ { native },
761
+ );
762
+ }
763
+
764
+ const current = valid.filter(
765
+ ({ record }) => record.sessionId === native.sessionId,
766
+ );
767
+ const exact = current.filter(({ record }) =>
768
+ exactSelectionApplies(record.selection, native.selection),
769
+ );
770
+ const requests = current.filter(
771
+ ({ record }) =>
772
+ record.lifecycleBoundary === "owner_request" &&
773
+ requestPrefixApplies(record.selection, native.selection),
774
+ );
775
+ const activeRequest = newest(requests);
776
+ const settled = newest(
777
+ exact.filter(({ record }) => record.lifecycleBoundary === "agent_settled"),
778
+ );
779
+ if (settled && (!activeRequest || newerThan(settled, activeRequest)))
780
+ return result({
781
+ syncState: "assessment_current",
782
+ projection: projection(settled.record),
783
+ applicability: "exact_selection",
784
+ nativeRef: nativeReference(native),
785
+ receiptRef: ref(settled.record),
786
+ retry: null,
787
+ });
788
+ if (activeRequest) {
789
+ const cutLength = activeRequest.record.selection.occurrenceCount;
790
+ const settlementPending = native.selection.occurrences
791
+ .slice(cutLength)
792
+ .some(
793
+ (occurrence) =>
794
+ occurrence.role === "assistant" && occurrence.outcome === "stop",
795
+ );
796
+ return result({
797
+ syncState: settlementPending ? "settlement_pending" : "request_current",
798
+ projection: projection(activeRequest.record),
799
+ applicability: "request_generation",
800
+ nativeRef: nativeReference(native),
801
+ receiptRef: ref(activeRequest.record),
802
+ retry: settlementPending
803
+ ? retry("settlement_pending", deadlineExpired)
804
+ : null,
805
+ });
806
+ }
807
+ const assessment = newest(
808
+ exact.filter(({ record }) => record.lifecycleBoundary !== "owner_request"),
809
+ );
810
+ if (assessment)
811
+ return result({
812
+ syncState: "assessment_current",
813
+ projection: projection(assessment.record),
814
+ applicability: "exact_selection",
815
+ nativeRef: nativeReference(native),
816
+ receiptRef: ref(assessment.record),
817
+ retry: null,
818
+ });
819
+ if (!deadlineExpired)
820
+ return result({
821
+ syncState: "awaiting_artifacts",
822
+ nativeRef: nativeReference(native),
823
+ retry: retry(
824
+ materializationUnreadable
825
+ ? "materialization_unreadable"
826
+ : "materialization_pending",
827
+ false,
828
+ ),
829
+ });
830
+ return terminalError(
831
+ invalidReason ??
832
+ (materializationUnreadable
833
+ ? "materialization_unreadable"
834
+ : materialization.availability === "missing"
835
+ ? "materialization_missing"
836
+ : "settlement_timeout"),
837
+ { native },
838
+ );
839
+ }