@lotics/cli 0.305.0 → 0.305.1

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.
@@ -119,7 +119,7 @@ export interface ToolExecuteResult {
119
119
  * failure was not a typed error, and from any server older than it. */
120
120
  error_code?: string;
121
121
  /** A refused input addressed per field: one sentence from a typed alias, a
122
- * list from a rejecting table workflow. */
122
+ * list from a refusing table check. */
123
123
  field_errors?: Record<string, string> | Record<string, string[]>;
124
124
  breaking_changes?: readonly ToolBreakingChange[];
125
125
  }
@@ -94,8 +94,7 @@ it — and a date column per state holds until something re-enters a state it al
94
94
 
95
95
  Measuring time-in-state or conversion needs one ROW per move: a link to the subject, the state left,
96
96
  the state entered, when. Hold those states as the source field's own `opt_` keys so the log carries
97
- no second vocabulary, and write the rows from that table's own lifecycle workflows, which covers
98
- every writer rather than one app's.
97
+ no second vocabulary, and write the row in the workflow that makes the move.
99
98
 
100
99
  ## Keep derived chains shallow
101
100
 
package/docs/workflows.md CHANGED
@@ -6,16 +6,13 @@ Only the forms below are accepted; anything else is refused at save with the lin
6
6
  what to write instead. A save that succeeds may still return `warnings` — advisory hints such as a
7
7
  loop that may never end. Read them.
8
8
 
9
- The same grammar serves an automation, a table's lifecycle workflow and an app's workflow, and the
10
- expressions of a table check. Only an automation's source opens with a trigger declaration; a
11
- lifecycle workflow takes its trigger from its table and event, an app workflow from the app that calls
12
- it.
9
+ The same grammar serves an automation and an app's workflow, and the expressions of a table check.
10
+ Only an automation's source opens with a trigger declaration; an app workflow takes its trigger from
11
+ the app that calls it.
13
12
 
14
13
  ## Triggers
15
14
 
16
- An automation answers an event outside the records: a schedule, a webhook, an inbound email. A
17
- reaction to a record being created, updated or deleted is a table lifecycle workflow (see
18
- **Table lifecycle workflows**).
15
+ An automation answers an event outside the records: a schedule, a webhook, an inbound email.
19
16
 
20
17
  ```
21
18
  on({ type: "recurring_schedule", cron_expression: "0 9 * * MON" });
@@ -70,8 +67,8 @@ connections and the steps each opens.
70
67
 
71
68
  `return` is a call, not a JavaScript `return` statement. `validate` ends the workflow with an error
72
69
  when a check's `fail_when` is truthy; the check's `field_key` names the control its message lands on —
73
- in an app workflow a declared input, or the `fld_` key of a field on a table the body names; a field of
74
- the trigger table in a lifecycle workflow; in an automation it is not checked.
70
+ in an app workflow a declared input, or the `fld_` key of a field on a table the body names; in an
71
+ automation it is not checked.
75
72
 
76
73
  The name in `const x = await tool({...})` is the step's id; later expressions read its output as
77
74
  `x.records`, `x.id` and so on. A `// id: my_step` comment directly above a statement sets an explicit
@@ -95,8 +92,8 @@ Bind it to branch on the decision:
95
92
 
96
93
  ```
97
94
  const approval = await wait_for_approval({
98
- approvers: record["fld_approvers"],
99
- prompt: `Approve the quote for ${record["fld_name"]}?`,
95
+ approvers: trigger.app_workflow.inputs.approvers,
96
+ prompt: `Approve this quote?`,
100
97
  });
101
98
 
102
99
  if (approval.status == "approved") {
@@ -120,7 +117,7 @@ step — summarize a record, classify, draft text.
120
117
 
121
118
  - `instructions` — a fixed string.
122
119
  - `input` — an object literal; its fields are expressions, evaluated and handed to the model (`{}` for
123
- none). It is the ONLY workflow data the agent sees: read `record`, prior steps, `runtime` or the loop
120
+ none). It is the ONLY workflow data the agent sees: read the trigger, prior steps, `runtime` or the loop
124
121
  item here, never inside `instructions` or `tools`, which are fixed literals and read no binding.
125
122
  - `tools` — the names of the tools it may call.
126
123
  - `model` — optional; omit it to follow the platform's default model.
@@ -141,13 +138,9 @@ Only `x` comes back: in text mode `x` is the string itself, in object mode `x.fi
141
138
 
