@tangle-network/agent-app 0.44.11 → 0.44.13
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 -4
- package/dist/assistant/index.js +6 -6
- package/dist/{attachment-validation-yJ6Id4pR.d.ts → attachment-validation-Zw8eslhN.d.ts} +1 -1
- package/dist/chat-routes/index.d.ts +4 -4
- package/dist/chat-routes/index.js +3 -3
- package/dist/chat-store/index.d.ts +3 -3
- package/dist/chat-store/index.js +2 -2
- package/dist/{chunk-J6TNPRQN.js → chunk-7SMPAAIT.js} +4 -4
- package/dist/{chunk-FEUW4G2L.js → chunk-DV2FA2PW.js} +2 -2
- package/dist/{chunk-ZRSUCTST.js → chunk-GEYACSFW.js} +2 -2
- package/dist/{chunk-5GA3MKOC.js → chunk-VM6VLJH6.js} +2 -2
- package/dist/{chunk-72TPRENZ.js → chunk-YTMKRL3L.js} +3 -3
- package/dist/{chunk-V3HO43PG.js → chunk-ZVEEWGDK.js} +14 -1
- package/dist/chunk-ZVEEWGDK.js.map +1 -0
- package/dist/{parts-DIKcC1p6.d.ts → parts-S1OINlRP.d.ts} +1 -1
- package/dist/{queue-DBkulxW1.d.ts → queue-vRI0Qx3X.d.ts} +1 -1
- package/dist/turn-health/index.d.ts +52 -4
- package/dist/turn-health/index.js +50 -6
- package/dist/turn-health/index.js.map +1 -1
- package/dist/{types-DeEOhQbv.d.ts → types-DB82fktc.d.ts} +12 -2
- package/dist/web-react/index.d.ts +6 -6
- package/dist/web-react/index.js +6 -6
- package/dist/work-product/index.d.ts +51 -4
- package/dist/work-product/index.js +93 -6
- package/dist/work-product/index.js.map +1 -1
- package/dist/work-product-react/index.d.ts +1 -1
- package/dist/work-product-react/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-V3HO43PG.js.map +0 -1
- /package/dist/{chunk-J6TNPRQN.js.map → chunk-7SMPAAIT.js.map} +0 -0
- /package/dist/{chunk-FEUW4G2L.js.map → chunk-DV2FA2PW.js.map} +0 -0
- /package/dist/{chunk-ZRSUCTST.js.map → chunk-GEYACSFW.js.map} +0 -0
- /package/dist/{chunk-5GA3MKOC.js.map → chunk-VM6VLJH6.js.map} +0 -0
- /package/dist/{chunk-72TPRENZ.js.map → chunk-YTMKRL3L.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-
|
|
8
|
-
import '../types-
|
|
7
|
+
import '../parts-S1OINlRP.js';
|
|
8
|
+
import '../types-DB82fktc.js';
|
|
9
9
|
import '../agent-activity-C8ZG0F0M.js';
|
|
10
10
|
import '../flow-types-CJxEmaRy.js';
|
|
11
|
-
import '../queue-
|
|
11
|
+
import '../queue-vRI0Qx3X.js';
|
|
12
12
|
import '../sandbox-terminal-ChNEdHF8.js';
|
|
13
13
|
import '../catalog/index.js';
|
|
14
14
|
import '../harness/index.js';
|
|
15
|
-
import '../attachment-validation-
|
|
15
|
+
import '../attachment-validation-Zw8eslhN.js';
|
|
16
16
|
import '../stream-normalizer-DnuqkZvw.js';
|
|
17
17
|
|
|
18
18
|
/**
|
package/dist/assistant/index.js
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
ChatMessages,
|
|
4
4
|
ModelPicker,
|
|
5
5
|
ProviderLogo
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-7SMPAAIT.js";
|
|
7
7
|
import "../chunk-FBVLEGEG.js";
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-YTMKRL3L.js";
|
|
9
|
+
import "../chunk-GEYACSFW.js";
|
|
10
10
|
import "../chunk-HCOROIRT.js";
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-VM6VLJH6.js";
|
|
12
|
+
import "../chunk-DV2FA2PW.js";
|
|
13
|
+
import "../chunk-ZVEEWGDK.js";
|
|
14
14
|
import "../chunk-5EPIPT4V.js";
|
|
15
15
|
import "../chunk-3ZK5IJSW.js";
|
|
16
16
|
import "../chunk-YJMCRXQQ.js";
|
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-S1OINlRP.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-S1OINlRP.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-Zw8eslhN.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-
|
|
14
|
+
import '../types-DB82fktc.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-
|
|
14
|
+
} from "../chunk-VM6VLJH6.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-
|
|
59
|
-
import "../chunk-
|
|
58
|
+
} from "../chunk-DV2FA2PW.js";
|
|
59
|
+
import "../chunk-ZVEEWGDK.js";
|
|
60
60
|
import {
|
|
61
61
|
asRecord,
|
|
62
62
|
asString,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { C as ChatMessagePart } from '../parts-
|
|
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-
|
|
1
|
+
import { C as ChatMessagePart } from '../parts-S1OINlRP.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-S1OINlRP.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-
|
|
9
|
+
import '../types-DB82fktc.js';
|
|
10
10
|
import '../plans/index.js';
|
|
11
11
|
|
|
12
12
|
/**
|
package/dist/chat-store/index.js
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
mentionInputToPart,
|
|
17
17
|
mentionPartsFromMessageParts,
|
|
18
18
|
toChatMessageParts
|
|
19
|
-
} from "../chunk-
|
|
20
|
-
import "../chunk-
|
|
19
|
+
} from "../chunk-DV2FA2PW.js";
|
|
20
|
+
import "../chunk-ZVEEWGDK.js";
|
|
21
21
|
import {
|
|
22
22
|
attachmentPartKey
|
|
23
23
|
} from "../chunk-5EPIPT4V.js";
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
WorkProductCard,
|
|
6
6
|
workProductPartsFromMessageParts
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YTMKRL3L.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-
|
|
19
|
+
} from "./chunk-VM6VLJH6.js";
|
|
20
20
|
import {
|
|
21
21
|
attachmentPartsFromMessageParts,
|
|
22
22
|
formatBytes
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-DV2FA2PW.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-
|
|
3959
|
+
//# sourceMappingURL=chunk-7SMPAAIT.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
persistedPartToWorkProduct
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZVEEWGDK.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-
|
|
370
|
+
//# sourceMappingURL=chunk-DV2FA2PW.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
unresolvedBlockingExceptions
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZVEEWGDK.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-
|
|
141
|
+
//# sourceMappingURL=chunk-GEYACSFW.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatBytes
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DV2FA2PW.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-
|
|
239
|
+
//# sourceMappingURL=chunk-VM6VLJH6.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseReviewQueueItem
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GEYACSFW.js";
|
|
4
4
|
import {
|
|
5
5
|
persistedPartToWorkProduct
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZVEEWGDK.js";
|
|
7
7
|
|
|
8
8
|
// src/web-react/work-product.tsx
|
|
9
9
|
import { useCallback, useEffect, useState } from "react";
|
|
@@ -341,4 +341,4 @@ export {
|
|
|
341
341
|
QualityCheckList,
|
|
342
342
|
ProvenanceStamp
|
|
343
343
|
};
|
|
344
|
-
//# sourceMappingURL=chunk-
|
|
344
|
+
//# sourceMappingURL=chunk-YTMKRL3L.js.map
|
|
@@ -46,6 +46,19 @@ 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.find !== void 0) {
|
|
50
|
+
if (typeof locatorRaw.find !== "string" || locatorRaw.find.trim().length === 0) {
|
|
51
|
+
return fail(`${path}.locator.find`, "must be a non-empty string \u2014 the value or text to locate in the source");
|
|
52
|
+
}
|
|
53
|
+
locator.find = locatorRaw.find;
|
|
54
|
+
}
|
|
55
|
+
if (locatorRaw.findOccurrence !== void 0) {
|
|
56
|
+
const value = locatorRaw.findOccurrence;
|
|
57
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
58
|
+
return fail(`${path}.locator.findOccurrence`, "must be a positive integer (1 = the first occurrence)");
|
|
59
|
+
}
|
|
60
|
+
locator.findOccurrence = value;
|
|
61
|
+
}
|
|
49
62
|
if (locatorRaw.span !== void 0) {
|
|
50
63
|
const spanRaw = asRecord(locatorRaw.span);
|
|
51
64
|
if (!spanRaw) return fail(`${path}.locator.span`, "must be an object { start, end } when present");
|
|
@@ -204,4 +217,4 @@ export {
|
|
|
204
217
|
workProductToPersistedPart,
|
|
205
218
|
persistedPartToWorkProduct
|
|
206
219
|
};
|
|
207
|
-
//# sourceMappingURL=chunk-
|
|
220
|
+
//# sourceMappingURL=chunk-ZVEEWGDK.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. Correct when the offsets are\n * computed by a tool; see {@link EvidenceLocator.find} for the form a model\n * should use, because models do not count characters reliably. */\n span?: EvidenceSpan\n /** The value or distinctive text to LOCATE in the source. The platform finds\n * the line containing it and derives both `span` and `quote` from that —\n * so the model neither retypes the quote nor computes an offset, and a\n * citation that lands on the wrong line becomes unrepresentable rather than\n * merely wrong. The preferred citation form for an agent. */\n find?: string\n /** Which occurrence of `find` to cite when the value appears more than once\n * (1-based, default 1). */\n findOccurrence?: number\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.find !== undefined) {\n if (typeof locatorRaw.find !== 'string' || locatorRaw.find.trim().length === 0) {\n return fail(`${path}.locator.find`, 'must be a non-empty string — the value or text to locate in the source')\n }\n locator.find = locatorRaw.find\n }\n if (locatorRaw.findOccurrence !== undefined) {\n const value = locatorRaw.findOccurrence\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) {\n return fail(`${path}.locator.findOccurrence`, 'must be a positive integer (1 = the first occurrence)')\n }\n locator.findOccurrence = value\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 { 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-
|
|
3
|
+
import { c as WorkProductPersistedPart } from './types-DB82fktc.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-
|
|
1
|
+
import { W as WorkProductRef, a as WorkProductProvenance, b as WorkProductRecord } from './types-DB82fktc.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The review queue is a PROJECTION, not a store — a client-safe pure fold of
|
|
@@ -153,7 +153,11 @@ interface TurnHealthVerdict {
|
|
|
153
153
|
* reported so a WINDOW can be judged: a product whose deliverable is tool
|
|
154
154
|
* output and which produced zero tool calls across every turn in the
|
|
155
155
|
* lookback has a dead tool surface, and that is the shape no per-turn rule
|
|
156
|
-
* can see.
|
|
156
|
+
* can see.
|
|
157
|
+
*
|
|
158
|
+
* Only meaningful alongside {@link interpretedParts} > 0. Zero tool calls
|
|
159
|
+
* read off a row that persisted NO parts is not an observation about the
|
|
160
|
+
* tool surface — it is the absence of one. */
|
|
157
161
|
toolCalls: number;
|
|
158
162
|
/** True when nothing about this turn could be judged — no interpretable part
|
|
159
163
|
* AND no visible text. Callers MUST exclude these from any healthy/unhealthy
|
|
@@ -177,6 +181,19 @@ interface TurnHealthVerdict {
|
|
|
177
181
|
* {@link partsReadable}. Named in the alert so a reader can see EXACTLY what
|
|
178
182
|
* the detector was blind to instead of taking "unreadable" on faith. */
|
|
179
183
|
opaquePartTypes: string[];
|
|
184
|
+
/** How many parts this module actually READ (text, artifact, tool, or a known
|
|
185
|
+
* non-output kind).
|
|
186
|
+
*
|
|
187
|
+
* The third blindness, and the one that shipped a false page. `partsReadable`
|
|
188
|
+
* only says nothing was *uninterpretable*; a row that persisted NO parts at
|
|
189
|
+
* all satisfies that vacuously. Production: 97 of tax-agent's 156 assistant
|
|
190
|
+
* rows predate parts persistence entirely and store `[]`, and reading "zero
|
|
191
|
+
* tool calls" off them raised a critical `dead_tool_surface` against a
|
|
192
|
+
* product whose own `turn_events` table holds 243 `tool_call` frames.
|
|
193
|
+
*
|
|
194
|
+
* So a tool verdict requires BOTH `partsReadable` AND this being > 0 —
|
|
195
|
+
* evidence that was read, not merely evidence that failed to be unreadable. */
|
|
196
|
+
interpretedParts: number;
|
|
180
197
|
}
|
|
181
198
|
/**
|
|
182
199
|
* Judge one settled turn.
|
|
@@ -480,12 +497,36 @@ interface SweepOptions {
|
|
|
480
497
|
* returns fluent prose and HTTP 200.
|
|
481
498
|
*
|
|
482
499
|
* This is the fourth failure shape, and the only one no per-turn rule can
|
|
483
|
-
* see.
|
|
484
|
-
*
|
|
485
|
-
*
|
|
500
|
+
* see.
|
|
501
|
+
*
|
|
502
|
+
* The verdict is drawn ONLY over turns whose parts were present and
|
|
503
|
+
* readable. An earlier revision counted rows that stored no parts, and the
|
|
504
|
+
* production example this comment used to cite was that bug rather than a
|
|
505
|
+
* finding: tax-agent's 97 parts-less rows all predate its parts persistence
|
|
506
|
+
* (they stop at 2026-07-17T02:20Z, the encrypted rows start 02:58Z), while
|
|
507
|
+
* the same database's `turn_events` table holds 243 `tool_call` frames over
|
|
508
|
+
* 15 turns. The tool surface was never dead; the rows were empty. */
|
|
486
509
|
expectsToolCalls?: boolean;
|
|
487
510
|
/** Turns needed before a dead tool surface is called. Default 10. */
|
|
488
511
|
minTurnsForToolSurface?: number;
|
|
512
|
+
/**
|
|
513
|
+
* Make an at-rest parts encoding readable, so this sweep can judge a product
|
|
514
|
+
* that does not store parts in the clear.
|
|
515
|
+
*
|
|
516
|
+
* Without it, a product that encrypts `parts` (tax-agent wraps the whole
|
|
517
|
+
* array in one `__encrypted_parts__` part) is permanently UNMEASURABLE by the
|
|
518
|
+
* tool-surface rule: every row is opaque, so the honest verdict is "cannot
|
|
519
|
+
* certify" forever. This seam is how such a product gets a real verdict
|
|
520
|
+
* instead of permanent blindness — the sweep stays domain-free and the
|
|
521
|
+
* product supplies the decoder.
|
|
522
|
+
*
|
|
523
|
+
* Return the decoded parts value (it is parsed exactly like a stored one).
|
|
524
|
+
* Returning `null`/`undefined` or THROWING leaves the row's stored value in
|
|
525
|
+
* place, so a decoder that fails reports the row as opaque. It must never
|
|
526
|
+
* report success as an empty array: that manufactures the exact absent-parts
|
|
527
|
+
* blindness this module now refuses to draw conclusions from.
|
|
528
|
+
*/
|
|
529
|
+
decodeParts?: (raw: unknown, row: PersistedTurnRow) => Promise<unknown> | unknown;
|
|
489
530
|
now?: number;
|
|
490
531
|
}
|
|
491
532
|
/** What the sweep found. Returned as well as alerted, so a cron can log it and
|
|
@@ -515,6 +556,13 @@ interface SweepResult {
|
|
|
515
556
|
* {@link unreadableTurns} (a row can have readable text and opaque parts).
|
|
516
557
|
* No tool verdict is drawn over these. */
|
|
517
558
|
opaquePartsTurns: number;
|
|
559
|
+
/** Turns that persisted NO parts at all. Distinct from
|
|
560
|
+
* {@link opaquePartsTurns}: nothing failed to parse, there was simply
|
|
561
|
+
* nothing there. Also excluded from every tool verdict. */
|
|
562
|
+
noPartsTurns: number;
|
|
563
|
+
/** Turns a tool verdict may actually be drawn from — parts present AND
|
|
564
|
+
* interpretable. The denominator behind `dead_tool_surface`. */
|
|
565
|
+
toolReadableTurns: number;
|
|
518
566
|
alerts: TurnHealthAlert[];
|
|
519
567
|
}
|
|
520
568
|
declare const SHELL_ERROR_REPLY_PREFIXES: readonly string[];
|
|
@@ -105,7 +105,11 @@ function classifyTurnOutcome(input) {
|
|
|
105
105
|
toolCalls,
|
|
106
106
|
unreadable: true,
|
|
107
107
|
partsReadable: false,
|
|
108
|
-
opaquePartTypes: uniqueOpaque
|
|
108
|
+
opaquePartTypes: uniqueOpaque,
|
|
109
|
+
// Structurally 0 here — `unreadable` is only reached when
|
|
110
|
+
// `interpretedParts === 0`. Carried rather than hardcoded so the field
|
|
111
|
+
// has one source on every return path.
|
|
112
|
+
interpretedParts
|
|
109
113
|
};
|
|
110
114
|
}
|
|
111
115
|
if (input.gated) {
|
|
@@ -125,7 +129,8 @@ function classifyTurnOutcome(input) {
|
|
|
125
129
|
toolCalls,
|
|
126
130
|
unreadable: false,
|
|
127
131
|
partsReadable,
|
|
128
|
-
opaquePartTypes: uniqueOpaque
|
|
132
|
+
opaquePartTypes: uniqueOpaque,
|
|
133
|
+
interpretedParts
|
|
129
134
|
};
|
|
130
135
|
}
|
|
131
136
|
function severityOf(reasons) {
|
|
@@ -339,6 +344,15 @@ function createTurnHealthLifecycle(options) {
|
|
|
339
344
|
}
|
|
340
345
|
|
|
341
346
|
// src/turn-health/sweep.ts
|
|
347
|
+
async function decodeRowParts(decode, row) {
|
|
348
|
+
if (!decode) return row.parts;
|
|
349
|
+
try {
|
|
350
|
+
const decoded = await decode(row.parts, row);
|
|
351
|
+
return decoded ?? row.parts;
|
|
352
|
+
} catch {
|
|
353
|
+
return row.parts;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
342
356
|
function parseParts(raw) {
|
|
343
357
|
if (Array.isArray(raw)) return raw;
|
|
344
358
|
if (typeof raw !== "string" || raw.trim().length === 0) return [];
|
|
@@ -404,18 +418,20 @@ async function sweepSilentFailures(options) {
|
|
|
404
418
|
let unreadableTurns = 0;
|
|
405
419
|
let toolReadableTurns = 0;
|
|
406
420
|
let opaquePartsTurns = 0;
|
|
421
|
+
let noPartsTurns = 0;
|
|
407
422
|
const opaqueTypes = /* @__PURE__ */ new Set();
|
|
408
423
|
const malformedSamples = [];
|
|
409
424
|
const rejectedSamples = [];
|
|
410
425
|
for (const row of turns) {
|
|
411
426
|
const verdict = classifyTurnOutcome({
|
|
412
427
|
finalText: row.content,
|
|
413
|
-
parts: parseParts(row
|
|
428
|
+
parts: parseParts(await decodeRowParts(options.decodeParts, row)),
|
|
414
429
|
outputTokens: row.outputTokens ?? null
|
|
415
430
|
});
|
|
416
431
|
toolCalls += verdict.toolCalls;
|
|
417
432
|
if (verdict.toolCalls > 0) turnsWithToolCalls += 1;
|
|
418
|
-
if (verdict.partsReadable) toolReadableTurns += 1;
|
|
433
|
+
if (verdict.partsReadable && verdict.interpretedParts > 0) toolReadableTurns += 1;
|
|
434
|
+
else if (verdict.partsReadable) noPartsTurns += 1;
|
|
419
435
|
else {
|
|
420
436
|
opaquePartsTurns += 1;
|
|
421
437
|
for (const t of verdict.opaquePartTypes) opaqueTypes.add(t);
|
|
@@ -488,13 +504,39 @@ async function sweepSilentFailures(options) {
|
|
|
488
504
|
details: [
|
|
489
505
|
`${toolReadableTurns} assistant turns with readable parts in the lookback, none of which called a tool`,
|
|
490
506
|
"the product declares its deliverable comes from tool calls, so it has answered without doing anything",
|
|
491
|
-
...opaquePartsTurns > 0 ? [`${opaquePartsTurns} further turn(s) had unreadable parts and were not judged`] : []
|
|
507
|
+
...opaquePartsTurns > 0 ? [`${opaquePartsTurns} further turn(s) had unreadable parts and were not judged`] : [],
|
|
508
|
+
...noPartsTurns > 0 ? [`${noPartsTurns} further turn(s) persisted no parts at all and were not judged`] : []
|
|
492
509
|
],
|
|
493
510
|
data: {
|
|
494
511
|
turnsJudged: toolReadableTurns,
|
|
495
512
|
toolCalls: 0,
|
|
496
513
|
turnsWithToolCalls: 0,
|
|
497
|
-
turnsNotJudged: opaquePartsTurns
|
|
514
|
+
turnsNotJudged: opaquePartsTurns + noPartsTurns,
|
|
515
|
+
opaquePartsTurns,
|
|
516
|
+
noPartsTurns
|
|
517
|
+
},
|
|
518
|
+
at: now
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
if (options.expectsToolCalls && toolReadableTurns < minTurnsForToolSurface && opaquePartsTurns + noPartsTurns > 0) {
|
|
522
|
+
alerts.push({
|
|
523
|
+
product: options.product,
|
|
524
|
+
severity: "warning",
|
|
525
|
+
key: `sweep:${options.product}:tool_surface_unmeasurable`,
|
|
526
|
+
title: `${options.product}: tool surface could not be certified \u2014 only ${toolReadableTurns} of ${turns.length} turns carried readable parts`,
|
|
527
|
+
details: [
|
|
528
|
+
...noPartsTurns > 0 ? [`${noPartsTurns} turn(s) persisted no parts at all \u2014 nothing to read a tool call from`] : [],
|
|
529
|
+
...opaquePartsTurns > 0 ? [
|
|
530
|
+
`${opaquePartsTurns} turn(s) stored parts this sweep cannot interpret (${[...opaqueTypes].join(", ") || "unnamed"}) \u2014 supply \`decodeParts\` to make them readable`
|
|
531
|
+
] : [],
|
|
532
|
+
"this is NOT a dead tool surface finding; it is the absence of evidence either way"
|
|
533
|
+
],
|
|
534
|
+
data: {
|
|
535
|
+
toolReadableTurns,
|
|
536
|
+
noPartsTurns,
|
|
537
|
+
opaquePartsTurns,
|
|
538
|
+
turnsSeen: turns.length,
|
|
539
|
+
opaqueTypes: [...opaqueTypes]
|
|
498
540
|
},
|
|
499
541
|
at: now
|
|
500
542
|
});
|
|
@@ -534,6 +576,8 @@ async function sweepSilentFailures(options) {
|
|
|
534
576
|
toolCalls,
|
|
535
577
|
unreadableTurns,
|
|
536
578
|
opaquePartsTurns,
|
|
579
|
+
noPartsTurns,
|
|
580
|
+
toolReadableTurns,
|
|
537
581
|
alerts
|
|
538
582
|
};
|
|
539
583
|
}
|