@tangle-network/agent-app 0.44.5 → 0.44.7

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.
@@ -0,0 +1,142 @@
1
+ import { AgentProfile } from '@tangle-network/agent-interface';
2
+
3
+ /**
4
+ * Profile fingerprinting — prove WHICH profile a turn actually executed.
5
+ *
6
+ * The backtest invariant this exists to enforce: an eval's score is only worth
7
+ * publishing if the benchmarked profile IS the shipped profile. The failure
8
+ * mode is structural, not hypothetical — a product's eval composed the
9
+ * production profile in one module, executed a hand-rolled stub in another,
10
+ * and stamped the composed profile's identity onto the stub's scorecard.
11
+ * Nothing compared the two, so nothing could notice.
12
+ *
13
+ * A `ProfileFingerprint` is a channelled identity of the profile handed to the
14
+ * sandbox SDK: the system-prompt digest plus the names of every capability
15
+ * surface (MCP servers, subagents, file mounts, hub connections) and the
16
+ * model/harness the turn dispatched at. It is deliberately NOT a byte-exhaustive
17
+ * serialization — channels are what drift in practice, and a channelled diff
18
+ * names the surface that moved instead of reporting "bytes differ".
19
+ *
20
+ * The write half of the seam is `StreamSandboxPromptOptions.onProfileResolved`
21
+ * (`/sandbox`): the one place the final profile exists is inside
22
+ * `streamSandboxPrompt` after the system-prompt override, the MCP merge, and
23
+ * reasoning-effort attachment, so that is where the fingerprint is taken. A
24
+ * caller re-deriving a profile to fingerprint it would reintroduce the exact
25
+ * gap this closes.
26
+ */
27
+
28
+ /** The dispatch context a profile cannot see but a turn's identity includes. */
29
+ interface ProfileFingerprintContext {
30
+ model?: string;
31
+ harness?: string;
32
+ }
33
+ /** Channelled identity of one executed (or composed) profile. */
34
+ interface ProfileFingerprint {
35
+ /** sha256 over every channel below — one value to log/compare. */
36
+ hash: string;
37
+ /** sha256 of `prompt.systemPrompt` ('' when absent). */
38
+ promptSha: string;
39
+ /** UTF-8 byte length of `prompt.systemPrompt`. */
40
+ promptBytes: number;
41
+ /** Sorted MCP server keys. */
42
+ mcpKeys: string[];
43
+ /** Sorted subagent names. */
44
+ subagentNames: string[];
45
+ /** Sorted `resources.files[].path` mounts. */
46
+ fileMountPaths: string[];
47
+ /** Sorted hub connection ids (alias-qualified when present). */
48
+ connectionIds: string[];
49
+ model?: string;
50
+ harness?: string;
51
+ }
52
+ /** Fingerprint a profile as the SDK would receive it. */
53
+ declare function fingerprintAgentProfile(profile: AgentProfile, context?: ProfileFingerprintContext): Promise<ProfileFingerprint>;
54
+ /** One drifted channel between two fingerprints, rendered as comparable strings. */
55
+ interface ProfileDriftEntry {
56
+ channel: 'promptSha' | 'promptBytes' | 'mcpKeys' | 'subagentNames' | 'fileMountPaths' | 'connectionIds' | 'model' | 'harness';
57
+ a: string;
58
+ b: string;
59
+ }
60
+ interface ProfileDrift {
61
+ equal: boolean;
62
+ drift: ProfileDriftEntry[];
63
+ }
64
+ /** Channel-by-channel comparison of two fingerprints. */
65
+ declare function diffProfileFingerprints(a: ProfileFingerprint, b: ProfileFingerprint): ProfileDrift;
66
+ /** Human-readable drift report; exactly 'profiles identical' when equal. */
67
+ declare function formatProfileDrift(drift: ProfileDrift): string;
68
+
69
+ /**
70
+ * The composed-system-prompt byte budget, split out of `./index` so it imports
71
+ * NOTHING.
72
+ *
73
+ * `./index` pulls `@tangle-network/agent-eval` (the evolvable-section seam), so
74
+ * `/sandbox` — which has no agent-eval import and must not gain one — could not
75
+ * enforce the budget from there. The gate is the same function either way; only
76
+ * its module home moved. `./index` re-exports every symbol below, so the
77
+ * published `/profile` surface is byte-identical.
78
+ *
79
+ * Why the gate belongs at more than one call site: `composeAgentProfile` is
80
+ * OPT-IN. A product may hand-build its `AgentProfile`, and one does —
81
+ * creative-agent's create-time profile is deliberately minimal and its full
82
+ * system prompt rides the PER-TURN backend (`buildPromptBackend`), which never
83
+ * touches the composer. `/sandbox` therefore runs this gate at the three points
84
+ * where the profile that actually executes exists.
85
+ */
86
+ /** Byte budget on the FINAL composed `prompt.systemPrompt`. Past this the
87
+ * model degrades sharply (a 122,659-byte prompt shipped once and the model
88
+ * returned empty answers), so the default gate throws well before that. */
89
+ declare const DEFAULT_MAX_SYSTEM_PROMPT_BYTES = 40000;
90
+ /** Budget config for the composed system prompt. */
91
+ interface ComposeProfileBudget {
92
+ /** Byte cap on the composed `prompt.systemPrompt`.
93
+ * Default {@link DEFAULT_MAX_SYSTEM_PROMPT_BYTES}. */
94
+ maxSystemPromptBytes?: number;
95
+ /** Downgrade the over-budget throw to a `console.warn` — the escape hatch
96
+ * for a product with a known-big prompt that must still ship (it yells on
97
+ * every compose instead of blocking). */
98
+ warnOnly?: boolean;
99
+ /** Required to raise {@link maxSystemPromptBytes} above
100
+ * {@link DEFAULT_MAX_SYSTEM_PROMPT_BYTES} or to set {@link warnOnly}: a
101
+ * written reason naming what stays inline and why it cannot be mounted.
102
+ * Weakening the cap is a product decision that outlives the person making
103
+ * it, and the usual cause is reference material concatenated into the prompt
104
+ * that belongs in `resources.files`; demanding the sentence here keeps that
105
+ * from happening by accident. */
106
+ overBudgetReason?: string;
107
+ }
108
+ /** Largest markdown-heading-delimited sections of a prompt, by UTF-8 bytes.
109
+ * Cheap heuristic: split on `#`-heading lines; the preamble before the first
110
+ * heading reports as "(preamble)". */
111
+ declare function largestPromptSections(prompt: string, top?: number): Array<{
112
+ title: string;
113
+ bytes: number;
114
+ }>;
115
+ /** Enforce {@link ComposeProfileBudget} on a composed system prompt: over
116
+ * budget throws (or warns with `warnOnly`) with the actual size and the
117
+ * top-3 largest sections. Exported so a product assembling its prompt
118
+ * outside `composeAgentProfile` (e.g. via the `/prompt` assembler) can
119
+ * run the same gate at its own final-composition point. */
120
+ declare function assertSystemPromptWithinBudget(systemPrompt: string, budget?: ComposeProfileBudget,
121
+ /** Prefixed to the message so a throw from a turn/provision choke point says
122
+ * WHERE it fired — "composed systemPrompt" alone reads like a compose-time
123
+ * error even when it fired on `driveSandboxTurn`. */
124
+ origin?: string): void;
125
+ /** Run {@link assertSystemPromptWithinBudget} over a profile-shaped object's
126
+ * `prompt.systemPrompt`. Structural on purpose: `/sandbox` calls this on the
127
+ * SDK's `AgentProfile` and on the product-supplied seam result without either
128
+ * module importing the other's types. A profile with no string systemPrompt
129
+ * is a no-op — there is nothing to measure.
130
+ *
131
+ * The `hint` exists because this gate can fire on a product that ALREADY made
132
+ * a deliberate budget decision somewhere else. gtm-agent composes with
133
+ * `{ maxSystemPromptBytes: 50_000 }`; without the hint, the shell's 40 KB
134
+ * default reads as the gate contradicting a choice the product already made
135
+ * rather than as "declare the same number here too". */
136
+ declare function assertProfilePromptWithinBudget(profile: {
137
+ prompt?: {
138
+ systemPrompt?: unknown;
139
+ };
140
+ } | undefined, budget?: ComposeProfileBudget, origin?: string, hint?: string): void;
141
+
142
+ export { type ComposeProfileBudget as C, DEFAULT_MAX_SYSTEM_PROMPT_BYTES as D, type ProfileDrift as P, type ProfileDriftEntry as a, type ProfileFingerprint as b, type ProfileFingerprintContext as c, assertProfilePromptWithinBudget as d, assertSystemPromptWithinBudget as e, diffProfileFingerprints as f, fingerprintAgentProfile as g, formatProfileDrift as h, largestPromptSections as l };
@@ -18,7 +18,7 @@ import '../auth-anc7mv2W.js';
18
18
  import '../types-BCxK0wyS.js';
