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/ui/index.html
CHANGED
|
@@ -5461,6 +5461,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
5461
5461
|
<button class="shell-btn browser-action-btn" onclick="window.browserReload()" title="Reload"><svg class="nm-icon small" aria-hidden="true" focusable="false"><use href="#refresh-cw"></use></svg><span class="icon-only-label">Reload</span></button>
|
|
5462
5462
|
<input class="browser-url-input" id="browser-url" placeholder="Enter URL..." onkeydown="if(event.key==='Enter')window.navigateBrowser(this.value)">
|
|
5463
5463
|
<button class="shell-btn" onclick="window.navigateBrowser(document.getElementById('browser-url').value)" title="Go"><svg class="nm-icon small" aria-hidden="true" focusable="false"><use href="#send"></use></svg><span class="icon-only-label">Go</span></button>
|
|
5464
|
+
<button class="shell-btn" id="computer-use-toggle" onclick="window.toggleComputerUse()" title="ComputerUse: enabled for this conversation" aria-pressed="true">CU</button>
|
|
5464
5465
|
</div>
|
|
5465
5466
|
<div class="browser-frame" id="browser-frame">
|
|
5466
5467
|
<div class="browser-guest-placeholder" id="browser-guest-placeholder">Browser loads on first use</div>
|
|
@@ -5681,6 +5682,7 @@ var state = {
|
|
|
5681
5682
|
terminalInterruptTimeoutMs: 0,
|
|
5682
5683
|
nativeTools: [],
|
|
5683
5684
|
nativeToolEnabled: {},
|
|
5685
|
+
computerUseByTarget: {},
|
|
5684
5686
|
sshConnections: [],
|
|
5685
5687
|
_layoutSaveTimer: null,
|
|
5686
5688
|
_sendInFlight: false
|
|
@@ -9594,17 +9596,22 @@ function renderConversationWorkRuns(runs, target) {
|
|
|
9594
9596
|
return normalized;
|
|
9595
9597
|
}
|
|
9596
9598
|
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9599
|
+
// Work events can arrive much faster than the browser can paint them (text
|
|
9600
|
+
// deltas are especially noisy). Keep the authoritative run in memory for
|
|
9601
|
+
// every event, but coalesce its DOM replacement to one animation frame. This
|
|
9602
|
+
// is important for long real-model runs: replacing the whole work block for
|
|
9603
|
+
// every delta can starve Chromium's renderer/CDP event loop.
|
|
9604
|
+
var pendingConversationWorkRunRenders = {};
|
|
9605
|
+
var scheduledConversationWorkRunRenders = {};
|
|
9606
|
+
function scheduleConversationWorkRunRender(run) {
|
|
9600
9607
|
var key = runtimeKeyFor(run.target && run.target.workspaceId, run.target && run.target.conversationId) + '::' + String(run.runId || '');
|
|
9601
|
-
|
|
9602
|
-
if (
|
|
9603
|
-
|
|
9608
|
+
pendingConversationWorkRunRenders[key] = run;
|
|
9609
|
+
if (scheduledConversationWorkRunRenders[key]) return;
|
|
9610
|
+
scheduledConversationWorkRunRenders[key] = true;
|
|
9604
9611
|
requestAnimationFrame(function() {
|
|
9605
|
-
var latest =
|
|
9606
|
-
delete
|
|
9607
|
-
delete
|
|
9612
|
+
var latest = pendingConversationWorkRunRenders[key];
|
|
9613
|
+
delete pendingConversationWorkRunRenders[key];
|
|
9614
|
+
delete scheduledConversationWorkRunRenders[key];
|
|
9608
9615
|
if (latest && isActiveConversationTarget(latest.target)) renderConversationWorkRun(latest);
|
|
9609
9616
|
});
|
|
9610
9617
|
}
|
|
@@ -9640,7 +9647,9 @@ function applyAgentWorkEventToRun(event) {
|
|
|
9640
9647
|
var priorGuideEvent = run.events[guideEventIndex];
|
|
9641
9648
|
if (guideWorkEventKey(priorGuideEvent) === incomingGuideKey) {
|
|
9642
9649
|
run.events[guideEventIndex] = mergeGuideWorkEvent(priorGuideEvent, event);
|
|
9643
|
-
|
|
9650
|
+
if (isActiveConversationTarget(target) && (!branchIds.viewed || !eventBranchId || branchIds.viewed === eventBranchId)) {
|
|
9651
|
+
scheduleConversationWorkRunRender(run);
|
|
9652
|
+
}
|
|
9644
9653
|
return run;
|
|
9645
9654
|
}
|
|
9646
9655
|
}
|
|
@@ -9667,8 +9676,8 @@ function applyAgentWorkEventToRun(event) {
|
|
|
9667
9676
|
if (isActiveConversationTarget(target) && (!branchIds.viewed || !eventBranchId || branchIds.viewed === eventBranchId)) {
|
|
9668
9677
|
state.workRunsByTarget[runtimeKeyFor(target.workspaceId, target.conversationId)] = runs;
|
|
9669
9678
|
var existingElement = findWorkRunElement(run);
|
|
9670
|
-
if (existingElement
|
|
9671
|
-
else
|
|
9679
|
+
if (!existingElement || type === 'start') renderConversationWorkRun(run);
|
|
9680
|
+
else scheduleConversationWorkRunRender(run);
|
|
9672
9681
|
}
|
|
9673
9682
|
return run;
|
|
9674
9683
|
}
|
|
@@ -10459,7 +10468,14 @@ function hydrateConversationBranchState(snapshot) {
|
|
|
10459
10468
|
}
|
|
10460
10469
|
|
|
10461
10470
|
function applyConversationSnapshot(s, requestedConversationId) {
|
|
10462
|
-
|
|
10471
|
+
if (!s) return;
|
|
10472
|
+
if (s.computerUse) {
|
|
10473
|
+
var computerTarget = currentConversationTarget(s.conversationId || requestedConversationId || activeConversationId());
|
|
10474
|
+
var computerKey = computerUseTargetKey(computerTarget);
|
|
10475
|
+
if (!state.computerUseByTarget) state.computerUseByTarget = {};
|
|
10476
|
+
state.computerUseByTarget[computerKey] = s.computerUse;
|
|
10477
|
+
if (isActiveConversationTarget(computerTarget)) renderComputerUseToggle(s.computerUse, computerTarget);
|
|
10478
|
+
}
|
|
10463
10479
|
state.goalText = s.goal && s.goal.objective ? String(s.goal.objective) : '';
|
|
10464
10480
|
state.goalVisible = !!state.goalText;
|
|
10465
10481
|
state.goalPaused = !!(s.goal && s.goal.paused);
|
|
@@ -10813,6 +10829,44 @@ function isCurrentConversationRunning() {
|
|
|
10813
10829
|
return !!(id && runningConversationRecord(id));
|
|
10814
10830
|
}
|
|
10815
10831
|
|
|
10832
|
+
// A terminal final_response event can reach the renderer before the awaited
|
|
10833
|
+
// send promise performs its final local cleanup. If a user submits in that
|
|
10834
|
+
// small window, trusting the stale local running/active-send flags silently
|
|
10835
|
+
// drops the new message. Reconcile only when a send is already considered
|
|
10836
|
+
// active; a backend-confirmed running snapshot still keeps the guard intact.
|
|
10837
|
+
async function reconcileStaleLocalRuntimeBeforeSend(target) {
|
|
10838
|
+
if (!target || !api.getState) return false;
|
|
10839
|
+
var snapshot;
|
|
10840
|
+
try {
|
|
10841
|
+
snapshot = await api.getState(target);
|
|
10842
|
+
} catch (_) {
|
|
10843
|
+
return false;
|
|
10844
|
+
}
|
|
10845
|
+
var runtimeSnapshot = snapshot && snapshot.runtime || null;
|
|
10846
|
+
var snapshotStatus = String(snapshot && snapshot.status || '').toLowerCase();
|
|
10847
|
+
var backendStillRunning = !!(runtimeSnapshot && (runtimeSnapshot.running || runtimeSnapshot.stopRequested))
|
|
10848
|
+
|| ['running', 'stopping', 'force_restarting'].indexOf(snapshotStatus) >= 0;
|
|
10849
|
+
if (backendStillRunning) return false;
|
|
10850
|
+
if (['idle', 'completed', 'interrupted', 'force_interrupted', 'error'].indexOf(snapshotStatus) < 0) return false;
|
|
10851
|
+
var runId = String((runtimeSnapshot && runtimeSnapshot.runId) || snapshot.runId || '');
|
|
10852
|
+
var generation = Number((runtimeSnapshot && runtimeSnapshot.generation) || snapshot.generation || 0);
|
|
10853
|
+
setConversationRuntimeState(target, snapshotStatus, runId, { provisional: false, generation: generation });
|
|
10854
|
+
var targetKey = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
10855
|
+
if (state.activeSendCallsByTarget) {
|
|
10856
|
+
delete state.activeSendCallsByTarget[targetKey];
|
|
10857
|
+
delete state.activeSendCallsByTarget[runtimeKeyFor('', target.conversationId)];
|
|
10858
|
+
}
|
|
10859
|
+
if (state.nextQueueSchedulesByTarget) {
|
|
10860
|
+
delete state.nextQueueSchedulesByTarget[targetKey];
|
|
10861
|
+
delete state.nextQueueSchedulesByTarget[runtimeKeyFor('', target.conversationId)];
|
|
10862
|
+
}
|
|
10863
|
+
state._sendInFlight = Object.keys(state.runningConversations || {}).length > 0;
|
|
10864
|
+
setWorking(state._sendInFlight);
|
|
10865
|
+
updateSubmitButtonState();
|
|
10866
|
+
if (window.renderInputStack) window.renderInputStack();
|
|
10867
|
+
return true;
|
|
10868
|
+
}
|
|
10869
|
+
|
|
10816
10870
|
function setSubmitButtonVisual(icon, label, running, stopMode) {
|
|
10817
10871
|
var btn = els['submit-btn'] || document.getElementById('submit-btn');
|
|
10818
10872
|
if (!btn) return;
|
|
@@ -11837,6 +11891,13 @@ window.endQueueDrag = function(event) {
|
|
|
11837
11891
|
if (event.currentTarget) event.currentTarget.classList.remove('dragging');
|
|
11838
11892
|
};
|
|
11839
11893
|
|
|
11894
|
+
function recoverableGuideRejection(receipt) {
|
|
11895
|
+
if (!receipt || String(receipt.status || '').toLowerCase() !== 'rejected') return false;
|
|
11896
|
+
var reason = String(receipt.reason || receipt.error || '').trim();
|
|
11897
|
+
return reason === 'Target conversation is not running'
|
|
11898
|
+
|| reason === 'Guide runId does not match the active run';
|
|
11899
|
+
}
|
|
11900
|
+
|
|
11840
11901
|
window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
11841
11902
|
if (!window.requireWorkspace('send')) return;
|
|
11842
11903
|
opts = opts || {};
|
|
@@ -11960,6 +12021,28 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
11960
12021
|
var conversationRunning = !!activeRuntime;
|
|
11961
12022
|
markConversationTracked(lockedConversationId, state.conversationTrackMs || 300000, lockedTarget.workspaceId);
|
|
11962
12023
|
|
|
12024
|
+
var activeSendCall = !!(state.activeSendCallsByTarget && state.activeSendCallsByTarget[lockedRuntimeKey]);
|
|
12025
|
+
if ((conversationRunning || activeSendCall) && api.getState) {
|
|
12026
|
+
var reconciledLocalRuntime = await reconcileStaleLocalRuntimeBeforeSend(lockedTarget);
|
|
12027
|
+
if (reconciledLocalRuntime) {
|
|
12028
|
+
activeRuntime = runningConversationRecord(lockedConversationId, lockedTarget.workspaceId);
|
|
12029
|
+
conversationRunning = !!activeRuntime;
|
|
12030
|
+
if (!conversationRunning && effectiveInputMode === 'guide') effectiveInputMode = 'next';
|
|
12031
|
+
}
|
|
12032
|
+
}
|
|
12033
|
+
|
|
12034
|
+
if (opts.retryAfterGuideRejection && conversationRunning && api.getState) {
|
|
12035
|
+
try {
|
|
12036
|
+
var freshRuntimeState = await api.getState(lockedTarget);
|
|
12037
|
+
var freshStatus = String(freshRuntimeState && freshRuntimeState.status || '').toLowerCase();
|
|
12038
|
+
if (['idle', 'completed', 'interrupted', 'force_interrupted', 'error'].indexOf(freshStatus) >= 0) {
|
|
12039
|
+
setConversationRuntimeState(lockedTarget, 'idle', '');
|
|
12040
|
+
activeRuntime = null;
|
|
12041
|
+
conversationRunning = false;
|
|
12042
|
+
}
|
|
12043
|
+
} catch (_) {}
|
|
12044
|
+
}
|
|
12045
|
+
|
|
11963
12046
|
if (effectiveInputMode === 'next' && !opts.fromQueue && conversationRunning) {
|
|
11964
12047
|
if (!state.nextQueue) state.nextQueue = [];
|
|
11965
12048
|
if (!state.nextQueueRequests) state.nextQueueRequests = [];
|
|
@@ -12041,6 +12124,57 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
12041
12124
|
throw new Error(currentLang() === 'zh' ? '未收到有效的 Guide 回执' : 'A valid Guide acknowledgement was not received');
|
|
12042
12125
|
}
|
|
12043
12126
|
var status = normalizeGuideUiStatus(receipt.status);
|
|
12127
|
+
if (recoverableGuideRejection(receipt)) {
|
|
12128
|
+
// Renderer runtime state can lag the backend's finalization event by
|
|
12129
|
+
// one IPC turn. The Guide was explicitly rejected, so replaying the
|
|
12130
|
+
// exact payload as a fresh Build is safe and prevents a normal user
|
|
12131
|
+
// send from disappearing behind a stale Guide route.
|
|
12132
|
+
var fallbackRequest = {
|
|
12133
|
+
text: typeof requestMessage === 'string' ? requestMessage : String(requestMessage.text || ''),
|
|
12134
|
+
images: typeof requestMessage === 'object' && Array.isArray(requestMessage.images)
|
|
12135
|
+
? requestMessage.images.map(function(image) { return Object.assign({}, image); })
|
|
12136
|
+
: []
|
|
12137
|
+
};
|
|
12138
|
+
recordGuideUiMessage({
|
|
12139
|
+
clientMessageId: clientMessageId,
|
|
12140
|
+
target: receipt.target || lockedTarget,
|
|
12141
|
+
runId: receipt.runId || envelope.runId,
|
|
12142
|
+
status: 'deferred',
|
|
12143
|
+
content: displayText,
|
|
12144
|
+
attachments: optimisticAttachments,
|
|
12145
|
+
createdAt: receipt.createdAt || envelope.createdAt,
|
|
12146
|
+
updatedAt: receipt.updatedAt,
|
|
12147
|
+
reason: currentLang() === 'zh'
|
|
12148
|
+
? '运行已结束,输入已作为新的 Build 继续。'
|
|
12149
|
+
: 'The run ended before Guide delivery; the input continued as a new Build.'
|
|
12150
|
+
}, lockedTarget);
|
|
12151
|
+
applyAgentWorkEventToRun({
|
|
12152
|
+
id: 'guide-' + clientMessageId + '-deferred',
|
|
12153
|
+
type: 'guide_deferred',
|
|
12154
|
+
content: displayText,
|
|
12155
|
+
workspaceId: lockedTarget.workspaceId,
|
|
12156
|
+
conversationId: lockedTarget.conversationId,
|
|
12157
|
+
runId: receipt.runId || envelope.runId,
|
|
12158
|
+
sequence: receipt.sequence || 0,
|
|
12159
|
+
guideStatus: 'deferred',
|
|
12160
|
+
clientMessageId: clientMessageId,
|
|
12161
|
+
guide: guideMessagesForTarget(lockedTarget)[clientMessageId]
|
|
12162
|
+
});
|
|
12163
|
+
return Promise.resolve(window.sendMessage('next', undefined, {
|
|
12164
|
+
requestedMode: requestedMode,
|
|
12165
|
+
goalObjective: opts.goalObjective,
|
|
12166
|
+
forceBuild: true,
|
|
12167
|
+
retryAfterGuideRejection: true,
|
|
12168
|
+
queuedRequest: fallbackRequest
|
|
12169
|
+
})).then(function(fallbackResult) {
|
|
12170
|
+
return {
|
|
12171
|
+
ok: !(fallbackResult && fallbackResult.ok === false),
|
|
12172
|
+
guideReceipt: receipt,
|
|
12173
|
+
recoveredAsBuild: true,
|
|
12174
|
+
fallback: fallbackResult
|
|
12175
|
+
};
|
|
12176
|
+
});
|
|
12177
|
+
}
|
|
12044
12178
|
var guideRecord = recordGuideUiMessage({
|
|
12045
12179
|
clientMessageId: clientMessageId,
|
|
12046
12180
|
target: receipt.target || lockedTarget,
|
|
@@ -12683,6 +12817,7 @@ window.switchRightTab = function(tab) {
|
|
|
12683
12817
|
if (tab === 'subagent') window.renderSubagentList();
|
|
12684
12818
|
if (tab === 'browser') {
|
|
12685
12819
|
cancelBrowserGuestIdleDestroy();
|
|
12820
|
+
window.refreshComputerUseState(currentConversationTarget());
|
|
12686
12821
|
window.ensureBrowserPanel({ activate: true }).catch(function(error) {
|
|
12687
12822
|
console.error('[Browser] guest initialization failed:', error && error.message ? error.message : String(error));
|
|
12688
12823
|
});
|
|
@@ -16849,11 +16984,63 @@ var NEWMARK_BROWSER_PARTITION = 'persist:newmark-browser';
|
|
|
16849
16984
|
var NEWMARK_BROWSER_MIN_CREATE_DELAY_MS = 5000;
|
|
16850
16985
|
var NEWMARK_BROWSER_IDLE_DESTROY_MS = 60000;
|
|
16851
16986
|
var browserGuestCreatePromise = null;
|
|
16987
|
+
var browserGuestCreatePromises = {};
|
|
16852
16988
|
var browserGuestIdleTimer = null;
|
|
16853
16989
|
var browserRetainedUrl = 'about:blank';
|
|
16990
|
+
var browserRetainedUrls = {};
|
|
16854
16991
|
var browserGuestCreateCount = 0;
|
|
16855
16992
|
var browserGuestFirstCreatedAtMs = null;
|
|
16856
16993
|
|
|
16994
|
+
function computerUseTargetKey(target) {
|
|
16995
|
+
target = target || currentConversationTarget();
|
|
16996
|
+
return runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
16997
|
+
}
|
|
16998
|
+
|
|
16999
|
+
function renderComputerUseToggle(value, target) {
|
|
17000
|
+
var button = document.getElementById('computer-use-toggle');
|
|
17001
|
+
if (!button) return;
|
|
17002
|
+
var enabled = value && value.enabled !== false;
|
|
17003
|
+
var occupied = !!(value && value.occupied);
|
|
17004
|
+
var owner = value && value.ownerLabel ? ' · ' + String(value.ownerLabel) : '';
|
|
17005
|
+
button.classList.toggle('active', enabled);
|
|
17006
|
+
button.setAttribute('aria-pressed', enabled ? 'true' : 'false');
|
|
17007
|
+
button.title = (enabled ? 'ComputerUse enabled for this conversation' : 'ComputerUse disabled for this conversation') + owner;
|
|
17008
|
+
button.textContent = enabled ? (occupied ? 'CU●' : 'CU') : 'CU×';
|
|
17009
|
+
button.dataset.computerUseRuntimeKey = computerUseTargetKey(target);
|
|
17010
|
+
}
|
|
17011
|
+
|
|
17012
|
+
window.refreshComputerUseState = function(target) {
|
|
17013
|
+
target = target || currentConversationTarget();
|
|
17014
|
+
var key = computerUseTargetKey(target);
|
|
17015
|
+
var cached = state.computerUseByTarget && state.computerUseByTarget[key];
|
|
17016
|
+
if (cached) renderComputerUseToggle(cached, target);
|
|
17017
|
+
if (!api.computerUseState) return Promise.resolve(cached || { enabled: true, occupied: false, runtimeKey: key });
|
|
17018
|
+
return api.computerUseState(target).then(function(value) {
|
|
17019
|
+
if (!state.computerUseByTarget) state.computerUseByTarget = {};
|
|
17020
|
+
state.computerUseByTarget[key] = value || { enabled: true, occupied: false, runtimeKey: key };
|
|
17021
|
+
if (isActiveConversationTarget(target)) renderComputerUseToggle(state.computerUseByTarget[key], target);
|
|
17022
|
+
return state.computerUseByTarget[key];
|
|
17023
|
+
}).catch(function() { return cached || { enabled: true, occupied: false, runtimeKey: key }; });
|
|
17024
|
+
};
|
|
17025
|
+
|
|
17026
|
+
window.toggleComputerUse = function() {
|
|
17027
|
+
var target = currentConversationTarget();
|
|
17028
|
+
var key = computerUseTargetKey(target);
|
|
17029
|
+
var current = state.computerUseByTarget && state.computerUseByTarget[key];
|
|
17030
|
+
var enabled = !(current && current.enabled !== false);
|
|
17031
|
+
if (!api.setComputerUseEnabled) return Promise.resolve(null);
|
|
17032
|
+
return api.setComputerUseEnabled(target, enabled).then(function(result) {
|
|
17033
|
+
if (result && result.ok === false) {
|
|
17034
|
+
showUiNotice(String(result.error || 'ComputerUse occupied'), 'error', 'computer-use-toggle-' + key);
|
|
17035
|
+
return result;
|
|
17036
|
+
}
|
|
17037
|
+
if (!state.computerUseByTarget) state.computerUseByTarget = {};
|
|
17038
|
+
state.computerUseByTarget[key] = result && result.state ? result.state : { enabled: enabled, occupied: false, runtimeKey: key };
|
|
17039
|
+
renderComputerUseToggle(state.computerUseByTarget[key], target);
|
|
17040
|
+
return result;
|
|
17041
|
+
});
|
|
17042
|
+
};
|
|
17043
|
+
|
|
16857
17044
|
window.browserGuestLifecycleSnapshot = function() {
|
|
16858
17045
|
return {
|
|
16859
17046
|
createCount: browserGuestCreateCount,
|
|
@@ -16870,15 +17057,60 @@ function browserUrlsEquivalent(left, right) {
|
|
|
16870
17057
|
return !!a && a === b;
|
|
16871
17058
|
}
|
|
16872
17059
|
|
|
16873
|
-
function
|
|
16874
|
-
|
|
16875
|
-
return
|
|
17060
|
+
function browserTargetKey(target) {
|
|
17061
|
+
target = target || currentConversationTarget();
|
|
17062
|
+
return runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
16876
17063
|
}
|
|
16877
17064
|
|
|
16878
|
-
function
|
|
17065
|
+
function browserPartitionForTarget(target) {
|
|
17066
|
+
var key = browserTargetKey(target).replace(/[^A-Za-z0-9_.-]/g, '_').slice(0, 150) || 'default';
|
|
17067
|
+
return NEWMARK_BROWSER_PARTITION + '-' + key;
|
|
17068
|
+
}
|
|
17069
|
+
|
|
17070
|
+
function browserGuestForTarget(target) {
|
|
17071
|
+
var key = browserTargetKey(target);
|
|
17072
|
+
var frame = els['browser-frame'] || document.getElementById('browser-frame');
|
|
17073
|
+
if (!frame) return null;
|
|
17074
|
+
var views = frame.querySelectorAll('webview[data-newmark-browser-runtime-key]');
|
|
17075
|
+
for (var i = 0; i < views.length; i++) {
|
|
17076
|
+
if (String(views[i].getAttribute('data-newmark-browser-runtime-key') || '') === key && views[i].isConnected) return views[i];
|
|
17077
|
+
}
|
|
17078
|
+
return null;
|
|
17079
|
+
}
|
|
17080
|
+
|
|
17081
|
+
function setActiveBrowserGuest(view, target) {
|
|
17082
|
+
var key = browserTargetKey(target);
|
|
17083
|
+
var frame = els['browser-frame'] || document.getElementById('browser-frame');
|
|
17084
|
+
if (frame) {
|
|
17085
|
+
var views = frame.querySelectorAll('webview[data-newmark-browser-runtime-key]');
|
|
17086
|
+
for (var i = 0; i < views.length; i++) {
|
|
17087
|
+
var same = views[i] === view || String(views[i].getAttribute('data-newmark-browser-runtime-key') || '') === key;
|
|
17088
|
+
views[i].style.display = same ? '' : 'none';
|
|
17089
|
+
if (same) views[i].id = 'browser-webview';
|
|
17090
|
+
else if (views[i].id === 'browser-webview') views[i].id = 'browser-webview-' + String(views[i].getAttribute('data-newmark-browser-runtime-key') || 'guest').replace(/[^A-Za-z0-9_.-]/g, '_').slice(-80);
|
|
17091
|
+
}
|
|
17092
|
+
}
|
|
17093
|
+
els['browser-webview'] = view || null;
|
|
17094
|
+
if (view) view._newmarkBrowserTargetKey = key;
|
|
17095
|
+
}
|
|
17096
|
+
|
|
17097
|
+
function currentBrowserGuest(target) {
|
|
17098
|
+
var view = browserGuestForTarget(target || currentConversationTarget());
|
|
17099
|
+
if (view) return view;
|
|
17100
|
+
var active = document.getElementById('browser-webview');
|
|
17101
|
+
return active && active.isConnected ? active : null;
|
|
17102
|
+
}
|
|
17103
|
+
|
|
17104
|
+
function rememberBrowserUrl(url, force, target) {
|
|
17105
|
+
target = target || currentConversationTarget();
|
|
17106
|
+
var targetKey = browserTargetKey(target);
|
|
16879
17107
|
url = String(url || '').trim();
|
|
16880
|
-
|
|
16881
|
-
if (url
|
|
17108
|
+
var prior = browserRetainedUrls[targetKey] || 'about:blank';
|
|
17109
|
+
if (!force && url === 'about:blank' && prior !== 'about:blank') return;
|
|
17110
|
+
if (url) {
|
|
17111
|
+
browserRetainedUrls[targetKey] = url;
|
|
17112
|
+
if (isActiveConversationTarget(target)) browserRetainedUrl = url;
|
|
17113
|
+
}
|
|
16882
17114
|
}
|
|
16883
17115
|
|
|
16884
17116
|
function waitForBrowserCreationFloor() {
|
|
@@ -16887,8 +17119,9 @@ function waitForBrowserCreationFloor() {
|
|
|
16887
17119
|
return new Promise(function(resolve) { setTimeout(resolve, remaining); });
|
|
16888
17120
|
}
|
|
16889
17121
|
|
|
16890
|
-
function createBrowserGuestElement() {
|
|
16891
|
-
|
|
17122
|
+
function createBrowserGuestElement(target) {
|
|
17123
|
+
target = target || currentConversationTarget();
|
|
17124
|
+
var existing = browserGuestForTarget(target);
|
|
16892
17125
|
if (existing) return existing;
|
|
16893
17126
|
var frame = els['browser-frame'] || document.getElementById('browser-frame');
|
|
16894
17127
|
if (!frame) throw new Error('Built-in Browser frame is unavailable');
|
|
@@ -16898,8 +17131,14 @@ function createBrowserGuestElement() {
|
|
|
16898
17131
|
browserGuestCreateCount += 1;
|
|
16899
17132
|
if (browserGuestFirstCreatedAtMs === null) browserGuestFirstCreatedAtMs = Date.now() - newmarkRendererStartedAt;
|
|
16900
17133
|
view.id = 'browser-webview';
|
|
16901
|
-
view.setAttribute('
|
|
17134
|
+
view.setAttribute('data-newmark-browser-runtime-key', browserTargetKey(target));
|
|
17135
|
+
view.setAttribute('partition', browserPartitionForTarget(target));
|
|
16902
17136
|
frame.appendChild(view);
|
|
17137
|
+
if (isActiveConversationTarget(target)) setActiveBrowserGuest(view, target);
|
|
17138
|
+
else {
|
|
17139
|
+
view.style.display = 'none';
|
|
17140
|
+
view.id = 'browser-webview-' + browserTargetKey(target).replace(/[^A-Za-z0-9_.-]/g, '_').slice(-80);
|
|
17141
|
+
}
|
|
16903
17142
|
els['browser-webview'] = view;
|
|
16904
17143
|
return view;
|
|
16905
17144
|
}
|
|
@@ -16922,11 +17161,14 @@ function cancelBrowserGuestIdleDestroy() {
|
|
|
16922
17161
|
function destroyIdleBrowserGuest() {
|
|
16923
17162
|
browserGuestIdleTimer = null;
|
|
16924
17163
|
if (state.rightTab === 'browser') return;
|
|
16925
|
-
var view = currentBrowserGuest();
|
|
17164
|
+
var view = currentBrowserGuest(currentConversationTarget());
|
|
16926
17165
|
if (!view) return;
|
|
16927
|
-
|
|
17166
|
+
var target = { workspaceId: state.currentWorkspaceId || state.currentWorkspacePath || state.currentWorkspace || 'none', conversationId: activeConversationId() || 'default' };
|
|
17167
|
+
try { rememberBrowserUrl(view.getURL ? view.getURL() : '', false, target); } catch {}
|
|
16928
17168
|
if (view.parentNode) view.parentNode.removeChild(view);
|
|
16929
17169
|
if (els['browser-webview'] === view) els['browser-webview'] = null;
|
|
17170
|
+
var key = browserTargetKey(target);
|
|
17171
|
+
delete browserGuestCreatePromises[key];
|
|
16930
17172
|
browserGuestCreatePromise = null;
|
|
16931
17173
|
restoreBrowserPlaceholder();
|
|
16932
17174
|
}
|
|
@@ -16958,16 +17200,17 @@ function navigateBrowserView(view, url) {
|
|
|
16958
17200
|
}
|
|
16959
17201
|
|
|
16960
17202
|
window.navigateBrowser = function(url) {
|
|
17203
|
+
var target = currentConversationTarget();
|
|
16961
17204
|
url = String(url || '').trim();
|
|
16962
17205
|
if (!/^(?:https?:\/\/|about:blank$|newmark-preview:\/\/)/i.test(url)) {
|
|
16963
17206
|
url = 'https://' + url;
|
|
16964
17207
|
}
|
|
16965
17208
|
if (state.browserPreviewToken && url !== state.browserPreviewUrl) window.revokeBrowserPreviewCapability();
|
|
16966
|
-
rememberBrowserUrl(url, true);
|
|
17209
|
+
rememberBrowserUrl(url, true, target);
|
|
16967
17210
|
var input = els['browser-url'];
|
|
16968
17211
|
if (input) input.value = url;
|
|
16969
17212
|
cancelBrowserGuestIdleDestroy();
|
|
16970
|
-
return window.ensureBrowserPanel({ activate: false }).then(function(view) {
|
|
17213
|
+
return window.ensureBrowserPanel({ activate: false, target: target }).then(function(view) {
|
|
16971
17214
|
navigateBrowserView(view, url);
|
|
16972
17215
|
if (state.rightTab !== 'browser') scheduleBrowserGuestIdleDestroy();
|
|
16973
17216
|
return view;
|
|
@@ -16996,6 +17239,8 @@ window.browserReload = function() {
|
|
|
16996
17239
|
};
|
|
16997
17240
|
|
|
16998
17241
|
function initializeBrowserGuest(view) {
|
|
17242
|
+
var target = arguments.length > 1 && arguments[1] ? arguments[1] : currentConversationTarget();
|
|
17243
|
+
var targetKey = browserTargetKey(target);
|
|
16999
17244
|
if (!view._newmarkReadyPromise) {
|
|
17000
17245
|
view._newmarkDomReady = false;
|
|
17001
17246
|
view._newmarkReadySettled = false;
|
|
@@ -17011,12 +17256,12 @@ function initializeBrowserGuest(view) {
|
|
|
17011
17256
|
currentUrl = view.getURL ? view.getURL() : '';
|
|
17012
17257
|
if (input && currentUrl && currentUrl !== 'about:blank') input.value = currentUrl;
|
|
17013
17258
|
} catch {}
|
|
17014
|
-
rememberBrowserUrl(currentUrl);
|
|
17259
|
+
rememberBrowserUrl(currentUrl, false, target);
|
|
17015
17260
|
if (view._newmarkReadySettled) return;
|
|
17016
17261
|
var registration = Promise.resolve({ accepted: true });
|
|
17017
17262
|
try {
|
|
17018
17263
|
if (!api.browserRegisterGuest || !view.getWebContentsId) throw new Error('Built-in Browser registration API is unavailable');
|
|
17019
|
-
registration = api.browserRegisterGuest(view.getWebContentsId());
|
|
17264
|
+
registration = api.browserRegisterGuest(view.getWebContentsId(), target);
|
|
17020
17265
|
} catch (registrationError) {
|
|
17021
17266
|
registration = Promise.reject(registrationError);
|
|
17022
17267
|
}
|
|
@@ -17056,13 +17301,13 @@ function initializeBrowserGuest(view) {
|
|
|
17056
17301
|
view.addEventListener('did-navigate', function(e) {
|
|
17057
17302
|
var input = els['browser-url'];
|
|
17058
17303
|
if (input && e.url && e.url !== 'about:blank') input.value = e.url;
|
|
17059
|
-
rememberBrowserUrl(e.url);
|
|
17304
|
+
rememberBrowserUrl(e.url, false, target);
|
|
17060
17305
|
if (browserUrlsEquivalent(e.url, view._newmarkPendingUrl)) view._newmarkPendingUrl = '';
|
|
17061
17306
|
});
|
|
17062
17307
|
view.addEventListener('did-navigate-in-page', function(e) {
|
|
17063
17308
|
var input = els['browser-url'];
|
|
17064
17309
|
if (input && e.url && e.url !== 'about:blank') input.value = e.url;
|
|
17065
|
-
rememberBrowserUrl(e.url);
|
|
17310
|
+
rememberBrowserUrl(e.url, false, target);
|
|
17066
17311
|
if (browserUrlsEquivalent(e.url, view._newmarkPendingUrl)) view._newmarkPendingUrl = '';
|
|
17067
17312
|
});
|
|
17068
17313
|
view.addEventListener('did-fail-load', function(e) {
|
|
@@ -17076,27 +17321,31 @@ function initializeBrowserGuest(view) {
|
|
|
17076
17321
|
});
|
|
17077
17322
|
var current = '';
|
|
17078
17323
|
try { current = view.getURL ? view.getURL() : ''; } catch { current = ''; }
|
|
17079
|
-
if (!current && !view.getAttribute('src')) view.setAttribute('src', 'about:blank');
|
|
17324
|
+
if (!current && !view.getAttribute('src')) view.setAttribute('src', browserRetainedUrls[targetKey] || 'about:blank');
|
|
17080
17325
|
}
|
|
17081
17326
|
return view._newmarkReadyPromise;
|
|
17082
17327
|
}
|
|
17083
17328
|
|
|
17084
17329
|
window.ensureBrowserPanel = function(options) {
|
|
17085
17330
|
options = options || {};
|
|
17331
|
+
var target = options.target || currentConversationTarget();
|
|
17332
|
+
var targetKey = browserTargetKey(target);
|
|
17086
17333
|
cancelBrowserGuestIdleDestroy();
|
|
17087
|
-
if (!
|
|
17088
|
-
|
|
17089
|
-
return initializeBrowserGuest(createBrowserGuestElement());
|
|
17334
|
+
if (!browserGuestCreatePromises[targetKey]) {
|
|
17335
|
+
browserGuestCreatePromises[targetKey] = waitForBrowserCreationFloor().then(function() {
|
|
17336
|
+
return initializeBrowserGuest(createBrowserGuestElement(target), target);
|
|
17090
17337
|
}).catch(function(error) {
|
|
17091
|
-
|
|
17338
|
+
delete browserGuestCreatePromises[targetKey];
|
|
17092
17339
|
throw error;
|
|
17093
17340
|
});
|
|
17094
17341
|
}
|
|
17095
|
-
|
|
17342
|
+
browserGuestCreatePromise = browserGuestCreatePromises[targetKey];
|
|
17343
|
+
return browserGuestCreatePromises[targetKey].then(function(view) {
|
|
17344
|
+
if (isActiveConversationTarget(target)) setActiveBrowserGuest(view, target);
|
|
17096
17345
|
if (options.activate === true) {
|
|
17097
17346
|
var current = '';
|
|
17098
17347
|
try { current = view.getURL ? view.getURL() : ''; } catch { current = ''; }
|
|
17099
|
-
var targetUrl =
|
|
17348
|
+
var targetUrl = browserRetainedUrls[targetKey] || 'about:blank';
|
|
17100
17349
|
if (targetUrl !== 'about:blank' && (!current || current === 'about:blank') && !view._newmarkPendingUrl) {
|
|
17101
17350
|
navigateBrowserView(view, targetUrl);
|
|
17102
17351
|
}
|
|
@@ -17479,6 +17728,13 @@ window.switchConversation = function(idx) {
|
|
|
17479
17728
|
if (previousId) markConversationTracked(previousId, state.conversationTrackMs || 300000);
|
|
17480
17729
|
setActiveWorkspaceConversation(idx);
|
|
17481
17730
|
var activeId = activeConversationId();
|
|
17731
|
+
var activeBrowserTarget = currentConversationTarget(activeId);
|
|
17732
|
+
var activeBrowserView = browserGuestForTarget(activeBrowserTarget);
|
|
17733
|
+
if (activeBrowserView) setActiveBrowserGuest(activeBrowserView, activeBrowserTarget);
|
|
17734
|
+
else if (state.rightTab === 'browser') window.ensureBrowserPanel({ target: activeBrowserTarget, activate: true }).catch(function(error) {
|
|
17735
|
+
console.error('[Browser] conversation-bound guest initialization failed:', error && error.message ? error.message : String(error));
|
|
17736
|
+
});
|
|
17737
|
+
window.refreshComputerUseState(activeBrowserTarget);
|
|
17482
17738
|
state.backendQueue = backendQueueForTarget(currentConversationTarget(activeId));
|
|
17483
17739
|
holdForegroundConversation(activeId, 4500);
|
|
17484
17740
|
markConversationTracked(activeId, state.conversationTrackMs || 300000);
|
|
@@ -19680,9 +19936,10 @@ function schedulePostStartupUiRendering() {
|
|
|
19680
19936
|
cacheEls();
|
|
19681
19937
|
setupAgentWorkEvents();
|
|
19682
19938
|
if (api.onBrowserEnsureGuest) {
|
|
19683
|
-
api.onBrowserEnsureGuest(function() {
|
|
19939
|
+
api.onBrowserEnsureGuest(function(target) {
|
|
19684
19940
|
cancelBrowserGuestIdleDestroy();
|
|
19685
|
-
|
|
19941
|
+
var requestedTarget = target || currentConversationTarget();
|
|
19942
|
+
window.ensureBrowserPanel({ target: requestedTarget, activate: isActiveConversationTarget(requestedTarget) }).catch(function(error) {
|
|
19686
19943
|
console.error('[Browser] guest initialization failed:', error && error.message ? error.message : String(error));
|
|
19687
19944
|
});
|
|
19688
19945
|
});
|