@myagentroam/node 0.9.102 → 0.9.104

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.
Files changed (60) hide show
  1. package/dist/computer-use-runtime-version.js +1 -1
  2. package/dist/computer-use-runtime.js +19 -2
  3. package/dist/connector.js +2 -3
  4. package/dist/ctags-binary.js +6 -3
  5. package/dist/runner/mar-agent/byted-ark-imagegen-skill.js +18 -0
  6. package/dist/runner/mar-agent/codex-imagegen-skill.js +35 -0
  7. package/dist/runner/mar-agent/computer-use-skill.js +37 -0
  8. package/dist/runner/mar-agent/hosted-conversation-runtime.js +41 -21
  9. package/dist/runner/mar-agent/image-generation-extensions.js +59 -0
  10. package/dist/runner/mar-agent/image-generation-validation.js +50 -0
  11. package/dist/runner/mar-agent/image-generation.js +337 -0
  12. package/dist/runner/mar-agent/managed-run-controller.js +40 -27
  13. package/dist/runner/mar-agent/runtime-model-factory.js +11 -0
  14. package/dist/service/node-run-event-bridge.js +5 -1
  15. package/dist/service/session-message-service.js +0 -4
  16. package/dist/service/workspace-upload-service.js +2 -0
  17. package/package.json +4 -4
  18. package/vendor/ctags/NOTICE +19 -7
  19. package/vendor/ctags/darwin-arm64/ctags +0 -0
  20. package/vendor/ctags/darwin-x64/ctags +0 -0
  21. package/vendor/ctags/licenses/official-snapshot/darwin-arm64/COPYING +340 -0
  22. package/vendor/ctags/licenses/official-snapshot/darwin-arm64/RECEIPT.yml +27 -0
  23. package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libiconv.yml +24 -0
  24. package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libjansson.yml +19 -0
  25. package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libxml2.yml +20 -0
  26. package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libyaml.yml +7 -0
  27. package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libz.yml +13 -0
  28. package/vendor/ctags/licenses/official-snapshot/darwin-x64/COPYING +340 -0
  29. package/vendor/ctags/licenses/official-snapshot/darwin-x64/RECEIPT.yml +27 -0
  30. package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libiconv.yml +24 -0
  31. package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libjansson.yml +19 -0
  32. package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libxml2.yml +20 -0
  33. package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libyaml.yml +7 -0
  34. package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libz.yml +13 -0
  35. package/vendor/ctags/licenses/official-snapshot/linux-arm64/COPYING +340 -0
  36. package/vendor/ctags/licenses/official-snapshot/linux-arm64/RECEIPT.yml +27 -0
  37. package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libiconv.yml +24 -0
  38. package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libjansson.yml +19 -0
  39. package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libxml2.yml +20 -0
  40. package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libyaml.yml +7 -0
  41. package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libz.yml +13 -0
  42. package/vendor/ctags/licenses/official-snapshot/linux-x64/COPYING +340 -0
  43. package/vendor/ctags/licenses/official-snapshot/linux-x64/RECEIPT.yml +27 -0
  44. package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libiconv.yml +24 -0
  45. package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libjansson.yml +19 -0
  46. package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libxml2.yml +20 -0
  47. package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libyaml.yml +7 -0
  48. package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libz.yml +13 -0
  49. package/vendor/ctags/licenses/win32-x64/COPYING +340 -0
  50. package/vendor/ctags/licenses/win32-x64/COPYING.MinGW-w64-runtime.txt +240 -0
  51. package/vendor/ctags/licenses/win32-x64/Copyright.libxml2 +23 -0
  52. package/vendor/ctags/licenses/win32-x64/LICENCE.pcre2 +94 -0
  53. package/vendor/ctags/licenses/win32-x64/LICENSE.janssen +19 -0
  54. package/vendor/ctags/licenses/win32-x64/LICENSE.libyaml +19 -0
  55. package/vendor/ctags/linux-arm64/ctags +0 -0
  56. package/vendor/ctags/linux-x64/ctags +0 -0
  57. package/vendor/ctags/manifest.json +29 -12
  58. package/vendor/ctags/source/universal-ctags-ab95af198f8eaeb6d50dc96e3adf52e769d3e5f0.tar.gz +0 -0
  59. package/vendor/ctags/win32-x64/ctags.exe +0 -0
  60. package/vendor/ctags/source/universal-ctags-6.2.1.tar.gz +0 -0
