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.
Files changed (197) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +248 -0
  3. package/bin/oma.mjs +499 -0
  4. package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
  5. package/dist/scripts/alpha-smoke-models.mjs +92 -0
  6. package/dist/scripts/alpha-smoke.mjs +581 -0
  7. package/dist/scripts/oma-doctor.js +230 -0
  8. package/dist/scripts/oma-models.js +299 -0
  9. package/dist/scripts/oma-workspaces.js +133 -0
  10. package/dist/src/control-plane/admin/auth.js +43 -0
  11. package/dist/src/control-plane/admin/routes.js +76 -0
  12. package/dist/src/control-plane/admin/service.js +102 -0
  13. package/dist/src/control-plane/admission.js +90 -0
  14. package/dist/src/control-plane/agents/routes.js +63 -0
  15. package/dist/src/control-plane/agents/service.js +687 -0
  16. package/dist/src/control-plane/agents/store.js +391 -0
  17. package/dist/src/control-plane/agents/types.js +1 -0
  18. package/dist/src/control-plane/api-constants.js +3 -0
  19. package/dist/src/control-plane/app.js +907 -0
  20. package/dist/src/control-plane/console/auth.js +68 -0
  21. package/dist/src/control-plane/console/static.js +112 -0
  22. package/dist/src/control-plane/deployment-runtime-config.js +328 -0
  23. package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
  24. package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
  25. package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
  26. package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
  27. package/dist/src/control-plane/deployment-storage.js +354 -0
  28. package/dist/src/control-plane/egress/image.js +48 -0
  29. package/dist/src/control-plane/egress/policy.js +571 -0
  30. package/dist/src/control-plane/egress/presets.js +77 -0
  31. package/dist/src/control-plane/egress/proxy.js +72 -0
  32. package/dist/src/control-plane/egress/ssrf.js +106 -0
  33. package/dist/src/control-plane/egress/utils/debug.js +14 -0
  34. package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
  35. package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
  36. package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
  37. package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
  38. package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
  39. package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
  40. package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
  41. package/dist/src/control-plane/environments/routes.js +54 -0
  42. package/dist/src/control-plane/environments/service.js +112 -0
  43. package/dist/src/control-plane/environments/store.js +132 -0
  44. package/dist/src/control-plane/environments/types.js +1 -0
  45. package/dist/src/control-plane/errors.js +81 -0
  46. package/dist/src/control-plane/events/broadcaster.js +217 -0
  47. package/dist/src/control-plane/events/constants.js +2 -0
  48. package/dist/src/control-plane/events/persist.js +39 -0
  49. package/dist/src/control-plane/events/request.js +190 -0
  50. package/dist/src/control-plane/events/routes.js +159 -0
  51. package/dist/src/control-plane/events/runtime-helpers.js +134 -0
  52. package/dist/src/control-plane/events/service.js +1985 -0
  53. package/dist/src/control-plane/events/session-guards.js +29 -0
  54. package/dist/src/control-plane/events/sse.js +6 -0
  55. package/dist/src/control-plane/events/store.js +772 -0
  56. package/dist/src/control-plane/events/tool-persistence.js +102 -0
  57. package/dist/src/control-plane/events/types.js +21 -0
  58. package/dist/src/control-plane/files/routes.js +121 -0
  59. package/dist/src/control-plane/files/service.js +65 -0
  60. package/dist/src/control-plane/files/store-common.js +87 -0
  61. package/dist/src/control-plane/files/store-local.js +488 -0
  62. package/dist/src/control-plane/files/store-memory.js +270 -0
  63. package/dist/src/control-plane/files/store.js +2 -0
  64. package/dist/src/control-plane/files/types.js +7 -0
  65. package/dist/src/control-plane/http.js +25 -0
  66. package/dist/src/control-plane/ids.js +43 -0
  67. package/dist/src/control-plane/logging.js +263 -0
  68. package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
  69. package/dist/src/control-plane/models/catalog.js +78 -0
  70. package/dist/src/control-plane/models/config-security.js +376 -0
  71. package/dist/src/control-plane/models/deployment-config.js +93 -0
  72. package/dist/src/control-plane/models/routes.js +32 -0
  73. package/dist/src/control-plane/models/service.js +138 -0
  74. package/dist/src/control-plane/observability/instruments.js +65 -0
  75. package/dist/src/control-plane/observability/metrics.js +125 -0
  76. package/dist/src/control-plane/observability/routes.js +95 -0
  77. package/dist/src/control-plane/openapi/document.js +282 -0
  78. package/dist/src/control-plane/openapi/routes.js +78 -0
  79. package/dist/src/control-plane/request-idempotency.js +85 -0
  80. package/dist/src/control-plane/secrets/envelope.js +132 -0
  81. package/dist/src/control-plane/secrets/master-key.js +44 -0
  82. package/dist/src/control-plane/secrets/routes.js +20 -0
  83. package/dist/src/control-plane/secrets/service.js +71 -0
  84. package/dist/src/control-plane/secrets/store.js +212 -0
  85. package/dist/src/control-plane/secrets/types.js +3 -0
  86. package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
  87. package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
  88. package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
  89. package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
  90. package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
  91. package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
  92. package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
  93. package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
  94. package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
  95. package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
  96. package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
  97. package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
  98. package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
  99. package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
  100. package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
  101. package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
  102. package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
  103. package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
  104. package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
  105. package/dist/src/control-plane/sessions/pi/translator.js +100 -0
  106. package/dist/src/control-plane/sessions/request.js +164 -0
  107. package/dist/src/control-plane/sessions/resources.js +63 -0
  108. package/dist/src/control-plane/sessions/routes.js +88 -0
  109. package/dist/src/control-plane/sessions/serialize.js +24 -0
  110. package/dist/src/control-plane/sessions/service.js +733 -0
  111. package/dist/src/control-plane/sessions/store.js +538 -0
  112. package/dist/src/control-plane/sessions/types.js +1 -0
  113. package/dist/src/control-plane/skills/archive.js +169 -0
  114. package/dist/src/control-plane/skills/routes.js +41 -0
  115. package/dist/src/control-plane/skills/service.js +29 -0
  116. package/dist/src/control-plane/skills/store.js +233 -0
  117. package/dist/src/control-plane/skills/types.js +5 -0
  118. package/dist/src/control-plane/sqlite-transaction.js +15 -0
  119. package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
  120. package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
  121. package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
  122. package/dist/src/control-plane/vaults/routes.js +66 -0
  123. package/dist/src/control-plane/vaults/service.js +553 -0
  124. package/dist/src/control-plane/vaults/store.js +691 -0
  125. package/dist/src/control-plane/vaults/types.js +1 -0
  126. package/dist/src/control-plane/wake-loop.js +65 -0
  127. package/dist/src/control-plane/wiring.js +92 -0
  128. package/dist/src/control-plane/workspace.js +4 -0
  129. package/dist/src/control-plane/workspaces/store.js +158 -0
  130. package/dist/src/egress-proxy-main.js +103 -0
  131. package/dist/src/main.js +124 -0
  132. package/dist/src/types/agents.js +1 -0
  133. package/dist/src/types/common.js +1 -0
  134. package/dist/src/types/environments.js +1 -0
  135. package/dist/src/types/events.js +121 -0
  136. package/dist/src/types/files.js +1 -0
  137. package/dist/src/types/json.js +19 -0
  138. package/dist/src/types/sessions.js +1 -0
  139. package/dist/ui/managed-agents-console/README.md +52 -0
  140. package/dist/ui/managed-agents-console/docs/agents.md +23 -0
  141. package/dist/ui/managed-agents-console/docs/console.md +23 -0
  142. package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
  143. package/dist/ui/managed-agents-console/docs/environments.md +27 -0
  144. package/dist/ui/managed-agents-console/docs/events.md +19 -0
  145. package/dist/ui/managed-agents-console/docs/files.md +17 -0
  146. package/dist/ui/managed-agents-console/docs/github.md +11 -0
  147. package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
  148. package/dist/ui/managed-agents-console/docs/memory.md +11 -0
  149. package/dist/ui/managed-agents-console/docs/migration.md +17 -0
  150. package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
  151. package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
  152. package/dist/ui/managed-agents-console/docs/overview.md +41 -0
  153. package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
  154. package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
  155. package/dist/ui/managed-agents-console/docs/reference.md +27 -0
  156. package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
  157. package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
  158. package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
  159. package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
  160. package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
  161. package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
  162. package/dist/ui/managed-agents-console/docs/skills.md +15 -0
  163. package/dist/ui/managed-agents-console/docs/tools.md +23 -0
  164. package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
  165. package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
  166. package/dist/ui/managed-agents-console/index.html +47 -0
  167. package/dist/ui/managed-agents-console/serve.mjs +116 -0
  168. package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
  169. package/dist/ui/managed-agents-console/src/api.js +1043 -0
  170. package/dist/ui/managed-agents-console/src/app.jsx +633 -0
  171. package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
  172. package/dist/ui/managed-agents-console/src/console.css +733 -0
  173. package/dist/ui/managed-agents-console/src/data.js +188 -0
  174. package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
  175. package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
  176. package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
  177. package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
  178. package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
  179. package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
  180. package/dist/ui/managed-agents-console/src/sse.js +185 -0
  181. package/dist/ui/managed-agents-console/src/states.jsx +90 -0
  182. package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
  183. package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
  184. package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
  185. package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
  186. package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
  187. package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
  188. package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
  189. package/dist/ui/openapi-docs/VENDOR.md +27 -0
  190. package/dist/ui/openapi-docs/index.html +16 -0
  191. package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
  192. package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
  193. package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
  194. package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
  195. package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
  196. package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
  197. package/package.json +53 -0
