token-goat 2.9.6 → 2.9.9
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 +7 -4
- package/SECURITY.md +1 -1
- package/dist/{token-goat-chunk-3HJQR4OO.mjs → token-goat-chunk-BYDDWQ2T.mjs} +200 -32
- package/dist/{token-goat-chunk-3ZAMYHQG.mjs → token-goat-chunk-DRH4CVGF.mjs} +5 -5
- package/dist/{token-goat-chunk-TX64YIIV.mjs → token-goat-chunk-K5GKX6ND.mjs} +5 -5
- package/dist/{token-goat-chunk-T2OE7MYM.mjs → token-goat-chunk-KIEOFWLL.mjs} +2794 -2760
- package/dist/{token-goat-chunk-46VKOCUH.mjs → token-goat-chunk-L6Q2XM5X.mjs} +2 -2
- package/dist/{token-goat-chunk-C7LLUVUT.mjs → token-goat-chunk-LQ3SGWIW.mjs} +3 -3
- package/dist/{token-goat-chunk-LXIC7MTW.mjs → token-goat-chunk-OBDTBOQA.mjs} +1 -1
- package/dist/{token-goat-chunk-EIZYCVBM.mjs → token-goat-chunk-P5SQX2VK.mjs} +408 -84
- package/dist/{token-goat-chunk-J7LGMKF3.mjs → token-goat-chunk-PNMGVJ4C.mjs} +44 -9
- package/dist/{token-goat-chunk-7GJBID7S.mjs → token-goat-chunk-QCHD2ENV.mjs} +5 -3
- package/dist/{token-goat-chunk-UJORWTI4.mjs → token-goat-chunk-WBPVYAEO.mjs} +2 -2
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/docs/install.md +62 -0
- package/docs/security.md +2 -2
- package/package.json +1 -1
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
runZipRead,
|
|
87
87
|
symbolNamesInFile,
|
|
88
88
|
upsertNote
|
|
89
|
-
} from "./token-goat-chunk-
|
|
89
|
+
} from "./token-goat-chunk-L6Q2XM5X.mjs";
|
|
90
90
|
import {
|
|
91
91
|
DEFAULT_RECONCILE_BUDGET_MS,
|
|
92
92
|
GEMINI_TOOL_NAME_MAP,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
runReconcile,
|
|
109
109
|
storeWebOutput,
|
|
110
110
|
summarizeResidentContext
|
|
111
|
-
} from "./token-goat-chunk-
|
|
111
|
+
} from "./token-goat-chunk-QCHD2ENV.mjs";
|
|
112
112
|
import {
|
|
113
113
|
AGENT_SALT_MARKER,
|
|
114
114
|
BASH_OUTPUT_SUBDIR,
|
|
@@ -190,6 +190,7 @@ import {
|
|
|
190
190
|
isParseSkipEligible,
|
|
191
191
|
isRecallCacheType,
|
|
192
192
|
isSuppressionCategory,
|
|
193
|
+
isTreeSitterAvailable,
|
|
193
194
|
isWorkerRunning,
|
|
194
195
|
listBlobs,
|
|
195
196
|
listOutputs,
|
|
@@ -230,12 +231,14 @@ import {
|
|
|
230
231
|
storeCompact,
|
|
231
232
|
storeOutput,
|
|
232
233
|
tokenGoatHome,
|
|
234
|
+
treeSitterCoreAvailable,
|
|
235
|
+
treeSitterCoreLoadError,
|
|
233
236
|
trimToBudget,
|
|
234
237
|
urlPolicyDenialReason,
|
|
235
238
|
visionTokensSavedByText,
|
|
236
239
|
walkProject,
|
|
237
240
|
writeCompact
|
|
238
|
-
} from "./token-goat-chunk-
|
|
241
|
+
} from "./token-goat-chunk-BYDDWQ2T.mjs";
|
|
239
242
|
import {
|
|
240
243
|
C,
|
|
241
244
|
CONFIG_KEY_ENV_OVERRIDES,
|
|
@@ -346,6 +349,7 @@ import {
|
|
|
346
349
|
savedTokensFromBytes,
|
|
347
350
|
sleepSync,
|
|
348
351
|
stripAnsi,
|
|
352
|
+
stripLower,
|
|
349
353
|
stripOwnHooksFromMap,
|
|
350
354
|
stripStaleGroupHooks,
|
|
351
355
|
suggestPackageNames,
|
|
@@ -368,7 +372,7 @@ import {
|
|
|
368
372
|
withFileLock,
|
|
369
373
|
withRetryOnLock,
|
|
370
374
|
writeJsonSettings
|
|
371
|
-
} from "./token-goat-chunk-
|
|
375
|
+
} from "./token-goat-chunk-KIEOFWLL.mjs";
|
|
372
376
|
import {
|
|
373
377
|
__export,
|
|
374
378
|
init_define_import_meta_env
|
|
@@ -6313,6 +6317,29 @@ function checkTsCompiler() {
|
|
|
6313
6317
|
message: err2 !== null ? `unavailable: ${extractErrorMessage(err2)}` : "unavailable (not attempted)"
|
|
6314
6318
|
};
|
|
6315
6319
|
}
|
|
6320
|
+
var TREE_SITTER_LANGUAGES = ["typescript", "javascript", "python", "go", "rust", "ruby", "java", "c", "cpp"];
|
|
6321
|
+
function checkTreeSitter() {
|
|
6322
|
+
const name = "Tree-sitter";
|
|
6323
|
+
if (!treeSitterCoreAvailable()) {
|
|
6324
|
+
const err2 = treeSitterCoreLoadError();
|
|
6325
|
+
const cause = err2 !== null ? `: ${extractErrorMessage(err2)}` : " (not attempted)";
|
|
6326
|
+
return {
|
|
6327
|
+
name,
|
|
6328
|
+
status: "warn",
|
|
6329
|
+
message: "unavailable -- the source skeleton fold, the code body fold's disk-parse fallback, and tree-sitter indexing are all disabled; likely cause: the optional native dependency `tree-sitter` is not installed, or is not resolvable from the bundle's location" + cause
|
|
6330
|
+
};
|
|
6331
|
+
}
|
|
6332
|
+
const available = TREE_SITTER_LANGUAGES.filter((lang) => isTreeSitterAvailable(lang));
|
|
6333
|
+
if (available.length === TREE_SITTER_LANGUAGES.length) {
|
|
6334
|
+
return { name, status: "ok", message: `available (${available.length}/${TREE_SITTER_LANGUAGES.length} grammars)` };
|
|
6335
|
+
}
|
|
6336
|
+
const missing = TREE_SITTER_LANGUAGES.filter((lang) => !available.includes(lang));
|
|
6337
|
+
return {
|
|
6338
|
+
name,
|
|
6339
|
+
status: "warn",
|
|
6340
|
+
message: `partially available (${available.length}/${TREE_SITTER_LANGUAGES.length} grammars); missing: ${missing.join(", ")}`
|
|
6341
|
+
};
|
|
6342
|
+
}
|
|
6316
6343
|
function checkEmbeddings(config) {
|
|
6317
6344
|
const name = "Embeddings";
|
|
6318
6345
|
if ((config.indexing?.embeddings_enabled ?? true) === false) {
|
|
@@ -6688,6 +6715,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
|
|
|
6688
6715
|
const actualDataDir = dataDir2 || dataDir();
|
|
6689
6716
|
results.push(checkInstall());
|
|
6690
6717
|
results.push(checkTsCompiler());
|
|
6718
|
+
results.push(checkTreeSitter());
|
|
6691
6719
|
results.push(checkStrayClaudeMdBlocks());
|
|
6692
6720
|
results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
|
|
6693
6721
|
results.push(checkDbExists(actualDataDir));
|
|
@@ -14937,11 +14965,14 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
|
|
|
14937
14965
|
function discoverMcpConfig(projectRoot, home) {
|
|
14938
14966
|
const mcpJsonPath = path28.join(projectRoot, ".mcp.json");
|
|
14939
14967
|
const claudeJsonPath = path28.join(home, ".claude.json");
|
|
14940
|
-
const
|
|
14968
|
+
const copilotJsonPath = path28.join(home, ".copilot", "mcp-config.json");
|
|
14969
|
+
const sourcesChecked = [mcpJsonPath, claudeJsonPath, copilotJsonPath];
|
|
14941
14970
|
const fromMcpJson = readMcpJsonFile(mcpJsonPath);
|
|
14942
14971
|
if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
|
|
14943
14972
|
const fromClaudeJson = readClaudeJsonConfig(claudeJsonPath, projectRoot);
|
|
14944
14973
|
if (fromClaudeJson !== null) return { servers: fromClaudeJson, sourcePath: claudeJsonPath, sourcesChecked };
|
|
14974
|
+
const fromCopilotJson = readMcpJsonFile(copilotJsonPath);
|
|
14975
|
+
if (fromCopilotJson !== null) return { servers: fromCopilotJson, sourcePath: copilotJsonPath, sourcesChecked };
|
|
14945
14976
|
return { servers: null, sourcePath: null, sourcesChecked };
|
|
14946
14977
|
}
|
|
14947
14978
|
function analyzeMcpCache() {
|
|
@@ -15730,7 +15761,7 @@ async function cmdMcpServe() {
|
|
|
15730
15761
|
let StdioServerTransport;
|
|
15731
15762
|
try {
|
|
15732
15763
|
;
|
|
15733
|
-
({ createMcpServer } = await import("./token-goat-chunk-
|
|
15764
|
+
({ createMcpServer } = await import("./token-goat-chunk-LQ3SGWIW.mjs"));
|
|
15734
15765
|
({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
|
|
15735
15766
|
} catch (err2) {
|
|
15736
15767
|
process.stderr.write(
|
|
@@ -15755,7 +15786,7 @@ async function cmdHook(event, opts) {
|
|
|
15755
15786
|
if (typeof opts.harness === "string" && opts.harness.length > 0) {
|
|
15756
15787
|
process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
|
|
15757
15788
|
}
|
|
15758
|
-
const { relay } = await import("./token-goat-chunk-
|
|
15789
|
+
const { relay } = await import("./token-goat-chunk-K5GKX6ND.mjs");
|
|
15759
15790
|
await relay(event);
|
|
15760
15791
|
}
|
|
15761
15792
|
async function cmdInstall(opts) {
|
|
@@ -16688,7 +16719,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
|
|
|
16688
16719
|
}
|
|
16689
16720
|
async function cmdCompress(opts) {
|
|
16690
16721
|
try {
|
|
16691
|
-
const bashRunner = await import("./token-goat-chunk-
|
|
16722
|
+
const bashRunner = await import("./token-goat-chunk-WBPVYAEO.mjs");
|
|
16692
16723
|
if (opts.compress === false) {
|
|
16693
16724
|
process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
|
|
16694
16725
|
return;
|
|
@@ -16961,8 +16992,12 @@ async function cmdSkillSection(nameHeading, headingArg) {
|
|
|
16961
16992
|
const body = decodeSource(fs29.readFileSync(filePath));
|
|
16962
16993
|
const extracted = extractNamedSection(body, heading);
|
|
16963
16994
|
if (!extracted) {
|
|
16964
|
-
const messages = [`Section '${heading}' not found in skill '${skillName}'`];
|
|
16965
16995
|
const allHeadings = listSections(filePath);
|
|
16996
|
+
const wanted = stripLower(heading);
|
|
16997
|
+
if (allHeadings.some((h) => stripLower(h) === wanted)) {
|
|
16998
|
+
throw new CliError(`Section '${heading}' in skill '${skillName}' is present but empty`);
|
|
16999
|
+
}
|
|
17000
|
+
const messages = [`Section '${heading}' not found in skill '${skillName}'`];
|
|
16966
17001
|
const available = filterSimilarHeadings(allHeadings, heading);
|
|
16967
17002
|
if (available.length > 0) messages.push(didYouMean(available));
|
|
16968
17003
|
else if (allHeadings.length === 0) messages.push(`skill '${skillName}' has no headings`);
|
|
@@ -9,11 +9,12 @@ import {
|
|
|
9
9
|
isBlobStale,
|
|
10
10
|
loadBlob,
|
|
11
11
|
storeBlob
|
|
12
|
-
} from "./token-goat-chunk-
|
|
12
|
+
} from "./token-goat-chunk-BYDDWQ2T.mjs";
|
|
13
13
|
import {
|
|
14
14
|
SYMBOL_BODY_CHAR_CAP,
|
|
15
15
|
copilotCliMcpToolsDir,
|
|
16
16
|
countNoun,
|
|
17
|
+
displaySafeText,
|
|
17
18
|
extractErrorMessage,
|
|
18
19
|
fingerprintFile,
|
|
19
20
|
foldPath,
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
resolveIndexPath,
|
|
25
26
|
shortFingerprint,
|
|
26
27
|
toDisplayPath
|
|
27
|
-
} from "./token-goat-chunk-
|
|
28
|
+
} from "./token-goat-chunk-KIEOFWLL.mjs";
|
|
28
29
|
import {
|
|
29
30
|
registerReset
|
|
30
31
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -840,8 +841,9 @@ function repeatedSkillBodyHint(injections) {
|
|
|
840
841
|
const worst = injections[0];
|
|
841
842
|
if (worst === void 0) return null;
|
|
842
843
|
if (worst.count < SKILL_BODY_REPEAT_THRESHOLD || worst.bytes < LARGE_SKILL_BODY_BYTES) return null;
|
|
844
|
+
const skill = displaySafeText(worst.skill);
|
|
843
845
|
const tokens = formatTokenEstimate(estimateTokensFromLength(worst.bytes));
|
|
844
|
-
return `The \`${
|
|
846
|
+
return `The \`${skill}\` skill body has been injected ${worst.count} times this session (${formatBytes(worst.bytes)} total, ~${tokens} tok est). Slash expansion and the Skill tool both send the whole body every time, and no hook can intercept either. If it is already loaded, work from it instead of re-invoking; to reread one part, use \`token-goat skill-section ${skill} '<heading>'\`.`;
|
|
845
847
|
}
|
|
846
848
|
function readTranscriptTail(transcriptPath, maxBytes = RESIDENT_TAIL_MAX_BYTES) {
|
|
847
849
|
let fd = null;
|
|
@@ -3,7 +3,7 @@ const require = __cjsRequire(import.meta.url);
|
|
|
3
3
|
import {
|
|
4
4
|
deliveredOutputBytes,
|
|
5
5
|
wrappedShell
|
|
6
|
-
} from "./token-goat-chunk-
|
|
6
|
+
} from "./token-goat-chunk-OBDTBOQA.mjs";
|
|
7
7
|
import {
|
|
8
8
|
ToolFilter,
|
|
9
9
|
capTokens,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
recordStat,
|
|
16
16
|
selectFilter,
|
|
17
17
|
shlexSplit
|
|
18
|
-
} from "./token-goat-chunk-
|
|
18
|
+
} from "./token-goat-chunk-KIEOFWLL.mjs";
|
|
19
19
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
20
20
|
import {
|
|
21
21
|
init_define_import_meta_env
|
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-P5SQX2VK.mjs";
|
|
6
|
+
import "./token-goat-chunk-QCHD2ENV.mjs";
|
|
7
|
+
import "./token-goat-chunk-BYDDWQ2T.mjs";
|
|
8
|
+
import "./token-goat-chunk-OBDTBOQA.mjs";
|
|
9
|
+
import "./token-goat-chunk-KIEOFWLL.mjs";
|
|
10
10
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
11
11
|
import {
|
|
12
12
|
init_define_import_meta_env
|
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-PNMGVJ4C.mjs";
|
|
6
|
+
import "./token-goat-chunk-L6Q2XM5X.mjs";
|
|
7
|
+
import "./token-goat-chunk-QCHD2ENV.mjs";
|
|
8
|
+
import "./token-goat-chunk-BYDDWQ2T.mjs";
|
|
9
9
|
import {
|
|
10
10
|
installEpipeGuard
|
|
11
|
-
} from "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-KIEOFWLL.mjs";
|
|
12
12
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
13
13
|
import {
|
|
14
14
|
init_define_import_meta_env
|
package/docs/install.md
CHANGED
|
@@ -223,6 +223,68 @@ To upgrade cleanly:
|
|
|
223
223
|
|
|
224
224
|
**AI agents handling an upgrade:** complete all three steps in sequence. After step 3, confirm no `token_goat` entries remain in `settings.json` before reporting the upgrade done.
|
|
225
225
|
|
|
226
|
+
### Command auto-approval and permissions
|
|
227
|
+
|
|
228
|
+
By default, `token-goat install` configures hook triggers only. It never writes permission entries into your environment. If your setup requires confirmation for shell commands, an approval prompt can appear whenever file names, line numbers, or arguments change.
|
|
229
|
+
|
|
230
|
+
You can add wildcard rules to avoid repeated prompts for routine queries:
|
|
231
|
+
|
|
232
|
+
#### Claude Code (`~/.claude/settings.json`)
|
|
233
|
+
|
|
234
|
+
To allow all `token-goat` commands without prompting:
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"permissions": {
|
|
239
|
+
"allow": [
|
|
240
|
+
"Bash(token-goat *)",
|
|
241
|
+
"Bash(token-goat:*)"
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
To limit auto-approval strictly to read-only queries:
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"permissions": {
|
|
252
|
+
"allow": [
|
|
253
|
+
"Bash(token-goat read *)",
|
|
254
|
+
"Bash(token-goat symbol *)",
|
|
255
|
+
"Bash(token-goat section *)",
|
|
256
|
+
"Bash(token-goat outline *)",
|
|
257
|
+
"Bash(token-goat skeleton *)",
|
|
258
|
+
"Bash(token-goat brief *)",
|
|
259
|
+
"Bash(token-goat json-query *)",
|
|
260
|
+
"Bash(token-goat yaml-query *)",
|
|
261
|
+
"Bash(token-goat xml-query *)",
|
|
262
|
+
"Bash(token-goat map *)",
|
|
263
|
+
"Bash(token-goat refs *)",
|
|
264
|
+
"Bash(token-goat deps *)"
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### Codex CLI (`~/.codex/config.toml`)
|
|
271
|
+
|
|
272
|
+
Running `token-goat install --codex` automatically computes and writes `trusted_hash` into `[hooks.state]` so Codex trusts the hooks. To allow automatic command execution, set `approval_policy` in `~/.codex/config.toml`:
|
|
273
|
+
|
|
274
|
+
```toml
|
|
275
|
+
approval_policy = "never"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### Copilot and VS Code MCP
|
|
279
|
+
|
|
280
|
+
For VS Code using the token-goat MCP server (`token-goat install --vscode`), enable MCP auto-approval in your VS Code `settings.json`:
|
|
281
|
+
|
|
282
|
+
```json
|
|
283
|
+
{
|
|
284
|
+
"chat.mcp.autoApprove": true
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
226
288
|
## What gets installed?
|
|
227
289
|
|
|
228
290
|
`token-goat install` writes the following on your machine — nothing else, anywhere. Every entry is reversed by `token-goat uninstall`. Integrations for other harnesses are additive on the way out as well as in, so a plain uninstall does not touch one you installed with `--codex`, `--copilot`, or a sibling flag: rather than undo something you did not ask about, it names each one still present and the flag that removes it. Run `token-goat doctor` at any time to see which of these are currently present.
|
package/docs/security.md
CHANGED
|
@@ -15,12 +15,12 @@ Outbound network is reserved to these explicit cases:
|
|
|
15
15
|
- Google Drive API calls, only if you already authorized Drive in Claude Code. Token-goat never prompts for its own auth.
|
|
16
16
|
- Image fetches from URLs: either explicit via `token-goat fetch-image <url>`, or when the AI agent issues a WebFetch call that returns image content — the hook intercepts and shrinks the image. The URL always originates from the agent's work, not from token-goat itself.
|
|
17
17
|
- `token-goat screenshot <url>` navigates a headless browser to the URL you give it, subject to the target restrictions described below.
|
|
18
|
-
- The first `token-goat semantic` run on a machine downloads the embedding model from `huggingface.co`, pinned to an immutable commit rather than a mutable branch and checked against a recorded SHA-256 and byte length before it is used, and only once `onnxruntime-node` has been installed (see below — it is not part of a default install). Subsequent runs use the local cache, re-verify it, and make no network call. Skip the download entirely by setting `indexing.embeddings_enabled = false` (it is on by default), in which case `semantic` falls back to full-text search.
|
|
18
|
+
- The first `token-goat semantic` run on a machine downloads the embedding model from `huggingface.co`, pinned to an immutable commit rather than a mutable branch and checked against a recorded SHA-256 and byte length before it is used, and only once `onnxruntime-node` has been installed (see below — it is not part of a default install). Subsequent runs use the local cache, re-verify it, and make no network call. Setting `TOKEN_GOAT_MODEL_CACHE_DIR` adds one more place to look before the network: a directory you name that outlives any single data root, which is how a continuous-integration job stops refetching the weights once per worker. A file taken from there is checked against the same recorded digest as a downloaded one and dropped from the shared directory if it fails, so a tampered copy costs a download and cannot substitute different weights. It is an environment variable only, so no project config file can point it anywhere. Skip the download entirely by setting `indexing.embeddings_enabled = false` (it is on by default), in which case `semantic` falls back to full-text search.
|
|
19
19
|
- The first optical-character read of an image downloads the English language data (about 4 MB) from `cdn.jsdelivr.net`, at a fixed version path. Subsequent reads use the local cache. This happens for an explicit `token-goat image-text`, and also for the automatic text extraction the image-shrink hook performs when the agent reads a screenshot; turn the automatic one off with `image_shrink.ocr_enabled = false`.
|
|
20
20
|
|
|
21
21
|
**One switch for all of it.** Set `network.offline = true` (env `TOKEN_GOAT_OFFLINE`) and every one of the paths above refuses instead of connecting, saying so rather than failing quietly. Anything already cached keeps working: a machine that has the embedding model still runs `semantic`, and one that has the language data still reads text out of images. This is one of the settings a per-project config file may not touch, so cloning a repository cannot switch it back off.
|
|
22
22
|
|
|
23
|
-
**A repository cannot reconfigure the security controls.** A project-root `.token-goat.toml` layers on top of your global config, which is what it is for: hint thresholds, indexing settings, compression tuning. But that file arrives with the repository, so whoever wrote the repository wrote it. Seven whole sections are therefore off limits to it, and come from your global config or the environment only: `injection` (prompt-injection fencing), `webfetch` (the fetch allow and deny lists), `gdrive` (the Google Drive integration), `mcp` (root confinement and the allowed-roots list), `network` (offline mode), `redaction` (the secret-redaction rules), and `screenshot` (the headless browser).
|
|
23
|
+
**A repository cannot reconfigure the security controls.** A project-root `.token-goat.toml` layers on top of your global config, which is what it is for: hint thresholds, indexing settings, compression tuning. But that file arrives with the repository, so whoever wrote the repository wrote it. Seven whole sections are therefore off limits to it, and come from your global config or the environment only: `injection` (prompt-injection fencing), `webfetch` (the fetch allow and deny lists), `gdrive` (the Google Drive integration), `mcp` (root confinement and the allowed-roots list), `network` (offline mode), `redaction` (the secret-redaction rules), and `screenshot` (the headless browser). Thirteen individual keys inside otherwise-overridable sections are locked the same way. Five decide what gets indexed at all: `indexing.cross_project_symbols`, `indexing.skip_dirs`, `indexing.skip_files`, `indexing.large_file_skip_kb` and `indexing.large_file_symbol_only_kb`, which matter because an unindexed file answers `symbol`, `read` and `semantic` in the same words a name that never existed does. Five more are the switches that decide whether a large file arrives folded rather than whole: `hints.fold_code_bodies`, `hints.fold_comment_blocks`, `hints.fold_prose_paragraphs`, `hints.outline_large_documents` and `hints.skeleton_large_sources`. The last three are `image_shrink.max_image_pixels`, the decompression-bomb cap, `worker.blocked_roots`, the folders you have kept out of the index, and `compact_assist.summary_budget_chars`, the length target token-goat hands to whoever writes your compaction summary, which a repository must not be able to set low enough to erase the session. A project file that sets one of them is ignored, and token-goat prints a line naming what it dropped. Everything else stays project-overridable.
|
|
24
24
|
|
|
25
25
|
**The lock covers the config file, not the environment.** These settings still read a `TOKEN_GOAT_*` environment variable, and a repository has ways to set one: a `.envrc` for direnv, a `terminal.integrated.env.*` block in a committed `.vscode/settings.json`, a `containerEnv` entry in a devcontainer. Refusing environment overrides would break the operator who exports a variable in their own shell, which is the legitimate case and the common one, so token-goat reports instead of refusing: `token-goat doctor` prints a `Security config overrides` line naming every locked security setting the environment is currently deciding, and the variable to unset. It covers all twenty locked keys that read an environment variable, and it derives that set from the same two tables that define what a project config may not write, rather than from a list kept alongside them. Settings with a safe side (booleans) are reported when the environment holds them open; settings without one (lists, sizes) are reported whenever the environment supplies a value at all, since there is nothing to compare against. A default install, where nothing is set, prints a single ok line.
|
|
26
26
|
|