token-goat 2.9.8 → 2.9.10
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 +2 -2
- package/SECURITY.md +1 -1
- package/dist/{token-goat-chunk-Q4VCAIBR.mjs → token-goat-chunk-2TJBY5RD.mjs} +5 -5
- package/dist/{token-goat-chunk-2SBHUPBF.mjs → token-goat-chunk-7ALFFPJW.mjs} +7 -7
- package/dist/{token-goat-chunk-REHUP2OE.mjs → token-goat-chunk-ATVQ6IIT.mjs} +1 -1
- package/dist/{token-goat-chunk-EZZ7IJYM.mjs → token-goat-chunk-DCYHAUW3.mjs} +3 -3
- package/dist/{token-goat-chunk-3YANZKER.mjs → token-goat-chunk-DSHYDU53.mjs} +5 -5
- package/dist/{token-goat-chunk-WT5OLTT6.mjs → token-goat-chunk-JS44SRAX.mjs} +2 -2
- package/dist/{token-goat-chunk-TXZY7C24.mjs → token-goat-chunk-PA5UXNOT.mjs} +100 -14
- package/dist/{token-goat-chunk-VEEHPNBV.mjs → token-goat-chunk-SLGOIXKF.mjs} +2 -2
- package/dist/{token-goat-chunk-JZYJH76S.mjs → token-goat-chunk-VCEK77ED.mjs} +20 -1
- package/dist/{token-goat-chunk-6RAUVAKW.mjs → token-goat-chunk-YFXFUG3L.mjs} +2 -2
- package/dist/{token-goat-chunk-XYLPMQ6I.mjs → token-goat-chunk-ZQODJQTP.mjs} +81 -7
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/docs/security.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Restart your AI sessions. Run `token-goat stats` a couple of minutes after your
|
|
|
37
37
|
|
|
38
38
|
> **Built and continually improved, free, by one person. If it saves you tokens, drop a ⭐️ at the top of this page. One click. Makes my day. Also, if you'd like anything added, [drop me a line](mailto:token-goat@dfkhelper.com).**
|
|
39
39
|
|
|
40
|
-
[Install](docs/install.md) · [CLI](docs/cli.md) · [What gets installed?](docs/install.md#what-gets-installed) · [Stats](#stats-display) · [Security & uninstall](docs/security.md)
|
|
40
|
+
[Install](docs/install.md) · [CLI](docs/cli.md) · [Architecture Q&A](docs/architecture-qa.html) · [What gets installed?](docs/install.md#what-gets-installed) · [Stats](#stats-display) · [Security & uninstall](docs/security.md)
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
@@ -323,7 +323,7 @@ corpus of captured shell output through the real compression path and reports ho
|
|
|
323
323
|
result got, alongside a count of the lines each case is not allowed to lose. Deleting output makes
|
|
324
324
|
the first number look better, so the second one is what keeps the first honest.
|
|
325
325
|
|
|
326
|
-
Every command, with its flags, its output, and the comparison workflows that chain them: **[CLI reference](docs/cli.md)**.
|
|
326
|
+
Every command, with its flags, its output, and the comparison workflows that chain them: **[CLI reference](docs/cli.md)**. For architectural analysis, empirical evaluations, and technical answers to common engineering questions: **[Architecture & Technical Q&A](docs/architecture-qa.html)**.
|
|
327
327
|
|
|
328
328
|
## MCP server
|
|
329
329
|
|
package/SECURITY.md
CHANGED
|
@@ -101,7 +101,7 @@ For a scanner that ingests a bill of materials rather than a lockfile, `npm run
|
|
|
101
101
|
|
|
102
102
|
## Verifying what you installed
|
|
103
103
|
|
|
104
|
-
Every published version is built and pushed by one workflow, [`.github/workflows/publish.yml`](.github/workflows/publish.yml), which runs only when a GitHub release is published (or manually, and then only from `main`). It publishes with npm provenance, so npm holds a signed attestation tying the tarball to the commit and workflow run that produced it. Nothing is ever published from a
|
|
104
|
+
Every published version is built and pushed by one workflow, [`.github/workflows/publish.yml`](.github/workflows/publish.yml), which runs only when a GitHub release is published (or manually, and then only from `main`). It publishes with npm provenance, so npm holds a signed attestation tying the tarball to the commit and workflow run that produced it. Nothing is ever published from a developer workstation.
|
|
105
105
|
|
|
106
106
|
To check a copy you already have:
|
|
107
107
|
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
buildEvent,
|
|
5
5
|
relay,
|
|
6
6
|
relayInProcess
|
|
7
|
-
} from "./token-goat-chunk-
|
|
7
|
+
} from "./token-goat-chunk-ZQODJQTP.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-SLGOIXKF.mjs";
|
|
12
|
+
import "./token-goat-chunk-PA5UXNOT.mjs";
|
|
13
|
+
import "./token-goat-chunk-ATVQ6IIT.mjs";
|
|
14
|
+
import "./token-goat-chunk-VCEK77ED.mjs";
|
|
15
15
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
16
16
|
import "./token-goat-chunk-A37V4PBF.mjs";
|
|
17
17
|
export {
|
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
runZipRead,
|
|
87
87
|
symbolNamesInFile,
|
|
88
88
|
upsertNote
|
|
89
|
-
} from "./token-goat-chunk-
|
|
89
|
+
} from "./token-goat-chunk-YFXFUG3L.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-SLGOIXKF.mjs";
|
|
112
112
|
import {
|
|
113
113
|
AGENT_SALT_MARKER,
|
|
114
114
|
BASH_OUTPUT_SUBDIR,
|
|
@@ -238,7 +238,7 @@ import {
|
|
|
238
238
|
visionTokensSavedByText,
|
|
239
239
|
walkProject,
|
|
240
240
|
writeCompact
|
|
241
|
-
} from "./token-goat-chunk-
|
|
241
|
+
} from "./token-goat-chunk-PA5UXNOT.mjs";
|
|
242
242
|
import {
|
|
243
243
|
C,
|
|
244
244
|
CONFIG_KEY_ENV_OVERRIDES,
|
|
@@ -372,7 +372,7 @@ import {
|
|
|
372
372
|
withFileLock,
|
|
373
373
|
withRetryOnLock,
|
|
374
374
|
writeJsonSettings
|
|
375
|
-
} from "./token-goat-chunk-
|
|
375
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
376
376
|
import {
|
|
377
377
|
__export,
|
|
378
378
|
init_define_import_meta_env
|
|
@@ -15761,7 +15761,7 @@ async function cmdMcpServe() {
|
|
|
15761
15761
|
let StdioServerTransport;
|
|
15762
15762
|
try {
|
|
15763
15763
|
;
|
|
15764
|
-
({ createMcpServer } = await import("./token-goat-chunk-
|
|
15764
|
+
({ createMcpServer } = await import("./token-goat-chunk-DCYHAUW3.mjs"));
|
|
15765
15765
|
({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
|
|
15766
15766
|
} catch (err2) {
|
|
15767
15767
|
process.stderr.write(
|
|
@@ -15786,7 +15786,7 @@ async function cmdHook(event, opts) {
|
|
|
15786
15786
|
if (typeof opts.harness === "string" && opts.harness.length > 0) {
|
|
15787
15787
|
process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
|
|
15788
15788
|
}
|
|
15789
|
-
const { relay } = await import("./token-goat-chunk-
|
|
15789
|
+
const { relay } = await import("./token-goat-chunk-2TJBY5RD.mjs");
|
|
15790
15790
|
await relay(event);
|
|
15791
15791
|
}
|
|
15792
15792
|
async function cmdInstall(opts) {
|
|
@@ -16719,7 +16719,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
|
|
|
16719
16719
|
}
|
|
16720
16720
|
async function cmdCompress(opts) {
|
|
16721
16721
|
try {
|
|
16722
|
-
const bashRunner = await import("./token-goat-chunk-
|
|
16722
|
+
const bashRunner = await import("./token-goat-chunk-JS44SRAX.mjs");
|
|
16723
16723
|
if (opts.compress === false) {
|
|
16724
16724
|
process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
|
|
16725
16725
|
return;
|
|
@@ -2,7 +2,7 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
detectHarness
|
|
5
|
-
} from "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
6
6
|
import {
|
|
7
7
|
init_define_import_meta_env
|
|
8
8
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
runSkeleton,
|
|
26
26
|
runSymbol,
|
|
27
27
|
withPinnedReads
|
|
28
|
-
} from "./token-goat-chunk-
|
|
28
|
+
} from "./token-goat-chunk-YFXFUG3L.mjs";
|
|
29
29
|
import {
|
|
30
30
|
buildProjectMap,
|
|
31
31
|
embeddingsDepsAvailable,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
getProjectIndexCounts,
|
|
35
35
|
isWorkerRunning,
|
|
36
36
|
mapLookupBytesSaved
|
|
37
|
-
} from "./token-goat-chunk-
|
|
37
|
+
} from "./token-goat-chunk-PA5UXNOT.mjs";
|
|
38
38
|
import {
|
|
39
39
|
ENV_KEYS,
|
|
40
40
|
VERSION,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
recordStat,
|
|
49
49
|
resolveProjectRoot,
|
|
50
50
|
savedTokensFromBytes
|
|
51
|
-
} from "./token-goat-chunk-
|
|
51
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
52
52
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
53
53
|
import {
|
|
54
54
|
init_define_import_meta_env
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
expandGlobs,
|
|
8
8
|
leftoverIntegrations,
|
|
9
9
|
run
|
|
10
|
-
} from "./token-goat-chunk-
|
|
11
|
-
import "./token-goat-chunk-
|
|
12
|
-
import "./token-goat-chunk-
|
|
13
|
-
import "./token-goat-chunk-
|
|
14
|
-
import "./token-goat-chunk-
|
|
10
|
+
} from "./token-goat-chunk-7ALFFPJW.mjs";
|
|
11
|
+
import "./token-goat-chunk-YFXFUG3L.mjs";
|
|
12
|
+
import "./token-goat-chunk-SLGOIXKF.mjs";
|
|
13
|
+
import "./token-goat-chunk-PA5UXNOT.mjs";
|
|
14
|
+
import "./token-goat-chunk-VCEK77ED.mjs";
|
|
15
15
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
16
16
|
import "./token-goat-chunk-A37V4PBF.mjs";
|
|
17
17
|
export {
|
|
@@ -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-ATVQ6IIT.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-VCEK77ED.mjs";
|
|
19
19
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
20
20
|
import {
|
|
21
21
|
init_define_import_meta_env
|
|
@@ -101,7 +101,7 @@ import {
|
|
|
101
101
|
toDisplayPath,
|
|
102
102
|
toKB,
|
|
103
103
|
withFileLock
|
|
104
|
-
} from "./token-goat-chunk-
|
|
104
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
105
105
|
import {
|
|
106
106
|
registerReset
|
|
107
107
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -10268,6 +10268,51 @@ function modelDir() {
|
|
|
10268
10268
|
function downloadUrl(file) {
|
|
10269
10269
|
return `https://huggingface.co/${DEFAULT_MODEL}/resolve/${PINNED_MODEL_REVISION}/${file.name}`;
|
|
10270
10270
|
}
|
|
10271
|
+
function sharedModelCacheDir() {
|
|
10272
|
+
const raw = process.env["TOKEN_GOAT_MODEL_CACHE_DIR"]?.trim();
|
|
10273
|
+
if (!raw) return null;
|
|
10274
|
+
return path7.join(raw, ...DEFAULT_MODEL.split("/"), PINNED_MODEL_REVISION);
|
|
10275
|
+
}
|
|
10276
|
+
async function copyFromSharedCache(shared, file, target) {
|
|
10277
|
+
const source = path7.join(shared, file.name);
|
|
10278
|
+
const temp = `${target}.${process.pid}.shared`;
|
|
10279
|
+
try {
|
|
10280
|
+
const info = fs11.lstatSync(source, { throwIfNoEntry: false });
|
|
10281
|
+
if (!info?.isFile() || info.size !== file.bytes) return false;
|
|
10282
|
+
fs11.copyFileSync(source, temp);
|
|
10283
|
+
if (await sha256Of(temp) !== file.sha256) {
|
|
10284
|
+
fs11.rmSync(temp, { force: true, maxRetries: 20, retryDelay: 25 });
|
|
10285
|
+
fs11.rmSync(source, { force: true, maxRetries: 20, retryDelay: 25 });
|
|
10286
|
+
return false;
|
|
10287
|
+
}
|
|
10288
|
+
fs11.renameSync(temp, target);
|
|
10289
|
+
return true;
|
|
10290
|
+
} catch {
|
|
10291
|
+
try {
|
|
10292
|
+
fs11.rmSync(temp, { force: true, maxRetries: 20, retryDelay: 25 });
|
|
10293
|
+
} catch {
|
|
10294
|
+
}
|
|
10295
|
+
return false;
|
|
10296
|
+
}
|
|
10297
|
+
}
|
|
10298
|
+
function publishToSharedCache(shared, file, target) {
|
|
10299
|
+
const destination = path7.join(shared, file.name);
|
|
10300
|
+
const temp = `${destination}.${process.pid}.partial`;
|
|
10301
|
+
let created = false;
|
|
10302
|
+
try {
|
|
10303
|
+
if (fs11.existsSync(destination)) return;
|
|
10304
|
+
fs11.mkdirSync(path7.dirname(destination), { recursive: true });
|
|
10305
|
+
fs11.copyFileSync(target, temp, fs11.constants.COPYFILE_EXCL);
|
|
10306
|
+
created = true;
|
|
10307
|
+
fs11.renameSync(temp, destination);
|
|
10308
|
+
} catch {
|
|
10309
|
+
if (!created) return;
|
|
10310
|
+
try {
|
|
10311
|
+
fs11.rmSync(temp, { force: true, maxRetries: 20, retryDelay: 25 });
|
|
10312
|
+
} catch {
|
|
10313
|
+
}
|
|
10314
|
+
}
|
|
10315
|
+
}
|
|
10271
10316
|
var _ort = null;
|
|
10272
10317
|
var _ortError = null;
|
|
10273
10318
|
var _ortLoadAttempted = false;
|
|
@@ -10363,6 +10408,7 @@ async function ensureModelFiles(modelName = DEFAULT_MODEL) {
|
|
|
10363
10408
|
ensureDataDirPrivate();
|
|
10364
10409
|
const dir = modelDir();
|
|
10365
10410
|
const offline = loadConfig().network.offline;
|
|
10411
|
+
const shared = sharedModelCacheDir();
|
|
10366
10412
|
for (const file of MODEL_FILES) {
|
|
10367
10413
|
const target = path7.join(dir, file.name);
|
|
10368
10414
|
if (fs11.existsSync(target)) {
|
|
@@ -10370,6 +10416,10 @@ async function ensureModelFiles(modelName = DEFAULT_MODEL) {
|
|
|
10370
10416
|
if (digest === file.sha256) continue;
|
|
10371
10417
|
fs11.rmSync(target, { force: true });
|
|
10372
10418
|
}
|
|
10419
|
+
if (shared) {
|
|
10420
|
+
fs11.mkdirSync(path7.dirname(target), { recursive: true });
|
|
10421
|
+
if (await copyFromSharedCache(shared, file, target)) continue;
|
|
10422
|
+
}
|
|
10373
10423
|
if (offline) {
|
|
10374
10424
|
throw new Error(
|
|
10375
10425
|
`Offline mode is on (network.offline): refusing to download ${file.name} for the embedding model. Copy the pinned files into ${dir} on a connected machine to use semantic search here.`
|
|
@@ -10380,6 +10430,7 @@ async function ensureModelFiles(modelName = DEFAULT_MODEL) {
|
|
|
10380
10430
|
`Downloading the embedding model, once (${file.name}, ${Math.round(file.bytes / 1024 / 1024)} MB) into ${dir}`
|
|
10381
10431
|
);
|
|
10382
10432
|
await download(file, target);
|
|
10433
|
+
if (shared) publishToSharedCache(shared, file, target);
|
|
10383
10434
|
}
|
|
10384
10435
|
return dir;
|
|
10385
10436
|
}
|
|
@@ -20023,6 +20074,41 @@ function readIntToolInput(event, key) {
|
|
|
20023
20074
|
}
|
|
20024
20075
|
return void 0;
|
|
20025
20076
|
}
|
|
20077
|
+
function readRequestedSliceWindow(event) {
|
|
20078
|
+
const rawOffset = readIntToolInput(event, "offset");
|
|
20079
|
+
const rawLimit = readIntToolInput(event, "limit");
|
|
20080
|
+
if (rawOffset !== void 0 || rawLimit !== void 0) {
|
|
20081
|
+
return {
|
|
20082
|
+
offset: rawOffset !== void 0 && rawOffset >= 1 ? Math.floor(rawOffset) : 1,
|
|
20083
|
+
limit: rawLimit !== void 0 && rawLimit > 0 ? Math.floor(rawLimit) : void 0,
|
|
20084
|
+
isExplicitSlice: true
|
|
20085
|
+
};
|
|
20086
|
+
}
|
|
20087
|
+
const rawRange = event.toolInput["view_range"] ?? event.toolInput["lines"] ?? event.toolInput["range"];
|
|
20088
|
+
if (Array.isArray(rawRange) && rawRange.length >= 1) {
|
|
20089
|
+
const rawStart = Number(rawRange[0]);
|
|
20090
|
+
const startVal = Number.isFinite(rawStart) && rawStart >= 1 ? Math.floor(rawStart) : 1;
|
|
20091
|
+
if (rawRange.length >= 2) {
|
|
20092
|
+
const rawEnd = Number(rawRange[1]);
|
|
20093
|
+
if (rawEnd === -1) {
|
|
20094
|
+
return { offset: startVal, isExplicitSlice: true };
|
|
20095
|
+
}
|
|
20096
|
+
if (Number.isFinite(rawEnd) && rawEnd >= startVal) {
|
|
20097
|
+
const lineCount = Math.floor(rawEnd - startVal + 1);
|
|
20098
|
+
return { offset: startVal, limit: lineCount, isExplicitSlice: true };
|
|
20099
|
+
}
|
|
20100
|
+
}
|
|
20101
|
+
return { offset: startVal, isExplicitSlice: true };
|
|
20102
|
+
}
|
|
20103
|
+
const startLine = readIntToolInput(event, "start_line") ?? readIntToolInput(event, "startLine") ?? readIntToolInput(event, "start");
|
|
20104
|
+
const endLine = readIntToolInput(event, "end_line") ?? readIntToolInput(event, "endLine") ?? readIntToolInput(event, "end");
|
|
20105
|
+
if (startLine !== void 0 || endLine !== void 0) {
|
|
20106
|
+
const effectiveStart = startLine !== void 0 && startLine >= 1 ? Math.floor(startLine) : 1;
|
|
20107
|
+
const effectiveLimit = endLine !== void 0 && endLine >= effectiveStart ? Math.floor(endLine - effectiveStart + 1) : void 0;
|
|
20108
|
+
return { offset: effectiveStart, limit: effectiveLimit, isExplicitSlice: true };
|
|
20109
|
+
}
|
|
20110
|
+
return { isExplicitSlice: false };
|
|
20111
|
+
}
|
|
20026
20112
|
function readStartLine(event) {
|
|
20027
20113
|
const resp = event.raw["tool_response"];
|
|
20028
20114
|
if (resp === null || typeof resp !== "object") return 1;
|
|
@@ -20086,11 +20172,10 @@ function scanRequestedSlice(absPath, offset, limit) {
|
|
|
20086
20172
|
}
|
|
20087
20173
|
}
|
|
20088
20174
|
function estimateRequestedSlice(event, absPath) {
|
|
20089
|
-
const
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
const
|
|
20093
|
-
const scan = scanRequestedSlice(absPath, effectiveOffset, limit);
|
|
20175
|
+
const window2 = readRequestedSliceWindow(event);
|
|
20176
|
+
if (window2.limit === void 0 || window2.limit <= 0) return { kind: "unbounded" };
|
|
20177
|
+
const effectiveOffset = window2.offset !== void 0 && window2.offset >= 1 ? window2.offset : 1;
|
|
20178
|
+
const scan = scanRequestedSlice(absPath, effectiveOffset, window2.limit);
|
|
20094
20179
|
if (scan === null) return { kind: "unbounded" };
|
|
20095
20180
|
if (scan.nearSingleLine) return { kind: "nearSingleLine" };
|
|
20096
20181
|
if (scan.trustworthy) return { kind: "bytes", bytes: scan.bytes };
|
|
@@ -20330,7 +20415,7 @@ function preReadHandlerInner(event) {
|
|
|
20330
20415
|
} catch {
|
|
20331
20416
|
}
|
|
20332
20417
|
}
|
|
20333
|
-
const compactUnrangedRead =
|
|
20418
|
+
const compactUnrangedRead = !readRequestedSliceWindow(event).isExplicitSlice;
|
|
20334
20419
|
if (event.toolName !== "Grep" && compactUnrangedRead && loadConfig().hints.stable_doc_compacts && _isDocFile(normalized)) {
|
|
20335
20420
|
const compactPath = compactPathFor(normalized);
|
|
20336
20421
|
if (isCompactFresh(compactPath, normalized)) {
|
|
@@ -20413,7 +20498,7 @@ function preReadHandlerInner(event) {
|
|
|
20413
20498
|
}
|
|
20414
20499
|
return denyOutput(message);
|
|
20415
20500
|
}
|
|
20416
|
-
const unrangedRead =
|
|
20501
|
+
const unrangedRead = !readRequestedSliceWindow(event).isExplicitSlice;
|
|
20417
20502
|
if (loadConfig().hints.subagent_markdown_first_read_deny && event.agentId !== void 0 && unrangedRead && SUBAGENT_MD_FIRST_READ_DENY_EXT_RE.test(basename12) && markdownSize !== null && markdownSize >= SUBAGENT_MD_FIRST_READ_DENY_BYTES) {
|
|
20418
20503
|
recordStat("subagent_markdown_first_read_deny", 0, 0);
|
|
20419
20504
|
return denyOutput(
|
|
@@ -20783,9 +20868,9 @@ function readWindowFromDisk(event, normalized) {
|
|
|
20783
20868
|
const size = statSize(normalized);
|
|
20784
20869
|
if (size === null || size > SLICE_ESTIMATE_SCAN_CAP_BYTES) return null;
|
|
20785
20870
|
const text = decodeSource(fs21.readFileSync(normalized));
|
|
20786
|
-
const
|
|
20787
|
-
const
|
|
20788
|
-
const
|
|
20871
|
+
const window2 = readRequestedSliceWindow(event);
|
|
20872
|
+
const start = window2.offset !== void 0 && window2.offset >= 1 ? window2.offset : 1;
|
|
20873
|
+
const limit = window2.limit;
|
|
20789
20874
|
if (start === 1 && (limit === void 0 || limit <= 0)) return text;
|
|
20790
20875
|
const lines2 = text.split("\n");
|
|
20791
20876
|
const from = start - 1;
|
|
@@ -20908,7 +20993,7 @@ function elideAlreadyServedLines(event, respText) {
|
|
|
20908
20993
|
function structuralFoldInputs(event, respText) {
|
|
20909
20994
|
const filePath = getFilePath(event);
|
|
20910
20995
|
if (filePath === void 0) return null;
|
|
20911
|
-
if (
|
|
20996
|
+
if (readRequestedSliceWindow(event).isExplicitSlice) return null;
|
|
20912
20997
|
if (isTruncatedReadDelivery(event, respText)) return null;
|
|
20913
20998
|
if (redactSecrets(respText).count > 0) return null;
|
|
20914
20999
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
@@ -20943,14 +21028,15 @@ function foldCodeBodies(event, respText) {
|
|
|
20943
21028
|
if (filePath === void 0) return null;
|
|
20944
21029
|
const normalized = normalizePath(filePath);
|
|
20945
21030
|
if (isImagePath(normalized)) return null;
|
|
20946
|
-
const
|
|
21031
|
+
const sliceWin = readRequestedSliceWindow(event);
|
|
21032
|
+
const requestedOffset = sliceWin.offset;
|
|
20947
21033
|
if (requestedOffset !== void 0 && readStartLine(event) !== requestedOffset) return null;
|
|
20948
21034
|
if (isTruncatedReadDelivery(event, respText)) return null;
|
|
20949
21035
|
if (redactSecrets(respText).count > 0) return null;
|
|
20950
21036
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
20951
21037
|
if (parsed === null) return null;
|
|
20952
21038
|
const shown = displaySafePath(toDisplayPath(findProject(getCwd(event) ?? process.cwd())?.root, normalized));
|
|
20953
|
-
const folded = foldDelivery(parsed.rows, normalized, shown,
|
|
21039
|
+
const folded = foldDelivery(parsed.rows, normalized, shown, sliceWin.isExplicitSlice);
|
|
20954
21040
|
if (folded === null) return null;
|
|
20955
21041
|
const rewritten = [...parsed.header, fenceUntrustedFileContent(folded.numbered.join("\n")), ...parsed.trailer].join("\n");
|
|
20956
21042
|
const originalBytes = Buffer.byteLength(respText, "utf-8");
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isBlobStale,
|
|
10
10
|
loadBlob,
|
|
11
11
|
storeBlob
|
|
12
|
-
} from "./token-goat-chunk-
|
|
12
|
+
} from "./token-goat-chunk-PA5UXNOT.mjs";
|
|
13
13
|
import {
|
|
14
14
|
SYMBOL_BODY_CHAR_CAP,
|
|
15
15
|
copilotCliMcpToolsDir,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
resolveIndexPath,
|
|
26
26
|
shortFingerprint,
|
|
27
27
|
toDisplayPath
|
|
28
|
-
} from "./token-goat-chunk-
|
|
28
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
29
29
|
import {
|
|
30
30
|
registerReset
|
|
31
31
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -12,7 +12,7 @@ init_define_import_meta_env();
|
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
function resolveVersion() {
|
|
14
14
|
if (true) {
|
|
15
|
-
return "2.9.
|
|
15
|
+
return "2.9.10";
|
|
16
16
|
}
|
|
17
17
|
const require2 = createRequire(import.meta.url);
|
|
18
18
|
const pkg = require2("../package.json");
|
|
@@ -9636,6 +9636,25 @@ function remapToolInput(copilotToolName, input) {
|
|
|
9636
9636
|
if (idKey !== undefined && idKey in out) {
|
|
9637
9637
|
out = Object.assign({}, out, { bash_id: out[idKey] })
|
|
9638
9638
|
}
|
|
9639
|
+
if (Array.isArray(out.view_range) && out.view_range.length >= 1) {
|
|
9640
|
+
const rawStart = Number(out.view_range[0])
|
|
9641
|
+
if (Number.isFinite(rawStart) && rawStart >= 1) {
|
|
9642
|
+
const mapped = { offset: Math.floor(rawStart) }
|
|
9643
|
+
if (out.view_range.length >= 2) {
|
|
9644
|
+
const rawEnd = Number(out.view_range[1])
|
|
9645
|
+
if (Number.isFinite(rawEnd) && rawEnd >= rawStart) {
|
|
9646
|
+
mapped.limit = Math.floor(rawEnd - rawStart + 1)
|
|
9647
|
+
}
|
|
9648
|
+
}
|
|
9649
|
+
out = Object.assign({}, out, mapped)
|
|
9650
|
+
}
|
|
9651
|
+
}
|
|
9652
|
+
if ('old_str' in out && !('old_string' in out)) {
|
|
9653
|
+
out = Object.assign({}, out, { old_string: out.old_str })
|
|
9654
|
+
}
|
|
9655
|
+
if ('new_str' in out && !('new_string' in out)) {
|
|
9656
|
+
out = Object.assign({}, out, { new_string: out.new_str })
|
|
9657
|
+
}
|
|
9639
9658
|
return out
|
|
9640
9659
|
}
|
|
9641
9660
|
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
walkProject,
|
|
68
68
|
yamlLineClosesQuote,
|
|
69
69
|
yamlOpenQuoteAfter
|
|
70
|
-
} from "./token-goat-chunk-
|
|
70
|
+
} from "./token-goat-chunk-PA5UXNOT.mjs";
|
|
71
71
|
import {
|
|
72
72
|
Database,
|
|
73
73
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
@@ -125,7 +125,7 @@ import {
|
|
|
125
125
|
unsupportedLanguageName,
|
|
126
126
|
windowsCmdQuoteArg,
|
|
127
127
|
withExtension
|
|
128
|
-
} from "./token-goat-chunk-
|
|
128
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
129
129
|
import {
|
|
130
130
|
registerReset
|
|
131
131
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
storeWebOutput,
|
|
19
19
|
summarizeResidentContext,
|
|
20
20
|
taskListPruneHint
|
|
21
|
-
} from "./token-goat-chunk-
|
|
21
|
+
} from "./token-goat-chunk-SLGOIXKF.mjs";
|
|
22
22
|
import {
|
|
23
23
|
BASH_OUTPUT_SUBDIR,
|
|
24
24
|
GENERIC_SERVED_OUTPUT_KEY,
|
|
@@ -125,12 +125,12 @@ import {
|
|
|
125
125
|
wasCliReadThisSession,
|
|
126
126
|
wasFileReadThisSession,
|
|
127
127
|
wasHintShown
|
|
128
|
-
} from "./token-goat-chunk-
|
|
128
|
+
} from "./token-goat-chunk-PA5UXNOT.mjs";
|
|
129
129
|
import {
|
|
130
130
|
bashOutputCapBytes,
|
|
131
131
|
canRunWrappedShell,
|
|
132
132
|
deliveredOutputBytes
|
|
133
|
-
} from "./token-goat-chunk-
|
|
133
|
+
} from "./token-goat-chunk-ATVQ6IIT.mjs";
|
|
134
134
|
import {
|
|
135
135
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
136
136
|
ENV_KEYS,
|
|
@@ -194,7 +194,7 @@ import {
|
|
|
194
194
|
stripAnsiEscapes,
|
|
195
195
|
toDisplayPath,
|
|
196
196
|
toKB
|
|
197
|
-
} from "./token-goat-chunk-
|
|
197
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
198
198
|
import {
|
|
199
199
|
init_define_import_meta_env
|
|
200
200
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -798,7 +798,7 @@ init_define_import_meta_env();
|
|
|
798
798
|
var TRACKED_SKILL = "token-goat";
|
|
799
799
|
var MAX_COMMANDS_SHOWN = 8;
|
|
800
800
|
async function currentCommandNames() {
|
|
801
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
801
|
+
const { buildProgram } = await import("./token-goat-chunk-DSHYDU53.mjs");
|
|
802
802
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
803
803
|
}
|
|
804
804
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -9994,8 +9994,8 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
|
|
|
9994
9994
|
|
|
9995
9995
|
// src/hooks_tool_failure.ts
|
|
9996
9996
|
init_define_import_meta_env();
|
|
9997
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9998
|
-
import { dirname as dirname2 } from "node:path";
|
|
9997
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9998
|
+
import { dirname as dirname2, relative } from "node:path";
|
|
9999
9999
|
var FAILURE_SUFFIX = ".tool-failures.json";
|
|
10000
10000
|
var MAX_TRACKED_FAILURES = 64;
|
|
10001
10001
|
var SIGNATURE_ERROR_CHARS = 200;
|
|
@@ -10045,6 +10045,71 @@ function repeatFailureNotice(toolName) {
|
|
|
10045
10045
|
const tool = toolName === void 0 || toolName === "" ? "This tool" : displaySafeText(toolName);
|
|
10046
10046
|
return `[token-goat] ${tool} just failed with the same error as an earlier call this session. Retrying it unchanged will fail the same way -- change the arguments, the tool, or the approach.`;
|
|
10047
10047
|
}
|
|
10048
|
+
var MAX_EDIT_DIAGNOSE_BYTES = 10 * 1024 * 1024;
|
|
10049
|
+
function diagnoseEditFailure(event, errorText) {
|
|
10050
|
+
const toolName = getToolName(event);
|
|
10051
|
+
if (!toolName || !/^(edit|str_replace_editor)$/i.test(toolName)) {
|
|
10052
|
+
return null;
|
|
10053
|
+
}
|
|
10054
|
+
const isMultiple = /multiple matches|not unique|found \d+ matches|matches \d+ times|more than one match/i.test(errorText);
|
|
10055
|
+
const isNotFound = /no match|not found|could not find|zero matches|string to replace.*not found/i.test(errorText);
|
|
10056
|
+
if (!isMultiple && !isNotFound) {
|
|
10057
|
+
return null;
|
|
10058
|
+
}
|
|
10059
|
+
const filePath = getFilePath(event) ?? (typeof event.toolInput["path"] === "string" ? event.toolInput["path"] : void 0);
|
|
10060
|
+
if (!filePath) return null;
|
|
10061
|
+
const oldString = typeof event.toolInput["old_string"] === "string" ? event.toolInput["old_string"] : typeof event.toolInput["old_str"] === "string" ? event.toolInput["old_str"] : typeof event.toolInput["target"] === "string" ? event.toolInput["target"] : void 0;
|
|
10062
|
+
if (oldString === void 0 || oldString === "") return null;
|
|
10063
|
+
const absPath = normalizePath(filePath);
|
|
10064
|
+
if (!existsSync2(absPath)) return null;
|
|
10065
|
+
try {
|
|
10066
|
+
const stat = statSync4(absPath);
|
|
10067
|
+
if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
|
|
10068
|
+
const fileContent = readFileSync3(absPath, "utf8");
|
|
10069
|
+
const relDisplay = displaySafeText(relative(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
|
|
10070
|
+
if (isMultiple) {
|
|
10071
|
+
const matchLines = [];
|
|
10072
|
+
let pos = 0;
|
|
10073
|
+
while (pos < fileContent.length) {
|
|
10074
|
+
const idx = fileContent.indexOf(oldString, pos);
|
|
10075
|
+
if (idx === -1) break;
|
|
10076
|
+
const line = fileContent.slice(0, idx).split("\n").length;
|
|
10077
|
+
matchLines.push(line);
|
|
10078
|
+
pos = idx + Math.max(1, oldString.length);
|
|
10079
|
+
}
|
|
10080
|
+
if (matchLines.length > 1) {
|
|
10081
|
+
const lineList = matchLines.slice(0, 5).join(", ");
|
|
10082
|
+
const overflow = matchLines.length > 5 ? ` (+${matchLines.length - 5} more)` : "";
|
|
10083
|
+
return `[token-goat] Edit failed: string matched ${matchLines.length} times in ${relDisplay} on lines ${lineList}${overflow}. Include 2-3 lines of surrounding context to make old_str unique.`;
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
if (isNotFound) {
|
|
10087
|
+
const normOld = oldString.replace(/\r\n/g, "\n");
|
|
10088
|
+
const normFile = fileContent.replace(/\r\n/g, "\n");
|
|
10089
|
+
if (normFile.includes(normOld)) {
|
|
10090
|
+
return `[token-goat] Edit failed: string not found in ${relDisplay}, but matches with normalized line endings. Check CRLF vs LF line endings or whitespace.`;
|
|
10091
|
+
}
|
|
10092
|
+
const firstLine = oldString.split(/\r?\n/)[0]?.trim() ?? "";
|
|
10093
|
+
if (firstLine.length >= 10) {
|
|
10094
|
+
const fileLines = fileContent.split("\n");
|
|
10095
|
+
const similarLines = [];
|
|
10096
|
+
for (let i = 0; i < fileLines.length; i++) {
|
|
10097
|
+
const lineText = fileLines[i];
|
|
10098
|
+
if (lineText !== void 0 && lineText.includes(firstLine)) {
|
|
10099
|
+
similarLines.push(i + 1);
|
|
10100
|
+
if (similarLines.length >= 3) break;
|
|
10101
|
+
}
|
|
10102
|
+
}
|
|
10103
|
+
if (similarLines.length > 0) {
|
|
10104
|
+
return `[token-goat] Edit failed: string not found in ${relDisplay}. A similar line was found on line ${similarLines.join(", ")} \u2014 view that range to copy exact text.`;
|
|
10105
|
+
}
|
|
10106
|
+
}
|
|
10107
|
+
return `[token-goat] Edit failed: string not found in ${relDisplay}. View the target lines to copy the exact current content and indentation.`;
|
|
10108
|
+
}
|
|
10109
|
+
} catch {
|
|
10110
|
+
}
|
|
10111
|
+
return null;
|
|
10112
|
+
}
|
|
10048
10113
|
function postToolUseFailureHandler(event) {
|
|
10049
10114
|
try {
|
|
10050
10115
|
if (!event.sessionId) return passOutput();
|
|
@@ -10056,6 +10121,7 @@ function postToolUseFailureHandler(event) {
|
|
|
10056
10121
|
const signature = failureSignature(toolName, errorText);
|
|
10057
10122
|
const ledger = readLedger(target);
|
|
10058
10123
|
const priorState = ledger.seen[signature];
|
|
10124
|
+
const editDiagnostic = diagnoseEditFailure(event, errorText);
|
|
10059
10125
|
if (priorState === void 0) {
|
|
10060
10126
|
ledger.seen[signature] = false;
|
|
10061
10127
|
ledger.order.push(signature);
|
|
@@ -10064,11 +10130,19 @@ function postToolUseFailureHandler(event) {
|
|
|
10064
10130
|
if (evicted !== void 0) delete ledger.seen[evicted];
|
|
10065
10131
|
}
|
|
10066
10132
|
writeLedger(target, ledger);
|
|
10133
|
+
if (editDiagnostic !== null) {
|
|
10134
|
+
ledger.seen[signature] = true;
|
|
10135
|
+
writeLedger(target, ledger);
|
|
10136
|
+
return contextOutput(editDiagnostic);
|
|
10137
|
+
}
|
|
10067
10138
|
return passOutput();
|
|
10068
10139
|
}
|
|
10069
10140
|
if (priorState) return passOutput();
|
|
10070
10141
|
ledger.seen[signature] = true;
|
|
10071
10142
|
writeLedger(target, ledger);
|
|
10143
|
+
if (editDiagnostic !== null) {
|
|
10144
|
+
return contextOutput(editDiagnostic);
|
|
10145
|
+
}
|
|
10072
10146
|
return contextOutput(repeatFailureNotice(toolName));
|
|
10073
10147
|
} catch {
|
|
10074
10148
|
return passOutput();
|
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-ZQODJQTP.mjs";
|
|
6
|
+
import "./token-goat-chunk-SLGOIXKF.mjs";
|
|
7
|
+
import "./token-goat-chunk-PA5UXNOT.mjs";
|
|
8
|
+
import "./token-goat-chunk-ATVQ6IIT.mjs";
|
|
9
|
+
import "./token-goat-chunk-VCEK77ED.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-7ALFFPJW.mjs";
|
|
6
|
+
import "./token-goat-chunk-YFXFUG3L.mjs";
|
|
7
|
+
import "./token-goat-chunk-SLGOIXKF.mjs";
|
|
8
|
+
import "./token-goat-chunk-PA5UXNOT.mjs";
|
|
9
9
|
import {
|
|
10
10
|
installEpipeGuard
|
|
11
|
-
} from "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-VCEK77ED.mjs";
|
|
12
12
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
13
13
|
import {
|
|
14
14
|
init_define_import_meta_env
|
package/docs/security.md
CHANGED
|
@@ -15,7 +15,7 @@ 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, and an entry that is not a plain file of the expected size is passed over without being read at all, so a tampered copy costs a download and cannot substitute different weights, stall the read, or fill the disk. Token-goat also writes there, and treats the directory as somebody else's: it creates its temporary file exclusively rather than through whatever is already sitting at that name, and removes only what it created. Point it at a directory you control all the same, since nothing token-goat does can stop another writer filling it with entries that will simply be rejected. 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.
|