@myagentroam/agent 0.9.158 → 0.9.160

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 (130) hide show
  1. package/dist/agent-core/extensions/agents.d.ts +69 -0
  2. package/dist/agent-core/extensions/agents.js +160 -0
  3. package/dist/agent-core/extensions/contracts.d.ts +260 -0
  4. package/dist/agent-core/extensions/contracts.js +1 -0
  5. package/dist/agent-core/extensions/data.d.ts +84 -0
  6. package/dist/agent-core/extensions/data.js +83 -0
  7. package/dist/agent-core/extensions/environment.d.ts +27 -0
  8. package/dist/agent-core/extensions/environment.js +52 -0
  9. package/dist/agent-core/extensions/files.d.ts +64 -0
  10. package/dist/agent-core/extensions/files.js +26 -0
  11. package/dist/agent-core/extensions/hooks.d.ts +32 -0
  12. package/dist/agent-core/extensions/hooks.js +135 -0
  13. package/dist/agent-core/extensions/manager.d.ts +39 -0
  14. package/dist/agent-core/extensions/manager.js +458 -0
  15. package/dist/agent-core/extensions/mcp.d.ts +99 -0
  16. package/dist/agent-core/extensions/mcp.js +433 -0
  17. package/dist/agent-core/extensions/model-request.d.ts +60 -0
  18. package/dist/agent-core/extensions/model-request.js +167 -0
  19. package/dist/agent-core/extensions/model-stream.d.ts +9 -0
  20. package/dist/agent-core/extensions/model-stream.js +101 -0
  21. package/dist/agent-core/extensions/models.d.ts +43 -0
  22. package/dist/agent-core/extensions/models.js +1 -0
  23. package/dist/agent-core/extensions/network.d.ts +32 -0
  24. package/dist/agent-core/extensions/network.js +97 -0
  25. package/dist/agent-core/extensions/operation.d.ts +29 -0
  26. package/dist/agent-core/extensions/operation.js +98 -0
  27. package/dist/agent-core/extensions/processes.d.ts +52 -0
  28. package/dist/agent-core/extensions/processes.js +283 -0
  29. package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
  30. package/dist/agent-core/extensions/prompt-composition.js +106 -0
  31. package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
  32. package/dist/agent-core/extensions/prompt-input.js +46 -0
  33. package/dist/agent-core/extensions/resources.d.ts +27 -0
  34. package/dist/agent-core/extensions/resources.js +221 -0
  35. package/dist/agent-core/extensions/services.d.ts +31 -0
  36. package/dist/agent-core/extensions/services.js +61 -0
  37. package/dist/agent-core/extensions/session-input.d.ts +151 -0
  38. package/dist/agent-core/extensions/session-input.js +351 -0
  39. package/dist/agent-core/extensions/session-messages.d.ts +41 -0
  40. package/dist/agent-core/extensions/session-messages.js +76 -0
  41. package/dist/agent-core/extensions/session.d.ts +68 -0
  42. package/dist/agent-core/extensions/session.js +593 -0
  43. package/dist/agent-core/extensions/settings.d.ts +59 -0
  44. package/dist/agent-core/extensions/settings.js +115 -0
  45. package/dist/agent-core/extensions/store.d.ts +10 -0
  46. package/dist/agent-core/extensions/store.js +131 -0
  47. package/dist/agent-core/extensions/tools.d.ts +61 -0
  48. package/dist/agent-core/extensions/tools.js +266 -0
  49. package/dist/agent-core/host/contracts.d.ts +25 -0
  50. package/dist/agent-core/host/local-agent-host.d.ts +10 -1
  51. package/dist/agent-core/host/local-agent-host.js +132 -20
  52. package/dist/agent-core/host/local-file-access.d.ts +30 -0
  53. package/dist/agent-core/host/local-file-access.js +107 -0
  54. package/dist/agent-core/host/local-network-access.d.ts +6 -0
  55. package/dist/agent-core/host/local-network-access.js +97 -0
  56. package/dist/agent-core/host/local-process-executor.d.ts +5 -1
  57. package/dist/agent-core/host/local-session-environment.d.ts +18 -0
  58. package/dist/agent-core/host/local-session-environment.js +102 -0
  59. package/dist/agent-core/model/anthropic-messages.js +73 -23
  60. package/dist/agent-core/model/contracts.d.ts +21 -0
  61. package/dist/agent-core/model/openai-responses.d.ts +3 -1
  62. package/dist/agent-core/model/openai-responses.js +248 -97
  63. package/dist/agent-core/model/text-blocks.d.ts +7 -0
  64. package/dist/agent-core/model/text-blocks.js +40 -0
  65. package/dist/agent-core/prompts/index.d.ts +6 -2
  66. package/dist/agent-core/prompts/index.js +38 -24
  67. package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
  68. package/dist/agent-core/runtime/context/normalize.js +21 -1
  69. package/dist/agent-core/runtime/context/projection.js +35 -1
  70. package/dist/agent-core/runtime/context/request-budget.js +5 -2
  71. package/dist/agent-core/runtime/context/restore.d.ts +5 -0
  72. package/dist/agent-core/runtime/context/restore.js +47 -4
  73. package/dist/agent-core/runtime/context/usage.js +2 -3
  74. package/dist/agent-core/runtime/contracts.d.ts +2 -0
  75. package/dist/agent-core/runtime/execution.js +5 -2
  76. package/dist/agent-core/sdk/agent.js +177 -40
  77. package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
  78. package/dist/agent-core/sdk/extension-agents.js +51 -0
  79. package/dist/agent-core/sdk/extension-models.d.ts +13 -0
  80. package/dist/agent-core/sdk/extension-models.js +128 -0
  81. package/dist/agent-core/sdk/extension-session.d.ts +30 -0
  82. package/dist/agent-core/sdk/extension-session.js +103 -0
  83. package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
  84. package/dist/agent-core/sdk/extension-tools.js +126 -0
  85. package/dist/agent-core/sdk/session.d.ts +26 -2
  86. package/dist/agent-core/sdk/session.js +1215 -506
  87. package/dist/agent-core/sdk/types.d.ts +3 -0
  88. package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
  89. package/dist/agent-core/session/jsonl-store.d.ts +28 -8
  90. package/dist/agent-core/session/jsonl-store.js +124 -84
  91. package/dist/agent-core/session/types.d.ts +4 -1
  92. package/dist/agent-core/storage/file-lock.d.ts +4 -0
  93. package/dist/agent-core/storage/file-lock.js +83 -0
  94. package/dist/agent-core/subagent/contracts.d.ts +11 -1
  95. package/dist/agent-core/subagent/scheduler.d.ts +8 -0
  96. package/dist/agent-core/subagent/session-controller.d.ts +22 -1
  97. package/dist/agent-core/subagent/session-controller.js +355 -98
  98. package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
  99. package/dist/agent-core/tools/code-mode/cells.js +5 -15
  100. package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
  101. package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
  102. package/dist/agent-core/tools/code-mode/index.js +1 -2
  103. package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
  104. package/dist/agent-core/tools/code-mode/observations.js +16 -5
  105. package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
  106. package/dist/agent-core/tools/execution.d.ts +11 -2
  107. package/dist/agent-core/tools/execution.js +58 -21
  108. package/dist/agent-core/tools/input.d.ts +2 -0
  109. package/dist/agent-core/tools/input.js +36 -0
  110. package/dist/agent-core/tools/local/apply-patch.js +21 -17
  111. package/dist/agent-core/tools/local/exec.d.ts +4 -4
  112. package/dist/agent-core/tools/local/exec.js +234 -62
  113. package/dist/agent-core/tools/local/registry.d.ts +6 -0
  114. package/dist/agent-core/tools/local/registry.js +60 -9
  115. package/dist/agent-core/tools/local/view-image.js +108 -2
  116. package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
  117. package/dist/agent-core/tools/mcp/catalog.js +4 -4
  118. package/dist/agent-core/tools/mcp/history.js +2 -2
  119. package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
  120. package/dist/agent-core/tools/mcp/tool-search.js +2 -2
  121. package/dist/agent-core/tools/output-projection.d.ts +3 -1
  122. package/dist/agent-core/tools/output-projection.js +11 -2
  123. package/dist/agent-core/tools/runtime-context.d.ts +1 -0
  124. package/dist/agent-core/tools/runtime.d.ts +3 -1
  125. package/dist/agent-core/tools/runtime.js +22 -7
  126. package/dist/agent-core/tools/scheduler.d.ts +2 -0
  127. package/dist/agent-core/tools/scheduler.js +43 -6
  128. package/dist/index.d.ts +21 -0
  129. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
  130. package/package.json +1 -1