@@ -0,0 +1,337 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdir, rename, rm, writeFile } from 'node:fs/promises';
3
+ import { basename, isAbsolute, join, posix } from 'node:path';
4
+ import { MarAgentError } from '@myagentroam/agent';
5
+ import { requestHttp, acquireCredential, appendCredentialHeaders } from '@myagentroam/agent/host/network';
6
+ const IMAGE_MAX_BYTES = 20_971_520;
7
+ const RESPONSE_MAX_BYTES = Math.ceil((IMAGE_MAX_BYTES * 4) / 3) + 4096;
8
+ const RESERVED_HEADERS = new Set([
9
+ 'accept',
10
+ 'authorization',
11
+ 'connection',
12
+ 'content-length',
13
+ 'content-type',
14
+ 'host',
15
+ 'proxy-authorization',
16
+ 'transfer-encoding',
17
+ 'x-codex-image-turn-id'
18
+ ]);
19
+ const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
20
+ const MAX_EDIT_IMAGES = 5;
21
+ const SUPPORTED_EDIT_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp']);
22
+ const IMAGE_GENERATION_RESPONSE_TIMEOUT_MS = 5 * 60 * 1000;
23
+ export const codexImageGenToolDefinition = {
24
+ name: 'codex_image_gen',
25
+ agentScope: 'ROOT_ONLY',
26
+ 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
+ inputSchema: {
28
+ type: 'object',
29
+ properties: {
30
+ prompt: { type: 'string', minLength: 1, maxLength: 32_768 },
31
+ referenced_image_paths: {
32
+ type: 'array',
33
+ maxItems: MAX_EDIT_IMAGES,
34
+ items: {
35
+ type: 'string',
36
+ description: 'Absolute local path of an image to edit.'
37
+ }
38
+ },
39
+ num_last_images_to_include: {
40
+ type: 'integer',
41
+ minimum: 1,
42
+ maximum: MAX_EDIT_IMAGES,
43
+ description: 'Number of most recent in-memory conversation images to edit.'
44
+ }
45
+ },
46
+ required: ['prompt'],
47
+ additionalProperties: false
48
+ }
49
+ };
50
+ export const bytedArkImageGenToolDefinition = {
51
+ ...codexImageGenToolDefinition,
52
+ name: 'byted_ark_image_gen',
53
+ description: 'Generate or edit one PNG using the configured Ark Images API, only when explicitly requested. First load skill({name:"byted_ark-imagegen"}). Omit both selectors for text-to-image; use absolute referenced_image_paths or num_last_images_to_include for image-to-image, never both. Up to five references, exactly one output; no group generation. Returns a managed image path, not inspected pixels.'
54
+ };
55
+ export async function executeCodexImageGen(arguments_, input) {
56
+ input.signal.throwIfAborted();
57
+ const capability = input.configuration.imageGeneration;
58
+ if (capability === undefined || capability.credentialProvider === undefined)
59
+ throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Image generation requires an independent credential provider.');
60
+ const request = parseInput(arguments_);
61
+ const images = await selectedEditImages(request, input);
62
+ const ark = capability.provider === 'BYTED_ARK';
63
+ if (!ark && capability.provider !== 'CODEX')
64
+ throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Unsupported image provider.');
65
+ const endpoint = images.length === 0 ? capability.generateEndpoint : capability.editEndpoint;
66
+ const body = JSON.stringify(ark
67
+ ? {
68
+ model: capability.imageModelId,
69
+ prompt: request.prompt,
70
+ ...(images.length === 0
71
+ ? {}
72
+ : {
73
+ image: images.length === 1
74
+ ? `data:${images[0].mimeType};base64,${images[0].data.toString('base64')}`
75
+ : images.map((image) => `data:${image.mimeType};base64,${image.data.toString('base64')}`)
76
+ }),
77
+ size: '2K',
78
+ output_format: 'png',
79
+ response_format: 'b64_json',
80
+ watermark: false,
81
+ stream: false,
82
+ sequential_image_generation: 'disabled'
83
+ }
84
+ : {
85
+ ...(images.length === 0
86
+ ? {}
87
+ : {
88
+ images: images.map((image) => ({
89
+ image_url: `data:${image.mimeType};base64,${image.data.toString('base64')}`
90
+ }))
91
+ }),
92
+ prompt: request.prompt,
93
+ background: 'auto',
94
+ model: capability.imageModelId,
95
+ n: 1,
96
+ quality: 'auto',
97
+ size: 'auto'
98
+ });
99
+ const turnId = randomUUID();
100
+ let reason = 'REQUEST';
101
+ for (let authenticationAttempt = 0; authenticationAttempt < 2; authenticationAttempt++) {
102
+ let credentialRevision;
103
+ const response = await requestHttp(endpoint, async () => {
104
+ const credential = await waitForImageStep(acquireCredential({ credentialProvider: capability.credentialProvider }, reason, 'Image generation requires a credential.'), input.signal);
105
+ credentialRevision = credential.revision;
106
+ const headers = appendCredentialHeaders(new Headers({
107
+ authorization: `Bearer ${credential.bearer.reveal()}`,
108
+ accept: 'application/json',
109
+ 'content-type': 'application/json',
110
+ ...(ark ? {} : { 'x-codex-image-turn-id': turnId })
111
+ }), credential, RESERVED_HEADERS);
112
+ return {
113
+ request: {
114
+ method: 'POST',
115
+ headers,
116
+ body,
117
+ redirect: 'manual'
118
+ },
119
+ httpVersion: capability.httpVersion,
120
+ ...(credential.socks5Url === undefined
121
+ ? {}
122
+ : { socks5Url: credential.socks5Url.reveal() })
123
+ };
124
+ }, input.signal, IMAGE_GENERATION_RESPONSE_TIMEOUT_MS);
125
+ if (!ark && response.status === 401 && authenticationAttempt === 0) {
126
+ await response.body?.cancel().catch(() => undefined);
127
+ if (credentialRevision !== undefined)
128
+ await capability.credentialProvider.invalidate(credentialRevision);
129
+ reason = 'UNAUTHORIZED';
130
+ continue;
131
+ }
132
+ const payload = await readJsonResponse(response, input.signal);
133
+ if (!response.ok)
134
+ throw imageGenerationResponseError(response.status, payload);
135
+ const encoded = imageBase64(payload);
136
+ const data = decodePng(encoded);
137
+ const fileName = generatedFileName(input.callId);
138
+ const directory = join(input.sessionDirectory, 'generated_images');
139
+ const path = join(directory, fileName);
140
+ const displayPath = input.displayDirectory === undefined ? path : posix.join(input.displayDirectory, fileName);
141
+ input.signal.throwIfAborted();
142
+ await mkdir(directory, { recursive: true });
143
+ const temporary = join(directory, `.${fileName}.${randomUUID()}.tmp`);
144
+ try {
145
+ input.signal.throwIfAborted();
146
+ await writeFile(temporary, data, { flag: 'wx', mode: 0o600 });
147
+ input.signal.throwIfAborted();
148
+ await rename(temporary, path);
149
+ }
150
+ finally {
151
+ await rm(temporary, { force: true });
152
+ }
153
+ const sha256 = createHash('sha256').update(data).digest('hex');
154
+ return {
155
+ content: `Generated image saved to ${displayPath}.\n`,
156
+ artifacts: [
157
+ {
158
+ kind: 'image',
159
+ name: fileName,
160
+ mimeType: 'image/png',
161
+ byteSize: data.byteLength,
162
+ sha256,
163
+ fileName
164
+ }
165
+ ]
166
+ };
167
+ }
168
+ throw new MarAgentError('MAR_AGENT_IMAGE_GENERATION_FAILED', 'Image generation failed.');
169
+ }
170
+ function parseInput(value) {
171
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
172
+ return invalidInput();
173
+ const input = value;
174
+ const referencedImagePaths = input.referenced_image_paths === null ? undefined : input.referenced_image_paths;
175
+ const numLastImagesToInclude = input.num_last_images_to_include === null ? undefined : input.num_last_images_to_include;
176
+ if (Object.keys(input).some((key) => key !== 'prompt' && key !== 'referenced_image_paths' && key !== 'num_last_images_to_include') ||
177
+ typeof input.prompt !== 'string' ||
178
+ input.prompt.trim().length === 0 ||
179
+ input.prompt.length > 32_768 ||
180
+ (referencedImagePaths !== undefined &&
181
+ (!Array.isArray(referencedImagePaths) ||
182
+ referencedImagePaths.length > MAX_EDIT_IMAGES ||
183
+ referencedImagePaths.some((path) => typeof path !== 'string' || path.length === 0 || path.length > 4096 || !isAbsolute(path)))) ||
184
+ (numLastImagesToInclude !== undefined &&
185
+ (!Number.isInteger(numLastImagesToInclude) ||
186
+ numLastImagesToInclude < 1 ||
187
+ numLastImagesToInclude > MAX_EDIT_IMAGES)))
188
+ return invalidInput();
189
+ const paths = referencedImagePaths;
190
+ if (paths?.length && numLastImagesToInclude !== undefined)
191
+ return invalidInput();
192
+ return {
193
+ prompt: input.prompt,
194
+ ...(paths?.length ? { referencedImagePaths: [...paths] } : {}),
195
+ ...(numLastImagesToInclude === undefined
196
+ ? {}
197
+ : { numLastImagesToInclude: numLastImagesToInclude })
198
+ };
199
+ }
200
+ async function selectedEditImages(request, input) {
201
+ if (request.referencedImagePaths !== undefined) {
202
+ const images = [];
203
+ for (const path of request.referencedImagePaths)
204
+ images.push(validateEditImage(await input.readImage(path)));
205
+ return images;
206
+ }
207
+ if (request.numLastImagesToInclude === undefined)
208
+ return [];
209
+ const images = input.recentImages.slice(-request.numLastImagesToInclude);
210
+ if (images.length !== request.numLastImagesToInclude)
211
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', `Requested the last ${request.numLastImagesToInclude} conversation images, but only ${images.length} were available.`);
212
+ return images.map(validateEditImage);
213
+ }
214
+ function validateEditImage(image) {
215
+ if (!SUPPORTED_EDIT_MIME_TYPES.has(image.mimeType) ||
216
+ !Buffer.isBuffer(image.data) ||
217
+ image.data.byteLength === 0 ||
218
+ image.data.byteLength > IMAGE_MAX_BYTES)
219
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Referenced image is invalid or unsupported.');
220
+ return { mimeType: image.mimeType, data: Buffer.from(image.data) };
221
+ }
222
+ async function readJsonResponse(response, signal) {
223
+ const contentLength = Number(response.headers.get('content-length'));
224
+ if (Number.isFinite(contentLength) && contentLength > RESPONSE_MAX_BYTES) {
225
+ await response.body?.cancel().catch(() => undefined);
226
+ throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Image response exceeded the size limit.');
227
+ }
228
+ if (response.body === null)
229
+ return {};
230
+ const reader = response.body.getReader();
231
+ const abort = () => {
232
+ void reader.cancel(signal.reason).catch(() => undefined);
233
+ };
234
+ signal.addEventListener('abort', abort, { once: true });
235
+ const chunks = [];
236
+ let bytes = 0;
237
+ try {
238
+ signal.throwIfAborted();
239
+ for (;;) {
240
+ const chunk = await reader.read();
241
+ signal.throwIfAborted();
242
+ if (chunk.done)
243
+ break;
244
+ bytes += chunk.value.byteLength;
245
+ if (bytes > RESPONSE_MAX_BYTES)
246
+ throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Image response exceeded the size limit.');
247
+ chunks.push(chunk.value);
248
+ }
249
+ }
250
+ catch (error) {
251
+ await reader.cancel().catch(() => undefined);
252
+ throw error;
253
+ }
254
+ finally {
255
+ signal.removeEventListener('abort', abort);
256
+ reader.releaseLock();
257
+ }
258
+ try {
259
+ return JSON.parse(Buffer.concat(chunks, bytes).toString('utf8'));
260
+ }
261
+ catch (cause) {
262
+ throw new MarAgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.', { cause });
263
+ }
264
+ }
265
+ async function waitForImageStep(operation, signal) {
266
+ let abort = () => undefined;
267
+ const interrupted = new Promise((_resolve, reject) => {
268
+ abort = () => reject(signal.reason);
269
+ signal.addEventListener('abort', abort, { once: true });
270
+ if (signal.aborted)
271
+ abort();
272
+ });
273
+ try {
274
+ return await Promise.race([operation, interrupted]);
275
+ }
276
+ finally {
277
+ signal.removeEventListener('abort', abort);
278
+ }
279
+ }
280
+ function imageBase64(value) {
281
+ const data = value !== null &&
282
+ typeof value === 'object' &&
283
+ !Array.isArray(value) &&
284
+ Array.isArray(value.data)
285
+ ? value.data
286
+ : undefined;
287
+ const first = data?.length === 1 && data[0] !== null && typeof data[0] === 'object' && !Array.isArray(data[0])
288
+ ? data[0].b64_json
289
+ : undefined;
290
+ if (typeof first !== 'string' || first.length === 0 || first.length > RESPONSE_MAX_BYTES)
291
+ throw new MarAgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.');
292
+ return first;
293
+ }
294
+ function decodePng(encoded) {
295
+ if (encoded.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/u.test(encoded))
296
+ throw new MarAgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.');
297
+ const data = Buffer.from(encoded, 'base64');
298
+ if (data.byteLength === 0 ||
299
+ data.byteLength > IMAGE_MAX_BYTES ||
300
+ data.toString('base64') !== encoded ||
301
+ !data.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE))
302
+ throw new MarAgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is not a supported PNG.');
303
+ return data;
304
+ }
305
+ function generatedFileName(callId) {
306
+ const normalized = callId
307
+ .normalize('NFKC')
308
+ .replace(/[^a-zA-Z0-9._-]+/gu, '_')
309
+ .replace(/^[_\-.]+|[_\-.]+$/gu, '')
310
+ .slice(0, 96);
311
+ const stem = normalized.length > 0 ? normalized : createHash('sha256').update(callId).digest('hex');
312
+ const fileName = `${stem.startsWith('image_') ? stem : `image_${stem}`}.png`;
313
+ return basename(fileName);
314
+ }
315
+ function imageGenerationResponseError(status, value) {
316
+ const error = value !== null && typeof value === 'object' && !Array.isArray(value)
317
+ ? value.error
318
+ : undefined;
319
+ const record = error !== null && typeof error === 'object' && !Array.isArray(error)
320
+ ? error
321
+ : undefined;
322
+ const fallbackMessage = `Image generation failed with HTTP ${status}.`;
323
+ const rawCode = typeof record?.code === 'string' ? record.code.trim() : '';
324
+ const code = /^[a-zA-Z0-9._-]{1,80}$/u.test(rawCode)
325
+ ? rawCode
326
+ : 'MAR_AGENT_IMAGE_GENERATION_FAILED';
327
+ const rawMessage = typeof record?.message === 'string' ? record.message.trim() : '';
328
+ const message = rawMessage.length > 0 &&
329
+ rawMessage.length <= 1_000 &&
330
+ !/\b(?:authorization|bearer|api[_ -]?key|access[_ -]?token|refresh[_ -]?token|password|secret|proxy-authorization|chatgpt-account-id)\b|socks5:\/\//iu.test(rawMessage)
331
+ ? rawMessage
332
+ : fallbackMessage;
333
+ return new MarAgentError(code, message, { details: { status } });
334
+ }
335
+ function invalidInput() {
336
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Image generation input is invalid.');
337
+ }
@@ -1,8 +1,9 @@
1
+ import { computerUseHostSkill } from './computer-use-skill.js';
1
2
  import { createHash, randomUUID } from 'node:crypto';
