the lists re-read when the agent changes something

an agent deleted two proxy hosts and both stayed on screen. the person had the
answer in the transcript and a stale list six inches above it — which is the
one thing a chat beside a list has to get right, and the reason it is beside
the list at all.

so the chat panel bumps the refresh channel when a turn ran tools, and every
section re-reads. gated on hadToolCalls: a turn that only talked changed
nothing, and re-reading after every sentence would put six requests behind
every reply.

the channel is renamed with it. it was proxy-manager:connection-changed, named
after its only publisher; it has two now and the second is the bigger one, so
it says what it MEANS — something at NPM changed, read it again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-25 19:03:35 +00:00
co-authored by Claude Opus 5
parent 417e091dfe
commit 66383aa9a4
8 changed files with 57 additions and 29 deletions
+28 -5
View File
@@ -1,4 +1,6 @@
import { useCallback } from 'react';
import { ChatPanelWrapper, ChatHeader } from 'officerdev';
import { useNpmChanged } from './channel';
/**
* A chat, sitting under the two lists.
@@ -37,11 +39,32 @@ const CHAT_CONTEXT = { context: 'plugin', contextId: 'proxy-manager' } as const;
* behaviour. Mounting the composer directly meant reimplementing all of that or going without it, which is
* what this panel was doing.
*/
export const ChatPanel = ({ panelId }: { panelId: string }) => (
<div className="flex h-full min-h-0 flex-col">
<ChatPanelWrapper panelId={panelId} chatContext={CHAT_CONTEXT} />
</div>
);
export const ChatPanel = ({ panelId }: { panelId: string }) => {
const [, npmChanged] = useNpmChanged();
/**
* A turn that ran tools may have changed NPM, so every list on this screen re-reads.
*
* This is the whole reason the panel is worth having beside the lists rather than in `/chat`. An agent
* deleted two proxy hosts on 2026-08-25 and both stayed on screen afterwards, because nothing told the
* panels — the person had the answer in the transcript and a stale list six inches above it.
*
* Gated on `hadToolCalls`, which is the only discrimination that matters: a turn that only talked
* changed nothing, and re-reading after every sentence would put six requests behind every reply.
*/
const onTurnComplete = useCallback(
(hadToolCalls: boolean) => {
if (hadToolCalls) npmChanged();
},
[npmChanged],
);
return (
<div className="flex h-full min-h-0 flex-col">
<ChatPanelWrapper panelId={panelId} chatContext={CHAT_CONTEXT} onTurnComplete={onTurnComplete} />
</div>
);
};
/**
* The panel's header: the conversation picker, scoped to this plugin's directory.