@desplega.ai/agent-swarm 1.90.0 → 1.92.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.
Files changed (96) hide show
  1. package/README.md +2 -1
  2. package/openapi.json +803 -150
  3. package/package.json +5 -5
  4. package/src/artifact-sdk/server.ts +2 -1
  5. package/src/be/db.ts +337 -1
  6. package/src/be/memory/providers/sqlite-store.ts +6 -1
  7. package/src/be/memory/types.ts +1 -0
  8. package/src/be/migrations/083_script_workflows.sql +51 -0
  9. package/src/be/modelsdev-cache.json +42352 -38595
  10. package/src/be/scripts/typecheck.ts +181 -1
  11. package/src/be/seed-scripts/catalog/compound-insights.ts +398 -0
  12. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +911 -0
  13. package/src/be/seed-scripts/catalog/schedule-health.ts +73 -0
  14. package/src/be/seed-scripts/catalog/smart-recall.ts +65 -0
  15. package/src/be/seed-scripts/catalog/task-context-gathering.ts +92 -0
  16. package/src/be/seed-scripts/catalog/tool-usage.ts +59 -0
  17. package/src/be/seed-scripts/index.ts +54 -0
  18. package/src/be/seed-skills/index.ts +7 -0
  19. package/src/be/swarm-config-guard.ts +17 -0
  20. package/src/commands/artifact.ts +3 -2
  21. package/src/commands/profile-sync.ts +310 -0
  22. package/src/commands/runner.ts +134 -3
  23. package/src/hooks/hook.ts +32 -9
  24. package/src/http/db-query.ts +20 -5
  25. package/src/http/index.ts +57 -0
  26. package/src/http/integrations.ts +6 -1
  27. package/src/http/mcp-bridge.ts +117 -0
  28. package/src/http/mcp-oauth.ts +97 -39
  29. package/src/http/memory.ts +5 -2
  30. package/src/http/openapi.ts +2 -2
  31. package/src/http/pages-public.ts +10 -11
  32. package/src/http/pages.ts +7 -11
  33. package/src/http/script-runs.ts +555 -0
  34. package/src/http/scripts.ts +24 -1
  35. package/src/http/utils.ts +11 -4
  36. package/src/jira/app.ts +2 -3
  37. package/src/jira/webhook-lifecycle.ts +2 -1
  38. package/src/linear/app.ts +2 -3
  39. package/src/prompts/session-templates.ts +24 -4
  40. package/src/providers/claude-adapter.ts +86 -13
  41. package/src/script-workflows/executor.ts +110 -0
  42. package/src/script-workflows/harness.ts +73 -0
  43. package/src/script-workflows/label-lint.ts +51 -0
  44. package/src/script-workflows/limits.ts +22 -0
  45. package/src/script-workflows/supervisor.ts +139 -0
  46. package/src/script-workflows/workflow-ctx.ts +205 -0
  47. package/src/scripts-runtime/executors/native.ts +1 -0
  48. package/src/scripts-runtime/sdk-allowlist.ts +124 -0
  49. package/src/scripts-runtime/swarm-sdk.ts +198 -3
  50. package/src/scripts-runtime/types/stdlib.d.ts +287 -0
  51. package/src/scripts-runtime/types/swarm-sdk.d.ts +287 -0
  52. package/src/server.ts +2 -0
  53. package/src/slack/handlers.ts +11 -4
  54. package/src/slack/message-text.ts +98 -0
  55. package/src/slack/thread-buffer.ts +5 -3
  56. package/src/tests/claude-adapter-binary.test.ts +147 -4
  57. package/src/tests/claude-adapter-otel.test.ts +85 -1
  58. package/src/tests/db-query.test.ts +28 -0
  59. package/src/tests/error-tracker.test.ts +121 -0
  60. package/src/tests/harness-provider-resolution.test.ts +33 -0
  61. package/src/tests/hook-registration-nudge.test.ts +69 -0
  62. package/src/tests/mcp-oauth-manual-client.test.ts +213 -0
  63. package/src/tests/mcp-tools.test.ts +6 -0
  64. package/src/tests/pages-public-html.test.ts +41 -0
  65. package/src/tests/pages-public-json-redirect.test.ts +37 -2
  66. package/src/tests/profile-sync.test.ts +282 -0
  67. package/src/tests/prompt-template-session.test.ts +34 -5
  68. package/src/tests/script-runs-http.test.ts +278 -0
  69. package/src/tests/script-workflows-label-lint.test.ts +43 -0
  70. package/src/tests/script-workflows-runtime-e2e.test.ts +170 -0
  71. package/src/tests/scripts-mcp-e2e.test.ts +49 -2
  72. package/src/tests/scripts-runtime.test.ts +33 -0
  73. package/src/tests/seed-scripts.test.ts +347 -2
  74. package/src/tests/slack-message-text.test.ts +250 -0
  75. package/src/tests/system-default-skills.test.ts +40 -0
  76. package/src/tools/create-metric.ts +2 -3
  77. package/src/tools/create-page.ts +3 -6
  78. package/src/tools/db-query.ts +16 -6
  79. package/src/tools/memory-rate.ts +2 -1
  80. package/src/tools/memory-search.ts +1 -0
  81. package/src/tools/register-kapso-number.ts +2 -4
  82. package/src/tools/request-human-input.ts +2 -1
  83. package/src/tools/script-common.ts +2 -4
  84. package/src/tools/script-run.ts +7 -0
  85. package/src/tools/script-runs.ts +123 -0
  86. package/src/tools/slack-read.ts +12 -3
  87. package/src/tools/tool-config.ts +4 -1
  88. package/src/types.ts +52 -0
  89. package/src/utils/constants.ts +58 -8
  90. package/src/utils/error-tracker.ts +40 -1
  91. package/src/utils/internal-ai/complete-structured.ts +10 -4
  92. package/src/workflows/executors/raw-llm.ts +76 -59
  93. package/templates/skills/pages/content.md +205 -55
  94. package/templates/skills/script-workflows/config.json +14 -0
  95. package/templates/skills/script-workflows/content.md +68 -0
  96. package/templates/skills/swarm-scripts/content.md +45 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.90.0",