19
19
  import '../harness/index.js';
20
20
  import '../model-CdCDfBA9.js';
21
- import '../fingerprint-DbmOgy0n.js';
21
+ import '../budget-BOucfcb_.js';
22
22
 
23
23
  /**
24
24
  * Incremental ("draft") persistence of the assistant row WHILE a turn streams.
@@ -822,6 +822,15 @@ interface ModelFailoverStreamHandle {
822
822
  * sandbox turn — so the budget is capped rather than trusted.
823
823
  */
824
824
  declare const MAX_EMPTY_TURN_RETRIES = 3;
825
+ /**
826
+ * Coerce the caller's budget to a finite, bounded, non-negative integer.
827
+ *
828
+ * `Math.trunc(NaN)` is `NaN` and `retry >= NaN` is false for every `retry`, so
829
+ * a naive clamp turns a bad config value into a loop that opens sandbox streams
830
+ * until the worker dies. `Infinity` has the same shape. Both resolve to `0` —
831
+ * an unusable budget disables the retry rather than running unbounded.
832
+ */
833
+ declare function resolveEmptyTurnRetries(value: number | undefined): number;
825
834
  /**
826
835
  * Wrap `open` in reactive model failover, streaming from the first model in
827
836
  * `models` that reaches its commit point.
@@ -1661,4 +1670,4 @@ interface PromoteAgentFilePartOptions {
1661
1670
  /** Promote a part of an agent file with optional byte limits and MIME type detection */
