@myagentroam/node 0.9.119 → 0.9.120

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.
@@ -24,13 +24,11 @@ export class CodexRunner extends AbstractRunner {
24
24
  fastEnabled = false;
25
25
  environmentFingerprint;
26
26
  environmentRuns = new Set();
27
- profileCache = new Map();
28
- profileCacheTimers = new Map();
29
- profileRefreshes = new Map();
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.latestProfileCache?.profile.models ?? [] };
44
+ return { ...base, models: this.profileCache?.profile.models ?? [] };
47
45
  }
48
46
  cachedProfile(capabilities, environment = {}) {
49
47
  const base = this.profile(capabilities);
50
- const key = codexEnvironmentFingerprint(environment, capabilities.codex.version);
51
- const cached = this.profileCache.get(key);
52
- return cached === undefined ? { ...base, models: [] } : cached.profile;
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 key = codexEnvironmentFingerprint(environment, capabilities.codex.version);
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.get(key);
60
+ const cached = this.profileCacheVersion === version ? this.profileCache : undefined;
64
61
  if (cached?.successful === true && now < cached.fetchedAt + CODEX_MODEL_CACHE_MS)
65
- return this.selectCachedProfile(key, cached);
62
+ return cached.profile;
66
63
  if (cached?.successful === true) {
67
64
  if (now >= cached.retryAt) {
68
- const refresh = this.refreshCodexProfile(key, base, environment);
65
+ const refresh = this.refreshCodexProfile(version, base, environment);
69
66
  if (waitForRefresh)
70
67
  return refresh;
71
68
  }
72
- return this.selectCachedProfile(key, cached);
69
+ return cached.profile;
73
70
  }
74
71
  if (cached !== undefined && now < cached.retryAt)
75
72
  return cached.profile;
76
- return this.refreshCodexProfile(key, base, environment);
73
+ return this.refreshCodexProfile(version, base, environment);
77
74
  }
78
- refreshCodexProfile(key, base, environment) {
79
- const existing = this.profileRefreshes.get(key);
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(key, cache);
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.get(key);
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(key, {
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(() => this.profileRefreshes.delete(key));
119
- this.profileRefreshes.set(key, refresh);
112
+ .finally(() => {
113
+ if (this.profileRefresh === refresh)
114
+ this.profileRefresh = undefined;
115
+ });
116
+ this.profileRefresh = refresh;
120
117
  return refresh;
121
118
  }
122
- selectCachedProfile(key, cached) {
123
- if (cached.successful && this.selectedProfileKey === key)
124
- this.latestProfileCache = cached;
125
- return cached.profile;
126
- }
127
- cacheProfile(key, cache) {
128
- const previousTimer = this.profileCacheTimers.get(key);
129
- if (previousTimer !== undefined)
130
- clearTimeout(previousTimer);
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
- timer.unref();
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(environment) {
310
- if (environment === undefined)
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
- for (const timer of this.profileCacheTimers.values())
487
- clearTimeout(timer);
488
- this.profileCacheTimers.clear();
489
- this.profileCache.clear();
490
- this.profileKeyByEnvironment.clear();
491
- this.latestProfileCache = undefined;
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');
@@ -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 = new Map();
22
- profileRetryAt = new Map();
23
- profileRefreshes = new Map();
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.get(this.environmentFingerprint(environment));
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.get(cacheKey);
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 < (this.profileRetryAt.get(cacheKey) ?? 0))
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.profileRefreshes.get(cacheKey);
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, cacheKey, generation, cached).finally(() => {
77
- if (this.profileRefreshes.get(cacheKey) === refresh)
78
- this.profileRefreshes.delete(cacheKey);
75
+ const refresh = this.fetchProfile(capabilities, environment, generation, cached).finally(() => {
76
+ if (this.profileRefresh === refresh)
77
+ this.profileRefresh = undefined;
79
78
  });
80
- this.profileRefreshes.set(cacheKey, refresh);
79
+ this.profileRefresh = refresh;
81
80
  return refresh;
82
81
  }
83
- async fetchProfile(capabilities, environment, cacheKey, generation, stale) {
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.set(cacheKey, {
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.delete(cacheKey);
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.set(cacheKey, Date.now() + PROFILE_FAILURE_RETRY_MS);
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
- const fingerprint = this.environmentFingerprint(environment);
164
- return [...this.profileCache.values()]
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
- for (const [key, entry] of this.profileCache) {
170
- if (entry.discardAt > now)
171
- continue;
172
- this.profileCache.delete(key);
173
- this.profileRetryAt.delete(key);
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.clear();
344
- this.profileRefreshes.clear();
345
- this.profileRetryAt.clear();
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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.119",
3
+ "version": "0.9.120",
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/protocol": "0.9.119",
34
- "@myagentroam/agent": "0.9.119",
35
- "@myagentroam/code-navigation": "0.9.119"
33
+ "@myagentroam/agent": "0.9.120",
34
+ "@myagentroam/code-navigation": "0.9.120",
35
+ "@myagentroam/protocol": "0.9.120"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/ws": "^8.18.1"