The entry point for Kiro Crew's architecture: what the gateway is, how the pieces
connect, and where to read further. This is a map, not a manual. Every
subsystem has a module spec under
../system-specs/modules/; the
Feature and subsystem map below indexes all of
them with their owning source path.
For installation and the first run, see
../guides/install.md.
Three layers sit beneath Kiro Crew, and the distinction matters:
- An ACP harness is an agent runtime, not an agent.
kiro-cliis the default and baseline harness; it owns the LLM connection, tool execution (bash, file read/write, grep, glob), MCP server management, session persistence, context compaction, and ACP (the Agent Client Protocol): a JSON-RPC 2.0 stdio interface any orchestrator can drive. Other selectable harnesses implement that same transport with capability-gated differences. - Agent configs (JSON under
~/.kiro/agents/, or a project's own<project>/.kiro/agents/) tell the default harness how to behave: system prompt, enabled tools, MCP servers. With the default backend Kiro Crew runskiro-cli acp --agent <name>and generates its ownkirocrew.jsonthere (agent.py); other harnesses receive the equivalent projection through the ACP provider seam. - Kiro Crew is the gateway: a single asyncio process that multiplexes surfaces onto the selected harness and adds everything a runtime deliberately has no opinion about.
Kiro Crew is ACP-provider-only: agent.provider is fixed to acp.
agent.acp_backend selects the harness, with kiro-cli as the required baseline
and default. The authoritative backend inventory and capability matrix are in
../system-specs/modules/providers.md.
| Capability | kiro-cli alone | With Kiro Crew |
|---|---|---|
| Sessions | One per terminal | Many concurrent (channel threads, dashboard slots, cron jobs, subagents, task steps) |
| Surfaces | Terminal only | CLI, web dashboard, Electron desktop, and the messaging channels under src/kiro_crew/docs/ (Slack, Discord, Telegram, Teams, WeChat and more) |
| Persistence | Per-directory transcript | Cross-session memory (preferences, projects, daily history, lessons) |
| Cross-session awareness | None | Sessions bound to the same Global V1 or private member V2 store share its learning; separate member stores do not cross |
| Scheduling | None | Cron jobs (every / at / cron expression) with cross-process file locking |
| Autonomous tasks | None | TaskRunner: spec, decompose, execute, retry, replan, checkpoint |
| Self-learning | None | Lessons extracted from corrections, injected into later sessions |
| Tool gating | Per-agent config | An independent PreToolUse gate plus a two-level governance ceiling the agent cannot weaken |
| Context management | Manual compaction | Auto-compaction at a configurable threshold, budget-aware context assembly, decaying memory |
| Process resilience | Manual restart | Warm pool, circuit breaker, crash recovery, idle cleanup, orphan PID tracking |
- Unattended work. Cron jobs, TaskRunner specs, and subagents run with no human typing commands.
- Specialization. Different surfaces and jobs can each run a different agent config concurrently.
- Accumulation. Conversations feed the memory store bound to their session and lessons persist, so a later session on that store starts with what an earlier one learned.
kirocrew is itself just an agent config, at the same level as any other. What
makes it the coordinator is that the gateway defaults to it and it holds the MCP
tools (spawn_run, cron_add, task_run) that ask the gateway to open sessions
with other agents. The gateway code is agent-agnostic; coordination behavior
lives in the agent config.
KiroCrew Gateway
├── CLI chat / channel DM → agent.default_agent (falls back to kirocrew)
├── Dashboard slot → the slot's chosen agent (falls back to kirocrew)
├── Cron job → per-job agent_id, or agent_sequence
├── Subagent (spawn_run) → the spawn's `agent`, else the parent session's
└── TaskRunner (task_run) → the agent named when the plan starts
graph TB
subgraph "User Surfaces"
CLI[CLI<br/><code>kirocrew chat</code>]
CHAN[Messaging channels<br/>Slack, Discord, Telegram, …]
Dashboard[Web Dashboard<br/>React SPA]
Desktop[Desktop App<br/>Electron]
end
subgraph "KiroCrew Gateway"
GW[Single asyncio process<br/><i>Python / aiohttp</i>]
end
subgraph "Agent Backend"
KC[ACP harness<br/><i>kiro-cli by default</i>]
LLM[LLM Provider<br/><i>via harness auth</i>]
MCP[MCP Servers<br/><i>tools</i>]
end
CLI --> GW
CHAN --> GW
Dashboard --> GW
Desktop --> Dashboard
GW --> KC
KC --> LLM
KC --> MCP
A user message is hooked, routed to a session, enriched with context, forwarded to the selected harness over ACP, and streamed back.
sequenceDiagram
participant User
participant Surface as Channel / Dashboard / CLI
participant GW as Gateway
participant Hooks as HookManager
participant Session as SessionManager
participant Context as ContextBuilder
participant ACP as ACP harness (kiro-cli default)
participant LLM as LLM
User->>Surface: sends message
Surface->>GW: HTTP / WebSocket / channel transport
GW->>Hooks: auto-reply, transform, inject, deny
Hooks-->>GW: pass / block / auto-reply
GW->>Session: get_or_create(session_key, agent)
Session-->>GW: ACP harness process (warm or cold)
GW->>Context: assemble prompt context
Note over Context: memory + skills + lessons<br/>+ history + cross-tab
Context-->>GW: enriched context
GW->>ACP: JSON-RPC prompt(message + context)
ACP->>LLM: inference request
LLM-->>ACP: stream tokens + tool calls
ACP-->>GW: text_chunk / tool_call / complete events
GW-->>Surface: stream response back
GW->>GW: append to ConversationLog (JSONL)
GW->>GW: trigger memory consolidation (async)
Tool calls do not simply pass through: every one is evaluated at Kiro Crew's own PreToolUse gate before kiro-cli is allowed to run it. See Security layers.
| Path | Purpose |
|---|---|
src/kiro_crew/ |
Python backend: gateway, sessions, memory, cron, MCP servers, built-in apps |
src/kiro_crew/config/defaults.json, prompt.md |
Bundled base agent config and system prompt |
src/kiro_crew/builtin_skills/ |
Skills bundled into the package, copied to the data home on start |
src/kiro_crew/static/dist/ |
Staged frontend bundle served by the backend |
website/ |
React + TypeScript + Tailwind dashboard (Vite) |
website/electron/ |
Electron desktop shell |
skills/ |
Checkout-only reference skills, loaded via KIROCREW_PROJECT_DIR; not packaged |
packages/ |
Standalone SDK packages (kirocrew-client-py) |
packaging/, docker/ |
Desktop bundle + container build inputs |
scripts/ |
Build tooling, linters, dev helpers |
docs/ |
Contributor and architecture docs (this tree) |
test/ |
pytest suite |
A skill that a shipped feature depends on must live in
src/kiro_crew/builtin_skills/; only src/ is packaged, so a required skill
placed in top-level skills/ never reaches an installed user.
graph TB
subgraph "Entry Points"
CLI_MOD[cli.py<br/>argparse CLI]
SLACK_GW[slack/gateway.py<br/>service composition]
DASH_SRV[dashboard/server.py<br/>aiohttp + WebSocket]
end
subgraph "Session Layer"
SESS[session.py<br/>Session pool + warm pool]
ACP_CLIENT[acp/client.py<br/>JSON-RPC 2.0 over stdio]
end
subgraph "Context & Memory"
CTX[context.py<br/>Prompt assembly]
MEM[memory.py<br/>Structured memory + FTS5]
VEC[vector_memory.py<br/>Semantic search]
HIST[history.py<br/>JSONL conversation log]
LEARN[learn.py<br/>Lesson store]
EMBED[embeddings.py<br/>In-process embeddings]
end
subgraph "Orchestration"
CRON[cron.py<br/>Scheduled jobs]
TASK[taskrunner.py<br/>Autonomous tasks]
SUB[subagent.py<br/>Parallel agents]
NUDGE[autonudge.py<br/>Self-nudge loops]
HB[heartbeat.py<br/>Periodic maintenance]
end
subgraph "MCP Servers"
MCP_CORE[mcp_core.py<br/>spawn, learn, task, wait]
MCP_CRON[mcp_cron.py<br/>cron tools]
MCP_COMP[mcp_computer.py<br/>computer-use shim]
MCP_DISC[mcp_discovery.py<br/>Server detection]
MCP_HOT[mcp_hot_reload.py<br/>Live-reconcile gate]
end
subgraph "Security"
HOOKS[hooks.py<br/>PreToolUse gate]
SEC[security/<br/>Deny rules + paths]
PLAT[platform/<br/>Governance + CPP seam]
SEL[sel.py<br/>Security event log]
end
CLI_MOD --> SESS
SLACK_GW --> SESS
DASH_SRV --> SESS
SESS --> ACP_CLIENT
SESS --> CTX
CTX --> MEM
CTX --> VEC
CTX --> HIST
CTX --> LEARN
MEM --> EMBED
VEC --> EMBED
CRON --> SESS
TASK --> SESS
SUB --> SESS
NUDGE --> SESS
HB --> SESS
HOOKS --> SEC
HOOKS --> PLAT
HOOKS --> SEL
ACP_CLIENT --> HOOKS
Each session is an independent ACP connection with its own context and
transcript, keyed by a session_key that encodes its origin.
graph TB
subgraph "Session Sources"
S1[Channel thread]
S2[Dashboard slot]
S3[Cron job]
S4[Subagent]
S5[Task step]
S6[Heartbeat]
end
subgraph "Session Pool"
WARM[Warm Pool<br/><i>pre-started ACP harnesses</i>]
ACTIVE[Active Sessions<br/><i>keyed by session_key</i>]
end
subgraph "ACP Harness Processes"
P1[default: kiro-cli acp --agent kirocrew]
P2[default: kiro-cli acp --agent reviewer]
P3[alternative ACP harness]
end
S1 --> ACTIVE
S2 --> ACTIVE
S3 --> ACTIVE
S4 --> ACTIVE
S5 --> ACTIVE
S6 --> ACTIVE
WARM --> |"claim on demand"| ACTIVE
ACTIVE --> P1
ACTIVE --> P2
ACTIVE --> P3
- Warm pool (
session.pool_size, default0= off) pre-spawns processes so a new session starts without paying kiro-cli's cold start. Pooled processes older thansession.pool_ttl_secs(default 1800s) are discarded at claim time. - Idle timeout reclaims a session after
session.timeout_secs, default 3600s. - Turn ceiling:
agent.chat_turn_timeout_secsdefaults to 14400s (4h), clamped to 300s..86400s (CHAT_TURN_TIMEOUT_MAX, deliberately decoupled from the 14400s default) and never disable-able. It is a runaway backstop, so a turn that hits it ends with a card naming the limit rather than failing silently. The ACP transport's prompt timeout follows the configured ceiling (plus a margin) so the dashboard's card always fires first. - Tool-approval window:
agent.tool_approval_timeout_secsdefaults to 600s (10 min). It must expire inside the turn that opened it — otherwise an unanswered prompt is reported as a turn timeout and the real cause is lost — so it is bounded twice: to 60s below the turn ceiling at config load, and at arm time to the budget actually remaining in the running turn. A prompt arming with less than that margin left is declined immediately rather than waiting on a deadline the ceiling would beat. On expiry the tool is declined and a card says the approval went unanswered and to send the message again. - Circuit breaker: five consecutive failures on one session force a reset.
- Auto-compaction at
session.autocompact_pctof the context window (default 70%).
The pattern differs by who owns the conversation, and the difference is the point: a surface a human is watching keeps its session, a background job must not.
| Caller | Pattern |
|---|---|
| Channel handler (Slack and siblings) | Long-lived per thread; release() in finally; reclaimed by idle expiry |
| Dashboard slot | Long-lived per slot; release() in finally; closed explicitly by the user |
| Cron job | Per-job key cron:{id}; with persistent_session: false a fresh cron:{id}:{uuid} key per run so no context accumulates; the reaper reset()s a job that overruns |
| Heartbeat | One shared HEARTBEAT_KEY across a cycle's concurrent tasks, recycled once at cycle end (a per-task reset would tear the session out from under a sibling still running) |
| Subagent | Per-agent key subagent:{id}; release(cleanup=False) then reset(), so session files survive for spawn_continue and the tombstone pruner owns deletion |
kiro_crew.shutdown_event (an asyncio.Event) is the process-wide signal; every
background loop waits on it so Ctrl-C wakes them immediately instead of at the
next poll. _shutdown() in slack/gateway.py then tears down in a deliberate
order, and the order is load-bearing:
- Loop-stall watchdog off first. Killing every kiro-cli child produces a
waitpidreaping burst that can wedge the event loop past the watchdog's threshold; an armed watchdog would turn a clean quit into a crash exit. - Save active dashboard slots to history (off-loop, with a deadline, because the per-session lock and disk I/O must not stall shutdown), then stop file indexes.
- Cancel in-flight handler tasks.
- Stop the cron service, then the heartbeat service.
- Stop the pooled MCP gateway broker and its backends (spawned in their own session, so they would otherwise outlive the gateway).
- Concurrently: cancel subagents, close all sessions, close WebSocket connections and then the dashboard runner, close each channel client, and cancel background tasks (model download, memory-store auto-migration, update check).
Memory is what lets a new session benefit from past conversations without
replaying them. The diagram below is the Global V1 path. A member-bound V2
session instead uses one managed SQLite store under
~/.kiro/crew/memory_stores/<store>/; unavailable V2 memory fails explicitly
rather than falling back to Global V1.
graph LR
subgraph "Conversation"
MSG[User messages]
RESP[Agent responses]
end
subgraph "Immediate Storage"
JSONL[ConversationLog<br/>JSONL per session]
end
subgraph "Consolidation (async)"
CONSOL[LLM Consolidator<br/><i>prefs/projects @ 30 msgs<br/>daily history @ 3h idle</i>]
end
subgraph "Structured Memory"
PREFS[preferences.md<br/><i>user preferences</i>]
PROJ[projects.md<br/><i>active project context</i>]
DAILY[history/YYYY-MM-DD.md<br/><i>daily summaries</i>]
LESSONS[lessons.jsonl<br/><i>learned corrections</i>]
end
subgraph "Retrieval"
FTS[FTS5 full-text]
VSIM[Vector similarity<br/>in-process embeddings]
end
MSG --> JSONL
RESP --> JSONL
JSONL --> CONSOL
CONSOL --> PREFS
CONSOL --> PROJ
CONSOL --> DAILY
MSG --> LESSONS
PREFS --> FTS
PROJ --> FTS
DAILY --> FTS
PREFS --> VSIM
DAILY --> VSIM
Global V1 history decay (memory.read_recent_history, default window 14 days): a day
newer than the requested window is injected in full; days from the window
boundary through day 60 are summarized (header plus the first entry, with a count
of the rest); days 61 through 180 collapse to a one-line marker naming the date
and its conversation count. Nothing older than 180 days is read at all, and the
heartbeat prunes files older than memory.history_max_days (default 365) off
disk.
Embeddings are always-on and in-process, computed by vendored
llama-cpp-python under src/kiro_crew/_vendor/. memory.embedding_provider
accepts only llama_cpp; there is no external embedding daemon to install or
configure, and legacy config values are migrated to llama_cpp on load. The
EmbeddingBackend ABC is the swap seam for other runtimes.
graph TB
subgraph "Inbound"
USER_MSG[User Message]
end
subgraph "Gateway Security Layers"
OWNER[Owner Lock<br/><i>channel sender allowlist</i>]
GOV["Governance<br/><i>POLICY ∩ PROFILE</i>"]
HOOKS_SEC[PreToolUse Gate<br/><i>deny rules + governance</i>]
SANDBOX[OS Sandbox<br/><i>namespaces / seatbelt</i>]
REDACT[Output Redaction<br/><i>credentials scrubbed</i>]
AUDIT[SEL<br/><i>hash-chained audit</i>]
end
subgraph "kiro-cli"
TOOLS[Tool Execution]
end
USER_MSG --> OWNER
OWNER --> GOV
GOV --> HOOKS_SEC
HOOKS_SEC --> SANDBOX
SANDBOX --> TOOLS
TOOLS --> REDACT
HOOKS_SEC --> AUDIT
Outer to inner:
- Owner lock. Messaging gateways reject senders who are not the configured owner.
- Governance. Two levels,
effective = POLICY ∩ PROFILE, tightest wins. POLICY is the enterprise ceiling loaded at boot from a trust-root path (security_policy.json); PROFILE is a per-surface narrow-only scope. The policy, profile, and admission files sit insecurity._SENSITIVE_HOME_DIRS, so the agent can neither read nor write its own ceiling: that is the single mechanism making the ceiling un-disableable. - PreToolUse gate (
hooks.py). The one placeDeniedCommandRulerecords fromsecurity.BUILTIN_DENIED_RULESare enforced. They are not injected into kiro-cli's agent JSON asdeniedCommands: a config-injection model is only as strong as the config, and the agent can write agent JSON. Rules are default-ON and user-configurable from Settings → Security; the governancecommandsscope is the force-pin a user cannot opt out of. Sensitive-path blocking (~/.aws,~/.ssh, the trust-root files) runs here too — for the file tools' resolved paths. A shell command's text is deliberately not path-matched; what a spawned shell canopen()is decided by the OS sandbox tier below. - OS sandbox (
sandbox.py).agent.sandboxdefaults toauto, engaging OS-level isolation (user namespaces on Linux,sandbox-exec/Seatbelt on macOS) at the standard tier, which masks~/.gnupg,~/.docker,~/.azure,~/.config/gcloudand the crew vault but deliberately leaves~/.aws,~/.sshand~/.kubevisible so theawsCLI,credential_process, git-over-SSH andkubectlwork inside the agent. Setstrictto also mask those (at the cost of those tools); setoffto skip Kiro Crew's sandbox. On macOS, when kiro-cli's own internal sandbox is enabled, Kiro Crew delegates to it instead of applying either tier (the two are mutually exclusive because nested Seatbelt profiles fail with EPERM). - Output redaction. Credential shapes (AWS access key IDs, presigned-URL credential parameters, and more) are scrubbed before text reaches a user or an egress tool.
- SEL (
sel.py). Append-only, HMAC-SHA256 hash-chained JSONL at<data home>/security_events.jsonl.
Computer use is deliberately not governed by scopes. It is one operator
opt-in on the keystone computer_use.json, and its refusals run in band on the
tool dispatch path rather than at the fail-open PreToolUse gate. See
../system-specs/modules/computer-use.md.
Depth: security-deep-dive.md,
../system-specs/modules/security.md,
../system-specs/modules/governance.md.
src/kiro_crew/platform/ is the Composed Platform Providers seam. The core
defines extension-point Protocols (interfaces.py) and ships a Default*
adapter for each; PlatformContext (context.py) is the frozen bundle read via
current_context(). The core never imports a companion edition and never
branches on which edition is running. Same package, same boot:
security_authority.py holds the ADD-only deny floor and governance.py holds
the ceiling evaluator, which dispatches by control archetype and is
scope-name-agnostic (adding a scope is a SCOPE_CATALOG data change).
Spec: ../system-specs/modules/platform-context.md.
Tenet 8 in ../../TENETS.md says everything is an app. This
section is where that becomes a line you can point at in review.
The core is the trust boundary plus the state every app shares. Five things live below the line, and they are there for one reason each:
| In the core | Why it cannot be an app |
|---|---|
| Sessions and transcripts | Every surface reads the same conversation. Two implementations means two histories. |
| Memory and lessons | Same argument, across sessions instead of across surfaces. |
| Approvals and the PreToolUse gate | Its value is being unavoidable. An app-supplied gate is a gate with an off switch. |
| The governance ceiling | effective = POLICY ∩ PROFILE, tightest-wins, and the keystone files the agent cannot write. A replaceable ceiling is not a ceiling. |
| The event bus and identity | The thing apps agree through. It cannot itself be one of the parties. |
Everything above that line renders or interprets, and is an app: pages, overview and summary surfaces, review and triage workflows, editors, panels. When a surface up there cannot be built as an app, the missing seam is the bug to file.
That boundary is enforced against the agent, not against app code. Every
control in the table gates the agent's tool-call surface. An app's Python runs in
the gateway process: apps.module_loader._warn_third_party_execution states that the
permission system "does NOT restrict import, filesystem, network, or access to
in-memory credentials. Installing an app is therefore equivalent to granting it
full gateway-process privileges." So the table says what no app may be asked to
supply, and the mechanism that would stop one supplying it anyway does not exist
yet — the keystone path list is a mutable module-level list
(security._SENSITIVE_HOME_DIRS), and app admission admits when no policy file
is present (apps.admission). Read the table as the
intended boundary and
../request-for-change/rfc-app-sandbox-isolation.md
as the work that makes it real.
Replacement is whole-surface, not per-widget. An app takes over a named slot
and owns what appears there. Several apps each contributing a card into one page
needs layout negotiation between parties who cannot see each other, and produces
a surface nobody owns. This is what makes the per-job-family overview tractable:
a team swaps the whole overview, rather than five apps bidding for space inside
one. The card-composition shape already exists as edition seam 7,
registerOverviewStatCards, and it has no registrants in the stock build.
The shipped set is a starting opinion. Built-in apps are curated defaults, so users and field engineers pick which surfaces are central to their work. Because users take defaults, arguing about the default set is a product argument with a small blast radius, which is the point of moving it out of the architecture.
An app has to be able to do what a built-in page does, or "make it an app" becomes a way to decline a feature while appearing to accept it. Three gaps are open against that standard today:
- Apps add, and cannot intervene.
backend.hooks(routes,on_startup,on_shutdown) andsetup.onEnable/onDisableare the in-gateway entry points, and none of them lets an app take a position in a flow the core owns.HookManageris built only fromconfig.json'shookssection (hooks.HookManager.__init__) and exposes no registration path. - The platform states no version for its own app-facing surface.
minKiroCrewVersionis a floor an app declares about the gateway, checked at install and update only (apps.manager._check_min_version), so changing or withdrawing a seam carries no compatibility promise in the other direction. - Manifest fields that nothing reads.
ui.sidebar.sectionandui.sidebar.orderare documented and parsed, and the dashboard does not place apps by them, so navigation position is not yet app-controlled. Ten more fields are in the same state,jobFamiliesamong them.
Decomposing a core surface into an app is how the list above gets shorter, and the list is the evidence for which seam to build next.
Rationale, the full dead-field inventory, and the phased plan:
../request-for-change/rfc-everything-is-an-app.md.
Contracts: ../system-specs/modules/app-kit-platform.md,
../app-kit/manifest-reference.md.
graph TB
subgraph "Dashboard (React SPA)"
VITE[Vite build]
REDUX[Redux Toolkit<br/>state management]
RQ[React Query<br/>server cache]
ROUTER[React Router<br/>page routing]
WS_CLIENT[WebSocket client<br/>multiplexed live updates]
end
subgraph "Gateway API"
REST[REST API<br/>/api/*]
WS_SRV[WebSocket<br/>/api/ws]
STT_SRV[WebSocket<br/>/api/ws/stt]
STATIC[Static files<br/>/dist/*]
end
VITE --> STATIC
WS_CLIENT --> WS_SRV
REDUX --> REST
RQ --> REST
- Build: Vite, React 18, TypeScript, Tailwind CSS.
- Live updates: a single multiplexed WebSocket at
/api/wscarries chat chunks, slot state, subagent events, and notifications./api/chatstill serves atext/event-streamresponse for a caller that does not passws=1, so the SSE path remains the non-WebSocket fallback. - Bundling: the production build is staged into
src/kiro_crew/static/dist/and served by the Python backend, so apipinstall ships the dashboard. - Desktop: Electron wraps the same SPA with multi-tab
WebContentsView.
Frontend conventions (icons, components, i18n, data fetching) live in
website/AGENTS.md.
graph LR
subgraph "KiroCrew (local)"
GW2[Gateway]
end
subgraph "Required"
KIRO[kiro-cli<br/><i>baseline/default ACP harness</i>]
LLM2[LLM Provider<br/><i>via selected harness auth</i>]
end
subgraph "Optional"
ACP_ALT[Alternative ACP harness]
CHAN_API[Messaging APIs<br/><i>Slack, Discord, …</i>]
MCP_EXT[External MCP Servers<br/><i>user-configured</i>]
AWS[AWS<br/><i>cloud launcher, artifact deploy, cloud STT</i>]
end
GW2 --> KIRO
GW2 -.-> ACP_ALT
KIRO --> LLM2
ACP_ALT -.-> LLM2
GW2 -.-> CHAN_API
KIRO -.-> MCP_EXT
ACP_ALT -.-> MCP_EXT
GW2 -.-> AWS
| Dependency | Required | Purpose |
|---|---|---|
| kiro-cli | Yes | Baseline and default ACP harness; also serves the KAS backend |
| LLM provider | Yes | Reached through the selected ACP harness's authenticated connection |
| Alternative ACP harness | No | Optional runtime selected by agent.acp_backend; see the provider spec |
| Messaging APIs | No | Slack, Discord, Telegram, Webex, WeCom, Teams, Weixin gateways (the dashboard works without any) |
| AWS | No | Cloud launcher, artifact deploy, optional cloud STT |
| External MCP servers | No | Additional tools, user-configured |
Embeddings are not in this table on purpose: the runtime is vendored, so there is no optional embedding service to stand up.
Persistent state lives under ~/.kiro/crew/ (override with KIROCREW_HOME).
The root nests under kiro-cli's own ~/.kiro/ so every Kiro-family app shares
one directory a user can secure. A legacy ~/.kirocrew is fully deprecated and
does not auto-migrate; it survives only in sensitive-path deny lists. Selected
entries:
~/.kiro/crew/
├── config.json # user configuration (+ config.local.json overlay)
├── .env # channel tokens, owner id
├── security_policy.json # governance POLICY ceiling (trust root)
├── profiles/ # governance PROFILE scopes (trust root)
├── computer_use.json # computer-use keystone enable (trust root)
├── workspace/
│ ├── memory/ # preferences.md, projects.md, history/
│ ├── knowledge/ # knowledge.db (FTS5 + graph + vectors)
│ └── HEARTBEAT.md # heartbeat task list
├── members/ # stable crew-member identities and briefs
├── memory_stores/ # member/named stores (SQLite memory + lessons)
├── sessions/ # JSONL conversation logs (+ archive/)
├── lessons.jsonl # Global V1 learned corrections
├── crons.json # scheduled jobs
├── crons/ # cron script bodies
├── hooks.json # webhook workflow context
├── instances.json # remote instance registry
├── security_events.jsonl # SEL audit chain
├── artifacts/ # saved artifacts + version history
├── apps/ # installed apps
├── skills/ # skills copied from the bundle, plus user skills
├── snapshots/ # portable state snapshots
└── gateway.log # gateway log
Generated kiro-cli agent JSON does not live here: it is written to
~/.kiro/agents/ (kiro_home()/agents), because that is where kiro-cli reads
agent specs. That directory stays the only write target; a project's own
<project>/.kiro/agents/ is additionally read for sessions bound to a project
(kiro-cli searches it first, since Kiro Crew runs kiro-cli in that directory).
One row per module spec, with the source it describes. Follow the spec link for detail; this table is only an index.
| Subsystem | Owning source | Spec |
|---|---|---|
| ACP client (JSON-RPC transport to kiro-cli) | src/kiro_crew/acp/ |
acp-client.md |
| App Kit platform contracts | src/kiro_crew/apps/ |
app-kit-platform.md |
| Artifacts (persisted generated UI) | src/kiro_crew/artifacts.py |
artifacts.md |
| Browser automation auth layer | src/kiro_crew/browser/ |
browser.md |
| Channel history buffer | src/kiro_crew/channel_history.py |
channel-history.md |
| CLI surface | src/kiro_crew/cli.py |
cli.md |
| Cloud launcher (own EC2 instance) | src/kiro_crew/cloud/ |
cloud.md |
| Computer use (desktop GUI automation) | src/kiro_crew/computer_use/ |
computer-use.md |
| Configuration (dataclasses, loader, schema) | src/kiro_crew/config/ |
config.md |
| Dev Fleet app | src/kiro_crew/apps/builtins/dev_fleet/ |
dev-fleet.md |
| Governance model (POLICY ∩ PROFILE) | src/kiro_crew/platform/governance.py |
governance.md |
| Heartbeat (periodic background tasks) | src/kiro_crew/heartbeat.py |
heartbeat.md |
| Conversation history (JSONL + consolidation) | src/kiro_crew/history.py |
history.md |
| Instances (multi-instance over SSH) | src/kiro_crew/instances/ |
instances.md |
| Issue Radar app | src/kiro_crew/apps/builtins/issue_radar/ |
issue-radar.md |
| Knowledge library (ingest + hybrid retrieval) | src/kiro_crew/knowledge/ |
knowledge.md |
| Self-learning, cron, and dashboard API | src/kiro_crew/learn.py, cron.py, dashboard/ |
learn-cron-dashboard.md |
MCP Apps (interactive ui:// rendering) |
src/kiro_crew/mcp_gateway/ |
mcp-apps.md |
| Markdown Notebook app | src/kiro_crew/apps/builtins/md_notebook/ |
md-notebook.md |
| Meetings app | src/kiro_crew/apps/builtins/meetings/ |
meetings.md |
| Memory, skills, and hooks | src/kiro_crew/memory.py, skills.py, hooks.py |
memory-skills-hooks.md |
| Messaging transport abstraction | src/kiro_crew/messaging/ |
messaging.md |
| Metrics telemetry (default off) | src/kiro_crew/metrics/ |
metrics.md |
| Mochi app (desktop pet) | src/kiro_crew/apps/builtins/mochi/ |
mochi.md |
| Foreign-agent onboarding import | src/kiro_crew/onboarding_import.py |
onboarding-import.md |
| Papyrus app (LaTeX authoring) | src/kiro_crew/apps/builtins/papyrus/ |
papyrus.md |
| Persistent agent channels | src/kiro_crew/channel.py |
persistent-agent-channels.md |
| Platform context (CPP seam) | src/kiro_crew/platform/ |
platform-context.md |
| PPTX Maker app | src/kiro_crew/apps/builtins/pptx_maker/ |
pptx-maker.md |
| Providers (LLMProvider ABC + ACP provider) | src/kiro_crew/providers/ |
providers.md |
| Security controls (deny rules, paths, auth) | src/kiro_crew/security/ |
security.md |
| Security Event Log | src/kiro_crew/sel.py |
sel.md |
| Session manager (pool, expiry, compaction) | src/kiro_crew/session.py |
session.md |
| Side conversations | src/kiro_crew/dashboard/side_state.py |
side.md |
| Slack gateway and handler | src/kiro_crew/slack/ |
slack-gateway.md |
| Subagents (parallel background agents) | src/kiro_crew/subagent.py |
subagent.md |
| Task state machine | src/kiro_crew/task.py |
task.md |
| TaskRunner (spec to plan to execution) | src/kiro_crew/taskrunner.py |
taskrunner.md |
| Themes | src/kiro_crew/dashboard/handlers/themes.py |
themes.md |
| Third-party account connections | src/kiro_crew/connections/ |
connections.md |
This table indexes the principal subsystems, not every spec.
../system-specs/modules/README.md is the
complete spec index — one index, so a spec cannot be reachable from one and missing
from the other — and cross-cutting patterns are in
../system-specs/common/.
graph TB
subgraph "User"
U[You]
end
subgraph "Surfaces"
DASH[Dashboard :5476]
SL[Messaging channel]
TERM[CLI]
end
subgraph "Gateway (Python)"
ENTRY[Entry Points]
SESSION[Session Pool]
MEMORY[Memory + Context]
ORCH[Orchestration<br/><i>cron, tasks, subagents, heartbeat</i>]
SECURITY[Security Layers]
end
subgraph "Agent (kiro-cli)"
AGENT[Agent Process]
TOOL_EXEC[Tool Execution]
MCP_TOOLS[MCP Tools]
end
subgraph "Persistence"
DISK["~/.kiro/crew/<br/><i>config, memory, logs</i>"]
end
subgraph "Remote"
CLOUD_LLM[LLM Provider]
end
U --> DASH
U --> SL
U --> TERM
DASH --> ENTRY
SL --> ENTRY
TERM --> ENTRY
ENTRY --> SESSION
SESSION --> MEMORY
SESSION --> SECURITY
SECURITY --> AGENT
AGENT --> TOOL_EXEC
TOOL_EXEC --> MCP_TOOLS
AGENT --> CLOUD_LLM
MEMORY --> DISK
ORCH --> SESSION
SESSION --> DISK
The dashboard port default is 5476, overridable with KIROCREW_PORT.
../system-specs/README.md: the spec index../system-specs/modules/providers.md: ACP harnesses and capability differencesmcp.md: MCP server discovery and tool managementsecurity-deep-dive.md: security model in depthresource-protection.md: resource limits and backpressure../system-specs/modules/memory-skills-hooks.md: memory system designdesign-notes/: focused design notes on individual problems../guides/install.mdand../guides/windows-install.md: installation