@lotics/app-sdk 0.100.1 → 0.101.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.
Files changed (108) hide show
  1. package/AGENTS.md +32 -47
  2. package/dist/agent_stream.d.ts +131 -0
  3. package/dist/ask_ai.d.ts +27 -0
  4. package/dist/attachments.d.ts +58 -0
  5. package/dist/chunk-ARV5FAU5.js +1132 -0
  6. package/dist/comments.d.ts +89 -0
  7. package/dist/error_report.d.ts +9 -0
  8. package/dist/folder_pick.d.ts +8 -0
  9. package/dist/geolocation.d.ts +42 -0
  10. package/dist/hooks.d.ts +251 -0
  11. package/dist/{src/index.d.ts → index.d.ts} +13 -22
  12. package/dist/index.js +31309 -0
  13. package/dist/index.js.LEGAL.txt +11 -0
  14. package/dist/members.d.ts +32 -0
  15. package/dist/mock.d.ts +37 -0
  16. package/dist/mount.d.ts +19 -0
  17. package/dist/new_record.d.ts +37 -0
  18. package/dist/open_app.d.ts +12 -0
  19. package/dist/open_external.d.ts +10 -0
  20. package/dist/overlay.d.ts +25 -0
  21. package/dist/queries.d.ts +231 -0
  22. package/dist/recording.d.ts +47 -0
  23. package/dist/recording_state.d.ts +43 -0
  24. package/dist/rename_file.d.ts +13 -0
  25. package/dist/router.d.ts +10 -0
  26. package/dist/router.js +97 -0
  27. package/dist/row.d.ts +87 -0
  28. package/dist/rpc.d.ts +114 -0
  29. package/dist/select.d.ts +24 -0
  30. package/dist/shared_types.d.ts +8 -0
  31. package/dist/store.d.ts +43 -0
  32. package/dist/types.d.ts +36 -0
  33. package/dist/upload/optimize.d.ts +30 -0
  34. package/dist/upload/pipeline.d.ts +36 -0
  35. package/dist/upload/transport.d.ts +19 -0
  36. package/dist/url_params.d.ts +55 -0
  37. package/dist/use_recents.d.ts +15 -0
  38. package/dist/{src/use_url_state.d.ts → use_url_state.d.ts} +0 -2
  39. package/dist/viewer.d.ts +41 -0
  40. package/dist/written.d.ts +77 -0
  41. package/docs/ai.md +74 -133
  42. package/docs/data_fetching.md +209 -290
  43. package/docs/files.md +61 -51
  44. package/docs/members_and_options.md +92 -62
  45. package/docs/mutations.md +135 -205
  46. package/docs/navigation_and_state.md +26 -35
  47. package/docs/queries.md +144 -207
  48. package/docs/recipes.md +21 -45
  49. package/docs/runtime.md +74 -137
  50. package/docs/security.md +8 -11
  51. package/docs/workflows.md +189 -174
  52. package/package.json +27 -28
  53. package/dist/src/agent_stream.d.ts +0 -200
  54. package/dist/src/agent_stream.js +0 -314
  55. package/dist/src/ask_ai.d.ts +0 -40
  56. package/dist/src/ask_ai.js +0 -35
  57. package/dist/src/attachments.d.ts +0 -68
  58. package/dist/src/attachments.js +0 -93
  59. package/dist/src/comments.d.ts +0 -127
  60. package/dist/src/comments.js +0 -192
  61. package/dist/src/download.js +0 -54
  62. package/dist/src/geolocation.d.ts +0 -64
  63. package/dist/src/geolocation.js +0 -96
  64. package/dist/src/hooks.d.ts +0 -781
  65. package/dist/src/hooks.js +0 -860
  66. package/dist/src/index.js +0 -34
  67. package/dist/src/members.d.ts +0 -105
  68. package/dist/src/members.js +0 -62
  69. package/dist/src/mock.d.ts +0 -118
  70. package/dist/src/mock.js +0 -124
  71. package/dist/src/mount.d.ts +0 -47
  72. package/dist/src/mount.js +0 -34
  73. package/dist/src/new_record.d.ts +0 -74
  74. package/dist/src/new_record.js +0 -117
  75. package/dist/src/open_app.d.ts +0 -15
  76. package/dist/src/open_app.js +0 -18
  77. package/dist/src/open_external.d.ts +0 -16
  78. package/dist/src/open_external.js +0 -19
  79. package/dist/src/recording.d.ts +0 -59
  80. package/dist/src/recording.js +0 -30
  81. package/dist/src/recording_state.d.ts +0 -59
  82. package/dist/src/recording_state.js +0 -94
  83. package/dist/src/router.d.ts +0 -17
  84. package/dist/src/router.js +0 -144
  85. package/dist/src/row.d.ts +0 -159
  86. package/dist/src/row.js +0 -254
  87. package/dist/src/rpc.d.ts +0 -207
  88. package/dist/src/rpc.js +0 -904
  89. package/dist/src/select.d.ts +0 -48
  90. package/dist/src/select.js +0 -40
  91. package/dist/src/types.d.ts +0 -115
  92. package/dist/src/types.js +0 -1
  93. package/dist/src/upload/optimize.d.ts +0 -54
  94. package/dist/src/upload/optimize.js +0 -207
  95. package/dist/src/upload/pipeline.d.ts +0 -55
  96. package/dist/src/upload/pipeline.js +0 -52
  97. package/dist/src/upload/transport.d.ts +0 -42
  98. package/dist/src/upload/transport.js +0 -128
  99. package/dist/src/url_params.d.ts +0 -93
  100. package/dist/src/url_params.js +0 -215
  101. package/dist/src/use_optimistic.d.ts +0 -27
  102. package/dist/src/use_optimistic.js +0 -27
  103. package/dist/src/use_recents.d.ts +0 -19
  104. package/dist/src/use_recents.js +0 -71
  105. package/dist/src/use_url_state.js +0 -73
  106. package/dist/src/viewer.d.ts +0 -26
  107. package/dist/src/viewer.js +0 -47
  108. /package/dist/{src/download.d.ts → download.d.ts} +0 -0
