# Architecture

One plugin, `jig-core`, manages every tool the agent can call, whoever wrote
it. That is why the safety rules live in one place, and a new tool is governed
the day it is installed. (The [glossary](glossary.md) explains the words used
here.)

The covered tools are:
- Pi's own: `read`, `write`, `edit`, `bash`, `ls`, `find`, `grep`;
- Jig's own tools;
- tools from any other package.

For each call, `jig-core` does five things:
- works out where the call acts and whether it reads or writes;
- applies your rules;
- asks you when a rule says so;
- masks what must not reach the model;
- records what happened.

The tools themselves come from other plugins, such as `jig-git`. They only
describe what they do; they never implement safety themselves.

This works because Pi lets one extension take part in every tool call, through
hooks that run in this order for each call (Pi's `docs/extensions.md`):

```text
tool_execution_start → tool_call → tool_execution_update → tool_result → tool_execution_end
```

- A `tool_call` handler can **block** a call, **change** its input, or **ask**
  the user first. Pi's documentation shows exactly this: a handler that asks
  "Allow tool call?" based on the tool's hints.
- A `tool_result` handler can **replace** a result before the model sees it.
  That is where masking happens.
- `tool_execution_end` reports the tool name, whether it failed, its duration,
  and its parent call. That is where the record is written.
- Calls one tool makes through another (`ctx.executeTool`) pass through the
  same hooks, so nothing is ungoverned.

## One call, end to end

![Every tool call passes through jig-core's hooks before and after it runs](img/one-call.svg)

*Every call passes through the same three hooks. A call that is denied, or that you decline, never runs; what does run has its result masked, redacted and capped, and every call is recorded.*

<details>
<summary>Text version</summary>

```text
 the model asks for a tool call        e.g. mongo find {env: "prod"}, or bash "git push"
        │
        ▼
 tool_call hook ─ jig-core
   1. identify   which tool, from which plugin
   2. classify   environment + effect (read or write)
                   from: the tool's own declaration (Jig tools)
                         or a rule in your configuration (any tool)
                         or the built-in defaults for Pi's own tools
                         or the tool's hints (read-only, destructive, reaches outside)
                         or a default (unknown tools count as writes outside)
   3. decide     the rules table → allow / ask / deny
                   your configuration can always make it stricter,
                   but never lower the floor (see Permissions);
                   then the gates plugins registered, which can only
                   make it stricter still
   4. ask        only when the rule says ask: the approval screen,
                   inside this waiting call (see Approvals)
   5. block, or let it run
        │
        ▼
 the tool runs ─ its own code
   Jig tools fetch secrets through jig-core and mask their own results
        │
        ▼
 tool_result hook ─ jig-core
   6. redact     every known secret value is removed, before masking can split one
   7. mask       production results: personal data becomes tokens
                   (the backstop for tools that do not mask themselves)
   8. cap        long output is cut, and the full result goes to a spill file
        │
        ▼
 tool_execution_end ─ jig-core
   9. record     one line: tool, environment, effect, decision, your answer,
                 how long it waited for you, outcome, duration, parent call
        │
        ▼
 the result goes to the model
```

</details>

## What the tool manager can and cannot control

| It can | It cannot |
|---|---|
| Stop any call before it runs, and ask you first | See what a tool does inside its own code |
| Mask and redact any result before the model sees it | Un-send data a tool already sent somewhere |
| Record every call of every tool | Know the environment of a `bash` command |

- **Inside a tool,** a third-party tool that writes to production without
  saying so looks like any other call.
- **A `bash` command** is a shell string, and a shell string can reach
  anything.

What closes those gaps:

- **Unknown tools are treated as writes outside the machine,** so they ask
  until a rule or the tool's own hints say otherwise.
- **`bash` runs in a sandbox** that hides credentials. Without credentials, a
  shell command cannot do much harm outside the machine. The sandbox is the
  protection; command rules for `bash` are a convenience on top. `jig-sandbox`
  is that sandbox, and `/jig` says plainly when it is not installed. One command
  can run outside it, if you approve that exact command (see Permissions).
- **Pi's file tools refuse protected paths.** `read`, `write`, `edit`, `grep`,
  `find` and `ls` cannot read credential files (`~/.ssh`, `gh`'s token file,
  Pi's `auth.json`) or write Pi's agent folder, where `jig.json` and Jig's state
  live. This is a floor rule. `jig-sandbox` hides the same paths from `bash`;
  without it, `bash` can still reach them (see Permissions).
- **Remote actions go through tools,** not through `bash`. Jig's tools hold the
  credentials, so the tool manager sees, decides and records every remote
  action.

## Where things live

```text
jig (this repository, generic)
  jig-core   tool manager: classify, rules, approvals, secrets, sign-in, transports, signed-in HTTP requests, mask, redact, cap, record; watch engine
  jig-git    tools: local git, GitHub PRs and GitLab merge requests with reviews, releases; announces a push on the event bus
  jig-ops    tools: CI (GitHub Actions, GitLab, Jenkins, Concourse), deploy and runtime from a service catalog; the CI watch
  jig-mongo  tool: MongoDB, with allow and deny lists
  jig-jira   tool: Jira issues, with rendered previews
  jig-logs   tool: log search (Elasticsearch, OpenSearch, Loki)
  jig-api    tool: your saved API requests, run by name
  jig-review tools: the code review in the browser, approved with a passkey
  jig-memory tool: the handoff record (pasted evidence, open checks); ticket folders, notes, evidence, cards and recall
  jig-workflow tool: named workflows, with exit conditions decided from evidence; registers a gate that holds calls
  jig-lens   observability: reads jig-core's records, a summary in Pi and a local page; no tools
  jig-theme  the look: palettes as Pi themes, a footer, the answer colour; no tools
  jig-session the live session's state as a status file other programs read, such as the Herdr app files (jig-herdr, not a plugin)
  jig-sandbox the sandbox around the agent's bash, and the bash tool that replaces Pi's
  jig-agents tool: child agents, each a separate Pi with the same plugins; their questions come to your screen, their limits are counted in the parent
  bin/       the optional `pi` launcher (not a plugin; Pi never loads it): own tmux, reattach, the terminal's environment; see [the launcher](launcher.md)

company plugin (separate, private repository)
  the company's environments, sign-in service, internal tools and backends
```

- **A company plugin is configuration and knowledge,** supplied through `jig-core`'s public contract. [Writing a company
  plugin](company-plugin.md) says what goes in one, and [`examples/jig-acme`](../examples/jig-acme/README.md) is a tested example.
- **Plugins talk through `jig-core`,** never to each other's files.
  `jig-core` publishes the shared contracts:
  - how a tool declares what it does;
  - the record format;
  - the events other plugins can listen to.
- **One owner per file:** only `jig-core` writes the records, and `jig-lens`
  only reads them.
- **Tools are registered through `jig-core`'s kit** (`registerJigTool`), so `tools.deferred` in `jig.json` can keep a tool's
  description out of the context until Pi's `tool_search` finds it, and a workflow stage can bring tools in
  ([Deferred tools](../plugins/jig-core/docs/configuration.md#deferred-tools)).

## Platforms

Jig is TypeScript on Node, as Pi is, so it runs on Linux and macOS.
- **The sandbox is the exception.** `jig-sandbox` uses Anthropic's sandbox
  runtime: bubblewrap on Linux and WSL2, Seatbelt on macOS. The Mac side is written
  and not yet tested on a Mac, and its `filtered` and `ask` network modes mean `off`
  until the bridge between commands is tried there. Native Windows is not supported.
- **Secrets have a backend per platform** (see Secrets).

## Where Jig sits

![Jig is the harness you own, around Pi, which is the harness its builders made around the model; a dashed outer ring is a later team harness](img/harnesses.svg)

*Three harnesses, one inside the other, in [Birgitta Böckeler's](https://martinfowler.com/articles/harness-engineering.html) terms: the model is what gets harnessed, Pi's builders made the inner harness, and Jig is yours. A team harness would be the next ring if a team adopts Jig, and shared organizational memory would live there.*

## How Jig steers the agent

![Guides feed forward into Pi before it acts, sensors feed back after it acts, and you steer by changing both](img/harness-at-a-glance.svg)

*Guides go forward into Pi before it acts, sensors feed back after it acts, and you steer by changing both. A star marks a model deciding, a dot a program, a figure a person, and a dashed outline something planned for later.*

