@myagentroam/agent 0.9.159 → 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.
- package/dist/agent-core/extensions/agents.d.ts +69 -0
- package/dist/agent-core/extensions/agents.js +160 -0
- package/dist/agent-core/extensions/contracts.d.ts +260 -0
- package/dist/agent-core/extensions/contracts.js +1 -0
- package/dist/agent-core/extensions/data.d.ts +84 -0
- package/dist/agent-core/extensions/data.js +83 -0
- package/dist/agent-core/extensions/environment.d.ts +27 -0
- package/dist/agent-core/extensions/environment.js +52 -0
- package/dist/agent-core/extensions/files.d.ts +64 -0
- package/dist/agent-core/extensions/files.js +26 -0
- package/dist/agent-core/extensions/hooks.d.ts +32 -0
- package/dist/agent-core/extensions/hooks.js +135 -0
- package/dist/agent-core/extensions/manager.d.ts +39 -0
- package/dist/agent-core/extensions/manager.js +458 -0
- package/dist/agent-core/extensions/mcp.d.ts +99 -0
- package/dist/agent-core/extensions/mcp.js +433 -0
- package/dist/agent-core/extensions/model-request.d.ts +60 -0
- package/dist/agent-core/extensions/model-request.js +167 -0
- package/dist/agent-core/extensions/model-stream.d.ts +9 -0
- package/dist/agent-core/extensions/model-stream.js +101 -0
- package/dist/agent-core/extensions/models.d.ts +43 -0
- package/dist/agent-core/extensions/models.js +1 -0
- package/dist/agent-core/extensions/network.d.ts +32 -0
- package/dist/agent-core/extensions/network.js +97 -0
- package/dist/agent-core/extensions/operation.d.ts +29 -0
- package/dist/agent-core/extensions/operation.js +98 -0
- package/dist/agent-core/extensions/processes.d.ts +52 -0
- package/dist/agent-core/extensions/processes.js +283 -0
- package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
- package/dist/agent-core/extensions/prompt-composition.js +106 -0
- package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
- package/dist/agent-core/extensions/prompt-input.js +46 -0
- package/dist/agent-core/extensions/resources.d.ts +27 -0
- package/dist/agent-core/extensions/resources.js +221 -0
- package/dist/agent-core/extensions/services.d.ts +31 -0
- package/dist/agent-core/extensions/services.js +61 -0
- package/dist/agent-core/extensions/session-input.d.ts +151 -0
- package/dist/agent-core/extensions/session-input.js +351 -0
- package/dist/agent-core/extensions/session-messages.d.ts +41 -0
- package/dist/agent-core/extensions/session-messages.js +76 -0
- package/dist/agent-core/extensions/session.d.ts +68 -0
- package/dist/agent-core/extensions/session.js +593 -0
- package/dist/agent-core/extensions/settings.d.ts +59 -0
- package/dist/agent-core/extensions/settings.js +115 -0
- package/dist/agent-core/extensions/store.d.ts +10 -0
- package/dist/agent-core/extensions/store.js +131 -0
- package/dist/agent-core/extensions/tools.d.ts +61 -0
- package/dist/agent-core/extensions/tools.js +266 -0
- package/dist/agent-core/host/contracts.d.ts +25 -0
- package/dist/agent-core/host/local-agent-host.d.ts +10 -1
- package/dist/agent-core/host/local-agent-host.js +132 -20
- package/dist/agent-core/host/local-file-access.d.ts +30 -0
- package/dist/agent-core/host/local-file-access.js +107 -0
- package/dist/agent-core/host/local-network-access.d.ts +6 -0
- package/dist/agent-core/host/local-network-access.js +97 -0
- package/dist/agent-core/host/local-process-executor.d.ts +5 -1
- package/dist/agent-core/host/local-session-environment.d.ts +18 -0
- package/dist/agent-core/host/local-session-environment.js +102 -0
- package/dist/agent-core/model/anthropic-messages.js +73 -23
- package/dist/agent-core/model/contracts.d.ts +12 -0
- package/dist/agent-core/model/openai-responses.js +73 -12
- package/dist/agent-core/model/text-blocks.d.ts +7 -0
- package/dist/agent-core/model/text-blocks.js +40 -0
- package/dist/agent-core/prompts/index.d.ts +6 -2
- package/dist/agent-core/prompts/index.js +38 -24
- package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
- package/dist/agent-core/runtime/context/normalize.js +21 -1
- package/dist/agent-core/runtime/context/projection.js +35 -1
- package/dist/agent-core/runtime/context/request-budget.js +5 -2
- package/dist/agent-core/runtime/context/restore.d.ts +5 -0
- package/dist/agent-core/runtime/context/restore.js +47 -4
- package/dist/agent-core/runtime/context/usage.js +2 -3
- package/dist/agent-core/runtime/contracts.d.ts +2 -0
- package/dist/agent-core/runtime/execution.js +5 -2
- package/dist/agent-core/sdk/agent.js +177 -40
- package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
- package/dist/agent-core/sdk/extension-agents.js +51 -0
- package/dist/agent-core/sdk/extension-models.d.ts +13 -0
- package/dist/agent-core/sdk/extension-models.js +128 -0
- package/dist/agent-core/sdk/extension-session.d.ts +30 -0
- package/dist/agent-core/sdk/extension-session.js +103 -0
- package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
- package/dist/agent-core/sdk/extension-tools.js +126 -0
- package/dist/agent-core/sdk/session.d.ts +26 -2
- package/dist/agent-core/sdk/session.js +1215 -506
- package/dist/agent-core/sdk/types.d.ts +3 -0
- package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
- package/dist/agent-core/session/jsonl-store.d.ts +28 -8
- package/dist/agent-core/session/jsonl-store.js +124 -84
- package/dist/agent-core/session/types.d.ts +4 -1
- package/dist/agent-core/storage/file-lock.d.ts +4 -0
- package/dist/agent-core/storage/file-lock.js +83 -0
- package/dist/agent-core/subagent/contracts.d.ts +11 -1
- package/dist/agent-core/subagent/scheduler.d.ts +8 -0
- package/dist/agent-core/subagent/session-controller.d.ts +22 -1
- package/dist/agent-core/subagent/session-controller.js +355 -98
- package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
- package/dist/agent-core/tools/code-mode/cells.js +5 -15
- package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
- package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
- package/dist/agent-core/tools/code-mode/index.js +1 -2
- package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
- package/dist/agent-core/tools/code-mode/observations.js +16 -5
- package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
- package/dist/agent-core/tools/execution.d.ts +11 -2
- package/dist/agent-core/tools/execution.js +58 -21
- package/dist/agent-core/tools/input.d.ts +2 -0
- package/dist/agent-core/tools/input.js +36 -0
- package/dist/agent-core/tools/local/apply-patch.js +21 -17
- package/dist/agent-core/tools/local/exec.d.ts +4 -4
- package/dist/agent-core/tools/local/exec.js +234 -62
- package/dist/agent-core/tools/local/registry.d.ts +6 -0
- package/dist/agent-core/tools/local/registry.js +60 -9
- package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
- package/dist/agent-core/tools/mcp/catalog.js +4 -4
- package/dist/agent-core/tools/mcp/history.js +2 -2
- package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
- package/dist/agent-core/tools/mcp/tool-search.js +2 -2
- package/dist/agent-core/tools/output-projection.d.ts +3 -1
- package/dist/agent-core/tools/output-projection.js +11 -2
- package/dist/agent-core/tools/runtime-context.d.ts +1 -0
- package/dist/agent-core/tools/runtime.d.ts +3 -1
- package/dist/agent-core/tools/runtime.js +22 -7
- package/dist/agent-core/tools/scheduler.d.ts +2 -0
- package/dist/agent-core/tools/scheduler.js +43 -6
- package/dist/index.d.ts +21 -0
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
- package/package.json +1 -1
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
import { normalizeToolInput } from '../tools/input.js';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { MarAgentError } from '../error.js';
|
|
5
|
+
import { validateToolResult } from '../tools/output-projection.js';
|
|
6
|
+
import { withIndependentSessionTools } from '../tools/scheduler.js';
|
|
7
|
+
import { ExtensionHooks } from './hooks.js';
|
|
8
|
+
import { composePromptSections, normalizePromptComposition, renderPromptSections } from './prompt-composition.js';
|
|
9
|
+
import { resolveModelTextBlocks } from '../model/text-blocks.js';
|
|
10
|
+
import { ExtensionStore } from './store.js';
|
|
11
|
+
import { bindCoreFiles } from './files.js';
|
|
12
|
+
import { bindCoreNetwork } from './network.js';
|
|
13
|
+
import { ExtensionMcp } from './mcp.js';
|
|
14
|
+
import { bindCoreProcesses } from './processes.js';
|
|
15
|
+
import { bindCoreEnvironment } from './environment.js';
|
|
16
|
+
import { bindAgentController, bindCoreAgents, invokeCoreAgentOperation } from './agents.js';
|
|
17
|
+
import { bindCoreSettings, coreSettingsBinding, settingsCapabilities } from './settings.js';
|
|
18
|
+
import { ExtensionResourceScope } from './resources.js';
|
|
19
|
+
import { ExtensionServices } from './services.js';
|
|
20
|
+
import { ExtensionTools } from './tools.js';
|
|
21
|
+
import { ExtensionState } from './data.js';
|
|
22
|
+
import { invokeCorePromptSubmission } from './prompt-input.js';
|
|
23
|
+
/** Session-local definitions and facades; SDK supplies real Host sources for each operation. */
|
|
24
|
+
export class ExtensionSession {
|
|
25
|
+
id;
|
|
26
|
+
epoch;
|
|
27
|
+
operations;
|
|
28
|
+
capabilities;
|
|
29
|
+
diagnose;
|
|
30
|
+
baseTools;
|
|
31
|
+
models;
|
|
32
|
+
checkpoint;
|
|
33
|
+
sessionBackend;
|
|
34
|
+
hostPorts;
|
|
35
|
+
agentsBackend;
|
|
36
|
+
allowTool;
|
|
37
|
+
scheduleTool;
|
|
38
|
+
isEnding = false;
|
|
39
|
+
mcp;
|
|
40
|
+
tools = new ExtensionTools((binding, stage) => Object.freeze({
|
|
41
|
+
...binding,
|
|
42
|
+
invoke: (input, call) => this.toolAttempt(input, call, stage, async () => {
|
|
43
|
+
this.assertToolAvailable(binding, call);
|
|
44
|
+
const invoke = () => {
|
|
45
|
+
this.assertToolAvailable(binding, call);
|
|
46
|
+
return binding.invoke(input, call);
|
|
47
|
+
};
|
|
48
|
+
const schedule = this.toolFrame?.schedule ?? this.scheduleTool;
|
|
49
|
+
return binding.definition.name === 'code_exec' ||
|
|
50
|
+
binding.definition.name === 'code_exec_wait'
|
|
51
|
+
? invoke()
|
|
52
|
+
: schedule(binding.definition, invoke);
|
|
53
|
+
}, binding.source)
|
|
54
|
+
}), (call, callback) => this.operations.callback(call, callback));
|
|
55
|
+
#attempt = new AsyncLocalStorage();
|
|
56
|
+
hooks;
|
|
57
|
+
services;
|
|
58
|
+
#commands = [];
|
|
59
|
+
#prompts = [];
|
|
60
|
+
scopes = [];
|
|
61
|
+
#toolRefs = new WeakMap();
|
|
62
|
+
#references = 0;
|
|
63
|
+
#retired = false;
|
|
64
|
+
#ready = false;
|
|
65
|
+
/** The SDK calls this after admission, outside the composition manager's serial lane. */
|
|
66
|
+
async ready(call) {
|
|
67
|
+
this.assertLive();
|
|
68
|
+
call.signal.throwIfAborted();
|
|
69
|
+
if (this.#ready)
|
|
70
|
+
return;
|
|
71
|
+
// Mark before dispatch so nested work cannot recursively initialize the same generation.
|
|
72
|
+
this.#ready = true;
|
|
73
|
+
await this.hooks.emit('session.ready', { sessionId: this.id }, call);
|
|
74
|
+
}
|
|
75
|
+
assertLive() {
|
|
76
|
+
if (this.#retired)
|
|
77
|
+
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Session runtime is retired.');
|
|
78
|
+
}
|
|
79
|
+
retire() {
|
|
80
|
+
this.#retired = true;
|
|
81
|
+
for (const scope of this.scopes)
|
|
82
|
+
scope.retire();
|
|
83
|
+
}
|
|
84
|
+
get hasReferences() {
|
|
85
|
+
return this.#references > 0;
|
|
86
|
+
}
|
|
87
|
+
pin() {
|
|
88
|
+
this.assertLive();
|
|
89
|
+
this.#references++;
|
|
90
|
+
let released = false;
|
|
91
|
+
return () => {
|
|
92
|
+
if (!released) {
|
|
93
|
+
released = true;
|
|
94
|
+
this.#references--;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
#toolFrame = new AsyncLocalStorage();
|
|
99
|
+
get toolFrame() {
|
|
100
|
+
const frame = this.#toolFrame.getStore();
|
|
101
|
+
return frame?.active ? frame : undefined;
|
|
102
|
+
}
|
|
103
|
+
withTools(bindings, schedule, run) {
|
|
104
|
+
this.assertLive();
|
|
105
|
+
const frame = { bindings, schedule, active: true };
|
|
106
|
+
return this.#toolFrame.run(frame, () => {
|
|
107
|
+
try {
|
|
108
|
+
const result = run();
|
|
109
|
+
if (result instanceof Promise)
|
|
110
|
+
return result.finally(() => {
|
|
111
|
+
frame.active = false;
|
|
112
|
+
});
|
|
113
|
+
frame.active = false;
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
frame.active = false;
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
constructor(id, epoch, agentServices, operations, capabilities, diagnose, baseTools = () => [], models, checkpoint, sessionBackend, hostPorts, agentsBackend, allowTool = () => true, scheduleTool = (_definition, run) => run()) {
|
|
123
|
+
this.id = id;
|
|
124
|
+
this.epoch = epoch;
|
|
125
|
+
this.operations = operations;
|
|
126
|
+
this.capabilities = capabilities;
|
|
127
|
+
this.diagnose = diagnose;
|
|
128
|
+
this.baseTools = baseTools;
|
|
129
|
+
this.models = models;
|
|
130
|
+
this.checkpoint = checkpoint;
|
|
131
|
+
this.sessionBackend = sessionBackend;
|
|
132
|
+
this.hostPorts = hostPorts;
|
|
133
|
+
this.agentsBackend = agentsBackend;
|
|
134
|
+
this.allowTool = allowTool;
|
|
135
|
+
this.scheduleTool = scheduleTool;
|
|
136
|
+
this.services = new ExtensionServices('session', agentServices);
|
|
137
|
+
this.hooks = new ExtensionHooks(diagnose, (call, callback, settled) => this.operations.callback(call, callback, settled));
|
|
138
|
+
this.mcp = new ExtensionMcp(this.tools, this.hooks, (call) => this.capture(call, false, this.baseTools(call)), (binding) => this.toolRef(binding), (ref) => this.#toolRefs.get(ref)?.mcp !== undefined, (input, call) => this.invoke(input, call, this.invocationSnapshot(input, call)), () => this.diagnose({ code: 'MAR_AGENT_MCP_CLEANUP_FAILED', sessionId: this.id }));
|
|
139
|
+
}
|
|
140
|
+
context(extension, scope) {
|
|
141
|
+
const instanceId = scope.instanceId;
|
|
142
|
+
const settings = coreSettingsBinding(extension.bindings);
|
|
143
|
+
if (this.sessionBackend)
|
|
144
|
+
scope.effect(() => this.sessionBackend.own(instanceId));
|
|
145
|
+
const state = new ExtensionState(scope, () => this.diagnose({
|
|
146
|
+
code: 'MAR_AGENT_EXTENSION_STATE_OBSERVER_FAILED',
|
|
147
|
+
extensionId: extension.id,
|
|
148
|
+
sessionId: this.id
|
|
149
|
+
}), (call, callback) => this.operations.callback(call, callback));
|
|
150
|
+
const run = (operation, signal) => {
|
|
151
|
+
const parentSignal = this.operations.current()?.signal;
|
|
152
|
+
const inherited = signal && parentSignal ? AbortSignal.any([signal, parentSignal]) : (signal ?? parentSignal);
|
|
153
|
+
return scope.run(async (signal) => {
|
|
154
|
+
const parent = this.operations.current();
|
|
155
|
+
return this.operations.run({
|
|
156
|
+
sessionId: this.id,
|
|
157
|
+
sessionEpoch: this.epoch,
|
|
158
|
+
ownerInstanceId: instanceId,
|
|
159
|
+
extensionId: extension.id,
|
|
160
|
+
actor: 'extension',
|
|
161
|
+
entry: 'native-api',
|
|
162
|
+
signal,
|
|
163
|
+
...(parent?.executionId ? { executionId: parent.executionId } : {}),
|
|
164
|
+
...(parent?.turnId ? { turnId: parent.turnId } : {})
|
|
165
|
+
}, operation);
|
|
166
|
+
}, inherited);
|
|
167
|
+
};
|
|
168
|
+
const sessionAction = (request, call, leaf) => this.operations.continue(call, async () => {
|
|
169
|
+
if (request.kind === 'append')
|
|
170
|
+
return this.hooks.invoke('session.append', request.input, call, (input, effective) => leaf({ kind: 'append', input }, effective));
|
|
171
|
+
return leaf(request, call);
|
|
172
|
+
});
|
|
173
|
+
const register = (entries, value) => {
|
|
174
|
+
const entry = { value, scope };
|
|
175
|
+
const registration = scope.effect(() => () => {
|
|
176
|
+
const index = entries.indexOf(entry);
|
|
177
|
+
if (index !== -1)
|
|
178
|
+
entries.splice(index, 1);
|
|
179
|
+
});
|
|
180
|
+
entries.push(entry);
|
|
181
|
+
return registration;
|
|
182
|
+
};
|
|
183
|
+
const callback = (handler) => async (signal) => {
|
|
184
|
+
await this.#toolFrame.run(undefined, () => this.#attempt.run(undefined, () => this.hooks.independent(() => withIndependentSessionTools(() => this.operations.enter({
|
|
185
|
+
sessionId: this.id,
|
|
186
|
+
sessionEpoch: this.epoch,
|
|
187
|
+
ownerInstanceId: instanceId,
|
|
188
|
+
extensionId: extension.id,
|
|
189
|
+
actor: 'extension',
|
|
190
|
+
entry: 'native-api',
|
|
191
|
+
signal
|
|
192
|
+
}, (call) => this.operations.callback(call, handler))))));
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
sessionId: this.id,
|
|
196
|
+
...(this.agentsBackend
|
|
197
|
+
? {
|
|
198
|
+
agents: bindCoreAgents(this.agentsBackend, scope, run, this.hooks)
|
|
199
|
+
}
|
|
200
|
+
: {}),
|
|
201
|
+
...(settings ? { settings: bindCoreSettings(settings, run, this.hooks) } : {}),
|
|
202
|
+
...(this.hostPorts?.environment
|
|
203
|
+
? { environment: bindCoreEnvironment(this.hostPorts.environment, run, this.hooks) }
|
|
204
|
+
: {}),
|
|
205
|
+
...(this.hostPorts?.processes
|
|
206
|
+
? {
|
|
207
|
+
processes: bindCoreProcesses(this.hostPorts.processes, run, scope, this.hooks, () => this.diagnose({
|
|
208
|
+
code: 'MAR_AGENT_PROCESS_CLEANUP_FAILED',
|
|
209
|
+
extensionId: extension.id,
|
|
210
|
+
sessionId: this.id
|
|
211
|
+
}))
|
|
212
|
+
}
|
|
213
|
+
: {}),
|
|
214
|
+
...(this.sessionBackend
|
|
215
|
+
? {
|
|
216
|
+
session: {
|
|
217
|
+
captureRequest: (input = {}) => run((call) => this.sessionBackend.captureRequest(call, input)),
|
|
218
|
+
inspect: () => run((call) => this.sessionBackend.inspect(call)),
|
|
219
|
+
describe: () => run((call) => {
|
|
220
|
+
if (!this.sessionBackend?.describe)
|
|
221
|
+
throw new MarAgentError('MAR_AGENT_SESSION_INPUT_UNAVAILABLE', 'Session metadata is unavailable.');
|
|
222
|
+
return this.sessionBackend.describe(call);
|
|
223
|
+
}),
|
|
224
|
+
messages: (input = {}) => run((call) => this.sessionBackend.messages(input, call)),
|
|
225
|
+
append: (input) => run((call) => this.sessionBackend.enqueue({ kind: 'append', input }, call, sessionAction)),
|
|
226
|
+
submit: (input) => run(async (call) => {
|
|
227
|
+
const { queueSignal, ...request } = input;
|
|
228
|
+
if (queueSignal !== undefined && !(queueSignal instanceof AbortSignal))
|
|
229
|
+
throw new MarAgentError('MAR_AGENT_SESSION_INPUT_INVALID', 'Session input lifetime must be an AbortSignal.');
|
|
230
|
+
// Admission uses the caller's cancellation; accepted work may outlive it.
|
|
231
|
+
call.signal.throwIfAborted();
|
|
232
|
+
const accepted = queueSignal
|
|
233
|
+
? Object.freeze({
|
|
234
|
+
...call,
|
|
235
|
+
signal: AbortSignal.any([scope.signal, queueSignal])
|
|
236
|
+
})
|
|
237
|
+
: call;
|
|
238
|
+
let receipt;
|
|
239
|
+
const admission = await invokeCorePromptSubmission(this.hooks, request, call, async (content, effective) => {
|
|
240
|
+
effective.signal.throwIfAborted();
|
|
241
|
+
receipt = await this.sessionBackend.enqueue({
|
|
242
|
+
kind: 'submit',
|
|
243
|
+
input: {
|
|
244
|
+
...content,
|
|
245
|
+
...(request.provenance === undefined
|
|
246
|
+
? {}
|
|
247
|
+
: { provenance: request.provenance }),
|
|
248
|
+
...(request.modelAttribution === undefined
|
|
249
|
+
? {}
|
|
250
|
+
: { modelAttribution: request.modelAttribution }),
|
|
251
|
+
...(request.delivery === undefined
|
|
252
|
+
? {}
|
|
253
|
+
: { delivery: request.delivery }),
|
|
254
|
+
...(request.waitFor === undefined ? {} : { waitFor: request.waitFor })
|
|
255
|
+
}
|
|
256
|
+
}, queueSignal ? accepted : effective, sessionAction);
|
|
257
|
+
return {};
|
|
258
|
+
});
|
|
259
|
+
return (receipt ?? this.sessionBackend.handled(request, call, admission.result.value));
|
|
260
|
+
}),
|
|
261
|
+
compact: (input = {}) => run((call) => this.sessionBackend.enqueue({ kind: 'compact', input }, call, sessionAction))
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
: {}),
|
|
265
|
+
...(this.hostPorts?.files
|
|
266
|
+
? { files: bindCoreFiles(this.hostPorts.files, run, this.hooks) }
|
|
267
|
+
: {}),
|
|
268
|
+
...(this.hostPorts?.network
|
|
269
|
+
? {
|
|
270
|
+
network: bindCoreNetwork(this.hostPorts.network, run, scope, this.hooks, () => this.diagnose({
|
|
271
|
+
code: 'MAR_AGENT_NETWORK_CLEANUP_FAILED',
|
|
272
|
+
extensionId: extension.id,
|
|
273
|
+
sessionId: this.id
|
|
274
|
+
}))
|
|
275
|
+
}
|
|
276
|
+
: {}),
|
|
277
|
+
data: {
|
|
278
|
+
...(extension.dataDirectory === undefined
|
|
279
|
+
? {}
|
|
280
|
+
: {
|
|
281
|
+
store: new ExtensionStore(extension.dataDirectory).bind((operation) => run((call) => operation(call.signal)))
|
|
282
|
+
}),
|
|
283
|
+
state: {
|
|
284
|
+
read: (key) => run(async () => state.read(key)),
|
|
285
|
+
write: (key, value) => run((call) => state.write(key, value, call)),
|
|
286
|
+
delete: (key) => run((call) => state.write(key, undefined, call)),
|
|
287
|
+
watch: (handler) => state.watch(handler)
|
|
288
|
+
},
|
|
289
|
+
...(this.checkpoint
|
|
290
|
+
? {
|
|
291
|
+
checkpoint: {
|
|
292
|
+
get: (input) => run((call) => this.checkpoint.read(extension.id, input.key, call)),
|
|
293
|
+
set: (input) => run((call) => this.checkpoint.write(extension.id, input.key, input.value, input.expectedRevision, call)),
|
|
294
|
+
delete: (input) => run((call) => this.checkpoint.write(extension.id, input.key, undefined, input.expectedRevision, call))
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
: {})
|
|
298
|
+
},
|
|
299
|
+
...(this.models
|
|
300
|
+
? {
|
|
301
|
+
models: {
|
|
302
|
+
list: () => this.models.list(),
|
|
303
|
+
generate: (input) => run((call) => {
|
|
304
|
+
return this.models.generate(input, call, (request) => this.hooks.invoke('model.request', request, call, async (view) => view), (event, signal) => this.modelEvent(event, Object.freeze({ ...call, signal })), (code) => this.diagnose({
|
|
305
|
+
code,
|
|
306
|
+
sessionId: this.id,
|
|
307
|
+
extensionId: extension.id,
|
|
308
|
+
operationId: call.operationId
|
|
309
|
+
}));
|
|
310
|
+
}, input.signal)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
: {}),
|
|
314
|
+
...(this.hostPorts?.mcp
|
|
315
|
+
? { mcp: this.mcp.bind(scope, extension.id, this.hostPorts.mcp, run) }
|
|
316
|
+
: {}),
|
|
317
|
+
resources: scope,
|
|
318
|
+
services: this.services.bind(scope),
|
|
319
|
+
capabilities: settingsCapabilities(this.capabilities, settings !== undefined),
|
|
320
|
+
bindings: Object.freeze({ ...(extension.bindings ?? {}) }),
|
|
321
|
+
observability: {
|
|
322
|
+
log: (message) => this.diagnose({
|
|
323
|
+
code: 'MAR_AGENT_EXTENSION_LOG',
|
|
324
|
+
extensionId: extension.id,
|
|
325
|
+
sessionId: this.id,
|
|
326
|
+
message: message.slice(0, 1000)
|
|
327
|
+
})
|
|
328
|
+
},
|
|
329
|
+
hooks: {
|
|
330
|
+
intercept: (key, handler, options) => this.hooks.intercept(scope, key, handler, options),
|
|
331
|
+
observe: (key, handler, options) => this.hooks.observe(scope, key, handler, options)
|
|
332
|
+
},
|
|
333
|
+
tools: {
|
|
334
|
+
register: (spec) => this.tools.register(scope, extension.id, spec),
|
|
335
|
+
catalog: () => Object.freeze([...this.capture(this.operations.current()).values()].map((binding) => {
|
|
336
|
+
return this.toolRef(binding);
|
|
337
|
+
})),
|
|
338
|
+
check: (call) => run(async (context) => this.check(call, context)),
|
|
339
|
+
invoke: (call) => run((context) => this.invoke(call, context, this.invocationSnapshot(call, context)))
|
|
340
|
+
},
|
|
341
|
+
commands: {
|
|
342
|
+
register: (spec) => {
|
|
343
|
+
if (typeof spec.name !== 'string' ||
|
|
344
|
+
!spec.name.trim() ||
|
|
345
|
+
typeof spec.handler !== 'function' ||
|
|
346
|
+
(spec.override !== undefined && spec.override !== 'replace'))
|
|
347
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_COMMAND_INVALID', 'Command definition is invalid.');
|
|
348
|
+
if (spec.override !== 'replace' &&
|
|
349
|
+
this.#commands.some((entry) => entry.value.name === spec.name))
|
|
350
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_COMMAND_CONFLICT', 'Command name conflicts.');
|
|
351
|
+
return register(this.#commands, Object.freeze({ ...spec }));
|
|
352
|
+
},
|
|
353
|
+
list: () => [
|
|
354
|
+
...new Map(this.#commands
|
|
355
|
+
.filter((entry) => entry.scope.state === 'ACTIVE')
|
|
356
|
+
.map((entry) => [entry.value.name, entry])).values()
|
|
357
|
+
].map(({ value }) => ({
|
|
358
|
+
name: value.name,
|
|
359
|
+
...(value.description ? { description: value.description } : {})
|
|
360
|
+
})),
|
|
361
|
+
run: (input) => run(async (context) => {
|
|
362
|
+
const entry = this.#commands.findLast((entry) => entry.scope.state === 'ACTIVE' && entry.value.name === input.name);
|
|
363
|
+
if (!entry)
|
|
364
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_COMMAND_UNAVAILABLE', 'Command is unavailable.');
|
|
365
|
+
return entry.scope.run((signal) => this.operations.callback(Object.freeze({ ...context, signal, ownerInstanceId: entry.scope.instanceId }), (call) => entry.value.handler(input.args, call)), context.signal);
|
|
366
|
+
})
|
|
367
|
+
},
|
|
368
|
+
prompts: {
|
|
369
|
+
register: (input) => {
|
|
370
|
+
const section = normalizePromptComposition({ sections: [input] }).sections[0];
|
|
371
|
+
if (section.text === null)
|
|
372
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_PROMPT_INVALID', 'Prompt section is invalid.');
|
|
373
|
+
if (this.#prompts.some((entry) => entry.value.id === section.id))
|
|
374
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_PROMPT_CONFLICT', 'Prompt section id conflicts.');
|
|
375
|
+
return register(this.#prompts, Object.freeze({ id: section.id, text: section.text }));
|
|
376
|
+
},
|
|
377
|
+
sections: () => this.sections(),
|
|
378
|
+
compose: (system) => run((call) => this.compose(system, call)),
|
|
379
|
+
composeSections: (input) => {
|
|
380
|
+
const snapshot = normalizePromptComposition(input);
|
|
381
|
+
return run((call) => this.composeSections(snapshot, call));
|
|
382
|
+
},
|
|
383
|
+
composeDefault: (facts = {}) => {
|
|
384
|
+
const snapshot = normalizePromptComposition({ sections: [], facts });
|
|
385
|
+
return run(async (call) => {
|
|
386
|
+
if (!this.sessionBackend?.composePrompt)
|
|
387
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_PROMPT_UNAVAILABLE', 'Session default prompt composition is unavailable.');
|
|
388
|
+
return this.composeSections(await this.sessionBackend.composePrompt(snapshot.facts, call), call);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
clock: {
|
|
393
|
+
now: () => Date.now(),
|
|
394
|
+
sleep: (input) => run((call) => scope.sleep(input.durationMs, call.signal), input.signal),
|
|
395
|
+
after: (duration, handler) => scope.after(duration, callback(handler), () => this.diagnose({
|
|
396
|
+
code: 'MAR_AGENT_EXTENSION_TIMER_FAILED',
|
|
397
|
+
extensionId: extension.id,
|
|
398
|
+
sessionId: this.id
|
|
399
|
+
})),
|
|
400
|
+
every: (duration, handler) => scope.every(duration, callback(handler), () => this.diagnose({
|
|
401
|
+
code: 'MAR_AGENT_EXTENSION_TIMER_FAILED',
|
|
402
|
+
extensionId: extension.id,
|
|
403
|
+
sessionId: this.id
|
|
404
|
+
}))
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
toolRef(binding) {
|
|
409
|
+
const ref = Object.freeze({
|
|
410
|
+
definition: binding.definition,
|
|
411
|
+
source: binding.source,
|
|
412
|
+
...(binding.mcp === undefined ? {} : { mcp: binding.mcp })
|
|
413
|
+
});
|
|
414
|
+
this.#toolRefs.set(ref, binding);
|
|
415
|
+
return ref;
|
|
416
|
+
}
|
|
417
|
+
capture(context, includeStaged = false, base) {
|
|
418
|
+
this.assertLive();
|
|
419
|
+
if (!base && !includeStaged && this.toolFrame)
|
|
420
|
+
return this.toolFrame.bindings;
|
|
421
|
+
const tools = this.tools.capture(base ?? this.baseTools(context), includeStaged || ExtensionResourceScope.initializing);
|
|
422
|
+
return new Map([...tools].filter(([name, binding]) => this.allowTool(name, binding.definition)));
|
|
423
|
+
}
|
|
424
|
+
sections() {
|
|
425
|
+
return this.#prompts
|
|
426
|
+
.filter((entry) => entry.scope.state === 'ACTIVE')
|
|
427
|
+
.map((entry) => entry.value);
|
|
428
|
+
}
|
|
429
|
+
bindAgents(controller) {
|
|
430
|
+
return bindAgentController(controller, (key, input, leaf) => {
|
|
431
|
+
const call = this.operations.current();
|
|
432
|
+
if (!call)
|
|
433
|
+
throw new MarAgentError('MAR_AGENT_SUBAGENT_FAILED', 'Agent operation context is unavailable.');
|
|
434
|
+
return invokeCoreAgentOperation(this.hooks, key, input, call, leaf);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
async modelEvent(event, call) {
|
|
438
|
+
await this.hooks.emit('model.event', event, call);
|
|
439
|
+
return this.hooks.invoke('model.event', event, call, async (view) => view);
|
|
440
|
+
}
|
|
441
|
+
composeSections(input, call) {
|
|
442
|
+
this.assertLive();
|
|
443
|
+
return composePromptSections(this.hooks, input, this.sections(), call);
|
|
444
|
+
}
|
|
445
|
+
async compose(system, call) {
|
|
446
|
+
return (await this.composeModelSystem(system, call)).text;
|
|
447
|
+
}
|
|
448
|
+
async composeModelSystem(system, call) {
|
|
449
|
+
if (typeof system !== 'string' && (system === null || typeof system !== 'object'))
|
|
450
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_PROMPT_INVALID', 'Prompt composition is invalid.');
|
|
451
|
+
const input = typeof system === 'string'
|
|
452
|
+
? { sections: [{ id: 'core:base', text: system }] }
|
|
453
|
+
: normalizePromptComposition(system);
|
|
454
|
+
const sections = await this.composeSections(input, call);
|
|
455
|
+
const composed = renderPromptSections(sections.sections);
|
|
456
|
+
const result = await this.hooks.invoke('prompt.compose', composed, call, async (value) => value);
|
|
457
|
+
call.signal.throwIfAborted();
|
|
458
|
+
if (typeof result !== 'string')
|
|
459
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_PROMPT_INVALID', 'Prompt composition is invalid.');
|
|
460
|
+
if (!sections.sections.some((part) => part.scope !== undefined))
|
|
461
|
+
return { text: result };
|
|
462
|
+
const shared = sections.sections
|
|
463
|
+
.filter((part) => part.scope === 'shared' && part.text)
|
|
464
|
+
.map((part) => part.text)
|
|
465
|
+
.join('\n\n');
|
|
466
|
+
const session = sections.sections
|
|
467
|
+
.filter((part) => part.scope !== 'shared' && part.text)
|
|
468
|
+
.map((part) => part.text)
|
|
469
|
+
.join('\n\n');
|
|
470
|
+
const blocks = [];
|
|
471
|
+
if (shared)
|
|
472
|
+
blocks.push({ text: shared, cache: true });
|
|
473
|
+
if (session)
|
|
474
|
+
blocks.push({ text: `${shared ? '\n\n' : ''}${session}` });
|
|
475
|
+
return { text: result, blocks: resolveModelTextBlocks(result, blocks) };
|
|
476
|
+
}
|
|
477
|
+
async invoke(call, context, snapshot = this.capture(context)) {
|
|
478
|
+
this.assertLive();
|
|
479
|
+
const effective = Object.freeze({
|
|
480
|
+
...context,
|
|
481
|
+
...(call.callId === undefined ? {} : { toolCallId: call.callId })
|
|
482
|
+
});
|
|
483
|
+
return this.operations.continue(effective, () => this.toolAttempt(call, effective, 'result', () => this.hooks.invoke('tool.invoke', call, effective, async (target, effective) => {
|
|
484
|
+
const binding = snapshot.get(target.name);
|
|
485
|
+
if (!binding)
|
|
486
|
+
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool source is unavailable.');
|
|
487
|
+
this.assertToolAvailable(binding, effective);
|
|
488
|
+
return binding.invoke({
|
|
489
|
+
...target,
|
|
490
|
+
arguments: normalizeToolInput(target.arguments, binding.definition.inputSchema)
|
|
491
|
+
}, effective);
|
|
492
|
+
})));
|
|
493
|
+
}
|
|
494
|
+
assertToolAvailable(binding, call) {
|
|
495
|
+
this.assertLive();
|
|
496
|
+
call.signal.throwIfAborted();
|
|
497
|
+
if (!this.allowTool(binding.definition.name, binding.definition) ||
|
|
498
|
+
binding.available?.(call) === false)
|
|
499
|
+
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool source is unavailable.');
|
|
500
|
+
if (call.entry !== 'native-api' && !binding.definition.allowedCallers?.includes(call.entry))
|
|
501
|
+
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool caller is unavailable.');
|
|
502
|
+
}
|
|
503
|
+
toolAttempt(input, call, stage, invoke, source) {
|
|
504
|
+
const parent = this.#attempt.getStore();
|
|
505
|
+
const frame = { id: randomUUID(), active: true };
|
|
506
|
+
const attempt = Object.freeze({
|
|
507
|
+
attemptId: frame.id,
|
|
508
|
+
...(parent?.active ? { parentAttemptId: parent.id } : {}),
|
|
509
|
+
name: input.name,
|
|
510
|
+
...(input.callId === undefined ? {} : { callId: input.callId }),
|
|
511
|
+
stage,
|
|
512
|
+
...(source === undefined ? {} : { source })
|
|
513
|
+
});
|
|
514
|
+
return this.#attempt.run(frame, async () => {
|
|
515
|
+
try {
|
|
516
|
+
let result;
|
|
517
|
+
try {
|
|
518
|
+
result = validateToolResult(await invoke());
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
const cancelled = call.signal.aborted ||
|
|
522
|
+
(error instanceof MarAgentError && error.code === 'MAR_AGENT_CANCELLED');
|
|
523
|
+
let errorCode = cancelled ? 'MAR_AGENT_CANCELLED' : 'MAR_AGENT_TOOL_FAILED';
|
|
524
|
+
try {
|
|
525
|
+
if (error instanceof MarAgentError)
|
|
526
|
+
errorCode = error.code;
|
|
527
|
+
else if (error &&
|
|
528
|
+
typeof error === 'object' &&
|
|
529
|
+
'code' in error &&
|
|
530
|
+
typeof error.code === 'string')
|
|
531
|
+
errorCode = error.code.slice(0, 80);
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
/* A diagnostic getter must not replace the real failure. */
|
|
535
|
+
}
|
|
536
|
+
await this.hooks.emit(stage === 'result' ? 'tool.failed' : 'tool.attempt.failed', { ...attempt, status: cancelled ? 'cancelled' : 'failed', errorCode }, call, true);
|
|
537
|
+
throw error;
|
|
538
|
+
}
|
|
539
|
+
await this.hooks.emit(stage === 'result' ? 'tool.completed' : 'tool.attempt.completed', { ...attempt, result }, call);
|
|
540
|
+
return result;
|
|
541
|
+
}
|
|
542
|
+
finally {
|
|
543
|
+
frame.active = false;
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
check(call, context) {
|
|
548
|
+
let binding;
|
|
549
|
+
try {
|
|
550
|
+
binding = this.invocationSnapshot(call, context).get(call.name);
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
return { available: false, code: 'MAR_AGENT_TOOL_NOT_AVAILABLE' };
|
|
554
|
+
}
|
|
555
|
+
const available = !this.#retired &&
|
|
556
|
+
binding !== undefined &&
|
|
557
|
+
this.allowTool(call.name, binding.definition) &&
|
|
558
|
+
binding.available?.(context) !== false &&
|
|
559
|
+
(context.entry === 'native-api' ||
|
|
560
|
+
binding.definition.allowedCallers?.includes(context.entry) === true);
|
|
561
|
+
return {
|
|
562
|
+
available,
|
|
563
|
+
...(available ? { source: binding.source } : { code: 'MAR_AGENT_TOOL_NOT_AVAILABLE' })
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
invocationSnapshot(call, context) {
|
|
567
|
+
const snapshot = this.capture(context);
|
|
568
|
+
if (!call.ref)
|
|
569
|
+
return snapshot;
|
|
570
|
+
const binding = this.#toolRefs.get(call.ref);
|
|
571
|
+
if (!binding || binding.definition.name !== call.name)
|
|
572
|
+
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool reference is unavailable.');
|
|
573
|
+
return new Map([...snapshot, [call.name, binding]]);
|
|
574
|
+
}
|
|
575
|
+
activate() {
|
|
576
|
+
for (const scope of this.scopes)
|
|
577
|
+
scope.activate();
|
|
578
|
+
}
|
|
579
|
+
async dispose() {
|
|
580
|
+
this.retire();
|
|
581
|
+
let failures = 0;
|
|
582
|
+
for (const scope of [...this.scopes].reverse()) {
|
|
583
|
+
try {
|
|
584
|
+
await scope.dispose();
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
failures++;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (failures)
|
|
591
|
+
throw new MarAgentError('MAR_AGENT_EXTENSION_CLEANUP_FAILED', 'Session extension cleanup failed.', { details: { failures } });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type CoreJsonValue } from './data.js';
|
|
2
|
+
import type { CoreCapabilitySnapshot, CoreDomainFacts, CoreDomainOperations } from './contracts.js';
|
|
3
|
+
import type { ExtensionHooks } from './hooks.js';
|
|
4
|
+
import type { CoreOperationContext } from './operation.js';
|
|
5
|
+
export interface CoreSettingsReadInput {
|
|
6
|
+
readonly scope?: string;
|
|
7
|
+
readonly signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
export interface CoreSettingDefinition {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
readonly schema: boolean | Readonly<Record<string, CoreJsonValue>>;
|
|
12
|
+
readonly source: string;
|
|
13
|
+
readonly writable: boolean;
|
|
14
|
+
readonly sensitive?: boolean;
|
|
15
|
+
readonly owner?: string;
|
|
16
|
+
readonly label?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CoreSettingsDescription {
|
|
19
|
+
readonly revision: number;
|
|
20
|
+
readonly definitions: readonly CoreSettingDefinition[];
|
|
21
|
+
}
|
|
22
|
+
export interface CoreSettingsSnapshot {
|
|
23
|
+
readonly revision: number;
|
|
24
|
+
readonly values: Readonly<Record<string, CoreJsonValue>>;
|
|
25
|
+
}
|
|
26
|
+
export interface CoreSettingsUpdate extends CoreSettingsReadInput {
|
|
27
|
+
readonly key: string;
|
|
28
|
+
readonly value: CoreJsonValue;
|
|
29
|
+
readonly expectedRevision?: number;
|
|
30
|
+
}
|
|
31
|
+
export type CoreSettingsResult = {
|
|
32
|
+
readonly status: 'updated' | 'unchanged';
|
|
33
|
+
readonly revision: number;
|
|
34
|
+
readonly value: CoreJsonValue;
|
|
35
|
+
} | {
|
|
36
|
+
readonly status: 'denied';
|
|
37
|
+
readonly revision: number;
|
|
38
|
+
readonly reason: {
|
|
39
|
+
readonly code: string;
|
|
40
|
+
readonly message: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export interface CoreSettingsApi {
|
|
44
|
+
describe(input?: CoreSettingsReadInput): Promise<CoreSettingsDescription>;
|
|
45
|
+
read(input?: CoreSettingsReadInput): Promise<CoreSettingsSnapshot>;
|
|
46
|
+
update(input: CoreSettingsUpdate): Promise<CoreSettingsResult>;
|
|
47
|
+
}
|
|
48
|
+
export type CoreSettingsBackend = {
|
|
49
|
+
[Name in keyof CoreSettingsApi]: (input: NonNullable<Parameters<CoreSettingsApi[Name]>[0]>, call: CoreOperationContext) => Promise<Awaited<ReturnType<CoreSettingsApi[Name]>>>;
|
|
50
|
+
};
|
|
51
|
+
export interface CoreSettingsCompletion {
|
|
52
|
+
readonly operation: keyof CoreSettingsApi;
|
|
53
|
+
readonly revision: number;
|
|
54
|
+
readonly scope?: string;
|
|
55
|
+
readonly status?: CoreSettingsResult['status'];
|
|
56
|
+
}
|
|
57
|
+
export declare function coreSettingsBinding(bindings?: Readonly<Record<string, unknown>>): CoreSettingsBackend | undefined;
|
|
58
|
+
export declare function settingsCapabilities(base: CoreCapabilitySnapshot, bound: boolean): CoreCapabilitySnapshot;
|
|
59
|
+
export declare function bindCoreSettings(backend: CoreSettingsBackend, run: <Value>(operation: (call: CoreOperationContext) => Promise<Value>, signal?: AbortSignal) => Promise<Value>, hooks: ExtensionHooks<CoreDomainOperations, CoreDomainFacts>): CoreSettingsApi;
|