@tangle-network/agent-app 0.44.8 → 0.44.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/assistant/index.d.ts +4 -4
  2. package/dist/assistant/index.js +6 -6
  3. package/dist/{attachment-validation-DRuLEjAE.d.ts → attachment-validation-yJ6Id4pR.d.ts} +1 -1
  4. package/dist/chat-routes/index.d.ts +4 -4
  5. package/dist/chat-routes/index.js +3 -3
  6. package/dist/chat-store/index.d.ts +3 -3
  7. package/dist/chat-store/index.js +2 -2
  8. package/dist/{chunk-V55WJSR4.js → chunk-5GA3MKOC.js} +2 -2
  9. package/dist/{chunk-UDSY2F6N.js → chunk-72TPRENZ.js} +21 -8
  10. package/dist/chunk-72TPRENZ.js.map +1 -0
  11. package/dist/{chunk-AFNTRJQ7.js → chunk-FEUW4G2L.js} +2 -2
  12. package/dist/{chunk-TKRI463Z.js → chunk-J6TNPRQN.js} +4 -4
  13. package/dist/{chunk-F2CBC4DY.js → chunk-V3HO43PG.js} +15 -1
  14. package/dist/chunk-V3HO43PG.js.map +1 -0
  15. package/dist/{chunk-UP33Z633.js → chunk-ZRSUCTST.js} +2 -2
  16. package/dist/{parts-Co_lFgLI.d.ts → parts-DIKcC1p6.d.ts} +1 -1
  17. package/dist/{queue-CD5-_VTX.d.ts → queue-DBkulxW1.d.ts} +1 -1
  18. package/dist/{types-XIZqIdX1.d.ts → types-DeEOhQbv.d.ts} +25 -3
  19. package/dist/web-react/index.d.ts +6 -6
  20. package/dist/web-react/index.js +6 -6
  21. package/dist/work-product/index.d.ts +73 -5
  22. package/dist/work-product/index.js +131 -17
  23. package/dist/work-product/index.js.map +1 -1
  24. package/dist/work-product-react/index.d.ts +1 -1
  25. package/dist/work-product-react/index.js +3 -3
  26. package/package.json +1 -1
  27. package/dist/chunk-F2CBC4DY.js.map +0 -1
  28. package/dist/chunk-UDSY2F6N.js.map +0 -1
  29. /package/dist/{chunk-V55WJSR4.js.map → chunk-5GA3MKOC.js.map} +0 -0
  30. /package/dist/{chunk-AFNTRJQ7.js.map → chunk-FEUW4G2L.js.map} +0 -0
  31. /package/dist/{chunk-TKRI463Z.js.map → chunk-J6TNPRQN.js.map} +0 -0
  32. /package/dist/{chunk-UP33Z633.js.map → chunk-ZRSUCTST.js.map} +0 -0
@@ -4,15 +4,15 @@ import { ToolDetailRenderers, ChatUiMessage } from '../web-react/index.js';
4
4
  import '../contract-CEewO6DI.js';
5
5
  import '@tangle-network/agent-interface';
6
6
  import '../plans/index.js';
7
- import '../parts-Co_lFgLI.js';
8
- import '../types-XIZqIdX1.js';
7
+ import '../parts-DIKcC1p6.js';
8
+ import '../types-DeEOhQbv.js';
9
9
  import '../agent-activity-C8ZG0F0M.js';
10
10
  import '../flow-types-CJxEmaRy.js';
11
- import '../queue-CD5-_VTX.js';
11
+ import '../queue-DBkulxW1.js';
12
12
  import '../sandbox-terminal-ChNEdHF8.js';
13
13
  import '../catalog/index.js';
14
14
  import '../harness/index.js';
15
- import '../attachment-validation-DRuLEjAE.js';
15
+ import '../attachment-validation-yJ6Id4pR.js';
16
16
  import '../stream-normalizer-DnuqkZvw.js';
17
17
 
18
18
  /**
@@ -3,14 +3,14 @@ import {
3
3
  ChatMessages,
4
4
  ModelPicker,
5
5
  ProviderLogo
6
- } from "../chunk-TKRI463Z.js";
6
+ } from "../chunk-J6TNPRQN.js";
7
7
  import "../chunk-FBVLEGEG.js";
8
- import "../chunk-UDSY2F6N.js";
9
- import "../chunk-UP33Z633.js";
8
+ import "../chunk-72TPRENZ.js";
9
+ import "../chunk-ZRSUCTST.js";
10
10
  import "../chunk-HCOROIRT.js";
11
- import "../chunk-V55WJSR4.js";
12
- import "../chunk-AFNTRJQ7.js";
13
- import "../chunk-F2CBC4DY.js";
11
+ import "../chunk-5GA3MKOC.js";
12
+ import "../chunk-FEUW4G2L.js";
13
+ import "../chunk-V3HO43PG.js";
14
14
  import "../chunk-5EPIPT4V.js";
15
15
  import "../chunk-3ZK5IJSW.js";
16
16
  import "../chunk-YJMCRXQQ.js";
@@ -1,4 +1,4 @@
1
- import { M as FileMention } from './parts-Co_lFgLI.js';
1
+ import { M as FileMention } from './parts-DIKcC1p6.js';
2
2
 
3
3
  /**
4
4
  * `createSandboxFileIndexRoute` — server side of `@`-file-mentions
@@ -1,6 +1,6 @@
1
- import { C as ChatMessagePart, N as ChatTurnRequestPayload, O as ChatTurnPartInput, P as ChatTurnFilePartInput, b as ChatAttachmentPart, a as ChatAttachmentKind, g as ChatMentionPart } from '../parts-Co_lFgLI.js';
2
- export { Q as ChatAttachmentInput, f as ChatMentionKind, R as ChatTurnInputError, T as ChatTurnTextPartInput, U as DISPATCH_MAX_MEDIA_PARTS, V as DISPATCH_MAX_PARTS, W as DISPATCH_REQUEST_MAX_BYTES, X as DISPATCH_STRUCTURAL_RESERVE_BYTES, M as FileMention, Y as FileMentionsToPartsOptions, Z as INLINE_PARTS_MAX_BYTES, _ as MENTION_MAX_COUNT, $ as ProducerErrorEvent, a0 as ProducerNoticeEvent, a1 as ProducerPassthroughEvent, a2 as ProducerPassthroughEventType, a3 as ProducerReasoningEvent, a4 as ProducerTextEvent, a5 as ProducerToolCallEvent, a6 as ProducerToolResultEvent, a7 as ProducerUsageEvent, a8 as ProducerWireEvent, a9 as SandboxMentionPathCheck, aa as assertPromptPartsWithinCap, ab as base64WireLen, ac as buildMentionPromptBlock, ad as chatTurnRequestInit, ae as fileMentionsToParts, af as formatBytes, ag as mediaTypeForMentionPath, ah as mentionKindForPath, ai as parseChatTurnParts, aj as parseFileMentions, ak as promptPartsByteSize, al as validateSandboxMentionPath } from '../parts-Co_lFgLI.js';
3
- export { A as ALLOWED_ATTACHMENT_SNIFFED_MIMES, a as ATTACHMENT_ACCEPT, b as ATTACHMENT_MAX_COUNT, c as AttachmentTypeCheckResult, C as CreateSandboxFileIndexRouteOptions, F as FileIndexAuthorization, d as FileIndexCache, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse, M as MAX_ATTACHMENT_TOTAL_BYTES, h as MAX_BINARY_ATTACHMENT_BYTES, i as MAX_TEXT_ATTACHMENT_BYTES, S as SandboxFileTreeSource, j as SandboxTreeFile, k as SandboxTreeResult, l as SniffResult, m as attachmentSizeErrorMessage, n as attachmentTotalSizeErrorMessage, o as checkAttachmentType, p as createSandboxFileIndexRoute, s as sanitizeAttachmentFileName, q as sniffBinary } from '../attachment-validation-DRuLEjAE.js';
1
+ import { C as ChatMessagePart, N as ChatTurnRequestPayload, O as ChatTurnPartInput, P as ChatTurnFilePartInput, b as ChatAttachmentPart, a as ChatAttachmentKind, g as ChatMentionPart } from '../parts-DIKcC1p6.js';
2
+ export { Q as ChatAttachmentInput, f as ChatMentionKind, R as ChatTurnInputError, T as ChatTurnTextPartInput, U as DISPATCH_MAX_MEDIA_PARTS, V as DISPATCH_MAX_PARTS, W as DISPATCH_REQUEST_MAX_BYTES, X as DISPATCH_STRUCTURAL_RESERVE_BYTES, M as FileMention, Y as FileMentionsToPartsOptions, Z as INLINE_PARTS_MAX_BYTES, _ as MENTION_MAX_COUNT, $ as ProducerErrorEvent, a0 as ProducerNoticeEvent, a1 as ProducerPassthroughEvent, a2 as ProducerPassthroughEventType, a3 as ProducerReasoningEvent, a4 as ProducerTextEvent, a5 as ProducerToolCallEvent, a6 as ProducerToolResultEvent, a7 as ProducerUsageEvent, a8 as ProducerWireEvent, a9 as SandboxMentionPathCheck, aa as assertPromptPartsWithinCap, ab as base64WireLen, ac as buildMentionPromptBlock, ad as chatTurnRequestInit, ae as fileMentionsToParts, af as formatBytes, ag as mediaTypeForMentionPath, ah as mentionKindForPath, ai as parseChatTurnParts, aj as parseFileMentions, ak as promptPartsByteSize, al as validateSandboxMentionPath } from '../parts-DIKcC1p6.js';
3
+ export { A as ALLOWED_ATTACHMENT_SNIFFED_MIMES, a as ATTACHMENT_ACCEPT, b as ATTACHMENT_MAX_COUNT, c as AttachmentTypeCheckResult, C as CreateSandboxFileIndexRouteOptions, F as FileIndexAuthorization, d as FileIndexCache, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse, M as MAX_ATTACHMENT_TOTAL_BYTES, h as MAX_BINARY_ATTACHMENT_BYTES, i as MAX_TEXT_ATTACHMENT_BYTES, S as SandboxFileTreeSource, j as SandboxTreeFile, k as SandboxTreeResult, l as SniffResult, m as attachmentSizeErrorMessage, n as attachmentTotalSizeErrorMessage, o as checkAttachmentType, p as createSandboxFileIndexRoute, s as sanitizeAttachmentFileName, q as sniffBinary } from '../attachment-validation-yJ6Id4pR.js';
4
4
  import { ChatTurnIdentity, ChatTurnProducer } from '@tangle-network/agent-runtime';
5
5
  import { InteractionAnswerRoute, InteractionAnswerRouteOptions } from '../interactions/index.js';
6
6
  import { PersistedChatMessageForTurn } from '../stream/index.js';
@@ -11,7 +11,7 @@ import { J as JsonRecord } from '../stream-normalizer-DnuqkZvw.js';
11
11
  import { SandboxExecChannel, PromptInputPart } from '../sandbox/index.js';
12
12
  import '@tangle-network/agent-interface';
13
13
  import '../contract-CEewO6DI.js';
14
- import '../types-XIZqIdX1.js';
14
+ import '../types-DeEOhQbv.js';
15
15
  import '../plans/index.js';
16
16
  import '@tangle-network/sandbox';
17
17
  import '../auth-anc7mv2W.js';
@@ -11,7 +11,7 @@ import {
11
11
  createSandboxFileIndexRoute,
12
12
  sanitizeAttachmentFileName,
13
13
  sniffBinary
14
- } from "../chunk-V55WJSR4.js";
14
+ } from "../chunk-5GA3MKOC.js";
15
15
  import {
16
16
  coalesceDeltas,
17
17
  createBufferedTurnTap,
@@ -55,8 +55,8 @@ import {
55
55
  promptPartsByteSize,
56
56
  toChatMessageParts,
57
57
  validateSandboxMentionPath
58
- } from "../chunk-AFNTRJQ7.js";
59
- import "../chunk-F2CBC4DY.js";
58
+ } from "../chunk-FEUW4G2L.js";
59
+ import "../chunk-V3HO43PG.js";
60
60
  import {
61
61
  asRecord,
62
62
  asString,
@@ -1,12 +1,12 @@
1
- import { C as ChatMessagePart } from '../parts-Co_lFgLI.js';
2
- export { a as ChatAttachmentKind, b as ChatAttachmentPart, c as ChatFilePart, d as ChatImagePart, e as ChatInteractionPart, f as ChatMentionKind, g as ChatMentionPart, h as ChatNoticePart, i as ChatPartTime, j as ChatPlanPart, k as ChatReasoningPart, l as ChatStepFinishPart, m as ChatStepStartPart, n as ChatSubtaskPart, o as ChatTextPart, p as ChatToolPart, q as ChatToolState, r as ChatToolStatus, s as ChatUsageTokens, t as ChatWorkProductPart, D as DEFAULT_ATTACHMENT_PROMPT_HEADER, S as StorableHarnessPartKind, u as attachmentInputToPart, v as attachmentKindForMime, w as attachmentPartsFromMessageParts, x as buildAttachmentPromptBlock, y as historyContentWithAttachments, z as isChatAttachmentPart, A as isChatInteractionPart, B as isChatMentionPart, E as isChatPlanPart, F as isChatStepFinishPart, G as isChatTextPart, H as isChatToolPart, I as isChatWorkProductPart, J as mentionInputToPart, K as mentionPartsFromMessageParts, L as toChatMessageParts } from '../parts-Co_lFgLI.js';
1
+ import { C as ChatMessagePart } from '../parts-DIKcC1p6.js';
2
+ export { a as ChatAttachmentKind, b as ChatAttachmentPart, c as ChatFilePart, d as ChatImagePart, e as ChatInteractionPart, f as ChatMentionKind, g as ChatMentionPart, h as ChatNoticePart, i as ChatPartTime, j as ChatPlanPart, k as ChatReasoningPart, l as ChatStepFinishPart, m as ChatStepStartPart, n as ChatSubtaskPart, o as ChatTextPart, p as ChatToolPart, q as ChatToolState, r as ChatToolStatus, s as ChatUsageTokens, t as ChatWorkProductPart, D as DEFAULT_ATTACHMENT_PROMPT_HEADER, S as StorableHarnessPartKind, u as attachmentInputToPart, v as attachmentKindForMime, w as attachmentPartsFromMessageParts, x as buildAttachmentPromptBlock, y as historyContentWithAttachments, z as isChatAttachmentPart, A as isChatInteractionPart, B as isChatMentionPart, E as isChatPlanPart, F as isChatStepFinishPart, G as isChatTextPart, H as isChatToolPart, I as isChatWorkProductPart, J as mentionInputToPart, K as mentionPartsFromMessageParts, L as toChatMessageParts } from '../parts-DIKcC1p6.js';
3
3
  import * as drizzle_orm from 'drizzle-orm';
4
4
  import * as drizzle_orm_sqlite_core from 'drizzle-orm/sqlite-core';
5
5
  import { SQLiteColumnBuilderBase, AnySQLiteTable, AnySQLiteColumn, BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';
6
6
  export { a as attachmentPartKey } from '../stream-normalizer-DnuqkZvw.js';
7
7
  import '@tangle-network/agent-interface';
8
8
  import '../contract-CEewO6DI.js';
9
- import '../types-XIZqIdX1.js';
9
+ import '../types-DeEOhQbv.js';
10
10
  import '../plans/index.js';
11
11
 
12
12
  /**
@@ -16,8 +16,8 @@ import {
16
16
  mentionInputToPart,
17
17
  mentionPartsFromMessageParts,
18
18
  toChatMessageParts
19
- } from "../chunk-AFNTRJQ7.js";
20
- import "../chunk-F2CBC4DY.js";
19
+ } from "../chunk-FEUW4G2L.js";
20
+ import "../chunk-V3HO43PG.js";
21
21
  import {
22
22
  attachmentPartKey
23
23
  } from "../chunk-5EPIPT4V.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  formatBytes
3
- } from "./chunk-AFNTRJQ7.js";
3
+ } from "./chunk-FEUW4G2L.js";
4
4
 
5
5
  // src/chat-routes/binary-sniff.ts
6
6
  function bytesStartWith(bytes, offset, signature) {
@@ -236,4 +236,4 @@ export {
236
236
  attachmentTotalSizeErrorMessage,
237
237
  createSandboxFileIndexRoute
238
238
  };
239
- //# sourceMappingURL=chunk-V55WJSR4.js.map
239
+ //# sourceMappingURL=chunk-5GA3MKOC.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseReviewQueueItem
3
- } from "./chunk-UP33Z633.js";
3
+ } from "./chunk-ZRSUCTST.js";
4
4
  import {
5
5
  persistedPartToWorkProduct
6
- } from "./chunk-F2CBC4DY.js";
6
+ } from "./chunk-V3HO43PG.js";
7
7
 
8
8
  // src/web-react/work-product.tsx
9
9
  import { useCallback, useEffect, useState } from "react";
@@ -226,11 +226,24 @@ function EvidenceLineageTable({ evidence, resolveSourceUrl, className }) {
226
226
  /* @__PURE__ */ jsx("td", { className: "py-2 pr-3 font-mono text-xs text-foreground", children: entry.target }),
227
227
  /* @__PURE__ */ jsxs("td", { className: "py-2 pr-3 text-[13px] leading-snug text-foreground", children: [
228
228
  entry.claim,
229
- entry.locator.quote && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block border-l-2 border-border pl-2 text-xs italic text-muted-foreground", children: [
230
- "\u201C",
231
- entry.locator.quote,
232
- "\u201D"
233
- ] })
229
+ entry.locator.quote && // The basis is what a reviewer weighs, so it has to be
230
+ // visible per row, not only in the aggregate check detail.
231
+ // A platform-sliced quote came out of the document's own
232
+ // bytes; a model-typed one was proved to occur but was
233
+ // transcribed. Same text, different strength of evidence.
234
+ /* @__PURE__ */ jsxs(
235
+ "span",
236
+ {
237
+ className: `mt-0.5 block border-l-2 pl-2 text-xs italic ${entry.locator.quoteBasis === "span" ? "border-primary/60 text-foreground" : "border-border text-muted-foreground"}`,
238
+ title: entry.locator.quoteBasis === "span" ? "Sliced from the source document by the platform" : entry.locator.quoteBasis === "model" ? "Quoted by the agent, verified to occur in the source" : "Unverified quote",
239
+ children: [
240
+ "\u201C",
241
+ entry.locator.quote,
242
+ "\u201D",
243
+ entry.locator.quoteBasis && /* @__PURE__ */ jsx("span", { className: "ml-1.5 align-middle text-[10px] not-italic uppercase tracking-wide text-muted-foreground", children: entry.locator.quoteBasis === "span" ? "from source" : "verified" })
244
+ ]
245
+ }
246
+ )
234
247
  ] }),
235
248
  /* @__PURE__ */ jsxs("td", { className: "py-2 text-xs", children: [
236
249
  url ? /* @__PURE__ */ jsx(
@@ -328,4 +341,4 @@ export {
328
341
  QualityCheckList,
329
342
  ProvenanceStamp
330
343
  };
331
- //# sourceMappingURL=chunk-UDSY2F6N.js.map
344
+ //# sourceMappingURL=chunk-72TPRENZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web-react/work-product.tsx"],"sourcesContent":["/**\n * Work-product review surfaces — the sandbox-ui-FREE half of the vertical:\n *\n * - {@link ReviewQueuePanel}: the practice/matters/campaign review queue over\n * a `fetchQueue(cursor)` data port (the `AgentActivityPanel` shape).\n * - {@link WorkProductCard}: the persisted `type:'work_product'` transcript\n * anchor rendered inside `ChatMessages` — chat stays the driver surface.\n * - {@link EvidenceLineageTable}: target → claim → source rows with\n * click-through via an injected `resolveSourceUrl` (the\n * `MessageAttachments.resolveFileUrl` pattern).\n * - {@link ExceptionList} / {@link QualityCheckList}: severity-badged\n * exception rows and pass/fail check rows.\n * - {@link ProvenanceStamp}: profileHash prefix + runId + serving-model\n * chips + the optional backtest slot (\"182 cases · composite 0.81 ·\n * trust PASS\" — or \"quality: unverified\" when the trust gate failed).\n *\n * Anything hosting sandbox-ui (DiffView, CodeView, PillTabs) lives in\n * `./work-product-react` instead — this module must stay importable without\n * the optional sandbox-ui peer.\n */\n\nimport { useCallback, useEffect, useState } from 'react'\n\nimport {\n parseReviewQueueItem,\n type ReviewQueueItem,\n type ReviewQueueState,\n} from '../work-product/queue'\nimport type {\n EvidenceEntry,\n ExceptionEntry,\n ProfileBacktestSummary,\n QualityCheck,\n WorkProductPersistedPart,\n WorkProductProvenance,\n WorkProductStatus,\n} from '../work-product/types'\nimport { persistedPartToWorkProduct } from '../work-product/types'\n\nexport { parseReviewQueueItem }\nexport type { ReviewQueueItem, ReviewQueueState }\n\n// ── shared presentation vocabulary ────────────────────────────────────────\n\nconst STATE_LABELS: Record<ReviewQueueState, string> = {\n intake: 'Intake',\n missing_info: 'Missing info',\n working: 'Working',\n ready_for_review: 'Ready for review',\n changes_requested: 'Changes requested',\n approved: 'Approved',\n blocked: 'Blocked',\n}\n\nconst STATE_TONES: Record<ReviewQueueState, string> = {\n intake: 'bg-muted/40 text-muted-foreground',\n missing_info: 'bg-warning/10 text-warning',\n working: 'bg-primary/10 text-primary',\n ready_for_review: 'bg-success/10 text-success',\n changes_requested: 'bg-warning/10 text-warning',\n approved: 'bg-success/10 text-success',\n blocked: 'bg-destructive/10 text-destructive',\n}\n\nconst STATUS_LABELS: Record<WorkProductStatus, string> = {\n draft: 'Draft',\n blocked: 'Blocked',\n ready: 'Ready for review',\n changes_requested: 'Changes requested',\n approved: 'Approved',\n superseded: 'Superseded',\n}\n\nconst STATUS_TONES: Record<WorkProductStatus, string> = {\n draft: 'bg-primary/10 text-primary',\n blocked: 'bg-destructive/10 text-destructive',\n ready: 'bg-success/10 text-success',\n changes_requested: 'bg-warning/10 text-warning',\n approved: 'bg-success/10 text-success',\n superseded: 'bg-muted/40 text-muted-foreground',\n}\n\n/** Human label for a queue state. */\nexport function reviewQueueStateLabel(state: ReviewQueueState): string {\n return STATE_LABELS[state]\n}\n\n/** Human label for a work-product status. */\nexport function workProductStatusLabel(status: WorkProductStatus): string {\n return STATUS_LABELS[status]\n}\n\nfunction StatePill({ state }: { state: ReviewQueueState }) {\n return (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${STATE_TONES[state]}`}>\n {STATE_LABELS[state]}\n </span>\n )\n}\n\nfunction StatusPill({ status }: { status: WorkProductStatus }) {\n return (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${STATUS_TONES[status]}`}>\n {STATUS_LABELS[status]}\n </span>\n )\n}\n\n// ── transcript anchor helpers + card ──────────────────────────────────────\n\n/** Every persisted work-product anchor on one message, re-validated. */\nexport function workProductPartsFromMessageParts(\n parts: ReadonlyArray<Record<string, unknown>> | null | undefined,\n): WorkProductPersistedPart[] {\n if (!parts) return []\n const out: WorkProductPersistedPart[] = []\n for (const part of parts) {\n const typed = persistedPartToWorkProduct(part)\n if (typed) out.push(typed)\n }\n return out\n}\n\n/** Properties for the transcript anchor card rendered in chat */\nexport interface WorkProductCardProps {\n part: WorkProductPersistedPart\n /** Open the work product (queue detail / pane) — the card's only action. */\n onOpen?: (part: WorkProductPersistedPart) => void\n className?: string\n}\n\n/** The chat transcript anchor card for one work-product version — a compact,\n * system-authored pointer, not a parallel review surface. */\nexport function WorkProductCard({ part, onOpen, className }: WorkProductCardProps) {\n return (\n <div\n className={`flex items-center gap-3 rounded-xl border border-border bg-card px-4 py-3 ${className ?? ''}`}\n data-work-product-id={part.ref.id}\n >\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14 2 14 8 20 8\" />\n <path d=\"m9 15 2 2 4-4\" />\n </svg>\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-foreground\">{part.title}</p>\n <p className=\"text-[11px] text-muted-foreground\">\n {part.kind && <span className=\"font-mono\">{part.kind}</span>}\n {part.kind && ' · '}v{part.ref.version}\n </p>\n </div>\n <StatusPill status={part.status} />\n {onOpen && (\n <button\n type=\"button\"\n onClick={() => onOpen(part)}\n className=\"rounded-md border border-border px-2.5 py-1 text-xs font-medium text-foreground transition hover:bg-accent/30\"\n >\n Review\n </button>\n )}\n </div>\n )\n}\n\n// ── review queue panel ────────────────────────────────────────────────────\n\n/** One fetched page of queue items with an optional continuation cursor */\nexport interface ReviewQueuePage {\n items: ReviewQueueItem[]\n /** Opaque continuation token; absent ⇒ no further pages. */\n nextCursor?: string\n}\n\n/** Properties for the review queue panel over a fetch data port */\nexport interface ReviewQueuePanelProps {\n /** Data port — fetch one page of queue items (the projection's output,\n * re-validated here at the JSON boundary). */\n fetchQueue: (cursor?: string) => Promise<ReviewQueuePage>\n /** Open one queue item (navigate to its thread / detail pane). */\n onSelect?: (item: ReviewQueueItem) => void\n title?: string\n emptyLabel?: string\n className?: string\n}\n\n/** Merge a fetched page into held rows: dedupe by scopeKey, incoming wins,\n * newest updatedAt first. Exported for tests. */\nexport function mergeReviewQueuePages(existing: ReviewQueueItem[], incoming: ReviewQueueItem[]): ReviewQueueItem[] {\n const byScope = new Map<string, ReviewQueueItem>()\n for (const item of existing) byScope.set(item.scopeKey, item)\n for (const item of incoming) byScope.set(item.scopeKey, item)\n return [...byScope.values()].sort((a, b) => b.updatedAt - a.updatedAt)\n}\n\n/** The workspace review queue — cursor-paged, refreshable, selection via\n * callback. States, counts, and provenance render from the projection; the\n * panel holds no domain logic. */\nexport function ReviewQueuePanel({\n fetchQueue,\n onSelect,\n title = 'Review queue',\n emptyLabel = 'Nothing awaiting review.',\n className,\n}: ReviewQueuePanelProps) {\n const [items, setItems] = useState<ReviewQueueItem[]>([])\n const [cursor, setCursor] = useState<string | undefined>(undefined)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const load = useCallback(\n async (from?: string) => {\n setLoading(true)\n setError(null)\n try {\n const page = await fetchQueue(from)\n const validated = page.items\n .map((item) => parseReviewQueueItem(item))\n .filter((item): item is ReviewQueueItem => item !== null)\n setItems((prev) => mergeReviewQueuePages(from === undefined ? [] : prev, validated))\n setCursor(page.nextCursor)\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e))\n } finally {\n setLoading(false)\n }\n },\n [fetchQueue],\n )\n\n useEffect(() => {\n void load()\n }, [load])\n\n return (\n <div className={`space-y-2 ${className ?? ''}`}>\n <div className=\"flex items-center gap-2\">\n <h2 className=\"flex-1 text-sm font-semibold\">{title}</h2>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading}\n aria-label=\"Refresh\"\n className=\"rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground disabled:opacity-50\"\n >\n <svg viewBox=\"0 0 24 24\" className={`h-3.5 w-3.5 ${loading ? 'animate-spin' : ''}`} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M21 12a9 9 0 1 1-2.64-6.36\" />\n <polyline points=\"21 3 21 9 15 9\" />\n </svg>\n </button>\n </div>\n {error && <p className=\"rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive\">{error}</p>}\n {!error && items.length === 0 && !loading && (\n <p className=\"rounded-md border border-dashed border-border px-3 py-4 text-center text-xs text-muted-foreground\">{emptyLabel}</p>\n )}\n <ul className=\"space-y-1.5\">\n {items.map((item) => (\n <li key={item.scopeKey}>\n <button\n type=\"button\"\n onClick={() => onSelect?.(item)}\n className=\"flex w-full items-center gap-3 rounded-lg border border-border bg-card px-3 py-2.5 text-left transition hover:border-primary/40 hover:bg-accent/20\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-foreground\">\n {item.workProduct?.title ?? item.scopeKey}\n {item.workProduct && <span className=\"ml-1.5 text-[11px] text-muted-foreground\">v{item.workProduct.version}</span>}\n </p>\n <p className=\"mt-0.5 flex flex-wrap items-center gap-x-2 text-[11px] text-muted-foreground\">\n {item.workProduct?.kind && <span className=\"font-mono\">{item.workProduct.kind}</span>}\n {item.pendingAsk && <span>asks: {item.pendingAsk.title}</span>}\n {item.blockingExceptions > 0 && (\n <span className=\"text-destructive\">{item.blockingExceptions} blocking</span>\n )}\n {item.failedChecks > 0 && <span className=\"text-warning\">{item.failedChecks} failed checks</span>}\n {item.provenance && item.provenance.profileHash && (\n <span className=\"font-mono\">{item.provenance.profileHash.slice(0, 8)}</span>\n )}\n </p>\n </div>\n <StatePill state={item.state} />\n </button>\n </li>\n ))}\n </ul>\n {cursor && (\n <button\n type=\"button\"\n onClick={() => void load(cursor)}\n disabled={loading}\n className=\"w-full rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent/30 disabled:opacity-50\"\n >\n Load more\n </button>\n )}\n </div>\n )\n}\n\n// ── evidence lineage table ────────────────────────────────────────────────\n\n/** Properties for the evidence lineage table with source click-through */\nexport interface EvidenceLineageTableProps {\n evidence: readonly EvidenceEntry[]\n /** Resolve one entry's source document to an openable URL (signed\n * object-store/vault download) — the `resolveFileUrl` pattern. Absent →\n * sources render as plain refs without links. */\n resolveSourceUrl?: (entry: EvidenceEntry) => string\n className?: string\n}\n\nfunction locatorLabel(entry: EvidenceEntry): string | null {\n const parts: string[] = []\n if (entry.locator.page !== undefined) parts.push(`p.${entry.locator.page}`)\n if (entry.locator.range) parts.push(entry.locator.range)\n return parts.length ? parts.join(' · ') : null\n}\n\n/** Target → claim → source lineage rows: the \"every material value traceable\n * to source evidence\" surface. Each row names the artifact target, the\n * claim, and the source document location supporting it. */\nexport function EvidenceLineageTable({ evidence, resolveSourceUrl, className }: EvidenceLineageTableProps) {\n if (evidence.length === 0) {\n return <p className={`text-xs text-muted-foreground ${className ?? ''}`}>No evidence recorded.</p>\n }\n return (\n <div className={`overflow-x-auto ${className ?? ''}`}>\n <table className=\"w-full border-collapse text-left text-sm\">\n <thead>\n <tr className=\"border-b border-border text-[11px] uppercase tracking-wide text-muted-foreground\">\n <th className=\"py-1.5 pr-3 font-medium\">Target</th>\n <th className=\"py-1.5 pr-3 font-medium\">Claim</th>\n <th className=\"py-1.5 font-medium\">Source</th>\n </tr>\n </thead>\n <tbody>\n {evidence.map((entry) => {\n const url = resolveSourceUrl?.(entry)\n const locator = locatorLabel(entry)\n return (\n <tr key={entry.id} className=\"border-b border-border/50 align-top\">\n <td className=\"py-2 pr-3 font-mono text-xs text-foreground\">{entry.target}</td>\n <td className=\"py-2 pr-3 text-[13px] leading-snug text-foreground\">\n {entry.claim}\n {entry.locator.quote && (\n // The basis is what a reviewer weighs, so it has to be\n // visible per row, not only in the aggregate check detail.\n // A platform-sliced quote came out of the document's own\n // bytes; a model-typed one was proved to occur but was\n // transcribed. Same text, different strength of evidence.\n <span\n className={`mt-0.5 block border-l-2 pl-2 text-xs italic ${\n entry.locator.quoteBasis === 'span'\n ? 'border-primary/60 text-foreground'\n : 'border-border text-muted-foreground'\n }`}\n title={\n entry.locator.quoteBasis === 'span'\n ? 'Sliced from the source document by the platform'\n : entry.locator.quoteBasis === 'model'\n ? 'Quoted by the agent, verified to occur in the source'\n : 'Unverified quote'\n }\n >\n “{entry.locator.quote}”\n {entry.locator.quoteBasis && (\n <span className=\"ml-1.5 align-middle text-[10px] not-italic uppercase tracking-wide text-muted-foreground\">\n {entry.locator.quoteBasis === 'span' ? 'from source' : 'verified'}\n </span>\n )}\n </span>\n )}\n </td>\n <td className=\"py-2 text-xs\">\n {url ? (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-mono text-primary underline-offset-2 hover:underline\"\n >\n {entry.sourceRef}\n </a>\n ) : (\n <span className=\"font-mono text-muted-foreground\">{entry.sourceRef}</span>\n )}\n {locator && <span className=\"ml-1.5 text-muted-foreground\">{locator}</span>}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )\n}\n\n// ── exceptions + checks ───────────────────────────────────────────────────\n\nconst SEVERITY_TONES: Record<ExceptionEntry['severity'], string> = {\n blocking: 'bg-destructive/10 text-destructive',\n material: 'bg-warning/10 text-warning',\n advisory: 'bg-muted/40 text-muted-foreground',\n}\n\n/** Properties for the severity-badged exception list */\nexport interface ExceptionListProps {\n exceptions: readonly ExceptionEntry[]\n className?: string\n}\n\n/** Severity-badged exception rows with resolution state. */\nexport function ExceptionList({ exceptions, className }: ExceptionListProps) {\n if (exceptions.length === 0) {\n return <p className={`text-xs text-muted-foreground ${className ?? ''}`}>No exceptions flagged.</p>\n }\n return (\n <ul className={`space-y-1.5 ${className ?? ''}`}>\n {exceptions.map((entry) => (\n <li key={entry.id} className=\"flex items-start gap-2.5 rounded-lg border border-border bg-card px-3 py-2\">\n <span className={`mt-0.5 inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide ${SEVERITY_TONES[entry.severity]}`}>\n {entry.severity}\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className={`text-[13px] leading-snug ${entry.resolved ? 'text-muted-foreground line-through' : 'text-foreground'}`}>\n {entry.message}\n </p>\n <p className=\"mt-0.5 flex flex-wrap gap-x-2 text-[11px] text-muted-foreground\">\n <span className=\"font-mono\">{entry.kind}</span>\n {entry.targets && entry.targets.length > 0 && <span className=\"font-mono\">{entry.targets.join(', ')}</span>}\n {entry.resolved && <span>resolved{entry.resolvedBy ? ` by ${entry.resolvedBy}` : ''}</span>}\n {entry.resolutionNote && <span>{entry.resolutionNote}</span>}\n </p>\n </div>\n </li>\n ))}\n </ul>\n )\n}\n\n/** Properties for the pass/fail quality check list */\nexport interface QualityCheckListProps {\n checks: readonly QualityCheck[]\n className?: string\n}\n\n/** Pass/fail quality-check rows tagged with their source (agent self-report,\n * platform gate, judge ensemble). */\nexport function QualityCheckList({ checks, className }: QualityCheckListProps) {\n if (checks.length === 0) {\n return <p className={`text-xs text-muted-foreground ${className ?? ''}`}>No checks recorded.</p>\n }\n return (\n <ul className={`space-y-1 ${className ?? ''}`}>\n {checks.map((check) => (\n <li key={check.id} className=\"flex items-start gap-2 rounded-md px-1 py-1\">\n {check.passed ? (\n <svg viewBox=\"0 0 24 24\" className=\"mt-0.5 h-3.5 w-3.5 shrink-0 text-success\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n ) : (\n <svg viewBox=\"0 0 24 24\" className=\"mt-0.5 h-3.5 w-3.5 shrink-0 text-destructive\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-[13px] text-foreground\">\n {check.name}\n <span className=\"ml-1.5 text-[10px] uppercase tracking-wide text-muted-foreground\">{check.source}</span>\n </p>\n {check.detail && <p className=\"text-[11px] leading-snug text-muted-foreground\">{check.detail}</p>}\n </div>\n </li>\n ))}\n </ul>\n )\n}\n\n// ── provenance stamp ──────────────────────────────────────────────────────\n\n/** Properties for the provenance stamp with the optional backtest slot */\nexport interface ProvenanceStampProps {\n provenance: Pick<WorkProductProvenance, 'profileHash' | 'runId' | 'servingModels'> &\n Partial<Pick<WorkProductProvenance, 'costUsd' | 'producedAt'>>\n /** The product-resolved backtest summary for this profile hash. When its\n * trust gate failed, the composite renders as \"quality: unverified\" —\n * never a naked number. */\n backtest?: ProfileBacktestSummary\n className?: string\n}\n\n/** The audit line a reviewer approves against: which configuration produced\n * this document, what served it, and how that configuration measured on its\n * backtest. */\nexport function ProvenanceStamp({ provenance, backtest, className }: ProvenanceStampProps) {\n return (\n <div className={`flex flex-wrap items-center gap-x-2.5 gap-y-1 text-[11px] text-muted-foreground ${className ?? ''}`}>\n <span className=\"font-mono\" title={provenance.profileHash}>\n profile {provenance.profileHash ? provenance.profileHash.slice(0, 10) : '—'}\n </span>\n <span className=\"font-mono\" title={provenance.runId}>\n run {provenance.runId ? provenance.runId.slice(0, 10) : '—'}\n </span>\n {provenance.servingModels.length > 0 ? (\n provenance.servingModels.map((model) => (\n <span key={model} className=\"rounded-full bg-muted/40 px-2 py-0.5 font-mono\">\n {model}\n </span>\n ))\n ) : (\n <span className=\"italic\">serving model pending</span>\n )}\n {typeof provenance.costUsd === 'number' && <span>${provenance.costUsd.toFixed(provenance.costUsd < 0.01 ? 4 : 2)}</span>}\n {backtest &&\n (backtest.trust === 'pass' ? (\n <span className=\"text-success\">\n {backtest.cases} backtest cases · composite {backtest.composite.toFixed(2)} · trust PASS\n </span>\n ) : (\n <span className=\"text-warning\" title={backtest.trustReasons.join('; ')}>\n quality: unverified\n </span>\n ))}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  persistedPartToWorkProduct
3
- } from "./chunk-F2CBC4DY.js";
3
+ } from "./chunk-V3HO43PG.js";
4
4
  import {
5
5
  persistedPartToInteraction
6
6
  } from "./chunk-3ZK5IJSW.js";
@@ -367,4 +367,4 @@ export {
367
367
  buildAttachmentPromptBlock,
368
368
  historyContentWithAttachments
369
369
  };
370
- //# sourceMappingURL=chunk-AFNTRJQ7.js.map
370
+ //# sourceMappingURL=chunk-FEUW4G2L.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  WorkProductCard,
6
6
  workProductPartsFromMessageParts
7
- } from "./chunk-UDSY2F6N.js";
7
+ } from "./chunk-72TPRENZ.js";
8
8
  import {
9
9
  ATTACHMENT_ACCEPT,
10
10
  ATTACHMENT_MAX_COUNT,
@@ -16,11 +16,11 @@ import {
16
16
  checkAttachmentType,
17
17
  sanitizeAttachmentFileName,
18
18
  sniffBinary
19
- } from "./chunk-V55WJSR4.js";
19
+ } from "./chunk-5GA3MKOC.js";
20
20
  import {
21
21
  attachmentPartsFromMessageParts,
22
22
  formatBytes
23
- } from "./chunk-AFNTRJQ7.js";
23
+ } from "./chunk-FEUW4G2L.js";
24
24
  import {
25
25
  cancelStatusFor,
26
26
  fieldAcceptsFreeText,
@@ -3956,4 +3956,4 @@ export {
3956
3956
  useThinkingSeconds,
3957
3957
  ChatMessages
3958
3958
  };
3959
- //# sourceMappingURL=chunk-TKRI463Z.js.map
3959
+ //# sourceMappingURL=chunk-J6TNPRQN.js.map
@@ -46,6 +46,20 @@ function parseEvidenceInput(raw, path = "entry") {
46
46
  if (typeof locatorRaw.quote !== "string") return fail(`${path}.locator.quote`, "must be a string when present");
47
47
  locator.quote = locatorRaw.quote;
48
48
  }
49
+ if (locatorRaw.span !== void 0) {
50
+ const spanRaw = asRecord(locatorRaw.span);
51
+ if (!spanRaw) return fail(`${path}.locator.span`, "must be an object { start, end } when present");
52
+ for (const field of ["start", "end"]) {
53
+ const value = spanRaw[field];
54
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
55
+ return fail(`${path}.locator.span.${field}`, "must be a non-negative integer character offset");
56
+ }
57
+ }
58
+ if (spanRaw.end <= spanRaw.start) {
59
+ return fail(`${path}.locator.span`, `end (${String(spanRaw.end)}) must be greater than start (${String(spanRaw.start)}) \u2014 the range is half-open [start, end)`);
60
+ }
61
+ locator.span = { start: spanRaw.start, end: spanRaw.end };
62
+ }
49
63
  const entry = {
50
64
  id: record.id.trim(),
51
65
  sourceRef: record.sourceRef.trim(),
@@ -190,4 +204,4 @@ export {
190
204
  workProductToPersistedPart,
191
205
  persistedPartToWorkProduct
192
206
  };
193
- //# sourceMappingURL=chunk-F2CBC4DY.js.map
207
+ //# sourceMappingURL=chunk-V3HO43PG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/work-product/types.ts"],"sourcesContent":["/**\n * The reviewable work-product contract — the one durable object every Tangle\n * agent product converges on: an artifact plus its evidence map (source→field\n * lineage), exceptions, quality checks, version history, and run provenance,\n * produced by an agent and signed off by a professional.\n *\n * Import-free and client-safe by construction: the queue projection and the\n * React surfaces re-validate these shapes at JSON boundaries, so every type\n * and codec here must load in a browser bundle. Every domain word — artifact\n * `kind`, evidence `target`, exception `kind`, check `name`, `scopeKey`\n * format — is a STRING PARAMETER validated against product-supplied\n * vocabularies in the tool layer; nothing domain-specific is baked here.\n */\n\n// ── identity / status ────────────────────────────────────────────────────────\n\n/** Stable pointer at one version of one work product. */\nexport interface WorkProductRef {\n id: string\n version: number\n}\n\nexport type WorkProductStatus =\n | 'draft' // agent is emitting incrementally (queue: working)\n | 'blocked' // unresolved blocking exception (queue: blocked / missing-info)\n | 'ready' // submit_work_product accepted (queue: ready-for-review)\n | 'changes_requested' // reviewer verdict; the correction re-enters chat as a turn\n | 'approved' // reviewer sign-off on THIS version\n | 'superseded' // a newer version replaced this one\n\nconst WORK_PRODUCT_STATUSES: readonly WorkProductStatus[] = [\n 'draft',\n 'blocked',\n 'ready',\n 'changes_requested',\n 'approved',\n 'superseded',\n]\n\n/** Runtime guard for re-validating a status read off a JSON boundary. */\nexport function isWorkProductStatus(value: unknown): value is WorkProductStatus {\n return typeof value === 'string' && (WORK_PRODUCT_STATUSES as readonly string[]).includes(value)\n}\n\n// ── artifact ─────────────────────────────────────────────────────────────────\n\n/** Define the reviewable artifact body with its kind, sources, and structured field map */\nexport interface WorkProductArtifact {\n /** PARAMETER: 'return_package' | 'redline' | 'outreach_campaign' — validated\n * against the product's `artifactKinds` vocabulary at submit. */\n kind: string\n title: string\n /** Vault/object-store ref for the rendered document (large bodies). */\n path?: string\n /** Inline body when small (markdown/JSON). */\n content?: string\n mediaType?: string\n /** Diff-first products (legal): the source document the artifact redlines. */\n baseline?: { path?: string; content?: string }\n /** The structured field map lineage targets anchor to (tax: form-line ids). */\n fields?: Record<string, unknown>\n}\n\n// ── evidence / lineage: source doc + locator → field/claim ───────────────────\n\n/** A half-open `[start, end)` character range into the source document's text.\n * Offsets index exactly the string the product's `readSourceText` seam\n * returns for the same `sourceRef` — which is the string its document-reading\n * tool pages with an `offset`. The PLATFORM slices `locator.quote` out of it,\n * so a span citation cannot name text the document does not contain. */\nexport interface EvidenceSpan {\n start: number\n end: number\n}\n\n/** How `locator.quote` got there. Server-set on every path — never read from\n * model args, because the whole value of the distinction is that a reviewer\n * can trust it:\n * - `span` the platform sliced it out of the source bytes (unfalsifiable)\n * - `model` the model supplied the text and the platform PROVED it occurs */\nexport type QuoteBasis = 'span' | 'model'\n\n/** Point into a source document: page, free-form range, span, verbatim quote */\nexport interface EvidenceLocator {\n page?: number\n /** 'L120-L134' | 'B7' | '¶4' — free-form, non-empty when present. */\n range?: string\n /** Verbatim supporting quote from the source. Model-supplied and verified,\n * or platform-sliced from {@link EvidenceLocator.span} — read `quoteBasis`\n * to tell which. */\n quote?: string\n /** Character range the quote is sliced from. The preferred citation form:\n * two integers cannot be a fabricated quote. */\n span?: EvidenceSpan\n /** Server-set provenance for `quote`; a model-supplied value is discarded. */\n quoteBasis?: QuoteBasis\n}\n\n/** One lineage row: a source document location supporting one artifact claim */\nexport interface EvidenceEntry {\n /** Agent-supplied stable id — the same id re-emitted is an upsert/replace. */\n id: string\n /** Vault path / attachment id / object-store key of the SOURCE document. */\n sourceRef: string\n locator: EvidenceLocator\n /** Artifact field/claim the evidence supports: '1040.line_9' |\n * 'clause:indemnification' | 'icp.employee_count' — product vocabulary. */\n target: string\n /** The value/assertion at the target. */\n claim: string\n /** 0..1; absent = unstated (never defaulted). */\n confidence?: number\n}\n\n// ── exceptions ───────────────────────────────────────────────────────────────\n\nexport type ExceptionSeverity = 'blocking' | 'material' | 'advisory'\n\n/** One flagged problem with the work product, resolvable by agent or reviewer */\nexport interface ExceptionEntry {\n id: string\n severity: ExceptionSeverity\n /** PARAMETER: 'missing_document' | 'inconsistent_source' | … — validated\n * against the product's `exceptionKinds` vocabulary. */\n kind: string\n message: string\n /** Affected artifact targets. */\n targets?: string[]\n resolved: boolean\n resolvedBy?: 'agent' | 'reviewer'\n resolutionNote?: string\n}\n\n/** Unresolved blocking exceptions are what park a work product in `blocked`. */\nexport function unresolvedBlockingExceptions(exceptions: readonly ExceptionEntry[]): ExceptionEntry[] {\n return exceptions.filter((entry) => entry.severity === 'blocking' && !entry.resolved)\n}\n\n// ── quality checks ───────────────────────────────────────────────────────────\n\n/** One named quality verdict on the work product, tagged with who computed it */\nexport interface QualityCheck {\n id: string\n /** Product vocabulary: 'totals_reconcile', 'evidence_coverage', … */\n name: string\n passed: boolean\n detail?: string\n /** agent self-report | shell-computed | eval-ensemble verdict. */\n source: 'agent' | 'platform' | 'judge'\n}\n\n// ── provenance (the backtest spine) ──────────────────────────────────────────\n\n/** The audit triple binding a work product to the exact configuration and run\n * that produced it. `profileHash` is agent-eval's `agentProfileHash()` of the\n * EXACT shipped profile — the same hash that keys scorecard cells, so a\n * reviewer can see the measured backtest history of the configuration that\n * produced the document. */\nexport interface WorkProductProvenance {\n profileHash: string\n /** Chat turnId or mission-step run id. */\n runId: string\n /** Back-filled from the usage receipt / serving-model header at turn\n * completion — only the completed turn knows what actually served. */\n servingModels: string[]\n sessionId?: string\n missionRef?: { missionId: string; stepId: string }\n costUsd?: number\n producedAt: number\n}\n\n/** The product-resolved backtest summary for one profile hash (from its own\n * eval artifacts via agent-eval's `loadScorecard`) — the shell defines only\n * this TYPE and the `ProvenanceStamp` slot that renders it. */\nexport interface ProfileBacktestSummary {\n profileHash: string\n /** Backtest cases behind the composite. */\n cases: number\n composite: number\n /** The eval-campaign trust gate's verdict on whether the composite is\n * allowed to be believed; 'fail' renders as \"quality: unverified\". */\n trust: 'pass' | 'fail'\n trustReasons: string[]\n}\n\n// ── version history ──────────────────────────────────────────────────────────\n\n/** Frozen milestone row for one version of the work product */\nexport interface WorkProductVersionEntry {\n version: number\n status: WorkProductStatus\n provenance: WorkProductProvenance\n /** Frozen snapshot ref of this version's body (object-store key) — the\n * DiffView input for vN-1 vs vN. */\n artifactPath?: string\n reviewedBy?: string\n reviewNote?: string\n at: number\n}\n\n// ── the durable row ──────────────────────────────────────────────────────────\n\n/** Define the durable work product row accumulating artifact, lineage, exceptions, checks, and history */\nexport interface WorkProductRecord {\n /** Minted server-side; the model NEVER supplies ids — it addresses work by\n * `scopeKey`. */\n id: string\n workspaceId: string\n /** The chat thread that drives it (chat stays first). */\n threadId: string | null\n /** Product engagement key: 'return:acme:2025' | 'contract:acme-msa' |\n * 'campaign:cpa-pilots'. */\n scopeKey: string\n status: WorkProductStatus\n version: number\n /** `null` while the draft accumulates (evidence streams in before the\n * artifact arrives); non-null from the submit transition onward. Typed\n * honestly rather than forcing a placeholder artifact on every draft. */\n artifact: WorkProductArtifact | null\n evidence: EvidenceEntry[]\n exceptions: ExceptionEntry[]\n checks: QualityCheck[]\n provenance: WorkProductProvenance\n history: WorkProductVersionEntry[]\n createdAt: number\n updatedAt: number\n}\n\n// ── store port — the /missions MissionStorePort pattern ──────────────────────\n\n/** Fields a guarded write compares against the values the caller read. An\n * absent field is unguarded. A SQL implementation compares like-for-like\n * scalar columns; the in-memory store does the same. */\nexport interface WorkProductUpdateGuard {\n status?: WorkProductStatus\n version?: number\n}\n\n/** Fields a guarded write sets when the guard holds. */\nexport interface WorkProductPatch {\n status?: WorkProductStatus\n version?: number\n artifact?: WorkProductArtifact\n evidence?: EvidenceEntry[]\n exceptions?: ExceptionEntry[]\n checks?: QualityCheck[]\n provenance?: WorkProductProvenance\n history?: WorkProductVersionEntry[]\n updatedAt?: number\n}\n\n/** One audit-trail row, appended after every committed state change. */\nexport interface WorkProductAuditEvent {\n workProductId: string\n workspaceId: string\n /** Machine-readable transition name ('wp.created' | 'wp.evidence' |\n * 'wp.ready' | 'wp.verdict' | …). */\n step: string\n message: string\n metadata: Record<string, unknown>\n at: number\n}\n\n/**\n * Persistence seam — the product implements this over its own tables. The\n * invariant the implementation MUST keep: `update` applies `patch` ONLY when\n * every guard field still equals the stored value, and returns `null` when\n * the guard misses — a typed conflict, never a clobber.\n */\nexport interface WorkProductStorePort {\n load(id: string): Promise<WorkProductRecord | null>\n /** The scope's single OPEN accumulator row — status `draft` or `blocked` —\n * or null when the scope has only reviewed/terminal rows (or none). The\n * `changes_requested` row is found via {@link listByWorkspace}; reopening\n * it is the service's job. */\n findDraft(workspaceId: string, scopeKey: string): Promise<WorkProductRecord | null>\n listByWorkspace(workspaceId: string, opts?: { status?: WorkProductStatus[] }): Promise<WorkProductRecord[]>\n /** `extras` are opaque product-column values written in the SAME statement\n * as the record (single-write creation), or ignored when the table has no\n * extra columns. */\n insert(record: WorkProductRecord, extras?: Record<string, unknown>): Promise<WorkProductRecord>\n /** CAS: apply `patch` ONLY when guard fields equal stored values; null on a\n * miss — typed conflict, never a clobber. */\n update(id: string, guard: WorkProductUpdateGuard, patch: WorkProductPatch): Promise<WorkProductRecord | null>\n appendEvent(event: WorkProductAuditEvent): Promise<void>\n}\n\n// ── input codecs (strict, named-field errors) ────────────────────────────────\n\n/** Validation result whose failure names the exact field, so a tool layer can\n * hand the model a correctable error it can act on. */\nexport type WorkProductParseResult<T> =\n | { ok: true; value: T }\n | { ok: false; field: string; error: string }\n\nfunction fail<T>(field: string, error: string): WorkProductParseResult<T> {\n return { ok: false, field, error }\n}\n\nfunction nonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : null\n}\n\n/** Validate one raw evidence entry field-by-field. `path` prefixes the failure\n * field name (e.g. `entries[3]`) so batched calls name the exact offender. */\nexport function parseEvidenceInput(raw: unknown, path = 'entry'): WorkProductParseResult<EvidenceEntry> {\n const record = asRecord(raw)\n if (!record) return fail(path, 'must be an object')\n if (!nonEmptyString(record.id)) return fail(`${path}.id`, 'must be a non-empty string (stable per entry; re-emitting the same id replaces it)')\n if (!nonEmptyString(record.sourceRef)) return fail(`${path}.sourceRef`, 'must be a non-empty source document ref')\n if (!nonEmptyString(record.target)) return fail(`${path}.target`, 'must name the artifact field/claim this evidence supports')\n if (!nonEmptyString(record.claim)) return fail(`${path}.claim`, 'must state the value/assertion at the target')\n const locatorRaw = record.locator === undefined ? {} : asRecord(record.locator)\n if (!locatorRaw) return fail(`${path}.locator`, 'must be an object when present')\n const locator: EvidenceLocator = {}\n if (locatorRaw.page !== undefined) {\n if (typeof locatorRaw.page !== 'number' || !Number.isFinite(locatorRaw.page)) {\n return fail(`${path}.locator.page`, 'must be a finite number when present')\n }\n locator.page = locatorRaw.page\n }\n if (locatorRaw.range !== undefined) {\n if (!nonEmptyString(locatorRaw.range)) return fail(`${path}.locator.range`, 'must be a non-empty string when present')\n locator.range = locatorRaw.range\n }\n if (locatorRaw.quote !== undefined) {\n if (typeof locatorRaw.quote !== 'string') return fail(`${path}.locator.quote`, 'must be a string when present')\n locator.quote = locatorRaw.quote\n }\n if (locatorRaw.span !== undefined) {\n const spanRaw = asRecord(locatorRaw.span)\n if (!spanRaw) return fail(`${path}.locator.span`, 'must be an object { start, end } when present')\n for (const field of ['start', 'end'] as const) {\n const value = spanRaw[field]\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {\n return fail(`${path}.locator.span.${field}`, 'must be a non-negative integer character offset')\n }\n }\n if ((spanRaw.end as number) <= (spanRaw.start as number)) {\n return fail(`${path}.locator.span`, `end (${String(spanRaw.end)}) must be greater than start (${String(spanRaw.start)}) — the range is half-open [start, end)`)\n }\n locator.span = { start: spanRaw.start as number, end: spanRaw.end as number }\n }\n // `quoteBasis` is server-set. A model-supplied value is DROPPED rather than\n // rejected: it is not a correctable mistake the model should burn a turn on,\n // and silently honouring it would let a model label an unverified quote\n // 'span'. The tool layer stamps the real basis after resolution.\n const entry: EvidenceEntry = {\n id: (record.id as string).trim(),\n sourceRef: (record.sourceRef as string).trim(),\n locator,\n target: (record.target as string).trim(),\n claim: record.claim as string,\n }\n if (record.confidence !== undefined) {\n if (typeof record.confidence !== 'number' || !(record.confidence >= 0 && record.confidence <= 1)) {\n return fail(`${path}.confidence`, 'must be a number in 0..1 when present')\n }\n entry.confidence = record.confidence\n }\n return { ok: true, value: entry }\n}\n\nconst EXCEPTION_SEVERITIES: readonly ExceptionSeverity[] = ['blocking', 'material', 'advisory']\n\n/** Validate one raw exception entry field-by-field. Kind MEMBERSHIP in the\n * product vocabulary is the tool layer's check (it owns the config). */\nexport function parseExceptionInput(raw: unknown, path = 'exception'): WorkProductParseResult<ExceptionEntry> {\n const record = asRecord(raw)\n if (!record) return fail(path, 'must be an object')\n if (!nonEmptyString(record.id)) return fail(`${path}.id`, 'must be a non-empty string (stable per entry; re-emitting the same id replaces it)')\n if (!EXCEPTION_SEVERITIES.includes(record.severity as ExceptionSeverity)) {\n return fail(`${path}.severity`, `must be one of: ${EXCEPTION_SEVERITIES.join(', ')}`)\n }\n if (!nonEmptyString(record.kind)) return fail(`${path}.kind`, 'must be a non-empty exception kind')\n if (!nonEmptyString(record.message)) return fail(`${path}.message`, 'must describe the problem')\n const entry: ExceptionEntry = {\n id: (record.id as string).trim(),\n severity: record.severity as ExceptionSeverity,\n kind: (record.kind as string).trim(),\n message: record.message as string,\n resolved: record.resolved === true,\n }\n if (record.targets !== undefined) {\n if (!Array.isArray(record.targets) || !record.targets.every(nonEmptyString)) {\n return fail(`${path}.targets`, 'must be an array of non-empty strings when present')\n }\n entry.targets = record.targets as string[]\n }\n if (record.resolvedBy !== undefined) {\n if (record.resolvedBy !== 'agent' && record.resolvedBy !== 'reviewer') {\n return fail(`${path}.resolvedBy`, \"must be 'agent' or 'reviewer' when present\")\n }\n entry.resolvedBy = record.resolvedBy\n }\n if (record.resolutionNote !== undefined) {\n if (typeof record.resolutionNote !== 'string') return fail(`${path}.resolutionNote`, 'must be a string when present')\n entry.resolutionNote = record.resolutionNote\n }\n return { ok: true, value: entry }\n}\n\n/** Validate a raw artifact. Kind MEMBERSHIP in `artifactKinds` is the tool\n * layer's check. An artifact must carry a body: at least one of `path`,\n * `content`, or `fields`. */\nexport function parseArtifactInput(raw: unknown, path = 'artifact'): WorkProductParseResult<WorkProductArtifact> {\n const record = asRecord(raw)\n if (!record) return fail(path, 'must be an object')\n if (!nonEmptyString(record.kind)) return fail(`${path}.kind`, 'must be a non-empty artifact kind')\n if (!nonEmptyString(record.title)) return fail(`${path}.title`, 'must be a non-empty title')\n for (const key of ['path', 'content', 'mediaType'] as const) {\n if (record[key] !== undefined && !nonEmptyString(record[key])) {\n return fail(`${path}.${key}`, 'must be a non-empty string when present')\n }\n }\n let baseline: WorkProductArtifact['baseline']\n if (record.baseline !== undefined) {\n const baselineRecord = asRecord(record.baseline)\n if (!baselineRecord) return fail(`${path}.baseline`, 'must be an object when present')\n baseline = {}\n if (baselineRecord.path !== undefined) {\n if (!nonEmptyString(baselineRecord.path)) return fail(`${path}.baseline.path`, 'must be a non-empty string when present')\n baseline.path = (baselineRecord.path as string).trim()\n }\n if (baselineRecord.content !== undefined) {\n if (typeof baselineRecord.content !== 'string') return fail(`${path}.baseline.content`, 'must be a string when present')\n baseline.content = baselineRecord.content\n }\n }\n let fields: Record<string, unknown> | undefined\n if (record.fields !== undefined) {\n const fieldsRecord = asRecord(record.fields)\n if (!fieldsRecord) return fail(`${path}.fields`, 'must be an object when present')\n fields = fieldsRecord\n }\n if (record.path === undefined && record.content === undefined && fields === undefined) {\n return fail(path, 'must carry a body: at least one of path, content, or fields')\n }\n const artifact: WorkProductArtifact = {\n kind: (record.kind as string).trim(),\n title: (record.title as string).trim(),\n }\n if (record.path !== undefined) artifact.path = (record.path as string).trim()\n if (record.content !== undefined) artifact.content = record.content as string\n if (record.mediaType !== undefined) artifact.mediaType = (record.mediaType as string).trim()\n if (baseline !== undefined) artifact.baseline = baseline\n if (fields !== undefined) artifact.fields = fields\n return { ok: true, value: artifact }\n}\n\n/** Agent self-reported check input for `submit_work_product` (persisted with\n * `source: 'agent'`; `platform`/`judge` sources are never model-suppliable). */\nexport interface AgentCheckInput {\n id: string\n name: string\n passed: boolean\n detail?: string\n}\n\n/** Validate one agent self-check ensuring identifiers, verdict flag, and optional detail are well formed */\nexport function parseAgentCheckInput(raw: unknown, path = 'check'): WorkProductParseResult<AgentCheckInput> {\n const record = asRecord(raw)\n if (!record) return fail(path, 'must be an object')\n if (!nonEmptyString(record.id)) return fail(`${path}.id`, 'must be a non-empty string')\n if (!nonEmptyString(record.name)) return fail(`${path}.name`, 'must be a non-empty check name')\n if (typeof record.passed !== 'boolean') return fail(`${path}.passed`, 'must be a boolean')\n const check: AgentCheckInput = {\n id: (record.id as string).trim(),\n name: (record.name as string).trim(),\n passed: record.passed,\n }\n if (record.detail !== undefined) {\n if (typeof record.detail !== 'string') return fail(`${path}.detail`, 'must be a string when present')\n check.detail = record.detail\n }\n return { ok: true, value: check }\n}\n\n// ── chat transcript anchor (system-authored) ─────────────────────────────────\n\n/**\n * The persisted `type:'work_product'` transcript anchor — the chat card that\n * keeps chat the driver surface for review. The PLATFORM writes it on the\n * ready transition (and updates it on a verdict); no prompt ever teaches an\n * agent to author one. `/chat-store` aliases this as `ChatWorkProductPart` in\n * its stored-part union, exactly like the `interaction`/`plan` members.\n */\nexport interface WorkProductPersistedPart {\n type: 'work_product'\n ref: WorkProductRef\n kind: string\n title: string\n status: WorkProductStatus\n}\n\n/** Project the transcript anchor part from a record. Requires the submitted\n * artifact (the anchor is written on the ready transition, after which\n * `artifact` is always non-null); falls back to the scopeKey label for\n * defensive callers on an accumulating draft. */\nexport function workProductToPersistedPart(record: WorkProductRecord): WorkProductPersistedPart {\n return {\n type: 'work_product',\n ref: { id: record.id, version: record.version },\n kind: record.artifact?.kind ?? '',\n title: record.artifact?.title ?? record.scopeKey,\n status: record.status,\n }\n}\n\n/** Re-validate a stored/wire part into the typed anchor; null for junk. */\nexport function persistedPartToWorkProduct(part: Record<string, unknown>): WorkProductPersistedPart | null {\n if (!part || part.type !== 'work_product') return null\n const ref = asRecord(part.ref)\n if (!ref || !nonEmptyString(ref.id) || typeof ref.version !== 'number' || !Number.isFinite(ref.version)) return null\n if (typeof part.kind !== 'string' || !nonEmptyString(part.title) || !isWorkProductStatus(part.status)) return null\n return {\n type: 'work_product',\n ref: { id: ref.id as string, version: ref.version },\n kind: part.kind,\n title: part.title as string,\n status: part.status,\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unresolvedBlockingExceptions
3
- } from "./chunk-F2CBC4DY.js";
3
+ } from "./chunk-V3HO43PG.js";
4
4
 
5
5
  // src/work-product/queue.ts
6
6
  var OPEN_STATUSES = /* @__PURE__ */ new Set(["draft", "blocked", "ready", "changes_requested"]);
@@ -138,4 +138,4 @@ export {
138
138
  projectReviewQueue,
139
139
  parseReviewQueueItem
140
140
  };
141
- //# sourceMappingURL=chunk-UP33Z633.js.map
141
+ //# sourceMappingURL=chunk-ZRSUCTST.js.map
@@ -1,6 +1,6 @@
1
1
  import { Part } from '@tangle-network/agent-interface';
2
2
  import { a as ChatInteractionField, C as ChatInteractionStatus, I as InteractionAnswers, N as NoticeKind } from './contract-CEewO6DI.js';
3
- import { c as WorkProductPersistedPart } from './types-XIZqIdX1.js';
3
+ import { c as WorkProductPersistedPart } from './types-DeEOhQbv.js';
4
4
  import { ChatPlanPersistedPart } from './plans/index.js';
5
5
 
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { W as WorkProductRef, a as WorkProductProvenance, b as WorkProductRecord } from './types-XIZqIdX1.js';
1
+ import { W as WorkProductRef, a as WorkProductProvenance, b as WorkProductRecord } from './types-DeEOhQbv.js';
2
2
 
