diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 0fa8a1279c..f002d0e40a 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -121,6 +121,8 @@ ### Added - Added a deterministic quick-lane task classifier (`classifyQuickLane`) exposed through a read-only `gjc quick-lane classify` command. It is an auditable classification surface that performs no routing and is NOT wired into runtime routing: it only prints whether a task request carries quick-lane eligibility signals (explicit file paths, issue/PR numbers, named symbols, explicit tests, numbered steps, acceptance criteria, error references, code blocks, or an explicit `force:`/`!` override) or deep-lane exclusions (vague, ambiguous, exploratory, risk-sensitive, or multi-file/cross-contract scope), and it never alters deep-path defaults. The classifier is deterministic and fail-closed: any exclusion forces the deep lane even when a concrete anchor is present. The #3984 routing decision remains open. +- Gajae Pet now renders in iTerm2 using inline PNG frames, with the same reserved composer-side layout and lifecycle cleanup used by Kitty and Sixel. + - `session.list` now supports bounded pages with opaque continuation cursors and stable snapshot sequences. Internal ACP, coordinator, CLI, and broker-restart consumers drain every page so existing session discovery remains complete. - Bounded-memory cold-session disk offloading is rollout-ready behind the `sessionMemory` settings namespace. `sessionMemory.mode` (default `"shadow"`) selects `off`/`shadow`/`enabled`: shadow measures without changing observable session behavior, and canary/default-on remain release-channel decisions rather than user-facing enum values. Fixed implementation budgets preserve the ≤64 MiB steady-state guarantee; transcript v5 stays authoritative and derived `.spill.*` sidecars remain disposable. Context materialization uses the typed `SessionContextTooLargeError` preflight, while `sessionMemory.contextOverflowRecovery` independently controls the async compact-once recovery path. - Enabled explicit-path and managed-session resumes validate descriptor-bound commit metadata, base/tail hashes, terminal proof, and hot-record digests before retaining only the authenticated post-compaction tail. Managed TUI and nested review-subagent sessions read authoritative transcript ranges through retained/no-follow descriptor checks while keeping disposable sidecars in a verified per-process resident cache outside the managed tree. Fresh-process gates cover 120k compaction cycles, direct/captured forks, and one-million-record first build plus exact lazy reopen. diff --git a/packages/coding-agent/src/modes/components/gajae-pet-widget.ts b/packages/coding-agent/src/modes/components/gajae-pet-widget.ts index 9143ae2c02..9dca9e395e 100644 --- a/packages/coding-agent/src/modes/components/gajae-pet-widget.ts +++ b/packages/coding-agent/src/modes/components/gajae-pet-widget.ts @@ -38,6 +38,8 @@ const KITTY_DROP_FRACTION = 0.45; const petKittyDropPx = (cellHeightPx: number): number => Math.min(Math.max(0, cellHeightPx - 1), Math.floor(cellHeightPx * KITTY_DROP_FRACTION)); const PET_RAISE_ROWS = 1; +/** iTerm2 has no sub-cell placement parameter; use transparent padding in a row above. */ +const ITERM2_RAISE_FRACTION = 0.25; const allocatedPetKittyImageIds = new Set(); function allocatePetKittyImageId(): number { @@ -326,7 +328,7 @@ export class GajaePetWidget { kittyImageId: protocol === "kitty" ? this.#kittyImageId : undefined, // iTerm2 receives an explicit 1:1 pixel size; keep its raster inside the // same two-row footprint reserved for Kitty and Sixel. - iterm2TopPaddingPx: 0, + iterm2TopPaddingPx: Math.round(cell.heightPx * (1 - ITERM2_RAISE_FRACTION)), iterm2BottomPaddingPx: 0, }); this.#framedEditor.setReserve(this.#pixel.columns + PET_SIDE_MARGIN); diff --git a/packages/coding-agent/test/gajae-pet-widget.test.ts b/packages/coding-agent/test/gajae-pet-widget.test.ts index 148a16767e..e62fba6e62 100644 --- a/packages/coding-agent/test/gajae-pet-widget.test.ts +++ b/packages/coding-agent/test/gajae-pet-widget.test.ts @@ -178,7 +178,7 @@ describe("GajaePetWidget", () => { const payload = getEmitter()?.(); expect(payload).toContain("\x1b[28;76H"); expect(payload).toContain("\x1b]1337;File="); - expect(payload).toContain("width=36px;height=36px;preserveAspectRatio=0"); + expect(payload).toContain("width=36px;height=50px;preserveAspectRatio=0"); expect(payload).toEndWith("\x1b\\"); } finally { widget.dispose(); @@ -198,22 +198,22 @@ describe("GajaePetWidget", () => { written.length = 0; vi.advanceTimersByTime(1200); - expect(written.some(chunk => chunk.includes("\x1b[28;76H\x1b[4X") && chunk.includes("\x1b]1337;File="))).toBe( + expect(written.some(chunk => chunk.includes("\x1b[28;76H") && chunk.includes("\x1b]1337;File="))).toBe( true, ); setCellDimensions({ widthPx: 12, heightPx: 24 }); vi.advanceTimersByTime(100); - expect(getEmitter()?.()).toContain("width=48px;height=48px"); + expect(getEmitter()?.()).toContain("width=48px;height=66px"); setTerminalSize(12, 30); - expect(getEmitter()?.()).toContain("\x1b[28;76H\x1b[4X"); + expect(getEmitter()?.()).toContain("\x1b[28;76H"); setTerminalSize(80, 30); expect(getEmitter()?.()).toContain("\x1b]1337;File="); written.length = 0; widget.setMode("off"); - expect(written.some(chunk => chunk.includes("\x1b[28;76H\x1b[4X"))).toBe(true); + expect(written.some(chunk => chunk.includes("\x1b[28;76H"))).toBe(true); } finally { setCellDimensions(original); widget.dispose(); diff --git a/run-gjc-pet.sh b/run-gjc-pet.sh new file mode 100755 index 0000000000..5a968dc4d5 --- /dev/null +++ b/run-gjc-pet.sh @@ -0,0 +1,52 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +SOURCE_DIR="$SCRIPT_DIR" +ORIGINAL_PATH=${PATH-} + +if [ ! -f "$SOURCE_DIR/packages/coding-agent/src/cli.ts" ]; then + printf '%s\n' "ERROR: Gajae-Code checkout not found: $SOURCE_DIR" >&2 + exit 1 +fi + +if ! command -v bun >/dev/null 2>&1; then + printf '%s\n' 'ERROR: bun is not available on PATH.' >&2 + exit 1 +fi + +RUST_BIN='' +if command -v rustup >/dev/null 2>&1; then + RUSTC_PATH=$(rustup which rustc 2>/dev/null || true) + if [ -n "$RUSTC_PATH" ]; then + RUST_BIN=$(dirname -- "$RUSTC_PATH") + fi +fi + +RUN_PATH=$ORIGINAL_PATH +if [ -n "$RUST_BIN" ]; then + RUN_PATH="$RUST_BIN:$RUN_PATH" +fi + +# This launcher is for iTerm2. GJC normally disables image overlays inside a +# multiplexer because raw graphics escapes are not portable there; the explicit +# protocol tells GJC that this launcher's output is intended for iTerm2. +IMAGE_PROTOCOL=${GJC_FORCE_IMAGE_PROTOCOL:-iterm2} + +if [ ! -f "$SOURCE_DIR/packages/natives/native/pi_natives.darwin-arm64.node" ]; then + printf '%s\n' 'Native addon is missing; building it once...' + ( + cd "$SOURCE_DIR" + PATH="$RUN_PATH" GJC_FORCE_IMAGE_PROTOCOL="$IMAGE_PROTOCOL" bun --cwd=packages/natives run build + ) +fi + +printf '%s\n' "Starting local Gajae-Code from: $SOURCE_DIR" +( + cd "$SOURCE_DIR" + PATH="$RUN_PATH" GJC_FORCE_IMAGE_PROTOCOL="$IMAGE_PROTOCOL" bun packages/coding-agent/src/cli.ts "$@" +) + +# PATH is deliberately scoped to the subshell above and remains unchanged here. +PATH=$ORIGINAL_PATH +export PATH