feat(ai-tools): Settings → AI Tools, Default/Other lane picker, per-run bypass switch (#40 A1)

Settings → Dashboard gains an AI Tools section (installed tools with
Enabled/Disabled and Set default, available ones with a homepage link,
Refresh). useAiTools shares one catalog copy across the app and replaces
LANE_TOOL_LABEL. The Add-lane modal picks Default (<tool>) or Other…; the
router row reads Native (<tool>) and disables AI Router profiles for tools
that cannot use one. The run form gives catalog tools resume and a red
per-run Bypass permissions switch (Codex too). Claude Code / Codex settings
tabs hide while that tool is disabled. The Workspace probes each lane tool's
CLI fresh.

Docs: CLAUDE.md tool/bypass rule, AGENTS.md, LANES.md, API.md, CLI.md,
server/README.md, OpenAPI fragment + regenerated openapi.yaml.
This commit is contained in:
2026-09-28 11:19:30 +07:00
parent 77917203fd
commit fcfebe44f1
33 changed files with 1306 additions and 153 deletions
+160
View File
@@ -60,6 +60,8 @@ tags:
description: Durable parallel-work lanes and dashboard-managed git worktrees
- name: Push
description: Web Push notification subscriptions and broadcast (VAPID); also fires native Electron notifications when hosted in the desktop app
- name: AI Tools
description: The catalog of AI coding CLIs a lane can run (server/data/ai-tools/*.json + DASHBOARD_AI_TOOLS_DIR), what is installed on PATH, and the user's enable/default choice
components:
parameters:
SessionIdPath:
@@ -4078,6 +4080,91 @@ components:
type: string
description: Response body or error message, truncated to 200 characters.
example: UnauthorizedRegistration
AiToolsTool:
type: object
required:
- id
- label
- installed
- enabled
- source
properties:
id:
type: string
example: antigravity
label:
type: string
example: Antigravity
command:
type: string
nullable: true
example: agy
homepageUrl:
type: string
nullable: true
installed:
type: boolean
path:
type: string
nullable: true
example: /home/u/.local/bin/agy
enabled:
type: boolean
source:
type: string
enum:
- builtin
- custom
launcher:
type: string
nullable: true
enum:
- claude
- codex
- null
prompt:
type: object
nullable: true
properties:
mode:
type: string
enum:
- argv
- flag
- after-start
resume:
type: boolean
description: Offers Resume last session
bypass:
type: boolean
description: Offers the per-run Bypass permissions switch
routerCapable:
type: boolean
description: Can use an AI Router profile
AiToolsView:
type: object
required:
- tools
- default
properties:
tools:
type: array
items:
$ref: '#/components/schemas/AiToolsTool'
default:
type: string
nullable: true
description: Effective default tool id
AiToolsError:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
SessionsFacetsResponse:
type: object
description: 'Facet values for the Sessions page filter UI: the distinct working directories (cwd) and the distinct data-source ids seen across all sessions.'
@@ -11442,6 +11529,79 @@ paths:
example:
error:
message: Push service unavailable
/api/ai-tools:
get:
tags:
- AI Tools
summary: List the AI Tools catalog
description: Every catalog tool with whether it was found on PATH, whether it is enabled, and the effective default (stored default if installed+enabled, else claude, else the first installed enabled tool, else null). Arg arrays are never returned.
operationId: aiToolsList
responses:
'200':
description: The catalog with detection and the effective default
content:
application/json:
schema:
$ref: '#/components/schemas/AiToolsView'
/api/ai-tools/refresh:
post:
tags:
- AI Tools
summary: Re-detect installed tools
description: Merges the login shell's PATH ($SHELL -ilc, 5 s timeout, off with AGENTHUB_SHELL_PATH=0) into the server's, re-reads the catalog and re-detects. Same-origin only.
operationId: aiToolsRefresh
responses:
'200':
description: The catalog with detection and the effective default
content:
application/json:
schema:
$ref: '#/components/schemas/AiToolsView'
/api/ai-tools/{id}:
put:
tags:
- AI Tools
summary: Enable/disable a tool or make it the default
description: A disabled tool is hidden from Add lane and a NEW lane for it answers 400 EDISABLED; existing lanes keep working. Disabling the default clears it. Same-origin only.
operationId: aiToolsUpdate
parameters:
- name: id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
enabled:
type: boolean
default:
type: boolean
enum:
- true
responses:
'200':
description: The catalog with detection and the effective default
content:
application/json:
schema:
$ref: '#/components/schemas/AiToolsView'
'400':
description: EBADDEFAULT (not installed or disabled) or EBADFIELD
content:
application/json:
schema:
$ref: '#/components/schemas/AiToolsError'
'404':
description: ENOTOOL — unknown id
content:
application/json:
schema:
$ref: '#/components/schemas/AiToolsError'
/api/sessions/facets:
get:
tags: