@myagentroam/agent 0.9.134 → 0.9.136

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.
Files changed (69) hide show
  1. package/dist/cli/main.js +10 -1
  2. package/dist/host/local-agent-host.d.ts +9 -7
  3. package/dist/host/local-agent-host.js +77 -5
  4. package/dist/host/local-process-executor.d.ts +2 -0
  5. package/dist/index.d.ts +4 -2
  6. package/dist/index.js +1 -0
  7. package/dist/model/catalog-file.d.ts +4 -0
  8. package/dist/model/configuration.d.ts +13 -0
  9. package/dist/model/configuration.js +11 -0
  10. package/dist/model/contracts.d.ts +7 -0
  11. package/dist/model/openai-responses.d.ts +1 -1
  12. package/dist/model/openai-responses.js +90 -21
  13. package/dist/prompts/workflow.js +2 -0
  14. package/dist/runtime/context-gc-runtime.d.ts +1 -0
  15. package/dist/runtime/context-gc-runtime.js +3 -0
  16. package/dist/runtime/context-gc-tokens.d.ts +1 -0
  17. package/dist/runtime/context-gc-tokens.js +1 -1
  18. package/dist/runtime/context-gc.d.ts +1 -0
  19. package/dist/runtime/context-gc.js +85 -8
  20. package/dist/runtime/context-projection.js +19 -2
  21. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +29 -2
  22. package/dist/sdk/agent.js +212 -25
  23. package/dist/sdk/mcp-catalog.d.ts +13 -0
  24. package/dist/sdk/mcp-catalog.js +219 -0
  25. package/dist/sdk/types.d.ts +50 -1
  26. package/dist/session/context-gc-checkpoint.d.ts +7 -1
  27. package/dist/session/context-gc-checkpoint.js +28 -2
  28. package/dist/session/context-gc-replacements.d.ts +7 -0
  29. package/dist/session/context-gc-replacements.js +26 -0
  30. package/dist/session/jsonl-store.d.ts +6 -0
  31. package/dist/session/jsonl-store.js +92 -54
  32. package/dist/subagent/scheduler.d.ts +3 -3
  33. package/dist/subagent/session-controller.d.ts +1 -1
  34. package/dist/tools/agent-cancel.js +1 -0
  35. package/dist/tools/agent-message.js +1 -0
  36. package/dist/tools/agent-start.js +1 -0
  37. package/dist/tools/agent-wait.js +1 -0
  38. package/dist/tools/apply-patch.js +2 -1
  39. package/dist/tools/callers.d.ts +4 -0
  40. package/dist/tools/callers.js +18 -0
  41. package/dist/tools/code-mode/engine.d.ts +43 -0
  42. package/dist/tools/code-mode/engine.js +1 -0
  43. package/dist/tools/code-mode/quickjs-engine.d.ts +2 -0
  44. package/dist/tools/code-mode/quickjs-engine.js +454 -0
  45. package/dist/tools/code-mode/state.d.ts +20 -0
  46. package/dist/tools/code-mode/state.js +82 -0
  47. package/dist/tools/code-mode/worker.d.ts +1 -0
  48. package/dist/tools/code-mode/worker.js +65 -0
  49. package/dist/tools/code-mode.d.ts +5 -3
  50. package/dist/tools/code-mode.js +129 -120
  51. package/dist/tools/exec.js +1 -0
  52. package/dist/tools/execution-limits.d.ts +8 -0
  53. package/dist/tools/execution-limits.js +8 -0
  54. package/dist/tools/local-registry.d.ts +2 -0
  55. package/dist/tools/local-registry.js +1 -1
  56. package/dist/tools/question.js +1 -0
  57. package/dist/tools/read.js +1 -0
  58. package/dist/tools/registry.js +3 -0
  59. package/dist/tools/runtime-context.d.ts +2 -0
  60. package/dist/tools/runtime.js +3 -1
  61. package/dist/tools/skill.js +1 -0
  62. package/dist/tools/todo-read.js +1 -0
  63. package/dist/tools/todo-write.js +1 -0
  64. package/dist/tools/tool-search.d.ts +43 -0
  65. package/dist/tools/tool-search.js +278 -0
  66. package/dist/tools/view-image.js +1 -0
  67. package/dist/tools/web-fetch.js +1 -0
  68. package/dist/tools/web-search.js +1 -0
  69. package/package.json +3 -1
@@ -1,5 +1,5 @@
1
1
  import { ContextGcCheckpointCache, contextGcBaseFromRecords, contextGcLogicalPathKey, contextGcMinimumUsageGrowth, contextGcRecordSourceHash, contextGcTargetKey, normalizeContextGcReplacements } from '../session/context-gc-checkpoint.js';
2
- import { contextGcApplyPatchInputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcStaleReadReplacement, contextGcWebOutputReplacement, normalizeContextGcExecResult } from '../session/context-gc-replacements.js';
2
+ import { contextGcApplyPatchInputReplacement, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcStaleReadReplacement, contextGcWebOutputReplacement, normalizeContextGcCodeModeInput, normalizeContextGcExecResult } from '../session/context-gc-replacements.js';
3
3
  import { contextGcSubagentNeedsRetainedPriorFinal, contextGcSubagentOutputReplacement, contextGcSubagentPriorFinalIsRetained, contextGcSubagentSnapshotIsPreciselyCovered, contextGcSubagentSnapshotIsSuperseded, contextGcSubagentSnapshotTurnId, normalizeContextGcSubagentSnapshot } from '../session/context-gc-subagent.js';
4
4
  import { parseReadContextOutput } from '../tools/read-context.js';
5
5
  import { CONTEXT_GC_TOKEN_ESTIMATOR, contextGcCacheImpact, contextGcMessageTokens, contextGcPrefixTokens, contextGcReclaimedTokens } from './context-gc-tokens.js';
@@ -167,8 +167,12 @@ export function inspectContextGc(input) {
167
167
  });