@@ -0,0 +1,538 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { DatabaseSync } from "node:sqlite";
3
+ import { invalidRequest } from "../errors.js";
4
+ import { withSqliteTransaction } from "../sqlite-transaction.js";
5
+ const SCHEMA = `
6
+ CREATE TABLE IF NOT EXISTS sessions (
7
+ id TEXT PRIMARY KEY,
8
+ workspace_id TEXT NOT NULL,
9
+ type TEXT NOT NULL,
10
+ agent_id TEXT NOT NULL,
11
+ agent_version INTEGER NOT NULL,
12
+ environment_id TEXT NOT NULL,
13
+ vault_ids TEXT NOT NULL DEFAULT '[]',
14
+ status TEXT NOT NULL,
15
+ title TEXT,
16
+ metadata TEXT NOT NULL,
17
+ created_at TEXT NOT NULL,
18
+ updated_at TEXT NOT NULL,
19
+ archived_at TEXT,
20
+ usage TEXT
21
+ );
22
+ CREATE INDEX IF NOT EXISTS sessions_by_workspace ON sessions (workspace_id, id);
23
+ CREATE INDEX IF NOT EXISTS sessions_by_workspace_agent ON sessions (workspace_id, agent_id, id);
24
+ -- Partial index so the /metrics active-sessions gauge scans O(active), not
25
+ -- O(history) (plan 0121 §3.2).
26
+ CREATE INDEX IF NOT EXISTS idx_sessions_live ON sessions (archived_at) WHERE archived_at IS NULL;
27
+
28
+ CREATE TABLE IF NOT EXISTS session_resources (
29
+ id TEXT PRIMARY KEY,
30
+ workspace_id TEXT NOT NULL,
31
+ session_id TEXT NOT NULL,
32
+ type TEXT NOT NULL,
33
+ file_id TEXT NOT NULL,
34
+ mount_path TEXT NOT NULL,
35
+ position INTEGER NOT NULL,
36
+ created_at TEXT NOT NULL,
37
+ updated_at TEXT NOT NULL
38
+ );
39
+ CREATE INDEX IF NOT EXISTS session_resources_by_session
40
+ ON session_resources (workspace_id, session_id, id);
41
+
42
+ CREATE TABLE IF NOT EXISTS session_file_mount_snapshots (
43
+ workspace_id TEXT NOT NULL,
44
+ session_id TEXT NOT NULL,
45
+ resource_id TEXT NOT NULL,
46
+ file_id TEXT NOT NULL,
47
+ mount_path TEXT NOT NULL,
48
+ snapshot_file_id TEXT NOT NULL,
49
+ sha256 TEXT NOT NULL,
50
+ size_bytes INTEGER NOT NULL,
51
+ kind TEXT NOT NULL DEFAULT 'upload',
52
+ skill_snapshot_id TEXT,
53
+ PRIMARY KEY (workspace_id, session_id, resource_id)
54
+ );
55
+ CREATE TABLE IF NOT EXISTS session_skill_snapshots (
56
+ workspace_id TEXT NOT NULL,
57
+ session_id TEXT NOT NULL,
58
+ skill_snapshot_id TEXT NOT NULL,
59
+ skill_id TEXT NOT NULL,
60
+ version TEXT NOT NULL,
61
+ name TEXT NOT NULL,
62
+ description TEXT NOT NULL,
63
+ PRIMARY KEY (workspace_id, session_id, skill_snapshot_id)
64
+ );
65
+ CREATE INDEX IF NOT EXISTS session_file_mount_snapshots_by_session
66
+ ON session_file_mount_snapshots (workspace_id, session_id);
67
+
68
+ CREATE TABLE IF NOT EXISTS pending_internal_snapshot_deletes (
69
+ workspace_id TEXT NOT NULL,
70
+ session_id TEXT NOT NULL,
71
+ resource_id TEXT NOT NULL,
72
+ file_id TEXT NOT NULL,
73
+ mount_path TEXT NOT NULL,
74
+ snapshot_file_id TEXT NOT NULL,
75
+ sha256 TEXT NOT NULL,
76
+ size_bytes INTEGER NOT NULL,
77
+ kind TEXT NOT NULL DEFAULT 'upload',
78
+ skill_snapshot_id TEXT,
79
+ created_at TEXT NOT NULL,
80
+ last_attempt_at TEXT,
81
+ attempt_count INTEGER NOT NULL DEFAULT 0,
82
+ last_error TEXT,
83
+ PRIMARY KEY (workspace_id, session_id, resource_id)
84
+ );
85
+
86
+ CREATE TABLE IF NOT EXISTS pending_internal_snapshot_create_rollbacks (
87
+ workspace_id TEXT NOT NULL,
88
+ session_id TEXT NOT NULL,
89
+ resource_id TEXT NOT NULL,
90
+ file_id TEXT NOT NULL,
91
+ mount_path TEXT NOT NULL,
92
+ snapshot_file_id TEXT NOT NULL,
93
+ sha256 TEXT NOT NULL,
94
+ size_bytes INTEGER NOT NULL,
95
+ kind TEXT NOT NULL DEFAULT 'upload',
96
+ skill_snapshot_id TEXT,
97
+ created_at TEXT NOT NULL,
98
+ last_attempt_at TEXT,
99
+ attempt_count INTEGER NOT NULL DEFAULT 0,
100
+ last_error TEXT,
101
+ PRIMARY KEY (workspace_id, session_id, resource_id)
102
+ );
103
+ `;
104
+ export class SqliteSessionStore {
105
+ db;
106
+ cursorSigningKey = randomBytes(32);
107
+ insertStmt;
108
+ insertResourceStmt;
109
+ insertSnapshotStmt;
110
+ insertSkillSnapshotStmt;
111
+ retrieveActiveStmt;
112
+ countActiveStmt;
113
+ hasEnvironmentReferenceStmt;
114
+ countAllActiveStmt;
115
+ retrieveAnyStmt;
116
+ archiveStmt;
117
+ insertPendingSnapshotDeletesStmt;
118
+ deleteStmt;
119
+ deleteResourcesStmt;
120
+ deleteSnapshotsStmt;
121
+ resourcesBySessionStmt;
122
+ snapshotsBySessionStmt;
123
+ skillSnapshotsBySessionStmt;
124
+ pendingSnapshotDeleteWorkspacesStmt;
125
+ pendingSnapshotDeletesByWorkspaceStmt;
126
+ pendingSnapshotDeletesBySessionStmt;
127
+ recordPendingSnapshotDeleteAttemptStmt;
128
+ clearPendingSnapshotDeleteStmt;
129
+ insertPendingSnapshotCreateRollbackStmt;
130
+ clearPendingSnapshotCreateRollbacksBySessionStmt;
131
+ pendingSnapshotCreateRollbackWorkspacesStmt;
132
+ pendingSnapshotCreateRollbacksByWorkspaceStmt;
133
+ pendingSnapshotCreateRollbacksBySessionStmt;
134
+ recordPendingSnapshotCreateRollbackAttemptStmt;
135
+ clearPendingSnapshotCreateRollbackStmt;
136
+ listStmts = new Map();
137
+ constructor(db) {
138
+ this.db = db;
139
+ this.db.exec(SCHEMA);
140
+ ensureVaultIdsColumn(this.db);
141
+ ensureSnapshotColumns(this.db);
142
+ this.insertStmt = this.db.prepare(`INSERT INTO sessions (
143
+ id, workspace_id, type, agent_id, agent_version, environment_id,
144
+ vault_ids, status, title, metadata, created_at, updated_at, archived_at, usage
145
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
146
+ this.insertResourceStmt = this.db.prepare(`INSERT INTO session_resources (
147
+ id, workspace_id, session_id, type, file_id, mount_path, position, created_at, updated_at
148
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
149
+ this.insertSnapshotStmt = this.db.prepare(`INSERT INTO session_file_mount_snapshots (
150
+ workspace_id, session_id, resource_id, file_id, mount_path,
151
+ snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id
152
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
153
+ this.insertSkillSnapshotStmt = this.db.prepare(`INSERT INTO session_skill_snapshots
154
+ (workspace_id,session_id,skill_snapshot_id,skill_id,version,name,description)
155
+ VALUES (?,?,?,?,?,?,?)`);
156
+ this.retrieveActiveStmt = this.db.prepare(`SELECT * FROM sessions
157
+ WHERE workspace_id = ? AND id = ? AND archived_at IS NULL`);
158
+ this.countActiveStmt = this.db.prepare(`SELECT COUNT(*) AS n FROM sessions
159
+ WHERE workspace_id = ? AND archived_at IS NULL`);
160
+ this.hasEnvironmentReferenceStmt = this.db.prepare(`SELECT EXISTS(
161
+ SELECT 1 FROM sessions WHERE workspace_id = ? AND environment_id = ?
162
+ ) AS exists_value`);
163
+ this.countAllActiveStmt = this.db.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE archived_at IS NULL`);
164
+ this.retrieveAnyStmt = this.db.prepare(`SELECT * FROM sessions
165
+ WHERE workspace_id = ? AND id = ?`);
166
+ this.archiveStmt = this.db.prepare(`UPDATE sessions
167
+ SET status = 'terminated', updated_at = ?, archived_at = COALESCE(archived_at, ?)
168
+ WHERE workspace_id = ? AND id = ?`);
169
+ this.insertPendingSnapshotDeletesStmt = this.db.prepare(`INSERT OR IGNORE INTO pending_internal_snapshot_deletes (
170
+ workspace_id, session_id, resource_id, file_id, mount_path,
171
+ snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id, created_at
172
+ )
173
+ SELECT
174
+ workspace_id, session_id, resource_id, file_id, mount_path,
175
+ snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id, ?
176
+ FROM session_file_mount_snapshots
177
+ WHERE workspace_id = ? AND session_id = ?`);
178
+ this.deleteStmt = this.db.prepare(`DELETE FROM sessions
179
+ WHERE workspace_id = ? AND id = ?`);
180
+ this.deleteResourcesStmt = this.db.prepare(`DELETE FROM session_resources
181
+ WHERE workspace_id = ? AND session_id = ?`);
182
+ this.deleteSnapshotsStmt = this.db.prepare(`DELETE FROM session_file_mount_snapshots
183
+ WHERE workspace_id = ? AND session_id = ?`);
184
+ this.resourcesBySessionStmt = this.db.prepare(`SELECT * FROM session_resources
185
+ WHERE workspace_id = ? AND session_id = ?
186
+ ORDER BY position ASC, id ASC`);
187
+ this.snapshotsBySessionStmt = this.db.prepare(`SELECT * FROM session_file_mount_snapshots
188
+ WHERE workspace_id = ? AND session_id = ?
189
+ ORDER BY resource_id ASC`);
190
+ this.skillSnapshotsBySessionStmt = this.db.prepare(`SELECT * FROM session_skill_snapshots WHERE workspace_id=? AND session_id=? ORDER BY skill_snapshot_id`);
191
+ this.pendingSnapshotDeleteWorkspacesStmt = this.db.prepare(`SELECT DISTINCT workspace_id
192
+ FROM pending_internal_snapshot_deletes
193
+ ORDER BY workspace_id ASC`);
194
+ this.pendingSnapshotDeletesByWorkspaceStmt = this.db.prepare(`SELECT *
195
+ FROM pending_internal_snapshot_deletes
196
+ WHERE workspace_id = ?
197
+ ORDER BY session_id ASC, resource_id ASC`);
198
+ this.pendingSnapshotDeletesBySessionStmt = this.db.prepare(`SELECT *
199
+ FROM pending_internal_snapshot_deletes
200
+ WHERE workspace_id = ? AND session_id = ?
201
+ ORDER BY resource_id ASC`);
202
+ this.recordPendingSnapshotDeleteAttemptStmt = this.db.prepare(`UPDATE pending_internal_snapshot_deletes
203
+ SET last_attempt_at = ?, attempt_count = attempt_count + 1, last_error = ?
204
+ WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
205
+ this.clearPendingSnapshotDeleteStmt = this.db.prepare(`DELETE FROM pending_internal_snapshot_deletes
206
+ WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
207
+ this.insertPendingSnapshotCreateRollbackStmt = this.db.prepare(`INSERT INTO pending_internal_snapshot_create_rollbacks (
208
+ workspace_id, session_id, resource_id, file_id, mount_path,
209
+ snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id, created_at
210
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
211
+ this.clearPendingSnapshotCreateRollbacksBySessionStmt = this.db.prepare(`DELETE FROM pending_internal_snapshot_create_rollbacks
212
+ WHERE workspace_id = ? AND session_id = ?`);
213
+ this.pendingSnapshotCreateRollbackWorkspacesStmt = this.db.prepare(`SELECT DISTINCT workspace_id
214
+ FROM pending_internal_snapshot_create_rollbacks
215
+ ORDER BY workspace_id ASC`);
216
+ this.pendingSnapshotCreateRollbacksByWorkspaceStmt = this.db.prepare(`SELECT *
217
+ FROM pending_internal_snapshot_create_rollbacks
218
+ WHERE workspace_id = ?
219
+ ORDER BY session_id ASC, resource_id ASC`);
220
+ this.pendingSnapshotCreateRollbacksBySessionStmt = this.db.prepare(`SELECT *
221
+ FROM pending_internal_snapshot_create_rollbacks
222
+ WHERE workspace_id = ? AND session_id = ?
223
+ ORDER BY resource_id ASC`);
224
+ this.recordPendingSnapshotCreateRollbackAttemptStmt = this.db.prepare(`UPDATE pending_internal_snapshot_create_rollbacks
225
+ SET last_attempt_at = ?, attempt_count = attempt_count + 1, last_error = ?
226
+ WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
227
+ this.clearPendingSnapshotCreateRollbackStmt = this.db.prepare(`DELETE FROM pending_internal_snapshot_create_rollbacks
228
+ WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
229
+ }
230
+ static open(path = ":memory:") {
231
+ return new SqliteSessionStore(new DatabaseSync(path));
232
+ }
233
+ create(record) {
234
+ return this.createInTransaction(record);
235
+ }
236
+ createAndCompleteIdempotency(record, idempotency) {
237
+ return this.createInTransaction(record, idempotency);
238
+ }
239
+ createInTransaction(record, idempotency) {
240
+ const s = record.row;
241
+ return this.withTransaction(() => {
242
+ this.insertStmt.run(s.id, s.workspace_id, s.type, s.agent.id, s.agent.version, s.environment_id, JSON.stringify(s.vault_ids ?? []), s.status, s.title, JSON.stringify(s.metadata), s.created_at, s.updated_at, s.archived_at, s.usage);
243
+ for (const [position, resource] of s.resources.entries()) {
244
+ this.insertResourceStmt.run(resource.id, s.workspace_id, s.id, resource.type, resource.file_id, resource.mount_path, position, resource.created_at, resource.updated_at);
245
+ }
246
+ for (const snapshot of record.snapshots ?? []) {
247
+ this.insertSnapshotStmt.run(snapshot.workspace_id, snapshot.session_id, snapshot.resource_id, snapshot.file_id, snapshot.mount_path, snapshot.snapshot_file_id, snapshot.sha256, snapshot.size_bytes, snapshot.kind, snapshot.skill_snapshot_id);
248
+ }
249
+ for (const skill of record.skillSnapshots ?? []) {
250
+ this.insertSkillSnapshotStmt.run(skill.workspace_id, skill.session_id, skill.skill_snapshot_id, skill.skill_id, skill.version, skill.name, skill.description);
251
+ }
252
+ idempotency?.complete();
253
+ this.clearPendingSnapshotCreateRollbacksBySessionStmt.run(s.workspace_id, s.id);
254
+ return s;
255
+ });
256
+ }
257
+ retrieve(workspaceId, sessionId) {
258
+ const row = this.retrieveActiveStmt.get(workspaceId, sessionId);
259
+ return row ? this.deserialize(row) : undefined;
260
+ }
261
+ countActive(workspaceId) {
262
+ return this.countActiveStmt.get(workspaceId).n;
263
+ }
264
+ hasEnvironmentReference(workspaceId, environmentId) {
265
+ return this.hasEnvironmentReferenceStmt.get(workspaceId, environmentId).exists_value === 1;
266
+ }
267
+ // Unscoped, for the /metrics gauge (0121 C2) — served by idx_sessions_live.
268
+ countAllActive() {
269
+ return this.countAllActiveStmt.get().n;
270
+ }
271
+ retrieveAny(workspaceId, sessionId) {
272
+ const row = this.retrieveAnyStmt.get(workspaceId, sessionId);
273
+ return row ? this.deserialize(row) : undefined;
274
+ }
275
+ archive(workspaceId, sessionId, archivedAt) {
276
+ this.archiveStmt.run(archivedAt, archivedAt, workspaceId, sessionId);
277
+ return this.retrieveAny(workspaceId, sessionId);
278
+ }
279
+ delete(workspaceId, sessionId) {
280
+ const existing = this.retrieveAny(workspaceId, sessionId);
281
+ if (!existing)
282
+ return undefined;
283
+ const now = new Date().toISOString();
284
+ return this.withTransaction(() => {
285
+ this.insertPendingSnapshotDeletesStmt.run(now, workspaceId, sessionId);
286
+ this.deleteSnapshotsStmt.run(workspaceId, sessionId);
287
+ this.db.prepare("DELETE FROM session_skill_snapshots WHERE workspace_id=? AND session_id=?").run(workspaceId, sessionId);
288
+ this.deleteResourcesStmt.run(workspaceId, sessionId);
289
+ this.deleteStmt.run(workspaceId, sessionId);
290
+ return existing;
291
+ });
292
+ }
293
+ withTransaction(fn) {
294
+ return withSqliteTransaction(this.db, fn);
295
+ }
296
+ getFileMountSnapshots(workspaceId, sessionId) {
297
+ return this.snapshotsBySessionStmt.all(workspaceId, sessionId);
298
+ }
299
+ getSkillSnapshots(workspaceId, sessionId) {
300
+ return this.skillSnapshotsBySessionStmt.all(workspaceId, sessionId);
301
+ }
302
+ listPendingInternalSnapshotDeleteWorkspaces() {
303
+ const rows = this.pendingSnapshotDeleteWorkspacesStmt.all();
304
+ return rows.map((row) => row.workspace_id);
305
+ }
306
+ getPendingInternalSnapshotDeletes(workspaceId, sessionId) {
307
+ const rows = sessionId === undefined
308
+ ? this.pendingSnapshotDeletesByWorkspaceStmt.all(workspaceId)
309
+ : this.pendingSnapshotDeletesBySessionStmt.all(workspaceId, sessionId);
310
+ return rows;
311
+ }
312
+ recordPendingInternalSnapshotDeleteAttempt(workspaceId, sessionId, resourceId, attemptedAt, error) {
313
+ this.recordPendingSnapshotDeleteAttemptStmt.run(attemptedAt, error, workspaceId, sessionId, resourceId);
314
+ }
315
+ clearPendingInternalSnapshotDelete(workspaceId, sessionId, resourceId) {
316
+ this.clearPendingSnapshotDeleteStmt.run(workspaceId, sessionId, resourceId);
317
+ }
318
+ recordPendingInternalSnapshotCreateRollback(row, createdAt) {
319
+ this.insertPendingSnapshotCreateRollbackStmt.run(row.workspace_id, row.session_id, row.resource_id, row.file_id, row.mount_path, row.snapshot_file_id, row.sha256, row.size_bytes, row.kind, row.skill_snapshot_id, createdAt);
320
+ }
321
+ listPendingInternalSnapshotCreateRollbackWorkspaces() {
322
+ const rows = this.pendingSnapshotCreateRollbackWorkspacesStmt.all();
323
+ return rows.map((row) => row.workspace_id);
324
+ }
325
+ getPendingInternalSnapshotCreateRollbacks(workspaceId, sessionId) {
326
+ const rows = sessionId === undefined
327
+ ? this.pendingSnapshotCreateRollbacksByWorkspaceStmt.all(workspaceId)
328
+ : this.pendingSnapshotCreateRollbacksBySessionStmt.all(workspaceId, sessionId);
329
+ return rows;
330
+ }
331
+ recordPendingInternalSnapshotCreateRollbackAttempt(workspaceId, sessionId, resourceId, attemptedAt, error) {
332
+ this.recordPendingSnapshotCreateRollbackAttemptStmt.run(attemptedAt, error, workspaceId, sessionId, resourceId);
333
+ }
334
+ clearPendingInternalSnapshotCreateRollback(workspaceId, sessionId, resourceId) {
335
+ this.clearPendingSnapshotCreateRollbackStmt.run(workspaceId, sessionId, resourceId);
336
+ }
337
+ list(workspaceId, opts = {}) {
338
+ if (opts.page === "") {
339
+ return { data: [], next_page: null, prev_page: null };
340
+ }
341
+ const limit = normalizeLimit(opts.limit);
342
+ const order = opts.order ?? "desc";
343
+ const includeArchived = opts.includeArchived ?? false;
344
+ const cursor = opts.page === undefined
345
+ ? undefined
346
+ : decodeSessionCursor(opts.page, { order, agentId: opts.agentId, includeArchived }, workspaceId, this.cursorSigningKey);
347
+ const direction = cursor?.direction ?? "next";
348
+ const queryOrder = direction === "prev"
349
+ ? (order === "asc" ? "desc" : "asc")
350
+ : order;
351
+ const stmt = this.listStmt({
352
+ includeArchived,
353
+ hasAgent: opts.agentId !== undefined,
354
+ hasPage: cursor !== undefined,
355
+ order: queryOrder,
356
+ });
357
+ const rows = stmt.all(...selectListArgs(workspaceId, limit + 1, opts.agentId, cursor?.anchor));
358
+ const hasMoreInQueryDirection = rows.length > limit;
359
+ const selected = rows.slice(0, limit);
360
+ if (direction === "prev")
361
+ selected.reverse();
362
+ const data = selected.map((row) => this.deserialize(row));
363
+ const hasNext = direction === "prev" ? cursor !== undefined : hasMoreInQueryDirection;
364
+ const hasPrev = direction === "prev" ? hasMoreInQueryDirection : cursor !== undefined;
365
+ const context = { order, agentId: opts.agentId, includeArchived };
366
+ return {
367
+ data,
368
+ next_page: hasNext && data.length > 0
369
+ ? encodeSessionCursor(data[data.length - 1].id, "next", context, workspaceId, this.cursorSigningKey)
370
+ : null,
371
+ prev_page: hasPrev && data.length > 0
372
+ ? encodeSessionCursor(data[0].id, "prev", context, workspaceId, this.cursorSigningKey)
373
+ : null,
374
+ };
375
+ }
376
+ close() {
377
+ this.db.close();
378
+ }
379
+ listStmt(opts) {
380
+ const key = JSON.stringify(opts);
381
+ const existing = this.listStmts.get(key);
382
+ if (existing)
383
+ return existing;
384
+ const predicates = ["workspace_id = ?"];
385
+ if (opts.hasAgent)
386
+ predicates.push("agent_id = ?");
387
+ if (!opts.includeArchived)
388
+ predicates.push("archived_at IS NULL");
389
+ if (opts.hasPage)
390
+ predicates.push(opts.order === "asc" ? "id > ?" : "id < ?");
391
+ const stmt = this.db.prepare(`SELECT * FROM sessions
392
+ WHERE ${predicates.join(" AND ")}
393
+ ORDER BY id ${opts.order.toUpperCase()}
394
+ LIMIT ?`);
395
+ this.listStmts.set(key, stmt);
396
+ return stmt;
397
+ }
398
+ deserialize(row) {
399
+ return {
400
+ id: row.id,
401
+ workspace_id: row.workspace_id,
402
+ type: "session",
403
+ agent: {
404
+ type: "agent",
405
+ id: row.agent_id,
406
+ version: row.agent_version,
407
+ },
408
+ environment_id: row.environment_id,
409
+ vault_ids: parseVaultIds(row.vault_ids),
410
+ status: row.status,
411
+ title: row.title,
412
+ metadata: JSON.parse(row.metadata),
413
+ created_at: row.created_at,
414
+ updated_at: row.updated_at,
415
+ archived_at: row.archived_at,
416
+ // B.1 has no runtime usage source yet; Cycle C/D should deserialize this column.
417
+ usage: null,
418
+ resources: this.resourcesBySessionStmt.all(row.workspace_id, row.id),
419
+ };
420
+ }
421
+ }
422
+ function ensureVaultIdsColumn(db) {
423
+ const columns = db.prepare("PRAGMA table_info(sessions)").all();
424
+ if (columns.some((column) => column.name === "vault_ids"))
425
+ return;
426
+ db.exec("ALTER TABLE sessions ADD COLUMN vault_ids TEXT NOT NULL DEFAULT '[]'");
427
+ }
428
+ function ensureSnapshotColumns(db) {
429
+ for (const table of [
430
+ "session_file_mount_snapshots",
431
+ "pending_internal_snapshot_deletes",
432
+ "pending_internal_snapshot_create_rollbacks",
433
+ ]) {
434
+ const columns = db.prepare(`PRAGMA table_info(${table})`).all();
435
+ if (!columns.some((column) => column.name === "kind")) {
436
+ db.exec(`ALTER TABLE ${table} ADD COLUMN kind TEXT NOT NULL DEFAULT 'upload'`);
437
+ }
438
+ if (!columns.some((column) => column.name === "skill_snapshot_id")) {
439
+ db.exec(`ALTER TABLE ${table} ADD COLUMN skill_snapshot_id TEXT`);
440
+ }
441
+ }
442
+ }
443
+ function parseVaultIds(value) {
444
+ const parsed = JSON.parse(value);
445
+ return Array.isArray(parsed) && parsed.every((item) => typeof item === "string")
446
+ ? [...parsed]
447
+ : [];
448
+ }
449
+ function encodeSessionCursor(anchor, direction, context, workspaceId, signingKey) {
450
+ const payload = {
451
+ v: 1,
452
+ anchor,
453
+ direction,
454
+ order: context.order,
455
+ agentId: context.agentId ?? null,
456
+ includeArchived: context.includeArchived,
457
+ };
458
+ const encodedPayload = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
459
+ const signature = signSessionCursor(encodedPayload, workspaceId, signingKey);
460
+ return `${encodedPayload}.${signature.toString("base64url")}`;
461
+ }
462
+ function decodeSessionCursor(value, context, workspaceId, signingKey) {
463
+ let payload;
464
+ try {
465
+ const parts = value.split(".");
466
+ if (parts.length !== 2)
467
+ throw new Error("invalid signed cursor");
468
+ const [encodedPayload, encodedSignature] = parts;
469
+ const decoded = decodeCanonicalBase64url(encodedPayload);
470
+ const signature = decodeCanonicalBase64url(encodedSignature);
471
+ const expected = signSessionCursor(encodedPayload, workspaceId, signingKey);
472
+ if (signature.length !== expected.length ||
473
+ !timingSafeEqual(signature, expected)) {
474
+ throw new Error("invalid cursor signature");
475
+ }
476
+ payload = JSON.parse(decoded.toString("utf8"));
477
+ }
478
+ catch {
479
+ throw invalidRequest("invalid page cursor");
480
+ }
481
+ if (!isSessionCursorPayload(payload))
482
+ throw invalidRequest("invalid page cursor");
483
+ if (payload.order !== context.order) {
484
+ throw invalidRequest("page token order does not match request");
485
+ }
486
+ if (payload.agentId !== (context.agentId ?? null) ||
487
+ payload.includeArchived !== context.includeArchived) {
488
+ throw invalidRequest("page token filters do not match request");
489
+ }
490
+ return payload;
491
+ }
492
+ function signSessionCursor(encodedPayload, workspaceId, signingKey) {
493
+ return createHmac("sha256", signingKey)
494
+ .update("oma-session-page-v1\0", "utf8")
495
+ .update(workspaceId, "utf8")
496
+ .update("\0", "utf8")
497
+ .update(encodedPayload, "utf8")
498
+ .digest();
499
+ }
500
+ function decodeCanonicalBase64url(value) {
501
+ if (!/^[A-Za-z0-9_-]+$/.test(value)) {
502
+ throw new Error("non-canonical base64url");
503
+ }
504
+ const decoded = Buffer.from(value, "base64url");
505
+ if (decoded.toString("base64url") !== value) {
506
+ throw new Error("non-canonical base64url");
507
+ }
508
+ return decoded;
509
+ }
510
+ function isSessionCursorPayload(value) {
511
+ if (typeof value !== "object" || value === null)
512
+ return false;
513
+ const payload = value;
514
+ return payload.v === 1 &&
515
+ typeof payload.anchor === "string" && payload.anchor.length > 0 &&
516
+ (payload.direction === "next" || payload.direction === "prev") &&
517
+ (payload.order === "asc" || payload.order === "desc") &&
518
+ (payload.agentId === null || typeof payload.agentId === "string") &&
519
+ typeof payload.includeArchived === "boolean";
520
+ }
521
+ function selectListArgs(workspaceId, limit, agentId, page) {
522
+ if (agentId === undefined && page === undefined)
523
+ return [workspaceId, limit];
524
+ if (agentId !== undefined && page === undefined) {
525
+ return [workspaceId, agentId, limit];
526
+ }
527
+ if (agentId === undefined && page !== undefined) {
528
+ return [workspaceId, page, limit];
529
+ }
530
+ return [workspaceId, agentId, page, limit];
531
+ }
532
+ function normalizeLimit(limit) {
533
+ if (limit === undefined)
534
+ return 20;
535
+ if (!Number.isSafeInteger(limit) || limit <= 0)
536
+ return 20;
537
+ return Math.min(limit, 100);
538
+ }
@@ -0,0 +1 @@
1
+ export {};