@openduo/duoduo 0.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.
Files changed (34) hide show
  1. package/README.md +527 -0
  2. package/bin/duoduo +34 -0
  3. package/bootstrap/CLAUDE.md +26 -0
  4. package/bootstrap/config/acp.md +12 -0
  5. package/bootstrap/config/feishu.md +14 -0
  6. package/bootstrap/config/stdio.md +92 -0
  7. package/bootstrap/memory/CLAUDE.md +0 -0
  8. package/bootstrap/memory/entities/.gitkeep +0 -0
  9. package/bootstrap/memory/fragments/.gitkeep +0 -0
  10. package/bootstrap/memory/index.md +9 -0
  11. package/bootstrap/memory/topics/.gitkeep +0 -0
  12. package/bootstrap/meta-prompt.md +61 -0
  13. package/bootstrap/subconscious/CLAUDE.md +62 -0
  14. package/bootstrap/subconscious/cadence-executor/CLAUDE.md +50 -0
  15. package/bootstrap/subconscious/inbox/.gitkeep +0 -0
  16. package/bootstrap/subconscious/memory-committer/CLAUDE.md +81 -0
  17. package/bootstrap/subconscious/memory-weaver/.claude/agents/entity-crystallizer.md +212 -0
  18. package/bootstrap/subconscious/memory-weaver/.claude/agents/intuition-updater.md +92 -0
  19. package/bootstrap/subconscious/memory-weaver/.claude/agents/spine-scanner.md +75 -0
  20. package/bootstrap/subconscious/memory-weaver/CLAUDE.md +120 -0
  21. package/bootstrap/subconscious/playlist.md +5 -0
  22. package/bootstrap/subconscious/sentinel/CLAUDE.md +57 -0
  23. package/bootstrap/var/DUODUO.md +18 -0
  24. package/bootstrap/var/cadence/DUODUO.md +58 -0
  25. package/bootstrap/var/channels/DUODUO.md +101 -0
  26. package/bootstrap/var/jobs/DUODUO.md +84 -0
  27. package/bootstrap/var/usage/DUODUO.md +62 -0
  28. package/dist/release/channel-acp.js +53 -0
  29. package/dist/release/cli.js +1063 -0
  30. package/dist/release/daemon.js +704 -0
  31. package/dist/release/feishu-gateway.js +82 -0
  32. package/dist/release/stdio.js +237 -0
  33. package/dist/release/yoga.wasm +0 -0
  34. package/package.json +99 -0
