Commit graph

197 commits

Author SHA1 Message Date
iamtoruk
fee06a7e54 feat(app): Kimi Code live quota on the Plans page (#1136)
Ports the menubar's Kimi flow to the Electron quota module: read-only
~/.kimi-code/credentials (KIMI_CODE_HOME honored), api.kimi.com usages
snapshot, terminal expired-login state that never refreshes the CLI's
short-lived token, 5-hour/weekly window mapping with over-limit clamp,
membership plan label, and the standard per-provider Settings toggle.
2026-08-24 12:28:32 -07:00
iamtoruk
908a2e1a1d feat(app): period payloads persist for the session and revalidate behind an instant paint
The renderer memo was an LRU capped at 8-24 entries, so cycling periods
evicted payloads and returning to one forced a skeleton plus a 1.5-6s
recompute. The memo is now one latest payload per key for the app
session: a revisited period paints its cached numbers in the same
commit, skips the CLI entirely when the payload is under 30s old, and
otherwise revalidates behind the painted data and swaps in place.
Failed revalidates keep last-good data with the existing error surface;
first visits keep the loading state. (#1135 part 1)
2026-08-24 11:56:37 -07:00
iamtoruk
195bb9128e chore: release 0.9.21
Version 0.9.21 across CLI, desktop app, and Windows menubar; refreshed
LiteLLM pricing snapshot and fallback (catches the 2026-08-24 OpenAI
GPT-5.6 cut and DeepSeek v4 repricing mid-rollout, #1134 tracks
re-tightening); CHANGELOG stamped for 0.9.21; README release links to
0.9.21. Snapshot-pinned model tests updated to current catalog truth.
2026-08-24 11:16:55 -07:00
iamtoruk
04cd6d0bf3 Merge remote-tracking branch 'origin/main' into fix/ft002-harness-launcher
# Conflicts:
#	src/doctor.ts
2026-08-24 07:15:08 -07:00
iamtoruk
d03b92f4ff feat: gate the partial answer on the client rendering the indicator
Each surface holds its OWN serve child: the Swift menubar (mac/), GNOME and
Windows clients spawn `codeburn serve --stdio` too, and their payload
decoders ignore an unknown key — so they would have shown a partial total
with no way to say so. The paint is now opt-in via
CODEBURN_SERVE_PROGRESSIVE, which the Electron app sets on its own serve
child because its renderer shows the indexing banner. Unset means the child
answers only with complete data, so no surface can present partial totals
unlabelled.
2026-08-23 05:18:29 -07:00
iamtoruk
be1b212a3f feat: surface-wide progressive hydration via an explicit payload marker
The TTY got the fast cold start in #1109; every JSON surface still waited
for the full parse because a payload has no way to say "partial". Add one.

- `hydration: { complete, indexedFiles, totalFiles }` on the menubar payload,
  add-only and emitted ONLY by the resident serve child (which polls, and
  therefore converges). Absence keeps meaning "complete", so every one-shot
  output is byte-identical and no script can be handed partial data.
- serve: a cold child answers `status --format menubar-json` from the files
  the requested period can show (same mtime floor as the TUI) and schedules
  the unfloored fill behind it. Partial answers are never memoized; requests
  waiting behind the fill are heartbeated by id so the client's no-output
  watchdog stays armed.
- `stale` keeps its own meaning: a first paint is fresh but partial, so it
  reports through `hydration` and never sets `stale`.
- desktop app and web dash render an honest "indexing history · N/M files"
  notice while `complete` is false.
2026-08-23 04:49:58 -07:00
Resham Joshi
c9dab7deb6
Merge pull request #1114 from getagentseal/feat/1112-quota-providers
feat: live quota for Gemini, Copilot, Antigravity + per-provider toggles
2026-08-23 04:29:16 -07:00
iamtoruk
287791e2a9 feat: live quota for Gemini, Copilot, and Antigravity in the desktop app
Extends the existing borrowed-credential quota module (claude/codex) with
three providers, per-provider settings toggles, and a provider display map:

- gemini: Code Assist loadCodeAssist/retrieveUserQuota via the Gemini CLI's
  on-disk OAuth creds; optional env-gated token refresh; retired-tier
  responses degrade to terminalFailure with migration guidance
- copilot: copilot_internal/user with editor-plugin headers; token read-only
  from hosts.json/apps.json with one re-read on 401; marked as an internal
  API that may drift, failures degrade honestly
- antigravity: loopback-only Connect-RPC probe of the local language server
  (ps/lsof discovery via execFile, csrf token for the app variant, agy CLI
  accepted tokenless); TLS relaxation scoped to 127.0.0.1; no OAuth fallback
- settings: per-provider quota switches (default on) persisted renderer-side,
  honored in the main-process service so disabled fetchers never run; IPC
  disabled-list filtered against the provider allowlist
- sanitizeError extended for Google (ya29.) and GitHub (gh*_) token shapes

Kimi audited, not duplicated: the macOS menubar already fetches Kimi quota
live (KimiSubscriptionService.swift); the electron app not surfacing it is a
pre-existing gap, unchanged here.
2026-08-23 03:13:47 -07:00
iamtoruk
8f39cc5487 feat: open the interactive dashboard on today, falling back to 7 days when today is empty
The unset default opened on 7 days everywhere. It now opens on today and
only falls back to 7 days when today holds no sessions yet — the decision
is made from the today-scoped slice of the parse the first paint already
runs, so the probe costs a filter, not a second pass.

Explicit selections are untouched: -p/--period, --day, --from/--to, the
TUI period keys, the app's persisted default period, and every one-shot
(--format json, report/sessions/status, the piped non-TTY render) keep
the 7-day default they had.

Fixes #1111
2026-08-23 02:39:41 -07:00
Resham Joshi
554d32eba6
Merge pull request #1098 from avs-io/fix/pr-tab-empty-period
Some checks failed
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
macOS Menubar CI / test (push) Has been cancelled
fix(desktop): say why the PR tab is empty
2026-08-22 13:08:51 -07:00
iamtoruk
21382bb8e6 copy: drop the em dash from the wider-period hint (repo copy rule) 2026-08-22 12:46:42 -07:00
iamtoruk
affd40e230 payload: add-only stale marker for read-only stale menubar serves (#771)
isSessionHydrationComplete() (parser.ts, PR #937) already reports when a
read-only serve skipped or staled real on-disk changes; thread it through
buildMenubarPayload as an optional stale field, present and true only on a
stale serve, always absent otherwise, so older/newer CLI-app pairs stay
compatible. Mirrors the field into the desktop renderer types and the macOS
menubar's Codable payload model (data layer only, no view change).

Drafted with minimax/MiniMax-M3 via local gateway.
2026-08-22 11:38:46 -07:00
Aditya Vikram Singh
0a43427924 fix(desktop): name a custom range on the empty PR tab
The empty note still used the dormant 30D label after a calendar
selection. Use the same rangeLabel as the TopBar caption.
2026-08-22 23:58:50 +05:30
Aditya Vikram Singh
b095091aad fix(desktop): thread PR-tab period through App
Empty copy was defaulting to Today because App never passed the selected
period. Labels now share PERIOD_LABELS; the wider hint queries Lifetime.
2026-08-22 23:46:46 +05:30
Aditya Vikram Singh
7b802f4dd0 fix(desktop): say why the PR tab is empty
Today/week/month often have no session-linked PRs, so the tab
looked broken. Name the selected period and, when All has rows,
point at that count. Do not invent gh links. Do not change
attribution or the default period.
2026-08-22 23:24:59 +05:30
iamtoruk
4eabda17d7 fix: bound the serve drain, reap orphans on Windows, and de-flake the grace test
G1a - the stdin-EOF drain is now bounded (Promise.race against 45s, overridable
via CODEBURN_SERVE_DRAIN_MS for the test). Generous enough that no legitimate
request answered at EOF comes near it, and an async-wedged one releases the
child instead of turning it into the orphan the drain was added to prevent. The
comment names the ceiling: a SYNCHRONOUSLY wedged parse never yields to this
timer or to any other JS path, and only a signal can end that process.

G1b - Windows is the majority of the desktop fleet and had no orphan escape at
all: no ps, and no stdin-close recourse once the app has crashed. The reap now
works there. tasklist cannot report argv (image name and window title only) and
wmic is gone from current Windows, so the command line comes from
Get-CimInstance Win32_Process. Match discipline is unchanged - exact argv - with
quoting normalized on both sides, because we record a plain space-joined argv
while Windows reports the real quoted command line; a path with spaces still
round-trips. serveCommandMatches is extracted and tested directly.

G2 - the grace test raced node's cold boot (a 300ms window). The child now emits
one byte the moment it is ready, so the watchdog window is measured from
READINESS, not from spawn: boot cost cannot eat it at any machine speed. Timeout
raised on top of that. 6/6 green under 8 busy loops.

G3 - a served request heartbeats for its whole duration now, not just its parse:
the wrap moved to the one seam (runCaptured) that also covers aggregation and
payload serialization, whose ~8s tail lands back-to-back with the parse's own
quiet stretches. The one-shot path keeps the parse-only wrap, where an 8s tail
is comfortably inside the window on its own, and the CHANGELOG now says exactly
that instead of claiming every phase.

G4 - stillCold() anchors to coldStartBegan ?? bootedAt.

Text: the cold-timeout bound no longer claims to restore the "locate the CLI"
recovery (that is a not-found state, never cold-flagged), and the CHANGELOG
states the >15min case honestly - the ceiling still ends it, but partial cache
saves mean successive polls converge rather than each rescanning from nothing.
2026-08-22 10:36:58 -07:00
iamtoruk
3317189dd6 fix: address adversarial review of the timeout watchdog (F1-F7)
F1 - `codeburn serve` lost an in-flight request's response when stdin closed
mid-flight: runStdioServe returned before its queue drained, and the explicit
process.exit(0) then hit runCaptured's monkeypatched exit, throwing ExitSignal
and exiting 1 with the frame never written. The finally now awaits the queue.

F2 - the SIGTERM rationale was false. armSignalCleanup unlinks the refresh lock
and re-raises; it publishes no partial cache, and a lock left by SIGKILL already
self-heals through the stale-pid takeover. SIGTERM-first is kept for the real
(smaller) benefit - a clean lock release instead of a takeover - and every
comment plus the CHANGELOG now says only that.

F3 - the cold gate had no exit. overviewWarmed only flips on success, so an
install that can never hydrate sat behind an indexing splash forever with no
error and no route to the CLI recovery. The cold claim now expires with the cold
window itself.

F4 - the real CLI does not heartbeat the way the demo did: a cold parse's
inter-provider cache save measured 31.6s of total silence, which a 45s window
survives only until the machine is 1.5x slower. Under CODEBURN_PROGRESS a
running parse now emits a keepalive every 10s regardless of phase, so silence
genuinely means stopped. Consumers that do not know the event ignore it.

F5 - the orphan-reap identity check matched any `cli.js` running `serve`. The
pidfile now records the exact argv and `ps -ww` must match it exactly.

F6 - bump() re-armed the watchdog after settle, leaving a timer finish() never
clears when a killed child's buffered output landed.

F7 - kill paths dropped the child from activeChildren before SIGTERM and the
SIGKILL backstop was unref'd, so a quit inside the 5s grace orphaned a child
that ignores SIGTERM. It now stays registered until it actually dies.

N8 - the silence test wrote its only byte at t~0, so it passed without the
re-arm. The byte now lands mid-window and the kill is asserted from it.
N9 - documented why mutations keep a plain total cap.
2026-08-22 09:58:59 -07:00
iamtoruk
d8bd428054 fix(app): give the mutation-restart and destroy paths the same SIGTERM grace
A settings mutation replaces the resident serve child, which may be mid-write:
the same lock hazard a timed-out read has, so it gets the same treatment.
restartAfterMutation and destroy now go through killGracefully instead of
SIGKILL. reapAll's one-shot loop stays a hard kill and now says why - quit has a
1.5s flush budget, shorter than the grace, so waiting one out would wedge the
quit, and a one-shot read holds no lock worth releasing.
2026-08-22 09:26:29 -07:00
iamtoruk
d4b7b48dc8 fix(app): no-output watchdog replaces fixed timeouts; graceful kills; cold floor everywhere; orphan serve reaping
The desktop app capped every read at 45s of TOTAL runtime, so a slow machine's
warm `optimize` (52.5s on 0.9.20, 39.6s on main) was SIGKILLed mid-parse and the
panel painted a red "timed out" that a 60s poll then reproduced forever.

- cli.ts: the read timeout is now a no-output watchdog. The window restarts on
  every byte the child writes (serve: every frame carrying that request's id),
  so only a genuinely silent child times out. A 15-minute absolute ceiling still
  reaps a livelocked child. Every read spawn sets CODEBURN_PROGRESS=1 so a long
  parse heartbeats through it; progress lines are stripped from any surfaced
  stderr so they can never become the error message.
- cli.ts: a timed-out child gets SIGTERM first and SIGKILL only after a 5s
  grace, on both the spawn and the serve branch. That lets the CLI's signal
  cleanup publish its partial parse and release the cross-process refresh lock
  instead of leaving both stale.
- main.ts: the 10-minute cold floor now covers EVERY read while the first
  hydration is still running, not just the overview. Sections start polling the
  moment `ready` flips (which an overview error also does), and a 45s section
  spawn queued behind the cold parse was killed on arrival.
- main.ts/renderer: a timeout raised while the hydration is still running is
  flagged `cold`, and the renderer keeps its indexing splash (and keeps the
  sections gated) instead of latching ready and painting CliErrorPanel. Only
  timeouts are flagged; a permission or nonzero failure still surfaces.
- cli.ts/main.ts: the resident serve child's pid is recorded under userData and
  a serve orphaned by a previous crash is reaped at launch, after `ps` confirms
  the pid still belongs to a codeburn serve.
- src/main.ts: `codeburn serve` exits explicitly once its stdin closes, so a
  handle outliving the transport cannot keep the child alive as an orphan.
2026-08-22 09:22:38 -07:00
iamtoruk
c73d8ff6c8 snap: scope the entries main gained after the tightening
The ported declaration was written against main as of c9e6e2ec. Main has since
added dsh and carries five entries that commit never saw, all of them still bare
tool roots, so the port dropped them rather than reintroduce what the store
rejected. Each is restored at the path its provider opens:

  .dsh/sessions                          dsh.ts reads <DSH_HOME>/sessions only
  .kiro/sessions                         CLI store at sessions/cli, v2 IDE store
                                         at sessions/<hash> — siblings
  .quickwork/{profiles.json,sessions,metrics}
                                         profiles.json names the profile bases;
                                         the legacy layout is sessions/sessions.db
                                         plus metrics/
  .config/Claude/local-agent-mode-sessions
                                         Claude Desktop's local-agent-mode store
  .config/Open Design/{runs,data/runs,namespaces}
                                         the three discovery roots open-design.ts
                                         probes under its data dir

.lingtai and .lingtai-tui stay dropped. A LingTai ledger lives at
.lingtai/<agent>/logs/token_ledger.jsonl, and personal-files has no wildcard for
the agent segment; .lingtai-tui only exists to enumerate project homes that could
not be read anyway. Goose is narrowed to .local/share/goose/sessions, which holds
the only file it opens.

Four entries stay whole roots because the provider reads a file sitting directly
in the root: .config/github-copilot (JetBrains stores nest under a variable
<ide>/<kind>/<storeId>) and .local/share/{opencode,crush,kilo}. The new
app/scripts/snap-grants.test.ts asserts every other entry is at least one level
below its tool root, so a bare root cannot come back unnoticed.
2026-08-21 03:42:09 -07:00
iamtoruk
3168699927 snap: scope personal-files to log subdirectories
Snap Store review found the declaration requested each tool's entire root
directory. Those roots hold configuration and, in several cases, credentials,
and personal-files read is recursive, so the request granted read of every AI
tool's credential store.

Every path is now the subdirectory the provider actually opens: .claude/projects,
.codex/sessions, .cline/data, .vibe/logs/session, .hermes/profiles, .mux/sessions
and so on; two are single files (.forge/.forge.db, .zcode/cli/db/db.sqlite). The
editor entries name only the extension folders that hold transcripts rather than
the editor's whole configuration. No bare tool root remains.

One credential file is requested openly instead of implicitly: .claude/.credentials.json,
read-only, used to call Anthropic's usage endpoint for the live plan gauge.

Codex's equivalent needs read-write on the Codex CLI's auth.json to rotate the
token, so neither that file nor a Codex root is declared and the Codex live
gauge is disabled under $SNAP. Codex usage and cost analytics are unaffected;
they come from the session rollouts.

Also declares five providers that were missing entirely and would have shown no
data under the snap: opencode, crush, goose, kilo, kimi-code. Drops .lingtai,
whose per-agent directory sits above the log folder and cannot be expressed
without wildcards.

Ports 4fe760aafe0492a674476c1ca64bf0dbdc673dde onto current main; the entries
main gained since that commit are reconciled separately.
2026-08-21 03:38:25 -07:00
iamtoruk
911bd3f486 fix(plans): say monthly budget, not calendar month, and fit 80 columns
The budget window comes from computePeriodFromResetDay, which builds an
anniversary period from plan.resetDay (1-28, settable per plan with
`codeburn plan set --reset-day`). "Calendar-month budget" and "Next
calendar reset" are therefore wrong for anyone who moved the reset day,
which is the same class of inaccuracy this change set exists to remove.
Say "budget" and "Next budget reset" instead, and use one wording across
the TUI and the desktop cards.

Both TUI lines truncate end-first at the terminal width. The headline had
grown past the point where an 80-column terminal still showed the
percentage, so it drops "vs ... /mo" for "/ $300.00 budget", and the
status line drops the clause repeating "budget" from the headline. At 80
columns the longest label (custom plans carry their provider) now fits
the percentage, and the status line still shows the projection.
2026-08-19 11:30:07 -07:00
Aditya Vikram Singh
610c70ecb9 fix(plans): update desktop FX budget-copy assertion 2026-08-19 20:53:18 +05:30
Aditya Vikram Singh
9646890638 fix(plans): call sticker-price bars a budget, not live quota
SuperGrok Heavy is a $300/mo preset compared to parsed API-equivalent
spend on a calendar month. CodexBar's live weekly window is a different
class. TUI and desktop copy now say budget / calendar month and deny
a live provider window. No Grok Connect is invented.
2026-08-19 20:07:02 +05:30
iamtoruk
b8ca581d02 Merge remote-tracking branch 'origin/main' into pr995-rebase 2026-08-18 08:49:11 -07:00
iamtoruk
117aa833cc fix(optimize): scope the apply-able subtotal to the local MCP subset
A mixed local + claude.ai connector finding is class `fix`, but `--apply`
only mutates the local servers. classTotals now credits the `fix` group
with `applyTokensSaved` when present, so the "Fix now (apply-able)"
subtotal, the "apply-able: ~$X" headline and `summary.byClass.fix` (CLI,
TUI and desktop all read these) describe what apply can actually recover.
The finding keeps the whole opportunity in its own `tokensSaved`.

Also fixes the desktop connector fixture, which predated the class/basis
fields, and adds class-level coverage: connector-only findings resolve to
`nudge` (no apply payload), a local server named like a connector stays
manual-only, and local-only findings keep their full subtotal.
2026-08-18 08:22:33 -07:00
iamtoruk
c5df60e69a merge: main into connector guidance branch
Kept both sides' act-report tests: the PR's partial-action baseline
capture block and main's applied-fix verdict block.
2026-08-18 08:17:25 -07:00
iamtoruk
3936284811 optimize: show the applied-fix verdicts in the TUI and desktop app
Same section, compact: one line per still-applied fix with the verdict
glyph, and the undo command for the ones that measured nothing. The app
reads appliedFixes[] off the optimize JSON, tolerating its absence from
an older CLI.
2026-08-18 03:15:12 -07:00
iamtoruk
5660909801 optimize: per-group subtotals in every finding render
Each class header now carries its own token/dollar subtotal and finding
count, so the apply-able slice is never mistaken for the whole board; the
headline savings line names that slice explicitly. CLI and TUI share one
classHeaderLine helper, the desktop app reads the same numbers from the
new summary.byClass in --format json (add-only; the three subtotals sum to
findingCount and potentialSavingsTokens).

Also scopes the SHELL_PROFILE_SCOPE comment to what is actually true: the
MCP deferral plans refuse to rewrite a shell profile, but bash-output-cap
appends its own marker block to one.
2026-08-18 02:40:00 -07:00
iamtoruk
7c54cf85c2 optimize: classify findings as fix/nudge/keep and mark measured vs estimated
Every finding now resolves to a class (apply-able fix, habit nudge, or
informational keep) and a basis (measured from provider-counted usage, or
estimated from a schema/heuristic model), both from one table next to the
FindingId union. The class follows the plan layer: an id is 'fix' only when
buildPlan routes it, and an instance drops to 'nudge' when it lacks the
payload or cause its builder needs.

CLI, TUI and the desktop app group findings under Fix now / Habits / FYI
with continuous numbering; the CLI header reports 'N measured · M
estimated' in place of the blanket 'Estimates only.' footer. The JSON
report gains class + basis per finding and summary.measuredSavingsUSD;
existing fields are unchanged. The menubar's top three follow the same
order, since every surface reads the sorted findings list.

Sessions whose cost the provider never reported leave the cost-outliers
peer math; when nothing else is priced the comparison falls back to them
and the finding reports itself as estimated instead of disappearing.
2026-08-18 02:26:57 -07:00
iamtoruk
09965f93ae fix(dsh): cap zstd decode, coerce usage fields, scope the snap read
Security audit follow-ups on the DeepSeek Harness provider.

- **Decompression bomb.** Every zstd frame was decoded with no output bound, so
  a 16 KB crafted log expanded to ~916 MB of RSS (a 65 KB one declares 2 GB).
  Each frame now decodes under a 64 MB per-call cap, and the caps chain into a
  running per-file budget of MAX_SESSION_FILE_BYTES: a frame is given only the
  bytes the file has left, so node throws ERR_BUFFER_TOO_LARGE without
  allocating past the cap. The throw propagates out of the existing skip path,
  which discards the whole file rather than counting the frames read before the
  bomb, so a crafted tail cannot poison a partial total. The discovery header
  read takes the same per-frame cap. Measured on a 65 KB / 2 GB bomb: 916 MB
  -> 67 MB peak, zero calls emitted, one notice.
  Lines are still materialized eagerly; the byte budget bounds that, and making
  the read lazy would change readEventLines' contract for no further bound.
- **Usage type confusion.** Token fields were read with `?? 0` and never
  type-checked, so a string or array inputTokens flowed into the global totals
  and the persisted cache, where `0 + [1, 2]` becomes "01,2". They now go
  through numberOrZero (copilot.ts semantics: finite, positive, else 0).
  All-zero calls are still skipped.
- **Snap over-scope.** The personal-files read entry is `$HOME/.dsh/sessions`
  rather than all of `$HOME/.dsh`; the provider reads nothing else.
- **Third-party notice.** scanZstdFrames is transcribed from
  @deepseek-ai/dsh-session-persistence-jsonl. The published npm package is
  BSD-3-Clause (Copyright (c) 2026, DeepSeek) while the monorepo source
  declares MIT for the same package; THIRD_PARTY_NOTICES.md reproduces the
  stricter of the two and ships via package.json `files`.
2026-08-17 17:28:41 -07:00
iamtoruk
d9a9486b6d docs(dsh): finish the provider registration checklist
docs/providers/NEW_PROVIDER.md items the PR had not reached yet, plus the two
surfaces that are functional rather than cosmetic:

- docs/providers/dsh.md and its row in the provider index, documenting the
  storage layout, the JSONL-backend-only scope (the opt-in SQLite persistence
  backend is not read), and that DSH is a developer preview whose format
  version 0 implies no compatibility.
- CHANGELOG entry under Unreleased.
- README provider count 40 -> 41 and a data-locations row.
- app/package.json: $HOME/.dsh in the snap personal-files allowlist, without
  which the Linux snap build cannot read DSH sessions at all.
- UsageDataChangeGuard: the DSH sessions root, without which the menubar never
  notices a new session and does not refresh.
- Bumps the dsh parse version, since the parser's attribution changed.
2026-08-17 10:59:58 -07:00
iamtoruk
cb9fa71623 perf(parser): parallelize the cold Claude parse across worker threads
Reading, decoding and line-parsing a Claude session JSONL is per-file work that
touches nothing shared, so it moves onto worker_threads for a large cold parse.
parseClaudeFileFull() is the extracted unit both sides run; a worker runs it
against an empty dedup set and returns the result as a JSON string, and the
parent installs results in the order the serial loop would. Everything with
cross-file state stays on the main thread, and a file whose message ids were
already claimed (or whose worker failed) re-parses in-process, so the output is
identical to the serial path.

Thread count is decided per parse: never with <=2 cores, under 2 GB free memory,
fewer than 200 pending whole-file re-parses or under 200 MB behind them, so warm
and incremental runs spawn nothing. CODEBURN_PARSE_WORKERS overrides it. The pool
is terminated when the parse ends, so the resident serve child accumulates no
threads.
2026-08-17 01:19:20 -07:00
iamtoruk
f02eaf12c5 fix(serve): close review follow-ups on the shared-cache PR
Clear the per-directory Codex and Antigravity memo maps in the resident RSS
guard; document the single cache-dir rule (XDG_CACHE_HOME no longer
consulted, ledger migrated); stop output-overflow terminations from spending
the resident's unexpected-death budget.
2026-08-16 18:29:11 -07:00
Aditya Vikram Singh
8883ec4412 docs: clarify authoritative Windows installer build 2026-08-13 20:17:28 +05:30
Aditya Vikram Singh
864991fe3f fix(release): verify all desktop assets 2026-08-13 20:01:00 +05:30
Aditya Vikram Singh
fe6d183573 fix(release): harden Windows installer verification 2026-08-13 19:47:31 +05:30
Aditya Vikram Singh
eb7ebb534c ci(desktop): guard Windows installer artifacts 2026-08-13 19:33:26 +05:30
ozymandiashh
a95a2c5bf8 fix(desktop): close cache and lifecycle review gaps 2026-08-12 20:31:17 +03:00
Aditya Vikram Singh
1ea3d68e71 fix(optimize): label connector actions clearly 2026-08-12 21:51:57 +05:30
ozymandiashh
d8d343e83a perf(desktop): share cache state and eliminate duplicate cold hydration 2026-08-12 17:16:41 +03:00
iamtoruk
af97effb0d flathub: arch restriction file used in the submission 2026-08-12 06:12:06 -07:00
Resham Joshi
4c53e07d26
Merge pull request #976 from getagentseal/fix/flathub-tool-count
flathub: correct supported tool count to 40
2026-08-12 06:03:56 -07:00
iamtoruk
c755a3c521 snap: strict confinement with read-only personal-files plug
Classic has no fitting store category (per forum feedback) and strict is
electron-builder's well-supported path. One personal-files plug grants
read-only access to the dot-directories the supported tools write session
logs into; the app's own config and cache stay in the snap's private area.
2026-08-12 05:20:15 -07:00
iamtoruk
2eaad264b9 flathub: correct supported tool count to 40 2026-08-11 22:02:11 -07:00
iamtoruk
7e99b875d5 packaging: Snap Store target and CI, Flathub manifest
- electron-builder snap target (classic confinement) with package:snap script
- build-snap.yml: builds on ubuntu, uploads artifact, optional store publish
  gated on workflow_dispatch input + SNAPCRAFT_STORE_CREDENTIALS secret
- app/flathub: manifest repacking the released deb, desktop file, AppStream
  metainfo, and submission runbook
2026-08-11 08:16:39 -07:00
iamtoruk
0090decf7d chore: bump to 0.9.20 2026-08-10 15:38:47 -07:00
iamtoruk
6cd11ab436 fix(app,dash): punchcard tooltip never crops; remove the menubar Workflow strip
- Punchcard tooltips flipped below the cursor on the top rows (the
  overflow container clips anything above its own edge) and clamp
  horizontally near the strip edges. Applied to both the app and dash
  copies of the component.
- The menubar Workflow strip is removed (view, strip model, tests):
  the popover is the compact surface and the workflow metrics live in
  the desktop app, dash and TUI. The payload keeps emitting the block
  (add-only contract; the other surfaces read it).
2026-08-10 14:35:40 -07:00
iamtoruk
411d52f924 feat(app,menubar): surface parity — Spend punchcard in the app, Pull requests strip in the menubar
The release's new capabilities were unevenly surfaced: the Spend
punchcard existed only on the web dashboard, and the menubar decoded
nothing of the PR-attribution block the payload already carries.

- Desktop app: new Punchcard component (hour-of-day x weekday spend
  matrix, ported from the dash and restyled to the app's tokens) on the
  Spend page. Fed by a dedicated getTimeline bridge channel that fetches
  the payload WITH history.timeline; every other fetch keeps the lean
  --no-timeline path, and the serve child makes the extra fetch cheap.
  Hides gracefully when the payload has no timeline (older CLI).
- Menubar: PullRequestsSection renders the top three PRs by attributed
  spend under the Workflow strip; MenubarPayload now decodes the
  pullRequests block (decodeIfPresent, so older payloads are unchanged).
  Hidden when absent or empty.

Deliberately NOT ported: codex Tok/s (reads rollout files per session -
too heavy for payload cadence; stays a TUI/report analytics view) and
the punchcard in the menubar (a 7x24 matrix has no legible place in a
compact popover).

App suite 468 green (bridge channel pinned, mocks extended), swift
build + 156 tests green, CLI suite green.
2026-08-10 14:29:26 -07:00
iamtoruk
d78ab77d96 perf: resident serve process for the desktop app — panel fetches in milliseconds
Every CLI spawn on a large corpus pays seconds of fixed cost before any
query work: node boot, a 100MB+ session-cache JSON.parse, the discovery +
fingerprint sweep, and serve-time classification. The desktop app spawns
one CLI per panel fetch, so it pays that cost per panel.

codeburn serve --stdio is the same CLI kept warm: the app holds one child,
sends {id, args} per line, and gets the command's stdout back. Three layers
make it fast, each disabled outside serve so one-shot runs stay byte-exact:

- loadCache memo (session-cache.ts): the parsed cache object is reused
  while a stat() shows the file unchanged; saveCache updates it
  write-through. A rewrite by another process still forces a fresh read.
- burst reuse (parser.ts, CODEBURN_PARSE_BURST_MS, serve sets 10s): panel
  bursts anchor their range ends at their own new Date(), so the exact-key
  memo never hits in real traffic; within the window a re-anchored range is
  served by trimming the previous parse instead of re-running discovery.
- fresh commander program per request (main.ts buildProgram factory),
  because commander option state is sticky across parses.

The server allows only the app's read queries (status/overview/models/
sessions/compare/yield/spend/optimize/audit), refuses everything else
(client falls back to a spawn), serializes requests, and converts
process.exit into a caught signal. The app starts the child once at
startup; requests route through it only when warm, cold-start keeps the
spawn path with its progress events, any serve failure falls back to a
spawn, and three child deaths disable serve for the app run.

Measured on a real 17B-token corpus: panel fetches drop from ~7.4s per
spawn to 5-900ms warm (sessions/spend 5ms, status 898ms). One-shot CLI
output verified byte-identical against the pre-branch baseline.
2026-08-10 09:43:44 -07:00