@myagentroam/agent 0.9.85 → 0.9.86
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/model/contracts.d.ts +0 -1
- package/dist/model/openai-responses.js +18 -8
- package/dist/runtime/context-gc.js +3 -1
- package/dist/sdk/agent.js +1 -2
- package/dist/session/context-gc-replacements.js +5 -1
- package/dist/tools/exec.d.ts +1 -1
- package/dist/tools/exec.js +63 -37
- package/package.json +1 -1
|
@@ -35,9 +35,14 @@ export class OpenAiResponsesAdapter {
|
|
|
35
35
|
if (this.#cacheLeased ||
|
|
36
36
|
this.#cachedState.lastRequest !== undefined ||
|
|
37
37
|
this.#cachedState.lastResponse !== undefined ||
|
|
38
|
-
!
|
|
38
|
+
!responsesPersistedContinuationEnabled(this.#configuration) ||
|
|
39
39
|
checkpoint.version !== 2 ||
|
|
40
40
|
checkpoint.protocol !== 'OPENAI_RESPONSES' ||
|
|
41
|
+
typeof checkpoint.responseId !== 'string' ||
|
|
42
|
+
checkpoint.responseId.length === 0 ||
|
|
43
|
+
checkpoint.responseId.length > 1024 ||
|
|
44
|
+
!Number.isSafeInteger(checkpoint.inputPrefixLength) ||
|
|
45
|
+
checkpoint.inputPrefixLength < 0 ||
|
|
41
46
|
checkpoint.configurationHash !== responsesContinuationIdentityHash(this.#configuration))
|
|
42
47
|
return false;
|
|
43
48
|
this.#cachedState.restoredContinuation = checkpoint;
|
|
@@ -656,7 +661,7 @@ class OpenAiResponsesTurnSession {
|
|
|
656
661
|
clearResponsesContinuation(this.state);
|
|
657
662
|
}
|
|
658
663
|
continuationCheckpoint() {
|
|
659
|
-
if (!
|
|
664
|
+
if (!responsesPersistedContinuationEnabled(this.adapter.configuration))
|
|
660
665
|
return undefined;
|
|
661
666
|
const request = this.state.lastRequest;
|
|
662
667
|
const response = this.state.lastResponse;
|
|
@@ -668,8 +673,7 @@ class OpenAiResponsesTurnSession {
|
|
|
668
673
|
protocol: 'OPENAI_RESPONSES',
|
|
669
674
|
configurationHash: responsesContinuationIdentityHash(this.adapter.configuration),
|
|
670
675
|
responseId: response.responseId,
|
|
671
|
-
inputPrefixLength: baseline.length
|
|
672
|
-
inputPrefixHash: hashResponsesValue(baseline)
|
|
676
|
+
inputPrefixLength: baseline.length
|
|
673
677
|
};
|
|
674
678
|
}
|
|
675
679
|
prepareRequest(request) {
|
|
@@ -885,10 +889,7 @@ function incrementalResponsesInput(state, current) {
|
|
|
885
889
|
const completion = state.lastResponse;
|
|
886
890
|
if (previous === undefined || completion === undefined) {
|
|
887
891
|
const restored = state.restoredContinuation;
|
|
888
|
-
if (restored === undefined ||
|
|
889
|
-
current.input.length < restored.inputPrefixLength ||
|
|
890
|
-
restored.inputPrefixHash !==
|
|
891
|
-
hashResponsesValue(current.input.slice(0, restored.inputPrefixLength)))
|
|
892
|
+
if (restored === undefined || current.input.length < restored.inputPrefixLength)
|
|
892
893
|
return undefined;
|
|
893
894
|
state.lastResponse = { responseId: restored.responseId, outputItems: [] };
|
|
894
895
|
return current.input.slice(restored.inputPrefixLength);
|
|
@@ -920,6 +921,9 @@ function responsesContinuationEnabled(configuration) {
|
|
|
920
921
|
return (configuration.responsesPreviousResponseId === true &&
|
|
921
922
|
configuration.responsesTransport?.transport === 'WEBSOCKET');
|
|
922
923
|
}
|
|
924
|
+
function responsesPersistedContinuationEnabled(configuration) {
|
|
925
|
+
return responsesContinuationEnabled(configuration) && configuration.responsesEncoding !== 'LITE';
|
|
926
|
+
}
|
|
923
927
|
function responsesCredentialFingerprint(credential) {
|
|
924
928
|
return hashResponsesValue({
|
|
925
929
|
bearer: credential.bearer.reveal(),
|
|
@@ -1460,11 +1464,17 @@ function isInvalidPreviousResponseFields(code, message) {
|
|
|
1460
1464
|
normalizedCode === 'invalid_previous_response_id')
|
|
1461
1465
|
return true;
|
|
1462
1466
|
const normalizedMessage = message?.toLowerCase().replaceAll('`', '') ?? '';
|
|
1467
|
+
if ((normalizedCode === 'unsupported_parameter' ||
|
|
1468
|
+
normalizedCode === 'unsupported_previous_response_id') &&
|
|
1469
|
+
normalizedMessage.includes('previous_response_id'))
|
|
1470
|
+
return true;
|
|
1463
1471
|
return (normalizedMessage.includes('previous_response_id') &&
|
|
1464
1472
|
(normalizedMessage.includes('invalid') ||
|
|
1465
1473
|
normalizedMessage.includes('not found') ||
|
|
1466
1474
|
normalizedMessage.includes('does not exist') ||
|
|
1467
1475
|
normalizedMessage.includes('expired') ||
|
|
1476
|
+
normalizedMessage.includes('not supported') ||
|
|
1477
|
+
normalizedMessage.includes('unsupported') ||
|
|
1468
1478
|
normalizedMessage.includes('unavailable')));
|
|
1469
1479
|
}
|
|
1470
1480
|
const RESPONSE_ITEM_TYPES = new Set([
|
|
@@ -632,7 +632,9 @@ function collectExecCandidates(input, groups, existingTargets, usageRatio) {
|
|
|
632
632
|
}
|
|
633
633
|
for (const startGroup of execGroups) {
|
|
634
634
|
const start = invocations.get(startGroup.callId);
|
|
635
|
-
if (!start ||
|
|
635
|
+
if (!start ||
|
|
636
|
+
(start.input.action !== 'start' &&
|
|
637
|
+
!(start.input.action == null && typeof start.input.command === 'string')))
|
|
636
638
|
continue;
|
|
637
639
|
const lifecycle = [start];
|
|
638
640
|
let processId;
|
package/dist/sdk/agent.js
CHANGED
|
@@ -1605,8 +1605,7 @@ function isModelContinuationCheckpoint(value) {
|
|
|
1605
1605
|
isBoundedString(checkpoint.configurationHash, 64) &&
|
|
1606
1606
|
isBoundedString(checkpoint.responseId, 1_024) &&
|
|
1607
1607
|
Number.isSafeInteger(checkpoint.inputPrefixLength) &&
|
|
1608
|
-
checkpoint.inputPrefixLength >= 0
|
|
1609
|
-
isBoundedString(checkpoint.inputPrefixHash, 64));
|
|
1608
|
+
checkpoint.inputPrefixLength >= 0);
|
|
1610
1609
|
}
|
|
1611
1610
|
function isBoundedString(value, maximumLength) {
|
|
1612
1611
|
return typeof value === 'string' && value.length > 0 && value.length <= maximumLength;
|
|
@@ -74,7 +74,11 @@ export function contextGcWebOutputReplacement(toolName, originalContent, truncat
|
|
|
74
74
|
export function contextGcExecInputReplacement(arguments_, formatVersion = 3) {
|
|
75
75
|
if (!isRecord(arguments_))
|
|
76
76
|
return `<context_gc kind="terminal_exec_input"${formatVersion === 3 ? ' formatVersion="3"' : ''} originalBytes="${Buffer.byteLength(stableJson(arguments_))}"${formatVersion === 3 ? '' : ` originalSha256="${sha256(stableJson(arguments_))}"`} />`;
|
|
77
|
-
const action = typeof arguments_.action === 'string'
|
|
77
|
+
const action = typeof arguments_.action === 'string'
|
|
78
|
+
? arguments_.action
|
|
79
|
+
: typeof arguments_.command === 'string'
|
|
80
|
+
? 'start'
|
|
81
|
+
: 'unknown';
|
|
78
82
|
const original = stableJson(arguments_);
|
|
79
83
|
const retained = {
|
|
80
84
|
action,
|
package/dist/tools/exec.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ClientToolDefinition } from '../model/contracts.js';
|
|
|
2
2
|
import { type LocalProcessExecutor } from '../host/local-process-executor.js';
|
|
3
3
|
import { WorkspacePathResolver } from './shared/path-resolver.js';
|
|
4
4
|
export type ExecInput = {
|
|
5
|
-
action
|
|
5
|
+
action?: 'start';
|
|
6
6
|
command: string;
|
|
7
7
|
cwd?: string;
|
|
8
8
|
timeoutMs?: number;
|
package/dist/tools/exec.js
CHANGED
|
@@ -5,8 +5,8 @@ import { MarAgentError } from '../error.js';
|
|
|
5
5
|
import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
|
|
6
6
|
export const execToolDefinition = {
|
|
7
7
|
name: 'exec',
|
|
8
|
-
parallelSafety: '
|
|
9
|
-
description: 'Unrestricted general system execution.
|
|
8
|
+
parallelSafety: 'safe',
|
|
9
|
+
description: 'Unrestricted general system execution. Providing command starts any platform-native command, script, text/file tool, builder, generator, network client, or program in workspace/default cwd; action may be omitted for start. poll waits up to yieldMs for new output or termination and returns only output produced since the prior result; write sends stdin; cancel terminates the process tree. Returns status, processId, incremental stdout/stderr, cumulative stdoutBytes/stderrBytes, per-result dropped bytes, exitCode, and truncated; a completed status does not imply exitCode 0. Always inspect stderr/exitCode and consume terminal output or cancel.',
|
|
10
10
|
outputSchema: {
|
|
11
11
|
type: 'object',
|
|
12
12
|
required: ['content', 'data'],
|
|
@@ -42,8 +42,14 @@ export const execToolDefinition = {
|
|
|
42
42
|
inputSchema: {
|
|
43
43
|
type: 'object',
|
|
44
44
|
properties: {
|
|
45
|
-
action: {
|
|
46
|
-
|
|
45
|
+
action: {
|
|
46
|
+
enum: ['start', 'poll', 'write', 'cancel'],
|
|
47
|
+
description: 'Lifecycle operation. Omit when providing command to start a process.'
|
|
48
|
+
},
|
|
49
|
+
command: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Required for start; implies start when action is omitted.'
|
|
52
|
+
},
|
|
47
53
|
cwd: {
|
|
48
54
|
type: 'string',
|
|
49
55
|
description: 'Optional relative/absolute start directory; defaults to workspace.'
|
|
@@ -59,7 +65,7 @@ export const execToolDefinition = {
|
|
|
59
65
|
processId: { type: 'string', description: 'Required for poll, write and cancel.' },
|
|
60
66
|
data: { type: 'string', description: 'stdin data required for write.' }
|
|
61
67
|
},
|
|
62
|
-
required: [
|
|
68
|
+
required: [],
|
|
63
69
|
additionalProperties: false
|
|
64
70
|
}
|
|
65
71
|
};
|
|
@@ -71,6 +77,7 @@ export class ExecTool {
|
|
|
71
77
|
shell;
|
|
72
78
|
processExecutor;
|
|
73
79
|
#processes = new Map();
|
|
80
|
+
#startingProcesses = new Set();
|
|
74
81
|
constructor(paths, maxOutputBytes = TOOL_EXECUTION_LIMITS.execDefaultMaxOutputBytes, maxProcesses = TOOL_EXECUTION_LIMITS.maxProcesses, environment = process.env, shell, processExecutor = directLocalProcessExecutor) {
|
|
75
82
|
this.paths = paths;
|
|
76
83
|
this.maxOutputBytes = maxOutputBytes;
|
|
@@ -83,7 +90,7 @@ export class ExecTool {
|
|
|
83
90
|
if (signal?.aborted)
|
|
84
91
|
throw executionAbortReason(signal);
|
|
85
92
|
const input = parseExecInput(arguments_);
|
|
86
|
-
if (
|
|
93
|
+
if ('processId' in input) {
|
|
87
94
|
const process = this.#processes.get(input.processId);
|
|
88
95
|
if (!process || (owner !== undefined && process.ownerSessionId !== owner.sessionId))
|
|
89
96
|
throw new MarAgentError('MAR_AGENT_PROCESS_NOT_FOUND', 'Managed process was not found.');
|
|
@@ -98,38 +105,53 @@ export class ExecTool {
|
|
|
98
105
|
this.#processes.delete(input.processId);
|
|
99
106
|
return result;
|
|
100
107
|
}
|
|
101
|
-
if (this.#processes.size >= this.maxProcesses)
|
|
108
|
+
if (this.#processes.size + this.#startingProcesses.size >= this.maxProcesses)
|
|
102
109
|
throw new MarAgentError('MAR_AGENT_PROCESS_LIMIT', 'The managed process limit was reached.');
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
const child = await this.processExecutor.spawn({
|
|
106
|
-
command: input.command,
|
|
107
|
-
cwd: cwd.physicalPath,
|
|
108
|
-
shell: this.shell ?? true,
|
|
109
|
-
environment: this.environment,
|
|
110
|
-
detached: process.platform !== 'win32',
|
|
111
|
-
...(owner === undefined ? {} : { owner }),
|
|
112
|
-
...(signal === undefined ? {} : { signal })
|
|
113
|
-
});
|
|
114
|
-
let resolveExited;
|
|
115
|
-
let resolveSettled;
|
|
116
|
-
const managed = {
|
|
117
|
-
child,
|
|
110
|
+
let resolveStarting;
|
|
111
|
+
const starting = {
|
|
118
112
|
...(owner === undefined ? {} : { ownerSessionId: owner.sessionId }),
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
stdout: createOutputBuffer(),
|
|
122
|
-
stderr: createOutputBuffer(),
|
|
123
|
-
status: 'running',
|
|
124
|
-
exited: new Promise((resolve) => (resolveExited = resolve)),
|
|
125
|
-
settled: new Promise((resolve) => (resolveSettled = resolve)),
|
|
126
|
-
resolveExited,
|
|
127
|
-
resolveSettled,
|
|
128
|
-
exitObserved: child.exitCode !== null,
|
|
129
|
-
finalized: false,
|
|
130
|
-
waiters: new Set()
|
|
113
|
+
settled: new Promise((resolve) => (resolveStarting = resolve)),
|
|
114
|
+
resolveSettled: () => resolveStarting()
|
|
131
115
|
};
|
|
132
|
-
this.#
|
|
116
|
+
this.#startingProcesses.add(starting);
|
|
117
|
+
const id = randomUUID();
|
|
118
|
+
let child;
|
|
119
|
+
let managed;
|
|
120
|
+
try {
|
|
121
|
+
const cwd = await this.paths.resolveWithDisplay(input.cwd ?? '.');
|
|
122
|
+
child = await this.processExecutor.spawn({
|
|
123
|
+
command: input.command,
|
|
124
|
+
cwd: cwd.physicalPath,
|
|
125
|
+
shell: this.shell ?? true,
|
|
126
|
+
environment: this.environment,
|
|
127
|
+
detached: process.platform !== 'win32',
|
|
128
|
+
...(owner === undefined ? {} : { owner }),
|
|
129
|
+
...(signal === undefined ? {} : { signal })
|
|
130
|
+
});
|
|
131
|
+
let resolveExited;
|
|
132
|
+
let resolveSettled;
|
|
133
|
+
managed = {
|
|
134
|
+
child,
|
|
135
|
+
...(owner === undefined ? {} : { ownerSessionId: owner.sessionId }),
|
|
136
|
+
command: input.command,
|
|
137
|
+
cwd: cwd.displayPath,
|
|
138
|
+
stdout: createOutputBuffer(),
|
|
139
|
+
stderr: createOutputBuffer(),
|
|
140
|
+
status: 'running',
|
|
141
|
+
exited: new Promise((resolve) => (resolveExited = resolve)),
|
|
142
|
+
settled: new Promise((resolve) => (resolveSettled = resolve)),
|
|
143
|
+
resolveExited,
|
|
144
|
+
resolveSettled,
|
|
145
|
+
exitObserved: child.exitCode !== null,
|
|
146
|
+
finalized: false,
|
|
147
|
+
waiters: new Set()
|
|
148
|
+
};
|
|
149
|
+
this.#processes.set(id, managed);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
this.#startingProcesses.delete(starting);
|
|
153
|
+
starting.resolveSettled();
|
|
154
|
+
}
|
|
133
155
|
child.stdout.on('data', (chunk) => {
|
|
134
156
|
if (managed.finalized)
|
|
135
157
|
return;
|
|
@@ -182,6 +204,9 @@ export class ExecTool {
|
|
|
182
204
|
: []);
|
|
183
205
|
}
|
|
184
206
|
async disposeSession(sessionId) {
|
|
207
|
+
await Promise.all([...this.#startingProcesses]
|
|
208
|
+
.filter((process) => process.ownerSessionId === sessionId)
|
|
209
|
+
.map((process) => process.settled));
|
|
185
210
|
const owned = [...this.#processes.entries()].filter(([, process]) => process.ownerSessionId === sessionId);
|
|
186
211
|
await Promise.all(owned.map(([, process]) => process.status === 'running' ? this.#terminate(process, false) : process.settled));
|
|
187
212
|
for (const [processId] of owned)
|
|
@@ -239,6 +264,7 @@ export class ExecTool {
|
|
|
239
264
|
};
|
|
240
265
|
}
|
|
241
266
|
async dispose() {
|
|
267
|
+
await Promise.all([...this.#startingProcesses].map((process) => process.settled));
|
|
242
268
|
await Promise.all([...this.#processes.values()].map((process) => process.status === 'running' ? this.#terminate(process, false) : process.settled));
|
|
243
269
|
this.#processes.clear();
|
|
244
270
|
}
|
|
@@ -332,7 +358,7 @@ function parseExecInput(value) {
|
|
|
332
358
|
if (!value || typeof value !== 'object')
|
|
333
359
|
return invalidExecInput();
|
|
334
360
|
const input = value;
|
|
335
|
-
const action = input.action;
|
|
361
|
+
const action = input.action == null && typeof input.command === 'string' ? 'start' : input.action;
|
|
336
362
|
if (!['start', 'poll', 'write', 'cancel'].includes(String(action)))
|
|
337
363
|
return invalidExecInput();
|
|
338
364
|
const yieldMs = optionalBoundedInteger(input.yieldMs, 0, TOOL_EXECUTION_LIMITS.execMaxYieldMs);
|
|
@@ -343,7 +369,7 @@ function parseExecInput(value) {
|
|
|
343
369
|
return invalidExecInput();
|
|
344
370
|
const timeoutMs = optionalBoundedInteger(input.timeoutMs, 1, TOOL_EXECUTION_LIMITS.execMaxTimeoutMs);
|
|
345
371
|
return {
|
|
346
|
-
action,
|
|
372
|
+
...(input.action === 'start' ? { action: 'start' } : {}),
|
|
347
373
|
command: input.command,
|
|
348
374
|
...(typeof input.cwd === 'string' ? { cwd: input.cwd } : {}),
|
|
349
375
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|