1662
1671
  declare function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>;
1663
1672
 
1664
- export { type AssistantDraftSnapshot, type AssistantDraftStore, type AssistantDraftWriter, type AssistantDraftWriterOptions, type AssistantRowValues, type AttachmentPathArgs, type AttachmentPathCheck, type AttachmentReadResult, type AttachmentUploadAuthorization, type AttachmentWriteResult, type BuildDispatchPartsInput, ChatAttachmentKind, type ChatRouteDurableProjection, type ChatRouteDurableProjectionLogger, type ChatRouteEvent, type ChatTurnAuthorization, type ChatTurnAuthorizeArgs, type ChatTurnCompleteInput, ChatTurnFilePartInput, type ChatTurnGateResult, type ChatTurnHeartbeat, type ChatTurnInputPatch, type ChatTurnLifecycle, type ChatTurnLifecycleComplete, type ChatTurnLifecycleError, type ChatTurnLifecycleStart, type ChatTurnLock, type ChatTurnLockResult, type ChatTurnMessageStore, type ChatTurnModelFailover, ChatTurnPartInput, type ChatTurnProduceArgs, ChatTurnRequestPayload, type ChatTurnRouteProducer, type ChatTurnRoutes, type ChatTurnUsage, type CreateAttachmentUploadRouteOptions, type CreateChatTurnRoutesOptions, type CreateUploadRouteOptions, type DetachedTurnFinal, type DetachedTurnOptions, type DetachedTurnParts, type DetachedTurnResult, type DispatchPartsOutcome, type DraftPersistenceTuning, type DraftStoredMessage, type EmptyTurnRetryInfo, type FilePartPromotionOutcome, MAX_EMPTY_TURN_RETRIES, type ModelFailoverStreamHandle, type ModelFailoverStreamOptions, type ModelFallbackInfo, type OpenModelStream, PROMOTE_MAX_FILE_BYTES, type PromoteAgentFilePartOptions, type PromoteFilePartResult, PromptInputPart, type RawAgentFilePart, type ReadAttachmentFn, type ReadSandboxMentionFn, type ResolveChatAttachmentsOptions, type ResolveChatAttachmentsResult, type SandboxChatProducerOptions, type SandboxUploadSink, UPLOAD_INLINE_MAX_BYTES, UPLOAD_MAX_FILE_BYTES, type UploadAuthorization, type UploadedChatFile, type WriteAttachmentFn, assistantRowIdForTurn, buildDispatchParts, bytesToBase64, classifyTerminalFailure, createAssistantDraftWriter, createAttachmentUploadRoute, createChatTurnRoutes, createSandboxChatProducer, createUploadRoute, defaultValidateAttachmentPath, isCommittingSandboxEvent, isDraftContentEvent, promoteAgentFilePart, resolveChatAttachments, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, withDurableChatProjection };
1673
+ export { type AssistantDraftSnapshot, type AssistantDraftStore, type AssistantDraftWriter, type AssistantDraftWriterOptions, type AssistantRowValues, type AttachmentPathArgs, type AttachmentPathCheck, type AttachmentReadResult, type AttachmentUploadAuthorization, type AttachmentWriteResult, type BuildDispatchPartsInput, ChatAttachmentKind, type ChatRouteDurableProjection, type ChatRouteDurableProjectionLogger, type ChatRouteEvent, type ChatTurnAuthorization, type ChatTurnAuthorizeArgs, type ChatTurnCompleteInput, ChatTurnFilePartInput, type ChatTurnGateResult, type ChatTurnHeartbeat, type ChatTurnInputPatch, type ChatTurnLifecycle, type ChatTurnLifecycleComplete, type ChatTurnLifecycleError, type ChatTurnLifecycleStart, type ChatTurnLock, type ChatTurnLockResult, type ChatTurnMessageStore, type ChatTurnModelFailover, ChatTurnPartInput, type ChatTurnProduceArgs, ChatTurnRequestPayload, type ChatTurnRouteProducer, type ChatTurnRoutes, type ChatTurnUsage, type CreateAttachmentUploadRouteOptions, type CreateChatTurnRoutesOptions, type CreateUploadRouteOptions, type DetachedTurnFinal, type DetachedTurnOptions, type DetachedTurnParts, type DetachedTurnResult, type DispatchPartsOutcome, type DraftPersistenceTuning, type DraftStoredMessage, type EmptyTurnRetryInfo, type FilePartPromotionOutcome, MAX_EMPTY_TURN_RETRIES, type ModelFailoverStreamHandle, type ModelFailoverStreamOptions, type ModelFallbackInfo, type OpenModelStream, PROMOTE_MAX_FILE_BYTES, type PromoteAgentFilePartOptions, type PromoteFilePartResult, PromptInputPart, type RawAgentFilePart, type ReadAttachmentFn, type ReadSandboxMentionFn, type ResolveChatAttachmentsOptions, type ResolveChatAttachmentsResult, type SandboxChatProducerOptions, type SandboxUploadSink, UPLOAD_INLINE_MAX_BYTES, UPLOAD_MAX_FILE_BYTES, type UploadAuthorization, type UploadedChatFile, type WriteAttachmentFn, assistantRowIdForTurn, buildDispatchParts, bytesToBase64, classifyTerminalFailure, createAssistantDraftWriter, createAttachmentUploadRoute, createChatTurnRoutes, createSandboxChatProducer, createUploadRoute, defaultValidateAttachmentPath, isCommittingSandboxEvent, isDraftContentEvent, promoteAgentFilePart, resolveChatAttachments, resolveEmptyTurnRetries, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, withDurableChatProjection };
@@ -92,8 +92,8 @@ import {
92
92
  flattenHistory,
93
93
  readSandboxBinaryBytes,
94
94
  statSandboxFileSize
95
- } from "../chunk-JHYJZSFX.js";
96
- import "../chunk-IVUN7FL7.js";
95
+ } from "../chunk-JXIQSGOV.js";
96
+ import "../chunk-LWSJK546.js";
97
97
  import "../chunk-CQZSAR77.js";
