Vendored
+71
-76
@@ -46,10 +46,10 @@ function testCatalog() {
|
||||
assert.strictEqual(catalog.findCatalogEntry('qwen3-1.7b').ctxSize, 16384);
|
||||
assert.ok(catalog.findCatalogEntry('qwen3.5-4b').ctxSize >= 8192);
|
||||
const tiny = catalog.filterToolsForModel(
|
||||
[{ name: 'web_search' }, { name: 'qvac_capability' }, { name: 'cu_drag' }],
|
||||
[{ name: 'web_search' }, { name: 'browser' }, { name: 'webcam' }, { name: 'qvac_capability' }, { name: 'cu_drag' }],
|
||||
'qwen3.5-0.8b',
|
||||
);
|
||||
assert.deepStrictEqual(tiny.map((t) => t.name), ['web_search']);
|
||||
assert.deepStrictEqual(tiny.map((t) => t.name), ['web_search', 'browser', 'webcam']);
|
||||
assert.strictEqual(
|
||||
catalog.filterToolsForModel([{ name: 'todo_write' }, { name: 'cu_drag' }], 'qwen3.5-0.8b')[0].name,
|
||||
'todo_write',
|
||||
@@ -219,6 +219,9 @@ function testTruncateAndPerm() {
|
||||
const t = truncate.truncateWithMarker('x'.repeat(5000), 400);
|
||||
assert.ok(t.length < 5000);
|
||||
assert.ok(t.indexOf('truncated') >= 0);
|
||||
const rendered = truncate.renderToolResult({ ok: true, note: 'attached', images: [{ path: '/tmp/secret.png' }] });
|
||||
assert.ok(rendered.indexOf('attached') >= 0);
|
||||
assert.ok(rendered.indexOf('/tmp/secret.png') < 0);
|
||||
const pat = permRules.patternFromArgs('run_terminal_cmd', { command: 'git status -sb' });
|
||||
assert.strictEqual(pat, 'git status');
|
||||
assert.ok(policy.shellSafe('git status'));
|
||||
@@ -231,6 +234,32 @@ function testTruncateAndPerm() {
|
||||
assert.ok(toolBudget.shouldSkipShell(voice));
|
||||
}
|
||||
|
||||
function testVisionFollowUp() {
|
||||
const qvac = require('../lib/qvac.js');
|
||||
const frame = path.join(os.tmpdir(), 'agent-harness-webcam-test.webp');
|
||||
fs.writeFileSync(frame, Buffer.from('RIFF'));
|
||||
const hoisted = qvac.prepareVisionHistory([
|
||||
{ role: 'user', content: 'Do you see anything?' },
|
||||
{ role: 'assistant', content: '', tool_calls: [{ name: 'webcam' }] },
|
||||
{ role: 'tool', name: 'webcam', content: '{"ok":true,"note":"attached"}', images: [{ path: frame }] },
|
||||
]);
|
||||
const last = hoisted[hoisted.length - 1];
|
||||
const tool = hoisted[hoisted.length - 2];
|
||||
assert.strictEqual(tool.role, 'tool');
|
||||
assert.ok(!tool.images);
|
||||
assert.ok(!tool.attachments);
|
||||
assert.strictEqual(last.role, 'user');
|
||||
assert.ok(String(last.content).trim().length > 0);
|
||||
assert.strictEqual(last.content, qvac.VISION_FOLLOWUP_QUESTION);
|
||||
assert.strictEqual(last.attachments.length, 1);
|
||||
assert.strictEqual(last.attachments[0].path, frame);
|
||||
|
||||
const blank = qvac.prepareVisionHistory([{ role: 'user', content: '', images: [{ path: frame }] }])[0];
|
||||
assert.strictEqual(blank.role, 'user');
|
||||
assert.strictEqual(blank.content, qvac.VISION_FOLLOWUP_QUESTION);
|
||||
assert.strictEqual(blank.attachments[0].path, frame);
|
||||
}
|
||||
|
||||
function testPaths() {
|
||||
const dir = paths.ensureDir(path.join(os.tmpdir(), 'agent-harness-test'));
|
||||
assert.ok(fs.existsSync(dir));
|
||||
@@ -335,6 +364,7 @@ testNet();
|
||||
testCustomTools();
|
||||
testPlanTodosStationarity();
|
||||
testTruncateAndPerm();
|
||||
testVisionFollowUp();
|
||||
testPaths();
|
||||
testQvacWorkerDeps();
|
||||
testDevicePrefersGpu();
|
||||
@@ -350,8 +380,7 @@ testWebFetchTimeout()
|
||||
});
|
||||
|
||||
async function testWebFetchTimeout() {
|
||||
const orig = globalThis.fetch;
|
||||
globalThis.fetch = () => new Promise(() => {});
|
||||
tools.setBrowserBackend({ call: () => new Promise(() => {}) });
|
||||
const started = Date.now();
|
||||
try {
|
||||
const hung = await tools.webFetch('https://example.com/ip', 40);
|
||||
@@ -360,69 +389,55 @@ async function testWebFetchTimeout() {
|
||||
assert.strictEqual(hung.url, 'https://example.com/ip');
|
||||
assert.ok(Date.now() - started < 2000);
|
||||
} finally {
|
||||
globalThis.fetch = orig;
|
||||
tools.setBrowserBackend(null);
|
||||
}
|
||||
|
||||
globalThis.fetch = async (url) => ({
|
||||
status: 200,
|
||||
url: String(url),
|
||||
text: async () => '203.0.113.8',
|
||||
tools.setBrowserBackend({
|
||||
call: async (_action, payload) => ({
|
||||
url: payload.url,
|
||||
status: 200,
|
||||
html: '<html><body>203.0.113.8</body></html>',
|
||||
text: '203.0.113.8',
|
||||
}),
|
||||
});
|
||||
try {
|
||||
const ok = await tools.webFetch('https://ifconfig.me/ip', 200);
|
||||
assert.strictEqual(ok.status, 200);
|
||||
assert.strictEqual(ok.url, 'https://ifconfig.me/ip');
|
||||
assert.strictEqual(ok.text, '203.0.113.8');
|
||||
assert.ok(/203\.0\.113\.8/.test(ok.text));
|
||||
assert.ok(!ok.error);
|
||||
} finally {
|
||||
globalThis.fetch = orig;
|
||||
tools.setBrowserBackend(null);
|
||||
}
|
||||
|
||||
globalThis.fetch = async () => ({ status: 503, url: 'https://example.com', text: async () => 'down' });
|
||||
tools.setBrowserBackend({
|
||||
call: async () => ({ status: 503, url: 'https://example.com', html: '<p>down</p>', text: 'down', error: 'HTTP 503' }),
|
||||
});
|
||||
try {
|
||||
const failed = await tools.webFetch('https://example.com/status', 200);
|
||||
assert.ok(failed.error);
|
||||
assert.strictEqual(failed.status, 503);
|
||||
assert.strictEqual(failed.url, 'https://example.com');
|
||||
} finally {
|
||||
globalThis.fetch = orig;
|
||||
tools.setBrowserBackend(null);
|
||||
}
|
||||
}
|
||||
|
||||
function testGoogleSearchParseAndFallback() {
|
||||
const parsed = tools.parseGoogleHits(
|
||||
'<a href="/url?q=https://example.com/page&sa=U"><div class="BNeawe vvjwJb AP7Wnd">Example Domain</div></a>'
|
||||
);
|
||||
assert.strictEqual(parsed.length, 1);
|
||||
assert.strictEqual(parsed[0].url, 'https://example.com/page');
|
||||
assert.strictEqual(parsed[0].title, 'Example Domain');
|
||||
assert.strictEqual(tools.parseGoogleHits('<title>Google Search</title><noscript>Please click here</noscript>').length, 0);
|
||||
assert.ok(tools.SCHEMAS.find((t) => t.name === 'google_search'));
|
||||
assert.ok(tools.SCHEMAS.find((t) => t.name === 'fetch_page'));
|
||||
assert.ok(tools.SCHEMAS.find((t) => t.name === 'wiki_search'));
|
||||
const rss = require('../agent/web-search.js').parseRssItems(
|
||||
'<rss><item><title>Example</title><link>https://example.com/rss</link></item></rss>'
|
||||
);
|
||||
assert.strictEqual(rss[0].url, 'https://example.com/rss');
|
||||
assert.ok(tools.SCHEMAS.find((t) => t.name === 'web_search'));
|
||||
assert.ok(tools.SCHEMAS.find((t) => t.name === 'web_fetch'));
|
||||
}
|
||||
|
||||
async function testGoogleSearchFallsBackToDuckDuckGo() {
|
||||
const orig = globalThis.fetch;
|
||||
globalThis.fetch = async (url) => {
|
||||
const href = String(url);
|
||||
if (href.indexOf('google.com') >= 0) {
|
||||
return {
|
||||
status: 200,
|
||||
url: href,
|
||||
text: async () => '<title>Google Search</title><noscript>Please click here</noscript>',
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
url: href,
|
||||
text: async () => '<a class="result__a" href="https://duckduckgo.com/l/?uddg=https%3A%2F%2Fexample.com%2Fddg">DDG Example</a>',
|
||||
};
|
||||
};
|
||||
tools.setBrowserBackend({
|
||||
call: async (_action, payload) => {
|
||||
if (payload.engine === 'google') return [];
|
||||
return [{ url: 'https://example.com/ddg', title: 'DDG Example', source: payload.engine }];
|
||||
},
|
||||
});
|
||||
try {
|
||||
const hits = await tools.webSearch('example domain', 200);
|
||||
assert.ok(Array.isArray(hits));
|
||||
@@ -431,55 +446,35 @@ async function testGoogleSearchFallsBackToDuckDuckGo() {
|
||||
assert.strictEqual(hits[0].url, 'https://example.com/ddg');
|
||||
assert.strictEqual(hits[0].title, 'DDG Example');
|
||||
} finally {
|
||||
globalThis.fetch = orig;
|
||||
tools.setBrowserBackend(null);
|
||||
}
|
||||
|
||||
globalThis.fetch = async (url) => {
|
||||
const href = String(url);
|
||||
if (href.indexOf('google.com') >= 0) {
|
||||
return {
|
||||
status: 200,
|
||||
url: href,
|
||||
text: async () =>
|
||||
'<a href="/url?q=https://example.com/google&sa=U"><div class="BNeawe vvjwJb AP7Wnd">From Google</div></a>',
|
||||
};
|
||||
}
|
||||
throw new Error('duckduckgo should not run when google hits');
|
||||
};
|
||||
tools.setBrowserBackend({
|
||||
call: async (_action, payload) => {
|
||||
if (payload.engine === 'duckduckgo') throw new Error('duckduckgo should not run when google hits');
|
||||
return [{ url: 'https://example.com/google', title: 'From Google', source: payload.engine }];
|
||||
},
|
||||
});
|
||||
try {
|
||||
const hits = await tools.googleSearchWithFallback('example domain', 200);
|
||||
assert.strictEqual(hits[0].source, 'google');
|
||||
assert.strictEqual(hits[0].url, 'https://example.com/google');
|
||||
assert.strictEqual(hits[0].title, 'From Google');
|
||||
} finally {
|
||||
globalThis.fetch = orig;
|
||||
tools.setBrowserBackend(null);
|
||||
}
|
||||
|
||||
const bingHref =
|
||||
'https://www.bing.com/ck/a?!&&p=ae&u=a1aHR0cDovL3d3dy5leGFtcGxlLmNvbS8&ntb=1';
|
||||
globalThis.fetch = async (url) => {
|
||||
const href = String(url);
|
||||
if (href.indexOf('google.com') >= 0) {
|
||||
return { status: 200, url: href, text: async () => '<title>Google Search</title>' };
|
||||
}
|
||||
if (href.indexOf('duckduckgo.com') >= 0) {
|
||||
return {
|
||||
status: 202,
|
||||
url: href,
|
||||
text: async () => '<div class="anomaly-modal__title">Unfortunately, bots use DuckDuckGo too.</div>',
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
url: href,
|
||||
text: async () => '<li class="b_algo"><h2><a href="' + bingHref + '"><strong>Example Domain</strong></a></h2></li>',
|
||||
};
|
||||
};
|
||||
tools.setBrowserBackend({
|
||||
call: async (_action, payload) => {
|
||||
if (payload.engine === 'google') return [];
|
||||
return [{ url: 'http://www.example.com/', title: 'Example Domain', source: payload.engine }];
|
||||
},
|
||||
});
|
||||
try {
|
||||
const hits = await tools.googleSearchWithFallback('example domain', 200);
|
||||
assert.strictEqual(hits[0].source, 'bing');
|
||||
assert.strictEqual(hits[0].source, 'duckduckgo');
|
||||
assert.strictEqual(hits[0].url, 'http://www.example.com/');
|
||||
} finally {
|
||||
globalThis.fetch = orig;
|
||||
tools.setBrowserBackend(null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user