@@ -0,0 +1,115 @@
1
+ import { MarAgentError } from '../error.js';
2
+ import { cloneCoreJson } from './data.js';
3
+ export function coreSettingsBinding(bindings) {
4
+ const backend = bindings?.['settings'];
5
+ if (backend === undefined)
6
+ return undefined;
7
+ if (!backend ||
8
+ typeof backend !== 'object' ||
9
+ ['describe', 'read', 'update'].some((name) => typeof backend[name] !== 'function'))
10
+ throw new MarAgentError('MAR_AGENT_SETTINGS_BINDING_INVALID', 'Settings binding must provide describe, read and update.');
11
+ return backend;
12
+ }
13
+ export function settingsCapabilities(base, bound) {
14
+ const list = Object.freeze([
15
+ ...base.list().filter((id) => id !== 'settings'),
16
+ ...(bound ? ['settings'] : [])
17
+ ]);
18
+ return Object.freeze({ has: (id) => list.includes(id), list: () => list });
19
+ }
20
+ function copyInput(input) {
21
+ if (!input ||
22
+ typeof input !== 'object' ||
23
+ Array.isArray(input) ||
24
+ (input.scope !== undefined && (typeof input.scope !== 'string' || !input.scope)))
25
+ throw invalid('INPUT');
26
+ if ('key' in input) {
27
+ const update = input;
28
+ if (typeof update.key !== 'string' ||
29
+ !update.key ||
30
+ (update.expectedRevision !== undefined &&
31
+ (!Number.isSafeInteger(update.expectedRevision) || update.expectedRevision < 0)))
32
+ throw invalid('INPUT');
33
+ try {
34
+ return { ...input, value: cloneCoreJson(update.value) };
35
+ }
36
+ catch {
37
+ throw invalid('INPUT');
38
+ }
39
+ }
40
+ return { ...input };
41
+ }
42
+ function copyOutput(value) {
43
+ try {
44
+ return cloneCoreJson(value);
45
+ }
46
+ catch {
47
+ throw invalid('OUTPUT');
48
+ }
49
+ }
50
+ function invalid(kind) {
51
+ return new MarAgentError(`MAR_AGENT_SETTINGS_${kind}_INVALID`, `Settings ${kind === 'INPUT' ? 'input' : 'result'} is invalid.`);
52
+ }
53
+ function object(value) {
54
+ return !!value && typeof value === 'object' && !Array.isArray(value);
55
+ }
56
+ export function bindCoreSettings(backend, run, hooks) {
57
+ const invoke = async (name, raw) => {
58
+ const input = copyInput(raw);
59
+ return run((call) => hooks.invoke(`settings.${name}`, input, call, async (candidate, effective) => {
60
+ const supplied = copyInput(candidate);
61
+ if (name === 'update' && (!('key' in supplied) || !('value' in supplied)))
62
+ throw invalid('INPUT');
63
+ if (typeof backend[name] !== 'function')
64
+ throw new MarAgentError('MAR_AGENT_HOST_PORT_UNAVAILABLE', 'Bound settings operation is unavailable.', { details: { operation: name } });
65
+ const handler = backend[name];
66
+ const output = copyOutput(await handler.call(backend, supplied, effective));
67
+ if (!object(output) ||
68
+ !Number.isSafeInteger(output['revision']) ||
69
+ output['revision'] < 0)
70
+ throw invalid('OUTPUT');
71
+ if (name === 'describe') {
72
+ const definitions = output['definitions'];
73
+ if (!Array.isArray(definitions) ||
74
+ definitions.some((item) => !object(item) ||
75
+ typeof item['key'] !== 'string' ||
76
+ !item['key'] ||
77
+ typeof item['source'] !== 'string' ||
78
+ typeof item['writable'] !== 'boolean' ||
79
+ (item['sensitive'] !== undefined && typeof item['sensitive'] !== 'boolean') ||
80
+ (item['label'] !== undefined && typeof item['label'] !== 'string') ||
81
+ (item['owner'] !== undefined && typeof item['owner'] !== 'string') ||
82
+ (typeof item['schema'] !== 'boolean' && !object(item['schema']))) ||
83
+ new Set(definitions.map((item) => item.key)).size !==
84
+ definitions.length)
85
+ throw invalid('OUTPUT');
86
+ }
87
+ else if (name === 'read') {
88
+ if (!object(output['values']))
89
+ throw invalid('OUTPUT');
90
+ }
91
+ else if (!['updated', 'unchanged', 'denied'].includes(String(output['status'])) ||
92
+ (output['status'] === 'denied'
93
+ ? !object(output['reason']) ||
94
+ typeof output['reason']['code'] !== 'string' ||
95
+ !output['reason']['code'] ||
96
+ typeof output['reason']['message'] !== 'string'
97
+ : !('value' in output)))
98
+ throw invalid('OUTPUT');
99
+ await hooks.emit('settings.completed', {
100
+ operation: name,
101
+ revision: output['revision'],
102
+ ...(supplied.scope === undefined ? {} : { scope: supplied.scope }),
103
+ ...(name === 'update'
104
+ ? { status: output['status'] }
105
+ : {})
106
+ }, effective);
107
+ return output;
108
+ }), input.signal);
109
+ };
110
+ return {
111
+ describe: (input = {}) => invoke('describe', input),
112
+ read: (input = {}) => invoke('read', input),
113
+ update: (input) => invoke('update', input)
114
+ };
115
+ }
@@ -0,0 +1,10 @@
1
+ import { type CoreStoreApi } from './data.js';
2
+ type StoreRun = <T>(operation: (signal: AbortSignal) => Promise<T>) => Promise<T>;
3
+ /** One application-bound installation namespace, independent from Session JSONL or Code Mode state. */
4
+ export declare class ExtensionStore {
5
+ #private;
6
+ readonly directory: string;
7
+ constructor(directory: string);
8
+ bind(run: StoreRun): CoreStoreApi;
9
+ }
10
+ export {};
@@ -0,0 +1,131 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, open, readFile, rename, rm } from 'node:fs/promises';
3
+ import { isAbsolute, join } from 'node:path';
4
+ import { MarAgentError } from '../error.js';
5
+ import { acquireFileLock } from '../storage/file-lock.js';
6
+ import { cloneCoreJson } from './data.js';
7
+ /** One application-bound installation namespace, independent from Session JSONL or Code Mode state. */
8
+ export class ExtensionStore {
9
+ directory;
10
+ #path;
11
+ constructor(directory) {
12
+ this.directory = directory;
13
+ if (typeof directory !== 'string' || !isAbsolute(directory))
14
+ throw new MarAgentError('MAR_AGENT_EXTENSION_STORE_INVALID', 'Extension store requires an absolute data directory.');
15
+ this.#path = join(directory, 'core-store.json');
16
+ }
17
+ bind(run) {
18
+ const commit = (input) => run((signal) => this.#commit(input, signal));
19
+ return {
20
+ get: ({ key }) => run(async (signal) => {
21
+ validateKey(key);
22
+ const document = await this.#read(signal);
23
+ const value = Object.hasOwn(document.values, key) ? document.values[key] : undefined;
24
+ return {
25
+ revision: document.revision,
26
+ ...(value === undefined ? {} : { value: cloneCoreJson(value) })
27
+ };
28
+ }),
29
+ keys: () => run(async (signal) => {
30
+ const document = await this.#read(signal);
31
+ return { revision: document.revision, keys: Object.keys(document.values).sort() };
32
+ }),
33
+ set: async ({ key, value, expectedRevision }) => {
34
+ const copied = cloneCoreJson(value);
35
+ const result = await commit({
36
+ writes: [{ key, value: copied }],
37
+ ...(expectedRevision === undefined ? {} : { expectedRevision })
38
+ });
39
+ return { revision: result.revision, value: copied };
40
+ },
41
+ delete: ({ key, expectedRevision }) => commit({
42
+ writes: [{ key }],
43
+ ...(expectedRevision === undefined ? {} : { expectedRevision })
44
+ }),
45
+ commit
46
+ };
47
+ }
48
+ async #read(signal) {
49
+ signal.throwIfAborted();
50
+ let text;
51
+ try {
52
+ text = await readFile(this.#path, { encoding: 'utf8', signal });
53
+ }
54
+ catch (error) {
55
+ if (error.code === 'ENOENT')
56
+ return { revision: 0, values: {} };
57
+ throw error;
58
+ }
59
+ signal.throwIfAborted();
60
+ try {
61
+ const value = JSON.parse(text);
62
+ if (!value ||
63
+ !Number.isSafeInteger(value.revision) ||
64
+ value.revision < 0 ||
65
+ !value.values ||
66
+ typeof value.values !== 'object' ||
67
+ Array.isArray(value.values))
68
+ throw new Error();
69
+ const values = cloneCoreJson(value.values);
70
+ return { revision: value.revision, values };
71
+ }
72
+ catch {
73
+ throw new MarAgentError('MAR_AGENT_EXTENSION_STORE_CORRUPT', 'Extension store document is invalid.');
74
+ }
75
+ }
76
+ async #commit(input, signal) {
77
+ if (!input ||
78
+ !Array.isArray(input.writes) ||
79
+ (input.expectedRevision !== undefined &&
80
+ (!Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0)))
81
+ throw new MarAgentError('MAR_AGENT_EXTENSION_STORE_INVALID', 'Store mutation or expected revision is invalid.');
82
+ const writes = input.writes.map((write) => {
83
+ if (!write || typeof write !== 'object')
84
+ throw new MarAgentError('MAR_AGENT_EXTENSION_STORE_INVALID', 'Store mutation is invalid.');
85
+ const { key, value } = write;
86
+ validateKey(key);
87
+ return { key, ...(value === undefined ? {} : { value: cloneCoreJson(value) }) };
88
+ });
89
+ signal.throwIfAborted();
90
+ await mkdir(this.directory, { recursive: true });
91
+ const release = await acquireFileLock(join(this.directory, 'core-store.lock'), 'Extension store is busy.', 5000, { signal, code: 'MAR_AGENT_EXTENSION_STORE_LOCKED' });
92
+ const temporary = join(this.directory, `.core-store-${randomUUID()}.tmp`);
93
+ try {
94
+ const document = await this.#read(signal);
95
+ if (input.expectedRevision !== undefined && document.revision !== input.expectedRevision)
96
+ throw new MarAgentError('MAR_AGENT_EXTENSION_STORE_CONFLICT', 'Store revision changed before commit.');
97
+ const values = Object.assign(Object.create(null), document.values);
98
+ for (const { key, value } of writes)
99
+ if (value === undefined)
100
+ delete values[key];
101
+ else
102
+ values[key] = value;
103
+ const revision = document.revision + 1;
104
+ if (!Number.isSafeInteger(revision))
105
+ throw new MarAgentError('MAR_AGENT_EXTENSION_STORE_INVALID', 'Store revision is exhausted.');
106
+ const handle = await open(temporary, 'wx', 0o600);
107
+ try {
108
+ await handle.writeFile(JSON.stringify({ revision, values }));
109
+ await handle.sync();
110
+ }
111
+ finally {
112
+ await handle.close();
113
+ }
114
+ signal.throwIfAborted();
115
+ await rename(temporary, this.#path);
116
+ return { revision };
117
+ }
118
+ finally {
119
+ try {
120
+ await rm(temporary, { force: true });
121
+ }
122
+ finally {
123
+ await release();
124
+ }
125
+ }
126
+ }
127
+ }
128
+ function validateKey(key) {
129
+ if (typeof key !== 'string')
130
+ throw new MarAgentError('MAR_AGENT_EXTENSION_STORE_INVALID', 'Store key must be a string.');
131
+ }
@@ -0,0 +1,61 @@
1
+ import type { ClientToolDefinition, McpCatalogTool, ToolCall, ToolExecutionResult } from '../tools/contracts.js';
2
+ import type { CoreCallbackRunner, CoreOperationContext } from './operation.js';
3
+ import { ExtensionResourceScope, type CoreResourceRegistration } from './resources.js';
4
+ export interface CoreToolSpec extends ClientToolDefinition {
5
+ readonly override?: 'replace' | 'wrap';
6
+ /** Optional native validator/parser for schema features outside the built-in converter. */
7
+ readonly parseInput?: (input: unknown) => unknown | Promise<unknown>;
8
+ readonly validateOutput?: (data: unknown) => void | Promise<void>;
9
+ execute(input: unknown, call: CoreOperationContext, next: (input: unknown) => Promise<ToolExecutionResult>): ToolExecutionResult | Promise<ToolExecutionResult>;
10
+ }
11
+ export interface CoreToolRef {
12
+ readonly mcp?: McpCatalogTool;
13
+ readonly definition: ClientToolDefinition;
14
+ readonly source: Readonly<{
15
+ id: string;
16
+ revision: string;
17
+ kind: 'builtin' | 'host' | 'mcp' | 'extension';
18
+ instanceId?: string;
19
+ }>;
20
+ }
21
+ export interface CoreToolInvocation extends ToolCall {
22
+ readonly ref?: CoreToolRef;
23
+ }
24
+ export interface CoreToolCheck {
25
+ readonly available: boolean;
26
+ readonly code?: string;
27
+ readonly source?: CoreToolRef['source'];
28
+ }
29
+ export interface CoreToolAttempt {
30
+ readonly attemptId: string;
31
+ readonly parentAttemptId?: string;
32
+ readonly name: string;
33
+ readonly callId?: string;
34
+ readonly stage: 'leaf' | 'wrapper' | 'result';
35
+ readonly source?: CoreToolRef['source'];
36
+ }
37
+ export interface CoreToolCompletion extends CoreToolAttempt {
38
+ readonly result: ToolExecutionResult;
39
+ }
40
+ export interface CoreToolFailure extends CoreToolAttempt {
41
+ readonly status: 'failed' | 'cancelled';
42
+ readonly errorCode: string;
43
+ }
44
+ export interface CoreToolBinding extends CoreToolRef {
45
+ readonly mcp?: McpCatalogTool;
46
+ registered?(): boolean;
47
+ available?(context: CoreOperationContext): boolean;
48
+ invoke(call: ToolCall, context: CoreOperationContext): Promise<ToolExecutionResult>;
49
+ }
50
+ /** Builds one immutable catalog from real sources; snapshots keep their captured handlers. */
51
+ export declare class ExtensionTools {
52
+ #private;
53
+ readonly trace: (binding: CoreToolBinding, stage: 'leaf' | 'wrapper') => CoreToolBinding;
54
+ readonly callback: CoreCallbackRunner;
55
+ constructor(trace?: (binding: CoreToolBinding, stage: 'leaf' | 'wrapper') => CoreToolBinding, callback?: CoreCallbackRunner);
56
+ get revision(): number;
57
+ /** Internal bridge for a real owner-bound backend, not an author-supplied handler authority. */
58
+ contributeSource(owner: ExtensionResourceScope, source: CoreToolBinding): CoreResourceRegistration;
59
+ register(owner: ExtensionResourceScope, id: string, spec: CoreToolSpec): CoreResourceRegistration;
60
+ capture(base?: readonly CoreToolBinding[], includeStaged?: boolean): ReadonlyMap<string, CoreToolBinding>;
61
+ }
@@ -0,0 +1,266 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { MarAgentError } from '../error.js';
3
+ import { toolCallers } from '../tools/callers.js';
4
+ import { fromJSONSchema } from 'zod';
5
+ import { ExtensionResourceScope } from './resources.js';
6
+ /** Builds one immutable catalog from real sources; snapshots keep their captured handlers. */
7
+ export class ExtensionTools {
8
+ trace;
9
+ callback;
10
+ #entries = [];
11
+ #sources = [];
12
+ #revision = 0;
13
+ constructor(trace = (binding) => binding, callback = async (call, run) => run(call)) {
14
+ this.trace = trace;
15
+ this.callback = callback;
16
+ }
17
+ get revision() {
18
+ return this.#revision;
19
+ }
20
+ /** Internal bridge for a real owner-bound backend, not an author-supplied handler authority. */
21
+ contributeSource(owner, source) {
22
+ const entry = {
23
+ owner,
24
+ binding: Object.freeze({
25
+ ...source,
26
+ definition: deepFreeze(copyDefinition(source.definition)),
27
+ source: Object.freeze({ ...source.source }),
28
+ ...(source.mcp === undefined ? {} : { mcp: deepFreeze(structuredClone(source.mcp)) })
29
+ })
30
+ };
31
+ const registration = owner.effect(() => () => {
32
+ const index = this.#sources.indexOf(entry);
33
+ if (index !== -1) {
34
+ this.#sources.splice(index, 1);
35
+ this.#revision++;
36
+ }
37
+ });
38
+ this.#sources.push(entry);
39
+ this.#revision++;
40
+ return registration;
41
+ }
42
+ register(owner, id, spec) {
43
+ if (typeof spec.name !== 'string' ||
44
+ !spec.name.trim() ||
45
+ typeof spec.description !== 'string' ||
46
+ typeof spec.execute !== 'function' ||
47
+ !spec.inputSchema ||
48
+ typeof spec.inputSchema !== 'object' ||
49
+ Array.isArray(spec.inputSchema) ||
50
+ (spec.override !== undefined && spec.override !== 'replace' && spec.override !== 'wrap'))
51
+ throw new MarAgentError('MAR_AGENT_EXTENSION_TOOL_INVALID', 'Extension tool definition or handler is invalid.');
52
+ const definition = copyDefinition(spec);
53
+ const parse = spec.parseInput ?? schemaValidator(spec.inputSchema, 'MAR_AGENT_TOOL_INPUT_INVALID');
54
+ const validateOutput = spec.validateOutput ??
55
+ (spec.outputSchema
56
+ ? schemaValidator(spec.outputSchema, 'MAR_AGENT_TOOL_OUTPUT_INVALID')
57
+ : undefined);
58
+ toolCallers({
59
+ ...definition,
60
+ allowedCallers: spec.allowedCallers ?? ['direct', 'programmatic']
61
+ });
62
+ const entry = {
63
+ owner,
64
+ id,
65
+ revision: randomUUID(),
66
+ parse,
67
+ ...(validateOutput ? { validateOutput } : {}),
68
+ spec: Object.freeze({
69
+ ...definition,
70
+ ...(spec.override ? { override: spec.override } : {}),
71
+ execute: spec.execute
72
+ })
73
+ };
74
+ const registration = owner.effect(() => () => {
75
+ const index = this.#entries.indexOf(entry);
76
+ if (index !== -1) {
77
+ this.#entries.splice(index, 1);
78
+ this.#revision++;
79
+ }
80
+ });
81
+ this.#entries.push(entry);
82
+ this.#revision++;
83
+ return registration;
84
+ }
85
+ capture(base = [], includeStaged = false) {
86
+ const bindings = new Map();
87
+ const sources = this.#sources
88
+ .filter(({ owner }) => owner.state === 'ACTIVE' || (includeStaged && owner.state === 'STAGED'))
89
+ .filter(({ binding }) => binding.registered?.() !== false)
90
+ .map(({ binding }) => binding);
91
+ for (const original of [...base, ...sources]) {
92
+ const binding = Object.freeze({
93
+ ...original,
94
+ definition: deepFreeze(copyDefinition(original.definition)),
95
+ source: Object.freeze({ ...original.source }),
96
+ ...(original.mcp === undefined ? {} : { mcp: deepFreeze(structuredClone(original.mcp)) })
97
+ });
98
+ if (bindings.has(binding.definition.name))
99
+ throw new MarAgentError('MAR_AGENT_EXTENSION_TOOL_CONFLICT', 'Tool sources conflict.');
100
+ bindings.set(binding.definition.name, this.trace(binding, 'leaf'));
101
+ }
102
+ for (const entry of this.#entries) {
103
+ if (entry.owner.state !== 'ACTIVE' && !(includeStaged && entry.owner.state === 'STAGED'))
104
+ continue;
105
+ if (this.#entries
106
+ .slice(this.#entries.indexOf(entry) + 1)
107
+ .some((later) => later.owner === entry.owner && later.spec.name === entry.spec.name))
108
+ continue;
109
+ const lower = bindings.get(entry.spec.name);
110
+ if (lower && !entry.spec.override)
111
+ throw new MarAgentError('MAR_AGENT_EXTENSION_TOOL_CONFLICT', 'Tool replacement must be explicit.');
112
+ if (!lower && entry.spec.override === 'wrap') {
113
+ if (entry.owner.state === 'ACTIVE')
114
+ continue;
115
+ throw new MarAgentError('MAR_AGENT_EXTENSION_DEPENDENCY_MISSING', 'Tool wrapper has no lower implementation.');
116
+ }
117
+ const definition = copyDefinition(entry.spec);
118
+ definition.allowedCallers ??= lower
119
+ ? (lower.definition.allowedCallers ??
120
+ (lower.source.kind === 'mcp' ? ['direct', 'programmatic'] : ['direct']))
121
+ : ['direct', 'programmatic'];
122
+ if (entry.spec.override === 'wrap' && lower?.definition.agentScope !== undefined)
123
+ definition.agentScope ??= lower.definition.agentScope;
124
+ const binding = Object.freeze({
125
+ definition: deepFreeze(definition),
126
+ ...(entry.spec.override === 'wrap' && lower?.mcp ? { mcp: lower.mcp } : {}),
127
+ source: Object.freeze({
128
+ kind: 'extension',
129
+ id: entry.id,
130
+ revision: entry.revision,
131
+ instanceId: entry.owner.instanceId
132
+ }),
133
+ available: () => entry.owner.state === 'ACTIVE' ||
134
+ (entry.owner.state === 'STAGED' && ExtensionResourceScope.initializing),
135
+ invoke: async (call, context) => entry.owner.run((signal) => this.callback(Object.freeze({ ...context, signal, ownerInstanceId: entry.owner.instanceId }), async (context) => {
136
+ const signal = context.signal;
137
+ const input = await entry.parse(call.arguments);
138
+ signal.throwIfAborted();
139
+ const controller = new AbortController();
140
+ const effective = AbortSignal.any([signal, controller.signal]);
141
+ const pending = new Set();
142
+ let open = true;
143
+ let output;
144
+ try {
145
+ output = await entry.spec.execute(input, Object.freeze({
146
+ ...context,
147
+ signal: effective,
148
+ ownerInstanceId: entry.owner.instanceId
149
+ }), (input) => {
150
+ const result = Promise.resolve().then(() => {
151
+ effective.throwIfAborted();
152
+ if (!open)
153
+ throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool continuation has finished.');
154
+ if (!lower || entry.spec.override !== 'wrap')
155
+ throw new MarAgentError('MAR_AGENT_EXTENSION_DEPENDENCY_MISSING', 'Tool has no wrapper continuation.');
156
+ return lower.invoke({ ...call, arguments: input }, Object.freeze({ ...context, signal: effective }));
157
+ });
158
+ const settled = result.then(() => undefined, () => undefined);
159
+ pending.add(settled);
160
+ void settled.then(() => pending.delete(settled));
161
+ return result;
162
+ });
163
+ }
164
+ catch (error) {
165
+ controller.abort();
166
+ throw error;
167
+ }
168
+ finally {
169
+ while (pending.size)
170
+ await Promise.all([...pending]);
171
+ open = false;
172
+ }
173
+ if (!output || typeof output.content !== 'string')
174
+ throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_INVALID', 'Tool result must contain string content.');
175
+ await entry.validateOutput?.(output.data);
176
+ return output;
177
+ }), context.signal)
178
+ });
179
+ bindings.set(definition.name, this.trace(binding, entry.spec.override === 'wrap' ? 'wrapper' : 'leaf'));
180
+ }
181
+ const view = Object.freeze({
182
+ get size() {
183
+ return bindings.size;
184
+ },
185
+ get: (name) => bindings.get(name),
186
+ has: (name) => bindings.has(name),
187
+ entries: () => bindings.entries(),
188
+ keys: () => bindings.keys(),
189
+ values: () => bindings.values(),
190
+ [Symbol.iterator]: () => bindings[Symbol.iterator](),
191
+ forEach: (callback, thisArg) => {
192
+ for (const [key, value] of bindings)
193
+ callback.call(thisArg, value, key, view);
194
+ }
195
+ });
196
+ return view;
197
+ }
198
+ }
199
+ function schemaValidator(schema, code) {
200
+ let validator;
201
+ try {
202
+ validator = fromJSONSchema(withoutDefaults(schema));
203
+ }
204
+ catch {
205
+ throw new MarAgentError('MAR_AGENT_EXTENSION_TOOL_SCHEMA_UNSUPPORTED', 'Schema requires an explicit native parser.');
206
+ }
207
+ return (input) => {
208
+ if (!validator.safeParse(input).success)
209
+ throw new MarAgentError(code, 'Tool value does not match its declared schema.');
210
+ return input;
211
+ };
212
+ }
213
+ /** JSON Schema defaults are annotations, not permission to insert missing input values. */
214
+ function withoutDefaults(schema) {
215
+ if (typeof schema === 'boolean')
216
+ return schema;
217
+ const copied = structuredClone(schema);
218
+ delete copied.default;
219
+ for (const key of ['properties', 'patternProperties', '$defs', 'definitions']) {
220
+ const children = copied[key];
221
+ if (children && typeof children === 'object')
222
+ for (const [name, child] of Object.entries(children)) {
223
+ if (child && typeof child === 'object' && !Array.isArray(child))
224
+ children[name] = withoutDefaults(child);
225
+ }
226
+ }
227
+ for (const key of ['anyOf', 'allOf', 'oneOf', 'prefixItems', 'items']) {
228
+ const children = copied[key];
229
+ if (Array.isArray(children))
230
+ copied[key] = children.map((child) => withoutDefaults(child));
231
+ }
232
+ for (const key of [
233
+ 'items',
234
+ 'additionalItems',
235
+ 'additionalProperties',
236
+ 'contains',
237
+ 'not',
238
+ 'propertyNames'
239
+ ]) {
240
+ const child = copied[key];
241
+ if (child && typeof child === 'object' && !Array.isArray(child))
242
+ copied[key] = withoutDefaults(child);
243
+ }
244
+ return copied;
245
+ }
246
+ function deepFreeze(value) {
247
+ if (value && typeof value === 'object' && !Object.isFrozen(value)) {
248
+ for (const child of Object.values(value))
249
+ deepFreeze(child);
250
+ Object.freeze(value);
251
+ }
252
+ return value;
253
+ }
254
+ function copyDefinition(spec) {
255
+ return {
256
+ name: spec.name,
257
+ description: spec.description,
258
+ inputSchema: structuredClone(spec.inputSchema),
259
+ ...(spec.nativeType ? { nativeType: spec.nativeType } : {}),
260
+ ...(spec.outputSchema ? { outputSchema: structuredClone(spec.outputSchema) } : {}),
261
+ ...(spec.allowedCallers ? { allowedCallers: [...spec.allowedCallers] } : {}),
262
+ ...(spec.inputMode ? { inputMode: spec.inputMode } : {}),
263
+ ...(spec.parallelSafety ? { parallelSafety: spec.parallelSafety } : {}),
264
+ ...(spec.agentScope ? { agentScope: spec.agentScope } : {})
265
+ };
266
+ }
@@ -1,5 +1,11 @@
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
+ import type { CoreFilesBackend } from '../extensions/files.js';
4
+ import type { CoreNetworkBackend } from '../extensions/network.js';
5
+ import type { CoreProcessesBackend } from '../extensions/processes.js';
6
+ import type { CoreEnvironmentBackend } from '../extensions/environment.js';
7
+ import type { CoreOperationContext } from '../extensions/operation.js';
8
+ import type { CoreToolRef } from '../extensions/tools.js';
3
9
  export declare const MAR_AGENT_HOST_API_VERSION = "1.0";
4
10
  export type MarAgentHostCapability = 'workspace.read' | 'workspace.patch' | 'process.manage' | 'code.execute' | 'user-input.request' | 'event.publish' | 'session.store' | 'mcp.tools' | 'image.read';
5
11
  export interface HostDescription {
@@ -47,9 +53,11 @@ export interface HostSessionResourceSnapshot {
47
53
  }
48
54
  /** The Host owns workspace/tool effects; the SDK owns JSONL storage beneath its supplied HOME. */
49
55
  export interface MarAgentHost {
56
+ readonly extensionPorts?: CoreHostExtensionPorts;
50
57
  describe(): Promise<HostDescription>;
51
58
  executeTool(call: ToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
52
59
  listExecutionExtensions?(context: McpToolCatalogContext): Promise<HostExecutionExtensions>;
60
+ listSessionSkills?(context: Pick<McpToolCatalogContext, 'sessionId' | 'modelId'>): Promise<readonly HostSkill[]>;
53
61
  emit(event: MarAgentEvent): Promise<void> | void;
54
62
  requestUserInput?(input: {
55
63
  requestId: string;
@@ -62,3 +70,20 @@ export interface MarAgentHost {
62
70
  disposeSessionResources?(sessionId: string): Promise<void>;
63
71
  dispose(): Promise<void>;
64
72
  }
73
+ export interface CoreHostExtensionPorts {
74
+ readonly tools?: CoreHostToolsBackend;
75
+ readonly mcp?: import('../extensions/mcp.js').CoreMcpBackend;
76
+ readonly environment?: CoreEnvironmentBackend;
77
+ readonly processes?: CoreProcessesBackend;
78
+ readonly network?: CoreNetworkBackend;
79
+ readonly files?: CoreFilesBackend;
80
+ }
81
+ /** A captured application-owned handler, never author-supplied invocation authority. */
82
+ export interface CoreHostToolSource extends CoreToolRef {
83
+ readonly mcp?: import('../tools/contracts.js').McpCatalogTool;
84
+ available?(call: CoreOperationContext): boolean;
85
+ execute(input: ToolCall, call: CoreOperationContext): Promise<ToolExecutionResult>;
86
+ }
87
+ export interface CoreHostToolsBackend {
88
+ catalog(sessionId: string, executionId?: string): readonly CoreHostToolSource[];
89
+ }
@@ -2,8 +2,10 @@ import type { MarAgentEvent } from '../runtime/events.js';
2
2
  import type { CodeModeEngineFactory } from '../tools/code-mode/engine.js';
3
3
  import type { McpToolCallContext, McpToolCatalog, McpToolCatalogContext, ToolCall, ToolExecutionContext, ToolExecutionResult } from '../tools/contracts.js';
4
4
  import type { WorkspacePathMount } from '../tools/shared/path-resolver.js';
5
- import type { HostDescription, HostExecutionExtensions, MarAgentHost } from './contracts.js';
5
+ import type { HostDescription, HostExecutionExtensions, HostSkill, MarAgentHost, CoreHostToolsBackend } from './contracts.js';
6
+ import type { CoreNetworkBackend } from '../extensions/network.js';
6
7
  import { type LocalProcessExecutor } from './local-process-executor.js';
8
+ import type { CoreOperationContext } from '../extensions/operation.js';
7
9
  export interface LocalAgentMcpBinding {
8
10
  readonly tools: readonly import('../model/contracts.js').ClientToolDefinition[];
9
11
  readonly catalog?: () => McpToolCatalog;
@@ -21,8 +23,14 @@ export interface LocalAgentExecutionExtensions extends HostExecutionExtensions {
21
23
  }
22
24
  export declare class LocalAgentHost implements MarAgentHost {
23
25
  #private;
26
+ readonly extensionPorts: import('./contracts.js').CoreHostExtensionPorts;
24
27
  readonly events: MarAgentEvent[];
25
28
  constructor(input: {
29
+ mcp?: import('../extensions/mcp.js').CoreMcpBackend;
30
+ operationTools?: CoreHostToolsBackend;
31
+ beforeOperationToolExecute?: (input: ToolCall, call: CoreOperationContext) => Promise<void> | void;
32
+ callMcpOperation?: (input: ToolCall, call: CoreOperationContext, expected: NonNullable<McpToolCallContext['expectedTool']>) => Promise<ToolExecutionResult>;
33
+ network?: CoreNetworkBackend | false;
26
34
  homeDirectory: string;
27
35
  workspace: string;
28
36
  mode?: 'normal' | 'node';
@@ -48,6 +56,7 @@ export declare class LocalAgentHost implements MarAgentHost {
48
56
  executeTool(call: ToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
49
57
  bindExecutionExtensions(extensions: LocalAgentExecutionExtensions, mcp?: LocalAgentMcpBinding): LocalAgentExecutionMcpBinding;
50
58
  listExecutionExtensions(context: McpToolCatalogContext): Promise<HostExecutionExtensions>;
59
+ listSessionSkills(_context: Pick<McpToolCatalogContext, 'sessionId' | 'modelId'>): Promise<readonly HostSkill[]>;
51
60
  emit(event: MarAgentEvent): void;
52
61
  requestUserInput(input: {
53
62
  requestId: string;