Let the install set how many steps a run may take
The two limits were compiled in: a task run stopped at AppData.HarnessMaxToolSteps (200) and a chat reply at AppData.BotReplyMaxToolSteps (8), so an install that needed a longer task had to rebuild. Both are now `app_settings` rows (`harness_max_tool_steps` and `bot_reply_max_tool_steps`), read at the two loop call sites through grabHarnessMaxToolSteps and grabReplyMaxToolSteps, and edited by the super admin on Settings > General beside the prompt appendix. Zero clears a limit back to the code default, like the bot file quota. readStepLimit keeps the saved number only when it is a whole number of at least 1 and no more than AppData.BotStepLimitMax (1000), so a blank, a zero or a typo falls back instead of stopping every run before its first turn, and a huge number still ends in the wrap-up report. runTextLoop keeps AppData.HarnessMaxToolSteps as its default, so it stays free of database reads and its tests run as they did.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import readStepLimit from "./read-step-limit";
|
||||
|
||||
const base = { fallback: 200, max: 1000 };
|
||||
|
||||
describe("readStepLimit", () => {
|
||||
test("uses a saved limit inside the allowed range", () => {
|
||||
expect(readStepLimit({ ...base, saved: "40" })).toBe(40);
|
||||
expect(readStepLimit({ ...base, saved: " 12 " })).toBe(12);
|
||||
expect(readStepLimit({ ...base, saved: "1" })).toBe(1);
|
||||
expect(readStepLimit({ ...base, saved: "1000" })).toBe(1000);
|
||||
});
|
||||
|
||||
test("falls back when nothing was saved", () => {
|
||||
expect(readStepLimit({ ...base, saved: undefined })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: null })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: "" })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: " " })).toBe(200);
|
||||
});
|
||||
|
||||
test("falls back when the saved limit can't drive a run", () => {
|
||||
expect(readStepLimit({ ...base, saved: "0" })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: "-3" })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: "abc" })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: "12.5" })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: "NaN" })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: "12steps" })).toBe(200);
|
||||
});
|
||||
|
||||
test("caps at the ceiling, never past it", () => {
|
||||
expect(readStepLimit({ ...base, saved: "1001" })).toBe(200);
|
||||
expect(readStepLimit({ ...base, saved: "999999" })).toBe(200);
|
||||
expect(readStepLimit({ fallback: 8, max: 50, saved: "51" })).toBe(8);
|
||||
expect(readStepLimit({ fallback: 8, max: 50, saved: "50" })).toBe(50);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
type Params = {
|
||||
/**
|
||||
* What the install saved, as it came out of `app_settings`: undefined or
|
||||
* empty when it never did.
|
||||
*/
|
||||
saved?: string | null;
|
||||
/**
|
||||
* The code's own limit, used whenever the saved one can't be trusted.
|
||||
*/
|
||||
fallback: number;
|
||||
/**
|
||||
* The most a saved limit may ask for.
|
||||
*/
|
||||
max: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a tool-step limit saved in the settings, falling back to the code's
|
||||
* default when it is missing, not a whole number, below one, or above the
|
||||
* ceiling. The settings page and the saved row are both untrusted input:
|
||||
* without this a typo (or a zero) would stop every run before its first
|
||||
* turn, and a huge number would let one run alone run for hours.
|
||||
*/
|
||||
export default function readStepLimit({ saved, fallback, max }: Params) {
|
||||
const text = String(saved ?? "").trim();
|
||||
|
||||
if (!text) return fallback;
|
||||
|
||||
const value = Number(text);
|
||||
|
||||
if (!Number.isInteger(value) || value < 1 || value > max) return fallback;
|
||||
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user