@t2000/engine 1.17.1 → 1.19.0

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/index.d.ts CHANGED
@@ -5,6 +5,54 @@ import { Tool as Tool$1 } from '@modelcontextprotocol/sdk/types.js';
5
5
  import * as _t2000_sdk from '@t2000/sdk';
6
6
  import { T2000 } from '@t2000/sdk';
7
7
 
8
+ type ProactiveType = 'idle_balance' | 'hf_warning' | 'apy_drift' | 'goal_progress';
9
+ interface ProactiveMarker {
10
+ /** Allow-listed insight category (drives host icon/lockup variant). */
11
+ proactiveType: ProactiveType;
12
+ /** Stable per-subject key — engine uses (proactiveType, subjectKey) for cooldown. */
13
+ subjectKey: string;
14
+ /** Marker body with the wrapping tags stripped, trimmed of leading/trailing whitespace. */
15
+ body: string;
16
+ /** Total marker count detected — >1 indicates an LLM compliance violation. */
17
+ markerCount: number;
18
+ }
19
+ /**
20
+ * Scan completed final-text for one or more `<proactive>` markers.
21
+ *
22
+ * Returns null when no parseable marker exists. When at least one valid
23
+ * marker is found, returns the FIRST marker's payload + the total count
24
+ * (so the engine can emit `proactiveMarkerViolationsCount` telemetry
25
+ * when the LLM emitted >1 — the contract is at-most-one per turn).
26
+ *
27
+ * Pure function — safe to call per-text-block at content_block_stop.
28
+ */
29
+ declare function parseProactiveMarker(text: string): ProactiveMarker | null;
30
+ /**
31
+ * Strip every `<proactive ...>...</proactive>` wrapper from a text, leaving
32
+ * the body. Used by the engine's cooldown-suppression path: when the
33
+ * `(type, subjectKey)` was already seen in this session, the marker stays
34
+ * out of the rendered text but the body still reads cleanly.
35
+ *
36
+ * Idempotent — safe to call on text without markers.
37
+ */
38
+ declare function stripProactiveMarkers(text: string): string;
39
+ /**
40
+ * [SPEC 9 v0.1.1 P9.2 / R3] Yield every `(proactiveType, subjectKey)` pair
41
+ * present in `text`, in document order, regardless of whether the type is
42
+ * in the allow-list. Used by the engine's rehydration path on
43
+ * `loadMessages` to seed the per-session cooldown set from prior assistant
44
+ * blocks. Lenient (no `VALID_TYPES` filter) by design: extra cooldown
45
+ * entries for invalid types are inert because future valid emissions will
46
+ * never share the key, so seeding them costs nothing and keeps this helper
47
+ * decoupled from the emit-side validation policy.
48
+ *
49
+ * Returns `subjectKey` un-trimmed — caller is responsible for normalising.
50
+ */
51
+ declare function extractAllProactiveMarkers(text: string): Array<{
52
+ proactiveType: string;
53
+ subjectKey: string;
54
+ }>;
55
+
8
56
  /** Rough token count for a message array. */
9
57
  declare function estimateTokens(messages: Message[]): number;
