release 1.0.0: cost control, 41 tools, 29 skills, custom commands, auto-load
This commit is contained in:
1 parent
a22d8e13b1
commit
ffa9a02c26
97 files changed
+4791
-788
No files matched your search
+60
-4
@@ -1,3 +1,4 @@
|
||||
import { usageOf } from './helpers';
|
||||
import { expect, test } from 'bun:test';
|
||||
import { MockLanguageModelV4, simulateReadableStream } from 'ai/test';
|
||||
import type { LanguageModelV4CallOptions, LanguageModelV4StreamPart } from '@ai-sdk/provider';
|
||||
@@ -9,10 +10,7 @@ import { guardPlugin } from '../src/plugins-builtin';
|
||||
import { Session } from '../src/session';
|
||||
import { createTaskTool } from '../src/subagent';
|
||||
|
||||
const usage = {
|
||||
inputTokens: { total: 10, noCache: 10, cacheRead: 0, cacheWrite: 0 },
|
||||
outputTokens: { total: 5 },
|
||||
} as any;
|
||||
const usage = usageOf(10);
|
||||
|
||||
const stream = (parts: LanguageModelV4StreamPart[]) => ({
|
||||
stream: simulateReadableStream({ chunks: parts, chunkDelayInMs: null, initialDelayInMs: null }),
|
||||
@@ -268,3 +266,61 @@ test('subagent does not see the parent conversation', async () =>
|
||||
expect(JSON.stringify(seen[1]?.prompt)).not.toContain('MY-SECRET-PARENT-CONTEXT');
|
||||
expect(JSON.stringify(seen[1]?.prompt)).toContain('Look at glob src/*.');
|
||||
}));
|
||||
|
||||
test('an explore subagent runs on the cheaper model, not the parent model', async () =>
|
||||
inTempDir(async () => {
|
||||
// Two separate models: the parent's and the cheaper explore model. Whichever
|
||||
// one the subagent loop hits tells us which it was handed.
|
||||
let parentCalls = 0;
|
||||
const parent = new MockLanguageModelV4({
|
||||
doStream: async () => {
|
||||
if (parentCalls++ === 0)
|
||||
return stream(toolCall('c1', 'task', { description: 'search', prompt: 'find x', kind: 'explore' }));
|
||||
return stream(text('parent answer'));
|
||||
},
|
||||
});
|
||||
let cheapCalls = 0;
|
||||
const cheap = new MockLanguageModelV4({
|
||||
doStream: async () => {
|
||||
cheapCalls++;
|
||||
return stream(text('explore found nothing'));
|
||||
},
|
||||
});
|
||||
|
||||
const session = new Session({
|
||||
model: parent,
|
||||
askApproval: async () => 'deny',
|
||||
extraTools: { task: createTaskTool({ model: parent, subagentModel: cheap }) },
|
||||
autoApprove: ['task'],
|
||||
});
|
||||
|
||||
for await (const _ of session.send('search for x')) void _;
|
||||
|
||||
expect(cheapCalls).toBeGreaterThan(0);
|
||||
}));
|
||||
|
||||
test('a finished subagent reports its token use to the parent', async () =>
|
||||
inTempDir(async () => {
|
||||
let calls = 0;
|
||||
const model = new MockLanguageModelV4({
|
||||
doStream: async () => {
|
||||
if (calls++ === 0)
|
||||
return stream(toolCall('c1', 'task', { description: 'search', prompt: 'find x', kind: 'explore' }));
|
||||
if (calls === 2) return stream(text('findings'));
|
||||
return stream(text('done'));
|
||||
},
|
||||
});
|
||||
const usageEvents: { kind: string; inputTokens: number; outputTokens: number }[] = [];
|
||||
|
||||
const session = new Session({
|
||||
model,
|
||||
askApproval: async () => 'deny',
|
||||
extraTools: { task: createTaskTool({ model, onUsage: (u) => usageEvents.push(u) }) },
|
||||
autoApprove: ['task'],
|
||||
});
|
||||
|
||||
for await (const _ of session.send('go')) void _;
|
||||
|
||||
expect(usageEvents).toHaveLength(1);
|
||||
expect(usageEvents[0]).toMatchObject({ kind: 'explore' });
|
||||
}));
|
||||
Reference in new issue
Block a user