# Chat tools — what the chat agent can do directly

The chat agent (Forge's `/chat` page + the IDE plugin + Telegram bot) talks
to LLMs that can call **builtin tools** without round-tripping through a
background task. These cover the everyday "Forge knows / Forge owns" cases
so the agent doesn't have to dispatch a Claude CLI task just to read a
file or fire a pipeline.

This doc lists the tools the agent has by default and the workflows they
unlock. (Connector tools — `mantis.search_bugs`, `gitlab.list_my_todos`,
etc. — are documented in `17-connectors.md`.)

## File access — `<dataDir>/`

Forge's data dir (`~/.forge/data/` by default, override with `FORGE_DATA_DIR`)
holds everything Forge owns: pipelines, schedules, prompts, connectors,
the sqlite DB, the encryption key, and two cache pools — `cloned-projects/`
and `tmp/`. The chat agent can read most of it and write to `tmp/`.

### `save_tmp_file` — write to `<dataDir>/tmp/`
Use case: the user says *"save this report to a file"*, *"give me a
downloadable copy"*, *"export the results"*. The agent already has the
content in context — no CLI task needed.

The file lands in `<dataDir>/tmp/<filename>`. The response includes:
- `path`: `tmp/<filename>` — chat auto-links this string to the in-browser
  viewer at `/files/tmp/<filename>` (renders markdown, download button)
- `file_url`: `file://<abs-path>` — opens directly in Chrome on localhost,
  or the user can right-click → Show in Finder/Explorer
- `local_path`: absolute filesystem path

`tmp/` is **cache-managed** — counts in the user-menu Cache panel and gets
swept by the janitor after `scratchRetentionDays` (default 7 days). Tell
users their saved file is ephemeral; if they want it permanent, ask them
to copy it out.

### `read_forge_file` — read anywhere under `<dataDir>/`
Path is dataDir-relative: `tmp/foo.md`, `scratch/report.md`, `flows/x.yaml`,
`prompts/y.yaml`, `connectors/mantis/manifest.yaml`, etc. Use this instead
of dispatching a `cat` task when the user wants to inspect a Forge-owned
file.

Sensitive items at the dataDir root are refused: `.encrypt-key`,
`.enterprise-keys.json`, any `*.db*`, `forge.log*`, `*-tokens.json`. The
chat agent can't accidentally leak these into chat context.

Pass `as_base64: true` for binary files (pdf, images, zip).

### `list_forge_files` — list files anywhere under `<dataDir>/`
Pass `dir` as a dataDir-relative subdir (`tmp`, `scratch`, `flows`,
`connectors/mantis`). Each entry returns `path`, `kind` (file/dir),
`size`, `mtime`, and a `file_url` (file:// link).

Use this for *"what files are in tmp?"* / *"list my flows"* — never
dispatch a `ls` task for these.

### `scratch://<path>` — connector-arg ref protocol
Use case: the agent wants to **upload** a Forge-owned file to a connector
(e.g. `onedrive.upload_file`, `teams.send_message` with an attachment).
Instead of reading the file into context and base64-encoding it via the
model (unreliable for non-ASCII, expensive for large files), pass
`scratch://tmp/report.md` as the connector arg's value. Forge resolves
the path under `<dataDir>/`, base64-encodes server-side, then dispatches.

`scratch://` is the historical name for the **ref protocol**, not the
`scratch/` subdir — the path after it is dataDir-relative. Bare forms
(`scratch/<file>`, `/api/scratch/<file>`) are also accepted for
back-compat. Same sensitive-file blacklist as `read_forge_file`.

## Background tasks

### `dispatch_task` — fire-and-forget a Claude CLI task
For longer-running asks the user wants in the background ("analyze this
codebase and write findings", "run the test suite and summarize"). Returns
a `task_id`. Pair with `get_task_status` (or `start_watch`) to follow up.

### `cancel_task` — stop a running task by id
Use when the user says *"停止 / cancel / kill that task"*. Safe to call
on already-terminal tasks (returns ok + a note). Much cleaner than
telling the user to open the Tasks UI.

### `get_task_status` — check a dispatched task
Returns `status`, `terminal`, `result_summary` (truncated to 1KB),
`error`, `completed_at`. For long-running polls, prefer `start_watch`
over manual polling — see `24-watch.md`.

## Pipelines + schedules

The chat agent owns the full schedule CRUD surface and pipeline triggers:
`create_schedule`, `list_schedules`, `update_schedule`, `delete_schedule`,
`run_schedule_now`, `trigger_pipeline`, `get_pipeline_status`. See
`05-pipelines.md` and `13-schedules.md` for details on what the
underlying objects look like.

## Stop / mid-task interjection

The user can interrupt a runaway tool-call loop at any time:
- **Stop button** — aborts the loop at the next iteration boundary, OR
  between parallel tool calls (so a 5-way parallel batch can be stopped
  mid-way). Persists a "⏹ Stopped by user." message in the thread.
- **Send during streaming** — typed text is queued as a **note** for the
  running turn; the loop splices it in as a user message at the start of
  the next iteration. The first such note carries a `[mid-task
  interjection — …]` prefix so the model treats it as an authoritative
  redirect, not ambient chat.

Both work on the web `/chat` page and the extension. If the user opens the
same chat session from two clients (e.g. extension + webchat) and posts a
follow-up while a turn is running, the server merges it into the running
loop as a note — no parallel turns on one session.

See `10-troubleshooting.md` § "Chat keeps looping" for recovery when a
runaway loop pre-dates Stop being available.

## Help + introspection

`list_help_docs` + `read_help_doc` give the agent access to these same
docs so it can answer Forge questions directly. `list_forge_context`
returns the current instance's projects / agent profiles / installed
skills — call it before passing project / agent / skill names to
`dispatch_task` or `trigger_pipeline` to validate them.

`add_enterprise_key` registers a pasted enterprise marketplace key
(`fortinet:github_pat_…`) and triggers an immediate sync — see
`01-settings.md` § Marketplace Providers.
