@desplega.ai/agent-swarm 1.91.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.
- package/README.md +2 -1
- package/openapi.json +585 -5
- package/package.json +1 -1
- package/src/be/db.ts +337 -1
- package/src/be/migrations/083_script_workflows.sql +51 -0
- package/src/be/modelsdev-cache.json +42352 -38595
- package/src/be/scripts/typecheck.ts +49 -0
- package/src/be/seed-scripts/catalog/compound-insights.ts +216 -6
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +911 -0
- package/src/be/seed-scripts/catalog/task-context-gathering.ts +92 -0
- package/src/be/seed-scripts/catalog/tool-usage.ts +6 -3
- package/src/be/seed-scripts/index.ts +20 -2
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +17 -0
- package/src/commands/runner.ts +43 -2
- package/src/http/db-query.ts +20 -5
- package/src/http/index.ts +10 -0
- package/src/http/script-runs.ts +555 -0
- package/src/prompts/session-templates.ts +24 -4
- package/src/providers/claude-adapter.ts +60 -13
- package/src/script-workflows/executor.ts +110 -0
- package/src/script-workflows/harness.ts +73 -0
- package/src/script-workflows/label-lint.ts +51 -0
- package/src/script-workflows/limits.ts +22 -0
- package/src/script-workflows/supervisor.ts +139 -0
- package/src/script-workflows/workflow-ctx.ts +205 -0
- package/src/scripts-runtime/sdk-allowlist.ts +3 -0
- package/src/scripts-runtime/types/stdlib.d.ts +60 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +60 -0
- package/src/server.ts +2 -0
- package/src/slack/handlers.ts +11 -4
- package/src/slack/message-text.ts +98 -0
- package/src/slack/thread-buffer.ts +5 -3
- package/src/tests/claude-adapter-binary.test.ts +147 -4
- package/src/tests/db-query.test.ts +28 -0
- package/src/tests/error-tracker.test.ts +121 -0
- package/src/tests/harness-provider-resolution.test.ts +33 -0
- package/src/tests/mcp-tools.test.ts +6 -0
- package/src/tests/prompt-template-session.test.ts +34 -5
- package/src/tests/script-runs-http.test.ts +278 -0
- package/src/tests/script-workflows-label-lint.test.ts +43 -0
- package/src/tests/script-workflows-runtime-e2e.test.ts +170 -0
- package/src/tests/scripts-mcp-e2e.test.ts +49 -2
- package/src/tests/seed-scripts.test.ts +347 -2
- package/src/tests/slack-message-text.test.ts +250 -0
- package/src/tests/system-default-skills.test.ts +40 -0
- package/src/tools/db-query.ts +16 -6
- package/src/tools/script-runs.ts +123 -0
- package/src/tools/slack-read.ts +12 -3
- package/src/tools/tool-config.ts +4 -1
- package/src/types.ts +52 -0
- package/src/utils/error-tracker.ts +40 -1
- package/src/utils/internal-ai/complete-structured.ts +10 -4
- package/src/workflows/executors/raw-llm.ts +76 -59
- package/templates/skills/pages/content.md +205 -55
- package/templates/skills/script-workflows/config.json +14 -0
- package/templates/skills/script-workflows/content.md +68 -0
- package/templates/skills/swarm-scripts/content.md +2 -3
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
|
+
}
|
|
@@ -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);
|