Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c80edb9
refine(bug-report): home-abbreviate the captured workspace path
boggspa Jun 4, 2026
5fa16b5
polish(bug-report): title counter, animated saved-check, crisper seve…
boggspa Jun 4, 2026
6357596
feat(empty-states): warmer no-chats / no-runs / no-workspaces with th…
boggspa Jun 4, 2026
aba7aa5
polish(welcome): crisper dashboard tabs + mascot on the empty-range s…
boggspa Jun 4, 2026
2929682
tweak: use Avenir Next for AGBench default font
boggspa Jun 4, 2026
5aab0d7
fix(provider-accents): ensemble hierarchy tiles use the canonical pro…
boggspa Jun 4, 2026
1b3207a
fix(theme): Diff Studio + File Editor read correctly on light themes
boggspa Jun 4, 2026
a9d5482
tweak: replace status rails with rim highlights
boggspa Jun 4, 2026
f7c9e21
tweak: update general setting defaults
boggspa Jun 4, 2026
ad0b27f
feat(ensemble): per-round cost/latency readout + surface escalation s…
boggspa Jun 4, 2026
9f82ff5
fix(store): deleteChat now cleans up its run-event ledger + artifacts
boggspa Jun 4, 2026
f177987
feat(approvals): optional intent note on approval decisions
boggspa Jun 4, 2026
53fa14f
feat(ensemble): rename-stable participant identity (#pN handle + 'ren…
boggspa Jun 4, 2026
1329b3b
feat(capabilities): elicit + delegate as first-class capability rows
boggspa Jun 4, 2026
b6eb1f7
feat(composer): native-shell console rim + amber posture pill (rim sl…
boggspa Jun 4, 2026
6def607
feat: add update pill and changelog sheet
boggspa Jun 4, 2026
d3add65
feat(composer): seat the input in an inner module within a contrast-g…
boggspa Jun 4, 2026
7f2a54c
feat(git): add safe desktop git service
boggspa Jun 4, 2026
ac94ee1
feat(composer): full-width hard-corner input module + black/white fra…
boggspa Jun 4, 2026
1e18d43
fix(composer): inner module now reaches the right edge (full-bleed wi…
boggspa Jun 4, 2026
8d348fc
fix(git): harden desktop git workflow
boggspa Jun 4, 2026
4de6ac8
fix(ipc): register changelog-snapshot/mark-changelog-seen validators
boggspa Jun 4, 2026
06cc4ee
style(composer): pure black/white outer frame (no theme tint)
boggspa Jun 4, 2026
89da6a1
feat(composer): wire Git controls to preload API (real commit/PR flow)
boggspa Jun 4, 2026
dca4a07
fix(composer): solid outer frame + themed inner in light mode
boggspa Jun 4, 2026
434fb65
fix(composer): lift schedule/runtime row above the input (single chats)
boggspa Jun 4, 2026
48b6e5b
fix(kimi): coalesce tool calls into one card + show filenames
boggspa Jun 4, 2026
723550e
refactor(composer): gate Create-PR on canonical githubPrReadiness
boggspa Jun 4, 2026
525e274
docs(onboarding): add composer Git-flow tip
boggspa Jun 4, 2026
8bd03ee
style(composer): align above-rows with the solid console frame
boggspa Jun 4, 2026
ad0e539
style(composer): move permission picker after the + button (default s…
boggspa Jun 4, 2026
5cfa476
style(composer): reflect console redesign in onboarding + Settings pr…
boggspa Jun 4, 2026
f173fde
chore(release): 1.0.73
boggspa Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,38 @@ Notable changes to AGBench, the local-first macOS desktop workbench for running
and reviewing AI coding agents. Entries are user-facing highlights; execution,
history, and workspace state stay on your machine throughout.

## 1.0.73 — 2026-06-04

### Added
- **Commit & open PRs from the composer** — the Review changes menu now drives a
real Git flow: see your branch and changed files, write a message and Stage all
& Commit, then Create PR once the branch is pushed and ready (gated on a live
readiness check).
- **Clearer Ensemble cost & escalation** — each round shows real vs. estimated
spend (a latency line + an "API-equivalent" badge on estimates), and the
orchestrator's escalation signals surface inline, so a multi-seat panel's value
is visible rather than guessed.
- **Optional "why?" on approvals** — attach a short intent note when you allow or
deny an agent action; it's recorded in the approval ledger.

### Changed
- **Refined native composer** — the AGBench shell is now a cohesive console: the
input sits in a framed module (solid black/white outer frame, theme-tone inner
panel + provider rim, full-bleed and squared), the Ensemble / Create-PR / Steer
rows match the same solid frame, and the permission picker sits up front beside
the + button. Onboarding and Settings → Appearance previews reflect the new look.
- **Deleting a chat tidies up after itself** — removing a chat now also clears
that chat's own run-forensic artifacts (and only those).

### Fixed
- **Kimi tool calls** — repeated calls coalesce into a single inline card that
updates in place (instead of stacking) and now show the target filename,
matching the other providers.
- **Bug Report Refinement** — tidied how the in-app reporter shows your workspace
(now a friendly `~/…` label).
- Onboarding, empty states, the welcome dashboard, provider accent colours, and
the Diff Studio / File Editor light themes all got polish + readability fixes.

## 1.0.72 — 2026-06-04

### Security
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agbench",
"version": "1.0.72",
"version": "1.0.73",
"description": "AGBench desktop workspace",
"main": "./out/main/index.js",
"author": "Chris Izatt",
Expand Down
104 changes: 104 additions & 0 deletions src/main/AppStoreDeleteChat.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import fs from 'fs'
import { join } from 'path'
import { AppStore } from './store'
import type { ChatRecord, ChatRun } from './store/types'

const userDataPath = vi.hoisted(() => `/tmp/agentbench-delete-chat-test-${process.pid}`)

vi.mock('electron', () => ({
app: {
getPath: () => userDataPath
}
}))

const runEventPath = (runId: string): string => join(userDataPath, 'run-events', `${runId}.jsonl`)
const artifactDir = (runId: string): string => join(userDataPath, 'run-artifacts', runId)

function makeRun(runId: string): ChatRun {
return { runId, startedAt: '2026-05-08T00:00:00.000Z' }
}

function seedRunFiles(runId: string): void {
fs.mkdirSync(join(userDataPath, 'run-events'), { recursive: true })
fs.writeFileSync(runEventPath(runId), `{"runId":"${runId}"}\n`, 'utf8')
fs.mkdirSync(artifactDir(runId), { recursive: true })
fs.writeFileSync(join(artifactDir(runId), 'stdout.log'), 'stream\n', 'utf8')
}

function saveChatWithRuns(appChatId: string, runs: ChatRun[]): ChatRecord {
const chat: ChatRecord = {
appChatId,
scope: 'workspace',
chatKind: 'single',
provider: 'gemini',
title: appChatId,
workspaceId: 'workspace-1',
workspacePath: '/repo',
createdAt: 1,
updatedAt: 1,
archived: false,
messages: [],
runs
}
AppStore.saveChat(chat)
return chat
}

describe('AppStore.deleteChat run cleanup', () => {
beforeEach(() => {
fs.rmSync(userDataPath, { recursive: true, force: true })
fs.mkdirSync(join(userDataPath, 'chats'), { recursive: true })
})

it('removes the deleted chat run-event files and artifacts', () => {
saveChatWithRuns('chat-a', [makeRun('run-1'), makeRun('run-2')])
seedRunFiles('run-1')
seedRunFiles('run-2')

expect(fs.existsSync(runEventPath('run-1'))).toBe(true)
expect(fs.existsSync(artifactDir('run-1'))).toBe(true)

AppStore.deleteChat('chat-a')

// Chat JSON gone (behaviour preserved).
expect(fs.existsSync(join(userDataPath, 'chats', 'chat-a.json'))).toBe(false)
// Both runs' forensic files removed.
expect(fs.existsSync(runEventPath('run-1'))).toBe(false)
expect(fs.existsSync(artifactDir('run-1'))).toBe(false)
expect(fs.existsSync(runEventPath('run-2'))).toBe(false)
expect(fs.existsSync(artifactDir('run-2'))).toBe(false)
})

it('leaves a sibling chat with a prefix-similar run id untouched', () => {
// chat-a owns `run-1`; sibling chat-b owns `run-1-extra` whose id has
// `run-1` as a string prefix. A prefix/readdir-based delete would wrongly
// catch the sibling's files; an exact-name delete must not.
saveChatWithRuns('chat-a', [makeRun('run-1')])
saveChatWithRuns('chat-b', [makeRun('run-1-extra')])
seedRunFiles('run-1')
seedRunFiles('run-1-extra')

AppStore.deleteChat('chat-a')

// Deleted chat's run is gone...
expect(fs.existsSync(runEventPath('run-1'))).toBe(false)
expect(fs.existsSync(artifactDir('run-1'))).toBe(false)
// ...but the sibling's prefix-similar run is fully intact.
expect(fs.existsSync(runEventPath('run-1-extra'))).toBe(true)
expect(fs.existsSync(artifactDir('run-1-extra'))).toBe(true)
expect(fs.existsSync(join(userDataPath, 'chats', 'chat-b.json'))).toBe(true)
})

it('succeeds when a run-event file is already missing', () => {
// run-1 has files, run-2 was never persisted (missing on disk).
saveChatWithRuns('chat-a', [makeRun('run-1'), makeRun('run-2')])
seedRunFiles('run-1')
expect(fs.existsSync(runEventPath('run-2'))).toBe(false)

expect(() => AppStore.deleteChat('chat-a')).not.toThrow()

expect(fs.existsSync(runEventPath('run-1'))).toBe(false)
expect(fs.existsSync(join(userDataPath, 'chats', 'chat-a.json'))).toBe(false)
})
})
44 changes: 44 additions & 0 deletions src/main/AppStoreSettings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import fs from 'fs'
import { AppStore } from './store'

const userDataPath = vi.hoisted(() => `/tmp/agbench-settings-test-${process.pid}`)

vi.mock('electron', () => ({
app: {
getPath: () => userDataPath
}
}))

describe('AppStore settings defaults', () => {
beforeEach(() => {
fs.rmSync(userDataPath, { recursive: true, force: true })
fs.mkdirSync(userDataPath, { recursive: true })
})

it('defaults packaged update checks to the stable channel', () => {
expect(AppStore.getSettings().updateChannel).toBe('stable')
})

it('normalizes persisted changelog metadata on load', () => {
AppStore.updateSettings({
lastSeenChangelogVersion: ' 1.0.72 ',
pendingUpdateChangelog: {
version: ' 1.0.73 ',
releaseName: ' AGBench 1.0.73 ',
releaseDate: ' 2026-06-04T12:00:00.000Z ',
releaseNotes: [{ version: ' 1.0.73 ', note: 'Updater UI.' }, { version: '', note: '' }]
}
})

expect(AppStore.getSettings()).toMatchObject({
lastSeenChangelogVersion: '1.0.72',
pendingUpdateChangelog: {
version: '1.0.73',
releaseName: 'AGBench 1.0.73',
releaseDate: '2026-06-04T12:00:00.000Z',
releaseNotes: [{ version: '1.0.73', note: 'Updater UI.' }]
}
})
})
})
Loading
Loading