@adonis-agora/agent 0.25.2 → 0.25.4

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.
Files changed (181) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/assets/spa/assets/index-C1YrXiT7.js +9 -0
  3. package/dist/assets/spa/assets/index-DVnnBvfn.css +1 -0
  4. package/dist/assets/spa/index.html +2 -2
  5. package/dist/providers/agent_provider.d.ts.map +1 -1
  6. package/dist/providers/agent_provider.js +1 -1
  7. package/dist/providers/agent_provider.js.map +1 -1
  8. package/dist/providers/dashboard_provider.d.ts.map +1 -1
  9. package/dist/providers/dashboard_provider.js.map +1 -1
  10. package/dist/providers/mcp_provider.d.ts.map +1 -1
  11. package/dist/providers/mcp_provider.js.map +1 -1
  12. package/dist/src/actor-resolver.d.ts.map +1 -1
  13. package/dist/src/actor-resolver.js.map +1 -1
  14. package/dist/src/agent-deps-factory.d.ts.map +1 -1
  15. package/dist/src/agent-deps-factory.js.map +1 -1
  16. package/dist/src/agent-loop.d.ts.map +1 -1
  17. package/dist/src/agent-loop.js.map +1 -1
  18. package/dist/src/agent-registry.d.ts.map +1 -1
  19. package/dist/src/agent-service.d.ts.map +1 -1
  20. package/dist/src/agent-service.js.map +1 -1
  21. package/dist/src/ai-sdk/ai-sdk-model.d.ts.map +1 -1
  22. package/dist/src/ai-sdk/ai-sdk-model.js.map +1 -1
  23. package/dist/src/ai-sdk/attachment-fetch-downloader.js.map +1 -1
  24. package/dist/src/ai-sdk/index.d.ts +1 -1
  25. package/dist/src/ai-sdk/index.d.ts.map +1 -1
  26. package/dist/src/ai-sdk/index.js.map +1 -1
  27. package/dist/src/ai-tool-ref.d.ts.map +1 -1
  28. package/dist/src/authorizer.d.ts.map +1 -1
  29. package/dist/src/authorizer.js.map +1 -1
  30. package/dist/src/authz/authz-actor-resolver.d.ts.map +1 -1
  31. package/dist/src/authz/authz-tool-authorizer.d.ts.map +1 -1
  32. package/dist/src/authz/index.d.ts +3 -3
  33. package/dist/src/authz/index.d.ts.map +1 -1
  34. package/dist/src/authz/index.js +1 -1
  35. package/dist/src/authz/index.js.map +1 -1
  36. package/dist/src/base-tool.d.ts.map +1 -1
  37. package/dist/src/base-tool.js.map +1 -1
  38. package/dist/src/client/chat-client.d.ts.map +1 -1
  39. package/dist/src/client/chat-client.js.map +1 -1
  40. package/dist/src/client/index.d.ts +2 -2
  41. package/dist/src/client/index.d.ts.map +1 -1
  42. package/dist/src/client/index.js +1 -1
  43. package/dist/src/client/index.js.map +1 -1
  44. package/dist/src/dashboard/index.d.ts +1 -1
  45. package/dist/src/dashboard/index.d.ts.map +1 -1
  46. package/dist/src/dashboard/index.js.map +1 -1
  47. package/dist/src/data/index.d.ts +5 -5
  48. package/dist/src/data/index.d.ts.map +1 -1
  49. package/dist/src/data/index.js +3 -3
  50. package/dist/src/data/index.js.map +1 -1
  51. package/dist/src/data/sql-validator.d.ts.map +1 -1
  52. package/dist/src/data/sql-validator.js.map +1 -1
  53. package/dist/src/data/table-access.d.ts.map +1 -1
  54. package/dist/src/data/tenant-scope.d.ts.map +1 -1
  55. package/dist/src/data/tenant-scope.js.map +1 -1
  56. package/dist/src/define_config.d.ts +3 -3
  57. package/dist/src/define_config.d.ts.map +1 -1
  58. package/dist/src/define_config.js +1 -1
  59. package/dist/src/define_config.js.map +1 -1
  60. package/dist/src/durable/agent-run-workflow.d.ts.map +1 -1
  61. package/dist/src/durable/agent-run-workflow.js.map +1 -1
  62. package/dist/src/durable/durable-agent-runner.d.ts.map +1 -1
  63. package/dist/src/durable/durable-agent-runner.js.map +1 -1
  64. package/dist/src/durable/index.d.ts +2 -2
  65. package/dist/src/durable/index.d.ts.map +1 -1
  66. package/dist/src/durable/index.js +2 -2
  67. package/dist/src/durable/index.js.map +1 -1
  68. package/dist/src/hooks/tools.d.ts.map +1 -1
  69. package/dist/src/hooks/tools.js.map +1 -1
  70. package/dist/src/in-process-sink.d.ts.map +1 -1
  71. package/dist/src/index.d.ts +57 -57
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +46 -46
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/mcp/auth.js.map +1 -1
  76. package/dist/src/mcp/define_config.d.ts +3 -3
  77. package/dist/src/mcp/define_config.d.ts.map +1 -1
  78. package/dist/src/mcp/define_config.js +1 -1
  79. package/dist/src/mcp/define_config.js.map +1 -1
  80. package/dist/src/mcp/index.d.ts +4 -4
  81. package/dist/src/mcp/index.d.ts.map +1 -1
  82. package/dist/src/mcp/index.js +2 -2
  83. package/dist/src/mcp/index.js.map +1 -1
  84. package/dist/src/mcp/server.js +1 -1
  85. package/dist/src/mcp/server.js.map +1 -1
  86. package/dist/src/rag/chunk.js.map +1 -1
  87. package/dist/src/rag/embedding-retriever.d.ts.map +1 -1
  88. package/dist/src/rag/embedding-retriever.js.map +1 -1
  89. package/dist/src/rag/hybrid-retriever.d.ts.map +1 -1
  90. package/dist/src/rag/hybrid-retriever.js.map +1 -1
  91. package/dist/src/rag/index.d.ts +8 -8
  92. package/dist/src/rag/index.d.ts.map +1 -1
  93. package/dist/src/rag/index.js +6 -6
  94. package/dist/src/rag/index.js.map +1 -1
  95. package/dist/src/rag/ingest.js.map +1 -1
  96. package/dist/src/rag/keyword-retriever.d.ts.map +1 -1
  97. package/dist/src/rag/keyword-retriever.js.map +1 -1
  98. package/dist/src/rag/memory-vector-store.d.ts.map +1 -1
  99. package/dist/src/rag/memory-vector-store.js.map +1 -1
  100. package/dist/src/rag/pg-vector-store.d.ts.map +1 -1
  101. package/dist/src/rag/pg-vector-store.js.map +1 -1
  102. package/dist/src/rag/qdrant-store.d.ts.map +1 -1
  103. package/dist/src/rag/qdrant-store.js +3 -3
  104. package/dist/src/rag/qdrant-store.js.map +1 -1
  105. package/dist/src/rag/reranking-retriever.d.ts.map +1 -1
  106. package/dist/src/rag/reranking-retriever.js.map +1 -1
  107. package/dist/src/rag/vector-store.d.ts.map +1 -1
  108. package/dist/src/rag/vector-store.js.map +1 -1
  109. package/dist/src/rag-media/index.d.ts +4 -4
  110. package/dist/src/rag-media/index.d.ts.map +1 -1
  111. package/dist/src/rag-media/index.js +4 -4
  112. package/dist/src/rag-media/index.js.map +1 -1
  113. package/dist/src/rag-media/media-rag-ingestion.d.ts.map +1 -1
  114. package/dist/src/rag-media/media-rag-ingestion.js +2 -2
  115. package/dist/src/rag-media/media-rag-ingestion.js.map +1 -1
  116. package/dist/src/rag-media/text-extractor.d.ts.map +1 -1
  117. package/dist/src/rag-media/text-extractor.js.map +1 -1
  118. package/dist/src/react/use-agent-chat.js.map +1 -1
  119. package/dist/src/redis-token-stream-sink.d.ts.map +1 -1
  120. package/dist/src/redis-token-stream-sink.js.map +1 -1
  121. package/dist/src/runners/inline-agent-runner.d.ts.map +1 -1
  122. package/dist/src/runners/inline-agent-runner.js.map +1 -1
  123. package/dist/src/stores/factory.d.ts.map +1 -1
  124. package/dist/src/stores/factory.js.map +1 -1
  125. package/dist/src/stores/ledger-quota.d.ts.map +1 -1
  126. package/dist/src/stores/ledger-quota.js.map +1 -1
  127. package/dist/src/stores/lucid-governance-queries.d.ts.map +1 -1
  128. package/dist/src/stores/lucid-governance-queries.js.map +1 -1
  129. package/dist/src/stores/lucid-pricing.d.ts.map +1 -1
  130. package/dist/src/stores/lucid-pricing.js.map +1 -1
  131. package/dist/src/stores/lucid-schema.d.ts +6 -6
  132. package/dist/src/stores/lucid-schema.d.ts.map +1 -1
  133. package/dist/src/stores/lucid.d.ts.map +1 -1
  134. package/dist/src/stores/lucid.js.map +1 -1
  135. package/dist/src/telescope/dashboard.js.map +1 -1
  136. package/dist/src/telescope/data-providers.d.ts.map +1 -1
  137. package/dist/src/telescope/extension.js +1 -1
  138. package/dist/src/telescope/extension.js.map +1 -1
  139. package/dist/src/telescope/index.d.ts +3 -3
  140. package/dist/src/telescope/index.d.ts.map +1 -1
  141. package/dist/src/telescope/index.js +2 -2
  142. package/dist/src/telescope/index.js.map +1 -1
  143. package/dist/src/telescope/rag-data-providers.js +1 -1
  144. package/dist/src/telescope/rag-data-providers.js.map +1 -1
  145. package/dist/src/testing/fake-embedding-provider.d.ts +1 -1
  146. package/dist/src/testing/fake-embedding-provider.d.ts.map +1 -1
  147. package/dist/src/testing/fake-embedding-provider.js.map +1 -1
  148. package/dist/src/testing/fake-media.d.ts +2 -2
  149. package/dist/src/testing/fake-media.d.ts.map +1 -1
  150. package/dist/src/testing/fake-media.js +2 -2
  151. package/dist/src/testing/fake-media.js.map +1 -1
  152. package/dist/src/testing/fake-model-provider.d.ts.map +1 -1
  153. package/dist/src/testing/fake-model-provider.js.map +1 -1
  154. package/dist/src/testing/in-memory-actor-directory.d.ts.map +1 -1
  155. package/dist/src/testing/in-memory-actor-directory.js.map +1 -1
  156. package/dist/src/testing/in-memory-attachment-staging.d.ts.map +1 -1
  157. package/dist/src/testing/in-memory-governance-queries.d.ts.map +1 -1
  158. package/dist/src/testing/in-memory-governance-queries.js.map +1 -1
  159. package/dist/src/testing/in-memory-pricing.d.ts.map +1 -1
  160. package/dist/src/testing/in-memory-quota.d.ts.map +1 -1
  161. package/dist/src/testing/in-memory-quota.js.map +1 -1
  162. package/dist/src/testing/in-memory-sink.d.ts.map +1 -1
  163. package/dist/src/testing/in-memory-store.d.ts.map +1 -1
  164. package/dist/src/testing/in-memory-store.js +0 -1
  165. package/dist/src/testing/in-memory-store.js.map +1 -1
  166. package/dist/src/testing/index.d.ts +8 -8
  167. package/dist/src/testing/index.d.ts.map +1 -1
  168. package/dist/src/testing/index.js +8 -8
  169. package/dist/src/testing/index.js.map +1 -1
  170. package/dist/src/tool-discovery.js.map +1 -1
  171. package/dist/src/tool-registry.d.ts.map +1 -1
  172. package/dist/src/tool-registry.js.map +1 -1
  173. package/dist/stubs/main.js.map +1 -1
  174. package/package.json +19 -15
  175. package/skills/agent-governance/SKILL.md +233 -0
  176. package/skills/agent-personas-agents/SKILL.md +220 -0
  177. package/skills/agent-setup/SKILL.md +273 -0
  178. package/skills/agent-testing/SKILL.md +236 -0
  179. package/skills/agent-tools/SKILL.md +270 -0
  180. package/dist/assets/spa/assets/index-DshcMnMD.css +0 -1
  181. package/dist/assets/spa/assets/index-HVy8whBx.js +0 -9
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: agent-personas-agents
3
+ description: >-
4
+ Shape @adonis-agora/agent behavior per request: personas (Persona { id, label,
5
+ systemPrompt string | PromptBuilder, allowedTools }, defaultPersona, the
6
+ /agent/threads/personas/catalog route), named agents (agents: AgentDefinition[] with
7
+ tools/maxSteps/actorResolver overrides), delegatesTo multi-agent delegation via
8
+ synthesized ask_<target> agent-kind tools and DelegateEdge { agent, roles, ability },
9
+ HITL approval flow over POST /agent/tool-call/approve|reject with pending_approval
10
+ status, maxSteps budgeting, and PromptContext/basePrompt composition. Use for
11
+ "persona picker", "orchestrator delegates to specialists", "ask_researcher denied",
12
+ "action tool hangs waiting for approval", "prompt builder".
13
+ metadata:
14
+ type: core
15
+ library: "@adonis-agora/agent"
16
+ library_version: "0.25.2"
17
+ framework: adonisjs
18
+ sources:
19
+ - "DavideCarvalho/adonis-agent:packages/adonis/docs/authoring/personas-and-agents.mdx"
20
+ - "DavideCarvalho/adonis-agent:packages/adonis/docs/streaming-and-http.mdx"
21
+ - "DavideCarvalho/adonis-agent:packages/adonis/src/types.ts"
22
+ - "DavideCarvalho/adonis-agent:packages/adonis/src/agent-loop.ts"
23
+ ---
24
+
25
+ # Personas, named agents & delegation
26
+
27
+ The default setup is one agent. Two features extend it: **personas** reshape a single
28
+ agent per request (prompt + optional tool allow-list), and **named agents** with
29
+ `delegatesTo` turn one assistant into a small team that hands work around through
30
+ synthesized `ask_<target>` tools.
31
+
32
+ ## Setup
33
+
34
+ Personas on the implicit default agent; the caller selects one via `POST /agent/chat`'s
35
+ `persona` field, and it pins to the thread:
36
+
37
+ ```ts
38
+ // config/agent.ts
39
+ export default defineConfig({
40
+ model: () => aiSdkModel(openai('gpt-4o-mini')),
41
+ store: 'lucid',
42
+ stores: { lucid: stores.lucid() },
43
+ actorResolver: new AuthActorResolver(),
44
+ defaultAgent: {
45
+ systemPrompt: 'You are the assistant for our store.',
46
+ defaultPersona: 'shopper',
47
+ personas: [
48
+ {
49
+ id: 'shopper',
50
+ label: 'Shopping assistant',
51
+ systemPrompt: 'Help the customer find and track products. Be concise.',
52
+ allowedTools: ['getOrder', 'searchProducts'],
53
+ },
54
+ {
55
+ id: 'support',
56
+ label: 'Support agent',
57
+ systemPrompt: 'Help resolve issues. You may issue refunds with approval.',
58
+ allowedTools: ['getOrder', 'issueRefund'],
59
+ },
60
+ ],
61
+ },
62
+ })
63
+ ```
64
+
65
+ A persona's `systemPrompt` resolves WITH the agent's base prompt available as
66
+ `basePrompt`, so it can wrap rather than discard; it may be a `PromptBuilder`
67
+ (`(ctx: PromptContext) => string | Promise<string>`) composed from actor/persona/
68
+ pageContext. Render a picker from `GET /agent/threads/personas/catalog`.
69
+
70
+ Source: `packages/adonis/docs/authoring/personas-and-agents.mdx`.
71
+
72
+ ## Core patterns
73
+
74
+ ### Pattern 1 — orchestrator delegating to named specialists
75
+
76
+ Each `delegatesTo` edge auto-registers an `agent`-kind tool named `ask_<target>`
77
+ (non-alphanumerics become underscores) whose input is `{ task }`. You never write its
78
+ handler — the loop runs the target and feeds the answer back:
79
+
80
+ ```ts
81
+ // config/agent.ts
82
+ export default defineConfig({
83
+ // ...
84
+ defaultAgent: {
85
+ name: 'orchestrator',
86
+ systemPrompt: 'You coordinate specialists. Delegate work, then summarize.',
87
+ delegatesTo: [
88
+ { agent: 'researcher', roles: ['ANALYST', 'ADMIN'] }, // role-based gate
89
+ { agent: 'billing', ability: 'agent.delegate.billing' }, // ability-based gate (authz)
90
+ ],
91
+ },
92
+ agents: [
93
+ { name: 'researcher', systemPrompt: 'You research questions using read tools.', tools: ['searchDocs'] },
94
+ { name: 'billing', systemPrompt: 'You handle billing. Refunds require approval.', tools: ['getInvoice', 'issueRefund'] },
95
+ ],
96
+ })
97
+ ```
98
+
99
+ When the target has a flat-string base prompt, it is appended to the delegate tool's
100
+ generated description so the orchestrator's model knows what the specialist is for.
101
+
102
+ Source: `packages/adonis/docs/authoring/personas-and-agents.mdx` ("Multi-agent
103
+ delegation", "How a delegate tool is named").
104
+
105
+ ### Pattern 2 — approving a paused `action` tool (HITL)
106
+
107
+ An `action` call records `pending_approval` and suspends the run; the client reads the
108
+ pending `toolCallId` from thread detail, then decides. The decision is scoped to one
109
+ `(runId, toolCallId)` pair, so one run can never approve another's call:
110
+
111
+ ```bash
112
+ curl http://localhost:3333/agent/tool-call/approve \
113
+ -H 'content-type: application/json' \
114
+ -d '{"runId":"<runId>","toolCallId":"<toolCallId>"}'
115
+
116
+ curl http://localhost:3333/agent/tool-call/reject \
117
+ -H 'content-type: application/json' \
118
+ -d '{"runId":"<runId>","toolCallId":"<toolCallId>","reason":"not this account"}'
119
+ ```
120
+
121
+ On reject, the reason is fed back to the model and the turn continues. A UI can also poll
122
+ `GET /agent/approvals/mine` — the calling actor's own pending approvals.
123
+
124
+ Source: `packages/adonis/docs/streaming-and-http.mdx` ("Human-in-the-loop").
125
+
126
+ ### Pattern 3 — cap runaway loops with `maxSteps`
127
+
128
+ `maxSteps` bounds model↔tool iterations per turn (default 8). Give a shallow specialist a
129
+ smaller budget than the orchestrator:
130
+
131
+ ```ts
132
+ agents: [
133
+ { name: 'summarizer', systemPrompt: 'Summarize in one pass.', maxSteps: 2 },
134
+ ]
135
+ ```
136
+
137
+ Source: `packages/adonis/src/types.ts` (`AgentDefinition.maxSteps`),
138
+ `packages/adonis/src/agent-loop.ts` (`const maxSteps = deps.maxSteps ?? 8`).
139
+
140
+ ## Common mistakes
141
+
142
+ ### HIGH — bare-string `delegatesTo` edges under non-admin callers or authz
143
+
144
+ ```ts
145
+ // Wrong — declares NO roles and NO ability.
146
+ delegatesTo: ['researcher']
147
+ ```
148
+
149
+ ```ts
150
+ // Correct — say who may delegate.
151
+ delegatesTo: [{ agent: 'researcher', roles: ['ANALYST', 'ADMIN'] }]
152
+ ```
153
+
154
+ Mechanism: a delegate tool goes through the SAME gate as every other tool. A bare string
155
+ carries nothing, so under `DefaultToolAuthorizer` it is ADMIN-only, and under the authz
156
+ Bouncer adapter a spec without `ability` is denied outright — the edge becomes uncalled
157
+ for everyone but admins.
158
+ Source: `packages/adonis/docs/authoring/personas-and-agents.mdx` ("Authorizing a
159
+ delegation"), `packages/adonis/src/types.ts` (`DelegateEdge`).
160
+
161
+ ### MEDIUM — debugging "the orchestrator ignored the specialist" as a prompt problem
162
+
163
+ ```ts
164
+ // Symptom — the model apologizes and answers alone; no visible error anywhere.
165
+ // The delegation was DENIED and recorded failed:
166
+ // { toolName: 'ask_researcher', status: 'failed', error: 'Tool "ask_researcher" is not allowed...' }
167
+ ```
168
+
169
+ ```ts
170
+ // Correct move — check the tool-call feed (governance read-model / dashboard Tool calls panel).
171
+ // A denied delegation persists as failed — never auto_executed — and the error text goes back
172
+ // to the model as a tool result, which it paraphrases away silently.
173
+ ```
174
+
175
+ Mechanism: the loop re-applies all three gates before delegating (offered-tools check,
176
+ role check, input validation); on denial it records the call `failed`, skips
177
+ `agent.delegated`, and pushes `{ output: null, error }` into the transcript — so the run
178
+ completes normally while the delegation quietly never happened.
179
+ Source: `packages/adonis/src/agent-loop.ts` (agent-kind branch), `packages/adonis/docs/
180
+ authoring/personas-and-agents.mdx` ("A denied delegation is recorded, not retried").
181
+
182
+ ### MEDIUM — expecting a delegated sub-agent's action tools to wait for a human
183
+
184
+ ```ts
185
+ // Wrong assumption — nested researcher asks for approval; the run should pause.
186
+ defaultAgent: { delegatesTo: [{ agent: 'researcher' }] }
187
+ agents: [{ name: 'researcher', tools: ['issueRefund' /* action */] }]
188
+ ```
189
+
190
+ ```ts
191
+ // Correct — keep human-approval actions on the TOP-LEVEL agent only.
192
+ agents: [
193
+ { name: 'researcher', tools: ['searchDocs', 'getInvoice'] }, // read tools only
194
+ ]
195
+ ```
196
+
197
+ Mechanism: under the inline runner a sub-agent runs as a nested in-process loop with no
198
+ human attached, so its `action` tools are AUTO-DECLINED rather than left hanging; under
199
+ the durable runner the same delegation maps to a tracked child workflow instead.
200
+ Source: `packages/adonis/docs/authoring/personas-and-agents.mdx` (final paragraph).
201
+
202
+ ### LOW — using wall-clock time or randomness inside a PromptBuilder
203
+
204
+ ```ts
205
+ // Wrong — non-deterministic prompt; a durable replay recomputes a DIFFERENT prompt.
206
+ const builder: PromptBuilder = async (ctx) => `${ctx.basePrompt}\nNow: ${new Date().toISOString()}`
207
+ ```
208
+
209
+ ```ts
210
+ // Correct — derive from stable inputs (actor/persona/pageContext) resolved once per turn.
211
+ const builder: PromptBuilder = (ctx) => `${ctx.basePrompt}\nAssist ${ctx.actor.id} in ${ctx.actor.tenantRef ?? 'the org'}.`
212
+ ```
213
+
214
+ Mechanism: the loop resolves the effective system prompt exactly once per turn from
215
+ stable context; injecting entropy breaks replay determinism under the durable runner.
216
+ Source: `packages/adonis/docs/authoring/personas-and-agents.mdx` (replay-safety note),
217
+ `packages/adonis/src/agent-loop.ts` (`resolveSystemPrompt`).
218
+
219
+ See also: `agent-governance/SKILL.md` — the policy behind `roles`/`ability` gates;
220
+ `agent-testing/SKILL.md` — scripting multi-turn runs offline.
@@ -0,0 +1,273 @@
1
+ ---
2
+ name: agent-setup
3
+ description: >-
4
+ Set up @adonis-agora/agent in an AdonisJS app: node ace configure @adonis-agora/agent,
5
+ defineConfig in config/agent.ts (model via aiSdkModel from @adonis-agora/agent/ai-sdk,
6
+ stores.lucid()/stores.memory(), quotas.ledger/quotas.memory, pricingStores +
7
+ seedModelPrices + estimateCost, tokenSinks.redis multi-replica SSE sink,
8
+ AuthActorResolver identity seam), auto-created agent tables vs the published migration,
9
+ the cost fold (null vs $0.00), and route mounting under config path. Use for "set up
10
+ the agent", "config/agent.ts", "agent tables / migration", "costUsd is null or zero",
11
+ "quota not enforced", "401 on every agent route".
12
+ metadata:
13
+ type: core
14
+ library: "@adonis-agora/agent"
15
+ library_version: "0.25.2"
16
+ framework: adonisjs
17
+ sources:
18
+ - "DavideCarvalho/adonis-agent:packages/adonis/docs/getting-started.mdx"
19
+ - "DavideCarvalho/adonis-agent:packages/adonis/docs/config-reference.mdx"
20
+ - "DavideCarvalho/adonis-agent:packages/adonis/src/define_config.ts"
21
+ - "DavideCarvalho/adonis-agent:packages/adonis/src/stores/factory.ts"
22
+ ---
23
+
24
+ # Setting up @adonis-agora/agent
25
+
26
+ `@adonis-agora/agent` is a governed AI agent module for AdonisJS: one `defineConfig`
27
+ call in `config/agent.ts` wires a provider-agnostic agent loop, persistence, budgeting,
28
+ and the `/agent/*` HTTP+SSE routes. Only `model` is required — everything else is a
29
+ fail-closed default that this skill walks through.
30
+
31
+ ## Setup
32
+
33
+ Install and let the configure hook wire everything:
34
+
35
+ ```bash
36
+ pnpm add @adonis-agora/agent
37
+ node ace configure @adonis-agora/agent
38
+ ```
39
+
40
+ `configure` registers `@adonis-agora/agent/agent_provider` and
41
+ `@adonis-agora/agent/dashboard_provider` in `adonisrc.ts`, registers the Assembler init
42
+ hook that generates the typed `app/agent_tools` barrel, publishes `config/agent.ts`,
43
+ `config/mcp.ts`, and two migrations (the agent tables; a Postgres-only pgvector RAG chunk
44
+ table you can delete). Then add the model peer and write your config:
45
+
46
+ ```bash
47
+ pnpm add ai zod @ai-sdk/openai
48
+ ```
49
+
50
+ ```ts
51
+ // config/agent.ts
52
+ import { defineConfig, stores, AuthActorResolver } from '@adonis-agora/agent'
53
+ import { aiSdkModel } from '@adonis-agora/agent/ai-sdk'
54
+ import { openai } from '@ai-sdk/openai'
55
+
56
+ export default defineConfig({
57
+ // A lazy thunk keeps the provider SDK peer imported only at boot.
58
+ model: () => aiSdkModel(openai('gpt-4o-mini')),
59
+
60
+ store: 'lucid',
61
+ stores: {
62
+ memory: stores.memory(), // single-process; tests and scratch apps
63
+ lucid: stores.lucid(), // persists to SQL via @adonisjs/lucid
64
+ },
65
+
66
+ // Identity seam — fail-closed. Without a resolver every request throws → 401.
67
+ actorResolver: new AuthActorResolver(),
68
+
69
+ defaultAgent: {
70
+ systemPrompt: 'You are a helpful assistant for our app.',
71
+ },
72
+ })
73
+ ```
74
+
75
+ Run the migration for the six agent tables (`agent_thread`, `agent_message`,
76
+ `agent_tool_call`, `agent_token_usage`, `agent_model_pricing`, plus the run-lifecycle
77
+ columns):
78
+
79
+ ```bash
80
+ node ace migration:run
81
+ ```
82
+
83
+ You can skip it: the Lucid store provisions its own tables on first use
84
+ (`autoCreateTables` defaults to true), and the published migration delegates to the same
85
+ `createAgentTables` helper, so the two schemas can never drift.
86
+
87
+ Source: `packages/adonis/docs/getting-started.mdx`,
88
+ `packages/adonis/docs/stores/lucid.mdx`.
89
+
90
+ ## Core patterns
91
+
92
+ ### Pattern 1 — enforce a daily spend budget with `quotas.ledger`
93
+
94
+ Quotas are **opt-in**: omitting `quota` disables budgeting entirely (fail-open).
95
+ Configuring one makes the gate fail-closed — `check()` runs before the first model call
96
+ and over-budget turns throw `QuotaExceededError` before any tokens are spent.
97
+
98
+ ```ts
99
+ import { defineConfig, quotas } from '@adonis-agora/agent'
100
+
101
+ export default defineConfig({
102
+ // ...
103
+ quota: quotas.ledger({ limitTokens: 1_000_000 }), // enforced off the persisted usage ledger
104
+ })
105
+ ```
106
+
107
+ `quotas.memory({ limitTokens })` is the single-process variant for tests. The `day`
108
+ (`YYYY-MM-DD`, UTC) is stamped once by the runner, so day-bucketing stays deterministic
109
+ under durable replay.
110
+
111
+ Source: `packages/adonis/docs/governance/quota-and-cost.mdx`.
112
+
113
+ ### Pattern 2 — price turns with a `pricingStore` + `seedModelPrices`
114
+
115
+ With a Lucid main store, pricing mirrors the same connection automatically (table
116
+ auto-created) — no extra config. Cost per turn resolves in order: provider-reported
117
+ (a gateway) wins, else an estimate from the current price rows fetched once per run,
118
+ else `null` — never a fabricated `0`. Seed prices once (e.g. from an Ace command):
119
+
120
+ ```ts
121
+ import db from '@adonisjs/lucid/services/db'
122
+ import { seedModelPrices, LucidPricingStore } from '@adonis-agora/agent'
123
+
124
+ const pricing = new LucidPricingStore(db.connection())
125
+ await seedModelPrices(pricing, [
126
+ {
127
+ modelId: 'gpt-4o-mini',
128
+ inputPricePer1m: 0.15,
129
+ outputPricePer1m: 0.6,
130
+ cacheWritePricePer1m: 0.19, // optional — falls back to the input rate
131
+ cacheReadPricePer1m: 0.015, // optional — falls back to the input rate
132
+ },
133
+ ])
134
+ ```
135
+
136
+ Cache tokens are subsets of `inputTokens`, subtracted before the input rate applies.
137
+ Rollups (`spendByModel`/`spendByActor`) are sums, so an unpriced row contributes `$0.00`
138
+ even though the ledger row keeps `cost_usd: null` — if a total looks implausibly low,
139
+ check the pricing table first.
140
+
141
+ Source: `packages/adonis/docs/governance/quota-and-cost.mdx`,
142
+ `packages/adonis/src/spi/pricing-store.ts` (`seedModelPrices`, `estimateCost`).
143
+
144
+ ### Pattern 3 — multi-replica SSE with `tokenSinks.redis`
145
+
146
+ The default sink buffers each run's stream in process memory, which is single-replica: a
147
+ run started on pod A can only be re-attached on pod A. Switching sinks keeps the whole
148
+ SSE envelope identical.
149
+
150
+ ```ts
151
+ import { defineConfig, tokenSinks } from '@adonis-agora/agent'
152
+
153
+ export default defineConfig({
154
+ // ...
155
+ sink: tokenSinks.redis({ ttlSeconds: 3600 }), // replay keys self-expire (default 1h)
156
+ })
157
+ ```
158
+
159
+ Requires `@adonisjs/redis` installed and configured unless you pass `client:` (a
160
+ bring-your-own `RedisStreamClient`). Pair with `durable: true` for cross-instance resume.
161
+
162
+ Source: `packages/adonis/src/stores/factory.ts` (`tokenSinks`),
163
+ `packages/adonis/docs/streaming-and-http.mdx` ("Single-replica by default" callout).
164
+
165
+ ## Common mistakes
166
+
167
+ ### HIGH — omitting `actorResolver` and shipping 401s everywhere
168
+
169
+ ```ts
170
+ // Wrong — UnconfiguredActorResolver THROWS on every request; all routes answer 401.
171
+ export default defineConfig({ model: () => aiSdkModel(openai('gpt-4o-mini')) })
172
+ ```
173
+
174
+ ```ts
175
+ // Correct — read the authenticated principal; no identity is ever fabricated.
176
+ import { AuthActorResolver } from '@adonis-agora/agent'
177
+ export default defineConfig({
178
+ model: () => aiSdkModel(openai('gpt-4o-mini')),
179
+ actorResolver: new AuthActorResolver(),
180
+ })
181
+ ```
182
+
183
+ Mechanism: the provider installs `UnconfiguredActorResolver` by default, whose
184
+ `resolve()` unconditionally throws rather than invent a caller — the route replies 401
185
+ before the model ever runs.
186
+ Source: `packages/adonis/src/actor-resolver.ts` (`UnconfiguredActorResolver`),
187
+ `packages/adonis/docs/governance/authorization.mdx` ("No resolver, no service").
188
+
189
+ ### MEDIUM — expecting quota enforcement after configuring only a store
190
+
191
+ ```ts
192
+ // Wrong — no `quota` key means budgets are DISABLED (fail-open), even with a ledger full of usage.
193
+ export default defineConfig({ model, store: 'lucid', stores: { lucid: stores.lucid() } })
194
+ ```
195
+
196
+ ```ts
197
+ // Correct — opt in; the check then runs fail-closed before the first model call.
198
+ export default defineConfig({
199
+ ...,
200
+ quota: quotas.ledger({ limitTokens: 500_000 }),
201
+ })
202
+ ```
203
+
204
+ Mechanism: the loop skips both `check` and `bump` entirely when `deps.quota` is
205
+ undefined — there is no implicit limit.
206
+ Source: `packages/adonis/docs/governance/quota-and-cost.mdx` ("Quota is opt-in, and off
207
+ means open"), `packages/adonis/src/agent-loop.ts` (`if (deps.quota !== undefined)`).
208
+
209
+ ### HIGH — incrementing counters in a custom QuotaStore's `bump`
210
+
211
+ ```ts
212
+ // Wrong — double-counts every turn when paired with the persisted usage ledger.
213
+ const store: QuotaStore = {
214
+ async check(actorRef, day) { /* read my counter */ },
215
+ async bump(actorRef, day, tokens) { await redis.incrBy(key, tokens) },
216
+ }
217
+ ```
218
+
219
+ ```ts
220
+ // Correct — a ledger-backed store leaves bump empty: recordUsage already wrote the tokens.
221
+ class LedgerQuota implements QuotaStore {
222
+ async check(actorRef: string, day: string) { /* sum agent_token_usage */ }
223
+ async bump(): Promise<void> {} // deliberate no-op — the ledger is the source of truth
224
+ }
225
+ ```
226
+
227
+ Mechanism: `store.recordUsage(...)` has already persisted each turn's tokens;
228
+ `quotas.ledger.check` reads that ledger, so adding again in `bump` counts each turn twice.
229
+ Only a store keeping its OWN tally (`quotas.memory`, a bespoke Redis counter) should add.
230
+ Source: `packages/adonis/docs/governance/quota-and-cost.mdx` ("`bump` is a notification,
231
+ not necessarily an increment"), `packages/adonis/src/stores/ledger-quota.ts`.
232
+
233
+ ### MEDIUM — reading `$0.00` rollups as "this model is free"
234
+
235
+ ```ts
236
+ // Wrong — no prices seeded: usage rows persist costUsd null, but spendByModel sums them as 0.
237
+ await client.spendByModel({ fromDay: '2026-01-01', toDay: '2026-01-31' })
238
+ ```
239
+
240
+ ```ts
241
+ // Correct — seed current prices so estimates fold into costUsd.
242
+ await seedModelPrices(pricingStore, [{ modelId: 'gpt-4o-mini', inputPricePer1m: 0.15, outputPricePer1m: 0.6 }])
243
+ ```
244
+
245
+ Mechanism: an unpriced turn is deliberately `null` ("unknown") in the ledger, but SUM
246
+ rollups cannot preserve null — every unpriced row adds `$0.00`, which reads exactly like
247
+ a free model. Per-run/per-usage rows keep their nulls.
248
+ Source: `packages/adonis/docs/governance/quota-and-cost.mdx` ("`null` in the ledger,
249
+ `0` in the rollups").
250
+
251
+ ### MEDIUM — replacing the allowed attachment content types instead of extending
252
+
253
+ ```ts
254
+ // Wrong — REPLACES the default list: png/jpeg/pdf/csv now get 415.
255
+ attachmentAllowedContentTypes: ['application/json']
256
+ ```
257
+
258
+ ```ts
259
+ // Types match EXACTLY (no wildcards); spread the defaults you still want.
260
+ attachmentAllowedContentTypes: [
261
+ 'image/png', 'image/jpeg', 'image/gif', 'image/webp',
262
+ 'application/pdf', 'text/plain', 'text/csv',
263
+ 'text/markdown', // your addition
264
+ ],
265
+ ```
266
+
267
+ Mechanism: passing `attachmentAllowedContentTypes` replaces the default list rather than
268
+ merging, and matching is exact — no `text/*` prefix rule — so anything absent is rejected
269
+ with 415.
270
+ Source: `packages/adonis/src/define_config.ts` (`AgentConfig.attachmentAllowedContentTypes`).
271
+
272
+ See also: `agent-tools/SKILL.md` — registering tools the model can actually call;
273
+ `agent-governance/SKILL.md` — what `AuthActorResolver` feeds into.