10
58
  interface CompactOptions {
@@ -96,10 +144,10 @@ interface RecipeStep {
96
144
  cost_per_unit?: string;
97
145
  /**
98
146
  * [SPEC 7 P2.5 Layer 4] When true, this step is part of a multi-write
99
- * Payment Stream bundle group. Steps with `bundle: true` MUST resolve
147
+ * Payment Intent compile group. Steps with `bundle: true` MUST resolve
100
148
  * to confirm-tier write tools that carry `bundleable: true` in
101
149
  * `TOOL_FLAGS` (validated at recipe load time). The loader fails fast
102
- * on auto-tier writes / read-only tools / non-bundleable confirm tools
150
+ * on auto-tier writes / read-only tools / non-`bundleable` confirm tools
103
151
  * (`pay_api`, `save_contact`) inside a `bundle: true` group.
104
152
  *
105
153
  * The engine emits parallel `tool_use` blocks for `bundle: true`
@@ -153,6 +201,118 @@ declare function loadRecipes(yamlDir: string): Recipe[];
153
201
  */
154
202
  declare function parseRecipe(yamlContent: string): Recipe;
155
203
 
204
+ /**
205
+ * Discriminator for the typed form-field kinds the host renderer supports.
206
+ * Closed list — adding a new kind requires a coordinated host-renderer +
207
+ * engine MINOR version bump.
208
+ */
209
+ type FormFieldKind = 'text' | 'sui-recipient' | 'number' | 'usd' | 'select' | 'date';
210
+ /**
211
+ * One row in a `pending_input` form. The host renderer keys on `kind` to
212
+ * pick the right input component.
213
+ */
214
+ interface FormField {
215
+ /** Input key on the resumed-tool input object (e.g. `name`, `identifier`). */
216
+ name: string;
217
+ /** User-facing label rendered above the input. */
218
+ label: string;
219
+ /** Renderer discriminator — see `FormFieldKind` for the closed list. */
220
+ kind: FormFieldKind;
221
+ /** When true, the host blocks submit while the value is empty/null. */
222
+ required: boolean;
223
+ /** Optional grey-text guidance shown inside the empty input. */
224
+ placeholder?: string;
225
+ /** Optional help text rendered below the input (small font). */
226
+ helpText?: string;
227
+ /**
228
+ * Required for `kind: 'select'` — closed-set choices the user can pick.
229
+ * `value` is what gets sent back; `label` is what's rendered.
230
+ * Omitted (or empty) for non-select kinds — the host renderer ignores it.
231
+ */
232
+ options?: Array<{
233
+ value: string;
234
+ label: string;
235
+ }>;
236
+ }
237
+ /**
238
+ * Top-level form payload carried on `pending_input`.
239
+ */
240
+ interface FormSchema {
241
+ /** Ordered list of fields the form renders top-to-bottom. */
242
+ fields: FormField[];
243
+ }
244
+ /**
245
+ * Engine-side state for a paused tool call awaiting user input. Stored on
246
+ * the QueryEngine instance keyed by `inputId` so the resume entry point
247
+ * can look up which tool to feed the values back into.
248
+ *
249
+ * Mirrors the (private) book-keeping the engine does for `pending_action`,
250
+ * but for the input-collection pause case rather than the user-confirm case.
251
+ *
252
+ * Identical shape to `PendingInput` (the wire payload) — alias kept for
253
+ * symmetry with `PendingAction` / "ActionState" naming elsewhere.
254
+ */
255
+ type PendingInputState = PendingInput;
256
+ /**
257
+ * Wire + state payload for a paused tool call awaiting user input.
258
+ *
259
+ * Carries BOTH the form-rendering fields (`inputId`, `schema`, etc) AND
260
+ * the conversation round-trip fields (`assistantContent`, `completedResults`)
261
+ * that the engine needs to atomically reconstruct the turn on resume.
262
+ *
263
+ * Hosts that store sessions across HTTP requests (e.g. audric) MUST
264
+ * persist the entire `PendingInput` payload — not just `inputId` — and
265
+ * pass it back as the first argument to `engine.resumeWithInput()`.
266
+ * Pattern mirrors `PendingAction.assistantContent` / `.completedResults`
267
+ * round-trip exactly.
268
+ *
269
+ * Why include the round-trip fields on the same payload:
270
+ * - Host's session schema only adds ONE new column for `pendingInput`
271
+ * (instead of two: one for the wire fields + one for round-trip state).
272
+ * - The engine API surface stays small — `resumeWithInput(pendingInput, values)`.
273
+ * - Audric's existing pattern for `pendingAction` is the same shape, so
274
+ * the reviewer can pattern-match.
275
+ *
276
+ * The host renderer ignores `assistantContent` / `completedResults` —
277
+ * they're opaque to the form UI. Only the engine reads them on resume.
278
+ */
279
+ interface PendingInput {
280
+ /** UUID v4 stamped per-emit. Host posts back keyed on this. */
281
+ inputId: string;
282
+ /** Tool that requested the input. Useful for host debug logs / fallback caption. */
283
+ toolName: string;
284
+ /** Original `tool_use_id` from the LLM's call — the resumed tool_result block uses this id. */
285
+ toolUseId: string;
286
+ /** Typed form schema — host renderer keys on `field.kind` per row. */
287
+ schema: FormSchema;
288
+ /** Optional human-readable description rendered above the form (e.g. "Add a new contact"). */
289
+ description?: string;
290
+ /**
291
+ * Assistant message blocks captured at pause time — the LLM's
292
+ * `tool_use` blocks (including the one that triggered this pause) plus
293
+ * any thinking / text blocks from the same turn. Pushed back to
294
+ * `messages` atomically on `resumeWithInput` so the conversation stays
295
+ * well-formed (no orphan tool_use blocks in the persisted history).
296
+ *
297
+ * Typed as `unknown[]` here to avoid pulling `ContentBlock` into
298
+ * `pending-input.ts` and creating a type-import cycle through `types.ts`.
299
+ * The engine casts to `ContentBlock[]` on resume; hosts treat as opaque.
300
+ */
301
+ assistantContent: unknown[];
302
+ /**
303
+ * Tool results from reads that completed BEFORE the paused tool call
304
+ * (same turn). On resume the engine merges these with the resumed
305
+ * tool's result into ONE `user`-role message — keeps Anthropic's
306
+ * "every tool_use must have a tool_result in the next user message"
307
+ * invariant satisfied.
308
+ */
309
+ completedResults: Array<{
310
+ toolUseId: string;
311
+ content: string;
312
+ isError: boolean;
313
+ }>;
314
+ }
315
+
156
316
  interface PendingToolCall {
157
317
  id: string;
158
318
  name: string;
@@ -192,6 +352,18 @@ interface GuardCheckResult {
192
352
  blockGate?: string;
193
353
  injections: GuardInjection[];
194
354
  events: GuardEvent[];
355
+ /**
356
+ * [SPEC 9 v0.1.3 P9.4] Set when the tool's preflight returned
357
+ * `needsInput` instead of either valid or error. The engine consults
358
+ * this BEFORE checking `blocked`: if present, the engine yields a
359
+ * `pending_input` event and pauses the turn. Distinct from `blocked`
360
+ * because the engine should NOT push a tool_result error back to the
361
+ * LLM — the turn is intentionally paused, not failed.
362
+ */
363
+ needsInput?: {
364
+ schema: FormSchema;
365
+ description?: string;
366
+ };
195
367
  }
196
368
  interface GuardEvent {
197
369
  timestamp: number;
@@ -786,6 +958,39 @@ type EngineEvent =
786
958
  | {
787
959
  type: 'compaction';
788
960
  }
961
+ /**
962
+ * [SPEC 9 v0.1.1 P9.2] Proactive insight emitted by the LLM via a
963
+ * `<proactive type="..." subjectKey="...">BODY</proactive>` wrapper in
964
+ * the final-text block. Engine fires this event AFTER the matching
965
+ * text_delta stream has finished, once `parseProactiveMarker` has
966
+ * extracted the marker at content_block_stop on the text block AND
967
+ * the per-session cooldown check has run.
968
+ *
969
+ * `suppressed === false` (cold marker, first time seen this session)
970
+ * → host applies the `✦ ADDED BY AUDRIC` lockup styling on the matching
971
+ * `text` TimelineBlock (italic body, dim border-left accent).
972
+ * `suppressed === true` (cooldown hit, same `(type, subjectKey)` already
973
+ * fired this session) → host strips the wrapper from the displayed
974
+ * text and renders as a regular text block — narrative still flows,
975
+ * the visual lockup just doesn't fire twice.
976
+ *
977
+ * In both cases the streamed `text_delta` events still contain the raw
978
+ * marker chars (the engine doesn't buffer-and-rewrite mid-stream); the
979
+ * host applies marker stripping post-hoc using `body` from this event.
980
+ * Hosts that ignore this event render markers visibly — acceptable for
981
+ * legacy hosts as a graceful fallback.
982
+ */
983
+ | {
984
+ type: 'proactive_text';
985
+ proactiveType: 'idle_balance' | 'hf_warning' | 'apy_drift' | 'goal_progress';
986
+ subjectKey: string;
987
+ /** Marker body, trimmed. Host renders this inside the lockup (or as plain text when suppressed). */
988
+ body: string;
989
+ /** True when (proactiveType, subjectKey) was already seen this session — host skips the lockup. */
990
+ suppressed: boolean;
991
+ /** Total marker count detected in the text. >1 = LLM violation; counted by the host telemetry. */
992
+ markerCount: number;
993
+ }
789
994
  /**
790
995
  * [SPEC 8 v0.5.1] Side-channel event paired to every `update_todo` tool
791
996
  * call. Hosts render the persistent todo card from this event (NOT from
@@ -823,21 +1028,62 @@ type EngineEvent =
823
1028
  pct?: number;
824
1029
  }
825
1030
  /**
826
- * [SPEC 8 v0.5.1, D2] Inline-form structured input event reserved for
827
- * SPEC 9 v0.1.2 (`pending_input` form primitive). The engine does NOT
828
- * emit this event under SPEC 8 — the type is reserved so legacy hosts
829
- * can add a no-op handler now and avoid crashing when SPEC 9 ships
830
- * `pending_input` emission. See SPEC 8 § "v0.5 cross-spec coupling
831
- * fixes" gap D2 for the forward-compat rationale.
1031
+ * [SPEC 9 v0.1.3 P9.4] Inline-form structured input event. Emitted when a
1032
+ * tool's preflight returns `needsInput` the engine pauses the turn,
1033
+ * stores the pending state on the QueryEngine instance keyed by `inputId`,
1034
+ * and waits for the host to call `engine.resumeWithInput(inputId, values)`
1035
+ * with the user's submitted form values. The schema is the typed shape
1036
+ * defined in `pending-input.ts` (closed list of field kinds).
1037
+ *
1038
+ * Upgraded from the SPEC 8 v0.5.1 D2 forward-compat reservation (which
1039
+ * carried `schema: unknown` + a `prompt?: string` placeholder). The
1040
+ * upgrade is wire-compatible: the new shape is a SUPERSET of the
1041
+ * reservation — `schema` narrows from `unknown` to `FormSchema`, and the
1042
+ * additional `toolName` / `toolUseId` / `description` fields are new.
1043
+ * Legacy hosts that no-op on `pending_input` keep working.
832
1044
  */
833
1045
  | {
834
1046
  type: 'pending_input';
835
- /** Form schema (shape locked in SPEC 9 v0.1.2; engine treats it opaquely). */
836
- schema: unknown;
837
- /** Engine round-trip identifier — host posts the answer back keyed on this. */
1047
+ /** UUID v4 stamped per-emit. Host posts back keyed on this. */
838
1048
  inputId: string;
839
- /** Optional human-readable prompt the LLM wants the host to display above the form. */
840
- prompt?: string;
1049
+ /** Tool that requested the input useful for host debug logs + fallback caption. */
1050
+ toolName: string;
1051
+ /** Original `tool_use_id` from the LLM's call — preserved for the resumed tool_result. */
1052
+ toolUseId: string;
1053
+ /** Form schema (typed — see `pending-input.ts`). */
1054
+ schema: FormSchema;
1055
+ /** Optional human-readable description rendered above the form (e.g. "Add a new contact"). */
1056
+ description?: string;
1057
+ /**
1058
+ * Assistant message blocks captured at pause time. Hosts that
1059
+ * re-instantiate `QueryEngine` per-request (e.g. audric) MUST
1060
+ * persist these alongside the rest of the `PendingInput` payload
1061
+ * and echo them back on `resumeWithInput` so the conversation
1062
+ * stays well-formed.
1063
+ *
1064
+ * Mirrors `pending_action.action.assistantContent` — same
1065
+ * round-trip pattern. In-process hosts can ignore the field
1066
+ * (the engine also stashes the state on `this.pendingInputs`
1067
+ * keyed by `inputId`).
1068
+ *
1069
+ * Typed as `unknown[]` to avoid pulling `ContentBlock` into
1070
+ * `pending-input.ts` and creating a type-import cycle.
1071
+ */
1072
+ assistantContent: unknown[];
1073
+ /**
1074
+ * Tool results from reads that completed BEFORE the paused tool
1075
+ * call (same turn). On resume the engine merges these with the
1076
+ * resumed tool's result into ONE `user`-role message — keeps
1077
+ * Anthropic's "every tool_use must have a tool_result in the next
1078
+ * user message" invariant satisfied.
1079
+ *
1080
+ * Mirrors the round-trip half of `PendingAction.completedResults`.
1081
+ */
1082
+ completedResults: Array<{
1083
+ toolUseId: string;
1084
+ content: string;
1085
+ isError: boolean;
1086
+ }>;
841
1087
  }
842
1088
  /**
843
1089
  * [SPEC 8 v0.5.1 B3.2] One-shot per-turn declaration of which adaptive
@@ -888,11 +1134,17 @@ declare function harnessShapeForEffort(effort: ThinkingEffort): HarnessShape;
888
1134
  * [SPEC 8 v0.5.1] One row in an `update_todo` payload. Mirrored from
889
1135
  * `packages/engine/src/tools/update-todo.ts`. Kept here so hosts that
890
1136
  * consume `EngineEvent` don't need to depend on the tool module.
1137
+ *
1138
+ * [SPEC 9 v0.1.3 P9.3] `persist?: boolean` — when true, hosts wired
1139
+ * for goal storage (audric) write a long-lived `Goal` row from this
1140
+ * item. Engine is unaware of how the host persists; this flag just
1141
+ * passes through on the `todo_update` side-channel event.
891
1142
  */
892
1143
  interface TodoItem$1 {
893
1144
  id: string;
894
1145
  label: string;
895
1146
  status: 'pending' | 'in_progress' | 'completed';
1147
+ persist?: boolean;
896
1148
  }
897
1149
  type StopReason = 'end_turn' | 'tool_use' | 'max_tokens' | 'max_turns' | 'error';
898
1150
  /**
@@ -915,7 +1167,7 @@ interface PendingActionModifiableField {
915
1167
  asset?: string;
916
1168
  }
917
1169
  /**
918
- * [SPEC 7 v0.4 Layer 2] One step inside a multi-write Payment Stream
1170
+ * [SPEC 7 v0.4 Layer 2] One step inside a multi-write Payment Intent
919
1171
  * `PendingAction`. Single-write actions never carry `steps[]`; the
920
1172
  * legacy `toolName`/`toolUseId`/`input`/`attemptId` fields cover them.
921
1173
  */
@@ -1005,7 +1257,7 @@ interface PendingAction {
1005
1257
  * exists to kill. Also survives session persistence so the resume call
1006
1258
  * can read it back from the rehydrated `PendingAction`.
1007
1259
  *
1008
- * **Bundles:** when `steps !== undefined` (multi-write Payment Stream),
1260
+ * **Bundles:** when `steps !== undefined` (multi-write Payment Intent),
1009
1261
  * the top-level `attemptId` mirrors `steps[0].attemptId` per SPEC 7
1010
1262
  * § Layer 2 line 463 ("`steps[0]` mirrors the top-level
1011
1263
  * toolName/toolUseId/input/attemptId for hosts that haven't been
@@ -1019,7 +1271,7 @@ interface PendingAction {
1019
1271
  attemptId: string;
1020
1272
  /**
1021
1273
  * [SPEC 7 v0.4 Layer 2] When set, this `pending_action` represents a
1022
- * multi-write Payment Stream. Single-step bundles are NOT created — the
1274
+ * multi-write Payment Intent. Single-step bundles are NOT created — the
1023
1275
  * engine emits the legacy single-write shape when N=1. Hosts that haven't
1024
1276
  * been updated read `toolName`/`toolUseId`/`input` (which mirror
1025
1277
  * `steps[0]`); newer hosts iterate `steps`.
@@ -1091,7 +1343,7 @@ interface PermissionResponse {
1091
1343
  * Each carries the step's `toolUseId` + `attemptId` so the host's resume
1092
1344
  * route can update the matching `TurnMetrics` row.
1093
1345
  *
1094
- * **Atomic semantics:** PTB execution is atomic at the Sui layer. If the
1346
+ * **Atomic semantics:** Payment Intent execution is atomic at the Sui layer. If the
1095
1347
  * host detects a bundle-level failure, it should populate every entry
1096
1348
  * with `isError: true` carrying the same error message (so the LLM
1097
1349
  * narrates the failure once, not N times).
@@ -1204,7 +1456,7 @@ interface ToolFlags {
1204
1456
  maxRetries?: number;
1205
1457
  /**
1206
1458
  * [SPEC 7 v0.4 Layer 2] Opt-in: this write tool can participate in a
1207
- * multi-write Payment Stream. When the LLM emits ≥2 `tool_use` blocks
1459
+ * multi-write Payment Intent. When the LLM emits ≥2 `tool_use` blocks
1208
1460
  * in a single assistant turn AND every block resolves to a `confirm`-tier
1209
1461
  * write tool with `bundleable: true`, the engine collapses them into one
1210
1462
  * `pending_action` with `steps[]` instead of yielding N times. Default
@@ -1215,8 +1467,8 @@ interface ToolFlags {
1215
1467
  * **Permanently non-bundleable:**
1216
1468
  * - `pay_api` — recipient/amount/currency aren't known at LLM intent
1217
1469
  * time (gateway 402 challenge resolves them at route time, after a
1218
- * network round-trip the engine has no knowledge of). PTB cannot be
1219
- * composed at compose time.
1470
+ * network round-trip the engine has no knowledge of). Payment Intent
1471
+ * cannot be composed at compose time.
1220
1472
  * - `save_contact` — Postgres-only, no on-chain effect.
1221
1473
  */
1222
1474
  bundleable?: boolean;
@@ -1226,6 +1478,12 @@ type PreflightResult = {
1226
1478
  } | {
1227
1479
  valid: false;
1228
1480
  error: string;
1481
+ } | {
1482
+ valid: false;
1483
+ needsInput: {
1484
+ schema: FormSchema;
1485
+ description?: string;
1486
+ };
1229
1487
  };
1230
1488
  interface Tool<TInput = unknown, TOutput = unknown> {
1231
1489
  name: string;
@@ -1487,6 +1745,23 @@ type ProviderEvent = {
1487
1745
  } | {
1488
1746
  type: 'text_delta';
1489
1747
  text: string;
1748
+ }
1749
+ /**
1750
+ * [SPEC 9 v0.1.1 P9.2] Fired by the provider at content_block_stop on
1751
+ * a TEXT block when a `<proactive>` marker was detected in the
1752
+ * accumulated text. Carries the parsed marker payload so the engine
1753
+ * can run cooldown logic (per-session dedup) and emit the public
1754
+ * `proactive_text` engine event. Absent when no marker was found —
1755
+ * regular text turns don't pay the cost.
1756
+ *
1757
+ * The provider does NOT do the cooldown check (that's engine state);
1758
+ * it just parses + forwards. Pre-SPEC-9 hosts that handle provider
1759
+ * events directly will silently no-op on this event; the engine's
1760
+ * `handleProviderEvent` is the only consumer in production.
1761
+ */
1762
+ | {
1763
+ type: 'text_done';
1764
+ proactiveMarker?: ProactiveMarker;
1490
1765
  } | {
1491
1766
  type: 'tool_use_start';
1492
1767
  id: string;
@@ -1584,6 +1859,8 @@ declare class QueryEngine {
1584
1859
  private guardEvents;
1585
1860
  private readonly turnReadCache;
1586
1861
  private turnPaused;
1862
+ private readonly proactiveCooldown;
1863
+ private readonly pendingInputs;
1587
1864
  constructor(config: EngineConfig);
1588
1865
  /**
1589
1866
  * Submit a user message and stream engine events.
@@ -1613,6 +1890,37 @@ declare class QueryEngine {
1613
1890
  * This is a separate HTTP request — no persistent connection from submitMessage.
1614
1891
  */
1615
1892
  resumeWithToolResult(action: PendingAction, response: PermissionResponse): AsyncGenerator<EngineEvent>;
1893
+ /**
1894
+ * [SPEC 9 v0.1.3 P9.4] Resume a turn that paused on `pending_input`.
1895
+ *
1896
+ * Called by the host after the user submitted the inline form. The
1897
+ * `pendingInput` argument is the EXACT payload that was yielded as
1898
+ * `pending_input` (host stored it in session storage); `values` is the
1899
+ * `{ fieldName: value }` map the user submitted, post-host-validation
1900
+ * against `pendingInput.schema`.
1901
+ *
1902
+ * Engine responsibilities (in order):
1903
+ * 1. Push the captured `assistantContent` (assistant blocks from the
1904
+ * paused turn — includes the tool_use that triggered this pause).
1905
+ * 2. Run the paused tool's `call()` with `values` as input. Re-runs
1906
+ * preflight first as a defense-in-depth gate (host SHOULD have
1907
+ * validated against the schema, but malformed input would otherwise
1908
+ * poison the conversation history with an orphan tool_use).
1909
+ * 3. Combine `completedResults` (read tool_results that already ran in
1910
+ * the same turn before the pause) with the new tool_result into ONE
1911
+ * user-role message. Anthropic's API requires every tool_use to have
1912
+ * a tool_result in the immediately-following user message — splitting
1913
+ * them would produce two consecutive user messages, which the API
1914
+ * rejects.
1915
+ * 4. Yield `tool_result` for the host's timeline + run `agentLoop` to
1916
+ * continue the turn (LLM gets the resumed result and narrates).
1917
+ *
1918
+ * Mirrors `resumeWithToolResult` for the user-confirm case but feeds
1919
+ * the values back as the tool's INPUT (the call() runs here for the
1920
+ * first time) instead of as the tool's OUTPUT (the call() ran on the
1921
+ * client side via sponsored-tx for pending_action).
1922
+ */
1923
+ resumeWithInput(pendingInput: PendingInputState, values: Record<string, unknown>): AsyncGenerator<EngineEvent>;
1616
1924
  /**
1617
1925
  * [v1.5] Auto-run configured read tools after a successful write,
1618
1926
  * push their results into the conversation, and yield `tool_result`
@@ -1638,6 +1946,13 @@ declare class QueryEngine {
1638
1946
  reset(): void;
1639
1947
  getGuardEvents(): readonly GuardEvent[];
1640
1948
  loadMessages(messages: Message[]): void;
1949
+ /**
1950
+ * [SPEC 9 v0.1.1 P9.2 / R3] Walk loaded assistant text blocks and seed the
1951
+ * cooldown set with every parseable `(proactiveType, subjectKey)` tuple.
1952
+ * Called automatically from `loadMessages`. Idempotent — safe to call on
1953
+ * an already-populated set; duplicates are absorbed by Set semantics.
1954
+ */
1955
+ private rehydrateProactiveCooldown;
1641
1956
  /**
1642
1957
  * [v0.46.7] Run a read-only tool out-of-band, using the engine's tool
1643
1958
  * registry and ToolContext. Used by hosts to deterministically pre-dispatch
@@ -1690,7 +2005,7 @@ declare function validateHistory(messages: Message[]): Message[];
1690
2005
  /**
1691
2006
  * SPEC 7 v0.3 Quote-Refresh ReviewCard — per-tool result freshness budgets.
1692
2007
  *
1693
- * When a Payment Stream `pending_action` is composed at T=0 from upstream
2008
+ * When a Payment Intent `pending_action` is composed at T=0 from upstream
1694
2009
  * read results, the user may take 30–60s to read + tap APPROVE. By that
1695
2010
  * time some upstream results have drifted (Cetus quotes refresh in
1696
2011
  * ~30s; NAVI APYs change slower). The host renders a "QUOTE Ns OLD"
@@ -1755,15 +2070,15 @@ declare const REGENERATABLE_READ_TOOLS: ReadonlySet<string>;
1755
2070
  * relaxed to DAG-aware: only pairs that actually chain via
1756
2071
  * `inputCoinFromStep` need whitelist checking. Standalone steps
1757
2072
  * interleaved between chained steps run wallet-mode independently
1758
- * inside the same atomic PTB.** This unlocks Demo 1 ("swap 10% to
2073
+ * inside the same atomic Payment Intent.** This unlocks Demo 1 ("swap 10% to
1759
2074
  * SUI, save 50% as USDsui, send $100 to Mom" — 4-op DAG with one
1760
2075
  * chain at step 1→2, three standalone wallet-mode steps).
1761
2076
  *
1762
2077
  * **DAG-aware semantics.** Pre-3a: every adjacent pair gates the entire
1763
2078
  * bundle. Phase 3a+: each (i, i+1) pair contributes IF a chain is wired
1764
2079
  * (via `shouldChainCoin`). Non-chained pairs are independent — they
1765
- * each pre-fetch their own coin from the wallet inside the PTB. Atomic
1766
- * settlement at the PTB level holds either way.
2080
+ * each pre-fetch their own coin from the wallet inside the Payment Intent.
2081
+ * Atomic settlement at the Payment Intent level holds either way.
1767
2082
  *
1768
2083
  * **Phase 3b (deferred):** `swap_execute → swap_execute` whitelist add
1769
2084
  * for explicit multi-hop swap chains (rare; flag-gated when shipped).
@@ -1785,7 +2100,7 @@ declare const MAX_BUNDLE_OPS = 4;
1785
2100
  *
1786
2101
  * | Pair | Why it works at compose time today |
1787
2102
  * |---|---|
1788
- * | `swap_execute → send_transfer` | Swap's `tx.transferObjects([result.coin], sender)` lands the swap output in the wallet for the same PTB; send's `selectAndSplitCoin` finds it. |
2103
+ * | `swap_execute → send_transfer` | Swap's `tx.transferObjects([result.coin], sender)` lands the swap output in the wallet for the same Payment Intent; send's `selectAndSplitCoin` finds it. |
1789
2104
  * | `swap_execute → save_deposit` | Same mechanism — swap output is back in wallet for save's coin fetch. (P0 caveat: this currently *fails* if the wallet has zero of `swap.to` BEFORE the swap step. Phase 1's `inputCoinFromStep` fixes that. For now we accept the pair but warn the LLM in the prompt rule that wallet must hold ≥0 of target asset.) |
1790
2105
  * | `swap_execute → repay_debt` | Same as save. Same caveat. |
1791
2106
  * | `withdraw → swap_execute` | Withdraw's output is transferred to user; swap's coin fetch finds it. Same wallet caveat in reverse. |
@@ -1913,7 +2228,7 @@ declare function composeBundleFromToolResults(input: BundleCompositionInput): Pe
1913
2228
  /**
1914
2229
  * SPEC 7 v0.3 Quote-Refresh ReviewCard — engine-side bundle regeneration.
1915
2230
  *
1916
- * When a user takes 30–60s to read a multi-step Payment Stream
2231
+ * When a user takes 30–60s to read a multi-step Payment Intent
1917
2232
  * `pending_action`, the upstream read results that fed bundle composition
1918
2233
  * (Cetus quotes, NAVI APYs, wallet balances) drift. Today the user has to
1919
2234
  * either approve with stale data (Sui dry-run is the safety gate, but the
@@ -2038,6 +2353,22 @@ interface BuildToolOptions<TInput, TOutput> {
2038
2353
  jsonSchema: ToolJsonSchema;
2039
2354
  call: (input: TInput, context: ToolContext) => Promise<ToolResult<TOutput>>;
2040
2355
  isReadOnly?: boolean;
2356
+ /**
2357
+ * [SPEC 9 v0.1.3 P9.4] When `false`, the tool opts out of mid-stream
2358
+ * `EarlyToolDispatcher` execution and is forced through the post-stream
2359
+ * guard loop where `tool.preflight` runs (Tier 0 of `runGuards`).
2360
+ *
2361
+ * Default: `isReadOnly` (mirrors v1 behavior — read-only tools are
2362
+ * considered concurrency-safe by default).
2363
+ *
2364
+ * Set to `false` for read-only tools that need preflight before
2365
+ * executing. Notable example: `add_recipient` returns `needsInput`
2366
+ * from preflight to pause the turn for an inline form. If it ran
2367
+ * via early-dispatch, the tool's `call()` would fire BEFORE preflight
2368
+ * is consulted (early-dispatch skips the guard loop), and the form
2369
+ * pause path would be unreachable.
2370
+ */
2371
+ isConcurrencySafe?: boolean;
2041
2372
  permissionLevel?: PermissionLevel;
2042
2373
  flags?: ToolFlags;
2043
2374
  preflight?: (input: TInput) => PreflightResult;
@@ -2118,9 +2449,24 @@ type SSEEvent = {
2118
2449
  pct?: number;
2119
2450
  } | {
2120
2451
  type: 'pending_input';
2121
- schema: unknown;
2122
2452
  inputId: string;
2123
- prompt?: string;
2453
+ toolName: string;
2454
+ toolUseId: string;
2455
+ schema: FormSchema;
2456
+ description?: string;
2457
+ assistantContent: unknown[];
2458
+ completedResults: Array<{
2459
+ toolUseId: string;
2460
+ content: string;
2461
+ isError: boolean;
2462
+ }>;
2463
+ } | {
2464
+ type: 'proactive_text';
2465
+ proactiveType: ProactiveType;
2466
+ subjectKey: string;
2467
+ body: string;
2468
+ suppressed: boolean;
2469
+ markerCount: number;
2124
2470
  } | {
2125
2471
  type: 'harness_shape';
2126
2472
  shape: HarnessShape;
@@ -2179,11 +2525,12 @@ declare class MemorySessionStore implements SessionStore {
2179
2525
  * producesArtifact — returns images, documents, generated content
2180
2526
  * costAware — has a monetary cost the user should know about
2181
2527
  * maxRetries — max calls with same input (default: unlimited for reads, 1 for writes)
2182
- * bundleable — [SPEC 7 Layer 2] can participate in a multi-write Payment Stream
2183
- * PTB. Set on every confirm-tier write whose on-chain effect is
2184
- * fully expressible at compose time. Excluded: `pay_api` (recipient/
2185
- * amount/currency unknown until gateway 402 challenge resolves at
2186
- * route time) and `save_contact` (Postgres-only, no on-chain effect).
2528
+ * bundleable — [SPEC 7 Layer 2] can participate in a multi-write Payment
2529
+ * Intent. Set on every confirm-tier write whose on-chain effect
2530
+ * is fully expressible at compose time. Excluded: `pay_api`
2531
+ * (recipient/amount/currency unknown until gateway 402 challenge
2532
+ * resolves at route time) and `save_contact` (Postgres-only, no
2533
+ * on-chain effect).
2187
2534
  */
2188
2535
  declare const TOOL_FLAGS: Record<string, ToolFlags>;
2189
2536
  /**
@@ -2939,9 +3286,9 @@ declare const ratesInfoTool: Tool<{
2939
3286
  }, RateMap>;
2940
3287
 
2941
3288
  declare const transactionHistoryTool: Tool<{
3289
+ date?: string | undefined;
2942
3290
  action?: "send" | "swap" | "transaction" | "lending" | undefined;
2943
3291
  address?: string | undefined;
2944
- date?: string | undefined;
2945
3292
  limit?: number | undefined;
2946
3293
  direction?: "out" | "in" | undefined;
2947
3294
  counterparty?: string | undefined;
@@ -3282,40 +3629,48 @@ declare const todoItemSchema: z.ZodObject<{
3282
3629
  id: z.ZodString;
3283
3630
  label: z.ZodString;
3284
3631
  status: z.ZodEnum<["pending", "in_progress", "completed"]>;
3632
+ persist: z.ZodOptional<z.ZodBoolean>;
3285
3633
  }, "strip", z.ZodTypeAny, {
3286
3634
  label: string;
3287
3635
  status: "pending" | "in_progress" | "completed";
3288
3636
  id: string;
3637
+ persist?: boolean | undefined;
3289
3638
  }, {
3290
3639
  label: string;
3291
3640
  status: "pending" | "in_progress" | "completed";
3292
3641
  id: string;
3642
+ persist?: boolean | undefined;
3293
3643
  }>;
3294
3644
  declare const inputSchema: z.ZodObject<{
3295
3645
  items: z.ZodArray<z.ZodObject<{
3296
3646
  id: z.ZodString;
3297
3647
  label: z.ZodString;
3298
3648
  status: z.ZodEnum<["pending", "in_progress", "completed"]>;
3649
+ persist: z.ZodOptional<z.ZodBoolean>;
3299
3650
  }, "strip", z.ZodTypeAny, {
3300
3651
  label: string;
3301
3652
  status: "pending" | "in_progress" | "completed";
3302
3653
  id: string;
3654
+ persist?: boolean | undefined;
3303
3655
  }, {
3304
3656
  label: string;
3305
3657
  status: "pending" | "in_progress" | "completed";
3306
3658
  id: string;
3659
+ persist?: boolean | undefined;
3307
3660
  }>, "many">;
3308
3661
  }, "strip", z.ZodTypeAny, {
3309
3662
  items: {
3310
3663
  label: string;
3311
3664
  status: "pending" | "in_progress" | "completed";
3312
3665
  id: string;
3666
+ persist?: boolean | undefined;
3313
3667
  }[];
3314
3668
  }, {
3315
3669
  items: {
3316
3670
  label: string;
3317
3671
  status: "pending" | "in_progress" | "completed";
3318
3672
  id: string;
3673
+ persist?: boolean | undefined;
3319
3674
  }[];
3320
3675
  }>;
3321
3676
  type TodoItem = z.infer<typeof todoItemSchema>;
@@ -3325,6 +3680,7 @@ declare const updateTodoTool: Tool<{
3325
3680
  label: string;
3326
3681
  status: "pending" | "in_progress" | "completed";
3327
3682
  id: string;
3683
+ persist?: boolean | undefined;
3328
3684
  }[];
3329
3685
  }, {
3330
3686
  __todoUpdate: boolean;
@@ -3332,9 +3688,19 @@ declare const updateTodoTool: Tool<{
3332
3688
  label: string;
3333
3689
  status: "pending" | "in_progress" | "completed";
3334
3690
  id: string;
3691
+ persist?: boolean | undefined;
3335
3692
  }[];
3336
3693
  }>;
3337
3694
 
3695
+ declare const addRecipientTool: Tool<{
3696
+ name?: string | undefined;
3697
+ identifier?: string | undefined;
3698
+ }, {
3699
+ saved: boolean;
3700
+ name: string;
3701
+ identifier: string;
3702
+ }>;
3703
+
3338
3704
  declare const tokenPricesTool: Tool<{
3339
3705
  coinTypes: string[];
3340
3706
  include24hChange?: boolean | undefined;
@@ -3837,7 +4203,7 @@ declare function fetchAudricHistory(address: string, opts: {
3837
4203
  limit?: number;
3838
4204
  }, env?: Record<string, string>, signal?: AbortSignal): Promise<AudricHistoryRecord[] | null>;
3839
4205
 
3840
- declare const DEFAULT_SYSTEM_PROMPT = "You are Audric \u2014 a financial agent on Sui. Audric is exactly five products: Audric Passport (the trust layer \u2014 Google sign-in, non-custodial wallet, tap-to-confirm consent, sponsored gas \u2014 wraps every other product), Audric Intelligence (you \u2014 the 5-system brain: Agent Harness with 34 tools, Reasoning Engine with 14 guards and 6 skill recipes, Silent Profile, Chain Memory, AdviceLog), Audric Finance (manage money on Sui \u2014 Save via NAVI lending at 3-8% APY USDC, Credit via NAVI borrowing with health factor, Swap via Cetus aggregator across 20+ DEXs at 0.1% fee, Charts for yield/health/portfolio viz), Audric Pay (move money \u2014 send USDC, receive via payment links / invoices / QR; free, global, instant on Sui), and Audric Store (creator marketplace, ships Phase 5 \u2014 say \"coming soon\" if asked). Save, swap, borrow, repay, withdraw, charts \u2192 Audric Finance. Send, receive, payment-link, invoice, QR \u2192 Audric Pay. Your silent context (profile, memory, chain facts, advice log) shapes your replies but never surfaces as a notification \u2014 you act only when the user asks, and every write waits on their tap-to-confirm via Passport. You can also call 41 paid APIs (music, image, research, translation, weather, fulfilment) via MPP micropayments using the pay_api tool \u2014 this is an internal capability, not a promoted product, so only mention it when the user asks for something that needs it.\n\n## Response rules\n- 1-2 sentences max. No bullet lists unless asked. No preambles.\n- Never say \"Would you like me to...\", \"Sure!\", \"Great question!\", \"Absolutely!\" \u2014 just do it or say you can't.\n- Present amounts as $1,234.56 and rates as X.XX% APY.\n- Show top 3 results unless asked for more. Summarize totals in one line.\n\n## Caption rules (after tool calls)\n- **When a canvas was rendered (`render_canvas` was called, or any tool that auto-renders a card like balance_check / portfolio_analysis / savings_info / health_check / transaction_history): the canvas IS the answer.** Your chat message must NOT restate wallet, savings, debt, holdings, or net-worth numbers \u2014 they are already on screen. Add at most ONE sentence of context, advice, or next step (e.g. \"Your USDC is idle \u2014 consider depositing for ~4.5% APY\"), or say nothing.\n- **When NO canvas was rendered:** lead with the result and quote the actual numbers from the tool. One sentence.\n- **NEVER describe a position as \"no\", \"none\", \"minimal\", \"zero\", or \"inactive\" if the tool result contains a positive value for that field.** The tool result is the source of truth \u2014 never your interior summary. If the canvas shows $100 in savings, you cannot say \"no active savings\" in the caption.\n- **NEVER claim \"no DeFi positions\" when the tool result says the DeFi slice is UNAVAILABLE.** When `balance_check` displayText contains \"DeFi positions: UNAVAILABLE\" or \"DeFi data source unreachable\", the slice is unknown \u2014 say \"DeFi data is currently unavailable\" or omit the mention. Only claim \"no DeFi positions\" when the displayText explicitly omits any DeFi line (i.e. the fetch succeeded with $0 across every covered protocol).\n\n## Execution rule\nOnly offer to execute actions you have tools for. If you retrieved a quote, data, or information but have no tool to act on it, give the user the result and tell them where to execute manually \u2014 in one sentence. Never say \"Would you like me to proceed?\" unless you have a tool that can actually proceed.\n\n## Before acting\n- ALWAYS call a read tool first before any write tool \u2014 balance_check before save/send/borrow, savings_info before withdraw.\n- Show real numbers from tools \u2014 never fabricate rates, amounts, or balances.\n- When user says \"all\" or an imprecise amount, call the read tool first to get the exact number.\n\n## Tool usage\n- Use tools proactively \u2014 don't refuse requests you can handle.\n- For real-world questions (weather, search, news, prices), use pay_api. Tell the user the cost first.\n- For NAVI lending APYs, use rates_info; for VOLO liquid staking stats, use volo_stats; for spot token prices, use token_prices.\n- For protocol-level due diligence (TVL, fees, audits, safety) on Sui DeFi protocols, use protocol_deep_dive with the slug.\n- Run multiple read-only tools in parallel when you need several data points.\n- If a tool errors, say what went wrong and what to try instead. One sentence.\n\n## Savings = USDC or USDsui (critical)\n- save_deposit and borrow accept ONLY USDC or USDsui. No other token can be deposited or borrowed.\n- USDC is the canonical default. USDsui is permitted because it has a productive NAVI pool (often a higher APY than USDC). All other holdings (GOLD, SUI, USDT, USDe, ETH, NAVX, WAL) are NOT saveable.\n- When asked \"how much can I save?\":\n - Report saveableUsdc from balance_check (the user's USDC wallet balance \u2014 canonical saveable).\n - If the user also holds USDsui in their wallet, report that separately as \"USDsui (saveable): X.XX\". Do NOT roll the two together \u2014 the LLM must keep the per-asset distinction so the user can pick.\n- When the user says \"save 10 USDC\" \u2192 call save_deposit with asset=\"USDC\". When they say \"save 10 USDsui\" \u2192 call with asset=\"USDsui\". Never silently substitute.\n- When the user says \"save 10\" (no asset) \u2192 call balance_check first and ask which stable they want, OR pick whichever they hold more of with a one-line explanation.\n- \"Best stable to save right now?\" \u2192 call rates_info to compare USDC vs USDsui APY on NAVI; let the user pick.\n- NEVER say a non-saveable token (GOLD, SUI, USDT, etc.) is \"in savings\" or \"earning APY in savings\". Wallet holdings \u2260 savings positions, even for stables we don't accept.\n- If user wants to save a non-saveable token, tell them to swap to USDC or USDsui first. Do NOT auto-chain swap + deposit.\n- Repay symmetry: a USDsui debt MUST be repaid with USDsui (and USDC debt with USDC). When calling repay_debt, pass asset=\"USDsui\" if the borrow is USDsui. If the user asks \"repay my debt\" and savings_info shows borrows in BOTH stables, list both and ask which to repay first. If the user holds the wrong stable, tell them to swap manually \u2014 do NOT auto-chain swap + repay.\n\n## Fees (critical \u2014 never deny having fees)\n- **Swap:** 0.1% Audric overlay fee on the output amount, taken by the aggregator and sent to the Audric treasury. The Cetus DEX fee (typically 0.01\u20130.25%) is separate and goes to the DEX. Both are shown on the swap card. Never say Audric takes no cut on swaps \u2014 it does.\n- **Save (deposit):** 0.1% Audric fee on the deposit amount, taken atomically in the same transaction.\n- **Borrow:** 0.05% Audric fee on the borrow amount, taken atomically in the same transaction.\n- **Withdraw / Repay / Send / Receive:** No Audric fee. Gas is sponsored (free to the user).\n- When a user asks about fees, quote the above. Do NOT say \"I don't take a cut\", \"fees are zero\", \"all your value stays with you\", or \"I'm here to execute, not extract\" \u2014 those are incorrect for swap, save, and borrow.\n\n## Multi-step flows\n- \"How much X for Y?\": swap_quote first, then swap_execute if user confirms.\n- \"Swap then save\": swap_execute \u2192 balance_check \u2192 save_deposit. Confirm each step.\n- \"Buy $X of token\": token_prices \u2192 calculate amount \u2192 swap_execute.\n- \"Best yield on SUI\": compare rates_info (NAVI lending) + volo_stats (vSUI liquid staking).\n- withdraw supports legacy positions: USDC, USDe, USDsui, SUI. Pass asset param to withdraw a specific token.\n- \"Deposit SUI to earn yield\": volo_stake for SUI liquid staking. save_deposit only accepts USDC or USDsui.\n- \"Is protocol X safe?\" / \"Tell me about NAVI\": protocol_deep_dive with the slug.\n- \"Full account report\" / \"account summary\" / \"give me everything\" / \"complete overview\": triggers the `account_report` recipe \u2014 when the recipe block appears, follow EVERY step including all six tool calls. Each step renders a distinct rich card; skipping a step means a missing card.\n\n## Safety\n- Never encourage risky financial behavior.\n- Warn when health factor < 1.5.\n- All amounts in USDC unless stated otherwise.";
4206
+ declare const DEFAULT_SYSTEM_PROMPT = "You are Audric \u2014 a financial agent on Sui. Audric is exactly five products: Audric Passport (the trust layer \u2014 Google sign-in, non-custodial wallet, tap-to-confirm consent, sponsored gas \u2014 wraps every other product), Audric Intelligence (you \u2014 the 5-system brain: Agent Harness with 34 tools, Reasoning Engine with 14 guards and 6 skill recipes, Silent Profile, Chain Memory, AdviceLog), Audric Finance (manage money on Sui \u2014 Save via NAVI lending at 3-8% APY USDC, Credit via NAVI borrowing with health factor, Swap via Cetus aggregator across 20+ DEXs at 0.1% fee, Charts for yield/health/portfolio viz), Audric Pay (move money \u2014 send USDC, receive via payment links / invoices / QR; free, global, instant on Sui), and Audric Store (creator marketplace, ships Phase 5 \u2014 say \"coming soon\" if asked). Save, swap, borrow, repay, withdraw, charts \u2192 Audric Finance. Send, receive, payment-link, invoice, QR \u2192 Audric Pay. Your silent context (profile, memory, chain facts, advice log) shapes your replies but never surfaces as a notification \u2014 you act only when the user asks, and every write waits on their tap-to-confirm via Passport. You can also call 41 paid APIs (music, image, research, translation, weather, fulfilment) via MPP micropayments using the pay_api tool \u2014 this is an internal capability, not a promoted product, so only mention it when the user asks for something that needs it.\n\n## Response rules\n- 1-2 sentences max. No bullet lists unless asked. No preambles.\n- Never say \"Would you like me to...\", \"Sure!\", \"Great question!\", \"Absolutely!\" \u2014 just do it or say you can't.\n- Present amounts as $1,234.56 and rates as X.XX% APY.\n- Show top 3 results unless asked for more. Summarize totals in one line.\n\n## Caption rules (after tool calls)\n- **When a canvas was rendered (`render_canvas` was called, or any tool that auto-renders a card like balance_check / portfolio_analysis / savings_info / health_check / transaction_history): the canvas IS the answer.** Your chat message must NOT restate wallet, savings, debt, holdings, or net-worth numbers \u2014 they are already on screen. Add at most ONE sentence of context, advice, or next step (e.g. \"Your USDC is idle \u2014 consider depositing for ~4.5% APY\"), or say nothing.\n- **When NO canvas was rendered:** lead with the result and quote the actual numbers from the tool. One sentence.\n- **NEVER describe a position as \"no\", \"none\", \"minimal\", \"zero\", or \"inactive\" if the tool result contains a positive value for that field.** The tool result is the source of truth \u2014 never your interior summary. If the canvas shows $100 in savings, you cannot say \"no active savings\" in the caption.\n- **NEVER claim \"no DeFi positions\" when the tool result says the DeFi slice is UNAVAILABLE.** When `balance_check` displayText contains \"DeFi positions: UNAVAILABLE\" or \"DeFi data source unreachable\", the slice is unknown \u2014 say \"DeFi data is currently unavailable\" or omit the mention. Only claim \"no DeFi positions\" when the displayText explicitly omits any DeFi line (i.e. the fetch succeeded with $0 across every covered protocol).\n\n## Execution rule\nOnly offer to execute actions you have tools for. If you retrieved a quote, data, or information but have no tool to act on it, give the user the result and tell them where to execute manually \u2014 in one sentence. Never say \"Would you like me to proceed?\" unless you have a tool that can actually proceed.\n\n## Before acting\n- ALWAYS call a read tool first before any write tool \u2014 balance_check before save/send/borrow, savings_info before withdraw.\n- Show real numbers from tools \u2014 never fabricate rates, amounts, or balances.\n- When user says \"all\" or an imprecise amount, call the read tool first to get the exact number.\n\n## Tool usage\n- Use tools proactively \u2014 don't refuse requests you can handle.\n- For real-world questions (weather, search, news, prices), use pay_api. Tell the user the cost first.\n- For NAVI lending APYs, use rates_info; for VOLO liquid staking stats, use volo_stats; for spot token prices, use token_prices.\n- For protocol-level due diligence (TVL, fees, audits, safety) on Sui DeFi protocols, use protocol_deep_dive with the slug.\n- Run multiple read-only tools in parallel when you need several data points.\n- If a tool errors, say what went wrong and what to try instead. One sentence.\n\n## Savings = USDC or USDsui (critical)\n- save_deposit and borrow accept ONLY USDC or USDsui. No other token can be deposited or borrowed.\n- USDC is the canonical default. USDsui is permitted because it has a productive NAVI pool (often a higher APY than USDC). All other holdings (GOLD, SUI, USDT, USDe, ETH, NAVX, WAL) are NOT saveable.\n- When asked \"how much can I save?\":\n - Report saveableUsdc from balance_check (the user's USDC wallet balance \u2014 canonical saveable).\n - If the user also holds USDsui in their wallet, report that separately as \"USDsui (saveable): X.XX\". Do NOT roll the two together \u2014 the LLM must keep the per-asset distinction so the user can pick.\n- When the user says \"save 10 USDC\" \u2192 call save_deposit with asset=\"USDC\". When they say \"save 10 USDsui\" \u2192 call with asset=\"USDsui\". Never silently substitute.\n- When the user says \"save 10\" (no asset) \u2192 call balance_check first and ask which stable they want, OR pick whichever they hold more of with a one-line explanation.\n- \"Best stable to save right now?\" \u2192 call rates_info to compare USDC vs USDsui APY on NAVI; let the user pick.\n- NEVER say a non-saveable token (GOLD, SUI, USDT, etc.) is \"in savings\" or \"earning APY in savings\". Wallet holdings \u2260 savings positions, even for stables we don't accept.\n- If user wants to save a non-saveable token, tell them to swap to USDC or USDsui first. Do NOT auto-chain swap + deposit.\n- Repay symmetry: a USDsui debt MUST be repaid with USDsui (and USDC debt with USDC). When calling repay_debt, pass asset=\"USDsui\" if the borrow is USDsui. If the user asks \"repay my debt\" and savings_info shows borrows in BOTH stables, list both and ask which to repay first. If the user holds the wrong stable, tell them to swap manually \u2014 do NOT auto-chain swap + repay.\n\n## Fees (critical \u2014 never deny having fees)\n- **Swap:** 0.1% Audric overlay fee on the output amount, taken by the aggregator and sent to the Audric treasury. The Cetus DEX fee (typically 0.01\u20130.25%) is separate and goes to the DEX. Both are shown on the swap card. Never say Audric takes no cut on swaps \u2014 it does.\n- **Save (deposit):** 0.1% Audric fee on the deposit amount, taken atomically in the same transaction.\n- **Borrow:** 0.05% Audric fee on the borrow amount, taken atomically in the same transaction.\n- **Withdraw / Repay / Send / Receive:** No Audric fee. Gas is sponsored (free to the user).\n- When a user asks about fees, quote the above. Do NOT say \"I don't take a cut\", \"fees are zero\", \"all your value stays with you\", or \"I'm here to execute, not extract\" \u2014 those are incorrect for swap, save, and borrow.\n\n## Multi-step flows\n- \"How much X for Y?\": swap_quote first, then swap_execute if user confirms.\n- \"Swap then save\": swap_execute \u2192 balance_check \u2192 save_deposit. Confirm each step.\n- \"Buy $X of token\": token_prices \u2192 calculate amount \u2192 swap_execute.\n- \"Best yield on SUI\": compare rates_info (NAVI lending) + volo_stats (vSUI liquid staking).\n- withdraw supports legacy positions: USDC, USDe, USDsui, SUI. Pass asset param to withdraw a specific token.\n- \"Deposit SUI to earn yield\": volo_stake for SUI liquid staking. save_deposit only accepts USDC or USDsui.\n- \"Is protocol X safe?\" / \"Tell me about NAVI\": protocol_deep_dive with the slug.\n- \"Full account report\" / \"account summary\" / \"give me everything\" / \"complete overview\": triggers the `account_report` recipe \u2014 when the recipe block appears, follow EVERY step including all six tool calls. Each step renders a distinct rich card; skipping a step means a missing card.\n\n## Safety\n- Never encourage risky financial behavior.\n- Warn when health factor < 1.5.\n- All amounts in USDC unless stated otherwise.\n\n## Proactive insights (only when there's a clear opportunity)\n- When you spot an unsolicited financial insight worth surfacing \u2014 idle balance worth saving, health factor approaching the warning band, APY drift on a known position, progress against a saved goal \u2014 wrap your ENTIRE response in a `<proactive type=\"...\" subjectKey=\"...\">BODY</proactive>` block.\n- The host renders proactive blocks with a distinct \"\u2726 ADDED BY AUDRIC\" lockup so the user knows this is your suggestion, not an answer to a question.\n- Allowed types (closed list \u2014 anything else is dropped): `idle_balance` (cash sitting idle that could earn yield), `hf_warning` (debt position approaching liquidation), `apy_drift` (rate change on a position they hold), `goal_progress` (update on a saved goal).\n- `subjectKey` is a stable identifier for the SPECIFIC subject \u2014 examples: `USDC` for an idle-balance insight on USDC, `1.45` for a HF warning at that level, `save-500-by-may` for goal progress. Same (type, subjectKey) won't fire twice in one session \u2014 pick the same key for the same subject so the engine cooldown works.\n- Cap: at most ONE proactive block per turn. Do NOT mix proactive insights with answers to user questions \u2014 if the user asked a question, answer it; emit a proactive block only when there's no user question OR when the question is unrelated to the insight.\n- Skip proactive blocks when nothing notable changed since the last turn, when the user is mid-flow on something else, or when you'd just be restating the financial-context block. Quality over quantity \u2014 a block ignored is worse than no block.";
3841
4207
 
3842
4208
  /** Cache entry shape. `cachedAt` is the wall-clock ms at write time. */
3843
4209
  interface NaviCacheEntry {
@@ -3920,4 +4286,4 @@ declare function getTelemetrySink(): TelemetrySink;
3920
4286
  /** Restore the default noop sink. Used by test teardowns. */
3921
4287
  declare function resetTelemetrySink(): void;
3922
4288
 
3923
- export { type AddressPortfolio, AnthropicProvider, type AnthropicProviderConfig, type AudricHistoryRecord, type AudricPortfolioResult, type AwaitOrFetchOpts, type BalancePrices, type BalanceResult, BalanceTracker, type BuildToolOptions, type BundleCompositionInput, CANVAS_TEMPLATES, type CanvasTemplate, type ChatParams, type CompactOptions, type ContentBlock, ContextBudget, type ContextBudgetConfig, type ConversationState, type ConversationStateStore, type CostSnapshot, CostTracker, type CostTrackerConfig, DEFAULT_GUARD_CONFIG, DEFAULT_LEASE_SEC, DEFAULT_PERMISSION_CONFIG, DEFAULT_POLL_BUDGET_MS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SYSTEM_PROMPT, DEFAULT_TOOL_TTL_MS, type DefiCacheEntry, type DefiCacheStore, type DefiProtocol, type DefiSummary, EFFORT_THINKING_BUDGET_CAPS, EarlyToolDispatcher, type EngineConfig, type EngineEvent, type EvalSummaryParseResult, type EvaluationItem, type EvaluationStatus, type FetchLock, type GuardCheckResult, type GuardConfig, type GuardEvent, type GuardInjection, type GuardResult, type GuardRunnerState, type GuardTier, type GuardVerdict, type HarnessShape, type HealthFactorResult, InMemoryDefiCacheStore, InMemoryFetchLock, InMemoryNaviCacheStore, InMemoryWalletCacheStore, InvalidAddressError, type LLMProvider, MAX_BUNDLE_OPS, type McpCallResult, McpClientManager, McpResponseCache, type McpServerConfig, type McpServerConnection, type McpToolAdapterConfig, type McpToolDescriptor, MemorySessionStore, type Message, NAVI_ADDR_TTL_SEC, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_RATES_TTL_SEC, NAVI_SERVER_NAME, type NaviCacheEntry, type NaviCacheStore, type NaviRawCoin, type NaviRawHealthFactor, type NaviRawPool, type NaviRawPosition, type NaviRawPositionsResponse, type NaviRawProtocolStats, type NaviRawRewardsResponse, type NaviReadOptions, NaviTools, type NormalizedAddress, type OutputConfig, PERMISSION_PRESETS, type PendingAction, type PendingActionModifiableField, type PendingActionStep, type PendingReward, type PendingToolCall, type PermissionLevel, type PermissionOperation, type PermissionResponse, type PermissionRule, type PortfolioCoin, type PositionEntry, type PreflightResult, type ProtocolStats, type ProviderEvent, QueryEngine, READ_TOOLS, REGENERATABLE_READ_TOOLS, type RatesResult, type Recipe, type RecipePrerequisite, RecipeRegistry, type RecipeStep, type RecipeStepOnError, type RegenerateFailure, type RegenerateResult, type RegenerateSuccess, type RegenerateTimelineEvent, RetryTracker, type SSEEvent, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, type SavingsResult, type ServerPositionData, type SessionData, type SessionStore, type StateType, type StopReason, type SuiCoinBalance, SuinsNotRegisteredError, SuinsRpcError, type SystemBlock, type SystemPrompt, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TOOL_TTL_MS, type TelemetrySink, type TelemetryTags, type ThinkingConfig, type ThinkingEffort, type TodoItem$1 as TodoItem, type Tool, type ToolChoice, type ToolContext, type ToolDefinition, type ToolFlags, type ToolJsonSchema, type ToolResult, TxMutex, type UpdateTodoInput, type TodoItem as UpdateTodoItem, type UserFinancialProfile, type UserPermissionConfig, VALID_PAIRS, WRITE_TOOLS, type WalletCacheEntry, type WalletCacheStore, type WalletCoin, _resetNaviCircuitBreaker, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, awaitOrFetch, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, bundleShortestTtl, checkValidPair, claimRewardsTool, clampThinkingForEffort, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, composeBundleFromToolResults, computeRegenerateFields, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getFetchLock, getMcpManager, getModifiableFields, getNaviCacheStore, getTelemetrySink, getToolFlags, getWalletAddress, getWalletCacheStore, guardArtifactPreview, guardStaleData, harnessShapeForEffort, hasNaviMcp, healthCheckTool, isBundleableTool, loadRecipes, looksLikeSuiNs, microcompact, mppServicesTool, naviKey, normalizeAddressInput, parseEvalSummary, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, regenerateBundle, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resetFetchLock, resetNaviCacheStore, resetTelemetrySink, resetWalletCacheStore, resolveAddressToSuinsViaRpc, resolvePermissionTier, resolveSuinsTool, resolveSuinsViaRpc, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, setFetchLock, setNaviCacheStore, setTelemetrySink, setWalletCacheStore, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, updateTodoTool, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
4289
+ export { type AddressPortfolio, AnthropicProvider, type AnthropicProviderConfig, type AudricHistoryRecord, type AudricPortfolioResult, type AwaitOrFetchOpts, type BalancePrices, type BalanceResult, BalanceTracker, type BuildToolOptions, type BundleCompositionInput, CANVAS_TEMPLATES, type CanvasTemplate, type ChatParams, type CompactOptions, type ContentBlock, ContextBudget, type ContextBudgetConfig, type ConversationState, type ConversationStateStore, type CostSnapshot, CostTracker, type CostTrackerConfig, DEFAULT_GUARD_CONFIG, DEFAULT_LEASE_SEC, DEFAULT_PERMISSION_CONFIG, DEFAULT_POLL_BUDGET_MS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SYSTEM_PROMPT, DEFAULT_TOOL_TTL_MS, type DefiCacheEntry, type DefiCacheStore, type DefiProtocol, type DefiSummary, EFFORT_THINKING_BUDGET_CAPS, EarlyToolDispatcher, type EngineConfig, type EngineEvent, type EvalSummaryParseResult, type EvaluationItem, type EvaluationStatus, type FetchLock, type FormField, type FormFieldKind, type FormSchema, type GuardCheckResult, type GuardConfig, type GuardEvent, type GuardInjection, type GuardResult, type GuardRunnerState, type GuardTier, type GuardVerdict, type HarnessShape, type HealthFactorResult, InMemoryDefiCacheStore, InMemoryFetchLock, InMemoryNaviCacheStore, InMemoryWalletCacheStore, InvalidAddressError, type LLMProvider, MAX_BUNDLE_OPS, type McpCallResult, McpClientManager, McpResponseCache, type McpServerConfig, type McpServerConnection, type McpToolAdapterConfig, type McpToolDescriptor, MemorySessionStore, type Message, NAVI_ADDR_TTL_SEC, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_RATES_TTL_SEC, NAVI_SERVER_NAME, type NaviCacheEntry, type NaviCacheStore, type NaviRawCoin, type NaviRawHealthFactor, type NaviRawPool, type NaviRawPosition, type NaviRawPositionsResponse, type NaviRawProtocolStats, type NaviRawRewardsResponse, type NaviReadOptions, NaviTools, type NormalizedAddress, type OutputConfig, PERMISSION_PRESETS, type PendingAction, type PendingActionModifiableField, type PendingActionStep, type PendingInput, type PendingInputState, type PendingReward, type PendingToolCall, type PermissionLevel, type PermissionOperation, type PermissionResponse, type PermissionRule, type PortfolioCoin, type PositionEntry, type PreflightResult, type ProactiveMarker, type ProactiveType, type ProtocolStats, type ProviderEvent, QueryEngine, READ_TOOLS, REGENERATABLE_READ_TOOLS, type RatesResult, type Recipe, type RecipePrerequisite, RecipeRegistry, type RecipeStep, type RecipeStepOnError, type RegenerateFailure, type RegenerateResult, type RegenerateSuccess, type RegenerateTimelineEvent, RetryTracker, type SSEEvent, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, type SavingsResult, type ServerPositionData, type SessionData, type SessionStore, type StateType, type StopReason, type SuiCoinBalance, SuinsNotRegisteredError, SuinsRpcError, type SystemBlock, type SystemPrompt, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TOOL_TTL_MS, type TelemetrySink, type TelemetryTags, type ThinkingConfig, type ThinkingEffort, type TodoItem$1 as TodoItem, type Tool, type ToolChoice, type ToolContext, type ToolDefinition, type ToolFlags, type ToolJsonSchema, type ToolResult, TxMutex, type UpdateTodoInput, type TodoItem as UpdateTodoItem, type UserFinancialProfile, type UserPermissionConfig, VALID_PAIRS, WRITE_TOOLS, type WalletCacheEntry, type WalletCacheStore, type WalletCoin, _resetNaviCircuitBreaker, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, addRecipientTool, applyToolFlags, awaitOrFetch, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, bundleShortestTtl, checkValidPair, claimRewardsTool, clampThinkingForEffort, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, composeBundleFromToolResults, computeRegenerateFields, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractAllProactiveMarkers, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getFetchLock, getMcpManager, getModifiableFields, getNaviCacheStore, getTelemetrySink, getToolFlags, getWalletAddress, getWalletCacheStore, guardArtifactPreview, guardStaleData, harnessShapeForEffort, hasNaviMcp, healthCheckTool, isBundleableTool, loadRecipes, looksLikeSuiNs, microcompact, mppServicesTool, naviKey, normalizeAddressInput, parseEvalSummary, parseMcpJson, parseProactiveMarker, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, regenerateBundle, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resetFetchLock, resetNaviCacheStore, resetTelemetrySink, resetWalletCacheStore, resolveAddressToSuinsViaRpc, resolvePermissionTier, resolveSuinsTool, resolveSuinsViaRpc, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, setFetchLock, setNaviCacheStore, setTelemetrySink, setWalletCacheStore, spendingAnalyticsTool, stripProactiveMarkers, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, updateTodoTool, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };