@colixsystems/widget-sdk 0.244.0 → 0.245.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/README.md CHANGED
@@ -55,7 +55,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
55
55
  | **DATASTORE** | `useDatastoreSchema(tableId)` | `{ schema, loading, error, refetch }` | `schema(tableId)` — `datastore.read:<table>` |
56
56
  | **DATASTORE** | `useBoundColumns(tableId, shape, props)` | `{ columns, resolved, missing, loading, error }` | `schema(tableId)` (built on `useDatastoreSchema`) — `datastore.read:<table>`. Resolves author-bound column NAMES from `props` by exact name → case-insensitive name → first unclaimed column matching `shape[key].dataType`, so a column an author renamed after install still resolves instead of `record[props.titleField]` reading `undefined`. `columns` holds the resolved NAME (`record[columns.titleField]`); `resolved` holds the full `Column`; `missing` lists non-`optional` keys that never resolved. Falsy `tableId` collapses to `{ columns: {}, resolved: {}, missing: Object.keys(shape), loading: false, error: null }`. |
57
57
  | **DATASTORE** | `useInterpretDraft(tableId)` | `{ interpret, interpreting, error, result, available }` | `interpret(tableId, body)` — `datastore.read:<table>`. Turns ONE sentence a user typed ("walk at 11 am tomorrow") into DRAFT column values so a form can prefill itself. IMPERATIVE: call `interpret(text, { fields?, timeZone? })` from an event handler, never on mount. It DRAFTS and writes nothing — show the values for review, then submit through `useDatastoreMutation().create`. Resolves to `{ values, unresolved }`; `values` is keyed by column NAME (the shape `create()` takes) and `unresolved` names the fields the sentence did not state. Only text / number / boolean / date / datetime / array columns are drafted — `FILE`, `RELATION`, `USER` and `USER_GROUP` carry ids and are never guessed. Fails closed to an empty draft. **Every call spends the workspace's AI credits** and is rate-limited per actor, so call it once per user action (never on mount or in a render loop); once the workspace runs out the call is refused with a generic 429 — an app user is deliberately **not** told the workspace's billing state, since they have never heard of an AI credit and cannot buy one. Never surface a raw error to the person filling the form: say drafting is unavailable and keep every field editable by hand. `available` is false where the host brokers no interpreter. |
58
- | **AGENTS** | `useAgent(agentRef)` | `{ send, cancel, reset, messages, sending, resuming, error, available }` | `ctx.agents` — `ai.invoke:*`. Hold a conversation with one of the WORKSPACE's own AI agents (an author-written name + system prompt). Sending is IMPERATIVE: call `send(text)` from an event handler, never on mount — the first send opens the conversation, so a widget nobody types into costs nothing. On mount the hook DOES reopen the caller's most recent thread with this agent, so a refresh or a navigation keeps the conversation instead of starting a blank one; that resume only READS and spends no credit. `resuming` is true while it runs — render a loading state, not an empty thread. `messages` is the running transcript, oldest first. You **cannot** set or read the system prompt, replay an edited transcript, or pick a model: the server assembles every turn from the stored prompt plus the stored history, and `content` is the only thing that reaches the wire. `reset()` drops the thread (a "new chat" affordance), and the new thread is the one a later mount resumes. **Every send spends the workspace's AI credits** and is rate-limited per caller AND per workspace. Rejects with `AgentError`; branch on `.code` — `AI_QUOTA_EXCEEDED` (429) is the workspace's credit ceiling and will NOT clear on a retry, unlike `RATE_LIMITED`, so never offer "try again" for it, and `AGENT_DISABLED` (409) means the author turned it off, so hide the affordance. Never surface a raw error or the workspace's billing state to an app user. `available` is false when no agent is bound or the host brokers none — render a visible unbound state, never a blank box. An assistant message that used a tool also carries **`activity`** — what the agent DID with the user's data: the table it read, or the table and columns it CHANGED (never the values, and never a row the permissions refused). **Render a write.** A reply saying "done!" is not evidence a row was written, and if the agent got it wrong the user has no other way to notice. A talk-only turn omits the key entirely. |
58
+ | **AGENTS** | `useAgent(agentRef)` | `{ send, cancel, reset, messages, sending, resuming, error, available }` | `ctx.agents` — `ai.invoke:*`. Hold a conversation with one of the WORKSPACE's own AI agents (an author-written name + system prompt). Sending is IMPERATIVE: call `send(text)` from an event handler, never on mount — the first send opens the conversation, so a widget nobody types into costs nothing. On mount the hook DOES reopen the caller's most recent thread with this agent, so a refresh or a navigation keeps the conversation instead of starting a blank one; that resume only READS and spends no credit. `resuming` is true while it runs — render a loading state, not an empty thread. `messages` is the running transcript, oldest first. You **cannot** set or read the system prompt, replay an edited transcript, or pick a model: the server assembles every turn from the stored prompt plus the stored history, and `content` is the only thing that reaches the wire. `reset()` drops the thread (a "new chat" affordance), and the new thread is the one a later mount resumes. **Every send spends the workspace's AI credits** and is rate-limited per caller AND per workspace. Rejects with `AgentError`; branch on `.code` — `AI_QUOTA_EXCEEDED` (429) is the workspace's credit ceiling and will NOT clear on a retry, unlike `RATE_LIMITED`, so never offer "try again" for it, and `AGENT_DISABLED` (409) means the author turned it off, so hide the affordance. Never surface a raw error or the workspace's billing state to an app user. `available` is false when no agent is bound or the host brokers none — render a visible unbound state, never a blank box. An assistant message that used a tool also carries **`activity`** — what the agent DID with the user's data: the table it read (with the `search` text and `filters` it read with), or the table and columns it CHANGED (never the values, and never a row the permissions refused). **Render a write.** A reply saying "done!" is not evidence a row was written, and if the agent got it wrong the user has no other way to notice. A talk-only turn omits the key entirely. |
59
59
  | **DATASTORE** | `useDatastoreMutation(table)` | `{ create, update, delete }` | `records(table).{ create, update (PATCH), delete }` — `datastore.write:*`. **To CLEAR a cell, pass it empty** — `update(id, { when: null })` (also `""` or `[]`) empties that cell and it reads back as `null` (`[]` on an array column). There is no separate clear call, so never write a sentinel such as `0` or a placeholder date to mean "no value". `false` and `0` are values, not clears, and a REQUIRED column cannot be cleared. |
60
60
  | **DATASTORE** | `useDatastoreSubscription(table, handlers, options)` (optional: options) | `{ status }` — `"connecting" \| "live" \| "reconnecting" \| "fallback"` | `records(table).subscribe` — `datastore.read:<table>`. Live `onCreated` / `onUpdated` / `onDeleted` off the REQ-RT-07 socket; never throws, resolving to `{ status: "fallback" }` so the widget polls instead. A whole-table subscribe is gated on read-EVERY-row, because one envelope reaches every subscriber of the table — so for a table governed by per-record grants pass `options.scope`: `{ kind: "record", record_id }` for one row, or `{ kind: "parent", relation_column, record_id }` for the rows whose RELATION column points at that parent (the column must carry `inheritAcl`, else the subscribe reports `"fallback"`). For a live list of the signed-in user's own rows pass `{ kind: "granted" }`: every row they read through a grant to them or one of their groups, plus `onAccessGained(record)` / `onAccessLost({ id })` as rows enter and leave that set. Re-subscribes on the scope's VALUES, so a fresh object literal each render is fine. |
61
61
  | **DATASTORE** | `useTyping(tableId, recordId)` | `{ typingUserIds, setTyping, status }` — status `"connecting" \| "live" \| "unavailable"` | `records(table).typing` — `datastore.read:<table>`. Who else is typing in one record, over the same socket as `useDatastoreSubscription`. Call `setTyping(true)` on every keystroke (throttled to one signal per 3 s; `false` is sent after 5 s idle) and `setTyping(false)` on send, clear or blur. `typingUserIds` lists the OTHER users' ids; each drops out 6 s after their last signal. Needs a signed-in user who can read the record. Never throws: an anonymous visitor, a refused subscription or a host whose client lacks `typing` reads `{ typingUserIds: [], status: "unavailable" }`. Re-subscribes when either id changes. |
@@ -84,7 +84,11 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
84
84
 
85
85
  ## Status
86
86
 
87
- `v0.244.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
87
+ `v0.245.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
88
+
89
+ ### What's new in 0.245.0 (contract 1.199.0)
90
+
91
+ **An agent's activity says what it searched for.** A read entry in an assistant `AgentMessage`'s `activity` may now carry `search` (the text the agent searched every text column for) and `filters` (`{ column, operator, value }` conditions it read with). These are the agent's own arguments, never the rows that came back. When an agent misses an answer, this shows whether it searched for the wrong words or in the wrong column. Additive: a read with neither omits both keys, so existing renderers are unchanged.
88
92
 
89
93
  ### What's new in 0.244.0 (contract 1.198.0)
90
94
 
