chat/CLAUDE.md replaces the promptPrefix. the platform copies it into the chats
directory on every chat that starts there.
it says more than the prefix could afford to, and the part worth having is the
rule the prefix had no room for: a refusal is an answer, not an obstacle to
route around. no hunting for another credential, no reaching NPM another way,
no deciding the refusal is a bug and working around it while investigating.
that rule is written rather than enforced, and the file says why: the agent runs
with a shell, so most of those would work. stepping over the line is worse than
the thing it was asked to do, because the person ends up with neither the
outcome nor an accurate account of what happened.
prompted by a real turn today: a 403 on a delete, followed by a hunt for a
broader-scoped credential, ended by the owner interrupting it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
ChatPanelWrapper, not EmbeddableChat — one level up, and the session
persistence, the agent identity bar, the spawned-by bar and resume-the-newest
all come with it. mounting the composer directly meant reimplementing those or
going without.
and a header: the conversation picker, scoped to this plugin's own directory,
with new chat, inline rename and delete. none of it is new — it is what a
dashboard's chat panel has always had, pointed at a context instead of a path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
context='plugin' + contextId='proxy-manager', and the platform turns that into
$DATA_PATH/<email>/plugins/proxy-manager/chats. the path is never sent from
here — see resolveChatCwd and docs/commander-directory.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
EmbeddableChat is handed flex-1 min-h-0, which needs a flex column with a real
height above it. shipped without one, so the composer floated mid-panel and the
transcript ran below the fold. ChatPanelWrapper has the same div and I copied
the child without it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
naming officerdev/chat in layout.ts rendered a second Hosts panel. the shared
registry is not the thing that decides: PluginScreen renders the workspace with
appTypes={{ allowed, fallback: allowed[0] }} where allowed is THIS plugin's own
panel keys, so an unrecognised appType silently becomes the first panel. that
boundary is deliberate — its comment says a persisted layout must not be able
to render another plugin's panel inside this screen — and it is a different
mechanism from seedAppRegistry merging the panels, which is what the last
commit's reasoning confused it with.
so the panel is ours and EmbeddableChat inside it is the platform's. it is
exported from officerdev for exactly this: the whole composer, transcript,
queue and model picker, with no second implementation to fall behind the
first.
it carries a two-sentence promptPrefix, which is per-panel and costs nobody
else's conversation anything. deliberately short: the skill holds the
knowledge and reaches every session for two lines, so anything longer here
would be the skill written twice and this would be the stale copy. all it says
is where the agent is standing, which is the one thing the skill cannot know.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043