@myagentroam/node 0.12.1 → 0.12.2
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/hosting/hosted-conversation-node-service.js +1 -1
- package/dist/marctl/package.json +1 -1
- package/dist/marctl/src/direct-transport.mjs +2 -2
- package/dist/marctl/src/direct.mjs +2 -2
- package/dist/marctl-guidance.js +1 -1
- package/dist/native-session-history.js +1 -1
- package/dist/runner/codex-runner.js +2 -2
- package/dist/runner/mar-agent/codex-imagegen-skill.js +15 -23
- package/dist/runner/mar-agent/conversation-normalizer.js +4 -18
- package/dist/runner/mar-agent/image-generation-extensions.js +1 -1
- package/dist/runner/mar-agent/image-generation.js +73 -20
- package/dist/runner/mar-agent/session-runtime-registry.js +5 -3
- package/dist/workspace/workspace-git-exclude-service.js +1 -1
- package/package.json +4 -4
|
@@ -450,7 +450,7 @@ export class HostedConversationNodeService {
|
|
|
450
450
|
async startMessage(payload, input, steerPrompt) {
|
|
451
451
|
if (this.disablePrepared)
|
|
452
452
|
throw new Error('CONVERSATION_HOSTING_UNAVAILABLE');
|
|
453
|
-
|
|
453
|
+
const conversation = this.options.store.requireConversation(payload.userId, input.conversationId);
|
|
454
454
|
this.assertTemplateContext(payload, conversation);
|
|
455
455
|
if (this.cleanupLocks.has(conversation.id))
|
|
456
456
|
throw new Error('HOSTED_CONVERSATION_STATE_INVALID');
|
package/dist/marctl/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"0.12.
|
|
1
|
+
{"type":"module","version":"0.12.2"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { DirectFallbackError,
|
|
3
|
-
import {
|
|
2
|
+
import { DirectFallbackError, directControlError } from './direct-errors.mjs';
|
|
3
|
+
import { rawToString, isRecord } from './direct-control.mjs';
|
|
4
4
|
|
|
5
5
|
export class DirectTerminalTransport extends EventEmitter {
|
|
6
6
|
#channel;
|
|
@@ -310,8 +310,8 @@ export async function openDirectTerminal(options) {
|
|
|
310
310
|
signal
|
|
311
311
|
);
|
|
312
312
|
return transport;
|
|
313
|
-
} catch (
|
|
314
|
-
error = preparationError ??
|
|
313
|
+
} catch (caughtError) {
|
|
314
|
+
const error = preparationError ?? caughtError;
|
|
315
315
|
transportControlCleanup?.();
|
|
316
316
|
transportControlCleanup = undefined;
|
|
317
317
|
unsafeFallback = transport?.openSent === true;
|
package/dist/marctl-guidance.js
CHANGED
|
@@ -28,7 +28,7 @@ export const marctlInstructions = [
|
|
|
28
28
|
'Prefer agents send <id> --steer -m <text> for timely communication or course correction. --queue is best reserved for deferred input explicitly requested by the user or recipient: it is consumed after current work and earlier queued inputs finish, and with an active Goal it is not consumed until that Goal exits. Acceptance is not a guarantee of immediate processing.',
|
|
29
29
|
'An accepted message normally needs no separate acknowledgment. Requests for receipt-only replies and immediate agents view checks solely to confirm receipt are usually unnecessary.',
|
|
30
30
|
'Send can update a Goal alone with --goal <objective> or --clear-goal; omitted Goal leaves it unchanged. Decide an authorized Agent approval with send <id> --approval <approval-id> --decision approve|reject.',
|
|
31
|
-
'List accepts only --scope tree|active, optional --query <title-fragment> and --limit <1..100> (default 10). With a current Session it defaults to the containing collaboration tree, including idle Sessions; without one it defaults to active Workspace Sessions (running or sleeping), and explicit tree is rejected. Active Sessions come first, then recent activity descending. Returns currentAgentId (null without a Session), flat sessions with id, parentSessionId, title and status, and truncated; no cursor or pagination. Reuse known IDs directly.',
|
|
31
|
+
'List accepts only --scope tree|active, optional --query <title-fragment> and --limit <1..100> (default 10). With a current Session it defaults to the containing collaboration tree, including idle Sessions; without one it defaults to active Workspace Sessions (running or sleeping), and explicit tree is rejected. Active Sessions come first, then recent activity descending. Returns scope, currentAgentId (null without a Session), flat sessions with id, parentSessionId, title and status, and truncated; no cursor or pagination. Reuse known IDs directly.',
|
|
32
32
|
'Only direct-child updates are collected automatically. Sending to a non-child never subscribes; an explicit wait observes it only for that call.',
|
|
33
33
|
'Use agents config, rename, prompt and schedule for management. marctl nodes, ssh and scp use authorized Nodes.',
|
|
34
34
|
marctlSafety
|
|
@@ -365,7 +365,7 @@ function deleteTranscriptIndex(key) {
|
|
|
365
365
|
export async function locateNativeSessionPath(runner, workspacePath, externalSessionId) {
|
|
366
366
|
const wanted = canonical(workspacePath);
|
|
367
367
|
const key = indexKey(runner, wanted, externalSessionId);
|
|
368
|
-
|
|
368
|
+
const path = getTranscriptIndex(key);
|
|
369
369
|
if (path !== undefined)
|
|
370
370
|
return path;
|
|
371
371
|
const candidates = await nativeTranscriptAdapter(runner)?.locateCandidates?.(externalSessionId);
|
|
@@ -394,7 +394,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
394
394
|
const threads = [];
|
|
395
395
|
const cursors = new Set();
|
|
396
396
|
let cursor;
|
|
397
|
-
|
|
397
|
+
for (;;) {
|
|
398
398
|
const result = await this.client.listThreads({
|
|
399
399
|
limit: 200,
|
|
400
400
|
...(cursor === undefined ? {} : { cursor })
|
|
@@ -415,7 +415,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
415
415
|
throw new Error('CODEX_THREAD_LIST_INVALID');
|
|
416
416
|
cursors.add(next);
|
|
417
417
|
cursor = next;
|
|
418
|
-
}
|
|
418
|
+
}
|
|
419
419
|
}
|
|
420
420
|
async discoverSessions(context, workspace) {
|
|
421
421
|
const [threads, nativeSessions] = context.cleanup
|
|
@@ -1,36 +1,28 @@
|
|
|
1
1
|
export function codexImagegenSkill() {
|
|
2
2
|
return `## Working with images
|
|
3
3
|
|
|
4
|
-
Use codex_image_gen only
|
|
4
|
+
Use codex_image_gen only for image generation or editing the user requests or explicitly authorizes. Loading this skill is not authorization to add visuals.
|
|
5
5
|
|
|
6
6
|
### Choose the operation
|
|
7
|
-
- Use
|
|
8
|
-
-
|
|
9
|
-
- Inspect
|
|
10
|
-
- Use num_last_images_to_include only when a necessary image has no usable path, and choose the smallest suffix containing all inputs. This is the current Execution's available in-memory images, not a permanent conversation gallery. After a new Execution or Compact, use valid saved paths or ask for missing inputs again.
|
|
11
|
-
- The selectors are mutually exclusive and include at most five images. Omit both only for generation without input images. Do not pretend an unavailable reference was used.
|
|
7
|
+
- Use generated raster artwork for photos, illustrations, textures, and visual variants. Prefer direct source edits for existing SVG, icons, and other code-native assets.
|
|
8
|
+
- Omit both selectors for generation without references. Otherwise use up to five absolute referenced_image_paths, or the smallest num_last_images_to_include containing all needed images, never both. Prefer paths when all inputs have them. Recent images belong to this Execution, not a permanent gallery; after recovery or Compact, use saved paths or ask for missing inputs.
|
|
9
|
+
- Identify each reference's role. Inspect unseen PNG/JPEG/WebP inputs with view_image when available; TEXT-only models must not claim visual inspection. Do not silently omit required references.
|
|
12
10
|
|
|
13
11
|
### Shape the prompt
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- For
|
|
17
|
-
-
|
|
18
|
-
-
|
|
12
|
+
- State the subject, composition, style, and essential constraints; preserve exact requested lettering. For edits, specify what changes and what stays unchanged. Add only details that support the user's goal; clarify only when a missing decision blocks the result.
|
|
13
|
+
- Set transparent_background: true for cutouts, background removal, or transparency; preserve existing transparency during edits unless the user asks to change it. Preserve real alpha, not a drawn checkerboard.
|
|
14
|
+
- For a simple looping animation, set gif: {} or specify frame_count (2–16) and frame_duration_ms (20–2000). Defaults are 9 frames and 200 ms. Describe the motion and loop seam; keep subjects and camera stable unless asked otherwise.
|
|
15
|
+
- To edit a GIF, first inspect its frames and use available tools to make a PNG spritesheet with equal-sized cells in a regular grid. Pass that spritesheet through referenced_image_paths and set gif. Do not pass the GIF itself as an edit input; if preparation is unavailable, explain the limitation.
|
|
16
|
+
- For complex animation or more than 16 frames, use suitable available tools or explain the limit rather than stretching this tool's bounds.
|
|
19
17
|
|
|
20
18
|
### Generate and check
|
|
21
|
-
-
|
|
22
|
-
- In Code Mode, call tools.codex_image_gen. Independent calls
|
|
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.
|
|
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.
|
|
26
|
-
- For transparent deliverables, verify actual transparency rather than treating a drawn checkerboard as alpha. Preserve the alpha channel when copying or processing the result.
|
|
19
|
+
- One PNG or GIF is saved per call. Model, quality, and size remain service-controlled; do not invent API parameters, output paths, SVG support, or fallback tools.
|
|
20
|
+
- In Code Mode, call tools.codex_image_gen. Independent calls may use Promise.all; dependent edits must await their inputs. Generation can take minutes: await the result and do not blindly replay an interrupted or timed-out request. Report failures rather than claiming an artifact exists.
|
|
21
|
+
- Verify the actual format and requested transparency, then check PNG content (subject, lettering, and edit invariants) with view_image when available. For GIFs, check frames, timing, and the loop with available animation-aware tools; a first-frame preview does not verify animation. If inspection is unavailable, say so. Correct only issues within the requested scope.
|
|
27
22
|
|
|
28
23
|
### Keep and deliver
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
- Avoid overwriting existing assets unless replacement was requested. For preview-only work the managed file can remain in place.
|
|
32
|
-
- If the user asks to reuse an existing image, inspect or copy it rather than generating a replacement. If it is missing, state that fact; generate again only when authorized.
|
|
33
|
-
- Report the actual final path and any material unmet constraints. Do not dump a long generation prompt unless the user requests it.
|
|
24
|
+
- Trust the returned path and keep the managed original. Copy assets into the authorized project destination when needed; do not leave project dependencies in private Session storage. Do not overwrite existing assets unless replacement was requested.
|
|
25
|
+
- Reuse existing images when requested rather than regenerating them. Report the final path and material unmet constraints, not a long prompt dump.
|
|
34
26
|
|
|
35
|
-
This Host-provided skill has no bundled
|
|
27
|
+
This Host-provided skill has no bundled CLI fallback or separate API-key setup. Do not switch credentials, endpoints, or models. Follow the tool schema and Host file boundaries.`;
|
|
36
28
|
}
|
|
@@ -602,7 +602,7 @@ export class MarAgentConversationNormalizer {
|
|
|
602
602
|
this.#clearCodeModeOutputSnapshots(sourceCallId);
|
|
603
603
|
}
|
|
604
604
|
#startExecution(executionId) {
|
|
605
|
-
for (const
|
|
605
|
+
for (const state of this.#tools.values())
|
|
606
606
|
if (state.executionId !== executionId) {
|
|
607
607
|
state.active = false;
|
|
608
608
|
}
|
|
@@ -1734,20 +1734,6 @@ function truncateCharacters(value, maximumCharacters) {
|
|
|
1734
1734
|
? value
|
|
1735
1735
|
: characters.slice(0, maximumCharacters).join('');
|
|
1736
1736
|
}
|
|
1737
|
-
function boundedToolSummary(value) {
|
|
1738
|
-
const maximumCodeUnits = 10_000;
|
|
1739
|
-
if (value.length <= maximumCodeUnits)
|
|
1740
|
-
return value;
|
|
1741
|
-
let end = maximumCodeUnits - 1;
|
|
1742
|
-
const lastIncluded = value.charCodeAt(end - 1);
|
|
1743
|
-
const firstExcluded = value.charCodeAt(end);
|
|
1744
|
-
if (lastIncluded >= 0xd800 &&
|
|
1745
|
-
lastIncluded <= 0xdbff &&
|
|
1746
|
-
firstExcluded >= 0xdc00 &&
|
|
1747
|
-
firstExcluded <= 0xdfff)
|
|
1748
|
-
end--;
|
|
1749
|
-
return `${value.slice(0, end)}…`;
|
|
1750
|
-
}
|
|
1751
1737
|
function marAgentAgentId(value) {
|
|
1752
1738
|
const data = structuredRecord(value);
|
|
1753
1739
|
if (data === undefined)
|
|
@@ -1798,7 +1784,7 @@ function imageArtifacts(event) {
|
|
|
1798
1784
|
const name = typeof artifact?.name === 'string' ? compactRunnerText(artifact.name, 255) : '';
|
|
1799
1785
|
const byteSize = nonnegativeInteger(artifact?.byteSize);
|
|
1800
1786
|
if (artifact?.kind !== 'image' ||
|
|
1801
|
-
artifact.mimeType !== 'image/png' ||
|
|
1787
|
+
(artifact.mimeType !== 'image/png' && artifact.mimeType !== 'image/gif') ||
|
|
1802
1788
|
typeof artifact.sha256 !== 'string' ||
|
|
1803
1789
|
!/^[a-f0-9]{64}$/iu.test(artifact.sha256) ||
|
|
1804
1790
|
name.length === 0 ||
|
|
@@ -1810,8 +1796,8 @@ function imageArtifacts(event) {
|
|
|
1810
1796
|
fileName === '..' ||
|
|
1811
1797
|
fileName.includes('/') ||
|
|
1812
1798
|
fileName.includes('\\') ||
|
|
1813
|
-
!fileName.
|
|
1799
|
+
!fileName.endsWith(artifact.mimeType === 'image/gif' ? '.gif' : '.png'))
|
|
1814
1800
|
return [];
|
|
1815
|
-
return [{ name, mime:
|
|
1801
|
+
return [{ name, mime: artifact.mimeType, byteSize, fileName }];
|
|
1816
1802
|
});
|
|
1817
1803
|
}
|
|
@@ -19,7 +19,7 @@ export function createImageGenerationExtensions(input) {
|
|
|
19
19
|
{
|
|
20
20
|
name: codex ? 'codex-imagegen' : 'byted_ark-imagegen',
|
|
21
21
|
description: codex
|
|
22
|
-
? 'Generate or edit
|
|
22
|
+
? 'Generate or edit PNG images and looping GIFs with codex_image_gen only when explicitly requested. Load before calling the image tool.'
|
|
23
23
|
: 'Generate or edit one image with byted_ark_image_gen only when explicitly requested. Load before calling the image tool.',
|
|
24
24
|
requiredTools: [tool.name],
|
|
25
25
|
load: async () => codex
|
|
@@ -26,7 +26,7 @@ export const codexImageGenToolDefinition = {
|
|
|
26
26
|
agentScope: 'ROOT_ONLY',
|
|
27
27
|
allowedCallers: ['direct', 'programmatic'],
|
|
28
28
|
parallelSafety: 'safe',
|
|
29
|
-
description: '
|
|
29
|
+
description: 'Generate or edit one PNG or simple looping GIF with the configured Codex Images API, only when requested or explicitly authorized. First load skill({name:"codex-imagegen"}); reuse it while in context. Omit selectors only for generation without references; otherwise use up to five absolute referenced_image_paths OR the smallest sufficient num_last_images_to_include from this Execution. Set transparent_background for transparency and gif for 2–16 animation frames. Returns a managed file path, not inspected pixels.',
|
|
30
30
|
inputSchema: {
|
|
31
31
|
type: 'object',
|
|
32
32
|
properties: {
|
|
@@ -44,6 +44,19 @@ export const codexImageGenToolDefinition = {
|
|
|
44
44
|
minimum: 1,
|
|
45
45
|
maximum: MAX_EDIT_IMAGES,
|
|
46
46
|
description: 'Number of most recent in-memory conversation images to edit.'
|
|
47
|
+
},
|
|
48
|
+
transparent_background: {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
description: 'Request a transparent background; defaults to false.'
|
|
51
|
+
},
|
|
52
|
+
gif: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
description: 'Request a simple looping GIF. Use {} for 9 frames at 200 ms per frame.',
|
|
55
|
+
properties: {
|
|
56
|
+
frame_count: { type: 'integer', minimum: 2, maximum: 16 },
|
|
57
|
+
frame_duration_ms: { type: 'integer', minimum: 20, maximum: 2000 }
|
|
58
|
+
},
|
|
59
|
+
additionalProperties: false
|
|
47
60
|
}
|
|
48
61
|
},
|
|
49
62
|
required: ['prompt'],
|
|
@@ -53,6 +66,10 @@ export const codexImageGenToolDefinition = {
|
|
|
53
66
|
export const bytedArkImageGenToolDefinition = {
|
|
54
67
|
...codexImageGenToolDefinition,
|
|
55
68
|
name: 'byted_ark_image_gen',
|
|
69
|
+
inputSchema: {
|
|
70
|
+
...codexImageGenToolDefinition.inputSchema,
|
|
71
|
+
properties: Object.fromEntries(Object.entries(codexImageGenToolDefinition.inputSchema.properties ?? {}).filter(([key]) => key !== 'transparent_background' && key !== 'gif'))
|
|
72
|
+
},
|
|
56
73
|
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.'
|
|
57
74
|
};
|
|
58
75
|
export async function executeCodexImageGen(arguments_, input) {
|
|
@@ -60,11 +77,11 @@ export async function executeCodexImageGen(arguments_, input) {
|
|
|
60
77
|
const capability = input.configuration.imageGeneration;
|
|
61
78
|
if (capability === undefined || capability.credentialProvider === undefined)
|
|
62
79
|
throw new AgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Image generation requires an independent credential provider.');
|
|
63
|
-
const request = parseInput(arguments_);
|
|
64
|
-
const images = await selectedEditImages(request, input);
|
|
65
80
|
const ark = capability.provider === 'BYTED_ARK';
|
|
66
81
|
if (!ark && capability.provider !== 'CODEX')
|
|
67
82
|
throw new AgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Unsupported image provider.');
|
|
83
|
+
const request = parseInput(arguments_, ark);
|
|
84
|
+
const images = await selectedEditImages(request, input);
|
|
68
85
|
const endpoint = images.length === 0 ? capability.generateEndpoint : capability.editEndpoint;
|
|
69
86
|
const body = JSON.stringify(ark
|
|
70
87
|
? {
|
|
@@ -93,11 +110,11 @@ export async function executeCodexImageGen(arguments_, input) {
|
|
|
93
110
|
}))
|
|
94
111
|
}),
|
|
95
112
|
prompt: request.prompt,
|
|
96
|
-
background: '
|
|
113
|
+
background: request.transparentBackground ? 'transparent' : 'opaque',
|
|
97
114
|
model: capability.imageModelId,
|
|
98
|
-
n: 1,
|
|
99
115
|
quality: 'auto',
|
|
100
|
-
size: 'auto'
|
|
116
|
+
size: 'auto',
|
|
117
|
+
...(request.gif === undefined ? {} : { output_format: 'gif', ...request.gif })
|
|
101
118
|
});
|
|
102
119
|
const turnId = randomUUID();
|
|
103
120
|
let reason = 'REQUEST';
|
|
@@ -136,8 +153,10 @@ export async function executeCodexImageGen(arguments_, input) {
|
|
|
136
153
|
if (!response.ok)
|
|
137
154
|
throw imageGenerationResponseError(response.status, payload);
|
|
138
155
|
const encoded = imageBase64(payload);
|
|
139
|
-
const data =
|
|
140
|
-
|
|
156
|
+
const { data, format } = decodeImage(encoded);
|
|
157
|
+
if (ark && format !== 'png')
|
|
158
|
+
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Ark image response must be a PNG.');
|
|
159
|
+
const fileName = generatedFileName(input.callId, format);
|
|
141
160
|
const directory = join(input.sessionDirectory, 'generated_images');
|
|
142
161
|
const path = join(directory, fileName);
|
|
143
162
|
const displayPath = input.displayDirectory === undefined ? path : posix.join(input.displayDirectory, fileName);
|
|
@@ -160,7 +179,7 @@ export async function executeCodexImageGen(arguments_, input) {
|
|
|
160
179
|
{
|
|
161
180
|
kind: 'image',
|
|
162
181
|
name: fileName,
|
|
163
|
-
mimeType: 'image/png',
|
|
182
|
+
mimeType: format === 'gif' ? 'image/gif' : 'image/png',
|
|
164
183
|
byteSize: data.byteLength,
|
|
165
184
|
sha256,
|
|
166
185
|
fileName
|
|
@@ -170,16 +189,21 @@ export async function executeCodexImageGen(arguments_, input) {
|
|
|
170
189
|
}
|
|
171
190
|
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_FAILED', 'Image generation failed.');
|
|
172
191
|
}
|
|
173
|
-
function parseInput(value) {
|
|
192
|
+
function parseInput(value, ark) {
|
|
174
193
|
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
175
194
|
return invalidInput();
|
|
176
195
|
const input = value;
|
|
177
196
|
const referencedImagePaths = input.referenced_image_paths === null ? undefined : input.referenced_image_paths;
|
|
178
197
|
const numLastImagesToInclude = input.num_last_images_to_include === null ? undefined : input.num_last_images_to_include;
|
|
179
|
-
|
|
198
|
+
const transparentBackground = input.transparent_background ?? false;
|
|
199
|
+
if (Object.keys(input).some((key) => key !== 'prompt' &&
|
|
200
|
+
key !== 'referenced_image_paths' &&
|
|
201
|
+
key !== 'num_last_images_to_include' &&
|
|
202
|
+
(ark || (key !== 'gif' && key !== 'transparent_background'))) ||
|
|
180
203
|
typeof input.prompt !== 'string' ||
|
|
181
204
|
input.prompt.trim().length === 0 ||
|
|
182
205
|
input.prompt.length > 32_768 ||
|
|
206
|
+
typeof transparentBackground !== 'boolean' ||
|
|
183
207
|
(referencedImagePaths !== undefined &&
|
|
184
208
|
(!Array.isArray(referencedImagePaths) ||
|
|
185
209
|
referencedImagePaths.length > MAX_EDIT_IMAGES ||
|
|
@@ -194,12 +218,32 @@ function parseInput(value) {
|
|
|
194
218
|
return invalidInput();
|
|
195
219
|
return {
|
|
196
220
|
prompt: input.prompt,
|
|
221
|
+
transparentBackground,
|
|
222
|
+
...(input.gif === undefined || input.gif === null ? {} : { gif: parseGif(input.gif) }),
|
|
197
223
|
...(paths?.length ? { referencedImagePaths: [...paths] } : {}),
|
|
198
224
|
...(numLastImagesToInclude === undefined
|
|
199
225
|
? {}
|
|
200
226
|
: { numLastImagesToInclude: numLastImagesToInclude })
|
|
201
227
|
};
|
|
202
228
|
}
|
|
229
|
+
function parseGif(value) {
|
|
230
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
231
|
+
return invalidInput();
|
|
232
|
+
const options = value;
|
|
233
|
+
if (Object.keys(options).some((key) => key !== 'frame_count' && key !== 'frame_duration_ms'))
|
|
234
|
+
return invalidInput();
|
|
235
|
+
const count = options.frame_count ?? undefined;
|
|
236
|
+
const duration = options.frame_duration_ms ?? undefined;
|
|
237
|
+
if ((count !== undefined &&
|
|
238
|
+
(!Number.isInteger(count) || count < 2 || count > 16)) ||
|
|
239
|
+
(duration !== undefined &&
|
|
240
|
+
(!Number.isInteger(duration) || duration < 20 || duration > 2000)))
|
|
241
|
+
return invalidInput();
|
|
242
|
+
return {
|
|
243
|
+
...(count === undefined ? {} : { frame_count: count }),
|
|
244
|
+
...(duration === undefined ? {} : { frame_duration_ms: duration })
|
|
245
|
+
};
|
|
246
|
+
}
|
|
203
247
|
async function selectedEditImages(request, input) {
|
|
204
248
|
if (request.referencedImagePaths !== undefined) {
|
|
205
249
|
const images = [];
|
|
@@ -310,31 +354,36 @@ function imageBase64(value) {
|
|
|
310
354
|
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.');
|
|
311
355
|
return first;
|
|
312
356
|
}
|
|
313
|
-
function
|
|
357
|
+
function decodeImage(encoded) {
|
|
314
358
|
if (encoded.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/u.test(encoded))
|
|
315
359
|
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.');
|
|
316
360
|
const data = Buffer.from(encoded, 'base64');
|
|
361
|
+
const signature = data.subarray(0, 6);
|
|
362
|
+
const format = signature.equals(Buffer.from('GIF87a')) || signature.equals(Buffer.from('GIF89a'))
|
|
363
|
+
? 'gif'
|
|
364
|
+
: 'png';
|
|
317
365
|
if (data.byteLength === 0 ||
|
|
318
366
|
data.byteLength > IMAGE_MAX_BYTES ||
|
|
319
367
|
data.toString('base64') !== encoded ||
|
|
320
|
-
!data.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE))
|
|
321
|
-
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is not a supported PNG.');
|
|
322
|
-
return data;
|
|
368
|
+
(format === 'png' && !data.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE)))
|
|
369
|
+
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is not a supported PNG or GIF.');
|
|
370
|
+
return { data, format };
|
|
323
371
|
}
|
|
324
|
-
function generatedFileName(callId) {
|
|
372
|
+
function generatedFileName(callId, format) {
|
|
325
373
|
const normalized = callId
|
|
326
374
|
.normalize('NFKC')
|
|
327
375
|
.replace(/[^a-zA-Z0-9._-]+/gu, '_')
|
|
328
376
|
.replace(/^[_\-.]+|[_\-.]+$/gu, '')
|
|
329
377
|
.slice(0, 96);
|
|
330
378
|
const stem = normalized.length > 0 ? normalized : createHash('sha256').update(callId).digest('hex');
|
|
331
|
-
const fileName = `${stem.startsWith('image_') ? stem : `image_${stem}`}
|
|
379
|
+
const fileName = `${stem.startsWith('image_') ? stem : `image_${stem}`}.${format}`;
|
|
332
380
|
return basename(fileName);
|
|
333
381
|
}
|
|
334
382
|
function imageGenerationResponseError(status, value) {
|
|
335
|
-
const
|
|
336
|
-
? value
|
|
383
|
+
const payload = value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
384
|
+
? value
|
|
337
385
|
: undefined;
|
|
386
|
+
const error = payload?.error;
|
|
338
387
|
const record = error !== null && typeof error === 'object' && !Array.isArray(error)
|
|
339
388
|
? error
|
|
340
389
|
: undefined;
|
|
@@ -343,7 +392,11 @@ function imageGenerationResponseError(status, value) {
|
|
|
343
392
|
const code = /^[a-zA-Z0-9._-]{1,80}$/u.test(rawCode)
|
|
344
393
|
? rawCode
|
|
345
394
|
: 'MAR_AGENT_IMAGE_GENERATION_FAILED';
|
|
346
|
-
const rawMessage = typeof record?.message === 'string'
|
|
395
|
+
const rawMessage = typeof record?.message === 'string'
|
|
396
|
+
? record.message.trim()
|
|
397
|
+
: typeof payload?.detail === 'string'
|
|
398
|
+
? payload.detail.trim()
|
|
399
|
+
: '';
|
|
347
400
|
const message = rawMessage.length > 0 &&
|
|
348
401
|
rawMessage.length <= 1_000 &&
|
|
349
402
|
!/\b(?:authorization|bearer|api[_ -]?key|access[_ -]?token|refresh[_ -]?token|password|secret|proxy-authorization|chatgpt-account-id)\b|socks5:\/\//iu.test(rawMessage)
|
|
@@ -78,11 +78,13 @@ export class MarAgentSessionRuntimeRegistry {
|
|
|
78
78
|
this.#assertUsable(entry, lifetime);
|
|
79
79
|
}
|
|
80
80
|
const request = nativeId === undefined ? input : { ...input, externalSessionId: nativeId };
|
|
81
|
-
|
|
82
|
-
created = await createSessionRuntime(request, nativeId ?? input.sessionId, fingerprint, configuration, this.resources, () => created !== undefined &&
|
|
81
|
+
const created = {};
|
|
82
|
+
created.runtime = await createSessionRuntime(request, nativeId ?? input.sessionId, fingerprint, configuration, this.resources, () => created.runtime !== undefined &&
|
|
83
|
+
entry.runtime === created.runtime &&
|
|
84
|
+
entry.release === undefined
|
|
83
85
|
? entry.current?.runId
|
|
84
86
|
: undefined);
|
|
85
|
-
uncommitted = created;
|
|
87
|
+
uncommitted = created.runtime;
|
|
86
88
|
this.#addAlias(entry, uncommitted.session.id);
|
|
87
89
|
nodeLog('runner.mar-agent.runtime.created', {
|
|
88
90
|
runtimeId: uncommitted.runtimeId,
|
|
@@ -65,7 +65,7 @@ async function gitExcludeTarget(workspace, includeParentRepository) {
|
|
|
65
65
|
.relative(root, await realpath(workspace))
|
|
66
66
|
.split(path.sep)
|
|
67
67
|
.join('/');
|
|
68
|
-
const escaped = relative.replace(/[
|
|
68
|
+
const escaped = relative.replace(/[\\*?[\]#! ]/g, '\\$&');
|
|
69
69
|
return { exclude: await resolveGitExclude(root), prefix: relative === '' ? '/' : `/${escaped}/` };
|
|
70
70
|
}
|
|
71
71
|
async function mutateGitExclude(workspace, mutate, includeParentRepository) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
"turndown-plugin-gfm": "^1.0.2",
|
|
43
43
|
"ws": "^8.21.3",
|
|
44
44
|
"zod": "4.4.3",
|
|
45
|
-
"@myagentroam/protocol": "0.12.
|
|
46
|
-
"@myagentroam/agent": "0.12.
|
|
45
|
+
"@myagentroam/protocol": "0.12.2",
|
|
46
|
+
"@myagentroam/agent": "0.12.2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/turndown": "^5.0.6",
|
|
50
50
|
"@types/ws": "^8.18.1",
|
|
51
51
|
"@myagentroam/code-navigation": "0.11.1",
|
|
52
|
-
"@myagentroam/marctl": "0.12.
|
|
52
|
+
"@myagentroam/marctl": "0.12.2"
|
|
53
53
|
},
|
|
54
54
|
"exports": {
|
|
55
55
|
"./config": {
|