@myagentroam/node 0.9.102 → 0.9.103
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/computer-use-runtime-version.js +1 -1
- package/dist/computer-use-runtime.js +19 -2
- package/dist/ctags-binary.js +6 -3
- package/dist/runner/mar-agent/byted-ark-imagegen-skill.js +18 -0
- package/dist/runner/mar-agent/codex-imagegen-skill.js +35 -0
- package/dist/runner/mar-agent/computer-use-skill.js +37 -0
- package/dist/runner/mar-agent/hosted-conversation-runtime.js +41 -21
- package/dist/runner/mar-agent/image-generation-extensions.js +59 -0
- package/dist/runner/mar-agent/image-generation-validation.js +50 -0
- package/dist/runner/mar-agent/image-generation.js +337 -0
- package/dist/runner/mar-agent/managed-run-controller.js +40 -27
- package/dist/runner/mar-agent/runtime-model-factory.js +11 -0
- package/dist/service/session-message-service.js +0 -4
- package/package.json +4 -4
- package/vendor/ctags/NOTICE +19 -7
- package/vendor/ctags/darwin-arm64/ctags +0 -0
- package/vendor/ctags/darwin-x64/ctags +0 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-arm64/COPYING +340 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-arm64/RECEIPT.yml +27 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libiconv.yml +24 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libjansson.yml +19 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libxml2.yml +20 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libyaml.yml +7 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-arm64/dependencies/libz.yml +13 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-x64/COPYING +340 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-x64/RECEIPT.yml +27 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libiconv.yml +24 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libjansson.yml +19 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libxml2.yml +20 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libyaml.yml +7 -0
- package/vendor/ctags/licenses/official-snapshot/darwin-x64/dependencies/libz.yml +13 -0
- package/vendor/ctags/licenses/official-snapshot/linux-arm64/COPYING +340 -0
- package/vendor/ctags/licenses/official-snapshot/linux-arm64/RECEIPT.yml +27 -0
- package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libiconv.yml +24 -0
- package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libjansson.yml +19 -0
- package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libxml2.yml +20 -0
- package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libyaml.yml +7 -0
- package/vendor/ctags/licenses/official-snapshot/linux-arm64/dependencies/libz.yml +13 -0
- package/vendor/ctags/licenses/official-snapshot/linux-x64/COPYING +340 -0
- package/vendor/ctags/licenses/official-snapshot/linux-x64/RECEIPT.yml +27 -0
- package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libiconv.yml +24 -0
- package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libjansson.yml +19 -0
- package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libxml2.yml +20 -0
- package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libyaml.yml +7 -0
- package/vendor/ctags/licenses/official-snapshot/linux-x64/dependencies/libz.yml +13 -0
- package/vendor/ctags/licenses/win32-x64/COPYING +340 -0
- package/vendor/ctags/licenses/win32-x64/COPYING.MinGW-w64-runtime.txt +240 -0
- package/vendor/ctags/licenses/win32-x64/Copyright.libxml2 +23 -0
- package/vendor/ctags/licenses/win32-x64/LICENCE.pcre2 +94 -0
- package/vendor/ctags/licenses/win32-x64/LICENSE.janssen +19 -0
- package/vendor/ctags/licenses/win32-x64/LICENSE.libyaml +19 -0
- package/vendor/ctags/linux-arm64/ctags +0 -0
- package/vendor/ctags/linux-x64/ctags +0 -0
- package/vendor/ctags/manifest.json +29 -12
- package/vendor/ctags/source/universal-ctags-ab95af198f8eaeb6d50dc96e3adf52e769d3e5f0.tar.gz +0 -0
- package/vendor/ctags/win32-x64/ctags.exe +0 -0
- 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,
|
|
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 =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -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,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.103",
|
|
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.
|
|
34
|
-
"@myagentroam/code-navigation": "0.9.
|
|
35
|
-
"@myagentroam/protocol": "0.9.
|
|
33
|
+
"@myagentroam/agent": "0.9.103",
|
|
34
|
+
"@myagentroam/code-navigation": "0.9.103",
|
|
35
|
+
"@myagentroam/protocol": "0.9.103"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/ws": "^8.18.1"
|
package/vendor/ctags/NOTICE
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
This directory distributes Universal Ctags
|
|
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
|
|
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
|
-
|
|
10
|
-
|
|
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
|