3
+ "version": "1.92.0",
4
4
  "description": "Multi-agent orchestration for Claude Code, Codex, Gemini CLI, and other AI coding assistants",
5
5
  "license": "MIT",
6
6
  "author": "desplega.sh <contact@desplega.sh>",
@@ -60,15 +60,15 @@
60
60
  "dev": "bun --hot src/stdio.ts",
61
61
  "dev:http": "portless api.swarm bun --hot src/http.ts",
62
62
  "start": "bun src/stdio.ts",
63
- "start:http": "bun src/http.ts",
64
- "start:portless": "portless api.swarm bun src/http.ts",
63
+ "start:http": "bun --expose-gc src/http.ts",
64
+ "start:portless": "portless api.swarm bun --expose-gc src/http.ts",
65
65
  "inspector": "bunx @modelcontextprotocol/inspector --transport stdio bun src/stdio.ts",
66
66
  "inspector:http": "bunx @modelcontextprotocol/inspector --transport http https://api.swarm.localhost:1355/mcp",
67
67
  "lint": "biome check src",
68
68
  "lint:fix": "biome check --write src",
69
69
  "format": "biome format --write src",
70
- "build:binary": "bun build ./src/cli.tsx --compile --target=bun-linux-x64 --outfile ./dist/agent-swarm",
71
- "build:binary:arm64": "bun build ./src/cli.tsx --compile --target=bun-linux-arm64 --outfile ./dist/agent-swarm",
70
+ "build:binary": "bun build ./src/cli.tsx --compile --compile-exec-argv='--expose-gc' --target=bun-linux-x64 --outfile ./dist/agent-swarm",
71
+ "build:binary:arm64": "bun build ./src/cli.tsx --compile --compile-exec-argv='--expose-gc' --target=bun-linux-arm64 --outfile ./dist/agent-swarm",
72
72
  "docker:build:worker": "docker build -f Dockerfile.worker -t agent-swarm-worker:latest .",
73
73
  "docker:run:worker": "docker run --rm -it --env-file .env.docker -p 3202:3000 -v ./logs:/logs -v ./work/shared:/workspace/shared -v ./work/worker-1:/workspace/personal agent-swarm-worker:latest",
74
74
  "docker:run:lead": "docker run --rm -it --env-file .env.docker-lead -e AGENT_ROLE=lead -p 3201:3000 -v ./logs:/logs -v ./work/shared:/workspace/shared -v ./work/lead:/workspace/personal agent-swarm-worker:latest",
@@ -1,6 +1,7 @@
1
1
  import { Hono } from "hono";
2
2
  import { serveStatic } from "hono/bun";
3
3
  import { getApiKey } from "../utils/api-key";
4
+ import { getMcpBaseUrl } from "../utils/constants";
4
5
  import { BROWSER_SDK_JS } from "./browser-sdk";
5
6
  import { getAvailablePort } from "./port";
6
7
  import { createTunnel } from "./tunnel";
@@ -47,7 +48,7 @@ export function createBunHonoFetchHandler(app: Hono): (req: Request) => Promise<
47
48
  export function createArtifactServer(opts: ArtifactServerOptions): ArtifactServer {
48
49
  const agentId = process.env.AGENT_ID || "unknown";
49
50
  const apiKey = getApiKey();
50
- const mcpBaseUrl = process.env.MCP_BASE_URL || `http://localhost:${process.env.PORT || "3013"}`;
51
+ const mcpBaseUrl = getMcpBaseUrl();
51
52
 
52
53
  const app = new Hono();
53
54
 
package/src/be/db.ts CHANGED
@@ -62,6 +62,9 @@ import type {
62
62
  RepoGuidelines,
63
63
  ScheduledTask,
64
64
  ScheduledTaskSummary,
65
+ ScriptRun,
66
+ ScriptRunJournalEntry,
67
+ ScriptRunStatus,
65
68
  Service,
66
69
  ServiceStatus,
67
70
  SessionCost,
@@ -1887,6 +1890,19 @@ export function getInProgressTasksByContextKey(
1887
1890
  .map(rowToAgentTask);
1888
1891
  }
1889
1892
 
1893
+ export function getLatestTaskByContextKey(contextKey: string): AgentTask | null {
1894
+ if (!contextKey) return null;
1895
+ const row = getDb()
1896
+ .prepare<AgentTaskRow, [string]>(
1897
+ `SELECT * FROM agent_tasks
1898
+ WHERE contextKey = ?
1899
+ ORDER BY createdAt DESC
1900
+ LIMIT 1`,
1901
+ )
1902
+ .get(contextKey);
1903
+ return row ? rowToAgentTask(row) : null;
1904
+ }
1905
+
1890
1906
  /**
1891
1907
  * Find the most recent agent associated with a specific Slack thread.
1892
1908
  * No status filter — returns the last agent that touched this thread regardless of task state.
@@ -8991,7 +9007,7 @@ export function getAgentSkills(agentId: string, activeOnly = true): SkillWithIns
8991
9007
  SELECT s.*, 1 as isActive, s.createdAt as installedAt, 1 as sourceRank,
8992
9008
  CASE WHEN s.type = 'personal' THEN 0 ELSE 1 END as typeRank
8993
9009
  FROM skills s
8994
- WHERE s.systemDefault = 1
9010
+ WHERE (s.systemDefault = 1 OR s.scope = 'swarm')
8995
9011
  AND s.isEnabled = 1
8996
9012
  ORDER BY
8997
9013
  sourceRank,
@@ -11206,3 +11222,323 @@ export function countKv(namespace: string, opts: { prefix?: string }): number {
11206
11222
  .get(namespace, now);
11207
11223
  return row?.n ?? 0;
11208
11224
  }
11225
+
11226
+ // ─── Script Runs ────────────────────────────────────────────────────────────
11227
+
11228
+ type ScriptRunRow = {
11229
+ id: string;
11230
+ agentId: string;
11231
+ scriptName: string | null;
11232
+ source: string;
11233
+ args: string;
11234
+ status: string;
11235
+ pid: number | null;
11236
+ startedAt: string;
11237
+ finishedAt: string | null;
11238
+ output: string | null;
11239
+ error: string | null;
11240
+ last_heartbeat_at: string | null;
11241
+ idempotencyKey: string | null;
11242
+ requestedByUserId: string | null;
11243
+ created_by: string | null;
11244
+ updated_by: string | null;
11245
+ };
11246
+
11247
+ function parseJsonColumn(value: string | null): unknown | undefined {
11248
+ if (value === null) return undefined;
11249
+ return JSON.parse(value);
11250
+ }
11251
+
11252
+ function rowToScriptRun(row: ScriptRunRow): ScriptRun {
11253
+ return {
11254
+ id: row.id,
11255
+ agentId: row.agentId,
11256
+ scriptName: row.scriptName ?? undefined,
11257
+ source: row.source,
11258
+ args: JSON.parse(row.args),
11259
+ status: row.status as ScriptRunStatus,
11260
+ pid: row.pid ?? undefined,
11261
+ startedAt: row.startedAt,
11262
+ finishedAt: row.finishedAt ?? undefined,
11263
+ output: parseJsonColumn(row.output),
11264
+ error: row.error ?? undefined,
11265
+ lastHeartbeatAt: row.last_heartbeat_at ?? undefined,
11266
+ idempotencyKey: row.idempotencyKey ?? undefined,
11267
+ requestedByUserId: row.requestedByUserId ?? undefined,
11268
+ };
11269
+ }
11270
+
11271
+ export function createScriptRun(data: {
11272
+ id: string;
11273
+ agentId: string;
11274
+ source: string;
11275
+ args: unknown;
11276
+ scriptName?: string;
11277
+ idempotencyKey?: string;
11278
+ requestedByUserId?: string;
11279
+ createdBy?: string;
11280
+ updatedBy?: string;
11281
+ }): { run: ScriptRun; existing: boolean } {
11282
+ const db = getDb();
11283
+ if (data.idempotencyKey) {
11284
+ const existing = db
11285
+ .prepare<ScriptRunRow, [string]>("SELECT * FROM script_runs WHERE idempotencyKey = ?")
11286
+ .get(data.idempotencyKey);
11287
+ if (existing) return { run: rowToScriptRun(existing), existing: true };
11288
+ }
11289
+
11290
+ const row = db
11291
+ .prepare<
11292
+ ScriptRunRow,
11293
+ [
11294
+ string,
11295
+ string,
11296
+ string | null,
11297
+ string,
11298
+ string,
11299
+ string | null,
11300
+ string | null,
11301
+ string | null,
11302
+ string | null,
11303
+ ]
11304
+ >(
11305
+ `INSERT INTO script_runs
11306
+ (id, agentId, scriptName, source, args, idempotencyKey, requestedByUserId, created_by, updated_by)
11307
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
11308
+ RETURNING *`,
11309
+ )
11310
+ .get(
11311
+ data.id,
11312
+ data.agentId,
11313
+ data.scriptName ?? null,
11314
+ data.source,
11315
+ JSON.stringify(data.args ?? null),
11316
+ data.idempotencyKey ?? null,
11317
+ data.requestedByUserId ?? null,
11318
+ data.createdBy ?? null,
11319
+ data.updatedBy ?? data.createdBy ?? null,
11320
+ );
11321
+ if (!row) throw new Error("Failed to create script run");
11322
+ return { run: rowToScriptRun(row), existing: false };
11323
+ }
11324
+
11325
+ export function getScriptRun(id: string): ScriptRun | null {
11326
+ const row = getDb()
11327
+ .prepare<ScriptRunRow, [string]>("SELECT * FROM script_runs WHERE id = ?")
11328
+ .get(id);
11329
+ return row ? rowToScriptRun(row) : null;
11330
+ }
11331
+
11332
+ export function getScriptRunByIdempotencyKey(idempotencyKey: string): ScriptRun | null {
11333
+ const row = getDb()
11334
+ .prepare<ScriptRunRow, [string]>("SELECT * FROM script_runs WHERE idempotencyKey = ?")
11335
+ .get(idempotencyKey);
11336
+ return row ? rowToScriptRun(row) : null;
11337
+ }
11338
+
11339
+ export function listScriptRuns(opts?: {
11340
+ status?: ScriptRunStatus;
11341
+ agentId?: string;
11342
+ limit?: number;
11343
+ offset?: number;
11344
+ }): ScriptRun[] {
11345
+ const conditions: string[] = [];
11346
+ const params: Array<string | number> = [];
11347
+ if (opts?.status) {
11348
+ conditions.push("status = ?");
11349
+ params.push(opts.status);
11350
+ }
11351
+ if (opts?.agentId) {
11352
+ conditions.push("agentId = ?");
11353
+ params.push(opts.agentId);
11354
+ }
11355
+
11356
+ const limit = opts?.limit ?? 50;
11357
+ const offset = opts?.offset ?? 0;
11358
+ params.push(limit, offset);
11359
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
11360
+ const rows = getDb()
11361
+ .prepare<ScriptRunRow, Array<string | number>>(
11362
+ `SELECT * FROM script_runs ${where} ORDER BY startedAt DESC LIMIT ? OFFSET ?`,
11363
+ )
11364
+ .all(...params);
11365
+ return rows.map(rowToScriptRun);
11366
+ }
11367
+
11368
+ export function countScriptRuns(opts?: { status?: ScriptRunStatus; agentId?: string }): number {
11369
+ const conditions: string[] = [];
11370
+ const params: string[] = [];
11371
+ if (opts?.status) {
11372
+ conditions.push("status = ?");
11373
+ params.push(opts.status);
11374
+ }
11375
+ if (opts?.agentId) {
11376
+ conditions.push("agentId = ?");
11377
+ params.push(opts.agentId);
11378
+ }
11379
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
11380
+ const row = getDb()
11381
+ .prepare<{ count: number }, string[]>(`SELECT COUNT(*) AS count FROM script_runs ${where}`)
11382
+ .get(...params);
11383
+ return row?.count ?? 0;
11384
+ }
11385
+
11386
+ export function countActiveScriptRuns(): number {
11387
+ const row = getDb()
11388
+ .prepare<{ count: number }, []>(
11389
+ "SELECT COUNT(*) AS count FROM script_runs WHERE status IN ('running', 'paused')",
11390
+ )
11391
+ .get();
11392
+ return row?.count ?? 0;
11393
+ }
11394
+
11395
+ export function updateScriptRun(
11396
+ id: string,
11397
+ patch: Partial<{
11398
+ status: ScriptRunStatus;
11399
+ pid: number | null;
11400
+ finishedAt: string | null;
11401
+ output: unknown;
11402
+ error: string | null;
11403
+ lastHeartbeatAt: string | null;
11404
+ updatedBy: string | null;
11405
+ }>,
11406
+ ): void {
11407
+ const sets: string[] = [];
11408
+ const vals: Array<string | number | null> = [];
11409
+ if (patch.status !== undefined) {
11410
+ sets.push("status = ?");
11411
+ vals.push(patch.status);
11412
+ }
11413
+ if (patch.pid !== undefined) {
11414
+ sets.push("pid = ?");
11415
+ vals.push(patch.pid);
11416
+ }
11417
+ if (patch.finishedAt !== undefined) {
11418
+ sets.push("finishedAt = ?");
11419
+ vals.push(patch.finishedAt);
11420
+ }
11421
+ if ("output" in patch) {
11422
+ sets.push("output = ?");
11423
+ vals.push(patch.output === undefined ? null : JSON.stringify(patch.output));
11424
+ }
11425
+ if (patch.error !== undefined) {
11426
+ sets.push("error = ?");
11427
+ vals.push(patch.error);
11428
+ }
11429
+ if (patch.lastHeartbeatAt !== undefined) {
11430
+ sets.push("last_heartbeat_at = ?");
11431
+ vals.push(patch.lastHeartbeatAt);
11432
+ }
11433
+ if (patch.updatedBy !== undefined) {
11434
+ sets.push("updated_by = ?");
11435
+ vals.push(patch.updatedBy);
11436
+ }
11437
+ if (sets.length === 0) return;
11438
+ vals.push(id);
11439
+ getDb().run(`UPDATE script_runs SET ${sets.join(", ")} WHERE id = ?`, vals);
11440
+ }
11441
+
11442
+ export function getRunningScriptRuns(): ScriptRun[] {
11443
+ const rows = getDb()
11444
+ .prepare<ScriptRunRow, []>("SELECT * FROM script_runs WHERE status IN ('running', 'paused')")
11445
+ .all();
11446
+ return rows.map(rowToScriptRun);
11447
+ }
11448
+
11449
+ // ─── Script Run Journal ─────────────────────────────────────────────────────
11450
+
11451
+ type ScriptRunJournalRow = {
11452
+ id: string;
11453
+ runId: string;
11454
+ stepKey: string;
11455
+ stepType: string;
11456
+ config: string;
11457
+ status: string;
11458
+ result: string | null;
11459
+ error: string | null;
11460
+ startedAt: string;
11461
+ completedAt: string | null;
11462
+ created_by: string | null;
11463
+ updated_by: string | null;
11464
+ };
11465
+
11466
+ function rowToScriptRunJournalEntry(row: ScriptRunJournalRow): ScriptRunJournalEntry {
11467
+ return {
11468
+ id: row.id,
11469
+ runId: row.runId,
11470
+ stepKey: row.stepKey,
11471
+ stepType: row.stepType,
11472
+ config: JSON.parse(row.config),
11473
+ status: row.status as "completed" | "failed",
11474
+ result: parseJsonColumn(row.result),
11475
+ error: row.error ?? undefined,
11476
+ startedAt: row.startedAt,
11477
+ completedAt: row.completedAt ?? undefined,
11478
+ };
11479
+ }
11480
+
11481
+ export function getScriptRunJournalStep(
11482
+ runId: string,
11483
+ stepKey: string,
11484
+ ): ScriptRunJournalEntry | null {
11485
+ const row = getDb()
11486
+ .prepare<ScriptRunJournalRow, [string, string]>(
11487
+ "SELECT * FROM script_run_journal WHERE runId = ? AND stepKey = ?",
11488
+ )
11489
+ .get(runId, stepKey);
11490
+ return row ? rowToScriptRunJournalEntry(row) : null;
11491
+ }
11492
+
11493
+ export function upsertScriptRunJournalStep(data: {
11494
+ runId: string;
11495
+ stepKey: string;
11496
+ stepType: string;
11497
+ config: unknown;
11498
+ status: "completed" | "failed";
11499
+ result?: unknown;
11500
+ error?: string;
11501
+ }): void {
11502
+ getDb().run(
11503
+ `INSERT OR IGNORE INTO script_run_journal
11504
+ (id, runId, stepKey, stepType, config, status, result, error, completedAt)
11505
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
11506
+ [
11507
+ crypto.randomUUID(),
11508
+ data.runId,
11509
+ data.stepKey,
11510
+ data.stepType,
11511
+ JSON.stringify(data.config ?? {}),
11512
+ data.status,
11513
+ data.result !== undefined ? JSON.stringify(data.result) : null,
11514
+ data.error ?? null,
11515
+ ],
11516
+ );
11517
+ }
11518
+
11519
+ export function listScriptRunJournalSteps(runId: string): ScriptRunJournalEntry[] {
11520
+ const rows = getDb()
11521
+ .prepare<ScriptRunJournalRow, [string]>(
11522
+ "SELECT * FROM script_run_journal WHERE runId = ? ORDER BY startedAt ASC",
11523
+ )
11524
+ .all(runId);
11525
+ return rows.map(rowToScriptRunJournalEntry);
11526
+ }
11527
+
11528
+ export function countScriptRunJournalSteps(runId: string): number {
11529
+ const row = getDb()
11530
+ .prepare<{ count: number }, [string]>(
11531
+ "SELECT COUNT(*) AS count FROM script_run_journal WHERE runId = ?",
11532
+ )
11533
+ .get(runId);
11534
+ return row?.count ?? 0;
11535
+ }
11536
+
11537
+ export function countScriptRunJournalAgentTaskSteps(runId: string): number {
11538
+ const row = getDb()
11539
+ .prepare<{ count: number }, [string]>(
11540
+ "SELECT COUNT(*) AS count FROM script_run_journal WHERE runId = ? AND stepType = 'agent-task'",
11541
+ )
11542
+ .get(runId);
11543
+ return row?.count ?? 0;
11544
+ }
@@ -383,7 +383,7 @@ export class SqliteMemoryStore implements MemoryStore {
383
383
  }
384
384
 
385
385
  list(agentId: string, options: MemoryListOptions = {}): AgentMemory[] {
386
- const { scope = "all", limit = 20, offset = 0, isLead = false } = options;
386
+ const { scope = "all", limit = 20, offset = 0, isLead = false, source } = options;
387
387
  const db = getDb();
388
388
 
389
389
  const conditions: string[] = [];
@@ -407,6 +407,11 @@ export class SqliteMemoryStore implements MemoryStore {
407
407
  }
408
408
  }
409
409
 
410
+ if (source) {
411
+ conditions.push("source = ?");
412
+ params.push(source);
413
+ }
414
+
410
415
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
411
416
  params.push(limit, offset);
412
417
 
@@ -70,6 +70,7 @@ export interface MemoryListOptions {
70
70
  limit?: number;
71
71
  offset?: number;
72
72
  isLead?: boolean;
73
+ source?: AgentMemorySource;
73
74
  }
74
75
 
75
76
  export interface MemoryStats {
@@ -0,0 +1,51 @@
1
+ -- Script Workflows v1: durable background TypeScript scripts.
2
+
3
+ CREATE TABLE IF NOT EXISTS script_runs (
4
+ id TEXT PRIMARY KEY,
5
+ agentId TEXT NOT NULL,
6
+ scriptName TEXT,
7
+ source TEXT NOT NULL,
8
+ args TEXT NOT NULL DEFAULT 'null',
9
+ status TEXT NOT NULL DEFAULT 'running'
10
+ CHECK(status IN (
11
+ 'running',
12
+ 'paused',
13
+ 'completed',
14
+ 'failed',
15
+ 'cancelled',
16
+ 'aborted_limit'
17
+ )),
18
+ pid INTEGER,
19
+ startedAt TEXT NOT NULL DEFAULT (datetime('now')),
20
+ finishedAt TEXT,
21
+ output TEXT,
22
+ error TEXT,
23
+ last_heartbeat_at TEXT,
24
+ idempotencyKey TEXT UNIQUE,
25
+ requestedByUserId TEXT REFERENCES users(id),
26
+ created_by TEXT REFERENCES users(id),
27
+ updated_by TEXT REFERENCES users(id)
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS idx_script_runs_status ON script_runs(status);
31
+ CREATE INDEX IF NOT EXISTS idx_script_runs_agentId ON script_runs(agentId);
32
+ CREATE INDEX IF NOT EXISTS idx_script_runs_idempotencyKey ON script_runs(idempotencyKey)
33
+ WHERE idempotencyKey IS NOT NULL;
34
+
35
+ CREATE TABLE IF NOT EXISTS script_run_journal (
36
+ id TEXT PRIMARY KEY,
37
+ runId TEXT NOT NULL REFERENCES script_runs(id) ON DELETE CASCADE,
38
+ stepKey TEXT NOT NULL,
39
+ stepType TEXT NOT NULL,
40
+ config TEXT NOT NULL DEFAULT '{}',
41
+ status TEXT NOT NULL CHECK(status IN ('completed','failed')),
42
+ result TEXT,
43
+ error TEXT,
44
+ startedAt TEXT NOT NULL DEFAULT (datetime('now')),
45
+ completedAt TEXT,
46
+ created_by TEXT REFERENCES users(id),
47
+ updated_by TEXT REFERENCES users(id),
48
+ UNIQUE(runId, stepKey)
49
+ );
50
+
51
+ CREATE INDEX IF NOT EXISTS idx_srj_runId ON script_run_journal(runId);