package/dist/contract.cjs CHANGED
@@ -2064,8 +2064,8 @@ const HOOKS = [
2064
2064
  "`messages` is the running transcript, oldest first, each " +
2065
2065
  "{ id, role, content, created_at } with role 'user' | 'assistant'. " +
2066
2066
  "An assistant message that used a tool also carries `activity`: what " +
2067
- "the agent DID — the table it read, or the table and columns it " +
2068
- "CHANGED. Render a write: a reply saying 'done!' is not evidence a " +
2067
+ "the agent DID — the table it read and the search text and filters it " +
2068
+ "read with, or the table and columns it CHANGED. Render a write: a reply saying 'done!' is not evidence a " +
2069
2069
  "row was written, and the user has no other way to notice a mistake. " +
2070
2070
  "A talk-only turn omits the key entirely. " +
2071
2071
  "You CANNOT set the system prompt, replay an edited transcript, " +
@@ -6159,7 +6159,9 @@ const CONTRACT = deepFreeze({
6159
6159
  // useFileSignatures is described as the caller's own signatures.
6160
6160
  // 1.198.0: additive (sc-12088) — `useNavigation().scrollIntoView(ref)`
6161
6161
  // scrolls the page to an element inside the calling widget on both hosts.
6162
- version: "1.198.0",
6162
+ // 1.199.0: additive (sc-11952) — an `AgentActivity` read entry may carry
6163
+ // `search` and `filters`: what the agent searched for, never its rows.
6164
+ version: "1.199.0",
6163
6165
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6164
6166
  hooks: HOOKS,
6165
6167
  primitives: PRIMITIVES,
package/dist/contract.js CHANGED
@@ -2064,8 +2064,8 @@ const HOOKS = [
2064
2064
  "`messages` is the running transcript, oldest first, each " +
2065
2065
  "{ id, role, content, created_at } with role 'user' | 'assistant'. " +
2066
2066
  "An assistant message that used a tool also carries `activity`: what " +
2067
- "the agent DID — the table it read, or the table and columns it " +
2068
- "CHANGED. Render a write: a reply saying 'done!' is not evidence a " +
2067
+ "the agent DID — the table it read and the search text and filters it " +
2068
+ "read with, or the table and columns it CHANGED. Render a write: a reply saying 'done!' is not evidence a " +
2069
2069
  "row was written, and the user has no other way to notice a mistake. " +
2070
2070
  "A talk-only turn omits the key entirely. " +
2071
2071
  "You CANNOT set the system prompt, replay an edited transcript, " +
@@ -6155,7 +6155,9 @@ const CONTRACT = deepFreeze({
6155
6155
  // useFileSignatures is described as the caller's own signatures.
6156
6156
  // 1.198.0: additive (sc-12088) — `useNavigation().scrollIntoView(ref)`
6157
6157
  // scrolls the page to an element inside the calling widget on both hosts.
6158
- version: "1.198.0",
6158
+ // 1.199.0: additive (sc-11952) — an `AgentActivity` read entry may carry
6159
+ // `search` and `filters`: what the agent searched for, never its rows.
6160
+ version: "1.199.0",
6159
6161
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6160
6162
  hooks: HOOKS,
6161
6163
  primitives: PRIMITIVES,
package/dist/index.d.ts CHANGED
@@ -1790,6 +1790,17 @@ export interface AgentActivity {
1790
1790
  table?: string;
1791
1791
  record_id?: string;
1792
1792
  fields?: string[];
1793
+ /** A read's own search text: what the agent looked FOR, never what it found. */
1794
+ search?: string;
1795
+ /** A read's own filter conditions, as the agent asked for them. */
1796
+ filters?: AgentActivityFilter[];
1797
+ }
1798
+
1799
+ /** One condition a read filtered by; `operator` is eq, neq, gt, gte, lt, lte or contains. */
1800
+ export interface AgentActivityFilter {
1801
+ column: string;
1802
+ operator: string;
1803
+ value: string;
1793
1804
  }
1794
1805
 
1795
1806
  export interface AgentApi {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.244.0",
3
+ "version": "0.245.0",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
5
  "homepage": "https://github.com/Colix-AB/AppStudio",
6
6
  "type": "module",
package/src/contract.js CHANGED
@@ -2064,8 +2064,8 @@ const HOOKS = [
2064
2064
  "`messages` is the running transcript, oldest first, each " +
2065
2065
  "{ id, role, content, created_at } with role 'user' | 'assistant'. " +
2066
2066
  "An assistant message that used a tool also carries `activity`: what " +
2067
- "the agent DID — the table it read, or the table and columns it " +
2068
- "CHANGED. Render a write: a reply saying 'done!' is not evidence a " +
2067
+ "the agent DID — the table it read and the search text and filters it " +
2068
+ "read with, or the table and columns it CHANGED. Render a write: a reply saying 'done!' is not evidence a " +
2069
2069
  "row was written, and the user has no other way to notice a mistake. " +
2070
2070
  "A talk-only turn omits the key entirely. " +
2071
2071
  "You CANNOT set the system prompt, replay an edited transcript, " +
@@ -6155,7 +6155,9 @@ const CONTRACT = deepFreeze({
6155
6155
  // useFileSignatures is described as the caller's own signatures.
6156
6156
  // 1.198.0: additive (sc-12088) — `useNavigation().scrollIntoView(ref)`
6157
6157
  // scrolls the page to an element inside the calling widget on both hosts.
6158
- version: "1.198.0",
6158
+ // 1.199.0: additive (sc-11952) — an `AgentActivity` read entry may carry
6159
+ // `search` and `filters`: what the agent searched for, never its rows.
6160
+ version: "1.199.0",
6159
6161
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6160
6162
  hooks: HOOKS,
6161
6163
  primitives: PRIMITIVES,