7.3 KiB
Contributor Map (Agent-Oriented)
This is a fast navigation guide for contributors (human or AI). It answers:
- Where do I add a new tool?
- Where do I add a new skill?
- Where do I change routing/policy?
- What tests should I run?
For the execution-flow diagram, see docs/architecture/AGENT_DIAGRAM.md.
30-Second Repo Tour
src/
daemon/ Start-up wiring, service init, message routing
backends/ Native agent + orchestrator (tool loop lives here)
tools/ Tool interfaces, policy, executor, builtins
skills/ Skill loader/registry + install/watch infra
hooks/ Confirm/log/silent policy + autonomy resolution
sandbox/ Docker sandbox manager + sandboxed tool wrappers
models/ Provider clients + model router + retry/cost/capabilities
channels/ Chat adapters + pairing gate (Telegram/Discord/Slack/WhatsApp/Matrix/Signal/Mattermost/LINE/Feishu/Zalo/etc.)
gateway/ WebSocket JSON-RPC server + web UI + handlers
memory/ Hybrid search + embeddings + persistence
session/ SQLite store + session mgmt
cli/ CLI entrypoints + setup wizard
automation/ Cron/webhooks/heartbeat/gmail watcher
docs/
api/ Tool and gateway protocol docs
security/ Capability model, sandboxing, injection resistance
architecture/ Diagrams + contributor maps
config/
default.yaml Example configuration
Adding a New Tool
Where code goes
- Builtins live in
src/tools/builtin/. - Core types live in
src/tools/types.ts. - Tools are registered through the daemon wiring (see existing patterns in
src/daemon/index.ts).
Minimal tool skeleton
import type { Tool, ToolResult } from '../types.js';
export const myTool: Tool = {
name: 'my.tool',
description: 'What it does (model-facing).',
// Optional: gate credentialed actions
requiredSecretScopes: ['my_scope'],
inputSchema: {
type: 'object',
properties: {
foo: { type: 'string', description: '...' },
},
required: ['foo'],
},
execute: async (rawArgs: unknown, _context?: { signal?: AbortSignal }): Promise<ToolResult> => {
// ...
return { success: true, output: 'ok' };
},
};
Security checklist
- If the tool calls an external service or uses credentials:
- set
requiredSecretScopeson the tool. - ensure skill permissions can gate it (
manifest.json.permissions.secrets).
- set
- If it reads/writes files:
- use
file.*tools rather than bespoke FS access. - skills can restrict FS paths via
permissions.fs.
- use
Tests to add
- Unit tests in
src/tools/builtin/<tool>.test.ts. - If you touch policy/executor logic: add tests in
src/tools/policy.test.tsorsrc/tools/executor.test.ts.
Adding a New Skill
What a skill is
A skill is a package with:
SKILL.md: instructions injected into the system prompt.manifest.json: metadata + capability declarations.
Where skills live:
- Bundled skills:
skills/ - Managed skills (installed by Flynn): configured skill directory (see config)
Skill loading:
- Loader:
src/skills/loader.ts - Registry:
src/skills/registry.ts - Watcher (optional):
src/skills/watcher.ts
Capability permissions
If the skill is used via routing (intent target type skill), add permissions to manifest.json.
Without permissions, a skill is still loadable, but in skill context it has no tool access.
Reference: docs/security/SAFE_PERSONAL_AGENT.md.
Routing: Agents vs Skills vs Default
Where routing decisions happen:
- Inbound routing:
src/daemon/routing.ts
Inputs to routing:
- Channel + sender (agent router)
- Intent registry (regex rules) — can target
agentorskill - Metadata overrides (gateway / channel adapters)
If you need a new routing rule type:
- Intent targets live in the intent registry/types (see
src/intents/registry.ts).
Tool Policy + Execution
You will usually touch these files for capability/security work:
- Tool allowlisting:
src/tools/policy.ts - Tool runtime enforcement + audit:
src/tools/executor.ts - Confirmation/autonomy:
src/hooks/engine.ts,src/hooks/autonomy.ts
In skill context:
ToolPolicyContext.skillNameand.skillPermissionsare set insrc/daemon/routing.ts.- ToolPolicy filters available tools.
- ToolExecutor enforces fs/net/secret/injection restrictions even if a tool is somehow called.
Sandbox
Sandbox components:
- Docker sandbox manager:
src/sandbox/manager.ts - Docker implementation:
src/sandbox/docker.ts - Sandboxed tool wrappers:
src/sandbox/tools.ts - Tool replacement wiring:
src/daemon/routing.ts
Notes:
- Today the sandbox wiring replaces
shell.execandprocess.startwhen sandbox is enabled. - In skill context, high-risk execution defaults to sandbox unless the skill opts into host execution.
Gateway / API Surface
Gateway protocol docs:
docs/api/PROTOCOL.md
Gateway handlers:
src/gateway/handlers/(JSON-RPC methods)
Useful places to start:
src/gateway/server.ts(server lifecycle)src/gateway/protocol.ts(types)
Tests + Commands
Common checks:
pnpm typecheck
pnpm lint
pnpm test:run
Targeted tests for safety boundary changes:
- Tool policy:
pnpm test:run src/tools/policy.test.ts - Tool executor:
pnpm test:run src/tools/executor.test.ts - Skill loader:
pnpm test:run src/skills/loader.test.ts - Routing:
pnpm test:run src/daemon/routing.test.ts
First 3 PRs to Pick Up (Good Agent On-Ramps)
These are small, high-leverage changes that teach you the architecture quickly.
PR 1: Add a new "narrow" skill + permissions
Goal: add a skill that can only do one bounded thing (example: summarize a URL).
Deliverables:
skills/url-summarizer/SKILL.mdskills/url-summarizer/manifest.jsonwith permissions:tool_groups: ["group:web"]net: [{"host":"*","ports":[443]}](or narrower if you prefer)execution_environment: "sandbox"(default)
Acceptance:
- Skill loads (
flynn doctor/ skills list) - In skill context,
shell.execis not available web.fetchworks for https URLs
PR 2: Route into the skill via intents
Goal: make it easy to invoke the skill without special UI.
Deliverables:
- Add an
intents.rules[]entry targetingtype: skill- Patterns like:
summarize *,tldr *
- Patterns like:
Acceptance:
- A message like
summarize https://example.comroutes to the skill - Tool list is capability-filtered for that skill context
PR 3: Add an end-to-end safety test
Goal: lock in behavior so future refactors don’t weaken the boundary.
Deliverables:
- A test that asserts: when routed to a skill context with web-only permissions:
ToolPolicyexcludesshell.execandfile.writeToolExecutordenies a direct attempt to callfile.writeoutside allowed fs globs
Suggested test locations:
src/tools/policy.test.tssrc/tools/executor.test.ts
Where to Add What (Cheat Sheet)
New tool .................. src/tools/builtin/ + register in daemon
Tool allow/deny logic ...... src/tools/policy.ts
Tool runtime enforcement .... src/tools/executor.ts
New skill .................. skills/<name>/{SKILL.md,manifest.json}
Skill loader/validation ..... src/skills/loader.ts
Skill routing (intents) ..... src/daemon/routing.ts + config intents
Sandbox behavior ........... src/sandbox/* + src/daemon/routing.ts
Confirmation UX ............ src/hooks/* + frontends/gateway
Web UI changes ............. src/gateway/ui/