fix: sessionId durchreichen bei target_tab/sync/risk_request
Ereignisse ohne sessionId passierten den Panel-Filter ungehindert, wodurch ein frisch geoeffneter Chat den Arbeits-Tab oder Verlauf eines parallel laufenden Chats angezeigt bekam.
This commit is contained in:
+39
-22
@@ -386,15 +386,19 @@ function reportToolDone(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Sendet den aktuellen Target-Tab ans Panel und überträgt den Glow auf den neuen Tab. */
|
/** Sendet den aktuellen Target-Tab ans Panel und überträgt den Glow auf den neuen Tab. */
|
||||||
async function notifyTargetTab(): Promise<void> {
|
async function notifyTargetTab(sessionId?: string): Promise<void> {
|
||||||
const targetTabId = getTargetTabId();
|
// sessionId mitstempeln: ohne sie zeigte ein frisch geöffneter Chat den
|
||||||
|
// Arbeits-Tab eines ANDEREN, parallel laufenden Chats an — Ereignisse ohne
|
||||||
|
// sessionId passieren den Filter im Panel ungehindert.
|
||||||
|
const sid = sessionId ?? activeSessionId;
|
||||||
|
const targetTabId = getTargetTabId(sid);
|
||||||
if (targetTabId === null) {
|
if (targetTabId === null) {
|
||||||
sendToPanel({ type: 'target_tab', tabId: null });
|
sendToPanel({ type: 'target_tab', tabId: null, sessionId: sid });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const tab = await chrome.tabs.get(targetTabId);
|
const tab = await chrome.tabs.get(targetTabId);
|
||||||
sendToPanel({ type: 'target_tab', tabId: targetTabId, title: tab.title, url: tab.url });
|
sendToPanel({ type: 'target_tab', tabId: targetTabId, title: tab.title, url: tab.url, sessionId: sid });
|
||||||
// Nur während eines echten Laufs anzeigen.
|
// Nur während eines echten Laufs anzeigen.
|
||||||
if (agentRunActive) notifyGlow(true);
|
if (agentRunActive) notifyGlow(true);
|
||||||
// Tab in den Vordergrund bringen wenn focusWorkingTab aktiv
|
// Tab in den Vordergrund bringen wenn focusWorkingTab aktiv
|
||||||
@@ -403,7 +407,7 @@ async function notifyTargetTab(): Promise<void> {
|
|||||||
await chrome.tabs.update(targetTabId, { active: true }).catch(() => {});
|
await chrome.tabs.update(targetTabId, { active: true }).catch(() => {});
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
sendToPanel({ type: 'target_tab', tabId: null });
|
sendToPanel({ type: 'target_tab', tabId: null, sessionId: sid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,7 +438,7 @@ async function runTool(name: string, input: Record<string, unknown>, sessionId?:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tabId = await currentTabId(sessionId);
|
const tabId = await currentTabId(sessionId);
|
||||||
const { allowed, verdict } = await gate(tabId, name, input, (v) => askUser(v, name));
|
const { allowed, verdict } = await gate(tabId, name, input, (v) => askUser(v, name, sessionId));
|
||||||
|
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
return {
|
return {
|
||||||
@@ -464,11 +468,14 @@ async function runTool(name: string, input: Record<string, unknown>, sessionId?:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Fragt den Nutzer. Ohne offenes Panel wird gehandelt — Autonomie hat Vorrang. */
|
/** Fragt den Nutzer. Ohne offenes Panel wird gehandelt — Autonomie hat Vorrang. */
|
||||||
function askUser(verdict: RiskVerdict, toolName: string): Promise<boolean> {
|
function askUser(verdict: RiskVerdict, toolName: string, sessionId?: string): Promise<boolean> {
|
||||||
if (!panelPort) return Promise.resolve(true);
|
if (!panelPort) return Promise.resolve(true);
|
||||||
|
|
||||||
const requestId = crypto.randomUUID();
|
const requestId = crypto.randomUUID();
|
||||||
sendToPanel({ type: 'risk_request', requestId, name: toolName, verdict, timeoutMs: RISK_TIMEOUT_MS });
|
sendToPanel({
|
||||||
|
type: 'risk_request', requestId, name: toolName, verdict,
|
||||||
|
timeoutMs: RISK_TIMEOUT_MS, sessionId: sessionId ?? activeSessionId,
|
||||||
|
});
|
||||||
|
|
||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@@ -505,7 +512,7 @@ async function getActiveTab(sessionId?: string): Promise<chrome.tabs.Tab> {
|
|||||||
const [candidate] = await chrome.tabs.query({ active: true, currentWindow: true });
|
const [candidate] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||||
if (candidate?.id != null && await tabOwner(candidate.id, sessionId) === 'own') {
|
if (candidate?.id != null && await tabOwner(candidate.id, sessionId) === 'own') {
|
||||||
setTargetTabId(candidate.id, sessionId);
|
setTargetTabId(candidate.id, sessionId);
|
||||||
notifyTargetTab();
|
notifyTargetTab(sessionId);
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,14 +523,14 @@ async function getActiveTab(sessionId?: string): Promise<chrome.tabs.Tab> {
|
|||||||
if (!hasOwnGroup && candidate?.id != null
|
if (!hasOwnGroup && candidate?.id != null
|
||||||
&& await tabOwner(candidate.id, sessionId) === 'user') {
|
&& await tabOwner(candidate.id, sessionId) === 'user') {
|
||||||
setTargetTabId(candidate.id, sessionId);
|
setTargetTabId(candidate.id, sessionId);
|
||||||
notifyTargetTab();
|
notifyTargetTab(sessionId);
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fresh = await chrome.tabs.create({ url: 'about:blank', active: false });
|
const fresh = await chrome.tabs.create({ url: 'about:blank', active: false });
|
||||||
if (!fresh.id) throw new ToolError('NO_ACTIVE_TAB', 'Kein Tab verfügbar', false);
|
if (!fresh.id) throw new ToolError('NO_ACTIVE_TAB', 'Kein Tab verfügbar', false);
|
||||||
setTargetTabId(fresh.id, sessionId);
|
setTargetTabId(fresh.id, sessionId);
|
||||||
notifyTargetTab();
|
notifyTargetTab(sessionId);
|
||||||
return fresh;
|
return fresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,11 +977,11 @@ async function dispatchTool(name: string, input: Record<string, unknown>, sessio
|
|||||||
if (input.newTab) {
|
if (input.newTab) {
|
||||||
const newTab = await chrome.tabs.create({ url, active: false });
|
const newTab = await chrome.tabs.create({ url, active: false });
|
||||||
setTargetTabId(newTab.id!);
|
setTargetTabId(newTab.id!);
|
||||||
notifyTargetTab();
|
notifyTargetTab(sessionId);
|
||||||
return { tabId: newTab.id, url, loading: true, hint: 'Seite lädt — browser_wait oder screenshot nach kurzer Pause.', _method: 'synthetic' };
|
return { tabId: newTab.id, url, loading: true, hint: 'Seite lädt — browser_wait oder screenshot nach kurzer Pause.', _method: 'synthetic' };
|
||||||
}
|
}
|
||||||
await chrome.tabs.update(tabId!, { url });
|
await chrome.tabs.update(tabId!, { url });
|
||||||
notifyTargetTab();
|
notifyTargetTab(sessionId);
|
||||||
return { tabId, url, loading: true, hint: 'Seite lädt — browser_wait oder screenshot nach kurzer Pause.', _method: 'synthetic' };
|
return { tabId, url, loading: true, hint: 'Seite lädt — browser_wait oder screenshot nach kurzer Pause.', _method: 'synthetic' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1019,7 +1026,7 @@ async function dispatchTool(name: string, input: Record<string, unknown>, sessio
|
|||||||
// sessionId mitgeben: sonst landet der neue Tab in der Gruppe der gerade
|
// sessionId mitgeben: sonst landet der neue Tab in der Gruppe der gerade
|
||||||
// SICHTBAREN Konversation statt in der des aufrufenden Chats.
|
// SICHTBAREN Konversation statt in der des aufrufenden Chats.
|
||||||
setTargetTabId(newTab.id!, sessionId);
|
setTargetTabId(newTab.id!, sessionId);
|
||||||
notifyTargetTab();
|
notifyTargetTab(sessionId);
|
||||||
return { tabId: newTab.id, url, redirectedFrom: url !== requestedUrl ? requestedUrl : undefined, _method: 'synthetic' };
|
return { tabId: newTab.id, url, redirectedFrom: url !== requestedUrl ? requestedUrl : undefined, _method: 'synthetic' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1037,7 +1044,7 @@ async function dispatchTool(name: string, input: Record<string, unknown>, sessio
|
|||||||
await chrome.windows.update(t.windowId!, { focused: true });
|
await chrome.windows.update(t.windowId!, { focused: true });
|
||||||
}
|
}
|
||||||
setTargetTabId(tid, sessionId);
|
setTargetTabId(tid, sessionId);
|
||||||
notifyTargetTab();
|
notifyTargetTab(sessionId);
|
||||||
return { ok: true, tabId: tid, _method: 'synthetic' };
|
return { ok: true, tabId: tid, _method: 'synthetic' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1912,7 +1919,7 @@ chrome.runtime.onConnect.addListener((port) => {
|
|||||||
} else {
|
} else {
|
||||||
// Der Broker-Kanal kennt keine Bilder — lieber sagen als still schlucken.
|
// Der Broker-Kanal kennt keine Bilder — lieber sagen als still schlucken.
|
||||||
if (msg.images?.length) {
|
if (msg.images?.length) {
|
||||||
sendToPanel({ type: 'log', level: 'warn',
|
sendToPanel({ type: 'log', level: 'warn', sessionId,
|
||||||
text: `${msg.images.length} Bild(er) ignoriert: Bildanhänge gehen nur im Antrieb "direct".` });
|
text: `${msg.images.length} Bild(er) ignoriert: Bildanhänge gehen nur im Antrieb "direct".` });
|
||||||
}
|
}
|
||||||
// Aktuelle Tab-URL mitschicken — der Broker baut daraus den Kontext (z.B. Mercedes-Wissen).
|
// Aktuelle Tab-URL mitschicken — der Broker baut daraus den Kontext (z.B. Mercedes-Wissen).
|
||||||
@@ -1929,12 +1936,22 @@ chrome.runtime.onConnect.addListener((port) => {
|
|||||||
}
|
}
|
||||||
case 'sync': {
|
case 'sync': {
|
||||||
// Nachliefern statt neu erzeugen: der Puffer ist die Wahrheit.
|
// Nachliefern statt neu erzeugen: der Puffer ist die Wahrheit.
|
||||||
|
// ABER nur die Ereignisse DIESER Konversation: der Puffer ist global,
|
||||||
|
// ein frisch geöffneter Chat bekam sonst den kompletten Verlauf aller
|
||||||
|
// parallel laufenden Chats nachgespielt ("es fühlt sich wie eine an").
|
||||||
|
// Ereignisse ohne sessionId sind global (connection_status u.ä.) und
|
||||||
|
// bleiben drin.
|
||||||
const from = typeof msg.from === 'number' ? msg.from : 0;
|
const from = typeof msg.from === 'number' ? msg.from : 0;
|
||||||
const missed = eventBuffer.filter(e => e.seq > from);
|
const sid = (msg as any).sessionId || activeSessionId;
|
||||||
|
const missed = eventBuffer.filter(e => {
|
||||||
|
if (e.seq <= from) return false;
|
||||||
|
const evSid = (e.msg as any).sessionId;
|
||||||
|
return !evSid || evSid === sid;
|
||||||
|
});
|
||||||
if (panelPort) {
|
if (panelPort) {
|
||||||
try {
|
try {
|
||||||
panelPort.postMessage({
|
panelPort.postMessage({
|
||||||
type: 'sync_start', count: missed.length, running: isAgentRunning(),
|
type: 'sync_start', count: missed.length, running: isAgentRunning(sid),
|
||||||
latest: eventSeq, startedAt: runStartedAt || undefined,
|
latest: eventSeq, startedAt: runStartedAt || undefined,
|
||||||
});
|
});
|
||||||
// Direkt posten, nicht ueber sendToPanel — sonst landet alles erneut im Puffer.
|
// Direkt posten, nicht ueber sendToPanel — sonst landet alles erneut im Puffer.
|
||||||
@@ -1962,17 +1979,17 @@ chrome.runtime.onConnect.addListener((port) => {
|
|||||||
case 'set_config': {
|
case 'set_config': {
|
||||||
const next = await patchConfig(msg.patch);
|
const next = await patchConfig(msg.patch);
|
||||||
if (msg.patch.brokerUrl) { ws?.close(); setTimeout(connect, 300); }
|
if (msg.patch.brokerUrl) { ws?.close(); setTimeout(connect, 300); }
|
||||||
sendToPanel({ type: 'state', config: next, connected: ws?.readyState === WebSocket.OPEN, running: isAgentRunning(), version: EXTENSION_VERSION, runningSessions: getRunningSessions() });
|
sendToPanel({ type: 'state', config: next, connected: ws?.readyState === WebSocket.OPEN, running: isAgentRunning(activeSessionId), version: EXTENSION_VERSION, sessionId: activeSessionId, runningSessions: getRunningSessions() });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'get_state': {
|
case 'get_state': {
|
||||||
const cfg = await loadConfig();
|
const cfg = await loadConfig();
|
||||||
sendToPanel({ type: 'state', config: cfg, connected: ws?.readyState === WebSocket.OPEN, running: isAgentRunning(), version: EXTENSION_VERSION, runningSessions: getRunningSessions() });
|
sendToPanel({ type: 'state', config: cfg, connected: ws?.readyState === WebSocket.OPEN, running: isAgentRunning(activeSessionId), version: EXTENSION_VERSION, sessionId: activeSessionId, runningSessions: getRunningSessions() });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'run_tool': {
|
case 'run_tool': {
|
||||||
const callId = crypto.randomUUID();
|
const callId = crypto.randomUUID();
|
||||||
sendToPanel({ type: 'tool_executing', callId, name: msg.name, input: msg.input });
|
sendToPanel({ type: 'tool_executing', callId, name: msg.name, input: msg.input, sessionId: activeSessionId });
|
||||||
const started = performance.now();
|
const started = performance.now();
|
||||||
try {
|
try {
|
||||||
const result = await runTool(msg.name, msg.input);
|
const result = await runTool(msg.name, msg.input);
|
||||||
@@ -1996,7 +2013,7 @@ chrome.runtime.onConnect.addListener((port) => {
|
|||||||
const sid = (msg as any).sessionId;
|
const sid = (msg as any).sessionId;
|
||||||
if (sid) {
|
if (sid) {
|
||||||
activeSessionId = sid;
|
activeSessionId = sid;
|
||||||
notifyTargetTab();
|
notifyTargetTab(sid);
|
||||||
sendToPanel({
|
sendToPanel({
|
||||||
type: 'state',
|
type: 'state',
|
||||||
config: await loadConfig(),
|
config: await loadConfig(),
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ function openPort(): void {
|
|||||||
}
|
}
|
||||||
port = p;
|
port = p;
|
||||||
// Alles nachfordern, was waehrend geschlossenem Panel aufgelaufen ist.
|
// Alles nachfordern, was waehrend geschlossenem Panel aufgelaufen ist.
|
||||||
try { p.postMessage({ type: 'sync', from: lastSeq }); } catch { /* Reconnect greift */ }
|
try { p.postMessage({ type: 'sync', from: lastSeq, sessionId: activeConvId }); } catch { /* Reconnect greift */ }
|
||||||
|
|
||||||
p.onMessage.addListener((msg) => {
|
p.onMessage.addListener((msg) => {
|
||||||
// Sequenznummer merken: nach einem Abriss wird genau ab hier nachgeliefert.
|
// Sequenznummer merken: nach einem Abriss wird genau ab hier nachgeliefert.
|
||||||
@@ -2263,7 +2263,7 @@ function showRunningBanner(startedAt?: number): void {
|
|||||||
bar.remove();
|
bar.remove();
|
||||||
// Alles ab Null nachfordern: der Puffer im Worker hat den vollen Lauf.
|
// Alles ab Null nachfordern: der Puffer im Worker hat den vollen Lauf.
|
||||||
lastSeq = 0;
|
lastSeq = 0;
|
||||||
try { port?.postMessage({ type: 'sync', from: 0 }); } catch { /* egal */ }
|
try { port?.postMessage({ type: 'sync', from: 0, sessionId: activeConvId }); } catch { /* egal */ }
|
||||||
});
|
});
|
||||||
bar.append(btn);
|
bar.append(btn);
|
||||||
appendToChat(bar);
|
appendToChat(bar);
|
||||||
|
|||||||
Reference in New Issue
Block a user