@myagentroam/node 0.9.115 → 0.9.116

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.
@@ -4,6 +4,7 @@ import { declaredRunnerProfiles } from '../runner-profiles.js';
4
4
  import { AbstractRunner } from './abstract-runner.js';
5
5
  import { isWithinWorkspace } from './codex/conversation-parser.js';
6
6
  import { createHash } from 'node:crypto';
7
+ import { homedir } from 'node:os';
7
8
  import { openCodeConversationPage } from './opencode/conversation-parser.js';
8
9
  import { nodeLog } from '../operational.js';
9
10
  const PROFILE_ENTRY_TTL_MS = 30 * 60_000;
@@ -12,9 +13,8 @@ export class OpenCodeRunner extends AbstractRunner {
12
13
  clientFactory;
13
14
  name = 'opencode';
14
15
  discoveryScope = 'WORKSPACE_REQUIRED';
15
- modelsByWorkspace = new Map();
16
- contextLimitsByWorkspace = new Map();
17
- profileTimersByWorkspace = new Map();
16
+ contextLimits = new Map();
17
+ contextLimitsExpiresAt = 0;
18
18
  profileCache = new Map();
19
19
  profileRefreshes = new Map();
20
20
  execution = new Map();
@@ -34,11 +34,11 @@ export class OpenCodeRunner extends AbstractRunner {
34
34
  }).find((profile) => profile.runner === this.name);
35
35
  return { ...base, models: [] };
36
36
  }