98
98
  import "../chunk-ICOHEZK6.js";
99
99
  import "../chunk-3EJ6SFJI.js";
@@ -1081,6 +1081,11 @@ function createSandboxChatProducer(options) {
1081
1081
  if (!options.openEvents && !options.events) {
1082
1082
  throw new Error("createSandboxChatProducer: pass `openEvents` (failover-capable) or `events`");
1083
1083
  }
1084
+ if (!options.openEvents && resolveEmptyTurnRetries(options.emptyTurnRetries) > 0) {
1085
+ throw new Error(
1086
+ "createSandboxChatProducer: `emptyTurnRetries` requires `openEvents` \u2014 a fixed `events` stream cannot be re-opened"
1087
+ );
1088
+ }
1084
1089
  const chain = options.model ? buildModelChain(options.model, options.modelFailover === false ? [] : options.fallbackModels ?? []) : [];
1085
1090
  const pendingModelNotices = [];
1086
1091
  let modelNoticeCount = 0;
@@ -2341,6 +2346,7 @@ export {
2341
2346
  promptPartsByteSize,
2342
2347
  reconcileStaleTurnLock,
2343
2348
  resolveChatAttachments,
2349
+ resolveEmptyTurnRetries,
2344
2350
  rowIdOf,
2345
2351
  runDetachedTurn,
2346
2352
  sanitizeAttachmentFileName,