package/AGENTS.md CHANGED
@@ -1,30 +1,31 @@
1
- # @lotics/app-sdk — the SDK reference index
1
+ # @lotics/app-sdk — the custom-app reference index
2
2
 
3
- The **data + RPC** runtime for Lotics custom-code apps: typed React hooks over an origin-locked
4
- postMessage bridge, cell decoders, and the `mount()` entry point. **Data + RPC only — no UI.**
5
- Render with **`@lotics/ui`** (read `node_modules/@lotics/ui/AGENTS.md`); the two pair — the SDK
6
- fetches/mutates, `@lotics/ui` draws.
3
+ **A custom-code Lotics app reads and writes through this package.** `@lotics/app-sdk` is the typed
4
+ hooks over the host bridge, the cell readers and `mount()`; `@lotics/app-sdk/router` is
5
+ `AppRouter`. Data and RPC only — no component. What the app draws is its own.
7
6
 
8
- This file is the index. The comprehensive contract references live in **`docs/`** — read the
9
- owning area doc before building; it is the contract for a backend you cannot see. The **exact
10
- signature** of any hook or reader is its shipped source: `dist/src/<name>.d.ts` — **never guess a
11
- signature; open the file.**
7
+ **`@lotics/ui` and its engines are not on npm.** The component kit is installed only inside a
8
+ sandbox session's working tree; an app scaffolded on your machine draws with its own components.
9
+ Where a doc pairs an SDK value with a kit component, that pairing applies in a sandbox session.
10
+
11
+ This file is the index. The **exact type** of anything is its shipped declaration,
12
+ `dist/<name>.d.ts` for a hook or reader — **never guess a shape; open the file.**
12
13
 
13
14
  ## The area references
14
15
 
15
16
  | Doc | Read it for |
