anbaric 1.56.8 → 1.57.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/docs/api/cli.md CHANGED
@@ -46,7 +46,7 @@ anbaric app status link-media-brief # a different app, from anywhere
46
46
  | --- | --- |
47
47
  | `anbaric state-machines` | List registered state machines. |
48
48
  | `anbaric jobs create <sm-id> <start-state> [k=v …]` | Create a job and queue it for processing. |
49
- | `anbaric jobs list [state-machine-id]` | List jobs, optionally filtered by workflow. |
49
+ | `anbaric jobs list [state-machine-id]` | List jobs, newest first, a page at a time (100 by default — `--page 1` for the next). Narrow with `--state`, `--status`, `--app`; `--oldest` flips the order. Filters apply on the platform, across every job, before paging. |
50
50
  | `anbaric jobs stats` | Job counts per state and the queue size. |
51
51
  | `anbaric jobs watch <job-id>` | Follow a job's state and property changes live. |
52
52
  | `anbaric jobs set-state <job-id> <state>` | Move a job to a state and re-queue it. |
@@ -67,6 +67,9 @@ ask for, so the CLI runs unattended in scripts and CI.
67
67
  | `--name <name>` | `app configure`/`deploy`/`update` | App name; prompts if omitted (suggested from `package.json`). |
68
68
  | `--port <port>` | `app configure`/`deploy`/`update` | Internal port (1–65535); prompts if omitted. |
69
69
  | `--yes` | `app deploy`, `app tear-down`, `jobs kill-old` | Skip confirmation. (`app update` implies it.) |
70
+ | `--state <state>`, `--status <status>`, `--app <app>` | `jobs list` | Only jobs in that state / with that status (`active`, `Awaiting input`, `Failed`) / belonging to that app. |
71
+ | `--page <n>`, `--page-size <n>` | `jobs list` | Which page (from 0) and how many per page (default 100). |
72
+ | `--oldest` | `jobs list` | Oldest first instead of newest. |
70
73
  | `--help`, `-h` | all | Print usage. |
71
74
 
72
75
  ## Configuration and storage
@@ -136,20 +136,46 @@ actor : Actor
136
136
  constructor(name : string, actor : Actor, description : string = "", id? : string)
137
137
 
138
138
  // Replaceable function fields — assign your own:
139
+ reads : Reads // default: Reads.everything
139
140
  predicate : (job : Job) => boolean // default: () => true
140
141
  run : (job : Job) => Promise<Map<string, any>> // default: async () => new Map()
141
142
  ```
142
143
 
143
- You configure an action by assigning `predicate` and `run`:
144
+ You configure an action by assigning `predicate` and `run`, and `reads` when
145
+ the job holds more than the action needs:
144
146
 
145
147
  ```ts
146
148
  const sendWelcome = new Action("Send welcome email", new Code("welcome"));
149
+ sendWelcome.reads = Reads.only("email", "name");
147
150
  sendWelcome.run = async (job) => new Map([["welcomeSent", true]]);
148
151
  ```
149
152
 
153
+ - **`reads`** — which properties are loaded for the job before `predicate` and
154
+ `run` see it. Given the machine's property definitions, so it can be written
155
+ once and follow the schema. See [`Reads`](#reads).
150
156
  - **`predicate`** — return `false` to skip this action for a given job.
151
157
  - **`run`** — return a `Map` of the properties to change. Only properties in the
152
- machine's schema are applied; others are ignored with a warning.
158
+ machine's schema are applied; others are ignored with a warning. Only the
159
+ properties that actually changed are written back.
160
+
161
+ ### `Reads`
162
+
163
+ ```ts
164
+ type Reads = (definitions : Array<PropertyDefinition>) => Array<string>
165
+
166
+ Reads.everything // every property (the default)
167
+ Reads.nothing // none
168
+ Reads.only("summary", "articles") // a fixed few
169
+ Reads.where(definition => definition.id.startsWith("article")) // whatever matches
170
+ ```
171
+
172
+ A job is loaded with the union of what the current state's actions, awaits and
173
+ transitions read, and nothing else: a property that wasn't declared is simply
174
+ absent from `job.properties`. A step that reads everything loads everything, so
175
+ nothing changes until you declare. Declaring matters for jobs that carry a lot
176
+ — a workflow holding hundreds of article summaries stops moving all of them
177
+ for a step that scores one — and for [agentic actions](../features/ai-agents.md),
178
+ whose prompt carries the loaded properties and nothing more.
153
179
 
154
180
  See [Actions and actors](../features/actions-and-actors.md).
155
181
 
@@ -172,6 +198,7 @@ waitingFor? : AwaitParty
172
198
  fields : Array<string> = []
173
199
  resolveUrl : string | ((job : Job) => string) = ""
174
200
  metadata : (job : Job) => Map<string, any> // default: () => new Map()
201
+ reads : Reads // what resolveUrl and metadata read; default: Reads.everything
175
202
 
176
203
  constructor(name : string, waitingFor? : AwaitParty, description : string = "", id? : string)
177
204
 
@@ -218,18 +245,20 @@ transition whose predicate holds moves the job to `to`.
218
245
  ```ts
