smithers-orchestrator 0.22.0 → 0.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smithers-orchestrator",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "Public Smithers facade for durable coding-agent workflows and Gateway operation",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -35,15 +35,20 @@
35
35
  "import": "./src/control-plane.js",
36
36
  "default": "./src/control-plane.js"
37
37
  },
38
- "./*": {
39
- "types": "./src/index.d.ts",
40
- "import": "./src/*.js",
41
- "default": "./src/*.js"
38
+ "./jsx-runtime": {
39
+ "types": "./src/jsx-runtime.d.ts",
40
+ "import": "./src/jsx-runtime.js",
41
+ "default": "./src/jsx-runtime.js"
42
42
  },
43
43
  "./jsx-dev-runtime": {
44
- "types": "./src/index.d.ts",
44
+ "types": "./src/jsx-runtime.d.ts",
45
45
  "import": "./src/jsx-runtime.js",
46
46
  "default": "./src/jsx-runtime.js"
47
+ },
48
+ "./*": {
49
+ "types": "./src/index.d.ts",
50
+ "import": "./src/*.js",
51
+ "default": "./src/*.js"
47
52
  }
48
53
  },
49
54
  "files": [
@@ -61,27 +66,33 @@
61
66
  "incur": "^0.4.1",
62
67
  "react": "^19.2.5",
63
68
  "zod": "^4.3.6",
64
- "@smithers-orchestrator/agents": "0.22.0",
65
- "@smithers-orchestrator/cli": "0.22.0",
66
- "@smithers-orchestrator/components": "0.22.0",
67
- "@smithers-orchestrator/control-plane": "0.22.0",
68
- "@smithers-orchestrator/driver": "0.22.0",
69
- "@smithers-orchestrator/engine": "0.22.0",
70
- "@smithers-orchestrator/gateway-client": "0.22.0",
71
- "@smithers-orchestrator/errors": "0.22.0",
72
- "@smithers-orchestrator/gateway-react": "0.22.0",
73
- "@smithers-orchestrator/graph": "0.22.0",
74
- "@smithers-orchestrator/db": "0.22.0",
75
- "@smithers-orchestrator/memory": "0.22.0",
76
- "@smithers-orchestrator/observability": "0.22.0",
77
- "@smithers-orchestrator/openapi": "0.22.0",
78
- "@smithers-orchestrator/react-reconciler": "0.22.0",
79
- "@smithers-orchestrator/sandbox": "0.22.0",
80
- "@smithers-orchestrator/scorers": "0.22.0",
81
- "@smithers-orchestrator/scheduler": "0.22.0",
82
- "@smithers-orchestrator/server": "0.22.0",
83
- "@smithers-orchestrator/time-travel": "0.22.0",
84
- "@smithers-orchestrator/vcs": "0.22.0"
69
+ "@smithers-orchestrator/agents": "0.24.0",
70
+ "@smithers-orchestrator/components": "0.24.0",
71
+ "@smithers-orchestrator/control-plane": "0.24.0",
72
+ "@smithers-orchestrator/driver": "0.24.0",
73
+ "@smithers-orchestrator/db": "0.24.0",
74
+ "@smithers-orchestrator/engine": "0.24.0",
75
+ "@smithers-orchestrator/errors": "0.24.0",
76
+ "@smithers-orchestrator/gateway-client": "0.24.0",
77
+ "@smithers-orchestrator/gateway-react": "0.24.0",
78
+ "@smithers-orchestrator/graph": "0.24.0",
79
+ "@smithers-orchestrator/memory": "0.24.0",
80
+ "@smithers-orchestrator/observability": "0.24.0",
81
+ "@smithers-orchestrator/openapi": "0.24.0",
82
+ "@smithers-orchestrator/react-reconciler": "0.24.0",
83
+ "@smithers-orchestrator/sandbox": "0.24.0",
84
+ "@smithers-orchestrator/scheduler": "0.24.0",
85
+ "@smithers-orchestrator/scorers": "0.24.0",
86
+ "@smithers-orchestrator/server": "0.24.0",
87
+ "@smithers-orchestrator/time-travel": "0.24.0",
88
+ "@smithers-orchestrator/vcs": "0.24.0",
89
+ "@smithers-orchestrator/tool-context": "0.24.0",
90
+ "@smithers-orchestrator/cli": "0.24.0"
91
+ },
92
+ "optionalDependencies": {
93
+ "@electric-sql/pglite": "0.5.1",
94
+ "@electric-sql/pglite-socket": "0.2.1",
95
+ "pg": "^8.13.1"
85
96
  },
