@adminforth/agent 2.0.9 → 2.1.0

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 (187) hide show
  1. package/README.md +333 -10
  2. package/dist/application/ports.d.ts +66 -0
  3. package/dist/application/runTurnUseCase.d.ts +68 -0
  4. package/dist/application/runTurnUseCase.js +358 -0
  5. package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +1 -1
  6. package/dist/{agent → domain}/systemPrompt.js +1 -1
  7. package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
  8. package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
  9. package/dist/index.d.ts +3 -2
  10. package/dist/index.js +40 -35
  11. package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
  12. package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
  13. package/dist/llm/agentModels.js +65 -0
  14. package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +11 -2
  15. package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +38 -6
  16. package/dist/llm/langGraphLlm.d.ts +25 -0
  17. package/dist/llm/langGraphLlm.js +54 -0
  18. package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
  19. package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
  20. package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
  21. package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
  22. package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
  23. package/dist/llm/streamAdapter.d.ts +18 -0
  24. package/dist/llm/streamAdapter.js +117 -0
  25. package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +12 -2
  26. package/dist/{sessionStore.js → persistence/sessionStore.js} +33 -0
  27. package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
  28. package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
  29. package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +10 -6
  30. package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
  31. package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
  32. package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
  33. package/dist/{agent/tools → tools}/index.d.ts +1 -1
  34. package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
  35. package/dist/{endpoints → transport/http}/context.d.ts +2 -2
  36. package/dist/transport/http/context.js +1 -0
  37. package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +3 -3
  38. package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +7 -1
  39. package/dist/transport/sse/sseWriter.d.ts +29 -0
  40. package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +15 -0
  41. package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
  42. package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
  43. package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
  44. package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
  45. package/dist/types.d.ts +10 -4
  46. package/package.json +11 -2
  47. package/.woodpecker/buildRelease.sh +0 -13
  48. package/.woodpecker/buildSlackNotify.sh +0 -46
  49. package/.woodpecker/release.yml +0 -57
  50. package/agent/checkpointer.ts +0 -329
  51. package/agent/languageDetect.ts +0 -92
  52. package/agent/middleware/apiBasedTools.ts +0 -174
  53. package/agent/middleware/openAiResponsesContinuation.ts +0 -92
  54. package/agent/middleware/sequenceDebug.ts +0 -429
  55. package/agent/models/AgentModeResolver.ts +0 -9
  56. package/agent/models/AgentModelFactory.ts +0 -28
  57. package/agent/runtime/AgentContext.ts +0 -30
  58. package/agent/runtime/AgentRuntime.ts +0 -89
  59. package/agent/simpleAgent.ts +0 -199
  60. package/agent/skills/registry.ts +0 -162
  61. package/agent/speech/SpeechTurnService.ts +0 -179
  62. package/agent/systemPrompt.ts +0 -133
  63. package/agent/toolCallEvents.ts +0 -135
  64. package/agent/tools/AgentToolProvider.ts +0 -28
  65. package/agent/tools/apiTool.ts +0 -66
  66. package/agent/tools/fetchSkill.ts +0 -68
  67. package/agent/tools/fetchToolSchema.ts +0 -47
  68. package/agent/tools/getUserLocation.ts +0 -45
  69. package/agent/tools/index.ts +0 -31
  70. package/agent/tools/navigateUser.ts +0 -210
  71. package/agent/turn/TurnContextBuilder.ts +0 -36
  72. package/agent/turn/TurnLifecycleService.ts +0 -47
  73. package/agent/turn/TurnPersistenceService.ts +0 -33
  74. package/agent/turn/TurnPromptBuilder.ts +0 -51
  75. package/agent/turn/TurnStreamConsumer.ts +0 -69
  76. package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
  77. package/agent/turn/turnTypes.ts +0 -100
  78. package/agentEvents.ts +0 -75
  79. package/agentTurnService.ts +0 -314
  80. package/apiBasedTools.ts +0 -702
  81. package/build.log +0 -67
  82. package/chatSurfaceService.ts +0 -319
  83. package/custom/ChatFooter.vue +0 -173
  84. package/custom/ChatHeader.vue +0 -78
  85. package/custom/ChatSurface.vue +0 -160
  86. package/custom/CustomAutoScrollContainer.vue +0 -164
  87. package/custom/SessionsHistory.vue +0 -88
  88. package/custom/chat.ts +0 -82
  89. package/custom/composables/agentAudio/utils.ts +0 -220
  90. package/custom/composables/agentStore/constants.ts +0 -19
  91. package/custom/composables/agentStore/pageContext.ts +0 -8
  92. package/custom/composables/agentStore/useAgentChat.ts +0 -241
  93. package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
  94. package/custom/composables/agentStore/useAgentSessions.ts +0 -350
  95. package/custom/composables/useAgentAudio.ts +0 -272
  96. package/custom/composables/useAgentStore.ts +0 -510
  97. package/custom/composables/useAgentTransitions.ts +0 -58
  98. package/custom/conversation_area/ConversationArea.vue +0 -301
  99. package/custom/conversation_area/MessageRenderer.vue +0 -49
  100. package/custom/conversation_area/ProcessingTimeline.vue +0 -286
  101. package/custom/conversation_area/ReasoningRenderer.vue +0 -79
  102. package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
  103. package/custom/conversation_area/TextRenderer.vue +0 -182
  104. package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
  105. package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
  106. package/custom/conversation_area/ToolRenderer.vue +0 -178
  107. package/custom/conversation_area/ToolsGroup.vue +0 -59
  108. package/custom/env.d.ts +0 -7
  109. package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
  110. package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
  111. package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
  112. package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
  113. package/custom/package.json +0 -30
  114. package/custom/pnpm-lock.yaml +0 -2394
  115. package/custom/public/agentAudio/agent-processing.mp3 +0 -0
  116. package/custom/skills/analyze_data/SKILL.md +0 -25
  117. package/custom/skills/charts/SKILL.md +0 -193
  118. package/custom/skills/fetch_data/SKILL.md +0 -31
  119. package/custom/skills/mutate_data/SKILL.md +0 -121
  120. package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
  121. package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
  122. package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
  123. package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
  124. package/custom/tsconfig.json +0 -15
  125. package/custom/types.ts +0 -108
  126. package/custom/utils.ts +0 -38
  127. package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
  128. package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
  129. package/dist/agent/models/AgentModeResolver.d.ts +0 -9
  130. package/dist/agent/models/AgentModeResolver.js +0 -9
  131. package/dist/agent/simpleAgent.js +0 -118
  132. package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
  133. package/dist/agent/turn/TurnContextBuilder.js +0 -31
  134. package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
  135. package/dist/agent/turn/TurnLifecycleService.js +0 -47
  136. package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
  137. package/dist/agent/turn/TurnPersistenceService.js +0 -35
  138. package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
  139. package/dist/agent/turn/TurnPromptBuilder.js +0 -43
  140. package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
  141. package/dist/agent/turn/TurnStreamConsumer.js +0 -84
  142. package/dist/agentTurnService.d.ts +0 -24
  143. package/dist/agentTurnService.js +0 -269
  144. package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
  145. package/endpoints/chatSurfaces.ts +0 -66
  146. package/endpoints/context.ts +0 -52
  147. package/endpoints/core.ts +0 -181
  148. package/endpoints/sessions.ts +0 -183
  149. package/errors.ts +0 -10
  150. package/index.ts +0 -192
  151. package/pnpm-workspace.yaml +0 -4
  152. package/sanitizeSpeechText.ts +0 -32
  153. package/sessionStore.ts +0 -105
  154. package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
  155. package/tsconfig.json +0 -14
  156. package/types.ts +0 -127
  157. /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
  158. /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
  159. /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
  160. /package/dist/{agent → domain}/languageDetect.js +0 -0
  161. /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
  162. /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
  163. /package/dist/{agent → domain}/toolCallEvents.js +0 -0
  164. /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
  165. /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
  166. /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
  167. /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
  168. /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
  169. /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
  170. /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
  171. /package/dist/{errors.js → shared/errors.js} +0 -0
  172. /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
  173. /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
  174. /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
  175. /package/dist/{agent/tools → tools}/apiTool.js +0 -0
  176. /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
  177. /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
  178. /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
  179. /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
  180. /package/dist/{agent/tools → tools}/index.js +0 -0
  181. /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
  182. /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
  183. /package/dist/{agent → tools}/skills/registry.js +0 -0
  184. /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
  185. /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
  186. /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
  187. /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
package/README.md CHANGED
@@ -4,28 +4,351 @@
4
4
 
5
5
  [![Ask AI](https://tluma.ai/badge)](https://tluma.ai/ask-ai/devforth/adminforth)
6
6
 
7
- Adds a native AI agent to your AdminForth application.
7
+ Adds a native, tool-calling AI agent to your AdminForth application. The agent lives in a
8
+ chat panel inside the admin UI, can inspect and mutate your resources through safe
9
+ API-based tools, streams its answers token-by-token, and keeps persistent chat sessions.
10
+
11
+ > Full tutorial: [AdminForth Agent Documentation](https://adminforth.dev/docs/tutorial/Plugins/agent/)
8
12
 
9
13
  ## Features
10
14
 
11
- - Connect AdminForth with native AI agent workflows.
12
- - Expose API-based tools to agent-driven tasks.
13
- - Extend back-office operations with custom agent actions.
14
- - Keep agent capabilities close to your admin logic.
15
+ - **Chat agent in the admin UI** — a streaming chat surface injected into every page.
16
+ - **Tool calling over your resources** — the agent reads and (optionally) mutates records
17
+ through AdminForth's own API layer, so your access rules and validation still apply.
18
+ - **Progressive tool & skill disclosure** — the agent loads only the tools it needs, guided
19
+ by Markdown *skills* you can extend.
20
+ - **Human-in-the-loop approvals** — tools you mark as dangerous pause for explicit
21
+ approve/reject from the user before running.
22
+ - **Multiple modes** — expose several models (e.g. *Fast*, *Balanced*, *Smart Thinking*)
23
+ and let users switch between them.
24
+ - **Persistent sessions** — conversations are stored in your database; an optional
25
+ checkpointer persists full LangGraph state across turns.
26
+ - **Voice in and out** — optional speech-to-text and text-to-speech via an audio adapter.
27
+ - **External chat surfaces** — optionally serve the same agent through webhooks (e.g.
28
+ Telegram) with OAuth identity mapping.
29
+
30
+ Built on [LangChain / LangGraph](https://github.com/langchain-ai/langgraphjs).
31
+
32
+ ## How it works
33
+
34
+ For each user message the plugin creates a *turn*, builds the system prompt (including the
35
+ list of your resources and available skills), streams the model's output back over SSE, and
36
+ persists the prompt/response to your turn resource. Tools are executed through AdminForth's
37
+ API endpoints; conversation memory is kept per session (`thread_id = sessionId`).
38
+
39
+ ## Requirements
40
+
41
+ - An AdminForth app (`adminforth >= 3.8.2`, declared as a peer dependency).
42
+ - A **completion adapter** that supports agent/tool-calling. Any of these work:
43
+ - [`@adminforth/completion-adapter-openai-responses`](https://www.npmjs.com/package/@adminforth/completion-adapter-openai-responses)
44
+ - [`@adminforth/completion-adapter-anthropic-messages`](https://www.npmjs.com/package/@adminforth/completion-adapter-anthropic-messages)
45
+ - [`@adminforth/completion-adapter-google-gemini`](https://www.npmjs.com/package/@adminforth/completion-adapter-google-gemini)
46
+ - Two database-backed resources for **sessions** and **turns** (schemas below). A third
47
+ **checkpoint** resource is optional but recommended for reliable multi-turn memory.
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ npm install @adminforth/agent @adminforth/completion-adapter-openai-responses
53
+ ```
54
+
55
+ ## Setup
56
+
57
+ Setup has three parts: (1) create the storage resources, (2) configure the plugin, and
58
+ (3) register both in your AdminForth config.
59
+
60
+ ### 1. Create the storage resources
61
+
62
+ The plugin does not create tables for you — you expose ordinary AdminForth resources and
63
+ tell the plugin which fields to use. The field **names** are up to you; the mapping in the
64
+ plugin options connects them.
65
+
66
+ <details>
67
+ <summary><strong>Sessions resource</strong> (required)</summary>
68
+
69
+ ```ts
70
+ import { AdminForthDataTypes, type AdminForthResourceInput } from 'adminforth';
71
+ import { randomUUID } from 'crypto';
72
+
73
+ const sessionsResource: AdminForthResourceInput = {
74
+ dataSource: 'maindb',
75
+ table: 'sessions',
76
+ resourceId: 'sessions',
77
+ label: 'Sessions',
78
+ columns: [
79
+ { name: 'id', primaryKey: true, type: AdminForthDataTypes.STRING, fillOnCreate: () => randomUUID() },
80
+ { name: 'title', type: AdminForthDataTypes.STRING },
81
+ { name: 'turns', type: AdminForthDataTypes.INTEGER },
82
+ { name: 'asker_id', type: AdminForthDataTypes.STRING },
83
+ { name: 'created_at', type: AdminForthDataTypes.DATETIME, fillOnCreate: () => new Date().toISOString() },
84
+ ],
85
+ };
86
+
87
+ export default sessionsResource;
88
+ ```
89
+ </details>
90
+
91
+ <details>
92
+ <summary><strong>Turns resource</strong> (required)</summary>
93
+
94
+ ```ts
95
+ import { AdminForthDataTypes, type AdminForthResourceInput } from 'adminforth';
96
+ import { randomUUID } from 'crypto';
97
+
98
+ const turnsResource: AdminForthResourceInput = {
99
+ dataSource: 'maindb',
100
+ table: 'turns',
101
+ resourceId: 'turns',
102
+ label: 'Turns',
103
+ columns: [
104
+ { name: 'id', primaryKey: true, type: AdminForthDataTypes.STRING, fillOnCreate: () => randomUUID() },
105
+ { name: 'session_id', type: AdminForthDataTypes.STRING },
106
+ { name: 'created_at', type: AdminForthDataTypes.DATETIME, fillOnCreate: () => new Date().toISOString() },
107
+ { name: 'prompt', type: AdminForthDataTypes.TEXT },
108
+ { name: 'response', type: AdminForthDataTypes.TEXT },
109
+ // Optional: add a `debug` TEXT column and map it via turnResource.debugField
110
+ // to store per-turn debug traces.
111
+ ],
112
+ };
113
+
114
+ export default turnsResource;
115
+ ```
116
+ </details>
117
+
118
+ <details>
119
+ <summary><strong>Checkpoints resource</strong> (optional — enables persistent memory)</summary>
120
+
121
+ Without this resource the agent uses an in-memory checkpointer (`MemorySaver`), which is
122
+ lost on restart and not shared across instances. For production, add a checkpoint resource.
123
+ Rows accumulate over time, so pairing it with an auto-cleanup plugin is recommended.
124
+
125
+ ```ts
126
+ import { AdminForthDataTypes, type AdminForthResourceInput } from 'adminforth';
127
+
128
+ const checkpointsResource: AdminForthResourceInput = {
129
+ dataSource: 'maindb',
130
+ table: 'agent_checkpoints',
131
+ resourceId: 'agent_checkpoints',
132
+ label: 'Agent Checkpoints',
133
+ columns: [
134
+ { name: 'id', primaryKey: true, type: AdminForthDataTypes.STRING },
135
+ { name: 'thread_id', type: AdminForthDataTypes.STRING },
136
+ { name: 'checkpoint_namespace', type: AdminForthDataTypes.STRING },
137
+ { name: 'checkpoint_id', type: AdminForthDataTypes.STRING },
138
+ { name: 'parent_checkpoint_id', type: AdminForthDataTypes.STRING },
139
+ { name: 'row_kind', type: AdminForthDataTypes.STRING },
140
+ { name: 'task_id', type: AdminForthDataTypes.STRING },
141
+ { name: 'sequence', type: AdminForthDataTypes.INTEGER },
142
+ { name: 'created_at', type: AdminForthDataTypes.DATETIME },
143
+ { name: 'checkpoint_payload', type: AdminForthDataTypes.TEXT },
144
+ { name: 'metadata_payload', type: AdminForthDataTypes.TEXT },
145
+ { name: 'writes_payload', type: AdminForthDataTypes.TEXT },
146
+ { name: 'schema_version', type: AdminForthDataTypes.INTEGER },
147
+ ],
148
+ };
149
+
150
+ export default checkpointsResource;
151
+ ```
152
+ </details>
153
+
154
+ ### 2. Configure the plugin
155
+
156
+ ```ts
157
+ // globalPlugins.ts
158
+ import AdminForthAgent from '@adminforth/agent';
159
+ import CompletionAdapterOpenAIResponses from '@adminforth/completion-adapter-openai-responses';
160
+
161
+ // Reasoning effort is configured on the completion adapter, not on the plugin.
162
+ const createCompletionAdapter = (model: string, effort: 'low' | 'medium' | 'high') =>
163
+ new CompletionAdapterOpenAIResponses({
164
+ openAiApiKey: process.env.OPENAI_API_KEY as string,
165
+ model,
166
+ extraRequestBodyParameters: { reasoning: { effort } },
167
+ });
168
+
169
+ export const globalPlugins = [
170
+ new AdminForthAgent({
171
+ // The first mode is the default. Users can switch modes in the chat UI.
172
+ modes: [
173
+ { name: 'Balanced', completionAdapter: createCompletionAdapter('gpt-5.4-mini', 'medium') },
174
+ { name: 'Fast', completionAdapter: createCompletionAdapter('gpt-5.4-mini', 'low') },
175
+ { name: 'Smart Thinking', completionAdapter: createCompletionAdapter('gpt-5.4', 'high') },
176
+ ],
177
+ maxTokens: 10000,
178
+
179
+ sessionResource: {
180
+ resourceId: 'sessions',
181
+ idField: 'id',
182
+ titleField: 'title',
183
+ turnsField: 'turns',
184
+ askerIdField: 'asker_id',
185
+ createdAtField: 'created_at',
186
+ },
187
+ turnResource: {
188
+ resourceId: 'turns',
189
+ idField: 'id',
190
+ sessionIdField: 'session_id',
191
+ createdAtField: 'created_at',
192
+ promptField: 'prompt',
193
+ responseField: 'response',
194
+ },
15
195
 
16
- ## Documentation
196
+ // Optional but recommended in production:
197
+ checkpointResource: {
198
+ resourceId: 'agent_checkpoints',
199
+ idField: 'id',
200
+ threadIdField: 'thread_id',
201
+ checkpointNamespaceField: 'checkpoint_namespace',
202
+ checkpointIdField: 'checkpoint_id',
203
+ parentCheckpointIdField: 'parent_checkpoint_id',
204
+ rowKindField: 'row_kind',
205
+ taskIdField: 'task_id',
206
+ sequenceField: 'sequence',
207
+ createdAtField: 'created_at',
208
+ checkpointPayloadField: 'checkpoint_payload',
209
+ metadataPayloadField: 'metadata_payload',
210
+ writesPayloadField: 'writes_payload',
211
+ schemaVersionField: 'schema_version',
212
+ },
213
+ }),
214
+ ];
215
+ ```
17
216
 
18
- Full setup and configuration guide:
217
+ ### 3. Register in your AdminForth config
19
218
 
20
- [AdminForth Agent Documentation](https://adminforth.dev/docs/tutorial/Plugins/agent/)
219
+ ```ts
220
+ import sessionsResource from './resources/agent_resources/sessions.js';
221
+ import turnsResource from './resources/agent_resources/turns.js';
222
+ import checkpointsResource from './resources/agent_resources/checkpoints.js';
223
+ import { globalPlugins } from './globalPlugins.js';
224
+
225
+ const admin = new AdminForth({
226
+ // ...
227
+ resources: [
228
+ sessionsResource,
229
+ turnsResource,
230
+ checkpointsResource, // only if you configured checkpointResource
231
+ // ...your other resources
232
+ ],
233
+ globalPlugins,
234
+ });
235
+ ```
236
+
237
+ That's it — a chat panel now appears in the admin UI.
238
+
239
+ ## Configuration reference
240
+
241
+ | Option | Type | Required | Description |
242
+ | --- | --- | --- | --- |
243
+ | `modes` | `{ name: string; completionAdapter }[]` | ✅ | Selectable models. The **first** entry is the default mode. Each mode has its own completion adapter. |
244
+ | `sessionResource` | `ISessionResource` | ✅ | Field mapping for the sessions resource (see below). |
245
+ | `turnResource` | `ITurnResource` | ✅ | Field mapping for the turns resource. |
246
+ | `maxTokens` | `number` | — | Max generation tokens per model call. Default `1000`. |
247
+ | `systemPrompt` | `string` | — | Extra text appended to the built-in agent system prompt. |
248
+ | `placeholderMessages` | `({ adminUser, headers }) => string[] \| Promise<string[]>` | — | Example prompts preloaded into the chat textarea. Resolved once when the chat UI loads. |
249
+ | `stickByDefault` | `boolean` | — | Whether the chat panel is docked (sticky) by default. Default `false`. |
250
+ | `checkpointResource` | `ICheckpointResource` | — | Field mapping enabling the persistent LangGraph checkpointer. Falls back to in-memory `MemorySaver` when omitted. |
251
+ | `audioAdapter` | `AudioAdapter` | — | Enables voice input/output (speech-to-text and text-to-speech). |
252
+ | `chatSurfaceAdapters` | `ChatSurfaceAdapter[]` | — | External chat surfaces (e.g. Telegram) served via webhooks. |
253
+ | `chatExternalIdentityResource` | `object` | — | Maps external chat identities (provider + external user id) to admin users. Required for chat surfaces. |
254
+
255
+ ### `sessionResource` fields
256
+
257
+ `resourceId`, `idField`, `titleField`, `turnsField`, `askerIdField`, `createdAtField`.
258
+
259
+ ### `turnResource` fields
260
+
261
+ `resourceId`, `idField`, `sessionIdField`, `createdAtField`, `promptField`, `responseField`,
262
+ and optional `debugField` (when set, per-turn debug traces are written to it).
263
+
264
+ > **Reasoning effort** is set on the completion adapter (e.g.
265
+ > `extraRequestBodyParameters: { reasoning: { effort } }`), not on the plugin.
266
+
267
+ ## Tools & skills
268
+
269
+ The agent works through **API-based tools** generated from your AdminForth resources
270
+ (reading records, inspecting schema, and — through skills — creating/updating/deleting
271
+ records and running actions). Tools run via AdminForth's own API layer, so per-resource
272
+ permissions and validation are enforced.
273
+
274
+ To keep the model focused, tools are disclosed progressively:
275
+
276
+ - `get_resource` is always available (to inspect resource structure).
277
+ - The agent reads a **skill** (a `SKILL.md` file) to learn which tools a task needs, then
278
+ loads those tool schemas on demand.
279
+
280
+ Built-in skills cover fetching data, analytics/charts, and mutating data. You can add your
281
+ own skills by placing a `SKILL.md` (with `name` and `description` frontmatter) in a
282
+ `skills/<skill-name>/` directory under your custom components dir; plugin-provided skill
283
+ directories are also discovered.
284
+
285
+ ## Human-in-the-loop approvals
286
+
287
+ Tools whose definition marks them dangerous (`agent.isDangerous === true`) trigger an
288
+ approval interrupt: generation pauses and the UI shows an approve/reject prompt. The client
289
+ resolves it via `POST /agent/approval`, and the run resumes (or, on reject, the model is
290
+ told the action was declined). Approval state is currently held per process instance.
291
+
292
+ ## Voice
293
+
294
+ Provide an `audioAdapter` (e.g. [`@adminforth/audio-adapter-openai`](https://www.npmjs.com/package/@adminforth/audio-adapter-openai))
295
+ to enable the microphone button. Audio is transcribed to text, answered by the agent, and
296
+ (optionally) synthesized back to speech and streamed to the client. Client-side voice
297
+ activity detection is loaded automatically.
298
+
299
+ ## External chat surfaces (e.g. Telegram)
300
+
301
+ Pass `chatSurfaceAdapters` (e.g. [`@adminforth/chat-surface-adapter-telegram`](https://www.npmjs.com/package/@adminforth/chat-surface-adapter-telegram))
302
+ to expose the agent over a webhook at `POST /agent/surface/<adapter-name>/webhook`.
303
+ Incoming users are resolved to admin users through `chatExternalIdentityResource`
304
+ (pairs nicely with an OAuth adapter such as `@adminforth/oauth-adapter-telegram`), and
305
+ unauthorized accounts are rejected.
306
+
307
+ ## HTTP endpoints
308
+
309
+ All routes are registered under your AdminForth API base path.
310
+
311
+ | Method & path | Purpose |
312
+ | --- | --- |
313
+ | `POST /agent/response` | Send a message; streams the answer over SSE. |
314
+ | `POST /agent/approval` | Approve/reject a pending human-in-the-loop tool call. |
315
+ | `POST /agent/speech-response` | Multipart audio upload; streams transcript + answer (+ audio). |
316
+ | `POST /agent/get-placeholder-messages` | Placeholder prompts for the chat textarea. |
317
+ | `POST /agent/get-sessions` | List chat sessions. |
318
+ | `POST /agent/get-session-info` | Fetch a session's turns. |
319
+ | `POST /agent/create-session` | Create a new session. |
320
+ | `POST /agent/delete-session` | Delete a session and its turns. |
321
+ | `POST /agent/add-system-message-to-turns` | Append a system message turn. |
322
+ | `POST /agent/surface/<name>/webhook` | Inbound webhook for an external chat surface. |
323
+
324
+ The `/agent/response` and `/agent/approval` streams use the Vercel AI UI message stream
325
+ format (`x-vercel-ai-ui-message-stream: v1`); the frontend consumes them directly.
326
+
327
+ ## Contributing / tests
328
+
329
+ This package is developed inside the [AdminForth monorepo](https://github.com/devforth/adminforth).
330
+ Unit and characterization tests for the plugin live in `tests/jest_tests/`
331
+ (`adminforth_agent_*.test.ts`) and run with:
332
+
333
+ ```bash
334
+ cd tests/jest_tests
335
+ pnpm install
336
+ pnpm test
337
+ ```
21
338
 
22
339
  ## About AdminForth
23
340
 
24
- AdminForth is an open-source, agent-first admin framework for building robust admin panels and back-office applications faster.
341
+ AdminForth is an open-source, agent-first admin framework for building robust admin panels
342
+ and back-office applications faster.
25
343
 
26
344
  ## Related links
27
345
 
28
346
  - [AdminForth website](https://adminforth.dev)
347
+ - [Agent plugin docs](https://adminforth.dev/docs/tutorial/Plugins/agent/)
29
348
  - [npm package](https://www.npmjs.com/package/@adminforth/agent)
30
349
  - [More AdminForth plugins](https://adminforth.dev/docs/tutorial/ListOfPlugins/)
31
- - [Built by DevForth](https://devforth.io)
350
+ - [Built by DevForth](https://devforth.io)
351
+
352
+ ## License
353
+
354
+ MIT
@@ -0,0 +1,66 @@
1
+ import type { CompletionAdapter } from "adminforth";
2
+ import type { DetectedLanguage, PreviousUserMessage } from "../domain/languageDetect.js";
3
+ import type { AgentMessage, AgentStreamChunk, AgentTurnContext, AgentTurnObservability, PendingInterrupt } from "../domain/turnTypes.js";
4
+ export type AgentModelPurpose = "primary" | "summary";
5
+ /**
6
+ * The LLM provider adapter contract (the "provider port"). Extends AdminForth's
7
+ * CompletionAdapter with the agent-spec factory. Declared in the application layer
8
+ * so the infrastructure (llm/) depends on this contract — not the other way around.
9
+ */
10
+ export type AgentModeCompletionAdapter = CompletionAdapter & {
11
+ getLangChainAgentSpec(params: {
12
+ maxTokens: number;
13
+ purpose: AgentModelPurpose;
14
+ }): Promise<{
15
+ model: unknown;
16
+ middleware?: unknown[];
17
+ }> | {
18
+ model: unknown;
19
+ middleware?: unknown[];
20
+ };
21
+ };
22
+ /**
23
+ * Input for a single streamed agent turn. Either a fresh set of messages, or a
24
+ * human-in-the-loop resume payload for a previously interrupted turn.
25
+ */
26
+ export type LlmStreamInput = {
27
+ completionAdapter: AgentModeCompletionAdapter;
28
+ input: {
29
+ messages: AgentMessage[];
30
+ } | {
31
+ resume: unknown;
32
+ };
33
+ context: AgentTurnContext;
34
+ observability: AgentTurnObservability;
35
+ };
36
+ /**
37
+ * The boundary between the application layer and the concrete LLM runtime
38
+ * (LangChain / LangGraph). Everything provider-specific — model construction,
39
+ * the agent graph, middleware, the raw stream shape, and the LangGraph interrupt
40
+ * shape — lives behind this port and is normalized before crossing it.
41
+ */
42
+ export interface LlmPort {
43
+ /**
44
+ * Run one turn and return a normalized stream of typed chunks.
45
+ */
46
+ streamTurn(input: LlmStreamInput): Promise<AsyncIterable<AgentStreamChunk>>;
47
+ /**
48
+ * Detect the language the assistant should answer in for the current message.
49
+ */
50
+ detectLanguage(input: {
51
+ completionAdapter: AgentModeCompletionAdapter;
52
+ prompt: string;
53
+ previousUserMessages: PreviousUserMessage[];
54
+ }): Promise<DetectedLanguage | null>;
55
+ /**
56
+ * Rebuild the pending human-in-the-loop interrupts for a session from persisted
57
+ * checkpoint state (used when the in-process cache is empty — after a restart or
58
+ * on another instance). Returns already-normalized descriptors; the LangGraph
59
+ * interrupt shape never leaves the llm layer. Throws on a checkpoint/runtime
60
+ * failure (the caller must not treat that as "no pending interrupt").
61
+ */
62
+ getPendingInterrupts(input: {
63
+ completionAdapter: AgentModeCompletionAdapter;
64
+ sessionId: string;
65
+ }): Promise<PendingInterrupt[]>;
66
+ }
@@ -0,0 +1,68 @@
1
+ import { type IAdminForth } from "adminforth";
2
+ import type { AgentSessionStore } from "../persistence/sessionStore.js";
3
+ import type { PluginOptions } from "../types.js";
4
+ import type { LlmPort } from "./ports.js";
5
+ import type { DebugSink, HandleTurnInput, PendingInterrupt, RunAndPersistAgentResponseInput, RunAndPersistAgentResponseResult } from "../domain/turnTypes.js";
6
+ /**
7
+ * Build the provider-agnostic resume payload for a human-in-the-loop turn.
8
+ * Exported for unit testing; the LLM port wraps it into a LangGraph Command.
9
+ */
10
+ export declare function buildResumeValue(input: {
11
+ decision: "approve" | "reject";
12
+ interrupts?: PendingInterrupt[];
13
+ prompt?: string;
14
+ }): {
15
+ decisions: ({
16
+ type: "approve";
17
+ message?: undefined;
18
+ } | {
19
+ type: "reject";
20
+ message: string;
21
+ })[];
22
+ } | {
23
+ [k: string]: {
24
+ decisions: ({
25
+ type: "approve";
26
+ message?: undefined;
27
+ } | {
28
+ type: "reject";
29
+ message: string;
30
+ })[];
31
+ };
32
+ };
33
+ export type RunTurnUseCaseDeps = {
34
+ llm: LlmPort;
35
+ sessions: AgentSessionStore;
36
+ modes: PluginOptions["modes"];
37
+ getAdminforth: () => IAdminForth;
38
+ getAgentSystemPrompt: () => Promise<string>;
39
+ /** True when a persistent checkpointer is configured (not the in-memory MemorySaver). */
40
+ hasPersistentCheckpointer: boolean;
41
+ /** Factory for the per-turn debug sink; the concrete impl lives in the llm layer. */
42
+ createDebugSink: () => DebugSink;
43
+ };
44
+ /**
45
+ * Single entry point for running an agent turn: prepare (start/resume) → build
46
+ * the prompt → stream from the LLM port → consume the typed stream into SSE
47
+ * events → persist the result. Replaces the former AgentTurnService plus the
48
+ * TurnLifecycle/Context/Prompt/Persistence/StreamConsumer + ModeResolver stack.
49
+ */
50
+ export declare class RunTurnUseCase {
51
+ private readonly deps;
52
+ private readonly pendingInterrupts;
53
+ constructor(deps: RunTurnUseCaseDeps);
54
+ private resolveMode;
55
+ private buildContext;
56
+ /**
57
+ * Resolve the pending HITL interrupts for a resume. With a persistent checkpointer the
58
+ * checkpoint is authoritative (correct across instances and restarts); with the in-memory
59
+ * MemorySaver the in-process cache is authoritative (single process, cannot be stale).
60
+ */
61
+ private resolvePendingInterrupts;
62
+ private prepareTurn;
63
+ private buildMessages;
64
+ private handleInterrupt;
65
+ private runAgentTurn;
66
+ runAndPersistAgentResponse(input: RunAndPersistAgentResponseInput): Promise<RunAndPersistAgentResponseResult>;
67
+ handleTurn(input: HandleTurnInput): Promise<RunAndPersistAgentResponseResult>;
68
+ }