@lotics/cli 0.313.5 → 0.315.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.
@@ -263,9 +263,13 @@ its alias in `queries`, it is the model's. A caller sees the description and
263
263
  each input's `description` and nothing else, so each says what the value is and what leaving it out means;
264
264
  `check_model` notes one that says nothing.
265
265
 
266
- **Shape each for a caller that is not looking at a screen.** Take a list where one job covers many records.
267
- Make a write survive running twice. Return the ids of what was written under `outputs`, so the next run can
268
- read them. Gate what cannot be undone behind an approval step.
266
+ **Shape each for a caller that is not looking at a screen.** One job a person hands an agent — a paper read,
267
+ a message answered, a sheet brought in — is one declared workflow named for the job, never a chain of the
268
+ generated per-row workflows. Take a list where the job covers many rows, a record's lines among them. A body
269
+ that writes more than one row makes them in one `save_record` workflow step, all or nothing, and recognises
270
+ what exists by the identity the input itself carries, declared unique on its table, so the same input sent
271
+ twice updates instead of adding. Return the ids under `outputs`, so the next run can read them. Gate what
272
+ cannot be undone behind an approval step.
269
273
 
270
274
  ## Prove an app as an agent
271
275
 
@@ -281,3 +285,5 @@ Run on a throwaway record.
281
285
  6. `run_app_workflow` `record_activity` for the record, then `record_activity_undo` with the entry the act
282
286
  wrote: each field the act changed reads as it did before step 4, but one written since, which `kept`
283
287
  names. A row the act added under the record and a file it made stay.
288
+ 7. `run_app_workflow` the app's job workflow with the input a person would hand it — the paper read, the row:
289
+ it completes in one call. Call it again with the same input: nothing is added.
@@ -3246,6 +3246,38 @@ above. A provider's refusal carries the provider's own words.
3246
3246
  | `mime_type` | string | yes | |
3247
3247
  | `url` | string | yes | |
3248
3248
 
3249
+ ### save_record (workflow step)
3250
+
3251
+ - Save a record found by its identity, with its lines, all or nothing.
3252
+ - A call whose match is carried by no live record adds it; one carried by a record changes the cells of set that differ and appends add_to, so sending the same call twice writes nothing the second time. match holds the record's identity: field keys with their values, exactly the fields of one uniqueness its table declares (a unique field, or fields unique together). set holds its other cells. add_to lists items to append to a files, multi-select or many-link field, keeping what it holds. lines saves rows of other tables that link the record.
3253
+ - Each entry names the child table_id, its one-row link field to this table, match (the child fields that with link are one uniqueness of the child table) and rows, each stating every match field. link is set by the step and no row states it.
3254
+ - A line found by its match changes the cells that differ; one not found is added; a line not listed is left as it is.
3255
+ - Any refusal — a table check, a locked record, a value a field does not take, no write access to a table — saves nothing.
3256
+ - Returns { record_id, created, updated_field_keys, lines }: the cells changed on a record that existed, and per entry of lines the ids it added, changed and left unchanged.
3257
+
3258
+ **Inputs**
3259
+
3260
+ | Name | Type | Required | What it is |
3261
+ | --- | --- | --- | --- |
3262
+ | `table_id` | string | yes | |
3263
+ | `match` | object | yes | |
3264
+ | `set` | object \\| null | | |
3265
+ | `add_to` | object \\| null | | |
3266
+ | `lines` | object[] \\| null | | |
3267
+
3268
+ **Answer**
3269
+
3270
+ | Name | Type | Required | What it is |
3271
+ | --- | --- | --- | --- |
3272
+ | `record_id` | string | yes | |
3273
+ | `created` | boolean | yes | |
3274
+ | `updated_field_keys` | string[] | yes | |
3275
+ | `lines` | object[] | yes | |
3276
+ | `lines[].table_id` | string | yes | |
3277
+ | `lines[].created_ids` | string[] | yes | |
3278
+ | `lines[].updated_ids` | string[] | yes | |
3279
+ | `lines[].unchanged_ids` | string[] | yes | |
3280
+
3249
3281
  ## send
3250
3282
 
3251
3283
  ### send_notification (workflow step)
package/docs/workflows.md CHANGED
@@ -571,6 +571,61 @@ return({
571
571
  });
572
572
  ```
573
573
 
574
+ ### Saving a record with its lines
575
+
576
+ `save_record`, a workflow step, saves one record and the rows of other tables that hang off it, all or
577
+ nothing, and recognises what exists by the identity the input carries — so a call sent twice updates and
578
+ adds nothing. `match` is the record's identity, `{ field_key: value }`, and must be exactly a uniqueness
579
+ its table declares: one field marked `unique`, or the fields of one of the table's `unique` sets together
580
+ (text, number, date, single select and one-row link fields). A `match` that is no declared uniqueness is
581
+ refused, naming what the table does declare. A live record carrying the match is found and only the cells
582
+ of `set` that differ are written; none is found and it is added with `match` and `set`. `add_to` appends
583
+ to a files, multi-select or many-link field and keeps what it holds.
584
+
585
+ `lines` saves rows of other tables that link the record. Each entry names the child `table_id`, its
586
+ `link` — a field of the child holding one row of this table, which the step fills and no row states —
587
+ the child fields that, with `link`, are one uniqueness of the child table (`match`), and `rows`, each
588
+ stating every `match` field. A line found by its match changes only the cells that differ, one not found
589
+ is added, and a line the call does not list is left as it is: the step deletes nothing. Two rows of one
590
+ entry cannot share a match.
591
+
592
+ Everything is checked before the first write, and everything is written in one transaction: a table check
593
+ that refuses one line, a locked record or line the call would change, a value a field does not take, or a
594
+ table the run may not write refuses the call and saves nothing, naming the row. Two calls of one identity
595
+ take turns, so the second finds what the first saved. The answer is `record_id`, `created`,
596
+ `updated_field_keys` (the cells changed on a record that existed) and, per entry of `lines`, the ids it
597
+ added, changed and left unchanged.
598
+
599
+ ```
600
+ const inputs = trigger.app_workflow.inputs;
601
+
602
+ const sheet = await excel_get_rows({ // a workflow step
603
+ file_id: inputs.release_order,
604
+ columns: ["Size", "Quantity"],
605
+ });
606
+
607
+ const saved = await save_record({ // a workflow step
608
+ table_id: "tbl_release_orders",
609
+ match: { fld_number: inputs.number },
610
+ set: { fld_note: "Read from the paper" },
611
+ lines: [{
612
+ table_id: "tbl_release_lines",
613
+ link: "fld_order",
614
+ match: ["fld_size"],
615
+ rows: sheet.rows.map((row) => ({
616
+ fld_size: toString(row["Size"]),
617
+ fld_quantity: toNumber(row["Quantity"]),
618
+ })),
619
+ }],
620
+ });
621
+
622
+ return({
623
+ status: "success",
624
+ message: `${size(saved.lines[0].created_ids)} lines added`,
625
+ data: { record_id: saved.record_id },
626
+ });
627
+ ```
628
+
574
629
  ## Table checks
575
630
 
576
631
  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.313.5",
3
+ "version": "0.315.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {