feat(durable): per-model compaction settings via compaction.modelOverrides

Overrides keyed by provider/modelId apply at the threshold check, overflow
recovery, and compaction range selection.

closes #10942
This commit is contained in:
Mario Zechner 2026-10-16 08:20:12 +00:00
parent 6085986e4b
commit c4cf7957b4
4 changed files with 2 additions and 28 deletions

View file

@ -6,14 +6,6 @@
- `settings.compaction.modelOverrides` sets compaction thresholds per model, keyed by exact `"provider/modelId"`, so one harness can serve small- and large-window models; the threshold check, overflow recovery, and the compaction task use the entry of the model they act for ([#10942](https://github.com/earendil-works/pi/issues/10942)).
### Changed
- `ToolControl.terminate` is now typed `boolean` instead of `true`, matching `pi-agent-core` and `pi-coding-agent`; `false` does not end the run ([#10858](https://github.com/earendil-works/pi/issues/10858)).
### Fixed
- The README's Errors section now limits the `StorageRequestError` exemption to reads: a `StorageRequestError` thrown by `commit()` or `mintId()` fails the Harness like any other storage error ([#11014](https://github.com/earendil-works/pi/issues/11014)).
## [1.3.0] - 2026-10-15
### Added

View file

@ -719,7 +719,7 @@ The package root loads TypeBox, because the tool task validates arguments with p
Three rules decide what an error does:
1. **A storage error ends the Harness.** The call that hit it rejects with the error. Everything after it, and every pending wait, rejects with `SessionFailed` (its `cause` is that error); watches and event streams end with `session_failed`; running tasks get their abort signal; `onReport` gets the error once. The Harness then closes itself. Nothing is retried; a custom storage retries its own transient failures. Reopen to recover: commits are atomic, so the store is consistent, and resubmitting with the same `requestId` finds a submission that was already admitted.
2. **An invalid read fails only that call:** a storage read throws `StorageRequestError` for a bad cursor, an unknown conversation, or history a document does not keep. A read you cancelled fails only itself too. The exemption covers reads only: any error from `commit()` or `mintId()`, `StorageRequestError` included, ends the Harness as in rule 1.
2. **An invalid request fails only that call:** a storage throws `StorageRequestError` for a bad cursor, an unknown conversation, or history a document does not keep. A read you cancelled fails only itself too.
3. **Your code fails only its own unit:** a throwing task phase faults that task, a throwing tool gets an error result, a throwing hook is reported and skipped (`beforeTool` blocks the call), and a throwing commit callback rolls back that commit. Listeners that throw, or return a rejected promise, are reported and the others still run. The exception is a host `RegistryReader` whose `snapshot()` throws: the Harness cannot schedule without it, so it fails the Harness like a storage error.
`harness.closed` settles once the Harness has closed, by `close()` or after a storage error, so a host restarts on failure like this:

View file

@ -136,8 +136,7 @@ export interface ConversationHandle {
/** Post-tools controls requested by a tool result. */
export type ToolControl = {
readonly addTools?: readonly string[];
/** End the run without another model request when every result of the round sets it to `true`; `false` is a no-op. */
readonly terminate?: boolean;
readonly terminate?: true;
readonly handoff?: string;
};

View file

@ -673,23 +673,6 @@ describe("tool results", () => {
]);
await first.harness.close(context);
});
// #10858
it("does not terminate on terminate: false", async () => {
const setup = chatSetup();
const keep = tool("keep", async () => ({ output: [], control: { terminate: false } }));
addTool(setup.registry, keep);
const { harness, entries, status } = await run(
setup,
[calls(["keep", {}, "c1"]), DONE],
async (_harness, root) => {
await root.configure({ tools: [keep] }, context);
},
);
expect(status).toBe("done");
expect(entries.at(-1)!.kind).toBe("pi.assistant");
await harness.close(context);
});
});
describe("generation hooks", () => {