mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 03:10:38 +09:00
fix(ui): external adapter selection, config field placement, and transcript parser freshness
- Fix external adapters (hermes, droid) not auto-selected when navigating with ?adapterType= param — was using a stale module-level Set built before async adapter registration - Move SchemaConfigFields to render after thinking effort (same visual area as Claude's chrome toggle) instead of bottom of config section - Extract SelectField into its own component to fix React hooks order violation when schema fields change between renders - Add onAdapterChange() subscription in registry.ts so registerUIAdapter() notifies components when dynamic parsers load, fixing stale parser for old runs - Add parserTick to both RunTranscriptView and useLiveRunTranscripts to force recomputation on parser change
This commit is contained in:
parent
69a1593ff8
commit
47f3cdc1bb
13 changed files with 473 additions and 55 deletions
|
|
@ -27,7 +27,7 @@ import { PageTabBar } from "../components/PageTabBar";
|
|||
import { adapterLabels, roleLabels, help } from "../components/agent-config-primitives";
|
||||
import { MarkdownEditor } from "../components/MarkdownEditor";
|
||||
import { assetsApi } from "../api/assets";
|
||||
import { getUIAdapter, buildTranscript } from "../adapters";
|
||||
import { getUIAdapter, buildTranscript, onAdapterChange } from "../adapters";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { agentStatusDot, agentStatusDotDefault } from "../lib/status-colors";
|
||||
import { MarkdownBody } from "../components/MarkdownBody";
|
||||
|
|
@ -3762,10 +3762,20 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
|
|||
return redactPathValue(asRecord(evt?.payload ?? null), censorUsernameInLogs);
|
||||
}, [censorUsernameInLogs, events]);
|
||||
|
||||
const adapter = useMemo(() => getUIAdapter(adapterType), [adapterType]);
|
||||
// NOTE: adapter is NOT memoized because external adapters replace their
|
||||
// parseStdoutLine asynchronously after dynamic parser loading. Memoizing
|
||||
// on adapterType alone would stale the transcript with the fallback parser.
|
||||
// We subscribe to adapter registry changes to force transcript recomputation.
|
||||
const [parserTick, setParserTick] = useState(0);
|
||||
const adapter = getUIAdapter(adapterType);
|
||||
|
||||
useEffect(() => {
|
||||
return onAdapterChange(() => setParserTick((t) => t + 1));
|
||||
}, []);
|
||||
|
||||
const transcript = useMemo(
|
||||
() => buildTranscript(logLines, adapter.parseStdoutLine, { censorUsernameInLogs }),
|
||||
[adapter, censorUsernameInLogs, logLines],
|
||||
[adapter, censorUsernameInLogs, logLines, parserTick],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { AgentConfigForm, type CreateConfigValues } from "../components/AgentCon
|
|||
import { defaultCreateValues } from "../components/agent-config-defaults";
|
||||
import { getUIAdapter, listUIAdapters } from "../adapters";
|
||||
import { useDisabledAdaptersSync } from "../adapters/use-disabled-adapters";
|
||||
import { isValidAdapterType } from "../adapters/metadata";
|
||||
import { ReportsToPicker } from "../components/ReportsToPicker";
|
||||
import {
|
||||
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
|
||||
|
|
@ -29,10 +30,6 @@ import {
|
|||
import { DEFAULT_CURSOR_LOCAL_MODEL } from "@paperclipai/adapter-cursor-local";
|
||||
import { DEFAULT_GEMINI_LOCAL_MODEL } from "@paperclipai/adapter-gemini-local";
|
||||
|
||||
const SUPPORTED_ADVANCED_ADAPTER_TYPES = new Set<CreateConfigValues["adapterType"]>(
|
||||
listUIAdapters().map((adapter) => adapter.type as CreateConfigValues["adapterType"]),
|
||||
);
|
||||
|
||||
function createValuesForAdapterType(
|
||||
adapterType: CreateConfigValues["adapterType"],
|
||||
): CreateConfigValues {
|
||||
|
|
@ -114,9 +111,7 @@ export function NewAgent() {
|
|||
useEffect(() => {
|
||||
const requested = presetAdapterType;
|
||||
if (!requested) return;
|
||||
if (!SUPPORTED_ADVANCED_ADAPTER_TYPES.has(requested as CreateConfigValues["adapterType"])) {
|
||||
return;
|
||||
}
|
||||
if (!isValidAdapterType(requested)) return;
|
||||
setConfigValues((prev) => {
|
||||
if (prev.adapterType === requested) return prev;
|
||||
return createValuesForAdapterType(requested as CreateConfigValues["adapterType"]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue