@myagentroam/agent 0.10.68 → 0.11.1
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/extensions/tools.js +3 -0
- package/dist/agent-core/model/sse.js +5 -1
- package/dist/agent-core/prompts/workflow.js +1 -1
- package/dist/agent-core/runtime/compaction/generate.d.ts +28 -0
- package/dist/agent-core/runtime/compaction/generate.js +84 -0
- package/dist/agent-core/sdk/agent.js +30 -11
- package/dist/agent-core/sdk/session.d.ts +2 -1
- package/dist/agent-core/sdk/session.js +260 -266
- package/dist/agent-core/session/store/jsonl-store.js +11 -1
- package/dist/agent-core/session/store/records.d.ts +1 -1
- package/dist/agent-core/session/store/records.js +15 -3
- package/dist/agent-core/tools/callers.js +5 -1
- package/dist/agent-core/tools/code-mode/index.js +17 -8
- package/dist/agent-core/tools/contracts.d.ts +2 -0
- package/dist/agent-core/tools/local/apply-patch.js +1 -0
- package/dist/agent-core/tools/mcp/catalog.js +1 -0
- package/dist/agent-core/tools/mcp/tool-search.js +1 -1
- package/package.json +1 -1
|
@@ -880,6 +880,9 @@ export class JsonlSessionStore {
|
|
|
880
880
|
}
|
|
881
881
|
async #appendRecord(sessionId, input) {
|
|
882
882
|
this.#assertOpen();
|
|
883
|
+
const writer = this.#shared.writerLeases.get(sessionId);
|
|
884
|
+
if (writer?.appendFailure)
|
|
885
|
+
throw writer.appendFailure.error;
|
|
883
886
|
const before = this.#shared.index.sessions.find((item) => item.id === sessionId) ??
|
|
884
887
|
this.#shared.pendingSessions.get(sessionId);
|
|
885
888
|
if (!before)
|
|
@@ -913,7 +916,10 @@ export class JsonlSessionStore {
|
|
|
913
916
|
// The first append creates the first segment.
|
|
914
917
|
}
|
|
915
918
|
try {
|
|
916
|
-
await appendRecordBytes(path, serialized)
|
|
919
|
+
await appendRecordBytes(path, serialized, (error) => {
|
|
920
|
+
if (writer)
|
|
921
|
+
writer.appendFailure = { error };
|
|
922
|
+
});
|
|
917
923
|
}
|
|
918
924
|
catch (error) {
|
|
919
925
|
// A failed write/sync/close can leave a complete record on disk.
|
|
@@ -1232,6 +1238,8 @@ export class JsonlSessionStore {
|
|
|
1232
1238
|
if (lease) {
|
|
1233
1239
|
if (lease.executing || owner === undefined || lease.owner !== owner)
|
|
1234
1240
|
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
|
|
1241
|
+
if (lease.appendFailure)
|
|
1242
|
+
throw lease.appendFailure.error;
|
|
1235
1243
|
lease.executing = true;
|
|
1236
1244
|
}
|
|
1237
1245
|
else {
|
|
@@ -1261,6 +1269,8 @@ export class JsonlSessionStore {
|
|
|
1261
1269
|
let lease = this.#shared.writerLeases.get(sessionId);
|
|
1262
1270
|
if (lease && lease.owner !== owner)
|
|
1263
1271
|
throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
|
|
1272
|
+
if (lease?.appendFailure)
|
|
1273
|
+
throw lease.appendFailure.error;
|
|
1264
1274
|
if (!lease) {
|
|
1265
1275
|
const release = await this.#acquireSessionLease(sessionId);
|
|
1266
1276
|
lease = { owner, executing: false, retained: 0, release };
|
|
@@ -20,4 +20,4 @@ export declare function readRecordsFromTail(path: string, page?: {
|
|
|
20
20
|
readonly positions: Map<string, HistoryPagePositions>;
|
|
21
21
|
}): AsyncGenerator<SessionRecord>;
|
|
22
22
|
export declare function segmentNumberFromPath(path: string): number | undefined;
|
|
23
|
-
export declare function appendRecordBytes(path: string, serialized: string): Promise<void>;
|
|
23
|
+
export declare function appendRecordBytes(path: string, serialized: string, onWriteFailure: (error: unknown) => void): Promise<void>;
|
|
@@ -230,10 +230,11 @@ export function segmentNumberFromPath(path) {
|
|
|
230
230
|
const match = /events-(\d{6})\.jsonl$/.exec(path);
|
|
231
231
|
return match === null ? undefined : Number(match[1]);
|
|
232
232
|
}
|
|
233
|
-
export async function appendRecordBytes(path, serialized) {
|
|
233
|
+
export async function appendRecordBytes(path, serialized, onWriteFailure) {
|
|
234
|
+
const bytes = Buffer.from(serialized);
|
|
234
235
|
const handle = await open(path, 'a', 0o600);
|
|
236
|
+
let failure;
|
|
235
237
|
try {
|
|
236
|
-
const bytes = Buffer.from(serialized);
|
|
237
238
|
let offset = 0;
|
|
238
239
|
while (offset < bytes.length) {
|
|
239
240
|
const { bytesWritten } = await handle.write(bytes, offset, bytes.length - offset);
|
|
@@ -243,7 +244,18 @@ export async function appendRecordBytes(path, serialized) {
|
|
|
243
244
|
}
|
|
244
245
|
await handle.sync();
|
|
245
246
|
}
|
|
246
|
-
|
|
247
|
+
catch (error) {
|
|
248
|
+
failure = { error };
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
247
251
|
await handle.close();
|
|
248
252
|
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
failure ??= { error };
|
|
255
|
+
}
|
|
256
|
+
if (failure) {
|
|
257
|
+
// Opening/encoding failures cannot have appended bytes; write/sync/close failures can.
|
|
258
|
+
onWriteFailure(failure.error);
|
|
259
|
+
throw failure.error;
|
|
260
|
+
}
|
|
249
261
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { MarAgentError } from '../error.js';
|
|
2
2
|
/** Normalize trusted Host policy independently of discovery, Agent scope and parallel safety. */
|
|
3
3
|
export function toolCallers(tool) {
|
|
4
|
+
if (tool.codeModeExposure !== undefined && tool.codeModeExposure !== 'both')
|
|
5
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Code Mode exposure is invalid.');
|
|
4
6
|
const value = tool.allowedCallers;
|
|
5
7
|
if (value === undefined)
|
|
6
8
|
return tool.name.startsWith('mcp__') ? ['direct', 'programmatic'] : ['direct'];
|
|
@@ -31,5 +33,7 @@ export function allowsToolCaller(tool, caller) {
|
|
|
31
33
|
/** Code Mode Only changes model exposure, never the Host's underlying caller permissions. */
|
|
32
34
|
export function allowsDirectTool(tool, codeModeEnabled) {
|
|
33
35
|
return (allowsToolCaller(tool, 'direct') &&
|
|
34
|
-
(!codeModeEnabled ||
|
|
36
|
+
(!codeModeEnabled ||
|
|
37
|
+
tool.codeModeExposure === 'both' ||
|
|
38
|
+
!allowsToolCaller(tool, 'programmatic')));
|
|
35
39
|
}
|
|
@@ -21,14 +21,14 @@ import { failedToolResultExtensions } from '../result-extensions.js';
|
|
|
21
21
|
export const codeModeToolDefinition = {
|
|
22
22
|
name: 'code_exec',
|
|
23
23
|
description: `Run JavaScript to orchestrate or compose tool calls.
|
|
24
|
-
- Code Mode Only: call the programmatic tools listed below through tools.NAME
|
|
24
|
+
- Code Mode Only by default: call the programmatic tools listed below through tools.NAME, including single-tool operations. Tools explicitly exposed in both places may also be called directly; follow each tool's calling guidance. Direct-only tools remain separate model tools and cannot be called here.
|
|
25
25
|
- Run independent calls concurrently with Promise.all; await sequentially when data, side effects, or failure handling require ordering.
|
|
26
26
|
- Complete known, bounded steps and deterministic result processing in code when this avoids a model round trip. Return to the model when interpretation or a new decision is needed; do not invent extra work to fill a cell.
|
|
27
27
|
- Observe only evidence needed for the next decision or verification: use observe.result for original results, observe.text for derived values, and observe.image for image values. Preserve source and failure details needed for reasoning; keep other intermediate data in the cell.
|
|
28
28
|
- Single-tool cells are valid. Keep loops and retries bounded, and do not blindly replay operations with side effects.
|
|
29
29
|
- Evaluates source in a fresh restricted ECMAScript runtime with top-level await and Promise.all. It is not Node.js or a browser. JavaScript globals do not survive between cells; bounded JSON state is available through state.
|
|
30
30
|
- Accepts JavaScript source text. When exposed as a freeform tool, send the source directly; when exposed with a JSON input schema, put the source in its code field. Do not add another JSON wrapper, quoted string, or markdown code fence inside the source.
|
|
31
|
-
- Keep embedded commands, patches and source in JSON-escaped double-quoted strings when they contain backticks or literal \${...}; JavaScript template literals would interpolate
|
|
31
|
+
- Keep embedded commands, patches and source in JSON-escaped double-quoted strings when they contain backticks or literal \${...}; escape newlines as \\n, never put literal line breaks inside single- or double-quoted JS strings. JavaScript template literals would interpolate their contents before the tool runs. Do not paste Python literals such as None into JavaScript expressions.
|
|
32
32
|
- Use only standard ECMAScript built-ins and the explicitly documented tools, observe, state, catalog and cell namespaces. Do not use Node.js, browser or Web APIs. Module imports, filesystem and network APIs are unavailable. Obtain external capabilities only through available tools. Use tools.exec to start managed processes when permitted; use tools.exec_wait afterward to wait, write stdin or cancel.
|
|
33
33
|
|
|
34
34
|
Tool calls:
|
|
@@ -41,7 +41,7 @@ Built-in namespaces:
|
|
|
41
41
|
- observe.result(result): void registers an unmodified complete tools.NAME result or caught tool error as a trusted tool_result, using the original text and all images without repeating program data. Non-original, copied or modified values are rejected. This selects evidence for the next model response, not a log and not a new model call. Prefer observe.result for inspecting tool results.
|
|
42
42
|
- observe.text(value): void registers text/JSON values. observe.image(value): void registers one image value when image input is supported. Both accept original tool results for compatibility and use the same complete tool_result observation path; they do not select part of an original result. Extracted single images, copies and computed summaries are value observations, never trusted tool results. Observations are returned in registration order. Only explicitly observed evidence reaches the model; registration does not prove inspection. Sources and types are runtime-assigned. No out or callable observe(value) alias exists.
|
|
43
43
|
- state.get(key), state.has(key), state.set(key, value), state.delete(key) access bounded session JSON state. Mutations commit atomically only when the cell succeeds; failed or cancelled cells discard them.
|
|
44
|
-
- catalog.tools() returns a read-only
|
|
44
|
+
- 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.
|
|
45
45
|
- await cell.sleep(ms) performs a cancellable delay up to ${limits.codeSleepMaxMs} ms.
|
|
46
46
|
- await cell.yield_control() returns accumulated output to the model immediately while this cell continues running.
|
|
47
47
|
- 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.
|
|
@@ -106,7 +106,7 @@ export const codeExecWaitToolDefinition = {
|
|
|
106
106
|
function projectProgrammaticTool(tool) {
|
|
107
107
|
return {
|
|
108
108
|
name: tool.name,
|
|
109
|
-
description: `Call only as tools.${tool.name} inside code_exec, never as a direct model tool
|
|
109
|
+
description: `${allowsDirectTool(tool, true) ? `Call directly or as tools.${tool.name} inside code_exec.` : `Call only as tools.${tool.name} inside code_exec, never as a direct model tool.`}\nInside code_exec, pass a JavaScript value matching inputSchema.\n${tool.description}`,
|
|
110
110
|
inputSchema: stableToolSchema(tool.inputSchema)
|
|
111
111
|
};
|
|
112
112
|
}
|
|
@@ -121,14 +121,14 @@ export function projectCodeModeTools(tools, residentTools = tools) {
|
|
|
121
121
|
const programmatic = orderToolDefinitions(residentTools.filter((tool) => allowsToolCaller(tool, 'programmatic')));
|
|
122
122
|
const directory = JSON.stringify({
|
|
123
123
|
callableNames: programmatic.map((tool) => tool.name),
|
|
124
|
-
|
|
124
|
+
tools: programmatic.map(projectProgrammaticTool)
|
|
125
125
|
});
|
|
126
126
|
if (Buffer.byteLength(directory) > 32 * 1024)
|
|
127
127
|
throw new MarAgentError('MAR_AGENT_TOOL_EXPOSURE_LIMIT', 'Programmatic tool directory is too large.');
|
|
128
128
|
return direct.map((tool) => tool.name === 'code_exec'
|
|
129
129
|
? {
|
|
130
130
|
...tool,
|
|
131
|
-
description: `${codeModeToolDefinition.description}\nDeferred MCP definitions are returned by direct tool_search and become callable from the next model response, not later in the same response; they are not appended to this description.\nProgrammatic tools
|
|
131
|
+
description: `${codeModeToolDefinition.description}\nDeferred MCP definitions are returned by direct tool_search and become callable from the next model response, not later in the same response; they are not appended to this description.\nProgrammatic tools: ${directory}`
|
|
132
132
|
}
|
|
133
133
|
: tool);
|
|
134
134
|
}
|
|
@@ -145,14 +145,23 @@ export function projectCodeModeSearchResult(message, codeModeEnabled) {
|
|
|
145
145
|
};
|
|
146
146
|
const tools = message.toolSearchTools.map((tool) => ({
|
|
147
147
|
...(allowsToolCaller(tool, 'programmatic')
|
|
148
|
-
?
|
|
148
|
+
? {
|
|
149
|
+
...projectProgrammaticTool(tool),
|
|
150
|
+
...(allowsDirectTool(tool, true)
|
|
151
|
+
? { inputMode: tool.inputMode ?? 'json' }
|
|
152
|
+
: {})
|
|
153
|
+
}
|
|
149
154
|
: {
|
|
150
155
|
name: tool.name,
|
|
151
156
|
description: tool.description,
|
|
152
157
|
inputSchema: stableToolSchema(tool.inputSchema),
|
|
153
158
|
inputMode: tool.inputMode ?? 'json'
|
|
154
159
|
}),
|
|
155
|
-
caller: allowsToolCaller(tool, 'programmatic')
|
|
160
|
+
caller: allowsToolCaller(tool, 'programmatic')
|
|
161
|
+
? allowsDirectTool(tool, true)
|
|
162
|
+
? 'both'
|
|
163
|
+
: 'programmatic'
|
|
164
|
+
: 'direct'
|
|
156
165
|
}));
|
|
157
166
|
return {
|
|
158
167
|
...message,
|
|
@@ -10,6 +10,8 @@ export interface ClientToolDefinition {
|
|
|
10
10
|
outputSchema?: Record<string, unknown>;
|
|
11
11
|
/** Runtime-only policy. MCP defaults to both callers; other tools default to direct only. */
|
|
12
12
|
allowedCallers?: readonly ToolCaller[];
|
|
13
|
+
/** Runtime-only exposure override; retains both permitted entry points with Code Mode enabled. */
|
|
14
|
+
codeModeExposure?: 'both';
|
|
13
15
|
inputMode?: 'json' | 'freeform';
|
|
14
16
|
parallelSafety?: 'safe' | 'serial';
|
|
15
17
|
/** Preserved business-schema eligibility after framework metadata projection. */
|
|
@@ -9,6 +9,7 @@ import { parsePatch } from './apply-patch-parser.js';
|
|
|
9
9
|
export const applyPatchToolDefinition = {
|
|
10
10
|
name: 'apply_patch',
|
|
11
11
|
allowedCallers: ['direct', 'programmatic'],
|
|
12
|
+
codeModeExposure: 'both',
|
|
12
13
|
parallelSafety: 'serial',
|
|
13
14
|
description: `Preferred for precise, reviewable local edits. Do not batch all task files merely to reduce tool calls. Group only a few tightly coupled edits whose combined patch is quick to generate and review; split broad or independent work into successive bounded patches before composing them. Submit a raw *** Begin Patch / *** End Patch patch, each marker on its own line. Surrounding marker whitespace and an optional trailing *** are accepted; incomplete markers or extra text are rejected. Add File content lines, including blank lines, must start with +. Syntax errors include line/column and a bounded caret diagnostic with file content omitted. Supports Add File, Update File, Delete File, and Move to. Add File may replace an existing regular file, and Move to may replace an existing regular destination; duplicate operations on the same resolved path remain invalid. Update hunks accept @@ or @@ <change context>. Context-only hunks, including trailing hunks, and unchanged file sections are validated in order but are not written or reported as edits. The patch must include a change or move. Context is searched forward with Codex-style exact, whitespace-tolerant, then common Unicode punctuation-tolerant matching. Syntax and resolved paths are validated before writing, then file units are committed in order. Each file unit is atomic, and a Move keeps its source and destination atomic. On MAR_AGENT_PATCH_PARTIAL, earlier files remain applied; the error lists applied, failed, and pending paths, so retry only failed and pending paths. Existing file permissions, line endings, and final-newline state are preserved. Returns changed file paths and operations with per-file and total line counts when all units succeed. In Code Mode, the Promise resolves on success and rejects with code/message on failure; the returned envelope has data.files and data.totals, not an ok or success flag. Oversized patches are rejected without advertising defensive limits as recommended patch sizes. A first-unit MAR_AGENT_PATCH_CONFLICT applies nothing; hunk match conflicts identify the affected path and hunk so you can reread only that file before retrying.`,
|
|
14
15
|
inputMode: 'freeform',
|
|
@@ -150,6 +150,7 @@ export function assertMcpToolDefinition(value, options = {}) {
|
|
|
150
150
|
(!Array.isArray(value.allowedCallers) ||
|
|
151
151
|
!value.allowedCallers.length ||
|
|
152
152
|
value.allowedCallers.some((caller) => caller !== 'direct' && caller !== 'programmatic'))) ||
|
|
153
|
+
(value.codeModeExposure !== undefined && value.codeModeExposure !== 'both') ||
|
|
153
154
|
(value.inputMode !== undefined && value.inputMode !== 'json'))
|
|
154
155
|
invalid();
|
|
155
156
|
assertJson(value);
|
|
@@ -14,7 +14,7 @@ export const TOOL_SEARCH_LIMITS = Object.freeze({
|
|
|
14
14
|
});
|
|
15
15
|
export const toolSearchToolDefinition = {
|
|
16
16
|
name: 'tool_search',
|
|
17
|
-
description: 'Search deferred MCP metadata and return matching tool definitions for the next model call. Use the full callable name for exact selection, or keywords for BM25 search. Never executes the discovered tools. With Code Mode Only, programmatic tools
|
|
17
|
+
description: 'Search deferred MCP metadata and return matching tool definitions for the next model call. Use the full callable name for exact selection, or keywords for BM25 search. Never executes the discovered tools. With Code Mode Only, programmatic tools use code_exec unless explicitly exposed for both direct and programmatic calls. Already exposed tools need not be searched again. The selected definitions must fit the exposure budget or the search fails atomically.',
|
|
18
18
|
inputSchema: {
|
|
19
19
|
type: 'object',
|
|
20
20
|
properties: {
|