@myagentroam/node 0.11.15 → 0.11.16
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 -20
- 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 -0
- 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/native-session-projection-service.js +7 -24
- package/dist/service/node-control-channel.js +1 -1
- package/dist/service/node-request-service.js +1 -1
- package/dist/service/node-run-event-bridge.js +0 -24
- 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 +4 -4
|
@@ -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 = {};
|
|
@@ -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;
|
|
@@ -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,30 +27,6 @@ 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
30
|
reconcile(envelope) {
|
|
55
31
|
const payload = envelope.payload;
|
|
56
32
|
if (!Array.isArray(payload.runIds) || payload.runIds.some((id) => typeof id !== 'string')) {
|
|
@@ -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)
|
|
@@ -104,15 +104,6 @@ export function compactRunnerValue(value, limit) {
|
|
|
104
104
|
}
|
|
105
105
|
return { text: compactRunnerText(text, limit), truncated: text.length > limit };
|
|
106
106
|
}
|
|
107
|
-
export function codexTimestamp(value) {
|
|
108
|
-
if (typeof value === 'number' && Number.isFinite(value))
|
|
109
|
-
return value < 10_000_000_000 ? value * 1_000 : value;
|
|
110
|
-
if (typeof value === 'string') {
|
|
111
|
-
const timestamp = Date.parse(value);
|
|
112
|
-
return Number.isNaN(timestamp) ? null : timestamp;
|
|
113
|
-
}
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
107
|
export function nativePageEnd(cursor, sessionId, fallback) {
|
|
117
108
|
if (cursor === undefined)
|
|
118
109
|
return fallback;
|
|
@@ -128,42 +119,6 @@ export function nativePageEnd(cursor, sessionId, fallback) {
|
|
|
128
119
|
return fallback;
|
|
129
120
|
}
|
|
130
121
|
}
|
|
131
|
-
export function extractClaudeText(message) {
|
|
132
|
-
return claudeContentBlocks(message)
|
|
133
|
-
.flatMap((block) => block.type === 'text' && typeof block.text === 'string' ? [block.text] : [])
|
|
134
|
-
.join('');
|
|
135
|
-
}
|
|
136
|
-
export function extractClaudeToolUses(message) {
|
|
137
|
-
return claudeContentBlocks(message).flatMap((block) => {
|
|
138
|
-
if (block.type !== 'tool_use' || typeof block.id !== 'string' || typeof block.name !== 'string')
|
|
139
|
-
return [];
|
|
140
|
-
const input = block.input;
|
|
141
|
-
const command = isPlainRecord(input) && typeof input.command === 'string' ? input.command : undefined;
|
|
142
|
-
return [
|
|
143
|
-
{
|
|
144
|
-
id: block.id,
|
|
145
|
-
name: block.name,
|
|
146
|
-
input,
|
|
147
|
-
...(command === undefined ? {} : { command }),
|
|
148
|
-
inputTruncated: compactRunnerValue(input, 10_000).truncated
|
|
149
|
-
}
|
|
150
|
-
];
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
export function claudeContentBlocks(message) {
|
|
154
|
-
const content = message.message;
|
|
155
|
-
if (!isPlainRecord(content) || !Array.isArray(content.content))
|
|
156
|
-
return [];
|
|
157
|
-
return content.content.filter(isPlainRecord);
|
|
158
|
-
}
|
|
159
|
-
export function isClaudeCommandTool(name) {
|
|
160
|
-
return name === 'Bash' || name === 'bash' || name === 'Shell';
|
|
161
|
-
}
|
|
162
|
-
export function nativeClaudeCommand(entry) {
|
|
163
|
-
if (isPlainRecord(entry.input) && typeof entry.input.command === 'string')
|
|
164
|
-
return compactRunnerText(entry.input.command, 10_000);
|
|
165
|
-
return entry.inputSummary ?? entry.toolName;
|
|
166
|
-
}
|
|
167
122
|
export function boundedConversationItemId(prefix, value) {
|
|
168
123
|
const candidate = `${prefix}:${value}`;
|
|
169
124
|
if (candidate.length <= 88)
|