@a-dray/aglib 0.1.0 → 0.1.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../src/store/adapters/sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Store, StoreChange, StoreError, StoreConflict, SessionRead, SessionSummary, Runnable,\n} from \"../store.js\";\nimport type { Delivery, Entry, Stored } from \"../../session/entry.js\";\nimport type { JsonValue } from \"../../json.js\";\nimport { err, ok } from \"../../result.js\";\n\n/**\n * The minimum a driver must provide. Declared here rather than importing one so\n * the same file serves `node:sqlite` and `bun:sqlite`, and so an application can\n * hand in a database it already opened — which is what lets its own tables live\n * beside the log and join against it.\n */\nexport interface SqliteDatabase {\n exec(sql: string): void;\n query(sql: string): { all(...parameters: unknown[]): unknown[]; run(...parameters: unknown[]): unknown };\n}\n\nconst schema = `\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL,\n agent_ver TEXT NOT NULL,\n key TEXT,\n metadata TEXT NOT NULL DEFAULT '{}',\n pending TEXT NOT NULL DEFAULT '[]',\n last_seq INTEGER NOT NULL DEFAULT 0,\n running_since TEXT,\n updated_at TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS entries (\n session_id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n at TEXT NOT NULL,\n body TEXT NOT NULL,\n PRIMARY KEY (session_id, seq)\n );\n CREATE TABLE IF NOT EXISTS deliveries (\n session_id TEXT NOT NULL,\n sender TEXT NOT NULL,\n delivery_id TEXT NOT NULL,\n at TEXT NOT NULL,\n PRIMARY KEY (session_id, sender, delivery_id)\n );\n CREATE INDEX IF NOT EXISTS sessions_by_key ON sessions (key, updated_at DESC, id DESC);\n CREATE INDEX IF NOT EXISTS sessions_runnable ON sessions (updated_at) WHERE pending <> '[]';\n DROP INDEX IF EXISTS sessions_stranded; -- sessions_interrupted under its former name\n CREATE INDEX IF NOT EXISTS sessions_interrupted ON sessions (updated_at) WHERE running_since IS NOT NULL;\n CREATE INDEX IF NOT EXISTS deliveries_by_age ON deliveries (at);\n`;\n\ninterface SessionRow {\n id: string; agent_id: string; agent_ver: string; key: string | null;\n metadata: string; pending: string; last_seq: number; running_since: string | null; updated_at: string;\n}\n\n/** `updatedAt` and the session id, so a page boundary is a point and not an instant. */\nconst splitCursor = (cursor: string): [string, string] => {\n const cut = cursor.lastIndexOf(\"|\");\n return cut < 0 ? [cursor, \"\"] : [cursor.slice(0, cut), cursor.slice(cut + 1)];\n};\n\n/**\n * How long a claim holds before another worker may take the session.\n *\n * Long enough that an ordinary turn finishes inside it, short enough that a\n * worker killed mid-turn does not strand its session for an afternoon. If the\n * original worker is somehow still alive when it expires, its writes fail the\n * compare-and-swap, so the window governs wasted work and never correctness.\n */\nconst DEFAULT_CLAIM_MS = 10 * 60 * 1000;\n\n/**\n * How long a delivery id is remembered.\n *\n * `id` means \"this delivery, once\" — a webhook redelivered ten seconds later,\n * a retried send, a cron that fired twice all collapse to one. That has to\n * outlive the queue, because the common case is a duplicate arriving after the\n * first copy was already read. It cannot be remembered forever, so it is\n * remembered for a window an application can set and reason about.\n */\nconst DEFAULT_DELIVERY_MEMORY_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport function createSqliteStore(input: {\n database: SqliteDatabase;\n claimMs?: number;\n /** How long a delivery id is remembered. Defaults to seven days. */\n deliveryMemoryMs?: number;\n}): Store {\n const database = input.database;\n const claimMs = input.claimMs ?? DEFAULT_CLAIM_MS;\n const deliveryMemoryMs = input.deliveryMemoryMs ?? DEFAULT_DELIVERY_MEMORY_MS;\n database.exec(schema);\n\n const one = <T>(sql: string, parameters: readonly unknown[]): T | undefined =>\n database.query(sql).all(...parameters)[0] as T | undefined;\n const run = (sql: string, parameters: readonly unknown[]) => { database.query(sql).run(...parameters); };\n const notFound = (sessionId: string): StoreError =>\n ({ code: \"not-found\", message: `No session ${sessionId}`, retryable: false });\n const failed = (error: unknown): StoreError =>\n ({ code: \"failed\", message: error instanceof Error ? error.message : String(error), retryable: false });\n const queueOf = (row: { pending: string }): Delivery[] => JSON.parse(row.pending) as Delivery[];\n\n /**\n * Watchers, woken after a write is committed.\n *\n * Every call site is already past its `COMMIT`, which is what makes the change\n * readable. The microtask is for the other half: a watcher that throws must\n * not fail the write that woke it — it surfaces as the caller's own unhandled\n * error rather than as a refused append — and one that writes back through\n * this store must not do so from inside the stack of the write it is\n * answering.\n */\n const watchers = new Set<(change: StoreChange) => void>();\n const woke = (changes: readonly StoreChange[]): void => {\n if (!watchers.size || !changes.length) return;\n for (const watcher of [...watchers]) {\n // One microtask each. A watcher that throws must not fail the write that\n // woke it, and must not suppress the watchers after it in the loop — in\n // its own task the throw surfaces as the caller's unhandled error and\n // costs nobody else their wake. Membership is re-checked at delivery, so\n // a watcher that let go between the write and this task is not called:\n // `off()` says stop, and a queued wake is still a wake.\n queueMicrotask(() => {\n if (!watchers.has(watcher)) return;\n for (const change of changes) watcher(change);\n });\n }\n };\n\n return {\n /** Idempotent: a session that is already here keeps what it was created with. */\n async create({ sessionId, agent, key, metadata }) {\n try {\n const existing = one<{ id: string }>(`SELECT id FROM sessions WHERE id = ?`, [sessionId]);\n if (existing) return ok(undefined);\n run(\n `INSERT OR IGNORE INTO sessions (id, agent_id, agent_ver, key, metadata, updated_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [sessionId, agent.id, agent.version, key ?? null, JSON.stringify(metadata ?? {}), new Date().toISOString()],\n );\n // Only a session that really appeared is a change, and a new one has\n // nothing waiting on it — announcing `runnable: false` for a session\n // that already had input would have been wrong rather than spurious.\n woke([{ sessionId, runnable: false }]);\n return ok(undefined);\n } catch (error) { return err(failed(error)); }\n },\n\n async read({ sessionId, afterSeq = 0 }) {\n try {\n const session = one<SessionRow>(`SELECT * FROM sessions WHERE id = ?`, [sessionId]);\n if (!session) return err(notFound(sessionId));\n const rows = database.query(\n `SELECT seq, at, body FROM entries WHERE session_id = ? AND seq > ? ORDER BY seq`,\n ).all(sessionId, afterSeq) as { seq: number; at: string; body: string }[];\n return ok<SessionRead>({\n sessionId,\n agent: { id: session.agent_id, version: session.agent_ver },\n seq: session.last_seq,\n metadata: JSON.parse(session.metadata) as JsonValue,\n entries: rows.map((row): Stored => ({ ...(JSON.parse(row.body) as Entry), seq: row.seq, at: row.at })),\n pending: queueOf(session),\n });\n } catch (error) { return err(failed(error)); }\n },\n\n /**\n * Entries, deliveries, metadata and the queue in one transaction.\n *\n * The compare-and-swap on `last_seq` decides everything else: a caller\n * working from a stale position loses and is told the real one, and because\n * the queue is consumed here rather than in `next`, a loser leaves the\n * messages it was carrying untouched.\n */\n async append({ sessionId, expectedSeq, entries, enqueue, metadata, takePending }) {\n const at = new Date().toISOString();\n try {\n // Inside the try: on a busy database this is where the failure lands,\n // and a throw here escaped the `Result` the port promises.\n database.exec(\"BEGIN IMMEDIATE\");\n const session = one<SessionRow>(`SELECT * FROM sessions WHERE id = ?`, [sessionId]);\n if (!session) { database.exec(\"ROLLBACK\"); return err(notFound(sessionId)); }\n if (session.last_seq !== expectedSeq) {\n database.exec(\"ROLLBACK\");\n return err<StoreConflict>({\n code: \"conflict\", message: `Session moved to ${session.last_seq}`,\n retryable: true, actualSeq: session.last_seq,\n });\n }\n entries.forEach((entry, offset) => {\n run(`INSERT INTO entries (session_id, seq, at, body) VALUES (?, ?, ?, ?)`,\n [sessionId, expectedSeq + offset + 1, at, JSON.stringify(entry)]);\n });\n\n // An activation is open from the entry that starts it to the entry that\n // ends it. `next` reads this to keep a second one from beginning, and\n // the log is the only thing that knows — so it is derived from the log\n // rather than asserted by a caller.\n const opens = entries.some((entry) => entry.type === \"run.started\");\n const closes = entries.some((entry) => entry.type === \"run.finished\");\n // Only a write that records work renews the claim. Renewing on any\n // append at all meant a message *to* a session renewed the claim of the\n // worker that was supposed to read it — so a dead worker's session\n // stayed claimed for as long as anyone kept talking to it, invisible to\n // `next` (the claim never lapsed) and to `interrupted` (the queue was not\n // empty). An activation renews by doing its work, and being spoken to\n // is not its work.\n const worked = entries.length > 0;\n // Every write by an open activation renews its claim. Without this the\n // timestamp stayed at whenever the run began, so any turn outstanding\n // longer than the claim window was handed to a second worker while it\n // was still going — and a compare-and-swap cannot un-run a tool call.\n // A worker that dies still releases, because a dead worker stops writing.\n const runningSince = closes ? null : (opens || (worked && session.running_since)) ? at : session.running_since;\n\n const merged = metadata\n ? JSON.stringify({ ...(JSON.parse(session.metadata) as object), ...(metadata as object) })\n : session.metadata;\n const waiting = queueOf(session);\n if (takePending !== undefined && takePending > waiting.length) {\n // The one loss this design exists to prevent, reachable from a\n // caller's off-by-one: silently dropping messages nothing recorded.\n // `takePending` is only ever `claim.pending.length`, so a larger\n // number means the caller is working from a queue that is not there.\n database.exec(\"ROLLBACK\");\n return err<StoreError>({\n code: \"conflict\",\n message: `Asked to take ${takePending} deliveries from a queue of ${waiting.length}`,\n retryable: false,\n });\n }\n const queue = takePending ? waiting.slice(takePending) : waiting;\n /** Which sessions this write put input on, this one included. */\n const delivered = new Set<string>();\n run(\n `UPDATE sessions SET last_seq = ?, metadata = ?, pending = ?, running_since = ?, updated_at = ?\n WHERE id = ?`,\n [expectedSeq + entries.length, merged, JSON.stringify(queue), runningSince, at, sessionId],\n );\n\n // Swept before the ids are checked, not after: an id past its window is\n // forgotten, and one still inside it is not. Sweeping afterwards left\n // every expired id alive for exactly one more send.\n run(`DELETE FROM deliveries WHERE at < ?`,\n [new Date(Date.parse(at) - deliveryMemoryMs).toISOString()]);\n\n for (const delivery of enqueue ?? []) {\n const target = one<{ pending: string }>(`SELECT pending FROM sessions WHERE id = ?`, [delivery.sessionId]);\n if (!target) { database.exec(\"ROLLBACK\"); return err(notFound(delivery.sessionId)); }\n\n // Two deliveries with the same id from the same sender are the same\n // delivery, and stay so after the first has been read — the whole\n // point of an idempotency key is that it survives the queue. The\n // sender is part of it because the id is the sender's own: without\n // that, two agents numbering their messages from one silently\n // suppress each other.\n // The column is one string, so the two fields are joined into one\n // here. The encoding is this adapter's own and never leaves it: no\n // caller reads the column, and a store that keys the same fact some\n // other way is answering the same question, which is the one the\n // conformance suite asks.\n if (delivery.id) {\n const sender = delivery.from ? `${delivery.from.kind}:${delivery.from.id}` : \"\";\n const seen = one<{ delivery_id: string }>(\n `SELECT delivery_id FROM deliveries WHERE session_id = ? AND sender = ? AND delivery_id = ?`,\n [delivery.sessionId, sender, delivery.id]);\n if (seen) continue;\n run(`INSERT INTO deliveries (session_id, sender, delivery_id, at) VALUES (?, ?, ?, ?)`,\n [delivery.sessionId, sender, delivery.id, at]);\n }\n\n run(`UPDATE sessions SET pending = ?, updated_at = ? WHERE id = ?`,\n [JSON.stringify([...queueOf(target), delivery]), at, delivery.sessionId]);\n delivered.add(delivery.sessionId);\n }\n database.exec(\"COMMIT\");\n // After the commit, never before: a watcher that looked on an earlier\n // wake would find nothing and never be told again.\n woke([\n { sessionId, runnable: delivered.has(sessionId) || queue.length > 0 },\n ...[...delivered].filter((id) => id !== sessionId).map((id) => ({ sessionId: id, runnable: true })),\n ]);\n return ok({ seq: expectedSeq + entries.length });\n } catch (error) {\n database.exec(\"ROLLBACK\");\n return err(failed(error));\n }\n },\n\n async list({ key, limit = 50, before }) {\n try {\n // The cursor carries the id as well as the timestamp, because sessions\n // written in the same tick have no order without it — and a plain\n // `< before` then steps over every row that shares the boundary instant.\n const [beforeAt, beforeId] = before ? splitCursor(before) : [null, null];\n const rows = database.query(\n `SELECT * FROM sessions\n WHERE (? IS NULL OR key = ?)\n AND (? IS NULL OR updated_at < ? OR (updated_at = ? AND id < ?))\n ORDER BY updated_at DESC, id DESC LIMIT ?`,\n ).all(key ?? null, key ?? null, beforeAt, beforeAt, beforeAt, beforeId, limit) as SessionRow[];\n return ok(rows.map((row): SessionSummary => ({\n sessionId: row.id,\n agent: { id: row.agent_id, version: row.agent_ver },\n key: row.key,\n seq: row.last_seq,\n metadata: JSON.parse(row.metadata) as JsonValue,\n updatedAt: row.updated_at,\n })));\n } catch (error) { return err(failed(error)); }\n },\n\n /**\n * Claim a session that has input and no activation already running.\n *\n * The queue is reported and left in place; the append that commits it as\n * entries is what removes it. Nothing here is destructive, so a worker that\n * takes this and then dies costs one expired claim and no messages.\n */\n async next({ signal }) {\n // Nothing is owed to a caller that has given up. Answered rather than\n // refused: \"nothing to claim\" is true, and it needs no code of its own.\n if (signal?.aborted) return ok(undefined);\n const now = Date.now();\n const stale = new Date(now - claimMs).toISOString();\n try {\n database.exec(\"BEGIN IMMEDIATE\");\n const row = one<SessionRow>(\n `SELECT * FROM sessions\n WHERE pending <> '[]' AND (running_since IS NULL OR running_since < ?)\n ORDER BY updated_at LIMIT 1`, [stale]);\n if (!row) { database.exec(\"COMMIT\"); return ok(undefined); }\n run(`UPDATE sessions SET running_since = ? WHERE id = ?`, [new Date(now).toISOString(), row.id]);\n database.exec(\"COMMIT\");\n return ok<Runnable>({\n sessionId: row.id,\n seq: row.last_seq,\n pending: queueOf(row),\n metadata: JSON.parse(row.metadata) as JsonValue,\n });\n } catch (error) {\n database.exec(\"ROLLBACK\");\n return err(failed(error));\n }\n },\n\n /**\n * Claim a session whose activation was interrupted.\n *\n * The mirror of `next`, over the two columns that already exist: an open\n * run — `running_since` set, because the entry that would clear it never\n * committed — whose claim has lapsed, and an empty queue, because the\n * commit that opened the run took what made it runnable. Nothing else in\n * the store can see this session, which is why the port asks for it here\n * rather than leaving every application to write this query itself.\n */\n async interrupted({ signal }) {\n if (signal?.aborted) return ok(undefined);\n const now = Date.now();\n const stale = new Date(now - claimMs).toISOString();\n try {\n database.exec(\"BEGIN IMMEDIATE\");\n const row = one<SessionRow>(\n `SELECT * FROM sessions\n WHERE pending = '[]' AND running_since IS NOT NULL AND running_since < ?\n ORDER BY updated_at LIMIT 1`, [stale]);\n if (!row) { database.exec(\"COMMIT\"); return ok(undefined); }\n run(`UPDATE sessions SET running_since = ? WHERE id = ?`, [new Date(now).toISOString(), row.id]);\n database.exec(\"COMMIT\");\n return ok<Runnable>({\n sessionId: row.id,\n seq: row.last_seq,\n pending: [],\n metadata: JSON.parse(row.metadata) as JsonValue,\n });\n } catch (error) {\n database.exec(\"ROLLBACK\");\n return err(failed(error));\n }\n },\n\n /**\n * The feed is this store's own writes, and reaches exactly as far as they\n * do: a watcher here is woken by what this instance commits, and by nothing\n * else. Another process on the same file, a second store over the same\n * handle, and the application's own SQL beside the log are all invisible,\n * because sqlite has no channel to tell us — that is the miss the port\n * requires a heartbeat for. The case that matters is covered: the surface\n * that writes a delivery and the worker that reads it are the same process,\n * and usually the same store.\n */\n watch(watcher) {\n watchers.add(watcher);\n return () => { watchers.delete(watcher); };\n },\n\n async close() { watchers.clear(); /* the caller owns the handle it passed in */ },\n };\n}\n"]}
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../src/store/adapters/sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Store, StoreChange, StoreError, StoreConflict, SessionRead, SessionSummary, Runnable,\n} from \"../store.js\";\nimport type { Delivery, Entry, Stored } from \"../../session/entry.js\";\nimport type { JsonValue } from \"../../json.js\";\nimport { err, ok } from \"../../result.js\";\n\n/**\n * The minimum a driver must provide. Declared here rather than importing one so\n * the same file serves `node:sqlite` and `bun:sqlite`, and so an application can\n * hand in a database it already opened — which is what lets its own tables live\n * beside the log and join against it.\n */\nexport interface SqliteDatabase {\n exec(sql: string): void;\n query(sql: string): { all(...parameters: unknown[]): unknown[]; run(...parameters: unknown[]): unknown };\n}\n\nconst tables = `\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL,\n agent_ver TEXT NOT NULL,\n key TEXT,\n metadata TEXT NOT NULL DEFAULT '{}',\n pending TEXT NOT NULL DEFAULT '[]',\n last_seq INTEGER NOT NULL DEFAULT 0,\n running_since TEXT,\n updated_at TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS entries (\n session_id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n at TEXT NOT NULL,\n body TEXT NOT NULL,\n PRIMARY KEY (session_id, seq)\n );\n CREATE TABLE IF NOT EXISTS deliveries (\n session_id TEXT NOT NULL,\n sender TEXT NOT NULL,\n delivery_id TEXT NOT NULL,\n at TEXT NOT NULL,\n PRIMARY KEY (session_id, sender, delivery_id)\n );\n`;\n\n/**\n * Columns added after a table had already shipped.\n *\n * `CREATE TABLE IF NOT EXISTS` is a no-op against a table that exists, so a\n * column added later never appears in a database made by an older build — and\n * the first index over it fails with a raw `SQLiteError` from inside `bun:sqlite`,\n * which is no way to meet a store whose whole promise is that the log survives.\n * Additive only: a column is added, nothing is dropped, rewritten or lost.\n */\nconst added: readonly { table: string; column: string; type: string }[] = [\n { table: \"sessions\", column: \"running_since\", type: \"TEXT\" },\n];\n\nconst indexes = `\n CREATE INDEX IF NOT EXISTS sessions_by_key ON sessions (key, updated_at DESC, id DESC);\n CREATE INDEX IF NOT EXISTS sessions_runnable ON sessions (updated_at) WHERE pending <> '[]';\n DROP INDEX IF EXISTS sessions_stranded; -- sessions_interrupted under its former name\n CREATE INDEX IF NOT EXISTS sessions_interrupted ON sessions (updated_at) WHERE running_since IS NOT NULL;\n CREATE INDEX IF NOT EXISTS deliveries_by_age ON deliveries (at);\n`;\n\ninterface SessionRow {\n id: string; agent_id: string; agent_ver: string; key: string | null;\n metadata: string; pending: string; last_seq: number; running_since: string | null; updated_at: string;\n}\n\n/** `updatedAt` and the session id, so a page boundary is a point and not an instant. */\nconst splitCursor = (cursor: string): [string, string] => {\n const cut = cursor.lastIndexOf(\"|\");\n return cut < 0 ? [cursor, \"\"] : [cursor.slice(0, cut), cursor.slice(cut + 1)];\n};\n\n/**\n * How long a claim holds before another worker may take the session.\n *\n * Long enough that an ordinary turn finishes inside it, short enough that a\n * worker killed mid-turn does not strand its session for an afternoon. If the\n * original worker is somehow still alive when it expires, its writes fail the\n * compare-and-swap, so the window governs wasted work and never correctness.\n */\nconst DEFAULT_CLAIM_MS = 10 * 60 * 1000;\n\n/**\n * How long a delivery id is remembered.\n *\n * `id` means \"this delivery, once\" — a webhook redelivered ten seconds later,\n * a retried send, a cron that fired twice all collapse to one. That has to\n * outlive the queue, because the common case is a duplicate arriving after the\n * first copy was already read. It cannot be remembered forever, so it is\n * remembered for a window an application can set and reason about.\n */\nconst DEFAULT_DELIVERY_MEMORY_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport function createSqliteStore(input: {\n database: SqliteDatabase;\n claimMs?: number;\n /** How long a delivery id is remembered. Defaults to seven days. */\n deliveryMemoryMs?: number;\n}): Store {\n const database = input.database;\n const claimMs = input.claimMs ?? DEFAULT_CLAIM_MS;\n const deliveryMemoryMs = input.deliveryMemoryMs ?? DEFAULT_DELIVERY_MEMORY_MS;\n\n // Tables, then the columns an older build's tables lack, then the indexes —\n // which is the only order that works, because an index may be over a column\n // the migration is about to add.\n database.exec(tables);\n for (const { table, column, type } of added) {\n const present = database.query(`SELECT 1 FROM pragma_table_info(?) WHERE name = ?`).all(table, column);\n if (!present.length) database.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);\n }\n database.exec(indexes);\n\n const one = <T>(sql: string, parameters: readonly unknown[]): T | undefined =>\n database.query(sql).all(...parameters)[0] as T | undefined;\n const run = (sql: string, parameters: readonly unknown[]) => { database.query(sql).run(...parameters); };\n const notFound = (sessionId: string): StoreError =>\n ({ code: \"not-found\", message: `No session ${sessionId}`, retryable: false });\n const failed = (error: unknown): StoreError =>\n ({ code: \"failed\", message: error instanceof Error ? error.message : String(error), retryable: false });\n const queueOf = (row: { pending: string }): Delivery[] => JSON.parse(row.pending) as Delivery[];\n\n /**\n * Watchers, woken after a write is committed.\n *\n * Every call site is already past its `COMMIT`, which is what makes the change\n * readable. The microtask is for the other half: a watcher that throws must\n * not fail the write that woke it — it surfaces as the caller's own unhandled\n * error rather than as a refused append — and one that writes back through\n * this store must not do so from inside the stack of the write it is\n * answering.\n */\n const watchers = new Set<(change: StoreChange) => void>();\n const woke = (changes: readonly StoreChange[]): void => {\n if (!watchers.size || !changes.length) return;\n for (const watcher of [...watchers]) {\n // One microtask each. A watcher that throws must not fail the write that\n // woke it, and must not suppress the watchers after it in the loop — in\n // its own task the throw surfaces as the caller's unhandled error and\n // costs nobody else their wake. Membership is re-checked at delivery, so\n // a watcher that let go between the write and this task is not called:\n // `off()` says stop, and a queued wake is still a wake.\n queueMicrotask(() => {\n if (!watchers.has(watcher)) return;\n for (const change of changes) watcher(change);\n });\n }\n };\n\n return {\n /** Idempotent: a session that is already here keeps what it was created with. */\n async create({ sessionId, agent, key, metadata }) {\n try {\n const existing = one<{ id: string }>(`SELECT id FROM sessions WHERE id = ?`, [sessionId]);\n if (existing) return ok(undefined);\n run(\n `INSERT OR IGNORE INTO sessions (id, agent_id, agent_ver, key, metadata, updated_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [sessionId, agent.id, agent.version, key ?? null, JSON.stringify(metadata ?? {}), new Date().toISOString()],\n );\n // Only a session that really appeared is a change, and a new one has\n // nothing waiting on it — announcing `runnable: false` for a session\n // that already had input would have been wrong rather than spurious.\n woke([{ sessionId, runnable: false }]);\n return ok(undefined);\n } catch (error) { return err(failed(error)); }\n },\n\n async read({ sessionId, afterSeq = 0 }) {\n try {\n const session = one<SessionRow>(`SELECT * FROM sessions WHERE id = ?`, [sessionId]);\n if (!session) return err(notFound(sessionId));\n const rows = database.query(\n `SELECT seq, at, body FROM entries WHERE session_id = ? AND seq > ? ORDER BY seq`,\n ).all(sessionId, afterSeq) as { seq: number; at: string; body: string }[];\n return ok<SessionRead>({\n sessionId,\n agent: { id: session.agent_id, version: session.agent_ver },\n seq: session.last_seq,\n metadata: JSON.parse(session.metadata) as JsonValue,\n entries: rows.map((row): Stored => ({ ...(JSON.parse(row.body) as Entry), seq: row.seq, at: row.at })),\n pending: queueOf(session),\n });\n } catch (error) { return err(failed(error)); }\n },\n\n /**\n * Entries, deliveries, metadata and the queue in one transaction.\n *\n * The compare-and-swap on `last_seq` decides everything else: a caller\n * working from a stale position loses and is told the real one, and because\n * the queue is consumed here rather than in `next`, a loser leaves the\n * messages it was carrying untouched.\n */\n async append({ sessionId, expectedSeq, entries, enqueue, metadata, takePending }) {\n const at = new Date().toISOString();\n try {\n // Inside the try: on a busy database this is where the failure lands,\n // and a throw here escaped the `Result` the port promises.\n database.exec(\"BEGIN IMMEDIATE\");\n const session = one<SessionRow>(`SELECT * FROM sessions WHERE id = ?`, [sessionId]);\n if (!session) { database.exec(\"ROLLBACK\"); return err(notFound(sessionId)); }\n if (session.last_seq !== expectedSeq) {\n database.exec(\"ROLLBACK\");\n return err<StoreConflict>({\n code: \"conflict\", message: `Session moved to ${session.last_seq}`,\n retryable: true, actualSeq: session.last_seq,\n });\n }\n entries.forEach((entry, offset) => {\n run(`INSERT INTO entries (session_id, seq, at, body) VALUES (?, ?, ?, ?)`,\n [sessionId, expectedSeq + offset + 1, at, JSON.stringify(entry)]);\n });\n\n // An activation is open from the entry that starts it to the entry that\n // ends it. `next` reads this to keep a second one from beginning, and\n // the log is the only thing that knows — so it is derived from the log\n // rather than asserted by a caller.\n const opens = entries.some((entry) => entry.type === \"run.started\");\n const closes = entries.some((entry) => entry.type === \"run.finished\");\n // Only a write that records work renews the claim. Renewing on any\n // append at all meant a message *to* a session renewed the claim of the\n // worker that was supposed to read it — so a dead worker's session\n // stayed claimed for as long as anyone kept talking to it, invisible to\n // `next` (the claim never lapsed) and to `interrupted` (the queue was not\n // empty). An activation renews by doing its work, and being spoken to\n // is not its work.\n const worked = entries.length > 0;\n // Every write by an open activation renews its claim. Without this the\n // timestamp stayed at whenever the run began, so any turn outstanding\n // longer than the claim window was handed to a second worker while it\n // was still going — and a compare-and-swap cannot un-run a tool call.\n // A worker that dies still releases, because a dead worker stops writing.\n const runningSince = closes ? null : (opens || (worked && session.running_since)) ? at : session.running_since;\n\n const merged = metadata\n ? JSON.stringify({ ...(JSON.parse(session.metadata) as object), ...(metadata as object) })\n : session.metadata;\n const waiting = queueOf(session);\n if (takePending !== undefined && takePending > waiting.length) {\n // The one loss this design exists to prevent, reachable from a\n // caller's off-by-one: silently dropping messages nothing recorded.\n // `takePending` is only ever `claim.pending.length`, so a larger\n // number means the caller is working from a queue that is not there.\n database.exec(\"ROLLBACK\");\n return err<StoreError>({\n code: \"conflict\",\n message: `Asked to take ${takePending} deliveries from a queue of ${waiting.length}`,\n retryable: false,\n });\n }\n const queue = takePending ? waiting.slice(takePending) : waiting;\n /** Which sessions this write put input on, this one included. */\n const delivered = new Set<string>();\n run(\n `UPDATE sessions SET last_seq = ?, metadata = ?, pending = ?, running_since = ?, updated_at = ?\n WHERE id = ?`,\n [expectedSeq + entries.length, merged, JSON.stringify(queue), runningSince, at, sessionId],\n );\n\n // Swept before the ids are checked, not after: an id past its window is\n // forgotten, and one still inside it is not. Sweeping afterwards left\n // every expired id alive for exactly one more send.\n run(`DELETE FROM deliveries WHERE at < ?`,\n [new Date(Date.parse(at) - deliveryMemoryMs).toISOString()]);\n\n for (const delivery of enqueue ?? []) {\n const target = one<{ pending: string }>(`SELECT pending FROM sessions WHERE id = ?`, [delivery.sessionId]);\n if (!target) { database.exec(\"ROLLBACK\"); return err(notFound(delivery.sessionId)); }\n\n // Two deliveries with the same id from the same sender are the same\n // delivery, and stay so after the first has been read — the whole\n // point of an idempotency key is that it survives the queue. The\n // sender is part of it because the id is the sender's own: without\n // that, two agents numbering their messages from one silently\n // suppress each other.\n // The column is one string, so the two fields are joined into one\n // here. The encoding is this adapter's own and never leaves it: no\n // caller reads the column, and a store that keys the same fact some\n // other way is answering the same question, which is the one the\n // conformance suite asks.\n if (delivery.id) {\n const sender = delivery.from ? `${delivery.from.kind}:${delivery.from.id}` : \"\";\n const seen = one<{ delivery_id: string }>(\n `SELECT delivery_id FROM deliveries WHERE session_id = ? AND sender = ? AND delivery_id = ?`,\n [delivery.sessionId, sender, delivery.id]);\n if (seen) continue;\n run(`INSERT INTO deliveries (session_id, sender, delivery_id, at) VALUES (?, ?, ?, ?)`,\n [delivery.sessionId, sender, delivery.id, at]);\n }\n\n run(`UPDATE sessions SET pending = ?, updated_at = ? WHERE id = ?`,\n [JSON.stringify([...queueOf(target), delivery]), at, delivery.sessionId]);\n delivered.add(delivery.sessionId);\n }\n database.exec(\"COMMIT\");\n // After the commit, never before: a watcher that looked on an earlier\n // wake would find nothing and never be told again.\n woke([\n { sessionId, runnable: delivered.has(sessionId) || queue.length > 0 },\n ...[...delivered].filter((id) => id !== sessionId).map((id) => ({ sessionId: id, runnable: true })),\n ]);\n return ok({ seq: expectedSeq + entries.length });\n } catch (error) {\n database.exec(\"ROLLBACK\");\n return err(failed(error));\n }\n },\n\n async list({ key, limit = 50, before }) {\n try {\n // The cursor carries the id as well as the timestamp, because sessions\n // written in the same tick have no order without it — and a plain\n // `< before` then steps over every row that shares the boundary instant.\n const [beforeAt, beforeId] = before ? splitCursor(before) : [null, null];\n const rows = database.query(\n `SELECT * FROM sessions\n WHERE (? IS NULL OR key = ?)\n AND (? IS NULL OR updated_at < ? OR (updated_at = ? AND id < ?))\n ORDER BY updated_at DESC, id DESC LIMIT ?`,\n ).all(key ?? null, key ?? null, beforeAt, beforeAt, beforeAt, beforeId, limit) as SessionRow[];\n return ok(rows.map((row): SessionSummary => ({\n sessionId: row.id,\n agent: { id: row.agent_id, version: row.agent_ver },\n key: row.key,\n seq: row.last_seq,\n metadata: JSON.parse(row.metadata) as JsonValue,\n updatedAt: row.updated_at,\n })));\n } catch (error) { return err(failed(error)); }\n },\n\n /**\n * Claim a session that has input and no activation already running.\n *\n * The queue is reported and left in place; the append that commits it as\n * entries is what removes it. Nothing here is destructive, so a worker that\n * takes this and then dies costs one expired claim and no messages.\n */\n async next({ signal }) {\n // Nothing is owed to a caller that has given up. Answered rather than\n // refused: \"nothing to claim\" is true, and it needs no code of its own.\n if (signal?.aborted) return ok(undefined);\n const now = Date.now();\n const stale = new Date(now - claimMs).toISOString();\n try {\n database.exec(\"BEGIN IMMEDIATE\");\n const row = one<SessionRow>(\n `SELECT * FROM sessions\n WHERE pending <> '[]' AND (running_since IS NULL OR running_since < ?)\n ORDER BY updated_at LIMIT 1`, [stale]);\n if (!row) { database.exec(\"COMMIT\"); return ok(undefined); }\n run(`UPDATE sessions SET running_since = ? WHERE id = ?`, [new Date(now).toISOString(), row.id]);\n database.exec(\"COMMIT\");\n return ok<Runnable>({\n sessionId: row.id,\n seq: row.last_seq,\n pending: queueOf(row),\n metadata: JSON.parse(row.metadata) as JsonValue,\n });\n } catch (error) {\n database.exec(\"ROLLBACK\");\n return err(failed(error));\n }\n },\n\n /**\n * Claim a session whose activation was interrupted.\n *\n * The mirror of `next`, over the two columns that already exist: an open\n * run — `running_since` set, because the entry that would clear it never\n * committed — whose claim has lapsed, and an empty queue, because the\n * commit that opened the run took what made it runnable. Nothing else in\n * the store can see this session, which is why the port asks for it here\n * rather than leaving every application to write this query itself.\n */\n async interrupted({ signal }) {\n if (signal?.aborted) return ok(undefined);\n const now = Date.now();\n const stale = new Date(now - claimMs).toISOString();\n try {\n database.exec(\"BEGIN IMMEDIATE\");\n const row = one<SessionRow>(\n `SELECT * FROM sessions\n WHERE pending = '[]' AND running_since IS NOT NULL AND running_since < ?\n ORDER BY updated_at LIMIT 1`, [stale]);\n if (!row) { database.exec(\"COMMIT\"); return ok(undefined); }\n run(`UPDATE sessions SET running_since = ? WHERE id = ?`, [new Date(now).toISOString(), row.id]);\n database.exec(\"COMMIT\");\n return ok<Runnable>({\n sessionId: row.id,\n seq: row.last_seq,\n pending: [],\n metadata: JSON.parse(row.metadata) as JsonValue,\n });\n } catch (error) {\n database.exec(\"ROLLBACK\");\n return err(failed(error));\n }\n },\n\n /**\n * The feed is this store's own writes, and reaches exactly as far as they\n * do: a watcher here is woken by what this instance commits, and by nothing\n * else. Another process on the same file, a second store over the same\n * handle, and the application's own SQL beside the log are all invisible,\n * because sqlite has no channel to tell us — that is the miss the port\n * requires a heartbeat for. The case that matters is covered: the surface\n * that writes a delivery and the worker that reads it are the same process,\n * and usually the same store.\n */\n watch(watcher) {\n watchers.add(watcher);\n return () => { watchers.delete(watcher); };\n },\n\n async close() { watchers.clear(); /* the caller owns the handle it passed in */ },\n };\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import type { Sink } from "./render.js";
2
+ export interface TurnSource {
3
+ /** Whether a person is waiting, which decides prompting and how a failure ends. */
4
+ interactive: boolean;
5
+ lines: AsyncIterable<string>;
6
+ }
7
+ export declare function turnsFrom(input: {
8
+ /** The task on argv, if there was one. */
9
+ task: string;
10
+ /** Injected so a test never touches a terminal. */
11
+ stdin?: NodeJS.ReadStream;
12
+ isTTY?: boolean;
13
+ }): TurnSource;
14
+ /**
15
+ * The standard pair of channels for a terminal.
16
+ *
17
+ * The answer on stdout, the account of the run on stderr, so redirecting the
18
+ * first captures the answer and nothing else. Written out identically in three
19
+ * recipes before this existed, which is two more copies than a fact deserves.
20
+ */
21
+ export declare function terminalSink(options?: {
22
+ detail?: Sink["detail"];
23
+ }): Sink;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Where a person's turns come from.
3
+ *
4
+ * The inbound half of the one channel this package ships, and `render` is the
5
+ * outbound half. A channel does three things — decide which session a person is
6
+ * talking to, deliver a message exactly once, and put the result in a medium's
7
+ * own shape. For a terminal the first is "the one session" and the second is a
8
+ * pipe that cannot fail, so the two halves collapse to a keyboard and a screen,
9
+ * and both are the same for every application. A mail bridge keeps all three
10
+ * and is yours; this is the degenerate case, which is why it can be here.
11
+ *
12
+ * It is the one file in `src` that touches `process`, and it is confined to
13
+ * that: `render` takes a sink, this takes a stream, and neither reaches for a
14
+ * terminal it was not handed.
15
+ *
16
+ * **Multi-turn is the default, and it is not a flag.** A conversation is what
17
+ * this recipe is for, and one-shot is the special case — the one where nobody
18
+ * is there. So the fact that decides it is whether stdin is a terminal, not
19
+ * something an operator has to remember:
20
+ *
21
+ * - A person at a terminal gets a prompt, and any task on argv is simply
22
+ * their first turn.
23
+ * - A pipe or a redirect gets one shot and an exit code, so
24
+ * `recipe native-agent "…" > answer.txt` and `echo … | recipe` both behave
25
+ * the way every other command-line program does.
26
+ *
27
+ * There is no flag either way. `--once` existed to give a person at a terminal
28
+ * the script behaviour, and `echo "…" | recipe` already does that — a second
29
+ * way to say a thing the shell says better. Prompting into a pipe has no flag
30
+ * for the opposite reason: it would wait for a person who is not there.
31
+ */
32
+ import { createInterface } from "node:readline";
33
+ export function turnsFrom(input) {
34
+ const stdin = input.stdin ?? process.stdin;
35
+ const interactive = input.isTTY ?? stdin.isTTY === true;
36
+ if (!interactive) {
37
+ return {
38
+ interactive: false,
39
+ lines: (async function* () {
40
+ // A task on argv wins; otherwise the whole of stdin is the task, which
41
+ // is what a pipe means.
42
+ if (input.task) {
43
+ yield input.task;
44
+ return;
45
+ }
46
+ const piped = await new Promise((resolve) => {
47
+ let read = "";
48
+ stdin.setEncoding("utf8");
49
+ stdin.on("data", (chunk) => { read += chunk; });
50
+ stdin.on("end", () => resolve(read));
51
+ });
52
+ if (piped.trim())
53
+ yield piped.trim();
54
+ })(),
55
+ };
56
+ }
57
+ return {
58
+ interactive: true,
59
+ lines: (async function* () {
60
+ if (input.task)
61
+ yield input.task;
62
+ const reader = createInterface({ input: stdin, output: process.stderr, prompt: "› " });
63
+ reader.prompt();
64
+ for await (const line of reader) {
65
+ const said = line.trim();
66
+ // A blank line is someone thinking, not an empty question.
67
+ if (said)
68
+ yield said;
69
+ reader.prompt();
70
+ }
71
+ reader.close();
72
+ })(),
73
+ };
74
+ }
75
+ /**
76
+ * The standard pair of channels for a terminal.
77
+ *
78
+ * The answer on stdout, the account of the run on stderr, so redirecting the
79
+ * first captures the answer and nothing else. Written out identically in three
80
+ * recipes before this existed, which is two more copies than a fact deserves.
81
+ */
82
+ export function terminalSink(options = {}) {
83
+ return {
84
+ write: (text) => process.stdout.write(text),
85
+ status: (line) => process.stderr.write(line),
86
+ tty: process.stderr.isTTY === true,
87
+ ...(options.detail ? { detail: options.detail } : {}),
88
+ };
89
+ }
90
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAShD,MAAM,UAAU,SAAS,CAAC,KAMzB;IACC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC;IAC3C,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC;IAExD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO;YACL,WAAW,EAAE,KAAK;YAClB,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC;gBACrB,uEAAuE;gBACvE,wBAAwB;gBACxB,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;oBAAC,MAAM,KAAK,CAAC,IAAI,CAAC;oBAAC,OAAO;gBAAC,CAAC;gBAC7C,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;oBAClD,IAAI,IAAI,GAAG,EAAE,CAAC;oBACd,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;oBAC1B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBAChD,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;gBACvC,CAAC,CAAC,CAAC;gBACH,IAAI,KAAK,CAAC,IAAI,EAAE;oBAAE,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACvC,CAAC,CAAC,EAAE;SACL,CAAC;IACJ,CAAC;IAED,OAAO;QACL,WAAW,EAAE,IAAI;QACjB,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC;YACrB,IAAI,KAAK,CAAC,IAAI;gBAAE,MAAM,KAAK,CAAC,IAAI,CAAC;YACjC,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACvF,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzB,2DAA2D;gBAC3D,IAAI,IAAI;oBAAE,MAAM,IAAI,CAAC;gBACrB,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,CAAC;YACD,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC,CAAC,EAAE;KACL,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,UAAuC,EAAE;IACpE,OAAO;QACL,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QAC5C,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI;QAClC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Where a person's turns come from.\n *\n * The inbound half of the one channel this package ships, and `render` is the\n * outbound half. A channel does three things — decide which session a person is\n * talking to, deliver a message exactly once, and put the result in a medium's\n * own shape. For a terminal the first is \"the one session\" and the second is a\n * pipe that cannot fail, so the two halves collapse to a keyboard and a screen,\n * and both are the same for every application. A mail bridge keeps all three\n * and is yours; this is the degenerate case, which is why it can be here.\n *\n * It is the one file in `src` that touches `process`, and it is confined to\n * that: `render` takes a sink, this takes a stream, and neither reaches for a\n * terminal it was not handed.\n *\n * **Multi-turn is the default, and it is not a flag.** A conversation is what\n * this recipe is for, and one-shot is the special case — the one where nobody\n * is there. So the fact that decides it is whether stdin is a terminal, not\n * something an operator has to remember:\n *\n * - A person at a terminal gets a prompt, and any task on argv is simply\n * their first turn.\n * - A pipe or a redirect gets one shot and an exit code, so\n * `recipe native-agent \"…\" > answer.txt` and `echo … | recipe` both behave\n * the way every other command-line program does.\n *\n * There is no flag either way. `--once` existed to give a person at a terminal\n * the script behaviour, and `echo \"…\" | recipe` already does that — a second\n * way to say a thing the shell says better. Prompting into a pipe has no flag\n * for the opposite reason: it would wait for a person who is not there.\n */\nimport { createInterface } from \"node:readline\";\nimport type { Sink } from \"./render.js\";\n\nexport interface TurnSource {\n /** Whether a person is waiting, which decides prompting and how a failure ends. */\n interactive: boolean;\n lines: AsyncIterable<string>;\n}\n\nexport function turnsFrom(input: {\n /** The task on argv, if there was one. */\n task: string;\n /** Injected so a test never touches a terminal. */\n stdin?: NodeJS.ReadStream;\n isTTY?: boolean;\n}): TurnSource {\n const stdin = input.stdin ?? process.stdin;\n const interactive = input.isTTY ?? stdin.isTTY === true;\n\n if (!interactive) {\n return {\n interactive: false,\n lines: (async function* () {\n // A task on argv wins; otherwise the whole of stdin is the task, which\n // is what a pipe means.\n if (input.task) { yield input.task; return; }\n const piped = await new Promise<string>((resolve) => {\n let read = \"\";\n stdin.setEncoding(\"utf8\");\n stdin.on(\"data\", (chunk) => { read += chunk; });\n stdin.on(\"end\", () => resolve(read));\n });\n if (piped.trim()) yield piped.trim();\n })(),\n };\n }\n\n return {\n interactive: true,\n lines: (async function* () {\n if (input.task) yield input.task;\n const reader = createInterface({ input: stdin, output: process.stderr, prompt: \"› \" });\n reader.prompt();\n for await (const line of reader) {\n const said = line.trim();\n // A blank line is someone thinking, not an empty question.\n if (said) yield said;\n reader.prompt();\n }\n reader.close();\n })(),\n };\n}\n\n/**\n * The standard pair of channels for a terminal.\n *\n * The answer on stdout, the account of the run on stderr, so redirecting the\n * first captures the answer and nothing else. Written out identically in three\n * recipes before this existed, which is two more copies than a fact deserves.\n */\nexport function terminalSink(options: { detail?: Sink[\"detail\"] } = {}): Sink {\n return {\n write: (text) => process.stdout.write(text),\n status: (line) => process.stderr.write(line),\n tty: process.stderr.isTTY === true,\n ...(options.detail ? { detail: options.detail } : {}),\n };\n}\n"]}
@@ -62,7 +62,9 @@ export function createExecutor(input) {
62
62
  const context = {
63
63
  sessionId: input.sessionId, runId: input.runId, callId: call.callId,
64
64
  signal: signal ?? new AbortController().signal,
65
- enqueue: input.enqueue,
65
+ // Stamped here, and deliberately not taken from the caller: this is
66
+ // the one place that knows the sender without being told.
67
+ enqueue: (delivery) => input.enqueue({ ...delivery, from: { kind: "session", id: input.sessionId } }),
66
68
  report: (data) => input.report(call.callId, data),
67
69
  };
68
70
  const annotations = tool.spec.annotations;
@@ -1 +1 @@
1
- {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Decide, Tool, ToolContext, ToolExecutor } from \"./tool.js\";\nimport type { Delivery } from \"../session/entry.js\";\nimport type { ToolCall, ToolResult } from \"../session/entry.js\";\nimport type { ToolSpec } from \"./tool.js\";\n\nimport type { JsonValue } from \"../json.js\";\n\n/**\n * The one place a tool call becomes an effect: parse, validate, decide, run,\n * record. Failures are model-visible results, never throws — a model that gets\n * an exception learns nothing, while one that gets \"invalid arguments: x must\n * be a number\" fixes its next call.\n *\n * A denial is a result like any other, so the model reads why and can act on\n * it. Nothing here parks a batch waiting on a human: see `Decide`.\n */\nexport function createExecutor(input: {\n tools: readonly Tool[];\n decide?: Decide;\n sessionId: string;\n runId: string;\n enqueue(delivery: Delivery): void;\n report(callId: string, data: JsonValue): void;\n /** Adjacent read-only calls run together; this caps how many at once. */\n maxConcurrency?: number;\n}): ToolExecutor {\n const byName = new Map<string, Tool>();\n for (const tool of input.tools) {\n if (byName.has(tool.spec.name)) throw new Error(`duplicate tool '${tool.spec.name}'`);\n byName.set(tool.spec.name, tool);\n }\n const limit = input.maxConcurrency ?? 8;\n const list = (): readonly ToolSpec[] => [...byName.values()].map((tool) => tool.spec);\n const failed = (content: string): ToolResult => ({ content, isError: true });\n\n return {\n list,\n async execute({ calls, signal }) {\n interface Planned {\n callId: string;\n concurrent: boolean;\n run(): Promise<ToolResult>;\n }\n const planned: Planned[] = [];\n const seen = new Set<string>();\n\n for (const call of calls) {\n const settled = (result: ToolResult): Planned =>\n ({ callId: call.callId, concurrent: true, run: async () => result });\n\n if (seen.has(call.callId)) { planned.push(settled(failed(`call id '${call.callId}' was reused`))); continue; }\n seen.add(call.callId);\n\n const tool = byName.get(call.name);\n if (!tool) { planned.push(settled(failed(`Unknown tool '${call.name}'`))); continue; }\n\n let raw: unknown;\n try { raw = JSON.parse(call.arguments); }\n catch { planned.push(settled(failed(\"Tool arguments are not valid JSON\"))); continue; }\n\n const prepared = tool.prepare(raw);\n if (!prepared.ok) { planned.push(settled(prepared.error)); continue; }\n\n // Decisions always see parsed, schema-valid arguments — never the raw\n // string, and never before validation.\n const decision = input.decide\n ? await input.decide({\n tool: tool.spec, input: prepared.value.input,\n sessionId: input.sessionId, runId: input.runId, callId: call.callId,\n })\n : { action: \"execute\" as const };\n\n if (decision.action === \"reject\") {\n planned.push(settled(failed(`Tool use denied: ${decision.message}`)));\n continue;\n }\n\n const context: ToolContext = {\n sessionId: input.sessionId, runId: input.runId, callId: call.callId,\n signal: signal ?? new AbortController().signal,\n enqueue: input.enqueue,\n report: (data) => input.report(call.callId, data),\n };\n const annotations = tool.spec.annotations;\n planned.push({\n callId: call.callId,\n concurrent: annotations?.readOnly === true && annotations.sequential !== true,\n run: async () => {\n try { return await prepared.value.run(context); }\n catch (error) { return failed(error instanceof Error ? error.message : String(error)); }\n },\n });\n }\n\n const results: { callId: string; result: ToolResult }[] = [];\n let index = 0;\n while (index < planned.length) {\n if (signal?.aborted) {\n for (const item of planned.slice(index)) {\n results.push({ callId: item.callId, result: failed(\"Tool use cancelled\") });\n }\n break;\n }\n let end = index + 1;\n if (planned[index]!.concurrent) {\n while (end < planned.length && planned[end]!.concurrent && end - index < limit) end += 1;\n }\n const segment = planned.slice(index, end);\n const settled = await Promise.all(segment.map((item) => item.run()));\n segment.forEach((item, offset) => results.push({ callId: item.callId, result: settled[offset]! }));\n index = end;\n }\n return { results };\n },\n };\n}\n\nexport type { ToolCall };\n"]}
1
+ {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Decide, Tool, ToolContext, ToolExecutor } from \"./tool.js\";\nimport type { Delivery } from \"../session/entry.js\";\nimport type { ToolCall, ToolResult } from \"../session/entry.js\";\nimport type { ToolSpec } from \"./tool.js\";\n\nimport type { JsonValue } from \"../json.js\";\n\n/**\n * The one place a tool call becomes an effect: parse, validate, decide, run,\n * record. Failures are model-visible results, never throws — a model that gets\n * an exception learns nothing, while one that gets \"invalid arguments: x must\n * be a number\" fixes its next call.\n *\n * A denial is a result like any other, so the model reads why and can act on\n * it. Nothing here parks a batch waiting on a human: see `Decide`.\n */\nexport function createExecutor(input: {\n tools: readonly Tool[];\n decide?: Decide;\n sessionId: string;\n runId: string;\n enqueue(delivery: Delivery): void;\n report(callId: string, data: JsonValue): void;\n /** Adjacent read-only calls run together; this caps how many at once. */\n maxConcurrency?: number;\n}): ToolExecutor {\n const byName = new Map<string, Tool>();\n for (const tool of input.tools) {\n if (byName.has(tool.spec.name)) throw new Error(`duplicate tool '${tool.spec.name}'`);\n byName.set(tool.spec.name, tool);\n }\n const limit = input.maxConcurrency ?? 8;\n const list = (): readonly ToolSpec[] => [...byName.values()].map((tool) => tool.spec);\n const failed = (content: string): ToolResult => ({ content, isError: true });\n\n return {\n list,\n async execute({ calls, signal }) {\n interface Planned {\n callId: string;\n concurrent: boolean;\n run(): Promise<ToolResult>;\n }\n const planned: Planned[] = [];\n const seen = new Set<string>();\n\n for (const call of calls) {\n const settled = (result: ToolResult): Planned =>\n ({ callId: call.callId, concurrent: true, run: async () => result });\n\n if (seen.has(call.callId)) { planned.push(settled(failed(`call id '${call.callId}' was reused`))); continue; }\n seen.add(call.callId);\n\n const tool = byName.get(call.name);\n if (!tool) { planned.push(settled(failed(`Unknown tool '${call.name}'`))); continue; }\n\n let raw: unknown;\n try { raw = JSON.parse(call.arguments); }\n catch { planned.push(settled(failed(\"Tool arguments are not valid JSON\"))); continue; }\n\n const prepared = tool.prepare(raw);\n if (!prepared.ok) { planned.push(settled(prepared.error)); continue; }\n\n // Decisions always see parsed, schema-valid arguments — never the raw\n // string, and never before validation.\n const decision = input.decide\n ? await input.decide({\n tool: tool.spec, input: prepared.value.input,\n sessionId: input.sessionId, runId: input.runId, callId: call.callId,\n })\n : { action: \"execute\" as const };\n\n if (decision.action === \"reject\") {\n planned.push(settled(failed(`Tool use denied: ${decision.message}`)));\n continue;\n }\n\n const context: ToolContext = {\n sessionId: input.sessionId, runId: input.runId, callId: call.callId,\n signal: signal ?? new AbortController().signal,\n // Stamped here, and deliberately not taken from the caller: this is\n // the one place that knows the sender without being told.\n enqueue: (delivery) => input.enqueue({ ...delivery, from: { kind: \"session\", id: input.sessionId } }),\n report: (data) => input.report(call.callId, data),\n };\n const annotations = tool.spec.annotations;\n planned.push({\n callId: call.callId,\n concurrent: annotations?.readOnly === true && annotations.sequential !== true,\n run: async () => {\n try { return await prepared.value.run(context); }\n catch (error) { return failed(error instanceof Error ? error.message : String(error)); }\n },\n });\n }\n\n const results: { callId: string; result: ToolResult }[] = [];\n let index = 0;\n while (index < planned.length) {\n if (signal?.aborted) {\n for (const item of planned.slice(index)) {\n results.push({ callId: item.callId, result: failed(\"Tool use cancelled\") });\n }\n break;\n }\n let end = index + 1;\n if (planned[index]!.concurrent) {\n while (end < planned.length && planned[end]!.concurrent && end - index < limit) end += 1;\n }\n const segment = planned.slice(index, end);\n const settled = await Promise.all(segment.map((item) => item.run()));\n segment.forEach((item, offset) => results.push({ callId: item.callId, result: settled[offset]! }));\n index = end;\n }\n return { results };\n },\n };\n}\n\nexport type { ToolCall };\n"]}
@@ -24,8 +24,15 @@ export interface ToolContext {
24
24
  * This is the whole of agent-to-agent messaging: spawning a child, replying
25
25
  * to a parent and messaging a peer are the same call, and either the sending
26
26
  * turn commits with the delivery or neither happens.
27
+ *
28
+ * `from` is not yours to state. The executor stamps this session, because a
29
+ * tool that supplied its own sender could omit it — losing the provenance a
30
+ * recipient reads to tell a peer's message from its user's — or claim to be
31
+ * a session it is not. It was a value two callers each rebuilt from
32
+ * `sessionId`, which is a second field for a fact another field already
33
+ * determines.
27
34
  */
28
- enqueue(delivery: Delivery): void;
35
+ enqueue(delivery: Omit<Delivery, "from">): void;
29
36
  /** Progress for a live viewer. Never recovery state. */
30
37
  report(data: JsonValue): void;
31
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA0ExB;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAA4B,KAMrD;IACC,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAc,CAAC;IAC7D,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU;YACV,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjE,CAAC;QACF,OAAO,CAAC,GAAY;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,GAAG,CAAC;oBACT,OAAO,EAAE,yBAAyB,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBAChF,OAAO,EAAE,IAAI;iBACd,CAAC,CAAC;YACL,CAAC;YACD,OAAO,EAAE,CAAC;gBACR,KAAK,EAAE,MAAM,CAAC,IAAiB;gBAC/B,4DAA4D;gBAC5D,mEAAmE;gBACnE,GAAG,EAAE,KAAK,EAAE,OAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAyB,EAAE,OAAO,CAAC;aAC9F,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { Content } from \"../content.js\";\n\nimport type { Delivery, ToolCall, ToolResult } from \"../session/entry.js\";\nimport type { JsonValue } from \"../json.js\";\nimport { err, ok, type Result } from \"../result.js\";\nimport { z } from \"zod\";\n\n/** What a tool tells a model about itself. Declared here because a tool owns it. */\nexport interface ToolSpec {\n name: string;\n description: string;\n /** JSON Schema. */\n parameters: JsonValue;\n annotations?: { readOnly?: boolean; sequential?: boolean };\n}\n\nexport interface ToolContext {\n sessionId: string;\n runId: string;\n callId: string;\n signal: AbortSignal;\n /**\n * Deliver input to another session, committed with this run's own entries.\n * This is the whole of agent-to-agent messaging: spawning a child, replying\n * to a parent and messaging a peer are the same call, and either the sending\n * turn commits with the delivery or neither happens.\n */\n enqueue(delivery: Delivery): void;\n /** Progress for a live viewer. Never recovery state. */\n report(data: JsonValue): void;\n}\n\nexport interface Tool {\n spec: ToolSpec;\n /** Validates raw arguments once, before any authority decision sees them. */\n prepare(raw: unknown): Result<{ input: JsonValue; run(context: ToolContext): Promise<ToolResult> }, ToolResult>;\n}\n\n/**\n * The entire permission model.\n *\n * One function over parsed arguments, supplied by the application, which\n * already owns identity, tenancy and policy. There is no grant vocabulary, no\n * principal and no second batch-level hook: anything those expressed, a closure\n * expresses better.\n *\n * Two outcomes, not three. There was a `pause`, which parked the batch and\n * committed an approval request — and nothing could ever resolve it, because\n * resuming an exact batch is only possible in a harness we own, and three of\n * the four are not. An approval that works in one harness and dead-ends in the\n * others is worse than none. A call needing permission is rejected with a\n * message saying so; the application asks whoever approves, and their answer\n * arrives as ordinary input on the session, which is a path that already works\n * everywhere. `recipes/agent-service` shows the whole round trip.\n *\n * The gate does not weaken by being a rejection: this runs on every call and\n * cannot be routed around. What an application gives up is exact-argument\n * replay, since the model re-issues rather than resuming. An application that\n * needs the stronger thing keys its approval on the call id and a hash of the\n * parsed input, so a re-issue that differs misses and is refused again.\n */\nexport type Decide = (call: {\n tool: ToolSpec;\n input: JsonValue;\n sessionId: string;\n runId: string;\n callId: string;\n}) =>\n | { action: \"execute\" }\n | { action: \"reject\"; message: string }\n | Promise<{ action: \"execute\" } | { action: \"reject\"; message: string }>;\n\n/** What a harness is handed to reach application tools. */\nexport interface ToolExecutor {\n list(): readonly ToolSpec[];\n execute(input: { calls: readonly ToolCall[]; signal?: AbortSignal }):\n Promise<{ results: readonly { callId: string; result: ToolResult }[] }>;\n}\n\n/**\n * Declares a tool from a runtime schema. The argument type of `execute` is\n * derived from that schema, so there is never a hand-maintained interface to\n * keep in step with the validation.\n */\nexport function defineTool<TSchema extends z.ZodType>(input: {\n name: string;\n description: string;\n schema: TSchema;\n annotations?: ToolSpec[\"annotations\"];\n execute(args: z.output<TSchema>, context: ToolContext): ToolResult | Promise<ToolResult>;\n}): Tool {\n const parameters = z.toJSONSchema(input.schema) as JsonValue;\n return Object.freeze({\n spec: Object.freeze({\n name: input.name,\n description: input.description,\n parameters,\n ...(input.annotations ? { annotations: input.annotations } : {}),\n }),\n prepare(raw: unknown) {\n const parsed = input.schema.safeParse(raw);\n if (!parsed.success) {\n return err({\n content: `Invalid arguments for ${input.name}: ${z.prettifyError(parsed.error)}`,\n isError: true,\n });\n }\n return ok({\n input: parsed.data as JsonValue,\n // Throws are normalized by the executor, which has to cover\n // hand-written tools anyway; catching here too would be dead code.\n run: async (context: ToolContext) => input.execute(parsed.data as z.output<TSchema>, context),\n });\n },\n });\n}\n\n/** Re-exported for the same reason the model port re-exports its own. */\nexport type { Content, ToolResult };\n"]}
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiFxB;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAA4B,KAMrD;IACC,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAc,CAAC;IAC7D,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU;YACV,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjE,CAAC;QACF,OAAO,CAAC,GAAY;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,GAAG,CAAC;oBACT,OAAO,EAAE,yBAAyB,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBAChF,OAAO,EAAE,IAAI;iBACd,CAAC,CAAC;YACL,CAAC;YACD,OAAO,EAAE,CAAC;gBACR,KAAK,EAAE,MAAM,CAAC,IAAiB;gBAC/B,4DAA4D;gBAC5D,mEAAmE;gBACnE,GAAG,EAAE,KAAK,EAAE,OAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAyB,EAAE,OAAO,CAAC;aAC9F,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { Content } from \"../content.js\";\n\nimport type { Delivery, ToolCall, ToolResult } from \"../session/entry.js\";\nimport type { JsonValue } from \"../json.js\";\nimport { err, ok, type Result } from \"../result.js\";\nimport { z } from \"zod\";\n\n/** What a tool tells a model about itself. Declared here because a tool owns it. */\nexport interface ToolSpec {\n name: string;\n description: string;\n /** JSON Schema. */\n parameters: JsonValue;\n annotations?: { readOnly?: boolean; sequential?: boolean };\n}\n\nexport interface ToolContext {\n sessionId: string;\n runId: string;\n callId: string;\n signal: AbortSignal;\n /**\n * Deliver input to another session, committed with this run's own entries.\n * This is the whole of agent-to-agent messaging: spawning a child, replying\n * to a parent and messaging a peer are the same call, and either the sending\n * turn commits with the delivery or neither happens.\n *\n * `from` is not yours to state. The executor stamps this session, because a\n * tool that supplied its own sender could omit it — losing the provenance a\n * recipient reads to tell a peer's message from its user's — or claim to be\n * a session it is not. It was a value two callers each rebuilt from\n * `sessionId`, which is a second field for a fact another field already\n * determines.\n */\n enqueue(delivery: Omit<Delivery, \"from\">): void;\n /** Progress for a live viewer. Never recovery state. */\n report(data: JsonValue): void;\n}\n\nexport interface Tool {\n spec: ToolSpec;\n /** Validates raw arguments once, before any authority decision sees them. */\n prepare(raw: unknown): Result<{ input: JsonValue; run(context: ToolContext): Promise<ToolResult> }, ToolResult>;\n}\n\n/**\n * The entire permission model.\n *\n * One function over parsed arguments, supplied by the application, which\n * already owns identity, tenancy and policy. There is no grant vocabulary, no\n * principal and no second batch-level hook: anything those expressed, a closure\n * expresses better.\n *\n * Two outcomes, not three. There was a `pause`, which parked the batch and\n * committed an approval request — and nothing could ever resolve it, because\n * resuming an exact batch is only possible in a harness we own, and three of\n * the four are not. An approval that works in one harness and dead-ends in the\n * others is worse than none. A call needing permission is rejected with a\n * message saying so; the application asks whoever approves, and their answer\n * arrives as ordinary input on the session, which is a path that already works\n * everywhere. `recipes/agent-service` shows the whole round trip.\n *\n * The gate does not weaken by being a rejection: this runs on every call and\n * cannot be routed around. What an application gives up is exact-argument\n * replay, since the model re-issues rather than resuming. An application that\n * needs the stronger thing keys its approval on the call id and a hash of the\n * parsed input, so a re-issue that differs misses and is refused again.\n */\nexport type Decide = (call: {\n tool: ToolSpec;\n input: JsonValue;\n sessionId: string;\n runId: string;\n callId: string;\n}) =>\n | { action: \"execute\" }\n | { action: \"reject\"; message: string }\n | Promise<{ action: \"execute\" } | { action: \"reject\"; message: string }>;\n\n/** What a harness is handed to reach application tools. */\nexport interface ToolExecutor {\n list(): readonly ToolSpec[];\n execute(input: { calls: readonly ToolCall[]; signal?: AbortSignal }):\n Promise<{ results: readonly { callId: string; result: ToolResult }[] }>;\n}\n\n/**\n * Declares a tool from a runtime schema. The argument type of `execute` is\n * derived from that schema, so there is never a hand-maintained interface to\n * keep in step with the validation.\n */\nexport function defineTool<TSchema extends z.ZodType>(input: {\n name: string;\n description: string;\n schema: TSchema;\n annotations?: ToolSpec[\"annotations\"];\n execute(args: z.output<TSchema>, context: ToolContext): ToolResult | Promise<ToolResult>;\n}): Tool {\n const parameters = z.toJSONSchema(input.schema) as JsonValue;\n return Object.freeze({\n spec: Object.freeze({\n name: input.name,\n description: input.description,\n parameters,\n ...(input.annotations ? { annotations: input.annotations } : {}),\n }),\n prepare(raw: unknown) {\n const parsed = input.schema.safeParse(raw);\n if (!parsed.success) {\n return err({\n content: `Invalid arguments for ${input.name}: ${z.prettifyError(parsed.error)}`,\n isError: true,\n });\n }\n return ok({\n input: parsed.data as JsonValue,\n // Throws are normalized by the executor, which has to cover\n // hand-written tools anyway; catching here too would be dead code.\n run: async (context: ToolContext) => input.execute(parsed.data as z.output<TSchema>, context),\n });\n },\n });\n}\n\n/** Re-exported for the same reason the model port re-exports its own. */\nexport type { Content, ToolResult };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a-dray/aglib",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "A small TypeScript toolkit for building your own agent harness.",
6
6
  "license": "MIT",
@@ -27,6 +27,14 @@
27
27
  "types": "./dist/index.d.ts",
28
28
  "import": "./dist/index.js"
29
29
  },
30
+ "./render": {
31
+ "types": "./dist/render.d.ts",
32
+ "import": "./dist/render.js"
33
+ },
34
+ "./terminal": {
35
+ "types": "./dist/terminal.d.ts",
36
+ "import": "./dist/terminal.js"
37
+ },
30
38
  "./session": {
31
39
  "types": "./dist/session/index.d.ts",
32
40
  "import": "./dist/session/index.js"
@@ -113,6 +121,7 @@
113
121
  "recipe": "bun run build && bun scripts/recipe.ts",
114
122
  "docs": "bun scripts/docs.ts",
115
123
  "docs:check": "bun scripts/docs.ts --check",
124
+ "release": "bun scripts/release.ts",
116
125
  "node:verify": "node scripts/node-verify.mjs",
117
126
  "prepack": "bun run build"
118
127
  }