open-managed-agents 0.1.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/LICENSE +93 -0
- package/README.md +248 -0
- package/bin/oma.mjs +499 -0
- package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
- package/dist/scripts/alpha-smoke-models.mjs +92 -0
- package/dist/scripts/alpha-smoke.mjs +581 -0
- package/dist/scripts/oma-doctor.js +230 -0
- package/dist/scripts/oma-models.js +299 -0
- package/dist/scripts/oma-workspaces.js +133 -0
- package/dist/src/control-plane/admin/auth.js +43 -0
- package/dist/src/control-plane/admin/routes.js +76 -0
- package/dist/src/control-plane/admin/service.js +102 -0
- package/dist/src/control-plane/admission.js +90 -0
- package/dist/src/control-plane/agents/routes.js +63 -0
- package/dist/src/control-plane/agents/service.js +687 -0
- package/dist/src/control-plane/agents/store.js +391 -0
- package/dist/src/control-plane/agents/types.js +1 -0
- package/dist/src/control-plane/api-constants.js +3 -0
- package/dist/src/control-plane/app.js +907 -0
- package/dist/src/control-plane/console/auth.js +68 -0
- package/dist/src/control-plane/console/static.js +112 -0
- package/dist/src/control-plane/deployment-runtime-config.js +328 -0
- package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
- package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
- package/dist/src/control-plane/deployment-storage.js +354 -0
- package/dist/src/control-plane/egress/image.js +48 -0
- package/dist/src/control-plane/egress/policy.js +571 -0
- package/dist/src/control-plane/egress/presets.js +77 -0
- package/dist/src/control-plane/egress/proxy.js +72 -0
- package/dist/src/control-plane/egress/ssrf.js +106 -0
- package/dist/src/control-plane/egress/utils/debug.js +14 -0
- package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
- package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
- package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
- package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
- package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
- package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
- package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
- package/dist/src/control-plane/environments/routes.js +54 -0
- package/dist/src/control-plane/environments/service.js +112 -0
- package/dist/src/control-plane/environments/store.js +132 -0
- package/dist/src/control-plane/environments/types.js +1 -0
- package/dist/src/control-plane/errors.js +81 -0
- package/dist/src/control-plane/events/broadcaster.js +217 -0
- package/dist/src/control-plane/events/constants.js +2 -0
- package/dist/src/control-plane/events/persist.js +39 -0
- package/dist/src/control-plane/events/request.js +190 -0
- package/dist/src/control-plane/events/routes.js +159 -0
- package/dist/src/control-plane/events/runtime-helpers.js +134 -0
- package/dist/src/control-plane/events/service.js +1985 -0
- package/dist/src/control-plane/events/session-guards.js +29 -0
- package/dist/src/control-plane/events/sse.js +6 -0
- package/dist/src/control-plane/events/store.js +772 -0
- package/dist/src/control-plane/events/tool-persistence.js +102 -0
- package/dist/src/control-plane/events/types.js +21 -0
- package/dist/src/control-plane/files/routes.js +121 -0
- package/dist/src/control-plane/files/service.js +65 -0
- package/dist/src/control-plane/files/store-common.js +87 -0
- package/dist/src/control-plane/files/store-local.js +488 -0
- package/dist/src/control-plane/files/store-memory.js +270 -0
- package/dist/src/control-plane/files/store.js +2 -0
- package/dist/src/control-plane/files/types.js +7 -0
- package/dist/src/control-plane/http.js +25 -0
- package/dist/src/control-plane/ids.js +43 -0
- package/dist/src/control-plane/logging.js +263 -0
- package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
- package/dist/src/control-plane/models/catalog.js +78 -0
- package/dist/src/control-plane/models/config-security.js +376 -0
- package/dist/src/control-plane/models/deployment-config.js +93 -0
- package/dist/src/control-plane/models/routes.js +32 -0
- package/dist/src/control-plane/models/service.js +138 -0
- package/dist/src/control-plane/observability/instruments.js +65 -0
- package/dist/src/control-plane/observability/metrics.js +125 -0
- package/dist/src/control-plane/observability/routes.js +95 -0
- package/dist/src/control-plane/openapi/document.js +282 -0
- package/dist/src/control-plane/openapi/routes.js +78 -0
- package/dist/src/control-plane/request-idempotency.js +85 -0
- package/dist/src/control-plane/secrets/envelope.js +132 -0
- package/dist/src/control-plane/secrets/master-key.js +44 -0
- package/dist/src/control-plane/secrets/routes.js +20 -0
- package/dist/src/control-plane/secrets/service.js +71 -0
- package/dist/src/control-plane/secrets/store.js +212 -0
- package/dist/src/control-plane/secrets/types.js +3 -0
- package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
- package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
- package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
- package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
- package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
- package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
- package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
- package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
- package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
- package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
- package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
- package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
- package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
- package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
- package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
- package/dist/src/control-plane/sessions/pi/translator.js +100 -0
- package/dist/src/control-plane/sessions/request.js +164 -0
- package/dist/src/control-plane/sessions/resources.js +63 -0
- package/dist/src/control-plane/sessions/routes.js +88 -0
- package/dist/src/control-plane/sessions/serialize.js +24 -0
- package/dist/src/control-plane/sessions/service.js +733 -0
- package/dist/src/control-plane/sessions/store.js +538 -0
- package/dist/src/control-plane/sessions/types.js +1 -0
- package/dist/src/control-plane/skills/archive.js +169 -0
- package/dist/src/control-plane/skills/routes.js +41 -0
- package/dist/src/control-plane/skills/service.js +29 -0
- package/dist/src/control-plane/skills/store.js +233 -0
- package/dist/src/control-plane/skills/types.js +5 -0
- package/dist/src/control-plane/sqlite-transaction.js +15 -0
- package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
- package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
- package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
- package/dist/src/control-plane/vaults/routes.js +66 -0
- package/dist/src/control-plane/vaults/service.js +553 -0
- package/dist/src/control-plane/vaults/store.js +691 -0
- package/dist/src/control-plane/vaults/types.js +1 -0
- package/dist/src/control-plane/wake-loop.js +65 -0
- package/dist/src/control-plane/wiring.js +92 -0
- package/dist/src/control-plane/workspace.js +4 -0
- package/dist/src/control-plane/workspaces/store.js +158 -0
- package/dist/src/egress-proxy-main.js +103 -0
- package/dist/src/main.js +124 -0
- package/dist/src/types/agents.js +1 -0
- package/dist/src/types/common.js +1 -0
- package/dist/src/types/environments.js +1 -0
- package/dist/src/types/events.js +121 -0
- package/dist/src/types/files.js +1 -0
- package/dist/src/types/json.js +19 -0
- package/dist/src/types/sessions.js +1 -0
- package/dist/ui/managed-agents-console/README.md +52 -0
- package/dist/ui/managed-agents-console/docs/agents.md +23 -0
- package/dist/ui/managed-agents-console/docs/console.md +23 -0
- package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
- package/dist/ui/managed-agents-console/docs/environments.md +27 -0
- package/dist/ui/managed-agents-console/docs/events.md +19 -0
- package/dist/ui/managed-agents-console/docs/files.md +17 -0
- package/dist/ui/managed-agents-console/docs/github.md +11 -0
- package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
- package/dist/ui/managed-agents-console/docs/memory.md +11 -0
- package/dist/ui/managed-agents-console/docs/migration.md +17 -0
- package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
- package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
- package/dist/ui/managed-agents-console/docs/overview.md +41 -0
- package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
- package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
- package/dist/ui/managed-agents-console/docs/reference.md +27 -0
- package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
- package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
- package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
- package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
- package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
- package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
- package/dist/ui/managed-agents-console/docs/skills.md +15 -0
- package/dist/ui/managed-agents-console/docs/tools.md +23 -0
- package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
- package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
- package/dist/ui/managed-agents-console/index.html +47 -0
- package/dist/ui/managed-agents-console/serve.mjs +116 -0
- package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
- package/dist/ui/managed-agents-console/src/api.js +1043 -0
- package/dist/ui/managed-agents-console/src/app.jsx +633 -0
- package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
- package/dist/ui/managed-agents-console/src/console.css +733 -0
- package/dist/ui/managed-agents-console/src/data.js +188 -0
- package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
- package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
- package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
- package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
- package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
- package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
- package/dist/ui/managed-agents-console/src/sse.js +185 -0
- package/dist/ui/managed-agents-console/src/states.jsx +90 -0
- package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
- package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
- package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
- package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
- package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
- package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
- package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
- package/dist/ui/openapi-docs/VENDOR.md +27 -0
- package/dist/ui/openapi-docs/index.html +16 -0
- package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
- package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
- package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
- package/package.json +53 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { DatabaseSync } from "node:sqlite";
|
|
3
|
+
import { withSqliteTransaction } from "../sqlite-transaction.js";
|
|
4
|
+
const SCHEMA = `
|
|
5
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
6
|
+
id TEXT PRIMARY KEY,
|
|
7
|
+
workspace_id TEXT NOT NULL,
|
|
8
|
+
type TEXT NOT NULL,
|
|
9
|
+
name TEXT NOT NULL,
|
|
10
|
+
model TEXT NOT NULL,
|
|
11
|
+
system TEXT,
|
|
12
|
+
description TEXT,
|
|
13
|
+
tools TEXT NOT NULL,
|
|
14
|
+
skills TEXT NOT NULL,
|
|
15
|
+
mcp_servers TEXT NOT NULL,
|
|
16
|
+
metadata TEXT NOT NULL,
|
|
17
|
+
multiagent TEXT,
|
|
18
|
+
version INTEGER NOT NULL,
|
|
19
|
+
created_at TEXT NOT NULL,
|
|
20
|
+
updated_at TEXT NOT NULL,
|
|
21
|
+
archived_at TEXT
|
|
22
|
+
);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS agents_by_workspace ON agents (workspace_id, id);
|
|
24
|
+
CREATE TABLE IF NOT EXISTS agent_versions (
|
|
25
|
+
workspace_id TEXT NOT NULL,
|
|
26
|
+
agent_id TEXT NOT NULL,
|
|
27
|
+
version INTEGER NOT NULL,
|
|
28
|
+
name TEXT NOT NULL,
|
|
29
|
+
model TEXT NOT NULL,
|
|
30
|
+
system TEXT,
|
|
31
|
+
description TEXT,
|
|
32
|
+
tools TEXT NOT NULL,
|
|
33
|
+
skills TEXT NOT NULL,
|
|
34
|
+
mcp_servers TEXT NOT NULL,
|
|
35
|
+
metadata TEXT NOT NULL,
|
|
36
|
+
multiagent TEXT,
|
|
37
|
+
created_at TEXT NOT NULL,
|
|
38
|
+
updated_at TEXT NOT NULL,
|
|
39
|
+
PRIMARY KEY (workspace_id, agent_id, version)
|
|
40
|
+
);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS agent_versions_newest
|
|
42
|
+
ON agent_versions (workspace_id, agent_id, version DESC);
|
|
43
|
+
`;
|
|
44
|
+
export class SqliteAgentStore {
|
|
45
|
+
db;
|
|
46
|
+
cursorSigningKey = randomBytes(32);
|
|
47
|
+
insertStmt;
|
|
48
|
+
insertVersionStmt;
|
|
49
|
+
updateHeadStmt;
|
|
50
|
+
retrieveVersionStmt;
|
|
51
|
+
listVersionsFirstStmt;
|
|
52
|
+
listVersionsAfterStmt;
|
|
53
|
+
retrieveActiveStmt;
|
|
54
|
+
retrieveAnyStmt;
|
|
55
|
+
archiveStmt;
|
|
56
|
+
listActiveStmt;
|
|
57
|
+
listAllStmt;
|
|
58
|
+
listActiveSinceStmt;
|
|
59
|
+
listAllSinceStmt;
|
|
60
|
+
constructor(db) {
|
|
61
|
+
this.db = db;
|
|
62
|
+
this.db.exec(SCHEMA);
|
|
63
|
+
withSqliteTransaction(this.db, () => {
|
|
64
|
+
migratePersistedModels(this.db);
|
|
65
|
+
this.db.exec(`INSERT OR IGNORE INTO agent_versions (
|
|
66
|
+
workspace_id, agent_id, version, name, model, system, description,
|
|
67
|
+
tools, skills, mcp_servers, metadata, multiagent, created_at, updated_at
|
|
68
|
+
) SELECT workspace_id, id, version, name, model, system, description,
|
|
69
|
+
tools, skills, mcp_servers, metadata, multiagent, created_at, updated_at
|
|
70
|
+
FROM agents`);
|
|
71
|
+
assertAllPersistedModelsNormalized(this.db);
|
|
72
|
+
});
|
|
73
|
+
this.insertStmt = this.db.prepare(`INSERT INTO agents (
|
|
74
|
+
id, workspace_id, type, name, model, system, description, tools, skills,
|
|
75
|
+
mcp_servers, metadata, multiagent, version, created_at, updated_at,
|
|
76
|
+
archived_at
|
|
77
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
78
|
+
this.insertVersionStmt = this.db.prepare(`INSERT INTO agent_versions (
|
|
79
|
+
workspace_id, agent_id, version, name, model, system, description,
|
|
80
|
+
tools, skills, mcp_servers, metadata, multiagent, created_at, updated_at
|
|
81
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
82
|
+
this.updateHeadStmt = this.db.prepare(`UPDATE agents SET
|
|
83
|
+
name = ?, model = ?, system = ?, description = ?, tools = ?, skills = ?,
|
|
84
|
+
mcp_servers = ?, metadata = ?, multiagent = ?, version = ?, updated_at = ?
|
|
85
|
+
WHERE workspace_id = ? AND id = ? AND version = ? AND archived_at IS NULL`);
|
|
86
|
+
this.retrieveVersionStmt = this.db.prepare(`SELECT * FROM agent_versions
|
|
87
|
+
WHERE workspace_id = ? AND agent_id = ? AND version = ?`);
|
|
88
|
+
this.listVersionsFirstStmt = this.db.prepare(`SELECT * FROM agent_versions
|
|
89
|
+
WHERE workspace_id = ? AND agent_id = ?
|
|
90
|
+
ORDER BY version DESC LIMIT ?`);
|
|
91
|
+
this.listVersionsAfterStmt = this.db.prepare(`SELECT * FROM agent_versions
|
|
92
|
+
WHERE workspace_id = ? AND agent_id = ? AND version < ?
|
|
93
|
+
ORDER BY version DESC LIMIT ?`);
|
|
94
|
+
this.retrieveActiveStmt = this.db.prepare(`SELECT * FROM agents
|
|
95
|
+
WHERE workspace_id = ? AND id = ? AND archived_at IS NULL`);
|
|
96
|
+
this.retrieveAnyStmt = this.db.prepare(`SELECT * FROM agents
|
|
97
|
+
WHERE workspace_id = ? AND id = ?`);
|
|
98
|
+
this.archiveStmt = this.db.prepare(`UPDATE agents
|
|
99
|
+
SET archived_at = COALESCE(archived_at, ?),
|
|
100
|
+
updated_at = CASE WHEN archived_at IS NULL THEN ? ELSE updated_at END
|
|
101
|
+
WHERE workspace_id = ? AND id = ?`);
|
|
102
|
+
this.listActiveStmt = this.db.prepare(`SELECT * FROM agents
|
|
103
|
+
WHERE workspace_id = ? AND archived_at IS NULL
|
|
104
|
+
ORDER BY id ASC
|
|
105
|
+
LIMIT ?`);
|
|
106
|
+
this.listAllStmt = this.db.prepare(`SELECT * FROM agents
|
|
107
|
+
WHERE workspace_id = ?
|
|
108
|
+
ORDER BY id ASC
|
|
109
|
+
LIMIT ?`);
|
|
110
|
+
this.listActiveSinceStmt = this.db.prepare(`SELECT * FROM agents
|
|
111
|
+
WHERE workspace_id = ? AND id > ? AND archived_at IS NULL
|
|
112
|
+
ORDER BY id ASC
|
|
113
|
+
LIMIT ?`);
|
|
114
|
+
this.listAllSinceStmt = this.db.prepare(`SELECT * FROM agents
|
|
115
|
+
WHERE workspace_id = ? AND id > ?
|
|
116
|
+
ORDER BY id ASC
|
|
117
|
+
LIMIT ?`);
|
|
118
|
+
}
|
|
119
|
+
static open(path = ":memory:") {
|
|
120
|
+
return new SqliteAgentStore(new DatabaseSync(path));
|
|
121
|
+
}
|
|
122
|
+
create(record) {
|
|
123
|
+
const a = record.row;
|
|
124
|
+
return withSqliteTransaction(this.db, () => {
|
|
125
|
+
this.insertStmt.run(a.id, a.workspace_id, a.type, a.name, JSON.stringify(a.model), a.system, a.description, JSON.stringify(a.tools), JSON.stringify(a.skills), JSON.stringify(a.mcp_servers), JSON.stringify(a.metadata), a.multiagent === null ? null : JSON.stringify(a.multiagent), a.version, a.created_at, a.updated_at, a.archived_at);
|
|
126
|
+
this.insertVersion(a);
|
|
127
|
+
return a;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
update(record) {
|
|
131
|
+
const next = record.row;
|
|
132
|
+
return withSqliteTransaction(this.db, () => {
|
|
133
|
+
const current = this.retrieveAny(next.workspace_id, next.id);
|
|
134
|
+
if (!current)
|
|
135
|
+
throw new AgentUpdateMissingError();
|
|
136
|
+
if (current.archived_at !== null)
|
|
137
|
+
throw new AgentUpdateArchivedError();
|
|
138
|
+
if (current.version !== record.expectedVersion) {
|
|
139
|
+
throw new AgentUpdateConflictError();
|
|
140
|
+
}
|
|
141
|
+
if (next.version !== current.version + 1) {
|
|
142
|
+
throw new Error("Agent update must allocate exactly one version");
|
|
143
|
+
}
|
|
144
|
+
this.insertVersion(next);
|
|
145
|
+
const result = this.updateHeadStmt.run(next.name, JSON.stringify(next.model), next.system, next.description, JSON.stringify(next.tools), JSON.stringify(next.skills), JSON.stringify(next.mcp_servers), JSON.stringify(next.metadata), next.multiagent === null ? null : JSON.stringify(next.multiagent), next.version, next.updated_at, next.workspace_id, next.id, record.expectedVersion);
|
|
146
|
+
if (result.changes !== 1)
|
|
147
|
+
throw new AgentUpdateConflictError();
|
|
148
|
+
return next;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
retrieve(workspaceId, agentId) {
|
|
152
|
+
const row = this.retrieveActiveStmt.get(workspaceId, agentId);
|
|
153
|
+
return row ? deserialize(row) : undefined;
|
|
154
|
+
}
|
|
155
|
+
retrieveAny(workspaceId, agentId) {
|
|
156
|
+
const row = this.retrieveAnyStmt.get(workspaceId, agentId);
|
|
157
|
+
return row ? deserialize(row) : undefined;
|
|
158
|
+
}
|
|
159
|
+
retrieveVersion(workspaceId, agentId, version) {
|
|
160
|
+
const owner = this.retrieveAny(workspaceId, agentId);
|
|
161
|
+
if (!owner)
|
|
162
|
+
return undefined;
|
|
163
|
+
const row = this.retrieveVersionStmt.get(workspaceId, agentId, version);
|
|
164
|
+
return row ? deserializeVersion(row, owner.archived_at) : undefined;
|
|
165
|
+
}
|
|
166
|
+
listVersions(workspaceId, agentId, opts = {}) {
|
|
167
|
+
const owner = this.retrieveAny(workspaceId, agentId);
|
|
168
|
+
if (!owner)
|
|
169
|
+
throw new AgentUpdateMissingError();
|
|
170
|
+
const limit = normalizeLimit(opts.limit);
|
|
171
|
+
const anchor = opts.page === undefined
|
|
172
|
+
? undefined
|
|
173
|
+
: decodeVersionCursor(opts.page, workspaceId, agentId, this.cursorSigningKey);
|
|
174
|
+
const rows = (anchor === undefined
|
|
175
|
+
? this.listVersionsFirstStmt.all(workspaceId, agentId, limit + 1)
|
|
176
|
+
: this.listVersionsAfterStmt.all(workspaceId, agentId, anchor, limit + 1));
|
|
177
|
+
const selected = rows.slice(0, limit);
|
|
178
|
+
const data = selected.map((row) => deserializeVersion(row, owner.archived_at));
|
|
179
|
+
return {
|
|
180
|
+
data,
|
|
181
|
+
next_page: rows.length > limit && data.length > 0
|
|
182
|
+
? encodeVersionCursor(data[data.length - 1].version, workspaceId, agentId, this.cursorSigningKey)
|
|
183
|
+
: null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
archive(workspaceId, agentId, archivedAt) {
|
|
187
|
+
this.archiveStmt.run(archivedAt, archivedAt, workspaceId, agentId);
|
|
188
|
+
return this.retrieveAny(workspaceId, agentId);
|
|
189
|
+
}
|
|
190
|
+
list(workspaceId, opts = {}) {
|
|
191
|
+
if (opts.page === "") {
|
|
192
|
+
return { data: [], has_more: false, next_page: null };
|
|
193
|
+
}
|
|
194
|
+
const limit = normalizeLimit(opts.limit);
|
|
195
|
+
const queryLimit = limit + 1;
|
|
196
|
+
const includeArchived = opts.includeArchived ?? false;
|
|
197
|
+
const rows = this.listStmt(includeArchived, opts.page !== undefined).all(...selectListArgs(workspaceId, queryLimit, opts.page));
|
|
198
|
+
const pageRows = rows.slice(0, limit);
|
|
199
|
+
const data = pageRows.map(deserialize);
|
|
200
|
+
return {
|
|
201
|
+
data,
|
|
202
|
+
has_more: rows.length > limit,
|
|
203
|
+
next_page: rows.length > limit ? data[data.length - 1]?.id ?? null : null,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
close() {
|
|
207
|
+
this.db.close();
|
|
208
|
+
}
|
|
209
|
+
insertVersion(row) {
|
|
210
|
+
this.insertVersionStmt.run(row.workspace_id, row.id, row.version, row.name, JSON.stringify(row.model), row.system, row.description, JSON.stringify(row.tools), JSON.stringify(row.skills), JSON.stringify(row.mcp_servers), JSON.stringify(row.metadata), row.multiagent === null ? null : JSON.stringify(row.multiagent), row.created_at, row.updated_at);
|
|
211
|
+
}
|
|
212
|
+
listStmt(includeArchived, hasPage) {
|
|
213
|
+
if (includeArchived) {
|
|
214
|
+
return hasPage ? this.listAllSinceStmt : this.listAllStmt;
|
|
215
|
+
}
|
|
216
|
+
return hasPage ? this.listActiveSinceStmt : this.listActiveStmt;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
export class AgentUpdateMissingError extends Error {
|
|
220
|
+
}
|
|
221
|
+
export class AgentUpdateArchivedError extends Error {
|
|
222
|
+
}
|
|
223
|
+
export class AgentUpdateConflictError extends Error {
|
|
224
|
+
}
|
|
225
|
+
function encodeVersionCursor(anchor, workspaceId, agentId, signingKey) {
|
|
226
|
+
const payload = Buffer.from(JSON.stringify({ v: 1, anchor, agentId }), "utf8")
|
|
227
|
+
.toString("base64url");
|
|
228
|
+
const signature = signVersionCursor(payload, workspaceId, signingKey);
|
|
229
|
+
return `${payload}.${signature.toString("base64url")}`;
|
|
230
|
+
}
|
|
231
|
+
function decodeVersionCursor(cursor, workspaceId, agentId, signingKey) {
|
|
232
|
+
try {
|
|
233
|
+
const parts = cursor.split(".");
|
|
234
|
+
if (parts.length !== 2)
|
|
235
|
+
throw new Error("invalid cursor");
|
|
236
|
+
const payloadBytes = decodeCanonicalBase64url(parts[0]);
|
|
237
|
+
const signature = decodeCanonicalBase64url(parts[1]);
|
|
238
|
+
const expected = signVersionCursor(parts[0], workspaceId, signingKey);
|
|
239
|
+
if (signature.length !== expected.length || !timingSafeEqual(signature, expected)) {
|
|
240
|
+
throw new Error("invalid signature");
|
|
241
|
+
}
|
|
242
|
+
const parsed = JSON.parse(payloadBytes.toString("utf8"));
|
|
243
|
+
if (parsed.v !== 1 ||
|
|
244
|
+
!Number.isSafeInteger(parsed.anchor) ||
|
|
245
|
+
(parsed.anchor ?? 0) <= 0 ||
|
|
246
|
+
parsed.agentId !== agentId)
|
|
247
|
+
throw new Error("invalid payload");
|
|
248
|
+
return parsed.anchor;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
throw new Error("invalid page cursor");
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function signVersionCursor(payload, workspaceId, key) {
|
|
255
|
+
return createHmac("sha256", key)
|
|
256
|
+
.update("oma-agent-versions-page-v1\0", "utf8")
|
|
257
|
+
.update(workspaceId, "utf8")
|
|
258
|
+
.update("\0", "utf8")
|
|
259
|
+
.update(payload, "utf8")
|
|
260
|
+
.digest();
|
|
261
|
+
}
|
|
262
|
+
function decodeCanonicalBase64url(value) {
|
|
263
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value))
|
|
264
|
+
throw new Error("invalid base64url");
|
|
265
|
+
const decoded = Buffer.from(value, "base64url");
|
|
266
|
+
if (decoded.toString("base64url") !== value)
|
|
267
|
+
throw new Error("invalid base64url");
|
|
268
|
+
return decoded;
|
|
269
|
+
}
|
|
270
|
+
function selectListArgs(workspaceId, limit, page) {
|
|
271
|
+
return page === undefined ? [workspaceId, limit] : [workspaceId, page, limit];
|
|
272
|
+
}
|
|
273
|
+
function normalizeLimit(limit) {
|
|
274
|
+
if (limit === undefined)
|
|
275
|
+
return 20;
|
|
276
|
+
if (!Number.isSafeInteger(limit) || limit <= 0)
|
|
277
|
+
return 20;
|
|
278
|
+
return Math.min(limit, 100);
|
|
279
|
+
}
|
|
280
|
+
function migratePersistedModels(db) {
|
|
281
|
+
const updateHead = db.prepare("UPDATE agents SET model = ? WHERE workspace_id = ? AND id = ?");
|
|
282
|
+
const updateVersion = db.prepare(`UPDATE agent_versions SET model = ?
|
|
283
|
+
WHERE workspace_id = ? AND agent_id = ? AND version = ?`);
|
|
284
|
+
const heads = db.prepare("SELECT workspace_id, id, version, model FROM agents").all();
|
|
285
|
+
for (const row of heads) {
|
|
286
|
+
const parsed = parsePersistedModel(row.model, `agent ${row.id}`);
|
|
287
|
+
if (parsed.legacy) {
|
|
288
|
+
updateHead.run(JSON.stringify(parsed.model), row.workspace_id, row.id);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const versions = db.prepare(`SELECT workspace_id, agent_id AS id, version, model
|
|
292
|
+
FROM agent_versions`).all();
|
|
293
|
+
for (const row of versions) {
|
|
294
|
+
const parsed = parsePersistedModel(row.model, `agent version ${row.id}@${row.version}`);
|
|
295
|
+
if (parsed.legacy) {
|
|
296
|
+
updateVersion.run(JSON.stringify(parsed.model), row.workspace_id, row.id, row.version);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function assertAllPersistedModelsNormalized(db) {
|
|
301
|
+
const heads = db.prepare("SELECT workspace_id, id, version, model FROM agents").all();
|
|
302
|
+
const versions = db.prepare(`SELECT workspace_id, agent_id AS id, version, model FROM agent_versions`).all();
|
|
303
|
+
const rows = [...heads, ...versions];
|
|
304
|
+
for (const row of rows) {
|
|
305
|
+
const parsed = parsePersistedModel(row.model, `agent revision ${row.id}@${row.version}`);
|
|
306
|
+
if (parsed.legacy) {
|
|
307
|
+
throw new Error(`Agent model migration left a legacy row: ${row.id}@${row.version}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function parsePersistedModel(raw, location) {
|
|
312
|
+
let value;
|
|
313
|
+
try {
|
|
314
|
+
value = JSON.parse(raw);
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
throw new Error(`Invalid persisted model JSON for ${location}`);
|
|
318
|
+
}
|
|
319
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
320
|
+
throw new Error(`Invalid persisted model for ${location}`);
|
|
321
|
+
}
|
|
322
|
+
const model = value;
|
|
323
|
+
const legacy = model.provider === undefined;
|
|
324
|
+
const allowedKeys = legacy
|
|
325
|
+
? new Set(["id", "speed"])
|
|
326
|
+
: new Set(["provider", "id", "speed"]);
|
|
327
|
+
if (Object.keys(model).some((key) => !allowedKeys.has(key))) {
|
|
328
|
+
throw new Error(`Invalid persisted model fields for ${location}`);
|
|
329
|
+
}
|
|
330
|
+
if (typeof model.id !== "string" || model.id.length === 0) {
|
|
331
|
+
throw new Error(`Invalid persisted model id for ${location}`);
|
|
332
|
+
}
|
|
333
|
+
if (model.speed !== "standard" && model.speed !== "fast") {
|
|
334
|
+
throw new Error(`Invalid persisted model speed for ${location}`);
|
|
335
|
+
}
|
|
336
|
+
if (!legacy && (typeof model.provider !== "string" || model.provider.length === 0)) {
|
|
337
|
+
throw new Error(`Invalid persisted model provider for ${location}`);
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
model: {
|
|
341
|
+
provider: legacy ? "anthropic" : model.provider,
|
|
342
|
+
id: model.id,
|
|
343
|
+
speed: model.speed,
|
|
344
|
+
},
|
|
345
|
+
legacy,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function deserialize(row) {
|
|
349
|
+
return {
|
|
350
|
+
id: row.id,
|
|
351
|
+
workspace_id: row.workspace_id,
|
|
352
|
+
type: "agent",
|
|
353
|
+
name: row.name,
|
|
354
|
+
model: JSON.parse(row.model),
|
|
355
|
+
system: row.system,
|
|
356
|
+
description: row.description,
|
|
357
|
+
tools: JSON.parse(row.tools),
|
|
358
|
+
skills: JSON.parse(row.skills),
|
|
359
|
+
mcp_servers: JSON.parse(row.mcp_servers),
|
|
360
|
+
metadata: JSON.parse(row.metadata),
|
|
361
|
+
multiagent: row.multiagent === null
|
|
362
|
+
? null
|
|
363
|
+
: JSON.parse(row.multiagent),
|
|
364
|
+
version: row.version,
|
|
365
|
+
created_at: row.created_at,
|
|
366
|
+
updated_at: row.updated_at,
|
|
367
|
+
archived_at: row.archived_at,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function deserializeVersion(row, archivedAt) {
|
|
371
|
+
return {
|
|
372
|
+
id: row.agent_id,
|
|
373
|
+
workspace_id: row.workspace_id,
|
|
374
|
+
type: "agent",
|
|
375
|
+
name: row.name,
|
|
376
|
+
model: JSON.parse(row.model),
|
|
377
|
+
system: row.system,
|
|
378
|
+
description: row.description,
|
|
379
|
+
tools: JSON.parse(row.tools),
|
|
380
|
+
skills: JSON.parse(row.skills),
|
|
381
|
+
mcp_servers: JSON.parse(row.mcp_servers),
|
|
382
|
+
metadata: JSON.parse(row.metadata),
|
|
383
|
+
multiagent: row.multiagent === null
|
|
384
|
+
? null
|
|
385
|
+
: JSON.parse(row.multiagent),
|
|
386
|
+
version: row.version,
|
|
387
|
+
created_at: row.created_at,
|
|
388
|
+
updated_at: row.updated_at,
|
|
389
|
+
archived_at: archivedAt,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DEFAULT_WORKSPACE_ID } from "../workspace.js";
|