@aion0/forge 0.10.51 → 0.10.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ # Chat tools — what the chat agent can do directly
2
+
3
+ The chat agent (Forge's `/chat` page + the IDE plugin + Telegram bot) talks
4
+ to LLMs that can call **builtin tools** without round-tripping through a
5
+ background task. These cover the everyday "Forge knows / Forge owns" cases
6
+ so the agent doesn't have to dispatch a Claude CLI task just to read a
7
+ file or fire a pipeline.
8
+
9
+ This doc lists the tools the agent has by default and the workflows they
10
+ unlock. (Connector tools — `mantis.search_bugs`, `gitlab.list_my_todos`,
11
+ etc. — are documented in `17-connectors.md`.)
12
+
13
+ ## File access — `<dataDir>/`
14
+
15
+ Forge's data dir (`~/.forge/data/` by default, override with `FORGE_DATA_DIR`)
16
+ holds everything Forge owns: pipelines, schedules, prompts, connectors,
17
+ the sqlite DB, the encryption key, and two cache pools — `cloned-projects/`
18
+ and `tmp/`. The chat agent can read most of it and write to `tmp/`.
19
+
20
+ ### `save_tmp_file` — write to `<dataDir>/tmp/`
21
+ Use case: the user says *"save this report to a file"*, *"give me a
22
+ downloadable copy"*, *"export the results"*. The agent already has the
23
+ content in context — no CLI task needed.
24
+
25
+ The file lands in `<dataDir>/tmp/<filename>`. The response includes:
26
+ - `path`: `tmp/<filename>` — chat auto-links this string to the in-browser
27
+ viewer at `/files/tmp/<filename>` (renders markdown, download button)
28
+ - `file_url`: `file://<abs-path>` — opens directly in Chrome on localhost,
29
+ or the user can right-click → Show in Finder/Explorer
30
+ - `local_path`: absolute filesystem path
31
+
32
+ `tmp/` is **cache-managed** — counts in the user-menu Cache panel and gets
33
+ swept by the janitor after `scratchRetentionDays` (default 7 days). Tell
34
+ users their saved file is ephemeral; if they want it permanent, ask them
35
+ to copy it out.
36
+
37
+ ### `read_forge_file` — read anywhere under `<dataDir>/`
38
+ Path is dataDir-relative: `tmp/foo.md`, `scratch/report.md`, `flows/x.yaml`,
39
+ `prompts/y.yaml`, `connectors/mantis/manifest.yaml`, etc. Use this instead
40
+ of dispatching a `cat` task when the user wants to inspect a Forge-owned
41
+ file.
42
+
43
+ Sensitive items at the dataDir root are refused: `.encrypt-key`,
44
+ `.enterprise-keys.json`, any `*.db*`, `forge.log*`, `*-tokens.json`. The
45
+ chat agent can't accidentally leak these into chat context.
46
+
47
+ Pass `as_base64: true` for binary files (pdf, images, zip).
48
+
49
+ ### `list_forge_files` — list files anywhere under `<dataDir>/`
50
+ Pass `dir` as a dataDir-relative subdir (`tmp`, `scratch`, `flows`,
51
+ `connectors/mantis`). Each entry returns `path`, `kind` (file/dir),
52
+ `size`, `mtime`, and a `file_url` (file:// link).
53
+
54
+ Use this for *"what files are in tmp?"* / *"list my flows"* — never
55
+ dispatch a `ls` task for these.
56
+
57
+ ### `scratch://<path>` — connector-arg ref protocol
58
+ Use case: the agent wants to **upload** a Forge-owned file to a connector
59
+ (e.g. `onedrive.upload_file`, `teams.send_message` with an attachment).
60
+ Instead of reading the file into context and base64-encoding it via the
61
+ model (unreliable for non-ASCII, expensive for large files), pass
62
+ `scratch://tmp/report.md` as the connector arg's value. Forge resolves
63
+ the path under `<dataDir>/`, base64-encodes server-side, then dispatches.
64
+
65
+ `scratch://` is the historical name for the **ref protocol**, not the
66
+ `scratch/` subdir — the path after it is dataDir-relative. Bare forms
67
+ (`scratch/<file>`, `/api/scratch/<file>`) are also accepted for
68
+ back-compat. Same sensitive-file blacklist as `read_forge_file`.
69
+
70
+ ## Background tasks
71
+
72
+ ### `dispatch_task` — fire-and-forget a Claude CLI task
73
+ For longer-running asks the user wants in the background ("analyze this
74
+ codebase and write findings", "run the test suite and summarize"). Returns
75
+ a `task_id`. Pair with `get_task_status` (or `start_watch`) to follow up.
76
+
77
+ ### `cancel_task` — stop a running task by id
78
+ Use when the user says *"停止 / cancel / kill that task"*. Safe to call
79
+ on already-terminal tasks (returns ok + a note). Much cleaner than
80
+ telling the user to open the Tasks UI.
81
+
82
+ ### `get_task_status` — check a dispatched task
83
+ Returns `status`, `terminal`, `result_summary` (truncated to 1KB),
84
+ `error`, `completed_at`. For long-running polls, prefer `start_watch`
85
+ over manual polling — see `24-watch.md`.
86
+
87
+ ## Pipelines + schedules
88
+
89
+ The chat agent owns the full schedule CRUD surface and pipeline triggers:
90
+ `create_schedule`, `list_schedules`, `update_schedule`, `delete_schedule`,
91
+ `run_schedule_now`, `trigger_pipeline`, `get_pipeline_status`. See
92
+ `05-pipelines.md` and `13-schedules.md` for details on what the
93
+ underlying objects look like.
94
+
95
+ ## Stop / mid-task interjection
96
+
97
+ The user can interrupt a runaway tool-call loop at any time:
98
+ - **Stop button** — aborts the loop at the next iteration boundary, OR
99
+ between parallel tool calls (so a 5-way parallel batch can be stopped
100
+ mid-way). Persists a "⏹ Stopped by user." message in the thread.
101
+ - **Send during streaming** — typed text is queued as a **note** for the
102
+ running turn; the loop splices it in as a user message at the start of
103
+ the next iteration. The first such note carries a `[mid-task
104
+ interjection — …]` prefix so the model treats it as an authoritative
105
+ redirect, not ambient chat.
106
+
107
+ Both work on the web `/chat` page and the extension. If the user opens the
108
+ same chat session from two clients (e.g. extension + webchat) and posts a
109
+ follow-up while a turn is running, the server merges it into the running
110
+ loop as a note — no parallel turns on one session.
111
+
112
+ See `10-troubleshooting.md` § "Chat keeps looping" for recovery when a
113
+ runaway loop pre-dates Stop being available.
114
+
115
+ ## Help + introspection
116
+
117
+ `list_help_docs` + `read_help_doc` give the agent access to these same
118
+ docs so it can answer Forge questions directly. `list_forge_context`
119
+ returns the current instance's projects / agent profiles / installed
120
+ skills — call it before passing project / agent / skill names to
121
+ `dispatch_task` or `trigger_pipeline` to validate them.
122
+
123
+ `add_enterprise_key` registers a pasted enterprise marketplace key
124
+ (`fortinet:github_pat_…`) and triggers an immediate sync — see
125
+ `01-settings.md` § Marketplace Providers.
@@ -51,6 +51,7 @@ The token is valid for 24 hours. Store it in a variable and reuse for all API ca
51
51
  | `18-chrome-mcp.md` | Connect Forge Claude Code sessions to a real Chrome via chrome-devtools-mcp — dev-time browser access for connector authoring |
52
52
  | `23-automation-states.md` | Fortinet pipeline automation: GitLab MR stage labels, Mantis status flow, Teams notify policy |
53
53
  | `24-watch.md` | Background watches — async polling of long jobs (device upgrade, Jenkins build, test run) that report back in chat. Two triggers: connectors' declarative `async` block, and the `start_watch` builtin the assistant calls on the fly. Where to see/cancel them. |
54
+ | `25-chat-tools.md` | Builtin tools the chat agent has by default — file access (`save_tmp_file` / `read_forge_file` / `list_forge_files`), `scratch://` connector-ref protocol, `dispatch_task` / `cancel_task`, schedules CRUD, pipelines, Stop + mid-task notes. |
54
55
 
55
56
  ## Matching questions to docs
56
57
 
@@ -86,3 +87,4 @@ The token is valid for 24 hours. Store it in a variable and reuse for all API ca
86
87
  - Recipe / "From recipe" form / parameterized job → tell user: **recipes deprecated** along with Jobs; fire pipelines manually or via Schedules.
87
88
  - Mantis bug fix / fortinet-mantis-bug-fix / open MR for Mantis bug / fortinet-mr-review / pre-review / GitLab stage labels → `23-automation-states.md` (kept Fortinet pipelines)
88
89
  - Background watch / "watch this build" / "tell me when the upgrade is done" / progress chip / start_watch / async long job / why is the assistant polling → `24-watch.md`
90
+ - save_tmp_file / read_forge_file / list_forge_files / cancel_task / scratch:// ref / `<dataDir>/tmp/` / Stop / mid-task note / "what tools does the chat agent have" → `25-chat-tools.md`
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Scratch directory janitor — deletes stale files under <dataDir>/scratch/.
2
+ * Cache-dir janitor — deletes stale files under:
3
+ * <dataDir>/tmp/ — chat-saved files (save_tmp_file)
4
+ * <dataDir>/scratch/ — legacy chat-saved location (kept for back-compat)
3
5
  *
4
- * The save_scratch_file chat tool drops generated reports here so the user
5
- * can download them; without periodic sweeping the directory grows forever.
6
- * Retention is settings.scratchRetentionDays (default 7).
6
+ * Without periodic sweeping the dirs grow forever. Retention is
7
+ * settings.scratchRetentionDays (default 7).
7
8
  *
8
- * Conservative scope: only sweeps PLAIN FILES at the top level of scratch/.
9
- * - Skips dotfiles (.mcp.json, .claude/, .forge/) Forge/Claude project state
10
- * - Skips CLAUDE.md scratch project marker
11
- * - Skips subdirectories — could be user-created project trees we don't own
9
+ * Conservative scope: only top-level files. Skips dotfiles, the
10
+ * scratch-project marker CLAUDE.md, and subdirs (could be user-created
11
+ * project trees we don't own).
12
12
  */
13
13
 
14
14
  import { readdirSync, statSync, unlinkSync } from 'node:fs';
@@ -29,16 +29,13 @@ function retentionMs(): number {
29
29
  return days * ONE_DAY_MS;
30
30
  }
31
31
 
32
- export function cleanupScratch(): { scanned: number; deleted: number; freedBytes: number } {
33
- const root = join(getDataDir(), 'scratch');
32
+ function sweepDir(root: string, cutoff: number, skipNames: Set<string>): { scanned: number; deleted: number; freedBytes: number } {
34
33
  let scanned = 0, deleted = 0, freedBytes = 0;
35
- const maxAge = retentionMs();
36
- const cutoff = Date.now() - maxAge;
37
34
  let entries: string[] = [];
38
35
  try { entries = readdirSync(root); } catch { return { scanned, deleted, freedBytes }; }
39
36
  for (const name of entries) {
40
37
  if (name.startsWith('.')) continue;
41
- if (name === 'CLAUDE.md') continue;
38
+ if (skipNames.has(name)) continue;
42
39
  const p = join(root, name);
43
40
  let st;
44
41
  try { st = statSync(p); } catch { continue }
@@ -48,12 +45,24 @@ export function cleanupScratch(): { scanned: number; deleted: number; freedBytes
48
45
  try { unlinkSync(p); deleted++; freedBytes += st.size; }
49
46
  catch (e) { console.warn('[scratch-cleanup] unlink failed:', p, (e as Error).message); }
50
47
  }
51
- if (deleted > 0) {
52
- console.log(`[scratch-cleanup] deleted ${deleted}/${scanned} files, freed ${freedBytes} bytes`);
53
- }
54
48
  return { scanned, deleted, freedBytes };
55
49
  }
56
50
 
51
+ export function cleanupScratch(): { scanned: number; deleted: number; freedBytes: number } {
52
+ const cutoff = Date.now() - retentionMs();
53
+ const tmp = sweepDir(join(getDataDir(), 'tmp'), cutoff, new Set());
54
+ const scratch = sweepDir(join(getDataDir(), 'scratch'), cutoff, new Set(['CLAUDE.md']));
55
+ const total = {
56
+ scanned: tmp.scanned + scratch.scanned,
57
+ deleted: tmp.deleted + scratch.deleted,
58
+ freedBytes: tmp.freedBytes + scratch.freedBytes,
59
+ };
60
+ if (total.deleted > 0) {
61
+ console.log(`[scratch-cleanup] deleted ${total.deleted}/${total.scanned} files (tmp=${tmp.deleted}, scratch=${scratch.deleted}), freed ${total.freedBytes} bytes`);
62
+ }
63
+ return total;
64
+ }
65
+
57
66
  let started = false;
58
67
  export function startScratchCleanup(): void {
59
68
  if (started) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.10.51",
3
+ "version": "0.10.55",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {