UNPKG

agents

Version:

A home for your AI agents

458 lines (457 loc) • 18.1 kB
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js"; import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js"; //#region src/harness/store/store.ts const DEFAULT_PREFIX = "harness_"; /** * Characters per stored chunk of a JSON value. A Durable Object row holds * at most 2 MB and a character is at most 4 bytes of UTF-8, so a quarter of * a megabyte of characters always fits with room for the other columns. */ const CHUNK_CHARS = 256 * 1024; /** * Open the store over this object's SQLite database, creating its tables on * first use. Cheap: call it once per isolate and keep the result. * * @param storage - The Durable Object's storage. * @param options - Table prefix. * @returns The store. */ function openHarnessStore(storage, options = {}) { return new HarnessStore(storage, options); } function parseJson(text) { return JSON.parse(text); } function isStatus(value) { return value === "queued" || value === "running" || value === "done" || value === "unanswered"; } function chunks(text) { if (text.length <= CHUNK_CHARS) return [text]; const parts = []; for (let at = 0; at < text.length; at += CHUNK_CHARS) parts.push(text.slice(at, at + CHUNK_CHARS)); return parts; } var _storage = /* @__PURE__ */ new WeakMap(); var _sessions = /* @__PURE__ */ new WeakMap(); var _operations = /* @__PURE__ */ new WeakMap(); var _log = /* @__PURE__ */ new WeakMap(); var _HarnessStore_brand = /* @__PURE__ */ new WeakSet(); /** * The store. Construct it with `openHarnessStore`. * * @experimental The API may change before it stabilizes. */ var HarnessStore = class { /** * @param storage - The Durable Object's storage. * @param options - Table prefix. */ constructor(storage, options) { _classPrivateMethodInitSpec(this, _HarnessStore_brand); _classPrivateFieldInitSpec(this, _storage, void 0); _classPrivateFieldInitSpec(this, _sessions, void 0); _classPrivateFieldInitSpec(this, _operations, void 0); _classPrivateFieldInitSpec(this, _log, void 0); const prefix = options.prefix ?? DEFAULT_PREFIX; if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(prefix)) throw new Error(`Invalid harness store prefix ${JSON.stringify(prefix)}`); if (prefix.startsWith("_cf_")) throw new Error("The harness store prefix must not start with _cf_"); _classPrivateFieldSet2(_storage, this, storage); _classPrivateFieldSet2(_sessions, this, `${prefix}sessions`); _classPrivateFieldSet2(_operations, this, `${prefix}operations`); _classPrivateFieldSet2(_log, this, `${prefix}log`); _assertClassBrand(_HarnessStore_brand, this, _migrate).call(this); } /** * Run `fn` in one storage transaction. Writes inside it commit together or * not at all. * * @template T - What `fn` returns. * @param fn - Synchronous work against this store. * @returns What `fn` returned. */ transaction(fn) { return _classPrivateFieldGet2(_storage, this).transactionSync(fn); } /** * Create a session, or return the existing one with this id unchanged. * * @param input - The id, an optional parent, and the initial state. * `createdAt` keeps the creation time of a session imported from * elsewhere, so the session list stays in creation order; default now. * @returns The stored session. */ createSession(input) { const now = input.createdAt ?? Date.now(); _classPrivateFieldGet2(_storage, this).sql.exec(`INSERT INTO ${_classPrivateFieldGet2(_sessions, this)} (id, parent, state, created_at, updated_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT (id) DO NOTHING`, input.id, input.parent ?? null, JSON.stringify(input.state ?? null), now, now); const created = this.session(input.id); if (!created) throw new Error(`Session ${input.id} was not stored`); return created; } /** * Read one session. * * @param id - The session id. * @returns The session, or `undefined` when there is none. */ session(id) { const row = _classPrivateFieldGet2(_storage, this).sql.exec(`SELECT * FROM ${_classPrivateFieldGet2(_sessions, this)} WHERE id = ?`, id).toArray()[0]; return row ? _assertClassBrand(_HarnessStore_brand, this, _sessionOf).call(this, row) : void 0; } /** * Every session, oldest first. * * @returns The sessions. */ sessions() { return _classPrivateFieldGet2(_storage, this).sql.exec(`SELECT * FROM ${_classPrivateFieldGet2(_sessions, this)} ORDER BY created_at, id`).toArray().map((row) => _assertClassBrand(_HarnessStore_brand, this, _sessionOf).call(this, row)); } /** * Replace a session's harness-owned state. * * @param id - The session id. * @param state - The new state. * @returns False when there is no such session. */ setSessionState(id, state) { return _classPrivateFieldGet2(_storage, this).sql.exec(`UPDATE ${_classPrivateFieldGet2(_sessions, this)} SET state = ?, updated_at = ? WHERE id = ?`, JSON.stringify(state), Date.now(), id).rowsWritten > 0; } /** * Delete a session with its operations and logs. * * @param id - The session id. * @returns False when there was no such session. */ deleteSession(id) { return this.transaction(() => { const sql = _classPrivateFieldGet2(_storage, this).sql; sql.exec(`DELETE FROM ${_classPrivateFieldGet2(_operations, this)} WHERE session = ?`, id); sql.exec(`DELETE FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ?`, id); return sql.exec(`DELETE FROM ${_classPrivateFieldGet2(_sessions, this)} WHERE id = ?`, id).rowsWritten > 0; }); } /** * Durably queue an operation. Idempotent on `(session, id)`: a second * enqueue with the same id stores nothing and returns the first record. * * @param input - The session, the idempotency id, the input and options. * @returns The stored record, and whether this call created it. */ enqueue(input) { return this.transaction(() => { const existing = this.operation(input.session, input.id); if (existing) return { record: existing, accepted: false }; const sql = _classPrivateFieldGet2(_storage, this).sql; const next = sql.exec(`SELECT MAX(seq) AS seq FROM ${_classPrivateFieldGet2(_operations, this)}`).toArray()[0]?.seq ?? 0; sql.exec(`INSERT INTO ${_classPrivateFieldGet2(_operations, this)} (session, id, seq, status, input, meta, result, reason, created_at, settled_at) VALUES (?, ?, ?, 'queued', ?, ?, 'null', NULL, ?, NULL)`, input.session, input.id, next + 1, JSON.stringify(input.input), JSON.stringify(input.meta ?? null), Date.now()); const record = this.operation(input.session, input.id); if (!record) throw new Error(`Operation ${input.id} was not stored`); return { record, accepted: true }; }); } /** * Read one operation. * * @param session - The session id. * @param id - The operation id. * @returns The operation, or `undefined` when there is none. */ operation(session, id) { const row = _classPrivateFieldGet2(_storage, this).sql.exec(`SELECT * FROM ${_classPrivateFieldGet2(_operations, this)} WHERE session = ? AND id = ?`, session, id).toArray()[0]; return row ? _assertClassBrand(_HarnessStore_brand, this, _operationOf).call(this, row) : void 0; } /** * Operations in submission order, optionally narrowed to one session and * to some statuses. * * @param filter - The session and statuses to keep. Default: all. * @returns The matching operations, oldest first. */ operations(filter = {}) { const where = []; const params = []; if (filter.session !== void 0) { where.push("session = ?"); params.push(filter.session); } if (filter.status !== void 0) { if (filter.status.length === 0) return []; where.push(`status IN (${filter.status.map(() => "?").join(", ")})`); params.push(...filter.status); } return _classPrivateFieldGet2(_storage, this).sql.exec(`SELECT * FROM ${_classPrivateFieldGet2(_operations, this)} ${where.length > 0 ? `WHERE ${where.join(" AND ")}` : ""} ORDER BY seq`, ...params).toArray().map((row) => _assertClassBrand(_HarnessStore_brand, this, _operationOf).call(this, row)); } /** * Move a queued operation to `running`. * * @param session - The session id. * @param id - The operation id. * @returns False when it was not queued. */ start(session, id) { return _assertClassBrand(_HarnessStore_brand, this, _transition).call(this, session, id, ["queued"], "running"); } /** * Move a running operation back to `queued`, to run it again. * * @param session - The session id. * @param id - The operation id. * @returns False when it was not running. */ requeue(session, id) { return _assertClassBrand(_HarnessStore_brand, this, _transition).call(this, session, id, ["running"], "queued"); } /** * Settle a queued or running operation. * * @param session - The session id. * @param id - The operation id. * @param outcome - How it ended. * @returns False when it had already settled or does not exist. */ settle(session, id, outcome) { return _classPrivateFieldGet2(_storage, this).sql.exec(`UPDATE ${_classPrivateFieldGet2(_operations, this)} SET status = ?, result = ?, reason = ?, settled_at = ? WHERE session = ? AND id = ? AND status IN ('queued', 'running')`, outcome.status, JSON.stringify(outcome.status === "done" ? outcome.result : null), outcome.status === "unanswered" ? outcome.reason : null, Date.now(), session, id).rowsWritten > 0; } /** * Delete one operation, whatever its status. Its id may be enqueued * again. For replacing a record imported from elsewhere; a harness that * deletes an open operation must stop running it itself. * * @param session - The session id. * @param id - The operation id. * @returns False when there was no such operation. */ deleteOperation(session, id) { return _classPrivateFieldGet2(_storage, this).sql.exec(`DELETE FROM ${_classPrivateFieldGet2(_operations, this)} WHERE session = ? AND id = ?`, session, id).rowsWritten > 0; } /** * Forget a session's settled operations, as when the session starts * over. Open operations are kept. A forgotten operation's id may be * enqueued again. * * @param session - The session id. * @returns How many operations were deleted. */ deleteSettled(session) { return _classPrivateFieldGet2(_storage, this).sql.exec(`DELETE FROM ${_classPrivateFieldGet2(_operations, this)} WHERE session = ? AND status IN ('done', 'unanswered')`, session).rowsWritten; } /** * Replace an operation's harness-owned bookkeeping. * * @param session - The session id. * @param id - The operation id. * @param meta - The new value. * @returns False when there is no such operation. */ setOperationMeta(session, id, meta) { return _classPrivateFieldGet2(_storage, this).sql.exec(`UPDATE ${_classPrivateFieldGet2(_operations, this)} SET meta = ? WHERE session = ? AND id = ?`, JSON.stringify(meta), session, id).rowsWritten > 0; } /** * Append entries to a session's log. An entry with an id that is already * in the log replaces that entry in place, keeping its position. * * @param session - The session id. * @param log - The log's name, such as `"messages"`. * @param entries - The entries, in order. * @returns The position of the last entry written, or of the log's end. */ append(session, log, entries) { return this.transaction(() => { let end = this.end(session, log); for (const entry of entries) { const existing = entry.id === void 0 ? void 0 : _assertClassBrand(_HarnessStore_brand, this, _seqOf).call(this, session, log, entry.id); if (existing !== void 0) { _assertClassBrand(_HarnessStore_brand, this, _write).call(this, session, log, existing, entry); continue; } end += 1; _assertClassBrand(_HarnessStore_brand, this, _write).call(this, session, log, end, entry); } return end; }); } /** * Read a session's log in order. * * @param session - The session id. * @param log - The log's name. * @param range - Entries after position `after` (exclusive), at most `limit`. * @returns The entries. */ read(session, log, range = {}) { const after = range.after ?? 0; const limit = range.limit ?? -1; const rows = _classPrivateFieldGet2(_storage, this).sql.exec(`SELECT seq, part, id, data, created_at FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ? AND seq IN ( SELECT DISTINCT seq FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ? AND seq > ? ORDER BY seq LIMIT ? ) ORDER BY seq, part`, session, log, session, log, after, limit).toArray(); const entries = []; let current; const flush = () => { if (!current) return; entries.push({ seq: current.row.seq, id: current.row.id ?? void 0, data: parseJson(current.text), createdAt: current.row.created_at }); }; for (const row of rows) { if (current && current.row.seq === row.seq) { current.text += row.data; continue; } flush(); current = { row, text: row.data }; } flush(); return entries; } /** * One entry by id. * * @param session - The session id. * @param log - The log's name. * @param id - The entry id. * @returns The entry, or `undefined`. */ entry(session, log, id) { const seq = _assertClassBrand(_HarnessStore_brand, this, _seqOf).call(this, session, log, id); if (seq === void 0) return void 0; return this.read(session, log, { after: seq - 1, limit: 1 })[0]; } /** * The position of a log's last entry. * * @param session - The session id. * @param log - The log's name. * @returns The position, or 0 for an empty log. */ end(session, log) { return _classPrivateFieldGet2(_storage, this).sql.exec(`SELECT MAX(seq) AS seq FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ?`, session, log).toArray()[0]?.seq ?? 0; } /** * Empty a session's log. * * @param session - The session id. * @param log - The log's name. */ clear(session, log) { _classPrivateFieldGet2(_storage, this).sql.exec(`DELETE FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ?`, session, log); } /** * Copy one session's log onto the end of another's, keeping entry ids. * * @param from - The source session. * @param to - The target session. * @param log - The log's name. */ copy(from, to, log) { this.transaction(() => { const offset = this.end(to, log); _classPrivateFieldGet2(_storage, this).sql.exec(`INSERT INTO ${_classPrivateFieldGet2(_log, this)} (session, log, seq, part, id, data, created_at) SELECT ?, log, seq + ?, part, id, data, created_at FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ?`, to, offset, from, log); }); } }; function _migrate() { const sql = _classPrivateFieldGet2(_storage, this).sql; sql.exec(`CREATE TABLE IF NOT EXISTS ${_classPrivateFieldGet2(_sessions, this)} ( id TEXT PRIMARY KEY, parent TEXT, state TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL )`); sql.exec(`CREATE TABLE IF NOT EXISTS ${_classPrivateFieldGet2(_operations, this)} ( session TEXT NOT NULL, id TEXT NOT NULL, seq INTEGER NOT NULL, status TEXT NOT NULL, input TEXT NOT NULL, meta TEXT NOT NULL, result TEXT NOT NULL, reason TEXT, created_at INTEGER NOT NULL, settled_at INTEGER, PRIMARY KEY (session, id) )`); sql.exec(`CREATE INDEX IF NOT EXISTS ${_classPrivateFieldGet2(_operations, this)}_open ON ${_classPrivateFieldGet2(_operations, this)} (status, seq)`); sql.exec(`CREATE TABLE IF NOT EXISTS ${_classPrivateFieldGet2(_log, this)} ( session TEXT NOT NULL, log TEXT NOT NULL, seq INTEGER NOT NULL, part INTEGER NOT NULL, id TEXT, data TEXT NOT NULL, created_at INTEGER NOT NULL, PRIMARY KEY (session, log, seq, part) )`); sql.exec(`CREATE INDEX IF NOT EXISTS ${_classPrivateFieldGet2(_log, this)}_id ON ${_classPrivateFieldGet2(_log, this)} (session, log, id) WHERE id IS NOT NULL`); } function _sessionOf(row) { return { id: row.id, parent: row.parent ?? void 0, state: parseJson(row.state), createdAt: row.created_at, updatedAt: row.updated_at }; } function _transition(session, id, from, to) { return _classPrivateFieldGet2(_storage, this).sql.exec(`UPDATE ${_classPrivateFieldGet2(_operations, this)} SET status = ? WHERE session = ? AND id = ? AND status IN (${from.map(() => "?").join(", ")})`, to, session, id, ...from).rowsWritten > 0; } function _operationOf(row) { if (!isStatus(row.status)) throw new Error(`Unknown operation status ${row.status}`); return { session: row.session, id: row.id, seq: row.seq, status: row.status, input: parseJson(row.input), meta: parseJson(row.meta), result: parseJson(row.result), reason: row.reason ?? void 0, createdAt: row.created_at, settledAt: row.settled_at ?? void 0 }; } function _seqOf(session, log, id) { return _classPrivateFieldGet2(_storage, this).sql.exec(`SELECT seq FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ? AND id = ? LIMIT 1`, session, log, id).toArray()[0]?.seq; } function _write(session, log, seq, entry) { const sql = _classPrivateFieldGet2(_storage, this).sql; const createdAt = sql.exec(`SELECT created_at FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ? AND seq = ? LIMIT 1`, session, log, seq).toArray()[0]?.created_at ?? Date.now(); sql.exec(`DELETE FROM ${_classPrivateFieldGet2(_log, this)} WHERE session = ? AND log = ? AND seq = ?`, session, log, seq); chunks(JSON.stringify(entry.data)).forEach((part, index) => { sql.exec(`INSERT INTO ${_classPrivateFieldGet2(_log, this)} (session, log, seq, part, id, data, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, session, log, seq, index, entry.id ?? null, part, createdAt); }); } //#endregion export { openHarnessStore as n, HarnessStore as t }; //# sourceMappingURL=store-CslVzfN7.js.map