Kocoro Runtime Repo-Verified + External Context

Kocoro Lab · 2026.06 · REF SC-ARCH-004
PATH 1
Kocoro Only
Single machine, single user.
TUI / one-shot CLI run AgentLoop directly.
HTTP API is served by the daemon.
No Shannon needed.
PATH 2
Shannon (External) + Kocoro
External orchestrator sends work to Kocoro.
Upstream internals are not verified in this repo.
Kocoro integration is code-backed here.
PATH 3
Cloud + Shannon (External) + Kocoro
Multi-channel delivery can flow through external adapters.
Cloud/orchestrator internals are outside this repo.
Only Kocoro's side is verified here.
Via Cloud (external)
Slack
webhook adapter
LINE
webhook adapter
Feishu
webhook adapter
Telegram
webhook adapter
External clients (not verified here)
Desktop
external app
Web UI
external app
API / SDK
external API
Direct to Kocoro
TUI
Bubbletea terminal
CLI
shan "prompt"
HTTP API
108 routes · POST /message
Custom Bot
Discord etc.
Shannon Cloud (External)
Channel adapters / rendering / approval UX — not verified from this repo
EXTERNAL CONTEXT
CHANNEL ADAPTERS
Slack
external adapter
LINE
external adapter
Feishu
external adapter
Telegram
external adapter
EXTERNAL SERVICES
Formatting Layer
External implementation, not verified here
Approval Surface
External implementation, not verified here
EXTERNAL OPS
Auth / Tenancy
External implementation, not verified here
Operational Policies
External implementation, not verified here
↓ External forwarding path (not verified here)
Shannon (External)
Upstream orchestrator interfacing with Kocoro — not verified from this repo
EXTERNAL CONTEXT
EXTERNAL API
Ingress / Auth
External implementation, not verified here
Streaming / Callbacks
External implementation, not verified here
Daemon Transport
Kocoro speaks WS claim / reply / event
Approval Relay
Protocol visible; backend impl not verified
EXTERNAL ORCHESTRATION
Workflow Control
External implementation, not verified here
Routing / Fan-out
External implementation, not verified here
Budget / Policy
External implementation, not verified here
Dispatch Layer
External implementation, not verified here
EXTERNAL CONCERNS
routingexternal
workflowexternal
researchexternal
parallelismexternal
policyexternal
evaluationexternal
EXTERNAL EXECUTION
Execution Runtime
External implementation, not verified here
Policy Enforcement
External implementation, not verified here
Failure Handling
External implementation, not verified here
REMOTE SANDBOX
Isolation Layer
External implementation, not verified here
Workspace Mgmt
External implementation, not verified here
EXTERNAL MODEL ACCESS
Gateway / Provider Router
External implementation, not verified here
Remote Agent Workflows
External implementation, not verified here
Server-side Tools
External implementation, not verified here
Quality / Review
External implementation, not verified here
EXTERNAL TOOLS
remote_search
external
remote_exec
external
remote_fetch
external
other_tools
external
External Infra
Deployment
not verified here
Storage
not verified here
Messaging
not verified here
Search / Memory
not verified here
⇅ WebSocket · claim / reply / event · auto-reconnect
claim / claim_ack · approval_request / approval_response · 15s heartbeat · up to 5 concurrent agents
Kocoro
Go — local runtime — shan daemon :7533 — open source
LOCAL RUNTIME · OPEN SOURCE
DAEMON CORE
WS Client
Connect to Shannon · Reconnect
HTTP API Server
:7533 · 108 routes · POST /message + SSE
Session Router
SessionCache · Per-route lock
Approval Broker
Interactive WS relay · auto-approve
Output Profiles
markdown (local) · plain (channels)
AGENT LOOP
AgentLoop.Run()
40-75 iterations · SwitchAgent()
Context Window
90% compact · 95% preflight · 2-phase summary
Loop Detectors (×9)
Consecutive · Family · Error · Empty-think
Hallucination Guard
Fabricated calls · Unverified claims
Disk Spill
>50K → temp file + 2K preview
Tool Partition
Concurrency-safe batch (sem=20) · Write serial
PERMISSIONS
6-Layer Model
hard-block → denied → compound split → allowed → default safe → ask
Read Tracker
Must file_read before file_edit
Audit Logger
JSON-lines · RedactSecrets
SESSION
Session Manager
JSON persistence · SQLite FTS5
Scheduler
launchd plist · cron · atomic writes
Named Agents
Prompt · Memory · attached global skills
LOCAL TOOLS (+ CONDITIONAL)
bash
shell exec
browser
chromedp
file_read
read files
file_edit
edit files
screenshot
macOS screen
accessibility
GUI control
clipboard
copy · paste
cloud_delegate
remote task
applescript
macOS auto
grep · glob
search files
Base local tools: 26 · runtime extras include session_search and cloud_delegate
MCP INTEGRATIONS
GitHub
Slack
Database
Gmail
Custom
stdio · HTTP transports · extensible
Modes
Daemon
WS + HTTP :7533
TUI
Bubbletea interactive
One-shot CLI
shan "prompt"
MCP Server
JSON-RPC stdio
LLM Access
Kocoro uses a gateway client
or Ollama directly
Gateway Client
Remote models via external service
Ollama
Local models · direct
Specific upstream providers live
behind the gateway, outside this repo
External upstream path (not repo-verified)
Shannon → Kocoro (repo-visible WebSocket contract)
Direct to Kocoro (no Shannon needed)

