agent-work-layer 0.2.1 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 — 2026-10-10
4
+
5
+ Both packages: `agent-work-layer` 0.3.0 and `agent-work-layer-mcp` 0.3.0.
6
+
7
+ ### Added
8
+
9
+ - `SqliteStore`: a `DeskStore` backed by SQLite through Node's built-in `node:sqlite` (Node 22.13+), with an atomic CONFLICT check (#1).
10
+
11
+ ### Changed
12
+
13
+ - `agent-work-layer-mcp` now depends on `agent-work-layer` ^0.3.0.
14
+
3
15
  ## 0.2.1 — 2026-10-09
4
16
 
5
17
  Documentation only; `agent-work-layer` alone (`agent-work-layer-mcp` stays at 0.2.0).
package/README.md CHANGED
@@ -40,7 +40,7 @@ Node.js 20.11 or later. ESM only. Types included.
40
40
  ```ts
41
41
  import { WorkDesk } from "agent-work-layer";
42
42
 
43
- const desk = new WorkDesk(); // in-memory; use FileStore or your own DeskStore to persist
43
+ const desk = new WorkDesk(); // in-memory; use FileStore, SqliteStore or your own DeskStore to persist
44
44
 
45
45
  // Manager issues a job
46
46
  const { order } = await desk.create(
@@ -194,7 +194,7 @@ Any active state ──cancel──▶ cancelled · deadline or budget excee
194
194
  | Export | Purpose |
195
195
  |---|---|
196
196
  | `WorkDesk` | Runs the lifecycle: `create`, `accept`, `decline`, `start`, `submitReport`, `answer`, `review`, `cancel`, `get`, `list`, `waitForChange` |
197
- | `MemoryStore`, `FileStore`, `DeskStore` | Storage. Implement `DeskStore` (4 methods) to use a database |
197
+ | `MemoryStore`, `FileStore`, `SqliteStore`, `DeskStore` | Storage. Implement `DeskStore` (4 methods) to use a database |
198
198
  | `validateOrder`, `validateReport`, `validateChildOrder`, `hasInvalid` | Spec checks, returning `Finding[]` |
199
199
  | `isWorkOrder`, `isWorkReport` | Type guards |
200
200
  | `toolAllowed`, `patternCovered` | Tool pattern matching |
@@ -211,6 +211,16 @@ Any active state ──cancel──▶ cancelled · deadline or budget excee
211
211
 
212
212
  ### Custom storage
213
213
 
214
+ `MemoryStore` keeps records in memory, `FileStore` writes one JSON file per order, and `SqliteStore` keeps them in a SQLite database using Node's built-in `node:sqlite` (Node 22.13+, no extra dependency):
215
+
216
+ ```ts
217
+ import { WorkDesk, SqliteStore } from "agent-work-layer";
218
+
219
+ const desk = new WorkDesk({ store: new SqliteStore("./awl.db") }); // or ":memory:"
220
+ ```
221
+
222
+ To use another database, implement `DeskStore`:
223
+
214
224
  ```ts
215
225
  import type { DeskStore, OrderRecord } from "agent-work-layer";
216
226
 
package/dist/generated.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/generate.ts from package.json and schemas/*.json. Do not edit.
2
2
  /** This package's version. */
3
- export const VERSION = "0.2.1";
3
+ export const VERSION = "0.3.0";
4
4
  /** The Work Order JSON Schema (schemas/work-order.schema.json). */