3
3
  /**
4
4
  * The review queue is a PROJECTION, not a store — a client-safe pure fold of
@@ -38,13 +38,35 @@ interface WorkProductArtifact {
38
38
  /** The structured field map lineage targets anchor to (tax: form-line ids). */
39
39
  fields?: Record<string, unknown>;
40
40
  }
41
- /** Point into a source document: page, free-form range, verbatim quote */
41
+ /** A half-open `[start, end)` character range into the source document's text.
42
+ * Offsets index exactly the string the product's `readSourceText` seam
43
+ * returns for the same `sourceRef` — which is the string its document-reading
44
+ * tool pages with an `offset`. The PLATFORM slices `locator.quote` out of it,
45
+ * so a span citation cannot name text the document does not contain. */
46
+ interface EvidenceSpan {
47
+ start: number;
48
+ end: number;
49
+ }
50
+ /** How `locator.quote` got there. Server-set on every path — never read from
51
+ * model args, because the whole value of the distinction is that a reviewer
52
+ * can trust it:
53
+ * - `span` the platform sliced it out of the source bytes (unfalsifiable)
54
+ * - `model` the model supplied the text and the platform PROVED it occurs */
55
+ type QuoteBasis = 'span' | 'model';
56
+ /** Point into a source document: page, free-form range, span, verbatim quote */
42
57
  interface EvidenceLocator {
43
58
  page?: number;
44
59
  /** 'L120-L134' | 'B7' | '¶4' — free-form, non-empty when present. */
45
60
  range?: string;
46
- /** Verbatim supporting quote from the source. */
61
+ /** Verbatim supporting quote from the source. Model-supplied and verified,
62
+ * or platform-sliced from {@link EvidenceLocator.span} — read `quoteBasis`
63
+ * to tell which. */
47
64
  quote?: string;
65
+ /** Character range the quote is sliced from. The preferred citation form:
66
+ * two integers cannot be a fabricated quote. */
67
+ span?: EvidenceSpan;
68
+ /** Server-set provenance for `quote`; a model-supplied value is discarded. */
69
+ quoteBasis?: QuoteBasis;
48
70
  }
49
71
  /** One lineage row: a source document location supporting one artifact claim */
50
72
  interface EvidenceEntry {
@@ -265,4 +287,4 @@ declare function workProductToPersistedPart(record: WorkProductRecord): WorkProd
265
287
  /** Re-validate a stored/wire part into the typed anchor; null for junk. */
266
288
  declare function persistedPartToWorkProduct(part: Record<string, unknown>): WorkProductPersistedPart | null;
267
289
 
268
- export { type AgentCheckInput as A, type EvidenceEntry as E, type ProfileBacktestSummary as P, type QualityCheck as Q, type WorkProductRef as W, type WorkProductProvenance as a, type WorkProductRecord as b, type WorkProductPersistedPart as c, type WorkProductStorePort as d, type WorkProductAuditEvent as e, type WorkProductArtifact as f, type ExceptionEntry as g, type WorkProductStatus as h, type EvidenceLocator as i, type ExceptionSeverity as j, type WorkProductParseResult as k, type WorkProductPatch as l, type WorkProductUpdateGuard as m, type WorkProductVersionEntry as n, isWorkProductStatus as o, parseAgentCheckInput as p, parseArtifactInput as q, parseEvidenceInput as r, parseExceptionInput as s, persistedPartToWorkProduct as t, unresolvedBlockingExceptions as u, workProductToPersistedPart as w };
290
+ export { type AgentCheckInput as A, type EvidenceEntry as E, type ProfileBacktestSummary as P, type QualityCheck as Q, type WorkProductRef as W, type WorkProductProvenance as a, type WorkProductRecord as b, type WorkProductPersistedPart as c, type WorkProductStorePort as d, type WorkProductAuditEvent as e, type WorkProductArtifact as f, type ExceptionEntry as g, type WorkProductStatus as h, type EvidenceLocator as i, type EvidenceSpan as j, type ExceptionSeverity as k, type QuoteBasis as l, type WorkProductParseResult as m, type WorkProductPatch as n, type WorkProductUpdateGuard as o, type WorkProductVersionEntry as p, isWorkProductStatus as q, parseAgentCheckInput as r, parseArtifactInput as s, parseEvidenceInput as t, parseExceptionInput as u, persistedPartToWorkProduct as v, unresolvedBlockingExceptions as w, workProductToPersistedPart as x };
@@ -5,17 +5,17 @@ export { f as ChatFreeTextField, g as ComposerAnswerDelivery, h as INTERACTION_C
5
5
  import { ChatPlan } from '../plans/index.js';
6
6
  import { InteractionData } from '@tangle-network/agent-interface';
7
7
  export { InteractionData, InteractionOutcome, InteractionRequest } from '@tangle-network/agent-interface';
8
- import { M as FileMention, g as ChatMentionPart, b as ChatAttachmentPart, a as ChatAttachmentKind, Q as ChatAttachmentInput } from '../parts-Co_lFgLI.js';
9
- export { f as ChatMentionKind, P as ChatTurnFilePartInput, O as ChatTurnPartInput, N as ChatTurnRequestPayload, U as DISPATCH_MAX_MEDIA_PARTS, V as DISPATCH_MAX_PARTS, W as DISPATCH_REQUEST_MAX_BYTES, X as DISPATCH_STRUCTURAL_RESERVE_BYTES, $ as ProducerErrorEvent, a0 as ProducerNoticeEvent, a1 as ProducerPassthroughEvent, a2 as ProducerPassthroughEventType, a3 as ProducerReasoningEvent, a4 as ProducerTextEvent, a5 as ProducerToolCallEvent, a6 as ProducerToolResultEvent, a7 as ProducerUsageEvent, a8 as ProducerWireEvent, u as attachmentInputToPart, v as attachmentKindForMime, w as attachmentPartsFromMessageParts, ab as base64WireLen, ac as buildMentionPromptBlock, ad as chatTurnRequestInit, ae as fileMentionsToParts, z as isChatAttachmentPart, ag as mediaTypeForMentionPath, J as mentionInputToPart, ah as mentionKindForPath, K as mentionPartsFromMessageParts } from '../parts-Co_lFgLI.js';
10
- import { E as EvidenceEntry, g as ExceptionEntry, a as WorkProductProvenance, P as ProfileBacktestSummary, Q as QualityCheck, c as WorkProductPersistedPart, h as WorkProductStatus } from '../types-XIZqIdX1.js';
8
+ import { M as FileMention, g as ChatMentionPart, b as ChatAttachmentPart, a as ChatAttachmentKind, Q as ChatAttachmentInput } from '../parts-DIKcC1p6.js';
9
+ export { f as ChatMentionKind, P as ChatTurnFilePartInput, O as ChatTurnPartInput, N as ChatTurnRequestPayload, U as DISPATCH_MAX_MEDIA_PARTS, V as DISPATCH_MAX_PARTS, W as DISPATCH_REQUEST_MAX_BYTES, X as DISPATCH_STRUCTURAL_RESERVE_BYTES, $ as ProducerErrorEvent, a0 as ProducerNoticeEvent, a1 as ProducerPassthroughEvent, a2 as ProducerPassthroughEventType, a3 as ProducerReasoningEvent, a4 as ProducerTextEvent, a5 as ProducerToolCallEvent, a6 as ProducerToolResultEvent, a7 as ProducerUsageEvent, a8 as ProducerWireEvent, u as attachmentInputToPart, v as attachmentKindForMime, w as attachmentPartsFromMessageParts, ab as base64WireLen, ac as buildMentionPromptBlock, ad as chatTurnRequestInit, ae as fileMentionsToParts, z as isChatAttachmentPart, ag as mediaTypeForMentionPath, J as mentionInputToPart, ah as mentionKindForPath, K as mentionPartsFromMessageParts } from '../parts-DIKcC1p6.js';
10
+ import { E as EvidenceEntry, g as ExceptionEntry, a as WorkProductProvenance, P as ProfileBacktestSummary, Q as QualityCheck, c as WorkProductPersistedPart, h as WorkProductStatus } from '../types-DeEOhQbv.js';
11
11
  import { S as StepAgentActivity } from '../agent-activity-C8ZG0F0M.js';
12
12
  import { F as FlowTrace } from '../flow-types-CJxEmaRy.js';
13
- import { a as ReviewQueueItem, c as ReviewQueueState } from '../queue-CD5-_VTX.js';
14
- export { p as parseReviewQueueItem } from '../queue-CD5-_VTX.js';
13
+ import { a as ReviewQueueItem, c as ReviewQueueState } from '../queue-DBkulxW1.js';
14
+ export { p as parseReviewQueueItem } from '../queue-DBkulxW1.js';
15
15
  export { S as SandboxTerminalConnection, a as SandboxTerminalConnectionResponse, U as UseSandboxTerminalConnectionOptions, b as UseSandboxTerminalConnectionResult, t as tabTerminalConnectionId, u as useSandboxTerminalConnection } from '../sandbox-terminal-ChNEdHF8.js';
16
16
  import { CatalogModel } from '../catalog/index.js';
17
17
  import { Harness } from '../harness/index.js';
18
- export { a as ATTACHMENT_ACCEPT, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse } from '../attachment-validation-DRuLEjAE.js';
18
+ export { a as ATTACHMENT_ACCEPT, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse } from '../attachment-validation-yJ6Id4pR.js';
19
19
  export { a as attachmentPartKey } from '../stream-normalizer-DnuqkZvw.js';
20
20
 
21
21
  /** Represent durable plan decisions as either approved or rejected */
@@ -74,7 +74,7 @@ import {
74
74
  useSmoothText,
75
75
  useThinkingSeconds,
76
76
  waterfallLayout
77
- } from "../chunk-TKRI463Z.js";
77
+ } from "../chunk-J6TNPRQN.js";
78
78
  import "../chunk-FBVLEGEG.js";
79
79
  import {
80
80
  EvidenceLineageTable,
@@ -87,17 +87,17 @@ import {
87
87
  reviewQueueStateLabel,
88
88
  workProductPartsFromMessageParts,
89
89
  workProductStatusLabel
90
- } from "../chunk-UDSY2F6N.js";
90
+ } from "../chunk-72TPRENZ.js";
91
91
  import {
92
92
  parseReviewQueueItem
93
- } from "../chunk-UP33Z633.js";
93
+ } from "../chunk-ZRSUCTST.js";
94
94
  import {
95
95
  tabTerminalConnectionId,
96
96
  useSandboxTerminalConnection
97
97
  } from "../chunk-HCOROIRT.js";
98
98
  import {
99
99
  ATTACHMENT_ACCEPT
100
- } from "../chunk-V55WJSR4.js";
100
+ } from "../chunk-5GA3MKOC.js";
101
101
  import {
102
102
  DISPATCH_MAX_MEDIA_PARTS,
103
103
  DISPATCH_MAX_PARTS,
@@ -115,8 +115,8 @@ import {
115
115
  mentionInputToPart,
116
116
  mentionKindForPath,
117
117
  mentionPartsFromMessageParts
118
- } from "../chunk-AFNTRJQ7.js";
119
- import "../chunk-F2CBC4DY.js";
118
+ } from "../chunk-FEUW4G2L.js";
119
+ import "../chunk-V3HO43PG.js";
120
120
  import {
121
121
  attachmentPartKey
122
122
  } from "../chunk-5EPIPT4V.js";