@tangle-network/agent-app 0.43.66 → 0.43.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant/index.d.ts +4 -2
- package/dist/assistant/index.js +6 -3
- package/dist/assistant/index.js.map +1 -1
- package/dist/{attachment-validation-B2FFna9E.d.ts → attachment-validation-Dv1A_Puy.d.ts} +1 -1
- package/dist/chat-routes/index.d.ts +4 -3
- package/dist/chat-routes/index.js +6 -4
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +3 -2
- package/dist/chat-store/index.js +4 -1
- package/dist/chat-store/index.js.map +1 -1
- package/dist/{chunk-JWBZ74TW.js → chunk-7ESQUSAC.js} +5 -3
- package/dist/{chunk-JWBZ74TW.js.map → chunk-7ESQUSAC.js.map} +1 -1
- package/dist/{chunk-X3N2H6JE.js → chunk-AFNTRJQ7.js} +10 -1
- package/dist/chunk-AFNTRJQ7.js.map +1 -0
- package/dist/chunk-F2CBC4DY.js +193 -0
- package/dist/chunk-F2CBC4DY.js.map +1 -0
- package/dist/chunk-HRH7ASAG.js +759 -0
- package/dist/chunk-HRH7ASAG.js.map +1 -0
- package/dist/{chunk-YTSEDJWA.js → chunk-RXOTWZ4G.js} +22 -6
- package/dist/chunk-RXOTWZ4G.js.map +1 -0
- package/dist/chunk-UDSY2F6N.js +331 -0
- package/dist/chunk-UDSY2F6N.js.map +1 -0
- package/dist/chunk-UOAYS72M.js +80 -0
- package/dist/chunk-UOAYS72M.js.map +1 -0
- package/dist/chunk-UP33Z633.js +141 -0
- package/dist/chunk-UP33Z633.js.map +1 -0
- package/dist/{chunk-FMDMI25K.js → chunk-V55WJSR4.js} +2 -2
- package/dist/{chunk-YKBDH2UY.js → chunk-WL7XHLDK.js} +2 -2
- package/dist/{chunk-7CTIUCQ4.js → chunk-YEFFHORB.js} +2 -73
- package/dist/chunk-YEFFHORB.js.map +1 -0
- package/dist/eval-campaign/index.d.ts +2 -81
- package/dist/index.d.ts +5 -1
- package/dist/index.js +62 -8
- package/dist/model-resolution/index.d.ts +100 -1
- package/dist/model-resolution/index.js +108 -0
- package/dist/model-resolution/index.js.map +1 -1
- package/dist/{parts-Bg8qcDvB.d.ts → parts-2ymE5cs-.d.ts} +15 -2
- package/dist/queue-C24V13h9.d.ts +68 -0
- package/dist/runtime/index.js +3 -2
- package/dist/sandbox/index.js +3 -2
- package/dist/teams/index.js +5 -5
- package/dist/teams/invitations-api.js +4 -4
- package/dist/teams-react/index.js +3 -3
- package/dist/tools/index.js +8 -6
- package/dist/trust-gate-Dcm5xSva.d.ts +83 -0
- package/dist/types-CEchbvgz.d.ts +268 -0
- package/dist/web-react/index.d.ts +97 -5
- package/dist/web-react/index.js +31 -4
- package/dist/work-product/index.d.ts +331 -0
- package/dist/work-product/index.js +54 -0
- package/dist/work-product/index.js.map +1 -0
- package/dist/work-product-react/index.d.ts +36 -0
- package/dist/work-product-react/index.js +180 -0
- package/dist/work-product-react/index.js.map +1 -0
- package/package.json +15 -1
- package/dist/chunk-7CTIUCQ4.js.map +0 -1
- package/dist/chunk-X3N2H6JE.js.map +0 -1
- package/dist/chunk-YTSEDJWA.js.map +0 -1
- /package/dist/{chunk-FMDMI25K.js.map → chunk-V55WJSR4.js.map} +0 -0
- /package/dist/{chunk-YKBDH2UY.js.map → chunk-WL7XHLDK.js.map} +0 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { h as WorkProductProvenance, l as WorkProductStorePort, d as WorkProductAuditEvent, i as WorkProductRecord, W as WorkProductArtifact, Q as QualityCheck, E as EvidenceEntry, b as ExceptionEntry, k as WorkProductStatus, g as WorkProductPersistedPart } from '../types-CEchbvgz.js';
|
|
2
|
+
export { A as AgentCheckInput, a as EvidenceLocator, c as ExceptionSeverity, P as ProfileBacktestSummary, e as WorkProductParseResult, f as WorkProductPatch, j as WorkProductRef, m as WorkProductUpdateGuard, n as WorkProductVersionEntry, o as isWorkProductStatus, p as parseAgentCheckInput, q as parseArtifactInput, r as parseEvidenceInput, s as parseExceptionInput, t as persistedPartToWorkProduct, u as unresolvedBlockingExceptions, w as workProductToPersistedPart } from '../types-CEchbvgz.js';
|
|
3
|
+
import { c as AppToolContext, d as AppToolDefinition } from '../types-CBRyqijY.js';
|
|
4
|
+
import { JudgeVerdict } from '@tangle-network/agent-eval';
|
|
5
|
+
import { T as TrustItem } from '../trust-gate-Dcm5xSva.js';
|
|
6
|
+
export { R as ReviewQueueInputs, a as ReviewQueueItem, b as ReviewQueuePendingAsk, c as ReviewQueueState, d as ReviewQueueThread, p as parseReviewQueueItem, e as projectReviewQueue } from '../queue-C24V13h9.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The guarded work-product status machine — the `/missions` service PATTERN
|
|
10
|
+
* (load → validate against a transition table → CAS-write guarded on what was
|
|
11
|
+
* read → audit event) over {@link WorkProductStorePort}. Deliberately NOT a
|
|
12
|
+
* reuse of the mission service: missions' cursor/plan/budget machinery does
|
|
13
|
+
* not apply here, and the six-status review machine is a different contract.
|
|
14
|
+
*
|
|
15
|
+
* Concurrency contract: a scope's draft is driven by a single serialized turn
|
|
16
|
+
* owner, so real contention is rare. The service is the typed guard layer,
|
|
17
|
+
* not a serializer — every mutation re-reads the record and CAS-writes
|
|
18
|
+
* guarded on the `{status, version}` it read. A guard miss surfaces as
|
|
19
|
+
* `{ succeeded: false, conflict: true }` (retryable: re-read and re-apply),
|
|
20
|
+
* never a silent clobber.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** Discriminated outcome for guarded operations — `conflict` distinguishes a
|
|
24
|
+
* lost guarded race (retryable) from a logic rejection (illegal edge,
|
|
25
|
+
* missing row — deterministic, never retried). */
|
|
26
|
+
type WorkProductOutcome<T> = {
|
|
27
|
+
succeeded: true;
|
|
28
|
+
value: T;
|
|
29
|
+
} | {
|
|
30
|
+
succeeded: false;
|
|
31
|
+
error: string;
|
|
32
|
+
conflict: boolean;
|
|
33
|
+
};
|
|
34
|
+
/** Whether a legal edge exists from `from` to `to` in the review machine. */
|
|
35
|
+
declare function canTransitionWorkProduct(from: WorkProductStatus, to: WorkProductStatus): boolean;
|
|
36
|
+
/** Statuses a work product can never leave. */
|
|
37
|
+
declare function isWorkProductTerminal(status: WorkProductStatus): boolean;
|
|
38
|
+
/** Define the input required to create a new draft work product row */
|
|
39
|
+
interface CreateWorkProductInput {
|
|
40
|
+
/** Explicit row id — omit to use the service's generator. The MODEL never
|
|
41
|
+
* supplies ids; this is for deterministic server-side creation. */
|
|
42
|
+
id?: string;
|
|
43
|
+
workspaceId: string;
|
|
44
|
+
threadId: string | null;
|
|
45
|
+
scopeKey: string;
|
|
46
|
+
/** Version this draft will become; default 1. The tool layer passes
|
|
47
|
+
* `last reviewed version + 1` when a scope is re-engaged after approval. */
|
|
48
|
+
version?: number;
|
|
49
|
+
provenance: WorkProductProvenance;
|
|
50
|
+
/** Opaque product-column values handed VERBATIM to the store's insert. */
|
|
51
|
+
extras?: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
/** Payload for the draft→ready submit transition */
|
|
54
|
+
interface SubmitWorkProductInput {
|
|
55
|
+
artifact: WorkProductArtifact;
|
|
56
|
+
checks: QualityCheck[];
|
|
57
|
+
provenance: WorkProductProvenance;
|
|
58
|
+
/** Frozen snapshot ref of this version's body for the history entry;
|
|
59
|
+
* defaults to `artifact.path`. */
|
|
60
|
+
artifactPath?: string;
|
|
61
|
+
}
|
|
62
|
+
/** Reviewer verdict payload for the ready→approved / ready→changes_requested transition */
|
|
63
|
+
interface WorkProductVerdictInput {
|
|
64
|
+
verdict: 'approve' | 'request_changes';
|
|
65
|
+
reviewedBy: string;
|
|
66
|
+
note?: string;
|
|
67
|
+
}
|
|
68
|
+
/** Guarded mutation surface over the work-product store */
|
|
69
|
+
interface WorkProductService {
|
|
70
|
+
create(input: CreateWorkProductInput): Promise<WorkProductRecord>;
|
|
71
|
+
get(id: string): Promise<WorkProductRecord | null>;
|
|
72
|
+
/** The scope's open accumulator row (`draft`/`blocked`), or null. */
|
|
73
|
+
openDraft(workspaceId: string, scopeKey: string): Promise<WorkProductRecord | null>;
|
|
74
|
+
/** The scope's `changes_requested` row awaiting its correction turn, or null. */
|
|
75
|
+
awaitingCorrection(workspaceId: string, scopeKey: string): Promise<WorkProductRecord | null>;
|
|
76
|
+
/** The scope's `ready` row awaiting review, or null. */
|
|
77
|
+
awaitingReview(workspaceId: string, scopeKey: string): Promise<WorkProductRecord | null>;
|
|
78
|
+
/** `max(version over the scope's approved/superseded rows) + 1` — the
|
|
79
|
+
* version a fresh draft for the scope should carry. */
|
|
80
|
+
nextVersion(workspaceId: string, scopeKey: string): Promise<number>;
|
|
81
|
+
/** Re-open a `changes_requested` row as the next draft: version bumps +1
|
|
82
|
+
* and the correction turn accumulates into the same scope row. */
|
|
83
|
+
reopen(id: string): Promise<WorkProductOutcome<WorkProductRecord>>;
|
|
84
|
+
/** Merge evidence entries by id (same id replaces — the agent can correct
|
|
85
|
+
* itself mid-turn). Legal only while `draft`/`blocked`. */
|
|
86
|
+
upsertEvidence(id: string, entries: readonly EvidenceEntry[]): Promise<WorkProductOutcome<WorkProductRecord>>;
|
|
87
|
+
/** Merge exception entries by id, then reconcile the blocked flag: any
|
|
88
|
+
* unresolved blocking entry parks `draft`→`blocked`; resolving the last
|
|
89
|
+
* one releases `blocked`→`draft`. */
|
|
90
|
+
upsertExceptions(id: string, entries: readonly ExceptionEntry[]): Promise<WorkProductOutcome<WorkProductRecord>>;
|
|
91
|
+
/** Persist a checks array without transitioning — how a failed platform
|
|
92
|
+
* gate (e.g. evidence_coverage) stays visible on the still-draft row. */
|
|
93
|
+
recordChecks(id: string, checks: readonly QualityCheck[]): Promise<WorkProductOutcome<WorkProductRecord>>;
|
|
94
|
+
/** The terminal agent call: CAS `draft`→`ready` with artifact + checks +
|
|
95
|
+
* provenance, appending the version-history entry. Refuses while an
|
|
96
|
+
* unresolved blocking exception exists. */
|
|
97
|
+
submit(id: string, input: SubmitWorkProductInput): Promise<WorkProductOutcome<WorkProductRecord>>;
|
|
98
|
+
/** Reviewer verdict: CAS `ready`→`approved`/`changes_requested` + history
|
|
99
|
+
* entry. Approval also supersedes the scope's prior approved versions so
|
|
100
|
+
* exactly one approved version is current per scope. */
|
|
101
|
+
applyVerdict(id: string, input: WorkProductVerdictInput): Promise<WorkProductOutcome<WorkProductRecord>>;
|
|
102
|
+
/** Explicit replacement: CAS to `superseded` (legal from ready /
|
|
103
|
+
* changes_requested / approved). */
|
|
104
|
+
supersede(id: string): Promise<WorkProductOutcome<WorkProductRecord>>;
|
|
105
|
+
}
|
|
106
|
+
/** Configuration options for creating a work product service */
|
|
107
|
+
interface WorkProductServiceOptions {
|
|
108
|
+
store: WorkProductStorePort;
|
|
109
|
+
/** Injectable clock (epoch ms). Default `Date.now`. */
|
|
110
|
+
now?: () => number;
|
|
111
|
+
/** Row-id generator. Default `crypto.randomUUID()`. */
|
|
112
|
+
generateId?: () => string;
|
|
113
|
+
}
|
|
114
|
+
/** Create the guarded work-product service over a store port */
|
|
115
|
+
declare function createWorkProductService(options: WorkProductServiceOptions): WorkProductService;
|
|
116
|
+
/** In-memory store surface with audit trail access and unguarded direct writes for tests */
|
|
117
|
+
interface InMemoryWorkProductStore extends WorkProductStorePort {
|
|
118
|
+
/** The full audit trail, append order. */
|
|
119
|
+
events(): WorkProductAuditEvent[];
|
|
120
|
+
/** Unguarded direct write — simulates a concurrent owner in tests. */
|
|
121
|
+
put(record: WorkProductRecord): void;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* In-memory {@link WorkProductStorePort} — the portable backend for tests and
|
|
125
|
+
* reference assemblies. Records are deep-copied on every boundary so callers
|
|
126
|
+
* can never mutate stored state around the guards.
|
|
127
|
+
*/
|
|
128
|
+
declare function createInMemoryWorkProductStore(): InMemoryWorkProductStore;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Provenance stamping — the backtest spine. Every version of every work
|
|
132
|
+
* product carries `profileHash + runId + servingModels`, stamped at the two
|
|
133
|
+
* honest moments:
|
|
134
|
+
*
|
|
135
|
+
* 1. At dispatch, the product route composes the turn's profile and computes
|
|
136
|
+
* agent-eval's `agentProfileHash(profile)` — the SAME hash that keys the
|
|
137
|
+
* app's scorecard cells — and closes it (plus runId/sessionId) into the
|
|
138
|
+
* `provenance` seam `buildWorkProductTools` receives, so the model can
|
|
139
|
+
* neither omit nor forge it.
|
|
140
|
+
* 2. At turn completion, the route's existing lifecycle seam calls
|
|
141
|
+
* {@link finalizeWorkProductProvenance}: serving model and cost come from
|
|
142
|
+
* the usage receipt, because only the completed turn knows what actually
|
|
143
|
+
* served. `servingModels` is honestly EMPTY until then.
|
|
144
|
+
*
|
|
145
|
+
* The trust bridge ({@link workProductTrustInputs}) maps judge-sourced
|
|
146
|
+
* quality verdicts into `/eval-campaign`'s `trustVerdicts()` input with
|
|
147
|
+
* type-only imports — products call `trustVerdicts` from
|
|
148
|
+
* `@tangle-network/agent-app/eval-campaign` (which they already import for
|
|
149
|
+
* their ensemble loop). Deliberately NOT a value re-export here: it would put
|
|
150
|
+
* agent-eval's runtime on `/work-product`'s import path and drag the eval
|
|
151
|
+
* engine into every product worker bundle.
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
/** The dispatch-time provenance closure's output: everything the route knows
|
|
155
|
+
* before the turn completes. */
|
|
156
|
+
type WorkProductProvenanceBase = Omit<WorkProductProvenance, 'servingModels' | 'producedAt'>;
|
|
157
|
+
/** Stamp a full provenance from the dispatch-time base. `servingModels` starts
|
|
158
|
+
* empty (honestly absent — never guessed) until the completion back-fill. */
|
|
159
|
+
declare function stampProvenance(base: WorkProductProvenanceBase, now?: () => number): WorkProductProvenance;
|
|
160
|
+
/** Completion receipt for one run, from the turn's usage/lifecycle seam. */
|
|
161
|
+
interface FinalizeWorkProductProvenanceInput {
|
|
162
|
+
workspaceId: string;
|
|
163
|
+
/** The chat turnId / mission-step run id the records were stamped with. */
|
|
164
|
+
runId: string;
|
|
165
|
+
/** What actually served, from the usage receipt / serving-model header. */
|
|
166
|
+
servingModels: readonly string[];
|
|
167
|
+
costUsd?: number;
|
|
168
|
+
logger?: Pick<Console, 'warn'>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Back-fill `servingModels`/`costUsd` onto every record (and its history
|
|
172
|
+
* entries) stamped with `runId` — wire it into the chat route's existing
|
|
173
|
+
* `lifecycle.onTurnComplete` seam; no new hook. Returns the updated records.
|
|
174
|
+
* A CAS miss on one record is logged and skipped (the next completion or a
|
|
175
|
+
* re-read re-applies); it never throws mid-fleet.
|
|
176
|
+
*/
|
|
177
|
+
declare function finalizeWorkProductProvenance(store: WorkProductStorePort, input: FinalizeWorkProductProvenanceInput): Promise<WorkProductRecord[]>;
|
|
178
|
+
/**
|
|
179
|
+
* Trust-gate bridge: one `TrustItem` per work product whose production
|
|
180
|
+
* quality was scored by the product's eval-campaign ensemble. `verdictsFor`
|
|
181
|
+
* returns the per-judge raw verdicts the product retained for a record (the
|
|
182
|
+
* same verdicts `aggregateJudgeVerdicts` reduces); records without verdicts
|
|
183
|
+
* are omitted. Feed the result to `/eval-campaign`'s `trustVerdicts()` —
|
|
184
|
+
* an untrusted verdict renders as "quality: unverified", never a naked
|
|
185
|
+
* number. Zero statistics code here: pure mapping.
|
|
186
|
+
*/
|
|
187
|
+
declare function workProductTrustInputs<D extends string = string>(records: readonly WorkProductRecord[], verdictsFor: (record: WorkProductRecord) => readonly JudgeVerdict<D>[] | undefined): TrustItem<D>[];
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The agent-facing work-product side channel — three registry `customTools`
|
|
191
|
+
* built on `/tools`' `defineAppTool`, dispatched through `dispatchAppTool`'s
|
|
192
|
+
* single validation/outcome path (a thrown `ToolInputError` → correctable 4xx
|
|
193
|
+
* back to the model; any other throw → internal error; a call never silently
|
|
194
|
+
* succeeds without its effect). Deliberately NOT new `/tools` built-ins:
|
|
195
|
+
* extending `AppToolHandlers` would break every existing consumer, and the
|
|
196
|
+
* registry seam exists precisely for a product tool family like this.
|
|
197
|
+
*
|
|
198
|
+
* Partial-emission contract across a long turn: the draft ROW is the
|
|
199
|
+
* accumulator. Evidence and exceptions stream in as found via small batched
|
|
200
|
+
* calls; the artifact arrives once at the end. The agent addresses everything
|
|
201
|
+
* by `scopeKey` — the server mints row ids, the model never invents them, and
|
|
202
|
+
* identity (userId/workspaceId/threadId) rides the trusted `AppToolContext`
|
|
203
|
+
* from headers, never model args. A turn that dies mid-emission leaves a
|
|
204
|
+
* consistent draft the next turn resumes by the same scopeKey.
|
|
205
|
+
*/
|
|
206
|
+
|
|
207
|
+
/** Max entries per `upsert_evidence`/`flag_exception` call — keeps each call a
|
|
208
|
+
* small batch the model can correct precisely on a named-index failure. */
|
|
209
|
+
declare const MAX_WORK_PRODUCT_BATCH = 50;
|
|
210
|
+
/** The shell's one generic platform check: every material target has ≥1
|
|
211
|
+
* evidence row. Recorded as `QualityCheck{source:'platform'}`. */
|
|
212
|
+
declare const EVIDENCE_COVERAGE_CHECK = "evidence_coverage";
|
|
213
|
+
/** Domain seams for the three work-product tools — every domain word is a
|
|
214
|
+
* parameter; the shell bakes none. */
|
|
215
|
+
interface WorkProductToolConfig {
|
|
216
|
+
store: WorkProductStorePort;
|
|
217
|
+
/** PARAMETER — accepted `artifact.kind` values, validated on submit. */
|
|
218
|
+
artifactKinds: readonly string[];
|
|
219
|
+
/** PARAMETER — accepted exception `kind` values. */
|
|
220
|
+
exceptionKinds: readonly string[];
|
|
221
|
+
/** Fail-loud source check: resolve an evidence `sourceRef` to existence
|
|
222
|
+
* (vault stat / attachment lookup). A dangling ref is a `ToolInputError`
|
|
223
|
+
* naming the entry index — lineage can never point at nothing. */
|
|
224
|
+
resolveSourceRef: (ref: string, ctx: AppToolContext) => Promise<boolean>;
|
|
225
|
+
/** The material targets the platform coverage check requires evidence for.
|
|
226
|
+
* Product-owned vocabulary; omit to skip the coverage gate. */
|
|
227
|
+
materialTargets?: (artifact: WorkProductArtifact) => string[];
|
|
228
|
+
/** Per-turn provenance closure the ROUTE supplies (profileHash + runId are
|
|
229
|
+
* known at dispatch; trusted, never read from model args). */
|
|
230
|
+
provenance: (ctx: AppToolContext) => WorkProductProvenanceBase;
|
|
231
|
+
/** Called on the draft→ready commit so the route persists the transcript
|
|
232
|
+
* anchor part and the queue projection updates. */
|
|
233
|
+
onReady?: (record: WorkProductRecord, ctx: AppToolContext) => void | Promise<void>;
|
|
234
|
+
/** Injectable clock / id generator (tests, deterministic ids). */
|
|
235
|
+
now?: () => number;
|
|
236
|
+
generateId?: () => string;
|
|
237
|
+
}
|
|
238
|
+
/** Build the three work-product tools for `customTools` registration on the
|
|
239
|
+
* MCP server / HTTP handler / runtime executor. */
|
|
240
|
+
declare function buildWorkProductTools(config: WorkProductToolConfig): AppToolDefinition[];
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Framework-neutral work-product review endpoints — the
|
|
244
|
+
* `createInteractionAnswerRoute` factory pattern: web-standard
|
|
245
|
+
* `Request`/`Response`, ONE product-supplied `authorize` seam (session auth,
|
|
246
|
+
* workspace access, reviewer identity, rate limits), everything behind it
|
|
247
|
+
* mechanism. Server-only and subpath-only: never reachable from a client
|
|
248
|
+
* bundle (enforced by the browser-safety test).
|
|
249
|
+
*
|
|
250
|
+
* The verdict endpoint is deliberately NOT a second approval broker: agent
|
|
251
|
+
* asks stay on `/interactions`; this is the REVIEWER's plain authorized
|
|
252
|
+
* verdict on a ready package — one human-in-the-loop channel per direction.
|
|
253
|
+
* A `request_changes` note re-enters chat as the correction turn via the
|
|
254
|
+
* `onVerdict` seam; chat remains the driver surface.
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
/** Reviewer verdict wire body for the POST endpoint. */
|
|
258
|
+
type WorkProductVerdictBody = {
|
|
259
|
+
ok: true;
|
|
260
|
+
id: string;
|
|
261
|
+
verdict: 'approve';
|
|
262
|
+
note?: string;
|
|
263
|
+
} | {
|
|
264
|
+
ok: true;
|
|
265
|
+
id: string;
|
|
266
|
+
verdict: 'request_changes';
|
|
267
|
+
note: string;
|
|
268
|
+
} | {
|
|
269
|
+
ok: false;
|
|
270
|
+
error: string;
|
|
271
|
+
};
|
|
272
|
+
/** Validate the verdict POST body: `{ id, verdict, note? }`; a
|
|
273
|
+
* `request_changes` verdict REQUIRES a non-empty note — the note IS the
|
|
274
|
+
* correction turn the agent works from. */
|
|
275
|
+
declare function validateWorkProductVerdictBody(body: Record<string, unknown>): WorkProductVerdictBody;
|
|
276
|
+
/** The product seam's verdict for one request: authenticated reviewer +
|
|
277
|
+
* workspace, or a product-authored short-circuit Response (401/403/429…). */
|
|
278
|
+
type WorkProductRouteAuthorization = {
|
|
279
|
+
ok: true;
|
|
280
|
+
workspaceId: string;
|
|
281
|
+
reviewedBy: string;
|
|
282
|
+
} | {
|
|
283
|
+
ok: false;
|
|
284
|
+
response: Response;
|
|
285
|
+
};
|
|
286
|
+
/** Auth seam arguments carrying the request, the endpoint intent, and the parsed verdict body */
|
|
287
|
+
interface WorkProductAuthorizeArgs {
|
|
288
|
+
request: Request;
|
|
289
|
+
intent: 'list' | 'detail' | 'verdict';
|
|
290
|
+
/** The parsed, validated POST body (verdict intent only). */
|
|
291
|
+
body?: Record<string, unknown>;
|
|
292
|
+
}
|
|
293
|
+
/** Configuration options assembling the review endpoints from the store and product seams */
|
|
294
|
+
interface WorkProductRoutesOptions {
|
|
295
|
+
store: WorkProductStorePort;
|
|
296
|
+
/** Authenticate + authorize the caller; the ONLY product access step. */
|
|
297
|
+
authorize: (args: WorkProductAuthorizeArgs) => Promise<WorkProductRouteAuthorization>;
|
|
298
|
+
/** Post-verdict product seam: post the `request_changes` note into the
|
|
299
|
+
* driving chat thread as the correction turn, notify, etc. Runs after the
|
|
300
|
+
* transition commits; a throw is logged, never unwinds the verdict. */
|
|
301
|
+
onVerdict?: (args: {
|
|
302
|
+
record: WorkProductRecord;
|
|
303
|
+
verdict: 'approve' | 'request_changes';
|
|
304
|
+
note?: string;
|
|
305
|
+
reviewedBy: string;
|
|
306
|
+
}) => void | Promise<void>;
|
|
307
|
+
/** Persist/update the transcript anchor part reflecting the new status, so
|
|
308
|
+
* the chat card flips with the verdict. */
|
|
309
|
+
persistAnchorPart?: (part: WorkProductPersistedPart, record: WorkProductRecord) => void | Promise<void>;
|
|
310
|
+
/** Future integration point fired on approval (push to a DMS/CRM/export
|
|
311
|
+
* pipeline). A stub seam by design — export itself stays the product's
|
|
312
|
+
* signed object-store download. */
|
|
313
|
+
onExport?: (record: WorkProductRecord) => void | Promise<void>;
|
|
314
|
+
logger?: Pick<Console, 'warn' | 'error'>;
|
|
315
|
+
now?: () => number;
|
|
316
|
+
}
|
|
317
|
+
/** Assembled review endpoints returning web-standard Responses */
|
|
318
|
+
interface WorkProductRoutes {
|
|
319
|
+
/** GET — the workspace's records for the queue projection. Optional
|
|
320
|
+
* `?status=a,b` filter. */
|
|
321
|
+
list: (request: Request) => Promise<Response>;
|
|
322
|
+
/** GET — one record by id (404 when absent or outside the workspace). */
|
|
323
|
+
detail: (request: Request, id: string) => Promise<Response>;
|
|
324
|
+
/** POST `{ id, verdict, note? }` — the reviewer verdict: CAS transition +
|
|
325
|
+
* history entry + product seams. 409 when the record is no longer ready. */
|
|
326
|
+
verdict: (request: Request) => Promise<Response>;
|
|
327
|
+
}
|
|
328
|
+
/** Create the work-product review endpoints over the store port and product seams */
|
|
329
|
+
declare function createWorkProductRoutes(options: WorkProductRoutesOptions): WorkProductRoutes;
|
|
330
|
+
|
|
331
|
+
export { type CreateWorkProductInput, EVIDENCE_COVERAGE_CHECK, EvidenceEntry, ExceptionEntry, type FinalizeWorkProductProvenanceInput, type InMemoryWorkProductStore, MAX_WORK_PRODUCT_BATCH, QualityCheck, type SubmitWorkProductInput, TrustItem, WorkProductArtifact, WorkProductAuditEvent, type WorkProductAuthorizeArgs, type WorkProductOutcome, WorkProductPersistedPart, WorkProductProvenance, type WorkProductProvenanceBase, WorkProductRecord, type WorkProductRouteAuthorization, type WorkProductRoutes, type WorkProductRoutesOptions, type WorkProductService, type WorkProductServiceOptions, WorkProductStatus, WorkProductStorePort, type WorkProductToolConfig, type WorkProductVerdictBody, type WorkProductVerdictInput, buildWorkProductTools, canTransitionWorkProduct, createInMemoryWorkProductStore, createWorkProductRoutes, createWorkProductService, finalizeWorkProductProvenance, isWorkProductTerminal, stampProvenance, validateWorkProductVerdictBody, workProductTrustInputs };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EVIDENCE_COVERAGE_CHECK,
|
|
3
|
+
MAX_WORK_PRODUCT_BATCH,
|
|
4
|
+
buildWorkProductTools,
|
|
5
|
+
canTransitionWorkProduct,
|
|
6
|
+
createInMemoryWorkProductStore,
|
|
7
|
+
createWorkProductRoutes,
|
|
8
|
+
createWorkProductService,
|
|
9
|
+
finalizeWorkProductProvenance,
|
|
10
|
+
isWorkProductTerminal,
|
|
11
|
+
stampProvenance,
|
|
12
|
+
validateWorkProductVerdictBody,
|
|
13
|
+
workProductTrustInputs
|
|
14
|
+
} from "../chunk-HRH7ASAG.js";
|
|
15
|
+
import {
|
|
16
|
+
parseReviewQueueItem,
|
|
17
|
+
projectReviewQueue
|
|
18
|
+
} from "../chunk-UP33Z633.js";
|
|
19
|
+
import {
|
|
20
|
+
isWorkProductStatus,
|
|
21
|
+
parseAgentCheckInput,
|
|
22
|
+
parseArtifactInput,
|
|
23
|
+
parseEvidenceInput,
|
|
24
|
+
parseExceptionInput,
|
|
25
|
+
persistedPartToWorkProduct,
|
|
26
|
+
unresolvedBlockingExceptions,
|
|
27
|
+
workProductToPersistedPart
|
|
28
|
+
} from "../chunk-F2CBC4DY.js";
|
|
29
|
+
import "../chunk-YEFFHORB.js";
|
|
30
|
+
export {
|
|
31
|
+
EVIDENCE_COVERAGE_CHECK,
|
|
32
|
+
MAX_WORK_PRODUCT_BATCH,
|
|
33
|
+
buildWorkProductTools,
|
|
34
|
+
canTransitionWorkProduct,
|
|
35
|
+
createInMemoryWorkProductStore,
|
|
36
|
+
createWorkProductRoutes,
|
|
37
|
+
createWorkProductService,
|
|
38
|
+
finalizeWorkProductProvenance,
|
|
39
|
+
isWorkProductStatus,
|
|
40
|
+
isWorkProductTerminal,
|
|
41
|
+
parseAgentCheckInput,
|
|
42
|
+
parseArtifactInput,
|
|
43
|
+
parseEvidenceInput,
|
|
44
|
+
parseExceptionInput,
|
|
45
|
+
parseReviewQueueItem,
|
|
46
|
+
persistedPartToWorkProduct,
|
|
47
|
+
projectReviewQueue,
|
|
48
|
+
stampProvenance,
|
|
49
|
+
unresolvedBlockingExceptions,
|
|
50
|
+
validateWorkProductVerdictBody,
|
|
51
|
+
workProductToPersistedPart,
|
|
52
|
+
workProductTrustInputs
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { i as WorkProductRecord, E as EvidenceEntry, P as ProfileBacktestSummary } from '../types-CEchbvgz.js';
|
|
3
|
+
|
|
4
|
+
type WorkProductPaneTab = 'artifact' | 'diff' | 'lineage' | 'exceptions' | 'checks' | 'history';
|
|
5
|
+
/** Properties for the tabbed work-product review pane */
|
|
6
|
+
interface WorkProductPaneProps {
|
|
7
|
+
workProduct: WorkProductRecord;
|
|
8
|
+
/** Which tab opens first — a product choice, not a heuristic: tax passes
|
|
9
|
+
* 'lineage', legal 'diff', gtm 'artifact'. Default 'artifact'. */
|
|
10
|
+
defaultTab?: WorkProductPaneTab;
|
|
11
|
+
/** Resolved artifact body when it lives at `artifact.path` rather than
|
|
12
|
+
* inline. Falls back to `artifact.content`. */
|
|
13
|
+
currentContent?: string;
|
|
14
|
+
/** Resolved baseline body for diff-first products. Falls back to
|
|
15
|
+
* `artifact.baseline.content`. */
|
|
16
|
+
baselineContent?: string;
|
|
17
|
+
/** Load a frozen version body by its `history[].artifactPath` ref for the
|
|
18
|
+
* vN-1 → vN compare. Omit to hide history compares. */
|
|
19
|
+
loadVersionBody?: (ref: string) => Promise<string | null>;
|
|
20
|
+
/** Resolve one evidence entry's source to an openable URL (lineage
|
|
21
|
+
* click-through). */
|
|
22
|
+
resolveSourceUrl?: (entry: EvidenceEntry) => string;
|
|
23
|
+
/** Backtest summary for the record's profile hash, rendered on the
|
|
24
|
+
* provenance stamp. */
|
|
25
|
+
backtest?: ProfileBacktestSummary;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The tabbed review pane over one work product: Artifact | Diff | Lineage |
|
|
30
|
+
* Exceptions | Checks | History. Diff renders baseline→current for redline
|
|
31
|
+
* artifacts and vN-1→vN for history compares; lineage is the
|
|
32
|
+
* every-value-traceable table; provenance is stamped across the header.
|
|
33
|
+
*/
|
|
34
|
+
declare function WorkProductPane({ workProduct, defaultTab, currentContent, baselineContent, loadVersionBody, resolveSourceUrl, backtest, className, }: WorkProductPaneProps): react.JSX.Element;
|
|
35
|
+
|
|
36
|
+
export { WorkProductPane, type WorkProductPaneProps, type WorkProductPaneTab };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EvidenceLineageTable,
|
|
3
|
+
ExceptionList,
|
|
4
|
+
ProvenanceStamp,
|
|
5
|
+
QualityCheckList,
|
|
6
|
+
workProductStatusLabel
|
|
7
|
+
} from "../chunk-UDSY2F6N.js";
|
|
8
|
+
import "../chunk-UP33Z633.js";
|
|
9
|
+
import {
|
|
10
|
+
unresolvedBlockingExceptions
|
|
11
|
+
} from "../chunk-F2CBC4DY.js";
|
|
12
|
+
|
|
13
|
+
// src/work-product-react/index.tsx
|
|
14
|
+
import { useCallback, useMemo, useState } from "react";
|
|
15
|
+
import {
|
|
16
|
+
CodeSurface,
|
|
17
|
+
DiffView,
|
|
18
|
+
FileBreadcrumb,
|
|
19
|
+
PillTabs,
|
|
20
|
+
computeDiffStats
|
|
21
|
+
} from "@tangle-network/sandbox-ui/workbench";
|
|
22
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
23
|
+
function filenameOf(path, fallback) {
|
|
24
|
+
if (!path) return fallback;
|
|
25
|
+
const segments = path.split("/");
|
|
26
|
+
return segments[segments.length - 1] || fallback;
|
|
27
|
+
}
|
|
28
|
+
function WorkProductPane({
|
|
29
|
+
workProduct,
|
|
30
|
+
defaultTab = "artifact",
|
|
31
|
+
currentContent,
|
|
32
|
+
baselineContent,
|
|
33
|
+
loadVersionBody,
|
|
34
|
+
resolveSourceUrl,
|
|
35
|
+
backtest,
|
|
36
|
+
className
|
|
37
|
+
}) {
|
|
38
|
+
const artifact = workProduct.artifact;
|
|
39
|
+
const body = currentContent ?? artifact?.content ?? "";
|
|
40
|
+
const baseline = baselineContent ?? artifact?.baseline?.content;
|
|
41
|
+
const hasDiff = baseline !== void 0;
|
|
42
|
+
const tabs = useMemo(() => {
|
|
43
|
+
const items = [{ value: "artifact", label: "Artifact" }];
|
|
44
|
+
if (hasDiff) items.push({ value: "diff", label: "Diff" });
|
|
45
|
+
items.push(
|
|
46
|
+
{ value: "lineage", label: `Lineage (${workProduct.evidence.length})` },
|
|
47
|
+
{ value: "exceptions", label: `Exceptions (${workProduct.exceptions.length})` },
|
|
48
|
+
{ value: "checks", label: `Checks (${workProduct.checks.length})` },
|
|
49
|
+
{ value: "history", label: `History (${workProduct.history.length})` }
|
|
50
|
+
);
|
|
51
|
+
return items;
|
|
52
|
+
}, [hasDiff, workProduct.evidence.length, workProduct.exceptions.length, workProduct.checks.length, workProduct.history.length]);
|
|
53
|
+
const initialTab = tabs.some((tab2) => tab2.value === defaultTab) ? defaultTab : "artifact";
|
|
54
|
+
const [tab, setTab] = useState(initialTab);
|
|
55
|
+
const [compare, setCompare] = useState(null);
|
|
56
|
+
const [compareError, setCompareError] = useState(null);
|
|
57
|
+
const diffFilename = filenameOf(artifact?.path, artifact?.title ?? workProduct.scopeKey);
|
|
58
|
+
const diffStats = useMemo(
|
|
59
|
+
() => hasDiff ? computeDiffStats(baseline ?? "", body) : null,
|
|
60
|
+
[hasDiff, baseline, body]
|
|
61
|
+
);
|
|
62
|
+
const loadCompare = useCallback(
|
|
63
|
+
async (from, to) => {
|
|
64
|
+
if (!loadVersionBody || !from.artifactPath || !to.artifactPath) return;
|
|
65
|
+
setCompareError(null);
|
|
66
|
+
try {
|
|
67
|
+
const [fromBody, toBody] = await Promise.all([
|
|
68
|
+
loadVersionBody(from.artifactPath),
|
|
69
|
+
loadVersionBody(to.artifactPath)
|
|
70
|
+
]);
|
|
71
|
+
if (fromBody === null || toBody === null) {
|
|
72
|
+
setCompareError("A version snapshot could not be loaded.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
setCompare({ fromVersion: from.version, toVersion: to.version, baseline: fromBody, current: toBody });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
setCompareError(error instanceof Error ? error.message : String(error));
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
[loadVersionBody]
|
|
81
|
+
);
|
|
82
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex min-h-0 flex-col gap-3 ${className ?? ""}`, children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
84
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
85
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-sm font-semibold text-foreground", children: [
|
|
86
|
+
artifact?.title ?? workProduct.scopeKey,
|
|
87
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-[11px] font-normal text-muted-foreground", children: [
|
|
88
|
+
"v",
|
|
89
|
+
workProduct.version
|
|
90
|
+
] })
|
|
91
|
+
] }),
|
|
92
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
93
|
+
artifact?.kind && /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
94
|
+
artifact.kind,
|
|
95
|
+
" \xB7 "
|
|
96
|
+
] }),
|
|
97
|
+
workProductStatusLabel(workProduct.status),
|
|
98
|
+
unresolvedBlockingExceptions(workProduct.exceptions).length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-destructive", children: [
|
|
99
|
+
" \xB7 ",
|
|
100
|
+
unresolvedBlockingExceptions(workProduct.exceptions).length,
|
|
101
|
+
" blocking"
|
|
102
|
+
] })
|
|
103
|
+
] })
|
|
104
|
+
] }),
|
|
105
|
+
/* @__PURE__ */ jsx(ProvenanceStamp, { provenance: workProduct.provenance, backtest })
|
|
106
|
+
] }),
|
|
107
|
+
/* @__PURE__ */ jsx(PillTabs, { items: tabs, value: tab, onChange: setTab, "aria-label": "Work product views" }),
|
|
108
|
+
tab === "artifact" && /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-auto rounded-lg border border-border", children: [
|
|
109
|
+
artifact?.path && /* @__PURE__ */ jsx(FileBreadcrumb, { path: artifact.path, className: "border-b border-border px-3 py-2" }),
|
|
110
|
+
body ? /* @__PURE__ */ jsx(CodeSurface, { code: body, filename: diffFilename }) : artifact?.fields ? /* @__PURE__ */ jsx(CodeSurface, { code: JSON.stringify(artifact.fields, null, 2), filename: `${diffFilename}.json`, language: "json" }) : /* @__PURE__ */ jsx("p", { className: "px-4 py-6 text-center text-xs text-muted-foreground", children: "No artifact body yet." })
|
|
111
|
+
] }),
|
|
112
|
+
tab === "diff" && hasDiff && /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-auto rounded-lg border border-border", children: [
|
|
113
|
+
diffStats && /* @__PURE__ */ jsxs("p", { className: "border-b border-border px-3 py-2 text-[11px] text-muted-foreground", children: [
|
|
114
|
+
/* @__PURE__ */ jsxs("span", { className: "text-success", children: [
|
|
115
|
+
"+",
|
|
116
|
+
diffStats.added
|
|
117
|
+
] }),
|
|
118
|
+
" ",
|
|
119
|
+
/* @__PURE__ */ jsxs("span", { className: "text-destructive", children: [
|
|
120
|
+
"\u2212",
|
|
121
|
+
diffStats.removed
|
|
122
|
+
] })
|
|
123
|
+
] }),
|
|
124
|
+
/* @__PURE__ */ jsx(DiffView, { filename: diffFilename, baseline: baseline ?? "", current: body })
|
|
125
|
+
] }),
|
|
126
|
+
tab === "lineage" && /* @__PURE__ */ jsx(EvidenceLineageTable, { evidence: workProduct.evidence, resolveSourceUrl }),
|
|
127
|
+
tab === "exceptions" && /* @__PURE__ */ jsx(ExceptionList, { exceptions: workProduct.exceptions }),
|
|
128
|
+
tab === "checks" && /* @__PURE__ */ jsx(QualityCheckList, { checks: workProduct.checks }),
|
|
129
|
+
tab === "history" && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
130
|
+
workProduct.history.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No versions yet." }),
|
|
131
|
+
workProduct.history.map((entry, index) => {
|
|
132
|
+
const prior = workProduct.history.slice(0, index).reverse().find((candidate) => candidate.version < entry.version && candidate.artifactPath);
|
|
133
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-card px-3 py-2", children: [
|
|
134
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
135
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-foreground", children: [
|
|
136
|
+
"v",
|
|
137
|
+
entry.version
|
|
138
|
+
] }),
|
|
139
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: workProductStatusLabel(entry.status) }),
|
|
140
|
+
entry.reviewedBy && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-muted-foreground", children: [
|
|
141
|
+
"by ",
|
|
142
|
+
entry.reviewedBy
|
|
143
|
+
] }),
|
|
144
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1" }),
|
|
145
|
+
loadVersionBody && prior?.artifactPath && entry.artifactPath && /* @__PURE__ */ jsxs(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
type: "button",
|
|
149
|
+
onClick: () => void loadCompare(prior, entry),
|
|
150
|
+
className: "rounded-md border border-border px-2 py-0.5 text-[11px] font-medium text-foreground transition hover:bg-accent/30",
|
|
151
|
+
children: [
|
|
152
|
+
"Compare v",
|
|
153
|
+
prior.version,
|
|
154
|
+
" \u2192 v",
|
|
155
|
+
entry.version
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
] }),
|
|
160
|
+
entry.reviewNote && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[13px] leading-snug text-foreground", children: entry.reviewNote }),
|
|
161
|
+
/* @__PURE__ */ jsx(ProvenanceStamp, { provenance: entry.provenance, className: "mt-1" })
|
|
162
|
+
] }, `${entry.version}:${entry.status}:${entry.at}`);
|
|
163
|
+
}),
|
|
164
|
+
compareError && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive", children: compareError }),
|
|
165
|
+
compare && /* @__PURE__ */ jsxs("div", { className: "overflow-auto rounded-lg border border-border", children: [
|
|
166
|
+
/* @__PURE__ */ jsxs("p", { className: "border-b border-border px-3 py-2 text-[11px] text-muted-foreground", children: [
|
|
167
|
+
"v",
|
|
168
|
+
compare.fromVersion,
|
|
169
|
+
" \u2192 v",
|
|
170
|
+
compare.toVersion
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ jsx(DiffView, { filename: diffFilename, baseline: compare.baseline, current: compare.current })
|
|
173
|
+
] })
|
|
174
|
+
] })
|
|
175
|
+
] });
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
WorkProductPane
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/work-product-react/index.tsx"],"sourcesContent":["/**\n * `./work-product-react` — the sandbox-ui-composed review pane, mirroring\n * `/composer`'s optional-peer rule: this is the ONLY work-product surface\n * that imports `@tangle-network/sandbox-ui` (its `workbench` primitives:\n * `PillTabs`, `CodeSurface`, `DiffView`, `FileBreadcrumb`,\n * `computeDiffStats`). `/web-react`'s queue/card/lineage components stay\n * sandbox-ui-free; importing THIS subpath requires the otherwise optional\n * sandbox-ui peer.\n *\n * The pane never fetches: bodies arrive as resolved strings\n * (`currentContent`/`baselineContent`) and history compares load through the\n * injected `loadVersionBody(ref)` seam. The default tab is a PROP — tax\n * passes `'lineage'` (lineage before diff for tax), legal passes `'diff'`\n * (the redline IS the artifact), gtm passes `'artifact'`.\n */\n\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n CodeSurface,\n DiffView,\n FileBreadcrumb,\n PillTabs,\n computeDiffStats,\n type PillTabItem,\n} from '@tangle-network/sandbox-ui/workbench'\n\nimport {\n EvidenceLineageTable,\n ExceptionList,\n ProvenanceStamp,\n QualityCheckList,\n workProductStatusLabel,\n} from '../web-react/work-product'\nimport type {\n EvidenceEntry,\n ProfileBacktestSummary,\n WorkProductRecord,\n WorkProductVersionEntry,\n} from '../work-product/types'\nimport { unresolvedBlockingExceptions } from '../work-product/types'\n\nexport type WorkProductPaneTab = 'artifact' | 'diff' | 'lineage' | 'exceptions' | 'checks' | 'history'\n\n/** Properties for the tabbed work-product review pane */\nexport interface WorkProductPaneProps {\n workProduct: WorkProductRecord\n /** Which tab opens first — a product choice, not a heuristic: tax passes\n * 'lineage', legal 'diff', gtm 'artifact'. Default 'artifact'. */\n defaultTab?: WorkProductPaneTab\n /** Resolved artifact body when it lives at `artifact.path` rather than\n * inline. Falls back to `artifact.content`. */\n currentContent?: string\n /** Resolved baseline body for diff-first products. Falls back to\n * `artifact.baseline.content`. */\n baselineContent?: string\n /** Load a frozen version body by its `history[].artifactPath` ref for the\n * vN-1 → vN compare. Omit to hide history compares. */\n loadVersionBody?: (ref: string) => Promise<string | null>\n /** Resolve one evidence entry's source to an openable URL (lineage\n * click-through). */\n resolveSourceUrl?: (entry: EvidenceEntry) => string\n /** Backtest summary for the record's profile hash, rendered on the\n * provenance stamp. */\n backtest?: ProfileBacktestSummary\n className?: string\n}\n\ninterface VersionCompare {\n fromVersion: number\n toVersion: number\n baseline: string\n current: string\n}\n\n/** Trailing path segment for a diff header. */\nfunction filenameOf(path: string | undefined, fallback: string): string {\n if (!path) return fallback\n const segments = path.split('/')\n return segments[segments.length - 1] || fallback\n}\n\n/**\n * The tabbed review pane over one work product: Artifact | Diff | Lineage |\n * Exceptions | Checks | History. Diff renders baseline→current for redline\n * artifacts and vN-1→vN for history compares; lineage is the\n * every-value-traceable table; provenance is stamped across the header.\n */\nexport function WorkProductPane({\n workProduct,\n defaultTab = 'artifact',\n currentContent,\n baselineContent,\n loadVersionBody,\n resolveSourceUrl,\n backtest,\n className,\n}: WorkProductPaneProps) {\n const artifact = workProduct.artifact\n const body = currentContent ?? artifact?.content ?? ''\n const baseline = baselineContent ?? artifact?.baseline?.content\n const hasDiff = baseline !== undefined\n\n const tabs = useMemo(() => {\n const items: PillTabItem<WorkProductPaneTab>[] = [{ value: 'artifact', label: 'Artifact' }]\n if (hasDiff) items.push({ value: 'diff', label: 'Diff' })\n items.push(\n { value: 'lineage', label: `Lineage (${workProduct.evidence.length})` },\n { value: 'exceptions', label: `Exceptions (${workProduct.exceptions.length})` },\n { value: 'checks', label: `Checks (${workProduct.checks.length})` },\n { value: 'history', label: `History (${workProduct.history.length})` },\n )\n return items\n }, [hasDiff, workProduct.evidence.length, workProduct.exceptions.length, workProduct.checks.length, workProduct.history.length])\n\n const initialTab = tabs.some((tab) => tab.value === defaultTab) ? defaultTab : 'artifact'\n const [tab, setTab] = useState<WorkProductPaneTab>(initialTab)\n const [compare, setCompare] = useState<VersionCompare | null>(null)\n const [compareError, setCompareError] = useState<string | null>(null)\n\n const diffFilename = filenameOf(artifact?.path, artifact?.title ?? workProduct.scopeKey)\n const diffStats = useMemo(\n () => (hasDiff ? computeDiffStats(baseline ?? '', body) : null),\n [hasDiff, baseline, body],\n )\n\n const loadCompare = useCallback(\n async (from: WorkProductVersionEntry, to: WorkProductVersionEntry) => {\n if (!loadVersionBody || !from.artifactPath || !to.artifactPath) return\n setCompareError(null)\n try {\n const [fromBody, toBody] = await Promise.all([\n loadVersionBody(from.artifactPath),\n loadVersionBody(to.artifactPath),\n ])\n if (fromBody === null || toBody === null) {\n setCompareError('A version snapshot could not be loaded.')\n return\n }\n setCompare({ fromVersion: from.version, toVersion: to.version, baseline: fromBody, current: toBody })\n } catch (error) {\n setCompareError(error instanceof Error ? error.message : String(error))\n }\n },\n [loadVersionBody],\n )\n\n return (\n <div className={`flex min-h-0 flex-col gap-3 ${className ?? ''}`}>\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-foreground\">\n {artifact?.title ?? workProduct.scopeKey}\n <span className=\"ml-1.5 text-[11px] font-normal text-muted-foreground\">v{workProduct.version}</span>\n </p>\n <p className=\"text-[11px] text-muted-foreground\">\n {artifact?.kind && <span className=\"font-mono\">{artifact.kind} · </span>}\n {workProductStatusLabel(workProduct.status)}\n {unresolvedBlockingExceptions(workProduct.exceptions).length > 0 && (\n <span className=\"text-destructive\"> · {unresolvedBlockingExceptions(workProduct.exceptions).length} blocking</span>\n )}\n </p>\n </div>\n <ProvenanceStamp provenance={workProduct.provenance} backtest={backtest} />\n </div>\n\n <PillTabs items={tabs} value={tab} onChange={setTab} aria-label=\"Work product views\" />\n\n {tab === 'artifact' && (\n <div className=\"min-h-0 flex-1 overflow-auto rounded-lg border border-border\">\n {artifact?.path && <FileBreadcrumb path={artifact.path} className=\"border-b border-border px-3 py-2\" />}\n {body ? (\n <CodeSurface code={body} filename={diffFilename} />\n ) : artifact?.fields ? (\n <CodeSurface code={JSON.stringify(artifact.fields, null, 2)} filename={`${diffFilename}.json`} language=\"json\" />\n ) : (\n <p className=\"px-4 py-6 text-center text-xs text-muted-foreground\">No artifact body yet.</p>\n )}\n </div>\n )}\n\n {tab === 'diff' && hasDiff && (\n <div className=\"min-h-0 flex-1 overflow-auto rounded-lg border border-border\">\n {diffStats && (\n <p className=\"border-b border-border px-3 py-2 text-[11px] text-muted-foreground\">\n <span className=\"text-success\">+{diffStats.added}</span>{' '}\n <span className=\"text-destructive\">−{diffStats.removed}</span>\n </p>\n )}\n <DiffView filename={diffFilename} baseline={baseline ?? ''} current={body} />\n </div>\n )}\n\n {tab === 'lineage' && (\n <EvidenceLineageTable evidence={workProduct.evidence} resolveSourceUrl={resolveSourceUrl} />\n )}\n\n {tab === 'exceptions' && <ExceptionList exceptions={workProduct.exceptions} />}\n\n {tab === 'checks' && <QualityCheckList checks={workProduct.checks} />}\n\n {tab === 'history' && (\n <div className=\"space-y-2\">\n {workProduct.history.length === 0 && <p className=\"text-xs text-muted-foreground\">No versions yet.</p>}\n {workProduct.history.map((entry, index) => {\n const prior = workProduct.history\n .slice(0, index)\n .reverse()\n .find((candidate) => candidate.version < entry.version && candidate.artifactPath)\n return (\n <div key={`${entry.version}:${entry.status}:${entry.at}`} className=\"rounded-lg border border-border bg-card px-3 py-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium text-foreground\">v{entry.version}</span>\n <span className=\"text-[11px] text-muted-foreground\">{workProductStatusLabel(entry.status)}</span>\n {entry.reviewedBy && <span className=\"text-[11px] text-muted-foreground\">by {entry.reviewedBy}</span>}\n <span className=\"flex-1\" />\n {loadVersionBody && prior?.artifactPath && entry.artifactPath && (\n <button\n type=\"button\"\n onClick={() => void loadCompare(prior, entry)}\n className=\"rounded-md border border-border px-2 py-0.5 text-[11px] font-medium text-foreground transition hover:bg-accent/30\"\n >\n Compare v{prior.version} → v{entry.version}\n </button>\n )}\n </div>\n {entry.reviewNote && <p className=\"mt-1 text-[13px] leading-snug text-foreground\">{entry.reviewNote}</p>}\n <ProvenanceStamp provenance={entry.provenance} className=\"mt-1\" />\n </div>\n )\n })}\n {compareError && <p className=\"rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive\">{compareError}</p>}\n {compare && (\n <div className=\"overflow-auto rounded-lg border border-border\">\n <p className=\"border-b border-border px-3 py-2 text-[11px] text-muted-foreground\">\n v{compare.fromVersion} → v{compare.toVersion}\n </p>\n <DiffView filename={diffFilename} baseline={compare.baseline} current={compare.current} />\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["tab"]}
|