返回 CodeWhale
index.mjs
1 // Tools that use `exec.core.call` and report exactly what came back, so a test
2 // can see every outcome the core can produce: a result, or a typed refusal.
3 export const name = 'core-call'
4 export const inject = ['tools', 'commands']
5
6 const outcome = async (promise) => {
7 try {
8 return { ok: await promise }
9 } catch (error) {
10 return { failed: { name: error.name, code: error.code ?? null, message: error.message } }
11 }
12 }
13
14 export function apply(ctx) {
15 // One core call: `{name, input}` -> `{ok: {content, isError, structured?}}` or `{failed: {code, message}}`.
16 ctx.tools.register({
17 name: 'cc_call',
18 description: 'Ask the core to run a tool and report the outcome.',
19 parameters: {
20 type: 'object',
21 properties: { name: { type: 'string' }, input: { type: 'object' } },
22 required: ['name'],
23 additionalProperties: false,
24 },
25 execute: async ({ name, input }, exec) => {
26 if (exec.core === undefined) return { noCore: true }
27 return outcome(exec.core.call(name, input ?? {}))
28 },
29 })
30
31 // `count` calls of the same tool, one after another or all at once.
32 ctx.tools.register({
33 name: 'cc_many',
34 description: 'Ask the core to run a tool several times and report every outcome.',
35 parameters: {
36 type: 'object',
37 properties: { name: { type: 'string' }, input: { type: 'object' }, count: { type: 'number' }, parallel: { type: 'boolean' } },
38 required: ['name', 'count'],
39 additionalProperties: false,
40 },
41 execute: async ({ name, input, count, parallel }, exec) => {
42 if (exec.core === undefined) return { noCore: true }
43 const one = () => outcome(exec.core.call(name, input ?? {}))
44 if (parallel) return { outcomes: await Promise.all(Array.from({ length: count }, one)) }
45 const outcomes = []
46 for (let i = 0; i < count; i++) outcomes.push(await one())
47 return { outcomes }
48 },
49 })
50
51 ctx.tools.register({
52 name: 'cc_probe',
53 description: 'Report whether this call has exec.core, and what exec holds.',
54 parameters: { type: 'object', properties: {}, additionalProperties: false },
55 execute: (_input, exec) => ({ hasCore: 'core' in exec, keys: Object.keys(exec).sort(), coreKeys: exec.core ? Object.keys(exec.core).sort() : null }),
56 })
57
58 // A bad request the host refuses before sending anything.
59 ctx.tools.register({
60 name: 'cc_local',
61 description: 'Make core calls the host itself refuses to send.',
62 parameters: { type: 'object', properties: {}, additionalProperties: false },
63 execute: async (_input, exec) => ({
64 emptyName: await outcome(exec.core.call('')),
65 longName: await outcome(exec.core.call('x'.repeat(200))),
66 notJson: await outcome(exec.core.call('read', { fn: () => 1 })),
67 cyclic: await outcome(exec.core.call('read', (() => { const a = {}; a.a = a; return a })())),
68 }),
69 })
70
71 // Sleeps after (or without) a core call; for call-deadline tests.
72 ctx.tools.register({
73 name: 'cc_then_sleep',
74 description: 'Make one core call, then wait `ms` milliseconds or until cancelled.',
75 parameters: {
76 type: 'object',
77 properties: { name: { type: 'string' }, input: { type: 'object' }, ms: { type: 'number' } },
78 required: ['name', 'ms'],
79 additionalProperties: false,
80 },
81 execute: async ({ name, input, ms }, exec) => {
82 const first = await outcome(exec.core.call(name, input ?? {}))
83 await new Promise((resolve, reject) => {
84 const timer = setTimeout(resolve, ms)
85 exec.signal.addEventListener('abort', () => {
86 clearTimeout(timer)
87 reject(new Error('aborted'))
88 })
89 })
90 return { first }
91 },
92 })
93
94 ctx.commands.register({
95 name: 'cc-probe',
96 description: 'Report whether a command invocation has core.',
97 handler: (invocation) => ({ kind: 'success', text: JSON.stringify({ hasCore: 'core' in invocation, keys: Object.keys(invocation).sort() }) }),
98 })
99 }
100
100 lines Plain Text