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 +12 -0
- package/README.md +12 -2
- package/dist/generated.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/store.d.ts +16 -0
- package/dist/store.js +51 -0
- package/package.json +1 -1
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.
|
|
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