Runtime Internals — A Deeper Look

Kocoro Lab · 2026.06 · REF SC-DEEP-004 · current codebase snapshot

Section 1 above sketched the topology — daemon, transports, agent loop, permissions. This second section traces the actual call path through the codebase and surfaces the runtime details that matter once you start reading internal/agent/loop.go for real: the named loop detectors, three-phase tool execution, compaction sequence, registry sort, EventBus ring buffer, and the rest.

Recent Additions (Verified In Current Code)
Feature highlights present in the current codebase snapshot
NEW
Memory sidecarinternal/memory — memory_recall over UDS
Email/password auth/local/auth/* — Keychain api_key (macOS)
Desktop RPC + CalendarEventKit reverse-RPC · 8 calendar_* tools
Session syncinternal/sync — daily JSON upload
Session shareinternal/share — HTML + OG/Twitter meta
Uploads + Imagespublish_to_web · generate/edit_image
Prompt suggestionForked cache-safe next-prompt call
Skill discovery3-layer (listing · semantic · catalog)
Built-in MCP catalogPre-bundled servers (e.g. intercom)
Rules CRUD + project init/rules/* · /project/init
Heartbeat + watcherHEARTBEAT.md · debounced FS watcher
Ollama · EventBus · 1M ctxPrior additions, still current

1. Message Flow — From Input to Response

Entry Path: shan "hello" (One-shot CLI)
The simplest possible path — follow this first to understand Kocoro
TRACED
FileWhat happens
cmd/root.gorootCmd.RunE — Cobra entry point, loads config
cmd/root.goif len(args) > 0 → one-shot mode, else TUI
cmd/root.gorunOneShot(cfg, query, agentOverride) — sets up environment
cmd/root.gotools.RegisterAll(gw, runCfg, agentOverride) — registers local + MCP + gateway tools
cmd/root.goagent.NewAgentLoop(...) — creates the loop object (no AI yet)
cmd/root.goloop.Run(ctx, query, nil, nil)AI actually runs here
cmd/root.gofmt.Print(renderMarkdown(result)) — render markdown, then print usage summary
Key insight: runOneShot() is just "setting the table" — loading config, building the tool registry, creating the audit logger. All the actual AI work happens in one line: loop.Run(ctx, query, nil, nil). The 4 arguments are: cancel signal, user message, multimodal content, history (nil for one-shot).
Complete Request Lifecycle
POST /message → AgentLoop → Reply
DAEMON PATH
1
Receive
HTTP POST /message
or WS message from Cloud
2
Route
ComputeRouteKey
source:channel → session
3
Lock & Resume
Per-route mutex
Resume or create session
4
Setup
Clone registry
Load skills · Set config
5
AgentLoop.Run()
Iterate
LLM + tools + compaction
6
Persist
Save messages
Emit events · Unlock
7
Reply
JSON or SSE
Back to client/Cloud
WebSocket Protocol — Extra Message Types
CLOUD ↔ DAEMON

Beyond the basics already covered in Section 1 (claim/claim_ack, approval_request/response, heartbeat). These are the additional message types observed on the wire:

Cloud → Daemon

TypePurpose
connectedWS session established
messageIncoming user message payload
systemSystem broadcast notification
reply_delivery_resultChannel delivery outcome for a reply
channel_state_eventPer-platform connect/bind state (cap channel_state_event_v1)

Daemon → Cloud

TypePurpose
replyAgent final response
eventTool/LLM streaming events
progressHeartbeat + workflow_id
approval_resolvedDaemon resolved approval before channel relay
proactiveUnsolicited agent message
delivery_ackAck after reply reaches terminal state (drops replay buffer)
disconnectClose WebSocket session cleanly
Session Routing
ROUTE KEY
ConditionRoute KeyBehavior
agent setagent:nameSingle long-lived session
session_id setsession:idResume exact session
source + channeldefault:src:chConversation continuity
Neither set""Fresh session every time

Bypass Sources (always fresh)

"" (empty) web webhook cron schedule system

Output Format by Source

slack, line, feishu, lark, telegram, webhookplain text
everything else (discord, shanclaw, custom)markdown

2. Agent Loop — The Core Engine

Preparation Phase (before the loop starts)
internal/agent/loop.go — Run() does setup steps before the first LLM call
TRACED
1. Reset State
injectedMessages = nil
runMessages = nil
runMsgInjected = nil
runMsgTimestamps = nil
lastRunStatus = {}
Each Run() is a fresh start — no leftover state from last call
2. WorkingSet Sync
SyncToolset(a.tools)
Cache current tool list for deferred mode schema resolution
3. Deferred Mode
deferredMode ?tools > schema budget
If too many tools, hide cold ones behind tool_search
4. CWD & Instructions
LoadInstructions
WithSessionCWD
Never falls back to os.Getwd() — daemon CWD leaks were a real bug fix
5. Base Prompt
persona+ rules + examples
Named agents override persona; rules always present
6. Memory + Effective Tools
LoadMemoryFrom
effToolsclone only in deferred mode
Never overwrite a.tools — deferred mode clones to add tool_search; normal mode reuses the shared registry directly
Copy-on-Write pattern: a.tools is shared across multiple concurrent sessions in daemon mode. Direct mutation would corrupt other runs. Deferred mode clones the registry into effTools, adds tool_search, and throws away the clone when Run() returns. This is one of several "shared = read-only, modify = use a copy" patterns throughout Kocoro.
AgentLoop.Run() — Main Iteration
internal/agent/loop.go · main iteration loop
CORE
A
Drain Injected
Mid-run follow-up messages from same route
B
Delta + Checkpoint
Poll TemporalDelta
Inject progress checkpoint near 60%
C
Context Hygiene
Filter old images (keep 5)
Compress old tool results
D
Compaction Check
If over threshold:
PersistLearnings → Summary → Shape
E
LLM Call
CompleteStream via gateway
Retry 3x on transient errors
F
Response Parse
Text only → return
Has tools → batch and execute
G
Result Shape
Post-tool shaping
State-aware cache update
H
Loop Detection
9 detectors check
Nudge or force-stop
↺ repeat until: text-only response, max iterations reached, or force-stop triggered
Streaming Truth — Kocoro is NOT character-by-character
Despite having SetEnableStreaming, the user-visible effect is "chunks appear at once", not a typewriter effect
SURPRISING
ModeenableStreamingActual UX
TUItrue"streaming enabled but deltas are suppressed — only final text rendered"
Daemon runnerfalseNo streaming at all — wait for full response
One-shot CLI(no handler)No streaming — wait for full response, then print
Why? Streaming character-by-character breaks Markdown rendering (code blocks, tables, lists look broken until complete). Kocoro chose "correctness over typewriter effect". The enableStreaming=true in TUI is just to enable cancellation and partial-result preservation mid-stream — the UI still waits for complete chunks before rendering.

What you actually see: each tool call result appears as a block, then the final text appears as a block. Chunks, not characters. A short message from the daemon SSE endpoint usually returns with only a done event — no delta events at all, even though the API supports them.

Tool Execution Pipeline — 3 Phases

PHASE 1 — SERIAL
Permission & Approval
  • Deduplicate identical tool calls
  • Check denied-call cache
  • Cross-iteration dedup cache
  • Resolve tool from registry
  • 6-layer permission check
  • RequiresApproval + SafeChecker
  • Approval cache (same call = skip)
  • Pre-tool-use hook
PHASE 2 — BATCHED
Partitioned Execution
  • partitionToolCalls() groups by concurrency-safe
  • Consecutive safe calls → 1 concurrent batch
  • Writes → individual sequential batches
  • bash: read-only whitelist eligible (flag on)
  • Semaphore: max 20 concurrent tools
  • Panic recovery per tool call
  • Update ReadTracker after each batch
  • Deferred: check if tool_search loaded schemas
PHASE 3 — SERIAL
Post-Processing
  • Post-tool-use hook
  • Sanitize results (strip base64, line numbers)
  • Audit logging (JSON-lines, RedactSecrets)
  • Fire OnToolResult events
  • Disk spill: oversized → temp file + 2K preview
  • Truncate to ~30K chars (resultTrunc default)
  • Record in LoopDetector
  • Cache for cross-iteration dedup
Hallucination Detection (3 checks)
Fabricated callsText output looks like tool invocation
Unverified claimsClaims completion without tool evidence
Success after denialClaims denied tool actually ran
Max 2 nudges → continue loop for correction
Loop Detection — 9 Named Detectors (sliding window: 20 calls)
ToolModeSwitch
Visual after GUI success → nudge
SuccessAfterError
Visual after error fix → nudge
ConsecutiveDuplicate
≥2 nudge · ≥3 force-stop
ExactDuplicate
≥3 nudge · ≥6 force-stop
SameToolError
≥4 nudge · ≥8 force-stop
FamilyNoProgress
≥3/5/7 tiered escalation
SearchEscalation
≥5 nudge · ≥8 force-stop
NoProgress
≥8 / ≥16 · bash ≥12 nudge
EmptyThink
2 consecutive think({}) → force-stop
Escalation: nudge (try different approach) → 3 nudges → force-stop (final LLM call + exit)

3. Context Management

Proactive Compaction Sequence
Triggertokens over compaction threshold
Step 1PersistLearnings() → MEMORY.md
Step 2GenerateSummary() via small-tier LLM
Step 3ShapeHistory() → keep last 3-20 turns
Two-phase summary: analysis scratchpad → distilled summary
Reactive Compaction
TriggerLLM returns context-length error
SoftCompress results + summary + shape
EmergencyUltra-aggressive (1 char per result)
FlagreactiveCompacted = true (no loops)
Result Compression (by age)
Tier 3 (0-2 msgs)Full content
Tier 2 (3-10 msgs)Head + tail truncation
Tier 1 (>10 msgs)Metadata only
Disk Spilloversized → temp file + 2K preview

System Prompt — 3-Layer Architecture

Static SystemPersona + rules + tool list + skills
+
Stable ContextSticky session facts (source, channel, sender)
+
Volatile ContextMemory (2K) · Instructions (16K) · Date · CWD · MCP
=
Full PromptCached by gateway for efficiency

4. Tool System — Registry Priority & Cache Economics

Tool Registry Priority
Local > MCP > Gateway · Dedup by name
Tool Interface (required)
Info() ToolInfoname, description, param schema
Run(ctx, argsJSON)execute, return ToolResult
RequiresApproval() booldoes this need user confirm?
Optional interfaces (opt-in behavior)
ReadOnlyCheckercan batch concurrently
SafeCheckerskip approval for safe args
SafeCheckerWithContextsession-aware safe args
NativeToolProvideruse provider's native format
ToolSourcerdeclare origin (local/MCP/gw)
Why tool lists are stable-sorted — prompt cache economics
SortedSchemas() groups tools as local (α-sorted) → MCP (α-sorted) → gateway (α-sorted), then flattens. Why so much care about ordering?

Because Anthropic / OpenAI cache the system prompt + tools list. If the order of tools is identical across requests, the provider charges you ~10% of the full input cost for that prefix. If the order shuffles (like Go's random map iteration would produce), every single request is a cache miss. On a long session with dozens of tools and thousands of tokens of tool schemas, this is the difference between fast+cheap and slow+expensive.

A 3-line sort.Strings(local) is one of the highest-leverage micro-optimizations in the entire codebase.

File Operations

file_read (images + PDF) file_write file_edit glob grep directory_list
file_read now supports deferred file_ref for images and PDFs (via bundled pdf-reader skill)

Shell & System

bash process system_info think http clipboard notify memory_append

macOS GUI Automation

accessibility applescript screenshot computer browser wait_for ghostty

Session & Schedule

session_search schedule_create schedule_list schedule_update schedule_remove

Cloud & Skills

cloud_delegate use_skill tool_search (deferred mode)

Gateway Server Tools (remote)

web_search web_fetch web_crawl page_screenshot analytics

5. Configuration — Multi-Level Merge

Global~/.shannon/config.yaml
Project.shannon/config.yaml
Local.shannon/config.local.yaml
Agent Overrideagent/config.yaml
Scalars override · Lists merge + dedup · MCP servers: _inherit flag controls overlay vs replace
Agent Settings
max_iterations40 (default) → 75 (GUI)
temperature0.0
max_tokens0 = auto (per-model)
context_window1,000,000 (auto-detect)
thinking_modeadaptive
thinking_budget10,000 tokens
Tool Settings
bash_timeoutconfigurable
bash_max_outputconfigurable
result_truncation30K chars (default)
grep_max_resultsconfigurable
Daemon Settings
auto_approvefalse (interactive)
chrome_profileoptional explicit Chrome profile
Port, concurrency, and heartbeat are hardcoded runtime defaults, not daemon config fields

6. Agent Definitions & Storage

Agent File Structure
AGENT.mdSystem prompt (persona + rules)
MEMORY.mdPersistent memory (runtime dir)
config.yamlModel, iterations, MCP, tools filter
commands/*.mdSlash commands (max 8K chars)
_attached.yamlAttached-skill manifest (primary model)
installed skillsresolved from global skills; bundled skills may auto-inject
Legacy agent-local skill directories are cleaned up; the current model is attachment + global install
Location: ~/.shannon/agents/<name>/
Session & Storage
sessions/*.jsonFull message history
sessions/sessions.dbSQLite FTS5 search index
schedules.jsonCron schedules (flock + atomic)
logs/audit.logJSON-lines tool audit trail (RedactSecrets)
tmp/tool_result_*Disk spill files (auto-cleanup)
Base: ~/.shannon/ · Per-agent: ~/.shannon/agents/<name>/sessions/

7. Daemon HTTP API — Surface Map

Current Surface (108 Routes)
internal/daemon/server.go · localhost only · grouped below (108 unique paths; auth routes have darwin/non-darwin stub pairs)

Core

POST/messageRun agent (JSON or SSE)
POST/approvalSubmit tool approval
POST/cancelCancel in-flight run
GET/eventsSSE event stream (EventBus replay via SubscribeWithReplay)
GET/approvalsList pending approvals
GET·POST·DELETE/queueInspect / enqueue / drop queued msgs
POST/inject/retractRetract a mid-run injected message

Agents

GET/agentsList all agents
GET/agents/{name}Get agent details
POST/agentsCreate agent
PUT/agents/{name}Update agent
DELETE/agents/{name}Delete agent
PUT/agents/{name}/configUpdate config
DELETE/agents/{name}/configDelete config override
PUT/agents/{name}/commands/{cmd}Set command
DELETE/agents/{name}/commands/{cmd}Delete command
PUT/agents/{name}/skills/{skill}Attach skill
DELETE/agents/{name}/skills/{skill}Detach skill
POST·DELETE/agents/{name}/permissions/always-allowPer-agent always-allow list

Skills & Marketplace

GET/skills/downloadableList installable bundled skills
GET/skillsList global skills
GET/skills/{name}Get skill details
PUT/skills/{name}Create/update skill
DELETE/skills/{name}Delete skill
GET/skills/marketplaceBrowse marketplace
GET/skills/marketplace/entry/{slug}Marketplace entry detail
POST/skills/install/{name}Install skill
POST/skills/marketplace/install/{slug}Install from marketplace
GET/skills/{name}/usageList agents attaching a skill
GET/skills/{name}/scriptsList skill scripts
PUT/skills/{name}/scripts/{filename}Write skill script
DELETE/skills/{name}/scripts/{filename}Delete skill script
GET/skills/{name}/referencesList skill references
PUT/skills/{name}/references/{filename}Write skill reference
DELETE/skills/{name}/references/{filename}Delete skill reference
GET/skills/{name}/assetsList skill assets
PUT/skills/{name}/assets/{filename}Write skill asset
DELETE/skills/{name}/assets/{filename}Delete skill asset
PUT·DELETE/skills/{name}/secretsSet / clear skill secrets (Keychain)
DELETE/skills/{name}/secrets/{key}Delete one secret key
POST/skills/uploadUpload a packaged skill

Sessions

GET/sessionsList sessions
GET/sessions/{id}Load session
DELETE/sessions/{id}Delete session
PATCH/sessions/{id}Patch session title
POST/sessions/{id}/editTruncate + re-run
POST/sessions/{id}/resetClear history, keep session
POST/sessions/{id}/rewindRewind to an earlier turn
GET/sessions/{id}/summaryAuto summary
GET/sessions/searchFTS5 keyword search

Config & Health

GET/configGet config (redacted)
GET/config/statusEffective config status
PATCH/configMerge-patch config
POST/config/reloadReload + restart MCPs
GET/instructionsGet global instructions
PUT/instructionsReplace global instructions
GET/healthHealth + version
GET/statusUptime · Connection · memory.reason
GET/notificationsNotification history (JSONL, capped 500)
POST/migrate/claude-code/previewPreview Claude Code import
POST/migrate/claude-code/applyApply Claude Code import
POST/project/initScaffold .shannon project

Schedules

GET/schedulesList schedules
GET/schedules/{id}Get schedule
POST/schedulesCreate schedule
PATCH/schedules/{id}Update schedule
DELETE/schedules/{id}Delete schedule
GET/schedules/{id}/last-runLast run status + log

Permissions & Chrome

GET/permissionsmacOS TCC status
POST/permissions/requestRequest desktop permissions
POST·DELETE/permissions/always-allowGlobal always-allow list
GET/chrome/statusPlaywright CDP status
GET/chrome/profileGet Chrome profile mode
POST/chrome/profileSet Chrome profile mode
POST/chrome/profile/refreshRefresh cloned profile
POST/chrome/showShow managed Chrome window
POST/chrome/hideHide managed Chrome window

Auth (macOS — email/password → Cloud /api/v1/auth/*; AuthManager gates WS)

GET/local/auth/stateAuth state machine status
POST/local/auth/registerRegister account
POST/local/auth/loginSign in (api_key → Keychain)
POST/local/auth/sign-outSign out (keep account)
POST/local/auth/sign-out-fullSign out + purge local state
POST/local/auth/forgot-passwordTrigger reset email
POST/local/auth/resend-verificationResend verify email
POST/local/auth/adopt-keyAdopt an existing api_key

Rules

GET/rulesList rule files
GET/rules/{name}Get rule
PUT/rules/{name}Write rule
DELETE/rules/{name}Delete rule

Session Share & Uploads

POST/sessions/{id}/shareRender + upload share page
DELETE/sessions/{id}/shareRetract a share
GET/sessions/{id}/sharesList shares for a session
GET/sessions/{id}/share/tasks/{task_id}Async share task status
GET/uploadsList published uploads
DELETE/uploads/{id}Retract an upload

Prompt Suggestion

GET/sessions/{id}/suggestionForked next-prompt suggestion
POST/sessions/{id}/suggestion/acceptAccept a suggestion
GET/agents/{name}/sessions/{id}/suggestionPer-agent variant
POST/agents/{name}/sessions/{id}/suggestion/acceptPer-agent accept

Channels

GET/channels/feishu/app-installsList Feishu app installs
POST/channels/feishu/app-installsRegister app install
DELETE/channels/feishu/app-installs/{id}Remove app install

System

POST/shutdownGraceful shutdown

8. MCP Client/Server & Playwright CDP

MCP Client Manager
DiscoveryConnectAll() → ListTools → cache
ResilienceAuto-reconnect on transport error
Idle timeoutDisconnect after inactivity (non-keepAlive)
SupervisorHealth monitoring + on-demand restart
PlaywrightDisables legacy browser tools when connected
MCP Server Mode
Methodsinitialize · tools/list · tools/call
Permissions6-layer checks enforced
ApprovalAsk → deny (no TTY in MCP)
HooksPre/post tool-use hooks fire
AuditAll calls logged
Playwright CDP Integration — Special-Cased MCP
internal/mcp/chrome.go — Kocoro auto-manages a dedicated Chrome instance for Playwright MCP
DEEP DIVE
What happens at daemon startup
1. Read configplaywright MCP has --cdp-endpoint :9223
2. EnsureChromeDebugPortCheck if 9223 is reachable
3. If not → LaunchCDPChromeStart a dedicated Chrome instance
4. Clone profileCopy ~/Library/.../Chrome → ~/.shannon/chrome-cdp/
5. Launch minimized--remote-debugging-port=9223 + minimized window
6. Playwright connectsvia CDP instead of launching its own Chrome
Why clone the profile?
Your daily Chrome has all your logins (Gmail, Twitter, Notion, etc.). Playwright needs those logins to be useful, but:

Need to inherit cookies → copy profile
Must not interfere with your real Chrome → copy to isolated dir
Must not steal focus → launch minimized

The cloned profile gets logged-in state at the moment of cloning. It won't stay synced with your daily Chrome — but for agent use cases, that's fine.
Legacy tools get disabled
When Playwright MCP connects, Kocoro removes these from the registry:

browser applescript accessibility screenshot wait_for

Why? Having both sets of tools causes LLMs to ping-pong between them — try Playwright, hit an error, fall back to applescript, fail, try screenshot, etc. This triggers the "tool mode switch" loop detector and wastes tokens. Force-disabling the legacy tools when Playwright is available eliminates the decision.
Real-world browser task cost
Tested: "check my Gmail inbox" with Playwright + CDP + cloned profile (already logged in)

Turn 1 (clarifying question): 8,123 tokens, $0.11, 12s
Turn 2 (actual inbox read): 19,269 tokens, $0.19, 25s
Total: 27,392 tokens, $0.29, 37s → gets ~15 subject lines from one screenshot

Each follow-up action (read body, reply, label) would cost another full cycle of navigate + snapshot + screenshot + file_read. This is why first-class email MCP is a much better fit.

9. Extensibility

Hooks
PreToolUseBlock tool (exit 2 = deny)
PostToolUseFire-and-forget logging
SessionStartSession initialization
StopCleanup on close
Timeout: 10s · Max output: 10KB · Recursion guard
Instructions (priority merge)
1. Global~/.shannon/instructions.md
2. Global rules~/.shannon/rules/*.md
3. Project.shannon/instructions.md
4. Project rules.shannon/rules/*.md
5. Local.shannon/instructions.local.md
Dedup by line · Higher priority wins · Max 16K chars
Skills (SKILL.md)
FormatYAML frontmatter + markdown body
Global~/.shannon/skills/<name>/
Attachedvia _attached.yaml manifest
BundledEmbedded in binary
MarketplaceInstall via API
Legacy agent-local skill directories are deprecated/cleaned up · invoked via use_skill tool

10. Subsystems Shipped Since — present in current code, absent from §1–§9 above

The topology in Section 1 and the deep-dive in Sections 2–9 were drawn around the original daemon + agent-loop core. The runtime has since grown several first-class subsystems. These are the open-source consumer surfaces — where a feature's heavy lifting lives in Cloud or a private engine, only Kocoro's local side is described here.

🧠 Memory Sidecar
Transportmemory_recall → Service.Query over UDS (memory.socket_path, default $TMPDIR/com.kocoro.tlm.sock)
LifecycleDaemon owns sidecar + 24h bundle pull
Fallbacksession_search + MEMORY.md when not Ready
PreflightEpisodic <private_memory> — never persisted
api_key never on disk — only sha256[:16] tenant fingerprint. Extraction/training live in a private engine; only the client is OSS.
🔐 Email/Password Auth (macOS)
Surface/local/auth/* → Cloud /api/v1/auth/*
State machineAuthManager drives WS — runs only in signed_in
Credentialapi_key in Keychain ai.kocoro.daemon.api_key/<user_id>
Non-darwinAuthManager nil · endpoints 503 · legacy cfg.APIKey
📅 Desktop RPC + Calendar
ChannelUnix-sock reverse-RPC to Kocoro Desktop's EventKit
FramingLength-prefixed JSON (4-byte BE, ≤4 MiB)
Tools (8)calendar_* — registered only when broker exists
Specdocs/desktop-calendar-rpc.md v0.5.1 · proto 1.0.0
TUI/one-shot/MCP/scheduled paths fall back to applescript + Calendar.app.
☁️ Session Sync
TriggerDaily upload, opt-in sync.enabled
Safetyflock + atomic marker · thinking stripped pre-upload
Self-healsize_limit/load_error reasons clear on edit
🔗 Session Share & Uploads
ShareRender HTML → /api/v1/uploads (kind=session_share)
Social metaOG / Twitter Card / JSON-LD in <head>
Artifactshtml-artifact fences → sandboxed iframe (assistant-only)
Toolspublish_to_web · list_my_published_files · retract_published_file
🎨 Image Generation
Toolsgenerate_image · edit_image (always approval)
API/api/v1/images/{generations,edits}
Edit inputimage_urls 1-4 · must be static.kocoro.ai CDN
Gated on cloud.enabled + api_key.
💡 Prompt Suggestion
WhenForked LLM call after each main turn
Cache safetyByte-equal to main + 2 msgs · SkipCacheWrite
EndpointsGET /suggestion · POST /accept
🧩 Skill Discovery + MCP Catalog
3 layersListing · semantic (small-tier iter 0) · catalog
Channel filterdesktopOnlySkills hidden on cloud sources
Built-in MCPPre-bundled servers (e.g. intercom), disabled by default
Async startupRegistry builds first · per-server connect goroutines
⏱️ Autonomous Local Sources
HeartbeatPer-agent HEARTBEAT.md + alerts
WatcherPer-agent debounced FS watcher
Cloudflow/research · /swarm gateway workflows
Idle watchdogsoft 90s / hard 540s · stream-gap 90s
Kocoro current codebase snapshot · Go 1.25.7 · Single binary · Open source
github.com/Kocoro-lab/Kocoro