219
246
  to : string
220
247
  predicate : (job : Job) => boolean // default: () => true
248
+ reads : Reads // default: everything if guarded, nothing if not
221
249
 
222
- constructor(to : string, predicate? : (job : Job) => boolean)
250
+ constructor(to : string, predicate? : (job : Job) => boolean, reads? : Reads)
223
251
  ```
224
252
 
225
253
  ```ts
226
- new Transition("active", (job) => job.properties.get("welcomeSent") === true)
254
+ new Transition("active", (job) => job.properties.get("welcomeSent") === true, Reads.only("welcomeSent"))
227
255
  new Transition("scoring") // unguarded: the actions run, then the job moves on
228
256
  ```
229
257
 
230
258
  The predicate is optional. Omit it when a state's actions simply run and the job
231
259
  should move on, rather than inventing a sentinel property for the transition to
232
- read. Guard a transition only when the move is conditional.
260
+ read. Guard a transition only when the move is conditional, and say what the
261
+ guard reads so the job is loaded with only that.
233
262
 
234
263
  ---
235
264
 
@@ -268,6 +297,45 @@ A job whose action threw is `FAILED`, with the reason in its audit trail. It
268
297
  stays in its state rather than transitioning, and an update that moves it on
269
298
  returns it to `ACTIVE` — so a failure is recoverable, not terminal.
270
299
 
300
+ ### Reading a job
301
+
302
+ ```ts
303
+ persistence.retrieve(id, actor) : Promise<Job> // every property
304
+ persistence.retrieve(id, actor, ["summary", "score"]) : Promise<Job> // only those
305
+ ```
306
+
307
+ Properties are stored one by one, so a job can be read with only the keys
308
+ wanted — that's what the machine does for every step, from what the step
309
+ [declares it reads](#reads) — and a save writes only the properties that
310
+ changed. A property absent from a partial read is never touched by a save of
311
+ that job, and no save ever removes a property. Over HTTP the same read is
312
+ `GET /api/v2/jobs/<id>?keys=summary,score`, and the properties in a `PUT` body
313
+ are the ones written.
314
+
315
+ ### Listing jobs
316
+
317
+ ```ts
318
+ const persistence = JobPersistenceFactory.instance();
319
+
320
+ persistence.list(actor) : Promise<Array<Job>> // first 100, oldest first
321
+ persistence.list(actor, pageSize, page, query?) : Promise<Array<Job>>
322
+
323
+ type JobPersistence.Query = {
324
+ workflowId? : string,
325
+ appId? : string,
326
+ state? : string,
327
+ status? : string, // "active", "Awaiting input", "Failed"
328
+ killed? : boolean,
329
+ order? : "oldest" | "newest" // by when the job was started; oldest by default
330
+ }
331
+ ```
332
+
333
+ A listing is a **page**, never a cap: `pageSize` jobs (default 100) at `page`
334
+ (from 0), and every job the store holds is reachable by asking for the next
335
+ page until one comes back short. The query narrows the set on the store before
336
+ paging — a filter sees every job, and page numbers count matching jobs only.
337
+ Deployed, the store holds every job on the tenant across all apps and machines.
338
+
271
339
  ---
272
340
 
273
341
  ## `PropertyDefinition`
package/docs/api/web.md CHANGED
@@ -94,10 +94,43 @@ GET /api/v2/whoami → { "id": "user-123", "roles": ["admin"] }
94
94
 
95
95
  Returns the authenticated user for the current request.
96
96
 
97
+ ### Jobs
98
+
99
+ ```
100
+ GET /api/v2/jobs?page=0&pageSize=100&order=newest&workflowId=…&appId=…&state=…&status=…&killed=false
101
+ ```
102
+
103
+ Every job on the tenant, across all apps and state machines, **paged**: the
104
+ response is one page of at most `pageSize` jobs (default 100) at page `page`
105
+ (from 0). The page size is not a cap — keep asking for the next page until one
106
+ comes back shorter than `pageSize`, which is the last. `order` is by when the
107
+ job was started: `oldest` (the default, for compatibility) or `newest`.
108
+
109
+ The other parameters are filters, applied on the platform **before** paging, so
110
+ a filter sees every job and page numbers count matching jobs only. All are
111
+ optional and combine with AND: `workflowId` (the state machine), `appId`,
112
+ `state`, `status` (`active`, `Awaiting input`, `Failed`) and `killed`
113
+ (`true`/`false`).
114
+
115
+ In code the same listing is `JobPersistenceFactory.instance().list(actor,
116
+ pageSize, page, query)`; the CLI's `anbaric jobs list` and the MCP tool
117
+ `anbaric_jobs_list` take the same filters.
118
+
119
+ ```
120
+ GET /api/v2/jobs/<id> every property
121
+ GET /api/v2/jobs/<id>?keys=summary,score only those
122
+ PUT /api/v2/jobs/<id> body: a serialised job
123
+ ```
124
+
125
+ A job's properties are stored one by one. A `GET` with `keys` returns only
126
+ those; the properties in a `PUT` body are **upserted** — written if given,
127
+ left alone if not — so a caller that read a few keys can write a few keys
128
+ without disturbing the rest, and no `PUT` ever removes a property.
129
+
97
130
  ### Other resources
98
131
 
99
132
  Also under `/api/v2`, reached through the cloud clients rather than raw HTTP:
100
- `jobs`, `state-machines`, `queue`, `consumers`, and (when enabled) `documents`,
133
+ `state-machines`, `queue`, `consumers`, and (when enabled) `documents`,
101
134
  `secrets`, `audits`. Prefer the typed clients and factories over calling these by
102
135
  hand.
103
136
 
@@ -32,6 +32,32 @@ Actions in a state run **in order** each time a job is processed. An action that
32
32
  returns an unchanged value doesn't churn the job — the machine only advances (or
33
33
  schedules a re-check) when something actually changes.
34
34
 
35
+ ### Declare what an action reads
36
+
37
+ A job can hold a lot — hundreds of article summaries, say — while a given
38
+ action needs one property of it. Declare that, and only that is loaded:
39
+
40
+ ```ts
41
+ import {Reads} from "anbaric";
42
+
43
+ chargeCard.reads = Reads.only("total", "paid");
44
+ ```
45
+
46
+ `reads` is a function of the machine's property definitions, so a mapping can
47
+ be written once and keep up as the schema grows: `Reads.everything` (the
48
+ default — nothing changes until you declare), `Reads.nothing`,
49
+ `Reads.only(...keys)`, or `Reads.where(definition => …)` to pick by name. A
50
+ transition's guard takes a `reads` too, and an `Await`'s `resolveUrl` and
51
+ `metadata` have one.
52
+
53
+ Before a job is processed in a state, the machine loads the union of what that
54
+ state's actions, awaits and transitions read; a property that wasn't declared
55
+ is simply absent from `job.properties`, so declare honestly. Afterwards it
56
+ writes back only the properties that changed — never the ones it read, and
57
+ never the ones it didn't load. For an [agentic action](ai-agents.md) the
58
+ declaration also decides what reaches the model: the prompt carries the loaded
59
+ properties and nothing more.
60
+
35
61
  ## Actors: who does the work
36
62
 
37
63
  An actor is a small identity object — `type`, `id`, and `roles`. There are four
@@ -45,7 +45,11 @@ constrained to the schema; the result is applied to the job, and the transition
45
45
  advances it. The decision is recorded against `triager`.
46
46
 
47
47
  The job's current properties are appended to the prompt automatically, so the
48
- model sees the data it's reasoning about.
48
+ model sees the data it's reasoning about — the properties the action
49
+ [declares it reads](actions-and-actors.md#declare-what-an-action-reads), which
50
+ is all of them until you say otherwise. On a job that holds a lot, declare
51
+ them: `triage.reads = Reads.only("subject")` keeps a ticket's attachments,
52
+ history and everything else out of every call.
49
53
 
50
54
  ## The output schema
51
55
 
@@ -43,6 +43,12 @@ against your schema before applying them. Every property a job holds must have a
43
43
  `PropertyDefinition`, or the change is rejected. This is what keeps a job's data
44
44
  trustworthy no matter who or what wrote it.
45
45
 
46
+ Properties are stored one by one, and a step is given only the ones it
47
+ [declares it reads](actions-and-actors.md#declare-what-an-action-reads) — by
48
+ default all of them. So a job can carry a great deal of data without every
49
+ step paying for all of it: a step that scores one summary loads one summary,
50
+ and writes back only what it changed.
51
+
46
52
  ## Nothing is hard-wired
47
53
 
48
54
  Persistence, queueing, document/secret/SQL stores — all of them come from
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anbaric",
3
- "version": "1.56.8",
3
+ "version": "1.57.0",
4
4
  "description": "Everything needed to write an Anbaric app: state machines, jobs, document and secret stores, local in-memory implementations and the Anbaric Cloud clients",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,9 +24,9 @@
24
24
  "prepublishOnly": "npm run build"
25
25
  },
26
26
  "dependencies": {
27
- "anbaric-impl-cloud": "^1.56.8",
28
- "anbaric-data-store": "^1.56.8",
29
- "anbaric-state-machine": "^1.56.8",
30
- "anbaric-tsapi": "^1.56.8"
27
+ "anbaric-impl-cloud": "^1.57.0",
28
+ "anbaric-data-store": "^1.57.0",
29
+ "anbaric-state-machine": "^1.57.0",
30
+ "anbaric-tsapi": "^1.57.0"
31
31
  }
32
32
  }