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 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
- # Temporary Holding Version
1
+ # dopus
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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 {}