2
3
  import { homedir } from 'node:os';
3
4
  import { access, constants } from 'node:fs/promises';
4
5
  import { BUNDLED_RIPGREP_VERSION, bundledRipgrepPath, prependBundledRipgrepToPath } from '../../ripgrep-binary.js';
5
- import { LocalAgentHost, SecretValue, createMarAgent } from '@myagentroam/agent';
6
+ import { LocalAgentHost, createMarAgent } from '@myagentroam/agent';
6
7
  import { marAgentReasoningEffortSchema, marAgentRuntimeModelConfigurationSchema } from '@myagentroam/protocol';
7
8
  import { boundedConversationItemId } from '../codex/conversation-parser.js';
8
9
  import { parseSecretEnvironment } from '../../util/secret-environment.js';
@@ -10,6 +11,8 @@ import { MarAgentMcpClientCache } from './mcp-client-pool.js';
10
11
  import { MarAgentConversationNormalizer } from './conversation-normalizer.js';
11
12
  import { parseRunnerImageAttachments } from '../../util/node-operation-parsers.js';
12
13
  import { runnerErrorProjection } from '../../util/runner-error.js';
14
+ import { MarAgentRuntimeModelFactory, sdkModelConfigurations, closeImageCredentialProviders } from './runtime-model-factory.js';
15
+ import { createImageGenerationExtensions } from './image-generation-extensions.js';
13
16
  export class MarAgentManagedRunController {
14
17
  runner;
15
18
  host;
@@ -32,18 +35,13 @@ export class MarAgentManagedRunController {
32
35
  this.host = host;
33
36
  this.#resourceIdleTtlMs = options.resourceIdleTtlMs ?? 10 * 60_000;
34
37
  this.#createAgent = options.createAgent ?? createMarAgent;
35
- this.#modelFactory = options.modelFactory ?? {
36
- create: (configurations) => configurations.map((configuration) => {
37
- const { apiKey, credential, ...model } = configuration;
38
- const value = apiKey ?? (credential?.type === 'INLINE_API_KEY' ? credential.value : undefined);
39
- if (value === undefined)
40
- throw new Error('MAR_AGENT_RUNTIME_CREDENTIALS_UNSUPPORTED');
41
- return {
42
- ...model,
43
- apiKey: new SecretValue(value)
44
- };
45
- })
46
- };
38
+ this.#modelFactory =
39
+ options.modelFactory ??
40
+ new MarAgentRuntimeModelFactory({
41
+ resolve: async () => {
42
+ throw new Error('MAR_AGENT_RUNTIME_CREDENTIALS_UNSUPPORTED');
43
+ }
44
+ });
47
45
  this.#computerUse = options.computerUse;
48
46
  }
49
47
  dispose() {
@@ -243,6 +241,7 @@ export class MarAgentManagedRunController {
243
241
  let executionMcpLease;
244
242
  let executionMcpBinding;
245
243
  let computerUseBinding;
244
+ let executionModels = [];
246
245
  try {
247
246
  const sessionKey = input.externalSessionId ?? input.sessionId;
248
247
  const acquisitionKeys = [...new Set([sessionKey, input.sessionId])];
@@ -253,6 +252,7 @@ export class MarAgentManagedRunController {
253
252
  this.#runtimeAcquisitions.set(key, acquisition);
254
253
  try {
255
254
  runtime = await acquisition;
255
+ executionModels = runtime.models;
256
256
  }
257
257
  finally {
258
258
  for (const key of acquisitionKeys)
@@ -298,17 +298,25 @@ export class MarAgentManagedRunController {
298
298
  if (new Set([...userTools, ...nodeTools].map((tool) => tool.name)).size !==
299
299
  userTools.length + nodeTools.length)
300
300
  throw new Error('MAR_AGENT_MCP_TOOL_NAMES_CONFLICT');
301
- if (userTools.length > 0 || nodeTools.length > 0)
302
- executionMcpBinding = runtime.host.bindExecutionMcp({
303
- tools: [...nodeTools, ...userTools],
304
- call: async (call, signal) => {
305
- if (computerUseBinding?.tools.some((tool) => tool.name === call.name))
306
- return computerUseBinding.call(call, signal);
307
- if (executionMcpLease?.pool.definitions().some((tool) => tool.name === call.name))
308
- return executionMcpLease.pool.call(call.name, call.arguments);
309
- throw new Error('MAR_AGENT_MCP_TOOL_NOT_FOUND');
310
- }
311
- });
301
+ const imageExtensions = createImageGenerationExtensions({
302
+ configuration: executionModels.find((model) => model.id === input.model.id)
303
+ ?.imageGeneration,
304
+ homeDirectory: homedir(),
305
+ host: () => runtime.host
306
+ });
307
+ executionMcpBinding = runtime.host.bindExecutionExtensions({
308
+ ...imageExtensions,
309
+ skills: [...(imageExtensions.skills ?? []), computerUseHostSkill]
310
+ }, {
311
+ tools: [...nodeTools, ...userTools],
312
+ call: async (call, signal) => {
313
+ if (computerUseBinding?.tools.some((tool) => tool.name === call.name))
314
+ return computerUseBinding.call(call, signal);
315
+ if (executionMcpLease?.pool.definitions().some((tool) => tool.name === call.name))
316
+ return executionMcpLease.pool.call(call.name, call.arguments);
317
+ throw new Error('MAR_AGENT_MCP_TOOL_NOT_FOUND');
318
+ }
319
+ });
312
320
  const session = runtime.session;
313
321
  const reasoningEffort = input.effort
314
322
  ? marAgentReasoningEffortSchema.parse(input.effort)
@@ -372,6 +380,7 @@ export class MarAgentManagedRunController {
372
380
  this.#normalizers.delete(input.runId);
373
381
  this.host.active.delete(input.runId);
374
382
  this.#executions.delete(input.runId);
383
+ await closeImageCredentialProviders(executionModels);
375
384
  await computerUseBinding?.close().catch(() => undefined);
376
385
  runtime?.host.unbindExecutionMcp(executionMcpBinding);
377
386
  await executionMcpLease?.release().catch(() => undefined);
@@ -425,9 +434,10 @@ export class MarAgentManagedRunController {
425
434
  });
426
435
  try {
427
436
  await existing.agent.reconfigureModels({
428
- models,
437
+ models: sdkModelConfigurations(models),
429
438
  defaultModelId: input.model.id
430
439
  });
440
+ existing.models = models;
431
441
  }
432
442
  catch (error) {
433
443
  await closeModelCredentialProviders(models);
@@ -463,6 +473,7 @@ export class MarAgentManagedRunController {
463
473
  const mcpLease = await this.#mcpClients.acquire(sharedMcpServers, input.cwd);
464
474
  let agent;
465
475
  let runtime;
476
+ let models = [];
466
477
  try {
467
478
  const mcp = mcpLease.pool;
468
479
  const localHost = new LocalAgentHost({
@@ -481,14 +492,14 @@ export class MarAgentManagedRunController {
481
492
  allowUntrustedTls: allowsUntrustedWebFetchTls(hostConfiguration.environment),
482
493
  resourceIdleTtlMs: this.#resourceIdleTtlMs
483
494
  });
484
- const models = this.#modelFactory.create(input.models, {
495
+ models = this.#modelFactory.create(input.models, {
485
496
  ...(input.runtimeScopeId === undefined ? {} : { runtimeScopeId: input.runtimeScopeId }),
486
497
  executionId: input.runId
487
498
  });
488
499
  agent = await this.#createAgent({
489
500
  mode: 'node',
490
501
  host: localHost,
491
- models,
502
+ models: sdkModelConfigurations(models),
492
503
  defaultModelId: input.model.id,
493
504
  ...(input.personalInstructions ? { systemInstruction: input.personalInstructions } : {})
494
505
  });
@@ -502,6 +513,7 @@ export class MarAgentManagedRunController {
502
513
  host: localHost,
503
514
  mcpLease,
504
515
  dynamicCredentials,
516
+ models,
505
517
  released: false
506
518
  };
507
519
  this.#sessionRuntimes.set(sessionKey, runtime);
@@ -512,6 +524,7 @@ export class MarAgentManagedRunController {
512
524
  }
513
525
  catch (error) {
514
526
  await agent?.dispose().catch(() => undefined);
527
+ await closeModelCredentialProviders(models);
515
528
  await mcpLease.release().catch(() => undefined);
516
529
  throw error;
517
530
  }
@@ -1,5 +1,16 @@
1
1
  import { SecretValue } from '@myagentroam/agent';
2
2
  import { marAgentRuntimeModelConfigurationSchema, resolveMarAgentDefaultReasoningEffort } from '@myagentroam/protocol';
3
+ export function sdkModelConfigurations(models) {
4
+ return models.map(({ imageGeneration: _imageGeneration, ...model }) => model);
5
+ }
6
+ export async function closeImageCredentialProviders(models) {
7
+ const textProviders = new Set(models.map((model) => model.credentialProvider));
8
+ const imageProviders = new Set(models.flatMap((model) => model.imageGeneration === undefined ||
9
+ textProviders.has(model.imageGeneration.credentialProvider)
10
+ ? []
11
+ : [model.imageGeneration.credentialProvider]));
12
+ await Promise.allSettled([...imageProviders].map((provider) => provider.close()));
13
+ }
3
14
  class PendingCredentialResolution {
4
15
  abort;
5
16
  initialReason;
@@ -96,9 +96,13 @@ export class NodeRunEventBridge {
96
96
  else {
97
97
  if (payload.event.eventType !== 'run.started' && payload.event.eventType !== 'run.running')
98
98
  return;
99
- payload = { turn, segments: conversationSegments(turn) };
99
+ payload = { turn };
100
100
  }
101
101
  }
102
+ if (category === 'conversation' &&
103
+ isPlainRecord(payload) &&
104
+ isNodeConversationTurn(payload.turn))
105
+ return { ...payload, segments: conversationSegments(payload.turn) };
102
106
  return payload;
103
107
  }
104
108
  emitConversationItem(runId, item) {
@@ -190,10 +190,6 @@ export class SessionMessageService {
190
190
  return {
191
191
  session: this.options.present(sessionForPresentation),
192
192
  run: created.run,
193
- ...(created.queueItem === undefined &&
194
- (internalMission || missionMessage?.activated === true)
195
- ? { turn: this.options.runtime.conversationTurnForRun(created.run.id) }
196
- : {}),
197
193
  ...(created.queueItem === undefined ? {} : { queueItemId: created.queueItem.id }),
198
194
  delivery,
199
195
  idempotent: false,
@@ -3,6 +3,7 @@ import { lstat, mkdir, open, readFile, rename, rm } from 'node:fs/promises';
3
3
  import { dirname, join, posix, resolve } from 'node:path';
4
4
  import { parseComposerAttachmentUpload, validComposerAttachmentName } from '../util/node-operation-parsers.js';
5
5
  import { preflightWorkspaceUpload, workspaceUploadTemporaryName } from '../workspace.js';
6
+ import { ensureWorkspaceGitExclude } from './workspace-git-exclude-service.js';
6
7
  const CHUNK_BYTES = 512 * 1024;
7
8
  const TTL_MS = 60 * 60_000;
8
9
  const MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
@@ -258,6 +259,7 @@ export class WorkspaceUploadService {
258
259
  await rm(dirname(attachment.targetPath), { recursive: true, force: true }).catch(() => undefined);
259
260
  }
260
261
  async attachmentTarget(workspacePath, id, name) {
262
+ await ensureWorkspaceGitExclude(workspacePath);
261
263
  const mar = resolve(workspacePath, '.mar');
262
264
  await mkdir(mar, { recursive: true, mode: 0o700 });
263
265
  await safeDirectory(mar);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.102",
3
+ "version": "0.9.104",
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.102",
34
- "@myagentroam/code-navigation": "0.9.102",
35
- "@myagentroam/protocol": "0.9.102"
33
+ "@myagentroam/agent": "0.9.104",
34
+ "@myagentroam/protocol": "0.9.104",
35
+ "@myagentroam/code-navigation": "0.9.104"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/ws": "^8.18.1"
@@ -1,10 +1,22 @@
1
- This directory distributes Universal Ctags 6.2.1 as a separate executable work.
1
+ This directory distributes Universal Ctags binaries as a separate executable
2
+ work. All bundled binaries are official upstream artifacts built from commit
3
+ ab95af198f8eaeb6d50dc96e3adf52e769d3e5f0.
2
4
 
3
5
  Universal Ctags is licensed under GPL-2.0-or-later. Its complete corresponding
4
- source is included at source/universal-ctags-6.2.1.tar.gz. The Linux x64 program
5
- was built without source changes from that archive using configure --disable-xml
6
- with optional pkg-config dependencies unavailable. The Windows x64 program is
7
- the upstream universal-ctags/ctags-win32 v6.2.1 ctags-v6.2.1-x64.zip executable.
6
+ source is included at:
8
7
 
9
- The Windows upstream package also distributes the MinGW-w64 runtime, jansson,
10
- and libyaml license texts copied into licenses/.
8
+ source/universal-ctags-ab95af198f8eaeb6d50dc96e3adf52e769d3e5f0.tar.gz
9
+
10
+ The official source URL, per-platform release URLs, archive SHA-256 values and
11
+ executable SHA-256 values are recorded in manifest.json.
12
+
13
+ Linux x64/ARM64 and macOS x64/ARM64 come from the official
14
+ universal-ctags/ctags-nightly-build 2026.09.16 release. Their upstream package
15
+ metadata and dependency notices are preserved under
16
+ licenses/official-snapshot/<target>/.
17
+
18
+ Windows x64 comes from the official universal-ctags/ctags-win32
19
+ 2026-09-16/p6.2.20260913.0-2-gab95af1 release. Its distributed license texts
20
+ are preserved under licenses/win32-x64/.
21
+
22
+ No bundled Ctags executable is built by this project.
Binary file
Binary file