86
97
  "devDependencies": {
87
98
  "@types/bun": "latest",
@@ -0,0 +1,19 @@
1
+ import { createSmithers } from "../index.js";
2
+ import { z } from "zod";
3
+ import type { AgentLike } from "@smithers-orchestrator/agents";
4
+
5
+ const agent = {
6
+ generate: async () => ({ ok: true }),
7
+ } satisfies AgentLike;
8
+
9
+ const { Task, outputs } = createSmithers({
10
+ next: z.object({
11
+ ok: z.boolean(),
12
+ }),
13
+ });
14
+
15
+ const _TaskForkJsx = (
16
+ <Task id="next" output={outputs.next} agent={agent} fork="source">
17
+ Continue from the source task.
18
+ </Task>
19
+ );
package/src/create.js CHANGED
@@ -14,8 +14,11 @@ import { Approval as BaseApproval, Workflow as BaseWorkflow, Task as BaseTask, S
14
14
  import { zodToTable } from "@smithers-orchestrator/db/zodToTable";
15
15
  import { zodToCreateTableSQL, syncZodTableSchema } from "@smithers-orchestrator/db/zodToCreateTableSQL";
16
16
  import { camelToSnake } from "@smithers-orchestrator/db/utils/camelToSnake";
17
- import { resolve } from "node:path";
17
+ import { SmithersDb } from "@smithers-orchestrator/db/adapter";
18
+ import { POSTGRES } from "@smithers-orchestrator/db/dialect";
19
+ import { resolve, join } from "node:path";
18
20
  import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
21
+ import { findSmithersAnchorDir } from "./findSmithersAnchorDir.js";
19
22
  /** @typedef {import("@smithers-orchestrator/components").ApprovalProps<any, any>} ApprovalProps */
20
23
  /** @typedef {import("@smithers-orchestrator/components").SandboxProps} SandboxProps */
21
24
  /** @typedef {import("@smithers-orchestrator/components").SignalProps<any>} SignalProps */
@@ -173,43 +176,14 @@ function mergeAlertPolicies(base, override) {
173
176
  return merged;
174
177
  }
175
178
  /**
176
- * Schema-driven API users define only Zod schemas, the framework owns the entire storage layer.
177
- *
178
- * @template {Record<string, import("zod").ZodObject<any>>} Schemas
179
- * @param {Schemas} schemas
180
- * @param {CreateSmithersOptions} [opts]
181
- * @returns {import("./CreateSmithersApi.ts").CreateSmithersApi<Schemas>}
182
- *
183
- * @example
184
- * ```ts
185
- * const { Workflow, Task, smithers, outputs } = createSmithers({
186
- * discover: discoverOutputSchema,
187
- * research: researchOutputSchema,
188
- * });
179
+ * Generate the Drizzle table metadata, schema registry, and output refs shared by
180
+ * every backend. The Drizzle tables carry only column/name metadata — the actual
181
+ * storage is created per-dialect by the caller; dialect-aware engine reads consult
182
+ * these objects via getTableColumns/getTableName, never to issue SQLite queries.
189
183
  *
190
- * export default smithers((ctx) => (
191
- * <Workflow name="my-workflow">
192
- * <Task id="discover" output={outputs.discover} agent={myAgent}>...</Task>
193
- * </Workflow>
194
- * ));
195
- * ```
184
+ * @param {Record<string, any>} schemas
196
185
  */
197
- export function createSmithers(schemas, opts) {
198
- const dbPath = opts?.dbPath ?? "./smithers.db";
199
- const absDbPath = resolve(process.cwd(), dbPath);
200
- if (process.env.SMITHERS_HOT === "1") {
201
- const sig = computeSchemaSig(schemas, absDbPath);
202
- const cached = hotCache.get(absDbPath);
203
- if (cached) {
204
- if (cached.schemaSig !== sig) {
205
- throw new SmithersError("SCHEMA_CHANGE_HOT", "[smithers hot] Schema change detected; restart required to apply schema changes.");
206
- }
207
- cached.setModuleAlertPolicy(opts?.alertPolicy);
208
- return cached.api;
209
- }
210
- // Will cache after creating the API below
211
- }
212
- // 1. Generate Drizzle tables from Zod schemas
186
+ function prepareSmithersTables(schemas) {
213
187
  const tables = {};
214
188
  const inputTable = schemas.input
215
189
  ? zodToTable("input", schemas.input, { isInput: true })
@@ -223,77 +197,39 @@ export function createSmithers(schemas, opts) {
223
197
  const tableName = camelToSnake(name);
224
198
  tables[name] = zodToTable(tableName, zodSchema);
225
199
  }
226
- // 2. Create SQLite db
227
- const sqlite = new Database(dbPath);
228
- sqlite.run(`PRAGMA journal_mode = ${opts?.journalMode ?? "WAL"}`);
229
- // 30s timeout: concurrent worktrees each spawn agent processes that all write
230
- // to smithers.db simultaneously. 5s is too short and causes SQLITE_IOERR_VNODE
231
- // on macOS when the VFS can't acquire the WAL shared-memory lock in time.
232
- sqlite.run("PRAGMA busy_timeout = 30000");
233
- // NORMAL is safe in WAL mode (no data loss on crash) and reduces fsync
234
- // stalls that contribute to WAL checkpoint contention across processes.
235
- sqlite.run("PRAGMA synchronous = NORMAL");
236
- // Ensure no exclusive lock is held, allowing multiple readers/writers.
237
- sqlite.run("PRAGMA locking_mode = NORMAL");
238
- sqlite.run("PRAGMA foreign_keys = ON");
239
- // Register a process-exit hook to explicitly close the Database.
240
- // bun:sqlite's GC finalizer calls sqlite3_close() which fatally aborts if
241
- // Drizzle's cached prepared statements haven't been finalized first.
242
- // Calling close() ourselves lets sqlite3 finalize everything gracefully.
243
- let dbClosed = false;
244
- const closeDb = () => {
245
- if (dbClosed)
246
- return;
247
- dbClosed = true;
248
- try {
249
- sqlite.close();
250
- }
251
- catch { }
252
- process.removeListener("exit", closeDb);
253
- };
254
- process.once("exit", closeDb);
255
- // 3. Auto-create tables, and ALTER any existing tables to add columns the
256
- // current schema introduced (CREATE TABLE IF NOT EXISTS would silently
257
- // skip the columns and a later upsert would fail with "no column named X").
258
- if (schemas.input) {
259
- syncZodTableSchema(sqlite, "input", schemas.input, { isInput: true });
260
- }
261
- else {
262
- sqlite.exec(`CREATE TABLE IF NOT EXISTS "input" (run_id TEXT PRIMARY KEY, payload TEXT)`);
263
- try {
264
- const cols = sqlite.query(`PRAGMA table_info("input")`).all();
265
- const hasPayload = cols.some((col) => col?.name === "payload");
266
- if (!hasPayload) {
267
- sqlite.run(`ALTER TABLE "input" ADD COLUMN payload TEXT`);
268
- }
269
- }
270
- catch {
271
- // ignore - older SQLite or permission issues; input payload remains best-effort
272
- }
273
- }
274
- for (const [name, zodSchema] of Object.entries(schemas)) {
275
- if (name === "input")
276
- continue;
277
- const tableName = camelToSnake(name);
278
- syncZodTableSchema(sqlite, tableName, zodSchema);
279
- }
280
- // 4. Create Drizzle instance with all tables in the schema
281
200
  const drizzleSchema = { input: inputTable };
282
201
  for (const [key, table] of Object.entries(tables)) {
283
202
  drizzleSchema[key] = table;
284
203
  }
285
- const db = drizzle(sqlite, { schema: drizzleSchema });
286
- // 5. Build schema registry for engine resolution of string output keys
287
204
  const schemaRegistry = new Map();
288
205
  for (const [name, zodSchema] of Object.entries(schemas)) {
289
206
  if (name === "input")
290
207
  continue;
291
208
  schemaRegistry.set(name, { table: tables[name], zodSchema });
292
209
  }
293
- // 6. Build output refs and reverse lookup: ZodObject reference → schema key name
294
210
  const { outputs, zodToKeyName, ambiguousZodSchemas } = prepareOutputSchemas(schemas);
295
- // 7. Context + hooks
296
- const { SmithersContext: RuntimeSmithersContext, useCtx, } = createSmithersContext();
211
+ return { tables, inputTable, drizzleSchema, schemaRegistry, outputs, zodToKeyName, ambiguousZodSchemas };
212
+ }
213
+ /**
214
+ * Construct the public createSmithers API object around a prepared database
215
+ * handle and shared table metadata. Backend-agnostic: `db` is either a Drizzle
216
+ * bun:sqlite instance or a Postgres descriptor; every engine read/write below it
217
+ * is dialect-aware.
218
+ *
219
+ * @param {{
220
+ * db: unknown;
221
+ * tables: Record<string, unknown>;
222
+ * schemaRegistry: Map<string, unknown>;
223
+ * outputs: Record<string, unknown>;
224
+ * zodToKeyName: Map<unknown, string>;
225
+ * ambiguousZodSchemas: Set<unknown>;
226
+ * opts?: CreateSmithersOptions;
227
+ * inputSchema?: unknown;
228
+ * }} config
229
+ */
230
+ function buildSmithersApi(config) {
231
+ const { db, tables, schemaRegistry, outputs, zodToKeyName, ambiguousZodSchemas, opts, inputSchema } = config;
232
+ const { SmithersContext: RuntimeSmithersContext, useCtx } = createSmithersContext();
297
233
  const ctxRef = { current: null };
298
234
  let moduleAlertPolicy = opts?.alertPolicy;
299
235
  /**
@@ -352,9 +288,8 @@ export function createSmithers(schemas, opts) {
352
288
  });
353
289
  }
354
290
  /**
355
- * @param {(ctx: SmithersCtx<Schemas>) => React.ReactElement} build
291
+ * @param {(ctx: SmithersCtx<any>) => React.ReactElement} build
356
292
  * @param {SmithersWorkflowOptions} [smithersOpts]
357
- * @returns {SmithersWorkflow<Schemas>}
358
293
  */
359
294
  function boundSmithers(build, smithersOpts) {
360
295
  const workflowOpts = {
@@ -372,6 +307,7 @@ export function createSmithers(schemas, opts) {
372
307
  return React.createElement(RuntimeSmithersContext.Provider, { value: ctxRef.current }, React.createElement(GlobalSmithersContext.Provider, { value: ctxRef.current }, build(ctx)));
373
308
  },
374
309
  opts: workflowOpts,
310
+ inputSchema,
375
311
  schemaRegistry,
376
312
  zodToKeyName,
377
313
  ambiguousZodSchemas,
@@ -402,9 +338,122 @@ export function createSmithers(schemas, opts) {
402
338
  useCtx,
403
339
  smithers: boundSmithers,
404
340
  db,
405
- tables: tables,
341
+ tables,
406
342
  outputs,
407
343
  };
344
+ return { api, setModuleAlertPolicy };
345
+ }
346
+ /**
347
+ * Schema-driven API — users define only Zod schemas, the framework owns the entire storage layer.
348
+ *
349
+ * @template {Record<string, import("zod").ZodObject<any>>} Schemas
350
+ * @param {Schemas} schemas
351
+ * @param {CreateSmithersOptions} [opts]
352
+ * @returns {import("./CreateSmithersApi.ts").CreateSmithersApi<Schemas>}
353
+ *
354
+ * @example
355
+ * ```ts
356
+ * const { Workflow, Task, smithers, outputs } = createSmithers({
357
+ * discover: discoverOutputSchema,
358
+ * research: researchOutputSchema,
359
+ * });
360
+ *
361
+ * export default smithers((ctx) => (
362
+ * <Workflow name="my-workflow">
363
+ * <Task id="discover" output={outputs.discover} agent={myAgent}>...</Task>
364
+ * </Workflow>
365
+ * ));
366
+ * ```
367
+ */
368
+ export function createSmithers(schemas, opts) {
369
+ // Resolve the DB path from the nearest .smithers/ anchor so that running a
370
+ // workflow from a subdirectory always creates/uses the project-root DB, not
371
+ // a new one at CWD. An explicit opts.dbPath overrides this entirely.
372
+ const anchorDir = findSmithersAnchorDir(process.cwd());
373
+ const defaultDbPath = anchorDir ? join(anchorDir, "smithers.db") : "./smithers.db";
374
+ const dbPath = opts?.dbPath ?? defaultDbPath;
375
+ const absDbPath = resolve(process.cwd(), dbPath);
376
+ if (process.env.SMITHERS_HOT === "1") {
377
+ const sig = computeSchemaSig(schemas, absDbPath);
378
+ const cached = hotCache.get(absDbPath);
379
+ if (cached) {
380
+ if (cached.schemaSig !== sig) {
381
+ throw new SmithersError("SCHEMA_CHANGE_HOT", "[smithers hot] Schema change detected; restart required to apply schema changes.");
382
+ }
383
+ cached.setModuleAlertPolicy(opts?.alertPolicy);
384
+ return cached.api;
385
+ }
386
+ // Will cache after creating the API below
387
+ }
388
+ // 1. Generate Drizzle tables + schema metadata from Zod schemas.
389
+ const { tables, drizzleSchema, schemaRegistry, outputs, zodToKeyName, ambiguousZodSchemas } = prepareSmithersTables(schemas);
390
+ // 2. Create SQLite db
391
+ const sqlite = new Database(dbPath);
392
+ sqlite.run(`PRAGMA journal_mode = ${opts?.journalMode ?? "WAL"}`);
393
+ // 30s timeout: concurrent worktrees each spawn agent processes that all write
394
+ // to smithers.db simultaneously. 5s is too short and causes SQLITE_IOERR_VNODE
395
+ // on macOS when the VFS can't acquire the WAL shared-memory lock in time.
396
+ sqlite.run("PRAGMA busy_timeout = 30000");
397
+ // NORMAL is safe in WAL mode (no data loss on crash) and reduces fsync
398
+ // stalls that contribute to WAL checkpoint contention across processes.
399
+ sqlite.run("PRAGMA synchronous = NORMAL");
400
+ // Ensure no exclusive lock is held, allowing multiple readers/writers.
401
+ sqlite.run("PRAGMA locking_mode = NORMAL");
402
+ sqlite.run("PRAGMA foreign_keys = ON");
403
+ // Register a process-exit hook to explicitly close the Database.
404
+ // bun:sqlite's GC finalizer calls sqlite3_close() which fatally aborts if
405
+ // Drizzle's cached prepared statements haven't been finalized first.
406
+ // Calling close() ourselves lets sqlite3 finalize everything gracefully.
407
+ let dbClosed = false;
408
+ const closeDb = () => {
409
+ if (dbClosed)
410
+ return;
411
+ dbClosed = true;
412
+ try {
413
+ sqlite.close();
414
+ }
415
+ catch { }
416
+ process.removeListener("exit", closeDb);
417
+ };
418
+ process.once("exit", closeDb);
419
+ // 3. Auto-create tables, and ALTER any existing tables to add columns the
420
+ // current schema introduced (CREATE TABLE IF NOT EXISTS would silently
421
+ // skip the columns and a later upsert would fail with "no column named X").
422
+ if (schemas.input) {
423
+ syncZodTableSchema(sqlite, "input", schemas.input, { isInput: true });
424
+ }
425
+ else {
426
+ sqlite.exec(`CREATE TABLE IF NOT EXISTS "input" (run_id TEXT PRIMARY KEY, payload TEXT)`);
427
+ try {
428
+ const cols = sqlite.query(`PRAGMA table_info("input")`).all();
429
+ const hasPayload = cols.some((col) => col?.name === "payload");
430
+ if (!hasPayload) {
431
+ sqlite.run(`ALTER TABLE "input" ADD COLUMN payload TEXT`);
432
+ }
433
+ }
434
+ catch {
435
+ // ignore - older SQLite or permission issues; input payload remains best-effort
436
+ }
437
+ }
438
+ for (const [name, zodSchema] of Object.entries(schemas)) {
439
+ if (name === "input")
440
+ continue;
441
+ const tableName = camelToSnake(name);
442
+ syncZodTableSchema(sqlite, tableName, zodSchema);
443
+ }
444
+ // 4. Create Drizzle instance with all tables in the schema
445
+ const db = drizzle(sqlite, { schema: drizzleSchema });
446
+ // 5. Build the public API around the prepared db + table metadata.
447
+ const { api, setModuleAlertPolicy } = buildSmithersApi({
448
+ db,
449
+ tables,
450
+ schemaRegistry,
451
+ outputs,
452
+ zodToKeyName,
453
+ ambiguousZodSchemas,
454
+ opts,
455
+ inputSchema: schemas.input,
456
+ });
408
457
  if (process.env.SMITHERS_HOT === "1") {
409
458
  const sig = computeSchemaSig(schemas, absDbPath);
410
459
  hotCache.set(absDbPath, {
@@ -415,3 +464,117 @@ export function createSmithers(schemas, opts) {
415
464
  }
416
465
  return api;
417
466
  }
467
+ /**
468
+ * PostgreSQL/PGlite-backed equivalent of {@link createSmithers}. Asynchronous
469
+ * because connecting and provisioning schema over the wire is async (unlike the
470
+ * synchronous bun:sqlite path). Boots a node-postgres connection (`provider:
471
+ * "postgres"`) or an embedded PGlite over a local socket (`provider: "pglite"`),
472
+ * provisions the durable engine schema + the per-Zod-schema output tables with
473
+ * Postgres-typed DDL, and returns the same createSmithers API surface plus a
474
+ * `close()` teardown for the connection.
475
+ *
476
+ * @template {Record<string, import("zod").ZodObject<any>>} Schemas
477
+ * @param {Schemas} schemas
478
+ * @param {CreateSmithersOptions & ({ provider: "postgres"; connectionString?: string; connection?: object } | { provider: "pglite"; dataDir?: string })} opts
479
+ * @returns {Promise<import("./CreateSmithersApi.ts").CreateSmithersApi<Schemas> & { close: () => Promise<void> }>}
480
+ */
481
+ export async function createSmithersPostgres(schemas, opts) {
482
+ const provider = opts?.provider ?? "postgres";
483
+ // 1. Generate Drizzle tables + schema metadata from Zod schemas (shared).
484
+ const { tables, drizzleSchema, schemaRegistry, outputs, zodToKeyName, ambiguousZodSchemas } = prepareSmithersTables(schemas);
485
+ // 2. Boot the Postgres/PGlite connection.
486
+ const pgModule = await import("pg");
487
+ const pg = pgModule.default ?? pgModule;
488
+ // BIGINT (ms timestamps, counters) → JS number, matching SQLite's behavior.
489
+ pg.types.setTypeParser(20, (value) => (value === null ? null : Number(value)));
490
+ /** @type {Array<() => Promise<void>>} */
491
+ const teardown = [];
492
+ let connectionString = opts?.connectionString;
493
+ if (provider === "pglite") {
494
+ const { PGlite } = await import("@electric-sql/pglite");
495
+ const { PGLiteSocketServer } = await import("@electric-sql/pglite-socket");
496
+ const pglite = await PGlite.create(opts?.dataDir || undefined);
497
+ const port = await findFreePgPort();
498
+ const server = new PGLiteSocketServer({ db: pglite, host: "127.0.0.1", port, maxConnections: 5 });
499
+ await server.start();
500
+ teardown.push(async () => {
501
+ await server.stop().catch(() => {});
502
+ await pglite.close().catch(() => {});
503
+ });
504
+ connectionString = `postgres://postgres@127.0.0.1:${port}/postgres`;
505
+ }
506
+ const client = new pg.Client(connectionString ? { connectionString } : opts?.connection);
507
+ /** @type {{ api: import("./CreateSmithersApi.ts").CreateSmithersApi<Schemas> }} */
508
+ let built;
509
+ try {
510
+ await client.connect();
511
+ teardown.push(async () => {
512
+ await client.end().catch(() => {});
513
+ });
514
+ // 3. Postgres descriptor consumed by the engine + adapter. The Drizzle table
515
+ // objects (snake_case columns identical to the DDL below) are attached only
516
+ // for column/name metadata; the engine's reads/writes against this descriptor
517
+ // are dialect-aware and go through the @effect/sql adapter or raw $n queries.
518
+ const descriptor = { dialect: "postgres", connection: client, schema: drizzleSchema };
519
+ const adapter = new SmithersDb(descriptor);
520
+ // 4. Durable engine schema (idempotent), then the input + output tables with
521
+ // Postgres-typed DDL derived from the Zod schemas.
522
+ await adapter.internalStorage.ensureSchema();
523
+ if (schemas.input) {
524
+ await client.query({ text: zodToCreateTableSQL("input", schemas.input, { isInput: true, dialect: POSTGRES }) });
525
+ }
526
+ else {
527
+ await client.query({ text: `CREATE TABLE IF NOT EXISTS "input" (run_id TEXT PRIMARY KEY, payload TEXT)` });
528
+ }
529
+ for (const [name, zodSchema] of Object.entries(schemas)) {
530
+ if (name === "input")
531
+ continue;
532
+ const tableName = camelToSnake(name);
533
+ await client.query({ text: zodToCreateTableSQL(tableName, zodSchema, { dialect: POSTGRES }) });
534
+ }
535
+ // 5. Build the public API around the descriptor + table metadata.
536
+ built = buildSmithersApi({
537
+ db: descriptor,
538
+ tables,
539
+ schemaRegistry,
540
+ outputs,
541
+ zodToKeyName,
542
+ ambiguousZodSchemas,
543
+ opts,
544
+ inputSchema: schemas.input,
545
+ });
546
+ }
547
+ catch (e) {
548
+ // Drain any teardown registered so far (socket server, pg client) so a
549
+ // failure after boot does not leak the port / connection.
550
+ for (const fn of teardown.reverse()) {
551
+ await fn().catch(() => {});
552
+ }
553
+ throw e;
554
+ }
555
+ const { api } = built;
556
+ return {
557
+ ...api,
558
+ close: async () => {
559
+ for (const fn of teardown.reverse()) {
560
+ await fn();
561
+ }
562
+ },
563
+ };
564
+ }
565
+ /**
566
+ * @returns {Promise<number>}
567
+ */
568
+ async function findFreePgPort() {
569
+ const net = await import("node:net");
570
+ return new Promise((resolveFn, reject) => {
571
+ const srv = net.createServer();
572
+ srv.unref();
573
+ srv.on("error", reject);
574
+ srv.listen(0, "127.0.0.1", () => {
575
+ const address = srv.address();
576
+ const port = typeof address === "object" && address ? address.port : 0;
577
+ srv.close(() => resolveFn(port));
578
+ });
579
+ });
580
+ }
@@ -0,0 +1,38 @@
1
+ import { resolve, dirname, join } from "node:path";
2
+ import { existsSync, statSync } from "node:fs";
3
+
4
+ /**
5
+ * Walk upward from `from` and return the nearest directory that contains a
6
+ * `.smithers/` subdirectory, or `undefined` if none is found before the
7
+ * filesystem root.
8
+ *
9
+ * Directories at or above $HOME are excluded: a `~/.smithers` global pack
10
+ * must not be treated as a project anchor, so the DB would incorrectly land
11
+ * in the user's home directory.
12
+ *
13
+ * @param {string} from
14
+ * @returns {string | undefined}
15
+ */
16
+ export function findSmithersAnchorDir(from) {
17
+ let dir = resolve(from);
18
+ const fsRoot = resolve("/");
19
+ const home = process.env.HOME ? resolve(process.env.HOME) : undefined;
20
+ while (true) {
21
+ // Stop at or above HOME — anchors must be proper project directories
22
+ // strictly below the user's home directory.
23
+ // Use startsWith rather than length comparison so that symlinks and
24
+ // unusual mount points (e.g. /home2) don't produce false positives.
25
+ if (home && (dir === home || !dir.startsWith(home + "/"))) {
26
+ return undefined;
27
+ }
28
+ // Guard: never scan the filesystem root itself.
29
+ if (dir === fsRoot) {
30
+ return undefined;
31
+ }
32
+ const candidate = join(dir, ".smithers");
33
+ if (existsSync(candidate) && statSync(candidate).isDirectory()) {
34
+ return dir;
35
+ }
36
+ dir = dirname(dir);
37
+ }
38
+ }
package/src/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as ai from 'ai';
2
2
  import { Tool } from 'ai';
3
3
  import * as _smithers_graph_XmlNode from '@smithers-orchestrator/graph/XmlNode';
4
4
  import * as _smithers_time_travel_timetravel from '@smithers-orchestrator/time-travel/timetravel';
5
+ export { timeTravel } from '@smithers-orchestrator/time-travel/timetravel';
5
6
  import * as _smithers_graph_TaskDescriptor from '@smithers-orchestrator/graph/TaskDescriptor';
6
7
  import * as _smithers_components_SmithersWorkflow from '@smithers-orchestrator/components/SmithersWorkflow';
7
8
  import { SmithersWorkflow as SmithersWorkflow$1 } from '@smithers-orchestrator/components/SmithersWorkflow';
@@ -24,6 +25,7 @@ import * as _smithers_driver_RunStatus from '@smithers-orchestrator/driver/RunSt
24
25
  import * as _smithers_driver_RunResult from '@smithers-orchestrator/driver/RunResult';
25
26
  import * as _smithers_driver_RunOptions from '@smithers-orchestrator/driver/RunOptions';
26
27
  import * as _smithers_time_travel_revert from '@smithers-orchestrator/time-travel/revert';
28
+ export { revertToAttempt } from '@smithers-orchestrator/time-travel/revert';
27
29
  import * as _smithers_observability from '@smithers-orchestrator/observability';
28
30
  export { SmithersObservability, activeNodes, activeRuns, approvalsDenied, approvalsGranted, approvalsRequested, attemptDuration, cacheHits, cacheMisses, createSmithersObservabilityLayer, createSmithersOtelLayer, createSmithersRuntimeLayer, dbQueryDuration, dbRetries, dbTransactionDuration, dbTransactionRetries, dbTransactionRollbacks, externalWaitAsyncPending, hotReloadDuration, hotReloadFailures, hotReloads, httpRequestDuration, httpRequests, nodeDuration, nodesFailed, nodesFinished, nodesStarted, prometheusContentType, renderPrometheusMetrics, resolveSmithersObservabilityOptions, runsTotal, sandboxActive, sandboxBundleSizeBytes, sandboxCompletedTotal, sandboxCreatedTotal, sandboxDurationMs, sandboxPatchCount, sandboxTransportDurationMs, schedulerQueueDepth, smithersMetrics, timerDelayDuration, timersCancelled, timersCreated, timersFired, timersPending, toolCallsTotal, toolDuration, trackSmithersEvent, vcsDuration } from '@smithers-orchestrator/observability';
29
31
  import * as _smithers_driver_OutputKey from '@smithers-orchestrator/driver/OutputKey';
@@ -56,7 +58,7 @@ import { WorkflowProps } from '@smithers-orchestrator/components/components/Work
56
58
  import * as _smithers_server_gateway from '@smithers-orchestrator/server/gateway';
57
59
  export { Gateway } from '@smithers-orchestrator/server/gateway';
58
60
  import * as _smithers_agents from '@smithers-orchestrator/agents';
59
- export { AmpAgent, AnthropicAgent, AntigravityAgent, ClaudeCodeAgent, CodexAgent, ForgeAgent, GeminiAgent, HermesAgent, KimiAgent, OpenAIAgent, OpenCodeAgent, PiAgent } from '@smithers-orchestrator/agents';
61
+ export { AmpAgent, AnthropicAgent, AntigravityAgent, ClaudeCodeAgent, CodexAgent, ForgeAgent, GeminiAgent, HermesAgent, KimiAgent, OpenAIAgent, OpenCodeAgent, PiAgent, VibeAgent } from '@smithers-orchestrator/agents';
60
62
  import * as _smithers_scorers from '@smithers-orchestrator/scorers';
61
63
  export { aggregateScores, createScorer, faithfulnessScorer, latencyScorer, llmJudge, relevancyScorer, runScorersAsync, runScorersBatch, schemaAdherenceScorer, smithersScorers, toxicityScorer } from '@smithers-orchestrator/scorers';
62
64
  import * as _smithers_agents_capability_registry from '@smithers-orchestrator/agents/capability-registry';
@@ -70,6 +72,9 @@ export { isSmithersError } from '@smithers-orchestrator/errors/isSmithersError';
70
72
  export { knownSmithersErrorCodes } from '@smithers-orchestrator/errors/knownSmithersErrorCodes';
71
73
  export { signalRun } from '@smithers-orchestrator/engine/signals';
72
74
  export { usePatched } from '@smithers-orchestrator/engine/effect/versioning';
75
+ import { SmithersDb } from '@smithers-orchestrator/db/adapter';
76
+ export { SmithersDb } from '@smithers-orchestrator/db/adapter';
77
+ export { loadOutputs, loadOutputsEffect } from '@smithers-orchestrator/db';
73
78
  export { ensureSmithersTables } from '@smithers-orchestrator/db/ensure';
74
79
  export { markdownComponents } from '@smithers-orchestrator/components/markdownComponents';
75
80
  export { renderMdx } from '@smithers-orchestrator/components/renderMdx';
@@ -78,8 +83,8 @@ export { syncZodTableSchema, zodSchemaColumns, zodToCreateTableSQL } from '@smit
78
83
  export { camelToSnake } from '@smithers-orchestrator/db/utils/camelToSnake';
79
84
  export { unwrapZodType } from '@smithers-orchestrator/db/unwrapZodType';
80
85
  export { zodSchemaToJsonExample } from '@smithers-orchestrator/components/zod-to-example';
81
- export { BuilderApi, BuiltSmithersWorkflow, Smithers, StepOptions, fragment, renderFrame, runWorkflow, workflow } from '@smithers-orchestrator/engine';
82
- import { SmithersDb } from '@smithers-orchestrator/db/adapter';
86
+ export { BuilderApi, BuiltSmithersWorkflow, Smithers, StepOptions, approveNode, denyNode, fragment, getRun, listRuns, renderFrame, runWorkflow, workflow } from '@smithers-orchestrator/engine';
87
+ export { resolveWorktreePath } from '@smithers-orchestrator/graph';
83
88
 
84
89
  type SerializedCtx$1 = {
85
90
  runId: string;
@@ -173,7 +178,20 @@ type CreateSmithersOptions$1 = {
173
178
  * ```
174
179
  */
175
180
  declare function createSmithers<Schemas extends Record<string, zod.ZodObject<any>>>(schemas: Schemas, opts?: CreateSmithersOptions): CreateSmithersApi$1<Schemas>;
176
- type CreateSmithersOptions = CreateSmithersOptions$1;
181
+ type CreateSmithersPostgresOptions = CreateSmithersOptions & ({
182
+ provider?: "postgres";
183
+ connectionString?: string;
184
+ connection?: object;
185
+ } | {
186
+ provider: "pglite";
187
+ dataDir?: string;
188
+ });
189
+ /**
190
+ * PostgreSQL/PGlite-backed equivalent of createSmithers.
191
+ */
192
+ declare function createSmithersPostgres<Schemas extends Record<string, zod.ZodObject<any>>>(schemas: Schemas, opts?: CreateSmithersPostgresOptions): Promise<CreateSmithersApi$1<Schemas> & {
193
+ close: () => Promise<void>;
194
+ }>;
177
195
 
178
196
  /**
179
197
  * Create a SmithersWorkflow from an external build function.
@@ -301,13 +319,20 @@ type ConnectRequest = _smithers_server_gateway.ConnectRequest;
301
319
  type ContinueAsNewProps = _smithers_components.ContinueAsNewProps;
302
320
  type CreateScorerConfig = _smithers_scorers.CreateScorerConfig;
303
321
  type CreateSmithersApi<Schema> = CreateSmithersApi$1<Schema>;
322
+ type CreateSmithersOptions = CreateSmithersOptions$1;
304
323
  type DepsSpec = _smithers_components.DepsSpec;
305
324
  type EventFrame = _smithers_server_gateway.EventFrame;
306
325
  type ExternalSmithersConfig<S> = ExternalSmithersConfig$2<S>;
307
326
  type GatewayAuthConfig = _smithers_server_gateway.GatewayAuthConfig;
308
327
  type GatewayDefaults = _smithers_server_gateway.GatewayDefaults;
328
+ type GatewayOperatorUiConfig = _smithers_server_gateway.GatewayOperatorUiConfig;
309
329
  type GatewayOptions = _smithers_server_gateway.GatewayOptions;
330
+ type GatewayRegisterOptions = _smithers_server_gateway.GatewayRegisterOptions;
310
331
  type GatewayTokenGrant = _smithers_server_gateway.GatewayTokenGrant;
332
+ type GatewayUiConfig = _smithers_server_gateway.GatewayUiConfig;
333
+ type GatewayWebhookConfig = _smithers_server_gateway.GatewayWebhookConfig;
334
+ type GatewayWebhookRunConfig = _smithers_server_gateway.GatewayWebhookRunConfig;
335
+ type GatewayWebhookSignalConfig = _smithers_server_gateway.GatewayWebhookSignalConfig;
311
336
  type GraphSnapshot = _smithers_graph_GraphSnapshot.GraphSnapshot;
312
337
  type HelloResponse = _smithers_server_gateway.HelloResponse;
313
338
  type HostContainer = _smithers_react_reconciler_dom_renderer.HostContainer;
@@ -333,6 +358,7 @@ type MessageHistoryConfig = _smithers_memory.MessageHistoryConfig;
333
358
  type OpenAIAgentOptions<CALL_OPTIONS = never, TOOLS = ai.ToolSet> = _smithers_agents.OpenAIAgentOptions<CALL_OPTIONS, TOOLS>;
334
359
  type HermesAgentOptions<CALL_OPTIONS = never, TOOLS = ai.ToolSet> = _smithers_agents.HermesAgentOptions<CALL_OPTIONS, TOOLS>;
335
360
  type OpenCodeAgentOptions = _smithers_agents.OpenCodeAgentOptions;
361
+ type VibeAgentOptions = _smithers_agents.VibeAgentOptions;
336
362
  type OpenApiAuth = _smithers_openapi.OpenApiAuth;
337
363
  type OpenApiSpec = _smithers_openapi.OpenApiSpec;
338
364
  type OpenApiToolsOptions = _smithers_openapi.OpenApiToolsOptions;
@@ -408,4 +434,4 @@ type XmlElement = _smithers_graph_XmlNode.XmlElement;
408
434
  type XmlNode = _smithers_graph_XmlNode.XmlNode;
409
435
  type XmlText = _smithers_graph_XmlNode.XmlText;
410
436
 
411
- export { type AgentCapabilityRegistry, type AgentLike, type AgentToolDescriptor, type AggregateOptions, type AggregateScore, type AnthropicAgentOptions, type ApprovalAutoApprove, type ApprovalDecision, type ApprovalMode, type ApprovalOption, type ApprovalProps, type ApprovalRanking, type ApprovalRequest, type ApprovalSelection, type ColumnDef, type ConnectRequest, type ContinueAsNewProps, type CreateScorerConfig, type CreateSmithersApi, type DepsSpec, type EventFrame, type ExternalSmithersConfig, type GatewayAuthConfig, type GatewayDefaults, type GatewayOptions, type GatewayTokenGrant, type GraphSnapshot, type HelloResponse, type HermesAgentOptions, type HostContainer, type HostNodeJson, type InferDeps, type InferOutputEntry, type InferRow, type JjRevertResult, type KanbanProps, type KnownSmithersErrorCode, type LlmJudgeConfig, type MemoryFact, type MemoryLayerConfig, type MemoryMessage, type MemoryNamespace, type MemoryNamespaceKind, type MemoryProcessor, type MemoryProcessorConfig, type MemoryServiceApi, type MemoryStore, type MemoryThread, type MessageHistoryConfig, type OpenAIAgentOptions, type OpenApiAuth, type OpenApiSpec, type OpenApiToolsOptions, type OpenCodeAgentOptions, type OutputAccessor, type OutputKey, type OutputTarget, type PiAgentOptions, type PiExtensionUiRequest, type PiExtensionUiResponse, type PollerProps, type RequestFrame, type ResolvedSmithersObservabilityOptions, type ResponseFrame, type RevertOptions, type RevertResult, type RunJjOptions, type RunJjResult, type RunOptions, type RunResult, type RunStatus, type SagaProps, type SagaStepDef, type SagaStepProps, type SamplingConfig, type SandboxProps, type SandboxRuntime, type SandboxVolumeMount, type SandboxWorkspaceSpec, type SchemaRegistryEntry, type ScoreResult, type ScoreRow, type Scorer, type ScorerBinding, type ScorerContext, type ScorerFn, type ScorerInput, type ScorersMap, type SemanticRecallConfig, type SerializedCtx, type ServeOptions, type ServerOptions, type SignalProps, type SmithersAlertLabels, type SmithersAlertPolicy, type SmithersAlertPolicyDefaults, type SmithersAlertPolicyRule, type SmithersAlertReaction, type SmithersAlertReactionKind, type SmithersAlertReactionRef, type SmithersAlertSeverity, type SmithersCtx, type SmithersError, type SmithersErrorCode, type SmithersEvent, type SmithersLogFormat, type SmithersObservabilityOptions, type SmithersObservabilityService, type SmithersWorkflow, type SmithersWorkflowOptions, type TaskDescriptor, type TaskMemoryConfig, type TaskProps, type TimeTravelOptions, type TimeTravelResult, type TimerProps, type TryCatchFinallyProps, type WaitForEventProps, type WorkingMemoryConfig, type WorkspaceAddOptions, type WorkspaceInfo, type WorkspaceResult, type XmlElement, type XmlNode, type XmlText, bash, createExternalSmithers, createSmithers, defineTool, edit, getDefinedToolMetadata, grep, mdxPlugin, read, tools, write };
437
+ export { type AgentCapabilityRegistry, type AgentLike, type AgentToolDescriptor, type AggregateOptions, type AggregateScore, type AnthropicAgentOptions, type ApprovalAutoApprove, type ApprovalDecision, type ApprovalMode, type ApprovalOption, type ApprovalProps, type ApprovalRanking, type ApprovalRequest, type ApprovalSelection, type ColumnDef, type ConnectRequest, type ContinueAsNewProps, type CreateScorerConfig, type CreateSmithersApi, type CreateSmithersOptions, type DepsSpec, type EventFrame, type ExternalSmithersConfig, type GatewayAuthConfig, type GatewayDefaults, type GatewayOperatorUiConfig, type GatewayOptions, type GatewayRegisterOptions, type GatewayTokenGrant, type GatewayUiConfig, type GatewayWebhookConfig, type GatewayWebhookRunConfig, type GatewayWebhookSignalConfig, type GraphSnapshot, type HelloResponse, type HermesAgentOptions, type HostContainer, type HostNodeJson, type InferDeps, type InferOutputEntry, type InferRow, type JjRevertResult, type KanbanProps, type KnownSmithersErrorCode, type LlmJudgeConfig, type MemoryFact, type MemoryLayerConfig, type MemoryMessage, type MemoryNamespace, type MemoryNamespaceKind, type MemoryProcessor, type MemoryProcessorConfig, type MemoryServiceApi, type MemoryStore, type MemoryThread, type MessageHistoryConfig, type OpenAIAgentOptions, type OpenApiAuth, type OpenApiSpec, type OpenApiToolsOptions, type OpenCodeAgentOptions, type OutputAccessor, type OutputKey, type OutputTarget, type PiAgentOptions, type PiExtensionUiRequest, type PiExtensionUiResponse, type PollerProps, type RequestFrame, type ResolvedSmithersObservabilityOptions, type ResponseFrame, type RevertOptions, type RevertResult, type RunJjOptions, type RunJjResult, type RunOptions, type RunResult, type RunStatus, type SagaProps, type SagaStepDef, type SagaStepProps, type SamplingConfig, type SandboxProps, type SandboxRuntime, type SandboxVolumeMount, type SandboxWorkspaceSpec, type SchemaRegistryEntry, type ScoreResult, type ScoreRow, type Scorer, type ScorerBinding, type ScorerContext, type ScorerFn, type ScorerInput, type ScorersMap, type SemanticRecallConfig, type SerializedCtx, type ServeOptions, type ServerOptions, type SignalProps, type SmithersAlertLabels, type SmithersAlertPolicy, type SmithersAlertPolicyDefaults, type SmithersAlertPolicyRule, type SmithersAlertReaction, type SmithersAlertReactionKind, type SmithersAlertReactionRef, type SmithersAlertSeverity, type SmithersCtx, type SmithersError, type SmithersErrorCode, type SmithersEvent, type SmithersLogFormat, type SmithersObservabilityOptions, type SmithersObservabilityService, type SmithersWorkflow, type SmithersWorkflowOptions, type TaskDescriptor, type TaskMemoryConfig, type TaskProps, type TimeTravelOptions, type TimeTravelResult, type TimerProps, type TryCatchFinallyProps, type VibeAgentOptions, type WaitForEventProps, type WorkingMemoryConfig, type WorkspaceAddOptions, type WorkspaceInfo, type WorkspaceResult, type XmlElement, type XmlNode, type XmlText, bash, createExternalSmithers, createSmithers, createSmithersPostgres, defineTool, edit, getDefinedToolMetadata, grep, mdxPlugin, read, tools, write };
package/src/index.js CHANGED
@@ -25,6 +25,7 @@
25
25
  * @template Schema
26
26
  * @typedef {import("./CreateSmithersApi.ts").CreateSmithersApi<Schema>} CreateSmithersApi
27
27
  */
28
+ /** @typedef {import("./CreateSmithersOptions.ts").CreateSmithersOptions} CreateSmithersOptions */
28
29
  /** @typedef {import("@smithers-orchestrator/components").DepsSpec} DepsSpec */
29
30
  /** @typedef {import("@smithers-orchestrator/server/gateway").EventFrame} EventFrame */
30
31
  /**
@@ -33,8 +34,14 @@
33
34
  */
34
35
  /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayAuthConfig} GatewayAuthConfig */
35
36
  /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayDefaults} GatewayDefaults */
37
+ /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayOperatorUiConfig} GatewayOperatorUiConfig */
36
38
  /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayOptions} GatewayOptions */
39
+ /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayRegisterOptions} GatewayRegisterOptions */
37
40
  /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayTokenGrant} GatewayTokenGrant */
41
+ /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayUiConfig} GatewayUiConfig */
42
+ /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayWebhookConfig} GatewayWebhookConfig */
43
+ /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayWebhookRunConfig} GatewayWebhookRunConfig */
44
+ /** @typedef {import("@smithers-orchestrator/server/gateway").GatewayWebhookSignalConfig} GatewayWebhookSignalConfig */
38
45
  /** @typedef {import("@smithers-orchestrator/graph/GraphSnapshot").GraphSnapshot} GraphSnapshot */
39
46
  /** @typedef {import("@smithers-orchestrator/server/gateway").HelloResponse} HelloResponse */
40
47
  /** @typedef {import("@smithers-orchestrator/react-reconciler/dom/renderer").HostContainer} HostContainer */
@@ -86,6 +93,7 @@
86
93
  /** @typedef {import("@smithers-orchestrator/agents").PiExtensionUiRequest} PiExtensionUiRequest */
87
94
  /** @typedef {import("@smithers-orchestrator/agents").PiExtensionUiResponse} PiExtensionUiResponse */
88
95
  /** @typedef {import("@smithers-orchestrator/agents").OpenCodeAgentOptions} OpenCodeAgentOptions */
96
+ /** @typedef {import("@smithers-orchestrator/agents").VibeAgentOptions} VibeAgentOptions */
89
97
  /** @typedef {import("@smithers-orchestrator/components").PollerProps} PollerProps */
90
98
  /** @typedef {import("@smithers-orchestrator/server/gateway").RequestFrame} RequestFrame */
91
99
  /** @typedef {import("@smithers-orchestrator/observability").ResolvedSmithersObservabilityOptions} ResolvedSmithersObservabilityOptions */
@@ -171,18 +179,23 @@ export { knownSmithersErrorCodes } from "@smithers-orchestrator/errors/knownSmit
171
179
  // Components
172
180
  export { Approval, ApprovalGate, Aspects, Branch, CheckSuite, ClassifyAndRoute, ContentPipeline, ContinueAsNew, Debate, DecisionTable, DriftDetector, EscalationChain, GatherAndSynthesize, HumanTask, Kanban, Loop, MergeQueue, Optimizer, Panel, Parallel, Poller, Ralph, ReviewLoop, Runbook, Saga, Sandbox, ScanFixVerify, Sequence, Signal, Subflow, SuperSmithers, Supervisor, Task, Timer, TryCatchFinally, WaitForEvent, Workflow, Worktree, approvalDecisionSchema, approvalRankingSchema, approvalSelectionSchema, continueAsNew, } from "@smithers-orchestrator/components";
173
181
  // Agents
174
- export { AnthropicAgent, OpenAIAgent, HermesAgent, AmpAgent, AntigravityAgent, ClaudeCodeAgent, CodexAgent, GeminiAgent, PiAgent, KimiAgent, ForgeAgent, OpenCodeAgent, } from "@smithers-orchestrator/agents";
182
+ export { AnthropicAgent, OpenAIAgent, HermesAgent, AmpAgent, AntigravityAgent, ClaudeCodeAgent, CodexAgent, GeminiAgent, PiAgent, KimiAgent, ForgeAgent, VibeAgent, OpenCodeAgent, } from "@smithers-orchestrator/agents";
175
183
  // VCS
176
184
  export { runJj, getJjPointer, revertToJjPointer, isJjRepo, workspaceAdd, workspaceList, workspaceClose, } from "@smithers-orchestrator/vcs/jj";
177
185
  // Core API
178
- export { createSmithers } from "./create.js";
186
+ export { createSmithers, createSmithersPostgres } from "./create.js";
179
187
  export {
188
+ approveNode,
189
+ denyNode,
180
190
  fragment,
191
+ getRun,
192
+ listRuns,
181
193
  renderFrame,
182
194
  runWorkflow,
183
195
  Smithers,
184
196
  workflow,
185
197
  } from "@smithers-orchestrator/engine";
198
+ export { resolveWorktreePath } from "@smithers-orchestrator/graph";
186
199
  export { signalRun } from "@smithers-orchestrator/engine/signals";
187
200
  export { usePatched } from "@smithers-orchestrator/engine/effect/versioning";
188
201
  // Tools
@@ -204,7 +217,8 @@ export { createServeApp } from "@smithers-orchestrator/server/serve";
204
217
  // Observability
205
218
  export { SmithersObservability, createSmithersObservabilityLayer, createSmithersOtelLayer, createSmithersRuntimeLayer, smithersMetrics, trackSmithersEvent, activeNodes, activeRuns, externalWaitAsyncPending, approvalsDenied, approvalsGranted, approvalsRequested, timerDelayDuration, timersCancelled, timersCreated, timersFired, timersPending, attemptDuration, cacheHits, cacheMisses, dbQueryDuration, dbRetries, dbTransactionDuration, dbTransactionRetries, dbTransactionRollbacks, hotReloadDuration, hotReloadFailures, hotReloads, httpRequestDuration, httpRequests, nodeDuration, nodesFailed, nodesFinished, nodesStarted, prometheusContentType, renderPrometheusMetrics, resolveSmithersObservabilityOptions, runsTotal, sandboxActive, sandboxBundleSizeBytes, sandboxCompletedTotal, sandboxCreatedTotal, sandboxDurationMs, sandboxPatchCount, sandboxTransportDurationMs, schedulerQueueDepth, toolCallsTotal, toolDuration, vcsDuration, } from "@smithers-orchestrator/observability";
206
219
  // DB
207
- export { SmithersDb } from "@smithers-orchestrator/db/adapter";
220
+ export { SmithersDb } from "@smithers-orchestrator/db";
221
+ export { loadOutputs, loadOutputsEffect } from "@smithers-orchestrator/db";
208
222
  export { ensureSmithersTables } from "@smithers-orchestrator/db/ensure";
209
223
  // Renderer
210
224
  export { SmithersRenderer } from "@smithers-orchestrator/react-reconciler/dom/renderer";
@@ -0,0 +1,25 @@
1
+ import type { ReactElement } from "react";
2
+
3
+ /** Element key accepted by the JSX runtime factories. */
4
+ export type JsxRuntimeKey = string | number | undefined;
5
+
6
+ /**
7
+ * Smithers JSX runtime factories — the target of
8
+ * `jsxImportSource: "smithers-orchestrator"` and what `<Workflow/>`, `<Task/>`,
9
+ * etc. compile to. They are deliberately typed wide (`type`/`props` as
10
+ * `unknown`): components validate their own props, and keeping these signatures
11
+ * loose avoids expanding Smithers' deep workflow types during type-checking,
12
+ * which is otherwise expensive. Callers that build trees programmatically can
13
+ * use these directly, e.g. `jsx(Task, { id, output, agent, children }, key)`.
14
+ */
15
+ export function jsx(type: unknown, props: unknown, key?: JsxRuntimeKey): ReactElement;
16
+ export function jsxs(type: unknown, props: unknown, key?: JsxRuntimeKey): ReactElement;
17
+ export function jsxDEV(
18
+ type: unknown,
19
+ props: unknown,
20
+ key?: JsxRuntimeKey,
21
+ isStaticChildren?: boolean,
22
+ source?: unknown,
23
+ self?: unknown,
24
+ ): ReactElement;
25
+ export const Fragment: unknown;
package/src/tools/bash.js CHANGED
@@ -175,7 +175,7 @@ export async function bashTool(cmd, args = [], opts = undefined) {
175
175
 
176
176
  export const bash = defineTool({
177
177
  name: "bash",
178
- description: "Execute a shell command",
178
+ description: "Run an executable with arguments",
179
179
  schema: z.object({
180
180
  cmd: z.string(),
181
181
  args: z.array(z.string()).optional(),
@@ -1,29 +1,10 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
-
3
- const storage = new AsyncLocalStorage();
4
-
5
- export function runWithToolContext(ctx, fn) {
6
- return storage.run(ctx, fn);
7
- }
8
-
9
- export function getToolContext() {
10
- return storage.getStore();
11
- }
12
-
13
- export function getToolIdempotencyKey(ctx = getToolContext()) {
14
- if (!ctx) {
15
- return null;
16
- }
17
- if (typeof ctx.idempotencyKey === "string" && ctx.idempotencyKey.length > 0) {
18
- return ctx.idempotencyKey;
19
- }
20
- if (!ctx.runId || !ctx.nodeId) {
21
- return null;
22
- }
23
- return `smithers:${ctx.runId}:${ctx.nodeId}:${ctx.iteration ?? 0}`;
24
- }
25
-
26
- export function nextToolSeq(ctx) {
27
- ctx.seq = (ctx.seq ?? 0) + 1;
28
- return ctx.seq;
29
- }
1
+ // The tool-execution context now lives in its own low-level package so both the
2
+ // engine and smithers can depend on it without a cycle (the engine needs
3
+ // runWithToolContext to give in-process agent tools their run context). This file
4
+ // stays as a re-export so existing relative importers keep working.
5
+ export {
6
+ runWithToolContext,
7
+ getToolContext,
8
+ getToolIdempotencyKey,
9
+ nextToolSeq,
10
+ } from "@smithers-orchestrator/tool-context";
@@ -26,6 +26,8 @@ function defaultToolContext() {
26
26
  maxOutputBytes: 200_000,
27
27
  timeoutMs: 60_000,
28
28
  seq: 0,
29
+ // No-op unless the engine populated a real durability handle (flag on).
30
+ durabilitySnapshot: async () => ({ skipped: true }),
29
31
  };
30
32
  }
31
33
 
@@ -48,14 +50,28 @@ export function defineTool(options) {
48
50
  inputSchema: zodSchema(options.schema),
49
51
  execute: async (args) => {
50
52
  const toolContext = getToolContext();
53
+ // Merge the ambient context OVER the defaults, so a partial context from the
54
+ // engine (run/node/cwd + durabilitySnapshot) overrides what it sets and keeps
55
+ // sane defaults for the rest.
51
56
  const definedContext = {
52
- ...(toolContext ?? defaultToolContext()),
57
+ ...defaultToolContext(),
58
+ ...(toolContext ?? {}),
53
59
  idempotencyKey: getToolIdempotencyKey(toolContext),
54
60
  toolName: options.name,
55
61
  sideEffect,
56
62
  idempotent,
57
63
  };
58
- return options.execute(args, definedContext);
64
+ const result = await options.execute(args, definedContext);
65
+ // Strict Tier 1 snapshot at this tool boundary, before the agent proceeds.
66
+ // No-op by default; never delays past one jj snapshot and never fails the tool.
67
+ if (sideEffect && typeof definedContext.durabilitySnapshot === "function") {
68
+ try {
69
+ await definedContext.durabilitySnapshot(options.name, definedContext.toolUseId);
70
+ } catch {
71
+ /* snapshot failures never fail the tool */
72
+ }
73
+ }
74
+ return result;
59
75
  },
60
76
  });
61
77