@myagentroam/node 0.9.119 → 0.9.121
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 +49 -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/codex-runner.js +44 -76
- package/dist/runner/mar-agent/managed-run-controller.js +1 -0
- package/dist/runner/opencode-runner.js +27 -38
- package/dist/runner/runner-registry.js +8 -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
|
@@ -24,13 +24,11 @@ export class CodexRunner extends AbstractRunner {
|
|
|
24
24
|
fastEnabled = false;
|
|
25
25
|
environmentFingerprint;
|
|
26
26
|
environmentRuns = new Set();
|
|
27
|
-
profileCache
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
profileCache;
|
|
28
|
+
profileCacheVersion;
|
|
29
|
+
profileCacheTimer;
|
|
30
|
+
profileRefresh;
|
|
30
31
|
profileClients = new Set();
|
|
31
|
-
profileKeyByEnvironment = new Map();
|
|
32
|
-
latestProfileCache;
|
|
33
|
-
selectedProfileKey;
|
|
34
32
|
profileGeneration = 0;
|
|
35
33
|
constructor(client = new CodexAppServerClient(), profileClientFactory = () => new CodexAppServerClient()) {
|
|
36
34
|
super();
|
|
@@ -43,40 +41,39 @@ export class CodexRunner extends AbstractRunner {
|
|
|
43
41
|
claudeCodeAvailable: capabilities.claudeCode.available,
|
|
44
42
|
openCodeAvailable: capabilities.openCode?.available === true
|
|
45
43
|
}).find((profile) => profile.runner === this.name);
|
|
46
|
-
return { ...base, models: this.
|
|
44
|
+
return { ...base, models: this.profileCache?.profile.models ?? [] };
|
|
47
45
|
}
|
|
48
46
|
cachedProfile(capabilities, environment = {}) {
|
|
49
47
|
const base = this.profile(capabilities);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
return this.profileCache === undefined ||
|
|
49
|
+
this.profileCacheVersion !== capabilities.codex.version
|
|
50
|
+
? { ...base, models: [] }
|
|
51
|
+
: this.profileCache.profile;
|
|
53
52
|
}
|
|
54
53
|
async refreshProfile(capabilities, workspace, environment = {}, waitForRefresh = false) {
|
|
55
54
|
void workspace;
|
|
56
55
|
const base = this.profile(capabilities);
|
|
57
56
|
if (!capabilities.codex.available)
|
|
58
57
|
return { ...base, models: [] };
|
|
59
|
-
const
|
|
60
|
-
this.profileKeyByEnvironment.set(codexSecretEnvironmentFingerprint(environment), key);
|
|
61
|
-
this.selectedProfileKey = key;
|
|
58
|
+
const version = capabilities.codex.version;
|
|
62
59
|
const now = Date.now();
|
|
63
|
-
const cached = this.profileCache
|
|
60
|
+
const cached = this.profileCacheVersion === version ? this.profileCache : undefined;
|
|
64
61
|
if (cached?.successful === true && now < cached.fetchedAt + CODEX_MODEL_CACHE_MS)
|
|
65
|
-
return
|
|
62
|
+
return cached.profile;
|
|
66
63
|
if (cached?.successful === true) {
|
|
67
64
|
if (now >= cached.retryAt) {
|
|
68
|
-
const refresh = this.refreshCodexProfile(
|
|
65
|
+
const refresh = this.refreshCodexProfile(version, base, environment);
|
|
69
66
|
if (waitForRefresh)
|
|
70
67
|
return refresh;
|
|
71
68
|
}
|
|
72
|
-
return
|
|
69
|
+
return cached.profile;
|
|
73
70
|
}
|
|
74
71
|
if (cached !== undefined && now < cached.retryAt)
|
|
75
72
|
return cached.profile;
|
|
76
|
-
return this.refreshCodexProfile(
|
|
73
|
+
return this.refreshCodexProfile(version, base, environment);
|
|
77
74
|
}
|
|
78
|
-
refreshCodexProfile(
|
|
79
|
-
const existing = this.
|
|
75
|
+
refreshCodexProfile(version, base, environment) {
|
|
76
|
+
const existing = this.profileRefresh;
|
|
80
77
|
if (existing !== undefined)
|
|
81
78
|
return existing;
|
|
82
79
|
const generation = this.profileGeneration;
|
|
@@ -88,11 +85,8 @@ export class CodexRunner extends AbstractRunner {
|
|
|
88
85
|
successful: true,
|
|
89
86
|
retryAt: 0
|
|
90
87
|
};
|
|
91
|
-
if (generation === this.profileGeneration)
|
|
92
|
-
this.cacheProfile(
|
|
93
|
-
if (this.selectedProfileKey === key)
|
|
94
|
-
this.latestProfileCache = cache;
|
|
95
|
-
}
|
|
88
|
+
if (generation === this.profileGeneration)
|
|
89
|
+
this.cacheProfile(version, cache);
|
|
96
90
|
return cache.profile;
|
|
97
91
|
})
|
|
98
92
|
.catch((error) => {
|
|
@@ -101,12 +95,12 @@ export class CodexRunner extends AbstractRunner {
|
|
|
101
95
|
});
|
|
102
96
|
if (generation !== this.profileGeneration)
|
|
103
97
|
return { ...base, models: [] };
|
|
104
|
-
const cached = this.profileCache
|
|
98
|
+
const cached = this.profileCacheVersion === version ? this.profileCache : undefined;
|
|
105
99
|
if (cached !== undefined) {
|
|
106
100
|
cached.retryAt = Date.now() + CODEX_MODEL_FAILURE_RETRY_MS;
|
|
107
101
|
return cached.profile;
|
|
108
102
|
}
|
|
109
|
-
this.cacheProfile(
|
|
103
|
+
this.cacheProfile(version, {
|
|
110
104
|
profile: { ...base, models: [] },
|
|
111
105
|
defaultConfiguration: emptyCodexConfiguration(),
|
|
112
106
|
fetchedAt: 0,
|
|
@@ -115,33 +109,24 @@ export class CodexRunner extends AbstractRunner {
|
|
|
115
109
|
});
|
|
116
110
|
return { ...base, models: [] };
|
|
117
111
|
})
|
|
118
|
-
.finally(() =>
|
|
119
|
-
|
|
112
|
+
.finally(() => {
|
|
113
|
+
if (this.profileRefresh === refresh)
|
|
114
|
+
this.profileRefresh = undefined;
|
|
115
|
+
});
|
|
116
|
+
this.profileRefresh = refresh;
|
|
120
117
|
return refresh;
|
|
121
118
|
}
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.profileCache.set(key, cache);
|
|
132
|
-
const timer = setTimeout(() => {
|
|
133
|
-
this.profileCacheTimers.delete(key);
|
|
134
|
-
this.profileCache.delete(key);
|
|
135
|
-
for (const [environmentKey, profileKey] of this.profileKeyByEnvironment)
|
|
136
|
-
if (profileKey === key)
|
|
137
|
-
this.profileKeyByEnvironment.delete(environmentKey);
|
|
138
|
-
if (this.selectedProfileKey === key) {
|
|
139
|
-
this.selectedProfileKey = undefined;
|
|
140
|
-
this.latestProfileCache = undefined;
|
|
141
|
-
}
|
|
119
|
+
cacheProfile(version, cache) {
|
|
120
|
+
if (this.profileCacheTimer !== undefined)
|
|
121
|
+
clearTimeout(this.profileCacheTimer);
|
|
122
|
+
this.profileCache = cache;
|
|
123
|
+
this.profileCacheVersion = version;
|
|
124
|
+
this.profileCacheTimer = setTimeout(() => {
|
|
125
|
+
this.profileCacheTimer = undefined;
|
|
126
|
+
this.profileCache = undefined;
|
|
127
|
+
this.profileCacheVersion = undefined;
|
|
142
128
|
}, CODEX_PROFILE_ENTRY_TTL_MS);
|
|
143
|
-
|
|
144
|
-
this.profileCacheTimers.set(key, timer);
|
|
129
|
+
this.profileCacheTimer.unref();
|
|
145
130
|
}
|
|
146
131
|
async fetchCodexProfile(base, environment) {
|
|
147
132
|
const client = this.profileClientFactory();
|
|
@@ -306,11 +291,8 @@ export class CodexRunner extends AbstractRunner {
|
|
|
306
291
|
profile.models.some((model) => model.id === configuration.model && model.supportedEfforts.includes(configuration.effort)) &&
|
|
307
292
|
profile.accessOptions.some((option) => option.id === configuration.access));
|
|
308
293
|
}
|
|
309
|
-
profileCacheForEnvironment(
|
|
310
|
-
|
|
311
|
-
return this.latestProfileCache;
|
|
312
|
-
const key = this.profileKeyByEnvironment.get(codexSecretEnvironmentFingerprint(environment));
|
|
313
|
-
return key === undefined ? undefined : this.profileCache.get(key);
|
|
294
|
+
profileCacheForEnvironment(_environment) {
|
|
295
|
+
return this.profileCache;
|
|
314
296
|
}
|
|
315
297
|
async resumeExternal(external, context) {
|
|
316
298
|
if (external.runner !== this.name ||
|
|
@@ -483,13 +465,12 @@ export class CodexRunner extends AbstractRunner {
|
|
|
483
465
|
this.client.clearEnvironment();
|
|
484
466
|
this.environmentFingerprint = undefined;
|
|
485
467
|
this.environmentRuns.clear();
|
|
486
|
-
|
|
487
|
-
clearTimeout(
|
|
488
|
-
this.
|
|
489
|
-
this.profileCache
|
|
490
|
-
this.
|
|
491
|
-
this.
|
|
492
|
-
this.selectedProfileKey = undefined;
|
|
468
|
+
if (this.profileCacheTimer !== undefined)
|
|
469
|
+
clearTimeout(this.profileCacheTimer);
|
|
470
|
+
this.profileCacheTimer = undefined;
|
|
471
|
+
this.profileCache = undefined;
|
|
472
|
+
this.profileCacheVersion = undefined;
|
|
473
|
+
this.profileRefresh = undefined;
|
|
493
474
|
}
|
|
494
475
|
request(method, params) {
|
|
495
476
|
return this.client.request(method, params);
|
|
@@ -537,19 +518,6 @@ export class CodexRunner extends AbstractRunner {
|
|
|
537
518
|
function emptyCodexConfiguration() {
|
|
538
519
|
return { runner: 'codex', model: '', effort: '', access: 'on-request' };
|
|
539
520
|
}
|
|
540
|
-
function codexEnvironmentFingerprint(environment, version) {
|
|
541
|
-
return createHash('sha256')
|
|
542
|
-
.update(JSON.stringify({
|
|
543
|
-
environment: Object.entries(environment).sort(([left], [right]) => left.localeCompare(right)),
|
|
544
|
-
version: version ?? null
|
|
545
|
-
}))
|
|
546
|
-
.digest('base64url');
|
|
547
|
-
}
|
|
548
|
-
function codexSecretEnvironmentFingerprint(environment) {
|
|
549
|
-
return createHash('sha256')
|
|
550
|
-
.update(JSON.stringify(Object.entries(environment).sort(([left], [right]) => left.localeCompare(right))))
|
|
551
|
-
.digest('base64url');
|
|
552
|
-
}
|
|
553
521
|
function codexModelPage(value) {
|
|
554
522
|
if (!isPlainRecord(value) || !Array.isArray(value.data))
|
|
555
523
|
throw new Error('CODEX_MODELS_INVALID');
|
|
@@ -244,6 +244,7 @@ export class MarAgentManagedRunController {
|
|
|
244
244
|
let executionModels = [];
|
|
245
245
|
try {
|
|
246
246
|
const sessionKey = input.externalSessionId ?? input.sessionId;
|
|
247
|
+
// Reserve both identities while opening so a concurrent release can await the same runtime.
|
|
247
248
|
const acquisitionKeys = [...new Set([sessionKey, input.sessionId])];
|
|
248
249
|
if (acquisitionKeys.some((key) => this.#runtimeAcquisitions.get(key) !== undefined))
|
|
249
250
|
throw new Error('MAR_AGENT_SESSION_RUNTIME_ACTIVE');
|
|
@@ -18,9 +18,9 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
18
18
|
discoveryScope = 'WORKSPACE_REQUIRED';
|
|
19
19
|
contextLimits = new Map();
|
|
20
20
|
contextLimitsExpiresAt = 0;
|
|
21
|
-
profileCache
|
|
22
|
-
profileRetryAt =
|
|
23
|
-
|
|
21
|
+
profileCache;
|
|
22
|
+
profileRetryAt = 0;
|
|
23
|
+
profileRefresh;
|
|
24
24
|
profileGeneration = 0;
|
|
25
25
|
execution = new Map();
|
|
26
26
|
managedTurns = new Map();
|
|
@@ -41,7 +41,7 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
41
41
|
}
|
|
42
42
|
cachedProfile(capabilities, environment = {}) {
|
|
43
43
|
this.pruneProfileCaches(Date.now());
|
|
44
|
-
const cached = this.profileCache
|
|
44
|
+
const cached = this.profileCache;
|
|
45
45
|
return cached === undefined
|
|
46
46
|
? this.profile(capabilities)
|
|
47
47
|
: { ...this.profile(capabilities), models: cached.models };
|
|
@@ -50,10 +50,9 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
50
50
|
if (capabilities.openCode?.available !== true) {
|
|
51
51
|
return this.profile(capabilities);
|
|
52
52
|
}
|
|
53
|
-
const cacheKey = this.environmentFingerprint(environment);
|
|
54
53
|
const now = Date.now();
|
|
55
54
|
this.pruneProfileCaches(now);
|
|
56
|
-
const cached = this.profileCache
|
|
55
|
+
const cached = this.profileCache;
|
|
57
56
|
if (cached !== undefined && cached.expiresAt > now) {
|
|
58
57
|
nodeLog('runner.opencode.profile.cache-hit', {
|
|
59
58
|
models: cached.models.length,
|
|
@@ -63,24 +62,24 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
63
62
|
this.contextLimitsExpiresAt = cached.expiresAt;
|
|
64
63
|
return { ...this.profile(capabilities), models: cached.models };
|
|
65
64
|
}
|
|
66
|
-
if (now <
|
|
65
|
+
if (now < this.profileRetryAt)
|
|
67
66
|
return cached === undefined
|
|
68
67
|
? this.profile(capabilities)
|
|
69
68
|
: { ...this.profile(capabilities), models: cached.models };
|
|
70
|
-
const current = this.
|
|
69
|
+
const current = this.profileRefresh;
|
|
71
70
|
if (current !== undefined) {
|
|
72
71
|
nodeLog('runner.opencode.profile.single-flight-joined', {});
|
|
73
72
|
return current;
|
|
74
73
|
}
|
|
75
74
|
const generation = this.profileGeneration;
|
|
76
|
-
const refresh = this.fetchProfile(capabilities, environment,
|
|
77
|
-
if (this.
|
|
78
|
-
this.
|
|
75
|
+
const refresh = this.fetchProfile(capabilities, environment, generation, cached).finally(() => {
|
|
76
|
+
if (this.profileRefresh === refresh)
|
|
77
|
+
this.profileRefresh = undefined;
|
|
79
78
|
});
|
|
80
|
-
this.
|
|
79
|
+
this.profileRefresh = refresh;
|
|
81
80
|
return refresh;
|
|
82
81
|
}
|
|
83
|
-
async fetchProfile(capabilities, environment,
|
|
82
|
+
async fetchProfile(capabilities, environment, generation, stale) {
|
|
84
83
|
const client = this.managedClient(environment, 'runner-profile');
|
|
85
84
|
const startedAt = performance.now();
|
|
86
85
|
try {
|
|
@@ -92,14 +91,13 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
92
91
|
return this.profile(capabilities);
|
|
93
92
|
this.contextLimits = contextLimits;
|
|
94
93
|
this.contextLimitsExpiresAt = expiresAt;
|
|
95
|
-
this.profileCache
|
|
94
|
+
this.profileCache = {
|
|
96
95
|
expiresAt,
|
|
97
96
|
discardAt: Date.now() + PROFILE_STALE_TTL_MS,
|
|
98
|
-
environmentFingerprint: cacheKey,
|
|
99
97
|
models,
|
|
100
98
|
contextLimits
|
|
101
|
-
}
|
|
102
|
-
this.profileRetryAt
|
|
99
|
+
};
|
|
100
|
+
this.profileRetryAt = 0;
|
|
103
101
|
nodeLog('runner.opencode.profile.providers.completed', {
|
|
104
102
|
models: models.length,
|
|
105
103
|
durationMs: Math.round(performance.now() - startedAt)
|
|
@@ -109,7 +107,7 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
109
107
|
catch {
|
|
110
108
|
if (generation !== this.profileGeneration)
|
|
111
109
|
return this.profile(capabilities);
|
|
112
|
-
this.profileRetryAt
|
|
110
|
+
this.profileRetryAt = Date.now() + PROFILE_FAILURE_RETRY_MS;
|
|
113
111
|
nodeLog('runner.opencode.profile.providers.failed', {
|
|
114
112
|
durationMs: Math.round(performance.now() - startedAt)
|
|
115
113
|
});
|
|
@@ -121,10 +119,6 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
121
119
|
? this.profile(capabilities)
|
|
122
120
|
: { ...this.profile(capabilities), models: stale.models };
|
|
123
121
|
}
|
|
124
|
-
environmentFingerprint(environment) {
|
|
125
|
-
const entries = Object.entries(environment).sort(([left], [right]) => left.localeCompare(right));
|
|
126
|
-
return createHash('sha256').update(JSON.stringify(entries)).digest('hex');
|
|
127
|
-
}
|
|
128
122
|
available(capabilities) {
|
|
129
123
|
return capabilities.openCode?.available === true;
|
|
130
124
|
}
|
|
@@ -160,21 +154,16 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
160
154
|
}
|
|
161
155
|
cachedModels(environment) {
|
|
162
156
|
this.pruneProfileCaches(Date.now());
|
|
163
|
-
|
|
164
|
-
return
|
|
165
|
-
.filter((entry) => entry.environmentFingerprint === fingerprint)
|
|
166
|
-
.flatMap((entry) => entry.models);
|
|
157
|
+
void environment;
|
|
158
|
+
return this.profileCache?.models ?? [];
|
|
167
159
|
}
|
|
168
160
|
pruneProfileCaches(now) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
this.profileCache
|
|
173
|
-
this.
|
|
174
|
-
|
|
175
|
-
for (const [key, retryAt] of this.profileRetryAt)
|
|
176
|
-
if (retryAt <= now && !this.profileCache.has(key) && !this.profileRefreshes.has(key))
|
|
177
|
-
this.profileRetryAt.delete(key);
|
|
161
|
+
if (this.profileCache !== undefined && this.profileCache.discardAt <= now)
|
|
162
|
+
this.profileCache = undefined;
|
|
163
|
+
if (this.profileRetryAt <= now &&
|
|
164
|
+
this.profileCache === undefined &&
|
|
165
|
+
this.profileRefresh === undefined)
|
|
166
|
+
this.profileRetryAt = 0;
|
|
178
167
|
}
|
|
179
168
|
mcpConfiguration(raw, secrets) {
|
|
180
169
|
const servers = super.mcpConfiguration(raw, secrets);
|
|
@@ -340,9 +329,9 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
340
329
|
execution.abort.abort();
|
|
341
330
|
this.execution.clear();
|
|
342
331
|
this.managedTurns.clear();
|
|
343
|
-
this.profileCache
|
|
344
|
-
this.
|
|
345
|
-
this.profileRetryAt
|
|
332
|
+
this.profileCache = undefined;
|
|
333
|
+
this.profileRefresh = undefined;
|
|
334
|
+
this.profileRetryAt = 0;
|
|
346
335
|
this.contextLimits = new Map();
|
|
347
336
|
this.contextLimitsExpiresAt = 0;
|
|
348
337
|
const stopping = [
|
|
@@ -41,6 +41,14 @@ export class RunnerRegistry {
|
|
|
41
41
|
try {
|
|
42
42
|
return await runner.refreshProfile(capabilities, workspace, environment, waitForRefresh);
|
|
43
43
|
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
nodeLog('runner.profile.refresh.failed', {
|
|
46
|
+
runner: runner.name,
|
|
47
|
+
workspaceId: workspace?.id,
|
|
48
|
+
error: error instanceof Error ? error.message : String(error)
|
|
49
|
+
});
|
|
50
|
+
return runner.cachedProfile(capabilities, environment);
|
|
51
|
+
}
|
|
44
52
|
finally {
|
|
45
53
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
46
54
|
if (durationMs >= 100)
|
|
@@ -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.121",
|
|
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/
|
|
34
|
-
"@myagentroam/
|
|
35
|
-
"@myagentroam/
|
|
33
|
+
"@myagentroam/agent": "0.9.121",
|
|
34
|
+
"@myagentroam/code-navigation": "0.9.121",
|
|
35
|
+
"@myagentroam/protocol": "0.9.121"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/ws": "^8.18.1"
|