5
5
  export const workOrderSchema = {
6
6
  "$schema": "https://json-schema.org/draft/2020-12/schema",
package/dist/index.d.ts CHANGED
@@ -11,6 +11,6 @@ export { STATES, TERMINAL_STATES, ROLE_FOR, isTerminal, nextState, allowedAction
11
11
  export type { Role } from "./lifecycle.ts";
12
12
  export { WorkDesk, summarize, lastSeq } from "./desk.ts";
13
13
  export type { DeskOptions, ListFilter, WaitOptions, SubmitResult } from "./desk.ts";
14
- export { MemoryStore, FileStore } from "./store.ts";
14
+ export { MemoryStore, FileStore, SqliteStore } from "./store.ts";
15
15
  export type { DeskStore } from "./store.ts";
16
16
  export { VERSION } from "./version.ts";
package/dist/index.js CHANGED
@@ -8,5 +8,5 @@ export { AwlError, isAwlError } from "./errors.js";
8
8
  export { validateOrder, validateReport, validateChildOrder, hasInvalid, isWorkOrder, isWorkReport, toolAllowed, patternCovered, workOrderSchema, workReportSchema, } from "./validate.js";
9
9
  export { STATES, TERMINAL_STATES, ROLE_FOR, isTerminal, nextState, allowedActions } from "./lifecycle.js";
10
10
  export { WorkDesk, summarize, lastSeq } from "./desk.js";
11
- export { MemoryStore, FileStore } from "./store.js";
11
+ export { MemoryStore, FileStore, SqliteStore } from "./store.js";
12
12
  export { VERSION } from "./version.js";
package/dist/store.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { OrderRecord } from "./types.ts";
2
+ import type { DatabaseSync } from "node:sqlite";
2
3
  export interface DeskStore {
3
4
  /** Returns the record, or null when there is none. */
4
5
  read(id: string): Promise<OrderRecord | null>;
@@ -33,3 +34,18 @@ export declare class FileStore implements DeskStore {
33
34
  update(record: OrderRecord, expectedSeq: number): Promise<void>;
34
35
  list(): Promise<OrderRecord[]>;
35
36
  }
37
+ /**
38
+ * One SQLite database, using Node's built-in `node:sqlite` (Node 22.13+). Pass a file path,
39
+ * or ":memory:" for a throwaway database. Several processes may share a file; the CONFLICT
40
+ * check is atomic (`UPDATE ... WHERE last_seq = ?`).
41
+ */
42
+ export declare class SqliteStore implements DeskStore {
43
+ readonly db: DatabaseSync;
44
+ constructor(path?: string);
45
+ read(id: string): Promise<OrderRecord | null>;
46
+ insert(record: OrderRecord): Promise<void>;
47
+ update(record: OrderRecord, expectedSeq: number): Promise<void>;
48
+ list(): Promise<OrderRecord[]>;
49
+ /** Closes the database. */
50
+ close(): void;
51
+ }
package/dist/store.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // Storage for desks. Implement DeskStore to back a desk with a database.
2
2
  import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
3
4
  import { join } from "node:path";
4
5
  import { AwlError } from "./errors.js";
5
6
  const lastSeq = (r) => r.events.at(-1)?.seq ?? 0;
@@ -82,3 +83,53 @@ export class FileStore {
82
83
  return records.filter((r) => r !== null);
83
84
  }
84
85
  }
86
+ /**
87
+ * One SQLite database, using Node's built-in `node:sqlite` (Node 22.13+). Pass a file path,
88
+ * or ":memory:" for a throwaway database. Several processes may share a file; the CONFLICT
89
+ * check is atomic (`UPDATE ... WHERE last_seq = ?`).
90
+ */
91
+ export class SqliteStore {
92
+ db;
93
+ constructor(path = ":memory:") {
94
+ // Loaded here so importing the package does not need node:sqlite.
95
+ const { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
96
+ this.db = new DatabaseSync(path);
97
+ this.db.exec(`
98
+ PRAGMA busy_timeout = 5000;
99
+ CREATE TABLE IF NOT EXISTS orders (
100
+ id TEXT PRIMARY KEY,
101
+ last_seq INTEGER NOT NULL,
102
+ record TEXT NOT NULL
103
+ ) STRICT;
104
+ `);
105
+ }
106
+ async read(id) {
107
+ const row = this.db.prepare("SELECT record FROM orders WHERE id = ?").get(id);
108
+ return row ? JSON.parse(row.record) : null;
109
+ }
110
+ async insert(record) {
111
+ const { changes } = this.db
112
+ .prepare("INSERT INTO orders (id, last_seq, record) VALUES (?, ?, ?) ON CONFLICT (id) DO NOTHING")
113
+ .run(record.order.id, lastSeq(record), JSON.stringify(record));
114
+ if (!changes)
115
+ throw new AwlError("ALREADY_EXISTS", `order "${record.order.id}" already exists`);
116
+ }
117
+ async update(record, expectedSeq) {
118
+ const { changes } = this.db
119
+ .prepare("UPDATE orders SET last_seq = ?, record = ? WHERE id = ? AND last_seq = ?")
120
+ .run(lastSeq(record), JSON.stringify(record), record.order.id, expectedSeq);
121
+ if (changes)
122
+ return;
123
+ if (!(await this.read(record.order.id)))
124
+ throw new AwlError("NOT_FOUND", `no order "${record.order.id}"`);
125
+ throw new AwlError("CONFLICT", `order "${record.order.id}" changed since it was read`);
126
+ }
127
+ async list() {
128
+ const rows = this.db.prepare("SELECT record FROM orders ORDER BY id").all();
129
+ return rows.map((r) => JSON.parse(r.record));
130
+ }
131
+ /** Closes the database. */
132
+ close() {
133
+ this.db.close();
134
+ }
135
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-work-layer",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Work Orders and Work Reports for AI agents: a typed SDK, validator and lifecycle desk. The MCP server is in agent-work-layer-mcp.",
5
5
  "keywords": [
6
6
  "ai",