Something went wrong. Try again.
A local-first event pipeline for independent agents, built on Jazz.
Something went wrong. Try again.
213 kB · 2104 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105import http, { type IncomingMessage, type ServerResponse } from "node:http";import fs from "node:fs/promises";import path from "node:path";import type { JazzThoughtStore } from "../jazz/store.js";import { buildRecentRootActivity, buildRecentRootObservations, buildRecentSourceActivity, describeRunResult, presentActivityEvent, type RootActivityProjection,} from "../projections/activity.js";import { buildSourceHealth } from "../projections/source-health.js";import { auditRunEvidence } from "../agents/evidence.js";import type { AgentRun } from "../store/types.js";import type { ThoughtEvent } from "../events/types.js";import { ReviewDecisionConflictError, projectReviewQueue, recordBrowserReviewDecision,} from "../review/review.js";import { ReviewCapabilityVerifier } from "../review/web-capability.js";import { getArtifactBody, getArtifactCatalog, getArtifactContent } from "../artifacts/catalog.js";import { POST_TRAINING_COURSE } from "../courses/post-training.js";import { COURSE_QUESTION_EVENT_TYPE, COURSE_TUTOR_AGENT_ID, CourseQuestionConflictError, CourseRevisionConflictError, appendPostTrainingCourseQuestion, courseQuestionRequestSchema, parseCourseQuestionEvent,} from "../courses/questions.js";import { CourseChatCapabilityVerifier } from "../courses/web-capability.js";import { requireCompletedObservationOutput } from "../agents/output-lineage.js";import { CORRECTION_PROPOSAL_EVENT_TYPE, MEMORY_MATERIALIZATION_FAILED_EVENT_TYPE, MEMORY_MATERIALIZED_EVENT_TYPE, MEMORY_PROPOSAL_EVENT_TYPE, PROPOSAL_DECISION_EVENT_TYPE, correctionProposalPayloadSchema, memoryMaterializationFailedPayloadSchema, memoryMaterializedPayloadSchema, memoryProposalPayloadSchema, proposalDecisionPayloadSchema,} from "../agent-proposals/contracts.js";import { materializeMemoryDecision } from "../agent-proposals/memory-materializer.js";import { recordProposalDecision } from "../agent-proposals/review.js";import { z } from "zod";import { FONT_DEBUG_ASSET_PATH, FONT_DEBUG_SCRIPT } from "./font-debug.js";import { createDocumentRequestSchema, decideProposalRequestSchema, requestProposalRequestSchema, saveEditRequestSchema, selectContextRequestSchema,} from "../workbench/contracts.js";import type { WorkbenchRunnerRegistry } from "../workbench/runners.js";import { StaleBaseError, WorkbenchConflictError, WorkbenchNotFoundError, WorkbenchRunnerError, WorkbenchWorkflow,} from "../workbench/workflow.js";
const RUN_TERMINAL_EVENT_TYPES = [ "stream.thought.agent.run.completed", "stream.thought.agent.run.failed", "stream.thought.agent.run.blocked", "stream.thought.agent.run.abandoned",];const BLUESKY_POST_URI_PATTERN = /^at:\/\/[^/]{3,256}\/app\.bsky\.feed\.post\/[a-zA-Z0-9._~:-]{1,256}$/;const SAFE_CID_PATTERN = /^[a-zA-Z0-9]{8,128}$/;const BLUESKY_INLAY_CACHE_TTL_MS = 10 * 60_000;const BLUESKY_MEDIA_CACHE_TTL_MS = 10 * 60_000;const BLUESKY_MEDIA_CACHE_MAX_BYTES = 24 * 1024 * 1024;const BLUESKY_MEDIA_CACHE_MAX_ENTRIES = 96;const BLUESKY_MEDIA_MAX_BYTES = 7 * 1024 * 1024;const BLUESKY_MEDIA_CONTENT_TYPES = new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);const STREAM_APP_MANIFEST = `${JSON.stringify({ name: "Stream", short_name: "Stream", id: "./", start_url: "./", scope: "./", display: "standalone", background_color: "#0a0a0a", theme_color: "#0a0a0a", icons: [{ src: "assets/stream-icon.svg", sizes: "any", type: "image/svg+xml", purpose: "any maskable" }],})}\n`;const STREAM_APP_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" rx="52" fill="#0a0a0a"/><text x="128" y="174" text-anchor="middle" font-family="system-ui,sans-serif" font-size="136" font-weight="500" fill="#c4aacf">S</text></svg>`;const STREAM_SERVICE_WORKER = "self.addEventListener('install',()=>self.skipWaiting());self.addEventListener('activate',event=>event.waitUntil(self.clients.claim()));\n";
interface BlueskyPostInlay { uri: string; cid: string; url: string; author: { displayName: string; handle: string; avatar?: string; }; text: string; createdAt?: string; images: Array<{ thumb: string; fullsize: string; alt: string }>; external?: { uri: string; title: string; description: string; thumb?: string }; quote?: { uri: string; url: string; author: { displayName: string; handle: string; avatar?: string }; text: string; }; counts: { replies: number; reposts: number; likes: number };}
const blueskyPostViewSchema = z.object({ uri: z.string(), cid: z.string(), author: z.object({ handle: z.string(), displayName: z.string().optional(), avatar: z.string().url().optional(), }).passthrough(), record: z.object({ text: z.string().optional(), createdAt: z.string().optional(), }).passthrough(), embed: z.unknown().optional(), replyCount: z.number().int().nonnegative().optional(), repostCount: z.number().int().nonnegative().optional(), likeCount: z.number().int().nonnegative().optional(),}).passthrough();const blueskyPostResponseSchema = z.object({ posts: z.array(blueskyPostViewSchema) });const blueskyInlayCache = new Map<string, { expiresAt: number; value: BlueskyPostInlay }>();const blueskyMediaCache = new Map<string, { expiresAt: number; contentType: string; bytes: Buffer }>();let blueskyMediaCacheBytes = 0;let aroundFontCache: Buffer | undefined;
export interface InspectorServerOptions { host?: string; port?: number; reviewCapability?: Buffer | undefined; reviewVerifier?: ReviewCapabilityVerifier | undefined; courseChatCapability?: Buffer | undefined; courseChatVerifier?: CourseChatCapabilityVerifier | undefined; agentContextRoot?: string | undefined; proposalActor?: string | undefined; workbenchRunners?: WorkbenchRunnerRegistry | undefined;}
interface InspectorActivityCache { observations(fresh?: boolean): Promise<RootActivityProjection>; evidence(fresh?: boolean): Promise<RootActivityProjection>;}
function createInspectorActivityCache(store: JazzThoughtStore): InspectorActivityCache { let observationValue: RootActivityProjection | undefined; let evidenceValue: RootActivityProjection | undefined; let observationUpdatedAt = 0; let evidenceUpdatedAt = 0; let observationBuild: Promise<RootActivityProjection> | undefined; let evidenceBuild: Promise<RootActivityProjection> | undefined; const stillFresh = (updatedAt: number) => Date.now() - updatedAt < 5_000; const observations = async (fresh = false): Promise<RootActivityProjection> => { if (observationValue && (!fresh || stillFresh(observationUpdatedAt))) return observationValue; if (observationBuild) return observationBuild; observationBuild = buildRecentRootObservations(store).then((value) => { observationValue = value; observationUpdatedAt = Date.now(); return value; }).finally(() => { observationBuild = undefined; }); return observationBuild; }; const evidence = async (fresh = false): Promise<RootActivityProjection> => { if (evidenceValue && (!fresh || stillFresh(evidenceUpdatedAt))) return evidenceValue; if (evidenceBuild) return evidenceBuild; evidenceBuild = buildInspectorActivity(store).then((value) => { evidenceValue = value; evidenceUpdatedAt = Date.now(); return value; }).finally(() => { evidenceBuild = undefined; }); return evidenceBuild; }; void observations(true).then(() => evidence(true)).catch(() => undefined); return { observations, evidence };}
export async function startInspectorServer( store: JazzThoughtStore, options: InspectorServerOptions = {},): Promise<http.Server> { const host = options.host ?? "127.0.0.1"; if (!isLoopback(host)) throw new Error("thought stream inspector may only bind to a loopback address"); const port = options.port ?? 4317; const reviewVerifier = options.reviewVerifier ?? (options.reviewCapability ? new ReviewCapabilityVerifier(options.reviewCapability) : undefined); const courseChatVerifier = options.courseChatVerifier ?? (options.courseChatCapability ? new CourseChatCapabilityVerifier(options.courseChatCapability) : undefined); const proposalWritesEnabled = Boolean(reviewVerifier && options.agentContextRoot); const activityCache = createInspectorActivityCache(store); const workbench = new WorkbenchWorkflow(store, { actor: options.proposalActor ?? "operator:cameron", ...(options.workbenchRunners ? { runners: options.workbenchRunners } : {}), }); const server = http.createServer((request, response) => { void handleRequest(store, activityCache, request, response, reviewVerifier, courseChatVerifier, proposalWritesEnabled ? options.agentContextRoot : undefined, options.proposalActor ?? "operator:cameron", workbench).catch((error) => { sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) }); }); }); await new Promise<void>((resolve, reject) => { server.once("error", reject); server.listen(port, host, () => { server.off("error", reject); resolve(); }); }); return server;}
async function handleRequest( store: JazzThoughtStore, activityCache: InspectorActivityCache, request: IncomingMessage, response: ServerResponse, reviewVerifier?: ReviewCapabilityVerifier, courseChatVerifier?: CourseChatCapabilityVerifier, agentContextRoot?: string, proposalActor = "operator:cameron", workbench: WorkbenchWorkflow = new WorkbenchWorkflow(store, { actor: proposalActor }),): Promise<void> { const url = new URL(request.url ?? "/", "http://127.0.0.1"); if (request.method === "POST" && url.pathname.startsWith("/api/workbench/")) { await handleWorkbenchWrite(workbench, request, response, url, reviewVerifier); return; } if (request.method === "POST" && url.pathname === "/api/courses/post-training/questions" && url.search === "") { if (!courseChatVerifier) { request.resume(); sendJson(response, 405, { error: "Course questions are read-only" }); return; } let body: Buffer; try { body = await readBody(request, 4_096); } catch { request.resume(); sendJson(response, 400, { error: "Course question is invalid" }); return; } if (!courseChatVerifier.verify(request.headers, "POST", url.pathname, body)) { sendJson(response, 403, { error: "Course question could not be verified" }); return; } try { const input = courseQuestionRequestSchema.parse(JSON.parse(body.toString("utf8"))); const appended = await appendPostTrainingCourseQuestion(store, input, { actor: proposalActor }); sendJson(response, 202, { eventId: appended.event.id, inserted: appended.inserted, statusPath: `api/courses/post-training/questions/${encodeURIComponent(appended.event.id)}`, }); } catch (error) { if (error instanceof CourseRevisionConflictError) { sendJson(response, 409, { error: "The course changed; reload before asking" }); } else if (error instanceof CourseQuestionConflictError) { sendJson(response, 409, { error: "Question identity changed; create a new question" }); } else { sendJson(response, 400, { error: "Course question is invalid" }); } } return; } const proposalDecisionMatch = url.pathname.match(/^\/api\/proposals\/([^/]+)\/decisions$/); if (request.method === "POST" && proposalDecisionMatch) { if (!reviewVerifier || !agentContextRoot) { request.resume(); sendJson(response, 405, { error: "Proposal review is read-only" }); return; } let body: Buffer; try { body = await readBody(request, 34_816); } catch { request.resume(); sendJson(response, 400, { error: "Proposal decision is invalid" }); return; } if (!reviewVerifier.verify(request.headers, "POST", url.pathname, body)) { sendJson(response, 403, { error: "Proposal decision could not be verified" }); return; } try { const eventId = decodeURIComponent(proposalDecisionMatch[1]!); const input = proposalDecisionRequestSchema.parse(JSON.parse(body.toString("utf8"))); const result = await recordProposalDecision(store, { proposalEventId: eventId, ...input, actor: proposalActor }); const materialization = result.proposal.type === MEMORY_PROPOSAL_EVENT_TYPE && input.disposition !== "reject" ? await materializeMemoryDecision(store, result.decision.id, { contextRoot: agentContextRoot }) : undefined; sendJson(response, 201, { decisionEventId: result.decision.id, disposition: input.disposition, ...(result.projectedJudgment ? { judgmentEventId: result.projectedJudgment.id, receipt: "Correction accepted as a private quality judgment" } : {}), ...(materialization ? { materializationEventId: materialization.event.id, materializationStatus: materialization.status, receipt: materialization.status === "materialized" ? "Stream memory updated" : "Stream memory was not changed" } : {}), }); } catch (error) { const message = error instanceof Error ? error.message : "Proposal decision is invalid"; if (message.includes("already has a human decision") || message.includes("submission id conflicts")) { sendJson(response, 409, { error: "Proposal decision changed; reload before submitting" }); } else { sendJson(response, 400, { error: "Proposal decision is invalid" }); } } return; } const reviewDecisionMatch = url.pathname.match(/^\/api\/reviews\/([^/]+)\/decisions$/); if (request.method === "POST" && reviewDecisionMatch) { if (!reviewVerifier) { request.resume(); sendJson(response, 405, { error: "Inspector is read-only" }); return; } let body: Buffer; try { body = await readBody(request, 98_304); } catch { request.resume(); sendJson(response, 400, { error: "Review request is invalid" }); return; } if (!reviewVerifier.verify(request.headers, "POST", url.pathname, body)) { sendJson(response, 403, { error: "Review request could not be verified" }); return; } try { const itemId = decodeURIComponent(reviewDecisionMatch[1]!); const decision = await recordBrowserReviewDecision(store, itemId, JSON.parse(body.toString("utf8"))); sendJson(response, 201, { decisionEventId: decision.id, active: true }); } catch (error) { if (error instanceof ReviewDecisionConflictError) { sendJson(response, 409, { error: "Review decision changed; reload before submitting" }); } else { sendJson(response, 400, { error: "Review decision is invalid" }); } } return; } if (request.method !== "GET" && request.method !== "HEAD") { sendJson(response, 405, { error: "Inspector is read-only" }); return; } if (url.pathname === "/") { send(response, 200, "text/html; charset=utf-8", renderInspectorHtml()); return; } if (url.pathname === "/manifest.webmanifest") { send(response, 200, "application/manifest+json; charset=utf-8", STREAM_APP_MANIFEST); return; } if (url.pathname === "/assets/stream-icon.svg") { send(response, 200, "image/svg+xml; charset=utf-8", STREAM_APP_ICON); return; } if (url.pathname === FONT_DEBUG_ASSET_PATH) { send(response, 200, "text/javascript; charset=utf-8", FONT_DEBUG_SCRIPT); return; } if (url.pathname === "/sw.js") { send(response, 200, "text/javascript; charset=utf-8", STREAM_SERVICE_WORKER); return; } if (url.pathname === "/assets/around-regular.woff2") { try { aroundFontCache ??= await loadAroundFont(); sendBytes(response, 200, "font/woff2", aroundFontCache, "public, max-age=31536000, immutable"); } catch { sendJson(response, 502, { error: "Display font is unavailable" }); } return; } if (url.pathname === "/api/inlays/bluesky-media") { const mediaReferences = url.searchParams.getAll("url"); const source = mediaReferences.length === 1 && [...url.searchParams.keys()].every((key) => key === "url") ? safeBlueskyMediaUrl(mediaReferences[0]) : undefined; if (!source) { sendJson(response, 400, { error: "Bluesky media reference is invalid" }); return; } try { const media = await loadBlueskyMedia(source); sendBytes(response, 200, media.contentType, media.bytes, "private, max-age=600"); } catch { sendJson(response, 502, { error: "Bluesky media is unavailable" }); } return; } if (url.pathname === "/api/inlays/bluesky") { const uri = url.searchParams.get("uri"); const cid = url.searchParams.get("cid") ?? undefined; if (!uri || !BLUESKY_POST_URI_PATTERN.test(uri) || (cid !== undefined && !SAFE_CID_PATTERN.test(cid))) { sendJson(response, 400, { error: "Bluesky post reference is invalid" }); return; } try { sendJson(response, 200, await loadBlueskyPostInlay(uri, cid)); } catch { sendJson(response, 502, { error: "Bluesky post is unavailable" }); } return; } if (url.pathname === "/api/activity") { sendJson(response, 200, await activityCache.observations(url.searchParams.get("fresh") === "1")); return; } if (url.pathname === "/api/activity/evidence") { sendJson(response, 200, await activityCache.evidence(url.searchParams.get("fresh") === "1")); return; } if (url.pathname === "/api/system") { sendJson(response, 200, await buildInspectorSystem(store)); return; } if (url.pathname === "/api/snapshot") { const runs = await store.listRuns(); const [activity, system] = await Promise.all([ buildInspectorActivity(store), buildInspectorSystem(store, runs), ]); sendJson(response, 200, { activity, ...system }); return; } if (url.pathname === "/api/live") { streamInspectorActivity(store, request, response); return; } if (url.pathname === "/api/proposals") { sendJson(response, 200, await projectProposalQueue(store)); return; } if (url.pathname === "/api/reviews") { sendJson(response, 200, await projectReviewQueue(store)); return; } if (url.pathname === "/api/session") { sendJson(response, 200, { reviewWriteEnabled: false, courseChatEnabled: false }); return; } if (url.pathname === "/api/courses/post-training") { sendJson(response, 200, POST_TRAINING_COURSE); return; } const courseQuestionMatch = url.pathname.match(/^\/api\/courses\/post-training\/questions\/([^/]+)$/); if (courseQuestionMatch) { const eventId = decodeURIComponent(courseQuestionMatch[1]!); sendJson(response, 200, await postTrainingQuestionStatus(store, eventId)); return; } if (url.pathname === "/api/artifacts") { sendJson(response, 200, await getArtifactCatalog(store)); return; } if (url.pathname === "/api/workbench/documents") { sendJson(response, 200, { items: await workbench.listDocuments(), runners: workbench.listRunners() }); return; } if (url.pathname === "/api/workbench/candidates") { const documentId = url.searchParams.get("documentId"); if (!documentId || [...url.searchParams.keys()].some((key) => key !== "documentId")) { return sendJson(response, 400, { error: "Candidate lookup requires exactly one documentId" }); } const candidates = await workbench.listCandidates(documentId); if (!candidates) return sendJson(response, 404, { error: "Working document not found" }); sendJson(response, 200, candidates); return; } const workbenchDocumentMatch = url.pathname.match(/^\/api\/workbench\/documents\/([^/]+)$/); if (workbenchDocumentMatch) { const documentId = decodeURIComponent(workbenchDocumentMatch[1]!); const detail = await workbench.getDocument(documentId); if (!detail) return sendJson(response, 404, { error: "Working document not found" }); sendJson(response, 200, detail); return; } const sourceMatch = url.pathname.match(/^\/api\/sources\/([^/]+)$/); if (sourceMatch) { const sourceId = decodeURIComponent(sourceMatch[1]!); const [runs, sources] = await Promise.all([store.listRuns(), buildSourceHealth(store)]); const source = sources.find((candidate) => candidate.source === sourceId); if (!source) return sendJson(response, 404, { error: `Source not found: ${sourceId}` }); sendJson(response, 200, { source, activity: await buildRecentSourceActivity(store, runs, sourceId) }); return; } const artifactContentMatch = url.pathname.match(/^\/api\/artifacts\/([^/]+)\/content$/); if (artifactContentMatch) { const id = decodeURIComponent(artifactContentMatch[1]!); const detail = await getArtifactContent(store, id); if (!detail) return sendJson(response, 404, { error: `Artifact not found: ${id}` }); if (!detail.payload.mediaType.startsWith("image/")) return sendJson(response, 404, { error: "Artifact has no binary inspection route" }); response.writeHead(200, { "content-type": detail.payload.mediaType, "content-length": detail.bytes.byteLength, "x-content-type-options": "nosniff", "cache-control": "private, no-store" }); response.end(detail.bytes); return; } const artifactMatch = url.pathname.match(/^\/api\/artifacts\/([^/]+)$/); if (artifactMatch) { const id = decodeURIComponent(artifactMatch[1]!); const detail = await getArtifactBody(store, id); if (!detail) return sendJson(response, 404, { error: `Artifact not found: ${id}` }); const catalog = await getArtifactCatalog(store); const entry = catalog.entries.find((candidate) => candidate.eventId === id); sendJson(response, 200, { event: detail.event, ...(detail.text !== undefined ? { text: detail.text } : {}), ...(detail.text !== undefined && detail.mediaType === "text/markdown" ? { renderedHtml: renderArtifactMarkdown(detail.text) } : {}), ...(detail.contentPath ? { contentPath: detail.contentPath } : {}), bodySha256: detail.bodySha256, byteCount: detail.byteCount, mediaType: detail.mediaType, catalogEntry: entry, }); return; } const eventMatch = url.pathname.match(/^\/api\/events\/([^/]+)$/); if (eventMatch) { const id = decodeURIComponent(eventMatch[1]!); const event = await store.getEvent(id); if (!event) return sendJson(response, 404, { error: `Event not found: ${id}` }); const [children, runs, parent, root] = await Promise.all([ store.listChildEvents(event.id), store.listRuns(), event.parentEventId ? store.getEvent(event.parentEventId) : undefined, event.rootEventId === event.id ? event : store.getEvent(event.rootEventId), ]); const runInputs = await store.getEvents([...new Set(runs.flatMap((run) => run.inputEventIds))]); const runInputsById = new Map(runInputs.map((input) => [input.id, input])); const relevantRuns = runs.filter((run) => eventBelongsToRun(event, run, runInputsById)); sendJson(response, 200, { event, presentation: presentActivityEvent(event), parent, children, root, agentActivities: await Promise.all(relevantRuns.map((run) => expandRun(store, run))), }); return; } const runMatch = url.pathname.match(/^\/api\/runs\/([^/]+)$/); if (runMatch) { const id = decodeURIComponent(runMatch[1]!); const run = await store.getRun(id); if (!run) return sendJson(response, 404, { error: `Run not found: ${id}` }); const [activity, evidenceEvents] = await Promise.all([ expandRun(store, run), loadRunEvidenceEvents(store, [run]), ]); const evidence = auditRunEvidence([run], evidenceEvents)[0]; sendJson(response, 200, { ...activity, evidence }); return; } sendJson(response, 404, { error: "Not found" });}
const WORKBENCH_WRITE_BODY_LIMIT = 98_304;
/** * Trusted workbench mutations. Every route requires the same body-bound Review * capability as the proposal decision route; without a verifier the inspector * stays read-only (405). Request bodies are strict zod schemas and every * mutation carries a client-generated request/submission id so retries after * an uncertain outcome converge instead of duplicating effects. */async function handleWorkbenchWrite( workbench: WorkbenchWorkflow, request: IncomingMessage, response: ServerResponse, url: URL, reviewVerifier?: ReviewCapabilityVerifier,): Promise<void> { const createMatch = url.pathname === "/api/workbench/documents"; const versionMatch = url.pathname.match(/^\/api\/workbench\/documents\/([^/]+)\/versions$/); const selectionMatch = url.pathname.match(/^\/api\/workbench\/documents\/([^/]+)\/selections$/); const proposalMatch = url.pathname.match(/^\/api\/workbench\/documents\/([^/]+)\/proposals$/); const decisionMatch = url.pathname.match(/^\/api\/workbench\/proposals\/([^/]+)\/decisions$/); if (url.search !== "" || (!createMatch && !versionMatch && !selectionMatch && !proposalMatch && !decisionMatch)) { request.resume(); sendJson(response, 404, { error: "Not found" }); return; } if (!reviewVerifier) { request.resume(); sendJson(response, 405, { error: "Workbench is read-only" }); return; } let body: Buffer; try { body = await readBody(request, WORKBENCH_WRITE_BODY_LIMIT); } catch { request.resume(); sendJson(response, 400, { error: "Workbench request is invalid" }); return; } if (!reviewVerifier.verify(request.headers, "POST", url.pathname, body)) { sendJson(response, 403, { error: "Workbench request could not be verified" }); return; } try { const parsed: unknown = JSON.parse(body.toString("utf8")); if (createMatch) { const input = createDocumentRequestSchema.parse(parsed); const head = await workbench.createWorkingDocument(input); sendJson(response, 201, { documentId: head.documentId, versionId: head.versionId, sha256: head.sha256 }); return; } if (versionMatch) { const input = saveEditRequestSchema.parse(parsed); const head = await workbench.saveOperatorEdit({ documentId: decodeURIComponent(versionMatch[1]!), ...input }); sendJson(response, 201, { documentId: head.documentId, versionId: head.versionId, sha256: head.sha256 }); return; } if (selectionMatch) { const input = selectContextRequestSchema.parse(parsed); const selection = await workbench.selectContext({ documentId: decodeURIComponent(selectionMatch[1]!), ...input }); sendJson(response, 201, { selectionId: selection.selectionId, snapshotVersionId: selection.snapshotVersionId, eventId: selection.eventId }); return; } if (proposalMatch) { const input = requestProposalRequestSchema.parse(parsed); const result = await workbench.requestProposal({ documentId: decodeURIComponent(proposalMatch[1]!), ...input }); sendJson(response, 201, result); return; } const input = decideProposalRequestSchema.parse(parsed); const result = await workbench.decideProposal({ proposalEventId: decodeURIComponent(decisionMatch![1]!), ...input }); sendJson(response, 201, result); } catch (error) { if (error instanceof StaleBaseError) { sendJson(response, 409, { error: "This document changed since you opened it", stale: true, headVersionId: error.headVersionId }); } else if (error instanceof WorkbenchNotFoundError) { sendJson(response, 404, { error: error.message }); } else if (error instanceof WorkbenchConflictError) { sendJson(response, 409, { error: error.message }); } else if (error instanceof WorkbenchRunnerError) { sendJson(response, 502, { error: "The proposal runner failed; no proposal was recorded", runId: error.runId }); } else if (error instanceof z.ZodError) { sendJson(response, 400, { error: "Workbench request is invalid" }); } else { throw error; } }}
async function postTrainingQuestionStatus(store: JazzThoughtStore, eventId: string): Promise<Record<string, unknown>> { const event = await store.getEvent(eventId); if (!event || event.type !== COURSE_QUESTION_EVENT_TYPE || !parseCourseQuestionEvent(event)) { return { status: "not-found" }; } const runs = (await store.getRunsForTriggerEvents([event.id])) .filter((run) => run.agentId === COURSE_TUTOR_AGENT_ID) .sort((left, right) => right.attempt - left.attempt || right.createdAt.localeCompare(left.createdAt)); const run = runs[0]; if (!run) return { status: "pending", eventId: event.id }; if (!["completed", "failed", "blocked", "abandoned", "skipped"].includes(run.status)) { return { status: "running", eventId: event.id, runId: run.id }; } if (run.status !== "completed" || run.outputEventIds.length !== 1) { return { status: "failed", eventId: event.id, runId: run.id }; } const output = await store.getEvent(run.outputEventIds[0]!); if (!output) return { status: "failed", eventId: event.id, runId: run.id }; try { const lineage = await requireCompletedObservationOutput(store, output); if (lineage.run.id !== run.id || lineage.run.agentId !== COURSE_TUTOR_AGENT_ID || lineage.trigger.id !== event.id) { return { status: "failed", eventId: event.id, runId: run.id }; } } catch { return { status: "failed", eventId: event.id, runId: run.id }; } const answer = output.payload.summary; if (typeof answer !== "string" || answer.length === 0 || answer.length > 2_000) { return { status: "failed", eventId: event.id, runId: run.id }; } return { status: "completed", eventId: event.id, runId: run.id, outputEventId: output.id, answer, };}
const proposalDecisionRequestSchema = z.object({ disposition: z.enum(["accept", "edit", "reject"]), replacementText: z.string().min(1).max(32_768).optional(), submissionId: z.string().min(1).max(500),}).strict().superRefine((value, context) => { if ((value.disposition === "edit") !== (value.replacementText !== undefined)) { context.addIssue({ code: "custom", path: ["replacementText"], message: "Only edit decisions require replacement text" }); }});
export interface ProposalQueueItem { eventId: string; kind: "Memory suggestion" | "Proposed correction"; proposedText: string; reason: string; operation: string; originalText?: string | undefined; observedAt: string; status: "Awaiting review" | "Accepted" | "Edited and accepted" | "Rejected" | "Materialized" | "Failed"; decisionEventId?: string | undefined; receiptEventId?: string | undefined; failureReason?: string | undefined; technical: Record<string, string | number>;}
export async function projectProposalQueue(store: JazzThoughtStore): Promise<{ count: number; items: ProposalQueueItem[] }> { const [proposals, decisions, receipts] = await Promise.all([ store.listEvents({ types: [MEMORY_PROPOSAL_EVENT_TYPE, CORRECTION_PROPOSAL_EVENT_TYPE] }), store.listEvents({ types: [PROPOSAL_DECISION_EVENT_TYPE] }), store.listEvents({ types: [MEMORY_MATERIALIZED_EVENT_TYPE, MEMORY_MATERIALIZATION_FAILED_EVENT_TYPE] }), ]); const items: ProposalQueueItem[] = []; for (const proposal of proposals) { const decision = decisions.find((event) => event.payload.proposalEventId === proposal.id); const receipt = decision && receipts.find((event) => event.payload.decisionEventId === decision.id); const decisionPayload = decision ? proposalDecisionPayloadSchema.parse(decision.payload) : undefined; let status: ProposalQueueItem["status"] = "Awaiting review"; if (receipt?.type === MEMORY_MATERIALIZED_EVENT_TYPE) status = "Materialized"; else if (receipt?.type === MEMORY_MATERIALIZATION_FAILED_EVENT_TYPE) status = "Failed"; else if (decisionPayload?.disposition === "reject") status = "Rejected"; else if (decisionPayload?.disposition === "edit") status = "Edited and accepted"; else if (decisionPayload?.disposition === "accept") status = "Accepted"; if (proposal.type === MEMORY_PROPOSAL_EVENT_TYPE) { const payload = memoryProposalPayloadSchema.parse(proposal.payload); const failed = receipt?.type === MEMORY_MATERIALIZATION_FAILED_EVENT_TYPE ? memoryMaterializationFailedPayloadSchema.parse(receipt.payload) : undefined; if (receipt?.type === MEMORY_MATERIALIZED_EVENT_TYPE) memoryMaterializedPayloadSchema.parse(receipt.payload); items.push({ eventId: proposal.id, kind: "Memory suggestion", proposedText: decisionPayload?.replacementText ?? payload.proposedText, reason: payload.reason, operation: payload.operation === "append" ? "Append to Stream memory" : "Replace Stream memory", observedAt: proposal.observedAt, status, ...(decision ? { decisionEventId: decision.id } : {}), ...(receipt ? { receiptEventId: receipt.id } : {}), ...(failed ? { failureReason: humanMaterializationFailure(failed.reasonCode) } : {}), technical: { proposalEventId: proposal.id, agentId: payload.proposer.agentId, agentVersion: payload.proposer.agentVersion, runId: payload.proposer.runId, contextSnapshotId: payload.proposer.contextSnapshotId, targetDocumentId: payload.target.documentId, targetVersionId: payload.target.versionId, targetSha256: payload.target.sha256, proposedTextSha256: payload.proposedTextSha256, }, }); continue; } const payload = correctionProposalPayloadSchema.parse(proposal.payload); const target = await store.getEvent(payload.target.outputEventId); const originalText = typeof target?.payload.structuredOutput === "object" && target.payload.structuredOutput && !Array.isArray(target.payload.structuredOutput) && typeof (target.payload.structuredOutput as Record<string, unknown>).summary === "string" ? String((target.payload.structuredOutput as Record<string, unknown>).summary) : typeof target?.payload.summary === "string" ? target.payload.summary : "Original delivered reply unavailable"; items.push({ eventId: proposal.id, kind: "Proposed correction", proposedText: decisionPayload?.replacementText ?? payload.replacementText, reason: payload.reason, operation: "Correct the delivered reply", originalText, observedAt: proposal.observedAt, status, ...(decision ? { decisionEventId: decision.id } : {}), technical: { proposalEventId: proposal.id, agentId: payload.proposer.agentId, agentVersion: payload.proposer.agentVersion, runId: payload.proposer.runId, targetRunId: payload.target.runId, targetOutputEventId: payload.target.outputEventId, deliveryReceiptEventId: payload.target.deliveryReceiptEventId, outputContractId: payload.target.outputContract.id, outputContractVersion: payload.target.outputContract.version, outputContractSha256: payload.target.outputContract.sha256, replacementTextSha256: payload.replacementTextSha256, }, }); } items.sort((left, right) => right.observedAt.localeCompare(left.observedAt) || right.eventId.localeCompare(left.eventId)); return { count: items.length, items };}
function humanMaterializationFailure(code: string): string { return ({ "stale-base": "Stream memory changed after this suggestion was created", "symlink-refused": "The memory target did not pass the safe-file check", "frontmatter-invalid": "The approved text did not preserve Stream memory identity", "document-identity-changed": "The approved text changed Stream memory identity", "write-failed": "The trusted memory update could not be completed", } as Record<string, string>)[code] ?? "The trusted memory update failed its safety checks";}
function adapterSelectionFromSpec(spec: Record<string, unknown>): { digest: string; generation: number; release: Record<string, unknown> } | undefined { const digest = spec.adapterCatalogDigest; const generation = spec.adapterCatalogGeneration; const release = spec.modelAdapter; if (typeof digest !== "string" || !/^[a-f0-9]{64}$/.test(digest) || !Number.isSafeInteger(generation) || Number(generation) < 1) return undefined; if (!release || typeof release !== "object" || Array.isArray(release)) return undefined; return { digest, generation: Number(generation), release: release as Record<string, unknown> };}
export function renderArtifactMarkdown(markdown: string): string { let text = markdown.replace(/\r\n?/g, "\n"); if (text.startsWith("---\n")) { const frontmatterEnd = text.indexOf("\n---\n", 4); if (frontmatterEnd >= 0) text = text.slice(frontmatterEnd + 5); } const lines = text.split("\n"); let html = ""; let paragraph: string[] = []; let list: "ul" | "ol" | undefined; let code = false; let codeLines: string[] = []; const flushParagraph = (): void => { if (paragraph.length === 0) return; html += `<p>${renderInlineMarkdown(paragraph.join(" "))}</p>`; paragraph = []; }; const closeList = (): void => { if (!list) return; html += `</${list}>`; list = undefined; }; const flushCode = (): void => { html += `<pre><code>${escapeArtifactHtml(codeLines.join("\n"))}</code></pre>`; codeLines = []; }; for (const line of lines) { if (line.startsWith("```")) { flushParagraph(); closeList(); if (code) flushCode(); code = !code; continue; } if (code) { codeLines.push(line); continue; } const heading = line.match(/^(#{1,6})\s+(.+)$/); const unordered = line.match(/^\s*[-*+]\s+(.+)$/); const ordered = line.match(/^\s*\d+[.)]\s+(.+)$/); const quote = line.match(/^>\s?(.*)$/); if (heading) { flushParagraph(); closeList(); const level = heading[1]!.length; html += `<h${level}>${renderInlineMarkdown(heading[2]!)}</h${level}>`; continue; } if (unordered || ordered) { flushParagraph(); const next = unordered ? "ul" : "ol"; if (list !== next) { closeList(); list = next; html += `<${list}>`; } html += `<li>${renderInlineMarkdown((unordered ?? ordered)![1]!)}</li>`; continue; } if (quote) { flushParagraph(); closeList(); html += `<blockquote>${renderInlineMarkdown(quote[1]!)}</blockquote>`; continue; } if (!line.trim()) { flushParagraph(); closeList(); continue; } paragraph.push(line.trim()); } flushParagraph(); closeList(); if (code) flushCode(); return html;}
function renderInlineMarkdown(value: string): string { return escapeArtifactHtml(value) .replace(/`([^`]+)`/g, "<code>$1</code>") .replace(/\*\*\*([^*]+)\*\*\*/g, "<strong><em>$1</em></strong>") .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>") .replace(/\*([^*]+)\*/g, "<em>$1</em>") .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2" rel="noreferrer">$1</a>');}
function escapeArtifactHtml(value: string): string { return value.replace(/[&<>"']/g, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'", })[character]!);}
async function loadBlueskyPostInlay(uri: string, expectedCid?: string): Promise<BlueskyPostInlay> { const cacheKey = `${uri}\u0000${expectedCid ?? "current"}`; const cached = blueskyInlayCache.get(cacheKey); if (cached && cached.expiresAt > Date.now()) return cached.value; blueskyInlayCache.delete(cacheKey);
const endpoint = new URL("https://public.api.bsky.app/xrpc/app.bsky.feed.getPosts"); endpoint.searchParams.append("uris", uri); const response = await fetch(endpoint, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(4_000), }); if (!response.ok) throw new Error(`Bluesky AppView returned ${response.status}`); const parsed = blueskyPostResponseSchema.parse(await response.json()); const post = parsed.posts.find((candidate) => candidate.uri === uri); if (!post || (expectedCid && post.cid !== expectedCid)) throw new Error("Bluesky post did not match the observed strong reference");
const embed = blueskyEmbed(post.embed); const value: BlueskyPostInlay = { uri: post.uri, cid: post.cid, url: atUriToBlueskyUrl(post.uri), author: blueskyAuthor(post.author), text: post.record.text ?? "", ...(post.record.createdAt ? { createdAt: post.record.createdAt } : {}), images: embed.images, ...(embed.external ? { external: embed.external } : {}), ...(embed.quote ? { quote: embed.quote } : {}), counts: { replies: post.replyCount ?? 0, reposts: post.repostCount ?? 0, likes: post.likeCount ?? 0, }, }; blueskyInlayCache.set(cacheKey, { expiresAt: Date.now() + BLUESKY_INLAY_CACHE_TTL_MS, value }); while (blueskyInlayCache.size > 512) { const oldest = blueskyInlayCache.keys().next().value as string | undefined; if (!oldest) break; blueskyInlayCache.delete(oldest); } return value;}
async function loadAroundFont(): Promise<Buffer> { const fontPath = path.resolve(process.cwd(), "public/assets/around-regular.woff2"); const stat = await fs.lstat(fontPath); if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("Around font is not a regular file"); const bytes = await fs.readFile(fontPath); if (bytes.length < 4 || bytes.length > 65_536 || bytes.subarray(0, 4).toString("ascii") !== "wOF2") { throw new Error("Around font is invalid"); } return bytes;}
async function loadBlueskyMedia(source: string): Promise<{ contentType: string; bytes: Buffer }> { const cached = blueskyMediaCache.get(source); if (cached && cached.expiresAt > Date.now()) return cached; if (cached) removeBlueskyMediaCacheEntry(source, cached);
const response = await fetch(source, { headers: { accept: "image/webp,image/png,image/jpeg,image/gif" }, redirect: "error", signal: AbortSignal.timeout(5_000), }); if (!response.ok || !response.body) throw new Error("Bluesky CDN request failed"); const contentType = (response.headers.get("content-type") ?? "").split(";", 1)[0]!.trim().toLowerCase(); if (!BLUESKY_MEDIA_CONTENT_TYPES.has(contentType)) { await response.body.cancel(); throw new Error("Bluesky CDN media type is unsupported"); } const declaredLength = response.headers.get("content-length"); if (declaredLength && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > BLUESKY_MEDIA_MAX_BYTES)) { await response.body.cancel(); throw new Error("Bluesky CDN media is oversized"); } const reader = response.body.getReader(); const chunks: Buffer[] = []; let totalBytes = 0; while (true) { const chunk = await reader.read(); if (chunk.done) break; const bytes = Buffer.from(chunk.value); totalBytes += bytes.length; if (totalBytes > BLUESKY_MEDIA_MAX_BYTES) { await reader.cancel(); throw new Error("Bluesky CDN media is oversized"); } chunks.push(bytes); } const bytes = Buffer.concat(chunks, totalBytes); if (!matchesImageMagic(bytes, contentType)) throw new Error("Bluesky CDN media bytes are invalid"); const entry = { expiresAt: Date.now() + BLUESKY_MEDIA_CACHE_TTL_MS, contentType, bytes }; blueskyMediaCache.set(source, entry); blueskyMediaCacheBytes += bytes.length; trimBlueskyMediaCache(); return entry;}
function trimBlueskyMediaCache(): void { const now = Date.now(); for (const [key, entry] of blueskyMediaCache) { if (entry.expiresAt <= now) removeBlueskyMediaCacheEntry(key, entry); } while (blueskyMediaCache.size > BLUESKY_MEDIA_CACHE_MAX_ENTRIES || blueskyMediaCacheBytes > BLUESKY_MEDIA_CACHE_MAX_BYTES) { const oldest = blueskyMediaCache.entries().next().value as [string, { expiresAt: number; contentType: string; bytes: Buffer }] | undefined; if (!oldest) break; removeBlueskyMediaCacheEntry(oldest[0], oldest[1]); }}
function removeBlueskyMediaCacheEntry( key: string, entry: { expiresAt: number; contentType: string; bytes: Buffer },): void { if (!blueskyMediaCache.delete(key)) return; blueskyMediaCacheBytes = Math.max(0, blueskyMediaCacheBytes - entry.bytes.length);}
function matchesImageMagic(bytes: Buffer, contentType: string): boolean { if (contentType === "image/jpeg") return bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff; if (contentType === "image/png") return bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])); if (contentType === "image/webp") return bytes.length >= 12 && bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP"; if (contentType === "image/gif") return bytes.length >= 6 && ["GIF87a", "GIF89a"].includes(bytes.subarray(0, 6).toString("ascii")); return false;}
function blueskyEmbed(value: unknown): Pick<BlueskyPostInlay, "images" | "external" | "quote"> { const embed = objectValue(value); const type = stringValue(embed?.$type); const media = type === "app.bsky.embed.recordWithMedia#view" ? objectValue(embed?.media) : embed; const images = Array.isArray(media?.images) ? media.images.flatMap((candidate) => { const image = objectValue(candidate); const thumb = blueskyMediaProxyPath(image?.thumb); const fullsize = safeHttpsUrl(image?.fullsize); if (!thumb || !fullsize) return []; return [{ thumb, fullsize, alt: stringValue(image?.alt) ?? "" }]; }) : []; const externalView = objectValue(media?.external); const externalUri = safeHttpUrl(externalView?.uri); const externalThumb = blueskyMediaProxyPath(externalView?.thumb); const external = externalUri ? { uri: externalUri, title: stringValue(externalView?.title) ?? externalUri, description: stringValue(externalView?.description) ?? "", ...(externalThumb ? { thumb: externalThumb } : {}), } : undefined; const recordContainer = type === "app.bsky.embed.recordWithMedia#view" ? objectValue(embed?.record) : embed; const recordView = objectValue(recordContainer?.record); const quoteAuthor = objectValue(recordView?.author); const quoteValue = objectValue(recordView?.value); const quoteUri = stringValue(recordView?.uri); const quote = quoteUri && BLUESKY_POST_URI_PATTERN.test(quoteUri) && quoteAuthor && quoteValue ? { uri: quoteUri, url: atUriToBlueskyUrl(quoteUri), author: blueskyAuthor(quoteAuthor), text: stringValue(quoteValue.text) ?? "", } : undefined; return { images, ...(external ? { external } : {}), ...(quote ? { quote } : {}) };}
function blueskyAuthor(value: Record<string, unknown>): BlueskyPostInlay["author"] { const handle = stringValue(value.handle) ?? "unknown.handle"; const avatar = blueskyMediaProxyPath(value.avatar); return { displayName: stringValue(value.displayName)?.trim() || handle, handle, ...(avatar ? { avatar } : {}), };}
function objectValue(value: unknown): Record<string, unknown> | undefined { return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;}
function stringValue(value: unknown): string | undefined { return typeof value === "string" ? value : undefined;}
function safeHttpsUrl(value: unknown): string | undefined { const url = safeHttpUrl(value); return url?.startsWith("https://") ? url : undefined;}
function blueskyMediaProxyPath(value: unknown): string | undefined { const source = safeBlueskyMediaUrl(value); return source ? `api/inlays/bluesky-media?url=${encodeURIComponent(source)}` : undefined;}
function safeBlueskyMediaUrl(value: unknown): string | undefined { if (typeof value !== "string") return undefined; try { const url = new URL(value); if (url.protocol !== "https:" || url.hostname !== "cdn.bsky.app" || url.port !== "" || url.username || url.password) return undefined; if (!url.pathname.startsWith("/img/") || url.hash) return undefined; return url.toString(); } catch { return undefined; }}
function safeHttpUrl(value: unknown): string | undefined { if (typeof value !== "string") return undefined; try { const url = new URL(value); return url.protocol === "https:" || url.protocol === "http:" ? url.toString() : undefined; } catch { return undefined; }}
function atUriToBlueskyUrl(uri: string): string { const match = uri.match(BLUESKY_POST_URI_PATTERN); if (!match) return uri; const parts = uri.slice(5).split("/"); return `https://bsky.app/profile/${blueskyActorPathSegment(parts[0]!)}/post/${encodeURIComponent(parts[2]!)}`;}
function blueskyActorPathSegment(value: string): string { return encodeURIComponent(value).replaceAll("%3A", ":");}
export function renderInspectorHtml(): string { const html = `<!doctype html><html lang="en"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"> <meta name="theme-color" media="(prefers-color-scheme:light)" content="#ffffff"> <meta name="theme-color" media="(prefers-color-scheme:dark)" content="#0a0a0a"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-title" content="Stream"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <link rel="manifest" href="manifest.webmanifest"> <link rel="icon" href="assets/stream-icon.svg" type="image/svg+xml"> <title>Stream</title> <style> @font-face { font-family:Around; src:url("assets/around-regular.woff2") format("woff2"); font-style:normal; font-weight:400; font-display:swap } :root { color-scheme:light; --bg:#fff; --panel:#fff; --surface:#f5f5f5; --surface-raised:#efefec; --surface-control:#fff; --line:#e8e8e8; --line-strong:#c8c8c1; --text:#1a1a1a; --secondary:#353532; --muted:#777773; --accent:#b497bf; --accent-strong:#72567e; --accent-soft:rgba(180,151,191,.11); --link:#72567e; --amber:#8b6c33; --red:#a44444; --selected-bg:#1a1a1a; --selected-text:#fff; --shadow:0 18px 56px rgba(24,20,28,.14); --radius-content:16px; --radius-control:999px; --motion:160ms cubic-bezier(.2,.8,.2,1); --font-body:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; } @media(prefers-color-scheme:dark){:root{color-scheme:dark;--bg:#0a0a0a;--panel:#101010;--surface:#141414;--surface-raised:#1a1a1a;--surface-control:#050505;--line:#2a2a2a;--line-strong:#444;--text:#e5e5e5;--secondary:#b8b8b8;--muted:#8a8a8a;--accent:#c4aacf;--accent-strong:#d4b9df;--accent-soft:rgba(196,170,207,.12);--link:#d4b9df;--amber:#d5b26c;--red:#df8585;--selected-bg:#f4f4f2;--selected-text:#090909;--shadow:0 22px 70px rgba(0,0,0,.48)}} * { box-sizing:border-box } html { min-height:100%; background:var(--bg) } body { min-height:100%; margin:0; background:var(--bg); color:var(--text); font:15px/1.58 var(--font-body); -webkit-font-smoothing:antialiased; -webkit-tap-highlight-color:transparent } button,input,select,textarea { font:inherit } button { -webkit-tap-highlight-color:transparent } .app-header { width:min(640px,calc(100% - 32px)); margin:0 auto; padding:34px 0 20px } h1 { margin:0 0 16px; font:400 32px/1 Around,Inter,-apple-system,sans-serif; font-synthesis:none; letter-spacing:.025em; text-transform:uppercase } .muted { color:var(--muted) } main { width:min(640px,calc(100% - 32px)); margin:0 auto; padding-bottom:64px } section { min-width:0 } .visually-hidden { position:absolute!important; width:1px!important; height:1px!important; padding:0!important; margin:-1px!important; overflow:hidden!important; clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important } .filter-count { display:inline-grid; min-width:19px; height:19px; place-items:center; margin-left:6px; border-radius:50%; background:var(--accent); color:#111; font-size:10px } .filter-count[hidden] { display:none } .filter-chips { display:flex; gap:7px; margin:0 0 12px; overflow-x:auto; scrollbar-width:none } .filter-chips::-webkit-scrollbar { display:none } .filter-chip { min-height:32px; flex:0 0 auto; border:1px solid var(--line); border-radius:var(--radius-control); background:var(--surface); color:var(--secondary); padding:6px 10px; font-size:11px; cursor:pointer } .filter-chip:hover { border-color:var(--accent); color:var(--text) } .item { width:100%; min-width:0; display:block; border:0; border-radius:0; margin:0; padding:20px 0; background:transparent; color:inherit; text-align:left; font:inherit; cursor:pointer; overflow-wrap:anywhere; transition:opacity var(--motion) } .item:hover,.item.active { background:transparent } .item:hover .telegram-inlay,.item.active .telegram-inlay,.item:hover .email-inlay,.item.active .email-inlay,.item:hover .bluesky-inlay,.item.active .bluesky-inlay,.item:hover .x-inlay,.item.active .x-inlay { border-color:var(--accent); background:var(--accent-soft) } .item:focus-visible { outline:2px solid var(--accent); outline-offset:4px } .type { color:var(--accent-strong); font-size:13px; overflow-wrap:anywhere } .summary { min-width:0; margin:7px 0 9px; font:15px/1.58 var(--font-body); overflow-wrap:anywhere; white-space:pre-wrap } .meta { color:var(--muted); font-size:12px; overflow-wrap:anywhere } .consumer { min-width:0; padding:11px 0 2px; color:var(--secondary); font-size:13px; overflow-wrap:anywhere } .consumer + .consumer { margin-top:10px } .consumer strong { display:block; margin-bottom:3px; color:var(--accent-strong); font-size:13px } .badge { display:inline-block; border:1px solid var(--line); border-radius:var(--radius-control); padding:2px 7px; margin-left:5px; color:var(--amber); font-size:10px; line-height:1.45 } .failed { color:var(--red) } #detail-pane { display:none } main.detail-selected #list-pane { display:none } main.detail-selected #detail-pane { display:block } #detail { padding:8px 0 28px; animation:surface-in var(--motion) both } .detail-back { min-height:40px; border:0; border-radius:var(--radius-control); background:transparent; color:var(--link); padding:6px 0 14px; cursor:pointer } .empty { color:var(--muted); padding:36px 4px } h2 { font-size:18px; line-height:1.3; margin:0 0 12px; letter-spacing:-.01em } h3 { color:var(--muted); text-transform:uppercase; letter-spacing:.08em; font-size:11px; margin:24px 0 8px } pre { margin:0; padding:13px; border:1px solid var(--line); border-radius:12px; background:var(--surface); white-space:pre-wrap; overflow-wrap:anywhere; font:11px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace } .lineage { display:grid; gap:7px } .lineage button { color:var(--link); background:var(--surface); border:1px solid var(--line); border-radius:10px; padding:9px 10px; text-align:left; cursor:pointer; overflow-wrap:anywhere } .work { border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:15px; margin:9px 0 16px } .work-head { display:flex; align-items:center; gap:8px; margin-bottom:9px } .work-head strong { color:var(--accent-strong) } .work-summary { min-width:0; font-size:14px; margin:5px 0 10px; overflow-wrap:anywhere } .work-label { color:var(--muted); font-size:10px; text-transform:uppercase; letter-spacing:.08em; margin-top:10px } .proposal { border-left:2px solid var(--amber); padding-left:9px; margin-top:5px } .output-list { display:grid; gap:5px; margin-top:5px } .output-list button { color:var(--link); background:transparent; border:1px solid var(--line); border-radius:10px; padding:8px; text-align:left; cursor:pointer; overflow-wrap:anywhere } .facts { display:flex; flex-wrap:wrap; gap:5px 12px; color:var(--muted); font-size:11px; margin-top:10px } .facts strong { color:var(--text); font-weight:normal } .review-prompt { border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:15px; white-space:pre-wrap; overflow-wrap:anywhere; font:14px/1.6 inherit } .candidate-grid { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:8px } .candidate { min-width:0; border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:15px } .candidate h3 { margin:0 0 8px; color:var(--accent-strong) } .candidate-text { white-space:pre-wrap; overflow-wrap:anywhere; font:14px/1.58 inherit } .decision { border:1px solid color-mix(in srgb,var(--amber) 55%,var(--line)); border-radius:var(--radius-content); background:color-mix(in srgb,var(--amber) 8%,var(--panel)); padding:14px; margin-top:14px } .decision strong { color:var(--amber) } .review-form { margin-top:16px; padding-top:12px; display:grid; gap:10px } .form-row { display:grid; gap:5px } .form-row label { color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:.06em } select,textarea,input[type=text],input[type=search] { width:100%; min-height:44px; background:var(--surface-control); color:var(--text); border:1px solid var(--line-strong); border-radius:12px; padding:10px 12px; font:13px/1.45 inherit; outline:none; transition:border-color var(--motion),box-shadow var(--motion) } select:focus,textarea:focus,input:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft) } textarea { min-height:100px; resize:vertical } .choice-row { display:flex; flex-wrap:wrap; gap:9px 15px } .choice-row label { color:var(--text); text-transform:none; letter-spacing:0 } .submit-row { display:flex; align-items:center; gap:10px } .submit-row button { min-height:44px; border:1px solid var(--accent); border-radius:var(--radius-control); background:var(--accent-soft); color:var(--text); padding:9px 15px; cursor:pointer } .submit-row button:disabled { opacity:.5; cursor:not-allowed } .provenance { margin-top:10px; color:var(--muted); font-size:10px; overflow-wrap:anywhere } .human-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; margin:8px 0 14px } .human-card { min-width:0; border:1px solid var(--line); border-radius:12px; background:var(--surface); padding:12px } .human-card.wide { grid-column:1/-1 } .field-label { color:var(--muted); font-size:10px; text-transform:uppercase; letter-spacing:.08em; margin-bottom:4px } .field-value { color:var(--text); overflow-wrap:anywhere } .mono { font:11px/1.45 inherit; color:var(--link); overflow-wrap:anywhere } .relation-list { display:grid; gap:8px; margin:8px 0 14px } .relation-card { border-left:2px solid var(--accent); background:var(--surface); padding:10px } .artifact-content { max-height:500px; overflow:auto } .artifact-document { color:var(--text); font:15px/1.65 var(--font-body) } .artifact-document h1 { font-size:24px; line-height:1.2; margin:0 0 18px; letter-spacing:0 } .artifact-document h2 { font-size:19px; line-height:1.25; margin:28px 0 10px } .artifact-document h3 { color:var(--text); text-transform:none; letter-spacing:0; font-size:16px; margin:24px 0 8px } .artifact-document h4,.artifact-document h5,.artifact-document h6 { margin:20px 0 7px } .artifact-document p { margin:0 0 13px } .artifact-document ul,.artifact-document ol { margin:0 0 15px; padding-left:24px } .artifact-document li { margin:4px 0 } .artifact-document blockquote { margin:14px 0; padding:2px 0 2px 14px; border-left:2px solid var(--accent); color:var(--secondary) } .artifact-document code { padding:1px 4px; background:var(--surface); color:var(--accent); font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace } .artifact-document pre { margin:14px 0; max-height:none; font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace } .artifact-document pre code { padding:0; background:transparent; color:inherit } .artifact-document a { color:var(--link); text-underline-offset:2px } .artifact-metadata { margin-top:30px } .artifact-metadata > summary { padding:10px 0 } #course-pane[hidden] { display:none } .course-shell { padding:6px 0 120px } .course-heading { margin-bottom:20px } .course-heading h2 { margin:0 0 4px; font-size:24px; letter-spacing:-.025em } .course-heading p { margin:0; color:var(--secondary) } .course-core { margin:15px 0 0; border-left:2px solid var(--accent); padding:3px 0 3px 13px; color:var(--secondary); font-size:14px } .lesson-rail { display:flex; gap:7px; margin:0 0 24px; overflow-x:auto; scrollbar-width:none; overscroll-behavior-x:contain } .lesson-rail::-webkit-scrollbar { display:none } .lesson-rail button { min-width:38px; height:38px; flex:0 0 auto; border:1px solid var(--line-strong); border-radius:50%; background:var(--surface-control); color:var(--secondary); cursor:pointer } .lesson-rail button.active { border-color:var(--selected-bg); background:var(--selected-bg); color:var(--selected-text) } .lesson-rail button.complete:not(.active) { border-color:var(--accent); color:var(--accent-strong) } .course-progress { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:0 0 18px; color:var(--muted); font-size:12px } .course-progress button { min-height:34px; border:1px solid var(--line-strong); border-radius:var(--radius-control); background:var(--surface-control); color:var(--text); padding:5px 11px; cursor:pointer } .lesson-kicker { color:var(--accent-strong); font-size:12px; margin-bottom:5px } .lesson-title { margin:0; font-size:22px; line-height:1.22; letter-spacing:-.02em } .lesson-subtitle { margin:5px 0 0; color:var(--muted); font-size:14px } .lesson-objective { margin:17px 0 25px; border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:14px 15px } .lesson-objective strong { display:block; margin-bottom:4px; color:var(--accent-strong); font-size:11px; text-transform:uppercase; letter-spacing:.07em } .course-section { margin:0 0 30px; scroll-margin-top:18px } .course-section h3 { margin:0 0 10px; color:var(--text); font-size:17px; line-height:1.3; letter-spacing:-.01em; text-transform:none } .course-section p { margin:0 0 12px; color:var(--secondary) } .course-section ul { margin:4px 0 15px; padding-left:22px } .course-section li { margin:5px 0; color:var(--secondary) } .course-code { margin:15px 0 } .course-code figcaption { margin-bottom:6px; color:var(--muted); font-size:11px } .course-callout { margin:15px 0; border-left:2px solid var(--amber); background:color-mix(in srgb,var(--amber) 7%,var(--panel)); padding:11px 13px; color:var(--secondary) } .course-workshop { margin:32px 0; border:1px solid var(--line-strong); border-radius:var(--radius-content); background:var(--surface); padding:17px } .course-workshop h3 { margin:0 0 4px; color:var(--text); font-size:16px; text-transform:none; letter-spacing:0 } .course-workshop > p { margin:0 0 14px; color:var(--muted); font-size:13px } .workshop-controls { display:grid; gap:9px } .workshop-row { display:flex; align-items:center; justify-content:space-between; gap:12px; min-height:40px; padding:8px 0 } .workshop-row label { color:var(--secondary) } .workshop-row input[type=number] { width:70px; min-height:38px; padding:7px 9px } .workshop-row select { width:auto; min-width:130px; min-height:38px; padding:7px 9px } .workshop-result { margin-top:13px; border-radius:12px; background:var(--surface-control); padding:12px; color:var(--secondary); font-size:13px; white-space:pre-wrap } .workshop-result.pass { box-shadow:inset 3px 0 var(--accent) } .workshop-result.fail { box-shadow:inset 3px 0 var(--red) } .split-card,.factory-stage,.preference-candidate { border:1px solid var(--line); border-radius:12px; background:var(--surface-control); padding:12px } .split-card + .split-card,.factory-stage + .factory-stage { margin-top:8px } .split-card p,.preference-candidate p { margin:0 0 9px; color:var(--secondary) } .token-line { display:flex; flex-wrap:wrap; gap:5px; margin-top:12px } .token { border:1px solid var(--line); border-radius:7px; background:var(--surface-control); padding:4px 6px; color:var(--muted); font:11px/1.3 ui-monospace,SFMono-Regular,Menlo,monospace } .token.target { border-color:var(--accent); background:var(--accent-soft); color:var(--text) } .preference-grid { display:grid; grid-template-columns:1fr 1fr; gap:9px } .preference-candidate button { width:100%; min-height:40px; margin-top:8px; border:1px solid var(--accent); border-radius:var(--radius-control); background:transparent; color:var(--text); cursor:pointer } .reward-grid { display:grid; grid-template-columns:1fr 1fr; gap:9px; margin-top:12px } .reward-card { border:1px solid var(--line); border-radius:12px; background:var(--surface-control); padding:12px } .reward-card strong { display:block; margin-bottom:5px } .score { margin-top:9px; color:var(--accent-strong); font-size:18px } .course-references { margin-top:34px; padding-top:18px } .course-references h3 { margin:0 0 9px; color:var(--text); font-size:15px; text-transform:none; letter-spacing:0 } .course-reference { display:block; padding:10px 0; color:var(--link); text-decoration:none } .course-reference span { display:block; margin-top:2px; color:var(--muted); font-size:12px } .course-glossary { margin-top:28px; border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:0 14px } .course-glossary > summary { padding:13px 0; color:var(--text); font-size:14px } .glossary-row { padding:10px 0 } .glossary-row strong { display:block; color:var(--accent-strong); font-size:12px } .glossary-row span { display:block; margin-top:2px; color:var(--secondary); font-size:13px } .lesson-nav { display:flex; justify-content:space-between; gap:12px; margin-top:28px } .lesson-nav button { min-height:42px; border:1px solid var(--line-strong); border-radius:var(--radius-control); background:var(--surface-control); color:var(--text); padding:8px 14px; cursor:pointer } .lesson-nav button:disabled { visibility:hidden } .course-chat { position:fixed; z-index:20; right:max(18px,env(safe-area-inset-right)); bottom:max(18px,env(safe-area-inset-bottom)); width:min(420px,calc(100% - 36px)); pointer-events:none } .course-chat[hidden] { display:none } .course-chat-toggle { min-height:46px; float:right; border:1px solid var(--selected-bg); border-radius:var(--radius-control); background:var(--selected-bg); color:var(--selected-text); padding:10px 17px; box-shadow:var(--shadow); cursor:pointer; pointer-events:auto } .course-chat-panel { clear:both; overflow:hidden; border:1px solid var(--line-strong); border-radius:20px; background:var(--panel); box-shadow:var(--shadow); pointer-events:auto } .course-chat-panel[hidden] { display:none } .course-chat-head { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:13px 14px } .course-chat-head strong { font-size:14px } .course-chat-head span { display:block; color:var(--muted); font-size:11px } .course-chat-close { width:36px; height:36px; border:0; border-radius:50%; background:var(--surface); color:var(--text); cursor:pointer } .course-chat-log { max-height:min(42svh,360px); overflow:auto; padding:12px 14px } .course-chat-empty { color:var(--muted); font-size:13px } .course-chat-message { margin:0 0 10px; border-radius:13px; padding:10px 11px; white-space:pre-wrap; overflow-wrap:anywhere; font-size:13px } .course-chat-message.user { margin-left:38px; background:var(--selected-bg); color:var(--selected-text) } .course-chat-message.tutor { margin-right:24px; background:var(--surface); color:var(--text) } .course-chat-message.pending { color:var(--muted) } .course-chat-compose { display:grid; grid-template-columns:1fr auto; gap:8px; padding:10px } .course-chat-compose textarea { min-height:46px; max-height:120px; resize:none } .course-chat-send { min-width:62px; border:1px solid var(--accent); border-radius:12px; background:var(--accent-soft); color:var(--text); cursor:pointer } .course-chat-send:disabled { opacity:.5; cursor:not-allowed } details { margin-top:14px; padding-top:10px } summary { color:var(--muted); cursor:pointer; font-size:12px } details pre { margin-top:8px } .primary-nav,.context-nav { display:flex; align-items:center; gap:8px; overflow-x:auto; scrollbar-width:none } .primary-nav::-webkit-scrollbar,.context-nav::-webkit-scrollbar { display:none } .primary-nav { width:100%; gap:20px } .primary-nav button { min-width:max-content; min-height:32px; flex:0 0 auto; border:0; border-bottom:1px solid transparent; border-radius:0; background:transparent; color:var(--muted); padding:0; cursor:pointer; transition:border-color var(--motion),color var(--motion),transform var(--motion) } .primary-nav button:hover { color:var(--text) } .primary-nav button.active { border-bottom-color:var(--accent); background:transparent; color:var(--text) } .primary-nav .filter-nav { margin-left:auto } .primary-nav button:active,.context-nav button:active { transform:scale(.98) } .primary-nav button:focus-visible,.context-nav button:focus-visible { outline:2px solid var(--accent); outline-offset:2px } .context-nav { width:max-content; max-width:100%; margin-top:11px; gap:3px; border:1px solid var(--line); border-radius:var(--radius-control); background:var(--surface); padding:3px } .context-nav[hidden] { display:none } .context-nav button { min-height:36px; border:0; border-radius:var(--radius-control); background:transparent; color:var(--muted); padding:7px 13px; font-size:12px; cursor:pointer; transition:background var(--motion),color var(--motion),transform var(--motion) } .context-nav button.active { background:var(--surface-raised); color:var(--text) } .suggestion-content { border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:15px; margin:8px 0 14px; white-space:pre-wrap; overflow-wrap:anywhere; font:14px/1.6 var(--font-body) } .suggestion-compare { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:8px 0 14px } .suggestion-compare > div { min-width:0 } .suggestion-actions { display:grid; gap:10px; padding-top:14px; margin-top:16px } .suggestion-buttons { display:flex; flex-wrap:wrap; gap:8px } .suggestion-buttons button { border:1px solid var(--accent); background:var(--accent-soft); color:var(--text); padding:9px 12px; font:inherit; cursor:pointer } .suggestion-buttons button.reject { border-color:var(--red) } .wb-card { border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:14px; margin:8px 0 14px; overflow-wrap:anywhere } .wb-card + .wb-card { margin-top:10px } .wb-head { display:flex; flex-wrap:wrap; align-items:center; gap:6px 8px; margin-bottom:6px } .wb-head strong { color:var(--accent-strong) } .wb-form { display:grid; gap:10px; margin:8px 0 14px } .wb-body { min-height:260px; font:13px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace } .wb-note { color:var(--muted); font-size:12px; margin-top:6px } .wb-warn { color:var(--amber); font-size:12px; margin-top:8px } .wb-error { color:var(--red); font-size:12px } .wb-result { min-height:18px; font-size:12px; color:var(--muted); overflow-wrap:anywhere } .wb-actions { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:10px } .wb-actions button { min-height:40px; border:1px solid var(--accent); border-radius:var(--radius-control); background:var(--accent-soft); color:var(--text); padding:8px 13px; cursor:pointer } .wb-actions button.reject { border-color:var(--red); background:transparent } .wb-actions button.quiet { border-color:var(--line-strong); background:var(--surface-control) } .wb-actions button:disabled { opacity:.5; cursor:not-allowed } .wb-actions select { width:auto; min-width:160px; min-height:40px; padding:7px 10px } .wb-panel[hidden] { display:none } .wb-panel { margin-top:10px } .wb-check { display:flex; align-items:flex-start; gap:9px; padding:8px 0; color:var(--text) } .wb-check input { width:auto; min-height:0; margin-top:4px; flex:none } .wb-check .meta { display:block } .wb-source { border-left:2px solid var(--accent); padding:6px 0 6px 10px; margin:6px 0; font-size:13px } .wb-source .mono { display:block; margin-top:2px } .wb-list { display:grid; gap:6px } .wb-version { border:1px solid var(--line); border-radius:12px; background:var(--surface); padding:10px 12px; font-size:13px } .wb-version.head { border-color:var(--accent) } .diff { max-height:420px; overflow:auto; font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace } .diff span { display:block; white-space:pre-wrap } .diff .add { color:var(--accent-strong); background:var(--accent-soft) } .diff .del { color:var(--red) } .diff .hunk,.diff .file { color:var(--muted) } .wb-create { margin:16px 0 4px; border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface); padding:13px 14px } .wb-create .form-row { margin-bottom:8px } .spinner { display:inline-block; width:10px; height:10px; margin-right:7px; border:1px solid var(--line); border-top-color:var(--link); border-radius:50%; animation:spin .8s linear infinite; vertical-align:-1px } .retry { min-height:40px; margin-top:12px; border:1px solid var(--red); border-radius:var(--radius-control); background:transparent; color:var(--text); padding:8px 13px; cursor:pointer } .observation-head h2 { margin-bottom:4px } .observation-card { margin:15px 0; border:0; background:transparent; padding:0; font:15px/1.58 var(--font-body); overflow-wrap:anywhere } .observation-card .bluesky-inlay,.observation-card .x-inlay { border:0; border-radius:0; background:transparent; padding:0 } .observation-card p { margin:0; white-space:pre-wrap } .observation-links { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px } .observation-links a { border:1px solid var(--line); border-radius:var(--radius-control); color:var(--link); padding:7px 10px; text-decoration:none; font:11px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace } .processing-state { display:flex; align-items:center; gap:8px; margin:15px 0 4px; color:var(--muted); font-size:12px } .status-dot { width:7px; height:7px; border-radius:50%; background:var(--line-strong); flex:none } .technical-record { margin-top:26px } .technical-record > summary { padding:3px 0 8px } .technical-section { margin-top:16px } .technical-section h3 { margin-top:0 } .source-event-list { display:grid; gap:8px; margin:8px 0 16px } .source-event { width:100%; border:1px solid var(--line); border-radius:12px; background:var(--surface); color:var(--text); padding:11px 12px; text-align:left; cursor:pointer } .source-event:hover { border-color:var(--accent); background:var(--accent-soft) } .source-event .source-event-title { color:var(--accent-strong); margin-bottom:3px } .source-event .source-event-body { overflow:hidden; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; font:13px/1.48 var(--font-body) } .source-overview { margin-top:12px } .source-state { border-left:2px solid var(--line-strong); padding-left:10px } .source-state.good { border-left-color:var(--accent) } .source-state.waiting { border-left-color:var(--amber) } .feed-processing { margin-top:12px; padding-top:9px } .feed-processing > summary { width:max-content; min-height:32px; display:flex; align-items:center; gap:6px; border:1px solid transparent; border-radius:var(--radius-control); padding:4px 8px; color:var(--muted); list-style:none; transition:border-color var(--motion),color var(--motion) } .feed-processing > summary::-webkit-details-marker { display:none } .feed-processing > summary::before { content:'›'; display:inline-block; color:var(--accent); transform:rotate(0); transition:transform var(--motion) } .feed-processing[open] > summary::before { transform:rotate(90deg) } .feed-processing > summary:hover { border-color:var(--line); color:var(--text) } .feed-links { display:flex; flex-wrap:wrap; gap:8px; margin:10px 0 0 } .feed-links a { color:var(--link); text-decoration:none; border-bottom:1px solid var(--line-strong); font-size:12px } .processing-window { margin-top:9px; color:var(--muted); font-size:12px } .activity-kind { margin-bottom:9px; color:var(--muted); font-size:13px } .source-inlay { min-width:0 } .telegram-inlay { border:1px solid var(--line); border-radius:20px; background:var(--surface-control); padding:15px 17px; transition:border-color var(--motion),background var(--motion),transform var(--motion) } .telegram-author { margin-bottom:6px; color:var(--secondary); font-size:13px; font-weight:600 } .telegram-text { color:var(--text); font-size:16px; line-height:1.55; white-space:pre-wrap; overflow-wrap:anywhere } .telegram-note { margin-top:8px; color:var(--muted); font-size:12px } .email-inlay { border:1px solid var(--line); border-radius:16px; background:var(--surface-control); padding:15px 17px; transition:border-color var(--motion),background var(--motion),transform var(--motion) } .email-author { margin-bottom:5px; color:var(--secondary); font-size:13px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap } .email-subject { color:var(--text); font-size:16px; font-weight:600; line-height:1.45; overflow-wrap:anywhere } .email-preview { margin-top:5px; color:var(--secondary); font-size:14px; line-height:1.5; overflow:hidden; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3 } .email-note { margin-top:8px; color:var(--muted); font-size:12px } .x-inlay { border:1px solid var(--line); border-radius:16px; background:var(--surface-control); padding:15px 17px; transition:border-color var(--motion),background var(--motion),transform var(--motion) } .x-head { display:flex; align-items:baseline; justify-content:space-between; min-width:0; gap:12px } .x-author { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text); font-size:14px; font-weight:600; text-decoration:none } .x-time { flex:none; color:var(--muted); font-size:11px; text-decoration:none } .x-relation { margin-top:3px; color:var(--muted); font-size:12px } .x-relation a { color:var(--link); text-decoration:none } .x-text { margin-top:10px; color:var(--text); font-size:16px; line-height:1.55; white-space:pre-wrap; overflow-wrap:anywhere } .x-note { margin-top:8px; color:var(--muted); font-size:12px } .bluesky-inlay { min-height:74px; border:1px solid var(--line); border-radius:var(--radius-content); background:var(--surface-control); padding:15px 16px; transition:border-color var(--motion),background var(--motion),transform var(--motion) } .bluesky-inlay.loading { display:flex; align-items:center; color:var(--muted); font-size:12px } .bluesky-inlay.failed { min-height:0; color:var(--muted) } .bsky-header { display:flex; align-items:center; min-width:0; gap:9px; margin-bottom:8px } .bsky-avatar-link { display:block; flex:none } .bsky-avatar { width:38px; height:38px; flex:none; border-radius:50%; object-fit:cover; background:var(--surface-raised) } .bsky-author { min-width:0; color:inherit; line-height:1.25; text-decoration:none } .bsky-name { color:var(--text); font-size:14px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap } .bsky-handle { color:var(--muted); font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap } .bsky-time { margin-left:auto; flex:none; color:var(--muted); font-size:11px; text-decoration:none } .bsky-text { color:var(--text); font-size:15px; line-height:1.56; white-space:pre-wrap; overflow-wrap:anywhere } .bsky-images { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:2px; margin-top:10px; overflow:hidden; border-radius:7px } .bsky-images.single { grid-template-columns:1fr } .bsky-images a { display:block; min-width:0 } .bsky-images img { display:block; width:100%; height:100%; max-height:300px; aspect-ratio:16/10; object-fit:cover; background:var(--surface-raised) } .bsky-external { display:flex; min-width:0; margin-top:10px; overflow:hidden; border:1px solid var(--line); border-radius:7px; color:inherit; text-decoration:none } .bsky-external img { width:92px; min-height:76px; flex:none; object-fit:cover; background:var(--surface-raised) } .bsky-external-body { min-width:0; padding:8px 10px } .bsky-external-title { overflow:hidden; color:var(--text); font-size:12px; font-weight:600; text-overflow:ellipsis; white-space:nowrap } .bsky-external-description { display:-webkit-box; margin-top:2px; overflow:hidden; color:var(--muted); font-size:11px; -webkit-box-orient:vertical; -webkit-line-clamp:2 } .bsky-quote { display:block; margin-top:10px; border:1px solid var(--line); border-radius:7px; padding:9px 10px; color:inherit; text-decoration:none } .bsky-quote-author { color:var(--secondary); font-size:11px; font-weight:600 } .bsky-quote-text { margin-top:4px; color:var(--secondary); font-size:12px; line-height:1.45; white-space:pre-wrap } .bsky-stats { display:flex; flex-wrap:wrap; gap:12px; margin-top:9px; color:var(--muted); font-size:11px } .filter-dialog { width:min(440px,calc(100% - 32px)); max-height:min(720px,calc(100svh - 32px)); margin:auto; border:1px solid var(--line-strong); border-radius:24px; background:var(--panel); color:var(--text); padding:0; box-shadow:var(--shadow) } .filter-dialog::backdrop { background:rgba(0,0,0,.58); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px) } .filter-sheet { display:grid; max-height:inherit; overflow:hidden } .filter-sheet-head { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:18px 18px 13px } .filter-sheet-head h2 { margin:0; font-size:16px; letter-spacing:0 } .icon-button { width:44px; height:44px; display:grid; place-items:center; border:1px solid var(--line); border-radius:50%; background:var(--surface-control); color:var(--secondary); font-size:20px; cursor:pointer } .filter-bar { display:grid; gap:11px; padding:16px 18px; overflow:auto } .filter-sheet-actions { display:flex; justify-content:space-between; gap:10px; padding:13px 18px max(16px,env(safe-area-inset-bottom)); background:var(--panel) } .filter-sheet-actions button { min-height:44px; border:1px solid var(--line-strong); border-radius:var(--radius-control); background:var(--surface-control); color:var(--text); padding:9px 16px; cursor:pointer } .filter-sheet-actions .primary { border-color:var(--selected-bg); background:var(--selected-bg); color:var(--selected-text) } @keyframes spin { to { transform:rotate(360deg) } } @keyframes surface-in { from { opacity:0; transform:translateY(5px) } to { opacity:1; transform:none } } @keyframes sheet-in { from { opacity:0; transform:translateY(18px) scale(.98) } to { opacity:1; transform:none } } .filter-dialog[open] { animation:sheet-in var(--motion) both } @media (prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto!important; transition-duration:.01ms!important; animation-duration:.01ms!important; animation-iteration-count:1!important } .spinner { animation:none; border-color:var(--link) } } body.detail-selected .app-header { padding-bottom:10px } body.detail-selected .app-header h1 { margin-bottom:0 } body.detail-selected .primary-nav,body.detail-selected .context-nav { display:none } @media(max-width:640px){ body { overscroll-behavior-y:none; font-size:15px } .app-header { width:100%; padding:max(16px,env(safe-area-inset-top)) 16px 13px; background:var(--bg) } h1 { margin:0 0 14px; font-size:24px; letter-spacing:.05em } main { width:100%; padding:0 16px max(44px,env(safe-area-inset-bottom)) } .primary-nav { width:auto; gap:8px; margin:0 -16px; padding:0 16px 2px; overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch } .primary-nav button { min-height:36px; padding:5px 16px; border:1px solid var(--line-strong); border-radius:var(--radius-control); background:var(--surface-control); font-size:13px; line-height:1 } .primary-nav button:hover { border-color:var(--accent) } .primary-nav button.active { border-color:var(--selected-bg); background:var(--selected-bg); color:var(--selected-text) } .context-nav { margin-top:10px } .context-nav button { min-height:36px; padding:5px 15px } .primary-nav .filter-nav { margin-left:0 } .candidate-grid,.human-grid,.suggestion-compare { grid-template-columns:1fr } .preference-grid,.reward-grid { grid-template-columns:1fr } .human-card.wide { grid-column:auto } .item { padding:18px 0 } .telegram-inlay { border-radius:22px; padding:16px 18px } .bluesky-inlay { padding:14px 15px } .bsky-external img { width:82px } .bsky-images img { max-height:360px } #detail { padding-top:14px } .detail-back { min-height:44px; padding:8px 0 15px } .filter-dialog { width:100%; max-width:none; max-height:min(78svh,720px); margin:auto 0 0; border-width:1px 0 0; border-radius:24px 24px 0 0 } .filter-dialog[open] { animation-name:sheet-in } .filter-sheet-head { padding-top:20px } .course-shell { padding-top:18px; padding-bottom:145px } .course-heading h2 { font-size:22px } .lesson-rail { margin-right:-16px; padding-right:16px } .course-workshop { margin-left:-4px; margin-right:-4px; padding:15px } .course-chat { left:10px; right:10px; bottom:max(10px,env(safe-area-inset-bottom)); width:auto } .course-chat-toggle { margin-right:2px } .course-chat-panel { border-radius:20px } .course-chat-log { max-height:44svh } } </style></head><body><header class="app-header"><h1>Stream</h1><nav class="primary-nav" aria-label="Stream destinations"><button id="events-tab" class="active">Feed</button><button id="learn-tab">Learn</button><button id="review-tab">Review</button><button id="artifacts-tab">Artifacts</button><button id="documents-tab">Documents</button><button id="system-tab">System</button><button id="filter-open" class="filter-nav" hidden>Filter<span id="filter-count" class="filter-count" hidden></span></button></nav><nav id="review-nav" class="context-nav" aria-label="Review views" hidden><button id="suggestions-tab">Suggestions</button><button id="reviews-tab">Comparisons</button></nav><nav id="system-nav" class="context-nav" aria-label="System views" hidden><button id="runs-tab">Runs</button><button id="sources-tab">Sources</button></nav></header><main><section id="list-pane"><h2 id="list-title" class="visually-hidden">Recent activity</h2><div id="active-filters" class="filter-chips" hidden></div><div id="list"><div class="empty"><span class="spinner" aria-hidden="true"></span>Loading recent activity…</div></div></section><section id="course-pane" hidden></section><section id="detail-pane"><button id="detail-back" class="detail-back" aria-label="Back to list">← Back</button><div id="detail"></div></section></main><aside id="course-chat" class="course-chat" hidden><button id="course-chat-toggle" class="course-chat-toggle" aria-expanded="false">Ask about this lesson</button><section id="course-chat-panel" class="course-chat-panel" role="dialog" aria-label="Ask the model factory tutor" hidden><div class="course-chat-head"><div><strong>Ask the tutor</strong><span id="course-chat-context"></span></div><button id="course-chat-close" class="course-chat-close" aria-label="Close course chat">×</button></div><div id="course-chat-log" class="course-chat-log"></div><div class="course-chat-compose"><textarea id="course-chat-input" maxlength="2000" placeholder="Ask about this lesson"></textarea><button id="course-chat-send" class="course-chat-send">Send</button></div></section></aside><dialog id="filters-dialog" class="filter-dialog" aria-labelledby="filter-title"><div class="filter-sheet"><div class="filter-sheet-head"><h2 id="filter-title">Filter feed</h2><button id="filter-close" class="icon-button" aria-label="Close filters">×</button></div><div id="filter-bar" class="filter-bar"></div><div class="filter-sheet-actions"><button id="filter-reset">Reset</button><button id="filter-apply" class="primary">Done</button></div></div></dialog><script src="/assets/font-debug.js" defer></script><script>const routeLists={events:'feed',learn:'learn/post-training',suggestions:'review/suggestions',reviews:'review/comparisons',artifacts:'artifacts',documents:'documents',runs:'system/runs',sources:'system/sources'};const routeDetails={event:{tab:'events',path:'feed/observations'},suggestion:{tab:'suggestions',path:'review/suggestions'},review:{tab:'reviews',path:'review/comparisons'},artifact:{tab:'artifacts',path:'artifacts'},document:{tab:'documents',path:'documents'},run:{tab:'runs',path:'system/runs'},source:{tab:'sources',path:'system/sources'}};function parseRoute(hash=location.hash){ let parts;try{const fragment=hash.startsWith('#/')?hash.slice(2):hash.startsWith('#')?hash.slice(1):hash;parts=fragment.split('/').filter(Boolean).map(value=>decodeURIComponent(value))}catch{return {tab:'events',selected:null}} const path=parts.join('/');for(const [tab,listPath] of Object.entries(routeLists))if(path===listPath)return {tab,selected:null,lessonId:null}; if(parts.length===3&&parts[0]==='learn'&&parts[1]==='post-training')return {tab:'learn',selected:null,lessonId:parts[2]}; for(const [kind,spec] of Object.entries(routeDetails)){const base=spec.path.split('/');if(parts.length===base.length+1&&base.every((value,index)=>parts[index]===value)&&parts.at(-1))return {tab:spec.tab,selected:{kind,id:parts.at(-1)}}} return {tab:'events',selected:null};}function routeHash(tab,selected){const path=tab==='learn'&&state?.courseLessonId?routeLists.learn+'/'+encodeURIComponent(state.courseLessonId):selected?routeDetails[selected.kind]?.path+'/'+encodeURIComponent(selected.id):routeLists[tab];return '#/'+(path??routeLists.events)}const initialRoute=parseRoute();const state={activity:null,system:null,proposals:null,reviews:null,artifacts:null,documents:null,course:null,session:null,inlays:new Map(),loading:{activity:true,system:true,proposals:true,reviews:true,artifacts:true,documents:true,course:true,session:true},errors:{},workbench:{documentId:null,detail:null,candidates:null,requests:{},selecting:false,checked:{events:new Set(),versions:new Set()}},tab:initialRoute.tab,selected:null,courseLessonId:initialRoute.lessonId??null,courseCompleted:readCourseProgress(),courseQuestions:[],coursePendingReceipts:readCourseReceipts(),courseReceiptsResumed:false,courseSubmitting:false,courseChatOpen:false,proposalReceipt:'',eventFilters:{source:'all',activity:'all',processing:'all',query:''},sourceScope:'inputs',activityRefreshInFlight:false,activityRefreshPending:false};function writeRoute(mode='push'){ const hash=routeHash(state.tab,state.selected);const url=location.pathname+location.search+hash;const existing=history.state?.streamInspector===true?history.state:null; if(mode==='replace')history.replaceState(existing??{streamInspector:true,hasInternalParent:false},'',url); else if(location.hash!==hash)history.pushState({streamInspector:true,hasInternalParent:true},'',url);}history.replaceState(history.state?.streamInspector===true?history.state:{streamInspector:true,hasInternalParent:false},'',location.pathname+location.search+routeHash(initialRoute.tab,initialRoute.selected));const resources={activity:{path:'api/activity',label:'recent activity'},system:{path:'api/system',label:'system evidence'},proposals:{path:'api/proposals',label:'suggestions'},reviews:{path:'api/reviews',label:'review queue'},artifacts:{path:'api/artifacts',label:'artifacts'},documents:{path:'api/workbench/documents',label:'working documents'},course:{path:'api/courses/post-training',label:'post-training course'},session:{path:'api/session',label:'session'}};const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));const json=value=>esc(JSON.stringify(value,null,2));const bskyActorPathSegment=value=>encodeURIComponent(value).replaceAll('%3A',':');const agentName=id=>id==='resident-letta-conversation'?'Stream':id==='conceptualizer'?'Conceptualizer':state.system?.agents?.find(agent=>agent.id===id)?.spec?.name??id;async function fetchJson(path,label,options){const response=await fetch(path,options);const contentType=(response.headers.get('content-type')??'').split(';',1)[0].trim().toLowerCase();if(!response.ok)throw new Error(label+' failed (HTTP '+response.status+')');if(contentType!=='application/json')throw new Error(label+' returned '+(contentType||'an unknown content type')+' instead of JSON');return response.json()}async function loadResource(key){const resource=resources[key];state[key]=null;state.loading[key]=true;delete state.errors[key];if(tabResource()===key)renderList();try{state[key]=await fetchJson(resource.path,resource.label)}catch(error){state.errors[key]=error instanceof Error?error.message:String(error)}finally{state.loading[key]=false;if(tabResource()===key||(key==='system'&&state.tab==='events'))renderList();else if(key==='session')syncCourseChat();if((key==='proposals'&&state.selected?.kind==='suggestion')||(key==='reviews'&&state.selected?.kind==='review'))void select(state.selected.kind,state.selected.id,{history:false,preserveScroll:true})}}async function loadActivityEvidence(fresh=false){if(!state.activity)return;try{state.activity=await fetchJson('api/activity/evidence'+(fresh?'?fresh=1':''),'processing evidence');if(state.tab==='events')renderList()}catch{}}async function load(){ renderList(); if(initialRoute.selected)void select(initialRoute.selected.kind,initialRoute.selected.id,{history:false}); const primaryResource=tabResource(); await loadResource('session'); await loadResource(primaryResource); if(primaryResource==='activity')await loadActivityEvidence(true); for(const key of ['activity','course','artifacts','documents','proposals','reviews','system'])if(key!==primaryResource)await loadResource(key); if(primaryResource!=='activity')await loadActivityEvidence(true); startLiveUpdates();}function tabResource(){return state.tab==='events'?'activity':state.tab==='learn'?'course':state.tab==='suggestions'?'proposals':state.tab==='reviews'?'reviews':state.tab==='artifacts'?'artifacts':state.tab==='documents'?'documents':'system'}function primaryTab(){return state.tab==='events'?'events':state.tab==='learn'?'learn':state.tab==='suggestions'||state.tab==='reviews'?'review':state.tab==='artifacts'?'artifacts':state.tab==='documents'?'documents':'system'}function syncNavigation(){ const primary=primaryTab(); for(const id of ['events','learn','review','artifacts','documents','system']){const button=document.querySelector('#'+id+'-tab');const active=id===primary;button.classList.toggle('active',active);if(active)button.setAttribute('aria-current','page');else button.removeAttribute('aria-current')} const review=document.querySelector('#review-nav');const system=document.querySelector('#system-nav');review.hidden=primary!=='review';system.hidden=primary!=='system'; for(const id of ['suggestions','reviews','runs','sources']){const button=document.querySelector('#'+id+'-tab');const active=id===state.tab;button.classList.toggle('active',active);button.setAttribute('aria-pressed',String(active))}}function emptyMessage(){return state.tab==='events'?'No recent activity has been recorded.':state.tab==='runs'?'No agent runs have been recorded.':state.tab==='sources'?'No configured or observed sources are available.':state.tab==='suggestions'?'No suggestions are waiting.':state.tab==='reviews'?'No reviews are waiting.':state.tab==='documents'?'No working documents yet. Open an observation and choose Create working document.':'No artifacts are available.'}function renderList(){ const list=document.querySelector('#list'); const isEvents=state.tab==='events'; const isRuns=state.tab==='runs'; const isSources=state.tab==='sources'; const isSuggestions=state.tab==='suggestions'; const isReviews=state.tab==='reviews'; const isArtifacts=state.tab==='artifacts'; const isDocuments=state.tab==='documents'; syncNavigation(); const isCourse=state.tab==='learn';const listPane=document.querySelector('#list-pane');const coursePane=document.querySelector('#course-pane');listPane.hidden=isCourse;coursePane.hidden=!isCourse; if(isCourse){hideFilterControls();renderCourse();syncCourseChat();return}else syncCourseChat(); document.querySelector('#list-title').textContent=isEvents?'Recent activity':isRuns?'Agent runs':isSuggestions?'Suggestions':isReviews?'Comparisons':isArtifacts?'Artifacts':isDocuments?'Working documents':'Sources'; const resourceKey=tabResource();const resource=resources[resourceKey];const error=state.errors[resourceKey]; const ready=isEvents?state.activity:isRuns||isSources?state.system:isSuggestions?state.proposals:isReviews?state.reviews:isDocuments?state.documents:state.artifacts; const allValues=!ready?[]:isEvents?state.activity.items:isRuns?state.system.runs:isSuggestions?state.proposals.items:isReviews?state.reviews.items:isArtifacts?state.artifacts.entries:isDocuments?state.documents.items:state.system.sources; const values=isEvents?filterEvents(allValues):isSources?filterSources(allValues):allValues; if(isEvents&&ready)renderEventFilters(allValues);else if(isSources&&ready)renderSourceFilter();else hideFilterControls(); const empty=isEvents&&ready&&values.length===0?eventFilterEmptyMessage():emptyMessage(); const rootWindowNotice=isEvents&&ready&&state.activity.rootWindowComplete===false?'<div class="processing-window">Some older observations are outside this bounded feed window.</div>':''; const listContent=values.map(value=>isEvents?eventItem(value):isRuns?runItem(value):isSuggestions?suggestionItem(value):isReviews?reviewItem(value):isArtifacts?artifactItem(value):isDocuments?documentItem(value):sourceItem(value)).join('')||'<div class="empty">'+esc(empty)+'</div>'; list.innerHTML=error?'<div class="empty failed">'+esc(error)+'<div><button class="retry" data-resource="'+esc(resourceKey)+'">Retry</button></div></div>':!ready?'<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading '+esc(resource.label)+'…</div>':rootWindowNotice+listContent; list.querySelector('.retry')?.addEventListener('click',event=>loadResource(event.currentTarget.dataset.resource)); list.querySelectorAll('.item[data-kind]').forEach(item=>{const open=event=>{if(event.target.closest('a,details,input,select,textarea')&&event.target!==item)return;list.querySelectorAll('.item').forEach(value=>value.classList.remove('active'));item.classList.add('active');select(item.dataset.kind,item.dataset.id)};item.addEventListener('click',open);item.addEventListener('keydown',event=>{if((event.key==='Enter'||event.key===' ')&&event.target===item){event.preventDefault();open(event)}})}); if(state.selected)list.querySelectorAll('.item[data-kind]').forEach(item=>item.classList.toggle('active',item.dataset.kind===state.selected.kind&&item.dataset.id===state.selected.id)); hydrateInlays(list);}function renderCourse(){ const pane=document.querySelector('#course-pane');const error=state.errors.course; if(error){pane.innerHTML='<div class="empty failed">'+esc(error)+'<div><button class="retry" id="course-retry">Retry</button></div></div>';pane.querySelector('#course-retry')?.addEventListener('click',()=>loadResource('course'));return} if(!state.course){pane.innerHTML='<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading post-training course…</div>';return} const lessons=state.course.lessons??[];const completedCount=lessons.filter(value=>state.courseCompleted.has(value.id)).length;let lesson=lessons.find(value=>value.id===state.courseLessonId)??lessons[0]; if(!lesson){pane.innerHTML='<div class="empty failed">The course contains no lessons.</div>';return} state.courseLessonId=lesson.id; const lessonIndex=lessons.findIndex(value=>value.id===lesson.id);const previous=lessons[lessonIndex-1];const next=lessons[lessonIndex+1]; const rail=lessons.map(value=>'<button data-lesson="'+esc(value.id)+'" class="'+[value.id===lesson.id?'active':'',state.courseCompleted.has(value.id)?'complete':''].filter(Boolean).join(' ')+'" aria-label="Lesson '+esc(value.number)+': '+esc(value.title)+'" title="'+esc(value.title)+'">'+esc(value.number)+'</button>').join(''); const sections=lesson.sections.map(renderCourseSection).join(''); const references=lesson.references.map(reference=>'<a class="course-reference" href="'+esc(reference.url)+'" target="_blank" rel="noreferrer">'+esc(reference.label)+'<span>'+esc(reference.use)+'</span></a>').join(''); const glossary='<details class="course-glossary"><summary>Course glossary</summary>'+state.course.glossary.map(entry=>'<div class="glossary-row"><strong>'+esc(entry.term)+'</strong><span>'+esc(entry.definition)+'</span></div>').join('')+'</details>'; pane.innerHTML='<article class="course-shell"><header class="course-heading"><h2>'+esc(state.course.title)+'</h2><p>'+esc(state.course.subtitle)+'</p><div class="course-core">'+esc(state.course.oneThing)+'</div></header><nav class="lesson-rail" aria-label="Course lessons">'+rail+'</nav><div class="course-progress"><span>'+esc(completedCount)+' of '+esc(lessons.length)+' lessons complete</span><button id="lesson-complete">'+(state.courseCompleted.has(lesson.id)?'Completed':'Mark lesson complete')+'</button></div><div class="lesson-kicker">Lesson '+esc(lesson.number)+' of '+esc(lessons.length)+'</div><h2 class="lesson-title">'+esc(lesson.title)+'</h2><p class="lesson-subtitle">'+esc(lesson.subtitle)+'</p><div class="lesson-objective"><strong>By the end</strong>'+esc(lesson.objective)+'</div>'+sections+renderCourseWorkshop(lesson.workshop)+glossary+'<section class="course-references"><h3>Primary references</h3>'+references+'</section><nav class="lesson-nav" aria-label="Lesson navigation"><button id="lesson-previous" '+(previous?'':'disabled')+'>← '+(previous?'Lesson '+esc(previous.number):'Previous')+'</button><button id="lesson-next" '+(next?'':'disabled')+'>'+(next?'Lesson '+esc(next.number):'Next')+' →</button></nav></article>'; pane.querySelectorAll('[data-lesson]').forEach(button=>button.addEventListener('click',()=>selectCourseLesson(button.dataset.lesson))); pane.querySelector('#lesson-previous')?.addEventListener('click',()=>{if(previous)selectCourseLesson(previous.id)}); pane.querySelector('#lesson-next')?.addEventListener('click',()=>{if(next)selectCourseLesson(next.id)}); pane.querySelector('#lesson-complete')?.addEventListener('click',()=>toggleCourseComplete(lesson.id)); bindCourseWorkshop(lesson.workshop.kind); syncCourseChat();}function readCourseProgress(){try{const value=JSON.parse(localStorage.getItem('thoughtstream-course-post-training-progress')??'[]');return new Set(Array.isArray(value)?value.filter(item=>typeof item==='string'):[])}catch{return new Set()}}function toggleCourseComplete(lessonId){if(state.courseCompleted.has(lessonId))state.courseCompleted.delete(lessonId);else state.courseCompleted.add(lessonId);try{localStorage.setItem('thoughtstream-course-post-training-progress',JSON.stringify([...state.courseCompleted]))}catch{}renderCourse()}function readCourseReceipts(){try{const value=JSON.parse(sessionStorage.getItem('thoughtstream-course-post-training-receipts')??'[]');return Array.isArray(value)?value.filter(validCourseReceipt).slice(-8):[]}catch{return []}}function validCourseReceipt(value){return Boolean(value&&typeof value.eventId==='string'&&typeof value.lessonId==='string'&&typeof value.createdAt==='number'&&typeof value.statusPath==='string'&&new RegExp('^api/courses/post-training/questions/[A-Za-z0-9_-]+$').test(value.statusPath))}function writeCourseReceipts(){try{sessionStorage.setItem('thoughtstream-course-post-training-receipts',JSON.stringify(state.coursePendingReceipts.slice(-8)))}catch{}}function saveCourseReceipt(receipt){if(!validCourseReceipt(receipt))throw new Error('The tutor returned an invalid receipt.');state.coursePendingReceipts=state.coursePendingReceipts.filter(value=>value.statusPath!==receipt.statusPath);state.coursePendingReceipts.push(receipt);writeCourseReceipts()}function removeCourseReceipt(statusPath){state.coursePendingReceipts=state.coursePendingReceipts.filter(value=>value.statusPath!==statusPath);writeCourseReceipts()}function renderCourseSection(section){ const paragraphs=section.paragraphs.map(value=>'<p>'+esc(value)+'</p>').join(''); const bullets=section.bullets?.length?'<ul>'+section.bullets.map(value=>'<li>'+esc(value)+'</li>').join('')+'</ul>':''; const code=section.code?'<figure class="course-code"><figcaption>'+esc(section.code.caption)+'</figcaption><pre><code>'+esc(section.code.code)+'</code></pre></figure>':''; const callout=section.callout?'<div class="course-callout">'+esc(section.callout)+'</div>':''; return '<section id="course-section-'+esc(section.id)+'" class="course-section"><h3>'+esc(section.title)+'</h3>'+paragraphs+bullets+code+callout+'</section>';}function renderCourseWorkshop(workshop){return '<section class="course-workshop" data-workshop="'+esc(workshop.kind)+'"><h3>'+esc(workshop.title)+'</h3><p>'+esc(workshop.prompt)+'</p>'+workshopBody(workshop.kind)+'</section>'}function workshopBody(kind){ if(kind==='capability-contract')return '<div class="workshop-controls"><label class="workshop-row"><span>Require the named file to change</span><input id="cap-mutation" type="checkbox" checked></label><label class="workshop-row"><span>Require direct execution of that file</span><input id="cap-execution" type="checkbox" checked></label><label class="workshop-row"><span>Require exact stdout</span><input id="cap-output" type="checkbox" checked></label><label class="workshop-row"><span>Keep the answer out of the prompt</span><input id="cap-hidden" type="checkbox" checked></label></div><div id="workshop-result" class="workshop-result"></div>'; if(kind==='machine-gate')return '<div class="workshop-row"><label for="gate-preset">Scenario</label><select id="gate-preset"><option value="baseline">Native baseline</option><option value="one-round">One strong Pi round</option><option value="answer-only">Answer-only candidate</option><option value="promotion">Promotion candidate</option></select></div><div id="gate-rounds"></div><label class="workshop-row"><span>Scores come from the full functional Pi gate</span><input id="gate-functional" type="checkbox"></label><label class="workshop-row"><span>Frozen regression suite retained</span><input id="gate-regression" type="checkbox" checked></label><div id="workshop-result" class="workshop-result"></div>'; if(kind==='data-split'){const cards=[['verified-demo','A generated tool trajectory passes the executable verifier.'],['template-sibling','A paraphrase shares the same hidden answer and template as a training item.'],['locked-problem','Project Euler problem 2 remains locked until the earlier release clears promotion.'],['regression','A previously promoted tool-use task must remain stable.']];return '<div id="split-cards">'+cards.map(([id,text])=>'<div class="split-card"><p>'+esc(text)+'</p><select data-split="'+id+'"><option value="">Choose</option><option value="training">Training</option><option value="development">Development eval</option><option value="confirmation">Confirmation or regression</option><option value="reject">Reject as leakage</option></select></div>').join('')+'</div><div id="workshop-result" class="workshop-result">Place all four items.</div>'} if(kind==='environment')return '<div class="workshop-controls"><label class="workshop-row"><span>Verify file mutation</span><input data-env="mutation" type="checkbox" checked></label><label class="workshop-row"><span>Verify direct execution</span><input data-env="execution" type="checkbox" checked></label><label class="workshop-row"><span>Verify exact stdout</span><input data-env="output" type="checkbox" checked></label><label class="workshop-row"><span>Deny network and escape paths</span><input data-env="isolation" type="checkbox" checked></label></div><div id="workshop-result" class="workshop-result"></div>'; if(kind==='loss-mask')return '<div class="workshop-controls"><label class="workshop-row"><span>System tokens contribute to loss</span><input data-mask="system" type="checkbox"></label><label class="workshop-row"><span>User tokens contribute to loss</span><input data-mask="user" type="checkbox"></label><label class="workshop-row"><span>Assistant tokens contribute to loss</span><input data-mask="assistant" type="checkbox" checked></label><label class="workshop-row"><span>Tool-result tokens contribute to loss</span><input data-mask="tool" type="checkbox"></label></div><div id="loss-tokens" class="token-line"></div><div id="workshop-result" class="workshop-result"></div>'; if(kind==='preference')return '<div class="workshop-row"><label for="preference-criterion">Criterion</label><select id="preference-criterion"><option value="functional correctness">Functional correctness</option><option value="clarity">Clarity</option><option value="brevity">Brevity</option></select></div><div class="preference-grid"><div class="preference-candidate"><strong>Candidate A</strong><p>“The answer is 42.” No file was changed or executed.</p><button data-choice="A">Prefer A</button></div><div class="preference-candidate"><strong>Candidate B</strong><p>Creates answer.py, executes it, and returns exact stdout 42.</p><button data-choice="B">Prefer B</button></div></div><div id="workshop-result" class="workshop-result">Choose under the named criterion.</div>'; if(kind==='reward')return '<div class="workshop-controls"><label class="workshop-row"><span>Exact answer <output id="reward-answer-value">4</output></span><input data-reward="answer" type="range" min="0" max="5" value="4"></label><label class="workshop-row"><span>Tool use <output id="reward-tool-value">3</output></span><input data-reward="tool" type="range" min="0" max="5" value="3"></label><label class="workshop-row"><span>No escape <output id="reward-safe-value">3</output></span><input data-reward="safe" type="range" min="0" max="5" value="3"></label><label class="workshop-row"><span>Brevity <output id="reward-brief-value">1</output></span><input data-reward="brief" type="range" min="0" max="5" value="1"></label></div><div class="reward-grid"><div class="reward-card"><strong>Executed trajectory</strong><span>Correct, uses tools, contained, longer.</span><div id="reward-legitimate" class="score"></div></div><div class="reward-card"><strong>Answer-only shortcut</strong><span>Correct final text, no tools, contained, short.</span><div id="reward-shortcut" class="score"></div></div></div><div id="workshop-result" class="workshop-result"></div>'; if(kind==='factory'){const stages=[['data','Dataset lineage and decontamination'],['rollout','Rollout and environment receipts'],['learner','Learner checkpoint integrity'],['eval','Unseen capability and regression gates'],['registry','Immutable candidate registry entry'],['canary','Serving canary and rollback pointer']];return '<div id="factory-stages">'+stages.map(([id,label],index)=>'<label class="factory-stage workshop-row"><span>'+(index+1)+'. '+esc(label)+'</span><input data-factory="'+id+'" type="checkbox" '+(id==='eval'?'':'checked')+'></label>').join('')+'</div><div id="workshop-result" class="workshop-result"></div>'} return '';}function bindCourseWorkshop(kind){ if(kind==='capability-contract'){const update=()=>{const mutation=document.querySelector('#cap-mutation').checked;const execution=document.querySelector('#cap-execution').checked;const output=document.querySelector('#cap-output').checked;const hidden=document.querySelector('#cap-hidden').checked;const complete=mutation&&execution&&output&&hidden;setWorkshopResult(complete?'Executable capability contract: the model must act, produce the exact result, and cannot read the answer.':'Incomplete contract: '+[!mutation?'answer-only text can pass':null,!execution?'an unexecuted edit can pass':null,!output?'the behavior has no exact terminal check':null,!hidden?'the evaluator leaks the answer':null].filter(Boolean).join('; ')+'.',complete)};document.querySelectorAll('#course-pane .course-workshop input').forEach(input=>input.addEventListener('change',update));update();return} if(kind==='machine-gate'){const presets={baseline:{scores:[[7,5,5],[7,5,5]],functional:false,regression:true},'one-round':{scores:[[6,6,6],[7,5,7]],functional:true,regression:true},'answer-only':{scores:[[8,8,8],[8,8,8]],functional:false,regression:true},promotion:{scores:[[7,6,7],[6,7,6]],functional:true,regression:true}};const renderInputs=preset=>{document.querySelector('#gate-rounds').innerHTML=preset.scores.map((round,index)=>'<div class="split-card"><strong>Round '+(index+1)+'</strong>'+['JavaScript','Julia','Python'].map((language,languageIndex)=>'<label class="workshop-row"><span>'+language+'</span><input type="number" min="0" max="8" value="'+round[languageIndex]+'" data-gate-round="'+index+'" data-gate-language="'+languageIndex+'"></label>').join('')+'</div>').join('');document.querySelector('#gate-functional').checked=preset.functional;document.querySelector('#gate-regression').checked=preset.regression;document.querySelectorAll('[data-gate-round],#gate-functional,#gate-regression').forEach(input=>input.addEventListener('change',update));update()};const update=()=>{const scores=[0,1].map(round=>[0,1,2].map(language=>Number(document.querySelector('[data-gate-round="'+round+'"][data-gate-language="'+language+'"]').value)));const weak=[];scores.forEach((round,index)=>round.forEach((score,language)=>{if(score<6)weak.push('round '+(index+1)+' '+['JavaScript','Julia','Python'][language]+' '+score+'/8')}));if(!document.querySelector('#gate-functional').checked)weak.push('scores are not from the functional Pi gate');if(!document.querySelector('#gate-regression').checked)weak.push('regression failed');setWorkshopResult(weak.length?'Blocked: '+weak.join('; ')+'.':'Promotion eligible: every functional Pi language slice cleared 6/8 twice and regression held.',weak.length===0)};const select=document.querySelector('#gate-preset');select.addEventListener('change',()=>renderInputs(presets[select.value]));renderInputs(presets[select.value]);return} if(kind==='data-split'){const expected={'verified-demo':'training','template-sibling':'reject','locked-problem':'confirmation','regression':'confirmation'};const update=()=>{const selections=[...document.querySelectorAll('[data-split]')];const complete=selections.every(select=>select.value);const correct=complete&&selections.every(select=>select.value===expected[select.dataset.split]);setWorkshopResult(!complete?'Place all four items.':correct?'Correct. The verified trajectory can train; the dependent sibling is leakage; locked and regression material stay outside optimization.':'At least one split leaks dependence or spends confirmation evidence during optimization.',correct)};document.querySelectorAll('[data-split]').forEach(select=>select.addEventListener('change',update));return} if(kind==='environment'){const update=()=>{const values=Object.fromEntries([...document.querySelectorAll('[data-env]')].map(input=>[input.dataset.env,input.checked]));const shortcuts=[];if(!values.mutation)shortcuts.push('answer-only prose');if(!values.execution)shortcuts.push('an unexecuted file edit');if(!values.output)shortcuts.push('a wrong result with plausible actions');if(!values.isolation)shortcuts.push('answer lookup or workspace escape');setWorkshopResult(shortcuts.length?'The evaluator now accepts or cannot exclude: '+shortcuts.join(', ')+'.':'The four checks distinguish the intended trajectory from the common shortcuts.',shortcuts.length===0)};document.querySelectorAll('[data-env]').forEach(input=>input.addEventListener('change',update));update();return} if(kind==='loss-mask'){const groups={system:['<system>','You are a coding agent.'],user:['<user>','Create answer.py and run it.'],assistant:['<assistant>','write_file','answer.py','print(42)','run','python answer.py'],tool:['<tool>','exit=0 stdout=42']};const update=()=>{const targets=new Set([...document.querySelectorAll('[data-mask]')].filter(input=>input.checked).map(input=>input.dataset.mask));document.querySelector('#loss-tokens').innerHTML=Object.entries(groups).flatMap(([group,tokens])=>tokens.map(token=>'<span class="token '+(targets.has(group)?'target':'')+'">'+esc(token)+'</span>')).join('');setWorkshopResult(targets.size?'Gradient target: '+[...targets].join(', ')+' tokens. Teacher forcing supplies every earlier token whether the model would have generated it or not.':'No tokens contribute to loss, so this example produces no supervised update.',targets.size>0)};document.querySelectorAll('[data-mask]').forEach(input=>input.addEventListener('change',update));update();return} if(kind==='preference'){document.querySelectorAll('[data-choice]').forEach(button=>button.addEventListener('click',()=>{const criterion=document.querySelector('#preference-criterion').value;const choice=button.dataset.choice;setWorkshopResult(['Preference record','criterion: '+criterion,'chosen: candidate '+choice,'rejected: candidate '+(choice==='A'?'B':'A'),'The update can only learn the criterion expressed by this pair and its surrounding dataset.'].join(String.fromCharCode(10)),choice==='B'&&criterion==='functional correctness')}));return} if(kind==='reward'){const update=()=>{const weights=Object.fromEntries([...document.querySelectorAll('[data-reward]')].map(input=>[input.dataset.reward,Number(input.value)]));for(const [key,value] of Object.entries(weights))document.querySelector('#reward-'+key+'-value').textContent=String(value);const legitimate=weights.answer+weights.tool+weights.safe+weights.brief*.35;const shortcut=weights.answer+weights.safe+weights.brief;document.querySelector('#reward-legitimate').textContent=legitimate.toFixed(2);document.querySelector('#reward-shortcut').textContent=shortcut.toFixed(2);const shortcutWins=shortcut>=legitimate;setWorkshopResult(shortcutWins?'The shortcut earns at least as much reward. The optimizer is being asked to remove tool use.':'The executed trajectory earns more, but hidden variants and regression gates still need to test the learned policy.',!shortcutWins)};document.querySelectorAll('[data-reward]').forEach(input=>input.addEventListener('input',update));update();return} if(kind==='factory'){const update=()=>{const stages=[...document.querySelectorAll('[data-factory]')];const failed=stages.filter(input=>!input.checked).map(input=>input.closest('label').querySelector('span').textContent.replace(/^\d+\.\s*/,''));setWorkshopResult(failed.length?'Release blocked at: '+failed.join('; ')+'. The checkpoint remains a candidate.':'All named gates pass. The candidate may enter controlled serving with the rollback pointer intact.',failed.length===0)};document.querySelectorAll('[data-factory]').forEach(input=>input.addEventListener('change',update));update()}}function setWorkshopResult(text,pass){const result=document.querySelector('#workshop-result');if(!result)return;result.textContent=text;result.classList.toggle('pass',Boolean(pass));result.classList.toggle('fail',pass===false)}function selectCourseLesson(lessonId,options={}){if(!state.course?.lessons?.some(lesson=>lesson.id===lessonId))return;state.courseLessonId=lessonId;renderCourse();if(options.history!==false)writeRoute(options.history??'push');window.scrollTo({top:0,behavior:options.preserveScroll?'auto':'smooth'})}function syncCourseChat(){ const shell=document.querySelector('#course-chat');const available=state.tab==='learn'&&Boolean(state.course);shell.hidden=!available;if(!available)return; const lesson=state.course.lessons.find(value=>value.id===state.courseLessonId)??state.course.lessons[0];document.querySelector('#course-chat-context').textContent=lesson?'Lesson '+lesson.number+': '+lesson.title:''; const enabled=Boolean(state.session?.courseChatEnabled&&state.session?.csrfToken);const input=document.querySelector('#course-chat-input');const send=document.querySelector('#course-chat-send');input.disabled=!enabled;send.disabled=!enabled||state.courseSubmitting;input.placeholder=enabled?'Ask about this lesson':'Sign in with OAuth to ask questions';renderCourseChatLog();if(enabled&&!state.courseReceiptsResumed)resumeCourseReceipts();}function renderCourseChatLog(){const log=document.querySelector('#course-chat-log');if(!log)return;log.innerHTML=state.courseQuestions.length?state.courseQuestions.map(message=>'<div class="course-chat-message '+esc(message.role)+' '+(message.status==='pending'?'pending':'')+'">'+esc(message.text)+'</div>').join(''):'<div class="course-chat-empty">Ask about the current lesson. The tutor receives this lesson, not a client-authored transcript.</div>';log.scrollTop=log.scrollHeight}function setCourseChatOpen(open){state.courseChatOpen=open;const panel=document.querySelector('#course-chat-panel');const toggle=document.querySelector('#course-chat-toggle');panel.hidden=!open;toggle.hidden=open;toggle.setAttribute('aria-expanded',String(open));if(open){renderCourseChatLog();setTimeout(()=>document.querySelector('#course-chat-input').focus(),0)}}async function submitCourseQuestion(){ if(state.courseSubmitting||!state.session?.courseChatEnabled||!state.session?.csrfToken||!state.course)return;const input=document.querySelector('#course-chat-input');const question=input.value.trim();if(!question)return;const lesson=state.course.lessons.find(value=>value.id===state.courseLessonId)??state.course.lessons[0];if(!lesson)return; state.courseSubmitting=true;const requestId=crypto.randomUUID();const pending={id:requestId,role:'tutor',status:'pending',text:'Submitting…'};state.courseQuestions.push({id:requestId+'-user',role:'user',status:'complete',text:question},pending);input.value='';syncCourseChat(); try{const receipt=await fetchJson('api/courses/post-training/questions','course question',{method:'POST',headers:{'content-type':'application/json','x-thoughtstream-csrf':state.session.csrfToken},body:JSON.stringify({requestId,courseRevision:state.course.revision,lessonId:lesson.id,question})});const stored={eventId:receipt.eventId,statusPath:receipt.statusPath,lessonId:lesson.id,createdAt:Date.now()};saveCourseReceipt(stored);pending.text='Waiting for the tutor…';state.courseSubmitting=false;syncCourseChat();void pollCourseQuestion(stored.statusPath,pending,true)}catch(error){state.courseSubmitting=false;pending.status='failed';pending.text=error instanceof Error?error.message:'The tutor could not accept the question.';syncCourseChat()}}function resumeCourseReceipts(){state.courseReceiptsResumed=true;for(const receipt of state.coursePendingReceipts){if(state.courseQuestions.some(message=>message.id===receipt.eventId))continue;const pending={id:receipt.eventId,role:'tutor',status:'pending',text:'Resuming the saved tutor receipt…'};state.courseQuestions.push({id:receipt.eventId+'-user',role:'user',status:'complete',text:'Question submitted before this reload.'},pending);void pollCourseQuestion(receipt.statusPath,pending,true)}renderCourseChatLog()}async function pollCourseQuestion(path,pending,removeTerminal){const deadline=Date.now()+10*60*1000;let delay=900;while(Date.now()<deadline){if(state.tab!=='learn'||document.hidden){await new Promise(resolve=>setTimeout(resolve,1000));continue}try{const status=await fetchJson(path,'course answer');if(status.status==='completed'){pending.status='complete';pending.text=status.answer;if(removeTerminal)removeCourseReceipt(path);renderCourseChatLog();return}if(status.status==='failed'||status.status==='not-found'){pending.status='failed';pending.text='The tutor could not answer this question.';if(removeTerminal)removeCourseReceipt(path);renderCourseChatLog();return}pending.text=status.status==='running'?'Working through the lesson…':'Waiting for the tutor…'}catch{pending.text='Connection interrupted. The receipt is saved in this tab.'}renderCourseChatLog();await new Promise(resolve=>setTimeout(resolve,delay));delay=Math.min(5000,Math.round(delay*1.45))}pending.status='failed';pending.text='The tutor is still working. Reload this tab to resume the saved receipt.';renderCourseChatLog()}function startLiveUpdates(){const stream=new EventSource('api/live');stream.addEventListener('change',()=>scheduleActivityRefresh())}let activityRefreshTimer;function scheduleActivityRefresh(){clearTimeout(activityRefreshTimer);activityRefreshTimer=setTimeout(()=>void refreshActivity(),120)}async function refreshActivity(){if(state.activityRefreshInFlight){state.activityRefreshPending=true;return}state.activityRefreshInFlight=true;try{state.activity=await fetchJson(resources.activity.path+'?fresh=1',resources.activity.label);delete state.errors.activity;renderList();await loadActivityEvidence(true);if(state.tab==='runs'||state.tab==='sources')await loadResource('system');if(state.selected?.kind==='source')await showSource(state.selected.id);else if(state.selected&&(state.selected.kind==='event'||state.selected.kind==='run'))await select(state.selected.kind,state.selected.id,{preserveScroll:true})}catch(error){state.errors.activity=error instanceof Error?error.message:String(error)}finally{state.activityRefreshInFlight=false;if(state.activityRefreshPending){state.activityRefreshPending=false;scheduleActivityRefresh()}}}const inputSourceKinds=new Set(['filesystem','rss','jetstream','fastmail','telegram','x-webhook']);function isInputSource(source){return inputSourceKinds.has(source.kind)}function filterSources(sources){return state.sourceScope==='all'?sources:sources.filter(source=>state.sourceScope==='inputs'?isInputSource(source):!isInputSource(source))}function renderSourceFilter(){const bar=document.querySelector('#filter-bar');showFilterControls('Filter sources');if(document.querySelector('#filters-dialog').open&&bar.querySelector('#source-scope-filter')){renderActiveFilters();return}bar.innerHTML='<label class="field-label" for="source-scope-filter">Source class</label><select id="source-scope-filter" aria-label="Filter source registry"><option value="inputs" '+(state.sourceScope==='inputs'?'selected':'')+'>Inputs</option><option value="internal" '+(state.sourceScope==='internal'?'selected':'')+'>Internal producers</option><option value="all" '+(state.sourceScope==='all'?'selected':'')+'>All source records</option></select>';bar.querySelector('#source-scope-filter').addEventListener('change',event=>{state.sourceScope=event.target.value;renderList()});renderActiveFilters()}function filterEvents(events){const filters=state.eventFilters;const query=filters.query.trim().toLowerCase();return events.filter(event=>{const observation=observationPresentation(event);if(filters.source!=='all'&&event.source!==filters.source)return false;if(filters.activity!=='all'&&observation.title!==filters.activity)return false;const processed=(event.consumerRuns??[]).length>0;const processingComplete=event.consumerRunsComplete!==false;if(filters.processing==='processed'&&!processed)return false;if(filters.processing==='unprocessed'&&(processed||!processingComplete))return false;if(query&&!([observation.title,observation.body,observation.objectLabel,observation.sourceLabel,event.summary,event.source,event.type].join(' ').toLowerCase().includes(query)))return false;return true})}function renderEventFilters(events){const bar=document.querySelector('#filter-bar');showFilterControls('Filter feed');if(document.querySelector('#filters-dialog').open&&bar.querySelector('#event-query')){renderActiveFilters();return}const sourceMap=new Map((state.system?.sources??[]).map(source=>[source.source,sourceLabelFromId(source.source)]));events.forEach(event=>sourceMap.set(event.source,observationSourceLabel(event)));const sourceOptions=[...sourceMap].sort((a,b)=>a[1].localeCompare(b[1])).map(([value,label])=>'<option value="'+esc(value)+'" '+(state.eventFilters.source===value?'selected':'')+'>'+esc(label)+'</option>').join('');const activities=[...new Set(events.map(event=>observationPresentation(event).title))].sort().map(value=>'<option value="'+esc(value)+'" '+(state.eventFilters.activity===value?'selected':'')+'>'+esc(value)+'</option>').join('');bar.innerHTML='<label class="field-label" for="event-query">Search</label><input id="event-query" type="search" placeholder="Search observations" value="'+esc(state.eventFilters.query)+'"><label class="field-label" for="event-source-filter">Source</label><select id="event-source-filter" aria-label="Filter observations by source"><option value="all">All sources</option>'+sourceOptions+'</select><label class="field-label" for="event-activity-filter">Activity</label><select id="event-activity-filter" aria-label="Filter observations by activity"><option value="all">All activity</option>'+activities+'</select><label class="field-label" for="event-processing-filter">Processing</label><select id="event-processing-filter" aria-label="Filter observations by processing"><option value="all">Any processing</option><option value="processed" '+(state.eventFilters.processing==='processed'?'selected':'')+'>Processed</option><option value="unprocessed" '+(state.eventFilters.processing==='unprocessed'?'selected':'')+'>Not processed</option></select>';bar.querySelector('#event-source-filter').addEventListener('change',event=>{state.eventFilters.source=event.target.value;renderList()});bar.querySelector('#event-activity-filter').addEventListener('change',event=>{state.eventFilters.activity=event.target.value;renderList()});bar.querySelector('#event-processing-filter').addEventListener('change',event=>{state.eventFilters.processing=event.target.value;renderList()});bar.querySelector('#event-query').addEventListener('input',event=>{state.eventFilters.query=event.target.value;renderList()});renderActiveFilters()}function showFilterControls(title){document.querySelector('#filter-open').hidden=false;document.querySelector('#filter-title').textContent=title}function hideFilterControls(){document.querySelector('#filter-open').hidden=true;document.querySelector('#active-filters').hidden=true;document.querySelector('#filter-bar').innerHTML='';const dialog=document.querySelector('#filters-dialog');if(dialog.open)dialog.close()}function activeFilters(){if(state.tab==='sources')return state.sourceScope==='inputs'?[]:[{key:'scope',label:state.sourceScope==='internal'?'Internal producers':'All sources'}];const entries=[];if(state.eventFilters.source!=='all')entries.push({key:'source',label:sourceLabelFromId(state.eventFilters.source)});if(state.eventFilters.activity!=='all')entries.push({key:'activity',label:state.eventFilters.activity});if(state.eventFilters.processing!=='all')entries.push({key:'processing',label:state.eventFilters.processing==='processed'?'Processed':'Not processed'});if(state.eventFilters.query.trim())entries.push({key:'query',label:'“'+state.eventFilters.query.trim()+'”'});return entries}function renderActiveFilters(){const entries=activeFilters();const container=document.querySelector('#active-filters');const count=document.querySelector('#filter-count');container.hidden=entries.length===0;count.hidden=entries.length===0;count.textContent=String(entries.length);container.innerHTML=entries.map(entry=>'<button class="filter-chip" data-filter="'+esc(entry.key)+'">'+esc(entry.label)+' ×</button>').join('');container.querySelectorAll('[data-filter]').forEach(button=>button.addEventListener('click',()=>{clearFilter(button.dataset.filter);renderList()}))}function clearFilter(key){if(key==='scope'){state.sourceScope='inputs';return}if(key==='source')state.eventFilters.source='all';if(key==='activity')state.eventFilters.activity='all';if(key==='processing')state.eventFilters.processing='all';if(key==='query')state.eventFilters.query=''}function eventFilterEmptyMessage(){if(state.eventFilters.source!=='all'){const source=state.system?.sources?.find(value=>value.source===state.eventFilters.source);if(source&&!source.hasActivityEvidence)return sourceLabelFromId(source.source)+' is configured but has no durable activity yet.'}return 'No observations match these filters.'}function eventItem(event){ const observation=observationPresentation(event); const consumers=(event.consumerRuns??[]).map(run=>'<div class="consumer"><strong>'+esc(agentName(run.agentId))+'</strong>'+(run.status==='completed'?'':' <span class="badge '+(run.status==='failed'?'failed':'')+'">'+esc(run.status)+'</span>')+'<br>'+esc(run.description)+'</div>').join(''); const outsideWindow=!state.activity.processingPending&&!consumers&&event.consumerRunsComplete===false?'<div class="processing-window">Earlier processing may be outside this window.</div>':''; const processing=(consumers?'<details class="feed-processing"><summary>'+event.consumerRuns.length+' processor'+(event.consumerRuns.length===1?'':'s')+'</summary>'+consumers+'</details>':'')+outsideWindow; const links=observation.renderer==='bluesky-post'?observationAnchor(observation.parentLabel,observation.parentUrl):observationAnchor(observation.urlLabel,observation.url)+observationAnchor(observation.parentLabel,observation.parentUrl); const source=observation.renderer==='generic'?esc(observation.sourceLabel)+' · ':''; return '<article class="item event-item" tabindex="0" data-kind="event" data-id="'+esc(event.id)+'">'+renderSourceInlay(event,observation)+'<div class="meta">'+source+esc(new Date(event.occurredAt).toLocaleString())+'</div>'+(links?'<div class="feed-links">'+links+'</div>':'')+processing+'</article>';}const sourceRenderers={ 'telegram-message':renderTelegramInlay, 'email-message':renderEmailInlay, 'bluesky-post':renderBlueskyInlay, 'x-post':renderXInlay,};function renderSourceInlay(event,observation,showActivity=true){ const activity=showActivity?'<div class="activity-kind">'+esc(observation.title)+'</div>':''; const renderer=sourceRenderers[observation.renderer]??renderGenericInlay; return activity+renderer(event,observation);}function renderTelegramInlay(_event,observation){const telegram=observation.telegram??{};const notes=[];if(telegram.edited)notes.push('edited');if(telegram.attachmentCount)notes.push(telegram.attachmentCount+' attachment'+(telegram.attachmentCount===1?'':'s'));return '<div class="source-inlay telegram-inlay"><div class="telegram-author">'+esc(telegram.senderName??'Telegram sender')+'</div><div class="telegram-text">'+esc(observation.body||observation.objectLabel)+'</div>'+(notes.length?'<div class="telegram-note">'+esc(notes.join(' · '))+'</div>':'')+'</div>'}function renderEmailInlay(_event,observation){const email=observation.email??{};const sender=email.senderName&&email.senderEmail?email.senderName+' · '+email.senderEmail:email.senderName||email.senderEmail||'Fastmail';const notes=[];if(email.attachmentCount)notes.push(email.attachmentCount+' attachment'+(email.attachmentCount===1?'':'s'));return '<div class="source-inlay email-inlay"><div class="email-author">'+esc(sender)+'</div><div class="email-subject">'+esc(email.subject||observation.objectLabel)+'</div>'+(observation.body?'<div class="email-preview">'+esc(observation.body)+'</div>':'')+(notes.length?'<div class="email-note">'+esc(notes.join(' · '))+'</div>':'')+'</div>'}function renderBlueskyInlay(_event,observation){return observation.bluesky?.uri?'<div class="source-inlay bluesky-inlay loading" data-bsky-uri="'+esc(observation.bluesky.uri)+'" data-bsky-cid="'+esc(observation.bluesky.cid??'')+'"><span class="spinner" aria-hidden="true"></span>Loading post…</div>':renderGenericInlay(_event,observation)}function renderXInlay(_event,observation){const x=observation.x??{};const author=x.authorHandle?'@'+x.authorHandle:'X account';const profile=x.authorHandle?'https://x.com/'+encodeURIComponent(x.authorHandle):observation.url;const authorLink=profile?'<a class="x-author" href="'+esc(profile)+'" target="_blank" rel="noreferrer">'+esc(author)+'</a>':'<div class="x-author">'+esc(author)+'</div>';const time=x.createdAt&&observation.url?'<a class="x-time" href="'+esc(observation.url)+'" target="_blank" rel="noreferrer">'+esc(new Date(x.createdAt).toLocaleString([], {month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}))+'</a>':'';const relation=x.replyToHandle?'<div class="x-relation">Replying to <a href="https://x.com/'+esc(encodeURIComponent(x.replyToHandle))+'" target="_blank" rel="noreferrer">@'+esc(x.replyToHandle)+'</a></div>':'';const notes=[];if(x.attachmentCount)notes.push(x.attachmentCount+' media attachment'+(x.attachmentCount===1?'':'s'));if(x.language&&x.language!=='en')notes.push(x.language.toUpperCase());if(x.possiblySensitive)notes.push('marked sensitive');return '<div class="source-inlay x-inlay"><div class="x-head">'+authorLink+time+'</div>'+relation+'<div class="x-text">'+esc(observation.body||observation.objectLabel)+'</div>'+(notes.length?'<div class="x-note">'+esc(notes.join(' · '))+'</div>':'')+'</div>'}function renderGenericInlay(_event,observation){return '<div class="summary">'+esc(observation.body||observation.objectLabel)+'</div>'}let inlayObserver;function hydrateInlays(root){ inlayObserver?.disconnect();const nodes=[...root.querySelectorAll('.bluesky-inlay[data-bsky-uri]')];if(!nodes.length)return; const observe=node=>{const key=node.dataset.bskyUri+'|'+(node.dataset.bskyCid??'');const cached=state.inlays.get(key);if(cached){paintBlueskyInlay(node,cached);return}void loadBlueskyInlay(node,key)}; if(!('IntersectionObserver'in window)){nodes.forEach(observe);return} inlayObserver=new IntersectionObserver(entries=>entries.forEach(entry=>{if(!entry.isIntersecting)return;inlayObserver.unobserve(entry.target);observe(entry.target)}),{rootMargin:'240px 0px'});nodes.forEach(node=>inlayObserver.observe(node));}async function loadBlueskyInlay(node,key){ try{const query=new URLSearchParams({uri:node.dataset.bskyUri});if(node.dataset.bskyCid)query.set('cid',node.dataset.bskyCid);const post=await fetchJson('api/inlays/bluesky?'+query.toString(),'Bluesky post');state.inlays.set(key,post);if(node.isConnected)paintBlueskyInlay(node,post)}catch{if(!node.isConnected)return;const url=bskyPostUrl(node.dataset.bskyUri);node.className='source-inlay bluesky-inlay failed';node.innerHTML=url?'<a href="'+esc(url)+'" target="_blank" rel="noreferrer">Open post on Bluesky</a>':'Post unavailable'}}function paintBlueskyInlay(node,post){node.className='source-inlay bluesky-inlay';node.innerHTML=renderBlueskyPost(post);bindBlueskyMediaFailures(node)}function bindBlueskyMediaFailures(node){ node.querySelectorAll('.bsky-images img').forEach(image=>image.addEventListener('error',()=>{const grid=image.closest('.bsky-images');image.closest('a')?.remove();if(!grid?.querySelector('img'))grid?.remove()},{once:true})); node.querySelectorAll('.bsky-avatar,.bsky-external img').forEach(image=>image.addEventListener('error',()=>image.remove(),{once:true}));}function renderBlueskyPost(post){ const author=post.author??{};const profile='https://bsky.app/profile/'+encodeURIComponent(author.handle??'');const avatar=author.avatar?'<img class="bsky-avatar" src="'+esc(author.avatar)+'" alt="">':'<span class="bsky-avatar"></span>';const date=post.createdAt?'<a class="bsky-time" href="'+esc(post.url)+'" target="_blank" rel="noreferrer">'+esc(new Date(post.createdAt).toLocaleDateString())+'</a>':''; const images=(post.images??[]).map(image=>'<a href="'+esc(image.fullsize)+'" target="_blank" rel="noreferrer"><img src="'+esc(image.thumb)+'" alt="'+esc(image.alt)+'" loading="lazy"></a>').join(''); const imageBlock=images?'<div class="bsky-images '+(post.images.length===1?'single':'')+'">'+images+'</div>':''; const external=post.external?'<a class="bsky-external" href="'+esc(post.external.uri)+'" target="_blank" rel="noreferrer">'+(post.external.thumb?'<img src="'+esc(post.external.thumb)+'" alt="" loading="lazy">':'')+'<span class="bsky-external-body"><span class="bsky-external-title">'+esc(post.external.title)+'</span><span class="bsky-external-description">'+esc(post.external.description)+'</span></span></a>':''; const quote=post.quote?'<a class="bsky-quote" href="'+esc(post.quote.url)+'" target="_blank" rel="noreferrer"><div class="bsky-quote-author">'+esc(post.quote.author.displayName)+' · @'+esc(post.quote.author.handle)+'</div><div class="bsky-quote-text">'+esc(post.quote.text)+'</div></a>':''; const counts=post.counts??{};const stats='<div class="bsky-stats"><span>'+compactCount(counts.replies)+' replies</span><span>'+compactCount(counts.reposts)+' reposts</span><span>'+compactCount(counts.likes)+' likes</span></div>'; return '<div class="bsky-header"><a class="bsky-avatar-link" href="'+esc(profile)+'" target="_blank" rel="noreferrer">'+avatar+'</a><a class="bsky-author" href="'+esc(profile)+'" target="_blank" rel="noreferrer"><div class="bsky-name">'+esc(author.displayName??author.handle)+'</div><div class="bsky-handle">@'+esc(author.handle)+'</div></a>'+date+'</div><div class="bsky-text">'+esc(post.text)+'</div>'+imageBlock+external+quote+stats;}function compactCount(value){const count=Number(value??0);return count>=1000000?(count/1000000).toFixed(count>=10000000?0:1)+'m':count>=1000?(count/1000).toFixed(count>=10000?0:1)+'k':String(count)}function runItem(run){const evidence=state.system.runEvidence.find(report=>report.runId===run.id);const kind=isModelRun(run)?'model':'rule';return '<button class="item" data-kind="run" data-id="'+esc(run.id)+'"><div class="type">'+esc(agentName(run.agentId))+' <span class="badge">'+kind+'</span> <span class="badge '+(run.status==='failed'?'failed':'')+'">'+esc(run.status)+'</span>'+(evidence&&!evidence.consistent?'<span class="badge failed">evidence contradiction</span>':'')+'</div><div class="summary">'+esc(run.summary??run.errorText??run.id)+'</div><div class="meta">LLM inference: '+(kind==='model'?'yes':'no')+' · '+esc(run.startedAt??run.createdAt)+' · '+esc(run.provider)+'/'+esc(run.model)+'</div></button>'}function sourceItem(source){ const runtime=source.control?.runtime;const upstream=source.control?.upstream;const badges=[]; badges.push('<span class="badge">'+(isInputSource(source)?esc(humanLabel(source.kind))+' input':'internal producer')+'</span>'); if(source.configured)badges.push('<span class="badge '+(source.enabled?'':'failed')+'">'+(source.enabled?'configured':'disabled')+'</span>'); if(runtime)badges.push('<span class="badge '+(runtime.current?'':'failed')+'">runtime '+(runtime.current?'ready':'stale')+'</span>'); if(upstream)badges.push('<span class="badge '+(upstream.current&&upstream.registered&&upstream.valid?'':'failed')+'">provider '+(upstream.registered&&upstream.valid?'registered':'unavailable')+'</span>'); badges.push('<span class="badge '+(source.status==='failing'?'failed':'')+'">'+(source.hasActivityEvidence?(source.status==='unknown'?'activity recorded':'activity '+source.status):'awaiting first activity')+'</span>'); const summary=upstream?(upstream.liveSubscriptionCount+'/'+upstream.desiredSubscriptionCount+' subscriptions '+(upstream.subscriptionsConverged?'converged':'drifting')):source.hasActivityEvidence?(source.recordCount+' durable record'+(source.recordCount===1?'':'s')):'No durable activity yet.'; const meta=source.status==='failing'?'currently failing · last failure '+(source.lastFailureAt?new Date(source.lastFailureAt).toLocaleString():'time unavailable'):source.lastSuccessAt?'last activity '+new Date(source.lastSuccessAt).toLocaleString():(source.hasActivityEvidence?'activity timestamp unavailable':'waiting for the first observation'); return '<button class="item" data-kind="source" data-id="'+esc(source.source)+'"><div class="type">'+esc(source.source)+' '+badges.join(' ')+'</div><div class="summary">'+esc(summary)+'</div><div class="meta">'+esc(meta)+'</div></button>';}function suggestionItem(item){return '<button class="item" data-kind="suggestion" data-id="'+esc(item.eventId)+'"><div class="type">'+esc(item.kind)+' <span class="badge '+(item.status==='Failed'?'failed':'')+'">'+esc(item.status)+'</span></div><div class="summary">'+esc(item.proposedText.slice(0,240))+'</div><div class="meta">'+esc(item.reason)+' · '+esc(new Date(item.observedAt).toLocaleString())+'</div></button>'}function reviewItem(item){const status=item.decision?item.decision.disposition:'unreviewed';return '<button class="item" data-kind="review" data-id="'+esc(item.id)+'"><div class="type">'+esc(item.campaign.label)+' <span class="badge">'+esc(status)+'</span></div><div class="summary">'+esc(item.prompt.slice(0,240))+'</div><div class="meta">'+esc(item.criterion.label)+' · '+esc(item.privacy)+(item.decision?.externalExportEligible?' · export eligible':'')+'</div></button>'}async function select(kind,id,options={}){ const route=routeDetails[kind];if(!route)return;state.tab=route.tab;state.selected={kind,id};syncNavigation();document.querySelector('main').classList.add('detail-selected');document.body.classList.add('detail-selected');if(options.history!==false)writeRoute();if(!options.preserveScroll)window.scrollTo({top:0,behavior:'smooth'}); if(kind==='source'){await showSource(id);return} if(kind==='document'){await showDocument(id);return} if(kind==='suggestion'){const detail=document.querySelector('#detail');if(state.errors.proposals){detail.innerHTML='<div class="empty failed">'+esc(state.errors.proposals)+'</div>';return}if(!state.proposals){detail.innerHTML='<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading suggestion…</div>';return}const item=state.proposals.items.find(value=>value.eventId===id);detail.innerHTML=renderSuggestion(item);bindSuggestionForm(item);return} if(kind==='review'){const detail=document.querySelector('#detail');if(state.errors.reviews){detail.innerHTML='<div class="empty failed">'+esc(state.errors.reviews)+'</div>';return}if(!state.reviews){detail.innerHTML='<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading review…</div>';return}const item=state.reviews.items.find(value=>value.id===id);detail.innerHTML=renderReview(item);bindReviewForm(item);return} if(kind==='artifact'){document.querySelector('#detail').innerHTML='<div class="empty">Loading artifact…</div>';const response=await fetch('api/artifacts/'+encodeURIComponent(id));const data=await response.json();document.querySelector('#detail').innerHTML=renderArtifact(data);return} const selection=kind+':'+id;const label=kind==='event'?'observation':'run';const detail=document.querySelector('#detail'); detail.innerHTML='<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading '+label+'…</div>'; try{const data=await fetchJson('api/'+(kind==='event'?'events/':'runs/')+encodeURIComponent(id),label);if(state.selected?.kind+':'+state.selected?.id!==selection)return;detail.innerHTML=kind==='event'?renderEvent(data):renderRun(data);document.querySelectorAll('#detail [data-event-id]').forEach(button=>button.addEventListener('click',()=>select('event',button.dataset.eventId)));if(kind==='event')bindWorkbenchCreate(data);hydrateInlays(detail)}catch(error){if(state.selected?.kind+':'+state.selected?.id!==selection)return;detail.innerHTML='<div class="empty failed">'+esc(error instanceof Error?error.message:String(error))+'<div><button class="retry" id="detail-retry">Retry</button></div></div>';document.querySelector('#detail-retry')?.addEventListener('click',()=>select(kind,id))}}async function showSource(id){const detail=document.querySelector('#detail');detail.innerHTML='<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading source activity…</div>';try{const data=await fetchJson('api/sources/'+encodeURIComponent(id),'source activity');if(state.selected?.kind!=='source'||state.selected.id!==id)return;detail.innerHTML=renderSource(data.source,data.activity.items);detail.querySelectorAll('[data-event-id]').forEach(button=>button.addEventListener('click',()=>select('event',button.dataset.eventId)))}catch(error){detail.innerHTML='<div class="empty failed">'+esc(error instanceof Error?error.message:String(error))+'</div>'}}function eventLink(label,event){return event?'<button data-event-id="'+esc(event.id)+'">'+esc(label)+': '+esc(event.type)+' · '+esc(event.id)+'</button>':''}function bskyPostUrl(uri){const parts=String(uri??'').split('/');return parts.length===5&&parts[0]==='at:'&&parts[1]===''&&parts[3]==='app.bsky.feed.post'&&parts[2]&&parts[4]?'https://bsky.app/profile/'+bskyActorPathSegment(parts[2])+'/post/'+encodeURIComponent(parts[4]):''}function sourceLabelFromId(source){if(source==='jetstream:cameron-atproto')return 'Cameron · Bluesky';if(source==='x:public-watch')return 'X public watch';if(source==='x:cameron-public')return 'Cameron · X';if(String(source??'').startsWith('telegram:'))return 'Telegram';if(String(source??'').startsWith('filesystem:'))return 'Filesystem';return humanLabel(source??'Source')}function observationSourceLabel(event){return sourceLabelFromId(event?.source)}function humanPrivacy(value){return value==='public-source'?'public source':value==='sensitive'?'sensitive':'private'}function observationPresentation(event){ if(event?.presentation)return {...event.presentation,renderer:event.presentation.renderer??'generic',sourceLabel:observationSourceLabel(event),url:event.presentation.url??'',urlLabel:event.presentation.urlLabel??'',parentUrl:event.presentation.parentUrl??'',parentLabel:event.presentation.parentLabel??''}; const payload=event?.payload??{};const record=payload.record??{};const operation=payload.operation??'observed'; if(event?.type==='stream.thought.source.atproto.commit'){ if(payload.collection==='app.bsky.feed.post'){ const reply=Boolean(record.reply?.parent?.uri);const deleted=operation==='delete'; return {renderer:'generic',title:deleted?'Deleted a Bluesky post':reply?'Replied on Bluesky':'Posted on Bluesky',body:record.text??'',objectLabel:deleted?'Post removed from Bluesky':'Bluesky post',sourceLabel:observationSourceLabel(event),url:bskyPostUrl(payload.atUri),urlLabel:reply?'Open reply':'Open post',parentUrl:bskyPostUrl(record.reply?.parent?.uri),parentLabel:'Open parent post'}; } if(payload.collection==='app.bsky.feed.like'){ const removed=operation==='delete';return {renderer:'generic',title:removed?'Removed a like':'Liked a post',body:'',objectLabel:'Bluesky post',sourceLabel:observationSourceLabel(event),url:bskyPostUrl(record.subject?.uri),urlLabel:removed?'Open affected post':'Open liked post'}; } if(payload.collection==='app.bsky.feed.repost'){ const removed=operation==='delete';return {renderer:'generic',title:removed?'Removed a repost':'Reposted on Bluesky',body:'',objectLabel:'Bluesky post',sourceLabel:observationSourceLabel(event),url:bskyPostUrl(record.subject?.uri),urlLabel:'Open post'}; } if(payload.collection==='app.bsky.graph.follow'){ const removed=operation==='delete';const subject=record.subject;return {renderer:'generic',title:removed?'Stopped following an account':'Followed an account',body:'',objectLabel:'Bluesky account',sourceLabel:observationSourceLabel(event),url:subject?'https://bsky.app/profile/'+bskyActorPathSegment(subject):'',urlLabel:'Open profile'}; } } if(event?.type==='stream.thought.source.email.observed'){ const finalOperation=operation==='created'||operation==='destroyed'?operation:'updated';const sender=Array.isArray(payload.from)?payload.from[0]??{}:{};const subject=typeof payload.subject==='string'?payload.subject:'';const preview=typeof payload.preview==='string'?payload.preview:'';const attachments=Array.isArray(payload.attachments)?payload.attachments:[]; return {renderer:'email-message',title:finalOperation==='created'?'New email':finalOperation==='destroyed'?'Email removed':'Email updated',body:finalOperation==='destroyed'?'':preview,objectLabel:subject||(finalOperation==='destroyed'?'Message removed':'Email without a subject'),sourceLabel:'Fastmail',url:'',urlLabel:'',parentUrl:'',parentLabel:'',email:{operation:finalOperation,senderName:typeof sender.name==='string'?sender.name:'',senderEmail:typeof sender.email==='string'?sender.email:'',subject,attachmentCount:attachments.length}}; } const finalType=String(event?.type??'observation').split('.').at(-1)??'observation'; const body=payload.text??payload.title??payload.summary??payload.path??''; return {renderer:'generic',title:humanLabel(finalType),body,objectLabel:humanLabel(payload.collection?.split('.').at(-1)??'Observation'),sourceLabel:observationSourceLabel(event),url:'',urlLabel:'',parentUrl:'',parentLabel:''};}function observationAnchor(label,url){return url?'<a href="'+esc(url)+'" target="_blank" rel="noreferrer">'+esc(label)+'</a>':''}function renderEvent(data){ if(data.error)return '<div class="failed">'+esc(data.error)+'</div>'; const e=data.event; const observation=data.presentation?observationPresentation({...e,presentation:data.presentation}):observationPresentation(e); const work=(data.agentActivities??[]).map(renderAgentWork).join(''); const processing=work?'<h3>Processing</h3>'+work:'<div class="processing-state"><span class="status-dot"></span>No agent processed this observation.</div>'; const links=observationAnchor(observation.urlLabel,observation.url)+observationAnchor(observation.parentLabel,observation.parentUrl); const content='<div class="observation-card">'+renderSourceInlay(e,observation,false)+(links?'<div class="observation-links">'+links+'</div>':'')+'</div>'; const lineage=(data.root?.id!==e.id?eventLink('root',data.root):'')+(data.parent?eventLink('parent',data.parent):'')+data.children.map(child=>eventLink('child',child)).join(''); const technical='<details class="technical-record"><summary>Technical details</summary><div class="facts"><span>Event type: <strong>'+esc(e.type)+'</strong></span><span>Source: <strong>'+esc(e.source)+'</strong></span><span>Occurred: <strong>'+esc(e.occurredAt)+'</strong></span><span>Observed: <strong>'+esc(e.observedAt)+'</strong></span></div><div class="technical-section"><h3>Event ID</h3><div class="mono">'+esc(e.id)+'</div></div>'+(lineage?'<div class="technical-section"><h3>Lineage and execution records</h3><div class="lineage">'+lineage+'</div></div>':'')+'<div class="technical-section"><h3>Raw payload</h3><pre>'+json(e.payload)+'</pre></div><div class="technical-section"><h3>Canonical envelope</h3><pre>'+json(e)+'</pre></div></details>'; return '<div class="observation-head"><h2>'+esc(observation.title)+'</h2><div class="meta">'+esc(observation.sourceLabel)+' · '+esc(new Date(e.occurredAt).toLocaleString())+' · '+esc(humanPrivacy(e.privacy))+'</div></div>'+content+renderWorkbenchCreate(e,observation)+processing+technical;}
function documentItem(item){return '<button class="item" data-kind="document" data-id="'+esc(item.documentId)+'"><div class="type">Working document'+(item.pendingProposalCount?' <span class="badge">'+esc(item.pendingProposalCount)+' proposal'+(item.pendingProposalCount===1?'':'s')+' waiting</span>':'')+'</div><div class="summary">'+esc(item.title)+'</div><div class="meta">'+esc(item.versionCount)+' version'+(item.versionCount===1?'':'s')+' · '+esc(item.proposalCount)+' proposal'+(item.proposalCount===1?'':'s')+' · updated '+esc(new Date(item.updatedAt).toLocaleString())+'</div></button>'}function shortId(value){const text=String(value??'');return text.length>18?text.slice(0,10)+'…'+text.slice(-6):text}function workbenchDefaultTitle(observation){const firstLine=(observation.body||'').split(/\\r?\\n/).map(line=>line.trim()).find(Boolean)||'';const label=observation.objectLabel&&!['Observation','Message'].includes(observation.objectLabel)?observation.objectLabel:'';const base=(firstLine||label||observation.title||'observation').slice(0,80);return ('Notes: '+base).slice(0,200)}function renderWorkbenchCreate(e,observation){ if(!state.session?.reviewWriteEnabled)return '<div class="wb-note">Creating a working document needs the operator session with write access; this session is read-only.</div>'; return '<div class="wb-create" id="wb-create"><div class="form-row"><label for="wb-create-title">Working document title</label><input id="wb-create-title" type="text" maxlength="200" value="'+esc(workbenchDefaultTitle(observation))+'"></div><div class="wb-actions"><button id="wb-create-button">Create working document</button><span id="wb-create-result" class="wb-result"></span></div></div>';}function bindWorkbenchCreate(data){const button=document.querySelector('#wb-create-button');if(!button)return;const e=data.event;const observation=data.presentation?observationPresentation({...e,presentation:data.presentation}):observationPresentation(e);button.addEventListener('click',async()=>{const title=document.querySelector('#wb-create-title').value.trim();const result=document.querySelector('#wb-create-result');if(!title){result.textContent='Enter a title first.';return}button.disabled=true;result.textContent='Creating…';const body='# '+title+'\\n\\nCreated from '+(observation.title||e.type)+' ('+e.type+').\\n\\n'+(observation.body?'> '+observation.body.split(/\\r?\\n/).join('\\n> ')+'\\n':'');try{const created=await workbenchPost('api/workbench/documents',{originEventId:e.id,title,body},'create:'+e.id);await loadResource('documents');select('document',created.documentId)}catch(error){result.textContent=workbenchErrorText(error);button.disabled=false}})}function workbenchErrorText(error){return error?.uncertain?'The request was sent but no response arrived. Reload to see whether it applied; a retry reuses the same request id.':error?.message??String(error)}async function workbenchPost(path,body,key){ const requests=state.workbench.requests;const request=requests[key]??(requests[key]={id:crypto.randomUUID(),status:'idle'}); if(request.status==='uncertain'&&state.workbench.documentId){await reloadWorkbenchDocument({quiet:true})} const idField=key.startsWith('decision:')?'submissionId':'requestId';const payload={...body,[idField]:request.id};request.status='sending'; let response;try{response=await fetch(path,{method:'POST',headers:{'content-type':'application/json','x-thoughtstream-csrf':state.session?.csrfToken??''},body:JSON.stringify(payload)})}catch{request.status='uncertain';const error=new Error('uncertain');error.uncertain=true;throw error} let data={};try{data=await response.json()}catch{} delete requests[key]; if(!response.ok){const error=new Error(data.error??('Request failed (HTTP '+response.status+')'));error.status=response.status;error.data=data;throw error} return data;}function draftKey(documentId){return 'thoughtstream-workbench-draft:'+documentId}function readDraft(documentId){try{const value=JSON.parse(localStorage.getItem(draftKey(documentId))??'null');return value&&typeof value.title==='string'&&typeof value.body==='string'&&typeof value.baseVersionId==='string'?value:null}catch{return null}}function writeDraft(documentId,draft){try{localStorage.setItem(draftKey(documentId),JSON.stringify(draft))}catch{}}function clearDraft(documentId){try{localStorage.removeItem(draftKey(documentId))}catch{}}async function showDocument(id){const detail=document.querySelector('#detail');state.workbench.documentId=id;state.workbench.detail=null;state.workbench.candidates=null;state.workbench.selecting=false;state.workbench.checked={events:new Set(),versions:new Set()};detail.innerHTML='<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading working document…</div>';await reloadWorkbenchDocument()}async function reloadWorkbenchDocument(options={}){const id=state.workbench.documentId;const detail=document.querySelector('#detail');try{const data=await fetchJson('api/workbench/documents/'+encodeURIComponent(id),'working document');if(state.selected?.kind!=='document'||state.selected.id!==id)return;state.workbench.detail=data;renderWorkbenchDocument(options)}catch(error){if(options.quiet)return;detail.innerHTML='<div class="empty failed">'+esc(error instanceof Error?error.message:String(error))+'<div><button class="retry" id="wb-retry">Retry</button></div></div>';document.querySelector('#wb-retry')?.addEventListener('click',()=>reloadWorkbenchDocument())}}function renderDiff(diff){return '<pre class="diff">'+String(diff??'').split('\\n').filter(line=>!line.startsWith('Index: ')&&!/^=+$/.test(line)).map(line=>{const kind=line.startsWith('+++')||line.startsWith('---')?'file':line.startsWith('@@')?'hunk':line.startsWith('+')?'add':line.startsWith('-')?'del':'ctx';return '<span class="'+kind+'">'+esc(line)+'</span>'}).join('')+'</pre>'}function renderWorkbenchProposal(proposal,canWrite){ const badge=proposal.status==='accepted'?'accepted':proposal.status==='rejected'?'rejected':proposal.status==='stale'?'stale base':'awaiting decision'; const decision=proposal.decision?'<div class="wb-note">Decision '+esc(proposal.decision.disposition)+' · '+esc(new Date(proposal.decision.createdAt).toLocaleString())+(proposal.decision.resultVersionId?' · produced version '+esc(shortId(proposal.decision.resultVersionId)):'')+(proposal.decision.judgmentEventId?' · judgment recorded':' · no judgment recorded')+'</div>':''; const stale=!proposal.decision&&proposal.stale?'<div class="wb-warn">This proposal was made against an older version ('+esc(shortId(proposal.baseVersionId))+'). The document head is now '+esc(shortId(proposal.headVersionId))+'. Accept is disabled; request a new proposal, or reject this one.</div>':''; const controls=!proposal.decision&&canWrite?'<div class="wb-actions"><button data-wb-decide="accept" data-proposal="'+esc(proposal.eventId)+'" '+(proposal.stale?'disabled':'')+'>Accept</button><button class="reject" data-wb-decide="reject" data-proposal="'+esc(proposal.eventId)+'">Reject</button><span class="wb-result" id="wb-decide-result-'+esc(proposal.eventId)+'"></span></div>':!proposal.decision?'<div class="wb-note">Deciding needs the operator session with write access.</div>':''; const evidence=proposal.evidenceEventIds.length?'<div class="wb-note">Cites '+esc(proposal.evidenceEventIds.length)+' selected event'+(proposal.evidenceEventIds.length===1?'':'s')+'</div>':'<div class="wb-note">Cites no selected events</div>'; return '<div class="wb-card" data-proposal-card="'+esc(proposal.eventId)+'"><div class="wb-head"><strong>'+esc(proposal.runnerId)+'@'+esc(proposal.runnerRevision)+'</strong><span class="badge">'+esc(badge)+'</span><span class="badge">'+(proposal.inference==='none'?'no LLM inference':proposal.inference==='model'?'model inference':'inference unknown')+'</span></div><div class="work-summary">'+esc(proposal.reason)+'</div><div class="meta">Proposed '+esc(new Date(proposal.createdAt).toLocaleString())+' · base '+esc(shortId(proposal.baseVersionId))+' · '+esc(proposal.proposedTextChars)+' chars · sha '+esc(proposal.proposedTextSha256.slice(0,12))+'</div>'+evidence+stale+decision+'<details open><summary>Diff against base version</summary>'+renderDiff(proposal.diff)+'</details>'+controls+'</div>';}function renderWorkbenchSelection(selection,latest){ const events=selection.selectedEvents.map(event=>'<div class="wb-source">'+esc(event.type)+' · '+esc(new Date(event.occurredAt).toLocaleString())+'<div class="meta">'+esc(event.excerpt.slice(0,160))+'</div><span class="mono">'+esc(event.eventId)+' · payload '+esc(event.payloadHash.slice(0,12))+'</span></div>').join(''); const versions=selection.selectedVersions.map(version=>'<div class="wb-source">Document version<span class="mono">'+esc(version.versionId)+' · sha '+esc(version.sha256.slice(0,12))+'</span></div>').join(''); const body=(events+versions)||'<div class="wb-note">Empty selection.</div>'; return latest?'<div class="wb-card"><div class="wb-head"><strong>Selected evidence</strong><span class="badge">current</span></div><div class="meta">Recorded '+esc(new Date(selection.createdAt).toLocaleString())+' against version '+esc(shortId(selection.baseVersionId))+' · snapshot '+esc(selection.snapshotSha256.slice(0,12))+'</div>'+body+'</div>':'<details><summary>Earlier selection · '+esc(new Date(selection.createdAt).toLocaleString())+'</summary>'+body+'</details>';}function renderWorkbenchDocument(options={}){ const data=state.workbench.detail;const detail=document.querySelector('#detail');if(!data)return;const canWrite=Boolean(state.session?.reviewWriteEnabled);const head=data.head; const draft=readDraft(head.documentId);const editing=options.keepDraft&&document.querySelector('#wb-body')?{title:document.querySelector('#wb-title').value,body:document.querySelector('#wb-body').value}:null; const useDraft=editing??(draft&&(draft.title!==head.title||draft.body!==head.body)?draft:null); const title=useDraft?useDraft.title:head.title;const body=useDraft?useDraft.body:head.body; const draftNote=useDraft&&!editing?'<div class="wb-warn">Unsaved draft restored from this browser'+(draft.baseVersionId!==head.versionId?' (written against an older version; the document has changed since)':'')+'. <button class="filter-chip" id="wb-discard-draft">Discard draft</button></div>':''; const origin=data.origin?'<button class="source-event" data-event-id="'+esc(data.origin.eventId)+'"><div class="source-event-title">'+esc(humanLabel(data.origin.type.split('.').at(-1)))+' · '+esc(sourceLabelFromId(data.origin.source))+'</div><div class="source-event-body">'+esc(data.origin.excerpt)+'</div><div class="meta">'+esc(new Date(data.origin.occurredAt).toLocaleString())+'</div></button>':'<div class="wb-note">Origin event unavailable.</div>'; const editor='<div class="wb-form"><div class="form-row"><label for="wb-title">Title</label><input id="wb-title" type="text" maxlength="200" value="'+esc(title)+'" '+(canWrite?'':'readonly')+'></div><div class="form-row"><label for="wb-body">Body</label><textarea id="wb-body" class="wb-body" maxlength="64000" '+(canWrite?'':'readonly')+'>'+esc(body)+'</textarea></div>'+(canWrite?'<div class="wb-actions"><button id="wb-save">Save</button><span id="wb-save-result" class="wb-result"></span></div>':'<div class="wb-note">Editing needs the operator session with write access.</div>')+draftNote+'</div>'; const selections=[...data.selections].sort((a,b)=>b.createdAt.localeCompare(a.createdAt));const latest=selections[0]; const sources='<div class="wb-note">Exact evidence the operator selected, recorded by event id and payload hash or by version id and content hash. A runner receives this snapshot plus the current document head and nothing else.</div>'+(latest?renderWorkbenchSelection(latest,true):'<div class="wb-note">No context selected yet.</div>')+selections.slice(1).map(selection=>renderWorkbenchSelection(selection,false)).join('')+(canWrite?'<div class="wb-actions"><button id="wb-select-open" class="quiet">'+(state.workbench.selecting?'Close context picker':'Select context')+'</button><span id="wb-select-result" class="wb-result"></span></div><div id="wb-select-panel" class="wb-panel" '+(state.workbench.selecting?'':'hidden')+'></div>':''); const runnerOptions=(data.runners??[]).map(runner=>'<option value="'+esc(runner.id)+'">'+esc(runner.label)+'</option>').join(''); const request=canWrite?'<div class="wb-actions"><select id="wb-runner" aria-label="Proposal runner">'+runnerOptions+'</select><button id="wb-request" '+(latest?'':'disabled')+'>Request proposal</button><span id="wb-request-result" class="wb-result">'+(latest?'':'Select context first.')+'</span></div>':''; const proposals=[...data.proposals].sort((a,b)=>b.createdAt.localeCompare(a.createdAt)); const proposalList=proposals.length?proposals.map(proposal=>renderWorkbenchProposal(proposal,canWrite)).join(''):'<div class="wb-note">No proposals yet.</div>'; const versions='<div class="wb-list">'+[...data.versions].reverse().map(version=>'<div class="wb-version '+(version.versionId===head.versionId?'head':'')+'"><strong>'+esc(humanLabel(version.reason))+'</strong>'+(version.versionId===head.versionId?' <span class="badge">head</span>':'')+'<div class="meta">'+esc(new Date(version.createdAt).toLocaleString())+' · '+esc(version.sizeBytes)+' bytes · sha '+esc(version.sha256.slice(0,12))+'</div><div class="mono">'+esc(version.versionId)+'</div></div>').join('')+'</div>'; const technical='<details class="technical-record"><summary>Technical details</summary><div class="human-grid"><div class="human-card wide"><div class="field-label">Document ID</div><div class="mono">'+esc(head.documentId)+'</div></div><div class="human-card wide"><div class="field-label">Head version</div><div class="mono">'+esc(head.versionId)+'</div></div><div class="human-card"><div class="field-label">Head SHA-256</div><div class="mono">'+esc(head.sha256)+'</div></div><div class="human-card"><div class="field-label">Created event</div><div class="mono">'+esc(data.createdEventId)+'</div></div></div></details>'; detail.innerHTML='<h2>'+esc(head.title)+'</h2><div class="meta">Working document · head '+esc(shortId(head.versionId))+' · updated '+esc(new Date(head.updatedAt).toLocaleString())+'</div><h3>Created from</h3>'+origin+'<h3>Document</h3>'+editor+'<h3>Sources</h3>'+sources+'<h3>Proposals</h3>'+request+proposalList+'<h3>Versions</h3>'+versions+technical; detail.querySelectorAll('[data-event-id]').forEach(button=>button.addEventListener('click',()=>select('event',button.dataset.eventId))); bindWorkbenchDocument(head,canWrite,latest); if(state.workbench.selecting)renderWorkbenchCandidates();}function bindWorkbenchDocument(head,canWrite,latest){ const titleInput=document.querySelector('#wb-title');const bodyInput=document.querySelector('#wb-body'); document.querySelector('#wb-discard-draft')?.addEventListener('click',()=>{clearDraft(head.documentId);renderWorkbenchDocument()}); if(!canWrite)return; let draftTimer;const persistDraft=()=>{clearTimeout(draftTimer);draftTimer=setTimeout(()=>{if(titleInput.value===head.title&&bodyInput.value===head.body)clearDraft(head.documentId);else writeDraft(head.documentId,{title:titleInput.value,body:bodyInput.value,baseVersionId:head.versionId,savedAt:Date.now()})},200)}; titleInput.addEventListener('input',persistDraft);bodyInput.addEventListener('input',persistDraft); document.querySelector('#wb-save').addEventListener('click',async()=>{const button=document.querySelector('#wb-save');const result=document.querySelector('#wb-save-result');const title=titleInput.value.trim();if(!title){result.textContent='Enter a title first.';return}button.disabled=true;result.textContent='Saving…';result.className='wb-result';try{await workbenchPost('api/workbench/documents/'+encodeURIComponent(head.documentId)+'/versions',{baseVersionId:head.versionId,title,body:bodyInput.value},'save:'+head.documentId);clearDraft(head.documentId);await loadResource('documents');await reloadWorkbenchDocument()}catch(error){button.disabled=false;result.className='wb-result wb-error';if(error.status===409&&error.data?.stale){writeDraft(head.documentId,{title:titleInput.value,body:bodyInput.value,baseVersionId:head.versionId,savedAt:Date.now()});result.innerHTML=esc(error.message)+'. Your draft is kept in this browser. <button class="filter-chip" id="wb-save-reload">Reload document</button>';document.querySelector('#wb-save-reload').addEventListener('click',()=>reloadWorkbenchDocument({keepDraft:true}))}else if(error.uncertain){result.innerHTML=esc(workbenchErrorText(error))+' <button class="filter-chip" id="wb-save-reconcile">Reload document</button>';document.querySelector('#wb-save-reconcile').addEventListener('click',()=>reloadWorkbenchDocument({keepDraft:true}))}else result.textContent=error.message}}); document.querySelector('#wb-select-open').addEventListener('click',async()=>{state.workbench.selecting=!state.workbench.selecting;const panel=document.querySelector('#wb-select-panel');panel.hidden=!state.workbench.selecting;document.querySelector('#wb-select-open').textContent=state.workbench.selecting?'Close context picker':'Select context';if(state.workbench.selecting){panel.innerHTML='<div class="empty"><span class="spinner" aria-hidden="true"></span>Loading candidates…</div>';try{state.workbench.candidates=await fetchJson('api/workbench/candidates?documentId='+encodeURIComponent(head.documentId),'context candidates');renderWorkbenchCandidates()}catch(error){panel.innerHTML='<div class="wb-error">'+esc(error.message)+'</div>'}}}); document.querySelector('#wb-request')?.addEventListener('click',async()=>{if(!latest)return;const button=document.querySelector('#wb-request');const result=document.querySelector('#wb-request-result');const runnerId=document.querySelector('#wb-runner').value;button.disabled=true;result.className='wb-result';result.textContent='Requesting proposal from '+runnerId+'…';try{await workbenchPost('api/workbench/documents/'+encodeURIComponent(head.documentId)+'/proposals',{selectionId:latest.selectionId,runnerId},'proposal:'+head.documentId+':'+latest.selectionId);await loadResource('documents');await reloadWorkbenchDocument({keepDraft:true})}catch(error){button.disabled=false;result.className='wb-result wb-error';result.textContent=workbenchErrorText(error)}}); document.querySelectorAll('[data-wb-decide]').forEach(button=>button.addEventListener('click',async()=>{const proposalId=button.dataset.proposal;const disposition=button.dataset.wbDecide;const card=button.closest('[data-proposal-card]');const result=card.querySelector('.wb-result');card.querySelectorAll('[data-wb-decide]').forEach(value=>value.disabled=true);result.className='wb-result';result.textContent=disposition==='accept'?'Accepting…':'Rejecting…';try{await workbenchPost('api/workbench/proposals/'+encodeURIComponent(proposalId)+'/decisions',{disposition},'decision:'+proposalId);await loadResource('documents');await reloadWorkbenchDocument({keepDraft:true})}catch(error){result.className='wb-result wb-error';if(error.status===409&&error.data?.stale){result.innerHTML=esc(error.message)+'. This proposal is stale; request a new one. <button class="filter-chip" id="wb-decide-reload">Reload document</button>';document.querySelector('#wb-decide-reload').addEventListener('click',()=>reloadWorkbenchDocument({keepDraft:true}))}else result.textContent=workbenchErrorText(error);card.querySelectorAll('[data-wb-decide]').forEach(value=>{value.disabled=value.dataset.wbDecide==='accept'&&error.status===409&&error.data?.stale})}}));}function renderWorkbenchCandidates(){ const panel=document.querySelector('#wb-select-panel');const candidates=state.workbench.candidates;if(!panel||!candidates)return;const checked=state.workbench.checked; const events=candidates.events.map(event=>'<label class="wb-check"><input type="checkbox" data-candidate-event="'+esc(event.eventId)+'" '+(checked.events.has(event.eventId)?'checked':'')+'><span>'+esc(event.title)+(event.origin?' <span class="badge">origin</span>':'')+'<span class="meta">'+esc(sourceLabelFromId(event.source))+' · '+esc(new Date(event.occurredAt).toLocaleString())+'</span><span class="meta">'+esc(event.excerpt)+'</span></span></label>').join(''); const versions=candidates.versions.map(version=>'<label class="wb-check"><input type="checkbox" data-candidate-version="'+esc(version.versionId)+'" '+(checked.versions.has(version.versionId)?'checked':'')+'><span>Document version · '+esc(humanLabel(version.reason))+(version.head?' <span class="badge">head</span>':'')+'<span class="meta">'+esc(new Date(version.createdAt).toLocaleString())+' · sha '+esc(version.sha256.slice(0,12))+'</span></span></label>').join(''); panel.innerHTML='<div class="wb-card"><div class="wb-head"><strong>Choose the evidence to record</strong></div><div class="wb-note">Only checked items enter the selection. Each is recorded by exact id and hash.'+(candidates.bounded?' This list is bounded to recent observations.':'')+'</div><div class="field-label" style="margin-top:10px">Observations</div>'+(events||'<div class="wb-note">No recent observations.</div>')+'<div class="field-label" style="margin-top:10px">Versions of this document</div>'+(versions||'<div class="wb-note">No versions.</div>')+'<div class="wb-actions"><button id="wb-select-save">Record selection</button><span id="wb-select-save-result" class="wb-result"></span></div></div>'; panel.querySelectorAll('[data-candidate-event]').forEach(input=>input.addEventListener('change',()=>{if(input.checked)checked.events.add(input.dataset.candidateEvent);else checked.events.delete(input.dataset.candidateEvent)})); panel.querySelectorAll('[data-candidate-version]').forEach(input=>input.addEventListener('change',()=>{if(input.checked)checked.versions.add(input.dataset.candidateVersion);else checked.versions.delete(input.dataset.candidateVersion)})); panel.querySelector('#wb-select-save').addEventListener('click',async()=>{const button=panel.querySelector('#wb-select-save');const result=panel.querySelector('#wb-select-save-result');const eventIds=[...checked.events];const versionIds=[...checked.versions];if(eventIds.length+versionIds.length===0){result.textContent='Check at least one item.';return}button.disabled=true;result.className='wb-result';result.textContent='Recording…';const documentId=state.workbench.documentId;try{await workbenchPost('api/workbench/documents/'+encodeURIComponent(documentId)+'/selections',{eventIds,versionIds},'select:'+documentId+':'+[...eventIds,...versionIds].sort().join(','));state.workbench.selecting=false;state.workbench.checked={events:new Set(),versions:new Set()};await reloadWorkbenchDocument({keepDraft:true})}catch(error){button.disabled=false;result.className='wb-result wb-error';result.textContent=workbenchErrorText(error)}});}function renderRun(data){ if(data.error)return '<div class="failed">'+esc(data.error)+'</div>'; return '<h2>'+esc(agentName(data.run.agentId))+' · '+esc(data.kind)+'</h2><div class="meta">agent '+esc(data.run.agentId)+' · '+esc(data.run.id)+'</div><h3>what it did</h3>'+renderAgentWork(data)+'<details><summary>terminal evidence · '+(data.evidence.consistent?'consistent':'contradiction')+'</summary><pre class="'+(data.evidence.consistent?'':'failed')+'">'+json(data.evidence)+'</pre></details><details><summary>run receipt</summary><pre>'+json(data.run)+'</pre></details><details><summary>trace · '+data.trace.length+'</summary><pre>'+json(data.trace)+'</pre></details>';}function renderAgentWork(activity){ const run=activity.run; const result=run.result??{}; const kind=activity.kind??(isModelRun(run)?'model':'rule'); const llm=kind==='model'; const summary=activity.description??result.summary??run.errorText??(run.status==='completed'?'Completed without a recorded summary.':'No result recorded yet.'); const inputs=(activity.inputs??[]).map(event=>eventLink('trigger',event)).join(''); const outputs=(activity.outputs??[]).map(event=>'<button data-event-id="'+esc(event.id)+'"><strong>'+esc(event.type)+'</strong><br><span class="meta">'+esc(event.payload?.summary??event.id)+'</span></button>').join(''); const triggerSource=(activity.inputs??[])[0]?.source; const execution='<details><summary>execution details</summary>'+(inputs?'<div class="work-label">trigger</div><div class="output-list">'+inputs+'</div>':'')+(outputs?'<div class="work-label">derived record'+(activity.outputs.length===1?'':'s')+'</div><div class="output-list">'+outputs+'</div>':'<div class="work-label">derived records · none</div>')+'<div class="meta" style="margin-top:10px">agent '+esc(run.agentId)+' · context '+esc(run.contextManifest?.contextStrategy??'not recorded')+' · '+esc(run.provider)+'/'+esc(run.model)+' · attempt '+esc(run.attempt)+'</div></details>'; return '<div class="work"><div class="work-head"><strong>'+esc(agentName(run.agentId))+'</strong><span class="badge">'+esc(kind)+'</span><span class="badge '+(run.status==='failed'?'failed':'')+'">'+esc(run.status)+'</span></div><div class="work-summary">'+esc(summary)+'</div><div class="facts"><span>LLM inference: <strong>'+(llm?'yes':'no')+'</strong></span><span>trigger source: <strong>'+esc(triggerSource??'not recorded')+'</strong></span><span>derived records: <strong>'+esc((activity.outputs??[]).length)+'</strong></span><span>external actions: <strong>'+(run.contextManifest?.externalActions===false?'disabled':'not recorded')+'</strong></span></div>'+execution+'</div>';}function isModelRun(run){return !(run.provider==='deterministic'&&run.model==='deterministic')}function renderSource(source,recent=[]){ if(!source)return '<div class="failed">Source not found.</div>'; const runtime=source.control?.runtime;const upstream=source.control?.upstream; const recentEvents=recent.length?'<div class="source-event-list">'+recent.map(event=>{const observation=observationPresentation(event);return '<button class="source-event" data-event-id="'+esc(event.id)+'"><div class="source-event-title">'+esc(observation.title)+'</div><div class="source-event-body">'+esc(observation.body||observation.objectLabel)+'</div><div class="meta">'+esc(new Date(event.occurredAt).toLocaleString())+' · '+esc(humanPrivacy(event.privacy))+(event.consumerRuns?.length?' · '+event.consumerRuns.length+' consumer'+(event.consumerRuns.length===1?'':'s'):' · not processed')+'</div></button>'}).join('')+'</div>':'<div class="processing-state"><span class="status-dot"></span>No durable observations from this source yet.</div>'; const configuration=source.configured?(source.enabled?'Enabled':'Disabled'):'Observed only'; const runtimeState=runtime?(runtime.current?'Ready':'Stale'):'Not recorded'; const providerState=upstream?(upstream.registered&&upstream.valid?'Registered':'Unavailable'):'Not applicable'; const subscriptionState=upstream?upstream.liveSubscriptionCount+'/'+upstream.desiredSubscriptionCount+(upstream.subscriptionsConverged?' converged':' drifting'):'Not recorded'; const activityState=source.hasActivityEvidence?humanLabel(source.status):'Awaiting first activity'; const lastActivity=source.lastSuccessAt?new Date(source.lastSuccessAt).toLocaleString():'None recorded'; const cards='<div class="human-grid source-overview"><div class="human-card source-state '+(source.enabled?'good':'waiting')+'"><div class="field-label">Configuration</div><div class="field-value">'+esc(configuration)+'</div></div><div class="human-card source-state '+(runtime?.current?'good':'waiting')+'"><div class="field-label">Runtime</div><div class="field-value">'+esc(runtimeState)+'</div></div><div class="human-card source-state '+(upstream?.registered&&upstream?.valid?'good':'')+'"><div class="field-label">Provider</div><div class="field-value">'+esc(providerState)+'</div></div><div class="human-card source-state '+(upstream?.subscriptionsConverged?'good':'')+'"><div class="field-label">Subscriptions</div><div class="field-value">'+esc(subscriptionState)+'</div></div><div class="human-card source-state '+(source.hasActivityEvidence?'good':'waiting')+'"><div class="field-label">Data plane</div><div class="field-value">'+esc(activityState)+'</div></div><div class="human-card source-state"><div class="field-label">Last completed activity</div><div class="field-value">'+esc(lastActivity)+'</div></div></div>'; const control={configured:source.configured,enabled:source.enabled,kind:source.kind,lane:source.control?.lane,runtime:runtime?{state:runtime.state,current:runtime.current,readyAt:runtime.readyAt,revision:runtime.revision}:undefined,upstream:upstream?{registered:upstream.registered,valid:upstream.valid,current:upstream.current,webhookId:upstream.webhookId,subscriptions:upstream.liveSubscriptionCount+'/'+upstream.desiredSubscriptionCount,subscriptionsConverged:upstream.subscriptionsConverged,checkedAt:upstream.checkedAt}:undefined}; const evidence={status:source.status,hasActivityEvidence:source.hasActivityEvidence,operationsStarted:source.operationsStarted,operationsCompleted:source.operationsCompleted,operationFailures:source.operationFailures,recoveries:source.recoveries,inFlight:source.inFlight,lastSuccessAt:source.lastSuccessAt,lastFailureAt:source.lastFailureAt,lastError:source.lastError,cursorId:source.cursorId,cursor:source.cursor,updatedAt:source.updatedAt}; const technical='<details class="technical-record"><summary>Technical details</summary><div class="technical-section"><h3>Source ID</h3><div class="mono">'+esc(source.source)+'</div></div><div class="technical-section"><h3>Control plane</h3><pre>'+json(control)+'</pre></div><div class="technical-section"><h3>Data-plane evidence</h3><pre>'+json(evidence)+'</pre></div></details>'; return '<h2>'+esc(sourceLabelFromId(source.source))+'</h2><div class="meta">'+esc(source.source)+'</div><h3>Recent observations</h3>'+recentEvents+'<h3>Connection</h3>'+cards+'<div class="meta">Configuration, runtime readiness, provider registration, and delivered activity are independent receipts.</div>'+technical;}function renderSuggestion(item){ if(!item)return '<div class="failed">Suggestion not found.</div>'; const content=item.kind==='Memory suggestion'?'<article class="artifact-document suggestion-content">'+renderMarkdownClient(item.proposedText)+'</article>':'<div class="suggestion-compare"><div><h3>Original delivered reply</h3><div class="suggestion-content">'+esc(item.originalText)+'</div></div><div><h3>Proposed replacement</h3><div class="suggestion-content">'+esc(item.proposedText)+'</div></div></div>'; const details=Object.entries(item.technical).map(([key,value])=>'<div class="human-card"><div class="field-label">'+esc(humanLabel(key))+'</div><div class="mono">'+esc(value)+'</div></div>').join(''); const receipt=state.proposalReceipt?'<div class="decision"><strong>'+esc(state.proposalReceipt)+'</strong></div>':''; const failure=item.failureReason?'<div class="decision failed"><strong>Stream memory was not changed.</strong><div>'+esc(item.failureReason)+'</div></div>':''; const controls=item.status==='Awaiting review'?(state.session.reviewWriteEnabled?'<div class="suggestion-actions"><div class="field-label">Review this suggestion</div><div class="form-row"><label for="suggestion-edit">Edit before accepting</label><textarea id="suggestion-edit">'+esc(item.proposedText)+'</textarea></div><div class="suggestion-buttons"><button data-disposition="accept">Accept as written</button><button data-disposition="edit">Accept edited text</button><button class="reject" data-disposition="reject">Reject suggestion</button></div><div id="suggestion-result" class="muted"></div></div>':'<div class="empty">Sign in with Cameron’s OAuth operator session to review suggestions. Basic access is read-only.</div>'):''; return '<h2>'+esc(item.kind)+'</h2><div class="facts"><span>Status: <strong>'+esc(item.status)+'</strong></span><span>Action: <strong>'+esc(item.operation)+'</strong></span><span>Observed: <strong>'+esc(new Date(item.observedAt).toLocaleString())+'</strong></span></div><h3>Reason</h3><div class="work-summary">'+esc(item.reason)+'</div><h3>Suggested content</h3>'+content+receipt+failure+controls+'<details><summary>Technical details</summary><div class="human-grid">'+details+'</div></details>';}function renderMarkdownClient(markdown){return esc(markdown).split(/\\n\\n+/).map(block=>'<p>'+block.replace(/\\n/g,'<br>')+'</p>').join('')}function bindSuggestionForm(item){if(!item||item.status!=='Awaiting review'||!state.session.reviewWriteEnabled)return;document.querySelectorAll('.suggestion-buttons button').forEach(button=>button.addEventListener('click',()=>submitSuggestion(item,button.dataset.disposition)))}async function submitSuggestion(item,disposition){const result=document.querySelector('#suggestion-result');document.querySelectorAll('.suggestion-buttons button').forEach(button=>button.disabled=true);result.textContent='Saving decision…';const body={disposition,submissionId:crypto.randomUUID()};if(disposition==='edit')body.replacementText=document.querySelector('#suggestion-edit').value;try{const response=await fetch('api/proposals/'+encodeURIComponent(item.eventId)+'/decisions',{method:'POST',headers:{'content-type':'application/json','x-thoughtstream-csrf':state.session.csrfToken},body:JSON.stringify(body)});const receipt=await response.json();if(!response.ok)throw new Error(receipt.error??'Suggestion decision failed');state.proposalReceipt=receipt.receipt??(disposition==='reject'?'Suggestion rejected':'Suggestion accepted');state.proposals=await (await fetch('api/proposals')).json();renderList();select('suggestion',item.eventId)}catch(error){result.textContent=error.message;document.querySelectorAll('.suggestion-buttons button').forEach(button=>button.disabled=false)}}function renderReview(item){ if(!item)return '<div class="failed">Review item not found.</div>'; const evidence=item.evidence?'<h3>evidence</h3><div class="review-prompt">'+esc(item.evidence)+'</div>':''; const candidates=item.candidates.map(candidate=>'<div class="candidate"><h3>candidate '+esc(candidate.label)+'</h3><div class="candidate-text">'+esc(candidate.response)+'</div>'+(candidate.provenance?'<details><summary>provenance</summary><pre>'+json(candidate.provenance)+'</pre></details>':'')+'</div>').join(''); const decision=item.decision?renderReviewDecision(item.decision,item.decisionHistoryCount):''; const form=state.session.reviewWriteEnabled?renderReviewForm(item):'<div class="empty">OAuth Review write authority is not active for this session.</div>'; return '<h2>'+esc(item.campaign.label)+'</h2><div class="meta">campaign '+esc(item.campaign.id)+'@'+esc(item.campaign.version)+' · criterion '+esc(item.criterion.id)+'@'+esc(item.criterion.version)+' · '+esc(item.privacy)+'</div><h3>prompt</h3><div class="review-prompt">'+esc(item.prompt)+'</div>'+evidence+'<h3>candidates</h3><div class="candidate-grid">'+candidates+'</div>'+decision+form;}function renderReviewDecision(decision,historyCount){return '<div class="decision"><strong>active decision · '+esc(decision.disposition)+'</strong>'+(decision.preferredCandidate?' · candidate '+esc(decision.preferredCandidate):'')+(decision.preferenceStrength?' · '+esc(decision.preferenceStrength):'')+'<div class="meta">'+(decision.trainingEligible?'training eligible':'evaluation only')+' · '+(decision.externalExportEligible?'external export eligible':'not externally exportable')+' · '+historyCount+' decision event'+(historyCount===1?'':'s')+'</div></div>'}function renderReviewForm(item){ const current=item.decision??{}; const reasonCodes=item.criterion.reasonCodes??[]; const tags=item.criterion.responseTags??[]; const checks=(name,values,selected)=>values.map(value=>'<label><input type="checkbox" name="'+name+'" value="'+esc(value)+'" '+(selected?.includes(value)?'checked':'')+'> '+esc(value)+'</label>').join(''); return '<div class="review-form"><div class="form-row"><label for="review-disposition">judgment</label><select id="review-disposition"><option value="underdetermined">underdetermined</option><option value="malformed">malformed</option><option value="skip">skip</option><option value="prefer">prefer one candidate</option><option value="tie">tie</option><option value="correct">neither · write correction</option></select></div><div id="preference-fields"><div class="form-row"><label for="preferred-candidate">preferred candidate</label><select id="preferred-candidate"><option value="A">A</option><option value="B">B</option></select></div><div class="form-row"><label for="preference-strength">strength</label><select id="preference-strength"><option value="slight">slight</option><option value="strong">strong</option></select></div></div><div id="correction-fields" class="form-row"><label for="replacement-response">replacement response</label><textarea id="replacement-response">'+esc(current.replacementResponse??'')+'</textarea></div><div class="form-row"><label for="review-confidence">confidence</label><select id="review-confidence"><option value="">not recorded</option><option value="low">low</option><option value="medium">medium</option><option value="high">high</option></select></div>'+(reasonCodes.length?'<div class="form-row"><label>reason codes · choose at most two</label><div class="choice-row">'+checks('reason-code',reasonCodes,current.reasonCodes)+'</div></div>':'')+(tags.length?'<div class="form-row"><label>response tags · choose at most two</label><div class="choice-row">'+checks('response-tag',tags,current.responseTags)+'</div></div>':'')+'<div class="form-row"><label for="review-notes">private note · excluded from export</label><textarea id="review-notes"></textarea></div><div class="choice-row"><label><input id="training-eligible" type="checkbox" '+(current.trainingEligible?'checked':'')+'> use valid preference/correction for training</label></div><div class="meta">'+(item.trainingExportPreauthorized?'This public-source prompt may enter an externally exported dataset when training is enabled.':'This item cannot be externally exported from the browser.')+'</div><div class="submit-row"><button id="review-submit">append decision</button><span id="review-result" class="meta"></span></div></div>';}function bindReviewForm(item){ if(!item||!state.session.reviewWriteEnabled)return; const disposition=document.querySelector('#review-disposition'); const current=item.decision; disposition.value=current?.disposition??'underdetermined'; if(current?.preferredCandidate)document.querySelector('#preferred-candidate').value=current.preferredCandidate;if(current?.preferenceStrength)document.querySelector('#preference-strength').value=current.preferenceStrength;if(current?.confidence)document.querySelector('#review-confidence').value=current.confidence; const sync=()=>{const value=disposition.value;document.querySelector('#preference-fields').style.display=value==='prefer'?'grid':'none';document.querySelector('#correction-fields').style.display=value==='correct'?'grid':'none';const training=document.querySelector('#training-eligible');if(value!=='prefer'&&value!=='correct'){training.checked=false;training.disabled=true}else training.disabled=false}; disposition.addEventListener('change',sync);sync();document.querySelector('#review-submit').addEventListener('click',()=>submitReview(item));}async function submitReview(item){ const button=document.querySelector('#review-submit');const result=document.querySelector('#review-result');button.disabled=true;result.textContent='saving'; const disposition=document.querySelector('#review-disposition').value;const selected=name=>[...document.querySelectorAll('input[name="'+name+'"]:checked')].map(input=>input.value); const body={disposition,confidence:document.querySelector('#review-confidence').value||undefined,reasonCodes:selected('reason-code'),responseTags:selected('response-tag'),notes:document.querySelector('#review-notes').value.trim()||undefined,trainingEligible:document.querySelector('#training-eligible').checked,submissionId:crypto.randomUUID(),supersedesDecisionEventId:item.decision?.eventId}; if(disposition==='prefer'){body.preferredCandidate=document.querySelector('#preferred-candidate').value;body.preferenceStrength=document.querySelector('#preference-strength').value}if(disposition==='correct')body.replacementResponse=document.querySelector('#replacement-response').value; if(body.reasonCodes.length>2||body.responseTags.length>2){result.textContent='choose at most two codes and two tags';button.disabled=false;return} try{const response=await fetch('api/reviews/'+encodeURIComponent(item.id)+'/decisions',{method:'POST',headers:{'content-type':'application/json','x-thoughtstream-csrf':state.session.csrfToken},body:JSON.stringify(body)});if(!response.ok)throw new Error((await response.json()).error??'write failed');const reviews=await fetch('api/reviews');state.reviews=await reviews.json();renderList();select('review',item.id)}catch(error){result.textContent=error.message;button.disabled=false}}function artifactItem(entry){const status=entry.superseded?' · Superseded':'';return '<button class="item" data-kind="artifact" data-id="'+esc(entry.eventId)+'"><div class="summary">'+esc(entry.title)+'</div><div class="meta">'+esc(humanLabel(entry.kind))+' · '+esc(humanMediaType(entry.mediaType))+' · '+esc(humanLabel(entry.visibility))+' · '+esc(formatBytes(entry.bodySizeBytes))+status+'</div></button>'}function renderArtifact(data){ if(data.error)return '<div class="failed">'+esc(data.error)+'</div>';const e=data.event;const entry=data.catalogEntry||{}; const superseded=entry.superseded?'<span class="badge failed">superseded</span>':'<span class="badge" style="color:var(--link)">active</span>'; const content=data.renderedHtml!==undefined?'<article class="artifact-document">'+data.renderedHtml+'</article>':data.text!==undefined?'<pre class="artifact-content">'+esc(data.text)+'</pre>':data.contentPath?'<img src="'+esc(data.contentPath)+'" alt="Private artifact: '+esc(e.payload.title)+'" style="max-width:100%;max-height:600px">':'<div class="failed">Content unavailable</div>'; const provenance='<div class="human-grid"><div class="human-card"><div class="field-label">Source</div><div class="field-value">'+esc(humanLabel(e.payload.provenance.source))+'</div></div><div class="human-card"><div class="field-label">Source label</div><div class="field-value">'+esc(humanLabel(e.payload.provenance.label))+'</div></div></div>'; const relations=(e.payload.relations??[]).map(relation=>'<div class="relation-card"><div class="field-label">'+esc(humanLabel(relation.type))+'</div><div class="field-value">'+esc(relation.type==='materialized-from-request'?'Artifact storage request':'Related event')+'</div><div class="mono">'+esc(relation.eventId)+'</div></div>').join(''); const record='<div class="human-grid"><div class="human-card"><div class="field-label">Artifact</div><div class="field-value">'+esc(e.payload.artifactId)+' · version '+esc(e.payload.artifactVersion)+'</div></div><div class="human-card"><div class="field-label">Stored</div><div class="field-value">'+esc(new Date(e.occurredAt).toLocaleString())+'</div></div><div class="human-card wide"><div class="field-label">Artifact event</div><div class="mono">'+esc(e.id)+'</div></div>'+(entry.supersedesArtifactEventId?'<div class="human-card wide"><div class="field-label">Supersedes artifact event</div><div class="mono">'+esc(entry.supersedesArtifactEventId)+'</div></div>':'')+'</div>'; const metadata='<details class="artifact-metadata"><summary>Artifact details</summary><div class="facts"><span>Status: <strong>'+superseded+'</strong></span><span>Format: <strong>'+esc(humanMediaType(e.payload.mediaType))+'</strong></span><span>Access: <strong>'+esc(humanLabel(e.payload.visibility))+'</strong></span><span>Public release: <strong>'+(e.payload.publicationEligible?'Eligible':'Not allowed')+'</strong></span><span>Size: <strong>'+esc(formatBytes(entry.bodySizeBytes))+'</strong></span></div><h3>Summary</h3><div class="work-summary">'+esc(e.payload.summary)+'</div><h3>Provenance</h3>'+provenance+(relations?'<h3>Relations</h3><div class="relation-list">'+relations+'</div>':'')+'<h3>Record</h3>'+record+'<div class="human-card wide" style="margin-top:8px"><div class="field-label">SHA-256 fingerprint</div><div class="mono">'+esc(data.bodySha256)+'</div></div></details>'; return '<h2>'+esc(e.payload.title)+'</h2>'+content+metadata;}function humanLabel(value){return String(value??'').replace(/[-_.:]+/g,' ').split(' ').map(word=>word?word[0].toUpperCase()+word.slice(1):word).join(' ')}function humanMediaType(value){return ({'text/markdown':'Markdown','text/plain':'Plain text','application/json':'JSON document','application/yaml':'YAML document','text/yaml':'YAML document','image/png':'PNG image','image/jpeg':'JPEG image'})[value]??humanLabel(value)}function formatBytes(value){const n=Number(value);if(!Number.isFinite(n))return 'Unknown';if(n<1024)return n+' bytes';if(n<1024*1024)return (n/1024).toFixed(n<10240?1:0)+' KB';return (n/(1024*1024)).toFixed(1)+' MB'}function closeDetail(options={}){state.selected=null;document.querySelector('main').classList.remove('detail-selected');document.body.classList.remove('detail-selected');document.querySelector('#detail').innerHTML='';renderList();if(options.history!==false)writeRoute(options.history??'push');if(!options.preserveScroll)window.scrollTo({top:0,behavior:'smooth'})}function switchTab(tab,options={}){state.tab=tab;state.selected=null;document.querySelector('main').classList.remove('detail-selected');document.body.classList.remove('detail-selected');document.querySelector('#detail').innerHTML='';renderList();if(options.history!==false)writeRoute(options.history??'push');if(!options.preserveScroll)window.scrollTo({top:0,behavior:'smooth'})}function applyRoute(route){if(route.tab==='learn'){if(route.lessonId)state.courseLessonId=route.lessonId;switchTab('learn',{history:false})}else if(route.selected)void select(route.selected.kind,route.selected.id,{history:false});else switchTab(route.tab,{history:false})}function navigateBack(){if(history.state?.streamInspector===true&&history.state.hasInternalParent===true)history.back();else closeDetail({history:'replace'})}document.querySelector('#detail-back').addEventListener('click',navigateBack);document.querySelector('#events-tab').addEventListener('click',()=>switchTab('events'));document.querySelector('#learn-tab').addEventListener('click',()=>switchTab('learn'));document.querySelector('#review-tab').addEventListener('click',()=>switchTab(state.tab==='suggestions'||state.tab==='reviews'?state.tab:'suggestions'));document.querySelector('#artifacts-tab').addEventListener('click',()=>switchTab('artifacts'));document.querySelector('#documents-tab').addEventListener('click',()=>switchTab('documents'));document.querySelector('#system-tab').addEventListener('click',()=>switchTab(state.tab==='runs'||state.tab==='sources'?state.tab:'runs'));for(const tab of ['suggestions','reviews','runs','sources'])document.querySelector('#'+tab+'-tab').addEventListener('click',()=>switchTab(tab));document.querySelector('#course-chat-toggle').addEventListener('click',()=>setCourseChatOpen(true));document.querySelector('#course-chat-close').addEventListener('click',()=>setCourseChatOpen(false));document.querySelector('#course-chat-send').addEventListener('click',()=>void submitCourseQuestion());document.querySelector('#course-chat-input').addEventListener('keydown',event=>{if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();void submitCourseQuestion()}});window.addEventListener('popstate',()=>applyRoute(parseRoute()));const filterDialog=document.querySelector('#filters-dialog');document.querySelector('#filter-open').addEventListener('click',()=>{if(!filterDialog.open)filterDialog.showModal()});document.querySelector('#filter-close').addEventListener('click',()=>filterDialog.close());document.querySelector('#filter-apply').addEventListener('click',()=>filterDialog.close());document.querySelector('#filter-reset').addEventListener('click',()=>{if(state.tab==='sources'){state.sourceScope='inputs';const scope=document.querySelector('#source-scope-filter');if(scope)scope.value='inputs'}else{state.eventFilters={source:'all',activity:'all',processing:'all',query:''};const source=document.querySelector('#event-source-filter');const activity=document.querySelector('#event-activity-filter');const processing=document.querySelector('#event-processing-filter');const query=document.querySelector('#event-query');if(source)source.value='all';if(activity)activity.value='all';if(processing)processing.value='all';if(query)query.value=''}renderList()});filterDialog.addEventListener('click',event=>{if(event.target===filterDialog)filterDialog.close()});if('serviceWorker'in navigator)void navigator.serviceWorker.register('sw.js',{scope:'./'}).catch(()=>undefined);load().catch(error=>{document.querySelector('#list').innerHTML='<div class="failed">'+esc(error.message)+'</div>'});</script></body></html>`; return html;}
function sendJson(response: ServerResponse, status: number, value: unknown): void { send(response, status, "application/json; charset=utf-8", JSON.stringify(value));}
function sendBytes(response: ServerResponse, status: number, contentType: string, body: Buffer, cacheControl: string): void { response.writeHead(status, { "content-type": contentType, "content-length": String(body.length), "cache-control": cacheControl, "x-content-type-options": "nosniff", }); response.end(body);}
function streamInspectorActivity( store: JazzThoughtStore, request: IncomingMessage, response: ServerResponse,): void { response.writeHead(200, { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-store, no-transform", connection: "keep-alive", "x-accel-buffering": "no", "x-content-type-options": "nosniff", }); if (request.method === "HEAD") { response.end(); return; } response.write("retry: 2000\n\n"); let timer: NodeJS.Timeout | undefined; const notify = () => { if (timer || response.writableEnded || response.destroyed) return; timer = setTimeout(() => { timer = undefined; if (!response.writableEnded && !response.destroyed) { response.write(`event: change\ndata: ${JSON.stringify({ at: new Date().toISOString() })}\n\n`); } }, 100); timer.unref(); }; const unsubscribe = store.subscribeInspectorActivity(notify); const heartbeat = setInterval(() => { if (!response.writableEnded && !response.destroyed) response.write(": keepalive\n\n"); }, 15_000); heartbeat.unref(); const close = () => { request.off("aborted", close); response.off("close", close); if (timer) clearTimeout(timer); clearInterval(heartbeat); unsubscribe(); }; request.once("aborted", close); response.once("close", close);}
function send(response: ServerResponse, status: number, contentType: string, body: string): void { response.writeHead(status, { "content-type": contentType, "cache-control": "no-store", "content-security-policy": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'unsafe-inline' https://fonts.googleapis.com; connect-src 'self'; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com; worker-src 'self'; frame-ancestors 'none'; base-uri 'none'", "x-content-type-options": "nosniff", "x-frame-options": "DENY", }); response.end(body);}
async function readBody(request: IncomingMessage, maxBytes: number): Promise<Buffer> { const declared = request.headers["content-length"]; if (typeof declared === "string" && (!/^\d+$/.test(declared) || Number(declared) > maxBytes)) { throw new Error("Request body too large"); } const parts: Buffer[] = []; let bytes = 0; for await (const part of request) { const buffer = Buffer.isBuffer(part) ? part : Buffer.from(part); bytes += buffer.length; if (bytes > maxBytes) throw new Error("Request body too large"); parts.push(buffer); } if (bytes === 0) throw new Error("Request body is empty"); return Buffer.concat(parts);}
function isLoopback(host: string): boolean { return host === "127.0.0.1" || host === "::1" || host === "localhost";}
function inspectorRunSummary(run: AgentRun) { return { id: run.id, agentId: run.agentId, agentVersion: run.agentVersion, status: run.status, provider: run.provider, model: run.model, createdAt: run.createdAt, ...(run.startedAt ? { startedAt: run.startedAt } : {}), ...(run.completedAt ? { completedAt: run.completedAt } : {}), ...(run.errorText ? { errorText: run.errorText } : {}), ...(typeof run.result?.summary === "string" ? { summary: run.result.summary } : {}), };}
function eventBelongsToRun(event: ThoughtEvent, run: AgentRun, inputsById: Map<string, ThoughtEvent>): boolean { const payloadRunId = typeof event.payload.runId === "string" ? event.payload.runId : undefined; return payloadRunId === run.id || run.inputEventIds.includes(event.id) || run.outputEventIds.includes(event.id) || run.inputEventIds.some((id) => batchReferencesEvent(inputsById.get(id), event.id));}
function batchReferencesEvent(input: ThoughtEvent | undefined, eventId: string): boolean { if (input?.type !== "stream.thought.derived.event.batch" || !Array.isArray(input.payload.members)) return false; return input.payload.members.some((reference) => ( reference !== null && typeof reference === "object" && !Array.isArray(reference) && (reference as Record<string, unknown>).eventId === eventId ));}
async function loadRunEvidenceEvents(store: JazzThoughtStore, runs: AgentRun[]): Promise<ThoughtEvent[]> { const terminalEvents = await store.listEvents({ types: RUN_TERMINAL_EVENT_TYPES }); const referencedOutputIds = [ ...runs.flatMap((run) => run.outputEventIds), ...terminalEvents.map((event) => event.payload.outputEventId).filter((id): id is string => typeof id === "string"), ]; const outputEvents = await store.getEvents(referencedOutputIds); return [...new Map([...terminalEvents, ...outputEvents].map((event) => [event.id, event])).values()];}
async function buildInspectorActivity(store: JazzThoughtStore) { return buildRecentRootActivity(store);}
async function buildInspectorSystem( store: JazzThoughtStore, runs?: AgentRun[],) { const effectiveRuns = runs ?? await store.listRuns(); const [agents, sources, evidenceEvents] = await Promise.all([ store.listAgents(), buildSourceHealth(store), loadRunEvidenceEvents(store, effectiveRuns), ]); const runEvidence = auditRunEvidence(effectiveRuns, evidenceEvents); const adapterSelections = agents .map((agent) => ({ id: agent.id, version: agent.version, enabled: agent.enabled, adapter: adapterSelectionFromSpec(agent.spec), })) .filter((agent) => agent.adapter !== undefined); const adapterCatalogs = [...new Map(adapterSelections.map((selection) => [ `${selection.adapter!.digest}:${selection.adapter!.generation}`, { digest: selection.adapter!.digest, generation: selection.adapter!.generation }, ])).values()]; return { runs: effectiveRuns.slice().reverse().map(inspectorRunSummary), agents, sources, adapterInventory: { catalogs: adapterCatalogs, selections: adapterSelections }, runEvidence, evidenceContradictions: runEvidence.filter((report) => !report.consistent).length, };}
async function expandRun(store: JazzThoughtStore, run: AgentRun): Promise<{ run: AgentRun; kind: "rule" | "model"; description: string; inputs: ThoughtEvent[]; outputs: ThoughtEvent[]; trace: Awaited<ReturnType<JazzThoughtStore["listTrace"]>>;}> { const [inputs, outputs, trace] = await Promise.all([ Promise.all(run.inputEventIds.map((id) => store.getEvent(id))), Promise.all(run.outputEventIds.map((id) => store.getEvent(id))), store.listTrace(run.id), ]); const presentInputs = inputs.filter((event): event is ThoughtEvent => Boolean(event)); return { run, kind: run.provider === "deterministic" && run.model === "deterministic" ? "rule" : "model", description: describeRunResult(run, presentInputs), inputs: presentInputs, outputs: outputs.filter((event): event is ThoughtEvent => Boolean(event)), trace, };}