newmark-agent 0.6.7 → 0.6.8
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/dist/cli-commands.js +1 -1
- package/dist/conversation-utility-host.bundle.cjs +2060 -1166
- package/dist/core/agent.js +2 -0
- package/dist/core/agentKernelRunner.js +4 -0
- package/dist/core/computerUseSession.js +1 -1
- package/dist/core/fileWriteObservation.d.ts +41 -0
- package/dist/core/fileWriteObservation.js +454 -0
- package/dist/core/toolPolicy.d.ts +1 -1
- package/dist/core/toolPolicy.js +1 -1
- package/dist/tools/computerUse.d.ts +2 -0
- package/dist/tools/computerUse.js +206 -19
- package/dist/tools/index.d.ts +3 -4
- package/dist/tools/index.js +144 -67
- package/dist/wsl-agent-host.bundle.cjs +2060 -1166
- package/package.json +5 -3
|
@@ -605,11 +605,15 @@ function sceneGeneration(apps, elements) {
|
|
|
605
605
|
async function observeUiAutomation(maxChars, rootHandle, timeoutMs = 30000, lane = 'uia') {
|
|
606
606
|
const limit = Math.min(Math.max(Math.floor(maxChars || 30000), 1000), 200000);
|
|
607
607
|
const handleHex = String(rootHandle || '').trim().replace(/^0x/i, '').replace(/[^0-9a-f]/gi, '');
|
|
608
|
+
if (!/[1-9a-f]/i.test(handleHex)) {
|
|
609
|
+
return { elements: [], visited: 0, error: 'Window-scoped UI Automation requires a non-zero native window handle.', elapsedMs: 0 };
|
|
610
|
+
}
|
|
611
|
+
const hostProcessId = Math.max(1, Math.floor(process.pid));
|
|
608
612
|
const script = [
|
|
609
|
-
handleHex
|
|
610
|
-
? `$nativeHandle = [System.IntPtr]::new([Convert]::ToInt64(${psQuote(handleHex)}, 16)); $root = [System.Windows.Automation.AutomationElement]::FromHandle($nativeHandle)`
|
|
611
|
-
: '$root = [System.Windows.Automation.AutomationElement]::RootElement',
|
|
613
|
+
`$nativeHandle = [System.IntPtr]::new([Convert]::ToInt64(${psQuote(handleHex)}, 16)); $root = [System.Windows.Automation.AutomationElement]::FromHandle($nativeHandle)`,
|
|
612
614
|
'if ($null -eq $root) { throw "UI Automation root is unavailable." }',
|
|
615
|
+
'$rootProcessId = 0; try { $rootProcessId = [int]$root.Current.ProcessId } catch {}',
|
|
616
|
+
`if ($rootProcessId -eq ${hostProcessId}) { throw "UI Automation of the Newmark host window is skipped to avoid Electron self-accessibility hangs." }`,
|
|
613
617
|
'$cache = New-Object System.Windows.Automation.CacheRequest',
|
|
614
618
|
'$cache.TreeScope = [System.Windows.Automation.TreeScope]::Element',
|
|
615
619
|
'$cache.TreeFilter = [System.Windows.Automation.Automation]::ControlViewCondition',
|
|
@@ -663,6 +667,9 @@ async function observeUiAutomation(maxChars, rootHandle, timeoutMs = 30000, lane
|
|
|
663
667
|
return { elements: parseJsonArray(result.output), visited: 0, error: 'UI Automation returned invalid JSON.', elapsedMs: result.elapsedMs };
|
|
664
668
|
}
|
|
665
669
|
}
|
|
670
|
+
function skippedUiAutomation(error) {
|
|
671
|
+
return { elements: [], visited: 0, ...(error ? { error } : {}), elapsedMs: 0 };
|
|
672
|
+
}
|
|
666
673
|
async function observeAppWindows(maxChars, timeoutMs = 30000, lane = 'windows') {
|
|
667
674
|
const limit = Math.min(Math.max(Math.floor(maxChars || 30000), 1000), 200000);
|
|
668
675
|
const script = [
|
|
@@ -764,7 +771,162 @@ async function selectAppWindow(target, handle) {
|
|
|
764
771
|
return { apps, error: `No visible application window matched: ${target || handle}.`, elapsedMs: observed.elapsedMs };
|
|
765
772
|
return { app, apps, elapsedMs: observed.elapsedMs };
|
|
766
773
|
}
|
|
767
|
-
|
|
774
|
+
const MAX_WAIT_FOR_TIMEOUT_MS = 30_000;
|
|
775
|
+
const DEFAULT_WAIT_FOR_TIMEOUT_MS = 5_000;
|
|
776
|
+
const WAIT_FOR_POLL_INTERVAL_MS = 250;
|
|
777
|
+
function boundedWaitForTimeout(value) {
|
|
778
|
+
const requested = Number(value);
|
|
779
|
+
if (!Number.isFinite(requested) || requested <= 0)
|
|
780
|
+
return DEFAULT_WAIT_FOR_TIMEOUT_MS;
|
|
781
|
+
return Math.min(MAX_WAIT_FOR_TIMEOUT_MS, Math.max(WAIT_FOR_POLL_INTERVAL_MS, Math.floor(requested)));
|
|
782
|
+
}
|
|
783
|
+
async function waitForUi(options, ownerId) {
|
|
784
|
+
const startedAt = Date.now();
|
|
785
|
+
const timeoutMs = boundedWaitForTimeout(options.timeoutMs);
|
|
786
|
+
const deadline = startedAt + timeoutMs;
|
|
787
|
+
const workspacePath = options.workspacePath;
|
|
788
|
+
const cache = observations.get(observationKey(workspacePath, ownerId));
|
|
789
|
+
const requestedText = String(options.text || '').trim();
|
|
790
|
+
const normalizedQuery = normalizeText(requestedText);
|
|
791
|
+
const targetId = String(options.targetId || '').trim();
|
|
792
|
+
const appTarget = String(options.appTarget || '').trim();
|
|
793
|
+
const requestedHandle = String(options.windowHandle || '').trim();
|
|
794
|
+
const cachedTarget = targetId ? cache?.objects.find(object => object.target_id === targetId) : undefined;
|
|
795
|
+
let app;
|
|
796
|
+
let lastWindowError;
|
|
797
|
+
let attempts = 0;
|
|
798
|
+
let nextWindowPollAt = 0;
|
|
799
|
+
if (!normalizedQuery && !targetId && !appTarget && !requestedHandle) {
|
|
800
|
+
return { ok: false, action: 'wait_for', error: 'Provide text, target_id, app_target, or window_handle to wait for.' };
|
|
801
|
+
}
|
|
802
|
+
if (targetId && !cachedTarget) {
|
|
803
|
+
return { ok: false, action: 'wait_for', error: `target_id not found in latest observation: ${targetId}. Call computer_use observe first.` };
|
|
804
|
+
}
|
|
805
|
+
const directHandle = requestedHandle || (!appTarget ? String(cache?.windowHandle || '') : '');
|
|
806
|
+
if (directHandle) {
|
|
807
|
+
const direct = await observeAppWindowByHandle(directHandle);
|
|
808
|
+
attempts += 1;
|
|
809
|
+
if (!direct.app) {
|
|
810
|
+
return { ok: false, action: 'wait_for', status: 'window_unavailable', error: direct.error || 'The selected window is no longer available.', elapsed_ms: Date.now() - startedAt, attempts };
|
|
811
|
+
}
|
|
812
|
+
app = direct.app;
|
|
813
|
+
}
|
|
814
|
+
if (!app && !appTarget) {
|
|
815
|
+
return { ok: false, action: 'wait_for', error: 'Call computer_use observe/app_observe first, or provide app_target/window_handle.' };
|
|
816
|
+
}
|
|
817
|
+
if (targetId && cache?.windowHandle && app && cache.windowHandle.toLowerCase() !== app.handle.toLowerCase()) {
|
|
818
|
+
return { ok: false, action: 'wait_for', error: `target_id=${targetId} belongs to a different window than ${app.handle}. Observe that window before waiting on the target.` };
|
|
819
|
+
}
|
|
820
|
+
while (Date.now() <= deadline) {
|
|
821
|
+
if (options.signal?.aborted) {
|
|
822
|
+
return { ok: false, action: 'wait_for', status: 'cancelled', error: 'wait_for was cancelled.', elapsed_ms: Date.now() - startedAt, attempts };
|
|
823
|
+
}
|
|
824
|
+
if (!app && appTarget) {
|
|
825
|
+
if (Date.now() >= nextWindowPollAt) {
|
|
826
|
+
const observed = await observeAppWindows(30000, 750, 'windows_advisory');
|
|
827
|
+
attempts += 1;
|
|
828
|
+
lastWindowError = observed.error;
|
|
829
|
+
app = observed.apps.find(candidate => appMatches(candidate, appTarget));
|
|
830
|
+
nextWindowPollAt = Date.now() + 1000;
|
|
831
|
+
}
|
|
832
|
+
if (!app) {
|
|
833
|
+
const remaining = deadline - Date.now();
|
|
834
|
+
if (remaining <= 0)
|
|
835
|
+
break;
|
|
836
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(Math.max(1, nextWindowPollAt - Date.now()), remaining)));
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
if (targetId && cache?.windowHandle && app && cache.windowHandle.toLowerCase() !== app.handle.toLowerCase()) {
|
|
841
|
+
return { ok: false, action: 'wait_for', error: `target_id=${targetId} belongs to a different window than ${app.handle}. Observe that window before waiting on the target.`, elapsed_ms: Date.now() - startedAt, attempts };
|
|
842
|
+
}
|
|
843
|
+
if (app && !normalizedQuery && !targetId) {
|
|
844
|
+
return {
|
|
845
|
+
ok: true,
|
|
846
|
+
action: 'wait_for',
|
|
847
|
+
status: 'matched',
|
|
848
|
+
found: true,
|
|
849
|
+
condition: { app_target: appTarget || undefined, window_handle: app.handle },
|
|
850
|
+
app,
|
|
851
|
+
elapsed_ms: Date.now() - startedAt,
|
|
852
|
+
attempts,
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
if (app) {
|
|
856
|
+
const ui = await observeUiAutomation(Number(options.maxChars || 30000), app.handle, 750, 'uia_advisory');
|
|
857
|
+
attempts += 1;
|
|
858
|
+
if (ui.error) {
|
|
859
|
+
return { ok: false, action: 'wait_for', status: 'uia_unavailable', error: ui.error, app, elapsed_ms: Date.now() - startedAt, attempts };
|
|
860
|
+
}
|
|
861
|
+
const objects = semanticObjects(ui.elements, app.bbox.width, app.bbox.height, workspacePath, ownerId);
|
|
862
|
+
const match = objects.find(object => {
|
|
863
|
+
if (targetId && object.target_id !== targetId)
|
|
864
|
+
return false;
|
|
865
|
+
if (!normalizedQuery)
|
|
866
|
+
return true;
|
|
867
|
+
return normalizeText(object.name).includes(normalizedQuery) || normalizeText(object.automation_id).includes(normalizedQuery);
|
|
868
|
+
});
|
|
869
|
+
if (match) {
|
|
870
|
+
const generation = sceneGeneration([app], ui.elements);
|
|
871
|
+
observations.set(observationKey(workspacePath, ownerId), {
|
|
872
|
+
workspacePath,
|
|
873
|
+
objects,
|
|
874
|
+
windowHandle: app.handle,
|
|
875
|
+
width: Math.max(1, app.bbox.width),
|
|
876
|
+
height: Math.max(1, app.bbox.height),
|
|
877
|
+
at: new Date().toISOString(),
|
|
878
|
+
sceneGeneration: generation,
|
|
879
|
+
});
|
|
880
|
+
return {
|
|
881
|
+
ok: true,
|
|
882
|
+
action: 'wait_for',
|
|
883
|
+
status: 'matched',
|
|
884
|
+
found: true,
|
|
885
|
+
condition: {
|
|
886
|
+
...(requestedText ? { text: requestedText } : {}),
|
|
887
|
+
...(targetId ? { target_id: targetId } : {}),
|
|
888
|
+
...(appTarget ? { app_target: appTarget } : {}),
|
|
889
|
+
},
|
|
890
|
+
app,
|
|
891
|
+
scene_generation: generation,
|
|
892
|
+
element: {
|
|
893
|
+
target_id: match.target_id,
|
|
894
|
+
label: match.label,
|
|
895
|
+
role: match.role,
|
|
896
|
+
control_type: match.control_type,
|
|
897
|
+
automation_id: match.automation_id,
|
|
898
|
+
bbox: match.bbox,
|
|
899
|
+
center: match.center,
|
|
900
|
+
},
|
|
901
|
+
elapsed_ms: Date.now() - startedAt,
|
|
902
|
+
attempts,
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
const remaining = deadline - Date.now();
|
|
907
|
+
if (remaining <= 0)
|
|
908
|
+
break;
|
|
909
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(WAIT_FOR_POLL_INTERVAL_MS, remaining)));
|
|
910
|
+
}
|
|
911
|
+
return {
|
|
912
|
+
ok: true,
|
|
913
|
+
action: 'wait_for',
|
|
914
|
+
status: 'timed_out',
|
|
915
|
+
found: false,
|
|
916
|
+
condition: {
|
|
917
|
+
...(requestedText ? { text: requestedText } : {}),
|
|
918
|
+
...(targetId ? { target_id: targetId } : {}),
|
|
919
|
+
...(appTarget ? { app_target: appTarget } : {}),
|
|
920
|
+
...(requestedHandle ? { window_handle: requestedHandle } : {}),
|
|
921
|
+
},
|
|
922
|
+
...(app ? { app } : {}),
|
|
923
|
+
...(lastWindowError ? { warning: lastWindowError } : {}),
|
|
924
|
+
timeout_ms: timeoutMs,
|
|
925
|
+
elapsed_ms: Date.now() - startedAt,
|
|
926
|
+
attempts,
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage, app, includeRawUi = false, captureMaxWidth, captureMaxHeight, includeUiTree = true) {
|
|
768
930
|
const totalStarted = Date.now();
|
|
769
931
|
const outPath = ephemeralScreenshotPath('app');
|
|
770
932
|
let retainedForVision = false;
|
|
@@ -775,7 +937,9 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
|
|
|
775
937
|
], captureMaxWidth, captureMaxHeight);
|
|
776
938
|
const [result, ui] = await Promise.all([
|
|
777
939
|
runPowerShell(script, 30000),
|
|
778
|
-
|
|
940
|
+
includeUiTree
|
|
941
|
+
? observeUiAutomation(30000, app.handle, 750, 'uia_advisory')
|
|
942
|
+
: Promise.resolve(skippedUiAutomation()),
|
|
779
943
|
]);
|
|
780
944
|
if (!result.ok)
|
|
781
945
|
return captureFailure('app_observe', app);
|
|
@@ -798,7 +962,7 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
|
|
|
798
962
|
}
|
|
799
963
|
const cacheKey = observationKey(workspacePath, ownerId);
|
|
800
964
|
const generation = sceneGeneration([app], elements);
|
|
801
|
-
observations.set(cacheKey, { workspacePath, objects, width: Number(parsed.width || b.width || 1), height: Number(parsed.height || b.height || 1), at: new Date().toISOString(), sceneGeneration: generation });
|
|
965
|
+
observations.set(cacheKey, { workspacePath, objects, windowHandle: app.handle, width: Number(parsed.width || b.width || 1), height: Number(parsed.height || b.height || 1), at: new Date().toISOString(), sceneGeneration: generation });
|
|
802
966
|
const payload = {
|
|
803
967
|
ok: true,
|
|
804
968
|
action: 'app_observe',
|
|
@@ -807,8 +971,10 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
|
|
|
807
971
|
screenshot_retention: allowEphemeralVisionImage ? 'ephemeral-delete-after-vision-input' : 'ephemeral-deleted-before-tool-return',
|
|
808
972
|
coordinate_system: 'virtual-screen-pixels',
|
|
809
973
|
perception: {
|
|
810
|
-
mode: 'native-application-screenshot-plus-windows-ui-automation',
|
|
974
|
+
mode: includeUiTree ? 'native-application-screenshot-plus-windows-ui-automation' : 'native-application-screenshot-only',
|
|
811
975
|
application_scope: 'single-visible-window',
|
|
976
|
+
ui_tree_status: includeUiTree ? (ui.error ? 'unavailable' : 'captured') : 'skipped',
|
|
977
|
+
ui_tree_skipped: !includeUiTree,
|
|
812
978
|
element_count: elements.length,
|
|
813
979
|
visited_count: ui.visited,
|
|
814
980
|
scene_generation: generation,
|
|
@@ -819,6 +985,7 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
|
|
|
819
985
|
telemetry: {
|
|
820
986
|
capture_ms: result.elapsedMs,
|
|
821
987
|
uia_ms: ui.elapsedMs,
|
|
988
|
+
ui_tree_skipped: !includeUiTree,
|
|
822
989
|
total_ms: Date.now() - totalStarted,
|
|
823
990
|
element_count: elements.length,
|
|
824
991
|
visited_count: ui.visited,
|
|
@@ -1068,7 +1235,7 @@ function resolveTarget(targetId, workspacePath, ownerId) {
|
|
|
1068
1235
|
return { x: 0, y: 0, error: `target_id not found in latest observation: ${id}.` };
|
|
1069
1236
|
return { x: target.center.x, y: target.center.y, target };
|
|
1070
1237
|
}
|
|
1071
|
-
async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, includeRawUi = false, captureMaxWidth, captureMaxHeight, sparseWaitMs, signal) {
|
|
1238
|
+
async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, includeRawUi = false, captureMaxWidth, captureMaxHeight, sparseWaitMs, signal, includeUiTree = true) {
|
|
1072
1239
|
const totalStarted = Date.now();
|
|
1073
1240
|
const sparseDurationMs = boundedSparseWait(sparseWaitMs);
|
|
1074
1241
|
const sparseStarted = Date.now();
|
|
@@ -1100,8 +1267,12 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
|
|
|
1100
1267
|
const foreground = await foregroundWindowHandle();
|
|
1101
1268
|
const [result, ui, apps] = await Promise.all([
|
|
1102
1269
|
runPowerShell(script, 30000),
|
|
1103
|
-
|
|
1104
|
-
|
|
1270
|
+
includeUiTree && foreground.handle
|
|
1271
|
+
? observeUiAutomation(30000, foreground.handle, 750, 'uia_advisory')
|
|
1272
|
+
: Promise.resolve(skippedUiAutomation(includeUiTree ? 'Foreground window handle is unavailable; UI Automation tree capture was skipped.' : undefined)),
|
|
1273
|
+
includeUiTree
|
|
1274
|
+
? observeAppWindows(30000, 750, 'windows_advisory')
|
|
1275
|
+
: Promise.resolve({ apps: [], error: undefined, elapsedMs: 0 }),
|
|
1105
1276
|
]);
|
|
1106
1277
|
if (signal?.aborted)
|
|
1107
1278
|
throw sparseAbortError(signal);
|
|
@@ -1118,7 +1289,7 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
|
|
|
1118
1289
|
const screenHeight = Number(parsed.height || 1);
|
|
1119
1290
|
const objects = semanticObjects(ui.elements, screenWidth, screenHeight, workspacePath, ownerId);
|
|
1120
1291
|
const generation = sceneGeneration(apps.apps, ui.elements);
|
|
1121
|
-
observations.set(observationKey(workspacePath, ownerId), { workspacePath, objects, width: screenWidth, height: screenHeight, at: new Date().toISOString(), sceneGeneration: generation });
|
|
1292
|
+
observations.set(observationKey(workspacePath, ownerId), { workspacePath, objects, windowHandle: foreground.handle || undefined, width: screenWidth, height: screenHeight, at: new Date().toISOString(), sceneGeneration: generation });
|
|
1122
1293
|
const payload = {
|
|
1123
1294
|
ok: true,
|
|
1124
1295
|
action: 'observe',
|
|
@@ -1126,10 +1297,16 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
|
|
|
1126
1297
|
screenshot_retention: allowEphemeralVisionImage ? 'ephemeral-delete-after-vision-input' : 'ephemeral-deleted-before-tool-return',
|
|
1127
1298
|
coordinate_system: 'virtual-screen-pixels',
|
|
1128
1299
|
perception: {
|
|
1129
|
-
mode: 'native-screenshot-plus-windows-ui-automation',
|
|
1300
|
+
mode: includeUiTree ? 'native-screenshot-plus-windows-ui-automation' : 'native-screenshot-only',
|
|
1301
|
+
ui_tree_status: includeUiTree ? (ui.error ? 'unavailable' : 'captured') : 'skipped',
|
|
1302
|
+
ui_tree_skipped: !includeUiTree,
|
|
1130
1303
|
vision_assist: {
|
|
1131
|
-
when_model_supports_vision:
|
|
1132
|
-
|
|
1304
|
+
when_model_supports_vision: includeUiTree
|
|
1305
|
+
? 'The Agent sends this screenshot as image input together with the UI Automation element tree, so visual recognition and control bounding boxes can be used in the same decision step.'
|
|
1306
|
+
: 'The Agent receives this screenshot as image input without UI Automation traversal or semantic target IDs.',
|
|
1307
|
+
fallback_without_vision: includeUiTree
|
|
1308
|
+
? 'Use the UI Automation element tree, bbox, center coordinates, and semantic target IDs only; no screenshot file path is retained or exposed.'
|
|
1309
|
+
: 'This result has no semantic target IDs; request include_ui_tree=true for a UI Automation element tree.',
|
|
1133
1310
|
},
|
|
1134
1311
|
element_count: ui.elements.length,
|
|
1135
1312
|
visited_count: ui.visited,
|
|
@@ -1137,13 +1314,18 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
|
|
|
1137
1314
|
scene_summary: buildSceneSummary(objects, screenWidth, screenHeight),
|
|
1138
1315
|
objects: compactSemanticObjects(objects),
|
|
1139
1316
|
...(sparseDetails ? { sparse_measurement: sparseDetails } : {}),
|
|
1140
|
-
object_usage:
|
|
1317
|
+
object_usage: includeUiTree
|
|
1318
|
+
? 'Prefer target_id from scene_summary.high_priority_objects or objects for click/move/scroll when available; it resolves to the latest observed center coordinate and keeps actions tied to a semantic UI object.'
|
|
1319
|
+
: 'This screenshot-only observation has no semantic target IDs. Use visual coordinates or request another observe with include_ui_tree=true before using target_id.',
|
|
1141
1320
|
warning: foreground.error || ui.error || apps.error || undefined,
|
|
1142
|
-
note:
|
|
1321
|
+
note: includeUiTree
|
|
1322
|
+
? 'Native screenshot plus a bounded, foreground-window-scoped Windows UI Automation tree. No third-party server or code is loaded.'
|
|
1323
|
+
: 'Fast native screenshot path; UI Automation traversal was intentionally skipped. No third-party server or code is loaded.',
|
|
1143
1324
|
},
|
|
1144
1325
|
telemetry: {
|
|
1145
1326
|
capture_ms: result.elapsedMs,
|
|
1146
1327
|
uia_ms: ui.elapsedMs,
|
|
1328
|
+
ui_tree_skipped: !includeUiTree,
|
|
1147
1329
|
app_list_ms: apps.elapsedMs,
|
|
1148
1330
|
foreground_ms: foreground.elapsedMs,
|
|
1149
1331
|
total_ms: Date.now() - totalStarted,
|
|
@@ -1290,7 +1472,9 @@ async function foregroundSceneGeneration() {
|
|
|
1290
1472
|
const handle = handleResult.handle;
|
|
1291
1473
|
const [apps, ui] = await Promise.all([
|
|
1292
1474
|
observeAppWindows(30000),
|
|
1293
|
-
|
|
1475
|
+
handle
|
|
1476
|
+
? observeUiAutomation(30000, handle)
|
|
1477
|
+
: Promise.resolve(skippedUiAutomation('Foreground window handle is unavailable; UI Automation tree capture was skipped.')),
|
|
1294
1478
|
]);
|
|
1295
1479
|
return {
|
|
1296
1480
|
generation: sceneGeneration(apps.apps, ui.elements),
|
|
@@ -1428,7 +1612,7 @@ async function runComputerUse(options) {
|
|
|
1428
1612
|
result = { ok: true, action: 'takeover_stop', takeover: false };
|
|
1429
1613
|
}
|
|
1430
1614
|
else if (action === 'observe') {
|
|
1431
|
-
result = await screenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight, options.sparseWaitMs, options.signal);
|
|
1615
|
+
result = await screenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight, options.sparseWaitMs, options.signal, options.includeUiTree !== false);
|
|
1432
1616
|
}
|
|
1433
1617
|
else if (action === 'app_list') {
|
|
1434
1618
|
const first = await observeAppWindows(Number(options.maxChars || 60000));
|
|
@@ -1445,7 +1629,7 @@ async function runComputerUse(options) {
|
|
|
1445
1629
|
else if (action === 'app_observe') {
|
|
1446
1630
|
const selected = await selectAppWindow(options.appTarget, options.windowHandle);
|
|
1447
1631
|
result = selected.app
|
|
1448
|
-
? await cropScreenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, selected.app, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight)
|
|
1632
|
+
? await cropScreenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, selected.app, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight, options.includeUiTree !== false)
|
|
1449
1633
|
: { ok: false, action, error: selected.error, applications: selected.apps.slice(0, 20) };
|
|
1450
1634
|
}
|
|
1451
1635
|
else if (action === 'app_activate') {
|
|
@@ -1455,6 +1639,9 @@ async function runComputerUse(options) {
|
|
|
1455
1639
|
else if (action === 'wait') {
|
|
1456
1640
|
result = await wait(Number(options.durationMs || 1000));
|
|
1457
1641
|
}
|
|
1642
|
+
else if (action === 'wait_for') {
|
|
1643
|
+
result = await waitForUi(options, ownerId);
|
|
1644
|
+
}
|
|
1458
1645
|
else if (action === 'sequence') {
|
|
1459
1646
|
result = await executeSequence(options, ownerId);
|
|
1460
1647
|
}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface ToolExecutionContext {
|
|
|
16
16
|
actorId?: string;
|
|
17
17
|
workspaceId?: string;
|
|
18
18
|
runtimeKey?: string;
|
|
19
|
+
runId?: string;
|
|
19
20
|
backend?: string;
|
|
20
21
|
invocation?: 'agent' | 'cli';
|
|
21
22
|
signal?: AbortSignal;
|
|
@@ -42,6 +43,7 @@ export declare class ToolExecutor {
|
|
|
42
43
|
private readonly localOcr;
|
|
43
44
|
private readonly argumentValidators;
|
|
44
45
|
private readonly searchMcpPool;
|
|
46
|
+
private readonly fileWriteObservationGate;
|
|
45
47
|
private hostProfile;
|
|
46
48
|
constructor(root: string, config: ConfigManager, ssh?: SshManager | undefined, workspace?: WorkspaceManager | undefined);
|
|
47
49
|
webSearch(query: string, signal?: AbortSignal): Promise<string>;
|
|
@@ -72,16 +74,13 @@ export declare class ToolExecutor {
|
|
|
72
74
|
private decorateBrowserUseReceipt;
|
|
73
75
|
private sshWorkspace;
|
|
74
76
|
private checkWorkspaceAccess;
|
|
77
|
+
private fileWriteObservationScope;
|
|
75
78
|
private checkBashWorkspaceAccess;
|
|
76
79
|
private extractCommandPathRefs;
|
|
77
80
|
private looksLikePath;
|
|
78
81
|
private isReadonlyShellCommand;
|
|
79
82
|
private resolveBashTimeout;
|
|
80
83
|
private bash;
|
|
81
|
-
private fread;
|
|
82
|
-
private fwrite;
|
|
83
|
-
private fedit;
|
|
84
|
-
private fdelete;
|
|
85
84
|
private glob;
|
|
86
85
|
private grep;
|
|
87
86
|
private createProxyAgent;
|