pi-warden 1.7.0 → 1.11.0
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/CHANGELOG.md +62 -0
- package/dist/config.d.ts +12 -1
- package/dist/config.js +9 -1
- package/dist/conscience.d.ts +2 -1
- package/dist/conscience.js +3 -3
- package/dist/dedupe.d.ts +8 -0
- package/dist/dedupe.js +34 -0
- package/dist/extension.js +136 -36
- package/dist/filter.d.ts +5 -4
- package/dist/filter.js +5 -5
- package/dist/guard.d.ts +11 -3
- package/dist/guard.js +281 -15
- package/dist/relevance.d.ts +4 -3
- package/dist/relevance.js +2 -2
- package/dist/rules-calibrate.d.ts +2 -3
- package/dist/rules-calibrate.js +3 -4
- package/dist/rules-lint.d.ts +3 -3
- package/dist/rules-lint.js +2 -1
- package/dist/rules.d.ts +26 -4
- package/dist/rules.js +37 -12
- package/dist/shape.d.ts +1 -1
- package/dist/shape.js +3 -1
- package/dist/trial.d.ts +54 -0
- package/dist/trial.js +81 -0
- package/dist/turn-rules.d.ts +4 -5
- package/dist/turn-rules.js +4 -4
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,68 @@ How to keep this current: add the entry in the same pull request as the change,
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
<!-- Empty. Next release starts here. -->
|
|
10
|
+
|
|
11
|
+
## 1.11.0
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- SubQ backend: `"typesafeBackend": "subq"` in the user config sends judgments to `https://api.subq.ai` with the model `subq-snap-preview`, using the key in `SUBQ_API_KEY`. ([Configuration](docs/configuration.md#user-config))
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- pi-typesafe 0.10.0.
|
|
20
|
+
- Every judgment fits the backend's question limit: 16 questions per request on SubQ, 32 on the other backends. The rules guard, the end-of-run turn rules, the turn-start rules reminder, `/warden rules check`, the context filter, relevance compaction, and the conscience split a judgment with more questions into several requests and merge the answers by question id; the conscience repeats its disposition question in each request. On the other backends every guard sends the same requests as before, and a judge an extension author builds without `limits` keeps the 32-question limit. On SubQ a judgment with more than 16 questions uses more of `maxRequests`.
|
|
21
|
+
- The action guard and `/warden rules calibrate` also fit the backend's question limit. On SubQ, an action guard call with more than 16 questions (one per `rm` target the request does not authorize, plus any `questions` a library caller adds) and a calibration sample with more than 16 rules split into requests of at most 16, sent together, with the answers merged by question id; the verdict and the scores are the same as from one request. No action guard question repeats in each request, because none of them reads another's answer. On the other backends, and for a judge without `limits`, both send the same requests as before.
|
|
22
|
+
|
|
23
|
+
## 1.10.0
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- pi-warden now declares support for Pi 1.x: the `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` peer ranges go from `>=0.85.1 <1` to `>=0.85.1 <2`, so installing pi-warden next to Pi 1.x no longer fails with a peer dependency conflict. npm still prints an `ERESOLVE overriding peer dependency` warning, because pi-typesafe declares Pi `<1`.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- A run that a custom message starts no longer crashes the turn-start handler. Pi versions that fire `before_agent_start` for such a run (for example, a background subagent's report) give it no prompt text; the turn-start rules reminder and the conscience assessment now skip that run and write a `custom_trigger` line to the trace.
|
|
32
|
+
|
|
33
|
+
## 1.9.0
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- The action guard no longer lets text the agent writes into a shell comment sway Jev. The command in the request state and the `script_lines` are sent without shell comments (a `#` at the start of a word, outside quotes, including quotes that span lines, and heredoc bodies, and not `$#` or `${#…}`); a command the scan cannot read with certainty (an unclosed quote, backticks, arithmetic) is sent unchanged, while the confirm dialog and the trace keep the full command. Agent-bound reasons no longer carry Jev scores: the held-call message, the headless warn steer, the declined-call reason, and the intent, off-task, should-proceed, and large-output steers keep the words (`irreversible`, `possibly irreversible`) without the numbers, and the dialog, notices, trace, holds, and learning records keep them. ([Guards](docs/guards.md))
|
|
38
|
+
|
|
39
|
+
## 1.8.1
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Repeated runs (`context.dedupeRuns`) no longer cut text the model never saw. On the `claude-bridge` provider, Claude Code saves a tool result over its MCP output limit to a file and gives the model only a "saved to file" message; pi-warden counted that result as seen and then cut later reads of the saved file as repeats. A tool result over 20,000 characters on `claude-bridge` (judged by the provider of the call that made it) is no longer counted as seen, and a `read` or `bash` call that names a file in a Claude Code `tool-results` directory is never cut. Other providers, and `claude-bridge` results under the limit, are cut as before. ([Guards](docs/guards.md))
|
|
44
|
+
|
|
45
|
+
## 1.8.0
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- `trial.silentHalf` (default `false`, user config only): a randomized comparison of the guards in real work. Each session is put in arm `live` or `silent`, 50/50 from its session id, so a resumed session keeps its arm. A `silent` session judges as usual — every guard runs and every request is made — but records each delivery it would have made instead of sending it: rules, slop, prose, stuck and repeat, done-check, cause-check, open loops, intent-mismatch, off-task, should-proceed, large-output, and action-warning steers, conscience tips, preference notes, turn-start rules reminders, compact evidence, subagent triage wakes, call-waste notes, and tool-output compression. Everything that protects data stays live in both arms: irreversible calls are held or blocked, credential values are masked, security, credential, and prompt-injection notices are delivered, and runaway stops and the warnings about pi-warden itself are unaffected. One `pi-warden-trial` custom session entry names the arm at session start, and one per withheld delivery carries its kind, guard, time, and the redacted text clipped to 300 characters; custom entries stay out of the model's context, so both arms can be read back from the session logs. With the setting off, no entry is written and no delivery changes. ([Configuration](docs/configuration.md#user-config))
|
|
50
|
+
|
|
51
|
+
## 1.7.1
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- A/B benchmark tooling, registration v4 (`eval/preregistration.md`): the run moves to the CheapestInference flat-rate subscription with no paid agent model (`cheapestinference/deepseek-v4.1-flash`, then `cheapestinference/mimo-v2.5`), the models run one after the other at concurrency 3, the repeats (6 and 7) are sized from the smoke timings to about 24 hours each, and the Jev cap is $3 per model. The stopped v3 batches are named as a stopped pilot and never analysed.
|
|
56
|
+
- A/B batch Jev-error stop (`eval/jev-stop.mjs`): a run whose one failed Jev request is a cancelled trace-only sample (the guard's second request on one judged call in twenty, cancelled by its own 5-second deadline) is recorded as `cancelledTraceSamples: 1` and no longer stops the batch, found by time (the entry with a `jev:` line nearest to the failed request's time is within 1,000 ms of it and has no off-task answer) rather than by entry number; every other failure still does. Repo tooling; rides along with the next release.
|
|
57
|
+
- A/B batch re-run pass (`scripts/eval-ab.mjs`, `eval/batch.mjs`, Corrections 4 of `eval/preregistration.md`): `--resume <batch folder> --rerun-excluded`, once a model's main pass is finished, runs every block that holds an `infraError` run once more (all three cells, registered cell order); the first runs move to `superseded` in `runs.json` with `supersededAt` and their evidence folders to `superseded/` in the batch folder, and leave every metric while their Jev dollars stay in the batch total. One pass per batch: the exclusions after it are final and the 10% limit applies to them; a stop during the pass resumes with `--resume`. Repo tooling; rides along with the next release.
|
|
58
|
+
- A stall rule for batch runs (`eval/stall.mjs`, `--stall-min`, default 15): a pi process whose session log shows no new assistant message or tool result for that long is killed, and the run is an infrastructure failure, re-run and excluded like an API error. `--arc-timeout-min` sets the timeout of one turn of a multi-turn run.
|
|
59
|
+
- Equivalent dollars for the subscription models (`eval/config.mjs`): `cheapestinference/deepseek-v4.1-flash` at DeepSeek's `deepseek-flash` rates by call timestamp, `cheapestinference/mimo-v2.5` at Xiaomi's official MiMo-V2.5 price.
|
|
60
|
+
- `scripts/eval-timing.mjs`: per-run wall time, per-call queue wait and generation time, and runs per hour of a batch folder, from timings only; `node eval/power.mjs --registered R1,R2` prints the power at the registered sizes.
|
|
61
|
+
- A/B thesis decision tool (`eval/decide.mjs`, `scripts/thesis-decide.mjs`): the decision rules of `eval/preregistration.md` ("Decision rules", Corrections 7) computed from one model's `runs.json` (the per-run values, the paired blocks, the exact McNemar p, the one-sided Clopper-Pearson lower bound, the violation rate ratio and its lower bound, the paired bootstrap, the Wald and delta-method sensitivity bounds, and the decision of each rule and of the model); `node scripts/thesis-decide.mjs <runs.json> --label A --blocks 120` prints the table (`--json` for JSON), and `--metrics FILE` writes the analysis set's per-run values. Tests on made-up data in `tests/eval-decide.test.ts`. Repo tooling; rides along with the next release.
|
|
62
|
+
- A/B thesis decision tool metrics input (`scripts/thesis-decide.mjs`, `eval/decide.mjs`): the same script also reads a `--metrics` file (the analysis set's `task`, `family`, `repeat`, `cell`, `s`, `v`, `c`) and prints the same decision table, with a first line that says the input was a metrics file. Repo tooling; rides along with the next release.
|
|
63
|
+
- A/B thesis pooled view (`eval/decide.mjs`, `scripts/thesis-pooled.mjs`): the two models' runs pooled after the run to describe the three thesis claims (the hallucination proxy `h`, the pooled paired success, and the token count `t` with medians, the 90th percentile, the count over 1,000,000, and the paired warden-vs-control ratio), labelled post hoc and deciding nothing; the `--metrics` rows of `scripts/thesis-decide.mjs` also carry `h` and `t`. Repo tooling; rides along with the next release.
|
|
64
|
+
- A/B thesis registration, Corrections 5 to 7 (`eval/preregistration.md`): the stops and other events of both batches are recorded, and the analysis methods behind the decision rules' bounds and p-value are fixed before any of them is computed.
|
|
65
|
+
- A/B thesis report (`eval/reports/2026-10-10-thesis-v4/README.md`): the registered decision rules applied to both models of the v4 batch, with the decision tables, per-cell tables, pair counts and bounds, sensitivity bounds, the secondary trap-capable analysis, and the infrastructure and power limits; both models show success not more than 5 points below control (one-sided 95% lower bounds +0.00 and -2.14 points), model B (`cheapestinference/mimo-v2.5`) also supports the rule-violation and Jev-increment rules, and the cost rule is inconclusive on both.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- A/B run key isolation (`scripts/eval-ab.mjs`): the harness copies the TypeSafe key into a run's agent folder only for a cell that asks Jev, so an agent in any other cell cannot turn Jev on by itself, as 6 `warden-offline` runs of the v4 batch did. Repo tooling; rides along with the next release.
|
|
70
|
+
- Eval environment filtering (`eval/env.mjs`): `filterEnv` now drops an inherited `PI_WARDEN_TRACE_DIR`, so a `warden-offline` run cannot write its pi-warden trace into a folder inherited from the session that started the batch; only the cells that ask Jev get one, set by `scripts/eval-ab.mjs`.
|
|
9
71
|
## 1.7.0
|
|
10
72
|
|
|
11
73
|
### Added
|
package/dist/config.d.ts
CHANGED
|
@@ -352,6 +352,15 @@ export interface PrefsConfig {
|
|
|
352
352
|
/** At session start, send the standing preferences that pass every injection rule to the agent as one context message. */
|
|
353
353
|
inject: boolean;
|
|
354
354
|
}
|
|
355
|
+
/** The silent-half field trial (trial.ts): a random half of sessions records what it would have told the agent. */
|
|
356
|
+
export interface TrialConfig {
|
|
357
|
+
/**
|
|
358
|
+
* Put each session in arm live or silent, 50/50 from its id. A silent session judges as usual but records every
|
|
359
|
+
* suppressed delivery instead of sending it; holds, secret masking, security notices, and runaway stops stay live.
|
|
360
|
+
* Off by default, user file only: turning it on weakens guarding in half the sessions, so a project file must not.
|
|
361
|
+
*/
|
|
362
|
+
silentHalf: boolean;
|
|
363
|
+
}
|
|
355
364
|
/** Per-model steer adaptation: a steer kind a model rarely follows or often disputes becomes trace-only for it. */
|
|
356
365
|
export interface SteersConfig {
|
|
357
366
|
/** Make a steer kind trace-only for a model that rarely follows or often disputes it. */
|
|
@@ -469,6 +478,8 @@ export interface WardenConfig {
|
|
|
469
478
|
conscience: ConscienceConfig;
|
|
470
479
|
/** Standing preferences: corrections the user repeated in earlier sessions of this project. */
|
|
471
480
|
prefs: PrefsConfig;
|
|
481
|
+
/** Silent-half field trial: off by default, user file only. */
|
|
482
|
+
trial: TrialConfig;
|
|
472
483
|
/** Call-waste notes: advisory sentences attached to the tool result that triggers them. */
|
|
473
484
|
waste: WasteConfig;
|
|
474
485
|
/** Relevance compaction in place of Pi's compaction summary. Off by default. */
|
|
@@ -478,7 +489,7 @@ export interface WardenConfig {
|
|
|
478
489
|
}
|
|
479
490
|
export declare const PACKAGE_NAME = "pi-warden";
|
|
480
491
|
/** Bumped when WardenConfig gains a section; extension.ts checks it so a half-updated module graph is reported, not crashed on. */
|
|
481
|
-
export declare const CONFIG_SCHEMA =
|
|
492
|
+
export declare const CONFIG_SCHEMA = 14;
|
|
482
493
|
export declare const PROJECT_CONFIG_FILE = "pi-warden.json";
|
|
483
494
|
export declare function defaultConfig(): WardenConfig;
|
|
484
495
|
/** The user file is in the host's agent directory: `~/.pi/agent` on Pi, `~/.omp/agent` on oh-my-pi. `PI_CODING_AGENT_DIR` overrides it. */
|
package/dist/config.js
CHANGED
|
@@ -32,7 +32,7 @@ export const WARDEN_MODES = ["steer", "confirm", "advise"];
|
|
|
32
32
|
export const CONSCIENCE_SKILL_MODES = ["off", "recommend", "load"];
|
|
33
33
|
export const PACKAGE_NAME = "pi-warden";
|
|
34
34
|
/** Bumped when WardenConfig gains a section; extension.ts checks it so a half-updated module graph is reported, not crashed on. */
|
|
35
|
-
export const CONFIG_SCHEMA =
|
|
35
|
+
export const CONFIG_SCHEMA = 14;
|
|
36
36
|
export const PROJECT_CONFIG_FILE = `${PACKAGE_NAME}.json`;
|
|
37
37
|
export function defaultConfig() {
|
|
38
38
|
return {
|
|
@@ -102,6 +102,7 @@ export function defaultConfig() {
|
|
|
102
102
|
localFloor: 0.5,
|
|
103
103
|
},
|
|
104
104
|
prefs: { enabled: true, inject: true },
|
|
105
|
+
trial: { silentHalf: false },
|
|
105
106
|
waste: { enabled: true, tip: false, every: 20, sleep: true, paging: true, search: true, recheck: true },
|
|
106
107
|
// pi-claude-bridge compacts its own models and cancels on failure; its summary must not be replaced.
|
|
107
108
|
compaction: { enabled: false, keepThreshold: 0.5, maxSummaryTokens: 20000, timeoutMs: 20000, maxRequests: 12, skipProviders: ["claude-bridge"] },
|
|
@@ -646,9 +647,16 @@ export function applyUserOverrides(base, raw) {
|
|
|
646
647
|
learning: applyLearning(base.learning, raw.learning),
|
|
647
648
|
conscience: applyConscience(base.conscience, raw.conscience),
|
|
648
649
|
prefs: applyPrefs(base.prefs, raw.prefs),
|
|
650
|
+
trial: applyTrial(base.trial, raw.trial),
|
|
649
651
|
warnings: [...(base.warnings ?? []), ...warnings],
|
|
650
652
|
};
|
|
651
653
|
}
|
|
654
|
+
/** A project cannot turn the trial on: applyProjectOverrides never copies this section, so the user file is the only source. */
|
|
655
|
+
function applyTrial(base, raw) {
|
|
656
|
+
if (!isObject(raw))
|
|
657
|
+
return base;
|
|
658
|
+
return { silentHalf: boolean(raw.silentHalf, base.silentHalf) };
|
|
659
|
+
}
|
|
652
660
|
function applyWaste(base, raw) {
|
|
653
661
|
if (!isObject(raw))
|
|
654
662
|
return base;
|
package/dist/conscience.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Pre-measurement: all selections are trace-only. No steers, no auto-loading.
|
|
6
6
|
*/
|
|
7
7
|
import type { Skill } from "@earendil-works/pi-coding-agent";
|
|
8
|
-
import type { Questions } from "pi-typesafe";
|
|
8
|
+
import type { BackendLimits, Questions } from "pi-typesafe";
|
|
9
9
|
import type { JudgmentsOffReason } from "./backend.js";
|
|
10
10
|
import type { ConscienceConfig } from "./config.js";
|
|
11
11
|
import type { TaskSpine } from "./shape.js";
|
|
@@ -211,6 +211,7 @@ export interface Judge {
|
|
|
211
211
|
}): Promise<{
|
|
212
212
|
answers: Record<string, unknown>;
|
|
213
213
|
}>;
|
|
214
|
+
readonly limits?: BackendLimits | undefined;
|
|
214
215
|
}
|
|
215
216
|
export interface ConscienceDeps {
|
|
216
217
|
judge: Judge | undefined;
|
package/dist/conscience.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { choice, noul, score } from "pi-typesafe";
|
|
1
|
+
import { choice, DEFAULT_BACKEND_LIMITS, noul, score } from "pi-typesafe";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { redact } from "./redact.js";
|
|
4
4
|
import { SPINE_GOAL_LIMIT, SPINE_HISTORY_LIMIT, SPINE_HISTORY_TURNS } from "./shape.js";
|
|
@@ -16,7 +16,7 @@ export const SCORE_LEVELS = [
|
|
|
16
16
|
const MAX_ELIGIBLE = 256;
|
|
17
17
|
/** Max UTF-8 bytes per candidate description. */
|
|
18
18
|
const MAX_DESC_BYTES = 8192;
|
|
19
|
-
/** Max questions per single judge request (including the shared disposition question). */
|
|
19
|
+
/** Max questions per single judge request (including the shared disposition question); a backend with a lower limit gets its own. */
|
|
20
20
|
const MAX_QUESTIONS_PER_REQUEST = 32;
|
|
21
21
|
/* ─── Path/URL sanitizer (spec §6) ─────────────────────────────────── */
|
|
22
22
|
/** Strip absolute paths, URLs, and credentials from a description. */
|
|
@@ -510,7 +510,7 @@ export async function assess(prompt, recentContext, skills, tools, activeSkills,
|
|
|
510
510
|
const effectiveTimeout = Math.min(config.timeoutMs, sharedTimeoutMs);
|
|
511
511
|
const deadline = start + effectiveTimeout;
|
|
512
512
|
const MAX_IN_FLIGHT = 4;
|
|
513
|
-
const maxCandidatesPerRequest = MAX_QUESTIONS_PER_REQUEST - 1; // -1 for disposition
|
|
513
|
+
const maxCandidatesPerRequest = Math.min(MAX_QUESTIONS_PER_REQUEST, judge.limits?.maxQuestions ?? DEFAULT_BACKEND_LIMITS.maxQuestions) - 1; // -1 for disposition
|
|
514
514
|
const cappedRanked = gate.ranked.map(r => ({ candidate: r.candidate, capped: capDescription(r.candidate) }));
|
|
515
515
|
const workQueue = [];
|
|
516
516
|
for (let i = 0; i < cappedRanked.length; i += maxCandidatesPerRequest) {
|
package/dist/dedupe.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare const RUN_MIN_LINES = 20;
|
|
|
8
8
|
export declare const RUN_MIN_CHARS = 1500;
|
|
9
9
|
/** Errors and verdicts usually sit at the end of a result, so the last characters are never replaced. */
|
|
10
10
|
export declare const TAIL_KEEP_CHARS = 2000;
|
|
11
|
+
export declare const SWAP_LIMIT_CHARS = 20000;
|
|
11
12
|
export interface RepeatedRun {
|
|
12
13
|
/** First line of the run in the new text, 0-based. */
|
|
13
14
|
start: number;
|
|
@@ -37,5 +38,12 @@ export declare class SeenText {
|
|
|
37
38
|
}
|
|
38
39
|
/** Replaces each run with one pointer line; `path` holds the full original text. */
|
|
39
40
|
export declare function collapseRuns(text: string, runs: RepeatedRun[], path: string): string;
|
|
41
|
+
/** Whether `text` (a tool input) names a result file that `provider` saved instead of giving it to the model. */
|
|
42
|
+
export declare function savedResultIn(text: string, provider: string | undefined): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The seen items of a branch, without a tool result the model did not get: one over the output limit of the provider
|
|
45
|
+
* of the assistant message that called it. `provider` stands in when no assistant message names one.
|
|
46
|
+
*/
|
|
47
|
+
export declare function seenItems(entries: unknown[], provider: string | undefined): SeenItem[];
|
|
40
48
|
/** The texts a session entry puts in context and where the agent saw them. Thinking, images, and summaries are not indexed. */
|
|
41
49
|
export declare function seenItem(entry: unknown): SeenItem | undefined;
|
package/dist/dedupe.js
CHANGED
|
@@ -8,6 +8,15 @@ export const RUN_MIN_LINES = 20;
|
|
|
8
8
|
export const RUN_MIN_CHARS = 1500;
|
|
9
9
|
/** Errors and verdicts usually sit at the end of a result, so the last characters are never replaced. */
|
|
10
10
|
export const TAIL_KEEP_CHARS = 2000;
|
|
11
|
+
/**
|
|
12
|
+
* Providers whose host swaps a tool result over its output limit for a "saved to file" message: the model never gets
|
|
13
|
+
* that text, so it is not seen. claude-bridge runs Pi tools as Claude Code MCP tools, and nothing in the session marks the swap.
|
|
14
|
+
*/
|
|
15
|
+
const SWAPPING_PROVIDERS = new Set(["claude-bridge"]);
|
|
16
|
+
// Claude Code saves an MCP result over 50,000 characters or MAX_MCP_OUTPUT_TOKENS (25,000 by default) to a file; 20,000 stays under both even at 1.25 characters per token.
|
|
17
|
+
export const SWAP_LIMIT_CHARS = 20_000;
|
|
18
|
+
/** A file in a Claude Code session's tool-results directory, where it saves a swapped result. */
|
|
19
|
+
const SAVED_RESULT = /\/projects\/[^/\s"'\\]+\/[^/\s"'\\]+\/tool-results\/[^/\s"'\\]+/;
|
|
11
20
|
const BASE = 1_000_003;
|
|
12
21
|
/** BASE^(RUN_MIN_LINES - 1) mod 2^32, for the rolling window hash. */
|
|
13
22
|
const TOP = (() => { let power = 1; for (let index = 1; index < RUN_MIN_LINES; index++)
|
|
@@ -142,6 +151,31 @@ function textsOf(content) {
|
|
|
142
151
|
return [];
|
|
143
152
|
return content.filter(part => part?.type === "text" && typeof part.text === "string").map(part => part.text);
|
|
144
153
|
}
|
|
154
|
+
/** Whether `text` (a tool input) names a result file that `provider` saved instead of giving it to the model. */
|
|
155
|
+
export function savedResultIn(text, provider) {
|
|
156
|
+
return provider !== undefined && SWAPPING_PROVIDERS.has(provider) && SAVED_RESULT.test(text);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The seen items of a branch, without a tool result the model did not get: one over the output limit of the provider
|
|
160
|
+
* of the assistant message that called it. `provider` stands in when no assistant message names one.
|
|
161
|
+
*/
|
|
162
|
+
export function seenItems(entries, provider) {
|
|
163
|
+
const items = [];
|
|
164
|
+
let caller = provider;
|
|
165
|
+
for (const entry of entries) {
|
|
166
|
+
const message = entry?.message;
|
|
167
|
+
if (message?.role === "assistant")
|
|
168
|
+
caller = typeof message.provider === "string" ? message.provider : provider;
|
|
169
|
+
const item = seenItem(entry);
|
|
170
|
+
if (!item)
|
|
171
|
+
continue;
|
|
172
|
+
if (message?.role === "toolResult" && caller !== undefined && SWAPPING_PROVIDERS.has(caller)
|
|
173
|
+
&& item.texts.reduce((sum, text) => sum + text.length, 0) > SWAP_LIMIT_CHARS)
|
|
174
|
+
continue;
|
|
175
|
+
items.push(item);
|
|
176
|
+
}
|
|
177
|
+
return items;
|
|
178
|
+
}
|
|
145
179
|
/** The texts a session entry puts in context and where the agent saw them. Thinking, images, and summaries are not indexed. */
|
|
146
180
|
export function seenItem(entry) {
|
|
147
181
|
if (!entry || typeof entry !== "object")
|