168
168
  if (stageAPlan?.economics.passes) {
169
169
  const exec = collectExecCandidates(input, groups, existingTargets, usageRatio);
170
+ const codeMode = collectCodeModeCandidates(input, groups, existingTargets, usageRatio);
170
171
  const web = collectWebCandidates(input, groups, existingTargets, usageRatio);
171
- const protectedCandidateTokens = deterministicProtectedTokens + exec.protectedTokens + web.protectedTokens;
172
+ const protectedCandidateTokens = deterministicProtectedTokens +
173
+ exec.protectedTokens +
174
+ codeMode.protectedTokens +
175
+ web.protectedTokens;
172
176
  const stageAPlanWithProtectedCandidates = evaluateCandidateSet(input, existing, deterministicCandidates, {
173
177
  stage: 'STAGE_A',
174
178
  contextUsageTokens,
@@ -177,7 +181,7 @@ export function inspectContextGc(input) {
177
181
  normalRolloutOrdinal,
178
182
  protectedCandidateTokens
179
183
  }) ?? stageAPlan;
180
- const marginal = mergeMarginalLargeOutputCandidates(input, existing, deterministicCandidates, [...exec.eligible, ...web.eligible], stageAPlanWithProtectedCandidates, {
184
+ const marginal = mergeMarginalLargeOutputCandidates(input, existing, deterministicCandidates, [...exec.eligible, ...codeMode.eligible, ...web.eligible], stageAPlanWithProtectedCandidates, {
181
185
  contextUsageTokens,
182
186
  usageRatio,
183
187
  usageRecord,
@@ -185,7 +189,7 @@ export function inspectContextGc(input) {
185
189
  protectedCandidateTokens
186
190
  });
187
191
  const plan = finalizePlan(input, marginal ?? stageAPlanWithProtectedCandidates);
188
- const retryAfterRollouts = Math.min(exec.retryAfterRollouts, web.retryAfterRollouts);
192
+ const retryAfterRollouts = Math.min(exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts);
189
193
  return plan
190
194
  ? {
191
195
  plan,
@@ -196,26 +200,99 @@ export function inspectContextGc(input) {
196
200
  : {};
197
201
  }
198
202
  const exec = collectExecCandidates(input, groups, existingTargets, usageRatio);
203
+ const codeMode = collectCodeModeCandidates(input, groups, existingTargets, usageRatio);
199
204
  const web = collectWebCandidates(input, groups, existingTargets, usageRatio);
200
- const combined = evaluateCandidateSet(input, existing, [...deterministicCandidates, ...exec.eligible, ...web.eligible], {
205
+ const combined = evaluateCandidateSet(input, existing, [...deterministicCandidates, ...exec.eligible, ...codeMode.eligible, ...web.eligible], {
201
206
  stage: 'STAGE_A_AND_EXEC',
202
207
  contextUsageTokens,
203
208
  usageRatio,
204
209
  usageRecord,
205
210
  normalRolloutOrdinal,
206
- protectedCandidateTokens: deterministicProtectedTokens + exec.protectedTokens + web.protectedTokens
211
+ protectedCandidateTokens: deterministicProtectedTokens +
212
+ exec.protectedTokens +
213
+ codeMode.protectedTokens +
214
+ web.protectedTokens
207
215
  });
208
216
  if (combined?.economics.passes) {
209
217
  const plan = finalizePlan(input, combined);
210
218
  return plan ? { plan } : {};
211
219
  }
212
- const retryAfterRollouts = Math.min(stageA.retryAfterRollouts, subagent.retryAfterRollouts, exec.retryAfterRollouts, web.retryAfterRollouts);
213
- return deterministicProtectedTokens + exec.protectedTokens + web.protectedTokens >=
220
+ const retryAfterRollouts = Math.min(stageA.retryAfterRollouts, subagent.retryAfterRollouts, exec.retryAfterRollouts, codeMode.retryAfterRollouts, web.retryAfterRollouts);
221
+ return deterministicProtectedTokens +
222
+ exec.protectedTokens +
223
+ codeMode.protectedTokens +
224
+ web.protectedTokens >=
214
225
  contextGcMinimumReclaimedTokens(contextUsageTokens, input.contextWindowTokens) &&
215
226
  Number.isFinite(retryAfterRollouts)
216
227
  ? { retryAfterRollouts }
217
228
  : {};
218
229
  }
230
+ function collectCodeModeCandidates(input, groups, existingTargets, usageRatio) {
231
+ const eligible = [];
232
+ let protectedTokens = 0;
233
+ let retryAfterRollouts = Infinity;
234
+ for (const group of [...groups.values()].filter((item) => item.toolName === 'code_mode')) {
235
+ if (!group.call ||
236
+ !group.result ||
237
+ group.failed ||
238
+ !group.completed ||
239
+ group.call.message.role !== 'tool_call' ||
240
+ group.result.message.role !== 'tool' ||
241
+ typeof group.result.message.content !== 'string')
242
+ continue;
243
+ const code = normalizeContextGcCodeModeInput(group.call.message.arguments);
244
+ const completedPayload = group.completed.payload;
245
+ if (!code || !isRecord(completedPayload) || typeof completedPayload.truncated !== 'boolean')
246
+ continue;
247
+ const visibleTokens = contextGcMessageTokens(input, [
248
+ group.call.message,
249
+ group.result.message,
250
+ ...group.aliases.map((alias) => alias.message)
251
+ ]);
252
+ const minimumVisibleTokens = usageRatio >= COMPACT_CREDIT_ACTIVATION_RATIO
253
+ ? HIGH_WATER_LARGE_TERMINAL_OUTPUT_TOKENS
254
+ : LARGE_TERMINAL_OUTPUT_TOKENS;
255
+ if (visibleTokens < minimumVisibleTokens)
256
+ continue;
257
+ const callTarget = targetFor(group.call.record, group.call.message);
258
+ const resultTarget = targetFor(group.result.record, group.result.message);
259
+ if (!callTarget || !resultTarget)
260
+ continue;
261
+ const evidenceSequence = group.result.record.sequence;
262
+ const replacements = [];
263
+ if (!existingTargets.has(contextGcTargetKey(callTarget)))
264
+ replacements.push({
265
+ kind: 'TOOL_CALL_INPUT',
266
+ toolName: 'code_mode',
267
+ callId: group.callId,
268
+ target: callTarget,
269
+ replacementArguments: contextGcCodeModeInputReplacement(code),
270
+ providerAliasTargets: group.aliases
271
+ .map(({ record, message }) => targetFor(record, message))
272
+ .filter((target) => target !== undefined),
273
+ evidenceSequence
274
+ });
275
+ if (!existingTargets.has(contextGcTargetKey(resultTarget)))
276
+ replacements.push({
277
+ kind: 'CODE_MODE_OUTPUT',
278
+ callId: group.callId,
279
+ target: resultTarget,
280
+ replacementContent: contextGcCodeModeOutputReplacement(group.result.message.content, completedPayload.truncated),
281
+ evidenceSequence
282
+ });
283
+ if (replacements.length === 0)
284
+ continue;
285
+ const observedRollouts = successfulRolloutsAfter(input.records, evidenceSequence, input.modelId, input.contextWindowTokens);
286
+ const candidate = { replacements, observedRollouts, evidenceSequence };
287
+ if (observedRollouts >= CONTEXT_GC_POLICY.largeExecProtectionRollouts)
288
+ eligible.push(candidate);
289
+ else {
290
+ protectedTokens += candidateTokens(input, candidate);
291
+ retryAfterRollouts = Math.min(retryAfterRollouts, CONTEXT_GC_POLICY.largeExecProtectionRollouts - observedRollouts);
292
+ }
293
+ }
294
+ return { eligible, protectedTokens, retryAfterRollouts };
295
+ }
219
296
  function mergeMarginalLargeOutputCandidates(input, existing, deterministicCandidates, execCandidates, stageAPlan, state) {
220
297
  let selected = [...deterministicCandidates];
221
298
  let current = stageAPlan;
@@ -61,6 +61,7 @@ export function applyContextGcReplacementsToMessages(messages, messageSources, r
61
61
  else if (replacement.kind === 'TOOL_CALL_INPUT' && next.role === 'tool_call')
62
62
  next = { ...next, arguments: structuredClone(replacement.replacementArguments) };
63
63
  else if ((replacement.kind === 'EXEC_OUTPUT' ||
64
+ replacement.kind === 'CODE_MODE_OUTPUT' ||
64
65
  replacement.kind === 'SUBAGENT_OUTPUT' ||
65
66
  replacement.kind === 'WEB_OUTPUT') &&
66
67
  next.role === 'tool')
@@ -117,7 +118,19 @@ export function restoreModelContext(payload, providerModelId) {
117
118
  if (payload.role === 'tool' &&
118
119
  typeof payload.content === 'string' &&
119
120
  typeof payload.callId === 'string')
120
- return { role: 'tool', content: payload.content, callId: payload.callId };
121
+ return {
122
+ role: 'tool',
123
+ content: payload.content,
124
+ callId: payload.callId,
125
+ ...(Array.isArray(payload.toolSearchTools)
126
+ ? {
127
+ toolSearchTools: payload.toolSearchTools.filter((tool) => isRecord(tool) &&
128
+ typeof tool.name === 'string' &&
129
+ typeof tool.description === 'string' &&
130
+ isRecord(tool.inputSchema))
131
+ }
132
+ : {})
133
+ };
121
134
  if (payload.role === 'tool_call' &&
122
135
  typeof payload.callId === 'string' &&
123
136
  typeof payload.name === 'string')
@@ -164,13 +177,17 @@ export function restoreModelContext(payload, providerModelId) {
164
177
  export function providerToolCallId(message) {
165
178
  if (message.role !== 'provider' || message.provider !== 'OPENAI_RESPONSES' || !message.item)
166
179
  return undefined;
167
- if (message.item.type !== 'function_call' && message.item.type !== 'custom_tool_call')
180
+ if (message.item.type !== 'function_call' &&
181
+ message.item.type !== 'custom_tool_call' &&
182
+ message.item.type !== 'tool_search_call')
168
183
  return undefined;
169
184
  return typeof message.item.call_id === 'string' ? message.item.call_id : undefined;
170
185
  }
171
186
  export function providerToolName(message) {
172
187
  if (message.role !== 'provider' || !message.item)
173
188
  return undefined;
189
+ if (message.item.type === 'tool_search_call')
190
+ return 'tool_search';
174
191
  return typeof message.item.name === 'string' ? message.item.name : undefined;
175
192
  }
176
193
  function targetFromMessage(record, message) {
@@ -61,13 +61,14 @@ export class BubblewrapSandboxExecutor {
61
61
  validateProfile(this.options.profile);
62
62
  await validateBackend(this.#bubblewrapPath);
63
63
  const mounts = await this.#mounts();
64
+ const runtimeMounts = await trustedRuntimeMounts(request.runtimeDirectories ?? [], mounts);
64
65
  const cwd = await workingDirectory(request, mounts, this.options.profile.workingDirectory);
65
66
  const command = sandboxCommand(request, mounts);
66
67
  const launchDirectory = await mkdtemp(join(tmpdir(), 'mar-agent-bwrap-launch-'));
67
68
  const argumentPath = join(launchDirectory, 'arguments');
68
69
  const seccompPath = join(launchDirectory, 'seccomp.bpf');
69
70
  try {
70
- const arguments_ = this.#arguments(mounts, cwd, request);
71
+ const arguments_ = this.#arguments(mounts, cwd, request, runtimeMounts);
71
72
  await Promise.all([
72
73
  writeFile(argumentPath, `${arguments_.join('\0')}\0`, { mode: 0o600, flag: 'wx' }),
73
74
  writeFile(seccompPath, seccompFilter(process.arch), { mode: 0o600, flag: 'wx' })
@@ -150,7 +151,7 @@ export class BubblewrapSandboxExecutor {
150
151
  await prepareNestedMountDestinations(ordered);
151
152
  return ordered;
152
153
  }
153
- #arguments(mounts, cwd, request) {
154
+ #arguments(mounts, cwd, request, runtimeMounts) {
154
155
  const networkMode = this.options.profile.networkMode ?? 'NONE';
155
156
  const temporaryDirectory = this.options.profile.temporaryDirectory ?? { kind: 'EPHEMERAL' };
156
157
  const args = [
@@ -186,6 +187,8 @@ export class BubblewrapSandboxExecutor {
186
187
  '--tmpfs',
187
188
  '/',
188
189
  ...runtimeMountArguments(),
190
+ ...sandboxDirectoryArguments(runtimeMounts),
191
+ ...runtimeMounts.flatMap((mount) => ['--ro-bind', mount.source, mount.destination]),
189
192
  ...sandboxDirectoryArguments(mounts),
190
193
  ...(temporaryDirectory.kind === 'EPHEMERAL' ? ['--tmpfs', '/tmp'] : []),
191
194
  '--proc',
@@ -512,6 +515,30 @@ function sandboxDirectoryArguments(mounts) {
512
515
  })
513
516
  .flatMap((directory) => ['--dir', directory]);
514
517
  }
518
+ /** Host assets retain absolute module URLs, but cannot overlay or widen profile mounts. */
519
+ async function trustedRuntimeMounts(directories, mounts) {
520
+ if (!Array.isArray(directories) || directories.length > 16)
521
+ throw policyInvalid();
522
+ const result = [];
523
+ for (const path of directories) {
524
+ if (typeof path !== 'string' || path.length > 4096)
525
+ throw policyInvalid();
526
+ const source = await secureDirectory(path);
527
+ if (source.split('/').filter(Boolean).length < 3)
528
+ throw policyInvalid();
529
+ const overlaps = (left, right) => left === right || within(left, right) || within(right, left);
530
+ if (mounts.some((mount) => overlaps(source, mount.source) || overlaps(source, mount.destination)) ||
531
+ ['/home/mar', '/runtime', '/etc', '/proc', '/dev', '/run', '/tmp'].some((root) => overlaps(source, root)))
532
+ throw policyInvalid();
533
+ // These trees are already exposed read-only by the fixed runtime policy.
534
+ if (['/usr', '/bin', '/sbin', '/lib', '/lib64'].some((root) => within(root, source)))
535
+ continue;
536
+ if (result.some((mount) => overlaps(source, mount.source)))
537
+ throw policyInvalid();
538
+ result.push({ source, destination: source, access: 'READ_ONLY' });
539
+ }
540
+ return result;
541
+ }
515
542
  function runtimeMountArguments() {
516
543
  const args = [];
517
544
  const nodeRuntimeDirectory = resolve(process.execPath, '..', '..');