@namzu/sdk 45.1.0 → 46.0.0
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/CHANGELOG.md +111 -0
- package/dist/authorization/gate.d.ts +5 -2
- package/dist/authorization/gate.d.ts.map +1 -1
- package/dist/authorization/gate.js +25 -4
- package/dist/authorization/gate.js.map +1 -1
- package/dist/authorization/rules.d.ts.map +1 -1
- package/dist/authorization/rules.js +19 -0
- package/dist/authorization/rules.js.map +1 -1
- package/dist/authorization/shell-lexer.d.ts +24 -0
- package/dist/authorization/shell-lexer.d.ts.map +1 -1
- package/dist/authorization/shell-lexer.js +69 -51
- package/dist/authorization/shell-lexer.js.map +1 -1
- package/dist/pricing/catalogue.generated.d.ts.map +1 -1
- package/dist/pricing/catalogue.generated.js +28 -4
- package/dist/pricing/catalogue.generated.js.map +1 -1
- package/dist/public-runtime.d.ts +3 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +4 -2
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +3 -2
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +5 -2
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +3 -3
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/tool/callable.d.ts +22 -0
- package/dist/registry/tool/callable.d.ts.map +1 -0
- package/dist/registry/tool/callable.js +29 -0
- package/dist/registry/tool/callable.js.map +1 -0
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +8 -1
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.d.ts +37 -11
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +38 -12
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +4 -2
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +18 -5
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +43 -0
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +59 -16
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/skills/registry.d.ts +6 -0
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +1 -0
- package/dist/skills/registry.js.map +1 -1
- package/dist/tools/builtins/computer-use-coordinates.d.ts +65 -0
- package/dist/tools/builtins/computer-use-coordinates.d.ts.map +1 -0
- package/dist/tools/builtins/computer-use-coordinates.js +123 -0
- package/dist/tools/builtins/computer-use-coordinates.js.map +1 -0
- package/dist/tools/builtins/computer-use-image.d.ts +77 -0
- package/dist/tools/builtins/computer-use-image.d.ts.map +1 -0
- package/dist/tools/builtins/computer-use-image.js +223 -0
- package/dist/tools/builtins/computer-use-image.js.map +1 -0
- package/dist/tools/builtins/computer-use.d.ts +519 -14
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +1188 -183
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/index.d.ts +2 -1
- package/dist/tools/builtins/index.d.ts.map +1 -1
- package/dist/tools/builtins/index.js +1 -1
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/skill.d.ts +74 -0
- package/dist/tools/builtins/skill.d.ts.map +1 -1
- package/dist/tools/builtins/skill.js +214 -174
- package/dist/tools/builtins/skill.js.map +1 -1
- package/dist/tools/defineTool.d.ts +2 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +7 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/schedules/present.d.ts.map +1 -1
- package/dist/tools/schedules/present.js +2 -0
- package/dist/tools/schedules/present.js.map +1 -1
- package/dist/tools/schedules/schedule-tool.d.ts +6 -5
- package/dist/tools/schedules/schedule-tool.d.ts.map +1 -1
- package/dist/tools/schedules/schedule-tool.js +121 -30
- package/dist/tools/schedules/schedule-tool.js.map +1 -1
- package/dist/tools/schedules/types.d.ts +69 -1
- package/dist/tools/schedules/types.d.ts.map +1 -1
- package/dist/types/authorization/index.d.ts +21 -0
- package/dist/types/authorization/index.d.ts.map +1 -1
- package/dist/types/authorization/index.js +5 -0
- package/dist/types/authorization/index.js.map +1 -1
- package/dist/types/computer-use/index.d.ts +176 -0
- package/dist/types/computer-use/index.d.ts.map +1 -1
- package/dist/types/computer-use/index.js.map +1 -1
- package/dist/types/tool/index.d.ts +19 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/package.json +3 -1
- package/src/authorization/gate.ts +25 -4
- package/src/authorization/rules.ts +18 -0
- package/src/authorization/shell-lexer.ts +73 -45
- package/src/pricing/catalogue.generated.ts +28 -4
- package/src/pricing/rates.source.json +27 -6
- package/src/public-runtime.ts +16 -2
- package/src/public-tools.ts +19 -1
- package/src/public-types.ts +5 -0
- package/src/registry/tool/callable.ts +37 -0
- package/src/registry/tool/execute.ts +8 -1
- package/src/runtime/query/executor/tool-call-admission.ts +60 -16
- package/src/runtime/query/executor.ts +19 -4
- package/src/runtime/query/review-policy.ts +103 -15
- package/src/skills/registry.ts +8 -0
- package/src/tools/builtins/computer-use-coordinates.ts +144 -0
- package/src/tools/builtins/computer-use-image.ts +278 -0
- package/src/tools/builtins/computer-use.ts +1485 -191
- package/src/tools/builtins/index.ts +7 -1
- package/src/tools/builtins/skill.ts +304 -174
- package/src/tools/defineTool.ts +10 -0
- package/src/tools/schedules/present.ts +2 -0
- package/src/tools/schedules/schedule-tool.ts +139 -33
- package/src/tools/schedules/types.ts +69 -1
- package/src/types/authorization/index.ts +21 -0
- package/src/types/computer-use/index.ts +202 -0
- package/src/types/tool/index.ts +19 -0
|
@@ -7,7 +7,9 @@ import { scanSchedulePrompt } from './prompt-scan.js'
|
|
|
7
7
|
import type {
|
|
8
8
|
ScheduleBrowserGrant,
|
|
9
9
|
ScheduleBrowserSiteLevel,
|
|
10
|
+
ScheduleJobChanges,
|
|
10
11
|
ScheduleJobDraft,
|
|
12
|
+
ScheduleJobUpdateProposal,
|
|
11
13
|
ScheduleToolHost,
|
|
12
14
|
} from './types.js'
|
|
13
15
|
|
|
@@ -15,7 +17,8 @@ export const SCHEDULE_TOOL_NAME = 'schedule'
|
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* A person reads the whole proposal — the prompt, the rules, the schedule,
|
|
18
|
-
* the credential source — before answering `create`, `resume` or
|
|
20
|
+
* the credential source — before answering `create`, `update`, `resume` or
|
|
21
|
+
* `delete`.
|
|
19
22
|
* The executor's `DEFAULT_TOOL_TIMEOUT_MS` (two minutes) is sized for a tool
|
|
20
23
|
* call, not a person on the other end of a screen, and would abandon the
|
|
21
24
|
* call out from under them mid-read. `save_skill`
|
|
@@ -30,18 +33,25 @@ const BROWSER_PROFILE = /^[a-z0-9][a-z0-9-]{0,62}$/
|
|
|
30
33
|
|
|
31
34
|
const inputSchema = z.object({
|
|
32
35
|
action: z
|
|
33
|
-
.enum(['create', 'list', 'pause', 'resume', 'delete'])
|
|
34
|
-
.describe(
|
|
36
|
+
.enum(['create', 'list', 'update', 'pause', 'resume', 'delete'])
|
|
37
|
+
.describe(
|
|
38
|
+
'What to do. create, update, resume and delete ask the operator first. To change a job, update it: deleting and creating it again loses its history.',
|
|
39
|
+
),
|
|
35
40
|
name: z
|
|
36
41
|
.string()
|
|
37
42
|
.regex(/^[a-z0-9][a-z0-9-]{0,62}$/)
|
|
38
43
|
.optional()
|
|
39
44
|
.describe('create: job name, lowercase letters, digits and dashes'),
|
|
40
|
-
prompt: z
|
|
45
|
+
prompt: z
|
|
46
|
+
.string()
|
|
47
|
+
.min(1)
|
|
48
|
+
.max(20_000)
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('create, update: what the run is asked to do'),
|
|
41
51
|
when: z
|
|
42
52
|
.string()
|
|
43
53
|
.optional()
|
|
44
|
-
.describe('create: "every 30m", "0 9 * * 1-5" (cron), "at 2026-09-24 09:00", "in 2h"'),
|
|
54
|
+
.describe('create, update: "every 30m", "0 9 * * 1-5" (cron), "at 2026-09-24 09:00", "in 2h"'),
|
|
45
55
|
folder: z
|
|
46
56
|
.string()
|
|
47
57
|
.optional()
|
|
@@ -90,7 +100,9 @@ const inputSchema = z.object({
|
|
|
90
100
|
.describe('Browser access; omit for none'),
|
|
91
101
|
})
|
|
92
102
|
.optional()
|
|
93
|
-
.describe(
|
|
103
|
+
.describe(
|
|
104
|
+
'create: REQUIRED explicit permission set; there is no default. update: the whole new set, only when the permissions change',
|
|
105
|
+
),
|
|
94
106
|
budget: z
|
|
95
107
|
.object({
|
|
96
108
|
maxIterations: z
|
|
@@ -118,7 +130,7 @@ const inputSchema = z.object({
|
|
|
118
130
|
})
|
|
119
131
|
.optional()
|
|
120
132
|
.describe('Limits of ONE run; omit to use the defaults'),
|
|
121
|
-
job: z.string().optional().describe('pause/resume/delete: job name'),
|
|
133
|
+
job: z.string().optional().describe('update/pause/resume/delete: job name'),
|
|
122
134
|
allFolders: z.boolean().optional().describe('list: include jobs of other folders (names only)'),
|
|
123
135
|
})
|
|
124
136
|
|
|
@@ -208,6 +220,27 @@ function browserGrant(
|
|
|
208
220
|
}
|
|
209
221
|
}
|
|
210
222
|
|
|
223
|
+
/** A proposed permission set as a draft carries it, or why it is refused. */
|
|
224
|
+
function checkPermissions(
|
|
225
|
+
host: ScheduleToolHost,
|
|
226
|
+
proposed: NonNullable<Input['permissions']>,
|
|
227
|
+
): { ok: true; permissions: ScheduleJobDraft['permissions'] } | { ok: false; error: string } {
|
|
228
|
+
if (!proposed.preset && !proposed.rules && !proposed.browser) {
|
|
229
|
+
return {
|
|
230
|
+
ok: false,
|
|
231
|
+
error:
|
|
232
|
+
'permissions needs a preset, rules or a browser grant; an empty permission set is not a choice.',
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!proposed.browser) return { ok: true, permissions: proposed }
|
|
236
|
+
const grant = browserGrant(host, proposed.browser)
|
|
237
|
+
if (!grant.ok) return grant
|
|
238
|
+
return { ok: true, permissions: { ...proposed, browser: grant.grant } }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const NETWORK_WITH_HOST_SHELL =
|
|
242
|
+
'A scheduled job proposed here cannot combine web or browser access with a shell on the host. Deny bash, use execution "sandbox", or ask the operator to create it with `namzu schedule add`.'
|
|
243
|
+
|
|
211
244
|
async function create(
|
|
212
245
|
host: ScheduleToolHost,
|
|
213
246
|
input: Input,
|
|
@@ -221,18 +254,9 @@ async function create(
|
|
|
221
254
|
`create needs ${missing.join(', ')}. permissions is required: propose an explicit set (a preset and/or rules, plus unmatched).`,
|
|
222
255
|
)
|
|
223
256
|
}
|
|
224
|
-
const
|
|
225
|
-
if (!
|
|
226
|
-
|
|
227
|
-
'permissions needs a preset, rules or a browser grant; an empty permission set is not a choice.',
|
|
228
|
-
)
|
|
229
|
-
}
|
|
230
|
-
let permissions: ScheduleJobDraft['permissions'] = proposed
|
|
231
|
-
if (proposed.browser) {
|
|
232
|
-
const grant = browserGrant(host, proposed.browser)
|
|
233
|
-
if (!grant.ok) return refuse(grant.error)
|
|
234
|
-
permissions = { ...proposed, browser: grant.grant }
|
|
235
|
-
}
|
|
257
|
+
const checked = checkPermissions(host, input.permissions as NonNullable<Input['permissions']>)
|
|
258
|
+
if (!checked.ok) return refuse(checked.error)
|
|
259
|
+
const permissions = checked.permissions
|
|
236
260
|
const draft: ScheduleJobDraft = {
|
|
237
261
|
name: input.name as string,
|
|
238
262
|
prompt: input.prompt as string,
|
|
@@ -242,11 +266,7 @@ async function create(
|
|
|
242
266
|
permissions,
|
|
243
267
|
...(input.budget ? { budget: input.budget } : {}),
|
|
244
268
|
}
|
|
245
|
-
if (networkWithHostShell(draft))
|
|
246
|
-
return refuse(
|
|
247
|
-
'A scheduled job proposed here cannot combine web or browser access with a shell on the host. Deny bash, use execution "sandbox", or ask the operator to create it with `namzu schedule add`.',
|
|
248
|
-
)
|
|
249
|
-
}
|
|
269
|
+
if (networkWithHostShell(draft)) return refuse(NETWORK_WITH_HOST_SHELL)
|
|
250
270
|
let preview: Awaited<ReturnType<ScheduleToolHost['preview']>>
|
|
251
271
|
try {
|
|
252
272
|
preview = await host.preview(draft)
|
|
@@ -295,11 +315,94 @@ async function list(host: ScheduleToolHost, input: Input): Promise<ToolResult> {
|
|
|
295
315
|
if (jobs.length === 0) return { success: true, output: 'No scheduled jobs.', data: { jobs: [] } }
|
|
296
316
|
const lines = jobs.map(
|
|
297
317
|
(j) =>
|
|
298
|
-
`${j.name} · ${j.state} · ${j.schedule}${j.nextFireAt ? ` · next ${j.nextFireAt}` : ''}${j.lastStatus ? ` · last ${j.lastStatus}` : ''} · ${j.folder}`,
|
|
318
|
+
`${j.name} · ${j.state} · ${j.schedule}${j.nextFireAt ? ` · next ${j.nextFireAt}` : ''}${j.lastStatus ? ` · last ${j.lastStatus}` : ''} · ${j.folder}${j.inSessionFolder ? ' (this folder)' : ''}`,
|
|
299
319
|
)
|
|
300
320
|
return { success: true, output: lines.join('\n'), data: { jobs } }
|
|
301
321
|
}
|
|
302
322
|
|
|
323
|
+
/** The fields `update` may change, as the model set them. */
|
|
324
|
+
const CHANGEABLE = ['prompt', 'when', 'folder', 'tz', 'permissions', 'budget'] as const
|
|
325
|
+
|
|
326
|
+
async function update(
|
|
327
|
+
host: ScheduleToolHost,
|
|
328
|
+
input: Input,
|
|
329
|
+
signal: AbortSignal | undefined,
|
|
330
|
+
): Promise<ToolResult> {
|
|
331
|
+
if (!host.previewUpdate || !host.confirmUpdate || !host.update) {
|
|
332
|
+
return refuse(
|
|
333
|
+
'This host cannot change a scheduled job. Ask the operator to change it themselves; do not delete and create it again, which loses its history.',
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
if (!input.job) return refuse("update needs job (the job's name) and the fields to change.")
|
|
337
|
+
if (input.name !== undefined) {
|
|
338
|
+
return refuse(
|
|
339
|
+
'update cannot rename a job: name the job to change with job, and leave name out.',
|
|
340
|
+
)
|
|
341
|
+
}
|
|
342
|
+
const given = CHANGEABLE.filter((k) => input[k] !== undefined)
|
|
343
|
+
if (given.length === 0) {
|
|
344
|
+
return refuse(`update needs at least one of ${CHANGEABLE.join(', ')} to change.`)
|
|
345
|
+
}
|
|
346
|
+
let permissions: ScheduleJobDraft['permissions'] | undefined
|
|
347
|
+
if (input.permissions) {
|
|
348
|
+
const checked = checkPermissions(host, input.permissions)
|
|
349
|
+
if (!checked.ok) return refuse(checked.error)
|
|
350
|
+
permissions = checked.permissions
|
|
351
|
+
if (networkWithHostShell({ name: '', prompt: '', when: '', permissions }))
|
|
352
|
+
return refuse(NETWORK_WITH_HOST_SHELL)
|
|
353
|
+
}
|
|
354
|
+
const changes: ScheduleJobChanges = {
|
|
355
|
+
...(input.prompt !== undefined ? { prompt: input.prompt } : {}),
|
|
356
|
+
...(input.when !== undefined ? { when: input.when } : {}),
|
|
357
|
+
...(input.folder !== undefined ? { folder: input.folder } : {}),
|
|
358
|
+
...(input.tz !== undefined ? { tz: input.tz } : {}),
|
|
359
|
+
...(permissions ? { permissions } : {}),
|
|
360
|
+
...(input.budget ? { budget: input.budget } : {}),
|
|
361
|
+
}
|
|
362
|
+
let proposal: ScheduleJobUpdateProposal
|
|
363
|
+
try {
|
|
364
|
+
proposal = await host.previewUpdate(input.job, changes)
|
|
365
|
+
} catch (error) {
|
|
366
|
+
return refuse(error instanceof Error ? error.message : String(error))
|
|
367
|
+
}
|
|
368
|
+
let confirmed = false
|
|
369
|
+
try {
|
|
370
|
+
confirmed = await host.confirmUpdate(
|
|
371
|
+
{
|
|
372
|
+
...proposal,
|
|
373
|
+
promptFindings: scanSchedulePrompt(proposal.preview.prompt),
|
|
374
|
+
proposedBy: 'model',
|
|
375
|
+
},
|
|
376
|
+
signal,
|
|
377
|
+
)
|
|
378
|
+
} catch {
|
|
379
|
+
confirmed = false
|
|
380
|
+
}
|
|
381
|
+
// As for `create`: never save on an answer that came after the
|
|
382
|
+
// operator was no longer being asked.
|
|
383
|
+
if (signal?.aborted) confirmed = false
|
|
384
|
+
if (!confirmed) {
|
|
385
|
+
return {
|
|
386
|
+
...refuse(
|
|
387
|
+
`The operator did not confirm the change; "${proposal.preview.name}" was not changed.`,
|
|
388
|
+
),
|
|
389
|
+
data: { cancelled: true },
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
let saved: Awaited<ReturnType<NonNullable<ScheduleToolHost['update']>>>
|
|
393
|
+
try {
|
|
394
|
+
saved = await host.update(proposal.preview)
|
|
395
|
+
} catch (error) {
|
|
396
|
+
return refuse(error instanceof Error ? error.message : String(error))
|
|
397
|
+
}
|
|
398
|
+
const said = `Job "${saved.name}" was updated in place and keeps its history. It runs ${proposal.preview.schedule}.`
|
|
399
|
+
return {
|
|
400
|
+
success: true,
|
|
401
|
+
output: saved.note ? `${said} ${saved.note}` : said,
|
|
402
|
+
data: { name: saved.name, updated: true, changes: proposal.changes },
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
303
406
|
async function lifecycle(
|
|
304
407
|
host: ScheduleToolHost,
|
|
305
408
|
input: Input,
|
|
@@ -331,12 +434,13 @@ async function lifecycle(
|
|
|
331
434
|
}
|
|
332
435
|
|
|
333
436
|
/**
|
|
334
|
-
* The `schedule` tool: create, list, pause, resume and delete the
|
|
335
|
-
* scheduled jobs from a conversation.
|
|
437
|
+
* The `schedule` tool: create, list, update, pause, resume and delete the
|
|
438
|
+
* operator's scheduled jobs from a conversation.
|
|
336
439
|
*
|
|
337
|
-
* Creation, resuming and deleting are confirmed by a person
|
|
338
|
-
* (`ScheduleToolHost.confirm`), on a
|
|
339
|
-
*
|
|
440
|
+
* Creation, updating, resuming and deleting are confirmed by a person
|
|
441
|
+
* through the host (`ScheduleToolHost.confirm`, `confirmUpdate`), on a
|
|
442
|
+
* screen the host draws from its own computation. An update changes the
|
|
443
|
+
* job in place, so it keeps its id and its history. The model cannot propose that uncovered calls run without
|
|
340
444
|
* asking, nor web access beside a host shell. Register it only where a person
|
|
341
445
|
* is present to confirm: never in a headless run, a scheduled run or a
|
|
342
446
|
* sub-agent.
|
|
@@ -346,7 +450,7 @@ export function buildScheduleTools(host: ScheduleToolHost): ToolDefinition[] {
|
|
|
346
450
|
defineTool({
|
|
347
451
|
name: SCHEDULE_TOOL_NAME,
|
|
348
452
|
description:
|
|
349
|
-
"Manage the operator's scheduled jobs: prompts that run later in a folder, with nobody watching, under an explicit permission set. Use it only when the user asks for something to happen on a schedule. create, resume and delete are confirmed by the operator; pause is not. A job needs name, prompt, when and permissions (unmatched: park or deny, plus a preset, rules or a browser grant). Leave every other field (folder, tz, execution, budget, headed) unset unless the user asked for it: the defaults are the operator's, and the confirmation marks each value you chose. Scheduled runs cannot ask questions.",
|
|
453
|
+
"Manage the operator's scheduled jobs: prompts that run later in a folder, with nobody watching, under an explicit permission set. Use it only when the user asks for something to happen on a schedule. create, update, resume and delete are confirmed by the operator; pause is not. A job needs name, prompt, when and permissions (unmatched: park or deny, plus a preset, rules or a browser grant). To change a job, update it with job and only the fields that change; do not delete and recreate it. Leave every other field (folder, tz, execution, budget, headed) unset unless the user asked for it: the defaults are the operator's, and the confirmation marks each value you chose. Scheduled runs cannot ask questions.",
|
|
350
454
|
inputSchema,
|
|
351
455
|
category: 'custom',
|
|
352
456
|
permissions: [],
|
|
@@ -358,8 +462,8 @@ export function buildScheduleTools(host: ScheduleToolHost): ToolDefinition[] {
|
|
|
358
462
|
concurrencySafe: false,
|
|
359
463
|
presentCall: presentScheduleCall,
|
|
360
464
|
presentResult: presentScheduleResult,
|
|
361
|
-
// A person, not a tool, answers `create`/`resume`/`delete`;
|
|
362
|
-
// `OPERATOR_CONFIRM_TIMEOUT_MS`.
|
|
465
|
+
// A person, not a tool, answers `create`/`update`/`resume`/`delete`;
|
|
466
|
+
// see `OPERATOR_CONFIRM_TIMEOUT_MS`.
|
|
363
467
|
timeoutMs: OPERATOR_CONFIRM_TIMEOUT_MS,
|
|
364
468
|
async execute(input, context: ToolContext) {
|
|
365
469
|
switch (input.action) {
|
|
@@ -367,6 +471,8 @@ export function buildScheduleTools(host: ScheduleToolHost): ToolDefinition[] {
|
|
|
367
471
|
return create(host, input, context.abortSignal)
|
|
368
472
|
case 'list':
|
|
369
473
|
return list(host, input)
|
|
474
|
+
case 'update':
|
|
475
|
+
return update(host, input, context.abortSignal)
|
|
370
476
|
default:
|
|
371
477
|
return lifecycle(host, input, input.action, context.abortSignal)
|
|
372
478
|
}
|
|
@@ -114,6 +114,48 @@ export interface ScheduleJobSummary {
|
|
|
114
114
|
readonly lastStatus?: string
|
|
115
115
|
/** Present only for jobs in the session's own folder. */
|
|
116
116
|
readonly prompt?: string
|
|
117
|
+
/**
|
|
118
|
+
* True for a job whose folder is the session's own. A host that lists
|
|
119
|
+
* other folders' jobs sets it so the model can tell them apart; the tool
|
|
120
|
+
* marks such a job `(this folder)`.
|
|
121
|
+
*/
|
|
122
|
+
readonly inSessionFolder?: boolean
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* What the model proposes to change in an existing job. Every field left
|
|
127
|
+
* out keeps the job's current value. `permissions`, when given, is the whole
|
|
128
|
+
* new set, with the same limits as a new job's; the host keeps the job's
|
|
129
|
+
* `execution` when it is not given, and its additional directories.
|
|
130
|
+
*/
|
|
131
|
+
export interface ScheduleJobChanges {
|
|
132
|
+
readonly prompt?: string
|
|
133
|
+
readonly when?: string
|
|
134
|
+
readonly folder?: string
|
|
135
|
+
readonly tz?: string
|
|
136
|
+
readonly permissions?: ScheduleJobDraft['permissions']
|
|
137
|
+
readonly budget?: ScheduleJobDraft['budget']
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** A proposed change to a job, as the host computed it. */
|
|
141
|
+
export interface ScheduleJobUpdateProposal {
|
|
142
|
+
/** The job as it would be once changed. Every field is the host's own computation. */
|
|
143
|
+
readonly preview: ScheduleJobPreview
|
|
144
|
+
/**
|
|
145
|
+
* What differs from the job as it stands (and from edits saved since it
|
|
146
|
+
* was last confirmed), one line each: `- ` what goes, `+ ` what comes.
|
|
147
|
+
*/
|
|
148
|
+
readonly changes: readonly string[]
|
|
149
|
+
/** The change touches what a run may do: its rules, `unmatched`, where it runs, its browser grant. */
|
|
150
|
+
readonly permissionsChange: boolean
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** What the person confirming a change is shown. */
|
|
154
|
+
export interface ScheduleUpdateRequest extends ScheduleJobUpdateProposal {
|
|
155
|
+
/** The prompt tripwire's findings over `preview.prompt`. */
|
|
156
|
+
readonly promptFindings: readonly string[]
|
|
157
|
+
/** Always `model` from this tool. */
|
|
158
|
+
readonly proposedBy: 'model'
|
|
117
159
|
}
|
|
118
160
|
|
|
119
161
|
/** What the person answered to a proposed job. */
|
|
@@ -157,7 +199,14 @@ export interface ScheduleToolHost {
|
|
|
157
199
|
preview: ScheduleJobPreview,
|
|
158
200
|
options: { readonly paused: boolean },
|
|
159
201
|
): Promise<{ readonly name: string; readonly note?: string }>
|
|
160
|
-
/**
|
|
202
|
+
/**
|
|
203
|
+
* Jobs, the session folder's in full, other folders' without their
|
|
204
|
+
* prompts. `allFolders: false` lets a host list only the session
|
|
205
|
+
* folder's; a host may list every job regardless, marking the session
|
|
206
|
+
* folder's with {@link ScheduleJobSummary.inSessionFolder}. The tool says
|
|
207
|
+
* "No scheduled jobs." when this returns none, so a host that filters
|
|
208
|
+
* says so only for its folder.
|
|
209
|
+
*/
|
|
161
210
|
list(options: { readonly allFolders: boolean }): Promise<readonly ScheduleJobSummary[]>
|
|
162
211
|
/** A job by name or id prefix, or undefined. */
|
|
163
212
|
find(job: string): Promise<ScheduleJobSummary | undefined>
|
|
@@ -170,6 +219,25 @@ export interface ScheduleToolHost {
|
|
|
170
219
|
pause(job: string): Promise<void>
|
|
171
220
|
resume(job: string): Promise<void>
|
|
172
221
|
delete(job: string): Promise<void>
|
|
222
|
+
/**
|
|
223
|
+
* Validate a change to `job` (a name or id prefix) and compute what the
|
|
224
|
+
* person will be shown. Throws with a message on a refusal. The tool
|
|
225
|
+
* offers `update` only when a host has all three of `previewUpdate`,
|
|
226
|
+
* `confirmUpdate` and `update`.
|
|
227
|
+
*/
|
|
228
|
+
previewUpdate?(job: string, changes: ScheduleJobChanges): Promise<ScheduleJobUpdateProposal>
|
|
229
|
+
/**
|
|
230
|
+
* Ask the person whether to save the change. Anything but `true` — a
|
|
231
|
+
* thrown error, a closed screen — changes nothing. See `confirm` on
|
|
232
|
+
* `signal`.
|
|
233
|
+
*/
|
|
234
|
+
confirmUpdate?(request: ScheduleUpdateRequest, signal?: AbortSignal): Promise<boolean>
|
|
235
|
+
/**
|
|
236
|
+
* Save the change the person confirmed, to the same job: its id and its
|
|
237
|
+
* history are kept, and the confirmation is the person's. `note` as for
|
|
238
|
+
* `create`.
|
|
239
|
+
*/
|
|
240
|
+
update?(preview: ScheduleJobPreview): Promise<{ readonly name: string; readonly note?: string }>
|
|
173
241
|
}
|
|
174
242
|
|
|
175
243
|
/** A prompt the session re-sends to itself on an interval. */
|
|
@@ -115,6 +115,19 @@ export type AuthorizationRule =
|
|
|
115
115
|
*/
|
|
116
116
|
description: string
|
|
117
117
|
decide: AuthorizationPredicate
|
|
118
|
+
/**
|
|
119
|
+
* The reason for THIS call, when `decide` returned a decision: what
|
|
120
|
+
* in the input matched, and where. The gate reports it instead of
|
|
121
|
+
* {@link description}. Absent, returning `null` or throwing, the
|
|
122
|
+
* gate reports `description`.
|
|
123
|
+
*
|
|
124
|
+
* A rule that refuses on several grounds and reports one fixed
|
|
125
|
+
* sentence leaves the model, and the person reading the refusal,
|
|
126
|
+
* to guess which ground it was. A scheduled run's refusal listed
|
|
127
|
+
* everything the rule protects when one word of the command had
|
|
128
|
+
* matched.
|
|
129
|
+
*/
|
|
130
|
+
describe?: (call: AuthorizationPredicateCall) => string | null
|
|
118
131
|
}
|
|
119
132
|
|
|
120
133
|
/** The call an `AuthorizationRule` of type `predicate` is asked about. */
|
|
@@ -183,6 +196,14 @@ const PredicateSchema = z.object({
|
|
|
183
196
|
decide: z.custom<AuthorizationPredicate>((value) => typeof value === 'function', {
|
|
184
197
|
message: 'decide must be a function',
|
|
185
198
|
}),
|
|
199
|
+
// Named here so the gate's own parse keeps it: zod strips what a schema
|
|
200
|
+
// does not name.
|
|
201
|
+
describe: z
|
|
202
|
+
.custom<(call: AuthorizationPredicateCall) => string | null>(
|
|
203
|
+
(value) => typeof value === 'function',
|
|
204
|
+
{ message: 'describe must be a function' },
|
|
205
|
+
)
|
|
206
|
+
.optional(),
|
|
186
207
|
})
|
|
187
208
|
|
|
188
209
|
export const AuthorizationRuleSchema = z.discriminatedUnion('type', [
|
|
@@ -44,10 +44,41 @@ export interface ComputerUseCapabilities {
|
|
|
44
44
|
* model reads the reason once and does not try again.
|
|
45
45
|
*/
|
|
46
46
|
readonly unavailableReason?: string
|
|
47
|
+
/**
|
|
48
|
+
* The host implements {@link ComputerUseHost.listWindows} and
|
|
49
|
+
* {@link ComputerUseHost.focusWindow}. Absent or false, the tool does not
|
|
50
|
+
* offer `list_windows` or `focus_window`, even when the methods exist.
|
|
51
|
+
*/
|
|
52
|
+
readonly windows?: boolean
|
|
53
|
+
/**
|
|
54
|
+
* The host implements {@link ComputerUseHost.captureRegion}. Absent or
|
|
55
|
+
* false, `zoom` still works: the tool crops a full capture instead, which
|
|
56
|
+
* costs one whole-display capture per zoom.
|
|
57
|
+
*/
|
|
58
|
+
readonly regionCapture?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* The host implements {@link ComputerUseHost.uiSnapshot} and
|
|
61
|
+
* {@link ComputerUseHost.uiAct}: an accessibility tree (Windows UI
|
|
62
|
+
* Automation, macOS AX, AT-SPI, or a driver that wraps one) whose
|
|
63
|
+
* elements can be acted on by reference instead of by pixel.
|
|
64
|
+
*
|
|
65
|
+
* @experimental The UI-tree surface is reserved for the host packages that
|
|
66
|
+
* implement it; its shape may still change in a minor release.
|
|
67
|
+
*/
|
|
68
|
+
readonly uiTree?: boolean
|
|
47
69
|
}
|
|
48
70
|
|
|
49
71
|
// ---------------------------------------------------------------------------
|
|
50
72
|
// Geometry + screenshot payload
|
|
73
|
+
//
|
|
74
|
+
// Units, once for the whole file: every coordinate and size a host accepts or
|
|
75
|
+
// returns is in PHYSICAL pixels — the pixels of the captured bitmap, not
|
|
76
|
+
// logical points or DPI-scaled units. A 3440x1440 monitor at 150 % scaling is
|
|
77
|
+
// 3440x1440 here. A point a host is asked to act on is relative to the
|
|
78
|
+
// top-left of the display it last captured (the primary display until a host
|
|
79
|
+
// offers display selection); the host adds that display's origin itself.
|
|
80
|
+
// Window and UI-element bounds are the exception and say so: they are in
|
|
81
|
+
// virtual-desktop physical pixels, because a window can span displays.
|
|
51
82
|
// ---------------------------------------------------------------------------
|
|
52
83
|
|
|
53
84
|
export interface DisplayGeometry {
|
|
@@ -56,11 +87,144 @@ export interface DisplayGeometry {
|
|
|
56
87
|
readonly scaleFactor: number
|
|
57
88
|
}
|
|
58
89
|
|
|
90
|
+
/**
|
|
91
|
+
* The display a capture shows.
|
|
92
|
+
*
|
|
93
|
+
* `x`/`y` place it in the virtual desktop (a monitor left of the primary has
|
|
94
|
+
* a negative `x`); `width`/`height` are its physical size. `scaleFactor` is
|
|
95
|
+
* physical pixels per logical pixel — 1 at 96 DPI on Windows, 1.5 at 150 %,
|
|
96
|
+
* 2 on a Retina panel — reported for the host UI and for diagnosis; the tool
|
|
97
|
+
* never multiplies by it, because every coordinate crossing this interface
|
|
98
|
+
* is already physical.
|
|
99
|
+
*/
|
|
100
|
+
export interface DisplayInfo {
|
|
101
|
+
/** Stable for the host's lifetime; what a host would accept to select this display. */
|
|
102
|
+
readonly id: string
|
|
103
|
+
readonly x: number
|
|
104
|
+
readonly y: number
|
|
105
|
+
readonly width: number
|
|
106
|
+
readonly height: number
|
|
107
|
+
readonly scaleFactor: number
|
|
108
|
+
/** True for the display the operating system calls primary. */
|
|
109
|
+
readonly primary?: boolean
|
|
110
|
+
}
|
|
111
|
+
|
|
59
112
|
export interface ScreenshotResult {
|
|
60
113
|
readonly data: Buffer
|
|
61
114
|
readonly mimeType: 'image/png'
|
|
115
|
+
/** Physical pixel width of `data`. */
|
|
62
116
|
readonly width: number
|
|
117
|
+
/** Physical pixel height of `data`. */
|
|
63
118
|
readonly height: number
|
|
119
|
+
/**
|
|
120
|
+
* The display this capture shows. A host should always set it; one written
|
|
121
|
+
* before it existed does not, and the tool then assumes a single display at
|
|
122
|
+
* the origin whose size is the capture's own, with a scale factor of 1.
|
|
123
|
+
*/
|
|
124
|
+
readonly display?: DisplayInfo
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A rectangle in physical pixels. What its origin is relative to depends on where it appears. */
|
|
128
|
+
export interface Rect {
|
|
129
|
+
readonly x: number
|
|
130
|
+
readonly y: number
|
|
131
|
+
readonly width: number
|
|
132
|
+
readonly height: number
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** One top-level window, as {@link ComputerUseHost.listWindows} reports it. */
|
|
136
|
+
export interface WindowInfo {
|
|
137
|
+
/** Opaque and host-defined (an HWND in hex, a CGWindowID, an X11 window id); valid for {@link ComputerUseHost.focusWindow}. */
|
|
138
|
+
readonly id: string
|
|
139
|
+
readonly title: string
|
|
140
|
+
/** Application or process name, e.g. `msedge`, `Teams`, `Code`. */
|
|
141
|
+
readonly app: string
|
|
142
|
+
readonly pid: number
|
|
143
|
+
/** Virtual-desktop physical pixels — not display-relative. */
|
|
144
|
+
readonly bounds: Rect
|
|
145
|
+
readonly focused: boolean
|
|
146
|
+
readonly minimized: boolean
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* What {@link ComputerUseHost.focusWindow} achieved. Bringing a window to
|
|
151
|
+
* the front is a request the operating system can refuse (Windows'
|
|
152
|
+
* foreground lock is the common case), so a host reports the window that is
|
|
153
|
+
* actually in front afterwards rather than assuming its request held.
|
|
154
|
+
*/
|
|
155
|
+
export interface FocusWindowResult {
|
|
156
|
+
/** True only when `focusedId` is the requested window. */
|
|
157
|
+
readonly ok: boolean
|
|
158
|
+
/** The window in front after the attempt, or null when none could be read. */
|
|
159
|
+
readonly focusedId: string | null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* An action an accessibility element can take by reference.
|
|
164
|
+
*
|
|
165
|
+
* @experimental See {@link ComputerUseCapabilities.uiTree}.
|
|
166
|
+
*/
|
|
167
|
+
export type UiElementAction =
|
|
168
|
+
| 'invoke'
|
|
169
|
+
| 'focus'
|
|
170
|
+
| 'set_value'
|
|
171
|
+
| 'toggle'
|
|
172
|
+
| 'select'
|
|
173
|
+
| 'expand'
|
|
174
|
+
| 'collapse'
|
|
175
|
+
| 'scroll_into_view'
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* One accessibility element.
|
|
179
|
+
*
|
|
180
|
+
* @experimental See {@link ComputerUseCapabilities.uiTree}.
|
|
181
|
+
*/
|
|
182
|
+
export interface UiElement {
|
|
183
|
+
/**
|
|
184
|
+
* Opaque reference for {@link ComputerUseHost.uiAct}, valid until the
|
|
185
|
+
* host's next snapshot. Empty for an element the host cannot act on (a
|
|
186
|
+
* label, a group), which is in the tree for what it says.
|
|
187
|
+
*/
|
|
188
|
+
readonly ref: string
|
|
189
|
+
/** Platform role, e.g. `Button`, `Edit`, `ListItem` (UIA ControlType) or `AXButton`. */
|
|
190
|
+
readonly role: string
|
|
191
|
+
readonly name: string
|
|
192
|
+
readonly value?: string
|
|
193
|
+
/** A platform automation id, when the element has one. */
|
|
194
|
+
readonly automationId?: string
|
|
195
|
+
/** Virtual-desktop physical pixels, when the element is on screen. */
|
|
196
|
+
readonly bounds?: Rect
|
|
197
|
+
/** Element states such as `focused`, `disabled`, `selected`, `checked`, `expanded`. */
|
|
198
|
+
readonly states?: readonly string[]
|
|
199
|
+
readonly actions?: readonly UiElementAction[]
|
|
200
|
+
readonly children?: readonly UiElement[]
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* An accessibility tree for one window.
|
|
205
|
+
*
|
|
206
|
+
* @experimental See {@link ComputerUseCapabilities.uiTree}.
|
|
207
|
+
*/
|
|
208
|
+
export interface UiSnapshot {
|
|
209
|
+
readonly windowId?: string
|
|
210
|
+
/** The window's title, as its application sets it. */
|
|
211
|
+
readonly title?: string
|
|
212
|
+
/** The owning application or process name, as in {@link WindowInfo.app}. */
|
|
213
|
+
readonly app?: string
|
|
214
|
+
readonly root: UiElement
|
|
215
|
+
/** True when the host stopped walking the tree before it ended (a size or time bound). */
|
|
216
|
+
readonly truncated?: boolean
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* What {@link ComputerUseHost.uiAct} did.
|
|
221
|
+
*
|
|
222
|
+
* @experimental See {@link ComputerUseCapabilities.uiTree}.
|
|
223
|
+
*/
|
|
224
|
+
export interface UiActResult {
|
|
225
|
+
readonly ok: boolean
|
|
226
|
+
/** Why it did not, in words the model can act on (a stale ref, a disabled element). */
|
|
227
|
+
readonly detail?: string
|
|
64
228
|
}
|
|
65
229
|
|
|
66
230
|
export interface Point {
|
|
@@ -152,8 +316,46 @@ export interface ComputerUseHost {
|
|
|
152
316
|
readonly capabilities: ComputerUseCapabilities
|
|
153
317
|
|
|
154
318
|
getDisplayGeometry(): Promise<DisplayGeometry>
|
|
319
|
+
/**
|
|
320
|
+
* Points in `action` are physical pixels relative to the display of the
|
|
321
|
+
* most recent capture; a `screenshot` result carries that display in
|
|
322
|
+
* {@link ScreenshotResult.display}.
|
|
323
|
+
*/
|
|
155
324
|
execute(action: ComputerUseAction): Promise<ComputerUseResult>
|
|
156
325
|
|
|
326
|
+
/**
|
|
327
|
+
* The visible, titled top-level windows, front to back where the platform
|
|
328
|
+
* knows the order. Offered to the model only with
|
|
329
|
+
* {@link ComputerUseCapabilities.windows}.
|
|
330
|
+
*/
|
|
331
|
+
listWindows?(): Promise<readonly WindowInfo[]>
|
|
332
|
+
/**
|
|
333
|
+
* Bring a window to the front, restoring it when minimized. Offered only
|
|
334
|
+
* with {@link ComputerUseCapabilities.windows}.
|
|
335
|
+
*/
|
|
336
|
+
focusWindow?(id: string): Promise<FocusWindowResult>
|
|
337
|
+
/**
|
|
338
|
+
* Capture one region of the current display at full physical resolution.
|
|
339
|
+
* `rect` is display-relative physical pixels; the result's `width` and
|
|
340
|
+
* `height` are the region's. Used by `zoom` when
|
|
341
|
+
* {@link ComputerUseCapabilities.regionCapture} is set.
|
|
342
|
+
*/
|
|
343
|
+
captureRegion?(rect: Rect): Promise<ScreenshotResult>
|
|
344
|
+
/**
|
|
345
|
+
* The accessibility tree of one window, or of the focused window when
|
|
346
|
+
* `windowId` is omitted. Offered only with {@link ComputerUseCapabilities.uiTree}.
|
|
347
|
+
*
|
|
348
|
+
* @experimental
|
|
349
|
+
*/
|
|
350
|
+
uiSnapshot?(windowId?: string): Promise<UiSnapshot>
|
|
351
|
+
/**
|
|
352
|
+
* Act on an element from the latest {@link uiSnapshot}. `value` is the text
|
|
353
|
+
* for `set_value`. Offered only with {@link ComputerUseCapabilities.uiTree}.
|
|
354
|
+
*
|
|
355
|
+
* @experimental
|
|
356
|
+
*/
|
|
357
|
+
uiAct?(ref: string, action: UiElementAction, value?: string): Promise<UiActResult>
|
|
358
|
+
|
|
157
359
|
initialize?(): Promise<void>
|
|
158
360
|
dispose?(): Promise<void>
|
|
159
361
|
}
|
package/src/types/tool/index.ts
CHANGED
|
@@ -42,6 +42,12 @@ export interface SkillRegistryRef {
|
|
|
42
42
|
registeredName: string
|
|
43
43
|
description: string
|
|
44
44
|
location: string
|
|
45
|
+
/**
|
|
46
|
+
* The skill's directory as the host reads it. Optional so a registry
|
|
47
|
+
* written before it existed still satisfies this interface; what the
|
|
48
|
+
* `skill` tool hands a host's `resolveModelDirectory` to map.
|
|
49
|
+
*/
|
|
50
|
+
directory?: string
|
|
45
51
|
allowedTools?: string
|
|
46
52
|
invocation?: 'model' | 'operator' | 'both'
|
|
47
53
|
}[]
|
|
@@ -50,6 +56,7 @@ export interface SkillRegistryRef {
|
|
|
50
56
|
registeredName: string
|
|
51
57
|
description: string
|
|
52
58
|
location: string
|
|
59
|
+
directory?: string
|
|
53
60
|
allowedTools?: string
|
|
54
61
|
invocation?: 'model' | 'operator' | 'both'
|
|
55
62
|
}[]
|
|
@@ -927,6 +934,18 @@ export interface ToolDefinition<TInput = unknown> extends ToolPresentation<TInpu
|
|
|
927
934
|
isReadOnly?(input: TInput): boolean
|
|
928
935
|
isDestructive?(input: TInput): boolean
|
|
929
936
|
isConcurrencySafe?(input: TInput): boolean
|
|
937
|
+
/**
|
|
938
|
+
* This call sends what is on the operator's screen to the model provider:
|
|
939
|
+
* a screenshot, the titles of the open windows, a window's accessibility
|
|
940
|
+
* tree.
|
|
941
|
+
*
|
|
942
|
+
* Orthogonal to {@link isReadOnly}: a screenshot changes nothing and is
|
|
943
|
+
* still the one read a person may want to allow before it happens. A
|
|
944
|
+
* review policy given a consent record (`createReviewHandler`'s
|
|
945
|
+
* `screenConsent`) asks once per session before the first such call.
|
|
946
|
+
* Absent means the tool never does.
|
|
947
|
+
*/
|
|
948
|
+
capturesScreen?(input: TInput): boolean
|
|
930
949
|
|
|
931
950
|
/**
|
|
932
951
|
* Opt-in ordering boundary in a direct model tool-call batch. Earlier
|