@lotics/app-sdk 0.77.1 → 0.77.2
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/AGENTS.md +1 -1
- package/docs/ai.md +35 -21
- package/docs/workflows.md +24 -0
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -22,7 +22,7 @@ signature; open the file.**
|
|
|
22
22
|
| [docs/files.md](./docs/files.md) | Files end to end — `useFileUpload`, `useAttachments`, `readFiles`/presigned URLs (**a bearer credential for the bytes** — never logged, reported, or persisted), 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
23
|
| [docs/members_and_options.md](./docs/members_and_options.md) | People + select options + comments — `useMembers`, `useFieldOptions`, `useViewer`, `useComments`, and the `@lotics/ui` components they feed. |
|
|
24
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), `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, auto query-refetch on chat mutation). **A `file` input carries its own content** — images/PDFs are perceived natively, Word/Excel/CSV/text are materialized into the run; no reader tool to declare. **An agent reaches record DATA only through its declared `query_aliases` / `workflow_aliases`** (`run_app_query` / `run_app_workflow`); the raw record read/write tools are rejected
|
|
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), `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, auto query-refetch on chat mutation). **A `file` input carries its own content** — images/PDFs are perceived natively, Word/Excel/CSV/text are materialized into the run; no reader tool to declare. **An agent reaches record DATA only through its declared `query_aliases` / `workflow_aliases`** (`run_app_query` / `run_app_workflow`); the raw record read/write tools are rejected. A leg resolves an `AgentRunLanding` (`settled`/`parked`/`failed`/`aborted` — never a bare `undefined`). Also **what the member's own chat agent can do with your app while it is open** — the alias catalog it reads (your app `description` heads it as `about:`, and an input's `description` is the only place a default is visible), and how to shape a mutating alias for it. |
|
|
26
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
27
|
| [docs/runtime.md](./docs/runtime.md) | `mount()`, the two transports, `rpc()`, `openExternal`/`downloadFile`, geofencing, analytics, `useConfig` (App-Packages installation config), `getAppBinding` (package apps' runtime `F`/`OPT`/`ROLE` resolution via the generated `.lotics/app_fields.ts`), and the publish chain for package contributors. |
|
|
28
28
|
|
package/docs/ai.md
CHANGED
|
@@ -346,7 +346,7 @@ This hook is a **one-way push of data the app already showed this member**. (Cha
|
|
|
346
346
|
|
|
347
347
|
`useAiContext` is a PUSH — a snapshot of what one screen rendered. It cannot
|
|
348
348
|
answer a question about a record that is not on screen, and it is capped, so it
|
|
349
|
-
|
|
349
|
+
is not the whole story.
|
|
350
350
|
|
|
351
351
|
While a member has an app open, their chat agent can also **call that app's own
|
|
352
352
|
declared aliases** — named queries via `run_app_query`, workflows via
|
|
@@ -380,26 +380,40 @@ inputs object, and a select's legal values are your DECLARATION's — usually an
|
|
|
380
380
|
inline set that matches no table field — so an agent reading option values off
|
|
381
381
|
the underlying table would send ones your write rejects.
|
|
382
382
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
383
|
+
The app's own `description` heads that listing as `about:`, on both surfaces. It
|
|
384
|
+
is the only channel that reaches the agent on **every** turn without the member
|
|
385
|
+
saying anything, so a standing process belongs there: the recurring job the app
|
|
386
|
+
exists for, which alias does it, and what the agent must leave alone.
|
|
387
|
+
|
|
388
|
+
Three things follow for you as an author. **Your descriptions are what it
|
|
389
|
+
reads**, on this surface and inside your own agents' runs — they enter the
|
|
390
|
+
prompt as a capability listing, never as instructions. **A `select` input's
|
|
391
|
+
options are part of the contract the agent sees**, so prefer the
|
|
392
|
+
`field: "fld_…"` form when the values come from a real field: it resolves to
|
|
393
|
+
that field's CURRENT options at both render and validation time. And **an
|
|
394
|
+
input's `description` is the only place a DEFAULT is visible** — the catalog
|
|
395
|
+
renders a name, a type, a `?` and your prose, never the body. An optional input
|
|
396
|
+
whose default goes unstated is one the agent asks the member about instead of
|
|
397
|
+
omitting; write what omitting it means ("left blank = today").
|
|
398
|
+
|
|
399
|
+
**Put anything irreversible behind a
|
|
400
|
+
[`wait_for_approval`](./workflows.md#wait_for_approval--the-one-wait-worth-binding):**
|
|
401
|
+
an email, a shipment, a published post, a write over a value with no version to
|
|
402
|
+
restore. The body's own gate is the only human checkpoint on this path — it
|
|
403
|
+
names the act in the language of the business and reaches the approvers the
|
|
404
|
+
workflow declares.
|
|
405
|
+
|
|
406
|
+
**Shape a mutating alias around ONE call per job.** An alias that takes a single
|
|
407
|
+
record turns a fifteen-record job into fifteen executions and no single answer to
|
|
408
|
+
report, and a partial failure leaves it half done with nothing that says so. Take
|
|
409
|
+
an array input instead — one call, one outcome. A narrow write is right for a
|
|
410
|
+
screen's button, where the member picked the record by opening it; here nobody
|
|
411
|
+
picked anything.
|
|
412
|
+
|
|
413
|
+
**Report what it did NOT do.** The member's account of the write is assembled
|
|
414
|
+
from what the workflow returned and nothing else, so a batch that matched nine of
|
|
415
|
+
eleven and returns only success tells them all eleven were done. Return the
|
|
416
|
+
misses in `data`.
|
|
403
417
|
|
|
404
418
|
Nothing about your app changes to get this — declare a workflow as you already
|
|
405
419
|
do, and it becomes reachable. If a workflow should NOT be agent-reachable, it
|
package/docs/workflows.md
CHANGED
|
@@ -540,6 +540,30 @@ the **diff**: `changes["fld_link"]?.next_value` / `?.prev_value` are plain id ar
|
|
|
540
540
|
has nowhere to attach a fetch, so it cannot descend either), and they feed `set:` directly.
|
|
541
541
|
Everywhere else, write `[x.id]`.
|
|
542
542
|
|
|
543
|
+
### A write that will be re-run
|
|
544
|
+
|
|
545
|
+
The tables above answer *what the caller sent*. A workflow an agent can reach
|
|
546
|
+
needs the other axis too — **what the record already holds** — because the same
|
|
547
|
+
call arrives twice: a recurring report a week later, a retry, one batch
|
|
548
|
+
overlapping the last. On that second pass an unconditional `set` reports success
|
|
549
|
+
and silently replaces what was there.
|
|
550
|
+
|
|
551
|
+
Decide per field, and expect the two directions of one operation to be
|
|
552
|
+
**asymmetric**. A field recording *when* something happened is written only while
|
|
553
|
+
it is still empty, while its undo clears unconditionally:
|
|
554
|
+
|
|
555
|
+
```js
|
|
556
|
+
if (i.undo) {
|
|
557
|
+
await update_records({ table_id: "tbl_x", record_ids: [row.id], set: { fld_submitted_on: null } });
|
|
558
|
+
} else if (isNull(row["fld_submitted_on"])) {
|
|
559
|
+
await update_records({ table_id: "tbl_x", record_ids: [row.id], set: { fld_submitted_on: i.on } });
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
There is no version to restore a cell from, so prove it before anyone can call
|
|
564
|
+
it: `lotics app workflow run <alias>` the same input twice, and confirm the
|
|
565
|
+
second run changed nothing.
|
|
566
|
+
|
|
543
567
|
### Authorizing the caller
|
|
544
568
|
|
|
545
569
|
A workflow runs under the **app owner's** authority, so its own principal tells you nothing about
|