newmark-agent 0.4.8 → 0.5.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/dist/conversation-utility-host.bundle.cjs +238 -29
- package/dist/core/agent.d.ts +1 -0
- package/dist/core/agent.js +102 -10
- package/dist/core/agentKernelRunner.js +1 -1
- package/dist/core/autoRouter.d.ts +1 -1
- package/dist/core/autoRouter.js +16 -7
- package/dist/core/conversationKernel.d.ts +13 -8
- package/dist/core/conversationKernel.js +111 -10
- package/dist/core/electronUtilityAgentClient.d.ts +2 -8
- package/dist/core/electronUtilityRuntimePool.d.ts +3 -15
- package/dist/core/utilityAgentProtocol.d.ts +2 -8
- package/dist/core/workEventCoalescer.d.ts +16 -0
- package/dist/core/workEventCoalescer.js +52 -0
- package/dist/core/wslAgentClient.d.ts +2 -8
- package/dist/core/wslAgentProtocol.d.ts +2 -8
- package/dist/core/wslAgentRuntimePool.d.ts +3 -15
- package/dist/llm/provider.d.ts +5 -0
- package/dist/llm/provider.js +63 -1
- package/dist/main.js +221 -175
- package/dist/preload.js +2 -0
- package/dist/server.d.ts +17 -1
- package/dist/server.js +331 -9
- package/dist/ui/index.html +295 -51
- package/dist/wsl-agent-host.bundle.cjs +238 -29
- package/package.json +10 -5
package/dist/ui/index.html
CHANGED
|
@@ -4221,6 +4221,44 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4221
4221
|
.setting-control { flex: 1; }
|
|
4222
4222
|
.settings-load-error { color: var(--notice-error-text); }
|
|
4223
4223
|
|
|
4224
|
+
.remote-touch-copy {
|
|
4225
|
+
flex: 1;
|
|
4226
|
+
min-width: 0;
|
|
4227
|
+
display: flex;
|
|
4228
|
+
flex-direction: column;
|
|
4229
|
+
gap: 2px;
|
|
4230
|
+
}
|
|
4231
|
+
.remote-touch-copy-title {
|
|
4232
|
+
color: var(--text);
|
|
4233
|
+
font-size: 11px;
|
|
4234
|
+
font-weight: 700;
|
|
4235
|
+
line-height: 1.25;
|
|
4236
|
+
}
|
|
4237
|
+
.remote-touch-copy-desc {
|
|
4238
|
+
color: var(--text-dim);
|
|
4239
|
+
font-size: 10px;
|
|
4240
|
+
font-weight: 400;
|
|
4241
|
+
line-height: 1.35;
|
|
4242
|
+
}
|
|
4243
|
+
.remote-touch-action {
|
|
4244
|
+
flex: 0 0 120px;
|
|
4245
|
+
width: 120px;
|
|
4246
|
+
min-width: 120px;
|
|
4247
|
+
min-height: 28px;
|
|
4248
|
+
box-sizing: border-box;
|
|
4249
|
+
}
|
|
4250
|
+
.remote-touch-status {
|
|
4251
|
+
background: transparent !important;
|
|
4252
|
+
border-color: currentColor;
|
|
4253
|
+
box-shadow: 0 0 0 1px currentColor, 0 0 10px color-mix(in srgb, currentColor 42%, transparent);
|
|
4254
|
+
}
|
|
4255
|
+
.remote-touch-status:hover { background: transparent !important; }
|
|
4256
|
+
.remote-touch-status.listening { color: var(--accent2); }
|
|
4257
|
+
.remote-touch-status.error,
|
|
4258
|
+
.remote-touch-status.checking { color: #ffb454; }
|
|
4259
|
+
.remote-touch-status.off { color: var(--notice-error-text); }
|
|
4260
|
+
.remote-touch-status[aria-busy="true"] { cursor: wait; }
|
|
4261
|
+
|
|
4224
4262
|
.segmented-control {
|
|
4225
4263
|
display: grid;
|
|
4226
4264
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
@@ -6118,6 +6156,8 @@ var state = {
|
|
|
6118
6156
|
agentWorkUiByConversation: {},
|
|
6119
6157
|
workRunsByTarget: {},
|
|
6120
6158
|
workRunsByBranch: {},
|
|
6159
|
+
conversationMessagesByTarget: {},
|
|
6160
|
+
pendingConversationActivations: {},
|
|
6121
6161
|
runtimeBranchPathsByTarget: {},
|
|
6122
6162
|
workRunAnchorIndexesByTarget: {},
|
|
6123
6163
|
guideMessagesByTarget: {},
|
|
@@ -6302,6 +6342,9 @@ var state = {
|
|
|
6302
6342
|
configuredAgentBackend: 'windows',
|
|
6303
6343
|
agentBackendRestartRequired: false,
|
|
6304
6344
|
remoteTouchEnabled: true,
|
|
6345
|
+
remoteTouchServer: { enabled: true, listening: false, reachable: false, state: 'checking', host: '', port: 47890, error: '', checkedAt: '', startedAt: 0 },
|
|
6346
|
+
remoteTouchStatusBusy: false,
|
|
6347
|
+
_remoteTouchStatusTimer: null,
|
|
6305
6348
|
wslAvailable: false,
|
|
6306
6349
|
wslDistros: [],
|
|
6307
6350
|
defaultTerminalShell: 'powershell',
|
|
@@ -6484,6 +6527,10 @@ var NEWMARK_I18N = {
|
|
|
6484
6527
|
'settings.runInWslDesc': 'Keeps the Windows UI and native desktop tools local while running the persistent Agent kernel, model calls, shell, and file tools inside WSL.',
|
|
6485
6528
|
'settings.remoteTouch': 'Mobile remote-touch',
|
|
6486
6529
|
'settings.remoteTouchDesc': 'Allow mobile access on the same LAN / Tailscale network',
|
|
6530
|
+
'settings.remoteTouchListening': 'Listening',
|
|
6531
|
+
'settings.remoteTouchError': 'Unreachable',
|
|
6532
|
+
'settings.remoteTouchOff': 'Off',
|
|
6533
|
+
'settings.remoteTouchChecking': 'Checking',
|
|
6487
6534
|
'settings.remoteBehavior': 'Remote behavior',
|
|
6488
6535
|
'settings.remoteConnect': 'Start connection',
|
|
6489
6536
|
'settings.remoteConnectDesc': 'Show pairing QR',
|
|
@@ -7211,6 +7258,10 @@ var NEWMARK_I18N = {
|
|
|
7211
7258
|
'settings.runInWslDesc': 'Windows UI 与桌面原生工具保留在本机,持续 Agent 内核、模型请求、Shell 和文件工具运行在 WSL。',
|
|
7212
7259
|
'settings.remoteTouch': '移动端远程触及',
|
|
7213
7260
|
'settings.remoteTouchDesc': '允许移动端通过同一内网 / Tailscale 连接',
|
|
7261
|
+
'settings.remoteTouchListening': '监听中',
|
|
7262
|
+
'settings.remoteTouchError': '不可触及',
|
|
7263
|
+
'settings.remoteTouchOff': '已关闭',
|
|
7264
|
+
'settings.remoteTouchChecking': '检测中',
|
|
7214
7265
|
'settings.remoteBehavior': '远程行为',
|
|
7215
7266
|
'settings.remoteConnect': '发起连接',
|
|
7216
7267
|
'settings.remoteConnectDesc': '显示配对二维码',
|
|
@@ -7389,7 +7440,7 @@ var NEWMARK_I18N = {
|
|
|
7389
7440
|
'model.unavailable': '不可用',
|
|
7390
7441
|
'model.no': '否',
|
|
7391
7442
|
'model.yes': '是',
|
|
7392
|
-
'model.notChecked': '
|
|
7443
|
+
'model.notChecked': '未检测',
|
|
7393
7444
|
'archive.current': '归档当前对话',
|
|
7394
7445
|
'archive.archiving': '归档中...',
|
|
7395
7446
|
'archive.empty': '暂无归档。',
|
|
@@ -9607,11 +9658,18 @@ function recordGuideUiMessage(input, target) {
|
|
|
9607
9658
|
createdAt: String(receipt.createdAt || input.createdAt || (prior && prior.createdAt) || new Date().toISOString()),
|
|
9608
9659
|
updatedAt: String(receipt.updatedAt || input.updatedAt || new Date().toISOString()),
|
|
9609
9660
|
reason: explicitReason || (status === 'deferred' || status === 'rejected' ? String(prior && prior.reason || '') : ''),
|
|
9661
|
+
awaitingAck: input.awaitingAck === true || receipt.awaitingAck === true,
|
|
9610
9662
|
attachments: normalizeConversationImageAttachments(input.attachments || receipt.attachments || (prior && prior.attachments) || [])
|
|
9611
9663
|
};
|
|
9612
9664
|
messages[clientMessageId] = record;
|
|
9613
9665
|
if (isActiveConversationTarget(target)) {
|
|
9614
9666
|
var element = findGuideMessageElement(clientMessageId);
|
|
9667
|
+
if (element) {
|
|
9668
|
+
if (prior && prior.awaitingAck && !record.awaitingAck && element.closest && element.closest('.work-run-pending-guides')) {
|
|
9669
|
+
element.remove();
|
|
9670
|
+
element = null;
|
|
9671
|
+
}
|
|
9672
|
+
}
|
|
9615
9673
|
if (element) {
|
|
9616
9674
|
applyGuideMessageMeta(element, record);
|
|
9617
9675
|
if (record.runId && !element.classList.contains('work-run-guide-message')) element.remove();
|
|
@@ -9652,6 +9710,26 @@ function renderPendingGuideMessages(target, persistedGuideIds) {
|
|
|
9652
9710
|
});
|
|
9653
9711
|
pending.forEach(function(item) {
|
|
9654
9712
|
var element = findGuideMessageElement(item.clientMessageId);
|
|
9713
|
+
if (item.awaitingAck && item.runId) {
|
|
9714
|
+
var runElement = findWorkRunElement({ runId: item.runId });
|
|
9715
|
+
var runBody = runElement && runElement.querySelector('.conversation-work-run-body');
|
|
9716
|
+
if (runBody) {
|
|
9717
|
+
var pendingStack = runBody.querySelector(':scope > .work-run-pending-guides');
|
|
9718
|
+
if (!pendingStack) {
|
|
9719
|
+
pendingStack = document.createElement('div');
|
|
9720
|
+
pendingStack.className = 'work-run-pending-guides';
|
|
9721
|
+
runBody.appendChild(pendingStack);
|
|
9722
|
+
}
|
|
9723
|
+
if (!element || !pendingStack.contains(element)) {
|
|
9724
|
+
var holder = document.createElement('div');
|
|
9725
|
+
holder.innerHTML = renderWorkRunGuideMessage(Object.assign({}, item, { status: 'accepted' }));
|
|
9726
|
+
element = holder.firstElementChild;
|
|
9727
|
+
if (element) pendingStack.appendChild(element);
|
|
9728
|
+
}
|
|
9729
|
+
if (element) applyGuideMessageMeta(element, item);
|
|
9730
|
+
return;
|
|
9731
|
+
}
|
|
9732
|
+
}
|
|
9655
9733
|
if (element) applyGuideMessageMeta(element, item);
|
|
9656
9734
|
else addMsg('user', item.content || 'Guide', 'guide', item.model || state.model, undefined, {
|
|
9657
9735
|
clientMessageId: item.clientMessageId,
|
|
@@ -9915,7 +9993,7 @@ function normalizedWorkRun(run, target) {
|
|
|
9915
9993
|
var status = String(run.status || 'running');
|
|
9916
9994
|
var live = ['running', 'stopping', 'force_restarting'].indexOf(status) >= 0;
|
|
9917
9995
|
var expanded = run.expanded === undefined
|
|
9918
|
-
? live && state.expandToolsDefault !== false
|
|
9996
|
+
? (status === 'error' || (live && state.expandToolsDefault !== false))
|
|
9919
9997
|
: !!run.expanded;
|
|
9920
9998
|
return {
|
|
9921
9999
|
runId: String(run.runId || run.id || ('run-' + Date.now() + '-' + Math.random().toString(16).slice(2))),
|
|
@@ -10583,6 +10661,7 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
10583
10661
|
'<span class="conversation-work-run-status">' + esc(String(run.status || '')) + '</span>' +
|
|
10584
10662
|
'<span class="conversation-work-run-chevron" aria-hidden="true"></span></button>' +
|
|
10585
10663
|
'<div class="conversation-work-run-body">' + renderWorkRunEvents(run, expanded) + '</div>';
|
|
10664
|
+
renderPendingGuideMessages(run.target, {});
|
|
10586
10665
|
var wrapper = element.closest ? element.closest('.work-run-message') : null;
|
|
10587
10666
|
if (wrapper) {
|
|
10588
10667
|
wrapper.classList.toggle('flow-result-only', !!(run.flow && run.flow.activityVisibility === 'result-only'));
|
|
@@ -10729,14 +10808,14 @@ function applyAgentWorkEventToRun(event) {
|
|
|
10729
10808
|
}
|
|
10730
10809
|
run.sequence = Math.max(Number(run.sequence || 0), Number(event.sequence || 0));
|
|
10731
10810
|
if (type === 'done') { run.status = 'completed'; run.endedAt = event.endedAt || event.timestampIso || new Date().toISOString(); if (run.userToggled !== true) run.expanded = false; }
|
|
10732
|
-
else if (type === 'error') { run.status = 'error'; run.endedAt = event.endedAt || event.timestampIso || new Date().toISOString(); if (run.userToggled !== true) run.expanded =
|
|
10811
|
+
else if (type === 'error') { run.status = 'error'; run.endedAt = event.endedAt || event.timestampIso || new Date().toISOString(); if (run.userToggled !== true) run.expanded = true; }
|
|
10733
10812
|
else if (type === 'interrupted') { run.status = 'interrupted'; run.endedAt = event.endedAt || event.timestampIso || new Date().toISOString(); if (run.userToggled !== true) run.expanded = false; }
|
|
10734
10813
|
else if (type === 'force_interrupted') { run.status = 'force_interrupted'; run.endedAt = event.endedAt || event.timestampIso || new Date().toISOString(); if (run.userToggled !== true) run.expanded = false; }
|
|
10735
10814
|
else if (event.status && type.indexOf('guide') !== 0) {
|
|
10736
10815
|
run.status = String(event.status);
|
|
10737
10816
|
if (['completed', 'interrupted', 'force_interrupted', 'error'].indexOf(run.status) >= 0) {
|
|
10738
10817
|
run.endedAt = event.endedAt || event.timestampIso || new Date().toISOString();
|
|
10739
|
-
if (run.userToggled !== true) run.expanded =
|
|
10818
|
+
if (run.userToggled !== true) run.expanded = run.status === 'error';
|
|
10740
10819
|
}
|
|
10741
10820
|
}
|
|
10742
10821
|
if (isActiveConversationTarget(target) && (!branchIds.viewed || !eventBranchId || branchIds.viewed === eventBranchId)) {
|
|
@@ -11017,14 +11096,58 @@ function renderPersistedToolMessage(message) {
|
|
|
11017
11096
|
}, resultPrefix.test(content) || /\bcompleted\.?$/i.test(content) ? 'completed' : '');
|
|
11018
11097
|
}
|
|
11019
11098
|
|
|
11020
|
-
function
|
|
11021
|
-
|
|
11099
|
+
function conversationMessageCache(target) {
|
|
11100
|
+
var scopedTarget = target || currentConversationTarget();
|
|
11101
|
+
var key = runtimeKeyFor(scopedTarget.workspaceId, scopedTarget.conversationId);
|
|
11102
|
+
if (!state.conversationMessagesByTarget) state.conversationMessagesByTarget = {};
|
|
11103
|
+
return Array.isArray(state.conversationMessagesByTarget[key])
|
|
11104
|
+
? state.conversationMessagesByTarget[key].map(function(message) { return Object.assign({}, message); })
|
|
11105
|
+
: [];
|
|
11106
|
+
}
|
|
11107
|
+
|
|
11108
|
+
function cacheConversationMessages(messages, target) {
|
|
11109
|
+
var scopedTarget = target || currentConversationTarget();
|
|
11110
|
+
var key = runtimeKeyFor(scopedTarget.workspaceId, scopedTarget.conversationId);
|
|
11111
|
+
var cloned = Array.isArray(messages) ? messages.map(function(message) { return Object.assign({}, message); }) : [];
|
|
11112
|
+
if (!state.conversationMessagesByTarget) state.conversationMessagesByTarget = {};
|
|
11113
|
+
state.conversationMessagesByTarget[key] = cloned;
|
|
11114
|
+
return cloned.map(function(message) { return Object.assign({}, message); });
|
|
11115
|
+
}
|
|
11116
|
+
|
|
11117
|
+
function conversationHasReadableHistory(target) {
|
|
11118
|
+
return conversationMessageCache(target).length > 0 || workRunsForTarget(target).length > 0;
|
|
11119
|
+
}
|
|
11120
|
+
|
|
11121
|
+
function snapshotHasReadableConversationHistory(snapshot) {
|
|
11122
|
+
return !!(snapshot && ((Array.isArray(snapshot.chatMessages) && snapshot.chatMessages.length > 0)
|
|
11123
|
+
|| (Array.isArray(snapshot.workRuns) && snapshot.workRuns.length > 0)));
|
|
11124
|
+
}
|
|
11125
|
+
|
|
11126
|
+
function renderConversationHistoryFirst(target) {
|
|
11127
|
+
if (!isActiveConversationTarget(target)) return false;
|
|
11128
|
+
var cachedMessages = conversationMessageCache(target);
|
|
11129
|
+
if (cachedMessages.length > 0) {
|
|
11130
|
+
renderChatMessages(cachedMessages, target);
|
|
11131
|
+
renderLoadEarlierButton();
|
|
11132
|
+
return true;
|
|
11133
|
+
}
|
|
11134
|
+
if (workRunsForTarget(target).length > 0) {
|
|
11135
|
+
renderChatMessages([], target);
|
|
11136
|
+
renderLoadEarlierButton();
|
|
11137
|
+
return true;
|
|
11138
|
+
}
|
|
11139
|
+
return false;
|
|
11140
|
+
}
|
|
11141
|
+
|
|
11142
|
+
function renderChatMessages(messages, target) {
|
|
11143
|
+
var renderTarget = target || currentConversationTarget(activeConversationId());
|
|
11144
|
+
var normalizedMessages = cacheConversationMessages(messages, renderTarget);
|
|
11145
|
+
state.renderedChatMessages = normalizedMessages;
|
|
11022
11146
|
if (!els['chat-area']) return;
|
|
11023
11147
|
var chatWasAtBottom = shouldAutoScroll(els['chat-area']);
|
|
11024
11148
|
els['chat-area'].innerHTML = '';
|
|
11025
11149
|
if (chatWasAtBottom) _chatShouldAutoScroll = true;
|
|
11026
|
-
var renderConversationId =
|
|
11027
|
-
var renderTarget = currentConversationTarget(renderConversationId);
|
|
11150
|
+
var renderConversationId = renderTarget.conversationId;
|
|
11028
11151
|
var guideMessages = guideMessagesForTarget(renderTarget);
|
|
11029
11152
|
var persistedGuideIds = {};
|
|
11030
11153
|
var persistedRuns = workRunsForTarget(renderTarget).slice();
|
|
@@ -11050,7 +11173,7 @@ function renderChatMessages(messages) {
|
|
|
11050
11173
|
persistedRunIndexes[runId] = runIndex;
|
|
11051
11174
|
}
|
|
11052
11175
|
});
|
|
11053
|
-
|
|
11176
|
+
normalizedMessages.forEach(function(message, messageIndex) {
|
|
11054
11177
|
var runId = String(message && message.runId || '');
|
|
11055
11178
|
var role = String(message && message.role || '');
|
|
11056
11179
|
var indexedGuideId = String(message && message.clientMessageId || '');
|
|
@@ -11095,7 +11218,7 @@ function renderChatMessages(messages) {
|
|
|
11095
11218
|
var pendingWorkReview = conversationWorkUiState(renderConversationId).pendingWorkReview;
|
|
11096
11219
|
resetConversationWorkUi(renderConversationId, false);
|
|
11097
11220
|
conversationWorkUiState(renderConversationId).pendingWorkReview = pendingWorkReview;
|
|
11098
|
-
if (!
|
|
11221
|
+
if (!normalizedMessages.length) {
|
|
11099
11222
|
persistedRuns.forEach(function(run) { renderOwnedWorkRun(run, true); });
|
|
11100
11223
|
var pendingGuideCount = renderPendingGuideMessages(renderTarget, persistedGuideIds);
|
|
11101
11224
|
if (!persistedRuns.length && !pendingGuideCount) {
|
|
@@ -11533,8 +11656,19 @@ function loadActiveConversationMessages(conversationId) {
|
|
|
11533
11656
|
requestedConversationId === String(activeConversationId() || 'default') &&
|
|
11534
11657
|
responseConversationId === requestedConversationId;
|
|
11535
11658
|
if (!responseIsCurrent) return;
|
|
11536
|
-
applyConversationSnapshot(s, requestedConversationId);
|
|
11537
|
-
}).catch(function(){
|
|
11659
|
+
applyConversationSnapshot(s, requestedConversationId, { preserveReadableHistory: true, allowEmptyHistory: true });
|
|
11660
|
+
}).catch(function(error) {
|
|
11661
|
+
var stillCurrent = loadGeneration === state.conversationLoadGeneration &&
|
|
11662
|
+
requestedWorkspaceKey === currentWorkspaceKey() &&
|
|
11663
|
+
requestedConversationId === String(activeConversationId() || 'default');
|
|
11664
|
+
if (!stillCurrent) return;
|
|
11665
|
+
showUiNotice(
|
|
11666
|
+
currentLang() === 'zh' ? '历史记录增强加载失败,已保留当前内容。' : 'History enhancement failed; current content was preserved.',
|
|
11667
|
+
'error',
|
|
11668
|
+
'conversation-history-load-' + requestedWorkspaceKey + '-' + requestedConversationId
|
|
11669
|
+
);
|
|
11670
|
+
console.error('[Conversation] history enhancement failed:', error && error.message ? error.message : String(error));
|
|
11671
|
+
});
|
|
11538
11672
|
}
|
|
11539
11673
|
|
|
11540
11674
|
function hydrateConversationBranchState(snapshot) {
|
|
@@ -11551,7 +11685,7 @@ function hydrateConversationBranchState(snapshot) {
|
|
|
11551
11685
|
rememberRuntimeConversationBranchPath(currentConversationTarget(s.conversationId || activeConversationId()));
|
|
11552
11686
|
}
|
|
11553
11687
|
|
|
11554
|
-
function applyConversationSnapshot(s, requestedConversationId) {
|
|
11688
|
+
function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
11555
11689
|
if (!s) return;
|
|
11556
11690
|
if (s.computerUse) {
|
|
11557
11691
|
var computerTarget = currentConversationTarget(s.conversationId || requestedConversationId || activeConversationId());
|
|
@@ -11567,18 +11701,21 @@ function applyConversationSnapshot(s, requestedConversationId) {
|
|
|
11567
11701
|
workspaceId: String((s && (s.workspaceId || (s.target && s.target.workspaceId))) || runtimeWorkspaceId('')),
|
|
11568
11702
|
conversationId: String((s && (s.conversationId || (s.target && s.target.conversationId))) || requestedConversationId)
|
|
11569
11703
|
};
|
|
11704
|
+
var preserveReadableHistory = !!(options && options.preserveReadableHistory
|
|
11705
|
+
&& !snapshotHasReadableConversationHistory(s)
|
|
11706
|
+
&& (!options.allowEmptyHistory || conversationHasReadableHistory(snapshotTarget)));
|
|
11570
11707
|
if (s && s.runtimeKey) registerRuntimeKey(snapshotTarget, s.runtimeKey);
|
|
11571
11708
|
hydrateConversationBranchState(s);
|
|
11572
11709
|
rebindQueueToRuntimeBranch(snapshotTarget);
|
|
11573
|
-
if (s && Array.isArray(s.workRuns)) {
|
|
11710
|
+
if (s && Array.isArray(s.workRuns) && !preserveReadableHistory) {
|
|
11574
11711
|
var viewedBranchIdForSync = Array.isArray(s.viewedBranchNodePath) && s.viewedBranchNodePath.length
|
|
11575
11712
|
? String(s.viewedBranchNodePath[s.viewedBranchNodePath.length - 1])
|
|
11576
11713
|
: '';
|
|
11577
11714
|
syncWorkRunsSnapshot(s.workRuns, snapshotTarget, viewedBranchIdForSync || undefined);
|
|
11578
11715
|
}
|
|
11579
11716
|
applyAutoRouteRatingState(s);
|
|
11580
|
-
if (s && s.chatMessages) {
|
|
11581
|
-
renderChatMessages(s.chatMessages);
|
|
11717
|
+
if (s && Array.isArray(s.chatMessages) && !preserveReadableHistory) {
|
|
11718
|
+
renderChatMessages(s.chatMessages, snapshotTarget);
|
|
11582
11719
|
renderLoadEarlierButton();
|
|
11583
11720
|
}
|
|
11584
11721
|
if (s && s.conversationId) {
|
|
@@ -13464,18 +13601,10 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13464
13601
|
attachments: optimisticAttachments,
|
|
13465
13602
|
model: state.model,
|
|
13466
13603
|
createdAt: envelope.createdAt,
|
|
13604
|
+
awaitingAck: true,
|
|
13467
13605
|
allowStatusReset: retryingUnacknowledgedGuide
|
|
13468
13606
|
}, lockedTarget);
|
|
13469
|
-
|
|
13470
|
-
id: 'guide-' + clientMessageId + '-accepted',
|
|
13471
|
-
type: 'guide_accepted',
|
|
13472
|
-
content: displayText,
|
|
13473
|
-
workspaceId: lockedTarget.workspaceId,
|
|
13474
|
-
conversationId: lockedTarget.conversationId,
|
|
13475
|
-
runId: envelope.runId,
|
|
13476
|
-
clientMessageId: clientMessageId,
|
|
13477
|
-
guide: optimisticGuide
|
|
13478
|
-
});
|
|
13607
|
+
renderPendingGuideMessages(lockedTarget, {});
|
|
13479
13608
|
var enqueue = api.enqueueGuide ? api.enqueueGuide(envelope) : api.sendMessage(requestMessage, lockedConversationId);
|
|
13480
13609
|
return Promise.resolve(enqueue).then(function(receipt) {
|
|
13481
13610
|
if (!receipt || typeof receipt !== 'object' || !String(receipt.status || '').trim()) {
|
|
@@ -13631,6 +13760,8 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
13631
13760
|
var responseMsg = null;
|
|
13632
13761
|
var sendFailure = '';
|
|
13633
13762
|
try {
|
|
13763
|
+
var activationBeforeSend = pendingConversationActivation(lockedTarget);
|
|
13764
|
+
if (activationBeforeSend) await activationBeforeSend;
|
|
13634
13765
|
var sendPromise = api.sendMessage(requestMessage, lockedTarget);
|
|
13635
13766
|
if (requestedMode === 'goal') activateSubmittedGoal(opts.goalObjective || rawText);
|
|
13636
13767
|
var r = await sendPromise;
|
|
@@ -15464,7 +15595,7 @@ function formatModelStatus(status) {
|
|
|
15464
15595
|
if (raw === 'available' || raw === 'verified') return t('model.available');
|
|
15465
15596
|
if (raw === 'degraded') return t('model.degraded');
|
|
15466
15597
|
if (raw === 'unavailable' || raw === 'failed' || raw === 'error') return t('model.unavailable');
|
|
15467
|
-
if (raw === 'not checked' || raw === 'not_checked' || raw === 'pending') return t('model.notChecked');
|
|
15598
|
+
if (raw === 'not checked' || raw === 'not_checked' || raw === 'unvalidated' || raw === 'pending') return t('model.notChecked');
|
|
15468
15599
|
return status || t('model.notChecked');
|
|
15469
15600
|
}
|
|
15470
15601
|
|
|
@@ -15476,6 +15607,7 @@ function effectiveUiModelStatus(modelEntry) {
|
|
|
15476
15607
|
var textEvidence = capabilities.text === true || capabilities.text_input === true || capabilities.text_output === true
|
|
15477
15608
|
|| !!(modelEntry.evaluation && (modelEntry.evaluation.text_input || modelEntry.evaluation.text_output));
|
|
15478
15609
|
if (rawValidation === 'auth_error' || rawValidation === 'invalid_config') return rawValidation;
|
|
15610
|
+
if (String((modelEntry.validation && modelEntry.validation.level) || '').toLowerCase() === 'discovered') return 'unvalidated';
|
|
15479
15611
|
if (textEvidence && rawValidation === 'unavailable') return 'degraded';
|
|
15480
15612
|
return rawValidation || rawEvaluation;
|
|
15481
15613
|
}
|
|
@@ -15539,15 +15671,15 @@ function renderGeneralSettings() {
|
|
|
15539
15671
|
'<div class="setting-desc" id="agent-wsl-status">' + esc(state.agentBackend && state.agentBackend.connected ? t('settings.wslConnected') : t('settings.wslDisconnected')) + '</div></div></div>' : '';
|
|
15540
15672
|
return wslHtml + '<div class="setting-subsection">' +
|
|
15541
15673
|
'<div class="setting-subsection-title">' + esc(t('settings.remoteBehavior')) + '</div>' +
|
|
15542
|
-
'<div class="setting-row">' +
|
|
15543
|
-
'<
|
|
15544
|
-
'<
|
|
15545
|
-
'<
|
|
15546
|
-
'<span class="setting-desc" style="flex:1;min-width:140px;">' + esc(t('settings.remoteTouchDesc')) + '</span>' +
|
|
15674
|
+
'<div class="setting-row remote-touch-row">' +
|
|
15675
|
+
'<button type="button" id="remote-touch-status-button" class="sec-btn remote-touch-action remote-touch-status ' + esc(remoteTouchStatusClass()) + '" aria-pressed="' + (state.remoteTouchEnabled ? 'true' : 'false') + '" aria-busy="' + (state.remoteTouchStatusBusy ? 'true' : 'false') + '" onclick="window.setRemoteTouchEnabled(!state.remoteTouchEnabled)" title="' + esc(remoteTouchStatusTitle()) + '"' + (state.remoteTouchStatusBusy ? ' disabled' : '') + '>' + esc(remoteTouchStatusText()) + '</button>' +
|
|
15676
|
+
'<div class="remote-touch-copy"><div class="remote-touch-copy-title">' + esc(t('settings.remoteTouch')) + '</div>' +
|
|
15677
|
+
'<div class="remote-touch-copy-desc">' + esc(t('settings.remoteTouchDesc')) + '</div></div>' +
|
|
15547
15678
|
'</div>' +
|
|
15548
|
-
'<div class="setting-row" style="border-bottom:none;">' +
|
|
15549
|
-
'<button type="button" class="sec-btn primary" onclick="window.showMobilePairing()">' + esc(t('settings.remoteConnect')) + '</button>' +
|
|
15550
|
-
'<
|
|
15679
|
+
'<div class="setting-row remote-touch-row" style="border-bottom:none;">' +
|
|
15680
|
+
'<button type="button" id="remote-touch-connect-button" class="sec-btn primary remote-touch-action" onclick="window.showMobilePairing()">' + esc(t('settings.remoteConnect')) + '</button>' +
|
|
15681
|
+
'<div class="remote-touch-copy"><div class="remote-touch-copy-title">' + esc(t('mobile.pair')) + '</div>' +
|
|
15682
|
+
'<div class="remote-touch-copy-desc">' + esc(t('settings.remoteConnectDesc')) + '</div></div>' +
|
|
15551
15683
|
'</div>' +
|
|
15552
15684
|
'</div>' +
|
|
15553
15685
|
'<div class="setting-row">' +
|
|
@@ -15656,7 +15788,7 @@ function renderModelSettings() {
|
|
|
15656
15788
|
var normalizedStatus = String(statusRaw || '').toLowerCase();
|
|
15657
15789
|
var statusClass = normalizedStatus === 'available' || normalizedStatus === 'verified' || normalizedStatus === 'degraded'
|
|
15658
15790
|
? 'model-eval-ok'
|
|
15659
|
-
: (normalizedStatus === 'not checked' ? 'model-eval-pending' : 'model-eval-bad');
|
|
15791
|
+
: (normalizedStatus === 'not checked' || normalizedStatus === 'unvalidated' ? 'model-eval-pending' : 'model-eval-bad');
|
|
15660
15792
|
var details = '';
|
|
15661
15793
|
if (typeof modelEntry !== 'string') {
|
|
15662
15794
|
var tierMapCount = modelEntry.thinking_tier_map && typeof modelEntry.thinking_tier_map === 'object' ? Object.keys(modelEntry.thinking_tier_map).length : 0;
|
|
@@ -16225,13 +16357,96 @@ window.setAgentBackendMode = async function(mode) {
|
|
|
16225
16357
|
return true;
|
|
16226
16358
|
};
|
|
16227
16359
|
|
|
16360
|
+
function remoteTouchStatusClass() {
|
|
16361
|
+
if (state.remoteTouchStatusBusy) return 'checking';
|
|
16362
|
+
var status = state.remoteTouchServer || {};
|
|
16363
|
+
if (!state.remoteTouchEnabled || status.state === 'off') return 'off';
|
|
16364
|
+
return status.state === 'listening' && status.listening && status.reachable ? 'listening' : 'error';
|
|
16365
|
+
}
|
|
16366
|
+
|
|
16367
|
+
function remoteTouchStatusText() {
|
|
16368
|
+
var statusClass = remoteTouchStatusClass();
|
|
16369
|
+
if (statusClass === 'checking') return t('settings.remoteTouchChecking');
|
|
16370
|
+
if (statusClass === 'listening') return t('settings.remoteTouchListening');
|
|
16371
|
+
if (statusClass === 'error') return t('settings.remoteTouchError');
|
|
16372
|
+
return t('settings.remoteTouchOff');
|
|
16373
|
+
}
|
|
16374
|
+
|
|
16375
|
+
function remoteTouchStatusTitle() {
|
|
16376
|
+
var status = state.remoteTouchServer || {};
|
|
16377
|
+
var endpoint = status.host ? status.host + ':' + (status.port || 47890) : '';
|
|
16378
|
+
return [remoteTouchStatusText(), endpoint, status.error || ''].filter(Boolean).join(' · ');
|
|
16379
|
+
}
|
|
16380
|
+
|
|
16381
|
+
window.updateRemoteTouchStatusButton = function() {
|
|
16382
|
+
var button = document.getElementById('remote-touch-status-button');
|
|
16383
|
+
if (!button) return;
|
|
16384
|
+
button.className = 'sec-btn remote-touch-action remote-touch-status ' + remoteTouchStatusClass();
|
|
16385
|
+
button.textContent = remoteTouchStatusText();
|
|
16386
|
+
button.setAttribute('aria-pressed', state.remoteTouchEnabled ? 'true' : 'false');
|
|
16387
|
+
button.setAttribute('aria-busy', state.remoteTouchStatusBusy ? 'true' : 'false');
|
|
16388
|
+
button.title = remoteTouchStatusTitle();
|
|
16389
|
+
button.disabled = !!state.remoteTouchStatusBusy;
|
|
16390
|
+
};
|
|
16391
|
+
|
|
16392
|
+
window.refreshRemoteTouchServerStatus = async function() {
|
|
16393
|
+
if (!api.mobileServerStatus) return state.remoteTouchServer;
|
|
16394
|
+
try {
|
|
16395
|
+
var result = await api.mobileServerStatus();
|
|
16396
|
+
if (result && result.state) {
|
|
16397
|
+
state.remoteTouchServer = result;
|
|
16398
|
+
state.remoteTouchEnabled = !!result.enabled;
|
|
16399
|
+
}
|
|
16400
|
+
} catch (error) {
|
|
16401
|
+
state.remoteTouchServer = {
|
|
16402
|
+
enabled: state.remoteTouchEnabled,
|
|
16403
|
+
listening: false,
|
|
16404
|
+
reachable: false,
|
|
16405
|
+
state: state.remoteTouchEnabled ? 'error' : 'off',
|
|
16406
|
+
host: '',
|
|
16407
|
+
port: 47890,
|
|
16408
|
+
error: error && error.message ? error.message : String(error || ''),
|
|
16409
|
+
checkedAt: new Date().toISOString(),
|
|
16410
|
+
startedAt: 0
|
|
16411
|
+
};
|
|
16412
|
+
}
|
|
16413
|
+
window.updateRemoteTouchStatusButton();
|
|
16414
|
+
return state.remoteTouchServer;
|
|
16415
|
+
};
|
|
16416
|
+
|
|
16417
|
+
window.startRemoteTouchStatusPolling = function() {
|
|
16418
|
+
if (state._remoteTouchStatusTimer) clearInterval(state._remoteTouchStatusTimer);
|
|
16419
|
+
window.refreshRemoteTouchServerStatus().catch(function(){});
|
|
16420
|
+
state._remoteTouchStatusTimer = setInterval(function() {
|
|
16421
|
+
window.refreshRemoteTouchServerStatus().catch(function(){});
|
|
16422
|
+
}, 5000);
|
|
16423
|
+
};
|
|
16424
|
+
|
|
16228
16425
|
window.setRemoteTouchEnabled = async function(value) {
|
|
16229
16426
|
var next = !!value;
|
|
16230
16427
|
state.remoteTouchEnabled = next;
|
|
16231
|
-
|
|
16232
|
-
|
|
16428
|
+
state.remoteTouchStatusBusy = true;
|
|
16429
|
+
window.updateRemoteTouchStatusButton();
|
|
16430
|
+
try {
|
|
16431
|
+
var result = api.setRemoteTouchEnabled
|
|
16432
|
+
? await api.setRemoteTouchEnabled(next)
|
|
16433
|
+
: (api.saveSetting ? await api.saveSetting('remote', 'touch_enabled', next) : null);
|
|
16434
|
+
if (result && result.state) state.remoteTouchServer = result;
|
|
16435
|
+
} catch (error) {
|
|
16436
|
+
state.remoteTouchServer = {
|
|
16437
|
+
enabled: next,
|
|
16438
|
+
listening: false,
|
|
16439
|
+
reachable: false,
|
|
16440
|
+
state: next ? 'error' : 'off',
|
|
16441
|
+
host: '', port: 47890,
|
|
16442
|
+
error: error && error.message ? error.message : String(error || ''),
|
|
16443
|
+
checkedAt: new Date().toISOString(), startedAt: 0
|
|
16444
|
+
};
|
|
16445
|
+
} finally {
|
|
16446
|
+
state.remoteTouchStatusBusy = false;
|
|
16233
16447
|
}
|
|
16234
|
-
window.
|
|
16448
|
+
await window.refreshRemoteTouchServerStatus();
|
|
16449
|
+
window.updateRemoteTouchStatusButton();
|
|
16235
16450
|
};
|
|
16236
16451
|
|
|
16237
16452
|
window.setAgentWslDistro = async function(value) {
|
|
@@ -16477,6 +16692,7 @@ window.saveModelEdit = function(oldProvIdx, modelIdx) {
|
|
|
16477
16692
|
if (typeof previous === 'string') previous = { name: previous, display: previous };
|
|
16478
16693
|
var tierMap = window.parseThinkingTierMap(tierMapEl ? tierMapEl.value : '');
|
|
16479
16694
|
var updated = Object.assign({}, previous, {
|
|
16695
|
+
_previous_name: previous.name || name,
|
|
16480
16696
|
name: name,
|
|
16481
16697
|
display: name,
|
|
16482
16698
|
description: descEl ? descEl.value : '',
|
|
@@ -19214,6 +19430,12 @@ function holdForegroundConversation(id, ms) {
|
|
|
19214
19430
|
state.foregroundConversationHoldUntil = Date.now() + (ms || 4500);
|
|
19215
19431
|
}
|
|
19216
19432
|
|
|
19433
|
+
function pendingConversationActivation(target) {
|
|
19434
|
+
var scopedTarget = target || currentConversationTarget();
|
|
19435
|
+
var key = runtimeKeyFor(scopedTarget.workspaceId, scopedTarget.conversationId);
|
|
19436
|
+
return state.pendingConversationActivations && state.pendingConversationActivations[key];
|
|
19437
|
+
}
|
|
19438
|
+
|
|
19217
19439
|
function applyBackendConversations(items, activeId, workspaceId) {
|
|
19218
19440
|
if (!state.currentWorkspace || !items || !items.length) return;
|
|
19219
19441
|
var key = currentWorkspaceKey(workspaceId);
|
|
@@ -19275,14 +19497,24 @@ function syncBackendConversation() {
|
|
|
19275
19497
|
}
|
|
19276
19498
|
if (conv && api.ensureConversation) {
|
|
19277
19499
|
var requestedTarget = currentConversationTarget(conv.id);
|
|
19500
|
+
state.activeBackendConversationId = conv.id;
|
|
19501
|
+
var historyPromise = loadActiveConversationMessages(conv.id);
|
|
19278
19502
|
var activate = api.activateConversation ? api.activateConversation(requestedTarget) : api.ensureConversation(requestedTarget);
|
|
19279
|
-
|
|
19503
|
+
var activationPromise = activate.then(function(s) {
|
|
19280
19504
|
var id = (s && s.conversationId) || conv.id;
|
|
19281
19505
|
if (!requestIsCurrent() || String(id) !== requestedConversationId) return;
|
|
19282
19506
|
state.activeBackendConversationId = id;
|
|
19283
|
-
applyConversationSnapshot(s, id);
|
|
19284
|
-
|
|
19285
|
-
|
|
19507
|
+
applyConversationSnapshot(s, id, { preserveReadableHistory: true });
|
|
19508
|
+
}).catch(function(error) {
|
|
19509
|
+
if (!requestIsCurrent()) return;
|
|
19510
|
+
showUiNotice(
|
|
19511
|
+
currentLang() === 'zh' ? '对话运行时激活失败,历史记录仍可查看。' : 'Conversation runtime activation failed; history remains available.',
|
|
19512
|
+
'error',
|
|
19513
|
+
'conversation-activation-' + requestedWorkspaceKey + '-' + requestedConversationId
|
|
19514
|
+
);
|
|
19515
|
+
console.error('[Conversation] runtime activation failed:', error && error.message ? error.message : String(error));
|
|
19516
|
+
});
|
|
19517
|
+
return Promise.allSettled([historyPromise, activationPromise]);
|
|
19286
19518
|
}
|
|
19287
19519
|
if (conv) {
|
|
19288
19520
|
if (!requestIsCurrent()) return Promise.resolve();
|
|
@@ -19498,10 +19730,11 @@ window.newConversation = function(workspaceReference, branchCommunication) {
|
|
|
19498
19730
|
els['chat-area'].innerHTML = '';
|
|
19499
19731
|
var target = currentConversationTarget(id);
|
|
19500
19732
|
var activation = api.activateConversation ? api.activateConversation(target) : (api.ensureConversation ? api.ensureConversation(target) : Promise.resolve(null));
|
|
19501
|
-
|
|
19733
|
+
var activationKey = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
19734
|
+
var activationReady = activation.then(function(s) {
|
|
19502
19735
|
if (createdWorkspaceKey !== currentWorkspaceKey() || id !== String(activeConversationId() || 'default')) return;
|
|
19503
19736
|
state.activeBackendConversationId = String((s && s.conversationId) || id);
|
|
19504
|
-
if (s) applyConversationSnapshot(s, id);
|
|
19737
|
+
if (s) applyConversationSnapshot(s, id, { preserveReadableHistory: true });
|
|
19505
19738
|
if (branchCommunication && api.setConversationBranchCommunication) {
|
|
19506
19739
|
api.setConversationBranchCommunication(target, true).then(function() {
|
|
19507
19740
|
var convsNow = currentWorkspaceConversations();
|
|
@@ -19511,16 +19744,23 @@ window.newConversation = function(workspaceReference, branchCommunication) {
|
|
|
19511
19744
|
}
|
|
19512
19745
|
}).then(function() {
|
|
19513
19746
|
if (createdWorkspaceKey !== currentWorkspaceKey() || id !== String(activeConversationId() || 'default')) return;
|
|
19514
|
-
|
|
19515
|
-
|
|
19516
|
-
|
|
19517
|
-
|
|
19747
|
+
if (!conversationHasReadableHistory(target)) {
|
|
19748
|
+
renderChatMessages([], target);
|
|
19749
|
+
addMsg('system', '[System] ' + t('workspace.newConversation') + ': ' + summary, 'system', '');
|
|
19750
|
+
}
|
|
19518
19751
|
}).catch(function(error) {
|
|
19519
19752
|
if (createdWorkspaceKey === currentWorkspaceKey() && id === String(activeConversationId() || 'default')) {
|
|
19520
19753
|
showUiNotice((currentLang() === 'zh' ? '新对话持久化失败:' : 'Failed to persist the new conversation: ') + (error && error.message ? error.message : String(error)), 'error', 'new-conversation-activation-' + id);
|
|
19521
19754
|
}
|
|
19522
19755
|
throw error;
|
|
19756
|
+
}).finally(function() {
|
|
19757
|
+
if (state.pendingConversationActivations && state.pendingConversationActivations[activationKey] === activationReady) {
|
|
19758
|
+
delete state.pendingConversationActivations[activationKey];
|
|
19759
|
+
}
|
|
19523
19760
|
});
|
|
19761
|
+
if (!state.pendingConversationActivations) state.pendingConversationActivations = {};
|
|
19762
|
+
state.pendingConversationActivations[activationKey] = activationReady;
|
|
19763
|
+
return activationReady;
|
|
19524
19764
|
};
|
|
19525
19765
|
|
|
19526
19766
|
window.showNewConversationPage = function() {
|
|
@@ -19590,7 +19830,7 @@ window.switchConversation = function(idx) {
|
|
|
19590
19830
|
renderConversations();
|
|
19591
19831
|
var switchSeq = (state._conversationSwitchSeq || 0) + 1;
|
|
19592
19832
|
state._conversationSwitchSeq = switchSeq;
|
|
19593
|
-
|
|
19833
|
+
var restoredReadableHistory = renderConversationHistoryFirst(activeBrowserTarget);
|
|
19594
19834
|
// The Flow takeover bubble is conversation-local. Immediately reconcile it to
|
|
19595
19835
|
// the newly active conversation: if the incoming conversation owns a Flow,
|
|
19596
19836
|
// show it; otherwise hide the bubble instantly so no takeover ever leaks
|
|
@@ -19599,7 +19839,9 @@ window.switchConversation = function(idx) {
|
|
|
19599
19839
|
if (state._conversationLoadingMsgEl && state._conversationLoadingMsgEl.parentNode) {
|
|
19600
19840
|
state._conversationLoadingMsgEl.parentNode.removeChild(state._conversationLoadingMsgEl);
|
|
19601
19841
|
}
|
|
19602
|
-
state._conversationLoadingMsgEl =
|
|
19842
|
+
state._conversationLoadingMsgEl = restoredReadableHistory
|
|
19843
|
+
? null
|
|
19844
|
+
: addMsg('workflow running', t('conversation.loadingIsolated'), 'system', state.model);
|
|
19603
19845
|
syncBackendConversation().then(function() {
|
|
19604
19846
|
// Only the latest switch may touch the UI: an earlier switch's sync
|
|
19605
19847
|
// resolution must not restore drafts into or re-render the now-current
|
|
@@ -23088,6 +23330,7 @@ function schedulePostStartupUiRendering() {
|
|
|
23088
23330
|
}
|
|
23089
23331
|
|
|
23090
23332
|
updateMarqueeFromConfig();
|
|
23333
|
+
window.startRemoteTouchStatusPolling();
|
|
23091
23334
|
|
|
23092
23335
|
// Populate selects
|
|
23093
23336
|
if (els['mode-select']) els['mode-select'].value = state.mode;
|
|
@@ -23351,6 +23594,7 @@ function schedulePostStartupUiRendering() {
|
|
|
23351
23594
|
|
|
23352
23595
|
window.addEventListener('beforeunload', function() {
|
|
23353
23596
|
if (state._postStartupUiRendering && state._postStartupUiRendering.cancel) state._postStartupUiRendering.cancel();
|
|
23597
|
+
if (state._remoteTouchStatusTimer) clearInterval(state._remoteTouchStatusTimer);
|
|
23354
23598
|
cancelBrowserGuestIdleDestroy();
|
|
23355
23599
|
flushPendingConversationDraftPersists();
|
|
23356
23600
|
}, { once: true });
|