maka-agent 0.2.0-dev.26.20260909 → 0.2.0-dev.27.20260910
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/cli-core.js +21 -0
- package/dist/runtime-host-cli.js +43 -3
- package/dist/runtime-host-handoff-surface.js +10 -8
- package/dist/runtime-host-lifecycle-transaction.js +28 -3
- package/dist/runtime-host-local-source-retirement.js +1 -1
- package/dist/runtime-host-setup-command.js +29 -6
- package/dist/runtime-host-update-command.js +49 -5
- package/dist/session-export-command.js +72 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/usage-stats/types.js +2 -0
- package/node_modules/@maka/core/dist/workhub-routing.js +126 -0
- package/node_modules/@maka/core/package.json +2 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +7 -3
- package/node_modules/@maka/runtime/dist/builtin-tools.js +21 -2
- package/node_modules/@maka/runtime/dist/plugin-agent-service.js +131 -0
- package/node_modules/@maka/runtime/dist/plugin-approval-service.js +33 -0
- package/node_modules/@maka/runtime/dist/plugin-attachment-service.js +53 -0
- package/node_modules/@maka/runtime/dist/plugin-command-service.js +100 -0
- package/node_modules/@maka/runtime/dist/plugin-data-services.js +332 -0
- package/node_modules/@maka/runtime/dist/plugin-fs-service.js +62 -0
- package/node_modules/@maka/runtime/dist/plugin-goal-service.js +61 -0
- package/node_modules/@maka/runtime/dist/plugin-invocation-signal.js +24 -0
- package/node_modules/@maka/runtime/dist/plugin-llm-service.js +85 -0
- package/node_modules/@maka/runtime/dist/plugin-lsp-service.js +94 -0
- package/node_modules/@maka/runtime/dist/plugin-scope-registry.js +91 -0
- package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +71 -0
- package/node_modules/@maka/runtime/dist/plugin-shell-env-service.js +87 -0
- package/node_modules/@maka/runtime/dist/plugin-shell-service.js +74 -0
- package/node_modules/@maka/runtime/dist/plugin-skill-service.js +93 -0
- package/node_modules/@maka/runtime/dist/plugin-system-prompt-service.js +346 -0
- package/node_modules/@maka/runtime/dist/plugin-tool-service.js +18 -52
- package/node_modules/@maka/runtime/dist/plugin-user-question-service.js +43 -0
- package/node_modules/@maka/runtime/dist/plugin-web-service.js +69 -0
- package/node_modules/@maka/runtime/dist/session-export.js +177 -0
- package/node_modules/@maka/runtime/package.json +18 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +67 -18
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +46 -12
- package/node_modules/@maka/runtime-host/dist/operator/setup-frame.js +31 -21
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +9 -1
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +30 -5
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +25 -23
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +62 -4
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +452 -7
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +74 -0
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +30 -0
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +79 -16
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +25 -4
- package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +234 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +189 -92
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -35
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +92 -10
- package/node_modules/@maka/storage/dist/agent-run-store.js +54 -10
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +13 -6
- package/node_modules/@maka/storage/dist/execution-stores.js +1 -1
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +333 -30
- package/node_modules/@maka/storage/dist/session-store.js +2 -2
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +12 -5
- package/node_modules/@maka/storage/package.json +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
import { Service } from './plugin-kernel.js';
|
|
21
|
+
import { MakaPluginRuntimeError, pluginIdentity, registerPluginContribution, } from './plugin-runtime.js';
|
|
22
|
+
import { PluginScopeRegistry } from './plugin-scope-registry.js';
|
|
23
|
+
const PROMPT_NAME_PATTERN = /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u;
|
|
24
|
+
const VARIABLE_NAME_PATTERN = /^[a-z][a-z0-9_]*$/u;
|
|
25
|
+
const VARIABLE_REFERENCE_PATTERN = /\{\{([^{}]*)\}\}/gu;
|
|
26
|
+
const HOST_BASE_SECTION = 'maka:base';
|
|
27
|
+
export const PLUGIN_SYSTEM_PROMPT_SOURCE_ID = 'plugin.system-prompt';
|
|
28
|
+
/**
|
|
29
|
+
* Context-scoped, Fiber-owned System Prompt registry for trusted Host plugins.
|
|
30
|
+
*
|
|
31
|
+
* Profile contributions are inherited by Session roots. A Session contribution
|
|
32
|
+
* with the same name shadows its Profile counterpart before either provider is
|
|
33
|
+
* evaluated. Every assembly snapshots membership and resolves providers anew,
|
|
34
|
+
* so changes committed by one tool call appear at the next logical model step.
|
|
35
|
+
*/
|
|
36
|
+
export class PluginSystemPromptService extends Service {
|
|
37
|
+
sections = new PluginScopeRegistry();
|
|
38
|
+
contexts = new PluginScopeRegistry();
|
|
39
|
+
variables = new PluginScopeRegistry();
|
|
40
|
+
constructor(ctx) {
|
|
41
|
+
super(ctx, 'systemPrompt');
|
|
42
|
+
}
|
|
43
|
+
section(definition) {
|
|
44
|
+
const identity = pluginIdentity(this.ctx);
|
|
45
|
+
assertHostPromptScope(identity.scopeId);
|
|
46
|
+
validateSection(definition);
|
|
47
|
+
return registerPluginContribution(this.ctx, `systemPrompt.section(${JSON.stringify(definition.name)})`, () => this.publishSection(identity, definition));
|
|
48
|
+
}
|
|
49
|
+
context(definition) {
|
|
50
|
+
const identity = pluginIdentity(this.ctx);
|
|
51
|
+
assertHostPromptScope(identity.scopeId);
|
|
52
|
+
validateContext(definition);
|
|
53
|
+
return registerPluginContribution(this.ctx, `systemPrompt.context(${JSON.stringify(definition.name)})`, () => this.publishContext(identity, definition));
|
|
54
|
+
}
|
|
55
|
+
variable(name, provider) {
|
|
56
|
+
const identity = pluginIdentity(this.ctx);
|
|
57
|
+
assertHostPromptScope(identity.scopeId);
|
|
58
|
+
validateVariable(name, provider);
|
|
59
|
+
return registerPluginContribution(this.ctx, `systemPrompt.variable(${JSON.stringify(name)})`, () => this.publishVariable(identity, name, provider));
|
|
60
|
+
}
|
|
61
|
+
async assemble(context, baseText) {
|
|
62
|
+
validateAssemblyContext(context);
|
|
63
|
+
const visibleSections = this.sections.visible(context.sessionId);
|
|
64
|
+
const visibleContexts = this.contexts.visible(context.sessionId);
|
|
65
|
+
const visibleVariables = this.variables.visible(context.sessionId);
|
|
66
|
+
const sectionSnapshot = [...visibleSections.values()];
|
|
67
|
+
const contextSnapshot = [...visibleContexts.values()];
|
|
68
|
+
const variableSnapshot = [...visibleVariables.values()];
|
|
69
|
+
if (sectionSnapshot.length === 0 &&
|
|
70
|
+
contextSnapshot.length === 0 &&
|
|
71
|
+
variableSnapshot.length === 0) {
|
|
72
|
+
return Object.freeze({ text: baseText, contexts: Object.freeze([]) });
|
|
73
|
+
}
|
|
74
|
+
const variables = {};
|
|
75
|
+
for (const variable of variableSnapshot.sort(compareRegistration)) {
|
|
76
|
+
const value = await variable.provider(context);
|
|
77
|
+
if (value !== undefined && typeof value !== 'string') {
|
|
78
|
+
throw new MakaPluginRuntimeError('activation_failed', `System Prompt variable ${JSON.stringify(variable.name)} returned a non-string value`);
|
|
79
|
+
}
|
|
80
|
+
variables[variable.name] = value;
|
|
81
|
+
}
|
|
82
|
+
const resolved = [];
|
|
83
|
+
for (const section of sectionSnapshot) {
|
|
84
|
+
const value = typeof section.definition.text === 'string'
|
|
85
|
+
? section.definition.text
|
|
86
|
+
: await section.definition.text(context);
|
|
87
|
+
if (value !== undefined && typeof value !== 'string') {
|
|
88
|
+
throw new MakaPluginRuntimeError('activation_failed', `System Prompt section ${JSON.stringify(section.definition.name)} returned a non-string value`);
|
|
89
|
+
}
|
|
90
|
+
resolved.push({
|
|
91
|
+
...section,
|
|
92
|
+
name: section.definition.name,
|
|
93
|
+
order: section.definition.order,
|
|
94
|
+
text: value ?? '',
|
|
95
|
+
complete: section.definition.complete === true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const resolvedContexts = [];
|
|
99
|
+
for (const entry of contextSnapshot) {
|
|
100
|
+
const value = typeof entry.definition.text === 'string'
|
|
101
|
+
? entry.definition.text
|
|
102
|
+
: await entry.definition.text(context);
|
|
103
|
+
if (value !== undefined && typeof value !== 'string') {
|
|
104
|
+
throw new MakaPluginRuntimeError('activation_failed', `System Prompt context ${JSON.stringify(entry.definition.name)} returned a non-string value`);
|
|
105
|
+
}
|
|
106
|
+
if (!value)
|
|
107
|
+
continue;
|
|
108
|
+
resolvedContexts.push({
|
|
109
|
+
...entry,
|
|
110
|
+
name: entry.definition.name,
|
|
111
|
+
order: entry.definition.order,
|
|
112
|
+
text: interpolate(entry.definition.name, value, variables),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const complete = resolved.filter((section) => section.complete);
|
|
116
|
+
if (complete.length > 1) {
|
|
117
|
+
throw new MakaPluginRuntimeError('activation_failed', `Multiple complete System Prompt sections are active: ${complete
|
|
118
|
+
.map(({ name }) => JSON.stringify(name))
|
|
119
|
+
.sort()
|
|
120
|
+
.join(', ')}`);
|
|
121
|
+
}
|
|
122
|
+
const effective = complete.length
|
|
123
|
+
? complete
|
|
124
|
+
: [
|
|
125
|
+
...(baseText === undefined
|
|
126
|
+
? []
|
|
127
|
+
: [
|
|
128
|
+
{
|
|
129
|
+
entryId: HOST_BASE_SECTION,
|
|
130
|
+
scopeId: 'profile',
|
|
131
|
+
extensionId: 'maka',
|
|
132
|
+
generation: 0,
|
|
133
|
+
name: HOST_BASE_SECTION,
|
|
134
|
+
order: 0,
|
|
135
|
+
text: baseText,
|
|
136
|
+
complete: false,
|
|
137
|
+
},
|
|
138
|
+
]),
|
|
139
|
+
...resolved,
|
|
140
|
+
];
|
|
141
|
+
const rendered = effective
|
|
142
|
+
.sort(compareSections)
|
|
143
|
+
.map((section) => interpolate(section.name, section.text, variables))
|
|
144
|
+
.filter(Boolean)
|
|
145
|
+
.join('\n\n');
|
|
146
|
+
const contexts = Object.freeze(resolvedContexts.sort(compareContexts).map(({ name, text }) => Object.freeze({ name, text })));
|
|
147
|
+
const revision = promptRevision(resolved, resolvedContexts, variableSnapshot, variables);
|
|
148
|
+
return Object.freeze({
|
|
149
|
+
text: rendered || undefined,
|
|
150
|
+
contexts,
|
|
151
|
+
sourceRevision: Object.freeze({
|
|
152
|
+
id: PLUGIN_SYSTEM_PROMPT_SOURCE_ID,
|
|
153
|
+
revision,
|
|
154
|
+
}),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
inspect(rootId) {
|
|
158
|
+
return Object.freeze([
|
|
159
|
+
...this.sections.entries(rootId).map((entry) => ({
|
|
160
|
+
entryId: entry.entryId,
|
|
161
|
+
scopeId: entry.scopeId,
|
|
162
|
+
extensionId: entry.extensionId,
|
|
163
|
+
generation: entry.generation,
|
|
164
|
+
kind: 'section',
|
|
165
|
+
name: entry.definition.name,
|
|
166
|
+
order: entry.definition.order,
|
|
167
|
+
complete: entry.definition.complete === true,
|
|
168
|
+
})),
|
|
169
|
+
...this.contexts.entries(rootId).map((entry) => ({
|
|
170
|
+
entryId: entry.entryId,
|
|
171
|
+
scopeId: entry.scopeId,
|
|
172
|
+
extensionId: entry.extensionId,
|
|
173
|
+
generation: entry.generation,
|
|
174
|
+
kind: 'context',
|
|
175
|
+
name: entry.definition.name,
|
|
176
|
+
order: entry.definition.order,
|
|
177
|
+
})),
|
|
178
|
+
...this.variables.entries(rootId).map((entry) => ({
|
|
179
|
+
entryId: entry.entryId,
|
|
180
|
+
scopeId: entry.scopeId,
|
|
181
|
+
extensionId: entry.extensionId,
|
|
182
|
+
generation: entry.generation,
|
|
183
|
+
kind: 'variable',
|
|
184
|
+
name: entry.name,
|
|
185
|
+
})),
|
|
186
|
+
].sort(compareInspection));
|
|
187
|
+
}
|
|
188
|
+
publishSection(identity, definition) {
|
|
189
|
+
const rootId = identity.scopeId;
|
|
190
|
+
const existing = this.sections.get(rootId, definition.name);
|
|
191
|
+
assertOwner(existing, identity, 'section', definition.name);
|
|
192
|
+
const entry = {
|
|
193
|
+
...identity,
|
|
194
|
+
definition: Object.freeze({ ...definition }),
|
|
195
|
+
token: Symbol(definition.name),
|
|
196
|
+
retired: false,
|
|
197
|
+
};
|
|
198
|
+
return this.sections.publish(rootId, definition.name, entry);
|
|
199
|
+
}
|
|
200
|
+
publishVariable(identity, name, provider) {
|
|
201
|
+
const rootId = identity.scopeId;
|
|
202
|
+
const existing = this.variables.get(rootId, name);
|
|
203
|
+
assertOwner(existing, identity, 'variable', name);
|
|
204
|
+
const entry = {
|
|
205
|
+
...identity,
|
|
206
|
+
name,
|
|
207
|
+
provider,
|
|
208
|
+
token: Symbol(name),
|
|
209
|
+
retired: false,
|
|
210
|
+
};
|
|
211
|
+
return this.variables.publish(rootId, name, entry);
|
|
212
|
+
}
|
|
213
|
+
publishContext(identity, definition) {
|
|
214
|
+
const rootId = identity.scopeId;
|
|
215
|
+
const existing = this.contexts.get(rootId, definition.name);
|
|
216
|
+
assertOwner(existing, identity, 'context', definition.name);
|
|
217
|
+
const entry = {
|
|
218
|
+
...identity,
|
|
219
|
+
definition: Object.freeze({ ...definition }),
|
|
220
|
+
token: Symbol(definition.name),
|
|
221
|
+
retired: false,
|
|
222
|
+
};
|
|
223
|
+
return this.contexts.publish(rootId, definition.name, entry);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function assertHostPromptScope(scopeId) {
|
|
227
|
+
if (scopeId === 'desktop-ui') {
|
|
228
|
+
throw new MakaPluginRuntimeError('activation_failed', 'desktop-ui plugins cannot contribute Host System Prompt sections');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function validateSection(section) {
|
|
232
|
+
validateName(section.name, 'System Prompt section');
|
|
233
|
+
if (section.name === HOST_BASE_SECTION) {
|
|
234
|
+
throw new MakaPluginRuntimeError('activation_failed', 'The Host base System Prompt is reserved');
|
|
235
|
+
}
|
|
236
|
+
if (!Number.isFinite(section.order)) {
|
|
237
|
+
throw new MakaPluginRuntimeError('activation_failed', 'System Prompt section order must be finite');
|
|
238
|
+
}
|
|
239
|
+
if (typeof section.text !== 'string' && typeof section.text !== 'function') {
|
|
240
|
+
throw new MakaPluginRuntimeError('activation_failed', 'System Prompt section text is invalid');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function validateContext(context) {
|
|
244
|
+
validateName(context.name, 'System Prompt context');
|
|
245
|
+
if (!Number.isFinite(context.order)) {
|
|
246
|
+
throw new MakaPluginRuntimeError('activation_failed', 'System Prompt context order must be finite');
|
|
247
|
+
}
|
|
248
|
+
if (typeof context.text !== 'string' && typeof context.text !== 'function') {
|
|
249
|
+
throw new MakaPluginRuntimeError('activation_failed', 'System Prompt context text is invalid');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function validateVariable(name, provider) {
|
|
253
|
+
if (!VARIABLE_NAME_PATTERN.test(name) || Buffer.byteLength(name, 'utf8') > 128) {
|
|
254
|
+
throw new MakaPluginRuntimeError('activation_failed', `Invalid System Prompt variable: ${name}`);
|
|
255
|
+
}
|
|
256
|
+
if (typeof provider !== 'function') {
|
|
257
|
+
throw new MakaPluginRuntimeError('activation_failed', 'System Prompt variable provider is invalid');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function validateName(name, label) {
|
|
261
|
+
if (!PROMPT_NAME_PATTERN.test(name) || Buffer.byteLength(name, 'utf8') > 128) {
|
|
262
|
+
throw new MakaPluginRuntimeError('activation_failed', `Invalid ${label} name: ${name}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function validateAssemblyContext(context) {
|
|
266
|
+
if (!context.sessionId ||
|
|
267
|
+
!context.turnId ||
|
|
268
|
+
!context.cwd ||
|
|
269
|
+
/[\0\r\n]/u.test(context.sessionId) ||
|
|
270
|
+
/[\0\r\n]/u.test(context.turnId)) {
|
|
271
|
+
throw new Error('Invalid System Prompt assembly context');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function assertOwner(current, identity, kind, name) {
|
|
275
|
+
if (current && current.entryId !== identity.entryId) {
|
|
276
|
+
throw new MakaPluginRuntimeError('activation_failed', `System Prompt ${kind} ${JSON.stringify(name)} is already registered by ${current.entryId}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function interpolate(sectionName, text, variables) {
|
|
280
|
+
VARIABLE_REFERENCE_PATTERN.lastIndex = 0;
|
|
281
|
+
return text.replace(VARIABLE_REFERENCE_PATTERN, (reference, rawName) => {
|
|
282
|
+
if (!VARIABLE_NAME_PATTERN.test(rawName)) {
|
|
283
|
+
throw new MakaPluginRuntimeError('activation_failed', `Malformed System Prompt variable ${JSON.stringify(reference)} in section ${JSON.stringify(sectionName)}`);
|
|
284
|
+
}
|
|
285
|
+
if (!Object.hasOwn(variables, rawName)) {
|
|
286
|
+
throw new MakaPluginRuntimeError('activation_failed', `Unknown System Prompt variable ${JSON.stringify(rawName)} in section ${JSON.stringify(sectionName)}`);
|
|
287
|
+
}
|
|
288
|
+
const value = variables[rawName];
|
|
289
|
+
if (value === undefined) {
|
|
290
|
+
throw new MakaPluginRuntimeError('activation_failed', `System Prompt variable ${JSON.stringify(rawName)} has no value in section ${JSON.stringify(sectionName)}`);
|
|
291
|
+
}
|
|
292
|
+
return value;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function promptRevision(sections, contexts, variables, values) {
|
|
296
|
+
const canonical = {
|
|
297
|
+
sections: [...sections].sort(compareSections).map((section) => ({
|
|
298
|
+
scopeId: section.scopeId,
|
|
299
|
+
entryId: section.entryId,
|
|
300
|
+
extensionId: section.extensionId,
|
|
301
|
+
generation: section.generation,
|
|
302
|
+
name: section.name,
|
|
303
|
+
order: section.order,
|
|
304
|
+
complete: section.complete,
|
|
305
|
+
text: section.text,
|
|
306
|
+
})),
|
|
307
|
+
contexts: [...contexts].sort(compareContexts).map((context) => ({
|
|
308
|
+
scopeId: context.scopeId,
|
|
309
|
+
entryId: context.entryId,
|
|
310
|
+
extensionId: context.extensionId,
|
|
311
|
+
generation: context.generation,
|
|
312
|
+
name: context.name,
|
|
313
|
+
order: context.order,
|
|
314
|
+
text: context.text,
|
|
315
|
+
})),
|
|
316
|
+
variables: [...variables].sort(compareRegistration).map((variable) => ({
|
|
317
|
+
scopeId: variable.scopeId,
|
|
318
|
+
entryId: variable.entryId,
|
|
319
|
+
extensionId: variable.extensionId,
|
|
320
|
+
generation: variable.generation,
|
|
321
|
+
name: variable.name,
|
|
322
|
+
value: values[variable.name] ?? null,
|
|
323
|
+
})),
|
|
324
|
+
};
|
|
325
|
+
return `sha256:${createHash('sha256').update(JSON.stringify(canonical)).digest('hex')}`;
|
|
326
|
+
}
|
|
327
|
+
function compareSections(left, right) {
|
|
328
|
+
return left.order - right.order || compareCodeUnits(left.name, right.name);
|
|
329
|
+
}
|
|
330
|
+
function compareContexts(left, right) {
|
|
331
|
+
return left.order - right.order || compareCodeUnits(left.name, right.name);
|
|
332
|
+
}
|
|
333
|
+
function compareRegistration(left, right) {
|
|
334
|
+
return (compareCodeUnits(left.scopeId, right.scopeId) ||
|
|
335
|
+
compareCodeUnits(left.entryId, right.entryId) ||
|
|
336
|
+
left.generation - right.generation);
|
|
337
|
+
}
|
|
338
|
+
function compareInspection(left, right) {
|
|
339
|
+
return (compareCodeUnits(left.scopeId, right.scopeId) ||
|
|
340
|
+
compareCodeUnits(left.kind, right.kind) ||
|
|
341
|
+
compareCodeUnits(left.name, right.name) ||
|
|
342
|
+
compareCodeUnits(left.entryId, right.entryId));
|
|
343
|
+
}
|
|
344
|
+
function compareCodeUnits(left, right) {
|
|
345
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
346
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { REQUEST_COMPOSITION_MAX_TOOL_DESCRIPTION_LENGTH } from '@maka/core/run-composition';
|
|
20
20
|
import { Service } from './plugin-kernel.js';
|
|
21
21
|
import { MakaPluginRuntimeError, pluginIdentity, registerPluginContribution, } from './plugin-runtime.js';
|
|
22
|
+
import { PluginScopeRegistry } from './plugin-scope-registry.js';
|
|
22
23
|
import { bindToolActivationIdentity } from './tool-activation-identity.js';
|
|
23
24
|
import { TOOL_SEARCH_NAME, TOOL_SEARCH_PROVIDER_NAME } from './tool-availability.js';
|
|
24
25
|
/**
|
|
@@ -30,11 +31,13 @@ import { TOOL_SEARCH_NAME, TOOL_SEARCH_PROVIDER_NAME } from './tool-availability
|
|
|
30
31
|
* remain Host-owned and cannot be shadowed.
|
|
31
32
|
*/
|
|
32
33
|
export class PluginToolService extends Service {
|
|
33
|
-
|
|
34
|
+
registry = new PluginScopeRegistry();
|
|
34
35
|
onChanged;
|
|
36
|
+
agents;
|
|
35
37
|
constructor(ctx, options = {}) {
|
|
36
38
|
super(ctx, 'tools');
|
|
37
39
|
this.onChanged = options.onChanged;
|
|
40
|
+
this.agents = options.agents;
|
|
38
41
|
}
|
|
39
42
|
register(definition) {
|
|
40
43
|
const identity = pluginIdentity(this.ctx);
|
|
@@ -54,14 +57,7 @@ export class PluginToolService extends Service {
|
|
|
54
57
|
resolveContributions(sessionId, coreTools) {
|
|
55
58
|
if (!sessionId || /[\r\n\0]/u.test(sessionId))
|
|
56
59
|
throw new Error('Invalid Tool Session scope');
|
|
57
|
-
const visible =
|
|
58
|
-
for (const entry of this.layers.get('profile')?.values() ?? []) {
|
|
59
|
-
visible.set(entry.definition.name, entry);
|
|
60
|
-
}
|
|
61
|
-
const sessionRoot = `session:${sessionId}`;
|
|
62
|
-
for (const entry of this.layers.get(sessionRoot)?.values() ?? []) {
|
|
63
|
-
visible.set(entry.definition.name, entry);
|
|
64
|
-
}
|
|
60
|
+
const visible = this.registry.visible(sessionId);
|
|
65
61
|
const coreNames = new Set(coreTools.map(({ name }) => name));
|
|
66
62
|
for (const name of visible.keys()) {
|
|
67
63
|
if (coreNames.has(name)) {
|
|
@@ -74,13 +70,7 @@ export class PluginToolService extends Service {
|
|
|
74
70
|
});
|
|
75
71
|
}
|
|
76
72
|
inspect(rootId) {
|
|
77
|
-
|
|
78
|
-
? [[rootId, this.layers.get(rootId)]]
|
|
79
|
-
: [...this.layers.entries()];
|
|
80
|
-
return Object.freeze(layers
|
|
81
|
-
.flatMap(([, layer]) => [...(layer?.values() ?? [])])
|
|
82
|
-
.sort(compareRegistration)
|
|
83
|
-
.map((entry) => Object.freeze({
|
|
73
|
+
return Object.freeze([...this.registry.entries(rootId)].sort(compareRegistration).map((entry) => Object.freeze({
|
|
84
74
|
entryId: entry.entryId,
|
|
85
75
|
scopeId: entry.scopeId,
|
|
86
76
|
extensionId: entry.extensionId,
|
|
@@ -92,12 +82,7 @@ export class PluginToolService extends Service {
|
|
|
92
82
|
}
|
|
93
83
|
publish(identity, definition) {
|
|
94
84
|
const rootId = identity.scopeId;
|
|
95
|
-
|
|
96
|
-
if (!layer) {
|
|
97
|
-
layer = new Map();
|
|
98
|
-
this.layers.set(rootId, layer);
|
|
99
|
-
}
|
|
100
|
-
const existing = layer.get(definition.name);
|
|
85
|
+
const existing = this.registry.get(rootId, definition.name);
|
|
101
86
|
if (existing && existing.entryId !== identity.entryId) {
|
|
102
87
|
throw new MakaPluginRuntimeError('activation_failed', `Plugin Tool ${JSON.stringify(definition.name)} is already registered by ${existing.entryId}`);
|
|
103
88
|
}
|
|
@@ -110,7 +95,9 @@ export class PluginToolService extends Service {
|
|
|
110
95
|
}
|
|
111
96
|
entry.activeCalls += 1;
|
|
112
97
|
try {
|
|
113
|
-
return await
|
|
98
|
+
return await (this.agents
|
|
99
|
+
? this.agents.withInvocation(context, () => definition.impl(args, context))
|
|
100
|
+
: definition.impl(args, context));
|
|
114
101
|
}
|
|
115
102
|
finally {
|
|
116
103
|
entry.activeCalls -= 1;
|
|
@@ -138,35 +125,14 @@ export class PluginToolService extends Service {
|
|
|
138
125
|
retired: false,
|
|
139
126
|
drainWaiters: new Set(),
|
|
140
127
|
};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
layer.delete(definition.name);
|
|
150
|
-
if (layer.size === 0)
|
|
151
|
-
this.layers.delete(rootId);
|
|
152
|
-
throw error;
|
|
153
|
-
}
|
|
154
|
-
return async () => {
|
|
155
|
-
entry.retired = true;
|
|
156
|
-
const currentLayer = this.layers.get(rootId);
|
|
157
|
-
if (currentLayer?.get(definition.name)?.token === entry.token) {
|
|
158
|
-
if (existing && !existing.retired)
|
|
159
|
-
currentLayer.set(definition.name, existing);
|
|
160
|
-
else
|
|
161
|
-
currentLayer.delete(definition.name);
|
|
162
|
-
if (currentLayer.size === 0)
|
|
163
|
-
this.layers.delete(rootId);
|
|
164
|
-
this.notifyChanged(rootId);
|
|
165
|
-
}
|
|
166
|
-
if (entry.activeCalls > 0) {
|
|
167
|
-
await new Promise((resolve) => entry.drainWaiters.add(resolve));
|
|
168
|
-
}
|
|
169
|
-
};
|
|
128
|
+
return this.registry.publish(rootId, definition.name, entry, {
|
|
129
|
+
onChanged: (changedRootId) => this.notifyChanged(changedRootId),
|
|
130
|
+
onRetired: async (retired) => {
|
|
131
|
+
if (retired.activeCalls > 0) {
|
|
132
|
+
await new Promise((resolve) => retired.drainWaiters.add(resolve));
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
});
|
|
170
136
|
}
|
|
171
137
|
notifyChanged(rootId) {
|
|
172
138
|
this.ctx.emit('tools/change');
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { Service } from './plugin-kernel.js';
|
|
20
|
+
import { pluginInvocationSignal } from './plugin-invocation-signal.js';
|
|
21
|
+
/** Structured human-input surface bound to the current Agent invocation. */
|
|
22
|
+
export class PluginUserQuestionService extends Service {
|
|
23
|
+
agents;
|
|
24
|
+
constructor(ctx, agents) {
|
|
25
|
+
super(ctx, 'userQuestions');
|
|
26
|
+
this.agents = agents;
|
|
27
|
+
}
|
|
28
|
+
ask(questions) {
|
|
29
|
+
const ask = this.agents.requireInvocation().toolContext?.askUserQuestion;
|
|
30
|
+
if (!ask)
|
|
31
|
+
throw new Error('User questions are unavailable on this Agent surface');
|
|
32
|
+
return ask([...questions]);
|
|
33
|
+
}
|
|
34
|
+
requestForm(form, options = {}) {
|
|
35
|
+
const invocation = this.agents.requireInvocation();
|
|
36
|
+
const request = invocation.toolContext?.requestUserForm;
|
|
37
|
+
if (!request)
|
|
38
|
+
throw new Error('Structured user forms are unavailable on this Agent surface');
|
|
39
|
+
return request(form, {
|
|
40
|
+
cancellationSignal: pluginInvocationSignal(invocation.abortSignal, options.signal),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { WEB_SEARCH_DEFAULT_LIMIT, normalizeWebSearchLimit, normalizeWebSearchQuery, } from '@maka/core/web-search';
|
|
20
|
+
import { Service } from './plugin-kernel.js';
|
|
21
|
+
import { pluginInvocationSignal } from './plugin-invocation-signal.js';
|
|
22
|
+
/** Provider-policy-aware web search and fetch surface. */
|
|
23
|
+
export class PluginWebService extends Service {
|
|
24
|
+
agents;
|
|
25
|
+
webRuntime;
|
|
26
|
+
constructor(ctx, agents) {
|
|
27
|
+
super(ctx, 'web');
|
|
28
|
+
this.agents = agents;
|
|
29
|
+
}
|
|
30
|
+
bindRuntime(runtime) {
|
|
31
|
+
if (this.ctx.maka)
|
|
32
|
+
throw new Error('Only the Host may bind the Web Runtime');
|
|
33
|
+
if (this.webRuntime)
|
|
34
|
+
throw new Error('Plugin Web Runtime is already bound');
|
|
35
|
+
this.webRuntime = runtime;
|
|
36
|
+
return this.ctx.effect(() => () => {
|
|
37
|
+
if (this.webRuntime === runtime)
|
|
38
|
+
this.webRuntime = undefined;
|
|
39
|
+
}, 'web.bindRuntime()');
|
|
40
|
+
}
|
|
41
|
+
search(query, options = {}) {
|
|
42
|
+
const invocation = this.agents.requireInvocation();
|
|
43
|
+
const normalized = normalizeWebSearchQuery(query);
|
|
44
|
+
if (!normalized)
|
|
45
|
+
throw new TypeError('Web search query is invalid');
|
|
46
|
+
return this.runtime().search({
|
|
47
|
+
query: normalized,
|
|
48
|
+
limit: normalizeWebSearchLimit(options.limit ?? WEB_SEARCH_DEFAULT_LIMIT),
|
|
49
|
+
sessionId: invocation.sessionId,
|
|
50
|
+
abortSignal: pluginInvocationSignal(invocation.abortSignal, options.signal),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
fetch(url, options = {}) {
|
|
54
|
+
const invocation = this.agents.requireInvocation();
|
|
55
|
+
const parsed = new URL(url);
|
|
56
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
|
|
57
|
+
throw new TypeError('Web URL must use HTTP or HTTPS');
|
|
58
|
+
return this.runtime().fetch({
|
|
59
|
+
url: parsed.toString(),
|
|
60
|
+
sessionId: invocation.sessionId,
|
|
61
|
+
abortSignal: pluginInvocationSignal(invocation.abortSignal, options.signal),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
runtime() {
|
|
65
|
+
if (!this.webRuntime)
|
|
66
|
+
throw new Error('Plugin Web Runtime is unavailable');
|
|
67
|
+
return this.webRuntime;
|
|
68
|
+
}
|
|
69
|
+
}
|