@adonis-agora/agent-dashboard 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/package.json +7 -4
- package/skills/agent-console-client/SKILL.md +205 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @adonis-agora/agent-dashboard
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#82](https://github.com/DavideCarvalho/adonis-agora-agent/pull/82) [`59be4e9`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/59be4e9943b47d79ce5f006c1d24b9b39e052f84) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Add TanStack Intent agent skills. Both packages now ship a `skills/` directory
|
|
8
|
+
(five skills under @adonis-agora/agent: setup, tools, governance, personas &
|
|
9
|
+
multi-agent delegation, offline testing; one under @adonis-agora/agent-dashboard:
|
|
10
|
+
the governance console client) that coding agents can load as structured,
|
|
11
|
+
verified documentation. The directories are included in each package's `files`
|
|
12
|
+
allowlist, and `@tanstack/intent` is added as a devDependency so CI can enforce
|
|
13
|
+
skill validity via `intent validate`.
|
|
14
|
+
|
|
15
|
+
## 0.7.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#71](https://github.com/DavideCarvalho/adonis-agora-agent/pull/71) [`24c8d6f`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/24c8d6f81da6fc335ec96e4154f36a22ad762a8a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Delegation is authorizable, the actor directory is consumed, and `dashboard` is part of the typed config.
|
|
20
|
+
|
|
21
|
+
- **`delegatesTo` accepts an object edge.** A synthesized `ask_<target>` tool goes through the same
|
|
22
|
+
`RolesPolicy` gate as any other tool, and a bare-string edge carries neither `roles` nor `ability` —
|
|
23
|
+
which is ADMIN-only under `DefaultToolAuthorizer` and an outright deny under `authzToolAuthorizer`,
|
|
24
|
+
so delegation was unreachable under authz with no way to open it. `delegatesTo` now also takes
|
|
25
|
+
`{ agent, roles?, ability? }` (`DelegateEdge`), whose annotation lands on the synthesized spec. Bare
|
|
26
|
+
strings keep their existing fail-closed behaviour.
|
|
27
|
+
- **`ActorDirectory.resolveDisplay` is now called.** `actorDirectory` resolved into a provider field
|
|
28
|
+
that was never read, so governance surfaces always rendered raw refs. Every governance route
|
|
29
|
+
returning an `actorRef` now carries an optional `actorLabel`, filled from one batched directory
|
|
30
|
+
lookup per page and omitted for unknown refs. Fail-soft: an unbound or throwing directory leaves the
|
|
31
|
+
rows exactly as the read-model produced them.
|
|
32
|
+
- **`AgentConfig.dashboard` is typed.** The console's config block was read through an untyped
|
|
33
|
+
`config.get('agent.dashboard')` string path, so writing it inside `defineConfig({ ... })` was an
|
|
34
|
+
excess-property error. It is now a declared `AgentConfig` field and the provider reads it off the
|
|
35
|
+
typed config.
|
|
36
|
+
- **`engines.node` is a range again.** Both packages published an exact version (`v22.23.2` /
|
|
37
|
+
`v26.7.0`), which warns on install for every consumer on any other Node and hard-fails under
|
|
38
|
+
`engine-strict`. Renovate's global `rangeStrategy: "pin"` had rewritten the ranges; `engines` is now
|
|
39
|
+
excluded from pinning so it cannot happen again.
|
|
40
|
+
- JSDoc: the published source carried Portuguese doc comments (`sse.ts`, `spi/tool.ts`, `base-tool.ts`,
|
|
41
|
+
`stores/factory.ts`, `rag/qdrant-store.ts`, `ai-tool-ref.ts`) — translated to English. Corrected the
|
|
42
|
+
stale cost formula on `AgentGovernanceQueries` (it omitted the cache-token split), the attachment
|
|
43
|
+
allow-list default (an exact-match list of 7 types, not `text/*`), and two NestJS-era references.
|
|
44
|
+
|
|
3
45
|
## 0.7.0
|
|
4
46
|
|
|
5
47
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/agent-dashboard",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Read-only governance console (spend by model/actor, usage trend, recent threads & tool calls, quota) for @adonis-agora/agent — a dependency-light React SPA served by a thin AdonisJS provider. Part of the Agora ecosystem.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"files": [
|
|
33
33
|
"dist/",
|
|
34
34
|
"README.md",
|
|
35
|
-
"CHANGELOG.md"
|
|
35
|
+
"CHANGELOG.md",
|
|
36
|
+
"skills"
|
|
36
37
|
],
|
|
37
38
|
"sideEffects": false,
|
|
38
39
|
"peerDependencies": {
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"@adonisjs/core": "7.4.0",
|
|
50
|
+
"@tanstack/intent": "^0.3.2",
|
|
49
51
|
"@testing-library/react": "16.3.2",
|
|
50
52
|
"@types/node": "22.20.1",
|
|
51
53
|
"@types/react": "19.2.18",
|
|
@@ -57,10 +59,10 @@
|
|
|
57
59
|
"typescript": "5.9.3",
|
|
58
60
|
"vite": "7.3.6",
|
|
59
61
|
"vitest": "3.2.7",
|
|
60
|
-
"@adonis-agora/agent": "0.
|
|
62
|
+
"@adonis-agora/agent": "0.25.3"
|
|
61
63
|
},
|
|
62
64
|
"engines": {
|
|
63
|
-
"node": "
|
|
65
|
+
"node": ">=20.6.0"
|
|
64
66
|
},
|
|
65
67
|
"keywords": [
|
|
66
68
|
"adonisjs",
|
|
@@ -73,6 +75,7 @@
|
|
|
73
75
|
"react",
|
|
74
76
|
"spend",
|
|
75
77
|
"usage",
|
|
78
|
+
"tanstack-intent",
|
|
76
79
|
"agora"
|
|
77
80
|
],
|
|
78
81
|
"scripts": {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-console-client
|
|
3
|
+
description: >-
|
|
4
|
+
Serve and consume the @adonis-agora/agent governance console: the embedded provider
|
|
5
|
+
from node ace configure @adonis-agora/agent vs the standalone @adonis-agora/
|
|
6
|
+
agent-dashboard provider (register only ONE — duplicate-route error), the shared
|
|
7
|
+
config('agent').dashboard block { enabled, path, authorize, onUnauthenticated },
|
|
8
|
+
decideDashboardMount refusal reasons (no-governance-gate vs no-governance-queries),
|
|
9
|
+
and the framework-free AgentClient from @adonis-agora/agent-dashboard/client
|
|
10
|
+
(spendByModel/spendByActor/usageTrend/quotaToday/recentThreads/recentToolCalls/
|
|
11
|
+
pendingApprovals/approveToolCall) with window.__AGENT_DASHBOARD_BASE__ resolution.
|
|
12
|
+
Use for "dashboard blank or won't mount", "governance console 404", "build a custom
|
|
13
|
+
governance UI", "approve HITL calls from the browser".
|
|
14
|
+
metadata:
|
|
15
|
+
type: core
|
|
16
|
+
library: "@adonis-agora/agent-dashboard"
|
|
17
|
+
library_version: "0.7.1"
|
|
18
|
+
framework: adonisjs
|
|
19
|
+
sources:
|
|
20
|
+
- "DavideCarvalho/adonis-agent:packages/dashboard/README.md"
|
|
21
|
+
- "DavideCarvalho/adonis-agent:packages/dashboard/src/client/agent-client.ts"
|
|
22
|
+
- "DavideCarvalho/adonis-agent:packages/adonis/src/dashboard/define_config.ts"
|
|
23
|
+
- "DavideCarvalho/adonis-agent:packages/adonis/docs/governance/dashboard.mdx"
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# The governance console & browser client
|
|
27
|
+
|
|
28
|
+
`@adonis-agora/agent-dashboard` is a read-only React SPA over `@adonis-agora/agent`'s
|
|
29
|
+
cross-actor `/agent/governance/*` read routes — spend by model/actor, usage trend,
|
|
30
|
+
threads, tool calls, quota, and the HITL approvals inbox. Since agent@0.21.0 the same SPA
|
|
31
|
+
ships EMBEDDED in `@adonis-agora/agent`; this package remains for standalone installs and
|
|
32
|
+
for its framework-free browser client.
|
|
33
|
+
|
|
34
|
+
## Setup
|
|
35
|
+
|
|
36
|
+
Most apps need nothing: `node ace configure @adonis-agora/agent` registers an embedded
|
|
37
|
+
provider that serves the SPA at `<path>/dashboard` once `governanceAuthorize` exists:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// config/agent.ts — the gate is what makes the console mount at all.
|
|
41
|
+
import { defineConfig } from '@adonis-agora/agent'
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
model: () => aiSdkModel(openai('gpt-4o-mini')),
|
|
45
|
+
store: 'lucid',
|
|
46
|
+
stores: { lucid: stores.lucid() },
|
|
47
|
+
actorResolver: new AuthActorResolver(),
|
|
48
|
+
governanceAuthorize: (actor) => actor.roles?.includes('ADMIN') ?? false,
|
|
49
|
+
// dashboard: { enabled: true, path: '/agent/dashboard', authorize: (actor) => actor.roles?.includes('ADMIN') ?? false },
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Standalone install (independent release cadence):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm add @adonis-agora/agent-dashboard
|
|
57
|
+
node ace add @adonis-agora/agent-dashboard
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// adonisrc.ts — AFTER the agent provider; register only ONE of the two.
|
|
62
|
+
providers: [
|
|
63
|
+
() => import('@adonis-agora/agent/agent_provider'),
|
|
64
|
+
() => import('@adonis-agora/agent-dashboard'),
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Both providers read the SAME `config('agent').dashboard` block, so switching between them
|
|
69
|
+
needs no config change.
|
|
70
|
+
|
|
71
|
+
Source: `packages/dashboard/README.md`, `packages/adonis/README.md` ("Dashboard").
|
|
72
|
+
|
|
73
|
+
## Core patterns
|
|
74
|
+
|
|
75
|
+
### Pattern 1 — drive the governance API with `AgentClient`
|
|
76
|
+
|
|
77
|
+
The framework-free fetch client mirrors the read surface plus pricing and HITL actions;
|
|
78
|
+
it sends `credentials: 'same-origin'`, so the host's session cookie gates every call
|
|
79
|
+
exactly as the routes do server-side:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { AgentClient } from '@adonis-agora/agent-dashboard/client'
|
|
83
|
+
|
|
84
|
+
const client = new AgentClient({ baseUrl: '/agent' })
|
|
85
|
+
|
|
86
|
+
const byModel = await client.spendByModel({ fromDay: '2026-03-01', toDay: '2026-03-07' })
|
|
87
|
+
const trend = await client.usageTrend({ fromDay: '2026-03-01', toDay: '2026-03-07' })
|
|
88
|
+
const quota = await client.quotaToday()
|
|
89
|
+
const approvals = await client.pendingApprovals({})
|
|
90
|
+
|
|
91
|
+
// HITL decisions from your own UI:
|
|
92
|
+
await client.approveToolCall(runId, toolCallId)
|
|
93
|
+
await client.rejectToolCall(runId, toolCallId, 'not this account')
|
|
94
|
+
|
|
95
|
+
// Pricing table maintenance:
|
|
96
|
+
await client.upsertPrice({ modelId: 'gpt-4o-mini', inputPricePer1m: 0.15, outputPricePer1m: 0.6 })
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Non-2xx responses throw `AgentApiError` carrying the HTTP status for branching.
|
|
100
|
+
|
|
101
|
+
Source: `packages/dashboard/src/client/agent-client.ts`, `packages/dashboard/src/client/index.ts`.
|
|
102
|
+
|
|
103
|
+
### Pattern 2 — redirect to login via `onUnauthenticated`
|
|
104
|
+
|
|
105
|
+
The one denial `authorize` never sees is a failed actor resolution. Set a response header
|
|
106
|
+
inside the hook (typically a redirect); the gate detects it and skips its default
|
|
107
|
+
`401 { error }` JSON write:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// config/agent.ts
|
|
111
|
+
export default defineConfig({
|
|
112
|
+
// ...
|
|
113
|
+
dashboard: {
|
|
114
|
+
onUnauthenticated: (ctx) => ctx.response.redirect('/login'),
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Return normally without touching the response and the default JSON is written instead —
|
|
120
|
+
the hook is safe to omit or use purely for logging.
|
|
121
|
+
|
|
122
|
+
Source: `packages/adonis/src/dashboard/define_config.ts`
|
|
123
|
+
(`AgentDashboardUnauthenticatedHook`).
|
|
124
|
+
|
|
125
|
+
### Pattern 3 — diagnose a dead console from the boot warning
|
|
126
|
+
|
|
127
|
+
`decideDashboardMount(enabled, governanceAuthorize, governanceQueries)` produces three
|
|
128
|
+
distinct refusals, each logged with wording that names the responsible knob:
|
|
129
|
+
|
|
130
|
+
| Condition | Reason | Fix |
|
|
131
|
+
| --- | --- | --- |
|
|
132
|
+
| `enabled: false` | `disabled` | intentional; no warning |
|
|
133
|
+
| no `governanceAuthorize` | `no-governance-gate` | set the gate (or `() => true` deliberately) |
|
|
134
|
+
| `governanceQueries: false` | `no-governance-queries` | remove the `false` / wire a read-model |
|
|
135
|
+
|
|
136
|
+
The two non-disabled warnings are deliberately DIFFERENT strings so an operator staring
|
|
137
|
+
at a 404 can tell from the log alone which knob is responsible.
|
|
138
|
+
|
|
139
|
+
Source: `packages/adonis/src/dashboard/define_config.ts` (`decideDashboardMount`,
|
|
140
|
+
warning constants).
|
|
141
|
+
|
|
142
|
+
## Common mistakes
|
|
143
|
+
|
|
144
|
+
### HIGH — registering both dashboard providers
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
// Wrong — embedded AND standalone serve <path>/dashboard.
|
|
148
|
+
providers: [
|
|
149
|
+
() => import('@adonis-agora/agent/agent_provider'),
|
|
150
|
+
() => import('@adonis-agora/agent/dashboard_provider'),
|
|
151
|
+
() => import('@adonis-agora/agent-dashboard'),
|
|
152
|
+
]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
// Correct — exactly one of the two.
|
|
157
|
+
providers: [
|
|
158
|
+
() => import('@adonis-agora/agent/agent_provider'), // its embedded dashboard_provider rides along
|
|
159
|
+
]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Mechanism: both mounts claim the same route path, so boot throws AdonisJS's duplicate
|
|
163
|
+
route error — the app fails to start rather than double-serving.
|
|
164
|
+
Source: `packages/dashboard/README.md` ("Register only ONE of the two providers"),
|
|
165
|
+
`packages/adonis/README.md`.
|
|
166
|
+
|
|
167
|
+
### MEDIUM — setting only `dashboard.enabled: true` and expecting panels to load
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
// Wrong — enabled is necessary but NOT sufficient; every panel but Quota reads gated routes.
|
|
171
|
+
dashboard: { enabled: true }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
// Correct — the data gate is governanceAuthorize on the AGENT config.
|
|
176
|
+
governanceAuthorize: (actor) => actor.roles?.includes('ADMIN') ?? false,
|
|
177
|
+
dashboard: { enabled: true },
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Mechanism: without a gate the console refuses to mount entirely (boot warning naming the
|
|
181
|
+
knob) because it would otherwise load and fail on every panel — serving a shell that can
|
|
182
|
+
only 404 its own data is worse than not serving it.
|
|
183
|
+
Source: `packages/adonis/src/dashboard/define_config.ts` (`decideDashboardMount`,
|
|
184
|
+
`NO_GOVERNANCE_GATE_WARNING`).
|
|
185
|
+
|
|
186
|
+
### LOW — hard-coding `/agent` as the client base in a custom UI
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
// Wrong — breaks the moment the app mounts the agent under a prefix like /api/agent.
|
|
190
|
+
const client = new AgentClient({ baseUrl: '/agent' })
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
// Correct — reuse the same resolution the SPA uses: injected value wins, else derive.
|
|
195
|
+
import { resolveApiBase } from '@adonis-agora/agent-dashboard/client'
|
|
196
|
+
const client = new AgentClient({ baseUrl: resolveApiBase() })
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Mechanism: the provider injects the exact mounted base as `window.__AGENT_DASHBOARD_BASE__`
|
|
200
|
+
when serving index.html; absent that, `deriveApiBase(location.pathname)` strips the
|
|
201
|
+
trailing `/dashboard` segment — `/api/agent/dashboard` → `/api/agent`.
|
|
202
|
+
Source: `packages/dashboard/src/client/api-base.ts` (`resolveApiBase`, `deriveApiBase`).
|
|
203
|
+
|
|
204
|
+
See also: `agent-governance/SKILL.md` — the `governanceAuthorize` gate this console
|
|
205
|
+
consumes through; `agent-setup/SKILL.md` — seeding prices so `upsertPrice` rows matter.
|