chore: v2.8.23 — workspace groups, inner repos in Changes (opt-in), sub-agent tokens counted

Workspace groups: named, foldable, ordered sidebar sections kept per
account (agenthub.workspace.groups); a workspace joins one from its menu,
a drop on the header, a drag under a row, or Alt+Up/Down past a group's
edge; headers drag to reorder; a folded header shows its most urgent
state; New workspace in this group; fold/unfold all.

Inner repos: GET /api/lanes/:id/repos + ?repo= on git/fs routes; the
Changes panel shows a parent folder's repos only when Settings -> Git ->
Changes panel turns it on (agenthub.changes.innerRepos, off by default).

Cost: the hook writer adds a session's sub-agent transcripts to its
tokens and hours (server/lib/subagent-usage.js) — a sub-agent on the
session's own model was never counted live (~8% of a week here); an
imported session is re-raised on every pass, so a claude -p run first
seen with an empty transcript no longer stays at $0 (~10%). A one-time
raise-only catch-up (user_version 5) re-reads ~/.claude/projects.

Also: omp install script may redirect to its exact install.redirect URL
(#85, repo-links test exempts it), native paste in the terminal.
This commit is contained in:
2026-10-23 16:41:45 +07:00
parent 65a001dad4
commit c8bc4a95a9
54 changed files with 2521 additions and 112 deletions
+226 -1
View File
@@ -3,7 +3,7 @@
openapi: 3.0.3
info:
title: Agent Dashboard for Claude Code API
version: 2.8.20
version: 2.8.23
description: HTTP API for real-time Claude Code session monitoring, agent lifecycle tracking, analytics, pricing, hooks ingestion, and workflow intelligence.
contact:
name: Nguyễn Ngọc Trí Vĩ
@@ -12039,6 +12039,11 @@ paths:
required: false
schema:
type: integer
- name: repo
in: query
required: false
schema:
type: string
responses:
'200':
description: The file's bytes, as an attachment.
@@ -12082,6 +12087,11 @@ paths:
required: false
schema:
type: integer
- name: repo
in: query
required: false
schema:
type: string
requestBody:
required: true
content:
@@ -12143,6 +12153,11 @@ paths:
required: false
schema:
type: integer
- name: repo
in: query
required: false
schema:
type: string
responses:
'200':
description: The suggested message
@@ -14704,6 +14719,14 @@ paths:
type: integer
sessions:
type: integer
today:
type: object
description: Spent since the viewer's local midnight (`tz_offset`).
properties:
cost:
type: number
tokens:
type: integer
last7d:
type: object
properties:
@@ -17052,8 +17075,10 @@ paths:
schema:
type: string
enum:
- all
- mine
- hub
- unowned
- name: kind
in: query
schema:
@@ -18286,6 +18311,49 @@ paths:
description: ENOLANE — workspace not found.
'409':
description: ELIVE (a pane holds the session), ELOCKED, or EGIT (git refused, e.g. a dirty tree).
/api/lanes/{id}/repos:
get:
tags:
- Lanes
summary: The git repositories inside a workspace folder
description: 'For a parent folder holding several repos (server/lib/nested-repos.js): every directory up to 2 levels below the workspace folder that holds a `.git` entry, never through a symlink, a dot folder or `node_modules`, and never inside a repo already found; at most 50 repos and 5000 directory entries read. Disk reads only (no git call), cached 30 s; `?refresh=1` reads again. Each `path` (relative, `/`-separated) is what the /git and /fs routes take as `?repo=` — a value this walk did not produce answers 404 ENOREPO. `root` says whether the workspace folder itself is in a repo. A missing folder is an empty list.'
operationId: getLaneRepos
parameters:
- name: id
in: path
required: true
schema:
type: integer
- name: refresh
in: query
required: false
schema:
type: string
enum:
- '1'
responses:
'200':
description: '{root, items: [{path, name}], truncated}.'
content:
application/json:
schema:
type: object
properties:
root:
type: boolean
items:
type: array
items:
type: object
properties:
path:
type: string
name:
type: string
truncated:
type: boolean
'404':
description: ENOLANE — workspace not found.
/api/lanes/{id}/sources:
get:
tags:
@@ -22135,6 +22203,163 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/artifacts/{id}/claim:
post:
tags:
- Artifacts
summary: Take an unowned artifact
operationId: claimArtifact
description: Server mode. The user the row is suggested to (the owner of the workspace or home its session ran in), or an admin, becomes its owner. Audited. Same-origin only.
parameters:
- name: id
in: path
required: true
description: Artifact id.
schema:
type: string
responses:
'200':
description: Claimed.
content:
application/json:
schema:
type: object
properties:
item:
type: object
'400':
description: ELOCALMODE.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: 'EFORBIDDEN: not the suggested user.'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: ENOTFOUND.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: 'EOWNED: it already has an owner.'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/artifacts/{id}/decline:
post:
tags:
- Artifacts
summary: Say an unowned artifact offered to you is not yours
operationId: declineArtifact
description: Server mode. Drops the suggestion; the row stays unowned for an admin to place. Audited. Same-origin only.
parameters:
- name: id
in: path
required: true
description: Artifact id.
schema:
type: string
responses:
'200':
description: Declined.
content:
application/json:
schema:
type: object
properties:
id:
type: integer
declined:
type: boolean
'400':
description: ELOCALMODE.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: 'EFORBIDDEN: not the suggested user.'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: ENOTFOUND.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: EOWNED.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/artifacts/{id}/assign:
post:
tags:
- Artifacts
summary: Give an unowned artifact to a user (admin)
operationId: assignArtifact
description: Server mode, admin only. Body `{userId}` names an enabled user. Audited. Same-origin only.
parameters:
- name: id
in: path
required: true
description: Artifact id.
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- userId
properties:
userId:
type: integer
responses:
'200':
description: Assigned.
content:
application/json:
schema:
type: object
properties:
item:
type: object
'400':
description: ELOCALMODE / EBADFIELD.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: 'EFORBIDDEN: not an admin.'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: ENOTFOUND.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: EOWNED.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/settings/import:
post:
tags: