Skip to content

Open the Run Inspector from a button inside the Amicode chat - #22

Closed
kateebonner wants to merge 107 commits into
local/amicodefrom
kate/chat-open-inspector
Closed

Open the Run Inspector from a button inside the Amicode chat#22
kateebonner wants to merge 107 commits into
local/amicodefrom
kate/chat-open-inspector

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector had no affordance inside the chat — only the VS Code status-bar item and the command palette. This adds a titlebar button to the session header's right action cluster (beside the terminal toggle, so it survives narrow embeds where the md: cluster is hidden).

  • New pulse icon in the framed-panel idiom (terminal/review wear the same frame): a zero-order-hold staircase — the very thing the inspector plots
  • Posts the already-allowlisted amicode.openInspector through the existing bridge (postAmicode), which chat_panel.ts relays to revealInspector() — zero extension-side changes needed
  • inAmicode() gated: hidden in the public web/share and desktop builds, where there is no extension host

Verification

  • bun turbo typecheck (app + ui) clean; bun test: ui 221 pass, app 388 pass, 0 fail
  • Icon rendered and eyeballed at 80px and 16px next to the terminal icon (framed idiom matches, legible at small size)
  • Exercised live in the installed extension (VSIX vendored from this branch)

🤖 Generated with Claude Code

Rchari1 and others added 30 commits July 8, 2026 12:31
…ion-lookup.ts — shared by the About-You card and the onboarding wizard; card keeps debounce/sequencing/race guards

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… about-you with live institution/logo lookup → profile preview + open chat); saves through POST /amicode/profile so the home page autofills affiliation + logo; shows exactly once (fresh profile, dismiss remembered) + tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… the starter chips (Open chat owns 'start something'); PDFs upload via POST /amicode/library into ~/.amico/library (sanitized basename, %PDF- magic check, 30MB cap), GET lists newest-first; 'Discuss latest →' hands the agent the paper path + tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…jects

On a fresh browser profile against a bare `opencode serve`, the home
page's primary CTA (Meet-Amico card / "Open chat") did nothing: the
persisted client-side project list is empty, so startWithPrompt fell
through to openNewSession(), which needs the same newSessionProject()
that just came back empty and silently returns.

Fall back to the focused server's own working directory, synced from
GET /path (.directory; "" until loaded, so the guard holds). Open and
touch it as a project — self-healing: the home page tracks it from then
on — and start the draft with the prompt preserved. Deliberately not
sync.data.project: the server's "global" record has worktree "/".

Regression spec drives the real UI against a mocked server with no
localStorage seed; verified failing on the unfixed code and passing
with the fix. tsgo -b clean; bun test:unit 376 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
amicode(home): fix silent dead-end on 'Open chat' with no tracked projects
Prepend 'Aero' to the AMICODE wordmark/logo font-family chain (logo.tsx,
wordmark-v2.tsx), matching each file's current fallback stack. Additive
only. Re-applied fresh after the branch fast-forwarded 57 commits to
current origin/local/amicode — this had been uncommitted WIP on the old
base.

Aero is donationware (Nirmal Biswas / Picatype, $25+) — bundled preview-
only, must be licensed or replaced before any release. See AMICODE-
PATCHES.md #15.
The chat composer's handlePaste bailed out silently whenever
event.clipboardData was empty — which it always is inside the sandboxed
cross-origin webview iframe (navigator.clipboard is denied there too).
Cmd/Ctrl+V into the composer was a no-op.

The extension side of this bridge already ships on main (chat_panel.ts
answers clipboard-request), and home-cards.tsx already proved the pattern
for a different input. This wires the same bridge into the main composer:
platform.readClipboardText (new, mirrors readClipboardImage) -> entry.tsx
implements it via the postMessage bridge when framed -> attachments.ts
falls back to it when clipboardData yields no text.

See AMICODE-PATCHES.md #16 for the full trace + build/verify evidence.
Live test (Aaron) found the prior commit's fix was composer-only: pasting
into a generic "Connect <provider>" API-key dialog (dialog-connect-
provider.tsx - one of many such plain-input settings forms) was still a
silent no-op, since that input has no knowledge of the bridge.

Same root cause, generalized properly: one document-level paste listener
(installGlobalPasteFallback, entry.tsx) covers every form field and
contenteditable in the app, activating only when the native paste event
gave no text (the sandboxed-iframe symptom) - a no-op everywhere
clipboardData already works. The composer's own handler stopPropagation()s
on every paste it handles, so it never double-fires there.

See AMICODE-PATCHES.md #16 addendum for the full trace + build evidence.
…' (upload PDFs between about-you and the finish; ✓-list of uploads, continue-without-papers path; step skipped when upload isn't wired); home Library card stays; shared fileToBase64 util

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mo udd hit exactly this) + trunk

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… demoted to the quiet secondary — the autofilled home is the payoff shot

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
amicode: first-run onboarding wizard — welcome UI that autofills home-page affiliation + logo
…99

The fork rendered the old "digi" pixel-H mark in four independent copies
while amicode redesigned its mark (PR #99). This unifies them to a single
MARK_PATH and syncs the geometry to #99's FINAL square mark (viewBox
0 0 3600 3600) — not the intermediate "hackathon" geometry an earlier
draft of this branch had copied, which #99 itself later abandoned.

- logo.tsx: MARK_PATH = amico_reduced.svg's outer-bracket path (fill-rule
  evenodd). Mark/Splash render it; viewBox 64x56 → 3600 square.
  MarkDetailed = amico.svg's full mark (bracket + internal accents),
  viewBox 116 287 3377 3035 → 3600 square, for the Meet Amico card only.
- spinner.tsx (AmicoSpinner) + run-card.tsx (share-card SVG) both now
  import MARK_PATH instead of carrying their own copies — run-card was a
  fourth private copy of the old glyph; its transform is recalibrated
  (scale 0.55 → 0.011) for the 3600-unit space.
- favicon/amico.svg: reduced bracket on the yellow chip, viewBox 3600,
  path kept byte-identical to MARK_PATH.
- logo.css: aspect-ratio 8/7 → 1/1 (mark is square now).
- Small contexts use the reduced mark, the Meet Amico card uses the
  detailed one — matching amicode's own small/large split.

Rebased onto local/amicode (picks up PR #8; AMICODE-PATCHES.md conflict
resolved, entry 15 rewritten for the final geometry).

Checks: bun turbo typecheck (ui+app) green; ui bun test src 95 pass;
app vite build clean — new mark path in the bundle, old geometry and the
64x56 viewBox gone (0 chunks), favicon carries the new path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reduced bracket is right for small chrome (titlebar, footer, tiny
fallbacks), but the large brand-hero contexts should show the full mark.
Switch to MarkDetailed at:
- the new-session / main chat landing hero (both the v2 design view,
  144px, and the classic view, 112px)
- the onboarding wizard welcome step (56px)
The wizard's tiny 36px affiliation-logo fallback stays on the reduced
Mark (below where the internal accents resolve).

Checks: typecheck (ui+app) green; ui bun test src 99 pass; app build
clean with the detailed-mark accents present in the bundle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consolidate brand mark to one geometry; fix drift from amicode's redesign
Adds an "Inspect Run" button on the amicode problem-header rail, beside
the live run chip, that opens the VS Code Run Inspector on demand instead
of relying on auto-launch. It fires the existing host bridge —
postAmicode("amicode.openInspector") — which chat_panel.ts relays to the
already-allowlisted vscode command; no new bridge, allowlist, or route.

- entity-rail.tsx: new optional onInspectRun prop + button, gated on a run
  existing (hasRun) so it shows alongside the run chip, not before any
  solve. packages/ui stays bridge-agnostic (fires a callback only).
- message-timeline.tsx: wires onInspectRun to the bridge, passed only when
  framed in Amicode (inAmicode()), so the public web/share build shows
  nothing.
- use-amicode-commands.tsx: export postAmicode + inAmicode for reuse.

Pairs with harmoniqs/amicode#116 (stops the inspector auto-launching);
reaches amicode users after this is re-vendored into the extension binary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The message timeline had three independent systems writing scrollTop to
the bottom on the same content update, reading scroll geometry at slightly
different instants from a virtualizer whose item heights are estimates.
When virtua corrected a measured height a frame later, the writers had
already landed on slightly different positions -> visible bounce.

- session.tsx: gate createAutoScroll on the session's real working state
  instead of a hard-coded `true`. The auto-scroller's ResizeObserver was
  force-following the bottom on *any* reflow (image load, accordion expand,
  font swap), which read as the chat jumping on its own. Send-to-bottom and
  the jump button are unaffected (they use the force path).

- message-timeline.tsx: only realign via virtua's estimate-based
  scrollToIndex(align:"end") when the row set changes or status flips, not
  on every streamed token. Pure intra-row growth is left to the
  measured-bottom rAF lock, which pins against the real DOM height, so the
  two mechanisms stop disagreeing frame-to-frame. Safe because
  timelineRowKeys is memoized with `equals: sameKeys`.

- message-part.tsx: honor prefers-reduced-motion in the imperative
  ShellSubmessage reveal (the CSS already does; this JS animate() did not,
  and its initial render collapses width to 0 / blurs the value).

Verified: app + ui typecheck clean; message-part, scroll-view,
layout-scroll, file-tab-scroll, use-session-hash-scroll unit tests pass
(16/16). Perceptual smoothness during live streaming still wants a visual
pass against a real model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 3px accent-colored border-left (and the onboarding wizard's 4px accent border-top) read as a heavy yellow outline on every amicode card. Drop the rail from the home dashboard hero cards, the in-chat receipt/entity/ask/run cards, the problem rail, and the onboarding wizard — each keeps its neutral 1px hairline. The H-mark, live dots, hover tints, and focus outlines (keyboard a11y) are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Locks the contract the entity-rail "Inspect Run" button depends on:
postAmicode posts exactly {source:"amicode", kind:"command",
command:"amicode.openInspector"} (the command chat_panel.ts allowlists),
never throws if the parent frame rejects the post, and inAmicode()
reports false when unframed so the button stays hidden on the public web
build. Solid components can't be DOM-rendered under this repo's bun test
harness, so this covers the button's observable contract at the bridge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold the full AMICO receipt redesign into the rail removal: instead of just
dropping the accent bar, give the whole family one visual language.

- receipt card (card.tsx): the H-mark doubles as the working spinner, diffs
  render structured (dimmed old -> new) instead of a run-on string, a green
  check settles the done state, failure tints the card red, and it is a real
  <button> with a focus ring (was a click-only <div>, invisible to keyboard)
- entity view (entity-view.tsx): raw keys lead with a human label and keep the
  technical name beneath (params.drive_max -> "Drive max"), nested fields get a
  subhead, the arrived-from change is highlighted, the edit-in-chat affordance
  is always visible, and a footer states the read-only / edit-in-chat contract
- rail / ask-card / run-window share the surface + H-mark signature
- identity via an accent-tinted wash + gold hairline, no side rail
- new pure helpers receiptParts / humanizeKey / fieldGroup (receiptText output
  unchanged); interaction/animation styles in new amicode.css

Typecheck clean; 66/66 amicode tests pass; app build succeeds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
QNode {id, prereqs, relevant?(value predicate), optionsFrom?, batchable};
frontier(graph, answers) returns answerable-now nodes (prereqs met AND
optionsFrom answered AND relevant AND not-yet-answered). Value-dependent and
relevance-pruning guardrails both covered. v1 = tested reference the SCORE
batching policy mirrors (not runtime-wired; spec §4.1). 6/6 bun test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(amicode): "Inspect Run" button on the entity rail
Fix chat scroll jitter during streaming; respect reduced motion
chipText (system): composite branch -> compositeChip (N=1 matches the legacy
flat chip; N>1 = "platform · N×role · kind · arch"); entityRows: composite
branch -> compositeSystemRows (component-table + coupling-list, not a JSON
blob); new systemProjection (composite read-through; flat collapses to N=1
with isComposite flag). All parse BOTH shapes defensively in place -- the fork
repo cannot import the plugin normalizeSystem (spec §3 cross-repo). Never
throws. +5 tests; 73/73 amicode-ui bun suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the fork mainline up to date with the merged upstream stack
(PRs #6#12): onboarding wizard, Library upload, brand-mark
consolidation, receipt-card + entity-view redesign, Inspect Run
button, chat-jitter/reduced-motion fix, accent-rail removal.

Conflicts resolved:
- logo.tsx: auto-merged; the local 'Aero' font-family prepend survived
  origin's brand-mark rewrite (verified line 112 + wordmark-v2 intact).
- AMICODE-PATCHES.md: kept both sides. Local Aero (#15) + composer
  paste bridge (#16) preserved; origin's mark-drift entry renumbered
  15 -> 17 to sit after them. Local Aero + paste-bridge commits
  (a4fd244/c811798/b868a59) retained — still local-only WIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Piccolissimo + Altissimo PRO] segmented control on home (spec-20260709-093000); selection persists in localStorage, changes nothing about solves (future wiring: executor cloud-altissimo seam + issimo entitlement as the subscription gate) + tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… restore the <Logo> component and its Racing Sans One face (straight-sans experiment reverted per review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iew: keep trunk's MarkDetailed (redesign) + this branch's Logo typeface wordmark (Racing Sans One restore)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
aarontrowbridge and others added 27 commits July 12, 2026 02:03
…+ preview

The conflict-safe core of spec-20260712-014749 (all NEW files; no edits to the
message-part.tsx / entity-rail.tsx / run-window.tsx surfaces the dashboard agent
is actively changing):
- amico-presence.ts: pure presence state machine (dormant/stepping_in/on/
  settling/idle) — classify + transition; the shared brain rail+flow read. 14 tests.
- amico-presence.css: offset accent lane + rail wake/settle motion (v2 tokens,
  reduced-motion gated); new file, not the hot amicode.css.
- amico-presence.stories.tsx: self-contained Storybook preview of all 5 states +
  an auto-playing pop-in/out sequence (mock data).

Deferred (dashboard-agent file contention): the invasive wiring — stamp scrub in
message-part/card/ask-card/run-window, thinking-line decouple, rail wake/settle
hookup. Fully specified in the approved spec; needs a coordinated pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…inherits the app CSP and kills the inline runtime
…pill

The old edit mode floated a heavy row of five bordered buttons above every
card with a truncated label, drowning the content and reading as its own UI.
Rebuild: cards stay visible under a light scrim + dashed ring ("arranging,
not using"), each gets one compact grouped control pill in its top-right
corner (↑↓ reorder / ⚙ config / fork / ×), a banner orients, the add-back
tray only appears when something is hidden. Reorder ↑/↓ disable at row ends;
move buckets by hero/tile row (matches what the user sees). Overlay is
click-safe over the iframe; scrim blocks accidental widget clicks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…the frame

Moving display:none onto the frame wrapper left the cell holding an empty
grid column. Hide the whole cell when not editing and there's nothing to
render; keep the frame mounted under display:none so its amc:empty signal
keeps flowing and the cell re-appears if the widget later has content.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Realizes the approved spec (identity rule + working lane) — the conflict-scoped
pass (rail wake via CSS only; NO edit to entity-rail.tsx, which the widget kernel
is editing):
- message-part.tsx: drop the per-turn AMICO signature (data-slot=
  amicode-turn-signature); repurpose the amicode_* check as the in-domain signal;
  mount the thinking line in an offset accent lane BELOW the streamed parts, gated
  on inDomainTurn && working (decouples it from the old card-suppression so it now
  shows on in-domain turns — the reviewer's issue 5).
- card.tsx / ask-card.tsx / run-window.tsx: drop the 'AMICO' wordmark (keep the
  H-mark glyph as a subtle leading icon); remove orphaned separators.
- amico-presence.css: rail wakes on entry via [data-component=amicode-entity-rail]
  (session-sticky mount => plays once, no entity-rail.tsx edit).
- styles/index.css: load amico-presence.css in the components layer.

Verified: 20 tests pass, 0 typecheck errors. NOT visually run (heavy fork build;
main checkout held by widget agent). Deferred: grouping amicode cards INTO the
offset lane (open-question-2) + rail livedot-while-working — visual-iteration
follow-ups. Not merged/pushed; reconciles with wip/stage1-widget-kernel at merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…el build

Disjoint from the widget kernel (UI chat flow vs dashboard widgets); brings
the presence lane + un-stamped cards into the current build for live review.
Stage 2 widget-author WIP left uncommitted in the working tree, untouched.
…n-chat preview + pin

Prompt Amico to build a home widget; it renders a live preview in the chat and
you pin it to the dashboard. Rides the existing /^amicode_/ tool-name seam — no
stock-code, no new part type, no new route.

Server (opencode):
- authorWidget() in widgets.ts: validate structured manifest fields + js, assemble
  the manifest TOML, round-trip through parseManifest, write ~/.amico/widgets/<id>/;
  re-authoring an id overwrites (new content hash = hot-reload). Never throws.
- amicode_author_widget built-in tool (Tool.define + registry) whose description IS
  the authoring contract (mount signature, --amc-* tokens, fetch allowlist,
  empty-state). Output carries an AMICODE_WIDGET {json} sentinel for the preview.
- mergeDashboard: BUILT-IN widgets still auto-surface; USER/authored/forked widgets
  are now opt-in via pin (no home clutter while iterating in chat).

UI:
- widget-preview.ts: pure parseWidgetSentinel (mirrors parseDiffSentinel).
- WidgetPreviewCard: live WidgetFrame (reuses the kernel) + Pin button + warnings;
  reads a widgetHost transport off the ui-bridge; degrades to a note with no host.
- AmicodeToolCard: new <Match> for amicode_author_widget.
- ui-bridge: AmicodeWidgetHost {frameSrc, callbacks, pin}; entity-rail passes it in.

App:
- message-timeline provides widgetHost (frame src, fetch/prompt callbacks, pin =
  GET+append+POST /amicode/dashboard).

Tests: authorWidget 5, sentinel parser 4, dashboard opt-in 1 (ui 178 / server 40 green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…or.clipboard.read in the outer webview + copy-back

The webview half of the bidirectional clipboard bridge (extension half is in amicode#136):
image paste answered client-side in the outer webview where navigator.clipboard.read is
granted (the sandboxed iframe + text-only vscode.env.clipboard can't); copy writes back to
the OS clipboard; text-paste fallback on any failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
amicode: widget-kernel — home dashboard, in-chat widget authoring, Amico presence, clipboard/model bridges (fork half of amicode#136)
…in the chat

A persistent, breathing visualization of amico's neural pathways rendered
over the vault/skill/agent graph, living in the conversation flow as a
permanent timeline row (stable key, kept mounted, riding the timeline's own
row-measurement and bottom-lock machinery).

Behavior
- Auto-breathe: a 72px living slice at rest (close-up centered on amico's
  current position), expanding to a 224px whole-network view while a turn
  thinks (the amico core anchors dead center, zoom fits the farthest node);
  lingers 5s past completion, then collapses. Click/Enter toggles manually
  until the next turn reclaims auto. Folds entirely while a question dock
  is open — render loop paused, amico is waiting on the user.
- Live telemetry: every tool call of the session streams into the brain.
  Reads, edits, skills, tasks and amicode_* verbs COMMIT — a pulse travels
  the skeleton and the node claims its category color (skill names map onto
  real skeleton node ids; unknown files graft beside the current position);
  greps/globs/lists flash as considered scouts that may leak back to dark;
  the interview's question tool rings the pulse-designer node per question.
- Session persistence: completed messages arrive replay-flagged and restore
  the atlas instantly; the busy message animates live — every turn resumes
  the session's accumulated path. Turns with ≥2 commits chart a quiet
  constellation named "plate N · <prompt excerpt>".
- Spreading activation: each live commit charges up to 4 skeleton neighbors
  sub-threshold, so one event ripples like tissue instead of blipping.
- Cross-links: hovering a tool row (or an Explored/shell group chip) rings
  its node on the map via the amicode:brain-hover event; in the collapsed
  close-up the camera turns briskly to look at the glanced node.

Rendering
- Natively wide force layout for the strip (stretch-then-relax: locally
  isotropic distances inside a wide envelope — no projection squash), all
  pathways drawn clearly, untraversed tissue receded so the traversed path
  owns the contrast, where-we-are cursor ring legible in both themes.
- Assets ship CSP-split (the server forbids inline scripts and data: fonts
  in served HTML): public/brain.html + brain.js + brain-font.woff2,
  GENERATED from the canonical design artifact in amicode media/brain —
  re-split to update, do not hand-edit.
- The card wears the message cards' surface token, hairline border, 12px
  radius, and the centered content-column constraint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
amicode: the amico brain — a live map of the session's thought in the chat
The Run Inspector could only be opened from the VS Code status bar item or
the command palette — nothing inside the chat itself. Adds a framed-panel
button (new 'pulse' icon: a zero-order-hold staircase, matching the
terminal/review framed idiom) to the session header's right titlebar
cluster, beside the terminal toggle so it stays visible in narrow embeds.

Wiring is the existing bridge, end to end: the button posts the already-
allowlisted {source:"amicode", kind:"command", command:"amicode.openInspector"}
via postAmicode(); chat_panel.ts relays it to revealInspector(). Framed-only
(inAmicode() gate) — hidden in the public web/desktop builds where no
extension host exists. No V2-actions mirror: the desktop V2 layout is never
framed, so the gate would keep it permanently hidden there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jack-champagne

Copy link
Copy Markdown
Member

Closing as superseded — this one genuinely is.

This PR adds an "Open Run Inspector" button to session-header.tsx, calling postAmicode("amicode.openInspector"). That capability now reaches the same place by another route:

  • ddfd46977b "feat(amicode): add "Inspect Run" button to the entity rail" — added the button, firing the same amicode.openInspector command (packages/ui/src/amicode/entity-rail.tsx:279-300, wired at message-timeline.tsx:1741).
  • 8aa3d74a33 "refactor(amicode): rail attaches to the header" — moved the rail into the header, so the button already sits where this PR wanted to put it.

The one thing here with no equivalent on the default branch is the pulse icon added to packages/ui/src/components/icon.tsx. Not currently referenced by anything, so nothing is lost by closing — but if the rail's Inspect Run affordance ever wants a dedicated glyph, it's on branch kate/chat-open-inspector, which is preserved.

cc @kate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants