@myagentroam/node 0.9.120 → 0.9.122
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/dist/connector.js +10 -3
- package/dist/database.js +53 -167
- package/dist/migrations/demo.js +9 -0
- package/dist/migrations/types.js +1 -0
- package/dist/migrations/v013.js +327 -0
- package/dist/runner/mar-agent/managed-run-controller.js +1 -0
- package/dist/service/conversation-template-node-service.js +2 -35
- package/dist/service/goal-mcp-server.js +7 -1
- package/dist/service/hosted-conversation-node-service.js +17 -72
- package/dist/service/workspace-change-service.js +1 -0
- package/package.json +4 -4
- package/dist/migrations/v004.js +0 -91
- package/dist/migrations/v005.js +0 -33
- package/dist/migrations/v006.js +0 -191
- package/dist/migrations/v007.js +0 -52
- package/dist/migrations/v008.js +0 -28
- package/dist/migrations/v009.js +0 -163
- package/dist/migrations/v010.js +0 -96
- package/dist/migrations/v011.js +0 -67
- package/dist/migrations/v012.js +0 -8
|
@@ -515,8 +515,7 @@ export class ConversationTemplateNodeService {
|
|
|
515
515
|
manifest.entries.length !== numberValue(row, 'file_count') ||
|
|
516
516
|
manifest.entries.reduce((total, entry) => total + entry.size, 0) !==
|
|
517
517
|
numberValue(row, 'total_bytes') ||
|
|
518
|
-
snapshotContentHash(manifest.directories, manifest.entries
|
|
519
|
-
manifest.contentHash)
|
|
518
|
+
snapshotContentHash(manifest.directories, manifest.entries) !== manifest.contentHash)
|
|
520
519
|
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
521
520
|
const expected = new Map(manifest.entries.map((entry) => [entry.relativePath, entry]));
|
|
522
521
|
const expectedDirectories = new Set([...manifest.directories, 'inbox', 'output']);
|
|
@@ -734,17 +733,6 @@ async function listSnapshotEntries(root, expectedDirectories, actualDirectories,
|
|
|
734
733
|
}
|
|
735
734
|
return entries;
|
|
736
735
|
}
|
|
737
|
-
function legacyPublishedDirectories(entries) {
|
|
738
|
-
const directories = new Set(['inbox', 'output']);
|
|
739
|
-
for (const entry of entries) {
|
|
740
|
-
const parts = entry.relativePath.split('/');
|
|
741
|
-
for (let index = 1; index < parts.length; index += 1)
|
|
742
|
-
directories.add(parts.slice(0, index).join('/'));
|
|
743
|
-
}
|
|
744
|
-
directories.delete('inbox');
|
|
745
|
-
directories.delete('output');
|
|
746
|
-
return [...directories].sort((left, right) => left.localeCompare(right, 'en-US'));
|
|
747
|
-
}
|
|
748
736
|
function forcedExcluded(relativePath) {
|
|
749
737
|
const parts = relativePath.split('/');
|
|
750
738
|
if (parts.some((part) => {
|
|
@@ -814,19 +802,6 @@ function templateVersionManifest(input) {
|
|
|
814
802
|
typeof input['mcpConfigHash'] !== 'string' ||
|
|
815
803
|
typeof input['contentHash'] !== 'string')
|
|
816
804
|
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
817
|
-
if (input['schemaVersion'] === 1) {
|
|
818
|
-
if (!Array.isArray(input['files']))
|
|
819
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
820
|
-
const entries = input['files'].map((value) => manifestFile(value));
|
|
821
|
-
return {
|
|
822
|
-
schemaVersion: 1,
|
|
823
|
-
directories: legacyPublishedDirectories(entries),
|
|
824
|
-
entries,
|
|
825
|
-
contentHash: input['contentHash'],
|
|
826
|
-
mcpServers,
|
|
827
|
-
mcpConfigHash: input['mcpConfigHash']
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
805
|
if (input['schemaVersion'] !== 2 ||
|
|
831
806
|
typeof input['sourceWorkspacePath'] !== 'string' ||
|
|
832
807
|
!Array.isArray(input['directories']) ||
|
|
@@ -913,15 +888,7 @@ function safeNonNegativeInteger(input) {
|
|
|
913
888
|
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
914
889
|
return input;
|
|
915
890
|
}
|
|
916
|
-
function snapshotContentHash(directories, entries
|
|
917
|
-
if (schemaVersion === 1)
|
|
918
|
-
return hashText(entries
|
|
919
|
-
.map((entry) => {
|
|
920
|
-
if (entry.kind !== 'FILE')
|
|
921
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
922
|
-
return `${entry.relativePath}\0${entry.size}\0${entry.sha256}`;
|
|
923
|
-
})
|
|
924
|
-
.join('\n'));
|
|
891
|
+
function snapshotContentHash(directories, entries) {
|
|
925
892
|
return hashText([
|
|
926
893
|
...directories.map((directory) => `DIRECTORY\0${directory}`),
|
|
927
894
|
...entries.map((entry) => entry.kind === 'FILE'
|
|
@@ -9,6 +9,7 @@ export class GoalMcpServer {
|
|
|
9
9
|
goals;
|
|
10
10
|
changed;
|
|
11
11
|
capabilities = new Map();
|
|
12
|
+
sockets = new Set();
|
|
12
13
|
endpoint = process.platform === 'win32'
|
|
13
14
|
? `\\\\.\\pipe\\mar-goal-${process.pid}-${randomUUID()}`
|
|
14
15
|
: join(tmpdir(), `mar-goal-${process.pid}-${randomUUID()}.sock`);
|
|
@@ -48,7 +49,10 @@ export class GoalMcpServer {
|
|
|
48
49
|
return;
|
|
49
50
|
const server = await this.listening;
|
|
50
51
|
this.capabilities.clear();
|
|
51
|
-
|
|
52
|
+
const closed = new Promise((resolve, reject) => server.close((error) => (error === undefined ? resolve() : reject(error))));
|
|
53
|
+
for (const socket of this.sockets)
|
|
54
|
+
socket.destroy();
|
|
55
|
+
await closed;
|
|
52
56
|
if (process.platform !== 'win32')
|
|
53
57
|
await unlink(this.endpoint).catch(() => undefined);
|
|
54
58
|
}
|
|
@@ -65,6 +69,8 @@ export class GoalMcpServer {
|
|
|
65
69
|
this.capabilities.set(token, { ...capability, sessionId: nextId });
|
|
66
70
|
}
|
|
67
71
|
handle(socket) {
|
|
72
|
+
this.sockets.add(socket);
|
|
73
|
+
socket.once('close', () => this.sockets.delete(socket));
|
|
68
74
|
let body = '';
|
|
69
75
|
socket.setEncoding('utf8');
|
|
70
76
|
socket.on('data', (chunk) => {
|
|
@@ -1537,9 +1537,7 @@ export class HostedConversationNodeService {
|
|
|
1537
1537
|
processExecutor(paths, version, temporaryDirectory = paths.temporaryRootDirectory, generatedImagesDirectory, generatedImagesLogicalPath) {
|
|
1538
1538
|
return this.options.processExecutorFactory({
|
|
1539
1539
|
workspaceDirectory: version.templateDirectory,
|
|
1540
|
-
|
|
1541
|
-
? {}
|
|
1542
|
-
: { sourceWorkspacePath: version.sourceWorkspacePath }),
|
|
1540
|
+
sourceWorkspacePath: version.sourceWorkspacePath,
|
|
1543
1541
|
inboxDirectory: paths.uploadsDirectory,
|
|
1544
1542
|
outputDirectory: paths.outputDirectory,
|
|
1545
1543
|
...(generatedImagesDirectory === undefined || generatedImagesLogicalPath === undefined
|
|
@@ -1605,10 +1603,10 @@ export class HostedConversationNodeService {
|
|
|
1605
1603
|
if (!record(persisted))
|
|
1606
1604
|
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1607
1605
|
const snapshot = publishedTemplateSnapshot(persisted);
|
|
1608
|
-
const parsedDraft = publishedTemplateDraft(
|
|
1606
|
+
const parsedDraft = publishedTemplateDraft(persisted.draft);
|
|
1609
1607
|
const draft = parsedDraft.draft;
|
|
1610
1608
|
const mcpServers = publishedMcpServers(persisted.mcpServers);
|
|
1611
|
-
const contentHash = publishedTemplateContentHash(snapshot.directories, snapshot.entries
|
|
1609
|
+
const contentHash = publishedTemplateContentHash(snapshot.directories, snapshot.entries);
|
|
1612
1610
|
const totalBytes = snapshot.entries.reduce((total, entry) => total + entry.size, 0);
|
|
1613
1611
|
if (persisted.mcpConfigHash !== row.mcp_config_hash ||
|
|
1614
1612
|
createHash('sha256').update(JSON.stringify(mcpServers)).digest('hex') !==
|
|
@@ -1629,9 +1627,7 @@ export class HostedConversationNodeService {
|
|
|
1629
1627
|
templateId,
|
|
1630
1628
|
storageKey: row.storage_key,
|
|
1631
1629
|
templateDirectory: contentDirectory,
|
|
1632
|
-
|
|
1633
|
-
? {}
|
|
1634
|
-
: { sourceWorkspacePath: snapshot.sourceWorkspacePath }),
|
|
1630
|
+
sourceWorkspacePath: snapshot.sourceWorkspacePath,
|
|
1635
1631
|
draft,
|
|
1636
1632
|
modelPolicy: parsedDraft.modelPolicy,
|
|
1637
1633
|
mcpServers
|
|
@@ -1679,63 +1675,36 @@ function runtimeModels(value) {
|
|
|
1679
1675
|
}
|
|
1680
1676
|
function allowedModels(version, models) {
|
|
1681
1677
|
const allowed = new Set(version.modelPolicy.allowedIds);
|
|
1682
|
-
return models.filter((model) => model.enabled &&
|
|
1683
|
-
allowed.has(version.modelPolicy.identity === 'UPSTREAM_MODEL_ID' ? model.modelId : model.id));
|
|
1678
|
+
return models.filter((model) => model.enabled && allowed.has(model.modelId));
|
|
1684
1679
|
}
|
|
1685
1680
|
function selectModel(version, models, requestedModelId) {
|
|
1686
1681
|
const available = allowedModels(version, models);
|
|
1687
1682
|
const requested = available.find((candidate) => candidate.id === requestedModelId);
|
|
1688
1683
|
if (requested !== undefined)
|
|
1689
1684
|
return requested;
|
|
1690
|
-
const defaultModel = available.find((candidate) =>
|
|
1691
|
-
version.modelPolicy.defaultId);
|
|
1685
|
+
const defaultModel = available.find((candidate) => candidate.modelId === version.modelPolicy.defaultId);
|
|
1692
1686
|
if (defaultModel !== undefined)
|
|
1693
1687
|
return defaultModel;
|
|
1694
1688
|
const model = version.modelPolicy.allowedIds
|
|
1695
|
-
.map((allowedId) => available.find((candidate) =>
|
|
1696
|
-
? candidate.modelId
|
|
1697
|
-
: candidate.id) === allowedId))
|
|
1689
|
+
.map((allowedId) => available.find((candidate) => candidate.modelId === allowedId))
|
|
1698
1690
|
.find((candidate) => candidate !== undefined);
|
|
1699
1691
|
if (model === undefined)
|
|
1700
1692
|
throw new Error('HOSTED_CONVERSATION_MODEL_NOT_AVAILABLE');
|
|
1701
1693
|
return model;
|
|
1702
1694
|
}
|
|
1703
|
-
function publishedTemplateDraft(
|
|
1695
|
+
function publishedTemplateDraft(value) {
|
|
1704
1696
|
const current = conversationTemplateDraftSchema.safeParse(value);
|
|
1705
1697
|
if (current.success) {
|
|
1706
1698
|
const { allowedModels, defaultModelId, ...draft } = current.data;
|
|
1707
1699
|
return {
|
|
1708
1700
|
draft,
|
|
1709
1701
|
modelPolicy: {
|
|
1710
|
-
identity: 'UPSTREAM_MODEL_ID',
|
|
1711
1702
|
allowedIds: allowedModels.map((model) => model.modelId),
|
|
1712
1703
|
defaultId: defaultModelId
|
|
1713
1704
|
}
|
|
1714
1705
|
};
|
|
1715
1706
|
}
|
|
1716
|
-
|
|
1717
|
-
!record(value) ||
|
|
1718
|
-
'allowedModels' in value ||
|
|
1719
|
-
!Array.isArray(value.allowedModelIds))
|
|
1720
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1721
|
-
const allowedModelIds = value.allowedModelIds;
|
|
1722
|
-
const legacy = { ...value };
|
|
1723
|
-
delete legacy.allowedModelIds;
|
|
1724
|
-
const normalized = conversationTemplateDraftSchema.safeParse({
|
|
1725
|
-
...legacy,
|
|
1726
|
-
allowedModels: allowedModelIds.map((modelId) => ({ modelId, name: modelId }))
|
|
1727
|
-
});
|
|
1728
|
-
if (!normalized.success)
|
|
1729
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1730
|
-
const { allowedModels, defaultModelId, ...draft } = normalized.data;
|
|
1731
|
-
return {
|
|
1732
|
-
draft,
|
|
1733
|
-
modelPolicy: {
|
|
1734
|
-
identity: 'CONFIGURATION_ID',
|
|
1735
|
-
allowedIds: allowedModels.map((model) => model.modelId),
|
|
1736
|
-
defaultId: defaultModelId
|
|
1737
|
-
}
|
|
1738
|
-
};
|
|
1707
|
+
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1739
1708
|
}
|
|
1740
1709
|
function networkModels(models, networkMode) {
|
|
1741
1710
|
if (networkMode !== 'NONE')
|
|
@@ -1773,14 +1742,6 @@ function publishedMcpServers(value) {
|
|
|
1773
1742
|
});
|
|
1774
1743
|
}
|
|
1775
1744
|
function publishedTemplateSnapshot(manifest) {
|
|
1776
|
-
if (manifest.schemaVersion === 1) {
|
|
1777
|
-
const entries = publishedTemplateFiles(manifest.files);
|
|
1778
|
-
return {
|
|
1779
|
-
schemaVersion: 1,
|
|
1780
|
-
directories: legacyPublishedTemplateDirectories(entries),
|
|
1781
|
-
entries
|
|
1782
|
-
};
|
|
1783
|
-
}
|
|
1784
1745
|
if (manifest.schemaVersion !== 2 ||
|
|
1785
1746
|
typeof manifest.sourceWorkspacePath !== 'string' ||
|
|
1786
1747
|
!Array.isArray(manifest.directories) ||
|
|
@@ -1876,30 +1837,14 @@ function safePublishedTemplateSize(value) {
|
|
|
1876
1837
|
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1877
1838
|
return value;
|
|
1878
1839
|
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
}
|
|
1888
|
-
function publishedTemplateContentHash(directories, entries, schemaVersion) {
|
|
1889
|
-
const content = schemaVersion === 1
|
|
1890
|
-
? entries
|
|
1891
|
-
.map((entry) => {
|
|
1892
|
-
if (entry.kind !== 'FILE')
|
|
1893
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1894
|
-
return `${entry.relativePath}\0${entry.size}\0${entry.sha256}`;
|
|
1895
|
-
})
|
|
1896
|
-
.join('\n')
|
|
1897
|
-
: [
|
|
1898
|
-
...directories.map((directory) => `DIRECTORY\0${directory}`),
|
|
1899
|
-
...entries.map((entry) => entry.kind === 'FILE'
|
|
1900
|
-
? `${entry.kind}\0${entry.relativePath}\0${entry.size}\0${entry.executable ? 1 : 0}\0${entry.sha256}`
|
|
1901
|
-
: `${entry.kind}\0${entry.relativePath}\0${entry.size}\0${entry.linkTarget}\0${entry.targetKind}\0${entry.resolvedTarget}`)
|
|
1902
|
-
].join('\n');
|
|
1840
|
+
/** Current manifests hash directory and entry identities; changing the format requires a new version. */
|
|
1841
|
+
function publishedTemplateContentHash(directories, entries) {
|
|
1842
|
+
const content = [
|
|
1843
|
+
...directories.map((directory) => `DIRECTORY\0${directory}`),
|
|
1844
|
+
...entries.map((entry) => entry.kind === 'FILE'
|
|
1845
|
+
? `${entry.kind}\0${entry.relativePath}\0${entry.size}\0${entry.executable ? 1 : 0}\0${entry.sha256}`
|
|
1846
|
+
: `${entry.kind}\0${entry.relativePath}\0${entry.size}\0${entry.linkTarget}\0${entry.targetKind}\0${entry.resolvedTarget}`)
|
|
1847
|
+
].join('\n');
|
|
1903
1848
|
return createHash('sha256').update(content).digest('hex');
|
|
1904
1849
|
}
|
|
1905
1850
|
function validatePublishedSourceWorkspacePath(path) {
|
|
@@ -32,6 +32,7 @@ export class WorkspaceChangeService {
|
|
|
32
32
|
const existing = this.state.changesReads.get(workspace.id);
|
|
33
33
|
if (existing !== undefined)
|
|
34
34
|
return existing;
|
|
35
|
+
// Invalidation may arrive during this shared read; its old result must not repopulate the cache.
|
|
35
36
|
const version = this.state.changesVersions.get(workspace.id) ?? 0;
|
|
36
37
|
const request = this.state.changesLimiter.run(async () => {
|
|
37
38
|
const startedAt = performance.now();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.122",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"npm": "11.12.1",
|
|
31
31
|
"ws": "^8.21.3",
|
|
32
32
|
"zod": "4.4.3",
|
|
33
|
-
"@myagentroam/agent": "0.9.
|
|
34
|
-
"@myagentroam/code-navigation": "0.9.
|
|
35
|
-
"@myagentroam/protocol": "0.9.
|
|
33
|
+
"@myagentroam/agent": "0.9.122",
|
|
34
|
+
"@myagentroam/code-navigation": "0.9.122",
|
|
35
|
+
"@myagentroam/protocol": "0.9.122"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/ws": "^8.18.1"
|
package/dist/migrations/v004.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
export const NODE_SCHEMA_VERSION_V4 = 4;
|
|
2
|
-
/** Last supported released schema. Fresh databases start from this baseline. */
|
|
3
|
-
export const NODE_SCHEMA_V4 = String.raw `CREATE TABLE node_schema_migrations (
|
|
4
|
-
version INTEGER PRIMARY KEY, applied_at INTEGER NOT NULL
|
|
5
|
-
) STRICT;
|
|
6
|
-
CREATE TABLE workspaces (
|
|
7
|
-
id TEXT PRIMARY KEY, path TEXT NOT NULL UNIQUE,
|
|
8
|
-
kind TEXT NOT NULL CHECK (kind IN ('DIRECTORY', 'GIT_WORKSPACE')),
|
|
9
|
-
git_summary TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
10
|
-
) STRICT;
|
|
11
|
-
CREATE TABLE agent_sessions (
|
|
12
|
-
id TEXT PRIMARY KEY,
|
|
13
|
-
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
14
|
-
runner TEXT NOT NULL CHECK (runner IN ('codex', 'claude-code', 'opencode')),
|
|
15
|
-
pin_order INTEGER CHECK (pin_order IS NULL OR pin_order >= 0),
|
|
16
|
-
metadata TEXT NOT NULL CHECK (json_valid(metadata))
|
|
17
|
-
) STRICT;
|
|
18
|
-
CREATE TABLE terminal_sessions (
|
|
19
|
-
id TEXT PRIMARY KEY, workspace_id TEXT REFERENCES workspaces(id) ON DELETE SET NULL,
|
|
20
|
-
profile_id TEXT NOT NULL,
|
|
21
|
-
state TEXT NOT NULL CHECK (state IN ('RUNNING', 'EXITED', 'CLOSED')),
|
|
22
|
-
exit_code INTEGER, rows INTEGER NOT NULL CHECK (rows > 0 AND rows <= 1000),
|
|
23
|
-
cols INTEGER NOT NULL CHECK (cols > 0 AND cols <= 1000),
|
|
24
|
-
latest_sequence INTEGER NOT NULL CHECK (latest_sequence >= 0),
|
|
25
|
-
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
26
|
-
) STRICT;
|
|
27
|
-
CREATE TABLE runner_defaults (
|
|
28
|
-
runner TEXT PRIMARY KEY CHECK (runner IN ('codex', 'claude-code', 'opencode')),
|
|
29
|
-
model TEXT NOT NULL, effort TEXT NOT NULL, access TEXT NOT NULL
|
|
30
|
-
) STRICT;
|
|
31
|
-
CREATE TABLE mcp_installations (
|
|
32
|
-
target_kind TEXT NOT NULL CHECK (target_kind IN ('NODE','WORKSPACE')),
|
|
33
|
-
target_id TEXT NOT NULL, catalog_entry_id TEXT NOT NULL, server_name TEXT NOT NULL,
|
|
34
|
-
display_name TEXT NOT NULL, version TEXT NOT NULL,
|
|
35
|
-
enabled INTEGER NOT NULL CHECK (enabled IN (0,1)),
|
|
36
|
-
runtime TEXT NOT NULL CHECK (json_valid(runtime)),
|
|
37
|
-
configuration TEXT NOT NULL CHECK (json_valid(configuration)), installed_at INTEGER NOT NULL,
|
|
38
|
-
PRIMARY KEY (target_kind,target_id,server_name),
|
|
39
|
-
CHECK ((target_kind='NODE' AND target_id='') OR (target_kind='WORKSPACE' AND target_id<>''))
|
|
40
|
-
) STRICT;`;
|
|
41
|
-
export function assertNodeSchemaV4(database) {
|
|
42
|
-
const required = {
|
|
43
|
-
node_schema_migrations: ['version', 'applied_at'],
|
|
44
|
-
workspaces: ['id', 'path', 'kind', 'git_summary', 'created_at', 'updated_at'],
|
|
45
|
-
agent_sessions: ['id', 'workspace_id', 'runner', 'pin_order', 'metadata'],
|
|
46
|
-
terminal_sessions: [
|
|
47
|
-
'id',
|
|
48
|
-
'workspace_id',
|
|
49
|
-
'profile_id',
|
|
50
|
-
'state',
|
|
51
|
-
'exit_code',
|
|
52
|
-
'rows',
|
|
53
|
-
'cols',
|
|
54
|
-
'latest_sequence',
|
|
55
|
-
'created_at',
|
|
56
|
-
'updated_at'
|
|
57
|
-
],
|
|
58
|
-
runner_defaults: ['runner', 'model', 'effort', 'access'],
|
|
59
|
-
mcp_installations: [
|
|
60
|
-
'target_kind',
|
|
61
|
-
'target_id',
|
|
62
|
-
'catalog_entry_id',
|
|
63
|
-
'server_name',
|
|
64
|
-
'display_name',
|
|
65
|
-
'version',
|
|
66
|
-
'enabled',
|
|
67
|
-
'runtime',
|
|
68
|
-
'configuration',
|
|
69
|
-
'installed_at'
|
|
70
|
-
]
|
|
71
|
-
};
|
|
72
|
-
for (const [table, expected] of Object.entries(required)) {
|
|
73
|
-
const columns = database.prepare(`PRAGMA table_info(${table})`).all();
|
|
74
|
-
const actual = columns.map((column) => column.name);
|
|
75
|
-
// Later schema versions may add columns to this baseline table.
|
|
76
|
-
if (expected.some((name) => !actual.includes(name)))
|
|
77
|
-
throw new Error(`NODE_DATABASE_SCHEMA_INVALID:${table}`);
|
|
78
|
-
}
|
|
79
|
-
for (const table of ['agent_sessions', 'runner_defaults']) {
|
|
80
|
-
const definition = database
|
|
81
|
-
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name=?")
|
|
82
|
-
.get(table);
|
|
83
|
-
if (typeof definition?.sql !== 'string' || !definition.sql.includes("'opencode'"))
|
|
84
|
-
throw new Error(`NODE_DATABASE_SCHEMA_INVALID:${table}:runner`);
|
|
85
|
-
}
|
|
86
|
-
const row = database
|
|
87
|
-
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='mcp_installations'")
|
|
88
|
-
.get();
|
|
89
|
-
if (typeof row?.sql !== 'string' || !row.sql.includes("target_kind IN ('NODE','WORKSPACE')"))
|
|
90
|
-
throw new Error('NODE_DATABASE_SCHEMA_INVALID:mcp_installations');
|
|
91
|
-
}
|
package/dist/migrations/v005.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export const NODE_SCHEMA_VERSION_V5 = 5;
|
|
2
|
-
/** Adds MAR Agent to every persisted Runner discriminator. */
|
|
3
|
-
export const NODE_SCHEMA_MIGRATION_V5 = String.raw `
|
|
4
|
-
CREATE TABLE agent_sessions_v5 (
|
|
5
|
-
id TEXT PRIMARY KEY,
|
|
6
|
-
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
7
|
-
runner TEXT NOT NULL CHECK (runner IN ('mar-agent', 'codex', 'claude-code', 'opencode')),
|
|
8
|
-
pin_order INTEGER CHECK (pin_order IS NULL OR pin_order >= 0),
|
|
9
|
-
metadata TEXT NOT NULL CHECK (json_valid(metadata))
|
|
10
|
-
) STRICT;
|
|
11
|
-
INSERT INTO agent_sessions_v5 (id, workspace_id, runner, pin_order, metadata)
|
|
12
|
-
SELECT id, workspace_id, runner, pin_order, metadata FROM agent_sessions;
|
|
13
|
-
DROP TABLE agent_sessions;
|
|
14
|
-
ALTER TABLE agent_sessions_v5 RENAME TO agent_sessions;
|
|
15
|
-
|
|
16
|
-
CREATE TABLE runner_defaults_v5 (
|
|
17
|
-
runner TEXT PRIMARY KEY CHECK (runner IN ('mar-agent', 'codex', 'claude-code', 'opencode')),
|
|
18
|
-
model TEXT NOT NULL, effort TEXT NOT NULL, access TEXT NOT NULL
|
|
19
|
-
) STRICT;
|
|
20
|
-
INSERT INTO runner_defaults_v5 (runner, model, effort, access)
|
|
21
|
-
SELECT runner, model, effort, access FROM runner_defaults;
|
|
22
|
-
DROP TABLE runner_defaults;
|
|
23
|
-
ALTER TABLE runner_defaults_v5 RENAME TO runner_defaults;
|
|
24
|
-
`;
|
|
25
|
-
export function assertNodeSchemaV5(database) {
|
|
26
|
-
for (const table of ['agent_sessions', 'runner_defaults']) {
|
|
27
|
-
const definition = database
|
|
28
|
-
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name=?")
|
|
29
|
-
.get(table);
|
|
30
|
-
if (typeof definition?.sql !== 'string' || !definition.sql.includes("'mar-agent'"))
|
|
31
|
-
throw new Error(`NODE_DATABASE_SCHEMA_INVALID:${table}:runner`);
|
|
32
|
-
}
|
|
33
|
-
}
|
package/dist/migrations/v006.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
export const NODE_SCHEMA_VERSION_V6 = 6;
|
|
2
|
-
/** Adds rebuildable hosted-conversation indexes and immutable template publication metadata. */
|
|
3
|
-
export const NODE_SCHEMA_MIGRATION_V6 = String.raw `
|
|
4
|
-
CREATE TABLE conversation_template_versions (
|
|
5
|
-
id TEXT PRIMARY KEY,
|
|
6
|
-
template_id TEXT NOT NULL,
|
|
7
|
-
version_number INTEGER NOT NULL CHECK (version_number > 0),
|
|
8
|
-
draft_revision INTEGER NOT NULL CHECK (draft_revision > 0),
|
|
9
|
-
status TEXT NOT NULL CHECK (status IN ('AVAILABLE','DISABLED','CORRUPTED','RETAINED')),
|
|
10
|
-
storage_key TEXT NOT NULL UNIQUE,
|
|
11
|
-
manifest TEXT NOT NULL CHECK (json_valid(manifest)),
|
|
12
|
-
content_hash TEXT NOT NULL CHECK (length(content_hash) = 64),
|
|
13
|
-
manifest_hash TEXT NOT NULL CHECK (length(manifest_hash) = 64),
|
|
14
|
-
file_count INTEGER NOT NULL CHECK (file_count >= 0),
|
|
15
|
-
total_bytes INTEGER NOT NULL CHECK (total_bytes >= 0),
|
|
16
|
-
host_policy_version INTEGER NOT NULL CHECK (host_policy_version > 0),
|
|
17
|
-
published_by_user_id INTEGER NOT NULL CHECK (published_by_user_id > 0),
|
|
18
|
-
published_at INTEGER NOT NULL CHECK (published_at >= 0),
|
|
19
|
-
reference_count INTEGER NOT NULL DEFAULT 0 CHECK (reference_count >= 0),
|
|
20
|
-
UNIQUE (template_id, version_number)
|
|
21
|
-
) STRICT;
|
|
22
|
-
CREATE INDEX conversation_template_versions_template_status
|
|
23
|
-
ON conversation_template_versions(template_id, status, published_at);
|
|
24
|
-
|
|
25
|
-
CREATE TABLE conversation_template_publish_jobs (
|
|
26
|
-
id TEXT PRIMARY KEY,
|
|
27
|
-
template_id TEXT NOT NULL,
|
|
28
|
-
draft_revision INTEGER NOT NULL CHECK (draft_revision > 0),
|
|
29
|
-
stage TEXT NOT NULL CHECK (
|
|
30
|
-
stage IN ('VALIDATING','ENUMERATING','COPYING','SCANNING','MANIFEST','SEALED','REGISTERED')
|
|
31
|
-
),
|
|
32
|
-
status TEXT NOT NULL CHECK (status IN ('QUEUED','RUNNING','SUCCEEDED','FAILED')),
|
|
33
|
-
version_id TEXT REFERENCES conversation_template_versions(id) ON DELETE SET NULL,
|
|
34
|
-
error_code TEXT,
|
|
35
|
-
error_summary TEXT,
|
|
36
|
-
requested_by_user_id INTEGER NOT NULL CHECK (requested_by_user_id > 0),
|
|
37
|
-
draft TEXT NOT NULL CHECK (json_valid(draft)),
|
|
38
|
-
created_at INTEGER NOT NULL CHECK (created_at >= 0),
|
|
39
|
-
updated_at INTEGER NOT NULL CHECK (updated_at >= 0)
|
|
40
|
-
) STRICT;
|
|
41
|
-
CREATE INDEX conversation_template_publish_jobs_template
|
|
42
|
-
ON conversation_template_publish_jobs(template_id, created_at, id);
|
|
43
|
-
CREATE INDEX conversation_template_publish_jobs_status
|
|
44
|
-
ON conversation_template_publish_jobs(status, updated_at);
|
|
45
|
-
|
|
46
|
-
CREATE TABLE hosted_conversations (
|
|
47
|
-
id TEXT PRIMARY KEY,
|
|
48
|
-
user_storage_key TEXT NOT NULL,
|
|
49
|
-
conversation_storage_key TEXT NOT NULL,
|
|
50
|
-
client_request_id TEXT,
|
|
51
|
-
node_id TEXT NOT NULL,
|
|
52
|
-
session_id TEXT NOT NULL,
|
|
53
|
-
template_id TEXT NOT NULL,
|
|
54
|
-
template_name TEXT NOT NULL,
|
|
55
|
-
active_version_id TEXT NOT NULL REFERENCES conversation_template_versions(id),
|
|
56
|
-
pending_version_id TEXT REFERENCES conversation_template_versions(id),
|
|
57
|
-
title TEXT NOT NULL,
|
|
58
|
-
status TEXT NOT NULL CHECK (
|
|
59
|
-
status IN ('ACTIVE','UPGRADE_PENDING','UPGRADE_FAILED','RECOVERABLE_DELETED','DELETING')
|
|
60
|
-
),
|
|
61
|
-
model_id TEXT NOT NULL,
|
|
62
|
-
reasoning_effort TEXT,
|
|
63
|
-
template_deleted INTEGER NOT NULL DEFAULT 0 CHECK (template_deleted IN (0,1)),
|
|
64
|
-
recoverable_until INTEGER,
|
|
65
|
-
used_bytes INTEGER NOT NULL DEFAULT 0 CHECK (used_bytes >= 0),
|
|
66
|
-
max_bytes INTEGER NOT NULL CHECK (max_bytes > 0),
|
|
67
|
-
created_at INTEGER NOT NULL CHECK (created_at >= 0),
|
|
68
|
-
updated_at INTEGER NOT NULL CHECK (updated_at >= 0),
|
|
69
|
-
last_activity_at INTEGER NOT NULL CHECK (last_activity_at >= 0),
|
|
70
|
-
UNIQUE (user_storage_key, conversation_storage_key),
|
|
71
|
-
UNIQUE (user_storage_key, client_request_id)
|
|
72
|
-
) STRICT;
|
|
73
|
-
CREATE INDEX hosted_conversations_user_activity
|
|
74
|
-
ON hosted_conversations(user_storage_key, last_activity_at DESC, id DESC);
|
|
75
|
-
CREATE INDEX hosted_conversations_template
|
|
76
|
-
ON hosted_conversations(template_id, status, id);
|
|
77
|
-
|
|
78
|
-
CREATE TABLE conversation_files (
|
|
79
|
-
id TEXT PRIMARY KEY,
|
|
80
|
-
conversation_id TEXT NOT NULL REFERENCES hosted_conversations(id) ON DELETE CASCADE,
|
|
81
|
-
category TEXT NOT NULL CHECK (category IN ('UPLOAD','WORK','OUTPUT')),
|
|
82
|
-
relative_path TEXT NOT NULL,
|
|
83
|
-
display_name TEXT NOT NULL,
|
|
84
|
-
mime_type TEXT NOT NULL,
|
|
85
|
-
size INTEGER NOT NULL CHECK (size >= 0),
|
|
86
|
-
source TEXT NOT NULL CHECK (source IN ('USER','AGENT','SYSTEM')),
|
|
87
|
-
turn_id TEXT,
|
|
88
|
-
item_id TEXT,
|
|
89
|
-
created_at INTEGER NOT NULL CHECK (created_at >= 0),
|
|
90
|
-
updated_at INTEGER NOT NULL CHECK (updated_at >= 0),
|
|
91
|
-
UNIQUE (conversation_id, category, relative_path)
|
|
92
|
-
) STRICT;
|
|
93
|
-
CREATE INDEX conversation_files_conversation_updated
|
|
94
|
-
ON conversation_files(conversation_id, updated_at DESC, id);
|
|
95
|
-
|
|
96
|
-
CREATE TABLE hosted_conversation_messages (
|
|
97
|
-
conversation_id TEXT NOT NULL REFERENCES hosted_conversations(id) ON DELETE CASCADE,
|
|
98
|
-
client_message_id TEXT NOT NULL,
|
|
99
|
-
run_id TEXT NOT NULL,
|
|
100
|
-
status TEXT NOT NULL CHECK (status IN ('RUNNING','SUCCEEDED','FAILED','INTERRUPTED')),
|
|
101
|
-
created_at INTEGER NOT NULL CHECK (created_at >= 0),
|
|
102
|
-
updated_at INTEGER NOT NULL CHECK (updated_at >= 0),
|
|
103
|
-
PRIMARY KEY (conversation_id, client_message_id)
|
|
104
|
-
) STRICT;
|
|
105
|
-
`;
|
|
106
|
-
export function assertNodeSchemaV6(database) {
|
|
107
|
-
const required = {
|
|
108
|
-
conversation_template_versions: [
|
|
109
|
-
'id',
|
|
110
|
-
'template_id',
|
|
111
|
-
'version_number',
|
|
112
|
-
'draft_revision',
|
|
113
|
-
'status',
|
|
114
|
-
'storage_key',
|
|
115
|
-
'manifest',
|
|
116
|
-
'content_hash',
|
|
117
|
-
'manifest_hash',
|
|
118
|
-
'file_count',
|
|
119
|
-
'total_bytes',
|
|
120
|
-
'host_policy_version',
|
|
121
|
-
'published_by_user_id',
|
|
122
|
-
'published_at',
|
|
123
|
-
'reference_count'
|
|
124
|
-
],
|
|
125
|
-
conversation_template_publish_jobs: [
|
|
126
|
-
'id',
|
|
127
|
-
'template_id',
|
|
128
|
-
'draft_revision',
|
|
129
|
-
'stage',
|
|
130
|
-
'status',
|
|
131
|
-
'version_id',
|
|
132
|
-
'error_code',
|
|
133
|
-
'error_summary',
|
|
134
|
-
'requested_by_user_id',
|
|
135
|
-
'draft',
|
|
136
|
-
'created_at',
|
|
137
|
-
'updated_at'
|
|
138
|
-
],
|
|
139
|
-
hosted_conversations: [
|
|
140
|
-
'id',
|
|
141
|
-
'user_storage_key',
|
|
142
|
-
'conversation_storage_key',
|
|
143
|
-
'client_request_id',
|
|
144
|
-
'node_id',
|
|
145
|
-
'session_id',
|
|
146
|
-
'template_id',
|
|
147
|
-
'template_name',
|
|
148
|
-
'active_version_id',
|
|
149
|
-
'pending_version_id',
|
|
150
|
-
'title',
|
|
151
|
-
'status',
|
|
152
|
-
'model_id',
|
|
153
|
-
'reasoning_effort',
|
|
154
|
-
'template_deleted',
|
|
155
|
-
'recoverable_until',
|
|
156
|
-
'used_bytes',
|
|
157
|
-
'max_bytes',
|
|
158
|
-
'created_at',
|
|
159
|
-
'updated_at',
|
|
160
|
-
'last_activity_at'
|
|
161
|
-
],
|
|
162
|
-
conversation_files: [
|
|
163
|
-
'id',
|
|
164
|
-
'conversation_id',
|
|
165
|
-
'category',
|
|
166
|
-
'relative_path',
|
|
167
|
-
'display_name',
|
|
168
|
-
'mime_type',
|
|
169
|
-
'size',
|
|
170
|
-
'source',
|
|
171
|
-
'turn_id',
|
|
172
|
-
'item_id',
|
|
173
|
-
'created_at',
|
|
174
|
-
'updated_at'
|
|
175
|
-
],
|
|
176
|
-
hosted_conversation_messages: [
|
|
177
|
-
'conversation_id',
|
|
178
|
-
'client_message_id',
|
|
179
|
-
'run_id',
|
|
180
|
-
'status',
|
|
181
|
-
'created_at',
|
|
182
|
-
'updated_at'
|
|
183
|
-
]
|
|
184
|
-
};
|
|
185
|
-
for (const [table, expected] of Object.entries(required)) {
|
|
186
|
-
const columns = database.prepare(`PRAGMA table_info(${table})`).all();
|
|
187
|
-
const actual = columns.map((column) => column.name);
|
|
188
|
-
if (actual.length !== expected.length || expected.some((name) => !actual.includes(name)))
|
|
189
|
-
throw new Error(`NODE_DATABASE_SCHEMA_INVALID:${table}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
package/dist/migrations/v007.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
export const NODE_SCHEMA_VERSION_V7 = 7;
|
|
2
|
-
export const EMPTY_MCP_CONFIG_HASH = '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945';
|
|
3
|
-
/** Adds immutable MCP fingerprints and removes the retired hosted reasoning preference. */
|
|
4
|
-
export const NODE_SCHEMA_MIGRATION_V7 = String.raw `
|
|
5
|
-
ALTER TABLE conversation_template_versions
|
|
6
|
-
ADD COLUMN mcp_config_hash TEXT NOT NULL DEFAULT '${EMPTY_MCP_CONFIG_HASH}'
|
|
7
|
-
CHECK (length(mcp_config_hash) = 64);
|
|
8
|
-
ALTER TABLE hosted_conversations DROP COLUMN reasoning_effort;
|
|
9
|
-
CREATE TABLE conversation_files_v7 (
|
|
10
|
-
id TEXT PRIMARY KEY,
|
|
11
|
-
conversation_id TEXT NOT NULL REFERENCES hosted_conversations(id) ON DELETE CASCADE,
|
|
12
|
-
category TEXT NOT NULL CHECK (category IN ('UPLOAD','OUTPUT')),
|
|
13
|
-
relative_path TEXT NOT NULL,
|
|
14
|
-
display_name TEXT NOT NULL,
|
|
15
|
-
mime_type TEXT NOT NULL,
|
|
16
|
-
size INTEGER NOT NULL CHECK (size >= 0),
|
|
17
|
-
source TEXT NOT NULL CHECK (source IN ('USER','AGENT','SYSTEM')),
|
|
18
|
-
turn_id TEXT,
|
|
19
|
-
item_id TEXT,
|
|
20
|
-
created_at INTEGER NOT NULL CHECK (created_at >= 0),
|
|
21
|
-
updated_at INTEGER NOT NULL CHECK (updated_at >= 0),
|
|
22
|
-
UNIQUE (conversation_id, category, relative_path)
|
|
23
|
-
) STRICT;
|
|
24
|
-
INSERT INTO conversation_files_v7
|
|
25
|
-
(id,conversation_id,category,relative_path,display_name,mime_type,size,source,
|
|
26
|
-
turn_id,item_id,created_at,updated_at)
|
|
27
|
-
SELECT id,conversation_id,category,relative_path,display_name,mime_type,size,source,
|
|
28
|
-
turn_id,item_id,created_at,updated_at
|
|
29
|
-
FROM conversation_files
|
|
30
|
-
WHERE category IN ('UPLOAD','OUTPUT');
|
|
31
|
-
DROP TABLE conversation_files;
|
|
32
|
-
ALTER TABLE conversation_files_v7 RENAME TO conversation_files;
|
|
33
|
-
CREATE INDEX conversation_files_conversation_updated
|
|
34
|
-
ON conversation_files(conversation_id, updated_at DESC, id);
|
|
35
|
-
`;
|
|
36
|
-
export function assertNodeSchemaV7(database) {
|
|
37
|
-
const templateColumns = database
|
|
38
|
-
.prepare('PRAGMA table_info(conversation_template_versions)')
|
|
39
|
-
.all();
|
|
40
|
-
if (!templateColumns.some((column) => column.name === 'mcp_config_hash'))
|
|
41
|
-
throw new Error('NODE_DATABASE_SCHEMA_INVALID:conversation_template_versions');
|
|
42
|
-
const conversationColumns = database.prepare('PRAGMA table_info(hosted_conversations)').all();
|
|
43
|
-
if (conversationColumns === undefined ||
|
|
44
|
-
conversationColumns.some((column) => column.name === 'reasoning_effort'))
|
|
45
|
-
throw new Error('NODE_DATABASE_SCHEMA_INVALID:hosted_conversations');
|
|
46
|
-
const conversationFiles = database
|
|
47
|
-
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='conversation_files'")
|
|
48
|
-
.get();
|
|
49
|
-
if (typeof conversationFiles?.sql !== 'string' ||
|
|
50
|
-
!conversationFiles.sql.includes("category IN ('UPLOAD','OUTPUT')"))
|
|
51
|
-
throw new Error('NODE_DATABASE_SCHEMA_INVALID:conversation_files');
|
|
52
|
-
}
|