@myagentroam/agent 0.9.152 → 0.9.153

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.
@@ -1,7 +1,7 @@
1
1
  import type { MarAgentEvent } from '../runtime/events.js';
2
2
  import type { ClientToolDefinition, McpToolCallContext, McpToolCatalog, McpToolCatalogContext, ToolCall, ToolExecutionContext, ToolExecutionResult } from '../tools/contracts.js';
3
3
  export declare const MAR_AGENT_HOST_API_VERSION = "1.0";
4
- export type MarAgentHostCapability = 'workspace.read' | 'workspace.patch' | 'process.manage' | 'code.execute' | 'user-input.request' | 'event.publish' | 'session.store' | 'mcp.tools' | 'image.read' | 'web.search' | 'web.fetch';
4
+ export type MarAgentHostCapability = 'workspace.read' | 'workspace.patch' | 'process.manage' | 'code.execute' | 'user-input.request' | 'event.publish' | 'session.store' | 'mcp.tools' | 'image.read';
5
5
  export interface HostDescription {
6
6
  apiVersion: string;
7
7
  mode: 'normal' | 'node';
@@ -16,6 +16,7 @@ export interface LocalAgentExecutionMcpBinding extends LocalAgentMcpBinding {
16
16
  export interface LocalAgentExecutionExtensions extends HostExecutionExtensions {
17
17
  /** Host-private process environment, snapshotted when bound to an execution. */
18
18
  readonly environment?: Readonly<Record<string, string>>;
19
+ isToolAvailable?(name: string, context: Pick<ToolExecutionContext, 'sessionId' | 'executionId' | 'modelId'>): boolean;
19
20
  executeTool?(call: ToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
20
21
  }
21
22
  export declare class LocalAgentHost implements MarAgentHost {
@@ -36,12 +37,6 @@ export declare class LocalAgentHost implements MarAgentHost {
36
37
  extensions?: LocalAgentExecutionExtensions;
37
38
  environment?: NodeJS.ProcessEnv;
38
39
  guaranteedCommands?: Readonly<Record<string, string>>;
39
- webSearch?: (input: {
40
- query: string;
41
- maxResults: number;
42
- }, signal: AbortSignal) => Promise<ToolExecutionResult>;
43
- webFetch?: boolean;
44
- allowUntrustedTls?: boolean;
45
40
  resourceIdleTtlMs?: number;
46
41
  processExecutor?: LocalProcessExecutor;
47
42
  codeModeEngineFactory?: CodeModeEngineFactory;
@@ -25,8 +25,6 @@ export class LocalAgentHost {
25
25
  #environment;
26
26
  #guaranteedCommands;
27
27
  #shell;
28
- #hasWebSearch;
29
- #hasWebFetch;
30
28
  #resourceIdleTtlMs;
31
29
  constructor(input) {
32
30
  this.#home = resolve(input.homeDirectory);
@@ -34,15 +32,9 @@ export class LocalAgentHost {
34
32
  this.#environment = input.environment ?? process.env;
35
33
  this.#guaranteedCommands = input.guaranteedCommands ?? {};
36
34
  this.#shell = selectExecutionShell(this.#environment);
37
- this.#hasWebSearch = input.webSearch !== undefined;
38
- this.#hasWebFetch = input.webFetch !== false;
39
35
  this.#resourceIdleTtlMs = input.resourceIdleTtlMs;
40
36
  const processExecutor = input.processExecutor ?? directLocalProcessExecutor;
41
37
  this.#tools = new LocalToolRegistry(this.#workspace, this.#environment, this.#shell.path, {
42
- ...(input.webSearch ? { webSearch: input.webSearch } : {}),
43
- webFetch: this.#hasWebFetch,
44
- allowUntrustedTls: input.allowUntrustedTls ??
45
- this.#environment['MAR_AGENT_WEB_FETCH_ALLOW_UNTRUSTED_TLS'] === '1',
46
38
  ...(input.codeModeEngineFactory === undefined
47
39
  ? {}
48
40
  : { codeModeEngineFactory: input.codeModeEngineFactory }),
@@ -97,9 +89,7 @@ export class LocalAgentHost {
97
89
  'code.execute',
98
90
  'user-input.request',
99
91
  'event.publish',
100
- 'session.store',
101
- ...(this.#hasWebFetch ? ['web.fetch'] : []),
102
- ...(this.#hasWebSearch ? ['web.search'] : [])
92
+ 'session.store'
103
93
  ],
104
94
  limits: {
105
95
  maxProcesses: TOOL_EXECUTION_LIMITS.maxProcesses,
@@ -113,7 +103,9 @@ export class LocalAgentHost {
113
103
  const binding = this.#executionMcp.get(context.executionId);
114
104
  const extensions = binding?.extensions ?? this.#extensions;
115
105
  if (extensions?.tools?.some((tool) => tool.name === call.name)) {
116
- if (binding?.active === false || !extensions.executeTool)
106
+ if (binding?.active === false ||
107
+ !extensions.executeTool ||
108
+ extensions.isToolAvailable?.(call.name, context) === false)
117
109
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Host tool execution is unavailable.');
118
110
  context.signal.throwIfAborted();
119
111
  return extensions.executeTool(call, context);
@@ -141,8 +133,15 @@ export class LocalAgentHost {
141
133
  }
142
134
  async listExecutionExtensions(context) {
143
135
  const binding = this.#executionBinding(context);
144
- const { environment: _environment, ...extensions } = binding?.extensions ?? this.#extensions ?? {};
145
- return extensions;
136
+ const { environment: _environment, isToolAvailable, ...extensions } = binding?.extensions ?? this.#extensions ?? {};
137
+ return {
138
+ ...extensions,
139
+ ...(extensions.tools === undefined
140
+ ? {}
141
+ : {
142
+ tools: extensions.tools.filter((tool) => isToolAvailable?.(tool.name, context) !== false)
143
+ })
144
+ };
146
145
  }
147
146
  emit(event) {
148
147
  this.events.push(event);
@@ -423,6 +423,7 @@ export function createSessionOpener(services) {
423
423
  const catalogContext = {
424
424
  sessionId,
425
425
  executionId,
426
+ modelId: selectedModel.id,
426
427
  ...(sessionSource.type === 'subagent'
427
428
  ? internal.mcpExecutionScopeId === undefined
428
429
  ? {}
@@ -482,12 +483,8 @@ export function createSessionOpener(services) {
482
483
  ? selectedModelForTools.codeMode && description.capabilities.includes('code.execute')
483
484
  : tool.name === 'question'
484
485
  ? mode === 'plan'
485
- : tool.name === 'web_search'
486
- ? !selectedModelForTools.hostedWebSearch &&
487
- description.capabilities.includes('web.search')
488
- : tool.name === 'view_image'
489
- ? selectedModelForTools.inputCapabilities.includes('IMAGE')
490
- : tool.name !== 'web_fetch' || description.capabilities.includes('web.fetch');
486
+ : tool.name !== 'view_image' ||
487
+ selectedModelForTools.inputCapabilities.includes('IMAGE');
491
488
  };
492
489
  mcpRegistry.update(mcpCatalog, (tool) => toolEligible(tool.definition));
493
490
  rebindMcpToolSearchOutputs(messages, mcpRegistry);
@@ -840,6 +837,7 @@ export function createSessionOpener(services) {
840
837
  mode,
841
838
  sessionId,
842
839
  executionId,
840
+ modelId: selectedModel.id,
843
841
  signal: controller.signal,
844
842
  emit,
845
843
  skills: instructionContext?.skills ?? new Map()
@@ -25,6 +25,8 @@ export interface ToolExecutionContext {
25
25
  signal: AbortSignal;
26
26
  sessionId: string;
27
27
  executionId: string;
28
+ /** Current model identity for neutral Host-owned tool policies. */
29
+ modelId?: string;
28
30
  recentImages?: readonly {
29
31
  readonly mimeType: string;
30
32
  readonly data: Buffer;
@@ -39,7 +41,7 @@ export interface ToolExecutionContext {
39
41
  invoke(call: ToolCall, signal: AbortSignal): Promise<ToolExecutionResult>;
40
42
  };
41
43
  }
42
- export interface McpToolCatalogContext extends Pick<ToolExecutionContext, 'sessionId' | 'executionId'> {
44
+ export interface McpToolCatalogContext extends Pick<ToolExecutionContext, 'sessionId' | 'executionId' | 'modelId'> {
43
45
  executionScopeId?: string;
44
46
  claimExecutionScope?: boolean;
45
47
  }
@@ -4,13 +4,7 @@ import type { ToolCall, ToolExecutionContext, ToolExecutionResult } from '../con
4
4
  import { type WorkspacePathMount } from '../shared/path-resolver.js';
5
5
  export declare class LocalToolRegistry {
6
6
  #private;
7
- constructor(workspace: string, environment?: NodeJS.ProcessEnv, shell?: string, web?: {
8
- webSearch?: (input: {
9
- query: string;
10
- maxResults: number;
11
- }, signal: AbortSignal) => Promise<ToolExecutionResult>;
12
- webFetch?: boolean;
13
- allowUntrustedTls?: boolean;
7
+ constructor(workspace: string, environment?: NodeJS.ProcessEnv, shell?: string, options?: {
14
8
  processExecutor?: LocalProcessExecutor;
15
9
  codeModeEngineFactory?: CodeModeEngineFactory;
16
10
  restrictPaths?: boolean;
@@ -3,25 +3,25 @@ import { ApplyPatchTool, applyPatchToolInput } from './apply-patch.js';
3
3
  import { CodeModeTool } from '../code-mode/index.js';
4
4
  import { ExecTool, execToolInput } from './exec.js';
5
5
  import { ReadTool, formatBatchReadOutput } from './read.js';
6
- import { executeWebFetch } from './web-fetch.js';
7
- import { executeWebSearch } from './web-search.js';
8
6
  import { WorkspacePathResolver } from '../shared/path-resolver.js';
9
7
  import { ViewImageTool } from './view-image.js';
10
8
  export class LocalToolRegistry {
11
9
  #exec;
12
10
  #code;
13
11
  #handlers;
14
- constructor(workspace, environment = process.env, shell, web = {}) {
12
+ constructor(workspace, environment = process.env, shell, options = {}) {
15
13
  const paths = new WorkspacePathResolver(workspace, {
16
- ...(web.restrictPaths === undefined ? {} : { restrictPaths: web.restrictPaths }),
17
- ...(web.workspaceWritable === undefined ? {} : { rootWritable: web.workspaceWritable }),
18
- ...(web.pathMounts === undefined ? {} : { mounts: web.pathMounts })
14
+ ...(options.restrictPaths === undefined ? {} : { restrictPaths: options.restrictPaths }),
15
+ ...(options.workspaceWritable === undefined
16
+ ? {}
17
+ : { rootWritable: options.workspaceWritable }),
18
+ ...(options.pathMounts === undefined ? {} : { mounts: options.pathMounts })
19
19
  });
20
20
  const read = new ReadTool(paths);
21
21
  const viewImage = new ViewImageTool(paths);
22
22
  const patch = new ApplyPatchTool(paths);
23
- this.#exec = new ExecTool(paths, undefined, undefined, environment, shell, web.processExecutor);
24
- this.#code = new CodeModeTool(paths, environment, web.processExecutor, web.codeModeEngineFactory);
23
+ this.#exec = new ExecTool(paths, undefined, undefined, environment, shell, options.processExecutor);
24
+ this.#code = new CodeModeTool(paths, environment, options.processExecutor, options.codeModeEngineFactory);
25
25
  this.#handlers = new Map([
26
26
  ['code_exec', (arguments_, context) => this.#code.start(arguments_, context)],
27
27
  ['code_exec_wait', (arguments_, context) => this.#code.wait(arguments_, context)],
@@ -57,27 +57,7 @@ export class LocalToolRegistry {
57
57
  });
58
58
  return { content: JSON.stringify(result), data: result, truncated: result.truncated };
59
59
  }
60
- ]),
61
- ...(web.webSearch
62
- ? [
63
- [
64
- 'web_search',
65
- async (arguments_, context) => {
66
- return executeWebSearch(arguments_, context.signal, (input, signal) => web.webSearch(input, signal));
67
- }
68
- ]
69
- ]
70
- : []),
71
- ...(web.webFetch === false
72
- ? []
73
- : [
74
- [
75
- 'web_fetch',
76
- async (arguments_, context) => {
77
- return executeWebFetch(arguments_, context.signal, web.allowUntrustedTls === true);
78
- }
79
- ]
80
- ])
60
+ ])
81
61
  ]);
82
62
  }
83
63
  execute(call, context) {
@@ -12,8 +12,6 @@ import { executeTodoRead, todoReadToolDefinition } from './agent/todo-read.js';
12
12
  import { executeTodoWrite, todoWriteToolDefinition } from './agent/todo-write.js';
13
13
  import { executeToolSearch, toolSearchToolDefinition } from './mcp/tool-search.js';
14
14
  import { viewImageToolDefinition } from './local/view-image.js';
15
- import { webFetchToolDefinition } from './local/web-fetch.js';
16
- import { webSearchToolDefinition } from './local/web-search.js';
17
15
  export const BUILTIN_TOOL_DEFINITIONS = [
18
16
  skillToolDefinition,
19
17
  toolSearchToolDefinition,
@@ -30,9 +28,7 @@ export const BUILTIN_TOOL_DEFINITIONS = [
30
28
  agentStartToolDefinition,
31
29
  agentWaitToolDefinition,
32
30
  agentMessageToolDefinition,
33
- agentCancelToolDefinition,
34
- webSearchToolDefinition,
35
- webFetchToolDefinition
31
+ agentCancelToolDefinition
36
32
  ];
37
33
  export const AGENT_TOOL_HANDLERS = new Map([
38
34
  ['skill', executeSkill],
@@ -44,6 +44,7 @@ export interface ToolRuntimeContext {
44
44
  mode: ExecutionMode;
45
45
  sessionId: string;
46
46
  executionId: string;
47
+ modelId?: string;
47
48
  signal: AbortSignal;
48
49
  codeMode?: import('./contracts.js').ToolExecutionContext['codeMode'];
49
50
  skills: ReadonlyMap<string, AgentSkill>;
@@ -20,6 +20,7 @@ export async function executeRegisteredTool(name, arguments_, callId, context) {
20
20
  signal: context.signal,
21
21
  sessionId: context.sessionId,
22
22
  executionId: context.executionId,
23
+ ...(context.modelId === undefined ? {} : { modelId: context.modelId }),
23
24
  ...(context.recentImages === undefined ? {} : { recentImages: context.recentImages }),
24
25
  ...(context.codeMode === undefined ? {} : { codeMode: context.codeMode })
25
26
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.9.152",
3
+ "version": "0.9.153",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,4 +0,0 @@
1
- import type { ToolExecutionResult } from '../contracts.js';
2
- import type { ClientToolDefinition } from '../../model/contracts.js';
3
- export declare const webFetchToolDefinition: ClientToolDefinition;
4
- export declare function executeWebFetch(input: unknown, signal: AbortSignal, allowUntrustedTls?: boolean): Promise<ToolExecutionResult>;
@@ -1,85 +0,0 @@
1
- import { request as httpRequest } from 'node:http';
2
- import { request as httpsRequest } from 'node:https';
3
- import { MarAgentError } from '../../error.js';
4
- export const webFetchToolDefinition = {
5
- name: 'web_fetch',
6
- allowedCallers: ['direct', 'programmatic'],
7
- parallelSafety: 'safe',
8
- description: 'Fetch a specific HTTP or HTTPS URL, including explicitly permitted intranet URLs.',
9
- inputSchema: {
10
- type: 'object',
11
- properties: { url: { type: 'string' } },
12
- required: ['url'],
13
- additionalProperties: false
14
- }
15
- };
16
- export async function executeWebFetch(input, signal, allowUntrustedTls = false) {
17
- const value = input;
18
- if (typeof value?.url !== 'string')
19
- throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'URL is required.');
20
- let url;
21
- try {
22
- url = new URL(value.url);
23
- }
24
- catch {
25
- throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'URL is invalid.');
26
- }
27
- if (url.protocol !== 'http:' && url.protocol !== 'https:')
28
- throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Only HTTP and HTTPS URLs are supported.');
29
- return fetchWebUrl(url, signal, allowUntrustedTls);
30
- }
31
- async function fetchWebUrl(url, signal, allowUntrustedTls, redirects = 0) {
32
- if (redirects > 5)
33
- throw new MarAgentError('MAR_AGENT_WEB_FETCH_FAILED', 'Too many redirects.');
34
- return new Promise((resolve, reject) => {
35
- const request = (url.protocol === 'https:' ? httpsRequest : httpRequest)(url, {
36
- signal,
37
- headers: {
38
- accept: 'text/html,application/json,text/plain;q=0.9,*/*;q=0.1',
39
- 'accept-encoding': 'identity',
40
- 'user-agent': 'MyAgentRoam/0.9'
41
- },
42
- ...(url.protocol === 'https:' ? { rejectUnauthorized: !allowUntrustedTls } : {})
43
- }, (response) => {
44
- const status = response.statusCode ?? 0;
45
- if (status >= 300 && status < 400 && response.headers.location) {
46
- response.resume();
47
- void fetchWebUrl(new URL(response.headers.location, url), signal, allowUntrustedTls, redirects + 1).then(resolve, reject);
48
- return;
49
- }
50
- if (status < 200 || status >= 300) {
51
- response.resume();
52
- reject(new MarAgentError('MAR_AGENT_WEB_FETCH_FAILED', `HTTP ${status}.`));
53
- return;
54
- }
55
- const contentType = String(response.headers['content-type'] ?? 'text/plain').toLowerCase();
56
- if (!/^(text\/|application\/(json|xml|xhtml\+xml))/u.test(contentType)) {
57
- response.resume();
58
- reject(new MarAgentError('MAR_AGENT_WEB_FETCH_UNSUPPORTED_CONTENT', 'Response is not readable text.'));
59
- return;
60
- }
61
- const chunks = [];
62
- let bytes = 0;
63
- let truncated = false;
64
- response.on('data', (chunk) => {
65
- if (bytes >= 1_000_000) {
66
- truncated = true;
67
- return;
68
- }
69
- const remaining = 1_000_000 - bytes;
70
- chunks.push(chunk.subarray(0, remaining));
71
- bytes += Math.min(chunk.length, remaining);
72
- if (chunk.length > remaining)
73
- truncated = true;
74
- });
75
- response.on('end', () => resolve({
76
- content: Buffer.concat(chunks).toString('utf8'),
77
- data: { url: url.href, status },
78
- truncated
79
- }));
80
- response.on('error', reject);
81
- });
82
- request.on('error', reject);
83
- request.end();
84
- });
85
- }
@@ -1,7 +0,0 @@
1
- import type { ClientToolDefinition } from '../../model/contracts.js';
2
- import type { ToolExecutionResult } from '../contracts.js';
3
- export declare const webSearchToolDefinition: ClientToolDefinition;
4
- export declare function executeWebSearch(input: unknown, signal: AbortSignal, search: (input: {
5
- query: string;
6
- maxResults: number;
7
- }, signal: AbortSignal) => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>;
@@ -1,34 +0,0 @@
1
- import { MarAgentError } from '../../error.js';
2
- export const webSearchToolDefinition = {
3
- name: 'web_search',
4
- allowedCallers: ['direct', 'programmatic'],
5
- parallelSafety: 'safe',
6
- description: 'Search the web for current information. Use web_fetch to open a specific result.',
7
- inputSchema: {
8
- type: 'object',
9
- properties: {
10
- query: { type: 'string', description: 'Search query.' },
11
- maxResults: { type: 'integer', minimum: 1, maximum: 10, default: 8 }
12
- },
13
- required: ['query'],
14
- additionalProperties: false
15
- }
16
- };
17
- export async function executeWebSearch(input, signal, search) {
18
- const value = input;
19
- if (typeof value?.query !== 'string' || value.query.trim().length === 0)
20
- throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Search query is required.');
21
- try {
22
- return await search({
23
- query: value.query.trim(),
24
- maxResults: typeof value.maxResults === 'number'
25
- ? Math.min(10, Math.max(1, Math.trunc(value.maxResults)))
26
- : 8
27
- }, signal);
28
- }
29
- catch (error) {
30
- if (error instanceof MarAgentError)
31
- throw error;
32
- throw new MarAgentError('MAR_AGENT_WEB_SEARCH_FAILED', 'Web search failed.', { cause: error });
33
- }
34
- }