@openduo/duoduo 0.2.3 → 0.2.5
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 +52 -459
- package/dist/release/cli.js +547 -475
- package/dist/release/daemon.js +178 -177
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,525 +2,118 @@
|
|
|
2
2
|
|
|
3
3
|
**An autonomous agent runtime where intelligence is durable, not disposable.**
|
|
4
4
|
|
|
5
|
-
Most agent stacks are request/response wrappers
|
|
6
|
-
duoduo is built as a long-lived runtime with a durable body (filesystem), explicit event history (WAL), and a dual-loop cognitive model (foreground + subconscious).
|
|
7
|
-
|
|
8
|
-
## Why duoduo
|
|
9
|
-
|
|
10
|
-
duoduo is opinionated around one idea:
|
|
11
|
-
|
|
12
|
-
> An agent should behave like a living system with continuity, not a stateless function call.
|
|
13
|
-
|
|
14
|
-
That means:
|
|
15
|
-
|
|
16
|
-
- durable event history before execution
|
|
17
|
-
- recoverable session actors with mailbox scheduling
|
|
18
|
-
- background reflection that updates shared memory
|
|
19
|
-
- prompt topology the system can evolve over time
|
|
5
|
+
Most agent stacks are request/response wrappers: prompt in, answer out, state gone. duoduo is built as a long-lived runtime with a durable body (filesystem), explicit event history, and a dual-loop cognitive model — foreground conversations plus a background subconscious that runs continuously.
|
|
20
6
|
|
|
21
7
|
## Core Innovations
|
|
22
8
|
|
|
23
9
|
### 1. Filesystem-First, Event-Sourced Runtime
|
|
24
10
|
|
|
25
|
-
|
|
11
|
+
Most agent runtimes keep session state in process memory. duoduo externalizes everything — conversations, outputs, jobs, memory — to durable files. The canonical event log (WAL) is written before any action executes. If the process dies mid-turn, nothing is lost: the system rehydrates from files and resumes exactly where it left off.
|
|
26
12
|
|
|
27
|
-
|
|
28
|
-
- **Mailbox**: session work queue
|
|
29
|
-
- **Outbox**: durable delivery records
|
|
30
|
-
- **Registry**: resumable runtime/session state
|
|
31
|
-
|
|
32
|
-
If the process dies, the system rehydrates from files and continues.
|
|
13
|
+
This is not a backup strategy. It is the primary design: the filesystem is the database, the event log is the source of truth, and the process is stateless by default.
|
|
33
14
|
|
|
34
15
|
### 2. WAL-Before-Execute at the Gateway Boundary
|
|
35
16
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`Channel input -> canonical Spine event -> mailbox enqueue -> session wake`
|
|
39
|
-
|
|
40
|
-
Events are durably written before any agent turn executes. This gives replayability, auditability, and deterministic crash recovery.
|
|
17
|
+
Every inbound message follows a strict contract: write a canonical event to the append-only log _first_, then enqueue the session, then execute. This ordering gives three properties simultaneously: replayability (re-run any input from the log), auditability (every event is permanently recorded), and crash recovery (restart resumes from the last committed event, not from volatile memory).
|
|
41
18
|
|
|
42
19
|
### 3. One External Identity, Many Internal Sessions
|
|
43
20
|
|
|
44
|
-
Externally, duoduo
|
|
45
|
-
Internally, it orchestrates multiple session actors (channel sessions, job sessions, meta session), each with explicit lifecycle and concurrency control.
|
|
46
|
-
|
|
47
|
-
This separates user-facing continuity from execution scalability.
|
|
21
|
+
Externally, duoduo presents one coherent agent identity. Internally, it orchestrates multiple concurrent session actors — one per conversation channel, plus background job sessions and subconscious partitions — each with explicit lifecycle and concurrency control (one runner per session, enforced by lease locks). This separates user-facing continuity from execution scalability.
|
|
48
22
|
|
|
49
|
-
### 4. Dual-Loop Cognition
|
|
23
|
+
### 4. Dual-Loop Cognition: Cortex + Subconscious
|
|
50
24
|
|
|
51
|
-
|
|
52
|
-
- **Subconscious (background)**: cadence-driven, playlist-scheduled partition execution.
|
|
25
|
+
**Foreground (Cortex)** responds to live channel messages. Sessions persist across restarts and resume conversation history exactly.
|
|
53
26
|
|
|
54
|
-
Subconscious
|
|
27
|
+
**Background (Subconscious)** runs on a cadence regardless of foreground activity. It consolidates memory, reflects on past sessions, and maintains a broadcast board of curated knowledge that is automatically loaded into every future session's context. The subconscious runs even when no one is chatting.
|
|
55
28
|
|
|
56
29
|
### 5. Self-Programming Cognitive Topology
|
|
57
30
|
|
|
58
|
-
Subconscious
|
|
59
|
-
|
|
60
|
-
- modify partition prompts
|
|
61
|
-
- add/remove partitions
|
|
62
|
-
- adjust execution cadence
|
|
63
|
-
|
|
64
|
-
The runtime ships a scaffold; long-term behavior is increasingly authored by the agent through durable files.
|
|
65
|
-
|
|
66
|
-
### 6. Radical Minimal Runtime Layer
|
|
67
|
-
|
|
68
|
-
duoduo intentionally keeps application code thin and delegates reasoning/tool orchestration to the foundation model + SDK.
|
|
69
|
-
The runtime owns only what the model cannot reliably own by itself: durability, lifecycle, scheduling, and concurrency boundaries.
|
|
70
|
-
|
|
71
|
-
## How It Works
|
|
72
|
-
|
|
73
|
-
```text
|
|
74
|
-
Channel -> Gateway -> Spine WAL -> Mailbox -> Session Manager/Runner -> SDK -> Outbox -> Channel
|
|
75
|
-
|
|
|
76
|
-
+-> Cadence -> Meta Session -> Playlist Partitions -> Memory updates
|
|
77
|
-
```
|
|
31
|
+
Subconscious behavior is defined by filesystem files — each partition has its own prompt, schedule, and cooldown. Partitions can modify their own prompts, create new partitions, and adjust their execution schedule. The runtime ships a scaffold; long-term behavior is increasingly authored by the agent itself through durable files, making the system self-extending over time.
|
|
78
32
|
|
|
79
|
-
###
|
|
33
|
+
### 6. Minimal Runtime Layer, Maximum Model Delegation
|
|
80
34
|
|
|
81
|
-
|
|
35
|
+
duoduo keeps application code deliberately thin. It delegates reasoning, tool orchestration, and planning entirely to the foundation model and SDK. The runtime owns only what the model cannot reliably own by itself: durability, lifecycle, scheduling, and concurrency boundaries. This makes the system forward-compatible: as the model improves, the system improves without code changes.
|
|
82
36
|
|
|
83
|
-
|
|
37
|
+
## Core Principles
|
|
84
38
|
|
|
85
|
-
|
|
39
|
+
### Filesystem-First, Event-Sourced
|
|
86
40
|
|
|
87
|
-
|
|
41
|
+
All state — conversations, memory, jobs, session outputs — lives in files. Nothing critical is in process memory only. If the process dies, the system rehydrates from durable state and continues.
|
|
88
42
|
|
|
89
|
-
|
|
43
|
+
Every inbound message is written to an append-only event log before any action executes (WAL-before-execute). This gives replayability, auditability, and deterministic crash recovery.
|
|
90
44
|
|
|
91
|
-
|
|
45
|
+
### One Identity, Many Sessions
|
|
92
46
|
|
|
93
|
-
|
|
94
|
-
1. Gateway normalizes input and appends a canonical event to `~/.aladuo/var/events/<yyyy-mm-dd>.jsonl`.
|
|
95
|
-
1. Gateway enqueues a mailbox item to `~/.aladuo/var/sessions/<sha256(session_key)>/inbox/*.pending`.
|
|
96
|
-
1. Session Manager receives `session.wake`, starts/rehydrates the actor, and begins mailbox drain.
|
|
97
|
-
1. Runner merges inbox into `mailbox.md`, resolves `@evt(...)` from Spine, and executes one SDK turn.
|
|
98
|
-
1. During execution, stream chunks are emitted as `session.stream`; final output is persisted to `~/.aladuo/var/outbox/<channel_kind>/obx_*.json` and emitted as `session.output`.
|
|
99
|
-
1. Client receives output through WebSocket notifications or `channel.pull`; `channel.ack` advances delivery cursor.
|
|
100
|
-
1. On cadence ticks, Meta Session runs playlist partitions; memory-weaver writes fragments/dossiers and curates `~/aladuo/memory/CLAUDE.md`.
|
|
101
|
-
1. `memory-committer` reviews allowlisted kernel changes and commits meaningful memory/subconscious/config evolution into the kernel Git history.
|
|
102
|
-
1. Next foreground turn loads updated broadcast memory automatically, so behavior evolves with durable context, while Git preserves how that cognition changed over time.
|
|
47
|
+
Externally, duoduo presents one coherent agent. Internally, it orchestrates multiple session actors — one per conversation, plus background job sessions and subconscious partitions — each with explicit lifecycle and concurrency control.
|
|
103
48
|
|
|
104
|
-
|
|
49
|
+
### Dual-Loop Cognition
|
|
105
50
|
|
|
106
|
-
|
|
51
|
+
**Foreground (Cortex)** handles live conversations. Each channel (Feishu, CLI, etc.) maps to a dedicated session with its own history. Sessions persist across restarts and resume from where they left off.
|
|
107
52
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
```text
|
|
111
|
-
bootstrap/meta-prompt.md Identity prompt source (runner append chain)
|
|
112
|
-
kernel/config/<kind>.md Kind prompt (runner systemPrompt layer)
|
|
113
|
-
descriptor.md Channel prompt (runner systemPrompt layer)
|
|
114
|
-
memory/CLAUDE.md Broadcast board (agent-curated, via additionalDirectories)
|
|
115
|
-
cwd/CLAUDE.md Work/partition prompt (SDK auto-load)
|
|
116
|
-
cwd/CLAUDE.local.md Local runtime notes (SDK auto-load)
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Ownership model:
|
|
120
|
-
|
|
121
|
-
- `CLAUDE.md`: code-owned scaffold
|
|
122
|
-
- `CLAUDE.local.md`: agent-managed runtime notes
|
|
123
|
-
|
|
124
|
-
## Memory Model (Dossier-First)
|
|
125
|
-
|
|
126
|
-
```text
|
|
127
|
-
memory/
|
|
128
|
-
├── CLAUDE.md Cross-session broadcast board (auto-loaded)
|
|
129
|
-
├── index.md Bounded memory index
|
|
130
|
-
├── entities/ Entity dossiers
|
|
131
|
-
├── topics/ Topic dossiers
|
|
132
|
-
├── fragments/ Raw per-tick observations
|
|
133
|
-
└── state/ Memory cursor/state
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Memory formation path:
|
|
137
|
-
|
|
138
|
-
`Spine events -> memory-weaver partition -> fragments -> dossiers -> broadcast board`
|
|
139
|
-
|
|
140
|
-
Memory evolution path:
|
|
141
|
-
|
|
142
|
-
`dossiers/broadcast/config delta -> memory-committer partition -> kernel git commit`
|
|
143
|
-
|
|
144
|
-
## Runtime Filesystem
|
|
145
|
-
|
|
146
|
-
```text
|
|
147
|
-
~/.aladuo/ Runtime state (ephemeral)
|
|
148
|
-
var/events/ Spine WAL (JSONL)
|
|
149
|
-
var/registry/ Session/runtime snapshots
|
|
150
|
-
var/outbox/ Durable outbound records
|
|
151
|
-
var/sessions/ Session mailbox + metadata
|
|
152
|
-
var/jobs/active/ Job definitions + sidecars
|
|
153
|
-
var/cadence/ Cadence queue + inbox
|
|
154
|
-
run/locks/ Session lease locks
|
|
155
|
-
|
|
156
|
-
~/aladuo/ Kernel (persistent agent state)
|
|
157
|
-
.git/ Memory evolution history
|
|
158
|
-
.gitignore Runtime-noise exclusions for kernel history
|
|
159
|
-
CLAUDE.md System-plane prompt
|
|
160
|
-
CLAUDE.local.md Runtime notes
|
|
161
|
-
subconscious/ Partition system
|
|
162
|
-
memory/ Shared memory surface
|
|
163
|
-
```
|
|
53
|
+
**Background (Subconscious)** runs on a cadence regardless of foreground activity. It consolidates memory, reflects on past sessions, maintains a shared knowledge surface loaded into every future conversation, and executes scheduled jobs. Subconscious behavior is defined by filesystem files — partitions can modify their own prompts and schedule, making the system self-extending over time.
|
|
164
54
|
|
|
165
|
-
|
|
166
|
-
dedicated Git repository with a genesis commit, so duoduo's durable memory has both a current
|
|
167
|
-
surface and an auditable evolution path. The tracked history is intentionally curated:
|
|
55
|
+
### Channel-Agnostic Protocol
|
|
168
56
|
|
|
169
|
-
-
|
|
170
|
-
- `subconscious/*/CLAUDE.md`, `subconscious/playlist.md`
|
|
171
|
-
- `config/**/*.md`
|
|
57
|
+
The core runtime speaks a standard JSON-RPC protocol. Channel adapters (Feishu, ACP, etc.) are independent processes that translate between their native protocol and this standard. Adding a new channel requires zero changes to the core.
|
|
172
58
|
|
|
173
|
-
|
|
174
|
-
`memory/fragments/`, `subconscious/inbox/`, `.claude/`, `*.tmp`, and `CLAUDE.local.md`.
|
|
175
|
-
|
|
176
|
-
## Engineering Invariants
|
|
177
|
-
|
|
178
|
-
- **Container-first truth**: production behavior is defined by container runtime.
|
|
179
|
-
- **Deterministic I/O**: atomic durable writes around critical transitions.
|
|
180
|
-
- **Boundary clarity**: JSON schema at machine boundaries, Markdown for agent cognition.
|
|
181
|
-
- **No hidden side effects**: explicit lifecycle, explicit wake/schedule semantics.
|
|
182
|
-
|
|
183
|
-
## Project Structure (pnpm Monorepo)
|
|
59
|
+
## How It Works
|
|
184
60
|
|
|
185
61
|
```text
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
│ └── channel-feishu/ @openduo/channel-feishu — Feishu/Lark gateway
|
|
191
|
-
├── pnpm-workspace.yaml Workspace definition
|
|
192
|
-
├── tsconfig.base.json Shared TS compiler options
|
|
193
|
-
└── docker-compose.dev.yml Dev container with hot-reload
|
|
62
|
+
Channel → Gateway → Event Log → Mailbox → Session Runner → Outbox → Channel
|
|
63
|
+
↑
|
|
64
|
+
Cadence → Subconscious partitions ─┘
|
|
65
|
+
→ Job scanner → Job sessions
|
|
194
66
|
```
|
|
195
67
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
- **`@openduo/protocol`** — pure types and validators shared between kernel and channel packages. Zero runtime dependencies.
|
|
199
|
-
- **`@openduo/channel-feishu`** — independent Feishu gateway process that communicates with the daemon via JSON-RPC/WebSocket. Kernel does not depend on it.
|
|
68
|
+
**Gateway** normalizes raw channel inputs into canonical events, routes them to the right session, and handles commands (`/status`, `/cancel`, `/cd`, etc.) without requiring an active foreground session.
|
|
200
69
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
### Super Entry (Host Quick Start)
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
npx @openduo/duoduo
|
|
207
|
-
```
|
|
70
|
+
**Event Log** is the append-only source of truth. All events are durable before execution.
|
|
208
71
|
|
|
209
|
-
|
|
72
|
+
**Session Runner** drains session mailboxes and executes SDK turns. One runner per session, enforced by lease locks.
|
|
210
73
|
|
|
211
|
-
|
|
212
|
-
2. If unavailable, start an embedded host daemon in the same process.
|
|
213
|
-
3. Attach current workspace session and enter chat immediately.
|
|
74
|
+
**Subconscious** is a playlist-driven set of background partitions. Each runs stateless per cadence tick with its own prompt. Partitions can create new partitions.
|
|
214
75
|
|
|
215
|
-
|
|
76
|
+
**Job System** manages recurring work as files with cron schedules. The cadence scanner evaluates due jobs each tick and spawns dedicated sessions to execute them.
|
|
216
77
|
|
|
217
|
-
|
|
218
|
-
duoduo daemon status
|
|
219
|
-
duoduo daemon start
|
|
220
|
-
duoduo daemon stop
|
|
221
|
-
duoduo channel list
|
|
222
|
-
duoduo channel install ./duoduo-channel-feishu-<ver>.tgz
|
|
223
|
-
duoduo channel feishu start --gateway
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Where to use `npx @openduo/duoduo`:
|
|
227
|
-
|
|
228
|
-
1. In any terminal directory where you want to chat/attach that workspace session.
|
|
229
|
-
2. No prior global install is required.
|
|
230
|
-
|
|
231
|
-
Install options:
|
|
232
|
-
|
|
233
|
-
1. Run directly from local tarball (offline-friendly, no global install):
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
npx --yes --package /absolute/path/to/duoduo-<version>.tgz duoduo
|
|
237
|
-
```
|
|
78
|
+
**Memory Pipeline** reads the event log, extracts insights, and promotes them to a broadcast board automatically loaded into every future session's context.
|
|
238
79
|
|
|
239
|
-
|
|
80
|
+
## Installation
|
|
240
81
|
|
|
241
82
|
```bash
|
|
242
|
-
npm install -g /
|
|
83
|
+
npm install -g @openduo/duoduo
|
|
243
84
|
duoduo
|
|
244
85
|
```
|
|
245
86
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
### Public Distribution Direction
|
|
249
|
-
|
|
250
|
-
For external distribution, the practical split is:
|
|
87
|
+
Requires Node.js 20+. Docker is recommended for container mode.
|
|
251
88
|
|
|
252
|
-
|
|
253
|
-
2. Publish the production container image to GHCR.
|
|
89
|
+
On first run, an interactive wizard walks through model configuration, runtime mode, and directory setup.
|
|
254
90
|
|
|
255
|
-
|
|
91
|
+
## Runtime Modes
|
|
256
92
|
|
|
257
|
-
|
|
258
|
-
- GitHub's npm registry is workable for private/internal installs, but public package installs still add consumer-side auth friction, which is a poor fit for a zero-friction CLI entry.
|
|
259
|
-
- GHCR public containers are a better fit for private-source delivery because users can pull and run built images without checking out the repository.
|
|
93
|
+
**Container mode** (recommended) — runs the daemon inside Docker. Isolated, consistent toolchain, automatic restart. State persists in host-mounted directories.
|
|
260
94
|
|
|
261
|
-
|
|
95
|
+
**Host mode** — runs the daemon directly on your machine. Useful when Docker is unavailable.
|
|
262
96
|
|
|
263
|
-
|
|
264
|
-
pnpm run build:release
|
|
265
|
-
```
|
|
97
|
+
## Channel Plugins
|
|
266
98
|
|
|
267
|
-
|
|
99
|
+
Channels connect duoduo to external messaging platforms. Install and start a channel plugin alongside the daemon:
|
|
268
100
|
|
|
269
101
|
```bash
|
|
270
|
-
|
|
102
|
+
duoduo channel install @openduo/channel-feishu
|
|
103
|
+
duoduo channel feishu start
|
|
271
104
|
```
|
|
272
105
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
See also: `docs/40-ops/DistributionStrategy.md`
|
|
276
|
-
|
|
277
|
-
### Private Tarball Distribution (Core + Feishu Plugin)
|
|
278
|
-
|
|
279
|
-
```bash
|
|
280
|
-
pnpm run release:offline
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Outputs (default: `dist/offline/`):
|
|
284
|
-
|
|
285
|
-
1. `duoduo-<version>.tgz`
|
|
286
|
-
2. `duoduo-channel-feishu-<version>.tgz`
|
|
287
|
-
3. `install-offline.sh` (prints offline install/run commands)
|
|
288
|
-
|
|
289
|
-
Scope note: this flow packages private duoduo code as local tarballs. It does not
|
|
290
|
-
vendor all public npm dependencies; install hosts still need access to npm (or a
|
|
291
|
-
reachable internal mirror/cache) for public packages such as
|
|
292
|
-
`@anthropic-ai/claude-agent-sdk`.
|
|
293
|
-
|
|
294
|
-
With pre-pack verification:
|
|
295
|
-
|
|
296
|
-
```bash
|
|
297
|
-
bash scripts/release-offline.sh --verify
|
|
298
|
-
```
|
|
106
|
+
Available:
|
|
299
107
|
|
|
300
|
-
|
|
108
|
+
- [`@openduo/channel-feishu`](https://www.npmjs.com/package/@openduo/channel-feishu) — Feishu / Lark
|
|
301
109
|
|
|
302
|
-
|
|
303
|
-
pnpm install # Install all workspace dependencies
|
|
304
|
-
pnpm run dev # Start daemon in dev container (hot-reload)
|
|
305
|
-
pnpm run start:stdio # Connect CLI to running daemon
|
|
306
|
-
pnpm run start:acp # Start ACP gateway over stdio
|
|
307
|
-
pnpm test # Local tests (fast)
|
|
308
|
-
pnpm run test:container # Container test suite (source of truth)
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
For machine-specific mounts, use a local override file that is not committed:
|
|
312
|
-
|
|
313
|
-
```bash
|
|
314
|
-
cp docker-compose.dev.local.example.yml docker-compose.dev.local.yml
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
ACP gateway emits structured `session/update` tool lifecycle events (`tool_call` + `tool_call_update`)
|
|
318
|
-
with `pending/completed` status, parsed `rawInput/rawOutput`, and text `content` for client rendering compatibility.
|
|
319
|
-
When streaming chunks are available, ACP suppresses duplicated final assistant text and keeps full tool payloads
|
|
320
|
-
(`rawInput`/tool output summaries) without fixed 200-character truncation.
|
|
321
|
-
Real-time Claude thinking deltas are forwarded as `agent_thought_chunk` (not only `<think>...</think>` post-processing).
|
|
322
|
-
|
|
323
|
-
`scripts/container-acp.sh` can be invoked via absolute path from any working directory, for example:
|
|
324
|
-
|
|
325
|
-
```bash
|
|
326
|
-
bash /abs/path/to/aladuo/scripts/container-acp.sh
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Feishu Channel Gateway
|
|
330
|
-
|
|
331
|
-
```bash
|
|
332
|
-
# Set credentials in .env:
|
|
333
|
-
# FEISHU_APP_ID=cli_xxx
|
|
334
|
-
# FEISHU_APP_SECRET=xxx
|
|
335
|
-
|
|
336
|
-
pnpm run start:feishu # Foreground (interactive)
|
|
337
|
-
pnpm run start:feishu:bg # Background (detached)
|
|
338
|
-
pnpm run stop:feishu # Stop background instance
|
|
339
|
-
pnpm run logs:feishu # View logs
|
|
340
|
-
pnpm run logs:feishu:tail # Tail logs (live)
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
Host plugin mode (super entry + plugin manager):
|
|
344
|
-
|
|
345
|
-
```bash
|
|
346
|
-
# Build plugin artifact first (creates packages/channel-feishu/dist/plugin.js)
|
|
347
|
-
pnpm run build:channel:feishu
|
|
348
|
-
|
|
349
|
-
# Pack plugin tarball
|
|
350
|
-
pnpm --dir packages/channel-feishu pack
|
|
351
|
-
|
|
352
|
-
# Install and run from core CLI
|
|
353
|
-
duoduo channel install ./packages/channel-feishu/duoduo-channel-feishu-<ver>.tgz
|
|
354
|
-
duoduo channel feishu start --gateway
|
|
355
|
-
duoduo channel feishu status
|
|
356
|
-
duoduo channel feishu logs
|
|
357
|
-
duoduo channel feishu stop
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
The Feishu gateway supports inbound/outbound media bridging (image/file/audio/video/sticker and post embedded media) via `im.messageResource` + `channel.file.upload/download`.
|
|
361
|
-
Uploaded files are persisted at `${ALADUO_WORK_DIR}/inbox/<original-filename>/<sha256>.<ext>` with source metadata in `meta.d/<sha256>.yaml`.
|
|
362
|
-
When a user replies to an earlier Feishu image/file message, the gateway now rehydrates the referenced attachment, re-uploads it idempotently to recover the same local path, includes that path in the reply context text, and forwards recovered parent attachments through ingress for multimodal turns.
|
|
363
|
-
The gateway also persists observed Feishu session keys at `~/.cache/feishu-channel/watched-sessions.json` and re-opens pull subscriptions on startup, so job callbacks can resume proactive push after channel process restarts.
|
|
364
|
-
|
|
365
|
-
### Production (Single duoduo Repo)
|
|
366
|
-
|
|
367
|
-
This repo now includes a production compose path for a single independent duoduo instance:
|
|
368
|
-
|
|
369
|
-
```bash
|
|
370
|
-
pnpm run build:release # Build dist/release (default minify=true)
|
|
371
|
-
pnpm run build:release:plain # Build dist/release without minify
|
|
372
|
-
pnpm run prod:build # Build runtime app image (uses container/Dockerfile.app)
|
|
373
|
-
pnpm run prod # Start daemon only
|
|
374
|
-
pnpm run prod:feishu # Start daemon + feishu-gateway (profile=feishu)
|
|
375
|
-
pnpm run prod:logs # Tail logs
|
|
376
|
-
pnpm run prod:down # Stop
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
Minify is enabled by default in release build. Override with:
|
|
380
|
-
|
|
381
|
-
- local build: `ALADUO_MINIFY=false pnpm run build:release`
|
|
382
|
-
- image build: `ALADUO_MINIFY=false pnpm run prod:build`
|
|
383
|
-
|
|
384
|
-
Files:
|
|
385
|
-
|
|
386
|
-
- `docker-compose.prod.yml`
|
|
387
|
-
- `container/Dockerfile.app`
|
|
388
|
-
- `scripts/prod.sh`
|
|
389
|
-
|
|
390
|
-
For npm tarball distribution, `package.json.files` includes `bootstrap/` so runtime seeding assets are shipped with the package.
|
|
391
|
-
|
|
392
|
-
### Container QuickStart Without Repo Source
|
|
393
|
-
|
|
394
|
-
Once the production image is published, users can launch a daemon container directly from the installed CLI:
|
|
395
|
-
|
|
396
|
-
```bash
|
|
397
|
-
duoduo container plan --name demo
|
|
398
|
-
duoduo container up --name demo --env-file .env
|
|
399
|
-
duoduo --daemon-url http://127.0.0.1:20233
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
Default instance data lives under `~/.aladuo/containers/<name>/`:
|
|
403
|
-
|
|
404
|
-
- `workspace/`
|
|
405
|
-
- `kernel/`
|
|
406
|
-
- `runtime/`
|
|
407
|
-
- `claude/`
|
|
408
|
-
|
|
409
|
-
Users can fully override those host mounts:
|
|
410
|
-
|
|
411
|
-
```bash
|
|
412
|
-
duoduo container up \
|
|
413
|
-
--name prod \
|
|
414
|
-
--workspace-dir ~/data/duoduo/workspace \
|
|
415
|
-
--kernel-dir ~/data/duoduo/kernel \
|
|
416
|
-
--runtime-dir ~/data/duoduo/runtime \
|
|
417
|
-
--claude-dir ~/data/duoduo/claude \
|
|
418
|
-
--env-file ~/data/duoduo/prod.env
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
Operational commands:
|
|
422
|
-
|
|
423
|
-
```bash
|
|
424
|
-
duoduo container ps --name prod
|
|
425
|
-
duoduo container logs --name prod --follow
|
|
426
|
-
duoduo container down --name prod
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### One Gateway, Multiple Independent duoduo Instances (Claim/Routing)
|
|
430
|
-
|
|
431
|
-
Each duoduo is still an independent runtime volume/process. To run multiple isolated instances on one host:
|
|
432
|
-
|
|
433
|
-
1. Start each daemon with a unique env file and project/container names:
|
|
434
|
-
|
|
435
|
-
```bash
|
|
436
|
-
pnpm run prod -- --env-file .env.owner --project-name aladuo-owner --no-build
|
|
437
|
-
pnpm run prod -- --env-file .env.tenant-a --project-name aladuo-tenant-a --no-build
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
When `--project-name` is set, `scripts/prod.sh` auto-derives unique container names:
|
|
441
|
-
|
|
442
|
-
- daemon: `<project-name>-daemon`
|
|
443
|
-
- feishu: `<project-name>-feishu`
|
|
444
|
-
|
|
445
|
-
2. Start only one Feishu gateway (usually owner/root side):
|
|
446
|
-
|
|
447
|
-
```bash
|
|
448
|
-
pnpm run prod -- --env-file .env.owner --project-name aladuo-owner --with-feishu --no-build
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
In production compose, `feishu-gateway` defaults to `--gateway` mode.
|
|
452
|
-
You can override mode/args through env:
|
|
453
|
-
|
|
454
|
-
- `FEISHU_CLI_ARGS=--channel`
|
|
455
|
-
- `FEISHU_CLI_ARGS=\"--channel --bootstrap-session-key lark:oc_xxx:ou_xxx --root-chat-id oc_root\"`
|
|
456
|
-
|
|
457
|
-
3. In CS mode, use bind flow to claim target chat session to a target daemon URL:
|
|
458
|
-
|
|
459
|
-
- `/cs request <target_session_key> <daemon_url>`
|
|
460
|
-
- user confirms in target chat: `/bind <code>`
|
|
461
|
-
|
|
462
|
-
Example: route one chat to tenant daemon
|
|
463
|
-
|
|
464
|
-
- `/cs request lark:oc_xxx:ou_xxx http://aladuo-tenant-a-daemon:20233`
|
|
465
|
-
|
|
466
|
-
`docker-compose.prod.yml` uses shared external network `${ALADUO_NETWORK:-aladuo}` so gateway can route to other daemon containers by URL.
|
|
467
|
-
|
|
468
|
-
### Key Environment Variables
|
|
469
|
-
|
|
470
|
-
```bash
|
|
471
|
-
ALADUO_DAEMON_URL=http://127.0.0.1:20233
|
|
472
|
-
ALADUO_LOG_LEVEL=debug # debug|info|warn|error
|
|
473
|
-
ALADUO_LOG_RUNNER_THOUGHT_CHUNKS=0 # default 0; set 1 to show runner thought_chunk debug logs
|
|
474
|
-
ALADUO_LOG_SESSION_LIFECYCLE=0 # default 0; set 1 to show session-manager/registry lifecycle debug logs
|
|
475
|
-
ACP_LOG_LEVEL=warn # ACP gateway log level (default: warn; stdio-safe)
|
|
476
|
-
ALADUO_CADENCE_INTERVAL_MS=600000 # compose default dev+prod (runtime fallback in code: 300000)
|
|
477
|
-
ALADUO_RUNTIME_MODE=yolo # yolo|container (host mode uses yolo)
|
|
478
|
-
SYSTEM_PROMPT=... # daemon-level custom system prompt
|
|
479
|
-
APPEND_SYSTEM_PROMPT=... # append to Claude Code preset prompt
|
|
480
|
-
ALADUO_META_PROMPT_PATH=... # optional meta-prompt file to prepend into append chain
|
|
481
|
-
FEISHU_APP_ID= # Feishu app credentials (optional)
|
|
482
|
-
FEISHU_APP_SECRET=
|
|
483
|
-
```
|
|
110
|
+
## Packages
|
|
484
111
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
-
|
|
489
|
-
|
|
490
|
-
- `runner` debug logs suppress high-frequency `thought_chunk` entries by default; set `ALADUO_LOG_RUNNER_THOUGHT_CHUNKS=1` when debugging streaming thoughts.
|
|
491
|
-
- `session-manager` / `registry` lifecycle debug logs are suppressed by default; set `ALADUO_LOG_SESSION_LIFECYCLE=1` when debugging wakes, idle transitions, or registry session upserts.
|
|
492
|
-
- Container mode rejects both provided and previously bound workspaces outside `ALADUO_WORK_DIR`.
|
|
493
|
-
|
|
494
|
-
## JSON-RPC Surface
|
|
495
|
-
|
|
496
|
-
| Method | Purpose |
|
|
497
|
-
| ------------------------------ | ----------------------------- |
|
|
498
|
-
| `channel.ingress` | Ingest user message |
|
|
499
|
-
| `channel.command` | Ingest channel/system command |
|
|
500
|
-
| `channel.file.upload/download` | File transfer |
|
|
501
|
-
| `channel.pull` | Pull/replay outputs |
|
|
502
|
-
| `channel.ack` | Advance delivery cursor |
|
|
503
|
-
| `job.create/get/list` | Job lifecycle management |
|
|
504
|
-
|
|
505
|
-
WebSocket notifications: `session.stream` (token chunks), `session.output` (final records).
|
|
506
|
-
|
|
507
|
-
`channel.pull` supports channel outbound capability declaration:
|
|
508
|
-
|
|
509
|
-
- `channel_capabilities.outbound.accept_mime: string[]` (required when declared)
|
|
510
|
-
- `channel_capabilities.outbound.max_bytes?: number`
|
|
511
|
-
- MIME wildcard patterns: exact (`image/png`), `type/*` (`image/*`), `*/*`
|
|
512
|
-
- `*.*` is not protocol-valid; adapters may normalize it to `*/*` before request.
|
|
513
|
-
- If omitted, daemon defaults to `accept_mime: []` (no outbound attachment support).
|
|
514
|
-
|
|
515
|
-
## Documentation
|
|
516
|
-
|
|
517
|
-
- [Architecture overview](docs/20-architecture/BootFlow.md)
|
|
518
|
-
- [Offline-first session model](docs/20-architecture/OfflineFirstSessionModel.md)
|
|
519
|
-
- [Filesystem spec](docs/30-runtime/FilesystemSpec.md)
|
|
520
|
-
- [Runner spec](docs/30-runtime/runtime/RunnerSpec.md)
|
|
521
|
-
- [Memory spec](docs/30-runtime/memory/MemorySpec.md)
|
|
522
|
-
- [Gateway spec](docs/30-runtime/gateway/GatewaySpec.md)
|
|
523
|
-
- [Channel protocol](docs/30-runtime/channels/Channels.md)
|
|
112
|
+
| Package | Description |
|
|
113
|
+
| ------------------------- | ------------------------------------ |
|
|
114
|
+
| `@openduo/duoduo` | Core runtime + CLI |
|
|
115
|
+
| `@openduo/channel-feishu` | Feishu / Lark channel adapter |
|
|
116
|
+
| `@openduo/protocol` | Shared RPC types (zero dependencies) |
|
|
524
117
|
|
|
525
118
|
## License
|
|
526
119
|
|