@namzu/sdk 61.3.0 → 61.4.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 +6 -0
- package/dist/public-runtime.d.ts +1 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts +80 -0
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +172 -9
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/package.json +1 -1
- package/src/public-runtime.ts +4 -0
- package/src/tools/builtins/computer-use.ts +245 -7
|
@@ -91,11 +91,72 @@ const MAX_UI_SNAPSHOT_CHARS = 14_000
|
|
|
91
91
|
/** The refs of earlier snapshots the tool still resolves for the host. */
|
|
92
92
|
const MAX_HELD_UI_REFS = 4_000
|
|
93
93
|
|
|
94
|
+
/** One control an advisor may be asked to choose among. */
|
|
95
|
+
export interface ComputerUseAdvisorCandidate {
|
|
96
|
+
/** The ref the model was shown for it. */
|
|
97
|
+
readonly ref: string
|
|
98
|
+
/** The control as the model reads it, such as `Button "Save"`. */
|
|
99
|
+
readonly description: string
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** What an advisor is asked: which of these controls does this intent name? */
|
|
103
|
+
export interface ComputerUseAdvisorRequest {
|
|
104
|
+
/** What the model wants done, in its own words. */
|
|
105
|
+
readonly intent: string
|
|
106
|
+
readonly candidates: readonly ComputerUseAdvisorCandidate[]
|
|
107
|
+
/**
|
|
108
|
+
* The ref the model itself chose for this intent, when it chose one. Then the
|
|
109
|
+
* advisor is only watching: the answer is not acted on.
|
|
110
|
+
*/
|
|
111
|
+
readonly chosen?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** An advisor's answer: the candidates in order of fit, best first, each with how sure it is (0 to 1). */
|
|
115
|
+
export interface ComputerUseAdvice {
|
|
116
|
+
readonly ranked: readonly {
|
|
117
|
+
readonly ref: string
|
|
118
|
+
readonly confidence: number
|
|
119
|
+
}[]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* A fast second opinion on which control an intent names, for hosts that run a
|
|
124
|
+
* small decision model beside the main one. The tool never depends on it: an
|
|
125
|
+
* advisor that fails, is slow or is unsure is the same as none.
|
|
126
|
+
*/
|
|
127
|
+
export interface ComputerUseAdvisor {
|
|
128
|
+
/**
|
|
129
|
+
* Whether the tool may act on the answer. Then the model can name a control
|
|
130
|
+
* by words (`ui_do`) and the tool chooses; when false the advisor is asked
|
|
131
|
+
* about the controls the model chose by ref (`ui_act` with `intent`) and its
|
|
132
|
+
* answer is not used.
|
|
133
|
+
*/
|
|
134
|
+
readonly acts: boolean
|
|
135
|
+
pick(
|
|
136
|
+
request: ComputerUseAdvisorRequest,
|
|
137
|
+
signal?: AbortSignal,
|
|
138
|
+
): Promise<ComputerUseAdvice | undefined>
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The least confidence in the best control for the tool to act on it. */
|
|
142
|
+
const ADVICE_MIN_CONFIDENCE = 0.85
|
|
143
|
+
/** How far the best control must lead the next one. */
|
|
144
|
+
const ADVICE_MIN_MARGIN = 0.4
|
|
145
|
+
/** The most controls an advisor is asked about at once. */
|
|
146
|
+
const MAX_ADVISED_CANDIDATES = 255
|
|
147
|
+
|
|
94
148
|
/**
|
|
95
149
|
* How `createComputerUseTool` sizes screenshots, paces actions and bounds a
|
|
96
150
|
* batch. Every field is optional.
|
|
97
151
|
*/
|
|
98
152
|
export interface ComputerUseToolOptions {
|
|
153
|
+
/**
|
|
154
|
+
* A second opinion on which control an intent names; see
|
|
155
|
+
* {@link ComputerUseAdvisor}. With one that `acts`, the tool offers `ui_do`
|
|
156
|
+
* (name a control by words); with any, `ui_act` accepts an `intent`. Absent,
|
|
157
|
+
* neither exists and the tool is exactly what it was.
|
|
158
|
+
*/
|
|
159
|
+
readonly advisor?: ComputerUseAdvisor
|
|
99
160
|
/**
|
|
100
161
|
* What every screenshot and zoom image is fitted to before the model sees
|
|
101
162
|
* it. Default {@link STANDARD_SCREENSHOT_LIMITS}, which every current vision
|
|
@@ -270,6 +331,16 @@ const uiActSchema = z.object({
|
|
|
270
331
|
ref: z.string().min(1),
|
|
271
332
|
action: z.enum(UI_ACTIONS),
|
|
272
333
|
value: z.string().optional(),
|
|
334
|
+
/** What this is for, in a few words; only read when the tool has an advisor. */
|
|
335
|
+
intent: z.string().min(1).max(200).optional(),
|
|
336
|
+
})
|
|
337
|
+
const uiDoSchema = z.object({
|
|
338
|
+
type: z.literal('ui_do'),
|
|
339
|
+
/** The control, in words: "the Save button", "the name field". */
|
|
340
|
+
target: z.string().min(1).max(200),
|
|
341
|
+
action: z.enum(UI_ACTIONS),
|
|
342
|
+
value: z.string().optional(),
|
|
343
|
+
window_id: z.string().min(1).optional(),
|
|
273
344
|
})
|
|
274
345
|
|
|
275
346
|
/** What a batch may carry: everything but the image-returning actions and another batch. */
|
|
@@ -285,6 +356,7 @@ const batchItemSchema = z.discriminatedUnion('type', [
|
|
|
285
356
|
listWindowsSchema,
|
|
286
357
|
focusWindowSchema,
|
|
287
358
|
uiActSchema,
|
|
359
|
+
uiDoSchema,
|
|
288
360
|
])
|
|
289
361
|
|
|
290
362
|
const withFrame = { screenshot_id: screenshotIdSchema }
|
|
@@ -308,6 +380,7 @@ const actionSchema = z.discriminatedUnion('type', [
|
|
|
308
380
|
uiSnapshotSchema,
|
|
309
381
|
waitForSchema,
|
|
310
382
|
uiActSchema.extend(withFrame),
|
|
383
|
+
uiDoSchema.extend(withFrame),
|
|
311
384
|
z.object({
|
|
312
385
|
type: z.literal('batch'),
|
|
313
386
|
actions: z.array(batchItemSchema).min(1),
|
|
@@ -355,6 +428,7 @@ const ALL_ACTIONS: readonly ToolActionType[] = [
|
|
|
355
428
|
'ui_snapshot',
|
|
356
429
|
'wait_for',
|
|
357
430
|
'ui_act',
|
|
431
|
+
'ui_do',
|
|
358
432
|
'batch',
|
|
359
433
|
]
|
|
360
434
|
|
|
@@ -376,6 +450,7 @@ const DESTRUCTIVE_ACTIONS = new Set<string>([
|
|
|
376
450
|
'key',
|
|
377
451
|
'scroll',
|
|
378
452
|
'ui_act',
|
|
453
|
+
'ui_do',
|
|
379
454
|
])
|
|
380
455
|
|
|
381
456
|
/** Observations that show the model what is on the screen, on their own. */
|
|
@@ -454,6 +529,7 @@ function requiredCapability(type: string): keyof ComputerUseCapabilities | null
|
|
|
454
529
|
case 'ui_snapshot':
|
|
455
530
|
case 'wait_for':
|
|
456
531
|
case 'ui_act':
|
|
532
|
+
case 'ui_do':
|
|
457
533
|
return 'uiTree'
|
|
458
534
|
default:
|
|
459
535
|
return null
|
|
@@ -481,7 +557,11 @@ function windowCaptureAvailable(host: ComputerUseHost, caps: ComputerUseCapabili
|
|
|
481
557
|
)
|
|
482
558
|
}
|
|
483
559
|
|
|
484
|
-
function availableActions(
|
|
560
|
+
function availableActions(
|
|
561
|
+
host: ComputerUseHost,
|
|
562
|
+
caps: ComputerUseCapabilities,
|
|
563
|
+
advisor?: ComputerUseAdvisor,
|
|
564
|
+
): ToolActionType[] {
|
|
485
565
|
const displayScreenshot = hostActionAvailable(caps, 'screenshot')
|
|
486
566
|
const window = windowCaptureAvailable(host, caps)
|
|
487
567
|
const screenshot = displayScreenshot || window
|
|
@@ -516,6 +596,8 @@ function availableActions(host: ComputerUseHost, caps: ComputerUseCapabilities):
|
|
|
516
596
|
case 'wait_for':
|
|
517
597
|
case 'ui_act':
|
|
518
598
|
return uiTree
|
|
599
|
+
case 'ui_do':
|
|
600
|
+
return uiTree && advisor?.acts === true
|
|
519
601
|
case 'batch':
|
|
520
602
|
return false
|
|
521
603
|
default:
|
|
@@ -550,8 +632,9 @@ function buildDescription(
|
|
|
550
632
|
host: ComputerUseHost,
|
|
551
633
|
caps: ComputerUseCapabilities,
|
|
552
634
|
settings: ResolvedSettings,
|
|
635
|
+
advisor?: ComputerUseAdvisor,
|
|
553
636
|
): string {
|
|
554
|
-
const available = availableActions(host, caps)
|
|
637
|
+
const available = availableActions(host, caps, advisor)
|
|
555
638
|
const unavailable = unavailableHostActions(host, caps)
|
|
556
639
|
const intro = hostActionAvailable(caps, 'screenshot')
|
|
557
640
|
? `Controls the user's desktop on a ${caps.displayServer} host: screenshots, mouse and keyboard, for GUI tasks.`
|
|
@@ -597,6 +680,10 @@ function buildDescription(
|
|
|
597
680
|
lines.push(
|
|
598
681
|
'To start a Windows program, a shell command (Start-Process <program>, or cmd.exe /c start <program>) is surest when you have a shell tool. The Start menu searches by display names in the system language, and ENTER on a name it does not find opens a web search in the browser.',
|
|
599
682
|
)
|
|
683
|
+
if (available.includes('ui_do'))
|
|
684
|
+
lines.push(
|
|
685
|
+
'ui_do {target, action, value, window_id} acts on a control named in words ("the Save button") among those the latest ui_snapshot showed: a second model chooses it and the tool says which control it acted on. When it is not sure it acts on nothing and lists the closest controls: then use ui_act on one of their refs. Use ui_act with a ref whenever you already have one.',
|
|
686
|
+
)
|
|
600
687
|
if (available.includes('list_windows'))
|
|
601
688
|
lines.push(
|
|
602
689
|
'list_windows names the open windows with their ids; focus_window brings one to the front.',
|
|
@@ -695,6 +782,7 @@ const ACTION_REQUIREMENTS: Readonly<Record<ToolActionType, string>> = {
|
|
|
695
782
|
'ui_snapshot takes an optional window_id, root (a ref to open) and query (text to find)',
|
|
696
783
|
wait_for: 'wait_for needs until and match; role, window_id and timeout_ms are optional',
|
|
697
784
|
ui_act: 'ui_act needs ref and action, and value for set_value',
|
|
785
|
+
ui_do: 'ui_do needs target (the control, in words) and action, and value for set_value',
|
|
698
786
|
batch: 'batch needs actions',
|
|
699
787
|
}
|
|
700
788
|
|
|
@@ -712,10 +800,11 @@ function hostModelSchema(
|
|
|
712
800
|
host: ComputerUseHost,
|
|
713
801
|
caps: ComputerUseCapabilities,
|
|
714
802
|
settings: ResolvedSettings,
|
|
803
|
+
advisor?: ComputerUseAdvisor,
|
|
715
804
|
): Record<string, unknown> {
|
|
716
805
|
// An unavailable host remains a diagnostic tool. Avoid invalid empty enums
|
|
717
806
|
// on provider wires; every execution is still refused before host access.
|
|
718
|
-
const offered = availableActions(host, caps)
|
|
807
|
+
const offered = availableActions(host, caps, advisor)
|
|
719
808
|
const actions = offered.length > 0 ? offered : ALL_ACTIONS.filter((a) => a !== 'batch')
|
|
720
809
|
const items = actions.filter((action) => !OUTSIDE_BATCH.has(action))
|
|
721
810
|
const buttons = new Set<string>()
|
|
@@ -841,6 +930,30 @@ function hostModelSchema(
|
|
|
841
930
|
type: 'string',
|
|
842
931
|
description: 'The text set_value puts in the control.',
|
|
843
932
|
}
|
|
933
|
+
if (advisor)
|
|
934
|
+
fields.intent = {
|
|
935
|
+
type: 'string',
|
|
936
|
+
description:
|
|
937
|
+
'For ui_act: what this is for, in a few words ("save the file"). Optional; it only helps check the choice.',
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (present.includes('ui_do')) {
|
|
941
|
+
fields.target = {
|
|
942
|
+
type: 'string',
|
|
943
|
+
description:
|
|
944
|
+
'For ui_do: the control in words, such as "the Save button" or "the name field". The tool picks the control from the latest ui_snapshot.',
|
|
945
|
+
}
|
|
946
|
+
if (!present.includes('ui_act')) {
|
|
947
|
+
fields.action = {
|
|
948
|
+
type: 'string',
|
|
949
|
+
enum: [...UI_ACTIONS],
|
|
950
|
+
description: 'What ui_do does to the control.',
|
|
951
|
+
}
|
|
952
|
+
fields.value = {
|
|
953
|
+
type: 'string',
|
|
954
|
+
description: 'The text set_value puts in the control.',
|
|
955
|
+
}
|
|
956
|
+
}
|
|
844
957
|
}
|
|
845
958
|
if (!forItems && present.includes('zoom'))
|
|
846
959
|
fields.region = {
|
|
@@ -963,6 +1076,33 @@ function uiActLabel(
|
|
|
963
1076
|
}
|
|
964
1077
|
}
|
|
965
1078
|
|
|
1079
|
+
/** What a ui_do does, over the words the model named the control with. */
|
|
1080
|
+
function uiDoLabel(input: {
|
|
1081
|
+
readonly target: string
|
|
1082
|
+
readonly action: UiElementAction
|
|
1083
|
+
readonly value?: string
|
|
1084
|
+
}): string {
|
|
1085
|
+
const target = quotedText(input.target)
|
|
1086
|
+
switch (input.action) {
|
|
1087
|
+
case 'invoke':
|
|
1088
|
+
return `Press the control for ${target}`
|
|
1089
|
+
case 'set_value':
|
|
1090
|
+
return `Set the control for ${target} to ${quotedText(input.value ?? '')}`
|
|
1091
|
+
case 'toggle':
|
|
1092
|
+
return `Toggle the control for ${target}`
|
|
1093
|
+
case 'select':
|
|
1094
|
+
return `Select the control for ${target}`
|
|
1095
|
+
case 'expand':
|
|
1096
|
+
return `Expand the control for ${target}`
|
|
1097
|
+
case 'collapse':
|
|
1098
|
+
return `Collapse the control for ${target}`
|
|
1099
|
+
case 'focus':
|
|
1100
|
+
return `Focus the control for ${target}`
|
|
1101
|
+
case 'scroll_into_view':
|
|
1102
|
+
return `Scroll to the control for ${target}`
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
966
1106
|
/**
|
|
967
1107
|
* Human activity text for one action; the raw input remains the model-facing
|
|
968
1108
|
* record. `describe` names a ui_act ref's control when the tool knows it.
|
|
@@ -1008,6 +1148,8 @@ function itemLabel(
|
|
|
1008
1148
|
return `Wait until ${input.until.replace('_', ' ')} ${quotedText(input.match)}`
|
|
1009
1149
|
case 'ui_act':
|
|
1010
1150
|
return uiActLabel(input, describe)
|
|
1151
|
+
case 'ui_do':
|
|
1152
|
+
return uiDoLabel(input)
|
|
1011
1153
|
case 'batch':
|
|
1012
1154
|
return batchLabel(input.actions, describe)
|
|
1013
1155
|
}
|
|
@@ -1349,7 +1491,8 @@ export function createComputerUseTool(
|
|
|
1349
1491
|
}
|
|
1350
1492
|
: host.capabilities
|
|
1351
1493
|
const frames = new ScreenshotFrames()
|
|
1352
|
-
const available = new Set<string>(availableActions(host, caps))
|
|
1494
|
+
const available = new Set<string>(availableActions(host, caps, options.advisor))
|
|
1495
|
+
const advisor = options.advisor
|
|
1353
1496
|
|
|
1354
1497
|
// The controls of the latest ui_snapshot, by the ref the model was shown.
|
|
1355
1498
|
// A ref names a control, not one reading of it: the same host ref keeps the
|
|
@@ -1357,6 +1500,8 @@ export function createComputerUseTool(
|
|
|
1357
1500
|
// control. Whether the control is still there is the host's answer.
|
|
1358
1501
|
const uiRefs = new Map<string, UiRef>()
|
|
1359
1502
|
const uiRefByHost = new Map<string, string>()
|
|
1503
|
+
// The refs the latest reading of each window showed, so an advisor is asked about what is there now.
|
|
1504
|
+
const shownRefs = new Map<string, Set<string>>()
|
|
1360
1505
|
let uiSnapshots = 0
|
|
1361
1506
|
let uiRefCount = 0
|
|
1362
1507
|
let uiSnapshotWindowId: string | undefined
|
|
@@ -1371,6 +1516,30 @@ export function createComputerUseTool(
|
|
|
1371
1516
|
}
|
|
1372
1517
|
const label = (input: BatchItem | ActionInput): string => itemLabel(input, describeUiRef)
|
|
1373
1518
|
|
|
1519
|
+
/** The controls of a window's latest reading that offer an action (any, when none is named), as an advisor is shown them. */
|
|
1520
|
+
const advisedCandidates = (
|
|
1521
|
+
windowId: string | undefined,
|
|
1522
|
+
action?: UiElementAction,
|
|
1523
|
+
): ComputerUseAdvisorCandidate[] => {
|
|
1524
|
+
const shown = shownRefs.get(windowId ?? '')
|
|
1525
|
+
const out: ComputerUseAdvisorCandidate[] = []
|
|
1526
|
+
if (!shown) return out
|
|
1527
|
+
for (const ref of shown) {
|
|
1528
|
+
const entry = uiRefs.get(ref)
|
|
1529
|
+
if (!entry) continue
|
|
1530
|
+
const offered = entry.element.actions ?? []
|
|
1531
|
+
if (offered.length === 0) continue
|
|
1532
|
+
if (action !== undefined && !offered.includes(action)) continue
|
|
1533
|
+
const text = uiElementText(entry.element)
|
|
1534
|
+
out.push({
|
|
1535
|
+
ref,
|
|
1536
|
+
description: text.length > 80 ? `${text.slice(0, 79)}…` : text,
|
|
1537
|
+
})
|
|
1538
|
+
if (out.length >= MAX_ADVISED_CANDIDATES) break
|
|
1539
|
+
}
|
|
1540
|
+
return out
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1374
1543
|
const refusal = (type: string): string | null => {
|
|
1375
1544
|
if (available.has(type)) return null
|
|
1376
1545
|
const required = requiredCapability(type)
|
|
@@ -1705,6 +1874,14 @@ export function createComputerUseTool(
|
|
|
1705
1874
|
if (item.action === 'set_value' && item.value === undefined)
|
|
1706
1875
|
throw new StepFailure('set_value needs value, the text to put in the control')
|
|
1707
1876
|
return step(async () => {
|
|
1877
|
+
// A second opinion on the model's own choice: asked beside the action, never waited for.
|
|
1878
|
+
if (advisor && item.intent !== undefined) {
|
|
1879
|
+
const candidates = advisedCandidates(entry.windowId)
|
|
1880
|
+
if (candidates.length >= 2)
|
|
1881
|
+
void advisor
|
|
1882
|
+
.pick({ intent: item.intent, candidates, chosen: item.ref })
|
|
1883
|
+
.catch(() => undefined)
|
|
1884
|
+
}
|
|
1708
1885
|
let outcome: UiActResult
|
|
1709
1886
|
try {
|
|
1710
1887
|
outcome = await (host.uiAct as NonNullable<ComputerUseHost['uiAct']>)(
|
|
@@ -1728,6 +1905,62 @@ export function createComputerUseTool(
|
|
|
1728
1905
|
return deliveryOutcome(outcome)
|
|
1729
1906
|
})
|
|
1730
1907
|
}
|
|
1908
|
+
case 'ui_do': {
|
|
1909
|
+
if (!advisor?.acts)
|
|
1910
|
+
throw new StepFailure('ui_do is not available here; use ui_act with a ref')
|
|
1911
|
+
if (item.action === 'set_value' && item.value === undefined)
|
|
1912
|
+
throw new StepFailure('set_value needs value, the text to put in the control')
|
|
1913
|
+
if (uiSnapshots === 0)
|
|
1914
|
+
throw new StepFailure(
|
|
1915
|
+
'no ui_snapshot has been taken yet; take one, because ui_do chooses among the controls it showed',
|
|
1916
|
+
)
|
|
1917
|
+
const windowId = item.window_id ?? uiSnapshotWindowId
|
|
1918
|
+
const candidates = advisedCandidates(windowId, item.action)
|
|
1919
|
+
if (candidates.length === 0)
|
|
1920
|
+
throw new StepFailure(
|
|
1921
|
+
`no control in the latest ui_snapshot${windowId ? ` of window ${windowId}` : ''} offers ${item.action}; take a ui_snapshot and act with ui_act on a ref`,
|
|
1922
|
+
)
|
|
1923
|
+
const target = item.target
|
|
1924
|
+
return step(async (signal) => {
|
|
1925
|
+
let advice: ComputerUseAdvice | undefined
|
|
1926
|
+
try {
|
|
1927
|
+
advice = await advisor.pick({ intent: target, candidates }, signal)
|
|
1928
|
+
} catch {
|
|
1929
|
+
if (signal?.aborted) throw new StepFailure('cancelled')
|
|
1930
|
+
}
|
|
1931
|
+
const [best, next] = advice?.ranked ?? []
|
|
1932
|
+
const sure =
|
|
1933
|
+
best !== undefined &&
|
|
1934
|
+
best.confidence >= ADVICE_MIN_CONFIDENCE &&
|
|
1935
|
+
best.confidence - (next?.confidence ?? 0) >= ADVICE_MIN_MARGIN &&
|
|
1936
|
+
uiRefs.has(best.ref)
|
|
1937
|
+
if (!best || !sure) {
|
|
1938
|
+
const close = (advice?.ranked ?? [])
|
|
1939
|
+
.filter((entry) => uiRefs.has(entry.ref))
|
|
1940
|
+
.slice(0, 3)
|
|
1941
|
+
.map((entry) => describeUiRef(entry.ref))
|
|
1942
|
+
throw deliveryFailure({
|
|
1943
|
+
delivery: 'not_delivered',
|
|
1944
|
+
retrySafety: 'safe',
|
|
1945
|
+
detail: `ui_do was not sure which control is ${quotedText(target)}, so it acted on nothing.${close.length > 0 ? ` Closest: ${close.join('; ')}.` : ''} Use ui_act on one of the refs, or ui_snapshot {query: ...} to find it.`,
|
|
1946
|
+
})
|
|
1947
|
+
}
|
|
1948
|
+
const chosen = describeUiRef(best.ref) ?? best.ref
|
|
1949
|
+
const ran = await plan(
|
|
1950
|
+
{
|
|
1951
|
+
type: 'ui_act',
|
|
1952
|
+
ref: best.ref,
|
|
1953
|
+
action: item.action,
|
|
1954
|
+
...(item.value !== undefined ? { value: item.value } : {}),
|
|
1955
|
+
},
|
|
1956
|
+
undefined,
|
|
1957
|
+
).run(signal)
|
|
1958
|
+
const prefix = `chose ${chosen} for ${quotedText(target)} (${Math.round(best.confidence * 100)}% sure)`
|
|
1959
|
+
return typeof ran === 'string'
|
|
1960
|
+
? `${prefix}: ${ran}`
|
|
1961
|
+
: { ...ran, note: `${prefix}: ${ran.note}` }
|
|
1962
|
+
})
|
|
1963
|
+
}
|
|
1731
1964
|
}
|
|
1732
1965
|
}
|
|
1733
1966
|
|
|
@@ -1822,6 +2055,11 @@ export function createComputerUseTool(
|
|
|
1822
2055
|
}
|
|
1823
2056
|
}
|
|
1824
2057
|
uiSnapshotWindowId = snapshot.windowId ?? requestedWindowId
|
|
2058
|
+
{
|
|
2059
|
+
const key = uiSnapshotWindowId ?? ''
|
|
2060
|
+
const now = new Set(refs.keys())
|
|
2061
|
+
shownRefs.set(key, snapshot.region ? new Set([...(shownRefs.get(key) ?? []), ...now]) : now)
|
|
2062
|
+
}
|
|
1825
2063
|
if (uiSnapshotWindowId !== undefined) treeUsable.set(uiSnapshotWindowId, refs.size > 0)
|
|
1826
2064
|
uiSnapshotFrame = frames.latest()
|
|
1827
2065
|
const window =
|
|
@@ -2347,7 +2585,7 @@ export function createComputerUseTool(
|
|
|
2347
2585
|
if (step.item.type === 'focus_window' && windowCaptureAvailable(host, caps))
|
|
2348
2586
|
postWindowId = step.item.window_id
|
|
2349
2587
|
else if (
|
|
2350
|
-
step.item.type === 'ui_act' &&
|
|
2588
|
+
(step.item.type === 'ui_act' || step.item.type === 'ui_do') &&
|
|
2351
2589
|
uiSnapshotWindowId &&
|
|
2352
2590
|
!explicitDisplayFrame &&
|
|
2353
2591
|
windowCaptureAvailable(host, caps)
|
|
@@ -2504,9 +2742,9 @@ export function createComputerUseTool(
|
|
|
2504
2742
|
|
|
2505
2743
|
const tool = defineTool({
|
|
2506
2744
|
name: COMPUTER_USE_TOOL_NAME,
|
|
2507
|
-
description: buildDescription(host, caps, settings),
|
|
2745
|
+
description: buildDescription(host, caps, settings, options.advisor),
|
|
2508
2746
|
inputSchema: actionSchema,
|
|
2509
|
-
modelInputSchema: hostModelSchema(host, caps, settings),
|
|
2747
|
+
modelInputSchema: hostModelSchema(host, caps, settings, options.advisor),
|
|
2510
2748
|
validationErrorHint: `Action requirements: ${ALL_ACTIONS.map((action) => actionRequirement(action, host, caps)).join('; ')}. A batch is {"type":"batch","actions":[...]} with at most ${settings.maxBatchActions} actions and no screenshot, zoom, ui_snapshot or batch inside.`,
|
|
2511
2749
|
category: 'custom',
|
|
2512
2750
|
permissions: [],
|