dopus 0.0.0-stage → 0.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.
- package/LICENSE +21 -0
- package/README.md +223 -2
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +492 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +231 -0
- package/dist/version-BPyQRLlj.js +31 -0
- package/package.json +75 -4
- package/templates/prompts/app.ts +18 -0
- package/templates/prompts/presets/customer-support.ts +13 -0
- package/templates/prompts/presets/general.ts +12 -0
- package/templates/prompts/presets/internal-ops.ts +13 -0
- package/templates/ui/agent-chat.module.css +255 -0
- package/templates/ui/agent-chat.tsx +294 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 akodm
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,224 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dopus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Drop an AI agent into your existing Next.js app. Your server functions become its tools.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add dopus
|
|
7
|
+
pnpm dopus init
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- **Reuse what you have.** Register the server actions and functions your app already has. You don't write a second API for the agent.
|
|
11
|
+
- **Permissions come for free.** Tools run inside the agent's request, so `cookies()` and `headers()` in your server functions see the signed-in user. Your own auth and ownership checks keep working unchanged.
|
|
12
|
+
- **Writes wait for the user.** Mark a tool `confirm: true` and the user approves each call in the chat. Approvals are signed so a client cannot forge one.
|
|
13
|
+
- **You own the generated code.** `init` copies the route, prompts and chat UI into your project, shadcn-style. Only the runtime lives in the package.
|
|
14
|
+
|
|
15
|
+
Built on the [Vercel AI SDK](https://ai-sdk.dev) 7. Supports OpenAI, Anthropic and Google models.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Requirements: Next.js App Router, TypeScript, Node.js 22+, zod 4.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add dopus # or npm / yarn / bun
|
|
25
|
+
pnpm dopus init
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`init` asks for a provider, an API key and a prompt preset, then:
|
|
29
|
+
|
|
30
|
+
| Writes | What it is |
|
|
31
|
+
| --------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
32
|
+
| `.env.local` | Your API key and a generated `DOPUS_APPROVAL_SECRET`. Never `.env`. Mode 600. |
|
|
33
|
+
| `agent.config.ts` | Model, tools and prompt layers. Edit this. |
|
|
34
|
+
| `agent/prompts/preset.ts`, `agent/prompts/app.ts` | Your prompt layers. Yours to edit. |
|
|
35
|
+
| `app/api/agent/route.ts` | `POST /api/agent`. It speaks the AI SDK `useChat` protocol. |
|
|
36
|
+
| `components/agent/agent-chat.tsx` (+ `.module.css`) | Chat panel and floating widget. |
|
|
37
|
+
|
|
38
|
+
It also installs whatever is missing of `ai`, `zod`, `@ai-sdk/react` and the provider package. It checks that `.env.local` is git-ignored.
|
|
39
|
+
|
|
40
|
+
`init` is safe to run again. Existing files and env values are left alone unless you pass `--force` and confirm.
|
|
41
|
+
|
|
42
|
+
Then add the widget to your root layout:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
// app/layout.tsx
|
|
46
|
+
import { AgentWidget } from '../components/agent/agent-chat'
|
|
47
|
+
|
|
48
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
49
|
+
return (
|
|
50
|
+
<html lang="en">
|
|
51
|
+
<body>
|
|
52
|
+
{children}
|
|
53
|
+
<AgentWidget suggestions={['What are my open orders?']} />
|
|
54
|
+
</body>
|
|
55
|
+
</html>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### CLI options
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
dopus init [--provider openai|anthropic|google] [--model <id>]
|
|
64
|
+
[--preset general|customer-support|internal-ops]
|
|
65
|
+
[--yes] [--no-install] [--force] [--cwd <dir>]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`--yes` runs without prompts. It needs `--provider` and skips the API key prompt, so add the key to `.env.local` yourself afterwards.
|
|
69
|
+
|
|
70
|
+
## Tools
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// agent.config.ts
|
|
74
|
+
import { anthropic } from '@ai-sdk/anthropic'
|
|
75
|
+
import { defineAgent, defineTool } from 'dopus'
|
|
76
|
+
import { z } from 'zod'
|
|
77
|
+
import { cancelOrder, listMyOrders } from './app/actions/orders' // your existing server actions
|
|
78
|
+
import app, { locales } from './agent/prompts/app'
|
|
79
|
+
import preset from './agent/prompts/preset'
|
|
80
|
+
|
|
81
|
+
export default defineAgent({
|
|
82
|
+
model: anthropic('claude-sonnet-5-5'),
|
|
83
|
+
tools: {
|
|
84
|
+
listMyOrders: defineTool(() => listMyOrders(), {
|
|
85
|
+
description: "Lists the signed-in user's orders with id, item and status.",
|
|
86
|
+
usage: 'When the user asks about their orders.',
|
|
87
|
+
input: z.object({}),
|
|
88
|
+
}),
|
|
89
|
+
cancelOrder: defineTool((input) => cancelOrder(input.orderId), {
|
|
90
|
+
description: 'Cancels one open order of the signed-in user.',
|
|
91
|
+
input: z.object({ orderId: z.string() }),
|
|
92
|
+
confirm: true, // the user approves each call in the chat
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
prompt: { preset, app, locales },
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`defineTool(fn, options)` calls `fn(input, ctx)`. If your function takes positional arguments, adapt it inline: `(input) => cancelOrder(input.orderId)`.
|
|
100
|
+
|
|
101
|
+
| Option | |
|
|
102
|
+
| ------------- | -------------------------------------------------------------------------------------------------------- |
|
|
103
|
+
| `description` | **Required.** What the tool does. The model reads it. |
|
|
104
|
+
| `usage` | When the tool should, or should not, be used. Appended to the description. |
|
|
105
|
+
| `input` | **Required.** A zod schema. The model's arguments are validated before `fn` runs. |
|
|
106
|
+
| `confirm` | `true`, or `(input, ctx) => boolean`, to ask the user before each call. Use it for anything that writes. |
|
|
107
|
+
| `output` | `(result) => unknown`. Trims the result before the model sees it, for example to drop internal fields. |
|
|
108
|
+
|
|
109
|
+
`ctx` is `{ user, toolCallId, abortSignal }`. `user` is only set when you configure `identify` (see below).
|
|
110
|
+
|
|
111
|
+
### Errors
|
|
112
|
+
|
|
113
|
+
Only two kinds of error message reach the model:
|
|
114
|
+
|
|
115
|
+
- a `ToolError` you throw on purpose, such as `throw new ToolError('Order o-1 has already shipped')`;
|
|
116
|
+
- Next.js `redirect()`, `notFound()`, `forbidden()` and `unauthorized()`, which are translated into plain messages such as "the user is not signed in".
|
|
117
|
+
|
|
118
|
+
Any other error goes to `onError` and is replaced with a generic message, so database errors and stack details stay on the server. The browser only ever sees a generic error text.
|
|
119
|
+
|
|
120
|
+
## Permissions and security
|
|
121
|
+
|
|
122
|
+
**How permission inheritance works.** `createAgentHandler` runs the whole tool loop inside the `POST /api/agent` request. A server function called as a tool therefore sees that request's cookies and headers, just as it does when your own UI calls it. The repo's end-to-end test checks this on a real `next start` server. It confirms that tools read the caller's session cookie, return only the caller's data, and still apply the app's ownership check after an approval.
|
|
123
|
+
|
|
124
|
+
**What stays your job.**
|
|
125
|
+
|
|
126
|
+
- Authorize inside your server functions, as you already should. dopus does not add permissions; it inherits yours.
|
|
127
|
+
- Treat the agent like any other client. A user can only make it do what that user could already do through your server functions.
|
|
128
|
+
- Register only the functions you want the agent to call.
|
|
129
|
+
- Protect the route like your other API routes, with rate limits and auth if anonymous use costs you money.
|
|
130
|
+
|
|
131
|
+
**Approvals.** With `DOPUS_APPROVAL_SECRET` set (`init` generates it), every approval request is HMAC-signed. An approval with a missing or tampered signature is rejected and the tool does not run.
|
|
132
|
+
|
|
133
|
+
**Browser context is untrusted.** The chat UI sends `{ page, locale }` so the agent knows where the user is. The server accepts only those two keys, strips control characters and caps their length. Anything that matters for authorization, such as role, plan or tenant, must come from the server via `runtime`.
|
|
134
|
+
|
|
135
|
+
**Running tools outside the request.** Background jobs and queues lose the request's cookies. For that case, define `identify(request)` to resolve a serialisable user. Tools then receive it as `ctx.user` and can pass it on explicitly. Infra adapters that run tools elsewhere will build on this.
|
|
136
|
+
|
|
137
|
+
## Prompts
|
|
138
|
+
|
|
139
|
+
dopus builds the system prompt in four layers, ordered from stable to volatile so that providers can cache the prefix:
|
|
140
|
+
|
|
141
|
+
1. **Core**: about 15 lines of tool-use and safety rules, shipped in the package. Replace it with `prompt.core = '...'` or drop it with `false`. It is exported as `CORE_PROMPT`.
|
|
142
|
+
2. **Preset**: `agent/prompts/preset.ts`, copied by `init`. The presets are `general`, `customer-support` and `internal-ops`. Edit it freely.
|
|
143
|
+
3. **App context**: `agent/prompts/app.ts`, a description of your service. You write this.
|
|
144
|
+
4. **Runtime**: per-request facts. These are the browser's `locale` and `page`, plus whatever `runtime` returns.
|
|
145
|
+
|
|
146
|
+
Write your prompts once, in one language. The agent answers in the request's locale. For per-language tone or terminology, add short notes to `locales` (`{ ko: '존댓말을 사용하세요.' }`). Only the note that matches the request locale is included.
|
|
147
|
+
|
|
148
|
+
Anthropic gets an explicit cache breakpoint after layer 3. OpenAI and Gemini cache prompt prefixes automatically.
|
|
149
|
+
|
|
150
|
+
## Chat UI
|
|
151
|
+
|
|
152
|
+
`components/agent/agent-chat.tsx` exports two components:
|
|
153
|
+
|
|
154
|
+
- `<AgentChat />`: the panel;
|
|
155
|
+
- `<AgentWidget />`: a floating launcher that opens the panel.
|
|
156
|
+
|
|
157
|
+
Both accept `api`, `suggestions`, `labels` (every UI string, for translation) and `className`.
|
|
158
|
+
|
|
159
|
+
The UI shows each tool call as it runs. It renders an approve/decline card for confirmed tools and continues automatically once the user answers. It also has stop and retry buttons, and it does not send on Enter while an IME (Korean, Japanese, Chinese) is still composing.
|
|
160
|
+
|
|
161
|
+
Styling is a plain CSS module themed through `--dopus-*` variables, with dark mode included. It needs no Tailwind and no component library. The file is yours, so restyle it or swap in your own components.
|
|
162
|
+
|
|
163
|
+
## Configuration
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
defineAgent({
|
|
167
|
+
model, // any AI SDK LanguageModel, e.g. openai('gpt-5.5')
|
|
168
|
+
tools, // { name: defineTool(...) }; names match [a-zA-Z0-9_-]{1,64}
|
|
169
|
+
prompt: { core, preset, app, locales },
|
|
170
|
+
runtime: async (request, { user, client }) => ({ role: 'admin' }),
|
|
171
|
+
identify: async (request) => ({ id: 'user-1' }), // optional
|
|
172
|
+
maxSteps: 10, // model ↔ tool round trips per request
|
|
173
|
+
approvalSecret, // default: process.env.DOPUS_APPROVAL_SECRET
|
|
174
|
+
callOptions: {
|
|
175
|
+
temperature,
|
|
176
|
+
maxOutputTokens,
|
|
177
|
+
providerOptions,
|
|
178
|
+
maxRetries,
|
|
179
|
+
headers,
|
|
180
|
+
onEnd,
|
|
181
|
+
onStepEnd,
|
|
182
|
+
},
|
|
183
|
+
onError: (error) => {}, // default: console.error
|
|
184
|
+
})
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Providers
|
|
188
|
+
|
|
189
|
+
| Provider | Package | API key env | Default model |
|
|
190
|
+
| --------- | ------------------- | ------------------------------ | ------------------- |
|
|
191
|
+
| OpenAI | `@ai-sdk/openai` | `OPENAI_API_KEY` | `gpt-5.5` |
|
|
192
|
+
| Anthropic | `@ai-sdk/anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-5-5` |
|
|
193
|
+
| Google | `@ai-sdk/google` | `GOOGLE_GENERATIVE_AI_API_KEY` | `gemini-3.5-flash` |
|
|
194
|
+
|
|
195
|
+
To switch providers or models, change `model` in `agent.config.ts`. Any AI SDK model works there.
|
|
196
|
+
|
|
197
|
+
## Testing without an API key
|
|
198
|
+
|
|
199
|
+
Swap in a scripted `MockLanguageModelV4` from `ai/test` behind an environment variable. [`examples/next-app`](https://github.com/akodm/dopus/tree/main/examples/next-app) does this with `DOPUS_MOCK_MODEL=1`. Its e2e tests drive the real route through `next start` without calling any LLM.
|
|
200
|
+
|
|
201
|
+
## Status and roadmap
|
|
202
|
+
|
|
203
|
+
dopus is at 0.1, so expect breaking changes before 1.0. It is tested on Next.js 16 with the Node.js runtime.
|
|
204
|
+
|
|
205
|
+
Planned next:
|
|
206
|
+
|
|
207
|
+
- infra adapters for long-running agents (AWS first, then Cloudflare, then GCP);
|
|
208
|
+
- `dopus prompts diff` to compare your copies with newer templates;
|
|
209
|
+
- tool input schemas inferred from TypeScript types.
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
pnpm install
|
|
215
|
+
pnpm build && pnpm lint && pnpm typecheck && pnpm test
|
|
216
|
+
pnpm example:build && pnpm example:e2e # next start + scripted model
|
|
217
|
+
pnpm smoke # packed tarball → fresh app → dopus init → next build
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Design notes: [`docs/design.md`](docs/design.md).
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|