Files
flynn/docs/architecture/CONTRIBUTOR_MAP.md
T
2026-02-16 12:09:44 -08:00

7.3 KiB
Raw Blame History

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/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 requiredSecretScopes on the tool.
    • ensure skill permissions can gate it (manifest.json.permissions.secrets).
  • If it reads/writes files:
    • use file.* tools rather than bespoke FS access.
    • skills can restrict FS paths via permissions.fs.

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.ts or src/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 agent or skill
  • 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.skillName and .skillPermissions are set in src/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.exec and process.start when 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.md
  • skills/url-summarizer/manifest.json with 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.exec is not available
  • web.fetch works 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 targeting type: skill
    • Patterns like: summarize *, tldr *

Acceptance:

  • A message like summarize https://example.com routes 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 dont weaken the boundary.

Deliverables:

  • A test that asserts: when routed to a skill context with web-only permissions:
    • ToolPolicy excludes shell.exec and file.write
    • ToolExecutor denies a direct attempt to call file.write outside allowed fs globs

Suggested test locations:

  • src/tools/policy.test.ts
  • src/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/