@adminforth/agent 2.0.10 → 2.2.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.
- package/README.md +333 -10
- package/dist/application/ports.d.ts +66 -0
- package/dist/application/runTurnUseCase.d.ts +71 -0
- package/dist/application/runTurnUseCase.js +364 -0
- package/dist/custom/ChatFooter.vue +58 -30
- package/dist/custom/SteerDebugPanel.vue +45 -0
- package/dist/custom/SteerQueue.vue +54 -0
- package/dist/custom/composables/agentStore/constants.ts +1 -0
- package/dist/custom/composables/agentStore/useAgentSessions.ts +35 -16
- package/dist/custom/composables/agentStore/useAgentSteerQueue.ts +114 -0
- package/dist/custom/composables/useAgentStore.ts +46 -1
- package/dist/custom/conversation_area/MessageRenderer.vue +7 -1
- package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -1
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +3 -3
- package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +5 -1
- package/dist/domain/steerBuffer.d.ts +33 -0
- package/dist/domain/steerBuffer.js +51 -0
- package/dist/{agent → domain}/systemPrompt.js +1 -1
- package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
- package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +49 -35
- package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
- package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
- package/dist/llm/agentModels.js +65 -0
- package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +14 -2
- package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +41 -6
- package/dist/llm/langGraphLlm.d.ts +25 -0
- package/dist/llm/langGraphLlm.js +54 -0
- package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
- package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
- package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
- package/dist/llm/middleware/steerMiddleware.d.ts +21 -0
- package/dist/llm/middleware/steerMiddleware.js +50 -0
- package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
- package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
- package/dist/llm/streamAdapter.d.ts +18 -0
- package/dist/llm/streamAdapter.js +117 -0
- package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +19 -2
- package/dist/{sessionStore.js → persistence/sessionStore.js} +53 -0
- package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
- package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
- package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +0 -3
- package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
- package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
- package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
- package/dist/{agent/tools → tools}/index.d.ts +1 -1
- package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
- package/dist/{endpoints → transport/http}/context.d.ts +12 -4
- package/dist/transport/http/context.js +1 -0
- package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +23 -3
- package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +36 -1
- package/dist/transport/sse/sseWriter.d.ts +29 -0
- package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +27 -0
- package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
- package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
- package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
- package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
- package/dist/types.d.ts +10 -4
- package/package.json +11 -2
- package/.woodpecker/buildRelease.sh +0 -13
- package/.woodpecker/buildSlackNotify.sh +0 -46
- package/.woodpecker/release.yml +0 -57
- package/agent/checkpointer.ts +0 -329
- package/agent/languageDetect.ts +0 -92
- package/agent/middleware/apiBasedTools.ts +0 -174
- package/agent/middleware/openAiResponsesContinuation.ts +0 -92
- package/agent/middleware/sequenceDebug.ts +0 -429
- package/agent/models/AgentModeResolver.ts +0 -9
- package/agent/models/AgentModelFactory.ts +0 -28
- package/agent/runtime/AgentContext.ts +0 -30
- package/agent/runtime/AgentRuntime.ts +0 -89
- package/agent/simpleAgent.ts +0 -199
- package/agent/skills/registry.ts +0 -162
- package/agent/speech/SpeechTurnService.ts +0 -179
- package/agent/systemPrompt.ts +0 -133
- package/agent/toolCallEvents.ts +0 -135
- package/agent/tools/AgentToolProvider.ts +0 -28
- package/agent/tools/apiTool.ts +0 -66
- package/agent/tools/fetchSkill.ts +0 -68
- package/agent/tools/fetchToolSchema.ts +0 -47
- package/agent/tools/getUserLocation.ts +0 -45
- package/agent/tools/index.ts +0 -31
- package/agent/tools/navigateUser.ts +0 -210
- package/agent/turn/TurnContextBuilder.ts +0 -36
- package/agent/turn/TurnLifecycleService.ts +0 -47
- package/agent/turn/TurnPersistenceService.ts +0 -33
- package/agent/turn/TurnPromptBuilder.ts +0 -51
- package/agent/turn/TurnStreamConsumer.ts +0 -69
- package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
- package/agent/turn/turnTypes.ts +0 -100
- package/agentEvents.ts +0 -75
- package/agentTurnService.ts +0 -314
- package/apiBasedTools.ts +0 -712
- package/build.log +0 -67
- package/chatSurfaceService.ts +0 -319
- package/custom/ChatFooter.vue +0 -173
- package/custom/ChatHeader.vue +0 -78
- package/custom/ChatSurface.vue +0 -160
- package/custom/CustomAutoScrollContainer.vue +0 -164
- package/custom/SessionsHistory.vue +0 -88
- package/custom/chat.ts +0 -82
- package/custom/composables/agentAudio/utils.ts +0 -220
- package/custom/composables/agentStore/constants.ts +0 -19
- package/custom/composables/agentStore/pageContext.ts +0 -8
- package/custom/composables/agentStore/useAgentChat.ts +0 -241
- package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
- package/custom/composables/agentStore/useAgentSessions.ts +0 -350
- package/custom/composables/useAgentAudio.ts +0 -272
- package/custom/composables/useAgentStore.ts +0 -510
- package/custom/composables/useAgentTransitions.ts +0 -58
- package/custom/conversation_area/ConversationArea.vue +0 -301
- package/custom/conversation_area/MessageRenderer.vue +0 -49
- package/custom/conversation_area/ProcessingTimeline.vue +0 -286
- package/custom/conversation_area/ReasoningRenderer.vue +0 -79
- package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
- package/custom/conversation_area/TextRenderer.vue +0 -182
- package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
- package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
- package/custom/conversation_area/ToolRenderer.vue +0 -178
- package/custom/conversation_area/ToolsGroup.vue +0 -59
- package/custom/env.d.ts +0 -7
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
- package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
- package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
- package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
- package/custom/package.json +0 -30
- package/custom/pnpm-lock.yaml +0 -2394
- package/custom/public/agentAudio/agent-processing.mp3 +0 -0
- package/custom/skills/analyze_data/SKILL.md +0 -25
- package/custom/skills/charts/SKILL.md +0 -193
- package/custom/skills/fetch_data/SKILL.md +0 -31
- package/custom/skills/mutate_data/SKILL.md +0 -121
- package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
- package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
- package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
- package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
- package/custom/tsconfig.json +0 -15
- package/custom/types.ts +0 -108
- package/custom/utils.ts +0 -38
- package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
- package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
- package/dist/agent/models/AgentModeResolver.d.ts +0 -9
- package/dist/agent/models/AgentModeResolver.js +0 -9
- package/dist/agent/simpleAgent.js +0 -118
- package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
- package/dist/agent/turn/TurnContextBuilder.js +0 -31
- package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
- package/dist/agent/turn/TurnLifecycleService.js +0 -47
- package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
- package/dist/agent/turn/TurnPersistenceService.js +0 -35
- package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
- package/dist/agent/turn/TurnPromptBuilder.js +0 -43
- package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
- package/dist/agent/turn/TurnStreamConsumer.js +0 -84
- package/dist/agentTurnService.d.ts +0 -24
- package/dist/agentTurnService.js +0 -269
- package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
- package/endpoints/chatSurfaces.ts +0 -66
- package/endpoints/context.ts +0 -52
- package/endpoints/core.ts +0 -181
- package/endpoints/sessions.ts +0 -183
- package/errors.ts +0 -10
- package/index.ts +0 -192
- package/pnpm-workspace.yaml +0 -4
- package/sanitizeSpeechText.ts +0 -32
- package/sessionStore.ts +0 -105
- package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
- package/tsconfig.json +0 -14
- package/types.ts +0 -127
- /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
- /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
- /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
- /package/dist/{agent → domain}/languageDetect.js +0 -0
- /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.js +0 -0
- /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
- /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
- /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
- /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
- /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
- /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
- /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
- /package/dist/{errors.js → shared/errors.js} +0 -0
- /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
- /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
- /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
- /package/dist/{agent/tools → tools}/apiTool.js +0 -0
- /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
- /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
- /package/dist/{agent/tools → tools}/index.js +0 -0
- /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
- /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
- /package/dist/{agent → tools}/skills/registry.js +0 -0
- /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
- /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
- /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
|
[](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
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
217
|
+
### 3. Register in your AdminForth config
|
|
19
218
|
|
|
20
|
-
|
|
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
|
|
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,71 @@
|
|
|
1
|
+
import { type IAdminForth } from "adminforth";
|
|
2
|
+
import type { AgentSessionStore } from "../persistence/sessionStore.js";
|
|
3
|
+
import type { SteerBuffer } from "../domain/steerBuffer.js";
|
|
4
|
+
import type { PluginOptions } from "../types.js";
|
|
5
|
+
import type { LlmPort } from "./ports.js";
|
|
6
|
+
import type { DebugSink, HandleTurnInput, PendingInterrupt, RunAndPersistAgentResponseInput, RunAndPersistAgentResponseResult } from "../domain/turnTypes.js";
|
|
7
|
+
/**
|
|
8
|
+
* Build the provider-agnostic resume payload for a human-in-the-loop turn.
|
|
9
|
+
* Exported for unit testing; the LLM port wraps it into a LangGraph Command.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildResumeValue(input: {
|
|
12
|
+
decision: "approve" | "reject";
|
|
13
|
+
interrupts?: PendingInterrupt[];
|
|
14
|
+
prompt?: string;
|
|
15
|
+
}): {
|
|
16
|
+
decisions: ({
|
|
17
|
+
type: "approve";
|
|
18
|
+
message?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
type: "reject";
|
|
21
|
+
message: string;
|
|
22
|
+
})[];
|
|
23
|
+
} | {
|
|
24
|
+
[k: string]: {
|
|
25
|
+
decisions: ({
|
|
26
|
+
type: "approve";
|
|
27
|
+
message?: undefined;
|
|
28
|
+
} | {
|
|
29
|
+
type: "reject";
|
|
30
|
+
message: string;
|
|
31
|
+
})[];
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export type RunTurnUseCaseDeps = {
|
|
35
|
+
llm: LlmPort;
|
|
36
|
+
sessions: AgentSessionStore;
|
|
37
|
+
/** Per-session buffer of mid-turn steering instructions; cleared when a turn ends unconsumed. */
|
|
38
|
+
steerBuffer: SteerBuffer;
|
|
39
|
+
modes: PluginOptions["modes"];
|
|
40
|
+
getAdminforth: () => IAdminForth;
|
|
41
|
+
getAgentSystemPrompt: () => Promise<string>;
|
|
42
|
+
/** True when a persistent checkpointer is configured (not the in-memory MemorySaver). */
|
|
43
|
+
hasPersistentCheckpointer: boolean;
|
|
44
|
+
/** Factory for the per-turn debug sink; the concrete impl lives in the llm layer. */
|
|
45
|
+
createDebugSink: () => DebugSink;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Single entry point for running an agent turn: prepare (start/resume) → build
|
|
49
|
+
* the prompt → stream from the LLM port → consume the typed stream into SSE
|
|
50
|
+
* events → persist the result. Replaces the former AgentTurnService plus the
|
|
51
|
+
* TurnLifecycle/Context/Prompt/Persistence/StreamConsumer + ModeResolver stack.
|
|
52
|
+
*/
|
|
53
|
+
export declare class RunTurnUseCase {
|
|
54
|
+
private readonly deps;
|
|
55
|
+
private readonly pendingInterrupts;
|
|
56
|
+
constructor(deps: RunTurnUseCaseDeps);
|
|
57
|
+
private resolveMode;
|
|
58
|
+
private buildContext;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the pending HITL interrupts for a resume. With a persistent checkpointer the
|
|
61
|
+
* checkpoint is authoritative (correct across instances and restarts); with the in-memory
|
|
62
|
+
* MemorySaver the in-process cache is authoritative (single process, cannot be stale).
|
|
63
|
+
*/
|
|
64
|
+
private resolvePendingInterrupts;
|
|
65
|
+
private prepareTurn;
|
|
66
|
+
private buildMessages;
|
|
67
|
+
private handleInterrupt;
|
|
68
|
+
private runAgentTurn;
|
|
69
|
+
runAndPersistAgentResponse(input: RunAndPersistAgentResponseInput): Promise<RunAndPersistAgentResponseResult>;
|
|
70
|
+
handleTurn(input: HandleTurnInput): Promise<RunAndPersistAgentResponseResult>;
|
|
71
|
+
}
|