37
- async refreshProfile(capabilities, workspace, environment = {}) {
38
- if (capabilities.openCode?.available !== true || workspace === undefined) {
37
+ async refreshProfile(capabilities, _workspace, environment = {}) {
38
+ if (capabilities.openCode?.available !== true) {
39
39
  return this.profile(capabilities);
40
40
  }
41
- const cacheKey = this.profileCacheKey(workspace.path, environment);
41
+ const cacheKey = this.environmentFingerprint(environment);
42
42
  const now = Date.now();
43
43
  for (const [key, entry] of this.profileCache)
44
44
  if (entry.expiresAt <= now)
@@ -46,46 +46,42 @@ export class OpenCodeRunner extends AbstractRunner {
46
46
  const cached = this.profileCache.get(cacheKey);
47
47
  if (cached !== undefined) {
48
48
  nodeLog('runner.opencode.profile.cache-hit', {
49
- workspaceId: workspace.id,
50
49
  models: cached.models.length,
51
50
  remainingTtlMs: cached.expiresAt - now
52
51
  });
53
- this.modelsByWorkspace.set(workspace.path, cached.models);
54
- this.contextLimitsByWorkspace.set(workspace.path, cached.contextLimits);
55
- this.scheduleProfileExpiry(workspace.path);
52
+ this.contextLimits = cached.contextLimits;
53
+ this.contextLimitsExpiresAt = cached.expiresAt;
56
54
  return { ...this.profile(capabilities), models: cached.models };
57
55
  }
58
56
  const current = this.profileRefreshes.get(cacheKey);
59
57
  if (current !== undefined) {
60
- nodeLog('runner.opencode.profile.single-flight-joined', { workspaceId: workspace.id });
58
+ nodeLog('runner.opencode.profile.single-flight-joined', {});
61
59
  return current;
62
60
  }
63
- const refresh = this.fetchProfile(capabilities, workspace, environment, cacheKey).finally(() => {
61
+ const refresh = this.fetchProfile(capabilities, environment, cacheKey).finally(() => {
64
62
  if (this.profileRefreshes.get(cacheKey) === refresh)
65
63
  this.profileRefreshes.delete(cacheKey);
66
64
  });
67
65
  this.profileRefreshes.set(cacheKey, refresh);
68
66
  return refresh;
69
67
  }
70
- async fetchProfile(capabilities, workspace, environment, cacheKey) {
68
+ async fetchProfile(capabilities, environment, cacheKey) {
71
69
  const client = this.managedClient(environment);
72
70
  const startedAt = performance.now();
73
71
  try {
74
- const catalog = await client.providers(workspace.path);
72
+ const catalog = await client.providers(homedir());
75
73
  const models = openCodeProfileModels(catalog);
76
74
  const contextLimits = openCodeContextLimits(catalog);
77
- this.modelsByWorkspace.set(workspace.path, models);
78
- this.contextLimitsByWorkspace.set(workspace.path, contextLimits);
75
+ const expiresAt = Date.now() + PROFILE_ENTRY_TTL_MS;
76
+ this.contextLimits = contextLimits;
77
+ this.contextLimitsExpiresAt = expiresAt;
79
78
  this.profileCache.set(cacheKey, {
80
- expiresAt: Date.now() + PROFILE_ENTRY_TTL_MS,
81
- workspacePath: workspace.path,
82
- environmentFingerprint: this.environmentFingerprint(environment),
79
+ expiresAt,
80
+ environmentFingerprint: cacheKey,
83
81
  models,
84
82
  contextLimits
85
83
  });
86
- this.scheduleProfileExpiry(workspace.path);
87
84
  nodeLog('runner.opencode.profile.providers.completed', {
88
- workspaceId: workspace.id,
89
85
  models: models.length,
90
86
  durationMs: Math.round(performance.now() - startedAt)
91
87
  });
@@ -93,7 +89,6 @@ export class OpenCodeRunner extends AbstractRunner {
93
89
  }
94
90
  catch {
95
91
  nodeLog('runner.opencode.profile.providers.failed', {
96
- workspaceId: workspace.id,
97
92
  durationMs: Math.round(performance.now() - startedAt)
98
93
  });
99
94
  }
@@ -102,11 +97,6 @@ export class OpenCodeRunner extends AbstractRunner {
102
97
  }
103
98
  return this.profile(capabilities);
104
99
  }
105
- profileCacheKey(workspacePath, environment) {
106
- return createHash('sha256')
107
- .update(JSON.stringify({ workspacePath, environment: this.environmentFingerprint(environment) }))
108
- .digest('hex');
109
- }
110
100
  environmentFingerprint(environment) {
111
101
  const entries = Object.entries(environment).sort(([left], [right]) => left.localeCompare(right));
112
102
  return createHash('sha256').update(JSON.stringify(entries)).digest('hex');
@@ -125,18 +115,18 @@ export class OpenCodeRunner extends AbstractRunner {
125
115
  openCode: { available: true }
126
116
  });
127
117
  }
128
- supportsConfiguration(configuration, workspace, environment = {}) {
118
+ supportsConfiguration(configuration, _workspace, environment = {}) {
129
119
  if (configuration.runner !== this.name)
130
120
  return false;
131
- const models = this.cachedModels(workspace, environment);
121
+ const models = this.cachedModels(environment);
132
122
  const model = models.find((candidate) => candidate.id === configuration.model);
133
123
  return (model !== undefined &&
134
124
  (model.supportedEfforts.length === 0 ||
135
125
  model.supportedEfforts.includes(configuration.effort)) &&
136
126
  this.profileForValidation().accessOptions.some((candidate) => candidate.id === configuration.access));
137
127
  }
138
- defaultConfiguration(workspace, environment = {}) {
139
- const model = this.cachedModels(workspace, environment)[0];
128
+ defaultConfiguration(_workspace, environment = {}) {
129
+ const model = this.cachedModels(environment)[0];
140
130
  return {
141
131
  runner: this.name,
142
132
  model: model?.id ?? '',
@@ -144,13 +134,11 @@ export class OpenCodeRunner extends AbstractRunner {
144
134
  access: 'default'
145
135
  };
146
136
  }
147
- cachedModels(workspace, environment) {
137
+ cachedModels(environment) {
148
138
  const fingerprint = this.environmentFingerprint(environment);
149
139
  const now = Date.now();
150
140
  return [...this.profileCache.values()]
151
- .filter((entry) => entry.expiresAt > now &&
152
- entry.environmentFingerprint === fingerprint &&
153
- (workspace === undefined || entry.workspacePath === workspace.path))
141
+ .filter((entry) => entry.expiresAt > now && entry.environmentFingerprint === fingerprint)
154
142
  .flatMap((entry) => entry.models);
155
143
  }
156
144
  mcpConfiguration(raw, secrets) {
@@ -306,24 +294,20 @@ export class OpenCodeRunner extends AbstractRunner {
306
294
  const cache = object(tokens?.cache);
307
295
  const usedTokens = positiveNumber(tokens?.input) + positiveNumber(cache?.read);
308
296
  const modelId = provider !== undefined && model !== undefined ? `${provider}/${model}` : session.model;
309
- const limits = [...this.contextLimitsByWorkspace].find(([workspacePath]) => isWithinWorkspace(session.cwd, workspacePath))?.[1];
310
- const maxTokens = modelId === null ? undefined : limits?.get(modelId);
311
- return maxTokens === undefined || usedTokens <= 0
312
- ? undefined
313
- : { model: modelId, usedTokens, maxTokens };
297
+ if (modelId === null || usedTokens <= 0)
298
+ return undefined;
299
+ const maxTokens = Date.now() >= this.contextLimitsExpiresAt ? undefined : this.contextLimits.get(modelId);
300
+ return maxTokens === undefined ? undefined : { model: modelId, usedTokens, maxTokens };
314
301
  }
315
302
  async stop() {
316
303
  for (const execution of this.execution.values())
317
304
  execution.abort.abort();
318
305
  this.execution.clear();
319
306
  this.managedTurns.clear();
320
- for (const timer of this.profileTimersByWorkspace.values())
321
- clearTimeout(timer);
322
- this.profileTimersByWorkspace.clear();
323
307
  this.profileCache.clear();
324
308
  this.profileRefreshes.clear();
325
- this.modelsByWorkspace.clear();
326
- this.contextLimitsByWorkspace.clear();
309
+ this.contextLimits = new Map();
310
+ this.contextLimitsExpiresAt = 0;
327
311
  const stopping = [
328
312
  this.client.stop(),
329
313
  ...[...this.environmentClients.values()].map(({ client }) => client.stop()),
@@ -333,22 +317,6 @@ export class OpenCodeRunner extends AbstractRunner {
333
317
  await Promise.allSettled(stopping);
334
318
  this.stoppingClients.clear();
335
319
  }
336
- scheduleProfileExpiry(workspacePath) {
337
- this.clearProfileTimer(workspacePath);
338
- const timer = setTimeout(() => {
339
- this.profileTimersByWorkspace.delete(workspacePath);
340
- this.modelsByWorkspace.delete(workspacePath);
341
- this.contextLimitsByWorkspace.delete(workspacePath);
342
- }, PROFILE_ENTRY_TTL_MS);
343
- timer.unref();
344
- this.profileTimersByWorkspace.set(workspacePath, timer);
345
- }
346
- clearProfileTimer(workspacePath) {
347
- const timer = this.profileTimersByWorkspace.get(workspacePath);
348
- if (timer !== undefined)
349
- clearTimeout(timer);
350
- this.profileTimersByWorkspace.delete(workspacePath);
351
- }
352
320
  managedClient(environment, isolationKey) {
353
321
  const entries = Object.entries(environment).sort(([left], [right]) => left.localeCompare(right));
354
322
  if (entries.length === 0 && isolationKey === undefined)
@@ -22,11 +22,8 @@ export class RunnerService {
22
22
  profiles() {
23
23
  return this.runners.profiles(this.capabilities());
24
24
  }
25
- refreshProfiles(workspaceId, environment = {}) {
26
- const workspace = workspaceId === undefined ? undefined : this.database().getWorkspace(workspaceId);
27
- if (workspaceId !== undefined && workspace === undefined)
28
- throw new Error('WORKSPACE_NOT_FOUND');
29
- return this.runners.refreshProfiles(this.capabilities(), workspace, environment);
25
+ refreshProfiles(environment = {}) {
26
+ return this.runners.refreshProfiles(this.capabilities(), undefined, environment);
30
27
  }
31
28
  async usageStatuses() {
32
29
  return {
@@ -51,9 +48,6 @@ export class RunnerService {
51
48
  'configureModels' in marAgent &&
52
49
  typeof marAgent.configureModels === 'function')
53
50
  marAgent.configureModels(input.marAgentModels);
54
- const workspaceId = typeof input.workspaceId === 'string' && input.workspaceId.length > 0
55
- ? input.workspaceId
56
- : undefined;
57
51
  const environment = parseSecretEnvironment(input.secretEnvironment);
58
52
  const defaults = this.database()
59
53
  .getRunnerDefaults()
@@ -68,7 +62,7 @@ export class RunnerService {
68
62
  return normalized;
69
63
  });
70
64
  return {
71
- profiles: await this.refreshProfiles(workspaceId, environment),
65
+ profiles: await this.refreshProfiles(environment),
72
66
  defaults
73
67
  };
74
68
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.115",
3
+ "version": "0.9.116",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -30,9 +30,9 @@
30
30
  "npm": "11.12.1",
31
31
  "ws": "^8.21.3",
32
32
  "zod": "4.4.3",
33
- "@myagentroam/agent": "0.9.115",
34
- "@myagentroam/protocol": "0.9.115",
35
- "@myagentroam/code-navigation": "0.9.115"
33
+ "@myagentroam/protocol": "0.9.116",
34
+ "@myagentroam/agent": "0.9.116",
35
+ "@myagentroam/code-navigation": "0.9.116"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/ws": "^8.18.1"