@stigmer/runner 3.1.17 → 3.2.1
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 +1 -1
- package/dist/.build-fingerprint +1 -1
- package/dist/activities/execute-cursor/cost-guard.d.ts +41 -0
- package/dist/activities/execute-cursor/cost-guard.js +50 -0
- package/dist/activities/execute-cursor/cost-guard.js.map +1 -0
- package/dist/activities/execute-cursor/index.js +30 -0
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.d.ts +10 -1
- package/dist/activities/execute-cursor/turn-stream.js +42 -9
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.js +13 -3
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +9 -0
- package/dist/activities/execute-deep-agent/streaming-terminal.js +10 -1
- package/dist/activities/execute-deep-agent/streaming-terminal.js.map +1 -1
- package/dist/main.js +17 -0
- package/dist/main.js.map +1 -1
- package/dist/preflight.d.ts +46 -0
- package/dist/preflight.js +55 -0
- package/dist/preflight.js.map +1 -0
- package/dist/shared/tool-rounds.d.ts +30 -0
- package/dist/shared/tool-rounds.js +49 -0
- package/dist/shared/tool-rounds.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/preflight.test.ts +41 -0
- package/src/activities/execute-cursor/__tests__/cost-guard.test.ts +64 -0
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +118 -0
- package/src/activities/execute-cursor/cost-guard.ts +56 -0
- package/src/activities/execute-cursor/index.ts +30 -0
- package/src/activities/execute-cursor/turn-stream.ts +61 -10
- package/src/activities/execute-deep-agent/__tests__/streaming-terminal.test.ts +40 -0
- package/src/activities/execute-deep-agent/setup.ts +16 -3
- package/src/activities/execute-deep-agent/streaming-terminal.ts +11 -1
- package/src/main.ts +17 -0
- package/src/preflight.ts +59 -0
- package/src/shared/__tests__/tool-rounds.test.ts +57 -0
- package/src/shared/tool-rounds.ts +56 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it, vi, afterEach } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
MAX_TOOL_ROUNDS,
|
|
4
|
+
MIN_TOOL_ROUNDS,
|
|
5
|
+
SUPER_STEPS_PER_ROUND,
|
|
6
|
+
resolveRecursionLimit,
|
|
7
|
+
} from "../tool-rounds.js";
|
|
8
|
+
|
|
9
|
+
describe("resolveRecursionLimit", () => {
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.restoreAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("returns null for unset (proto contract: 0 = unlimited)", () => {
|
|
15
|
+
expect(resolveRecursionLimit(undefined)).toBeNull();
|
|
16
|
+
expect(resolveRecursionLimit(0)).toBeNull();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("returns null for negative values (treated as unset, never a tiny limit)", () => {
|
|
20
|
+
expect(resolveRecursionLimit(-5)).toBeNull();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("converts rounds to super-steps at the contract's x6 factor", () => {
|
|
24
|
+
expect(resolveRecursionLimit(10)).toBe(60);
|
|
25
|
+
expect(resolveRecursionLimit(100)).toBe(600);
|
|
26
|
+
expect(resolveRecursionLimit(MAX_TOOL_ROUNDS)).toBe(MAX_TOOL_ROUNDS * SUPER_STEPS_PER_ROUND);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("clamps below-range values up to the minimum with a warning", () => {
|
|
30
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
31
|
+
|
|
32
|
+
expect(resolveRecursionLimit(3)).toBe(MIN_TOOL_ROUNDS * SUPER_STEPS_PER_ROUND);
|
|
33
|
+
|
|
34
|
+
expect(warn).toHaveBeenCalledOnce();
|
|
35
|
+
expect(warn.mock.calls[0][0]).toContain("max_tool_rounds=3");
|
|
36
|
+
expect(warn.mock.calls[0][0]).toContain("clamping to 10");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("clamps above-range values down to the maximum with a warning", () => {
|
|
40
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
41
|
+
|
|
42
|
+
expect(resolveRecursionLimit(5000)).toBe(MAX_TOOL_ROUNDS * SUPER_STEPS_PER_ROUND);
|
|
43
|
+
|
|
44
|
+
expect(warn).toHaveBeenCalledOnce();
|
|
45
|
+
expect(warn.mock.calls[0][0]).toContain("max_tool_rounds=5000");
|
|
46
|
+
expect(warn.mock.calls[0][0]).toContain("clamping to 1000");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("does not warn for in-range values", () => {
|
|
50
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
51
|
+
|
|
52
|
+
resolveRecursionLimit(MIN_TOOL_ROUNDS);
|
|
53
|
+
resolveRecursionLimit(MAX_TOOL_ROUNDS);
|
|
54
|
+
|
|
55
|
+
expect(warn).not.toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExecutionConfig.max_tool_rounds → LangGraph recursion_limit resolution.
|
|
3
|
+
*
|
|
4
|
+
* Implements the proto contract (agentexecution/v1/spec.proto):
|
|
5
|
+
* - 0 / unset = unlimited — no recursionLimit is placed on the invoke
|
|
6
|
+
* config, preserving the run-until-done + loop-detection posture.
|
|
7
|
+
* - When set, valid range is 10–1000 rounds; out-of-range values are
|
|
8
|
+
* clamped to the nearest bound with a warning log.
|
|
9
|
+
* - recursion_limit = rounds × 6: each model-tool round consumes multiple
|
|
10
|
+
* graph super-steps because every active middleware hook is its own
|
|
11
|
+
* node; ×6 is the contract's floor estimate.
|
|
12
|
+
*
|
|
13
|
+
* The SAME resolved limit must feed both LangGraph's hard stop (the invoke
|
|
14
|
+
* config) and the ExecutionBudgetMiddleware's wrap-up advisory, so the
|
|
15
|
+
* "~80% of budget" warning and the enforcement point can never disagree.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export const SUPER_STEPS_PER_ROUND = 6;
|
|
19
|
+
export const MIN_TOOL_ROUNDS = 10;
|
|
20
|
+
export const MAX_TOOL_ROUNDS = 1000;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The advisory-only budget used when max_tool_rounds is unset: high enough
|
|
24
|
+
* to never fire on a healthy run. Deliberately NOT enforced as a hard
|
|
25
|
+
* recursionLimit — unset means unlimited per the proto contract.
|
|
26
|
+
*/
|
|
27
|
+
export const UNBOUNDED_ADVISORY_RECURSION_LIMIT = 6000;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The recursionLimit for the invoke config, or null when the execution is
|
|
31
|
+
* unbounded (max_tool_rounds 0/unset — the pre-existing default posture).
|
|
32
|
+
*/
|
|
33
|
+
export function resolveRecursionLimit(maxToolRounds: number | undefined): number | null {
|
|
34
|
+
if (!maxToolRounds || maxToolRounds <= 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return clampToolRounds(maxToolRounds) * SUPER_STEPS_PER_ROUND;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function clampToolRounds(requested: number): number {
|
|
41
|
+
if (requested < MIN_TOOL_ROUNDS) {
|
|
42
|
+
console.warn(
|
|
43
|
+
`[tool-rounds] max_tool_rounds=${requested} below the valid range ` +
|
|
44
|
+
`(${MIN_TOOL_ROUNDS}-${MAX_TOOL_ROUNDS}); clamping to ${MIN_TOOL_ROUNDS}`,
|
|
45
|
+
);
|
|
46
|
+
return MIN_TOOL_ROUNDS;
|
|
47
|
+
}
|
|
48
|
+
if (requested > MAX_TOOL_ROUNDS) {
|
|
49
|
+
console.warn(
|
|
50
|
+
`[tool-rounds] max_tool_rounds=${requested} above the valid range ` +
|
|
51
|
+
`(${MIN_TOOL_ROUNDS}-${MAX_TOOL_ROUNDS}); clamping to ${MAX_TOOL_ROUNDS}`,
|
|
52
|
+
);
|
|
53
|
+
return MAX_TOOL_ROUNDS;
|
|
54
|
+
}
|
|
55
|
+
return requested;
|
|
56
|
+
}
|