@myagentroam/node 0.11.15 → 0.11.17
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 +13 -24
- package/dist/native-session-history.js +35 -832
- package/dist/native-transcript-adapters.js +9 -0
- package/dist/native-transcript-model.js +1 -0
- package/dist/runner/abstract-runner.js +12 -0
- package/dist/runner/claude/conversation-parser.js +80 -1
- package/dist/runner/claude/managed-run-controller.js +4 -2
- package/dist/runner/claude/message-parsers.js +37 -0
- package/dist/runner/claude/native-context-usage.js +90 -0
- package/dist/runner/claude/native-transcript.js +347 -0
- package/dist/runner/claude/tool-normalizer.js +12 -2
- package/dist/runner/claude-code-runner.js +13 -2
- package/dist/runner/codex/conversation-parser.js +67 -3
- package/dist/runner/codex/managed-run-controller.js +3 -2
- package/dist/runner/codex/native-context-usage.js +42 -0
- package/dist/runner/codex/native-transcript.js +575 -0
- package/dist/runner/codex-runner.js +18 -3
- package/dist/runner/mar-agent/managed-run-controller.js +1 -1
- package/dist/runner/mar-agent-runner.js +6 -0
- package/dist/runner/opencode/conversation-parser.js +1 -1
- package/dist/runner/opencode/managed-run-controller.js +1 -1
- package/dist/runner/opencode-runner.js +7 -1
- package/dist/runner/runner-registry.js +4 -0
- package/dist/runtime-state.js +1 -6
- package/dist/service/continuous-run-coordinator.js +1 -0
- package/dist/service/conversation-template-manifest.js +179 -0
- package/dist/service/conversation-template-node-service.js +10 -132
- package/dist/service/external-session-resume-service.js +15 -0
- package/dist/service/fake-managed-run-service.js +1 -1
- package/dist/service/goal-continuation-coordinator.js +4 -3
- package/dist/service/goal-runtime.js +0 -9
- package/dist/service/hosted-conversation-node-service.js +16 -179
- package/dist/service/hosted-conversation-store.js +2 -33
- package/dist/service/native-session-projection-service.js +7 -24
- package/dist/service/node-control-channel.js +1 -1
- package/dist/service/node-control-message-service.js +0 -8
- package/dist/service/node-request-service.js +1 -1
- package/dist/service/node-run-event-bridge.js +0 -37
- package/dist/service/node-terminal-channel.js +1 -1
- package/dist/service/recent-run-projection-service.js +2 -1
- package/dist/service/run-workbench-service.js +1 -1
- package/dist/service/runner-interaction-service.js +2 -1
- package/dist/service/runner-service.js +6 -4
- package/dist/service/session-identity-service.js +1 -0
- package/dist/service/session-message-service.js +1 -0
- package/dist/service/session-query-service.js +1 -1
- package/dist/service/workbench-event-projection.js +1 -1
- package/dist/service/workbench-event-service.js +1 -1
- package/dist/service/workspace-file-service.js +0 -2
- package/dist/service/workspace-queue-workbench-service.js +2 -2
- package/dist/util/node-operation-parsers.js +0 -45
- package/dist/util/runner-native-session-parsers.js +17 -194
- package/package.json +5 -5
|
@@ -1,33 +1,19 @@
|
|
|
1
1
|
import { assertMcpServerNames } from '../mcp/naming.js';
|
|
2
2
|
import { modelUsageGrantSchema, steerComposerMessage } from '@myagentroam/protocol';
|
|
3
|
-
import {
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { constants } from 'node:fs';
|
|
5
5
|
import { lstat, mkdir, open, readFile, rename, rm, utimes } from 'node:fs/promises';
|
|
6
|
-
import {
|
|
7
|
-
import { toolResultResourceInputSchema, toolResultResourcePageSchema, conversationTurnSchema,
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { toolResultResourceInputSchema, toolResultResourcePageSchema, conversationTurnSchema, conversationTemplateSummarySchema, hostedConversationConfigUpdateInputSchema, hostedConversationCreateInputSchema, hostedConversationDeleteResultSchema, hostedConversationFileDeleteInputSchema, hostedConversationFileDeleteResultSchema, hostedConversationFileReadInputSchema, hostedConversationFileReadResultSchema, hostedConversationFilesSchema, hostedConversationHistoryInputSchema, hostedConversationHistoryPageSchema, hostedConversationIdInputSchema, hostedConversationImageReadInputSchema, hostedConversationListInputSchema, hostedConversationListPageSchema, hostedConversationMessageInputSchema, hostedConversationMetadataUpdateInputSchema, hostedConversationRollbackInputSchema, hostedConversationSnapshotSchema, hostedConversationSummarySchema, hostedConversationUnwatchInputSchema, hostedConversationUploadChunkInputSchema, hostedConversationUploadControlInputSchema, hostedConversationUploadCreateInputSchema, hostedConversationUploadPreflightInputSchema, hostedConversationUploadPreflightResultSchema, hostedConversationUploadSchema, hostedConversationWatchInputSchema, marAgentRuntimeModelConfigurationSchema, resolveMarAgentDefaultReasoningEffort } from '@myagentroam/protocol';
|
|
8
8
|
import { hostedConversationGeneratedImagesDisplayDirectory, hostedConversationImageRunId, marAgentGeneratedImagesDirectory } from '../runner/mar-agent/generated-images.js';
|
|
9
9
|
import { sessionTitleFromFirstMessage } from '../util/node-operation-parsers.js';
|
|
10
10
|
import { safeErrorCode } from '../util/safe-error.js';
|
|
11
11
|
import { SessionWatchSlotCoordinator } from './session-watch-slot-coordinator.js';
|
|
12
|
+
import { parseTemplateVersionManifest, templateContentHash, templateMcpConfigHash, templateManifestHash } from './conversation-template-manifest.js';
|
|
12
13
|
const DEFAULT_HOSTED_CONVERSATION_TITLE = 'New conversation';
|
|
13
14
|
const HOST_POLICY_VERSION = 1;
|
|
14
15
|
const MAX_FILE_READ_BYTES = 512 * 1024;
|
|
15
16
|
const WATCH_TTL_MS = 45_000;
|
|
16
|
-
const RUNTIME_LINK_ROOTS = ['/usr', '/bin', '/sbin', '/lib', '/lib64'];
|
|
17
|
-
const RESERVED_SANDBOX_ROOTS = [
|
|
18
|
-
'/workspace',
|
|
19
|
-
'/home/mar',
|
|
20
|
-
'/runtime',
|
|
21
|
-
'/usr',
|
|
22
|
-
'/bin',
|
|
23
|
-
'/sbin',
|
|
24
|
-
'/lib',
|
|
25
|
-
'/lib64',
|
|
26
|
-
'/proc',
|
|
27
|
-
'/dev',
|
|
28
|
-
'/run',
|
|
29
|
-
'/tmp'
|
|
30
|
-
];
|
|
31
17
|
/** Implements the Node-owned hosted-conversation domain over an injected MAR Agent runtime. */
|
|
32
18
|
export class HostedConversationNodeService {
|
|
33
19
|
options;
|
|
@@ -1719,8 +1705,7 @@ export class HostedConversationNodeService {
|
|
|
1719
1705
|
try {
|
|
1720
1706
|
const manifestText = await readFile(manifestPath, 'utf8');
|
|
1721
1707
|
const manifestJson = manifestText.endsWith('\n') ? manifestText.slice(0, -1) : manifestText;
|
|
1722
|
-
if (manifestJson !== row.manifest ||
|
|
1723
|
-
createHash('sha256').update(manifestJson).digest('hex') !== row.manifest_hash)
|
|
1708
|
+
if (manifestJson !== row.manifest || templateManifestHash(manifestText) !== row.manifest_hash)
|
|
1724
1709
|
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1725
1710
|
persisted = JSON.parse(manifestJson);
|
|
1726
1711
|
}
|
|
@@ -1729,16 +1714,14 @@ export class HostedConversationNodeService {
|
|
|
1729
1714
|
}
|
|
1730
1715
|
if (!record(persisted))
|
|
1731
1716
|
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1732
|
-
const snapshot =
|
|
1733
|
-
const
|
|
1734
|
-
const
|
|
1735
|
-
const
|
|
1736
|
-
const contentHash = publishedTemplateContentHash(snapshot.directories, snapshot.entries);
|
|
1717
|
+
const snapshot = parseTemplateVersionManifest(persisted);
|
|
1718
|
+
const { allowedModels, defaultModelId, ...draft } = snapshot.draft;
|
|
1719
|
+
const mcpServers = snapshot.mcpServers;
|
|
1720
|
+
const contentHash = templateContentHash(snapshot.directories, snapshot.entries);
|
|
1737
1721
|
const totalBytes = snapshot.entries.reduce((total, entry) => total + entry.size, 0);
|
|
1738
|
-
if (
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
persisted.contentHash !== row.content_hash ||
|
|
1722
|
+
if (snapshot.mcpConfigHash !== row.mcp_config_hash ||
|
|
1723
|
+
templateMcpConfigHash(mcpServers) !== row.mcp_config_hash ||
|
|
1724
|
+
snapshot.contentHash !== row.content_hash ||
|
|
1742
1725
|
contentHash !== row.content_hash ||
|
|
1743
1726
|
row.file_count !== snapshot.entries.length ||
|
|
1744
1727
|
row.total_bytes !== totalBytes)
|
|
@@ -1756,7 +1739,10 @@ export class HostedConversationNodeService {
|
|
|
1756
1739
|
templateDirectory: contentDirectory,
|
|
1757
1740
|
sourceWorkspacePath: snapshot.sourceWorkspacePath,
|
|
1758
1741
|
draft,
|
|
1759
|
-
modelPolicy:
|
|
1742
|
+
modelPolicy: {
|
|
1743
|
+
allowedIds: allowedModels.map((model) => model.modelId),
|
|
1744
|
+
defaultId: defaultModelId
|
|
1745
|
+
},
|
|
1760
1746
|
mcpServers
|
|
1761
1747
|
};
|
|
1762
1748
|
}
|
|
@@ -1825,160 +1811,11 @@ function selectModel(version, models, requestedModelId) {
|
|
|
1825
1811
|
throw new Error('HOSTED_CONVERSATION_MODEL_NOT_AVAILABLE');
|
|
1826
1812
|
return model;
|
|
1827
1813
|
}
|
|
1828
|
-
function publishedTemplateDraft(value) {
|
|
1829
|
-
const current = conversationTemplateDraftSchema.safeParse(value);
|
|
1830
|
-
if (current.success) {
|
|
1831
|
-
const { allowedModels, defaultModelId, ...draft } = current.data;
|
|
1832
|
-
return {
|
|
1833
|
-
draft,
|
|
1834
|
-
modelPolicy: {
|
|
1835
|
-
allowedIds: allowedModels.map((model) => model.modelId),
|
|
1836
|
-
defaultId: defaultModelId
|
|
1837
|
-
}
|
|
1838
|
-
};
|
|
1839
|
-
}
|
|
1840
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1841
|
-
}
|
|
1842
1814
|
function networkModels(models, networkMode) {
|
|
1843
1815
|
if (networkMode !== 'NONE')
|
|
1844
1816
|
return models;
|
|
1845
1817
|
return models.map((model) => model.hostedWebSearch ? { ...model, hostedWebSearch: false } : model);
|
|
1846
1818
|
}
|
|
1847
|
-
function publishedMcpServers(value) {
|
|
1848
|
-
if (!Array.isArray(value))
|
|
1849
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1850
|
-
return value.map((candidate) => {
|
|
1851
|
-
if (!record(candidate))
|
|
1852
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1853
|
-
const keys = Object.keys(candidate).sort().join(',');
|
|
1854
|
-
if (keys !== 'configuration,displayName,id,origin,runtime,serverName,transport,version')
|
|
1855
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1856
|
-
const runtime = mcpRuntimeDescriptorSchema.parse(candidate.runtime);
|
|
1857
|
-
const configuration = mcpInstallationConfigurationSchema.parse(candidate.configuration);
|
|
1858
|
-
if (typeof candidate.id !== 'string' ||
|
|
1859
|
-
typeof candidate.serverName !== 'string' ||
|
|
1860
|
-
typeof candidate.displayName !== 'string' ||
|
|
1861
|
-
typeof candidate.version !== 'string' ||
|
|
1862
|
-
(candidate.origin !== 'NODE' && candidate.origin !== 'WORKSPACE') ||
|
|
1863
|
-
candidate.transport !== runtime.transport)
|
|
1864
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1865
|
-
return {
|
|
1866
|
-
id: candidate.id,
|
|
1867
|
-
serverName: candidate.serverName,
|
|
1868
|
-
displayName: candidate.displayName,
|
|
1869
|
-
version: candidate.version,
|
|
1870
|
-
origin: candidate.origin,
|
|
1871
|
-
transport: runtime.transport,
|
|
1872
|
-
runtime,
|
|
1873
|
-
configuration
|
|
1874
|
-
};
|
|
1875
|
-
});
|
|
1876
|
-
}
|
|
1877
|
-
function publishedTemplateSnapshot(manifest) {
|
|
1878
|
-
if (manifest.schemaVersion !== 2 ||
|
|
1879
|
-
typeof manifest.sourceWorkspacePath !== 'string' ||
|
|
1880
|
-
!Array.isArray(manifest.directories) ||
|
|
1881
|
-
!Array.isArray(manifest.entries))
|
|
1882
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1883
|
-
validatePublishedSourceWorkspacePath(manifest.sourceWorkspacePath);
|
|
1884
|
-
const directories = manifest.directories.map(safePublishedTemplateRelativePath);
|
|
1885
|
-
if (new Set(directories).size !== directories.length ||
|
|
1886
|
-
directories.includes('inbox') ||
|
|
1887
|
-
directories.includes('output'))
|
|
1888
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1889
|
-
const entries = manifest.entries.map(publishedTemplateEntry);
|
|
1890
|
-
if (new Set(entries.map((entry) => entry.relativePath)).size !== entries.length)
|
|
1891
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1892
|
-
return {
|
|
1893
|
-
schemaVersion: 2,
|
|
1894
|
-
sourceWorkspacePath: manifest.sourceWorkspacePath,
|
|
1895
|
-
directories,
|
|
1896
|
-
entries
|
|
1897
|
-
};
|
|
1898
|
-
}
|
|
1899
|
-
function publishedTemplateEntry(value) {
|
|
1900
|
-
if (!record(value))
|
|
1901
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1902
|
-
if (value.kind === 'FILE')
|
|
1903
|
-
return publishedTemplateFile(value, true);
|
|
1904
|
-
if (value.kind !== 'SYMLINK' ||
|
|
1905
|
-
typeof value.linkTarget !== 'string' ||
|
|
1906
|
-
value.linkTarget.length === 0 ||
|
|
1907
|
-
value.linkTarget.includes('\0') ||
|
|
1908
|
-
(value.targetKind !== 'SNAPSHOT' && value.targetKind !== 'RUNTIME') ||
|
|
1909
|
-
typeof value.resolvedTarget !== 'string')
|
|
1910
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1911
|
-
const relativePath = safePublishedTemplateRelativePath(value.relativePath);
|
|
1912
|
-
const size = safePublishedTemplateSize(value.size);
|
|
1913
|
-
const linkTarget = value.linkTarget;
|
|
1914
|
-
const targetKind = value.targetKind;
|
|
1915
|
-
const resolvedTarget = value.resolvedTarget;
|
|
1916
|
-
if (size !== Buffer.byteLength(linkTarget))
|
|
1917
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1918
|
-
if (targetKind === 'SNAPSHOT')
|
|
1919
|
-
safePublishedTemplateRelativePath(resolvedTarget);
|
|
1920
|
-
else if (!isAbsolute(resolvedTarget) ||
|
|
1921
|
-
!RUNTIME_LINK_ROOTS.some((root) => resolvedTarget === root || within(root, resolvedTarget)))
|
|
1922
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1923
|
-
return {
|
|
1924
|
-
kind: 'SYMLINK',
|
|
1925
|
-
relativePath,
|
|
1926
|
-
size,
|
|
1927
|
-
linkTarget,
|
|
1928
|
-
targetKind,
|
|
1929
|
-
resolvedTarget
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1932
|
-
function publishedTemplateFile(value, requireKind = false) {
|
|
1933
|
-
if (!record(value) ||
|
|
1934
|
-
(requireKind && value.kind !== 'FILE') ||
|
|
1935
|
-
typeof value.executable !== 'boolean' ||
|
|
1936
|
-
typeof value.sha256 !== 'string' ||
|
|
1937
|
-
!/^[a-f0-9]{64}$/u.test(value.sha256))
|
|
1938
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1939
|
-
return {
|
|
1940
|
-
kind: 'FILE',
|
|
1941
|
-
relativePath: safePublishedTemplateRelativePath(value.relativePath),
|
|
1942
|
-
size: safePublishedTemplateSize(value.size),
|
|
1943
|
-
executable: value.executable,
|
|
1944
|
-
sha256: value.sha256
|
|
1945
|
-
};
|
|
1946
|
-
}
|
|
1947
|
-
function safePublishedTemplateRelativePath(value) {
|
|
1948
|
-
if (typeof value !== 'string' ||
|
|
1949
|
-
value.length === 0 ||
|
|
1950
|
-
value.startsWith('/') ||
|
|
1951
|
-
value.includes('\\') ||
|
|
1952
|
-
value.split('/').some((segment) => segment.length === 0 || segment === '.' || segment === '..'))
|
|
1953
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1954
|
-
return value;
|
|
1955
|
-
}
|
|
1956
|
-
function safePublishedTemplateSize(value) {
|
|
1957
|
-
if (!Number.isSafeInteger(value) || value < 0)
|
|
1958
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1959
|
-
return value;
|
|
1960
|
-
}
|
|
1961
|
-
/** Current manifests hash directory and entry identities; changing the format requires a new version. */
|
|
1962
|
-
function publishedTemplateContentHash(directories, entries) {
|
|
1963
|
-
const content = [
|
|
1964
|
-
...directories.map((directory) => `DIRECTORY\0${directory}`),
|
|
1965
|
-
...entries.map((entry) => entry.kind === 'FILE'
|
|
1966
|
-
? `${entry.kind}\0${entry.relativePath}\0${entry.size}\0${entry.executable ? 1 : 0}\0${entry.sha256}`
|
|
1967
|
-
: `${entry.kind}\0${entry.relativePath}\0${entry.size}\0${entry.linkTarget}\0${entry.targetKind}\0${entry.resolvedTarget}`)
|
|
1968
|
-
].join('\n');
|
|
1969
|
-
return createHash('sha256').update(content).digest('hex');
|
|
1970
|
-
}
|
|
1971
|
-
function validatePublishedSourceWorkspacePath(path) {
|
|
1972
|
-
if (!isAbsolute(path) ||
|
|
1973
|
-
resolve(path) !== path ||
|
|
1974
|
-
path === '/' ||
|
|
1975
|
-
RESERVED_SANDBOX_ROOTS.some((root) => path === root || within(root, path) || within(path, root)))
|
|
1976
|
-
throw new Error('CONVERSATION_TEMPLATE_VERSION_CORRUPTED');
|
|
1977
|
-
}
|
|
1978
|
-
function within(parent, candidate) {
|
|
1979
|
-
const relation = relative(parent, candidate);
|
|
1980
|
-
return relation.length > 0 && relation !== '..' && !relation.startsWith(`..${sep}`);
|
|
1981
|
-
}
|
|
1982
1819
|
function resolveMcpServers(version, environment) {
|
|
1983
1820
|
const resolveReferences = (references) => {
|
|
1984
1821
|
const resolved = {};
|
|
@@ -701,11 +701,8 @@ export class HostedConversationStore {
|
|
|
701
701
|
const paths = conversationPaths(directory);
|
|
702
702
|
await this.assertConversationLayout(paths);
|
|
703
703
|
let manifest;
|
|
704
|
-
let manifestNeedsUpgrade = false;
|
|
705
704
|
try {
|
|
706
|
-
|
|
707
|
-
manifest = stored.manifest;
|
|
708
|
-
manifestNeedsUpgrade = stored.upgraded;
|
|
705
|
+
manifest = hostedConversationManifestSchema.parse(JSON.parse(await readFile(paths.manifestPath, 'utf8')));
|
|
709
706
|
}
|
|
710
707
|
catch {
|
|
711
708
|
throw new Error('HOSTED_CONVERSATION_STORAGE_INVALID');
|
|
@@ -724,7 +721,7 @@ export class HostedConversationStore {
|
|
|
724
721
|
...manifest,
|
|
725
722
|
quota: { ...manifest.quota, usedBytes }
|
|
726
723
|
});
|
|
727
|
-
if (
|
|
724
|
+
if (next !== manifest)
|
|
728
725
|
await this.writeManifest(paths.manifestPath, next);
|
|
729
726
|
rebuilt.push({ manifest: next, files });
|
|
730
727
|
}
|
|
@@ -1220,34 +1217,6 @@ function conversationPaths(conversationDirectory) {
|
|
|
1220
1217
|
quotaDirectory: join(runtimeDirectory, 'quota')
|
|
1221
1218
|
};
|
|
1222
1219
|
}
|
|
1223
|
-
function storedHostedConversationManifest(input) {
|
|
1224
|
-
const stored = typeof input === 'object' && input !== null && !Array.isArray(input)
|
|
1225
|
-
? input
|
|
1226
|
-
: {};
|
|
1227
|
-
const withoutLegacyMetadata = { ...stored };
|
|
1228
|
-
delete withoutLegacyMetadata['recoverableUntil'];
|
|
1229
|
-
delete withoutLegacyMetadata['pendingVersionId'];
|
|
1230
|
-
const missingPinnedAt = !('pinnedAt' in stored);
|
|
1231
|
-
const missingTitleSource = !('titleSource' in stored);
|
|
1232
|
-
const legacyUpgradeStatus = stored.status === 'UPGRADE_PENDING' || stored.status === 'UPGRADE_FAILED';
|
|
1233
|
-
const hasPendingVersion = 'pendingVersionId' in stored;
|
|
1234
|
-
const upgraded = missingPinnedAt ||
|
|
1235
|
-
missingTitleSource ||
|
|
1236
|
-
'recoverableUntil' in stored ||
|
|
1237
|
-
hasPendingVersion ||
|
|
1238
|
-
legacyUpgradeStatus;
|
|
1239
|
-
return {
|
|
1240
|
-
manifest: hostedConversationManifestSchema.parse(upgraded
|
|
1241
|
-
? {
|
|
1242
|
-
...withoutLegacyMetadata,
|
|
1243
|
-
...(missingPinnedAt ? { pinnedAt: null } : {}),
|
|
1244
|
-
...(missingTitleSource ? { titleSource: 'CUSTOM' } : {}),
|
|
1245
|
-
...(legacyUpgradeStatus ? { status: 'ACTIVE' } : {})
|
|
1246
|
-
}
|
|
1247
|
-
: input),
|
|
1248
|
-
upgraded
|
|
1249
|
-
};
|
|
1250
|
-
}
|
|
1251
1220
|
function childPath(parent, key) {
|
|
1252
1221
|
if (!STORAGE_KEY.test(key))
|
|
1253
1222
|
throw new Error('HOSTED_CONVERSATION_STORAGE_INVALID');
|
|
@@ -117,28 +117,21 @@ export class NativeSessionProjectionService {
|
|
|
117
117
|
}
|
|
118
118
|
resumedConfiguration(session) {
|
|
119
119
|
const workspace = this.options.database()?.getWorkspace(session.workspaceId);
|
|
120
|
+
const runner = this.options.runners.require(session.runner);
|
|
120
121
|
const saved = this.options
|
|
121
122
|
.database()
|
|
122
123
|
?.getRunnerDefaults()
|
|
123
124
|
.find((value) => value.runner === session.runner);
|
|
124
|
-
const fallback = saved ??
|
|
125
|
+
const fallback = saved ?? runner.defaultConfiguration(workspace);
|
|
125
126
|
const configuration = {
|
|
126
127
|
runner: session.runner,
|
|
127
128
|
model: typeof session.model === 'string' && session.model.trim().length > 0
|
|
128
129
|
? session.model
|
|
129
130
|
: fallback.model,
|
|
130
131
|
effort: typeof session.effort === 'string' ? session.effort : fallback.effort,
|
|
131
|
-
access:
|
|
132
|
-
? session.access
|
|
133
|
-
: session.runner === 'mar-agent'
|
|
134
|
-
? 'full-access'
|
|
135
|
-
: fallback.access
|
|
132
|
+
access: runner.restoreStoredAccess(session.access, fallback.access)
|
|
136
133
|
};
|
|
137
|
-
return
|
|
138
|
-
configuration.access !== 'auto' &&
|
|
139
|
-
configuration.access !== 'full-access'
|
|
140
|
-
? { ...configuration, access: 'full-access' }
|
|
141
|
-
: configuration;
|
|
134
|
+
return configuration;
|
|
142
135
|
}
|
|
143
136
|
ensureResumedConfiguration(session) {
|
|
144
137
|
const configuration = this.resumedConfiguration(session);
|
|
@@ -315,32 +308,22 @@ export class NativeSessionProjectionService {
|
|
|
315
308
|
runner.forgetNativeAlias(nativeId);
|
|
316
309
|
}
|
|
317
310
|
}
|
|
311
|
+
const directIds = new Set();
|
|
318
312
|
for (const [directId, direct] of this.options.state.directNative) {
|
|
319
313
|
if (direct.runner === session.runner &&
|
|
320
314
|
direct.cwd === session.cwd &&
|
|
321
315
|
direct.externalSessionId !== null &&
|
|
322
316
|
nativeIds.has(direct.externalSessionId)) {
|
|
317
|
+
directIds.add(directId);
|
|
323
318
|
this.options.state.directNative.delete(directId);
|
|
324
|
-
this.options.state.externalActivity.delete(directId);
|
|
325
|
-
this.options.state.resumedByDirectId.delete(directId);
|
|
326
|
-
this.options.state.resumeFailures.delete(directId);
|
|
327
|
-
this.options.state.messageResumeFailures.delete(directId);
|
|
328
|
-
this.options.state.resumeFailureDetails.delete(directId);
|
|
329
|
-
const timer = this.options.state.failureTimers.get(directId);
|
|
330
|
-
if (timer !== undefined)
|
|
331
|
-
clearTimeout(timer);
|
|
332
|
-
this.options.state.failureTimers.delete(directId);
|
|
333
319
|
}
|
|
334
320
|
}
|
|
335
|
-
for (const [directId, resumedId] of this.options.state.resumedByDirectId)
|
|
336
|
-
if (projectionIds.has(resumedId))
|
|
337
|
-
this.options.state.resumedByDirectId.delete(directId);
|
|
338
321
|
for (const projectionId of projectionIds) {
|
|
339
322
|
if (this.options.runtime.getAgentSession(projectionId) !== undefined)
|
|
340
323
|
this.options.runtime.removeAgentSession(projectionId);
|
|
341
324
|
this.options.commands.removeSession(projectionId);
|
|
342
|
-
this.options.state.externalActivity.delete(projectionId);
|
|
343
325
|
}
|
|
326
|
+
this.options.releaseResumeState(directIds, projectionIds);
|
|
344
327
|
}
|
|
345
328
|
requireDatabase() {
|
|
346
329
|
const database = this.options.database();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
2
|
import { createEnvelope } from '@myagentroam/protocol';
|
|
3
3
|
import { ControlOutboundScheduler } from '../control-outbound-scheduler.js';
|
|
4
|
-
import { nodeConnectEndpoint } from '../
|
|
4
|
+
import { nodeConnectEndpoint } from '../util/node-operation-parsers.js';
|
|
5
5
|
export class NodeControlChannel {
|
|
6
6
|
options;
|
|
7
7
|
socket;
|
|
@@ -51,10 +51,6 @@ export class NodeControlMessageService {
|
|
|
51
51
|
}
|
|
52
52
|
if (envelope.type === 'ack')
|
|
53
53
|
return;
|
|
54
|
-
if (envelope.type === 'runs.reconcile') {
|
|
55
|
-
this.options.reconcileRuns(envelope);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
54
|
if (envelope.type === 'run.approval.decision') {
|
|
59
55
|
this.decideApproval(envelope);
|
|
60
56
|
return;
|
|
@@ -80,10 +76,6 @@ export class NodeControlMessageService {
|
|
|
80
76
|
this.cancelRun(envelope);
|
|
81
77
|
return;
|
|
82
78
|
}
|
|
83
|
-
if (envelope.type === 'lease.reconcile') {
|
|
84
|
-
this.options.send('ack', { type: envelope.type }, envelope.id);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
79
|
if (envelope.type !== 'error')
|
|
88
80
|
this.options.send('error', { code: 'MESSAGE_TYPE_UNKNOWN' }, envelope.id);
|
|
89
81
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { nodeLog } from '../operational.js';
|
|
2
|
-
import { validUserAuthorization } from '../
|
|
2
|
+
import { validUserAuthorization } from '../util/node-operation-parsers.js';
|
|
3
3
|
import { safePublicError } from '../util/safe-error.js';
|
|
4
4
|
/** Validates and executes the closed Admin-facing Node operation envelope. */
|
|
5
5
|
export class NodeRequestService {
|
|
@@ -27,41 +27,4 @@ export class NodeRunEventBridge {
|
|
|
27
27
|
queue: this.options.queues.present(workspaceId, sessionId)
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
migrateRunnerSessionIdentity(sessionId, nativeSessionId) {
|
|
31
|
-
for (const [turnId, managed] of this.options.codex.execution.managedTurns) {
|
|
32
|
-
if (managed.sessionId === sessionId)
|
|
33
|
-
this.options.codex.execution.managedTurns.set(turnId, {
|
|
34
|
-
...managed,
|
|
35
|
-
sessionId: nativeSessionId
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
for (const [runId, managed] of this.options.codex.execution.runs) {
|
|
39
|
-
if (managed.sessionId === sessionId)
|
|
40
|
-
this.options.codex.execution.runs.set(runId, { ...managed, sessionId: nativeSessionId });
|
|
41
|
-
}
|
|
42
|
-
for (const [runId, managed] of this.options.claude.execution.runs) {
|
|
43
|
-
if (managed.marSessionId === sessionId)
|
|
44
|
-
this.options.claude.execution.runs.set(runId, {
|
|
45
|
-
...managed,
|
|
46
|
-
marSessionId: nativeSessionId
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
for (const [runId, managed] of this.options.openCode.execution) {
|
|
50
|
-
if (managed.sessionId === sessionId)
|
|
51
|
-
this.options.openCode.execution.set(runId, { ...managed, sessionId: nativeSessionId });
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
reconcile(envelope) {
|
|
55
|
-
const payload = envelope.payload;
|
|
56
|
-
if (!Array.isArray(payload.runIds) || payload.runIds.some((id) => typeof id !== 'string')) {
|
|
57
|
-
this.options.send('error', { code: 'MESSAGE_INVALID' }, envelope.id);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
for (const runId of payload.runIds) {
|
|
61
|
-
const known = this.options.state.active.has(runId);
|
|
62
|
-
this.options.runtime.reconcileRun(runId, known);
|
|
63
|
-
this.options.send('lease.reconcile', { runId, known });
|
|
64
|
-
}
|
|
65
|
-
this.options.send('ack', { type: 'runs.reconcile' }, envelope.id);
|
|
66
|
-
}
|
|
67
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
2
|
import { terminalDataFrameSchema } from '@myagentroam/protocol';
|
|
3
|
-
import { nodeTerminalConnectEndpoint, isPlainRecord } from '../
|
|
3
|
+
import { nodeTerminalConnectEndpoint, isPlainRecord } from '../util/node-operation-parsers.js';
|
|
4
4
|
import { TerminalRelayState } from './terminal-relay-state.js';
|
|
5
5
|
import { TerminalRelaySender } from '@myagentroam/marctl/src/terminal-relay-sender.mjs';
|
|
6
6
|
export class NodeTerminalChannel {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { composerVisibleText } from '@myagentroam/protocol';
|
|
1
2
|
const TERMINAL_TTL_MS = 12 * 60 * 60_000;
|
|
2
3
|
const CLEANUP_INTERVAL_MS = 5 * 60_000;
|
|
3
4
|
const TERMINAL_LIMIT = 100;
|
|
@@ -91,7 +92,7 @@ function taskKey(userId, sessionId) {
|
|
|
91
92
|
return `${userId}\0${sessionId}`;
|
|
92
93
|
}
|
|
93
94
|
function messagePreview(value) {
|
|
94
|
-
const normalized = value
|
|
95
|
+
const normalized = value === undefined ? undefined : composerVisibleText(value).replace(/\s+/gu, ' ');
|
|
95
96
|
return normalized === undefined || normalized.length === 0 ? undefined : normalized.slice(0, 500);
|
|
96
97
|
}
|
|
97
98
|
function terminal(status) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runImageReadInputSchema } from '@myagentroam/protocol';
|
|
2
2
|
import { nodeOperationData } from '../util/node-operation-data.js';
|
|
3
3
|
import { isTerminalRunStatus } from '../util/node-operation-parsers.js';
|
|
4
|
-
import { isPlainRecord } from '../
|
|
4
|
+
import { isPlainRecord } from '../util/node-operation-parsers.js';
|
|
5
5
|
import { isHostedConversationImageRunId } from '../runner/mar-agent/generated-images.js';
|
|
6
6
|
import { assertRunOwner } from './run-ownership-policy.js';
|
|
7
7
|
export class RunWorkbenchService {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { denyPermission } from '../claude-agent-sdk.js';
|
|
2
|
-
import { boundedConversationItemId,
|
|
2
|
+
import { boundedConversationItemId, isTerminalRunStatus } from '../util/node-operation-parsers.js';
|
|
3
|
+
import { claudeUserInputQuestions } from '../runner/claude/conversation-parser.js';
|
|
3
4
|
export class RunnerInteractionService {
|
|
4
5
|
options;
|
|
5
6
|
constructor(options) {
|
|
@@ -52,13 +52,15 @@ export class RunnerService {
|
|
|
52
52
|
const defaults = this.database()
|
|
53
53
|
.getRunnerDefaults()
|
|
54
54
|
.map((configuration) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
configuration
|
|
55
|
+
const runner = this.runners.optional(configuration.runner);
|
|
56
|
+
if (runner === undefined)
|
|
57
|
+
return configuration;
|
|
58
|
+
const access = runner.restoreStoredAccess(configuration.access, configuration.access);
|
|
59
|
+
if (access === configuration.access)
|
|
58
60
|
return configuration;
|
|
59
61
|
const normalized = {
|
|
60
62
|
...configuration,
|
|
61
|
-
access
|
|
63
|
+
access
|
|
62
64
|
};
|
|
63
65
|
this.database().saveRunnerDefaults(normalized);
|
|
64
66
|
return normalized;
|
|
@@ -28,6 +28,7 @@ export class SessionIdentityService {
|
|
|
28
28
|
const session = this.options.runtime.promoteSessionIdentity(sessionId, nativeSessionId, {
|
|
29
29
|
deferRunChanges: true
|
|
30
30
|
});
|
|
31
|
+
this.options.migrateAttachments(sessionId, nativeSessionId);
|
|
31
32
|
this.options.migrateGoal(sessionId, nativeSessionId);
|
|
32
33
|
this.options.migrateCommands(sessionId, nativeSessionId);
|
|
33
34
|
this.options.migrateQueue(sessionId, nativeSessionId);
|
|
@@ -178,6 +178,7 @@ export class SessionMessageService {
|
|
|
178
178
|
sessionId: session.id,
|
|
179
179
|
clientMessageId,
|
|
180
180
|
content: publicContent,
|
|
181
|
+
...(internalGoal || goalMessage?.activated === true ? { goalInput: true } : {}),
|
|
181
182
|
...(internalSchedule ? { source: 'SCHEDULED' } : {}),
|
|
182
183
|
...(input.__suppressNotifications === true ? { suppressNotifications: true } : {}),
|
|
183
184
|
...(Number.isSafeInteger(input.__requestUserId)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { marctlSessionDetail } from './session-recent-activity.js';
|
|
2
|
-
import { isPlainRecord } from '../
|
|
2
|
+
import { isPlainRecord } from '../util/node-operation-parsers.js';
|
|
3
3
|
import { toolResultResourceInputSchema, toolResultResourcePageSchema } from '@myagentroam/protocol';
|
|
4
4
|
export class SessionQueryService {
|
|
5
5
|
options;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { conversationSegmentIdentityForItem, conversationSegments } from './conversation-segment-service.js';
|
|
2
2
|
import { runConversationItemId } from '../runtime-state.js';
|
|
3
|
-
import { isNodeAgentRun, isNodeConversationTurn, isPlainRecord } from '../
|
|
3
|
+
import { isNodeAgentRun, isNodeConversationTurn, isPlainRecord } from '../util/node-operation-parsers.js';
|
|
4
4
|
export function workbenchPayloadSessionId(getRun, category, payload) {
|
|
5
5
|
if (!isPlainRecord(payload))
|
|
6
6
|
return undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createEnvelope } from '@myagentroam/protocol';
|
|
2
|
-
import { isPlainRecord } from '../
|
|
2
|
+
import { isPlainRecord } from '../util/node-operation-parsers.js';
|
|
3
3
|
import { projectWorkbenchPayload, workbenchPayloadSessionId } from './workbench-event-projection.js';
|
|
4
4
|
export class WorkbenchEventService {
|
|
5
5
|
options;
|
|
@@ -189,7 +189,6 @@ export class WorkspaceFileService {
|
|
|
189
189
|
throw new Error('FILE_PATH_ESCAPE');
|
|
190
190
|
return this.serializeMutation(workspace.id, async () => {
|
|
191
191
|
const result = await writeWorkspaceTextFile(workspace.path, path, content, expectedRevision);
|
|
192
|
-
this.dispose(workspace.id);
|
|
193
192
|
this.onInvalidated(workspace.id);
|
|
194
193
|
return result;
|
|
195
194
|
});
|
|
@@ -221,7 +220,6 @@ export class WorkspaceFileService {
|
|
|
221
220
|
throw new Error('FILE_PATH_ESCAPE');
|
|
222
221
|
return this.serializeMutation(workspace.id, async () => {
|
|
223
222
|
const result = await mutateWorkspaceFile(workspace.path, action === 'MOVE' ? { action, path, destination: destination } : { action, path });
|
|
224
|
-
this.dispose(workspace.id);
|
|
225
223
|
this.onInvalidated(workspace.id);
|
|
226
224
|
return result;
|
|
227
225
|
});
|
|
@@ -177,7 +177,7 @@ export class WorkspaceQueueWorkbenchService {
|
|
|
177
177
|
}
|
|
178
178
|
publicQueueItem(itemId) {
|
|
179
179
|
const item = this.options.runtime.getQueueItem(itemId);
|
|
180
|
-
return item?.
|
|
180
|
+
return item?.goalInput === true ? undefined : item;
|
|
181
181
|
}
|
|
182
182
|
queuedCount(sessionId) {
|
|
183
183
|
return this.options.runtime
|
|
@@ -185,7 +185,7 @@ export class WorkspaceQueueWorkbenchService {
|
|
|
185
185
|
.itemIds.filter((id) => this.publicQueueItem(id) !== undefined).length;
|
|
186
186
|
}
|
|
187
187
|
requireItem(itemId, workspaceId) {
|
|
188
|
-
const item = this.
|
|
188
|
+
const item = this.publicQueueItem(itemId);
|
|
189
189
|
if (item === undefined)
|
|
190
190
|
throw new Error('QUEUE_ITEM_INVALID');
|
|
191
191
|
if (typeof workspaceId !== 'string' || item.workspaceId !== workspaceId)
|