142
139
  ## What an expression reads
143
140
 
144
- - `record` — the trigger record (on an update, the record as it now stands).
145
- - `prev_record` — the record before the change, same shape (updates and deletes only).
146
- - `changes` — the fields an update changed, a PARTIAL map: `changes["fld_x"]?.next_value` and
147
- `?.prev_value`, the `?.` required (updates only).
148
- - `trigger` — what a non-record trigger carries (see **Triggers**). `trigger.data`,
149
- `trigger.prev_data` and `trigger.changes` are the same three roots spelled long; a saved body reads
150
- back in the short form.
141
+ - `record`, `prev_record`, `changes` — in a table check, the write it checks (see **Table checks**).
142
+ `changes` is a PARTIAL map: `changes["fld_x"]?.next_value` and `?.prev_value`, the `?.` required.
143
+ - `trigger` — what the trigger carries (see **Triggers**).
151
144
  - a loop's item name, and `index`, the iteration's 0-based index.
152
145
  - `<step id>` — an earlier step's output (`dup.records`).
153
146
  - `runtime.timezone`, `runtime.workflow_id`, `runtime.execution_id`, `runtime.workspace_id`,
@@ -277,8 +270,9 @@ const found = await query_records({
277
270
  filters: { node_type: "condition", field_key: "fld_name", operator: "equals", value: "ACME Corp" },
278
271
  });
279
272
  const customer = first(found.records);
273
+ const order = await get_record({ table_id: "tbl_orders", record_id: trigger.app_workflow.inputs.order_id });
280
274
 
281
- if (customer && includes(record["fld_customer"], customer.id)) {
275
+ if (customer && includes(order.data["fld_customer"], customer.id)) {
282
276
  // …
283
277
  }
284
278
  ```
@@ -323,41 +317,6 @@ for (let start = 0; start < sheet.row_count; start += 50) {
323
317
  }
324
318
  ```
325
319
 
326
- ## Table lifecycle workflows
327
-
328
- A lifecycle workflow is bound to one table and one event, and its source has no `on({...})` line:
329
-
330
- - `after_create` — a create, and a draft's submit (the moment it becomes a record).
331
- - `after_update` — every field edit, a draft's included.
332
- - `after_delete`.
333
-
334
- A lifecycle workflow runs after the write commits, with every step; its errors are logged and never
335
- fail the write. What refuses a write before it lands is a table check (see **Table
336
- checks**). An `after_update` workflow that writes its own table saves with a `loop_potential`
337
- warning: its write fires it again.
338
-
339
- | reads | on |
340
- |---|---|
341
- | `record` | every event — the new record on create, the record as it now stands on update, the deleted record on delete |
342
- | `prev_record` | updates and deletes — the record before |
343
- | `changes["fld_x"]?.next_value` / `?.prev_value` | updates — the fields that changed |
344
-
345
- ### Gating with `if_source`
346
-
347
- `if_source` is one JavaScript expression, reading what the body reads, tested before the workflow
348
- runs. When it is falsy the workflow does not run at all — no execution, no log. Use it so a workflow
349
- that cares about some events only skips the rest:
350
-
351
- - a status reaching an option: `changes["fld_status"]?.next_value == "opt_done"`
352
- - a field cleared: `isNull(record["fld_assignee"])`
353
- - a direct write, not a workflow's cascade:
354
- `includes(["member", "chat_agent", "api_client"], runtime.change_origin.type)` — name every
355
- direct-write origin rather than testing for one: `member` is a person in the browser, and the same
356
- edit over MCP, the CLI or the API arrives as `api_client`.
357
-
358
- In `if_source`, `runtime.change_origin` is the origin of the write that fired it; in the body, it is
359
- the workflow's own.
360
-
361
320
  ## Table checks
362
321
 
363
322
  A table check is one condition its table's record writes must not meet, declared beside the table's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.305.0",
3
+ "version": "0.305.1",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {