@desplega.ai/agent-swarm 1.92.0 → 1.92.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/openapi.json +276 -3
- package/package.json +6 -6
- package/plugin/skills/pages/SKILL.md +5 -2
- package/src/be/db.ts +327 -20
- package/src/be/memory/constants.ts +2 -1
- package/src/be/memory/providers/openai-embedding.ts +2 -5
- package/src/be/memory/providers/sqlite-store.ts +293 -76
- package/src/be/memory/types.ts +35 -0
- package/src/be/migrations/084_script_run_journal_duration.sql +5 -0
- package/src/be/migrations/085_script_runs_kind.sql +9 -0
- package/src/be/migrations/086_pages_default_authed.sql +64 -0
- package/src/be/migrations/087_skill_files.sql +19 -0
- package/src/be/modelsdev-cache.json +264 -328
- package/src/be/seed-scripts/catalog/boot-triage.ts +221 -0
- package/src/be/seed-scripts/catalog/catalog-report.ts +457 -0
- package/src/be/seed-scripts/catalog/compound-insights.ts +94 -0
- package/src/be/seed-scripts/catalog/gh-pr-snapshot.ts +1 -1
- package/src/be/seed-scripts/catalog/memory-eval.ts +1059 -0
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +34 -439
- package/src/be/seed-scripts/catalog/schedule-health.ts +78 -2
- package/src/be/seed-scripts/catalog/task-failure-audit.ts +48 -1
- package/src/be/seed-scripts/index.ts +32 -4
- package/src/be/seed-skills/index.ts +0 -7
- package/src/be/skill-sync.ts +91 -7
- package/src/commands/runner.ts +6 -2
- package/src/heartbeat/templates.ts +20 -16
- package/src/http/index.ts +41 -7
- package/src/http/mcp-user.ts +23 -0
- package/src/http/mcp.ts +58 -0
- package/src/http/memory.ts +58 -0
- package/src/http/pages.ts +1 -1
- package/src/http/script-runs.ts +2 -0
- package/src/http/scripts.ts +39 -2
- package/src/http/skills.ts +225 -0
- package/src/providers/claude-adapter.ts +56 -24
- package/src/script-workflows/workflow-ctx.ts +7 -3
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/swarm-sdk.ts +13 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +2 -0
- package/src/tests/claude-adapter-binary.test.ts +135 -81
- package/src/tests/create-page-tool.test.ts +19 -2
- package/src/tests/heartbeat-checklist.test.ts +36 -0
- package/src/tests/mcp-transport-gc.test.ts +58 -0
- package/src/tests/memory-health-endpoint.test.ts +78 -0
- package/src/tests/memory-store.test.ts +221 -1
- package/src/tests/pages-http.test.ts +20 -2
- package/src/tests/pages-storage.test.ts +26 -0
- package/src/tests/scripts-mcp-e2e.test.ts +53 -0
- package/src/tests/seed-scripts.test.ts +123 -3
- package/src/tests/skill-files-http.test.ts +171 -0
- package/src/tests/skill-files.test.ts +162 -0
- package/src/tests/skill-get-file-tool.test.ts +110 -0
- package/src/tests/skill-sync.test.ts +125 -6
- package/src/tools/create-page.ts +2 -2
- package/src/tools/skills/index.ts +1 -0
- package/src/tools/skills/skill-get-file.ts +80 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/types.ts +20 -0
- package/src/utils/internal-ai/complete-structured.ts +2 -2
- package/templates/schedules/daily-blocker-digest/content.md +68 -54
- package/templates/schedules/daily-compounding-reflection/content.md +4 -4
- package/templates/schedules/daily-hn-briefing/content.md +5 -5
- package/templates/schedules/daily-workflow-health-audit/content.md +6 -6
- package/templates/schedules/gtm-weekly-review/content.md +9 -9
- package/templates/schedules/weekly-dependabot-triage/content.md +24 -20
- package/templates/skills/agentmail-sending/content.md +6 -7
- package/templates/skills/desloppify/content.md +8 -9
- package/templates/skills/jira-interaction/content.md +25 -33
- package/templates/skills/kapso-whatsapp/content.md +29 -30
- package/templates/skills/linear-interaction/content.md +8 -9
- package/templates/skills/profile-corruption-escalation/content.md +44 -85
- package/templates/skills/sprite-cli/content.md +4 -5
- package/templates/skills/turso-interaction/content.md +14 -17
- package/templates/skills/workflow-iterate/content.md +38 -391
- package/templates/skills/x-api-interactions/content.md +4 -6
- package/templates/skills/scheduled-task-resilience/config.json +0 -14
- package/templates/skills/scheduled-task-resilience/content.md +0 -95
package/src/be/db.ts
CHANGED
|
@@ -64,6 +64,7 @@ import type {
|
|
|
64
64
|
ScheduledTaskSummary,
|
|
65
65
|
ScriptRun,
|
|
66
66
|
ScriptRunJournalEntry,
|
|
67
|
+
ScriptRunKind,
|
|
67
68
|
ScriptRunStatus,
|
|
68
69
|
Service,
|
|
69
70
|
ServiceStatus,
|
|
@@ -71,6 +72,7 @@ import type {
|
|
|
71
72
|
SessionCostSource,
|
|
72
73
|
SessionLog,
|
|
73
74
|
Skill,
|
|
75
|
+
SkillFile,
|
|
74
76
|
SkillScope,
|
|
75
77
|
SkillType,
|
|
76
78
|
SkillWithInstallInfo,
|
|
@@ -113,6 +115,26 @@ export function isSqliteVecAvailable(): boolean {
|
|
|
113
115
|
return sqliteVecAvailable;
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
function loadSqliteVec(database: Database): void {
|
|
119
|
+
sqliteVecAvailable = false;
|
|
120
|
+
try {
|
|
121
|
+
const extensionPath = process.env.SQLITE_VEC_EXTENSION_PATH;
|
|
122
|
+
if (extensionPath) {
|
|
123
|
+
database.loadExtension(extensionPath);
|
|
124
|
+
} else {
|
|
125
|
+
const sqliteVec = require("sqlite-vec");
|
|
126
|
+
sqliteVec.load(database);
|
|
127
|
+
}
|
|
128
|
+
sqliteVecAvailable = true;
|
|
129
|
+
console.log(`[db] sqlite-vec loaded${extensionPath ? ` from ${extensionPath}` : ""}`);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.warn(
|
|
132
|
+
"[db] sqlite-vec not available, falling back to in-memory cosine:",
|
|
133
|
+
(err as Error).message,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
116
138
|
export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
|
|
117
139
|
if (db) {
|
|
118
140
|
return db;
|
|
@@ -129,6 +151,7 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
|
|
|
129
151
|
db = Database.deserialize(templateBytes);
|
|
130
152
|
db.run("PRAGMA busy_timeout = 5000;");
|
|
131
153
|
db.run("PRAGMA foreign_keys = ON;");
|
|
154
|
+
loadSqliteVec(db);
|
|
132
155
|
configureDbResolver(resolvePromptTemplate);
|
|
133
156
|
// Ensure the encryption key is resolved even when restoring from the test
|
|
134
157
|
// template. The cache may have been cleared via __resetEncryptionKeyForTests
|
|
@@ -154,22 +177,7 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
|
|
|
154
177
|
// `require.resolve("sqlite-vec-<platform>/vec0.so")` can't find the native
|
|
155
178
|
// asset — so we prefer an explicit filesystem path when set, and only fall
|
|
156
179
|
// back to the npm resolver for normal dev runs.
|
|
157
|
-
|
|
158
|
-
const extensionPath = process.env.SQLITE_VEC_EXTENSION_PATH;
|
|
159
|
-
if (extensionPath) {
|
|
160
|
-
database.loadExtension(extensionPath);
|
|
161
|
-
} else {
|
|
162
|
-
const sqliteVec = require("sqlite-vec");
|
|
163
|
-
sqliteVec.load(database);
|
|
164
|
-
}
|
|
165
|
-
sqliteVecAvailable = true;
|
|
166
|
-
console.log(`[db] sqlite-vec loaded${extensionPath ? ` from ${extensionPath}` : ""}`);
|
|
167
|
-
} catch (err) {
|
|
168
|
-
console.warn(
|
|
169
|
-
"[db] sqlite-vec not available, falling back to in-memory cosine:",
|
|
170
|
-
(err as Error).message,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
180
|
+
loadSqliteVec(database);
|
|
173
181
|
|
|
174
182
|
// Run database migrations (schema creation + incremental changes)
|
|
175
183
|
runMigrations(database);
|
|
@@ -347,6 +355,7 @@ export function closeDb(): void {
|
|
|
347
355
|
db.close();
|
|
348
356
|
db = null;
|
|
349
357
|
}
|
|
358
|
+
sqliteVecAvailable = false;
|
|
350
359
|
}
|
|
351
360
|
|
|
352
361
|
// ============================================================================
|
|
@@ -7091,7 +7100,7 @@ export function createPage(data: {
|
|
|
7091
7100
|
title: string;
|
|
7092
7101
|
description?: string;
|
|
7093
7102
|
contentType: PageContentType;
|
|
7094
|
-
authMode
|
|
7103
|
+
authMode?: PageAuthMode;
|
|
7095
7104
|
passwordHash?: string;
|
|
7096
7105
|
body: string;
|
|
7097
7106
|
needsCredentials?: string[];
|
|
@@ -7110,7 +7119,7 @@ export function createPage(data: {
|
|
|
7110
7119
|
data.title,
|
|
7111
7120
|
data.description ?? null,
|
|
7112
7121
|
data.contentType,
|
|
7113
|
-
data.authMode,
|
|
7122
|
+
data.authMode ?? "authed",
|
|
7114
7123
|
data.passwordHash ?? null,
|
|
7115
7124
|
data.body,
|
|
7116
7125
|
data.needsCredentials ? JSON.stringify(data.needsCredentials) : null,
|
|
@@ -8692,6 +8701,119 @@ function rowToSkillWithInstall(row: SkillWithInstallRow): SkillWithInstallInfo {
|
|
|
8692
8701
|
};
|
|
8693
8702
|
}
|
|
8694
8703
|
|
|
8704
|
+
type SkillFileRow = {
|
|
8705
|
+
id: string;
|
|
8706
|
+
skillId: string;
|
|
8707
|
+
path: string;
|
|
8708
|
+
content: string;
|
|
8709
|
+
mimeType: string;
|
|
8710
|
+
isBinary: number;
|
|
8711
|
+
size: number | null;
|
|
8712
|
+
createdAt: string;
|
|
8713
|
+
lastUpdatedAt: string;
|
|
8714
|
+
};
|
|
8715
|
+
|
|
8716
|
+
function rowToSkillFile(row: SkillFileRow): SkillFile {
|
|
8717
|
+
return {
|
|
8718
|
+
id: row.id,
|
|
8719
|
+
skillId: row.skillId,
|
|
8720
|
+
path: row.path,
|
|
8721
|
+
content: row.content,
|
|
8722
|
+
mimeType: row.mimeType,
|
|
8723
|
+
isBinary: row.isBinary === 1,
|
|
8724
|
+
size: row.size,
|
|
8725
|
+
createdAt: row.createdAt,
|
|
8726
|
+
lastUpdatedAt: row.lastUpdatedAt,
|
|
8727
|
+
};
|
|
8728
|
+
}
|
|
8729
|
+
|
|
8730
|
+
export type SkillFileInput = {
|
|
8731
|
+
path: string;
|
|
8732
|
+
content: string;
|
|
8733
|
+
mimeType?: string;
|
|
8734
|
+
isBinary?: boolean;
|
|
8735
|
+
size?: number | null;
|
|
8736
|
+
};
|
|
8737
|
+
|
|
8738
|
+
export type SkillFileManifestEntry = Omit<SkillFile, "content">;
|
|
8739
|
+
type NormalizedSkillFileInput = {
|
|
8740
|
+
path: string;
|
|
8741
|
+
content: string;
|
|
8742
|
+
mimeType: string;
|
|
8743
|
+
isBinary: boolean;
|
|
8744
|
+
size: number;
|
|
8745
|
+
};
|
|
8746
|
+
|
|
8747
|
+
export const SKILL_FILE_LIMITS = {
|
|
8748
|
+
maxCount: Number(process.env.SKILL_FILES_MAX_COUNT ?? 100),
|
|
8749
|
+
maxTotalBytes: Number(process.env.SKILL_FILES_MAX_TOTAL_BYTES ?? 10 * 1024 * 1024),
|
|
8750
|
+
maxFileBytes: Number(process.env.SKILL_FILES_MAX_FILE_BYTES ?? 500 * 1024),
|
|
8751
|
+
};
|
|
8752
|
+
|
|
8753
|
+
const BINARY_SKILL_FILE_PLACEHOLDER = "[binary file - not synced]";
|
|
8754
|
+
|
|
8755
|
+
export function normalizeSkillFilePath(path: string): string {
|
|
8756
|
+
const raw = path.trim().replace(/\\/g, "/");
|
|
8757
|
+
if (!raw) throw new Error("File path is required");
|
|
8758
|
+
if (raw.startsWith("/")) throw new Error("File path must be relative");
|
|
8759
|
+
|
|
8760
|
+
const parts = raw.split("/").filter(Boolean);
|
|
8761
|
+
if (parts.length === 0) throw new Error("File path is required");
|
|
8762
|
+
if (parts.some((part) => part === "." || part === "..")) {
|
|
8763
|
+
throw new Error("File path cannot contain traversal segments");
|
|
8764
|
+
}
|
|
8765
|
+
|
|
8766
|
+
const normalized = parts.join("/");
|
|
8767
|
+
if (normalized === "SKILL.md") {
|
|
8768
|
+
throw new Error("SKILL.md is stored on the skill record, not in skill_files");
|
|
8769
|
+
}
|
|
8770
|
+
return normalized;
|
|
8771
|
+
}
|
|
8772
|
+
|
|
8773
|
+
function byteSize(content: string): number {
|
|
8774
|
+
return Buffer.byteLength(content, "utf8");
|
|
8775
|
+
}
|
|
8776
|
+
|
|
8777
|
+
function normalizeSkillFileInput(input: SkillFileInput): NormalizedSkillFileInput {
|
|
8778
|
+
const path = normalizeSkillFilePath(input.path);
|
|
8779
|
+
const isBinary = input.isBinary === true;
|
|
8780
|
+
const content = isBinary ? input.content || BINARY_SKILL_FILE_PLACEHOLDER : input.content;
|
|
8781
|
+
const size = input.size ?? byteSize(content);
|
|
8782
|
+
if (!Number.isFinite(size) || size < 0) {
|
|
8783
|
+
throw new Error("File size must be a non-negative number");
|
|
8784
|
+
}
|
|
8785
|
+
if (size > SKILL_FILE_LIMITS.maxFileBytes) {
|
|
8786
|
+
throw new Error(`File ${path} exceeds max size ${SKILL_FILE_LIMITS.maxFileBytes}`);
|
|
8787
|
+
}
|
|
8788
|
+
|
|
8789
|
+
return {
|
|
8790
|
+
path,
|
|
8791
|
+
content,
|
|
8792
|
+
mimeType: input.mimeType ?? "text/plain",
|
|
8793
|
+
isBinary,
|
|
8794
|
+
size,
|
|
8795
|
+
};
|
|
8796
|
+
}
|
|
8797
|
+
|
|
8798
|
+
function assertSkillFileLimits(skillId: string, incoming: SkillFileInput[], replaceAll: boolean) {
|
|
8799
|
+
const existing = replaceAll ? [] : listSkillFileManifest(skillId);
|
|
8800
|
+
const byPath = new Map(existing.map((file) => [file.path, file.size ?? 0]));
|
|
8801
|
+
|
|
8802
|
+
for (const input of incoming) {
|
|
8803
|
+
const normalized = normalizeSkillFileInput(input);
|
|
8804
|
+
byPath.set(normalized.path, normalized.size);
|
|
8805
|
+
}
|
|
8806
|
+
|
|
8807
|
+
if (byPath.size > SKILL_FILE_LIMITS.maxCount) {
|
|
8808
|
+
throw new Error(`Skill file count exceeds max ${SKILL_FILE_LIMITS.maxCount}`);
|
|
8809
|
+
}
|
|
8810
|
+
|
|
8811
|
+
const total = [...byPath.values()].reduce((sum, size) => sum + size, 0);
|
|
8812
|
+
if (total > SKILL_FILE_LIMITS.maxTotalBytes) {
|
|
8813
|
+
throw new Error(`Skill files exceed max total size ${SKILL_FILE_LIMITS.maxTotalBytes}`);
|
|
8814
|
+
}
|
|
8815
|
+
}
|
|
8816
|
+
|
|
8695
8817
|
export interface SkillInsert {
|
|
8696
8818
|
name: string;
|
|
8697
8819
|
description: string;
|
|
@@ -8865,6 +8987,124 @@ export function updateSkill(
|
|
|
8865
8987
|
return row ? rowToSkill(row) : null;
|
|
8866
8988
|
}
|
|
8867
8989
|
|
|
8990
|
+
function bumpSkillVersion(skillId: string, now = new Date().toISOString()) {
|
|
8991
|
+
getDb()
|
|
8992
|
+
.prepare("UPDATE skills SET version = version + 1, lastUpdatedAt = ? WHERE id = ?")
|
|
8993
|
+
.run(now, skillId);
|
|
8994
|
+
}
|
|
8995
|
+
|
|
8996
|
+
export function listSkillFileManifest(skillId: string): SkillFileManifestEntry[] {
|
|
8997
|
+
return getDb()
|
|
8998
|
+
.prepare<SkillFileRow, [string]>(
|
|
8999
|
+
`SELECT id, skillId, path, content, mimeType, isBinary, size, createdAt, lastUpdatedAt
|
|
9000
|
+
FROM skill_files
|
|
9001
|
+
WHERE skillId = ?
|
|
9002
|
+
ORDER BY path ASC`,
|
|
9003
|
+
)
|
|
9004
|
+
.all(skillId)
|
|
9005
|
+
.map((row) => {
|
|
9006
|
+
const { content: _content, ...manifest } = rowToSkillFile(row);
|
|
9007
|
+
return manifest;
|
|
9008
|
+
});
|
|
9009
|
+
}
|
|
9010
|
+
|
|
9011
|
+
export function getSkillFiles(skillId: string): SkillFile[] {
|
|
9012
|
+
return getDb()
|
|
9013
|
+
.prepare<SkillFileRow, [string]>(
|
|
9014
|
+
`SELECT id, skillId, path, content, mimeType, isBinary, size, createdAt, lastUpdatedAt
|
|
9015
|
+
FROM skill_files
|
|
9016
|
+
WHERE skillId = ?
|
|
9017
|
+
ORDER BY path ASC`,
|
|
9018
|
+
)
|
|
9019
|
+
.all(skillId)
|
|
9020
|
+
.map(rowToSkillFile);
|
|
9021
|
+
}
|
|
9022
|
+
|
|
9023
|
+
export function getSkillFile(skillId: string, path: string): SkillFile | null {
|
|
9024
|
+
const normalizedPath = normalizeSkillFilePath(path);
|
|
9025
|
+
const row = getDb()
|
|
9026
|
+
.prepare<SkillFileRow, [string, string]>(
|
|
9027
|
+
`SELECT id, skillId, path, content, mimeType, isBinary, size, createdAt, lastUpdatedAt
|
|
9028
|
+
FROM skill_files
|
|
9029
|
+
WHERE skillId = ? AND path = ?`,
|
|
9030
|
+
)
|
|
9031
|
+
.get(skillId, normalizedPath);
|
|
9032
|
+
return row ? rowToSkillFile(row) : null;
|
|
9033
|
+
}
|
|
9034
|
+
|
|
9035
|
+
export function upsertSkillFile(skillId: string, input: SkillFileInput): SkillFile {
|
|
9036
|
+
const payload = normalizeSkillFileInput(input);
|
|
9037
|
+
assertSkillFileLimits(skillId, [payload], false);
|
|
9038
|
+
|
|
9039
|
+
const id = crypto.randomUUID();
|
|
9040
|
+
const now = new Date().toISOString();
|
|
9041
|
+
return upsertSkillFileUnchecked(skillId, payload, id, now, true);
|
|
9042
|
+
}
|
|
9043
|
+
|
|
9044
|
+
function upsertSkillFileUnchecked(
|
|
9045
|
+
skillId: string,
|
|
9046
|
+
payload: NormalizedSkillFileInput,
|
|
9047
|
+
id: string,
|
|
9048
|
+
now: string,
|
|
9049
|
+
bumpVersion: boolean,
|
|
9050
|
+
): SkillFile {
|
|
9051
|
+
const row = getDb()
|
|
9052
|
+
.prepare<SkillFileRow, (string | number | null)[]>(
|
|
9053
|
+
`INSERT INTO skill_files (
|
|
9054
|
+
id, skillId, path, content, mimeType, isBinary, size, createdAt, lastUpdatedAt
|
|
9055
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9056
|
+
ON CONFLICT(skillId, path) DO UPDATE SET
|
|
9057
|
+
content = excluded.content,
|
|
9058
|
+
mimeType = excluded.mimeType,
|
|
9059
|
+
isBinary = excluded.isBinary,
|
|
9060
|
+
size = excluded.size,
|
|
9061
|
+
lastUpdatedAt = excluded.lastUpdatedAt
|
|
9062
|
+
RETURNING *`,
|
|
9063
|
+
)
|
|
9064
|
+
.get(
|
|
9065
|
+
id,
|
|
9066
|
+
skillId,
|
|
9067
|
+
payload.path,
|
|
9068
|
+
payload.content,
|
|
9069
|
+
payload.mimeType,
|
|
9070
|
+
payload.isBinary ? 1 : 0,
|
|
9071
|
+
payload.size,
|
|
9072
|
+
now,
|
|
9073
|
+
now,
|
|
9074
|
+
);
|
|
9075
|
+
|
|
9076
|
+
if (!row) throw new Error("Failed to upsert skill file");
|
|
9077
|
+
if (bumpVersion) bumpSkillVersion(skillId, now);
|
|
9078
|
+
return rowToSkillFile(row);
|
|
9079
|
+
}
|
|
9080
|
+
|
|
9081
|
+
export function upsertSkillFiles(skillId: string, files: SkillFileInput[]): SkillFile[] {
|
|
9082
|
+
if (files.length === 0) return [];
|
|
9083
|
+
const normalized = files.map(normalizeSkillFileInput);
|
|
9084
|
+
assertSkillFileLimits(skillId, normalized, false);
|
|
9085
|
+
|
|
9086
|
+
const now = new Date().toISOString();
|
|
9087
|
+
return getDb().transaction(() => {
|
|
9088
|
+
const rows = normalized.map((file) =>
|
|
9089
|
+
upsertSkillFileUnchecked(skillId, file, crypto.randomUUID(), now, false),
|
|
9090
|
+
);
|
|
9091
|
+
bumpSkillVersion(skillId, now);
|
|
9092
|
+
return rows;
|
|
9093
|
+
})();
|
|
9094
|
+
}
|
|
9095
|
+
|
|
9096
|
+
export function deleteSkillFile(skillId: string, path: string): boolean {
|
|
9097
|
+
const normalizedPath = normalizeSkillFilePath(path);
|
|
9098
|
+
const result = getDb()
|
|
9099
|
+
.prepare("DELETE FROM skill_files WHERE skillId = ? AND path = ?")
|
|
9100
|
+
.run(skillId, normalizedPath);
|
|
9101
|
+
if (result.changes > 0) {
|
|
9102
|
+
bumpSkillVersion(skillId);
|
|
9103
|
+
return true;
|
|
9104
|
+
}
|
|
9105
|
+
return false;
|
|
9106
|
+
}
|
|
9107
|
+
|
|
8868
9108
|
export function deleteSkill(id: string): boolean {
|
|
8869
9109
|
const result = getDb().prepare("DELETE FROM skills WHERE id = ?").run(id);
|
|
8870
9110
|
return result.changes > 0;
|
|
@@ -11231,6 +11471,7 @@ type ScriptRunRow = {
|
|
|
11231
11471
|
scriptName: string | null;
|
|
11232
11472
|
source: string;
|
|
11233
11473
|
args: string;
|
|
11474
|
+
kind: string;
|
|
11234
11475
|
status: string;
|
|
11235
11476
|
pid: number | null;
|
|
11236
11477
|
startedAt: string;
|
|
@@ -11256,6 +11497,7 @@ function rowToScriptRun(row: ScriptRunRow): ScriptRun {
|
|
|
11256
11497
|
scriptName: row.scriptName ?? undefined,
|
|
11257
11498
|
source: row.source,
|
|
11258
11499
|
args: JSON.parse(row.args),
|
|
11500
|
+
kind: row.kind as ScriptRunKind,
|
|
11259
11501
|
status: row.status as ScriptRunStatus,
|
|
11260
11502
|
pid: row.pid ?? undefined,
|
|
11261
11503
|
startedAt: row.startedAt,
|
|
@@ -11322,6 +11564,67 @@ export function createScriptRun(data: {
|
|
|
11322
11564
|
return { run: rowToScriptRun(row), existing: false };
|
|
11323
11565
|
}
|
|
11324
11566
|
|
|
11567
|
+
// Persist a synchronous inline run (POST /api/scripts/run) as an already-terminal
|
|
11568
|
+
// row. Unlike createScriptRun these never get a journal and never use the
|
|
11569
|
+
// idempotencyKey column (inline idempotency lives in the kv table).
|
|
11570
|
+
export function recordInlineScriptRun(data: {
|
|
11571
|
+
id: string;
|
|
11572
|
+
agentId: string;
|
|
11573
|
+
source: string;
|
|
11574
|
+
args: unknown;
|
|
11575
|
+
scriptName?: string;
|
|
11576
|
+
status: "completed" | "failed";
|
|
11577
|
+
output?: unknown;
|
|
11578
|
+
error?: string;
|
|
11579
|
+
startedAt: string;
|
|
11580
|
+
finishedAt: string;
|
|
11581
|
+
requestedByUserId?: string;
|
|
11582
|
+
createdBy?: string;
|
|
11583
|
+
}): ScriptRun {
|
|
11584
|
+
const row = getDb()
|
|
11585
|
+
.prepare<
|
|
11586
|
+
ScriptRunRow,
|
|
11587
|
+
[
|
|
11588
|
+
string,
|
|
11589
|
+
string,
|
|
11590
|
+
string | null,
|
|
11591
|
+
string,
|
|
11592
|
+
string,
|
|
11593
|
+
string,
|
|
11594
|
+
string | null,
|
|
11595
|
+
string | null,
|
|
11596
|
+
string,
|
|
11597
|
+
string,
|
|
11598
|
+
string | null,
|
|
11599
|
+
string | null,
|
|
11600
|
+
string | null,
|
|
11601
|
+
]
|
|
11602
|
+
>(
|
|
11603
|
+
`INSERT INTO script_runs
|
|
11604
|
+
(id, agentId, scriptName, source, args, kind, status, output, error,
|
|
11605
|
+
startedAt, finishedAt, requestedByUserId, created_by, updated_by)
|
|
11606
|
+
VALUES (?, ?, ?, ?, ?, 'inline', ?, ?, ?, ?, ?, ?, ?, ?)
|
|
11607
|
+
RETURNING *`,
|
|
11608
|
+
)
|
|
11609
|
+
.get(
|
|
11610
|
+
data.id,
|
|
11611
|
+
data.agentId,
|
|
11612
|
+
data.scriptName ?? null,
|
|
11613
|
+
data.source,
|
|
11614
|
+
JSON.stringify(data.args ?? null),
|
|
11615
|
+
data.status,
|
|
11616
|
+
data.output === undefined ? null : JSON.stringify(data.output),
|
|
11617
|
+
data.error ?? null,
|
|
11618
|
+
data.startedAt,
|
|
11619
|
+
data.finishedAt,
|
|
11620
|
+
data.requestedByUserId ?? null,
|
|
11621
|
+
data.createdBy ?? null,
|
|
11622
|
+
data.createdBy ?? null,
|
|
11623
|
+
);
|
|
11624
|
+
if (!row) throw new Error("Failed to record inline script run");
|
|
11625
|
+
return rowToScriptRun(row);
|
|
11626
|
+
}
|
|
11627
|
+
|
|
11325
11628
|
export function getScriptRun(id: string): ScriptRun | null {
|
|
11326
11629
|
const row = getDb()
|
|
11327
11630
|
.prepare<ScriptRunRow, [string]>("SELECT * FROM script_runs WHERE id = ?")
|
|
@@ -11459,6 +11762,7 @@ type ScriptRunJournalRow = {
|
|
|
11459
11762
|
error: string | null;
|
|
11460
11763
|
startedAt: string;
|
|
11461
11764
|
completedAt: string | null;
|
|
11765
|
+
durationMs: number | null;
|
|
11462
11766
|
created_by: string | null;
|
|
11463
11767
|
updated_by: string | null;
|
|
11464
11768
|
};
|
|
@@ -11475,6 +11779,7 @@ function rowToScriptRunJournalEntry(row: ScriptRunJournalRow): ScriptRunJournalE
|
|
|
11475
11779
|
error: row.error ?? undefined,
|
|
11476
11780
|
startedAt: row.startedAt,
|
|
11477
11781
|
completedAt: row.completedAt ?? undefined,
|
|
11782
|
+
durationMs: row.durationMs ?? undefined,
|
|
11478
11783
|
};
|
|
11479
11784
|
}
|
|
11480
11785
|
|
|
@@ -11498,11 +11803,12 @@ export function upsertScriptRunJournalStep(data: {
|
|
|
11498
11803
|
status: "completed" | "failed";
|
|
11499
11804
|
result?: unknown;
|
|
11500
11805
|
error?: string;
|
|
11806
|
+
durationMs?: number;
|
|
11501
11807
|
}): void {
|
|
11502
11808
|
getDb().run(
|
|
11503
11809
|
`INSERT OR IGNORE INTO script_run_journal
|
|
11504
|
-
(id, runId, stepKey, stepType, config, status, result, error, completedAt)
|
|
11505
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
|
|
11810
|
+
(id, runId, stepKey, stepType, config, status, result, error, durationMs, completedAt)
|
|
11811
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
|
|
11506
11812
|
[
|
|
11507
11813
|
crypto.randomUUID(),
|
|
11508
11814
|
data.runId,
|
|
@@ -11512,6 +11818,7 @@ export function upsertScriptRunJournalStep(data: {
|
|
|
11512
11818
|
data.status,
|
|
11513
11819
|
data.result !== undefined ? JSON.stringify(data.result) : null,
|
|
11514
11820
|
data.error ?? null,
|
|
11821
|
+
data.durationMs ?? null,
|
|
11515
11822
|
],
|
|
11516
11823
|
);
|
|
11517
11824
|
}
|
|
@@ -22,5 +22,6 @@ export const ACCESS_BOOST_RECENCY_WINDOW_HOURS = numEnv("MEMORY_ACCESS_RECENCY_H
|
|
|
22
22
|
export const CANDIDATE_SET_MULTIPLIER = numEnv("MEMORY_CANDIDATE_MULTIPLIER", 3);
|
|
23
23
|
|
|
24
24
|
// Embedding defaults
|
|
25
|
-
export const
|
|
25
|
+
export const EMBEDDING_DIMENSIONS = numEnv("EMBEDDING_DIMENSIONS", 512);
|
|
26
|
+
export const DEFAULT_EMBEDDING_DIMENSIONS = EMBEDDING_DIMENSIONS;
|
|
26
27
|
export const DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import {
|
|
2
|
+
import { DEFAULT_EMBEDDING_MODEL, EMBEDDING_DIMENSIONS } from "../constants";
|
|
3
3
|
import type { EmbeddingProvider } from "../types";
|
|
4
4
|
|
|
5
5
|
interface OpenAIEmbeddingConfig {
|
|
@@ -21,10 +21,7 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
|
21
21
|
|
|
22
22
|
this.model = config?.model ?? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small";
|
|
23
23
|
|
|
24
|
-
this.dimensions =
|
|
25
|
-
config?.dimensions ??
|
|
26
|
-
Number(process.env.EMBEDDING_DIMENSIONS) ??
|
|
27
|
-
DEFAULT_EMBEDDING_DIMENSIONS;
|
|
24
|
+
this.dimensions = config?.dimensions ?? EMBEDDING_DIMENSIONS;
|
|
28
25
|
|
|
29
26
|
this.name = config?.model
|
|
30
27
|
? `openai/${config.model}`
|