@myagentroam/node 0.11.33 → 0.11.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connector.js +1 -0
- package/dist/database.js +3 -1
- package/dist/marctl-guidance.js +2 -2
- package/dist/runner/mar-agent/compact-extensions.js +3 -3
- package/dist/runner/mar-agent/execution-extensions.js +3 -3
- package/dist/runner/mar-agent/hosted-conversation-runtime.js +6 -5
- package/dist/runner/mar-agent/image-generation-extensions.js +6 -6
- package/dist/runner/mar-agent/image-generation-validation.js +3 -3
- package/dist/runner/mar-agent/image-generation.js +15 -15
- package/dist/runner/mar-agent/managed-run-controller.js +2 -2
- package/dist/runner/mar-agent/mcp-client-pool.js +4 -3
- package/dist/runner/mar-agent/runtime-model-factory.js +13 -11
- package/dist/runner/mar-agent/session-runtime.js +2 -2
- package/dist/runner/mar-agent/web-tools-extensions.js +5 -5
- package/dist/runner/mar-agent-runner.js +15 -15
- package/dist/service/hosted-conversation-sandbox-profile.js +2 -2
- package/dist/service/mar-agent-model-transport-test-service.js +2 -2
- package/dist/service/model-resource-publication-service.js +3 -3
- package/dist/service/prompt-completion-generator.js +2 -2
- package/dist/service/runtime-credential-control-service.js +8 -8
- package/dist/service/web-fetch-service.js +2 -2
- package/dist/service/web-search-error.js +4 -4
- package/dist/service/web-tool-service.js +5 -5
- package/dist/service/workspace-change-service.js +34 -19
- package/dist/workspace.js +3 -0
- package/package.json +4 -4
package/dist/connector.js
CHANGED
|
@@ -914,6 +914,7 @@ export class NodeConnector {
|
|
|
914
914
|
persist: (session, nativeSessionId) => {
|
|
915
915
|
this.database?.saveNativeSessionMetadata({
|
|
916
916
|
...(session.parentSessionId == null ? {} : { parentSessionId: session.parentSessionId }),
|
|
917
|
+
initializeParent: session.externalSessionId === null,
|
|
917
918
|
runner: session.runner,
|
|
918
919
|
externalSessionId: nativeSessionId,
|
|
919
920
|
workspaceId: session.workspaceId,
|
package/dist/database.js
CHANGED
|
@@ -315,7 +315,9 @@ export class NodeDatabase {
|
|
|
315
315
|
const model = input.model === undefined ? (current?.model ?? null) : input.model;
|
|
316
316
|
const effort = input.effort === undefined ? (current?.effort ?? null) : input.effort;
|
|
317
317
|
const access = input.access === undefined ? (current?.access ?? null) : input.access;
|
|
318
|
-
const parentSessionId = current === undefined
|
|
318
|
+
const parentSessionId = current === undefined || input.initializeParent === true
|
|
319
|
+
? (current?.parentSessionId ?? input.parentSessionId ?? null)
|
|
320
|
+
: (current.parentSessionId ?? null);
|
|
319
321
|
const metadata = JSON.stringify({
|
|
320
322
|
version: 1,
|
|
321
323
|
parentSessionId,
|
package/dist/marctl-guidance.js
CHANGED
|
@@ -5,7 +5,7 @@ export function hasMarctlContext(environment) {
|
|
|
5
5
|
!!environment['MAR_CTL_NODE_ID'] &&
|
|
6
6
|
!!environment['MAR_CTL_WORKSPACE_ID']);
|
|
7
7
|
}
|
|
8
|
-
export const marctlDescription = 'You are in a MAR-managed Workspace with marctl on PATH. Load this guide to create, coordinate, message or stop independent Sessions; inspect status/history/results; change titles, prompts or models; observe and decide approvals; manage Goals or scheduled tasks; or run commands/transfer files on other authorized MAR Nodes, even when marctl is not named. When creating an independent Session, prefer --runner mar-agent unless the user or applicable workflow specifies another Runner. Ordinary local shell work and built-in subagents alone do not require it.';
|
|
8
|
+
export const marctlDescription = 'You are in a MAR-managed Workspace with marctl on PATH. Load this guide to create, coordinate, message or stop independent Sessions; inspect status/history/results; change titles, prompts or models; observe and decide approvals; manage Goals or scheduled tasks for the current or other Sessions; or run commands/transfer files on other authorized MAR Nodes, even when marctl is not named. When creating an independent Session, prefer --runner mar-agent unless the user or applicable workflow specifies another Runner. Ordinary local shell work and built-in subagents alone do not require it.';
|
|
9
9
|
export const marctlInstructions = [
|
|
10
10
|
'## MAR Workspace CLI',
|
|
11
11
|
'Use your native shell tool to run marctl. Reuse loaded guidance while it remains in context. Consult local marctl help sessions for unclear options (shared by all Session subcommands) and marctl --version when version matters; neither needs a network request. Capabilities and permissions still apply.',
|
|
@@ -22,7 +22,7 @@ export const marctlInstructions = [
|
|
|
22
22
|
'show/config/history/graph/rename, prompt and schedule get/create/update may omit the Session ID to use the current Session. Without a current Session, specify an ID. send/goal/stop/wait/approval always require explicit target IDs.',
|
|
23
23
|
'Use sessions schedule list/get/create/update for the same tasks as the built-in schedule MCP, which remains available in other Runners and is hidden in MAR Agent while its marctl skill is available. list is Workspace-scoped. create requires --cron and text (-m/--message-file, up to 20,000 characters). update requires --expected-revision from get and at least cron or text. get returns task (null if absent), with id, revision, objective, cron, timezone and enabled; applied updates also return task. Reuse that task instead of probing its shape or refetching unchanged state. Schedule text is trimmed at both ends; compare after the same trimming; a conclusive update response needs no extra get. Other-owner edits return a PENDING proposal, not an applied change. Scheduled execution uses normal user permissions. Follow existing approval rules and do not retry conflicts or uncertain writes blindly.',
|
|
24
24
|
'Read results with sessions history <session-id> --limit 10 --json (default: latest 10 SEGMENTs, not messages; --limit 1..100). In segments[].items[], select kind === "assistant_message" and read payload.text; keep segment runId and user_message.payload.clientMessageId for queued-input correlation. Reuse the returned page; follow --cursor <nextCursor> only for missing evidence.',
|
|
25
|
-
'Use sessions send <session-id> --steer -m "Additional context: focus on the resume path." --json for decision-changing updates rather than routine reports: it may switch the current execution. Node decides delivery from actual state; an idle Session receives ordinary input. Choose exactly one of --steer or --queue, even when idle; missing, conflicting, repeated, or obsolete options are rejected locally before input is read or a request is sent. Use --queue to place input in a subsequent turn without interrupting active execution. New input resumes a paused queue using ordinary send admission after cancellation settles; later explicit stops still apply; with an active Goal, ordinary queued input waits until the Goal exits. start/send return clientMessageId and the accepted/queued result. A queued result may refer to the currently active Run: associate queueItemId/clientMessageId with history before deciding that input finished. There are no queue-item edit/delete/reorder commands; show returns only queuedCount.',
|
|
25
|
+
'Use sessions send <session-id> --steer -m "Additional context: focus on the resume path." --json for time-sensitive, decision-changing updates rather than routine reports: it prioritizes the update and may switch the current execution. Node decides delivery from actual state; an idle Session receives ordinary input. Choose exactly one of --steer or --queue, even when idle; missing, conflicting, repeated, or obsolete options are rejected locally before input is read or a request is sent. Use --queue to place input in a subsequent turn without interrupting active execution; during normal active work it waits for the current execution and earlier queued work, and can be delayed by an active Goal. Do not use it when the recipient needs the information during current work. New input resumes a paused queue using ordinary send admission after cancellation settles; later explicit stops still apply; with an active Goal, ordinary queued input waits until the Goal exits. start/send return clientMessageId and the accepted/queued result. A queued result may refer to the currently active Run: associate queueItemId/clientMessageId with history before deciding that input finished. There are no queue-item edit/delete/reorder commands; show returns only queuedCount.',
|
|
26
26
|
'Wait with sessions wait <session-id> --run <run-id> --timeout 5m --json through the existing Watch. Prefer --timeout 5m when blocked on task completion; use 30s for a short deadline. Continue useful independent work before waiting. If your shell tool returns a running process or cell, reuse its process/cell handle rather than starting another wait or polling show/list. A timeout may include progress, the last observed snapshot, not a fresh query. Timeout or cancelled waiting does not stop the task. Use sessions stop <session-id> --run <run-id> --json only to stop that active Run; ownership rules still apply.',
|
|
27
27
|
'Use wait --until approval to return early when a Run needs a decision. Inspect show/history, then use sessions approval decide <session-id> --run <public-run-id> --approval <approval-id> --decision approve|reject --json for one explicitly authorized request. This does not grant authority or auto-approve tools. Acknowledgment is not completion; observe status/history and wait again as needed.',
|
|
28
28
|
'Goal get/set/clear use the existing Session Goal semantics; see marctl help sessions. A Coordinator can work from ordinary messages or its existing schedule without a Goal. Worker completion never automatically wakes it. Actively wait/read results, or finish and reconcile on the next user message or scheduled input.',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildCompactEvidence, countModelTokens, generateModelText,
|
|
1
|
+
import { buildCompactEvidence, countModelTokens, generateModelText, AgentError } from '@myagentroam/agent';
|
|
2
2
|
import { highDensityCompactionPrompt } from './compact-prompt.js';
|
|
3
3
|
import { marAgentRuntimeCompactConfigurationSchema } from '@myagentroam/protocol';
|
|
4
4
|
/** Resolve optional dedicated credentials in the same factory batch without widening the SDK catalog. */
|
|
@@ -141,7 +141,7 @@ export async function generateMarAgentCompact(context, selected, signal) {
|
|
|
141
141
|
}
|
|
142
142
|
catch (error) {
|
|
143
143
|
signal.throwIfAborted();
|
|
144
|
-
if (error instanceof
|
|
144
|
+
if (error instanceof AgentError) {
|
|
145
145
|
const status = error.details?.status;
|
|
146
146
|
failure = {
|
|
147
147
|
...failure,
|
|
@@ -150,7 +150,7 @@ export async function generateMarAgentCompact(context, selected, signal) {
|
|
|
150
150
|
...(typeof error.details?.reason === 'string' ? { errorReason: error.details.reason } : {})
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
|
-
await report('FAILED', 'MODEL_FAILED', error instanceof
|
|
153
|
+
await report('FAILED', 'MODEL_FAILED', error instanceof AgentError
|
|
154
154
|
? error.code
|
|
155
155
|
: (failure.errorCode ?? 'MAR_AGENT_COMPACT_MODEL_FAILED'));
|
|
156
156
|
return undefined;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentError } from '@myagentroam/agent';
|
|
2
2
|
/** Composes Node-owned tools without replacing another domain's execution handler. */
|
|
3
3
|
export function combineExecutionExtensions(...sources) {
|
|
4
4
|
const tools = sources.flatMap((source) => source.tools ?? []);
|
|
5
5
|
if (new Set(tools.map((tool) => tool.name)).size !== tools.length)
|
|
6
|
-
throw new
|
|
6
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Host tool names conflict.');
|
|
7
7
|
return {
|
|
8
8
|
tools,
|
|
9
9
|
skills: sources.flatMap((source) => source.skills ?? []),
|
|
@@ -14,7 +14,7 @@ export function combineExecutionExtensions(...sources) {
|
|
|
14
14
|
executeTool: async (call, context) => {
|
|
15
15
|
const source = sources.find((source) => source.tools?.some((tool) => tool.name === call.name));
|
|
16
16
|
if (!source?.executeTool)
|
|
17
|
-
throw new
|
|
17
|
+
throw new AgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Host tool is unavailable.');
|
|
18
18
|
return source.executeTool(call, context);
|
|
19
19
|
}
|
|
20
20
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { LocalAgentHost
|
|
2
|
+
import { LocalAgentHost } from '@myagentroam/agent/host';
|
|
3
|
+
import { AgentError, createAgent, generateSessionTitle, openAgentSessionCatalog } from '@myagentroam/agent';
|
|
3
4
|
import { projectMarAgentHistory } from './history-projection.js';
|
|
4
5
|
import { MarAgentConversationNormalizer } from './conversation-normalizer.js';
|
|
5
6
|
import { hostedConversationGeneratedImagesDisplayDirectory, hostedConversationImageRunId, marAgentGeneratedImageSources, marAgentGeneratedImagesDirectory } from './generated-images.js';
|
|
@@ -28,10 +29,10 @@ export class MarAgentHostedConversationRuntime {
|
|
|
28
29
|
#registerNativeImages;
|
|
29
30
|
constructor(options = {}) {
|
|
30
31
|
this.#publishModelResource = options.publishModelResource;
|
|
31
|
-
this.#createAgent = options.createAgent ??
|
|
32
|
+
this.#createAgent = options.createAgent ?? createAgent;
|
|
32
33
|
this.#generateTitle = options.generateTitle ?? generateSessionTitle;
|
|
33
34
|
this.#createHost = options.createHost ?? ((input) => new LocalAgentHost(input));
|
|
34
|
-
this.#openCatalog = options.openCatalog ??
|
|
35
|
+
this.#openCatalog = options.openCatalog ?? openAgentSessionCatalog;
|
|
35
36
|
this.#now = options.now ?? Date.now;
|
|
36
37
|
this.#onEvent = options.onEvent;
|
|
37
38
|
this.#webSearch = options.webSearch;
|
|
@@ -120,7 +121,7 @@ export class MarAgentHostedConversationRuntime {
|
|
|
120
121
|
return { lastActivityAt: Date.parse(rolledBack.lastActivityAt) };
|
|
121
122
|
}
|
|
122
123
|
catch (error) {
|
|
123
|
-
if (error instanceof
|
|
124
|
+
if (error instanceof AgentError)
|
|
124
125
|
throw new Error(error.code, { cause: error });
|
|
125
126
|
throw error;
|
|
126
127
|
}
|
|
@@ -383,7 +384,7 @@ export class MarAgentHostedConversationRuntime {
|
|
|
383
384
|
mode: 'node',
|
|
384
385
|
host,
|
|
385
386
|
models: sdkModelConfigurations(models.filter((model) => input.models.some((candidate) => candidate.id === model.id))),
|
|
386
|
-
|
|
387
|
+
extensions: marAgentCompactExtensionSource(compactState),
|
|
387
388
|
defaultModelId: 'defaultModelId' in input ? input.defaultModelId : input.modelId,
|
|
388
389
|
workspaceMapping: {
|
|
389
390
|
logicalPath: '/workspace',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { AgentError } from '@myagentroam/agent';
|
|
3
3
|
import { codexImageGenToolDefinition, bytedArkImageGenToolDefinition, executeCodexImageGen } from './image-generation.js';
|
|
4
4
|
import { marAgentGeneratedImagesDirectory } from './generated-images.js';
|
|
5
5
|
import { normalizeGeneratedImageDisplayDirectory, validateImageGenerationConfiguration } from './image-generation-validation.js';
|
|
@@ -11,7 +11,7 @@ export function createImageGenerationExtensions(input) {
|
|
|
11
11
|
const displayDirectory = normalizeGeneratedImageDisplayDirectory(input.displayDirectory);
|
|
12
12
|
const codex = imageGeneration.provider === 'CODEX';
|
|
13
13
|
if (!codex && imageGeneration.provider !== 'BYTED_ARK')
|
|
14
|
-
throw new
|
|
14
|
+
throw new AgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Unsupported image provider.');
|
|
15
15
|
const tool = codex ? codexImageGenToolDefinition : bytedArkImageGenToolDefinition;
|
|
16
16
|
return {
|
|
17
17
|
tools: [tool],
|
|
@@ -29,11 +29,11 @@ export function createImageGenerationExtensions(input) {
|
|
|
29
29
|
],
|
|
30
30
|
executeTool: async (call, context) => {
|
|
31
31
|
if (call.name !== tool.name)
|
|
32
|
-
throw new
|
|
32
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Image tool is unavailable.');
|
|
33
33
|
context.signal.throwIfAborted();
|
|
34
34
|
const directory = marAgentGeneratedImagesDirectory(input.homeDirectory, context.sessionId);
|
|
35
35
|
if (directory === undefined || call.callId === undefined)
|
|
36
|
-
throw new
|
|
36
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Image execution identity is invalid.');
|
|
37
37
|
return executeCodexImageGen(call.arguments, {
|
|
38
38
|
configuration: { imageGeneration },
|
|
39
39
|
sessionDirectory: dirname(directory),
|
|
@@ -47,10 +47,10 @@ export function createImageGenerationExtensions(input) {
|
|
|
47
47
|
.executeTool({ name: 'view_image', arguments: { path } }, context);
|
|
48
48
|
const image = output.images?.length === 1 ? output.images[0] : undefined;
|
|
49
49
|
if (image === undefined || image.dataBase64 === undefined)
|
|
50
|
-
throw new
|
|
50
|
+
throw new AgentError('MAR_AGENT_TOOL_OUTPUT_INVALID', 'Referenced image result is invalid.');
|
|
51
51
|
const data = Buffer.from(image.dataBase64, 'base64');
|
|
52
52
|
if (data.byteLength === 0)
|
|
53
|
-
throw new
|
|
53
|
+
throw new AgentError('MAR_AGENT_TOOL_OUTPUT_INVALID', 'Referenced image result is invalid.');
|
|
54
54
|
return { mimeType: image.mimeType, data };
|
|
55
55
|
}
|
|
56
56
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { posix } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { AgentError } from '@myagentroam/agent';
|
|
3
3
|
export function validateImageGenerationConfiguration(configuration) {
|
|
4
4
|
const imageGeneration = configuration.imageGeneration;
|
|
5
5
|
if (imageGeneration === undefined)
|
|
@@ -32,7 +32,7 @@ export function normalizeGeneratedImageDisplayDirectory(value) {
|
|
|
32
32
|
normalized === '/' ||
|
|
33
33
|
normalized.length > 4096 ||
|
|
34
34
|
normalized.includes('\0'))
|
|
35
|
-
throw new
|
|
35
|
+
throw new AgentError('MAR_AGENT_HOST_INCOMPATIBLE', 'Generated image display directory is invalid.');
|
|
36
36
|
return normalized;
|
|
37
37
|
}
|
|
38
38
|
function isAllowedImageGenerationEndpoint(endpoint) {
|
|
@@ -46,5 +46,5 @@ function isAllowedImageGenerationEndpoint(endpoint) {
|
|
|
46
46
|
(endpoint.protocol === 'https:' || (endpoint.protocol === 'http:' && loopback)));
|
|
47
47
|
}
|
|
48
48
|
function invalidImageGenerationConfiguration() {
|
|
49
|
-
throw new
|
|
49
|
+
throw new AgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Model image generation configuration is invalid.');
|
|
50
50
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { mkdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { basename, isAbsolute, join, posix } from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import { AgentError } from '@myagentroam/agent';
|
|
5
5
|
import { requestHttp, acquireCredential, appendCredentialHeaders } from '@myagentroam/agent/host/network';
|
|
6
6
|
const IMAGE_MAX_BYTES = 20_971_520;
|
|
7
7
|
const RESPONSE_MAX_BYTES = Math.ceil((IMAGE_MAX_BYTES * 4) / 3) + 4096;
|
|
@@ -59,12 +59,12 @@ export async function executeCodexImageGen(arguments_, input) {
|
|
|
59
59
|
input.signal.throwIfAborted();
|
|
60
60
|
const capability = input.configuration.imageGeneration;
|
|
61
61
|
if (capability === undefined || capability.credentialProvider === undefined)
|
|
62
|
-
throw new
|
|
62
|
+
throw new AgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Image generation requires an independent credential provider.');
|
|
63
63
|
const request = parseInput(arguments_);
|
|
64
64
|
const images = await selectedEditImages(request, input);
|
|
65
65
|
const ark = capability.provider === 'BYTED_ARK';
|
|
66
66
|
if (!ark && capability.provider !== 'CODEX')
|
|
67
|
-
throw new
|
|
67
|
+
throw new AgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Unsupported image provider.');
|
|
68
68
|
const endpoint = images.length === 0 ? capability.generateEndpoint : capability.editEndpoint;
|
|
69
69
|
const body = JSON.stringify(ark
|
|
70
70
|
? {
|
|
@@ -168,7 +168,7 @@ export async function executeCodexImageGen(arguments_, input) {
|
|
|
168
168
|
]
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
|
-
throw new
|
|
171
|
+
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_FAILED', 'Image generation failed.');
|
|
172
172
|
}
|
|
173
173
|
function parseInput(value) {
|
|
174
174
|
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -211,7 +211,7 @@ async function selectedEditImages(request, input) {
|
|
|
211
211
|
return [];
|
|
212
212
|
const images = input.recentImages.slice(-request.numLastImagesToInclude);
|
|
213
213
|
if (images.length !== request.numLastImagesToInclude)
|
|
214
|
-
throw new
|
|
214
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', `Requested the last ${request.numLastImagesToInclude} conversation images, but only ${images.length} were available.`);
|
|
215
215
|
return images.map(validateEditImage);
|
|
216
216
|
}
|
|
217
217
|
function validateEditImage(image) {
|
|
@@ -219,14 +219,14 @@ function validateEditImage(image) {
|
|
|
219
219
|
!Buffer.isBuffer(image.data) ||
|
|
220
220
|
image.data.byteLength === 0 ||
|
|
221
221
|
image.data.byteLength > IMAGE_MAX_BYTES)
|
|
222
|
-
throw new
|
|
222
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Referenced image is invalid or unsupported.');
|
|
223
223
|
return { mimeType: image.mimeType, data: Buffer.from(image.data) };
|
|
224
224
|
}
|
|
225
225
|
async function readJsonResponse(response, signal) {
|
|
226
226
|
const contentLength = Number(response.headers.get('content-length'));
|
|
227
227
|
if (Number.isFinite(contentLength) && contentLength > RESPONSE_MAX_BYTES) {
|
|
228
228
|
await response.body?.cancel().catch(() => undefined);
|
|
229
|
-
throw new
|
|
229
|
+
throw new AgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Image response exceeded the size limit.');
|
|
230
230
|
}
|
|
231
231
|
if (response.body === null)
|
|
232
232
|
return {};
|
|
@@ -238,7 +238,7 @@ async function readJsonResponse(response, signal) {
|
|
|
238
238
|
};
|
|
239
239
|
readSignal.addEventListener('abort', abort, { once: true });
|
|
240
240
|
const onIdle = () => {
|
|
241
|
-
readController.abort(new
|
|
241
|
+
readController.abort(new AgentError('MAR_AGENT_IMAGE_GENERATION_READ_TIMEOUT', 'Image response body read timed out waiting for data.', {
|
|
242
242
|
details: {
|
|
243
243
|
reason: 'IMAGE_RESPONSE_READ_TIMEOUT',
|
|
244
244
|
idleTimeoutMs: IMAGE_GENERATION_READ_IDLE_TIMEOUT_MS
|
|
@@ -261,7 +261,7 @@ async function readJsonResponse(response, signal) {
|
|
|
261
261
|
readTimeout = setTimeout(onIdle, IMAGE_GENERATION_READ_IDLE_TIMEOUT_MS);
|
|
262
262
|
bytes += chunk.value.byteLength;
|
|
263
263
|
if (bytes > RESPONSE_MAX_BYTES)
|
|
264
|
-
throw new
|
|
264
|
+
throw new AgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Image response exceeded the size limit.');
|
|
265
265
|
chunks.push(chunk.value);
|
|
266
266
|
}
|
|
267
267
|
}
|
|
@@ -278,7 +278,7 @@ async function readJsonResponse(response, signal) {
|
|
|
278
278
|
return JSON.parse(Buffer.concat(chunks, bytes).toString('utf8'));
|
|
279
279
|
}
|
|
280
280
|
catch (cause) {
|
|
281
|
-
throw new
|
|
281
|
+
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.', { cause });
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
async function waitForImageStep(operation, signal) {
|
|
@@ -307,18 +307,18 @@ function imageBase64(value) {
|
|
|
307
307
|
? data[0].b64_json
|
|
308
308
|
: undefined;
|
|
309
309
|
if (typeof first !== 'string' || first.length === 0 || first.length > RESPONSE_MAX_BYTES)
|
|
310
|
-
throw new
|
|
310
|
+
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.');
|
|
311
311
|
return first;
|
|
312
312
|
}
|
|
313
313
|
function decodePng(encoded) {
|
|
314
314
|
if (encoded.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/u.test(encoded))
|
|
315
|
-
throw new
|
|
315
|
+
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is invalid.');
|
|
316
316
|
const data = Buffer.from(encoded, 'base64');
|
|
317
317
|
if (data.byteLength === 0 ||
|
|
318
318
|
data.byteLength > IMAGE_MAX_BYTES ||
|
|
319
319
|
data.toString('base64') !== encoded ||
|
|
320
320
|
!data.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE))
|
|
321
|
-
throw new
|
|
321
|
+
throw new AgentError('MAR_AGENT_IMAGE_GENERATION_RESPONSE_INVALID', 'Image response is not a supported PNG.');
|
|
322
322
|
return data;
|
|
323
323
|
}
|
|
324
324
|
function generatedFileName(callId) {
|
|
@@ -349,8 +349,8 @@ function imageGenerationResponseError(status, value) {
|
|
|
349
349
|
!/\b(?:authorization|bearer|api[_ -]?key|access[_ -]?token|refresh[_ -]?token|password|secret|proxy-authorization|chatgpt-account-id)\b|socks5:\/\//iu.test(rawMessage)
|
|
350
350
|
? rawMessage
|
|
351
351
|
: fallbackMessage;
|
|
352
|
-
return new
|
|
352
|
+
return new AgentError(code, message, { details: { status } });
|
|
353
353
|
}
|
|
354
354
|
function invalidInput() {
|
|
355
|
-
throw new
|
|
355
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Image generation input is invalid.');
|
|
356
356
|
}
|
|
@@ -6,7 +6,7 @@ import { marctlExecutionEnvironment, marctlHostSkill } from './marctl-skill.js';
|
|
|
6
6
|
import { createHash, randomUUID } from 'node:crypto';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { nodeLog } from '../../operational.js';
|
|
9
|
-
import {
|
|
9
|
+
import { createAgent, generateSessionTitle } from '@myagentroam/agent';
|
|
10
10
|
import { marAgentReasoningEffortSchema, resolveMarAgentDefaultReasoningEffort } from '@myagentroam/protocol';
|
|
11
11
|
import { boundedConversationItemId } from '../../util/node-operation-parsers.js';
|
|
12
12
|
import { parseSecretEnvironment } from '../../util/secret-environment.js';
|
|
@@ -43,7 +43,7 @@ export class MarAgentManagedRunController {
|
|
|
43
43
|
...(options.retainEnvironment === undefined
|
|
44
44
|
? {}
|
|
45
45
|
: { retainEnvironment: options.retainEnvironment }),
|
|
46
|
-
createAgent: options.createAgent ??
|
|
46
|
+
createAgent: options.createAgent ?? createAgent,
|
|
47
47
|
modelFactory: options.modelFactory ??
|
|
48
48
|
new MarAgentRuntimeModelFactory({
|
|
49
49
|
resolve: async () => {
|
|
@@ -4,7 +4,8 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
|
4
4
|
import { getDefaultEnvironment, StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
5
5
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
6
6
|
import { ToolListChangedNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
-
import { assertMcpToolDefinition, assertMcpToolCatalog
|
|
7
|
+
import { assertMcpToolDefinition, assertMcpToolCatalog } from '@myagentroam/agent/host';
|
|
8
|
+
import { AgentError } from '@myagentroam/agent';
|
|
8
9
|
import { canonicalJson } from '../../util/canonical-json.js';
|
|
9
10
|
import { ExecutorStdioClientTransport } from './executor-stdio-client-transport.js';
|
|
10
11
|
export class MarAgentMcpClientCache {
|
|
@@ -175,11 +176,11 @@ export class MarAgentMcpClientPool {
|
|
|
175
176
|
async call(name, arguments_, signal, expectedTool) {
|
|
176
177
|
const binding = this.#tools.get(name);
|
|
177
178
|
if (!binding || this.#closed)
|
|
178
|
-
throw new
|
|
179
|
+
throw new AgentError('MAR_AGENT_MCP_TOOL_NOT_FOUND', 'MCP tool is no longer available.');
|
|
179
180
|
if (expectedTool &&
|
|
180
181
|
(binding.metadata.sourceIdentity !== expectedTool.sourceIdentity ||
|
|
181
182
|
binding.metadata.definitionRevision !== expectedTool.definitionRevision))
|
|
182
|
-
throw new
|
|
183
|
+
throw new AgentError('MAR_AGENT_MCP_TOOL_DEFINITION_CHANGED', 'MCP tool binding or definition changed.');
|
|
183
184
|
signal?.throwIfAborted();
|
|
184
185
|
const result = await binding.client.callTool({
|
|
185
186
|
name: binding.nativeName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentError, SecretValue } from '@myagentroam/agent';
|
|
2
2
|
import { marAgentRuntimeModelConfigurationSchema, resolveMarAgentDefaultReasoningEffort } from '@myagentroam/protocol';
|
|
3
3
|
export function sdkModelConfigurations(models) {
|
|
4
4
|
return models.map(({ imageGeneration: _imageGeneration, ...model }) => model);
|
|
@@ -84,6 +84,9 @@ export class MarAgentRuntimeModelFactory {
|
|
|
84
84
|
responsesOptionalParameters: configuration.responsesOptionalParameters
|
|
85
85
|
}),
|
|
86
86
|
responsesPreviousResponseId: configuration.responsesPreviousResponseId,
|
|
87
|
+
...(configuration.responsesConfigurationUpdates === undefined
|
|
88
|
+
? {}
|
|
89
|
+
: { responsesConfigurationUpdates: configuration.responsesConfigurationUpdates }),
|
|
87
90
|
enabled: configuration.enabled
|
|
88
91
|
};
|
|
89
92
|
const { apiKey, credential } = configuration;
|
|
@@ -95,11 +98,11 @@ export class MarAgentRuntimeModelFactory {
|
|
|
95
98
|
apiKey: new SecretValue(credential.value)
|
|
96
99
|
};
|
|
97
100
|
if (credential?.type !== 'RUNTIME_TICKET')
|
|
98
|
-
throw new
|
|
101
|
+
throw new AgentError('MAR_AGENT_RUNTIME_MODEL_CREDENTIAL_INVALID', 'The runtime model credential is invalid.');
|
|
99
102
|
if (context.runtimeScopeId === undefined ||
|
|
100
103
|
context.runtimeScopeId !== credential.ticket.runtimeScopeId ||
|
|
101
104
|
!credential.ticket.allowedModelIds.includes(configuration.id))
|
|
102
|
-
throw new
|
|
105
|
+
throw new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_SCOPE_INVALID', 'The runtime credential does not authorize this execution or model.');
|
|
103
106
|
const provider = runtimeProvider(credential, configuration.id, context, tickets, providers, this.control, this.now);
|
|
104
107
|
return {
|
|
105
108
|
...model,
|
|
@@ -117,7 +120,6 @@ export class MarAgentRuntimeModelFactory {
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
: {}),
|
|
120
|
-
responsesConfigurationUpdates: configuration.responsesConfigurationUpdates === true,
|
|
121
123
|
credentialProvider: provider
|
|
122
124
|
};
|
|
123
125
|
});
|
|
@@ -141,7 +143,7 @@ function runtimeProvider(credential, consumerId, context, tickets, providers, co
|
|
|
141
143
|
if (context.runtimeScopeId === undefined ||
|
|
142
144
|
context.runtimeScopeId !== credential.ticket.runtimeScopeId ||
|
|
143
145
|
!credential.ticket.allowedModelIds.includes(consumerId))
|
|
144
|
-
throw new
|
|
146
|
+
throw new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_SCOPE_INVALID', 'The runtime credential does not authorize this execution or model.');
|
|
145
147
|
const key = `${credential.credentialId}\0${credential.ticket.runtimeScopeId}\0${credential.ticket.purpose ?? 'MODEL'}`;
|
|
146
148
|
let state = tickets.get(key);
|
|
147
149
|
if (state === undefined) {
|
|
@@ -153,7 +155,7 @@ function runtimeProvider(credential, consumerId, context, tickets, providers, co
|
|
|
153
155
|
tickets.set(key, state);
|
|
154
156
|
}
|
|
155
157
|
else if (!sameRuntimeCredentialTicket(state.ticket, credential.ticket))
|
|
156
|
-
throw new
|
|
158
|
+
throw new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_SCOPE_INVALID', 'The runtime credential does not authorize this execution or model.');
|
|
157
159
|
const provider = new ServerRuntimeCredentialProvider(control, state, consumerId, context.runtimeScopeId, context.executionId, now);
|
|
158
160
|
providers.push(provider);
|
|
159
161
|
return provider;
|
|
@@ -179,7 +181,7 @@ class ServerRuntimeCredentialProvider {
|
|
|
179
181
|
}
|
|
180
182
|
async acquire(reason) {
|
|
181
183
|
if (this.#closed || this.state.released)
|
|
182
|
-
throw new
|
|
184
|
+
throw new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_PROVIDER_CLOSED', 'The runtime credential provider is closed.');
|
|
183
185
|
if (isReusableCredential(this.state.credential, this.now())) {
|
|
184
186
|
if (isWithinRefreshWindow(this.state.ticket.expiresAt, this.now()))
|
|
185
187
|
this.refreshAutomatically();
|
|
@@ -264,9 +266,9 @@ class ServerRuntimeCredentialProvider {
|
|
|
264
266
|
reason
|
|
265
267
|
}, signal);
|
|
266
268
|
if (this.state.released)
|
|
267
|
-
throw new
|
|
269
|
+
throw new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_PROVIDER_CLOSED', 'The runtime credential provider is closed.');
|
|
268
270
|
if ('error' in response)
|
|
269
|
-
throw new
|
|
271
|
+
throw new AgentError(response.error.code, response.error.message);
|
|
270
272
|
this.state.ticket = response.nextTicket;
|
|
271
273
|
const credential = {
|
|
272
274
|
revision: response.credential.revision,
|
|
@@ -333,11 +335,11 @@ function clearRefreshTimer(state) {
|
|
|
333
335
|
}
|
|
334
336
|
function waitForCredential(pending, signal) {
|
|
335
337
|
if (signal.aborted)
|
|
336
|
-
return Promise.reject(new
|
|
338
|
+
return Promise.reject(new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_PROVIDER_CLOSED', 'The runtime credential provider is closed.'));
|
|
337
339
|
return new Promise((resolve, reject) => {
|
|
338
340
|
const abort = () => {
|
|
339
341
|
cleanup();
|
|
340
|
-
reject(new
|
|
342
|
+
reject(new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_PROVIDER_CLOSED', 'The runtime credential provider is closed.'));
|
|
341
343
|
};
|
|
342
344
|
const cleanup = () => signal.removeEventListener('abort', abort);
|
|
343
345
|
signal.addEventListener('abort', abort, { once: true });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { access, constants } from 'node:fs/promises';
|
|
4
|
-
import { LocalAgentHost } from '@myagentroam/agent';
|
|
4
|
+
import { LocalAgentHost } from '@myagentroam/agent/host';
|
|
5
5
|
import { marAgentRuntimeCompactConfigurationSchema } from '@myagentroam/protocol';
|
|
6
6
|
import { nodeExecutionEnvironment } from '../../node-execution-environment.js';
|
|
7
7
|
import { nodeLog } from '../../operational.js';
|
|
@@ -73,7 +73,7 @@ export async function createSessionRuntime(input, sessionKey, fingerprint, hostC
|
|
|
73
73
|
host: localHost,
|
|
74
74
|
models: sdkModelConfigurations(models.filter((model) => input.models.some((candidate) => candidate.id === model.id))),
|
|
75
75
|
defaultModelId: input.model.id,
|
|
76
|
-
|
|
76
|
+
extensions: marAgentCompactExtensionSource(compactState),
|
|
77
77
|
...(input.personalInstructions ? { systemInstruction: input.personalInstructions } : {})
|
|
78
78
|
});
|
|
79
79
|
const customTitle = dependencies.customTitle(input.sessionId) ?? undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentError } from '@myagentroam/agent';
|
|
2
2
|
import { WEB_FETCH_MAX_URL_LENGTH, webFetchRequestSchema, webSearchRequestSchema } from '../../service/web-fetch-contracts.js';
|
|
3
3
|
export const webSearchToolDefinition = {
|
|
4
4
|
name: 'web_search',
|
|
@@ -49,13 +49,13 @@ export function createWebToolsExtensions(input) {
|
|
|
49
49
|
isToolAvailable,
|
|
50
50
|
executeTool: async (call, context) => {
|
|
51
51
|
if (!isToolAvailable(call.name, context))
|
|
52
|
-
throw new
|
|
52
|
+
throw new AgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Node web tool is unavailable.');
|
|
53
53
|
context.signal.throwIfAborted();
|
|
54
54
|
const parsed = call.name === 'web_search'
|
|
55
55
|
? webSearchRequestSchema.safeParse(call.arguments)
|
|
56
56
|
: webFetchRequestSchema.safeParse(call.arguments);
|
|
57
57
|
if (!parsed.success)
|
|
58
|
-
throw new
|
|
58
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Node web tool input is invalid.');
|
|
59
59
|
try {
|
|
60
60
|
if (call.name === 'web_search')
|
|
61
61
|
return await input.webSearch(webSearchRequestSchema.parse(parsed.data), context.signal);
|
|
@@ -63,9 +63,9 @@ export function createWebToolsExtensions(input) {
|
|
|
63
63
|
return await input.webFetch({ ...request, url: new URL(request.url).href }, context.signal);
|
|
64
64
|
}
|
|
65
65
|
catch (error) {
|
|
66
|
-
if (context.signal.aborted || error instanceof
|
|
66
|
+
if (context.signal.aborted || error instanceof AgentError)
|
|
67
67
|
throw error;
|
|
68
|
-
throw new
|
|
68
|
+
throw new AgentError(call.name === 'web_search' ? 'MAR_AGENT_WEB_SEARCH_FAILED' : 'MAR_AGENT_WEB_FETCH_FAILED', call.name === 'web_search' ? 'Web search failed.' : 'Web fetch failed.', { cause: error });
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { marAgentNodeModelSummarySchema, parseComposerTags, resolveMarAgentDefaultReasoningEffort, serializeComposerTag } from '@myagentroam/protocol';
|
|
2
2
|
import { nodeLog } from '../operational.js';
|
|
3
3
|
import { runnerErrorProjection } from '../util/runner-error.js';
|
|
4
|
-
import {
|
|
4
|
+
import { AgentError, openAgentSessionCatalog } from '@myagentroam/agent';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
7
|
import { AbstractRunner } from './abstract-runner.js';
|
|
@@ -94,7 +94,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
94
94
|
async readToolResultResource(session, input) {
|
|
95
95
|
if (!session.externalSessionId)
|
|
96
96
|
throw new Error('SESSION_NOT_FOUND');
|
|
97
|
-
const catalog = await
|
|
97
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
98
98
|
try {
|
|
99
99
|
return await catalog.readToolResultResource(session.externalSessionId, {
|
|
100
100
|
resourceRef: input.resourceRef,
|
|
@@ -268,7 +268,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
268
268
|
async discoverSessions(context, workspace) {
|
|
269
269
|
let catalog;
|
|
270
270
|
try {
|
|
271
|
-
catalog = await
|
|
271
|
+
catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
272
272
|
const activeCatalog = catalog;
|
|
273
273
|
const listed = await activeCatalog.list({ limit: 200 });
|
|
274
274
|
if (context.cleanup) {
|
|
@@ -336,13 +336,13 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
336
336
|
async forkNative(session, boundary, title) {
|
|
337
337
|
if (session.externalSessionId === null)
|
|
338
338
|
throw new Error('SESSION_REWIND_UNAVAILABLE');
|
|
339
|
-
const catalog = await
|
|
339
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
340
340
|
try {
|
|
341
341
|
return (await catalog.fork(session.externalSessionId, { boundaryTurnId: boundary, title }))
|
|
342
342
|
.id;
|
|
343
343
|
}
|
|
344
344
|
catch (error) {
|
|
345
|
-
if (error instanceof
|
|
345
|
+
if (error instanceof AgentError)
|
|
346
346
|
throw new Error(error.code, { cause: error });
|
|
347
347
|
throw error;
|
|
348
348
|
}
|
|
@@ -354,7 +354,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
354
354
|
if (session.externalSessionId === null)
|
|
355
355
|
throw new Error('SESSION_ROLLBACK_UNAVAILABLE');
|
|
356
356
|
await this.#releaseSessionResources?.(session.externalSessionId);
|
|
357
|
-
const catalog = await
|
|
357
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
358
358
|
try {
|
|
359
359
|
if (targetClientMessageId !== undefined && targetTurnBoundary === undefined)
|
|
360
360
|
throw new Error('SESSION_ROLLBACK_TARGET_INVALID');
|
|
@@ -366,7 +366,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
366
366
|
return Date.parse(rolledBack.lastActivityAt);
|
|
367
367
|
}
|
|
368
368
|
catch (error) {
|
|
369
|
-
if (error instanceof
|
|
369
|
+
if (error instanceof AgentError)
|
|
370
370
|
throw new Error(error.code, { cause: error });
|
|
371
371
|
throw error;
|
|
372
372
|
}
|
|
@@ -381,12 +381,12 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
381
381
|
if (session.externalSessionId === null)
|
|
382
382
|
throw new Error('SESSION_CLEAR_UNAVAILABLE');
|
|
383
383
|
await this.#releaseSessionResources?.(session.externalSessionId);
|
|
384
|
-
const catalog = await
|
|
384
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
385
385
|
try {
|
|
386
386
|
return Date.parse((await catalog.clear(session.externalSessionId)).lastActivityAt);
|
|
387
387
|
}
|
|
388
388
|
catch (error) {
|
|
389
|
-
if (error instanceof
|
|
389
|
+
if (error instanceof AgentError)
|
|
390
390
|
throw new Error(error.code, { cause: error });
|
|
391
391
|
throw error;
|
|
392
392
|
}
|
|
@@ -398,7 +398,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
398
398
|
if (session.runner !== this.name || session.externalSessionId !== nativeSessionId)
|
|
399
399
|
return false;
|
|
400
400
|
await this.#releaseSessionResources?.(nativeSessionId);
|
|
401
|
-
const catalog = await
|
|
401
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
402
402
|
try {
|
|
403
403
|
return (await catalog.delete(nativeSessionId)).deleted ? true : 'MISSING';
|
|
404
404
|
}
|
|
@@ -409,7 +409,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
409
409
|
async readManagedRunnerTitle(session, nativeSessionId) {
|
|
410
410
|
if (session.runner !== this.name || session.externalSessionId !== nativeSessionId)
|
|
411
411
|
return undefined;
|
|
412
|
-
const catalog = await
|
|
412
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
413
413
|
try {
|
|
414
414
|
let cursor;
|
|
415
415
|
do {
|
|
@@ -426,7 +426,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
426
426
|
}
|
|
427
427
|
}
|
|
428
428
|
async restartInputState(sessionId, clientMessageId) {
|
|
429
|
-
const catalog = await
|
|
429
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
430
430
|
try {
|
|
431
431
|
let cursor;
|
|
432
432
|
const states = new Map();
|
|
@@ -460,7 +460,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
460
460
|
async readOfficialConversation(session, input) {
|
|
461
461
|
if (session.externalSessionId === null)
|
|
462
462
|
return undefined;
|
|
463
|
-
const catalog = await
|
|
463
|
+
const catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
464
464
|
try {
|
|
465
465
|
const limit = input.limit ?? 10;
|
|
466
466
|
let cursor = input.cursor;
|
|
@@ -524,7 +524,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
524
524
|
return undefined;
|
|
525
525
|
let catalog;
|
|
526
526
|
try {
|
|
527
|
-
catalog = await
|
|
527
|
+
catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
528
528
|
const usage = await catalog.contextUsage(session.externalSessionId);
|
|
529
529
|
return usage === undefined
|
|
530
530
|
? undefined
|
|
@@ -545,7 +545,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
545
545
|
async #readInputImage(sessionId, sequence, imageIndex, expected) {
|
|
546
546
|
let catalog;
|
|
547
547
|
try {
|
|
548
|
-
catalog = await
|
|
548
|
+
catalog = await openAgentSessionCatalog({ homeDirectory: this.homeDirectory });
|
|
549
549
|
const image = await catalog.readImage(sessionId, { sequence, imageIndex });
|
|
550
550
|
if (image === undefined ||
|
|
551
551
|
image.name !== expected.name ||
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { LINUX_BUBBLEWRAP_SANDBOX_POLICY_VERSION } from '@myagentroam/agent/sandbox/linux';
|
|
2
|
-
import {
|
|
2
|
+
import { AgentError } from '@myagentroam/agent';
|
|
3
3
|
import { posix } from 'node:path';
|
|
4
4
|
/** Compiles Node-owned hosted-conversation directories into the generic Agent sandbox profile. */
|
|
5
5
|
export function hostedConversationSandboxProfile(input) {
|
|
6
6
|
if ((input.generatedImagesDirectory === undefined) !==
|
|
7
7
|
(input.generatedImagesLogicalPath === undefined))
|
|
8
|
-
throw new
|
|
8
|
+
throw new AgentError('SANDBOX_POLICY_INVALID', 'Sandbox policy is invalid.');
|
|
9
9
|
const mounts = [
|
|
10
10
|
{
|
|
11
11
|
source: input.workspaceDirectory,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { testAgentModelTransport } from '@myagentroam/agent';
|
|
2
2
|
import { marAgentModelTransportTestInputSchema, marAgentModelTransportTestResultSchema } from '@myagentroam/protocol';
|
|
3
3
|
import { runnerErrorProjection } from '../util/runner-error.js';
|
|
4
4
|
export class MarAgentModelTransportTestService {
|
|
@@ -24,7 +24,7 @@ export class MarAgentModelTransportTestService {
|
|
|
24
24
|
if (configuration === undefined)
|
|
25
25
|
throw new Error('MAR_AGENT_MODEL_TEST_INPUT_INVALID');
|
|
26
26
|
try {
|
|
27
|
-
const completed = await (this.options.testTransport ??
|
|
27
|
+
const { completed } = await (this.options.testTransport ?? testAgentModelTransport)(configuration, AbortSignal.timeout(this.#timeoutMs));
|
|
28
28
|
return marAgentModelTransportTestResultSchema.parse(completed
|
|
29
29
|
? { success: true, code: null, message: null, httpStatus: null }
|
|
30
30
|
: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import { AgentError, openAgentSessionCatalog } from '@myagentroam/agent';
|
|
5
5
|
import { modelResourceReadInputSchema } from '@myagentroam/protocol';
|
|
6
6
|
const TTL_MS = 24 * 60 * 60_000;
|
|
7
7
|
/** Only capability metadata lives here. The SDK owns the sole immutable image file. */
|
|
@@ -88,14 +88,14 @@ export class ModelResourcePublicationService {
|
|
|
88
88
|
record.expiresAt !== input.expiresAt ||
|
|
89
89
|
this.identity(record.source, record.resourceId) !== this.identity(source, input.resourceId))
|
|
90
90
|
return undefined;
|
|
91
|
-
const catalog = await
|
|
91
|
+
const catalog = await openAgentSessionCatalog({
|
|
92
92
|
homeDirectory: record.source.homeDirectory
|
|
93
93
|
});
|
|
94
94
|
try {
|
|
95
95
|
return await catalog.readResource(record.source.sessionId, record.resourceId);
|
|
96
96
|
}
|
|
97
97
|
catch (error) {
|
|
98
|
-
if (error instanceof
|
|
98
|
+
if (error instanceof AgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND')
|
|
99
99
|
return undefined;
|
|
100
100
|
throw error;
|
|
101
101
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { countModelTokens, generateModelText,
|
|
1
|
+
import { countModelTokens, generateModelText, agentReasoningEffortSchema } from '@myagentroam/agent';
|
|
2
2
|
const MAX_INPUT_TOKENS = 12_288;
|
|
3
3
|
const MAX_OUTPUT_TOKENS = 2_048;
|
|
4
4
|
const INPUT_CONTEXT_RESERVE_TOKENS = 64;
|
|
@@ -296,7 +296,7 @@ function validateInput(input) {
|
|
|
296
296
|
model.maxOutputTokens <= 0)
|
|
297
297
|
throw invalidInput();
|
|
298
298
|
if (input.reasoningEffort !== undefined &&
|
|
299
|
-
!
|
|
299
|
+
!agentReasoningEffortSchema.safeParse(input.reasoningEffort).success)
|
|
300
300
|
throw invalidInput();
|
|
301
301
|
if (!Array.isArray(input.history) ||
|
|
302
302
|
!Array.isArray(input.referenceMessages) ||
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentError } from '@myagentroam/agent';
|
|
2
2
|
import { createEnvelope, runtimeCredentialResolveRequestSchema, runtimeCredentialResolveResponseSchema } from '@myagentroam/protocol';
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
4
4
|
/** Correlates the closed runtime-credential exchange on the authenticated control connection. */
|
|
@@ -33,7 +33,7 @@ export class RuntimeCredentialControlService {
|
|
|
33
33
|
return true;
|
|
34
34
|
const parsed = runtimeCredentialResolveResponseSchema.safeParse(envelope.payload);
|
|
35
35
|
if (!parsed.success) {
|
|
36
|
-
pending.reject(new
|
|
36
|
+
pending.reject(new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_RESPONSE_INVALID', 'The runtime credential response is invalid.'));
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
39
|
pending.resolve(parsed.data);
|
|
@@ -41,9 +41,9 @@ export class RuntimeCredentialControlService {
|
|
|
41
41
|
}
|
|
42
42
|
resolve(request, signal) {
|
|
43
43
|
if (this.#closed)
|
|
44
|
-
return Promise.reject(new
|
|
44
|
+
return Promise.reject(new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_CONTROL_CLOSED', 'The runtime credential control service is closed.'));
|
|
45
45
|
if (!this.#connected)
|
|
46
|
-
return Promise.reject(new
|
|
46
|
+
return Promise.reject(new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_CONNECTION_INVALID', 'The runtime credential control connection is unavailable.'));
|
|
47
47
|
const parsed = runtimeCredentialResolveRequestSchema.parse(request);
|
|
48
48
|
const envelope = createEnvelope('credential.resolve', parsed);
|
|
49
49
|
const generation = this.#generation;
|
|
@@ -57,8 +57,8 @@ export class RuntimeCredentialControlService {
|
|
|
57
57
|
pending.cleanup();
|
|
58
58
|
action(value);
|
|
59
59
|
};
|
|
60
|
-
const abort = () => settle((error) => reject(error), new
|
|
61
|
-
const timer = setTimeout(() => settle((error) => reject(error), new
|
|
60
|
+
const abort = () => settle((error) => reject(error), new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_PROVIDER_CLOSED', 'The runtime credential provider is closed.'));
|
|
61
|
+
const timer = setTimeout(() => settle((error) => reject(error), new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_REQUEST_TIMEOUT', 'The runtime credential request timed out.')), this.#timeoutMs);
|
|
62
62
|
timer.unref();
|
|
63
63
|
this.#pending.set(envelope.id, {
|
|
64
64
|
generation,
|
|
@@ -75,7 +75,7 @@ export class RuntimeCredentialControlService {
|
|
|
75
75
|
this.options.send(envelope);
|
|
76
76
|
}
|
|
77
77
|
catch (error) {
|
|
78
|
-
settle((cause) => reject(cause), new
|
|
78
|
+
settle((cause) => reject(cause), new AgentError('MAR_AGENT_RUNTIME_CREDENTIAL_REQUEST_SEND_FAILED', 'The runtime credential request could not be sent.', { cause: error }));
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
}
|
|
@@ -89,6 +89,6 @@ export class RuntimeCredentialControlService {
|
|
|
89
89
|
}
|
|
90
90
|
#rejectPending(code) {
|
|
91
91
|
for (const pending of [...this.#pending.values()])
|
|
92
|
-
pending.reject(new
|
|
92
|
+
pending.reject(new AgentError(code, 'The runtime credential control connection is no longer valid.'));
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -4,13 +4,13 @@ import { TLSSocket } from 'node:tls';
|
|
|
4
4
|
import { createBrotliDecompress, createGunzip, createInflate } from 'node:zlib';
|
|
5
5
|
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
7
|
-
import {
|
|
7
|
+
import { AgentError } from '@myagentroam/agent';
|
|
8
8
|
import { chromium, errors } from 'playwright';
|
|
9
9
|
import { WEB_FETCH_MAX_BYTES, webFetchRequestSchema } from './web-fetch-contracts.js';
|
|
10
10
|
const TIMEOUT_MS = 30_000;
|
|
11
11
|
const MAX_REDIRECTS = 5;
|
|
12
12
|
const MAX_BROWSER_REQUESTS = 2;
|
|
13
|
-
export class WebFetchError extends
|
|
13
|
+
export class WebFetchError extends AgentError {
|
|
14
14
|
code;
|
|
15
15
|
status;
|
|
16
16
|
publicMessage;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentError } from '@myagentroam/agent';
|
|
2
2
|
import { httpErrorResponseSchema } from '@myagentroam/protocol';
|
|
3
3
|
export async function readWebSearchError(response) {
|
|
4
4
|
let payload;
|
|
@@ -6,13 +6,13 @@ export async function readWebSearchError(response) {
|
|
|
6
6
|
payload = await response.json();
|
|
7
7
|
}
|
|
8
8
|
catch {
|
|
9
|
-
return new
|
|
9
|
+
return new AgentError('MAR_AGENT_WEB_SEARCH_FAILED', 'Web search failed.', {
|
|
10
10
|
details: { status: response.status }
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
const parsed = httpErrorResponseSchema.safeParse(payload);
|
|
14
14
|
if (!parsed.success)
|
|
15
|
-
return new
|
|
15
|
+
return new AgentError('MAR_AGENT_WEB_SEARCH_FAILED', 'Web search failed.', {
|
|
16
16
|
details: { status: response.status }
|
|
17
17
|
});
|
|
18
18
|
const failure = parsed.data.error;
|
|
@@ -26,7 +26,7 @@ export async function readWebSearchError(response) {
|
|
|
26
26
|
...(failure.details === undefined ? [] : [`details: ${failure.details}`]),
|
|
27
27
|
...identifiers
|
|
28
28
|
].join('; ');
|
|
29
|
-
return new
|
|
29
|
+
return new AgentError(failure.code, detail.length === 0 ? failure.message : `${failure.message} (${detail})`, {
|
|
30
30
|
details: {
|
|
31
31
|
status: response.status,
|
|
32
32
|
requestId: failure.requestId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentError } from '@myagentroam/agent';
|
|
2
2
|
import { readWebSearchError } from './web-search-error.js';
|
|
3
3
|
import { WebFetchService } from './web-fetch-service.js';
|
|
4
4
|
import { webFetchResultSchema } from './web-fetch-contracts.js';
|
|
@@ -21,7 +21,7 @@ export class WebToolService {
|
|
|
21
21
|
});
|
|
22
22
|
const parsed = webFetchResultSchema.safeParse(result);
|
|
23
23
|
if (!parsed.success)
|
|
24
|
-
throw new
|
|
24
|
+
throw new AgentError('MAR_AGENT_WEB_FETCH_FAILED', 'Web fetch returned an invalid result.');
|
|
25
25
|
return parsed.data;
|
|
26
26
|
};
|
|
27
27
|
close() {
|
|
@@ -59,14 +59,14 @@ export class WebToolService {
|
|
|
59
59
|
return { content, data: result };
|
|
60
60
|
}
|
|
61
61
|
catch (error) {
|
|
62
|
-
if (error instanceof
|
|
62
|
+
if (error instanceof AgentError || signal.aborted)
|
|
63
63
|
throw error;
|
|
64
|
-
throw new
|
|
64
|
+
throw new AgentError('MAR_AGENT_WEB_SEARCH_FAILED', 'Web search failed.', {
|
|
65
65
|
cause: error
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
function invalidResult() {
|
|
71
|
-
return new
|
|
71
|
+
return new AgentError('MAR_AGENT_WEB_SEARCH_FAILED', 'Web search returned an invalid result.');
|
|
72
72
|
}
|
|
@@ -8,6 +8,7 @@ const REPOSITORY_READ_CONCURRENCY = 4;
|
|
|
8
8
|
const WORKTREE_READ_CONCURRENCY = 4;
|
|
9
9
|
const CHANGE_VISIBILITY_CONCURRENCY = 16;
|
|
10
10
|
const SUBMODULE_LIMIT = 128;
|
|
11
|
+
const CHANGE_RESULT_LIMIT = 1000;
|
|
11
12
|
export class WorkspaceChangeService {
|
|
12
13
|
state;
|
|
13
14
|
constructor(state) {
|
|
@@ -92,6 +93,7 @@ export class WorkspaceChangeService {
|
|
|
92
93
|
...(summary.tracking === undefined ? {} : { tracking: summary.tracking }),
|
|
93
94
|
dirty: summary.changes.length > 0,
|
|
94
95
|
status: summary.changes.length > 0 ? 'DIRTY' : 'CLEAN',
|
|
96
|
+
...(summary.changesTruncated ? { changesTruncated: true } : {}),
|
|
95
97
|
changes
|
|
96
98
|
};
|
|
97
99
|
};
|
|
@@ -190,6 +192,7 @@ export class WorkspaceChangeService {
|
|
|
190
192
|
branch: repository.branch,
|
|
191
193
|
...(repository.tracking === undefined ? {} : { tracking: repository.tracking }),
|
|
192
194
|
status: repository.dirty ? 'DIRTY' : 'CLEAN',
|
|
195
|
+
...(repository.changesTruncated ? { changesTruncated: true } : {}),
|
|
193
196
|
changes: repository.changes
|
|
194
197
|
};
|
|
195
198
|
}
|
|
@@ -229,6 +232,33 @@ export class WorkspaceChangeService {
|
|
|
229
232
|
binary: false,
|
|
230
233
|
diffAvailable: true
|
|
231
234
|
});
|
|
235
|
+
let remainingChanges = CHANGE_RESULT_LIMIT;
|
|
236
|
+
const repositories = [
|
|
237
|
+
{
|
|
238
|
+
...root,
|
|
239
|
+
source: 'ROOT',
|
|
240
|
+
physicalPath: currentWorktree?.path ?? repositoryRoot,
|
|
241
|
+
current: true,
|
|
242
|
+
...(currentWorktree === undefined ? {} : { head: currentWorktree.head }),
|
|
243
|
+
status: root.dirty ? 'DIRTY' : 'CLEAN',
|
|
244
|
+
locked: currentWorktree?.locked ?? false,
|
|
245
|
+
prunable: currentWorktree?.prunable ?? false
|
|
246
|
+
},
|
|
247
|
+
...children.map((repository) => ({ ...repository, source: 'SUBMODULE' })),
|
|
248
|
+
...linkedRepositories
|
|
249
|
+
].map(({ dirty: _dirty, ...repository }) => {
|
|
250
|
+
void _dirty;
|
|
251
|
+
const changes = repository.changes.slice(0, remainingChanges).map(decorate);
|
|
252
|
+
remainingChanges -= changes.length;
|
|
253
|
+
return {
|
|
254
|
+
...repository,
|
|
255
|
+
changes,
|
|
256
|
+
...(('changesTruncated' in repository && repository.changesTruncated) ||
|
|
257
|
+
changes.length < repository.changes.length
|
|
258
|
+
? { changesTruncated: true }
|
|
259
|
+
: {})
|
|
260
|
+
};
|
|
261
|
+
});
|
|
232
262
|
const value = {
|
|
233
263
|
repositoryRoot,
|
|
234
264
|
workspace: {
|
|
@@ -240,27 +270,12 @@ export class WorkspaceChangeService {
|
|
|
240
270
|
partial: errors.length > 0 ||
|
|
241
271
|
children.some((group) => group.status === 'UNAVAILABLE') ||
|
|
242
272
|
linkedRepositories.some((group) => group.status === 'UNAVAILABLE') ||
|
|
243
|
-
worktreeList.truncated
|
|
273
|
+
worktreeList.truncated ||
|
|
274
|
+
repositories.some((group) => group.changesTruncated === true),
|
|
244
275
|
...(errors.length === 0 ? {} : { errors }),
|
|
245
276
|
branch: root.branch,
|
|
246
|
-
changes:
|
|
247
|
-
repositories
|
|
248
|
-
{
|
|
249
|
-
...root,
|
|
250
|
-
source: 'ROOT',
|
|
251
|
-
physicalPath: currentWorktree?.path ?? repositoryRoot,
|
|
252
|
-
current: true,
|
|
253
|
-
...(currentWorktree === undefined ? {} : { head: currentWorktree.head }),
|
|
254
|
-
status: root.dirty ? 'DIRTY' : 'CLEAN',
|
|
255
|
-
locked: currentWorktree?.locked ?? false,
|
|
256
|
-
prunable: currentWorktree?.prunable ?? false
|
|
257
|
-
},
|
|
258
|
-
...children.map((repository) => ({ ...repository, source: 'SUBMODULE' })),
|
|
259
|
-
...linkedRepositories
|
|
260
|
-
].map(({ dirty: _dirty, ...repository }) => {
|
|
261
|
-
void _dirty;
|
|
262
|
-
return { ...repository, changes: repository.changes.map(decorate) };
|
|
263
|
-
}),
|
|
277
|
+
changes: repositories[0]?.changes ?? [],
|
|
278
|
+
repositories,
|
|
264
279
|
worktrees,
|
|
265
280
|
worktreesTruncated: worktreeList.truncated
|
|
266
281
|
};
|
package/dist/workspace.js
CHANGED
|
@@ -1400,6 +1400,8 @@ export async function readCurrentChangesSummary(workspacePath, isolateSubmoduleF
|
|
|
1400
1400
|
return runReadOnlyGit(workspacePath, [...args, '--ignore-submodules=dirty'], options);
|
|
1401
1401
|
});
|
|
1402
1402
|
const records = result.text.split('\0');
|
|
1403
|
+
if (result.truncated)
|
|
1404
|
+
records.pop();
|
|
1403
1405
|
const header = records.shift() ?? '';
|
|
1404
1406
|
const output = [];
|
|
1405
1407
|
for (let index = 0; index < records.length; index += 1) {
|
|
@@ -1434,6 +1436,7 @@ export async function readCurrentChangesSummary(workspacePath, isolateSubmoduleF
|
|
|
1434
1436
|
branch,
|
|
1435
1437
|
...(head === undefined ? {} : { head }),
|
|
1436
1438
|
...(tracking === undefined ? {} : { tracking }),
|
|
1439
|
+
...(result.truncated ? { changesTruncated: true } : {}),
|
|
1437
1440
|
changes: output
|
|
1438
1441
|
};
|
|
1439
1442
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.35",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"sharp": "0.35.5",
|
|
39
39
|
"ws": "^8.21.3",
|
|
40
40
|
"zod": "4.4.3",
|
|
41
|
-
"@myagentroam/agent": "0.11.
|
|
42
|
-
"@myagentroam/protocol": "0.11.33",
|
|
41
|
+
"@myagentroam/agent": "0.11.35",
|
|
43
42
|
"@myagentroam/code-navigation": "0.11.0",
|
|
44
|
-
"@myagentroam/marctl": "0.11.
|
|
43
|
+
"@myagentroam/marctl": "0.11.2",
|
|
44
|
+
"@myagentroam/protocol": "0.11.35"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/ws": "^8.18.1"
|