16
17
  |---|---|
17
- | [docs/recipes.md](./docs/recipes.md) | Task-shaped how-tos for the actions whose mechanism is not guessable from the hooks — returning a generated file, returning structured data, parameterized lookups, composable optional filters, cell decoding, testing an AI action without spending credits. |
18
- | [docs/queries.md](./docs/queries.md) | **The query engine authoring reference** — AST node kinds, per-field-type operator support, filters/params/pruning, free-text search, combining tables (join/union/link/`unnest`/`record_id`), shaping (aggregates, date buckets, windows), runtime refinement bounds, limits & the efficiency playbook. |
19
- | [docs/data_fetching.md](./docs/data_fetching.md) | The four read hooks (`useQuery`/`useInfiniteQuery`/`usePaginatedQuery`/`useCount` — the last for a number with no rows, sharing the `(alias, params, filter)` count key the paginated hook uses, so a list and a badge over one set buy one count; a count is a full scan and stays its OWN request so rows paint without waiting for it, and `rows.length` is never a count since rows truncate at 10,000 — `useQuery` says so with `truncated`), the ROW type (`RowOf` — the alias's projected columns and nothing else, values `unknown`; `__source_record_id`/`__source_table_id` typed but optional), runtime `sort`/`filter` keys AND the `useFieldOptions` map keyed against that same projection (`AppQueryColumns`, `ColumnKeyOf`), cell readers (`row.*` — `row.num` and `row.bool` answer `null` for an EMPTY cell, so none is distinguishable from zero and an unanswered checkbox from a "no" — `readSelect`, `readMembers`, `readLinks`, `readFiles`, `readLocked`, and `readCreatedAt`/`readUpdatedAt` for the record timestamps every row-level result carries), caching — **arrival revalidates** (a re-mount renders cache *and* refreshes it in the background, `loading` never flips) — **realtime push** (a table one of your queries reads changes and that query refetches within about a second, alias-precise, records-only, host-embedded apps only) — and the fourth source, **this app's own successful write**, which re-reads every mounted query immediately rather than waiting on that push (→ [mutations](./docs/mutations.md)), data discipline, the pagination count as a second full execution (and `total` to suppress it), the search-as-you-type + record-picker patterns. |
20
- | [docs/mutations.md](./docs/mutations.md) | `useWorkflow` (the ONLY write path), the `WorkflowResult` resolve-never-throw contract (`field_errors` locates a refusal on the control it belongs to, where `message` can only say it at the dialog's scope), typed inputs, the automatic re-read a SUCCESSFUL write triggers over every mounted query (so a screen never waits on the host push to see its own write), diff-before-update, locked records, `useOptimistic`, `useNewRecord` (client-minted `rec_*` id so a new-record surface never remounts on its first save), read-after-write ordering (a re-read must not overtake an in-flight write), `useRecording` (the HOST records the member — mic, system sound, screen — and files the transcribed recording through a receiving workflow exactly once; `available` gates the control, `state` follows the recording). |
21
- | [docs/workflows.md](./docs/workflows.md) | **The workflow-BODY authoring reference** — the JS subset a `src/workflows/<alias>.ts` body may use: the parse-at-save/never-execute model, opaque `fld_*`/`opt_*` keys, expression sources + explicit `linked()` descent, every step form (tool call, `agent`, waits, `validate`, `return`), the accepted sugar and its canonical lowering, helpers + callback rules, record-write surfaces, the `recording` input a workflow declares to receive a recording, the traps, the bright line, and the verify loop — `check` (the only local gate: the app's own `npm run typecheck` never sees a body) → `dry_run_workflow` (static green is not a run) → `set`. |
22
- | [docs/files.md](./docs/files.md) | Files end to end — `useFileUpload`, `useAttachments` (**the add-queue, and which lifecycle it keeps**: a composer clears, a record passes `landed` and each entry leaves as the stored pile takes it over), `readFiles`/presigned URLs (**a bearer credential for the bytes** — never logged, reported, or persisted; and a LOOKUP of a files field is one list per linked row, which the reader opens itself), workflow-generated files, **naming a zip's entries** (`{ id, name }` per file — a file name, never a path), preview pairing, filter operators, the server-side delivery bounds. **Uploads declare a `fidelity`** (`standard` / `high` / `original`) — the app picks how much of the image survives storage; use `high` whenever text must stay legible. |
23
- | [docs/members_and_options.md](./docs/members_and_options.md) | People + select options + comments — `useMembers`, `useFieldOptions` (keyed by the alias's OWN columns, every key optional), `useViewer`, `useComments` (each comment carries its own resolved `author`, so a thread crossing a role boundary is legible without declaring member access), and the `@lotics/ui` components they feed. |
24
- | [docs/navigation_and_state.md](./docs/navigation_and_state.md) | `AppRouter` (embedded/standalone URL model), `useUrlState` + `urlParam` codecs, `useRecents`. |
25
- | [docs/ai.md](./docs/ai.md) | `useAgentRun` (structured vs free-text, streaming ai-sdk `parts` → `AgentRun`, the agent's ask-back — `pendingChoice`/`answerChoice` over the parked `awaiting_input` state — and the `AgentRunLanding` every leg resolves), `askAi` — plus the fields-vs-file razor for choosing between them — and `useAiContext` (push the current screen's view state to the member's ambient chat agent; caps, push-only semantics; a chat mutation refetches your queries through the realtime channel, not a separate poke). **A `file` input carries its own content** — no reader tool to declare. **An agent reaches record DATA only through its declared `query_aliases` / `workflow_aliases`.** Also **what the member's own chat agent can do with your app while it is open** — the alias catalog it reads and how to shape a mutating alias for it. |
26
- | [docs/security.md](./docs/security.md) | **Read before shipping** — the owner-principal model, `is_current_member` scoping, write attribution, group gates, public-app bounds, what runtime refinement cannot widen, and why a per-input bound is a tenancy floor rather than an authorization check (a caller-supplied id must be intersected with the record server-side). |
27
- | [docs/runtime.md](./docs/runtime.md) | `mount()`, the two transports, **the API address a standalone bundle reads out of its own page** (`<meta name="lotics-api-base">`, declared by whatever serves the app — no address is compiled into the SDK, so one bundle runs on any instance and a page without it refuses rather than guessing), `rpc()`, the design-time mock harness (`fixture` + `?__mock=1` — queries AND workflows, so an AI screen's in-flight/done/error states are reviewable without running or paying for anything; a fixture is rows or a FUNCTION of the call, and only the function form reproduces a per-call `filter`), `openExternal`/`openApp`/`downloadFile`, geofencing, and the publish chain for SDK contributors. |
18
+ | [docs/data_fetching.md](./docs/data_fetching.md) | The reads: `useQuery(alias, params, opts)` — its rows (`limit`), numbered pages (`page`), a keyset feed (`more`), its `total` or the total alone, with `total: { by }` a count per value of one column — `useQueries` for reads known only at render, `queryAll` outside React; every hook answers one `QueryState`. The ROW type (`RowOf` — the alias's projected columns and nothing else), runtime `sort`/`filter` keys, cell readers (`row.*`, `readSelect`, `readMembers`, `readLinks`, `readFiles`, `readLocked`, `readCreatedAt`/`readUpdatedAt`), the SDK's own cache — **arrival revalidates** — **realtime push**, a write drawn on every read from the press, a count as its own full scan, and the search-as-you-type and record-picker patterns. |
19
+ | [docs/queries.md](./docs/queries.md) | **The query engine reference** — AST node kinds, per-field-type operators, filters/params/pruning, free-text search, combining tables, shaping (aggregates, date buckets, windows), runtime refinement bounds, limits and the efficiency playbook. |
20
+ | [docs/mutations.md](./docs/mutations.md) | `useWorkflow` (the ONLY write path; `useWorkflows` for writes that are data), the `WorkflowResult` resolve-never-throw contract (`field_errors`), typed inputs, every write drawn on every read from the press — predicted from the workflow's own steps, taken back on a refusal, `pending` meanwhile — the re-read a successful write triggers over the tables its body names, diff-before-update, locked records, `useNewRecord`, read-after-write ordering (`writesSettled()`), `useRecording`. |
21
+ | [docs/workflows.md](./docs/workflows.md) | **The workflow-BODY reference** — the JS subset a body may use, opaque `fld_*`/`opt_*` keys, every step form, the accepted sugar, helpers, record-write surfaces, the traps and the verify loop. |
22
+ | [docs/recipes.md](./docs/recipes.md) | Task-shaped how-tos — returning a generated file, returning structured data, parameterized lookups, composable optional filters, cell decoding, testing an AI action without spending credits. |
23
+ | [docs/files.md](./docs/files.md) | Files end to end — `useFileUpload` and its `fidelity`, `renameFile` (a new file over the same bytes), `useAttachments`/`useAttachmentPiles`, `readFiles` and presigned URLs (a bearer credential — never logged or persisted), workflow-generated files, naming a zip's entries, the delivery bounds. |
24
+ | [docs/members_and_options.md](./docs/members_and_options.md) | People, select options and comments — `useMembers`, `useFieldOptions`, `useViewer`, `useWorkspaceTimezone`/`useWorkspaceCurrency`, `useAppContext`, the workspace's zone at the root, `useComments`. |
25
+ | [docs/navigation_and_state.md](./docs/navigation_and_state.md) | `AppRouter` (embedded/standalone URL model), `useUrlState` + `urlParam` codecs, `useRecents`, `useFolderPick`. |
26
+ | [docs/ai.md](./docs/ai.md) | `useAgentRun` (structured vs free-text, streaming parts, the agent's ask-back), `askAi`, `useAiContext`, and what the member's own chat agent can do with the app while it is open. |
27
+ | [docs/security.md](./docs/security.md) | **Read before shipping** — the owner-principal model, `is_current_member` scoping, write attribution, group gates, public-app bounds, why a per-input bound is a tenancy floor rather than an authorization check. |
28
+ | [docs/runtime.md](./docs/runtime.md) | `mount()` and the errors it reports to the host (`reportAppError`), the two transports, the API address a standalone bundle reads out of its own page (`<meta name="lotics-api-base">`), `rpc()`, the mock harness (`fixture` + `?__mock=1`), `openExternal`/`openApp`/`downloadFile`, geofencing, the peer dependencies. |
28
29
 
29
30
  ## Non-negotiables (each detailed in its doc)
30
31
 
@@ -34,33 +35,17 @@ signature; open the file.**
34
35
  A client-supplied member id is an IDOR. → [security](./docs/security.md)
35
36
  - **Server data is never copied into `useState`** — hooks are the source of truth; derive with
36
37
  `useMemo`. → [data_fetching](./docs/data_fetching.md)
37
- - **A successful write re-reads the screen for you** — do not chase a `useWorkflow` call with
38
- `refetch()`. Reach for it only where a write cannot have told you: a poll, a value nothing on
39
- this screen wrote, a total you supplied. → [mutations](./docs/mutations.md)
40
- - **Update writes are diffs** — send only changed fields, and *cleared* is a change: an optional
41
- input clears with `null` (never `""`), which `set_skip_null` drops and `set` performs.
42
- → [mutations](./docs/mutations.md)
43
- - **`project` only what you render; filter server-side.** A bare `from_table` over-ships every
44
- column, including files. → [queries](./docs/queries.md)
45
- - **Never hand-roll the serialization contract** — decode cells with the typed readers.
46
- → [data_fetching](./docs/data_fetching.md)
47
- - **Read only the columns the alias projects** — its option sets too, from the alias that CARRIES
48
- the column. Anything else is `undefined`, which every reader draws as a blank cell, so the row
49
- type and the option map are bounded to the projection. → [data_fetching](./docs/data_fetching.md)
50
- - **Narrow `__source_record_id` / `__source_table_id` before use** — a grouped query emits neither,
51
- so an unchecked read hands a workflow (or an AI record ref) `undefined`.
38
+ - **A write shows from the press, and the SDK does it, not the screen.** `useWorkflow` draws what
39
+ the workflow's own steps will write on every read before the server answers, takes it back on a
40
+ refusal, and re-reads the queries over the tables its body names on a success — so no screen
41
+ patches rows or calls `refetch()` after a write. → [mutations](./docs/mutations.md)
42
+ - **Update writes are diffs** — send only changed fields; an optional input clears with `null`
43
+ (never `""`). → [mutations](./docs/mutations.md)
44
+ - **`project` only what you render; filter server-side.** → [queries](./docs/queries.md)
45
+ - **Never hand-roll the serialization contract** — decode cells with the typed readers, and read
46
+ only the columns the alias projects. → [data_fetching](./docs/data_fetching.md)
47
+ - **Narrow `__source_record_id` / `__source_table_id` before use** — a grouped query emits neither.
52
48
  → [data_fetching](./docs/data_fetching.md)
53
49
  - **A standalone app takes its API address from the page it was served in**, never from a
54
- constant — the serving host declares `<meta name="lotics-api-base">`, and a bundle that finds
55
- none refuses instead of addressing someone else's instance. Nothing for an app to configure;
56
- build against 0.90.2 or later. → [runtime](./docs/runtime.md)
50
+ constant. → [runtime](./docs/runtime.md)
57
51
  - **Errors fail loud** — no swallowed catches, no silent fallbacks.
58
-
59
- ## Keeping this reference current
60
-
61
- These docs are the published contract — agents build apps from `node_modules` and cannot read the
62
- platform source, so a wrong or missing claim here becomes their bug. **Codify every new hook,
63
- reader, pattern, or contract change into its OWNING area doc + the index line above in the same
64
- change that adds it, and bump the package version** (publish CI fires on a version diff; `files`
65
- ships only on publish). Each fact lives in exactly one doc — link, never duplicate. The
66
- platform-internal model/IAM rationale stays in the repo's `docs/apps.md`.
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Folds the AI-SDK UI-message SSE stream into the `UIMessagePart[]` shape
3
+ * `@lotics/ui` `AgentRun` renders. `ai` is imported for types only, so no `ai`
4
+ * runtime enters the app bundle; unknown chunk types are ignored. A structured
5
+ * agent's `submit_result` input becomes `output`, never a part.
6
+ */
7
+ import type { UIMessagePart, UIDataTypes, UITools } from "ai";
8
+ /** An ai-sdk message part — the render model, tool-set-agnostic. */
9
+ export type AgentUIPart = UIMessagePart<UIDataTypes, UITools>;
10
+ /** Mirrors the server's `INTERACTIVE_APP_TOOLS`; a call parks the run as `awaiting_input`. */
11
+ export declare const INTERACTIVE_TOOLS: ReadonlySet<string>;
12
+ export interface AgentRunState {
13
+ status: "streaming" | "awaiting_input" | "completed" | "error";
14
+ parts: AgentUIPart[];
15
+ /** `submit_result`'s input, never the free text — a consumer reading `output.<field>` would crash on a string. */
16
+ output?: unknown;
17
+ error?: string;
18
+ }
19
+ export interface SettledAgentRun {
20
+ status: string;
21
+ output?: unknown;
22
+ error_message?: string | null;
23
+ /** On an `awaiting_input` row: rebuilds the ask for a client that lost the stream. */
24
+ pending_interactive?: {
25
+ tool_call_id: string;
26
+ tool_name: string;
27
+ input: unknown;
28
+ } | null;
29
+ }
30
+ /**
31
+ * Fold a polled row into the stream's state, for a dropped or truncated stream.
32
+ * The row owns status and structured output; a free-text row's string output
33
+ * never enters `state.output`.
34
+ */
35
+ export declare function adoptSettledRun(state: AgentRunState, settled: SettledAgentRun): AgentRunState;
36
+ export type ChoiceOption = {
37
+ label: string;
38
+ description: string;
39
+ };
40
+ export type ChoiceQuestion = {
41
+ question: string;
42
+ options: ChoiceOption[];
43
+ allow_custom?: boolean;
44
+ };
45
+ export type AskUserChoiceAnswer = {
46
+ type: "option";
47
+ question_index: number;
48
+ question_number: number;
49
+ question_text: string;
50
+ option_index: number;
51
+ option_letter: string;
52
+ option_label: string;
53
+ option_description: string;
54
+ } | {
55
+ type: "custom";
56
+ question_index: number;
57
+ question_number: number;
58
+ question_text: string;
59
+ text: string;
60
+ } | {
61
+ type: "skipped";
62
+ question_index: number;
63
+ question_number: number;
64
+ question_text: string;
65
+ };
66
+ export type AskUserChoiceOutput = {
67
+ answers: AskUserChoiceAnswer[];
68
+ skipped_by_user?: boolean;
69
+ };
70
+ export interface PendingChoice {
71
+ toolCallId: string;
72
+ questions: ChoiceQuestion[];
73
+ }
74
+ /** The pending `ask_user_choice`, non-null exactly while parked. */
75
+ export declare function pendingInteractiveCall(state: AgentRunState): PendingChoice | null;
76
+ /** `answers` align to `questions` by index (`ClarifyWizard`'s contract); a missing or empty one is skipped. */
77
+ export declare function buildChoiceOutput(questions: ChoiceQuestion[], answers: {
78
+ value: string;
79
+ custom: boolean;
80
+ }[]): AskUserChoiceOutput;
81
+ /** Settle the answered part and resume `streaming` for the continuation. */
82
+ export declare function applyInteractiveAnswer(state: AgentRunState, toolCallId: string, output: AskUserChoiceOutput): AgentRunState;
83
+ export declare function initialAgentRunState(): AgentRunState;
84
+ interface Chunk {
85
+ type?: string;
86
+ delta?: string;
87
+ /** `tool-input-delta` argument text, not prose. */
88
+ inputTextDelta?: string;
89
+ toolName?: string;
90
+ toolCallId?: string;
91
+ input?: unknown;
92
+ output?: unknown;
93
+ errorText?: string;
94
+ finishReason?: string;
95
+ }
96
+ export declare function reduceAgentChunk(state: AgentRunState, chunk: Chunk): AgentRunState;
97
+ /** Complete chunks so far and the partial frame to carry forward; `[DONE]` is dropped. */
98
+ export declare function parseSseChunks(buffer: string): {
99
+ chunks: Chunk[];
100
+ rest: string;
101
+ };
102
+ /**
103
+ * How one run leg ended, discriminated so a caller never races the hook's
104
+ * uncommitted state. A run failure is data (`failed`); only API misuse and a
105
+ * refused answer (the run stays parked) reject.
106
+ */
107
+ export type AgentRunLanding<TOutput> =
108
+ /** `output` is absent for a free-text agent, whose result is `text`. */
109
+ {
110
+ kind: "settled";
111
+ output?: TOutput;
112
+ text: string;
113
+ }
114
+ /** `pendingChoice` carries the question. */
115
+ | {
116
+ kind: "parked";
117
+ }
118
+ /** The error is user-facing. */
119
+ | {
120
+ kind: "failed";
121
+ error: string;
122
+ }
123
+ /** The client stopped listening; the run continues server-side — never a failure. */
124
+ | {
125
+ kind: "aborted";
126
+ };
127
+ export declare const ABORTED: AgentRunLanding<never>;
128
+ /** A free-text agent's result. */
129
+ export declare function proseOf(parts: readonly AgentUIPart[]): string;
130
+ export declare function landingOf(state: AgentRunState): AgentRunLanding<unknown>;
131
+ export {};
@@ -0,0 +1,27 @@
1
+ export interface AskAiArgs {
2
+ /** Prefilled only; nothing runs until the user sends. */
3
+ prompt?: string;
4
+ /** From `readFiles(cell)`; previewed beside the chat. */
5
+ file_ids?: string[];
6
+ /** The agent acts on them under the signed-in user's authority. */
7
+ record_ids?: string[];
8
+ /** Free-text grounding; the host adds the app's identity. */
9
+ context?: string;
10
+ }
11
+ /**
12
+ * Open a fresh chat seeded with files, records and a prompt the user sends
13
+ * themselves. For judgment that commits back into the app, use `useAgentRun`
14
+ * — `askAi`'s outcome lands in chat.
15
+ *
16
+ * ```tsx
17
+ * import { askAi } from "@lotics/app-sdk";
18
+ * await askAi({
19
+ * file_ids: [file.id],
20
+ * record_ids: [row.id],
21
+ * prompt: "Update the header of this invoice to match our letterhead.",
22
+ * });
23
+ * ```
24
+ *
25
+ * Rejects standalone.
26
+ */
27
+ export declare function askAi(args: AskAiArgs): Promise<void>;
@@ -0,0 +1,58 @@
1
+ import type { ImageFidelity } from "./upload/optimize.js";
2
+ export interface AttachedFile {
3
+ /** Local; the React key and `remove(id)` handle. */
4
+ id: string;
5
+ filename: string;
6
+ mime_type: string;
7
+ /** A local object URL, available before upload. */
8
+ preview_url: string;
9
+ status: "uploading" | "ready" | "error";
10
+ /** Set once `ready`. */
11
+ file_id?: string;
12
+ }
13
+ export interface AttachmentsOptions {
14
+ /**
15
+ * The stored ids the destination now holds; matching entries leave the queue
16
+ * with no gap on screen. Omitted, the queue accumulates until `clear`.
17
+ */
18
+ landed?: readonly string[];
19
+ }
20
+ export interface AttachmentsState {
21
+ /** Shared by every untouched pile, so never written through. */
22
+ files: readonly AttachedFile[];
23
+ /** Previews at once, uploads in the background; picking is the app's (`pickFiles`, paste, drop). */
24
+ add: (files: File[], options?: {
25
+ fidelity?: ImageFidelity;
26
+ }) => void;
27
+ /**
28
+ * `add`, resolving with every stored id in order — what a write waits on.
29
+ * Rejects with the first refusal; each entry keeps its own status.
30
+ */
31
+ attach: (files: File[], options?: {
32
+ fidelity?: ImageFidelity;
33
+ }) => Promise<string[]>;
34
+ remove: (id: string) => void;
35
+ clear: () => void;
36
+ /** Gate Send on it. */
37
+ uploading: boolean;
38
+ fileIds: string[];
39
+ }
40
+ export interface AttachmentPilesOptions {
41
+ /** {@link AttachmentsOptions.landed} by pile; a pile it omits accumulates until cleared. */
42
+ landed?: Readonly<Record<string, readonly string[]>>;
43
+ }
44
+ export interface AttachmentPiles {
45
+ /** The pile's queue, empty until something is added to it; its verbs are stable per pile. */
46
+ of: (pile: string) => AttachmentsState;
47
+ /** Every pile's queue at once. */
48
+ clearAll: () => void;
49
+ }
50
+ type Upload = (file: File, options?: {
51
+ fidelity?: ImageFidelity;
52
+ }) => Promise<{
53
+ id: string;
54
+ }>;
55
+ /** One queue per pile in one hook, so a caller holding a list of piles never calls a hook in a loop. */
56
+ export declare function useAttachmentPileQueues(upload: Upload, options?: AttachmentPilesOptions): AttachmentPiles;
57
+ export declare function useAttachmentQueue(upload: Upload, options?: AttachmentsOptions): AttachmentsState;
58
+ export {};