@myagentroam/agent 0.11.28 → 0.11.29
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/agent-core/tools/code-mode/cells.js +4 -2
- package/dist/agent-core/tools/code-mode/index.js +5 -2
- package/dist/agent-core/tools/local/exec.js +6 -4
- package/dist/agent-core/tools/runtime-resource-handles.d.ts +21 -0
- package/dist/agent-core/tools/runtime-resource-handles.js +28 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
import { permissionStopFromError } from '../permissions.js';
|
|
2
|
+
import { RuntimeResourceHandles } from '../runtime-resource-handles.js';
|
|
3
3
|
import { MarAgentError, describeMarAgentError } from '../../error.js';
|
|
4
4
|
import { truncateModelText } from '../../runtime/token-budget.js';
|
|
5
5
|
import { TOOL_EXECUTION_LIMITS as limits } from '../execution-limits.js';
|
|
@@ -39,6 +39,7 @@ function options(value, wait) {
|
|
|
39
39
|
/** Session-owned execution, separate from the cancellable model-tool wait. */
|
|
40
40
|
export class CodeCells {
|
|
41
41
|
#cells = new Map();
|
|
42
|
+
#handles = new RuntimeResourceHandles('cell', (id) => this.#cells.has(id));
|
|
42
43
|
#closed = false;
|
|
43
44
|
#disposing = new Set();
|
|
44
45
|
async start(value, context, run) {
|
|
@@ -75,7 +76,7 @@ export class CodeCells {
|
|
|
75
76
|
throw new MarAgentError('MAR_AGENT_PROCESS_LIMIT', `The Local Host shared running-cell limit (${limits.codeMaxConcurrentCells}) was reached.`);
|
|
76
77
|
const controller = new AbortController();
|
|
77
78
|
const cell = {
|
|
78
|
-
cellId:
|
|
79
|
+
cellId: this.#handles.create(),
|
|
79
80
|
sessionId: context.sessionId,
|
|
80
81
|
executionId: context.executionId,
|
|
81
82
|
controller,
|
|
@@ -160,6 +161,7 @@ export class CodeCells {
|
|
|
160
161
|
async wait(value, context) {
|
|
161
162
|
const input = options(value, true);
|
|
162
163
|
context.signal.throwIfAborted();
|
|
164
|
+
this.#handles.assertType(input.cellId);
|
|
163
165
|
const cell = this.#cells.get(input.cellId);
|
|
164
166
|
if (!cell || cell.sessionId !== context.sessionId)
|
|
165
167
|
throw new MarAgentError('MAR_AGENT_CODE_CELL_NOT_FOUND', 'Code Exec cell is unavailable in this Session.');
|
|
@@ -45,7 +45,7 @@ Built-in namespaces:
|
|
|
45
45
|
- catalog.tools() returns a read-only array of the tools callable in this cell, including name, description and inputSchema. It does not discover or authorize additional tools.
|
|
46
46
|
- await cell.sleep(ms) performs a cancellable delay up to ${limits.codeSleepMaxMs} ms.
|
|
47
47
|
- await cell.yield_control() returns accumulated output to the model immediately while this cell continues running.
|
|
48
|
-
- Optionally start source with // @options: {"yieldMs": ${limits.codeDefaultYieldMs}}. yieldMs defaults to ${limits.codeDefaultYieldMs} ms and controls this call's wait window (0..${limits.codeMaxYieldMs}), not nested tool timeouts or wait windows. A running result includes cellId; use direct code_exec_wait for new output, completion or termination.
|
|
48
|
+
- Optionally start source with // @options: {"yieldMs": ${limits.codeDefaultYieldMs}}. yieldMs defaults to ${limits.codeDefaultYieldMs} ms and controls this call's wait window (0..${limits.codeMaxYieldMs}), not nested tool timeouts or wait windows. A running result includes cellId (c_ plus 12 lowercase hex characters); use direct code_exec_wait for new output, completion or termination.
|
|
49
49
|
- waitOutcome separates snapshot, completion, timeout, and yield from the cell status. A foreground steer returns interrupted with reason=steer without stopping the cell or its internal tool waits.
|
|
50
50
|
- Registered cells are Session warm resources: ending or cancelling the model Execution cancels its wait, not the cell. Reuse retained cellId; do not start duplicate work. Host cleanup or explicit termination stops the cell.
|
|
51
51
|
- tools, observe, state, catalog and cell are frozen bindings. Do not shadow their names.
|
|
@@ -84,7 +84,10 @@ export const codeExecWaitToolDefinition = {
|
|
|
84
84
|
inputSchema: {
|
|
85
85
|
type: 'object',
|
|
86
86
|
properties: {
|
|
87
|
-
cellId: {
|
|
87
|
+
cellId: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
description: 'Copy the complete cellId returned by code_exec (c_ followed by 12 lowercase hex characters). Never use a processId (p_); use exec_wait for processes.'
|
|
90
|
+
},
|
|
88
91
|
yieldMs: {
|
|
89
92
|
type: 'integer',
|
|
90
93
|
minimum: 0,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RuntimeResourceHandles } from '../runtime-resource-handles.js';
|
|
2
2
|
import { StringDecoder } from 'node:string_decoder';
|
|
3
3
|
import { MarAgentError } from '../../error.js';
|
|
4
4
|
import { directLocalProcessExecutor, signalProcessTree } from '../../host/local-process-executor.js';
|
|
@@ -30,7 +30,7 @@ export const execToolDefinition = {
|
|
|
30
30
|
name: 'exec',
|
|
31
31
|
allowedCallers: ['direct', 'programmatic'],
|
|
32
32
|
parallelSafety: 'safe',
|
|
33
|
-
description: 'Start a platform-native command, script, or program in workspace/default cwd. Returns a stable processId, status, bounded stdout/stderr, cumulative byte counts, dropped-byte counts, exitCode and truncated. A completed status does not imply exitCode 0; inspect stderr/exitCode. For subsequent waiting, stdin writes or cancellation, call exec_wait directly with processId, not exec. Truncation does not stop the process. Cancelling the initial wait does not terminate the process. waitOutcome distinguishes snapshot, completion, timeout, and yield. A foreground wait may return interrupted with reason=steer; the process keeps running.',
|
|
33
|
+
description: 'Start a platform-native command, script, or program in workspace/default cwd. Returns a stable processId (p_ plus 12 lowercase hex characters), status, bounded stdout/stderr, cumulative byte counts, dropped-byte counts, exitCode and truncated. A completed status does not imply exitCode 0; inspect stderr/exitCode. For subsequent waiting, stdin writes or cancellation, call exec_wait directly with processId, not exec. Truncation does not stop the process. Cancelling the initial wait does not terminate the process. waitOutcome distinguishes snapshot, completion, timeout, and yield. A foreground wait may return interrupted with reason=steer; the process keeps running.',
|
|
34
34
|
outputSchema: {
|
|
35
35
|
type: 'object',
|
|
36
36
|
required: ['content', 'data'],
|
|
@@ -112,7 +112,7 @@ export const execWaitToolDefinition = {
|
|
|
112
112
|
processId: {
|
|
113
113
|
type: 'string',
|
|
114
114
|
minLength: 1,
|
|
115
|
-
description: '
|
|
115
|
+
description: 'Copy the complete processId returned by exec (p_ followed by 12 lowercase hex characters). Never use a cellId (c_); use code_exec_wait for cells.'
|
|
116
116
|
},
|
|
117
117
|
action: { enum: ['poll', 'write', 'cancel'], default: 'poll' },
|
|
118
118
|
yieldMs: {
|
|
@@ -154,6 +154,7 @@ export class ExecTool {
|
|
|
154
154
|
processExecutor;
|
|
155
155
|
#processes = new Map();
|
|
156
156
|
#completed = new Map();
|
|
157
|
+
#handles = new RuntimeResourceHandles('process', (id) => this.#processes.has(id) || this.#completed.has(id));
|
|
157
158
|
#startingProcesses = new Set();
|
|
158
159
|
constructor(paths, maxOutputBytes = TOOL_EXECUTION_LIMITS.execDefaultMaxOutputBytes, maxProcesses = TOOL_EXECUTION_LIMITS.maxProcesses, environment = process.env, shell, processExecutor = directLocalProcessExecutor) {
|
|
159
160
|
this.paths = paths;
|
|
@@ -168,6 +169,7 @@ export class ExecTool {
|
|
|
168
169
|
throw executionAbortReason(signal);
|
|
169
170
|
const input = parseExecInput(arguments_);
|
|
170
171
|
if ('processId' in input) {
|
|
172
|
+
this.#handles.assertType(input.processId);
|
|
171
173
|
const process = this.#processes.get(input.processId);
|
|
172
174
|
const completed = this.#completed.get(input.processId);
|
|
173
175
|
if (completed) {
|
|
@@ -294,7 +296,7 @@ export class ExecTool {
|
|
|
294
296
|
details: { reasonCode: error?.code ?? 'UNKNOWN' }
|
|
295
297
|
});
|
|
296
298
|
}
|
|
297
|
-
const id =
|
|
299
|
+
const id = this.#handles.create();
|
|
298
300
|
let resolveExited;
|
|
299
301
|
let resolveSettled;
|
|
300
302
|
const managed = {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare const types: {
|
|
2
|
+
readonly cell: {
|
|
3
|
+
readonly prefix: "c_";
|
|
4
|
+
readonly field: "cellId";
|
|
5
|
+
readonly tool: "code_exec_wait";
|
|
6
|
+
};
|
|
7
|
+
readonly process: {
|
|
8
|
+
readonly prefix: "p_";
|
|
9
|
+
readonly field: "processId";
|
|
10
|
+
readonly tool: "exec_wait";
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/** Typed runtime handles; resource owners retain registration and lifecycle authority. */
|
|
14
|
+
export declare class RuntimeResourceHandles {
|
|
15
|
+
private readonly kind;
|
|
16
|
+
private readonly isReserved;
|
|
17
|
+
constructor(kind: keyof typeof types, isReserved: (id: string) => boolean);
|
|
18
|
+
create(): string;
|
|
19
|
+
assertType(id: string): void;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { MarAgentError } from '../error.js';
|
|
3
|
+
const types = {
|
|
4
|
+
cell: { prefix: 'c_', field: 'cellId', tool: 'code_exec_wait' },
|
|
5
|
+
process: { prefix: 'p_', field: 'processId', tool: 'exec_wait' }
|
|
6
|
+
};
|
|
7
|
+
/** Typed runtime handles; resource owners retain registration and lifecycle authority. */
|
|
8
|
+
export class RuntimeResourceHandles {
|
|
9
|
+
kind;
|
|
10
|
+
isReserved;
|
|
11
|
+
constructor(kind, isReserved) {
|
|
12
|
+
this.kind = kind;
|
|
13
|
+
this.isReserved = isReserved;
|
|
14
|
+
}
|
|
15
|
+
create() {
|
|
16
|
+
let id;
|
|
17
|
+
do {
|
|
18
|
+
id = `${types[this.kind].prefix}${randomBytes(6).toString('hex')}`;
|
|
19
|
+
} while (this.isReserved(id));
|
|
20
|
+
return id;
|
|
21
|
+
}
|
|
22
|
+
assertType(id) {
|
|
23
|
+
const expected = types[this.kind];
|
|
24
|
+
const actual = Object.values(types).find((type) => id.startsWith(type.prefix));
|
|
25
|
+
if (actual && actual !== expected)
|
|
26
|
+
throw new MarAgentError('MAR_AGENT_RESOURCE_HANDLE_TYPE_MISMATCH', `Expected ${expected.field} (${expected.prefix}...). This looks like ${actual.field}; use ${actual.tool} for that resource.`, { details: { expected: expected.field, received: actual.field, tool: actual.tool } });
|
|
27
|
+
}
|
|
28
|
+
}
|