Channel adapters prefix platform IDs with their channel type
(e.g. "telegram:123"). Normalize in register.ts so the DB always
stores the canonical format. Removes fallback lookup from router.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move prefix handling from register.ts to router.ts. Users register with
raw platform IDs (what they naturally have), adapters send prefixed IDs
(their internal format). Router now tries stripping the channel type
prefix when the exact lookup fails, matching either format.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chat SDK adapters use prefixed platform IDs (e.g. "telegram:6037840640",
"discord:guildId:channelId") but users provide raw IDs during setup.
Without the prefix, the router can't match the registered messaging group
to incoming messages and silently drops them.
register.ts now auto-prefixes with the channel type if not already present.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use platformId directly as thread ID in deliver() and setTyping()
instead of calling encodeThreadId with Discord-shaped args — platformId
is already in the adapter's encoded format (e.g. "telegram:6037840640")
- Add triggerTyping() in delivery.ts, call from router on message route
- Enable Telegram channel in barrel
- Verified E2E: Telegram message in → agent → typing indicator → response
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add three-level isolation model (shared session, same agent, separate agent)
with agent-shared session mode for cross-channel shared sessions
- Create /manage-channels skill for wiring channels to agent groups
- Refactor all 12 v2 channel skills: lean SKILL.md + VERIFY.md + REMOVE.md
with structured Channel Info section for platform-specific metadata
- Create /add-discord-v2 skill (was missing)
- Add step 5a to setup SKILL.md invoking /manage-channels after channel install
- Update setup/verify.ts to check all 12 channel token types
- Add docs/v2-isolation-model.md explaining the isolation model
- Update v2-checklist.md and v2-setup-wiring.md to reflect completed work
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detailed status document for next session: what's done (two-DB split,
OneCLI, barrel, register.ts), what's not (channel skills don't call
register, no group creation step in setup, v1 add-discord incompatible).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Discord was directly imported in src/index.ts before the barrel wiring.
Moving to the barrel without uncommenting it broke Discord.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Import channel barrel from src/index.ts so channel skills that
uncomment lines in src/channels/index.ts actually execute
- Rewrite setup/register.ts to create v2 entities (agent_groups,
messaging_groups, messaging_group_agents) in data/v2.db instead
of v1's store/messages.db
- Fix setup/verify.ts to check v2 central DB for registered groups
- Add prominent "MESSAGE DROPPED" warnings in router when no agent
groups are wired, with actionable guidance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Eliminates SQLite write contention across the host-container mount
boundary by splitting the single session.db into two files, each with
exactly one writer:
inbound.db — host writes (messages_in, delivered tracking)
outbound.db — container writes (messages_out, processing_ack)
Key changes:
- Host uses even seq numbers, container uses odd (collision-free)
- Container heartbeat via file touch instead of DB UPDATE
- Scheduling MCP tools now emit system actions via messages_out
(host applies them to inbound.db during delivery)
- Host sweep reads processing_ack + heartbeat file for stale detection
- OneCLI ensureAgent() call added (was missing from v2, caused
applyContainerConfig to reject unknown agent identifiers)
Verified: tsc clean, 327 tests pass, real e2e through Docker works.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace `as never` cast with proper polyfill for channelIdFromThreadId.
Narrow GatewayAdapter cast to only the gateway code path in bridge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move all v1 files (index, router, container-runner, db, ipc, types,
logger, channels/registry, and all utilities) to src/v1/ as a
fully self-contained archive with no shared dependencies
- Rename v2 files to remove -v2 suffix (index-v2.ts → index.ts, etc.)
- Update all imports across v2 source, tests, and setup files
- Migrate shared utilities (config, env, container-runtime, mount-security,
timezone, group-folder) from pino logger to v2 log module
- Migrate setup/ files from logger to log with argument order swap
- Container agent-runner: move v1 entry to v1/, rename v2 to index.ts
- Update setup skill to offer all 13 v2 channels
- Install all Chat SDK adapter packages
- dist/index.js now runs v2; dist/v1/index.js runs v1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace in-memory Chat SDK state with SqliteStateAdapter — thread
subscriptions now persist across restarts
- Add migration 002 for chat_sdk_kv, subscriptions, locks, lists tables
- Handle /clear in agent-runner (reset sessionId) — SDK has
supportsNonInteractive:false for this command
- Pass /compact, /context, /cost, /files through to SDK as admin commands
- Skip admin commands in follow-up poll so they start fresh queries
- Emit compact_boundary events as user-visible feedback messages
- Pass NANOCLAW_ADMIN_USER_ID and NANOCLAW_ASSISTANT_NAME to containers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
End-to-end ask_user_question flow:
- Agent MCP tool writes question card to messages_out
- Host delivery creates pending_questions row, delivers as Discord Card with buttons
- Local webhook server receives Gateway INTERACTION_CREATE events
- Acknowledges interaction + updates card to show selected answer
- Routes response back to session DB as system message
- MCP tool poll picks up response and returns to agent
Key fixes:
- Poll loop now skips system messages (reserved for MCP tool responses)
- Gateway listener uses webhookUrl forwarding mode for interaction support
- Button custom_id encodes questionId + option text for self-contained routing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Host sweep: fix DELETE journal mode, busy_timeout, seq in recurrence INSERT
- Outbound files: delivery reads from outbox dir, passes buffers to adapter,
cleans up after delivery. Chat SDK bridge sends files via postMessage.
- Inbound attachments: formatter includes attachment info in prompts
- Commands: categorize /commands as admin, filtered, or passthrough.
Admin commands check sender against NANOCLAW_ADMIN_USER_ID.
Filtered commands silently dropped. Passthrough sent raw to agent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use DELETE journal mode for session DBs instead of WAL. WAL doesn't
sync reliably across Docker volume mounts (VirtioFS), causing dropped
writes and duplicate deliveries.
- Add 20s idle detection to end the query stream. The concurrent poll
tracks SDK activity via a new 'activity' provider event. When no SDK
events arrive for 20s and no messages are pending, the stream ends
and the poll loop continues.
- Add touchProcessing heartbeat so the host can distinguish active
agents from idle ones by checking status_changed recency.
- Catch query errors in the poll loop and write error responses to
messages_out instead of crashing the process.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ChannelAdapter interface with setup/deliver/teardown/setTyping lifecycle.
Self-registration pattern via channel-registry. Host wiring in index-v2
bridges inbound messages to routeInbound and outbound delivery to adapters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Override entrypoint to compile and run index-v2.js (no stdin)
- Add better-sqlite3 + @types to agent-runner dependencies
- Exclude test files from agent-runner tsconfig (Docker build)
- Add real e2e test script (host → container → Claude → session DB)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Host orchestrator connecting channel events to session DBs and
delivering responses back through channel adapters.
- session-manager.ts: session folder/DB lifecycle, message writing
- container-runner-v2.ts: Docker spawn with session + agent group
mounts, OneCLI, idle timeout, agent-runner recompilation
- router-v2.ts: inbound routing (channel → messaging group → agent
group → session → messages_in → wake container)
- delivery.ts: two-tier polling (1s active, 60s sweep) for
messages_out, channel adapter delivery
- host-sweep.ts: stale detection with backoff, recurrence, wake
containers for due messages
- index-v2.ts: thin entry point wiring everything together
- scripts/test-v2-agent.ts: real Claude provider integration test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Poll loop end-to-end with mock provider: message pickup, batch
processing, concurrent polling for late arrivals.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AgentProvider abstraction with Claude and Mock implementations.
Poll loop reads messages_in, formats by kind, queries provider,
writes results to messages_out. Concurrent polling pushes follow-up
messages into active queries.
- providers/types.ts: AgentProvider, AgentQuery, ProviderEvent
- providers/claude.ts: wraps Agent SDK with MessageStream, hooks,
transcript archiving
- providers/mock.ts: canned responses with push() support
- providers/factory.ts: createProvider()
- formatter.ts: format by kind (chat/task/webhook/system), XML
escaping, routing extraction
- poll-loop.ts: poll → format → query → write, concurrent polling
- mcp-tools.ts: MCP server with send_message tool
- index-v2.ts: new entry point (config from env, enters poll loop)
- 11 new tests, all 288 tests pass
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the v2 data layer: typed interfaces, central DB with migration
runner, per-entity CRUD, and agent-runner session DB operations.
- src/log.ts: concise message-first logging API
- src/types-v2.ts: AgentGroup, MessagingGroup, Session, MessageIn/Out
- src/db/: connection (WAL), migration runner, 001-initial schema,
CRUD for agent_groups, messaging_groups, sessions, pending_questions
- container/agent-runner/src/db/: session DB connection, messages_in
reads + status transitions, messages_out writes
- 31 new tests, all 277 tests pass
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skills document env vars in SKILL.md instead of patching config.ts.
Prettier printWidth 120 to keep log calls and signatures on one line.
Thin logging wrapper for one-line structured log calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Based on analysis of 33 skill branches. Maps each conflict hotspot
(index.ts, config.ts, container-runner.ts, db.ts) to its v2 solution.
Adds mount registration pattern so channel skills don't edit
container-runner. Config stays in the module that uses it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split DB by entity (agent-groups.ts, messaging-groups.ts, sessions.ts)
instead of one monolith. Numbered migration files replace inline
ALTER TABLE blocks. Channels use barrel pattern for self-registration.
Session DB split into messages-in.ts and messages-out.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Channels, MCP tools, and providers use registration patterns so
skill branches can add capabilities without conflicting. Index
stays thin. File map updated with channels/ and mcp-tools/
directories.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supply chain protection — npm will not install package versions
published less than 7 days ago.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Lint schedule now uses NanoClaw scheduled_tasks table instead of
Claude Code cron — runs in the group's agent container
2. CLAUDE.md must enforce one-at-a-time file ingestion — never batch
3. Expanded CLAUDE.md guidance: explain system, index files, point to
container skill, enforce ingest discipline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove hardcoded file path checks. Step 4 now discusses source types
with the user and helps install needed skills dynamically. Fix "use use"
typo and change curl example to file download.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove pre-written container skill. Instead, include llm-wiki.md
(Karpathy's gist) as the reference material and have the setup skill
guide the user through collaboratively building their own wiki schema,
container skill, and directory structure based on the pattern.
Add NanoClaw-specific notes: image vision, PDF reader, voice
transcription, curl for full document fetch, file attachment handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Container skill teaches the agent to maintain a structured, interlinked
wiki from ingested sources. Feature skill bootstraps the setup — directory
structure, group CLAUDE.md, optional scheduled lint.
Based on Karpathy's LLM Wiki pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace macOS-only `stat -f%z` with portable `wc -c` for Linux compat
- Replace `find | while` pipes with process substitution so TOTAL_FREED
counter survives the loop (pipe runs in subshell, losing mutations)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Session files (JSONLs, debug logs, todos, telemetry, group logs) accumulate
unboundedly — especially from daily cron tasks. This adds a cleanup script
that prunes old artifacts while protecting active sessions (read from DB),
and wires it into the main process on a 24h interval.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use sonnet[1m] for full 1M context window and set auto-compact at 200k
tokens to keep costs down while preserving access to extended context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches the pattern used by /setup and /update-nanoclaw. Captures
migration-specific properties (tier, phase, customization count,
skill interactions). Opt-out permanently disables across all skills.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Based on analysis of a live migration (v1.2.42 -> v1.2.47):
1. Absolute worktree paths: Bash tool resets cwd between calls,
so relative cd .upgrade-worktree fails. Store PROJECT_ROOT and
WORKTREE as absolute paths, use them throughout.
2. Smarter tier assessment: discount files from skill merges when
counting — a fork with 3 skills and no other changes is Tier 2,
not Tier 3 just because 24 files changed.
3. Inter-skill conflict analysis: new "Skill Interactions" section
in the migration guide captures conflicts between applied skills
(duplicate declarations, conflicting env var handling).
4. Cleaner swap recipe: use git reset --hard to the upgrade commit
instead of git checkout -B intermediate branch. Backup tag
preserves rollback. Copy guide to /tmp before worktree removal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces merge-based upgrades with a two-phase approach:
1. Extract: analyzes user's fork, captures customizations as a
migration guide (intent + implementation details in markdown)
2. Upgrade: checks out clean upstream in a worktree, reapplies
customizations from the guide, validates, and swaps in
Key features:
- Tiered complexity (lightweight/standard/complex)
- Sub-agent exploration with haiku for efficient analysis
- Incremental guide updates instead of full re-extraction
- Live e2e testing via worktree symlinks before swapping
- New-changes guard prevents losing unrecorded work
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Mount store/ separately as read-write so the main agent can access
the SQLite database directly.
- Add requiresTrigger parameter to the register_group MCP tool
(host IPC already supported it, but the tool never exposed it).
Defaults to false (no trigger).
- Update group registration instructions to ask user about trigger.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously required `[BREAKING]` at the start of the line, missing
entries formatted as `- [BREAKING] ...` in changelogs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The original regex didn't match the actual error ("No conversation
found with session ID: ..."). Added `no conversation found` pattern.
Removed the inline retry — clearing the session and returning 'error'
lets the existing group-queue.ts backoff loop retry with a fresh
session naturally. Simpler, no duplicate error paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The apple-container branch already includes the credential proxy code.
Applying /use-native-credential-proxy on top would conflict. Setup now
inlines the credential collection steps instead of delegating.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OneCLI is incompatible with Apple Container. Setup now picks the
credential system after the container runtime: Docker → OneCLI,
Apple Container → native credential proxy. Also marks Apple Container
as experimental, pauses after claude setup-token, limits AskUserQuestion
to multiple-choice, and removes telegram swarm upsell.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix container-runner bug: stopContainer() returns void but was
passed to exec() as a command string. Replace with direct call
and try/catch.
- Mock container-runtime in tests so they don't need Docker running.
- Increase claw-skill test timeout to handle slower python startup.
- Clear .env.example (telegram token was added by mistake).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop 23 transitive dependencies by replacing pino + pino-pretty with a
~70-line logger that matches the same output format and API. All 80+
call sites work unchanged. Production deps now: @onecli-sh/sdk,
better-sqlite3, cron-parser.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update SKILL.md to ask users during setup whether they want model
management tools (pull, delete, show, list-running) and set
OLLAMA_ADMIN_TOOLS=true in .env accordingly. Core inference tools
remain always available.
Incorporates #1456 by @bitcryptic-gw. Closes#1331.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
None of these are imported or referenced by the main codebase.
yaml had zero imports; zod is only used in container/agent-runner
(which has its own package.json); coverage-v8 was never configured.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>