@myagentroam/node 0.9.148 → 0.9.150

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 CHANGED
@@ -2157,7 +2157,7 @@ export class NodeConnector {
2157
2157
  if (isPlainRecord(payload) && isPlainRecord(payload.session)) {
2158
2158
  const workspaceId = payload.session.workspaceId;
2159
2159
  if (typeof workspaceId === 'string') {
2160
- this.workspaceSessionService.invalidateCache(workspaceId);
2160
+ this.workspaceSessionService.markCacheStale(workspaceId);
2161
2161
  void this.workspaceWorkbenchService?.invalidateTopic(workspaceId, 'sessions');
2162
2162
  }
2163
2163
  }
@@ -8,6 +8,7 @@ For text-to-image, provide prompt and omit both image selectors.
8
8
  For image-to-image, pass referenced_image_paths with existing absolute image paths, or use num_last_images_to_include for the smallest sufficient suffix of current Execution images. Never combine selectors. Do not invent missing references.
9
9
  Describe which reference provides the subject, composition or style; specify what to change and what to preserve. Preserve literal requested text.
10
10
  The service returns exactly one PNG with 2K size and no watermark. Group generation, video and streaming are not supported. Do not invent count, sequential, endpoint or model parameters.
11
+ In Code Mode, call tools.byted_ark_image_gen. Independent single-image calls can run with Promise.all; await earlier results when later images depend on them.
11
12
 
12
13
  Inspect references with view_image when that tool is available. Text-only models can submit references through the Host boundary but must not claim to have visually inspected them.
13
14
  Generation may take several minutes. Await the result; do not blindly retry timeouts, since the upstream may already have produced an image.
@@ -19,6 +19,7 @@ Use codex_image_gen only when the user requests image generation or editing, or
19
19
 
20
20
  ### Generate and check
21
21
  - Call codex_image_gen with its actual schema. It produces one PNG per call; use separate calls for separately requested assets. Do not add unsupported parameters for model, dimensions, quality, masks, output format, or destination.
22
+ - In Code Mode, call tools.codex_image_gen. Independent calls can run with Promise.all; await earlier results when later images depend on them.
22
23
  - The service configuration controls model and automatic size/quality/background. A prompt can request an aspect ratio or transparency, but these are not guaranteed API settings. Check the actual result before claiming compliance.
23
24
  - Generation may take minutes. Await the tool result; do not blindly repeat a timed-out or interrupted request because the upstream operation may already have run. Report failures rather than claiming an artifact exists.
24
25
  - A successful result returns the saved path, not inspected pixels. When view_image is available, use it for visual acceptance: subject, exact text, composition, edit invariants, and unwanted elements. Otherwise explicitly state that visual acceptance was not performed. Make targeted corrections only within the requested scope.
@@ -68,7 +68,8 @@ export class MarAgentConversationNormalizer {
68
68
  if (event.type === 'tool.started' ||
69
69
  event.type === 'tool.completed' ||
70
70
  event.type === 'tool.failed') {
71
- if (event.parentCallId !== undefined)
71
+ // Generated artifacts must remain deliverable even when the cell does not observe them.
72
+ if (event.parentCallId !== undefined && !isImageGenerationTool(event.toolName))
72
73
  return [];
73
74
  if (event.type === 'tool.started')
74
75
  return this.#toolStarted(event);
@@ -593,9 +594,7 @@ function genericToolItem(id, toolName, status, input, outputSummary = null, erro
593
594
  }, status);
594
595
  }
595
596
  const subagent = marAgentSubagentProjection(toolName, input, output, subagentTitle, knownSubagentDetails);
596
- const toolCategory = ['codex_image_gen', 'byted_ark_image_gen'].includes(toolName)
597
- ? 'IMAGE_GENERATION'
598
- : undefined;
597
+ const toolCategory = isImageGenerationTool(toolName) ? 'IMAGE_GENERATION' : undefined;
599
598
  return {
600
599
  itemId: id,
601
600
  kind: 'tool_call',
@@ -937,9 +936,11 @@ function boundedId(value) {
937
936
  function nonnegativeInteger(value) {
938
937
  return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : null;
939
938
  }
939
+ function isImageGenerationTool(toolName) {
940
+ return toolName === 'codex_image_gen' || toolName === 'byted_ark_image_gen';
941
+ }
940
942
  function imageArtifacts(event) {
941
- if (!['codex_image_gen', 'byted_ark_image_gen'].includes(event.toolName) ||
942
- !Array.isArray(event.artifacts))
943
+ if (!isImageGenerationTool(event.toolName) || !Array.isArray(event.artifacts))
943
944
  return [];
944
945
  return event.artifacts.flatMap((value) => {
945
946
  const artifact = record(value);
@@ -152,8 +152,6 @@ export class MarAgentHostedConversationRuntime {
152
152
  await input.onEvent?.(projected);
153
153
  }
154
154
  const result = await execution.result();
155
- if (result.status === 'cancelled')
156
- titleController.abort();
157
155
  const generatedTitle = await titleTask;
158
156
  if (generatedTitle && !titleController.signal.aborted)
159
157
  await agent
@@ -203,25 +201,31 @@ export class MarAgentHostedConversationRuntime {
203
201
  cancel: () => {
204
202
  if (settled || this.#active.get(input.sessionId)?.handle !== handle)
205
203
  return false;
206
- titleController.abort();
207
204
  execution.cancel();
208
205
  return true;
209
206
  }
210
207
  };
211
- this.#active.set(input.sessionId, { handle, completion });
208
+ this.#active.set(input.sessionId, {
209
+ handle,
210
+ completion,
211
+ cancelTitle: () => titleController.abort()
212
+ });
212
213
  return handle;
213
214
  }
214
215
  async releaseSession(input) {
215
216
  const active = this.#active.get(input.sessionId);
216
217
  if (active === undefined)
217
218
  return;
219
+ active.cancelTitle();
218
220
  active.handle.cancel();
219
221
  await active.completion.catch(() => undefined);
220
222
  }
221
223
  async dispose() {
222
224
  const active = [...this.#active.values()];
223
- for (const execution of active)
225
+ for (const execution of active) {
226
+ execution.cancelTitle();
224
227
  execution.handle.cancel();
228
+ }
225
229
  await Promise.allSettled(active.map((execution) => execution.completion));
226
230
  }
227
231
  async #readGeneratedTitle(homeDirectory, sessionId) {
@@ -23,6 +23,8 @@ const IMAGE_GENERATION_RESPONSE_TIMEOUT_MS = 5 * 60 * 1000;
23
23
  export const codexImageGenToolDefinition = {
24
24
  name: 'codex_image_gen',
25
25
  agentScope: 'ROOT_ONLY',
26
+ allowedCallers: ['direct', 'programmatic'],
27
+ parallelSafety: 'safe',
26
28
  description: 'Only when the user requests or explicitly authorizes image generation or editing, use the configured Codex Images API. First load and follow skill({name:"codex-imagegen"}); reload if its guidance is no longer in context. Do not generate unsolicited visuals. Omit both selectors for a new image without references. For image inputs, inspect unseen local images with view_image when available; TEXT-only models must not claim visual inspection. Use referenced_image_paths when every image has an absolute path; otherwise use num_last_images_to_include for the smallest sufficient number of currently available conversation images. Never provide both selectors. Include at most five images; if required inputs are missing, ask for them rather than silently omitting them. Recent images are in-memory in the current Execution, not a permanent history library. One PNG is saved in the current Session and its path is returned, not inspected pixels. Use view_image when available to check the result; otherwise report that visual acceptance was not performed. Full workflow guidance is in the codex-imagegen skill.',
27
29
  inputSchema: {
28
30
  type: 'object',
@@ -52,11 +52,15 @@ export class MarAgentManagedRunController {
52
52
  return this.#disposePromise;
53
53
  this.#disposed = true;
54
54
  this.#disposePromise = (async () => {
55
- for (const execution of this.#executions.values())
55
+ for (const execution of this.#executions.values()) {
56
+ execution.cancelTitle();
56
57
  execution.cancel();
58
+ }
57
59
  await Promise.allSettled([...new Set(this.#runtimeAcquisitions.values())]);
58
- for (const execution of this.#executions.values())
60
+ for (const execution of this.#executions.values()) {
61
+ execution.cancelTitle();
59
62
  execution.cancel();
63
+ }
60
64
  await Promise.all([...new Set(this.#sessionRuntimes.values())].map((runtime) => this.#releaseRuntime(runtime)));
61
65
  await this.#mcpClients.close();
62
66
  await this.#computerUse?.dispose();
@@ -74,8 +78,11 @@ export class MarAgentManagedRunController {
74
78
  await this.#computerUse?.releaseSession(sessionId);
75
79
  return;
76
80
  }
77
- if (runtime.activeRunId !== undefined)
78
- this.#executions.get(runtime.activeRunId)?.cancel();
81
+ if (runtime.activeRunId !== undefined) {
82
+ const execution = this.#executions.get(runtime.activeRunId);
83
+ execution?.cancelTitle();
84
+ execution?.cancel();
85
+ }
79
86
  await this.#releaseRuntime(runtime);
80
87
  await Promise.all([
81
88
  this.#computerUse?.releaseSession(sessionId),
@@ -379,10 +386,8 @@ export class MarAgentManagedRunController {
379
386
  ? session.compact(runInput)
380
387
  : session.run(runInput);
381
388
  this.#executions.set(input.runId, {
382
- cancel: () => {
383
- titleController.abort();
384
- handle.cancel();
385
- }
389
+ cancel: () => handle.cancel(),
390
+ cancelTitle: () => titleController.abort()
386
391
  });
387
392
  this.host.emit(input.runId, 'run.running', {}, 'RUNNING');
388
393
  for await (const event of handle) {
@@ -398,8 +403,6 @@ export class MarAgentManagedRunController {
398
403
  this.project(input.runId, event);
399
404
  }
400
405
  const result = await handle.result();
401
- if (result.status === 'cancelled')
402
- titleController.abort();
403
406
  const title = await titleTask;
404
407
  if (title && !titleController.signal.aborted)
405
408
  await runtime.agent
@@ -7,6 +7,7 @@ export class WorkspaceSessionService {
7
7
  cacheGenerations = new Map();
8
8
  initialReads = new Map();
9
9
  incompleteDiscoveries = new Set();
10
+ staleDiscoveries = new Set();
10
11
  constructor(state, options) {
11
12
  this.state = state;
12
13
  this.options = options;
@@ -56,7 +57,7 @@ export class WorkspaceSessionService {
56
57
  const previous = this.state.sessionCache.get(workspaceId)?.sessions;
57
58
  this.state.sessionCache.set(workspaceId, {
58
59
  sessions,
59
- expiresAt: Date.now() + CACHE_TTL_MS
60
+ expiresAt: this.staleDiscoveries.has(workspaceId) ? 0 : Date.now() + CACHE_TTL_MS
60
61
  });
61
62
  if (notify &&
62
63
  previous !== undefined &&
@@ -102,8 +103,12 @@ export class WorkspaceSessionService {
102
103
  })
103
104
  .finally(() => {
104
105
  acceptingBatches = false;
105
- if (this.state.sessionCacheReads.get(workspaceId) === read)
106
+ if (this.state.sessionCacheReads.get(workspaceId) === read) {
106
107
  this.state.sessionCacheReads.delete(workspaceId);
108
+ // Events during discovery require a trailing refresh, even if its result is unchanged.
109
+ if (this.staleDiscoveries.delete(workspaceId))
110
+ notifyCompletion = true;
111
+ }
107
112
  if (this.initialReads.get(workspaceId) === initial)
108
113
  this.initialReads.delete(workspaceId);
109
114
  if (notifyCompletion && (this.cacheGenerations.get(workspaceId) ?? 0) === generation)
@@ -137,17 +142,26 @@ export class WorkspaceSessionService {
137
142
  this.state.sessionCacheReads.clear();
138
143
  this.initialReads.clear();
139
144
  this.incompleteDiscoveries.clear();
145
+ this.staleDiscoveries.clear();
140
146
  }
141
147
  invalidate(workspaceId) {
142
148
  this.invalidateCache(workspaceId);
143
149
  this.stopWatch(workspaceId);
144
150
  }
151
+ markCacheStale(workspaceId) {
152
+ const cached = this.state.sessionCache.get(workspaceId);
153
+ if (cached !== undefined)
154
+ this.state.sessionCache.set(workspaceId, { ...cached, expiresAt: 0 });
155
+ if (this.state.sessionCacheReads.has(workspaceId))
156
+ this.staleDiscoveries.add(workspaceId);
157
+ }
145
158
  invalidateCache(workspaceId) {
146
159
  this.bumpCacheGeneration(workspaceId);
147
160
  this.state.sessionCache.delete(workspaceId);
148
161
  this.state.sessionCacheReads.delete(workspaceId);
149
162
  this.initialReads.delete(workspaceId);
150
163
  this.incompleteDiscoveries.delete(workspaceId);
164
+ this.staleDiscoveries.delete(workspaceId);
151
165
  }
152
166
  bumpCacheGeneration(workspaceId) {
153
167
  this.cacheGenerations.set(workspaceId, (this.cacheGenerations.get(workspaceId) ?? 0) + 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.148",
3
+ "version": "0.9.150",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -35,9 +35,9 @@
35
35
  "ws": "^8.21.3",
36
36
  "zod": "4.4.3",
37
37
  "@myagentroam/marctl": "0.9.120",
38
+ "@myagentroam/agent": "0.9.150",
38
39
  "@myagentroam/code-navigation": "0.9.129",
39
- "@myagentroam/agent": "0.9.148",
40
- "@myagentroam/protocol": "0.9.148"
40
+ "@myagentroam/protocol": "0.9.150"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/ws": "^8.18.1"