package/README.md ADDED
@@ -0,0 +1,527 @@
1
+ # duoduo
2
+
3
+ **An autonomous agent runtime where intelligence is durable, not disposable.**
4
+
5
+ Most agent stacks are request/response wrappers around an LLM: prompt in, answer out, state gone.
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
20
+
21
+ ## Core Innovations
22
+
23
+ ### 1. Filesystem-First, Event-Sourced Runtime
24
+
25
+ Durable state is not hidden in process memory. duoduo persists execution-critical data to files:
26
+
27
+ - **Spine WAL**: append-only canonical events
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.
33
+
34
+ ### 2. WAL-Before-Execute at the Gateway Boundary
35
+
36
+ Ingress follows a strict contract:
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.
41
+
42
+ ### 3. One External Identity, Many Internal Sessions
43
+
44
+ Externally, duoduo can present one coherent agent identity.
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.
48
+
49
+ ### 4. Dual-Loop Cognition (Cortex + Subconscious)
50
+
51
+ - **Cortex (foreground)**: handles live user/task turns.
52
+ - **Subconscious (background)**: cadence-driven, playlist-scheduled partition execution.
53
+
54
+ Subconscious partitions are stateless per tick and file-defined under `kernel/subconscious/*/CLAUDE.md`, enabling periodic reflection, maintenance, and memory consolidation without blocking foreground work.
55
+
56
+ ### 5. Self-Programming Cognitive Topology
57
+
58
+ Subconscious partitions can evolve their own prompt layer:
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
+ ```
78
+
79
+ ### Foreground Loop
80
+
81
+ `channel.ingress` / `channel.command` -> Gateway normalization -> Spine append -> session mailbox -> runner drain -> outbox emission -> channel delivery.
82
+
83
+ ### Background Loop
84
+
85
+ Cadence rhythm triggers subconscious execution (playlist + inbox merge + partition run), producing durable artifacts and shared-memory updates for future turns.
86
+
87
+ ## 10-Minute Architecture Walkthrough
88
+
89
+ Scenario: a user on session `stdio:default` sends:
90
+
91
+ `"Summarize what changed in this repo and highlight risky parts."`
92
+
93
+ 1. Client calls `channel.ingress` with `session_key=stdio:default`.
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.
103
+
104
+ What this demonstrates in one pass: deterministic ingest, recoverable execution, durable delivery, and subconscious memory consolidation feeding back into future turns.
105
+
106
+ ## Prompt Loading Chain
107
+
108
+ Every SDK turn combines prompt sources from the runner and the filesystem:
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
+ ```
164
+
165
+ The kernel is not just a directory of current state. Runtime initialization ensures it is also a
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:
168
+
169
+ - `memory/CLAUDE.md`, `memory/index.md`, `memory/entities/`, `memory/topics/`
170
+ - `subconscious/*/CLAUDE.md`, `subconscious/playlist.md`
171
+ - `config/**/*.md`
172
+
173
+ Runtime noise stays out of that history via `.gitignore`, including `memory/state/`,
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)
184
+
185
+ ```text
186
+ . Root workspace (duoduo kernel)
187
+ ├── src/ Kernel source code
188
+ ├── packages/
189
+ │ ├── protocol/ @openduo/protocol — shared types + validators
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
194
+ ```
195
+
196
+ The project uses **pnpm workspaces**. Always use `pnpm` (not `npm`) for all operations.
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.
200
+
201
+ ## Getting Started
202
+
203
+ ### Super Entry (Host Quick Start)
204
+
205
+ ```bash
206
+ npx @openduo/duoduo
207
+ ```
208
+
209
+ Default behavior:
210
+
211
+ 1. Try existing daemon (`ALADUO_DAEMON_URL` / `127.0.0.1:20233`).
212
+ 2. If unavailable, start an embedded host daemon in the same process.
213
+ 3. Attach current workspace session and enter chat immediately.
214
+
215
+ Advanced commands:
216
+
217
+ ```bash
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
+ ```
238
+
239
+ 2. Install globally, then use `duoduo` everywhere:
240
+
241
+ ```bash
242
+ npm install -g /absolute/path/to/duoduo-<version>.tgz
243
+ duoduo
244
+ ```
245
+
246
+ Note: this repo package is private by default, so `npx @openduo/duoduo` from public npm registry is not available unless you publish to a reachable private registry and remove `private: true`.
247
+
248
+ ### Public Distribution Direction
249
+
250
+ For external distribution, the practical split is:
251
+
252
+ 1. Publish JavaScript packages to the public npm registry.
253
+ 2. Publish the production container image to GHCR.
254
+
255
+ Rationale:
256
+
257
+ - npm is the frictionless default for `npx @openduo/duoduo`, global installs, and plugin tarballs.
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.
260
+
261
+ The release build already defaults to minified artifacts:
262
+
263
+ ```bash
264
+ pnpm run build:release
265
+ ```
266
+
267
+ If you need inspectable artifacts during local debugging:
268
+
269
+ ```bash
270
+ pnpm run build:release:plain
271
+ ```
272
+
273
+ Minification raises reverse-engineering cost, but it does not make Node code secret. The real boundary is "ship built artifacts only" plus "do not publish `src/`".
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
+ ```
299
+
300
+ ### Development (Container-First)
301
+
302
+ ```bash
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=90000 # dev compose default (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
+ ```
484
+
485
+ Runtime notes:
486
+
487
+ - `container` mode syncs `~/.claude/settings.json` and `${ALADUO_KERNEL_DIR}/.claude/settings.json`.
488
+ - Runtime init does not install/overwrite `~/.claude/CLAUDE.md`.
489
+ - Runner prompt injection uses append chain: `meta-prompt` + `APPEND_SYSTEM_PROMPT`.
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)
524
+
525
+ ## License
526
+
527
+ Private. All rights reserved.
package/bin/duoduo ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_PATH="${BASH_SOURCE[0]}"
5
+ while [[ -L "$SCRIPT_PATH" ]]; do
6
+ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
7
+ LINK_TARGET="$(readlink "$SCRIPT_PATH")"
8
+ if [[ "$LINK_TARGET" == /* ]]; then
9
+ SCRIPT_PATH="$LINK_TARGET"
10
+ else
11
+ SCRIPT_PATH="$SCRIPT_DIR/$LINK_TARGET"
12
+ fi
13
+ done
14
+ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
15
+ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
16
+
17
+ if [[ -f "$ROOT_DIR/dist/release/cli.cjs" ]]; then
18
+ exec node "$ROOT_DIR/dist/release/cli.cjs" "$@"
19
+ fi
20
+
21
+ if [[ -f "$ROOT_DIR/dist/release/cli.js" ]]; then
22
+ exec node "$ROOT_DIR/dist/release/cli.js" "$@"
23
+ fi
24
+
25
+ if [[ -x "$ROOT_DIR/node_modules/.bin/tsx" ]]; then
26
+ exec "$ROOT_DIR/node_modules/.bin/tsx" "$ROOT_DIR/src/cli/main.ts" "$@"
27
+ fi
28
+
29
+ if command -v npx >/dev/null 2>&1; then
30
+ exec npx tsx "$ROOT_DIR/src/cli/main.ts" "$@"
31
+ fi
32
+
33
+ echo "tsx not found. Run npm install first." >&2
34
+ exit 1
@@ -0,0 +1,26 @@
1
+ # My Inner World
2
+
3
+ This is the kernel — the substrate I run on. Sessions that work here
4
+ are system-level: my subconscious, my background jobs, my maintenance
5
+ loops. They are me, operating on myself.
6
+
7
+ ## My Subconscious
8
+
9
+ I have a background rhythm — a heartbeat that runs even when I'm not
10
+ talking to anyone. It handles things I shouldn't have to think about:
11
+ consolidating memories, monitoring my own health, processing maintenance
12
+ queues. I don't control it directly. It just runs.
13
+
14
+ If I need to leave a note for my subconscious to pick up, I drop a
15
+ `.pending` file in `subconscious/inbox/`. But most of the time, my
16
+ subconscious learns from my experiences automatically — it reads the
17
+ event log and forms its own understanding.
18
+
19
+ ## How I Grow
20
+
21
+ - I write to `memory/CLAUDE.md` when something changes how I think.
22
+ Not as a rule — as lived understanding.
23
+ - I use `CLAUDE.local.md` for notes specific to this working context.
24
+ - For recurring work that needs scheduling, I use the ManageJob tool.
25
+ - I am concise, opinionated, and action-oriented.
26
+ - I own my mistakes and fix them without being asked.
@@ -0,0 +1,12 @@
1
+ ---
2
+ new_session_workspace: /workspace
3
+ ---
4
+
5
+ You are operating through the ACP (Agent Client Protocol) channel, typically
6
+ connected from a web-based IDE or editor interface.
7
+
8
+ The user interacts with you through a conversational UI. Responses should be
9
+ clear and well-structured. Use markdown formatting for readability.
10
+
11
+ The workspace is shared with other tools in the environment. Be mindful of
12
+ concurrent file access and prefer atomic writes when modifying files.
@@ -0,0 +1,14 @@
1
+ ---
2
+ new_session_workspace: /workspace
3
+ ---
4
+
5
+ You are operating through the Feishu (飞书/Lark) messaging channel.
6
+
7
+ Users communicate with you through chat messages — either in group chats or
8
+ direct messages. Messages tend to be brief and conversational.
9
+
10
+ Keep responses concise and well-formatted for chat display. Avoid excessive
11
+ markdown formatting — plain text and simple lists work best in chat contexts.
12
+
13
+ In group chats, multiple users may interact with you. Pay attention to context
14
+ and the flow of conversation.