token-goat 2.8.6 → 2.9.2
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.
- package/README.md +27 -717
- package/SECURITY.md +1 -1
- package/dist/token-goat-chunk-CGWACYYZ.mjs +79 -0
- package/dist/{token-goat-chunk-XAWIELXH.mjs → token-goat-chunk-FXAPKVRG.mjs} +545 -217
- package/dist/{token-goat-chunk-SHN4UTL4.mjs → token-goat-chunk-JMUBXBG7.mjs} +5 -5
- package/dist/{token-goat-chunk-U7M2LTGP.mjs → token-goat-chunk-KKIB7O3Z.mjs} +854 -283
- package/dist/{token-goat-chunk-ZIPBLIUZ.mjs → token-goat-chunk-LEBYARO3.mjs} +28 -4
- package/dist/{token-goat-chunk-DK4VLLYB.mjs → token-goat-chunk-LJ3CHCTT.mjs} +2061 -3612
- package/dist/{token-goat-chunk-L2XHDICZ.mjs → token-goat-chunk-NY4HYRSK.mjs} +28 -27
- package/dist/{token-goat-chunk-MA5237JN.mjs → token-goat-chunk-Q4LOQY44.mjs} +205 -60
- package/dist/{token-goat-chunk-GUNYAGOZ.mjs → token-goat-chunk-UZ2NFOOZ.mjs} +10103 -117
- package/dist/{token-goat-chunk-TELKICYU.mjs → token-goat-chunk-VCNW7BGU.mjs} +155 -519
- package/dist/{token-goat-chunk-LJEETTER.mjs → token-goat-chunk-ZVN5WHTO.mjs} +5 -5
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/docs/C4_RUNTIME_ARCHITECTURE.md +303 -0
- package/docs/cli.md +339 -0
- package/docs/install.md +331 -0
- package/docs/security.md +102 -0
- package/package.json +7 -2
- package/dist/token-goat-chunk-2JZ66BBE.mjs +0 -7157
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
buildEvent,
|
|
5
5
|
relay,
|
|
6
6
|
relayInProcess
|
|
7
|
-
} from "./token-goat-chunk-
|
|
7
|
+
} from "./token-goat-chunk-Q4LOQY44.mjs";
|
|
8
8
|
import {
|
|
9
9
|
MAX_STDIN_BYTES,
|
|
10
10
|
readStdinJson
|
|
11
|
-
} from "./token-goat-chunk-
|
|
12
|
-
import "./token-goat-chunk-
|
|
13
|
-
import "./token-goat-chunk-
|
|
14
|
-
import "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-VCNW7BGU.mjs";
|
|
12
|
+
import "./token-goat-chunk-LJ3CHCTT.mjs";
|
|
13
|
+
import "./token-goat-chunk-CGWACYYZ.mjs";
|
|
14
|
+
import "./token-goat-chunk-UZ2NFOOZ.mjs";
|
|
15
15
|
import "./token-goat-chunk-AO2QD2AG.mjs";
|
|
16
16
|
import "./token-goat-chunk-AEX54RUZ.mjs";
|
|
17
17
|
export {
|
package/dist/token-goat-hook.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
relayInProcess
|
|
5
|
-
} from "./token-goat-chunk-
|
|
6
|
-
import "./token-goat-chunk-
|
|
7
|
-
import "./token-goat-chunk-
|
|
8
|
-
import "./token-goat-chunk-
|
|
9
|
-
import "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-Q4LOQY44.mjs";
|
|
6
|
+
import "./token-goat-chunk-VCNW7BGU.mjs";
|
|
7
|
+
import "./token-goat-chunk-LJ3CHCTT.mjs";
|
|
8
|
+
import "./token-goat-chunk-CGWACYYZ.mjs";
|
|
9
|
+
import "./token-goat-chunk-UZ2NFOOZ.mjs";
|
|
10
10
|
import "./token-goat-chunk-AO2QD2AG.mjs";
|
|
11
11
|
import "./token-goat-chunk-AEX54RUZ.mjs";
|
|
12
12
|
export {
|
package/dist/token-goat.core.mjs
CHANGED
|
@@ -2,13 +2,13 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
run
|
|
5
|
-
} from "./token-goat-chunk-
|
|
6
|
-
import "./token-goat-chunk-
|
|
7
|
-
import "./token-goat-chunk-
|
|
8
|
-
import "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-KKIB7O3Z.mjs";
|
|
6
|
+
import "./token-goat-chunk-FXAPKVRG.mjs";
|
|
7
|
+
import "./token-goat-chunk-VCNW7BGU.mjs";
|
|
8
|
+
import "./token-goat-chunk-LJ3CHCTT.mjs";
|
|
9
9
|
import {
|
|
10
10
|
installEpipeGuard
|
|
11
|
-
} from "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-UZ2NFOOZ.mjs";
|
|
12
12
|
import "./token-goat-chunk-AO2QD2AG.mjs";
|
|
13
13
|
import "./token-goat-chunk-AEX54RUZ.mjs";
|
|
14
14
|
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Token-Goat Runtime Architecture & Security Model (C4 Specification)
|
|
2
|
+
|
|
3
|
+
This document specifies the complete runtime architecture, hook processing flow, storage layout, background worker lifecycle, bridge integrations, and security trust boundaries of **Token-Goat**.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. System Context Diagram (C4 Level 1) & Trust Boundaries
|
|
8
|
+
|
|
9
|
+
Token-Goat sits between AI Agent Harnesses (Claude Code, VS Code Copilot, GitHub Copilot CLI, Codex) and the local operating system, filtering and narrowing context before it enters the model window.
|
|
10
|
+
|
|
11
|
+
```mermaid
|
|
12
|
+
flowchart TB
|
|
13
|
+
classDef person fill:#08427B,stroke:#073B6F,color:#ffffff,font-weight:bold;
|
|
14
|
+
classDef tgRuntime fill:#1168BD,stroke:#0B4884,color:#ffffff,font-weight:bold;
|
|
15
|
+
classDef agent fill:#7C3AED,stroke:#5B21B6,color:#ffffff,font-weight:bold;
|
|
16
|
+
classDef external fill:#475569,stroke:#334155,color:#ffffff;
|
|
17
|
+
classDef untrusted fill:#DC2626,stroke:#991B1B,color:#ffffff,font-weight:bold;
|
|
18
|
+
|
|
19
|
+
Developer["👤 Software Engineer / User<br/><small>[Person] Writes code, issues CLI commands, reviews tasks</small>"]:::person
|
|
20
|
+
|
|
21
|
+
subgraph HarnessBoundary ["AI Coding Agent Runtimes"]
|
|
22
|
+
ClaudeCode["🤖 Claude Code<br/><small>[Harness] Subprocess Hook Intercept</small>"]:::agent
|
|
23
|
+
CopilotCLI["🤖 GitHub Copilot CLI<br/><small>[Harness] Direct Tool Calls & Aliases</small>"]:::agent
|
|
24
|
+
VSCodeCopilot["🤖 VS Code Copilot Chat<br/><small>[Harness] stdio MCP Server</small>"]:::agent
|
|
25
|
+
CodexHermes["🤖 Codex / OpenCode / Hermes<br/><small>[Harness] Hook & Execution Bridge</small>"]:::agent
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
subgraph TokenGoatRuntime ["Token-Goat Engine (Local Machine)"]
|
|
29
|
+
TGCore["⚡ Token-Goat Runtime<br/><small>[TypeScript / Node.js]</small><br/>• Hook Relays & Normalization<br/>• SQLite Symbol & Ref Indexer<br/>• Structural Bash & Code Filters<br/>• Prompt Injection Fencing<br/>• MCP stdio Server"]:::tgRuntime
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
subgraph StorageBoundary ["Host Environment & Storage (Trust Boundary: Local User)"]
|
|
33
|
+
LocalFS["📁 Workspace Filesystem<br/><small>Git Repo Source Trees & Fixtures</small>"]:::external
|
|
34
|
+
AppDBs[("🗄️ Token-Goat Local Store<br/><small>global.db (WAL), Cache Blobs, Sessions</small>")]:::external
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph UntrustedZone ["Untrusted External Content (Trust Boundary: Untrusted Input)"]
|
|
38
|
+
WebPages["🌐 External Web & APIs<br/><small>Arbitrary web pages, docs, issue trackers</small>"]:::untrusted
|
|
39
|
+
ExternalFiles["📄 External Attachments<br/><small>PDFs, PPTX, Excel, Images, Archives</small>"]:::untrusted
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Developer -->|Runs interactive commands| TGCore
|
|
43
|
+
Developer -->|Prompts agent| ClaudeCode
|
|
44
|
+
Developer -->|Prompts agent| VSCodeCopilot
|
|
45
|
+
|
|
46
|
+
ClaudeCode -->|Fires lifecycle hooks via stdin/stdout| TGCore
|
|
47
|
+
CodexHermes -->|Fires tool hooks| TGCore
|
|
48
|
+
VSCodeCopilot -->|JSON-RPC over stdio| TGCore
|
|
49
|
+
CopilotCLI -->|Executes surgical commands| TGCore
|
|
50
|
+
|
|
51
|
+
TGCore -->|Reads/Indexes/Watches| LocalFS
|
|
52
|
+
TGCore -->|Persists index & content-addressed blobs| AppDBs
|
|
53
|
+
|
|
54
|
+
TGCore -->|Fetches requested URL/Doc| WebPages
|
|
55
|
+
TGCore -->|Parses slices of| ExternalFiles
|
|
56
|
+
TGCore -.->|Marks untrusted & sanitizes| ClaudeCode
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Trust Boundaries & Threat Surfaces
|
|
60
|
+
1. **Harness Hook Boundary (Local IPC)**: Hook inputs arrive via standard I/O (JSON payloads over `stdin`). Token-Goat fails soft (`{ continue: true }`) on any parsing or processing error so agent operations never block.
|
|
61
|
+
2. **Untrusted Payload Boundary (Web/Documents)**: Web pages (`webfetch.ts`) and document attachments (`pdf_extract.ts`, `docx_extract.ts`) are fenced before model exposure because of where they came from, never because a scanner recognized them. `untrusted_fence.ts` is the single decision point; `injection_scan.ts` runs alongside it and decides only whether the fence's notice names the patterns it matched.
|
|
62
|
+
3. **Storage Boundary (Local User Isolation)**: All persistent databases and caches are stored in user-scoped directories (`%LOCALAPPDATA%\dfk-helper\token-goat` on Windows, `~/.token-goat` on POSIX) with no multi-tenant cross-talk.
|
|
63
|
+
4. **Child Process Execution Boundary**: Bash command compression wraps commands inside a controlled runner (`bash_runner.ts`) capturing output with stream bounds (32 MiB/stream cap) and preserves exit codes.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 2. Container Diagram (C4 Level 2) & Storage Architecture
|
|
68
|
+
|
|
69
|
+
Token-Goat runs as a set of lightweight, ephemeral processes and an optional background worker daemon cooperating over SQLite and content-addressed disk files.
|
|
70
|
+
|
|
71
|
+
```mermaid
|
|
72
|
+
flowchart TB
|
|
73
|
+
classDef process fill:#1E40AF,stroke:#172554,color:#ffffff,font-weight:bold;
|
|
74
|
+
classDef shim fill:#2563EB,stroke:#1D4ED8,color:#ffffff;
|
|
75
|
+
classDef storage fill:#0D9488,stroke:#115E59,color:#ffffff,font-weight:bold;
|
|
76
|
+
classDef external fill:#64748B,stroke:#334155,color:#ffffff;
|
|
77
|
+
|
|
78
|
+
subgraph AgentSpace ["Agent Execution Context"]
|
|
79
|
+
Agent["AI Coding Harness (Claude Code / Codex)"]:::external
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
subgraph TGProcesses ["Token-Goat Runtime Containers"]
|
|
83
|
+
HookShim["🪝 Hook Shim<br/><small>[~/.claude/hooks/token-goat-shim.js]</small><br/>In-process fast path / Subprocess launcher"]:::shim
|
|
84
|
+
HookRelay["⚡ Hook Relay & Normalizer<br/><small>[dist/token-goat-hook.mjs]</small><br/>Normalizes Codex/Gemini/Claude schemas"]:::process
|
|
85
|
+
CLIBinary["💻 CLI Tool & Command Suite<br/><small>[dist/token-goat.mjs]</small><br/>100+ surgical read, outline, pack commands"]:::process
|
|
86
|
+
MCPServer["🔌 MCP stdio Server<br/><small>[token-goat mcp-serve]</small><br/>18 tools: symbol, read, retrieve, section"]:::process
|
|
87
|
+
WorkerDaemon["🔄 Background Indexer Daemon<br/><small>[worker.ts / worker_daemon.ts]</small><br/>Polls dirty queue every 2s, reindexes changes"]:::process
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
subgraph DiskStorage ["Persistent Storage Model (%LOCALAPPDATA% or ~/.token-goat)"]
|
|
91
|
+
GlobalDB[("🗄️ global.db (SQLite + WAL)<br/><small>• files, symbols, refs, chunks<br/>• symbols_fts (FTS5)<br/>• chunk_vectors (vec0 KNN)<br/>• stats</small>")]:::storage
|
|
92
|
+
DirtyQueue[("📋 queue/dirty.txt<br/><small>Append-only list of edited file paths</small>")]:::storage
|
|
93
|
+
SessionJSON[("📝 sessions/{session_id}.json<br/><small>Read/edit history, shown hints, cache indexes</small>")]:::storage
|
|
94
|
+
BlobStore[("📦 Content Blob Store<br/><small>• bash_outputs/{id}.json<br/>• web_outputs/{id}.json<br/>• images/ & skills/</small>")]:::storage
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Agent -->|Invokes hook| HookShim
|
|
98
|
+
HookShim -->|Loads fast in-process| HookRelay
|
|
99
|
+
HookRelay -->|Dispatches events to handlers| HookRelay
|
|
100
|
+
|
|
101
|
+
Agent -->|Spawns tool CLI| CLIBinary
|
|
102
|
+
Agent -->|Connects via stdio| MCPServer
|
|
103
|
+
|
|
104
|
+
HookRelay -->|Appends edited file paths on PostToolUse(Edit)| DirtyQueue
|
|
105
|
+
HookRelay -->|Loads & merges session state| SessionJSON
|
|
106
|
+
HookRelay -->|Stores compressed output| BlobStore
|
|
107
|
+
|
|
108
|
+
WorkerDaemon -->|Polls & drains| DirtyQueue
|
|
109
|
+
WorkerDaemon -->|Writes updated symbols/chunks| GlobalDB
|
|
110
|
+
|
|
111
|
+
CLIBinary -->|Queries symbols/sections| GlobalDB
|
|
112
|
+
MCPServer -->|Queries index & retrieves blobs| GlobalDB
|
|
113
|
+
MCPServer -->|Retrieves cached output by id| BlobStore
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Storage Model & Concurrency Guarantees
|
|
117
|
+
- **`global.db`**: Unified SQLite database opened with `journal_mode=WAL`, `synchronous=NORMAL`, and `busy_timeout=15000ms`. Writes use explicit transactions (`writeParseResult`).
|
|
118
|
+
- **Body Bounding Invariant**: Symbol bodies in `symbols` are bounded at `MAX_SYMBOL_BODY_CHARS` at the single `writeParseResult` choke point. Over-cap bodies are stored as `''` and sliced dynamically from source on read (`resolveBody`), preventing database bloating while preserving full symbol bounds.
|
|
119
|
+
- **Merge-on-Save Session State**: `sessions/{session_id}.json` is updated via atomic read-modify-write (`saveSessionState`) with set-union for seen hints and newest-wins for command indexes.
|
|
120
|
+
- **Content Blob Lifecycle**: `bash_outputs/` and `web_outputs/` store content-addressed JSON objects keyed by SHA-256 / random ID, pruned automatically by age (24h TTL) and count cap (200 / 4096 files).
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 3. Component Architecture (C4 Level 3)
|
|
125
|
+
|
|
126
|
+
### A. Hook Intercept & Context Optimization Subsystem
|
|
127
|
+
|
|
128
|
+
```mermaid
|
|
129
|
+
flowchart TB
|
|
130
|
+
classDef router fill:#1D4ED8,stroke:#1E3A8A,color:#ffffff,font-weight:bold;
|
|
131
|
+
classDef hook fill:#2563EB,stroke:#1D4ED8,color:#ffffff;
|
|
132
|
+
classDef filter fill:#0284C7,stroke:#0369A1,color:#ffffff;
|
|
133
|
+
classDef sec fill:#DC2626,stroke:#991B1B,color:#ffffff,font-weight:bold;
|
|
134
|
+
|
|
135
|
+
PayloadIn["Raw Hook Payload (stdin)"] --> Normalizer["Harness Normalizer<br/><small>(hooks_cli.ts::normalizePayload)</small>"]:::router
|
|
136
|
+
Normalizer --> Dispatcher["Hook Registry Dispatcher<br/><small>(hook_registry.ts::runHook)</small>"]:::router
|
|
137
|
+
|
|
138
|
+
subgraph LifecycleHandlers ["Hook Lifecycle Handlers"]
|
|
139
|
+
PreRead["preReadHandler<br/><small>(hooks_read.ts)</small><br/>Diff-on-reread, surgical hints"]:::hook
|
|
140
|
+
PreBash["preBashHandler<br/><small>(hooks_bash.ts)</small><br/>Command rewrite to 'token-goat compress'"]:::hook
|
|
141
|
+
PreFetch["preFetchHandler<br/><small>(hooks_fetch.ts)</small><br/>Image shrink & URL dedup hint"]:::hook
|
|
142
|
+
PostEdit["postEditHandler<br/><small>(hooks_edit.ts)</small><br/>Append to queue/dirty.txt"]:::hook
|
|
143
|
+
PreCompact["preCompactHandler<br/><small>(hooks_compact.ts)</small><br/>Adaptive session manifest"]:::hook
|
|
144
|
+
PreMCP["preMcpHandler<br/><small>(hooks_mcp.ts)</small><br/>Read-only MCP output dedup"]:::hook
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
subgraph FilterPipeline ["158 Bash & Code Output Filters (tool_filters/)"]
|
|
148
|
+
TestRunners["Test Runners (Vitest, Jest, Pytest, GoTest)"]:::filter
|
|
149
|
+
PackageMgrs["Package Managers (npm, pnpm, uv, pip, cargo)"]:::filter
|
|
150
|
+
Linters["Linters & Typecheckers (tsc, ruff, mypy, eslint)"]:::filter
|
|
151
|
+
GitCloud["VCS & Cloud (git diff, kubectl, terraform, gh)"]:::filter
|
|
152
|
+
GenericCompress["GenericFilter (ANSI/progress strip + line cap)"]:::filter
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
subgraph SecurityControls ["Security & Trust Layer"]
|
|
156
|
+
InjectionCheck["Prompt Injection Scanner<br/><small>(injection_scan.ts)</small>"]:::sec
|
|
157
|
+
SecretRedact["Secret Redaction<br/><small>(secret_redact.ts)</small>"]:::sec
|
|
158
|
+
OverflowGuard["Overflow Guard<br/><small>(overflow_guard.ts)</small>"]:::sec
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
Dispatcher --> PreRead
|
|
162
|
+
Dispatcher --> PreBash
|
|
163
|
+
Dispatcher --> PreFetch
|
|
164
|
+
Dispatcher --> PostEdit
|
|
165
|
+
Dispatcher --> PreCompact
|
|
166
|
+
Dispatcher --> PreMCP
|
|
167
|
+
|
|
168
|
+
PreBash --> FilterPipeline
|
|
169
|
+
PreFetch --> InjectionCheck
|
|
170
|
+
PreRead --> OverflowGuard
|
|
171
|
+
PreBash --> SecretRedact
|
|
172
|
+
|
|
173
|
+
PreRead --> Serializer["Hook Output Serializer<br/><small>(relay.ts::serializeOutput)</small>"]:::router
|
|
174
|
+
PreBash --> Serializer
|
|
175
|
+
PreCompact --> Serializer
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### B. Indexer, Parser & Worker Subsystem
|
|
179
|
+
|
|
180
|
+
```mermaid
|
|
181
|
+
flowchart TB
|
|
182
|
+
classDef parser fill:#059669,stroke:#047857,color:#ffffff,font-weight:bold;
|
|
183
|
+
classDef worker fill:#0D9488,stroke:#115E59,color:#ffffff;
|
|
184
|
+
classDef adapter fill:#10B981,stroke:#047857,color:#ffffff;
|
|
185
|
+
classDef db fill:#0284C7,stroke:#0369A1,color:#ffffff;
|
|
186
|
+
|
|
187
|
+
DirtyQueue["queue/dirty.txt"] --> WorkerLoop["Worker Loop (worker.ts)<br/><small>Polls every 2000ms</small>"]:::worker
|
|
188
|
+
WorkerLoop --> Fingerprint["fingerprintFile()<br/><small>Fast SHA check skips unchanged</small>"]:::worker
|
|
189
|
+
Fingerprint --> Parser["Parser Engine (parser.ts)<br/><small>detectLanguage() & parseContent()</small>"]:::parser
|
|
190
|
+
|
|
191
|
+
subgraph ParserAdapters ["Parser Language Adapters"]
|
|
192
|
+
TreeSitter["Inline Tree-Sitter Extractors<br/><small>TS, JS, Python, Go, Rust, Java, C/C++, Ruby</small>"]:::adapter
|
|
193
|
+
RegexInline["Inline Regex Extractors<br/><small>Markdown, JSON, YAML, TOML, CSS, Dockerfile</small>"]:::adapter
|
|
194
|
+
LangAdapters["src/languages/ Adapters (28 languages)<br/><small>C#, PHP, Kotlin, GraphQL, SQL, Proto, Apex, etc.</small>"]:::adapter
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
Parser --> TreeSitter
|
|
198
|
+
Parser --> RegexInline
|
|
199
|
+
Parser --> LangAdapters
|
|
200
|
+
|
|
201
|
+
TreeSitter --> ChokePoint["writeParseResult Choke Point<br/><small>Single atomic transaction<br/>MAX_SYMBOL_BODY_CHARS bound</small>"]:::parser
|
|
202
|
+
RegexInline --> ChokePoint
|
|
203
|
+
LangAdapters --> ChokePoint
|
|
204
|
+
|
|
205
|
+
ChokePoint --> GlobalDB[("global.db<br/><small>files, symbols, refs, chunks</small>")]:::db
|
|
206
|
+
|
|
207
|
+
Parser -.-> Embedder["Embedding Pipeline (embeddings.ts)<br/><small>Xenova/bge-small-en-v1.5 (384d)</small>"]:::parser
|
|
208
|
+
Embedder -.-> VecDB[("chunk_vectors (vec0 KNN)")]:::db
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 4. Dynamic Execution Flows & Lifecycle Scenarios (C4 Level 4)
|
|
214
|
+
|
|
215
|
+
### Scenario 1: Pre-Tool / Post-Tool Hook Execution Flow
|
|
216
|
+
|
|
217
|
+
```mermaid
|
|
218
|
+
sequenceDiagram
|
|
219
|
+
autonumber
|
|
220
|
+
participant Agent as AI Agent (Claude Code)
|
|
221
|
+
participant Shim as token-goat-shim.js
|
|
222
|
+
participant Relay as relay.ts / hook_registry.ts
|
|
223
|
+
participant Filter as ToolFilter (tool_filters/)
|
|
224
|
+
participant Store as session_store.ts & disk_cache.ts
|
|
225
|
+
|
|
226
|
+
Agent->>Shim: Invokes PreToolUse (e.g. tool=Bash, cmd="npx vitest run")
|
|
227
|
+
Shim->>Relay: In-process relayInProcess(payload)
|
|
228
|
+
Relay->>Store: loadSessionState(sessionId)
|
|
229
|
+
Relay->>Filter: Match command -> VitestFilter
|
|
230
|
+
Filter-->>Relay: Rewrite to "token-goat compress -f vitest -c 'npx vitest run'"
|
|
231
|
+
Relay-->>Agent: Return updatedInput with wrapped command
|
|
232
|
+
|
|
233
|
+
Agent->>Agent: Executes wrapped command in subprocess
|
|
234
|
+
Note over Agent: Subprocess applies VitestFilter, strips progress,<br/>collapses passing tests, captures stderr on failure
|
|
235
|
+
|
|
236
|
+
Agent->>Shim: Invokes PostToolUse (tool=Bash, output=...)
|
|
237
|
+
Shim->>Relay: relayInProcess(postPayload)
|
|
238
|
+
Relay->>Store: storeBashOutput(id, rawOutput) & recordBashOutput(cmd, id)
|
|
239
|
+
Relay->>Store: saveSessionState(sessionId) [Merge-on-save]
|
|
240
|
+
Relay-->>Agent: Return { continue: true }
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Scenario 2: Surgical Read vs Full Read Resolution
|
|
244
|
+
|
|
245
|
+
```mermaid
|
|
246
|
+
sequenceDiagram
|
|
247
|
+
autonumber
|
|
248
|
+
participant Agent as AI Coding Agent
|
|
249
|
+
participant CLI as token-goat read / MCP tool
|
|
250
|
+
participant Resolver as paths.ts::resolveIndexPath
|
|
251
|
+
participant Reader as index_reader.ts
|
|
252
|
+
participant DB as global.db (SQLite)
|
|
253
|
+
participant FS as Local Source File
|
|
254
|
+
|
|
255
|
+
Agent->>CLI: Request "src/parser.ts::writeParseResult"
|
|
256
|
+
CLI->>Resolver: resolveIndexPath("src/parser.ts")
|
|
257
|
+
Resolver-->>CLI: "c:/projects/token-goat/src/parser.ts" (canonical key)
|
|
258
|
+
CLI->>Reader: querySymbols(file="c:/...", name="writeParseResult")
|
|
259
|
+
Reader->>DB: SELECT * FROM symbols WHERE file_path=? AND name=?
|
|
260
|
+
DB-->>Reader: { line_start: 2420, line_end: 2473, body: "" (elided) }
|
|
261
|
+
Reader->>FS: Slices lines 2420-2473 dynamically (resolveBody)
|
|
262
|
+
FS-->>Reader: Exact function source code
|
|
263
|
+
Reader-->>Agent: Returns 54 lines (vs 2,800 lines of parser.ts)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Scenario 3: Untrusted Web/Document Ingestion & Injection Fencing
|
|
267
|
+
|
|
268
|
+
```mermaid
|
|
269
|
+
sequenceDiagram
|
|
270
|
+
autonumber
|
|
271
|
+
participant Agent as AI Agent
|
|
272
|
+
participant WebFetch as hooks_fetch.ts / webfetch.ts
|
|
273
|
+
participant Scanner as injection_scan.ts
|
|
274
|
+
participant Redactor as secret_redact.ts
|
|
275
|
+
participant Cache as web_outputs/{id}.json
|
|
276
|
+
|
|
277
|
+
Agent->>WebFetch: Fetches external doc/URL
|
|
278
|
+
WebFetch->>Scanner: scanForInjectionPatterns(content)
|
|
279
|
+
alt Patterns Matched
|
|
280
|
+
Scanner-->>WebFetch: Match: "Ignore previous instructions", "System Prompt Override", etc.
|
|
281
|
+
WebFetch->>WebFetch: Notice names the matched patterns
|
|
282
|
+
else No Pattern Matched
|
|
283
|
+
Scanner-->>WebFetch: No match
|
|
284
|
+
WebFetch->>WebFetch: Notice says content is untrusted, names nothing
|
|
285
|
+
end
|
|
286
|
+
WebFetch->>WebFetch: Wrap in the surface's untrusted fence either way
|
|
287
|
+
WebFetch->>Redactor: Redact sensitive tokens (Bearer, AWS keys, JWTs)
|
|
288
|
+
Redactor->>Cache: Persist to content-addressed cache
|
|
289
|
+
WebFetch-->>Agent: Returns safe, token-compressed extract
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 5. Security & Threat Model Summary Matrix
|
|
295
|
+
|
|
296
|
+
| Threat / Attack Surface | Risk | Token-Goat Architectural Mitigation |
|
|
297
|
+
|-------------------------|------|-------------------------------------|
|
|
298
|
+
| **Prompt Injection via Web/Docs** | Malicious instructions in external pages hijack the AI agent. | `untrusted_fence.ts` wraps every ingested external payload in a per-surface fence (`untrusted-web-content`, `untrusted-file-content`, `untrusted-tool-output`, `untrusted-github-content`) whether or not anything matched, so a novel phrasing gets no unmarked channel; `injection_scan.ts` names what it found in the notice and records it. Enforces that documents are reference data, not directives. |
|
|
299
|
+
| **Path Traversal / UNC Attacks** | User-controlled paths escaping project directory via `..`, Windows drive letters, or NTFS streams. | `paths.ts::safeJoin()` unconditionally rejects components with colons (`:`); `normalizePath()` canonicalizes separators and drive letters; `resolveIndexPath()` strictly anchors keys. |
|
|
300
|
+
| **SQLite Lock Contention / DoS** | Concurrent hooks & background indexer stalling agent execution. | `db.ts` enforces `busy_timeout=15000ms`, `WAL` journal mode, and fast in-memory retry. Single write transactions prevent reader starvation. |
|
|
301
|
+
| **Index Database Bloat / Amplification** | Huge files or minified JSON inflating DB to gigabytes. | Choke point `writeParseResult` strictly caps symbol body at `MAX_SYMBOL_BODY_CHARS`; over-cap bodies are elided (`''`) and sliced from disk on demand. Amplification guard ensures stored bytes $\le 4\times$ source size. |
|
|
302
|
+
| **Secret Exfiltration in Cache** | API keys or tokens in command stdout persisted to disk. | `secret_redact.ts` sanitizes known credential patterns before storing to `bash_outputs/` and `web_outputs/`. Blobs have strict 24-hour TTL and user-only file permissions. |
|
|
303
|
+
| **Hook Subprocess Failure Impact** | Crash in Token-Goat blocking agent prompt workflow. | All hook handlers are wrapped in `failSoft()`. Any exception outputs to stderr and immediately emits `{ continue: true }` to allow unhindered agent execution. |
|