diff --git a/.tickets/tex-nak8.md b/.tickets/tex-nak8.md
index 1aedb36..17656aa 100644
--- a/.tickets/tex-nak8.md
+++ b/.tickets/tex-nak8.md
@@ -35,3 +35,10 @@ Episode 000003 adversarial projection found one further current-v2 gap: use_focu
**2026-08-01T23:52:53Z**
Episode 000003 now projects its exact 10,133,930-byte two-source prefix set as 212 source events: 210 readable resident turns plus two structural roots, with zero unsupported events, zero nonconforming lines, and zero unsupported_* diagnostics. Focused test 26/26; full 212 pass/1 scale skip; lint and production build pass. Lync upstream ownership is tracked as lyn-cnew.
+
+**2026-08-02T08:21:50Z**
+
+Removed Textile's now-redundant Behold v2 presenter overlay. Textile now consumes
+the checksum-pinned Lync package from canonical commit `6e0734d` directly. Exact
+episode 000014 projects all 692 source events with no unsupported or unclaimed
+event; Textile retains only its reader, grouping, and storage responsibilities.
diff --git a/.tickets/tex-q3ad.md b/.tickets/tex-q3ad.md
new file mode 100644
index 0000000..36397f4
--- /dev/null
+++ b/.tickets/tex-q3ad.md
@@ -0,0 +1,30 @@
+---
+id: tex-q3ad
+status: closed
+deps: []
+links: []
+created: 2026-08-02T10:15:37Z
+type: task
+priority: 0
+assignee: deepfates
+external-ref: beh-wo5b
+tags: [behold, lync, reader]
+---
+# Read Behold null cognition as a private-life event
+
+Consume Lync's additive behold.entity-cognition-turn.v1 presenter so Textile shows admitted perception plus the resident's explicit no-bodily-action choice without fabricating an action or outcome.
+
+## Acceptance Criteria
+
+The checksum-pinned Lync candidate is updated, raw and indexed resident reading use the shared presenter, a focused Textile regression retains source identity and absent consequence fields, and full verify passes.
+
+
+## Notes
+
+**2026-08-02T10:15:42Z**
+
+Vendored exact Lync 860aa549... archive dad3cff0..., updated provenance/docs, and focused raw reader regression passes 28/28. Pending full Textile verify.
+
+**2026-08-02T10:16:18Z**
+
+Full Textile verify passes: lint, 228 tests plus one opt-in scale skip, and both production builds. The installed physical package is the exact checksum-pinned Lync candidate.
diff --git a/.tickets/tex-wrif.md b/.tickets/tex-wrif.md
index 53cbaae..649277c 100644
--- a/.tickets/tex-wrif.md
+++ b/.tickets/tex-wrif.md
@@ -53,3 +53,9 @@ Principal independent review on 2026-08-01: code and ownership design are accept
**2026-08-02T03:03:00Z**
2026-08-02 Behold episode 000003 ordinary browser check against current main: exact manifest is 2 sources / 20,556,307 bytes / digest 05307f02... The Stories drawer pointer Import Archive input accepts only one file; choosing a ZIP of the exact manifest + two Lync sources routes to the Twitter archive parser and fails for missing data/manifest.js. The intended hidden multiple input could not be opened through the keyboard/gamepad Import Archive action in the in-app browser, even though current source has the action. Therefore this exact retained source set is not yet claimed readable through the ordinary UI. Single-file historical Textile reading remains real. Candidate diagnosis is front-door multi-file activation/archive dispatch, distinct from the indexed-union scale work; reproduce before changing it.
+
+2026-08-02 exact browser correction on current main: the ordinary visible multi-file picker does admit the Behold manifest plus both canonical resident files, but the first episode-000012 attempt failed because WebKit supplied a 1,114,112-byte native Blob chunk to Lync's explicitly 1,048,576-byte indexed admission boundary. The browser File adapter now splits arbitrary native chunks into at-most-1-MiB pieces without changing, copying, or weakening the authenticated prefix. A focused regression supplies the observed oversized shape. The repaired visible local-only reader then opened exact episode 000014: 56,638,047 authenticated bytes, 692 source events, 690 readable turns, two structural roots, all presented, zero nonconforming events. Navigating the real Lark life exposed the 1h23m wall-clock gap after turn 228 rather than presenting stop/resume as one scene. Full verify is 227 pass / one opt-in scale skip plus lint and production builds. This crosses the browser front-door defect and current 56 MB checkpoint; the ticket remains open for its stated representative retained six-hour-scale browser exercise rather than promoting the existing synthetic projector gate.
+
+**2026-08-02T11:17:02Z**
+
+2026-08-02 pinned Lync candidate 0a511b6 adds truthful place_held_against_focus presentation. The exact episode-000019 ordered-prefix front door imported 68,444,160 authenticated bytes: 871 source events, 869 readable, two structural, zero unsupported diagnostics, 12,278 explicit source-only/withheld diagnostics. Full Textile verify passes (228 pass, one opt-in scale skip, lint, production builds). This repairs presentation completeness but does not close the ticket's retained six-hour browser exercise.
diff --git a/README.md b/README.md
index 8c923c9..5c1d3e4 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
> **Status:** private application. Version 0.1.0 is the first coherent source
> release candidate for the raw-Lync corpus workflow and UI; it is not an npm
> package. Textile supports `@deepfates/lync >=0.3.0 <0.5.0`; this checkout pins
-> the exact packed Lync 0.4.3 indexed-union candidate documented in
+> the exact packed Lync 0.4.3 indexed-union and presentation candidate documented in
> `vendor/LYNC-PROVENANCE.md` pending an owner-authorized registry release.
Textile is a tactile multiverse reader for branching stories, conversations,
@@ -120,6 +120,10 @@ O(events + edges + public presentation text), not O(source bytes). The ordinary
multi-file picker reports resident/source/readable/structural/diagnostic counts
and rejects changed, truncated, conflicting, dangling, or cyclic input. Existing
single-file raw imports keep their eager, lossless archive/export behavior.
+Source event times remain attached to their readable turns; when adjacent turns
+are at least five minutes apart, the story surface marks the elapsed interval so
+a stopped or otherwise discontinuous life is not presented as one continuous
+scene. The marker describes time only and does not invent an episode boundary.
Readable events above one MiB of text use an explicit bounded reader with
FIRST/PREV/NEXT/LAST 65,536-character windows. This is presentation
@@ -127,10 +131,11 @@ virtualization, not truncation: the exact complete source string remains in the
story model, full-tree JSON, and kept-context export paths.
Textile reads raw corpus events through Lync's non-mutating, kind/profile-aware
-presentation contract. The same contract is consumed by Splice and Curare; its
-checksum-pinned local candidate keeps this private app installable before Lync
-0.4 publication. For Behold `org.behold.inhabitant.v1`, it presents the safe
-resident perception/action/outcome allowlist, preserves the original source
+presentation contract. The same contract is consumed by Splice and Curare; the
+checksum-pinned Lync package keeps this private app installable before Lync 0.4
+publication. For Behold `org.behold.inhabitant.v1` and `.v2`, it presents the safe
+resident perception/action/outcome allowlist plus explicit non-action cognition,
+preserves the original source
events for full-tree export, and fails closed for unknown nested shapes. See
[the Behold resident Lync presentation boundary](docs/behold-resident-lync-presentation.md).
diff --git a/bun.lock b/bun.lock
index 74b2761..105aff2 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,7 +5,7 @@
"name": "textile",
"dependencies": {
"@ax-llm/ax": "^14.0.39",
- "@deepfates/lync": "file:vendor/deepfates-lync-0.4.3-indexed-union.tgz",
+ "@deepfates/lync": "file:vendor/deepfates-lync-0.4.3-0a511b6.tgz",
"@tailwindcss/postcss": "^4.1.17",
"@types/d3-flextree": "^2.1.4",
"@types/express": "^4.17.21",
@@ -250,7 +250,7 @@
"@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="],
- "@deepfates/lync": ["@deepfates/lync@vendor/deepfates-lync-0.4.3-indexed-union.tgz", { "bin": { "lync": "bin/lync.js" } }],
+ "@deepfates/lync": ["@deepfates/lync@vendor/deepfates-lync-0.4.3-0a511b6.tgz", { "bin": { "lync": "bin/lync.js" } }],
"@emotion/is-prop-valid": ["@emotion/is-prop-valid@1.4.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0" } }, "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw=="],
diff --git a/client/interface/components/StoryText.tsx b/client/interface/components/StoryText.tsx
index 0e50620..a2e6e50 100644
--- a/client/interface/components/StoryText.tsx
+++ b/client/interface/components/StoryText.tsx
@@ -23,6 +23,17 @@ interface StoryTextProps {
}
const STORY_PATH_WINDOW_SIZE = 48;
+const STORY_TIME_SEAM_MS = 5 * 60 * 1000;
+
+function elapsedTimeLabel(elapsedMs: number): string {
+ const totalMinutes = Math.floor(elapsedMs / 60_000);
+ const days = Math.floor(totalMinutes / (24 * 60));
+ const hours = Math.floor((totalMinutes % (24 * 60)) / 60);
+ const minutes = totalMinutes % 60;
+ if (days > 0) return `${days}d${hours > 0 ? ` ${hours}h` : ""} later`;
+ if (hours > 0) return `${hours}h${minutes > 0 ? ` ${minutes}m` : ""} later`;
+ return `${minutes}m later`;
+}
interface StoryNodeProseProps {
text: string;
@@ -215,6 +226,14 @@ export function StoryText({
{visiblePath.map((segment, visibleIndex) => {
const index = windowStart + visibleIndex;
const previous = currentPath[index - 1];
+ const elapsedMs =
+ previous?.createdAt !== undefined && segment.createdAt !== undefined
+ ? segment.createdAt - previous.createdAt
+ : null;
+ const timeSeam =
+ showTurnBoundaries && elapsedMs !== null && elapsedMs >= STORY_TIME_SEAM_MS
+ ? elapsedTimeLabel(elapsedMs)
+ : null;
const seam =
!showTurnBoundaries && previous
? storySeam(previous.text, segment.text)
@@ -253,6 +272,14 @@ export function StoryText({
data-node-id={segment.id}
data-source-text-length={segment.text.length}
>
+ {timeSeam ? (
+
+ {timeSeam}
+
+ ) : null}
{seam}
{
expect(render([human, model], 1)).not.toContain("story-turn-boundary");
});
+ it("makes a material source-time discontinuity visible without inventing an episode", () => {
+ const before: StoryNode = {
+ ...unknown,
+ id: "resident-228",
+ text: "Stop worrying.",
+ sourceId: "source-228",
+ createdAt: Date.parse("2026-08-02T06:05:27.978Z"),
+ };
+ const after: StoryNode = {
+ ...unknown,
+ id: "resident-229",
+ text: "Read the first private-life turn.",
+ sourceId: "source-229",
+ createdAt: Date.parse("2026-08-02T07:29:17.641Z"),
+ };
+ const html = render([before, after], 1);
+ expect(html).toContain("1h 23m later");
+ expect(html).toContain(
+ 'title="2026-08-02T06:05:27.978Z → 2026-08-02T07:29:17.641Z"',
+ );
+
+ const continuous = render(
+ [before, { ...after, createdAt: before.createdAt! + 4 * 60_000 }],
+ 1,
+ );
+ expect(continuous).not.toContain("story-time-seam");
+ });
+
it("presents a multi-megabyte turn as an explicit bounded reader window", () => {
const large: StoryNode = {
...unknown,
diff --git a/client/interface/lync/__tests__/orderedLyncSourceSet.test.ts b/client/interface/lync/__tests__/orderedLyncSourceSet.test.ts
index e4fcb4e..a2a453e 100644
--- a/client/interface/lync/__tests__/orderedLyncSourceSet.test.ts
+++ b/client/interface/lync/__tests__/orderedLyncSourceSet.test.ts
@@ -141,6 +141,37 @@ describe("Behold ordered Lync source sets", () => {
.rejects.toThrow(/read-only/);
});
+ it("normalizes browser-native Blob chunks to Lync's indexed admission bound", async () => {
+ const bytes = new Uint8Array(1024 * 1024 + 65_536).fill(7);
+ const file = {
+ name: "wide-browser-chunk.lync",
+ size: bytes.byteLength,
+ slice: () => ({
+ stream: () =>
+ new ReadableStream({
+ start(controller) {
+ controller.enqueue(bytes);
+ controller.close();
+ },
+ }),
+ arrayBuffer: async () => bytes.slice().buffer,
+ }),
+ } as unknown as File;
+ const binding = source(
+ 0,
+ "OxfordWide",
+ "/canonical/OxfordWide/wide-browser-chunk.lync",
+ bytes,
+ "org.behold.inhabitant.v2",
+ );
+ const chunks: Uint8Array[] = [];
+ for await (const chunk of orderedLyncFileSource({ binding, file }).stream()) {
+ chunks.push(chunk);
+ }
+ expect(chunks.map((chunk) => chunk.byteLength)).toEqual([1024 * 1024, 65_536]);
+ expect(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)))).toEqual(Buffer.from(bytes));
+ });
+
it("fails closed on union conflicts and source changes between index and projection", async () => {
const lines = aster.toString("utf8").trimEnd().split("\n");
const conflicting = JSON.parse(lines[1]!);
diff --git a/client/interface/lync/__tests__/rawLync.test.ts b/client/interface/lync/__tests__/rawLync.test.ts
index 5d989a6..2d0bc61 100644
--- a/client/interface/lync/__tests__/rawLync.test.ts
+++ b/client/interface/lync/__tests__/rawLync.test.ts
@@ -590,6 +590,45 @@ describe("raw .lync projection", () => {
.toEqual(raw.trim().split("\n").map((line) => JSON.parse(line)));
});
+ it("reads resident null cognition without fabricating an action outcome", () => {
+ const lines = readFileSync(oxfordResidentV2FixtureUrl, "utf8")
+ .trim()
+ .split("\n")
+ .map((line) => JSON.parse(line));
+ const event = lines[1];
+ const cognition = event.payload.payload;
+ cognition.protocol = "behold.entity-cognition-turn.v1";
+ cognition.id = "OxfordCedar:cognition:7";
+ cognition.utterance = {
+ assistant: { role: "assistant", content: '{"action":null,"arguments":{}}' },
+ };
+ delete cognition.action;
+ delete cognition.outcome;
+ delete cognition.nextObservation;
+
+ const projection = projectRawLyncFile(
+ `${lines.map(JSON.stringify).join("\n")}\n`,
+ "resident-cognition.lync",
+ );
+ expect(projection.readableEventCount).toBe(1);
+ expect(projection.unsupportedEventCount).toBe(0);
+ const beat = projection.snapshot.turns.find(
+ (turn) => turn.meta.sourceKind === "lync/turn",
+ );
+ expect(beat?.payload.text).toContain("OxfordCedar · cognition 7");
+ expect(beat?.payload.text).toContain("OxfordCedar chose no bodily action.");
+ expect(beat?.payload.text).not.toContain("unknown outcome");
+ expect(beat?.meta.sourcePresentationSections?.map((section) => section.role)).toEqual([
+ "perception",
+ "action",
+ ]);
+ const storedTurn = (beat?.meta.sourceEvent?.payload as {
+ payload?: Record;
+ })?.payload;
+ expect(storedTurn?.action).toBeUndefined();
+ expect(storedTurn?.outcome).toBeUndefined();
+ });
+
it("distinguishes resident chat input dispatch from independently observed delivery", () => {
for (const actionName of ["chat", "whisper"] as const) {
const events = readV2FixtureEvents();
diff --git a/client/interface/lync/indexedRawLync.ts b/client/interface/lync/indexedRawLync.ts
index f15350d..edbf58c 100644
--- a/client/interface/lync/indexedRawLync.ts
+++ b/client/interface/lync/indexedRawLync.ts
@@ -26,6 +26,11 @@ export interface IndexedRawLyncProjection extends RawLyncProjection {
ownership: OrderedLyncReviewOwnership;
}
+// Match Lync's default indexed-union admission bound. Browser Blob streams do
+// not promise a particular chunk size and WebKit can hand us a chunk slightly
+// larger than one MiB, so the File adapter—not the corpus—owns normalizing it.
+const INDEXED_LYNC_STREAM_CHUNK_BYTES = 1024 * 1024;
+
/** Browser File adapter that exposes only the manifest-authenticated prefix. */
export function orderedLyncFileSource(source: OrderedLyncSourceFile): ReReadableLyncSource {
const { binding, file } = source;
@@ -46,8 +51,14 @@ export function orderedLyncFileSource(source: OrderedLyncSourceFile): ReReadable
break;
}
const chunk = result.value.subarray(0, remaining);
- supplied += chunk.byteLength;
- yield chunk;
+ for (let start = 0; start < chunk.byteLength; start += INDEXED_LYNC_STREAM_CHUNK_BYTES) {
+ const bounded = chunk.subarray(
+ start,
+ Math.min(chunk.byteLength, start + INDEXED_LYNC_STREAM_CHUNK_BYTES),
+ );
+ supplied += bounded.byteLength;
+ yield bounded;
+ }
if (supplied === binding.endOffset) {
await reader.cancel();
break;
diff --git a/client/interface/lync/rawLyncPresentation.ts b/client/interface/lync/rawLyncPresentation.ts
index 4ebc69a..a491a82 100644
--- a/client/interface/lync/rawLyncPresentation.ts
+++ b/client/interface/lync/rawLyncPresentation.ts
@@ -1,13 +1,14 @@
import type { LyncEventBody } from "@deepfates/lync/events";
import {
- BEHOLD_INHABITANT_PROFILE,
presentLyncEvent,
- resolveLyncPresentationProfiles,
type LyncPresentation,
type LyncPresentationContext,
-} from "../../../vendor/lync-presentation/index.js";
+} from "@deepfates/lync/presentation";
-export { BEHOLD_INHABITANT_PROFILE, resolveLyncPresentationProfiles };
+export {
+ BEHOLD_INHABITANT_PROFILE,
+ resolveLyncPresentationProfiles,
+} from "@deepfates/lync/presentation";
export type {
LyncPresentation as RawLyncPresentation,
@@ -16,7 +17,7 @@ export type {
LyncPresentationRole as RawLyncPresentationRole,
LyncPresentationSection as RawLyncPresentationSection,
LyncPresentationSource as RawLyncPresentationSource,
-} from "../../../vendor/lync-presentation/index.js";
+} from "@deepfates/lync/presentation";
export type RawLyncPresentationContext = LyncPresentationContext;
diff --git a/client/interface/lync/rawLyncPresentationTypes.ts b/client/interface/lync/rawLyncPresentationTypes.ts
index 3b928ea..3fdda11 100644
--- a/client/interface/lync/rawLyncPresentationTypes.ts
+++ b/client/interface/lync/rawLyncPresentationTypes.ts
@@ -5,7 +5,7 @@ export type {
LyncPresentationRole as RawLyncPresentationRole,
LyncPresentationSection as RawLyncPresentationSection,
LyncPresentationSource as RawLyncPresentationSource,
-} from "../../../vendor/lync-presentation/index.js";
+} from "@deepfates/lync/presentation";
/**
* Compact on-Loom form for a presentation section. When a section is exactly
@@ -14,9 +14,9 @@ export type {
* expands this marker back to the public Lync presentation shape.
*/
export type StoredRawLyncPresentationSection =
- | import("../../../vendor/lync-presentation/index.js").LyncPresentationSection
+ | import("@deepfates/lync/presentation").LyncPresentationSection
| {
- role: import("../../../vendor/lync-presentation/index.js").LyncPresentationRole;
+ role: import("@deepfates/lync/presentation").LyncPresentationRole;
sourcePaths: string[];
sameAsTurnText: true;
};
diff --git a/client/interface/lync/storyLoom.ts b/client/interface/lync/storyLoom.ts
index a54e5f6..98e4c99 100644
--- a/client/interface/lync/storyLoom.ts
+++ b/client/interface/lync/storyLoom.ts
@@ -602,9 +602,13 @@ function turnToStoryNode(
return {
id: turn.id,
text,
- // Archive/portable artifacts need their source time for an honest reopen.
- // Keep legacy story projections byte-for-byte shaped as before.
- ...(meta?.archiveSource || meta?.portableTurnId ? { createdAt: turn.createdAt } : {}),
+ // Imported artifacts need their source time for honest reading. In
+ // particular, an indexed resident life may cross a stop/resume boundary
+ // while remaining one causal thread; dropping its event times makes that
+ // discontinuity look like one uninterrupted scene.
+ ...(meta?.archiveSource || meta?.portableTurnId || meta?.sourceId
+ ? { createdAt: turn.createdAt }
+ : {}),
continuations: [],
origin: originFromMeta(meta),
actor: meta?.author,
diff --git a/client/styles/terminal.css b/client/styles/terminal.css
index 3517345..8cff8fb 100644
--- a/client/styles/terminal.css
+++ b/client/styles/terminal.css
@@ -395,6 +395,14 @@ html, body {
margin-top: 0.65rem;
}
+.story-text .story-time-seam {
+ display: block;
+ margin: 0.9rem 0 0.45rem;
+ color: var(--theme-muted);
+ font-size: 0.82em;
+ opacity: 0.82;
+}
+
.story-text .story-path-window-notice {
display: block;
margin: 0.35rem 0 0.65rem;
diff --git a/docs/behold-resident-lync-presentation.md b/docs/behold-resident-lync-presentation.md
index 00c3ce7..0bad9cd 100644
--- a/docs/behold-resident-lync-presentation.md
+++ b/docs/behold-resident-lync-presentation.md
@@ -1,11 +1,11 @@
# Behold resident Lync presentation
Textile presents one declared Behold life profile without changing the source
-Lync. The domain contract is owned by Behold at commit
-`a95491b55da6bb716b858ff88e8cfa7e7429b6f0` in
-`docs/RESIDENT_LYNC_PRESENTATION.md`; Lync implements the exact presenter at
-commit `fee7adc14f0038d9d0be51bd16b930659ffae1f8`, and this file records
-Textile's consumption and the boundary actually exercised here.
+Lync. The domain contract is owned by Behold in
+`docs/RESIDENT_LYNC_PRESENTATION.md`; Lync implements the presenter consumed
+directly here through `@deepfates/lync/presentation`. The exact local package is
+from Lync commit `860aa549727bdec7fda2ba1571c0a6be1c787aa8` and is checksum-bound in
+`vendor/LYNC-PROVENANCE.md`.
## Dispatch
@@ -13,7 +13,8 @@ Lync's presenter runs only for a causally inherited Loom profile of
`org.behold.inhabitant.v1` or `org.behold.inhabitant.v2`. The root must declare
`behold.entity-loom.v1`; each turn must declare both
`behold.entity-turn-link.v1` on the outer `lync/turn` payload and
-`behold.entity-turn.v1` on its nested EntityTurn. The turn's body and action
+`behold.entity-turn.v1` or `behold.entity-cognition-turn.v1` on its nested
+private-life event. The event's body and action
profiles must both be exactly `minecraft-human-semantic-v1`.
There is no recursive text search and no generic fallback after this profile
@@ -22,7 +23,8 @@ action input remains unsupported or receives a named source-path diagnostic.
## Derived reading
-Textile can derive these sections from the ratified allowlist:
+Lync derives these sections from the ratified allowlist; Textile groups and
+renders the returned public sections:
- perceptions before and after the action: HUD condition and inventory,
player-list/chat/event information, visible entity semantics, focus, and
@@ -31,6 +33,10 @@ Textile can derive these sections from the ratified allowlist:
- action name, known action-specific input, source, and kind; and
- success/failure event plus known action-specific outcome evidence.
+An explicit cognition event presents its admitted perception and “chose no
+bodily action.” It has no action, outcome, or terminal observation; the reader
+rejects a cognition record that invents any of those fields.
+
Provider-private reasoning, request/response bodies, transport evidence,
controller state, absolute coordinates, stable hidden identifiers, navigation
conclusions, and arbitrary nested strings are never promoted to prose.
@@ -64,6 +70,9 @@ navigates both source ids in MAP, performs the ordinary full-tree JSON export,
and proves that the exported `sourceEvent` records reproduce those fixture
bytes exactly.
+The exact Behold episode 000014 ordered source set exercises the direct package
+boundary across 692 source events with no unsupported or unclaimed event.
+
This shared presentation contract does not change Keep/export meaning,
Backspace editing, MAP focus, or Textile's first-parent reading path. Typed
additional-parent/pointer navigation and contextual export remain separate
diff --git a/docs/splice-raw-lync-presentations.md b/docs/splice-raw-lync-presentations.md
index fe27c23..f50fe0b 100644
--- a/docs/splice-raw-lync-presentations.md
+++ b/docs/splice-raw-lync-presentations.md
@@ -1,6 +1,6 @@
# Splice raw-Lync presentation matrix
-Ground truth: Lync `fee7adc14f0038d9d0be51bd16b930659ffae1f8`,
+Ground truth: Lync `860aa549727bdec7fda2ba1571c0a6be1c787aa8`,
Splice `ea2ecb0c408590fef7fabe51335765a61831ab5e`, their
presentation/import tests, and Splice's
`splice lync archive|glowfic|ocr|tweet-embed` writers, source adapters, and
diff --git a/package.json b/package.json
index ed62a10..7ac2013 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
},
"dependencies": {
"@ax-llm/ax": "^14.0.39",
- "@deepfates/lync": "file:vendor/deepfates-lync-0.4.3-indexed-union.tgz",
+ "@deepfates/lync": "file:vendor/deepfates-lync-0.4.3-0a511b6.tgz",
"@tailwindcss/postcss": "^4.1.17",
"@types/d3-flextree": "^2.1.4",
"@types/express": "^4.17.21",
diff --git a/vendor/LYNC-PROVENANCE.md b/vendor/LYNC-PROVENANCE.md
index 4325d5e..612e14b 100644
--- a/vendor/LYNC-PROVENANCE.md
+++ b/vendor/LYNC-PROVENANCE.md
@@ -1,14 +1,15 @@
# Vendored Lync package
-`deepfates-lync-0.4.3-indexed-union.tgz` was produced with `pnpm pack`
+`deepfates-lync-0.4.3-0a511b6.tgz` was produced with `pnpm pack`
from the clean `@deepfates/lync` checkout at commit
-`e99bce30fa00fd833f527506d799d09ccfde89e6`.
+`0a511b6f918acf87800bf433f34311ee47a9c48b`.
- Declared package version: `0.4.3`
-- Archive SHA-256: `4295c1e0c7ad368f1b913a755853b733f3bd7a47d10d7cc7bba305e7a534b4a3`
-- Required unpublished surface: `@deepfates/lync/indexed-union`
+- Archive SHA-256: `4de871ce1117cff040fedc069944c3705e314168e01f151ae6d27239f6d1b745`
+- Required unpublished surfaces: `@deepfates/lync/indexed-union` and
+ `@deepfates/lync/presentation`
This exact local package keeps Textile reproducible before an owner-approved
0.4 registry release. It is a dependency boundary, not a fork: union parsing,
-conflict adjudication, topology, suppression, and source-byte verification
-remain owned by Lync.
+conflict adjudication, topology, suppression, source-byte verification, and
+domain presentation remain owned by Lync.
diff --git a/vendor/deepfates-lync-0.4.3-0a511b6.tgz b/vendor/deepfates-lync-0.4.3-0a511b6.tgz
new file mode 100644
index 0000000..79716da
Binary files /dev/null and b/vendor/deepfates-lync-0.4.3-0a511b6.tgz differ
diff --git a/vendor/deepfates-lync-0.4.3-indexed-union.tgz b/vendor/deepfates-lync-0.4.3-indexed-union.tgz
deleted file mode 100644
index 17c1a4d..0000000
Binary files a/vendor/deepfates-lync-0.4.3-indexed-union.tgz and /dev/null differ
diff --git a/vendor/lync-presentation/PROVENANCE.md b/vendor/lync-presentation/PROVENANCE.md
deleted file mode 100644
index 8418ec5..0000000
--- a/vendor/lync-presentation/PROVENANCE.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Vendored Lync presentation candidate
-
-These files come from the immutable local package artifact
-`/Users/deepfates/Hacking/data/artifacts/lync/deepfates-lync-0.4.2.tgz`,
-SHA-256
-`c83b01766b73656a334d49b28e54056472e2b171a981560ded642524fed3aba8`.
-The archive declares `@deepfates/lync` version `0.4.2` and was packed from
-`deepfates/lync` commit
-`0ec1b37db5759c1f8f12a47394a0300925a5bfad`.
-
-Vendored file digests are:
-
-- `index.js` SHA-256 `3471960558021f9ffb9a885d9f56ba1cc20de07a301041fd542952fdecea22c3`
-- `index.d.ts` SHA-256 `d7f523d246181fce6d99e60e7c36021ecda74ae83e8b8847a74e4ac260cc5a33`
-- `presenters/behold-inhabitant.js` SHA-256 `b8af885b3fd80da8ea65a2b01be9473f525520d8a25c80c2d840ed4a3a1a716c`
-
-The corresponding archive members have SHA-256
-`a8411061032cd81b96646c77dfbf629acc6cb02ffebf353aa4287734b6c95d1b`
-(`dist/presentation.js`),
-`dceac3a543c799f5be84a31eab80ac1ae712a0c974f9535925d32da1f3d5842b`
-(`dist/presentation.d.ts`), and
-`e027474eacb6f484ed8c4242a4af622b1879f0cd0e6700504abd772ecf2904e7`
-(`dist/presenters/behold-inhabitant.js`). `index.js` differs only by omission of
-its source-map trailer. The vendored Behold presenter also carries Textile's
-bounded `tex-nak8` reader overlay for four current resident lifecycle event
-shapes, the current world-event wait result, truthful chat/whisper input
-dispatch settlement, focused-use failure presentation, and Lync commit
-`ddace87`'s distinction between received private whispers and public chat; all
-other unknown shapes still fail closed. `index.d.ts` keeps Textile's published-0.3-compatible
-`@deepfates/lync/events` type import and declares the two exported profile
-constants directly.
-
-Lync owns the API, dispatch, presentation pacts, and tests. This exact compiled
-copy keeps the private application independently installable while its lock
-remains on published Lync 0.3. It has no runtime dependencies and performs no
-filesystem, network, DOM, clock, or storage I/O. Source-map trailers and map
-files are omitted.
-
-Regenerate by verifying and extracting that exact package artifact, copying
-`dist/presentation.js` and `dist/presenters/behold-inhabitant.js` with the same
-relative layout, omitting their source-map trailers, then applying the bounded
-reader overlay and declaration-only import adaptation described above. Replace
-this candidate with `@deepfates/lync/presentation` after an owner-authorized
-Lync release contains the same pact; do not add unrelated consumer-local
-presentation heuristics.
diff --git a/vendor/lync-presentation/index.d.ts b/vendor/lync-presentation/index.d.ts
deleted file mode 100644
index b2c35e4..0000000
--- a/vendor/lync-presentation/index.d.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import type { LyncEventBody } from "@deepfates/lync/events";
-export declare const BEHOLD_INHABITANT_PROFILE = "org.behold.inhabitant.v1";
-export declare const BEHOLD_INHABITANT_PROFILE_V2 = "org.behold.inhabitant.v2";
-export type LyncPresentationKind = "content" | "structure";
-export type LyncPresentationRole = "content" | "structure" | "perception" | "utterance" | "action" | "outcome";
-export interface LyncPresentationSection {
- role: LyncPresentationRole;
- text: string;
- /** Exact JSON paths in the source event used for this section. */
- sourcePaths: string[];
-}
-export interface LyncPresentationDiagnostic {
- code: string;
- sourcePath: string;
-}
-export interface LyncPresentationSource {
- id: string;
- parents: string[];
- author: {
- actor: string;
- via?: string;
- };
- kind: string;
-}
-/** A non-mutating readable projection over one exact source event. */
-export interface LyncPresentation {
- text: string;
- kind: LyncPresentationKind;
- contract: string;
- source: LyncPresentationSource;
- sections: LyncPresentationSection[];
- diagnostics: LyncPresentationDiagnostic[];
-}
-export interface LyncPresentationContext {
- /** Exact profile inherited from a causal loom root, when unambiguous. */
- loomProfile?: string;
-}
-export type LyncPresentationResult = {
- status: "presented";
- presentation: LyncPresentation;
-} | {
- status: "unsupported";
- contract: string;
- diagnostics: LyncPresentationDiagnostic[];
-} | {
- status: "unclaimed";
-};
-/**
- * Resolve one source event through exact profile, exact kind, then the small
- * generic text/message pact. A claimed profile or kind never falls through
- * when its payload is malformed: callers receive an explicit unsupported
- * decision instead of plausible prose from an unrelated nested field.
- */
-export declare function presentLyncEvent(event: LyncEventBody, context?: LyncPresentationContext): LyncPresentationResult;
-/**
- * Resolve the one profile inherited through an event's causal parents.
- * Parent order and event identities are untouched. Conflicting inherited
- * profiles deliberately resolve to no profile rather than choosing one.
- */
-export declare function resolveLyncPresentationProfiles(events: Iterable): Map;
-/** Convert source HTML to inert, readable plain text without executing it. */
-export declare function htmlToPlainText(html: string | null): string | null;
diff --git a/vendor/lync-presentation/index.js b/vendor/lync-presentation/index.js
deleted file mode 100644
index e3b996a..0000000
--- a/vendor/lync-presentation/index.js
+++ /dev/null
@@ -1,338 +0,0 @@
-import { BEHOLD_INHABITANT_PROFILE, BEHOLD_INHABITANT_PROFILE_V2, presentBeholdInhabitantEvent, presentBeholdInhabitantEventV2, } from "./presenters/behold-inhabitant.js";
-export { BEHOLD_INHABITANT_PROFILE, BEHOLD_INHABITANT_PROFILE_V2, } from "./presenters/behold-inhabitant.js";
-const splicePresenters = {
- "twitter/tweet": {
- contract: "splice/twitter-archive",
- present: (event) => content(event, firstString(event.payload, ["full_text", "fullText", "text"]), "splice/twitter-archive", firstStringPath(event.payload, ["full_text", "fullText", "text"])),
- },
- "twitter/like": {
- contract: "splice/twitter-archive",
- present: (event) => content(event, firstString(event.payload, ["full_text", "fullText", "text"]), "splice/twitter-archive", firstStringPath(event.payload, ["full_text", "fullText", "text"])),
- },
- "bluesky/post": {
- contract: "splice/bluesky-archive",
- present: (event) => {
- const nested = nestedString(event.payload, ["record", "text"]);
- return content(event, nested ?? stringField(event.payload, "text"), "splice/bluesky-archive", nested !== null ? "payload.record.text" : fieldPath(event.payload, "text"));
- },
- },
- "glowfic/post": {
- contract: "splice/glowfic-json",
- present: (event) => content(event, htmlToPlainText(stringField(event.payload, "content")), "splice/glowfic-json", fieldPath(event.payload, "content")),
- },
- "twitter/tweet-embed": {
- contract: "splice/twitter-embed-cache",
- present: (event) => content(event, htmlToPlainText(nestedString(event.payload, ["embed", "html"])), "splice/twitter-embed-cache", nestedString(event.payload, ["embed", "html"]) !== null
- ? "payload.embed.html"
- : null),
- },
- "ocr/page": {
- contract: "splice/ocr-text-import",
- present: (event) => content(event, stringField(event.payload, "text"), "splice/ocr-text-import", fieldPath(event.payload, "text")),
- },
- "ocr/document": {
- contract: "splice/ocr-text-import",
- present: (event) => content(event, stringField(event.payload, "text"), "splice/ocr-text-import", fieldPath(event.payload, "text")),
- },
- "glowfic/thread": {
- contract: "splice/glowfic-json",
- present: glowficThread,
- },
- "ocr/set": {
- contract: "splice/ocr-text-import",
- present: ocrSet,
- },
-};
-/**
- * Resolve one source event through exact profile, exact kind, then the small
- * generic text/message pact. A claimed profile or kind never falls through
- * when its payload is malformed: callers receive an explicit unsupported
- * decision instead of plausible prose from an unrelated nested field.
- */
-export function presentLyncEvent(event, context = {}) {
- if (context.loomProfile === BEHOLD_INHABITANT_PROFILE) {
- const presentation = presentBeholdInhabitantEvent(event);
- return presentation
- ? normalizePresentation(event, presentation)
- : unsupported("org.behold.presentation.inhabitant-turn.v1", "unsupported_profile_event");
- }
- if (context.loomProfile === BEHOLD_INHABITANT_PROFILE_V2) {
- const presentation = presentBeholdInhabitantEventV2(event);
- return presentation
- ? normalizePresentation(event, presentation)
- : unsupported("org.behold.presentation.inhabitant-turn.v2", "unsupported_profile_event");
- }
- const known = splicePresenters[event.kind];
- if (known) {
- const presentation = known.present(event);
- return presentation
- ? normalizePresentation(event, presentation)
- : unsupported(known.contract, "malformed_known_kind");
- }
- if (event.kind === "lync/pointer") {
- const presentation = lyncPointer(event);
- return presentation
- ? normalizePresentation(event, presentation)
- : unsupported("lync/pointer", "malformed_known_kind");
- }
- const generic = genericPresentation(event);
- return generic ? normalizePresentation(event, generic) : { status: "unclaimed" };
-}
-/**
- * Resolve the one profile inherited through an event's causal parents.
- * Parent order and event identities are untouched. Conflicting inherited
- * profiles deliberately resolve to no profile rather than choosing one.
- */
-export function resolveLyncPresentationProfiles(events) {
- const list = [...events];
- const byId = new Map(list.map((event) => [event.id, event]));
- const resolved = new Map();
- const visiting = new Set();
- const profileFor = (event) => {
- const cached = resolved.get(event.id);
- if (cached !== undefined)
- return cached;
- if (visiting.has(event.id))
- return null;
- visiting.add(event.id);
- let profile = null;
- if (event.kind === "lync/loom") {
- const meta = recordField(event.payload, "meta");
- profile = typeof meta?.profile === "string" ? meta.profile : null;
- }
- else {
- const inherited = new Set();
- for (const parentId of event.parents) {
- const parent = byId.get(parentId);
- if (!parent)
- continue;
- const parentProfile = profileFor(parent);
- if (parentProfile)
- inherited.add(parentProfile);
- }
- if (inherited.size === 1)
- profile = [...inherited][0] ?? null;
- }
- visiting.delete(event.id);
- resolved.set(event.id, profile);
- return profile;
- };
- const result = new Map();
- for (const event of list) {
- const profile = profileFor(event);
- if (profile)
- result.set(event.id, profile);
- }
- return result;
-}
-function normalizePresentation(event, presentation) {
- return {
- status: "presented",
- presentation: {
- ...presentation,
- source: presentation.source ?? presentationSource(event),
- sections: presentation.sections ?? [],
- diagnostics: presentation.diagnostics ?? [],
- },
- };
-}
-function unsupported(contract, code) {
- return {
- status: "unsupported",
- contract,
- diagnostics: [{ code, sourcePath: "payload" }],
- };
-}
-function content(event, text, contract, sourcePath) {
- if (text === null || sourcePath === null || text.length === 0)
- return null;
- return presentation(event, text, "content", contract, "content", [sourcePath]);
-}
-function presentation(event, text, kind, contract, role, sourcePaths) {
- return {
- text,
- kind,
- contract,
- source: presentationSource(event),
- sections: [{ role, text, sourcePaths }],
- diagnostics: [],
- };
-}
-function presentationSource(event) {
- return {
- id: event.id,
- parents: [...event.parents],
- author: {
- actor: event.author.actor,
- ...(typeof event.author.via === "string" ? { via: event.author.via } : {}),
- },
- kind: event.kind,
- };
-}
-function glowficThread(event) {
- const payload = event.payload;
- const title = stringField(payload, "title");
- const id = stringField(payload, "id");
- const authors = stringArray(payload.authors);
- const source = stringField(payload, "url");
- const lines = [
- `Glowfic thread: ${title ?? id ?? "untitled"}`,
- id && title ? `Thread ${id}` : null,
- authors.length ? `Authors: ${authors.join(", ")}` : null,
- source ? `Source: ${source}` : null,
- ].filter((line) => line !== null);
- const paths = [
- title !== null ? "payload.title" : null,
- id !== null ? "payload.id" : null,
- authors.length ? "payload.authors" : null,
- source !== null ? "payload.url" : null,
- ].filter((path) => path !== null);
- return presentation(event, lines.join("\n"), "structure", "splice/glowfic-json", "structure", paths);
-}
-function ocrSet(event) {
- const payload = event.payload;
- const locator = stringField(payload, "locator") ?? "unnamed set";
- const pages = integerField(payload, "pages");
- const documents = Array.isArray(payload.documents) ? payload.documents.length : null;
- const range = recordField(payload, "page_range");
- const min = range ? integerField(range, "min") : null;
- const max = range ? integerField(range, "max") : null;
- const counts = [
- pages === null ? null : `${pages} ${pages === 1 ? "page" : "pages"}`,
- documents === null ? null : `${documents} ${documents === 1 ? "document" : "documents"}`,
- ].filter((value) => value !== null);
- const lines = [
- `OCR set: ${locator}`,
- counts.length ? counts.join(" · ") : null,
- min === null || max === null ? null : `Page range: ${min}–${max}`,
- ].filter((line) => line !== null);
- const paths = [
- stringField(payload, "locator") !== null ? "payload.locator" : null,
- pages !== null ? "payload.pages" : null,
- documents !== null ? "payload.documents" : null,
- min !== null || max !== null ? "payload.page_range" : null,
- ].filter((path) => path !== null);
- return presentation(event, lines.join("\n"), "structure", "splice/ocr-text-import", "structure", paths);
-}
-function lyncPointer(event) {
- const name = stringField(event.payload, "name");
- const target = stringField(event.payload, "target");
- if (!name || !target)
- return null;
- return presentation(event, `Lync pointer: ${name}\nTarget: ${target}`, "structure", "lync/pointer", "structure", ["payload.name", "payload.target"]);
-}
-function genericPresentation(event) {
- const payload = event.payload;
- for (const field of ["text", "full_text", "fullText", "message"]) {
- const direct = stringField(payload, field);
- if (direct !== null) {
- return content(event, direct, "lync/generic-text-v1", `payload.${field}`);
- }
- }
- const message = recordField(payload, "message");
- if (!message)
- return null;
- const messageText = stringField(message, "content");
- if (messageText !== null) {
- return content(event, messageText, "lync/generic-message-v1", "payload.message.content");
- }
- const blocks = message.content;
- if (!Array.isArray(blocks))
- return null;
- const paths = [];
- const text = blocks
- .map((block, index) => {
- const record = recordValue(block);
- const value = record ? stringField(record, "text") : null;
- if (value !== null)
- paths.push(`payload.message.content[${index}].text`);
- return value ?? "";
- })
- .filter(Boolean)
- .join("");
- return text
- ? presentation(event, text, "content", "lync/generic-message-blocks-v1", "content", paths)
- : null;
-}
-/** Convert source HTML to inert, readable plain text without executing it. */
-export function htmlToPlainText(html) {
- if (html === null)
- return null;
- const withoutExecutable = html.replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, "");
- const withBreaks = withoutExecutable
- .replace(/
/gi, "\n")
- .replace(/<\/(p|div|blockquote|li|h[1-6])\s*>/gi, "\n");
- const decoded = decodeHtmlEntities(withBreaks.replace(/<[^>]*>/g, ""));
- const normalized = decoded
- .replace(/\r\n?/g, "\n")
- .replace(/[\t ]+\n/g, "\n")
- .replace(/\n[\t ]+/g, "\n")
- .replace(/[\t ]{2,}/g, " ")
- .replace(/\n{3,}/g, "\n\n")
- .trim();
- return normalized || null;
-}
-function decodeHtmlEntities(value) {
- const named = {
- amp: "&", apos: "'", gt: ">", hellip: "…", ldquo: "“", lsquo: "‘",
- lt: "<", mdash: "—", nbsp: " ", ndash: "–", quot: '"', rdquo: "”", rsquo: "’",
- };
- return value.replace(/&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]+);/gi, (match, entity) => {
- if (entity[0] === "#") {
- const base = entity[1]?.toLowerCase() === "x" ? 16 : 10;
- const digits = base === 16 ? entity.slice(2) : entity.slice(1);
- const codePoint = Number.parseInt(digits, base);
- if (Number.isSafeInteger(codePoint) && codePoint >= 0 && codePoint <= 0x10ffff) {
- return String.fromCodePoint(codePoint);
- }
- return match;
- }
- return named[entity.toLowerCase()] ?? match;
- });
-}
-function firstString(payload, fields) {
- for (const field of fields) {
- const value = stringField(payload, field);
- if (value !== null)
- return value;
- }
- return null;
-}
-function firstStringPath(payload, fields) {
- for (const field of fields) {
- if (stringField(payload, field) !== null)
- return `payload.${field}`;
- }
- return null;
-}
-function fieldPath(payload, field) {
- return stringField(payload, field) !== null ? `payload.${field}` : null;
-}
-function nestedString(payload, path) {
- let value = payload;
- for (const key of path) {
- const record = recordValue(value);
- if (!record)
- return null;
- value = record[key];
- }
- return typeof value === "string" ? value : null;
-}
-function stringField(payload, field) {
- return typeof payload[field] === "string" ? payload[field] : null;
-}
-function integerField(payload, field) {
- const value = payload[field];
- return Number.isInteger(value) ? value : null;
-}
-function stringArray(value) {
- return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
-}
-function recordField(payload, field) {
- return recordValue(payload[field]);
-}
-function recordValue(value) {
- return value !== null && typeof value === "object" && !Array.isArray(value)
- ? value
- : null;
-}
diff --git a/vendor/lync-presentation/presenters/behold-inhabitant.js b/vendor/lync-presentation/presenters/behold-inhabitant.js
deleted file mode 100644
index d98129c..0000000
--- a/vendor/lync-presentation/presenters/behold-inhabitant.js
+++ /dev/null
@@ -1,911 +0,0 @@
-export const BEHOLD_INHABITANT_PROFILE = "org.behold.inhabitant.v1";
-export const BEHOLD_INHABITANT_PROFILE_V2 = "org.behold.inhabitant.v2";
-const V1 = {
- profile: BEHOLD_INHABITANT_PROFILE,
- contract: "org.behold.presentation.inhabitant-turn.v1",
- version: 1,
-};
-const V2 = {
- profile: BEHOLD_INHABITANT_PROFILE_V2,
- contract: "org.behold.presentation.inhabitant-turn.v2",
- version: 2,
-};
-const HUMAN_PROFILE = "minecraft-human-semantic-v1";
-const OBSERVATION_PROTOCOL = "behold.minecraft-human-semantic-observation.v1";
-/**
- * Behold owns the meaning of this exact profile. Lync projects only the
- * allowlisted paths in Behold's v1 presentation pact; it never searches an
- * opaque payload for plausible prose.
- */
-export function presentBeholdInhabitantEvent(event) {
- return presentBeholdEvent(event, V1);
-}
-export function presentBeholdInhabitantEventV2(event) {
- return presentBeholdEvent(event, V2);
-}
-function presentBeholdEvent(event, profile) {
- if (event.kind === "lync/loom")
- return presentResidentLoom(event, profile);
- if (event.kind === "lync/turn")
- return presentResidentTurn(event, profile);
- return null;
-}
-function presentResidentLoom(event, profile) {
- const meta = recordField(event.payload, "meta");
- if (stringField(meta, "protocol") !== "behold.entity-loom.v1" ||
- stringField(meta, "profile") !== profile.profile) {
- return null;
- }
- const entityId = stringField(meta, "entityId");
- const circleId = stringField(meta, "circleId");
- if (!entityId)
- return null;
- const lines = [
- `Behold resident life: ${entityId}`,
- circleId ? `World circle: ${circleId}` : null,
- `Profile: ${profile.profile}`,
- ].filter((line) => line !== null);
- const section = {
- role: "structure",
- text: lines.join("\n"),
- sourcePaths: ["payload.meta"],
- };
- return {
- text: section.text,
- kind: "structure",
- contract: profile.contract,
- source: presentationSource(event),
- sections: [section],
- diagnostics: [],
- };
-}
-function presentResidentTurn(event, profile) {
- const link = recordField(event.payload, "meta");
- const turn = recordField(event.payload, "payload");
- if (!link ||
- !turn ||
- stringField(link, "protocol") !== "behold.entity-turn-link.v1" ||
- stringField(turn, "protocol") !== "behold.entity-turn.v1") {
- return null;
- }
- const profiles = recordField(turn, "profiles");
- if (stringField(profiles, "body") !== HUMAN_PROFILE ||
- stringField(profiles, "actions") !== HUMAN_PROFILE) {
- return null;
- }
- const entityId = stringField(turn, "entityId");
- const sequence = integerField(turn, "sequence");
- const model = stringField(turn, "model");
- if (!entityId || sequence === null || !model)
- return null;
- const diagnostics = [];
- const sections = [];
- const observation = presentObservation(turn.observation, entityId, "payload.payload.observation", diagnostics, profile);
- if (observation)
- sections.push(observation);
- const utterance = presentUtterance(turn.utterance);
- if (utterance)
- sections.push(utterance);
- sections.push(presentAction(turn.action, entityId, diagnostics, profile));
- sections.push(presentOutcome(turn.action, turn.outcome, diagnostics, profile));
- const nextObservation = presentObservation(turn.nextObservation, entityId, "payload.payload.nextObservation", diagnostics, profile);
- if (nextObservation)
- sections.push(nextObservation);
- const release = recordField(turn, "experimentRelease");
- const releaseId = stringField(release, "releaseId");
- const observedOrder = integerField(release, "residentObservedOrder");
- const structure = [
- `${entityId} · turn ${sequence}`,
- `Model: ${model}`,
- `Profiles: ${stringField(profiles, "policy") ?? "unknown"} · ${HUMAN_PROFILE} · ${stringField(profiles, "safety") ?? "unknown"}`,
- releaseId ? `Release: ${releaseId}` : null,
- observedOrder === null ? null : `Resident-observed order: ${observedOrder}`,
- ].filter((line) => line !== null);
- return {
- text: [structure.join("\n"), ...sections.map(sectionText)].join("\n\n"),
- kind: "content",
- contract: profile.contract,
- source: presentationSource(event),
- sections,
- diagnostics,
- };
-}
-function presentationSource(event) {
- return {
- id: event.id,
- parents: [...event.parents],
- author: {
- actor: event.author.actor,
- ...(typeof event.author.via === "string"
- ? { via: event.author.via }
- : {}),
- },
- kind: event.kind,
- };
-}
-function presentObservation(value, entityId, sourcePath, diagnostics, profile) {
- const observation = recordValue(value);
- if (!observation || stringField(observation, "protocol") !== OBSERVATION_PROTOCOL) {
- diagnostics.push({ code: "unsupported_observation_protocol", sourcePath });
- return null;
- }
- const bodyContract = recordField(observation, "bodyContract");
- if (stringField(bodyContract, "profile") !== HUMAN_PROFILE) {
- diagnostics.push({
- code: "unsupported_observation_body_profile",
- sourcePath,
- });
- return null;
- }
- const lines = [];
- const self = recordField(observation, "self");
- const condition = recordField(self, "condition");
- const conditionParts = [
- numberField(condition, "health") === null
- ? null
- : `health ${numberField(condition, "health")}`,
- numberField(condition, "food") === null
- ? null
- : `food ${numberField(condition, "food")}`,
- numberField(condition, "breathBubbles") === null
- ? null
- : `breath ${numberField(condition, "breathBubbles")}`,
- typeof condition?.sleeping === "boolean"
- ? condition.sleeping
- ? "sleeping"
- : "awake"
- : null,
- stringField(condition, "dimension"),
- stringField(condition, "daylight"),
- ].filter((part) => part !== null);
- if (conditionParts.length)
- lines.push(`${entityId}'s condition: ${conditionParts.join(" · ")}.`);
- const inventory = arrayField(self, "inventory").flatMap(presentInventoryItem);
- if (inventory.length)
- lines.push(`Inventory: ${inventory.join(", ")}.`);
- else if (Array.isArray(self?.inventory))
- lines.push("Inventory: empty.");
- const heldItem = presentInventoryItem(self?.heldItem);
- if (heldItem.length)
- lines.push(`Held item: ${heldItem.join(", ")}.`);
- if (observation.eventWindow !== undefined) {
- diagnostics.push({
- code: "source_only_event_window",
- sourcePath: `${sourcePath}.eventWindow`,
- });
- }
- if (self?.pose !== undefined) {
- diagnostics.push({
- code: "source_only_pose",
- sourcePath: `${sourcePath}.self.pose`,
- });
- }
- const scene = recordField(observation, "scene");
- const social = recordField(scene, "social");
- const players = stringArray(social?.playersOnline);
- if (players.length)
- lines.push(`Players online: ${players.join(", ")}.`);
- const entities = arrayField(scene, "entities").flatMap((item, index) => {
- const entity = recordValue(item);
- const name = stringField(entity, "name");
- const kind = stringField(entity, "kind");
- if (!name && !kind)
- return [];
- if (entity?.reference !== undefined) {
- diagnostics.push({
- code: "withheld_observation_local_reference",
- sourcePath: `${sourcePath}.scene.entities[${index}].reference`,
- });
- }
- const details = [
- kind,
- stringField(entity, "proximity"),
- stringField(entity, "relativeDirection"),
- stringField(entity, "visibility"),
- ].filter((detail) => detail !== null);
- return [
- `Saw ${name ?? kind}${details.length ? ` (${details.join(", ")})` : ""}.`,
- ];
- });
- lines.push(...entities);
- const focus = recordField(scene, "focus");
- if (focus) {
- const focusParts = [
- stringField(focus, "name"),
- stringField(focus, "kind"),
- stringField(focus, "material"),
- stringField(focus, "proximity"),
- stringField(focus, "relativeDirection"),
- stringField(focus, "visibility"),
- ].filter((part) => part !== null);
- if (focusParts.length)
- lines.push(`Focus: ${focusParts.join(" · ")}.`);
- if (focus.reference !== undefined) {
- diagnostics.push({
- code: "withheld_observation_local_reference",
- sourcePath: `${sourcePath}.scene.focus.reference`,
- });
- }
- }
- const terrain = recordField(scene, "terrain");
- const visualField = recordField(terrain, "visualField");
- if (stringField(visualField, "protocol") === "behold.visual-field.v1" &&
- visualField?.available === true) {
- const materials = arrayField(visualField, "materialLegend").flatMap((item) => {
- const name = stringField(recordValue(item), "name");
- return name ? [humanize(name)] : [];
- });
- const depths = arrayField(visualField, "depthLegend").flatMap((item) => {
- const label = stringField(recordValue(item), "label");
- return label ? [label] : [];
- });
- if (materials.length)
- lines.push(`Visible terrain: ${unique(materials).join(", ")}.`);
- if (depths.length)
- lines.push(`Visual depth bands: ${unique(depths).join(", ")}.`);
- if (visualField.materialRows !== undefined ||
- visualField.depthRows !== undefined) {
- diagnostics.push({
- code: "source_only_visual_field_encoding",
- sourcePath: `${sourcePath}.scene.terrain.visualField`,
- });
- }
- }
- const eventLines = arrayField(observation, "events").flatMap((item, index) => {
- const event = recordValue(item);
- const type = stringField(event, "type");
- const data = recordField(event, "data");
- if (type === "spawned")
- return ["Spawned into the world."];
- if (type === "condition_changed")
- return ["HUD condition changed."];
- if (type === "entity_became_visible") {
- const name = stringField(data, "name") ?? stringField(data, "kind") ?? "An entity";
- const proximity = stringField(data, "proximity");
- return [`${name} became visible${proximity ? ` ${proximity}` : ""}.`];
- }
- if (type === "entity_left_view") {
- const name = stringField(data, "name") ?? stringField(data, "kind") ?? "An entity";
- if (data?.lastSeenDistance !== undefined) {
- diagnostics.push({
- code: "withheld_incidental_numeric_estimate",
- sourcePath: `${sourcePath}.events[${index}].data.lastSeenDistance`,
- });
- }
- return [`${name} left the current view.`];
- }
- if (type === "chat_received") {
- const from = stringField(data, "from");
- const text = stringField(data, "text");
- if (profile.version === 2 && stringField(data, "channel") === "private") {
- diagnoseSourceOnlyFields(data, new Set(["from", "text", "channel", "addressed"]), `${sourcePath}.events[${index}].data`, diagnostics);
- return text
- ? [`Private whisper${from ? ` from ${from}` : ""}: ${text}`]
- : [];
- }
- return text
- ? [`Public chat${from ? ` from ${from}` : ""}: ${text}`]
- : [];
- }
- if (profile.version === 2 && type === "sound_heard") {
- diagnoseObservationEventEnvelope(event, `${sourcePath}.events[${index}]`, diagnostics);
- return presentSoundHeard(data, `${sourcePath}.events[${index}]`, diagnostics);
- }
- if (profile.version === 2 && type === "sound_sequence_heard") {
- diagnoseObservationEventEnvelope(event, `${sourcePath}.events[${index}]`, diagnostics);
- return presentSoundSequence(data, `${sourcePath}.events[${index}]`, diagnostics);
- }
- if (profile.version === 2 && type === "time_passed") {
- diagnoseObservationEventEnvelope(event, `${sourcePath}.events[${index}]`, diagnostics);
- diagnoseSourceOnlyFields(data, new Set(["elapsedMs"]), `${sourcePath}.events[${index}].data`, diagnostics);
- const elapsedMs = integerField(data, "elapsedMs");
- return elapsedMs !== null && elapsedMs >= 0
- ? [`Time passed: ${elapsedMs} ms.`]
- : unsupportedObservationEvent(`${sourcePath}.events[${index}]`, diagnostics);
- }
- if (profile.version === 2 &&
- [
- "action_failed",
- "controller_suspended",
- "cancellation_requested",
- "day_phase_changed",
- "died",
- "self_hurt",
- "visible_entity_hurt",
- "visible_block_changed",
- ].includes(type ?? "")) {
- return presentResidentLifecycleEvent(type ?? "", event, data, `${sourcePath}.events[${index}]`, diagnostics);
- }
- diagnostics.push({
- code: "unsupported_observation_event",
- sourcePath: `${sourcePath}.events[${index}]`,
- });
- return [];
- });
- lines.push(...eventLines);
- if (lines.length === 0) {
- diagnostics.push({ code: "empty_safe_observation_projection", sourcePath });
- return null;
- }
- return {
- role: "perception",
- text: lines.join(" "),
- sourcePaths: [sourcePath],
- };
-}
-function presentUtterance(value) {
- const assistant = recordField(recordValue(value), "assistant");
- const content = stringField(assistant, "content")?.trim();
- if (!content)
- return null;
- return {
- role: "utterance",
- text: content,
- sourcePaths: ["payload.payload.utterance.assistant.content"],
- };
-}
-function presentAction(value, entityId, diagnostics, profile) {
- const action = recordValue(value);
- const name = stringField(action, "name") ?? "unknown action";
- const source = stringField(action, "source") ?? "unknown source";
- const kind = stringField(action, "kind") ?? "unknown kind";
- const input = recordField(action, "input");
- let description = null;
- const paths = [
- "payload.payload.action.name",
- "payload.payload.action.source",
- "payload.payload.action.kind",
- ];
- if (action?.id !== undefined) {
- diagnostics.push({
- code: "source_only_action_identity",
- sourcePath: "payload.payload.action.id",
- });
- }
- if (action?.toolCallId !== undefined) {
- diagnostics.push({
- code: "source_only_tool_call_identity",
- sourcePath: "payload.payload.action.toolCallId",
- });
- }
- if (name === "look_direction") {
- const horizontal = stringField(input, "horizontal");
- const vertical = stringField(input, "vertical");
- if (horizontal) {
- description = `${entityId} looked ${horizontal}${vertical ? `, ${vertical}` : ""}.`;
- paths.push("payload.payload.action.input.horizontal");
- if (vertical)
- paths.push("payload.payload.action.input.vertical");
- }
- }
- else if (name === "move_controls") {
- const direction = stringField(input, "direction");
- const durationMs = integerField(input, "durationMs");
- if (direction) {
- description = `${entityId} held ${direction}${durationMs === null ? "" : ` for ${durationMs} ms`}.`;
- paths.push("payload.payload.action.input.direction");
- if (durationMs !== null)
- paths.push("payload.payload.action.input.durationMs");
- }
- }
- else if (name === "chat") {
- const text = stringField(input, "text");
- if (text) {
- description = `${entityId} sent public chat: ${text}`;
- paths.push("payload.payload.action.input.text");
- }
- }
- else if (name === "wait_for_event") {
- const reason = stringField(input, "reason");
- description = `${entityId} waited for another event${reason ? `: ${reason}` : "."}`;
- if (reason)
- paths.push("payload.payload.action.input.reason");
- }
- else if (profile.version === 2 &&
- ["attack_focused_entity", "dig_focused_block", "use_focused_block"].includes(name)) {
- diagnoseSourceOnlyFields(action, new Set(["id", "name", "input", "kind", "toolCallId", "source"]), "payload.payload.action", diagnostics, new Set(), "source_only_action_field");
- diagnoseSourceOnlyFields(input, new Set(), "payload.payload.action.input", diagnostics, new Set(), "source_only_action_input_field");
- if (input) {
- description = name === "attack_focused_entity"
- ? `${entityId} attempted one attack at the focused entity.`
- : name === "dig_focused_block"
- ? `${entityId} attempted to dig the focused block.`
- : `${entityId} attempted to use the focused block.`;
- }
- }
- else if (profile.version === 2 && name === "whisper") {
- diagnoseSourceOnlyFields(action, new Set(["id", "name", "input", "kind", "toolCallId", "source"]), "payload.payload.action", diagnostics, new Set(), "source_only_action_field");
- diagnoseSourceOnlyFields(input, new Set(["username", "text"]), "payload.payload.action.input", diagnostics, new Set(), "source_only_action_input_field");
- const username = stringField(input, "username")?.trim();
- const text = stringField(input, "text")?.trim();
- if (username && text) {
- description = `${entityId} whispered to ${username}: ${text}`;
- paths.push("payload.payload.action.input.username", "payload.payload.action.input.text");
- }
- }
- if (!description) {
- description = `${entityId} recorded ${name}; its input has no safe v${profile.version} Textile presenter.`;
- diagnostics.push({
- code: "unsupported_action_input",
- sourcePath: "payload.payload.action.input",
- });
- }
- return {
- role: "action",
- text: `[${source} · ${kind}] ${description}`,
- sourcePaths: paths,
- };
-}
-function presentOutcome(actionValue, outcomeValue, diagnostics, profile) {
- const action = recordValue(actionValue);
- const outcome = recordValue(outcomeValue);
- const actionName = stringField(action, "name") ?? "action";
- const eventType = stringField(outcome, "eventType") ?? "unknown outcome";
- const ok = typeof outcome?.ok === "boolean" ? outcome.ok : null;
- const result = recordField(outcome, "result");
- const paths = [
- "payload.payload.outcome.ok",
- "payload.payload.outcome.eventType",
- ];
- let detail = null;
- let v2ShownResultKeys = null;
- if (actionName === "look_direction") {
- const orientation = recordField(result, "orientation");
- const facing = stringField(orientation, "facing");
- const vertical = stringField(orientation, "vertical");
- if (facing) {
- detail = ` The body confirmed facing ${facing}${vertical ? ` and ${vertical}` : ""}.`;
- paths.push("payload.payload.outcome.result.orientation.facing");
- if (vertical)
- paths.push("payload.payload.outcome.result.orientation.vertical");
- }
- }
- else if (actionName === "move_controls" &&
- typeof result?.bodyMoved === "boolean") {
- detail = result.bodyMoved ? " The body moved." : " The body did not move.";
- paths.push("payload.payload.outcome.result.bodyMoved");
- }
- else if (profile.version === 2 && actionName === "chat" && result) {
- const resultOk = typeof result.ok === "boolean" ? result.ok : null;
- const status = stringField(result, "status")?.trim();
- const error = stringField(result, "error")?.trim();
- if (resultOk === true && (!status || status === "chat_input_dispatched")) {
- detail = " The public chat input was submitted; recipient delivery was not independently confirmed here.";
- v2ShownResultKeys = new Set(["ok", "status", "message"]);
- paths.push("payload.payload.outcome.result.ok");
- if (status)
- paths.push("payload.payload.outcome.result.status");
- }
- else if (resultOk === false && error) {
- detail = ` The public chat input was rejected: ${humanize(error)}.`;
- v2ShownResultKeys = new Set(["ok", "error", "maximumCharacters", "providedCharacters"]);
- paths.push("payload.payload.outcome.result.ok", "payload.payload.outcome.result.error");
- }
- }
- else if (actionName === "wait_for_event" &&
- typeof result?.sawPeerChat === "boolean") {
- detail = result.sawPeerChat
- ? " The resident observed peer chat."
- : " No peer chat was observed.";
- paths.push("payload.payload.outcome.result.sawPeerChat");
- }
- else if (profile.version === 2 &&
- actionName === "wait_for_event" &&
- result?.ok === true &&
- result?.status === "waiting_for_world_event") {
- detail = " The resident is waiting for a world event.";
- v2ShownResultKeys = new Set(["ok", "status"]);
- paths.push("payload.payload.outcome.result.ok", "payload.payload.outcome.result.status");
- }
- else if (profile.version === 2 && actionName === "whisper" && result) {
- diagnoseSourceOnlyFields(outcome, new Set(["ok", "eventType", "result"]), "payload.payload.outcome", diagnostics, new Set(), "source_only_outcome_field");
- const resultOk = typeof result.ok === "boolean" ? result.ok : null;
- const status = stringField(result, "status")?.trim();
- const error = stringField(result, "error")?.trim();
- if (resultOk === true && (!status || status === "whisper_input_dispatched")) {
- detail = " The private whisper input was submitted; recipient delivery was not independently confirmed here.";
- v2ShownResultKeys = new Set(["ok", "status", "message"]);
- paths.push("payload.payload.outcome.result.ok");
- if (status)
- paths.push("payload.payload.outcome.result.status");
- }
- else if (resultOk === false && error) {
- detail = ` The private whisper input was rejected: ${humanize(error)}.`;
- v2ShownResultKeys = new Set(["ok", "error", "maximumCharacters", "providedCharacters"]);
- paths.push("payload.payload.outcome.result.ok", "payload.payload.outcome.result.error");
- }
- }
- else if (profile.version === 2 &&
- result &&
- ["attack_focused_entity", "dig_focused_block"].includes(actionName)) {
- diagnoseSourceOnlyFields(outcome, new Set(["ok", "eventType", "result", "error", "cancellation"]), "payload.payload.outcome", diagnostics, new Set(["error", "cancellation"]), "source_only_outcome_field");
- const focused = presentFocusedActionResult(actionName, result, paths, diagnostics);
- detail = focused.detail;
- v2ShownResultKeys = focused.shownResultKeys;
- }
- if (profile.version === 2 &&
- result &&
- detail === null &&
- eventType === "action_failed" &&
- result.ok === false) {
- const error = stringField(result, "error")?.trim();
- if (error) {
- detail = ` The bodily attempt failed: ${humanize(error)}.`;
- v2ShownResultKeys = new Set(["ok", "error"]);
- paths.push("payload.payload.outcome.result.ok", "payload.payload.outcome.result.error");
- }
- }
- if (result && detail === null) {
- diagnostics.push({
- code: "unsupported_outcome_result",
- sourcePath: "payload.payload.outcome.result",
- });
- }
- if (result) {
- const shownResultKeys = v2ShownResultKeys ?? (actionName === "look_direction"
- ? new Set(["orientation"])
- : actionName === "move_controls"
- ? new Set(["bodyMoved"])
- : actionName === "wait_for_event"
- ? new Set(["sawPeerChat"])
- : new Set());
- for (const key of Object.keys(result)) {
- if (!shownResultKeys.has(key)) {
- diagnostics.push({
- code: "source_only_outcome_field",
- sourcePath: `payload.payload.outcome.result.${key}`,
- });
- }
- }
- }
- const terminal = outcomeTerminal(ok, eventType);
- return {
- role: "outcome",
- text: `${actionName} ${terminal} (${eventType}).${detail ?? ""}`,
- sourcePaths: paths,
- };
-}
-function presentResidentLifecycleEvent(type, event, data, eventPath, diagnostics) {
- diagnoseObservationEventEnvelope(event, eventPath, diagnostics);
- if (type === "day_phase_changed") {
- diagnoseSourceOnlyFields(data, new Set(["previous", "current"]), `${eventPath}.data`, diagnostics);
- const previous = stringField(data, "previous")?.trim();
- const current = stringField(data, "current")?.trim();
- return previous && current
- ? [`Day phase changed: ${humanize(previous)} → ${humanize(current)}.`]
- : unsupportedObservationEvent(eventPath, diagnostics);
- }
- if (type === "self_hurt") {
- diagnoseSourceOnlyFields(data, new Set(["name", "kind", "proximity"]), `${eventPath}.data`, diagnostics);
- const name = stringField(data, "name")?.trim();
- const kind = stringField(data, "kind")?.trim();
- const proximity = stringField(data, "proximity")?.trim();
- if (!name && !kind)
- return unsupportedObservationEvent(eventPath, diagnostics);
- const subject = name ?? humanize(kind ?? "resident");
- const details = [kind, proximity].filter((item) => Boolean(item));
- return [
- `${subject} was hurt${details.length ? ` (${details.join(", ")})` : ""}.`,
- ];
- }
- if (type === "visible_entity_hurt") {
- diagnoseSourceOnlyFields(data, new Set(["name", "kind", "proximity"]), `${eventPath}.data`, diagnostics);
- const name = stringField(data, "name")?.trim();
- const kind = stringField(data, "kind")?.trim();
- const proximity = stringField(data, "proximity")?.trim();
- if (!name && !kind)
- return unsupportedObservationEvent(eventPath, diagnostics);
- const subject = name ?? humanize(kind ?? "entity");
- const details = [kind, proximity].filter((item) => Boolean(item));
- return [
- `${subject} was seen taking damage${details.length ? ` (${details.join(", ")})` : ""}.`,
- ];
- }
- if (type === "died") {
- diagnoseSourceOnlyFields(data, new Set(), `${eventPath}.data`, diagnostics);
- return ["The resident died."];
- }
- if (type === "visible_block_changed") {
- diagnoseSourceOnlyFields(data, new Set(["before", "after"]), `${eventPath}.data`, diagnostics);
- const before = stringField(data, "before")?.trim();
- const after = stringField(data, "after")?.trim();
- return before && after
- ? [`Visible block changed: ${humanize(before)} → ${humanize(after)}.`]
- : unsupportedObservationEvent(eventPath, diagnostics);
- }
- if (type === "action_failed") {
- diagnoseSourceOnlyFields(data, new Set([
- "intent",
- "authorization",
- "result",
- "error",
- "cancellation",
- "failureKind",
- ]), `${eventPath}.data`, diagnostics, new Set(["authorization", "result", "cancellation", "failureKind"]));
- const intent = recordField(data, "intent");
- diagnoseIntentFields(intent, `${eventPath}.data.intent`, diagnostics);
- const tool = stringField(intent, "tool")?.trim();
- const error = stringField(data, "error")?.trim();
- if (!error)
- return unsupportedObservationEvent(eventPath, diagnostics);
- return [
- `Action failed${tool ? `: ${humanize(tool)}` : ""} (${humanize(error)}).`,
- ];
- }
- if (type === "controller_suspended") {
- diagnoseSourceOnlyFields(data, new Set(["reason", "activeIntent"]), `${eventPath}.data`, diagnostics);
- const activeIntent = recordField(data, "activeIntent");
- diagnoseIntentFields(activeIntent, `${eventPath}.data.activeIntent`, diagnostics);
- const reason = stringField(data, "reason")?.trim();
- const tool = stringField(activeIntent, "tool")?.trim();
- if (!reason)
- return unsupportedObservationEvent(eventPath, diagnostics);
- return [
- `Controller suspended (${humanize(reason)})${tool ? ` during ${humanize(tool)}` : ""}.`,
- ];
- }
- if (type === "cancellation_requested") {
- diagnoseSourceOnlyFields(data, new Set(["intent", "requestedBy", "reason"]), `${eventPath}.data`, diagnostics);
- const intent = recordField(data, "intent");
- diagnoseIntentFields(intent, `${eventPath}.data.intent`, diagnostics);
- const requestedBy = recordField(data, "requestedBy");
- diagnoseSourceOnlyFields(requestedBy, new Set(["source", "tool"]), `${eventPath}.data.requestedBy`, diagnostics);
- const tool = stringField(intent, "tool")?.trim();
- const requesterSource = stringField(requestedBy, "source")?.trim();
- const requesterTool = stringField(requestedBy, "tool")?.trim();
- const reason = stringField(data, "reason")?.trim();
- if (!reason)
- return unsupportedObservationEvent(eventPath, diagnostics);
- const requester = [requesterSource, requesterTool]
- .filter((item) => Boolean(item))
- .map(humanize)
- .join(" ");
- return [
- `Cancellation requested${tool ? ` for ${humanize(tool)}` : ""}${requester ? ` by ${requester}` : ""} (${humanize(reason)}).`,
- ];
- }
- return unsupportedObservationEvent(eventPath, diagnostics);
-}
-function diagnoseIntentFields(intent, sourcePath, diagnostics) {
- diagnoseSourceOnlyFields(intent, new Set(["source", "tool", "input", "observationSequence", "enqueuedAt"]), sourcePath, diagnostics, new Set(["source", "input", "observationSequence", "enqueuedAt"]));
-}
-function presentFocusedActionResult(actionName, result, paths, diagnostics) {
- const shownResultKeys = new Set();
- const details = [];
- const resultOk = typeof result.ok === "boolean" ? result.ok : null;
- if (resultOk !== null) {
- shownResultKeys.add("ok");
- paths.push("payload.payload.outcome.result.ok");
- }
- const error = stringField(result, "error")?.trim();
- if (error) {
- shownResultKeys.add("error");
- paths.push("payload.payload.outcome.result.error");
- details.push(`Body report: ${humanize(error)}.`);
- }
- if (actionName === "attack_focused_entity") {
- const status = stringField(result, "status")?.trim();
- const confirmation = stringField(result, "confirmation")?.trim();
- if (resultOk === true && status && confirmation) {
- shownResultKeys.add("status");
- shownResultKeys.add("confirmation");
- paths.push("payload.payload.outcome.result.status", "payload.payload.outcome.result.confirmation");
- details.push(`Body confirmation: ${humanize(status)} (${confirmation}).`);
- }
- }
- else {
- const changes = presentMaterialChanges(result, "changes", "Change evidence", paths, diagnostics);
- const attempted = presentMaterialChanges(result, "attemptedChanges", "Attempted change", paths, diagnostics);
- if (changes.length)
- shownResultKeys.add("changes");
- if (attempted.length)
- shownResultKeys.add("attemptedChanges");
- details.push(...changes, ...attempted);
- }
- return {
- detail: details.length ? ` ${details.join(" ")}` : null,
- shownResultKeys,
- };
-}
-function presentMaterialChanges(result, field, label, paths, diagnostics) {
- const raw = result[field];
- if (raw === undefined)
- return [];
- if (!Array.isArray(raw)) {
- diagnostics.push({
- code: "unsupported_outcome_change",
- sourcePath: `payload.payload.outcome.result.${field}`,
- });
- return [];
- }
- return raw.flatMap((item, index) => {
- const change = recordValue(item);
- const changePath = `payload.payload.outcome.result.${field}[${index}]`;
- diagnoseSourceOnlyFields(change, new Set([
- "verb",
- "position",
- "before",
- "after",
- "verified",
- "observed",
- "confirmation",
- "context",
- ]), changePath, diagnostics, new Set(["position", "context"]), "source_only_outcome_field");
- const confirmation = recordField(change, "confirmation");
- diagnoseSourceOnlyFields(confirmation, new Set([
- "source",
- "observedAt",
- "dimension",
- "position",
- "before",
- "after",
- "beforeStateId",
- "afterStateId",
- ]), `${changePath}.confirmation`, diagnostics, new Set([
- "observedAt",
- "dimension",
- "position",
- "before",
- "after",
- "beforeStateId",
- "afterStateId",
- ]), "source_only_outcome_field");
- const verb = stringField(change, "verb")?.trim();
- const before = stringField(change, "before")?.trim();
- const after = stringField(change, "after")?.trim();
- const verified = typeof change?.verified === "boolean" ? change.verified : null;
- const observed = typeof change?.observed === "boolean" ? change.observed : null;
- const confirmationSource = stringField(confirmation, "source")?.trim();
- if (!verb || !before || !after || verified === null || observed === null) {
- diagnostics.push({ code: "unsupported_outcome_change", sourcePath: changePath });
- return [];
- }
- paths.push(`${changePath}.verb`, `${changePath}.before`, `${changePath}.after`, `${changePath}.verified`, `${changePath}.observed`);
- if (confirmationSource)
- paths.push(`${changePath}.confirmation.source`);
- return [
- `${label}: ${humanize(verb)} ${humanize(before)} → ${humanize(after)}; verified ${verified ? "yes" : "no"}; observed ${observed ? "yes" : "no"}; confirmation ${confirmationSource ?? "none"}.`,
- ];
- });
-}
-function presentSoundHeard(data, eventPath, diagnostics) {
- diagnoseSourceOnlyFields(data, new Set(["sound", "distanceBand", "relativeDirection", "volume", "pitch"]), `${eventPath}.data`, diagnostics, new Set(["volume", "pitch"]));
- const sound = stringField(data, "sound")?.trim();
- const distanceBand = stringField(data, "distanceBand")?.trim();
- const relativeDirection = stringField(data, "relativeDirection")?.trim();
- if (!sound || !distanceBand || !relativeDirection) {
- return unsupportedObservationEvent(eventPath, diagnostics);
- }
- return [
- `Heard ${sound} (${distanceBand}, ${relativeDirection}).`,
- ];
-}
-function diagnoseObservationEventEnvelope(event, eventPath, diagnostics) {
- diagnoseSourceOnlyFields(event, new Set(["sequence", "type", "salience", "source", "isNew", "data"]), eventPath, diagnostics);
-}
-function presentSoundSequence(data, eventPath, diagnostics) {
- diagnoseSourceOnlyFields(data, new Set([
- "compaction",
- "fromSequence",
- "throughSequence",
- "omittedIndividualEvents",
- "occurrences",
- ]), `${eventPath}.data`, diagnostics);
- if (stringField(data, "compaction") !== "behold.sound-sequence.v1") {
- return unsupportedObservationEvent(eventPath, diagnostics);
- }
- const occurrences = arrayField(data, "occurrences");
- if (occurrences.length === 0) {
- return unsupportedObservationEvent(eventPath, diagnostics);
- }
- let total = 0;
- const descriptions = [];
- for (let index = 0; index < occurrences.length; index += 1) {
- const occurrence = recordValue(occurrences[index]);
- const occurrencePath = `${eventPath}.data.occurrences[${index}]`;
- diagnoseSourceOnlyFields(occurrence, new Set([
- "fromSequence",
- "throughSequence",
- "count",
- "firstAt",
- "lastAt",
- "salience",
- "data",
- ]), occurrencePath, diagnostics);
- const count = integerField(occurrence, "count");
- const sound = recordField(occurrence, "data");
- diagnoseSourceOnlyFields(sound, new Set(["sound", "distanceBand", "relativeDirection", "volume", "pitch"]), `${occurrencePath}.data`, diagnostics, new Set(["volume", "pitch"]));
- const name = stringField(sound, "sound")?.trim();
- const distanceBand = stringField(sound, "distanceBand")?.trim();
- const relativeDirection = stringField(sound, "relativeDirection")?.trim();
- if (count === null ||
- count < 1 ||
- !name ||
- !distanceBand ||
- !relativeDirection) {
- return unsupportedObservationEvent(eventPath, diagnostics);
- }
- total += count;
- descriptions.push(`${count} × ${name} (${distanceBand}, ${relativeDirection})`);
- }
- return [`Heard ${total} sounds: ${descriptions.join("; ")}.`];
-}
-function unsupportedObservationEvent(eventPath, diagnostics) {
- diagnostics.push({
- code: "unsupported_observation_event",
- sourcePath: eventPath,
- });
- return [];
-}
-function diagnoseSourceOnlyFields(value, allowed, sourcePath, diagnostics, explicitlySourceOnly = new Set(), code = "source_only_observation_event_field") {
- if (!value)
- return;
- for (const key of Object.keys(value)) {
- if (!allowed.has(key) || explicitlySourceOnly.has(key)) {
- diagnostics.push({
- code,
- sourcePath: `${sourcePath}.${key}`,
- });
- }
- }
-}
-function outcomeTerminal(ok, eventType) {
- if (ok === true)
- return "succeeded";
- if (/reject/i.test(eventType))
- return "was rejected";
- if (/cancel/i.test(eventType))
- return "was cancelled";
- if (/provider.*fail|fail.*provider/i.test(eventType)) {
- return "ended with a provider failure";
- }
- return ok === false ? "failed" : "ended";
-}
-function sectionText(section) {
- const label = section.role === "perception" ? "PERCEPTION" : section.role.toUpperCase();
- return `${label}\n${section.text}`;
-}
-function presentInventoryItem(value) {
- if (typeof value === "string")
- return [humanize(value)];
- const item = recordValue(value);
- const name = stringField(item, "name") ?? stringField(item, "kind");
- if (!name)
- return [];
- const count = integerField(item, "count");
- return [`${humanize(name)}${count === null ? "" : ` ×${count}`}`];
-}
-function unique(values) {
- return [...new Set(values)];
-}
-function humanize(value) {
- return value.replaceAll("_", " ");
-}
-function recordField(value, field) {
- return recordValue(value?.[field]);
-}
-function recordValue(value) {
- return value !== null && typeof value === "object" && !Array.isArray(value)
- ? value
- : null;
-}
-function stringField(value, field) {
- return typeof value?.[field] === "string" ? value[field] : null;
-}
-function integerField(value, field) {
- const item = value?.[field];
- return Number.isSafeInteger(item) ? item : null;
-}
-function numberField(value, field) {
- const item = value?.[field];
- return typeof item === "number" && Number.isFinite(item) ? item : null;
-}
-function arrayField(value, field) {
- const item = value?.[field];
- return Array.isArray(item) ? item : [];
-}
-function stringArray(value) {
- return Array.isArray(value)
- ? value.filter((item) => typeof item === "string")
- : [];
-}