blob: e83d8504f77c88a330f7398d8295973bab976e23 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { PluginAgentService } from '../plugin-agent-service.js';
import { PluginAttachmentService } from '../plugin-attachment-service.js';
import { PluginFilesystemService } from '../plugin-fs-service.js';
import { Context } from '../plugin-kernel.js';
import { PluginShellService } from '../plugin-shell-service.js';
import { PluginWebService } from '../plugin-web-service.js';
import type { MakaToolContext } from '../tool-runtime.js';
test('resource services preserve the current Session and cancellation context', async () => {
const root = new Context();
const agents = new PluginAgentService(root);
const fs = new PluginFilesystemService(root, agents);
const shell = new PluginShellService(root, agents);
const web = new PluginWebService(root, agents);
const attachments = new PluginAttachmentService(root, agents);
const calls: string[] = [];
fs.bindRuntime({
execute: async (operation, invocation) => {
calls.push(`fs:${operation.kind}:${invocation.sessionId}`);
return operation;
},
});
shell.bindRuntime({
run: async (options, invocation) => {
calls.push(`shell:${options.command}:${invocation.turnId}`);
return { ok: true };
},
});
web.bindRuntime({
search: async (input) => {
calls.push(`search:${input.query}:${input.sessionId}`);
return { ok: true, provider: 'tavily', results: [] };
},
fetch: async (input) => {
calls.push(`fetch:${input.url}:${input.sessionId}`);
return 'body';
},
});
attachments.bindRuntime({
create: async (input, invocation) => {
calls.push(`attachment:${input.name}:${invocation.turnId}`);
return {
kind: 'other',
name: input.name,
mimeType: input.mimeType,
bytes: 1,
ref: { kind: 'session_file', sessionId: invocation.sessionId, relativePath: 'a' },
};
},
read: async () => new Uint8Array([1]),
list: async () => [],
});
const context: MakaToolContext = {
sessionId: 'session-a',
turnId: 'turn-a',
cwd: '/workspace',
toolCallId: 'call-a',
abortSignal: new AbortController().signal,
emitOutput: () => undefined,
};
const plugin = root.extend({
maka: { rootId: 'profile', packageId: 'fixture', entryId: 'fixture', generation: 1 },
});
await agents.withInvocation(context, async () => {
await plugin.fs.read('README.md');
await plugin.shell.run({ command: 'pwd' });
await plugin.web.search(' maka ');
assert.equal(await plugin.web.fetch('https://example.com'), 'body');
await plugin.attachments.create({ name: 'a.txt', mimeType: 'text/plain', content: 'a' });
});
assert.deepEqual(calls, [
'fs:read:session-a',
'shell:pwd:turn-a',
'search:maka:session-a',
'fetch:https://example.com/:session-a',
'attachment:a.txt:turn-a',
]);
await root.fiber.dispose();
});
test('Web custom cancellation cannot replace Host invocation cancellation', async () => {
const root = new Context();
const agents = new PluginAgentService(root);
const web = new PluginWebService(root, agents);
const hostAbort = new AbortController();
const pluginAbort = new AbortController();
const signals: AbortSignal[] = [];
web.bindRuntime({
search: async (input) => {
if (input.abortSignal) signals.push(input.abortSignal);
return { ok: true, provider: 'tavily', results: [] };
},
fetch: async (input) => {
if (input.abortSignal) signals.push(input.abortSignal);
return 'body';
},
});
const context: MakaToolContext = {
sessionId: 'session-a',
turnId: 'turn-a',
cwd: '/workspace',
toolCallId: 'call-a',
abortSignal: hostAbort.signal,
emitOutput: () => undefined,
};
await agents.withInvocation(context, async () => {
await web.search('maka', { signal: pluginAbort.signal });
await web.fetch('https://example.com', { signal: pluginAbort.signal });
});
assert.deepEqual(
signals.map((signal) => signal.aborted),
[false, false],
);
hostAbort.abort(new Error('Host stopped'));
assert.deepEqual(
signals.map((signal) => signal.aborted),
[true, true],
);
assert.equal(pluginAbort.signal.aborted, false);
await root.fiber.dispose();
});