newmark-agent 0.3.10 → 0.3.11
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-help.d.ts +2 -0
- package/dist/cli-help.js +22 -0
- package/dist/conversation-utility-host.bundle.cjs +191 -53
- package/dist/core/agent.js +22 -4
- package/dist/core/agentKernelRunner.js +19 -2
- package/dist/core/browserControl.d.ts +8 -0
- package/dist/core/browserUsePageAdapter.d.ts +3 -0
- package/dist/core/browserUsePageAdapter.js +19 -2
- package/dist/core/computerUseSession.d.ts +44 -0
- package/dist/core/computerUseSession.js +105 -0
- package/dist/core/conversationKernel.d.ts +1 -0
- package/dist/core/conversationKernel.js +38 -0
- package/dist/core/electronBrowserUseHost.js +7 -0
- package/dist/core/electronUtilityAgentClient.js +84 -2
- package/dist/core/utilityHostToolRouter.d.ts +7 -0
- package/dist/core/utilityHostToolRouter.js +25 -33
- package/dist/launcher.js +13 -1
- package/dist/main.js +132 -18
- package/dist/preload.js +4 -2
- package/dist/tools/index.js +36 -49
- package/dist/ui/index.html +296 -39
- package/dist/wsl-agent-host.bundle.cjs +191 -53
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -73,6 +73,7 @@ const runtimeShutdown_1 = require("./core/runtimeShutdown");
|
|
|
73
73
|
const runtimeLifecycle_1 = require("./core/runtimeLifecycle");
|
|
74
74
|
const compat_1 = require("./core/compat");
|
|
75
75
|
const mcpManager_1 = require("./core/mcpManager");
|
|
76
|
+
const cli_help_1 = require("./cli-help");
|
|
76
77
|
const APP_NAME = 'Newmark Agent';
|
|
77
78
|
const APP_ID = 'ai.newmark.agent';
|
|
78
79
|
const CONFIG_RELOAD_RUNTIME_TIMEOUT_MS = 6_000;
|
|
@@ -116,7 +117,27 @@ let _forceQuit = false;
|
|
|
116
117
|
let forcedExitTimer = null;
|
|
117
118
|
let electronBrowserUseHost = null;
|
|
118
119
|
let browserUseEngine = null;
|
|
120
|
+
// A single renderer can host several hidden conversation-bound guests. The
|
|
121
|
+
// legacy host map is retained for focused-window discovery, while this map is
|
|
122
|
+
// the authoritative Browser-Use/right-sidebar binding.
|
|
119
123
|
const browserGuestContentsByHost = new Map();
|
|
124
|
+
const browserGuestBindingsByRuntime = new Map();
|
|
125
|
+
function browserGuestRuntimeKey(target) {
|
|
126
|
+
return (0, conversationTarget_1.normalizeConversationTarget)(target).runtimeKey;
|
|
127
|
+
}
|
|
128
|
+
function browserGuestBindingFor(runtimeKey) {
|
|
129
|
+
const binding = browserGuestBindingsByRuntime.get(String(runtimeKey || ''));
|
|
130
|
+
if (!binding)
|
|
131
|
+
return null;
|
|
132
|
+
const guest = electron_1.webContents.fromId(binding.guestId);
|
|
133
|
+
if (!guest || guest.isDestroyed() || guest.getType() !== 'webview' || guest.hostWebContents?.id !== binding.hostId) {
|
|
134
|
+
browserGuestBindingsByRuntime.delete(String(runtimeKey || ''));
|
|
135
|
+
if (browserGuestContentsByHost.get(binding.hostId) === binding.guestId)
|
|
136
|
+
browserGuestContentsByHost.delete(binding.hostId);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return binding;
|
|
140
|
+
}
|
|
120
141
|
let workspaceSwitchGeneration = 0;
|
|
121
142
|
let workspaceSelectionCoordinator = null;
|
|
122
143
|
function defaultTerminalShell() {
|
|
@@ -737,7 +758,15 @@ async function waitForWebContentsLoad(contents, timeoutMs = 15000) {
|
|
|
737
758
|
contents.once('did-fail-load', finish);
|
|
738
759
|
});
|
|
739
760
|
}
|
|
740
|
-
function registeredBrowserGuest(hostContentsId) {
|
|
761
|
+
function registeredBrowserGuest(hostContentsId, runtimeKey) {
|
|
762
|
+
if (runtimeKey) {
|
|
763
|
+
const binding = browserGuestBindingFor(runtimeKey);
|
|
764
|
+
if (binding && (!hostContentsId || binding.hostId === hostContentsId)) {
|
|
765
|
+
const guest = electron_1.webContents.fromId(binding.guestId);
|
|
766
|
+
if (guest && !guest.isDestroyed())
|
|
767
|
+
return guest;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
741
770
|
const hostIds = hostContentsId
|
|
742
771
|
? [hostContentsId]
|
|
743
772
|
: [
|
|
@@ -752,8 +781,14 @@ function registeredBrowserGuest(hostContentsId) {
|
|
|
752
781
|
if (!guestId)
|
|
753
782
|
continue;
|
|
754
783
|
const guest = electron_1.webContents.fromId(guestId);
|
|
755
|
-
if (guest && !guest.isDestroyed() && guest.getType() === 'webview' && guest.hostWebContents?.id === hostId)
|
|
784
|
+
if (guest && !guest.isDestroyed() && guest.getType() === 'webview' && guest.hostWebContents?.id === hostId) {
|
|
785
|
+
if (runtimeKey) {
|
|
786
|
+
const bound = browserGuestBindingsByRuntime.get(runtimeKey);
|
|
787
|
+
if (!bound || bound.hostId !== hostId || bound.guestId !== guest.id)
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
756
790
|
return guest;
|
|
791
|
+
}
|
|
757
792
|
browserGuestContentsByHost.delete(hostId);
|
|
758
793
|
}
|
|
759
794
|
return null;
|
|
@@ -774,37 +809,64 @@ async function boundedOperation(operation, timeoutMs, label) {
|
|
|
774
809
|
clearTimeout(timer);
|
|
775
810
|
}
|
|
776
811
|
}
|
|
777
|
-
function registerBrowserGuest(host, guest) {
|
|
812
|
+
function registerBrowserGuest(host, guest, requestedTarget) {
|
|
778
813
|
if (host.isDestroyed() || guest.isDestroyed() || guest.getType() !== 'webview' || guest.hostWebContents?.id !== host.id)
|
|
779
814
|
return false;
|
|
815
|
+
let target;
|
|
816
|
+
try {
|
|
817
|
+
target = (0, conversationTarget_1.normalizeConversationTarget)(conversationRuntimeTarget(requestedTarget));
|
|
818
|
+
}
|
|
819
|
+
catch {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
const prior = browserGuestBindingsByRuntime.get(target.runtimeKey);
|
|
823
|
+
if (prior && prior.guestId !== guest.id) {
|
|
824
|
+
const priorGuest = electron_1.webContents.fromId(prior.guestId);
|
|
825
|
+
if (priorGuest && !priorGuest.isDestroyed())
|
|
826
|
+
priorGuest.hostWebContents?.send('browser:guestVisibility', { runtimeKey: target.runtimeKey, visible: false });
|
|
827
|
+
}
|
|
828
|
+
browserGuestBindingsByRuntime.set(target.runtimeKey, {
|
|
829
|
+
hostId: host.id,
|
|
830
|
+
guestId: guest.id,
|
|
831
|
+
workspaceId: target.workspaceId,
|
|
832
|
+
conversationId: target.conversationId,
|
|
833
|
+
});
|
|
780
834
|
browserGuestContentsByHost.set(host.id, guest.id);
|
|
781
835
|
guest.once('destroyed', () => {
|
|
782
836
|
if (browserGuestContentsByHost.get(host.id) === guest.id)
|
|
783
837
|
browserGuestContentsByHost.delete(host.id);
|
|
838
|
+
const binding = browserGuestBindingsByRuntime.get(target.runtimeKey);
|
|
839
|
+
if (binding?.guestId === guest.id)
|
|
840
|
+
browserGuestBindingsByRuntime.delete(target.runtimeKey);
|
|
784
841
|
});
|
|
785
842
|
ensureElectronBrowserUseHost().attach(guest);
|
|
786
843
|
return true;
|
|
787
844
|
}
|
|
788
|
-
async function waitForRegisteredBrowserGuest(host, timeoutMs = 12_000) {
|
|
845
|
+
async function waitForRegisteredBrowserGuest(host, runtimeKey, timeoutMs = 12_000) {
|
|
789
846
|
const deadline = Date.now() + Math.max(250, timeoutMs);
|
|
790
847
|
while (!host.isDestroyed() && Date.now() < deadline) {
|
|
791
|
-
const guest = registeredBrowserGuest(host.id);
|
|
848
|
+
const guest = registeredBrowserGuest(host.id, runtimeKey);
|
|
792
849
|
if (guest)
|
|
793
850
|
return guest;
|
|
794
851
|
await new Promise(resolve => setTimeout(resolve, 25));
|
|
795
852
|
}
|
|
796
853
|
throw new Error('Built-in Browser guest did not become ready before the Browser-Use timeout');
|
|
797
854
|
}
|
|
798
|
-
async function ensureBrowserWebContents(boundContentsId) {
|
|
855
|
+
async function ensureBrowserWebContents(boundContentsId, runtimeKey) {
|
|
799
856
|
if (boundContentsId) {
|
|
800
857
|
const bound = electron_1.webContents.fromId(boundContentsId);
|
|
801
858
|
if (bound && !bound.isDestroyed() && bound.getType() === 'webview'
|
|
802
859
|
&& browserGuestContentsByHost.get(bound.hostWebContents?.id || 0) === bound.id) {
|
|
860
|
+
if (runtimeKey) {
|
|
861
|
+
const binding = browserGuestBindingsByRuntime.get(runtimeKey);
|
|
862
|
+
if (!binding || binding.guestId !== bound.id)
|
|
863
|
+
return await ensureBrowserWebContents(undefined, runtimeKey);
|
|
864
|
+
}
|
|
803
865
|
bound.hostWebContents?.send('browser:ensureGuest');
|
|
804
866
|
return bound;
|
|
805
867
|
}
|
|
806
868
|
}
|
|
807
|
-
const registered = registeredBrowserGuest();
|
|
869
|
+
const registered = registeredBrowserGuest(undefined, runtimeKey);
|
|
808
870
|
if (registered) {
|
|
809
871
|
registered.hostWebContents?.send('browser:ensureGuest');
|
|
810
872
|
return registered;
|
|
@@ -814,13 +876,13 @@ async function ensureBrowserWebContents(boundContentsId) {
|
|
|
814
876
|
const host = hostWindow?.webContents;
|
|
815
877
|
if (!host || host.isDestroyed())
|
|
816
878
|
throw new Error('Built-in Browser UI is unavailable');
|
|
817
|
-
host.send('browser:ensureGuest');
|
|
818
|
-
return await waitForRegisteredBrowserGuest(host);
|
|
879
|
+
host.send('browser:ensureGuest', runtimeKey ? { runtimeKey } : undefined);
|
|
880
|
+
return await waitForRegisteredBrowserGuest(host, runtimeKey, 12_000);
|
|
819
881
|
}
|
|
820
882
|
function ensureElectronBrowserUseHost() {
|
|
821
883
|
if (!electronBrowserUseHost) {
|
|
822
884
|
electronBrowserUseHost = new electronBrowserUseHost_1.ElectronBrowserUseHost({
|
|
823
|
-
resolveContents: async (
|
|
885
|
+
resolveContents: async (scope, boundContentsId) => await ensureBrowserWebContents(boundContentsId, scope.runtimeKey),
|
|
824
886
|
openExternal: async (url) => { await electron_1.shell.openExternal(url); },
|
|
825
887
|
});
|
|
826
888
|
}
|
|
@@ -833,10 +895,18 @@ function ensureBrowserUseEngine() {
|
|
|
833
895
|
}
|
|
834
896
|
return browserUseEngine;
|
|
835
897
|
}
|
|
836
|
-
function currentBrowserUseContext() {
|
|
837
|
-
const target = (0, conversationTarget_1.normalizeConversationTarget)(conversationRuntimeTarget());
|
|
898
|
+
function currentBrowserUseContext(requestedTarget) {
|
|
899
|
+
const target = (0, conversationTarget_1.normalizeConversationTarget)(conversationRuntimeTarget(requestedTarget));
|
|
838
900
|
return { runtimeKey: target.runtimeKey, actorId: agent?.runtimeActorId || terminalTakeover_1.ROOT_TERMINAL_ACTOR_ID };
|
|
839
901
|
}
|
|
902
|
+
function browserControlTargetInput(input) {
|
|
903
|
+
if (!input)
|
|
904
|
+
return undefined;
|
|
905
|
+
return {
|
|
906
|
+
workspaceId: String(input.workspaceId || ''),
|
|
907
|
+
conversationId: String(input.conversationId || 'default'),
|
|
908
|
+
};
|
|
909
|
+
}
|
|
840
910
|
async function runBoundBrowserUse(input, context, signal) {
|
|
841
911
|
return await ensureBrowserUseEngine().run((0, browserUse_1.bindBrowserUseRequest)(input, context), signal);
|
|
842
912
|
}
|
|
@@ -856,8 +926,10 @@ function browserSnapshotScript(maxChars) {
|
|
|
856
926
|
}
|
|
857
927
|
async function runBrowserControl(request) {
|
|
858
928
|
const action = request.action;
|
|
929
|
+
const requestedTarget = browserControlTargetInput(request.target);
|
|
930
|
+
const target = (0, conversationTarget_1.normalizeConversationTarget)(conversationRuntimeTarget(requestedTarget));
|
|
859
931
|
if (action === 'use') {
|
|
860
|
-
const receipt = await runBoundBrowserUse(request.browserUse, currentBrowserUseContext());
|
|
932
|
+
const receipt = await runBoundBrowserUse(request.browserUse, currentBrowserUseContext(requestedTarget));
|
|
861
933
|
return {
|
|
862
934
|
ok: receipt.ok,
|
|
863
935
|
action,
|
|
@@ -868,7 +940,7 @@ async function runBrowserControl(request) {
|
|
|
868
940
|
error: receipt.error,
|
|
869
941
|
};
|
|
870
942
|
}
|
|
871
|
-
const contents = await ensureBrowserWebContents();
|
|
943
|
+
const contents = await ensureBrowserWebContents(undefined, target.runtimeKey);
|
|
872
944
|
try {
|
|
873
945
|
if (action === 'open') {
|
|
874
946
|
await contents.loadURL(request.url || 'about:blank');
|
|
@@ -953,12 +1025,25 @@ function installBrowserControlBackend() {
|
|
|
953
1025
|
const args = userArgs();
|
|
954
1026
|
const command = args.find(a => a === 'flow' || a === 'edit');
|
|
955
1027
|
const isTuiArg = args.some(arg => arg.toLowerCase() === '--tui');
|
|
1028
|
+
const hasCliCommand = args.some(a => cli_commands_1.CLI_COMMANDS.includes(a));
|
|
1029
|
+
const isHelpArg = !hasCliCommand && (args.some(arg => ['--help', '-h'].includes(arg.toLowerCase())) || args[0]?.toLowerCase() === 'help');
|
|
1030
|
+
const isVersionArg = !hasCliCommand && args.some(arg => ['--version', '-v'].includes(arg.toLowerCase()));
|
|
956
1031
|
const isViewerArg = args.some(arg => arg.toLowerCase() === '--newmark-viewer');
|
|
957
1032
|
const isCliArg = args.includes('--cli');
|
|
958
1033
|
const isServerArg = args.includes('--server');
|
|
959
1034
|
const isFlowArg = command === 'flow';
|
|
960
1035
|
const isEditArg = command === 'edit';
|
|
961
|
-
|
|
1036
|
+
// Help/version are terminating discovery commands. They must be handled
|
|
1037
|
+
// before Electron's GUI/TUI/server branches can initialize runtime state or
|
|
1038
|
+
// spawn a window, so a product-new tester can use them safely in any surface.
|
|
1039
|
+
if (isHelpArg) {
|
|
1040
|
+
console.log((0, cli_help_1.newmarkHelpText)((0, installUpdate_1.currentAppVersion)()));
|
|
1041
|
+
process.exit(0);
|
|
1042
|
+
}
|
|
1043
|
+
if (isVersionArg) {
|
|
1044
|
+
console.log((0, installUpdate_1.currentAppVersion)());
|
|
1045
|
+
process.exit(0);
|
|
1046
|
+
}
|
|
962
1047
|
function viewerEscape(value) {
|
|
963
1048
|
return String(value || '').replace(/[&<>"']/g, character => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[character] || character);
|
|
964
1049
|
}
|
|
@@ -1513,7 +1598,12 @@ else {
|
|
|
1513
1598
|
rejectUiReadinessById(Number(fileRouterOwnerId), new Error('Startup window closed before UI readiness'));
|
|
1514
1599
|
fileRouter.revokeOwner(fileRouterOwnerId);
|
|
1515
1600
|
pdfPreviewServer.revokeOwner(fileRouterOwnerId);
|
|
1516
|
-
|
|
1601
|
+
const closedHostId = Number(fileRouterOwnerId);
|
|
1602
|
+
browserGuestContentsByHost.delete(closedHostId);
|
|
1603
|
+
for (const [runtimeKey, binding] of browserGuestBindingsByRuntime) {
|
|
1604
|
+
if (binding.hostId === closedHostId)
|
|
1605
|
+
browserGuestBindingsByRuntime.delete(runtimeKey);
|
|
1606
|
+
}
|
|
1517
1607
|
const remaining = electron_1.BrowserWindow.getAllWindows().filter(candidate => !candidate.isDestroyed() && candidate !== win);
|
|
1518
1608
|
if (mainWindow === win)
|
|
1519
1609
|
mainWindow = remaining[0] || null;
|
|
@@ -2179,6 +2269,7 @@ else {
|
|
|
2179
2269
|
electronBrowserUseHost = null;
|
|
2180
2270
|
browserControl_1.BrowserControl.setBackend(null);
|
|
2181
2271
|
browserGuestContentsByHost.clear();
|
|
2272
|
+
browserGuestBindingsByRuntime.clear();
|
|
2182
2273
|
if (sidecarProcess) {
|
|
2183
2274
|
sidecarProcess.kill();
|
|
2184
2275
|
sidecarProcess = null;
|
|
@@ -2569,9 +2660,9 @@ else {
|
|
|
2569
2660
|
}
|
|
2570
2661
|
}
|
|
2571
2662
|
});
|
|
2572
|
-
electron_1.ipcMain.handle('browser:registerGuest', (event, guestContentsId) => {
|
|
2663
|
+
electron_1.ipcMain.handle('browser:registerGuest', (event, guestContentsId, target) => {
|
|
2573
2664
|
const guest = electron_1.webContents.fromId(Number(guestContentsId || 0));
|
|
2574
|
-
return { accepted: !!guest && registerBrowserGuest(event.sender, guest) };
|
|
2665
|
+
return { accepted: !!guest && registerBrowserGuest(event.sender, guest, target) };
|
|
2575
2666
|
});
|
|
2576
2667
|
electron_1.ipcMain.handle('browser:control', async (_event, request) => {
|
|
2577
2668
|
return await browserControl_1.BrowserControl.run(request);
|
|
@@ -2955,6 +3046,28 @@ else {
|
|
|
2955
3046
|
runtimeDeferred: false,
|
|
2956
3047
|
};
|
|
2957
3048
|
});
|
|
3049
|
+
electron_1.ipcMain.handle('agent:computerUseState', async (_event, targetInput) => {
|
|
3050
|
+
if (!agent)
|
|
3051
|
+
return { enabled: false, occupied: false, runtimeKey: '' };
|
|
3052
|
+
const target = (0, conversationTarget_1.normalizeConversationTarget)(conversationRuntimeTarget(targetInput || agent.activeConversationId || 'default'));
|
|
3053
|
+
return utilityHostToolHandler.computerUseState(target.runtimeKey);
|
|
3054
|
+
});
|
|
3055
|
+
electron_1.ipcMain.handle('agent:setComputerUseEnabled', async (_event, targetInput, enabled) => {
|
|
3056
|
+
if (!agent)
|
|
3057
|
+
return { ok: false, error: 'Agent not initialized' };
|
|
3058
|
+
const target = (0, conversationTarget_1.normalizeConversationTarget)(conversationRuntimeTarget(targetInput || agent.activeConversationId || 'default'));
|
|
3059
|
+
const result = utilityHostToolHandler.setComputerUseEnabled(target.runtimeKey, enabled !== false, `conversation:${target.conversationId}`);
|
|
3060
|
+
if (!result.ok)
|
|
3061
|
+
return result;
|
|
3062
|
+
if (enabled === false) {
|
|
3063
|
+
try {
|
|
3064
|
+
const { runComputerUse } = require('./tools/computerUse');
|
|
3065
|
+
await runComputerUse({ action: 'takeover_stop', workspacePath: target.workspace?.path || root, ownerId: target.runtimeKey, invocation: 'agent' });
|
|
3066
|
+
}
|
|
3067
|
+
catch { }
|
|
3068
|
+
}
|
|
3069
|
+
return result;
|
|
3070
|
+
});
|
|
2958
3071
|
electron_1.ipcMain.handle('agent:updateGoal', async (_event, goal, targetInput) => {
|
|
2959
3072
|
if (!agent)
|
|
2960
3073
|
return null;
|
|
@@ -3057,6 +3170,7 @@ else {
|
|
|
3057
3170
|
pendingOptions: conversationSnapshot.pendingOptions || agent.pendingOptions,
|
|
3058
3171
|
flowSuspension: flowSuspensionForTarget(target),
|
|
3059
3172
|
flowRunning: flowRunningForTarget(target),
|
|
3173
|
+
computerUse: utilityHostToolHandler.computerUseState((0, conversationTarget_1.normalizeConversationTarget)(target).runtimeKey),
|
|
3060
3174
|
draft: agent.getStoredConversationDraft(target.conversationId) || '',
|
|
3061
3175
|
proxyEnabled: agent.config.getBool('proxy', 'enabled'),
|
|
3062
3176
|
proxyUrl: agent.config.getStr('proxy', 'url'),
|
package/dist/preload.js
CHANGED
|
@@ -34,11 +34,13 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
34
34
|
setConversationPinned: (id, pinned) => ipcRenderer.invoke('agent:setConversationPinned', id, pinned),
|
|
35
35
|
renameConversation: (id, title) => ipcRenderer.invoke('agent:renameConversation', id, title),
|
|
36
36
|
reorderConversations: (ids) => ipcRenderer.invoke('agent:reorderConversations', ids),
|
|
37
|
-
browserRegisterGuest: (guestContentsId) => ipcRenderer.invoke('browser:registerGuest', guestContentsId),
|
|
37
|
+
browserRegisterGuest: (guestContentsId, target) => ipcRenderer.invoke('browser:registerGuest', guestContentsId, target),
|
|
38
38
|
onBrowserEnsureGuest: (callback) => {
|
|
39
|
-
ipcRenderer.on('browser:ensureGuest', () => callback());
|
|
39
|
+
ipcRenderer.on('browser:ensureGuest', (_event, target) => callback(target));
|
|
40
40
|
},
|
|
41
41
|
browserControl: (request) => ipcRenderer.invoke('browser:control', request),
|
|
42
|
+
computerUseState: (target) => ipcRenderer.invoke('agent:computerUseState', target),
|
|
43
|
+
setComputerUseEnabled: (target, enabled) => ipcRenderer.invoke('agent:setComputerUseEnabled', target, enabled),
|
|
42
44
|
runFlow: (name, input, start) => ipcRenderer.invoke('flow:run', name, input, start),
|
|
43
45
|
resumeFlow: (response, target) => ipcRenderer.invoke('flow:resume', response, target),
|
|
44
46
|
guideFlow: (message, target) => ipcRenderer.invoke('flow:guide', message, target),
|
package/dist/tools/index.js
CHANGED
|
@@ -57,8 +57,7 @@ const nativeBash_1 = require("../core/nativeBash");
|
|
|
57
57
|
const toolArgumentValidator_1 = require("../core/toolArgumentValidator");
|
|
58
58
|
const localOcr_1 = require("../core/localOcr");
|
|
59
59
|
const visualTextFallback_1 = require("../core/visualTextFallback");
|
|
60
|
-
|
|
61
|
-
const COMPUTER_USE_LOCK_TTL_MS = 10 * 60 * 1000;
|
|
60
|
+
const computerUseSession_1 = require("../core/computerUseSession");
|
|
62
61
|
function normalizeComputerUseAction(action) {
|
|
63
62
|
return String(action || '').trim().toLowerCase();
|
|
64
63
|
}
|
|
@@ -154,50 +153,28 @@ async function abortableToolDelay(durationMs, signal) {
|
|
|
154
153
|
abort();
|
|
155
154
|
});
|
|
156
155
|
}
|
|
157
|
-
function
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
function computerUseLockError(action, owner) {
|
|
163
|
-
return JSON.stringify({
|
|
164
|
-
ok: false,
|
|
165
|
-
action,
|
|
166
|
-
error: `ComputerUse is already active in ${computerUseLock?.owner || 'another conversation'}. Stop it with computer_use takeover_stop or wait before using ComputerUse from another conversation.`,
|
|
167
|
-
lock_owner: computerUseLock?.owner || '',
|
|
168
|
-
requested_owner: owner,
|
|
169
|
-
}, null, 2);
|
|
170
|
-
}
|
|
171
|
-
function acquireComputerUseLock(action, owner, wsPath) {
|
|
172
|
-
const now = Date.now();
|
|
173
|
-
clearStaleComputerUseLock(now);
|
|
174
|
-
if (computerUseLock && computerUseLock.owner !== owner) {
|
|
175
|
-
return computerUseLockError(action, owner);
|
|
176
|
-
}
|
|
177
|
-
computerUseLock = {
|
|
178
|
-
owner,
|
|
156
|
+
function computerUseSessionScope(context, wsPath, owner) {
|
|
157
|
+
return {
|
|
158
|
+
runtimeKey: browserUseScope(context, wsPath).runtimeKey,
|
|
159
|
+
ownerLabel: owner,
|
|
179
160
|
workspacePath: path.resolve(wsPath || process.cwd()),
|
|
180
|
-
acquiredAt: computerUseLock?.owner === owner ? computerUseLock.acquiredAt : now,
|
|
181
|
-
updatedAt: now,
|
|
182
161
|
};
|
|
183
|
-
return null;
|
|
184
162
|
}
|
|
185
|
-
function
|
|
186
|
-
|
|
187
|
-
if (computerUseLock && computerUseLock.owner !== owner) {
|
|
188
|
-
return computerUseLockError(action, owner);
|
|
189
|
-
}
|
|
190
|
-
if (computerUseLock?.owner === owner)
|
|
191
|
-
computerUseLock = null;
|
|
192
|
-
return null;
|
|
163
|
+
function acquireComputerUseLock(action, owner, wsPath, context = {}, dryRun = false) {
|
|
164
|
+
return computerUseSession_1.defaultComputerUseSessionRegistry.authorize(action, computerUseSessionScope(context, wsPath, owner), dryRun);
|
|
193
165
|
}
|
|
194
|
-
function
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return computerUseLockError(action, owner);
|
|
198
|
-
}
|
|
166
|
+
function releaseComputerUseLock(action, owner, context = {}, wsPath = context.workspacePath || '') {
|
|
167
|
+
const scope = computerUseSessionScope(context, wsPath, owner);
|
|
168
|
+
computerUseSession_1.defaultComputerUseSessionRegistry.complete(action, scope);
|
|
199
169
|
return null;
|
|
200
170
|
}
|
|
171
|
+
function assertComputerUseLockOwner(action, owner, context = {}, wsPath = context.workspacePath || '') {
|
|
172
|
+
return computerUseSession_1.defaultComputerUseSessionRegistry.authorize(action, computerUseSessionScope(context, wsPath, owner));
|
|
173
|
+
}
|
|
174
|
+
// A competing conversation receives the stable user-facing marker
|
|
175
|
+
// "ComputerUse is already active" / "computerUse occupied".
|
|
176
|
+
// The two-argument releaseComputerUseLock(action, owner) form remains valid
|
|
177
|
+
// for direct callers; routed Build calls additionally provide their target.
|
|
201
178
|
class ToolExecutor {
|
|
202
179
|
config;
|
|
203
180
|
ssh;
|
|
@@ -754,8 +731,8 @@ class ToolExecutor {
|
|
|
754
731
|
const action = normalizeComputerUseAction(g('action'));
|
|
755
732
|
const owner = `${computerUseOwner(context, wsPath)}:${String(context.actorId || 'root')}`;
|
|
756
733
|
const lockGuard = action === 'takeover_stop'
|
|
757
|
-
? assertComputerUseLockOwner(action, owner)
|
|
758
|
-
: acquireComputerUseLock(action, owner, wsPath);
|
|
734
|
+
? assertComputerUseLockOwner(action, owner, context, wsPath)
|
|
735
|
+
: acquireComputerUseLock(action, owner, wsPath, context, args.dry_run === true);
|
|
759
736
|
if (lockGuard)
|
|
760
737
|
return lockGuard;
|
|
761
738
|
if (process.env.NEWMARK_WSL_DISTRO) {
|
|
@@ -772,7 +749,7 @@ class ToolExecutor {
|
|
|
772
749
|
}
|
|
773
750
|
finally {
|
|
774
751
|
if (action === 'takeover_stop')
|
|
775
|
-
releaseComputerUseLock(action, owner);
|
|
752
|
+
releaseComputerUseLock(action, owner, context, wsPath);
|
|
776
753
|
}
|
|
777
754
|
}
|
|
778
755
|
if (process.env.NEWMARK_ISOLATED_RUNTIME === '1') {
|
|
@@ -794,7 +771,7 @@ class ToolExecutor {
|
|
|
794
771
|
}
|
|
795
772
|
finally {
|
|
796
773
|
if (action === 'takeover_stop')
|
|
797
|
-
releaseComputerUseLock(action, owner);
|
|
774
|
+
releaseComputerUseLock(action, owner, context, wsPath);
|
|
798
775
|
}
|
|
799
776
|
}
|
|
800
777
|
const output = await (0, computerUse_1.runComputerUse)({
|
|
@@ -838,7 +815,7 @@ class ToolExecutor {
|
|
|
838
815
|
: undefined,
|
|
839
816
|
});
|
|
840
817
|
if (action === 'takeover_stop')
|
|
841
|
-
releaseComputerUseLock(action, owner);
|
|
818
|
+
releaseComputerUseLock(action, owner, context, wsPath);
|
|
842
819
|
return output;
|
|
843
820
|
}
|
|
844
821
|
case 'terminal_takeover': {
|
|
@@ -1352,9 +1329,17 @@ class ToolExecutor {
|
|
|
1352
1329
|
}
|
|
1353
1330
|
}
|
|
1354
1331
|
async browserRun(request, signal, context = {}, workspacePath = this.root) {
|
|
1332
|
+
const scope = browserUseScope(context, workspacePath);
|
|
1333
|
+
const scopedRequest = {
|
|
1334
|
+
...request,
|
|
1335
|
+
target: {
|
|
1336
|
+
workspaceId: context.workspaceId || (0, terminalTakeover_1.terminalTakeoverWorkspaceId)(workspacePath),
|
|
1337
|
+
conversationId: context.conversationId || 'default',
|
|
1338
|
+
runtimeKey: scope.runtimeKey,
|
|
1339
|
+
},
|
|
1340
|
+
};
|
|
1355
1341
|
if (process.env.NEWMARK_WSL_DISTRO) {
|
|
1356
|
-
const
|
|
1357
|
-
const result = await (0, wslHostToolBridge_1.requestWindowsHostTool)('browser_control', request, {
|
|
1342
|
+
const result = await (0, wslHostToolBridge_1.requestWindowsHostTool)('browser_control', scopedRequest, {
|
|
1358
1343
|
conversationId: context.conversationId || process.env.NEWMARK_CONVERSATION_ID || 'default',
|
|
1359
1344
|
workspaceId: process.env.NEWMARK_WORKSPACE_ID || context.workspaceId || (0, terminalTakeover_1.terminalTakeoverWorkspaceId)(workspacePath),
|
|
1360
1345
|
actorId: context.actorId || terminalTakeover_1.ROOT_TERMINAL_ACTOR_ID,
|
|
@@ -1364,10 +1349,12 @@ class ToolExecutor {
|
|
|
1364
1349
|
return this.formatBrowserResult(result);
|
|
1365
1350
|
}
|
|
1366
1351
|
if (process.env.NEWMARK_ISOLATED_RUNTIME === '1') {
|
|
1367
|
-
const result = await (0, utilityHostToolBridge_1.requestUtilityHostTool)('browser_control',
|
|
1352
|
+
const result = await (0, utilityHostToolBridge_1.requestUtilityHostTool)('browser_control', scopedRequest, undefined, 30_000, signal);
|
|
1368
1353
|
return this.formatBrowserResult(result);
|
|
1369
1354
|
}
|
|
1370
|
-
|
|
1355
|
+
// Preserve the cancellation contract of BrowserControl.run(request, signal)
|
|
1356
|
+
// while routing the concrete request through the target-bound copy above.
|
|
1357
|
+
const result = await browserControl_1.BrowserControl.run(scopedRequest, signal);
|
|
1371
1358
|
return this.formatBrowserResult(result);
|
|
1372
1359
|
}
|
|
1373
1360
|
formatBrowserResult(result) {
|