@spendgraph/workflows 0.2.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/README.md +149 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/noesis/attribution.d.ts +24 -0
- package/dist/noesis/attribution.js +43 -0
- package/dist/noesis/context.d.ts +18 -0
- package/dist/noesis/context.js +16 -0
- package/dist/noesis/draft/draft.d.ts +18 -0
- package/dist/noesis/draft/draft.js +50 -0
- package/dist/noesis/draft/index.d.ts +2 -0
- package/dist/noesis/draft/index.js +1 -0
- package/dist/noesis/draft/types.d.ts +27 -0
- package/dist/noesis/draft/types.js +1 -0
- package/dist/noesis/events.d.ts +158 -0
- package/dist/noesis/events.js +24 -0
- package/dist/noesis/execute/execute.d.ts +22 -0
- package/dist/noesis/execute/execute.js +85 -0
- package/dist/noesis/execute/index.d.ts +3 -0
- package/dist/noesis/execute/index.js +2 -0
- package/dist/noesis/execute/order.d.ts +11 -0
- package/dist/noesis/execute/order.js +28 -0
- package/dist/noesis/execute/types.d.ts +37 -0
- package/dist/noesis/execute/types.js +1 -0
- package/dist/noesis/gates/closure-checklist/index.d.ts +3 -0
- package/dist/noesis/gates/closure-checklist/index.js +2 -0
- package/dist/noesis/gates/closure-checklist/invoke.d.ts +5 -0
- package/dist/noesis/gates/closure-checklist/invoke.js +6 -0
- package/dist/noesis/gates/closure-checklist/prompt.d.ts +5 -0
- package/dist/noesis/gates/closure-checklist/prompt.js +32 -0
- package/dist/noesis/gates/closure-checklist/types.d.ts +20 -0
- package/dist/noesis/gates/closure-checklist/types.js +1 -0
- package/dist/noesis/gates/cqot-gate/index.d.ts +3 -0
- package/dist/noesis/gates/cqot-gate/index.js +2 -0
- package/dist/noesis/gates/cqot-gate/invoke.d.ts +5 -0
- package/dist/noesis/gates/cqot-gate/invoke.js +6 -0
- package/dist/noesis/gates/cqot-gate/prompt.d.ts +5 -0
- package/dist/noesis/gates/cqot-gate/prompt.js +27 -0
- package/dist/noesis/gates/cqot-gate/types.d.ts +23 -0
- package/dist/noesis/gates/cqot-gate/types.js +1 -0
- package/dist/noesis/gates/premortem/index.d.ts +3 -0
- package/dist/noesis/gates/premortem/index.js +2 -0
- package/dist/noesis/gates/premortem/invoke.d.ts +5 -0
- package/dist/noesis/gates/premortem/invoke.js +6 -0
- package/dist/noesis/gates/premortem/prompt.d.ts +5 -0
- package/dist/noesis/gates/premortem/prompt.js +37 -0
- package/dist/noesis/gates/premortem/types.d.ts +28 -0
- package/dist/noesis/gates/premortem/types.js +1 -0
- package/dist/noesis/gates/red-team/index.d.ts +3 -0
- package/dist/noesis/gates/red-team/index.js +2 -0
- package/dist/noesis/gates/red-team/invoke.d.ts +5 -0
- package/dist/noesis/gates/red-team/invoke.js +6 -0
- package/dist/noesis/gates/red-team/prompt.d.ts +5 -0
- package/dist/noesis/gates/red-team/prompt.js +25 -0
- package/dist/noesis/gates/red-team/types.d.ts +21 -0
- package/dist/noesis/gates/red-team/types.js +1 -0
- package/dist/noesis/gates/standards-review/index.d.ts +3 -0
- package/dist/noesis/gates/standards-review/index.js +2 -0
- package/dist/noesis/gates/standards-review/invoke.d.ts +5 -0
- package/dist/noesis/gates/standards-review/invoke.js +6 -0
- package/dist/noesis/gates/standards-review/prompt.d.ts +5 -0
- package/dist/noesis/gates/standards-review/prompt.js +26 -0
- package/dist/noesis/gates/standards-review/types.d.ts +20 -0
- package/dist/noesis/gates/standards-review/types.js +1 -0
- package/dist/noesis/gates/think-checkpoint/index.d.ts +3 -0
- package/dist/noesis/gates/think-checkpoint/index.js +2 -0
- package/dist/noesis/gates/think-checkpoint/invoke.d.ts +5 -0
- package/dist/noesis/gates/think-checkpoint/invoke.js +6 -0
- package/dist/noesis/gates/think-checkpoint/prompt.d.ts +5 -0
- package/dist/noesis/gates/think-checkpoint/prompt.js +28 -0
- package/dist/noesis/gates/think-checkpoint/types.d.ts +21 -0
- package/dist/noesis/gates/think-checkpoint/types.js +1 -0
- package/dist/noesis/index.d.ts +51 -0
- package/dist/noesis/index.js +27 -0
- package/dist/noesis/producers/cove-answers/index.d.ts +3 -0
- package/dist/noesis/producers/cove-answers/index.js +2 -0
- package/dist/noesis/producers/cove-answers/invoke.d.ts +5 -0
- package/dist/noesis/producers/cove-answers/invoke.js +6 -0
- package/dist/noesis/producers/cove-answers/prompt.d.ts +5 -0
- package/dist/noesis/producers/cove-answers/prompt.js +27 -0
- package/dist/noesis/producers/cove-answers/types.d.ts +32 -0
- package/dist/noesis/producers/cove-answers/types.js +1 -0
- package/dist/noesis/producers/cove-questions/index.d.ts +3 -0
- package/dist/noesis/producers/cove-questions/index.js +2 -0
- package/dist/noesis/producers/cove-questions/invoke.d.ts +5 -0
- package/dist/noesis/producers/cove-questions/invoke.js +6 -0
- package/dist/noesis/producers/cove-questions/prompt.d.ts +5 -0
- package/dist/noesis/producers/cove-questions/prompt.js +26 -0
- package/dist/noesis/producers/cove-questions/types.d.ts +17 -0
- package/dist/noesis/producers/cove-questions/types.js +1 -0
- package/dist/noesis/producers/decompose/index.d.ts +3 -0
- package/dist/noesis/producers/decompose/index.js +2 -0
- package/dist/noesis/producers/decompose/invoke.d.ts +5 -0
- package/dist/noesis/producers/decompose/invoke.js +6 -0
- package/dist/noesis/producers/decompose/prompt.d.ts +22 -0
- package/dist/noesis/producers/decompose/prompt.js +68 -0
- package/dist/noesis/producers/decompose/types.d.ts +41 -0
- package/dist/noesis/producers/decompose/types.js +1 -0
- package/dist/noesis/producers/discriminating-test/index.d.ts +3 -0
- package/dist/noesis/producers/discriminating-test/index.js +2 -0
- package/dist/noesis/producers/discriminating-test/invoke.d.ts +5 -0
- package/dist/noesis/producers/discriminating-test/invoke.js +6 -0
- package/dist/noesis/producers/discriminating-test/prompt.d.ts +5 -0
- package/dist/noesis/producers/discriminating-test/prompt.js +67 -0
- package/dist/noesis/producers/discriminating-test/types.d.ts +32 -0
- package/dist/noesis/producers/discriminating-test/types.js +1 -0
- package/dist/noesis/producers/plan-candidates/index.d.ts +3 -0
- package/dist/noesis/producers/plan-candidates/index.js +2 -0
- package/dist/noesis/producers/plan-candidates/invoke.d.ts +5 -0
- package/dist/noesis/producers/plan-candidates/invoke.js +6 -0
- package/dist/noesis/producers/plan-candidates/prompt.d.ts +5 -0
- package/dist/noesis/producers/plan-candidates/prompt.js +46 -0
- package/dist/noesis/producers/plan-candidates/types.d.ts +32 -0
- package/dist/noesis/producers/plan-candidates/types.js +1 -0
- package/dist/noesis/producers/qn-brief/index.d.ts +3 -0
- package/dist/noesis/producers/qn-brief/index.js +2 -0
- package/dist/noesis/producers/qn-brief/invoke.d.ts +5 -0
- package/dist/noesis/producers/qn-brief/invoke.js +6 -0
- package/dist/noesis/producers/qn-brief/prompt.d.ts +12 -0
- package/dist/noesis/producers/qn-brief/prompt.js +65 -0
- package/dist/noesis/producers/qn-brief/types.d.ts +42 -0
- package/dist/noesis/producers/qn-brief/types.js +1 -0
- package/dist/noesis/producers/toulmin/answer.d.ts +18 -0
- package/dist/noesis/producers/toulmin/answer.js +87 -0
- package/dist/noesis/producers/toulmin/index.d.ts +5 -0
- package/dist/noesis/producers/toulmin/index.js +4 -0
- package/dist/noesis/producers/toulmin/invoke.d.ts +18 -0
- package/dist/noesis/producers/toulmin/invoke.js +53 -0
- package/dist/noesis/producers/toulmin/prompt.d.ts +12 -0
- package/dist/noesis/producers/toulmin/prompt.js +60 -0
- package/dist/noesis/producers/toulmin/render.d.ts +14 -0
- package/dist/noesis/producers/toulmin/render.js +82 -0
- package/dist/noesis/producers/toulmin/types.d.ts +126 -0
- package/dist/noesis/producers/toulmin/types.js +1 -0
- package/dist/noesis/question.d.ts +24 -0
- package/dist/noesis/question.js +37 -0
- package/dist/noesis/repair/reflection/index.d.ts +3 -0
- package/dist/noesis/repair/reflection/index.js +2 -0
- package/dist/noesis/repair/reflection/invoke.d.ts +5 -0
- package/dist/noesis/repair/reflection/invoke.js +6 -0
- package/dist/noesis/repair/reflection/prompt.d.ts +5 -0
- package/dist/noesis/repair/reflection/prompt.js +26 -0
- package/dist/noesis/repair/reflection/types.d.ts +17 -0
- package/dist/noesis/repair/reflection/types.js +1 -0
- package/dist/noesis/retry.d.ts +25 -0
- package/dist/noesis/retry.js +60 -0
- package/dist/noesis/route.d.ts +29 -0
- package/dist/noesis/route.js +69 -0
- package/dist/noesis/router/triage/index.d.ts +3 -0
- package/dist/noesis/router/triage/index.js +2 -0
- package/dist/noesis/router/triage/invoke.d.ts +31 -0
- package/dist/noesis/router/triage/invoke.js +76 -0
- package/dist/noesis/router/triage/prompt.d.ts +11 -0
- package/dist/noesis/router/triage/prompt.js +34 -0
- package/dist/noesis/router/triage/types.d.ts +70 -0
- package/dist/noesis/router/triage/types.js +1 -0
- package/dist/noesis/run/artifacts.d.ts +12 -0
- package/dist/noesis/run/artifacts.js +31 -0
- package/dist/noesis/run/errors.d.ts +38 -0
- package/dist/noesis/run/errors.js +55 -0
- package/dist/noesis/run/index.d.ts +6 -0
- package/dist/noesis/run/index.js +4 -0
- package/dist/noesis/run/ledger.d.ts +110 -0
- package/dist/noesis/run/ledger.js +184 -0
- package/dist/noesis/run/loop.d.ts +15 -0
- package/dist/noesis/run/loop.js +91 -0
- package/dist/noesis/run/plan.d.ts +18 -0
- package/dist/noesis/run/plan.js +39 -0
- package/dist/noesis/run/produce.d.ts +50 -0
- package/dist/noesis/run/produce.js +114 -0
- package/dist/noesis/run/redo.d.ts +24 -0
- package/dist/noesis/run/redo.js +41 -0
- package/dist/noesis/run/result.d.ts +5 -0
- package/dist/noesis/run/result.js +51 -0
- package/dist/noesis/run/run.d.ts +4 -0
- package/dist/noesis/run/run.js +73 -0
- package/dist/noesis/run/stages/classify.d.ts +7 -0
- package/dist/noesis/run/stages/classify.js +23 -0
- package/dist/noesis/run/stages/direct.d.ts +6 -0
- package/dist/noesis/run/stages/direct.js +8 -0
- package/dist/noesis/run/stages/full.d.ts +14 -0
- package/dist/noesis/run/stages/full.js +21 -0
- package/dist/noesis/run/stages/index.d.ts +5 -0
- package/dist/noesis/run/stages/index.js +5 -0
- package/dist/noesis/run/stages/retrieve.d.ts +13 -0
- package/dist/noesis/run/stages/retrieve.js +38 -0
- package/dist/noesis/run/stages/standard.d.ts +12 -0
- package/dist/noesis/run/stages/standard.js +19 -0
- package/dist/noesis/run/state.d.ts +26 -0
- package/dist/noesis/run/state.js +13 -0
- package/dist/noesis/run/stream.d.ts +28 -0
- package/dist/noesis/run/stream.js +33 -0
- package/dist/noesis/run/verify.d.ts +27 -0
- package/dist/noesis/run/verify.js +78 -0
- package/dist/noesis/slugs.d.ts +16 -0
- package/dist/noesis/slugs.js +46 -0
- package/dist/noesis/stage.d.ts +56 -0
- package/dist/noesis/stage.js +52 -0
- package/dist/noesis/started.d.ts +18 -0
- package/dist/noesis/started.js +39 -0
- package/dist/noesis/types.d.ts +167 -0
- package/dist/noesis/types.js +1 -0
- package/dist/noesis/usage.d.ts +49 -0
- package/dist/noesis/usage.js +56 -0
- package/dist/noesis/verify/index.d.ts +2 -0
- package/dist/noesis/verify/index.js +1 -0
- package/dist/noesis/verify/types.d.ts +54 -0
- package/dist/noesis/verify/types.js +1 -0
- package/dist/noesis/verify/verify.d.ts +36 -0
- package/dist/noesis/verify/verify.js +94 -0
- package/package.json +59 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Emit } from "../../events.js";
|
|
2
|
+
import type { Usage } from "../../usage.js";
|
|
3
|
+
/** How hard the task is for a strong model with standard tools. */
|
|
4
|
+
export type Difficulty = "trivial" | "easy" | "medium" | "hard" | "expert" | "research";
|
|
5
|
+
/** Blast radius if the answer is wrong. Judged by consequence, not by topic. */
|
|
6
|
+
export type Stakes = "low" | "medium" | "high" | "critical";
|
|
7
|
+
/** The five signals and the summary. Nothing here is a route — code derives that. */
|
|
8
|
+
export interface TriageEstimates {
|
|
9
|
+
is_new_task: boolean;
|
|
10
|
+
difficulty: Difficulty;
|
|
11
|
+
stakes: Stakes;
|
|
12
|
+
confidence: number;
|
|
13
|
+
ambiguity: number;
|
|
14
|
+
tool_need: number;
|
|
15
|
+
summary: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* What the router classifies.
|
|
19
|
+
*
|
|
20
|
+
* The prompt's question box wraps every request in an `<attachments>` block, so
|
|
21
|
+
* all three fields render in one pass: `{file_content}` and `{image_content}`
|
|
22
|
+
* inside the block, `{question}` after it.
|
|
23
|
+
*
|
|
24
|
+
* Both attachment fields are optional and an unset one is sent as `(none)`
|
|
25
|
+
* rather than empty — empty tags read as an attachment the router cannot open,
|
|
26
|
+
* which it prices as ambiguity and tool need.
|
|
27
|
+
*/
|
|
28
|
+
export interface TriageInput {
|
|
29
|
+
/**
|
|
30
|
+
* The open task from earlier in the session, if there is one.
|
|
31
|
+
*
|
|
32
|
+
* First because it is sent first: the block goes ahead of the user message,
|
|
33
|
+
* which is the only thing that makes `is_new_task` mean anything. With no
|
|
34
|
+
* block the prompt is told to always answer true, so retriage never fires and
|
|
35
|
+
* every follow-up starts a fresh run. A summary of the open task, never the
|
|
36
|
+
* conversation.
|
|
37
|
+
*/
|
|
38
|
+
currentTask?: string;
|
|
39
|
+
/** The request being classified. Named for the variable it fills, like every stage. */
|
|
40
|
+
question: string;
|
|
41
|
+
file_content?: string;
|
|
42
|
+
image_content?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface TriageOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Attempts for this call, counting the first. Default 2.
|
|
47
|
+
*
|
|
48
|
+
* Separate from `maxRetries`, the loop's budget for a gate sending work
|
|
49
|
+
* backwards. This one covers a call that produced nothing to think about, and
|
|
50
|
+
* the two must not share an allowance.
|
|
51
|
+
*/
|
|
52
|
+
attempts?: number;
|
|
53
|
+
/** Overrides the model the `Llm` was built with. */
|
|
54
|
+
model?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Merged into the request body.
|
|
57
|
+
*
|
|
58
|
+
* Where `temperature` goes, and it is per model: Haiku 4.5 takes it, Sonnet 5
|
|
59
|
+
* rejects it outright.
|
|
60
|
+
*/
|
|
61
|
+
params?: Record<string, unknown>;
|
|
62
|
+
/** Where this stage says what it is doing. Set by `run()`. */
|
|
63
|
+
emit?: Emit;
|
|
64
|
+
}
|
|
65
|
+
export interface TriageResult extends Usage {
|
|
66
|
+
estimates: TriageEstimates;
|
|
67
|
+
rolloutId: string;
|
|
68
|
+
model: string;
|
|
69
|
+
latencyMs: number;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The artifacts as a stage that reasons about them reads them, long strings cut.
|
|
3
|
+
*
|
|
4
|
+
* Every stage handed the whole ledger — 3A, 3B, 3C, 4A and R — is reading it
|
|
5
|
+
* for the decisions it records, and a decision is legible from the head of a
|
|
6
|
+
* tool answer. 6C is the exception and takes the artifacts whole: it cites
|
|
7
|
+
* evidence rather than reviewing choices, and a citation cut in half is a
|
|
8
|
+
* citation of nothing.
|
|
9
|
+
*/
|
|
10
|
+
export declare const digest: (artifacts: Record<string, unknown>, limit?: number) => Record<string, unknown>;
|
|
11
|
+
/** One artifact, cut the same way — what an event carries rather than a prompt. */
|
|
12
|
+
export declare const digestOne: (artifact: unknown, limit?: number) => unknown;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much of any one string an artifact may carry into a prompt.
|
|
3
|
+
*
|
|
4
|
+
* A tool answer is the one artifact with no bound on its size: everything else
|
|
5
|
+
* is a model reply against a schema, and a wide `deep_recall` can be larger
|
|
6
|
+
* than every other stage put together.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_LIMIT = 2000;
|
|
9
|
+
const cut = (text, limit) => `${text.slice(0, limit)}… (${text.length - limit} more characters)`;
|
|
10
|
+
function shorten(value, limit) {
|
|
11
|
+
if (typeof value === "string")
|
|
12
|
+
return value.length > limit ? cut(value, limit) : value;
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return value.map((item) => shorten(item, limit));
|
|
15
|
+
if (value && typeof value === "object") {
|
|
16
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, shorten(item, limit)]));
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The artifacts as a stage that reasons about them reads them, long strings cut.
|
|
22
|
+
*
|
|
23
|
+
* Every stage handed the whole ledger — 3A, 3B, 3C, 4A and R — is reading it
|
|
24
|
+
* for the decisions it records, and a decision is legible from the head of a
|
|
25
|
+
* tool answer. 6C is the exception and takes the artifacts whole: it cites
|
|
26
|
+
* evidence rather than reviewing choices, and a citation cut in half is a
|
|
27
|
+
* citation of nothing.
|
|
28
|
+
*/
|
|
29
|
+
export const digest = (artifacts, limit = DEFAULT_LIMIT) => shorten(artifacts, limit);
|
|
30
|
+
/** One artifact, cut the same way — what an event carries rather than a prompt. */
|
|
31
|
+
export const digestOne = (artifact, limit = DEFAULT_LIMIT) => shorten(artifact, limit);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TriageEstimates } from "../router/triage/index.js";
|
|
2
|
+
import type { Routing } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* A route the table names with nothing behind it.
|
|
5
|
+
*
|
|
6
|
+
* Thrown only where the fallback ran — `RouteResult.route` is null — which is
|
|
7
|
+
* the one case where the branch really does not exist. A branch that ran and
|
|
8
|
+
* threw is a `RouteFailedError`, and the two are separate classes because the
|
|
9
|
+
* answer to them is: write the branch, versus try again.
|
|
10
|
+
*/
|
|
11
|
+
export declare class RouteNotBuiltError extends Error {
|
|
12
|
+
readonly route: string;
|
|
13
|
+
readonly estimates?: TriageEstimates | undefined;
|
|
14
|
+
readonly routing?: Routing | undefined;
|
|
15
|
+
readonly detail?: string | undefined;
|
|
16
|
+
constructor(route: string, estimates?: TriageEstimates | undefined, routing?: Routing | undefined, detail?: string | undefined);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A branch that ran and threw, with the estimates that chose it.
|
|
20
|
+
*
|
|
21
|
+
* `detail` is the branch's own message, carried up because the graph underneath
|
|
22
|
+
* catches whatever a branch throws and reports only a failed run. A caller
|
|
23
|
+
* catching this should decide whether the cause is worth retrying; catching
|
|
24
|
+
* `RouteNotBuiltError` never is.
|
|
25
|
+
*/
|
|
26
|
+
export declare class RouteFailedError extends Error {
|
|
27
|
+
readonly route: string;
|
|
28
|
+
readonly estimates?: TriageEstimates | undefined;
|
|
29
|
+
readonly routing?: Routing | undefined;
|
|
30
|
+
readonly detail?: string | undefined;
|
|
31
|
+
constructor(route: string, estimates?: TriageEstimates | undefined, routing?: Routing | undefined, detail?: string | undefined);
|
|
32
|
+
}
|
|
33
|
+
/** A branch named in the routing table with nothing behind it yet. */
|
|
34
|
+
export declare const notBuilt: (route: string) => () => never;
|
|
35
|
+
/** A route that has to look something up, with nothing to look it up with. */
|
|
36
|
+
export declare class NoToolsError extends Error {
|
|
37
|
+
constructor();
|
|
38
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A route the table names with nothing behind it.
|
|
3
|
+
*
|
|
4
|
+
* Thrown only where the fallback ran — `RouteResult.route` is null — which is
|
|
5
|
+
* the one case where the branch really does not exist. A branch that ran and
|
|
6
|
+
* threw is a `RouteFailedError`, and the two are separate classes because the
|
|
7
|
+
* answer to them is: write the branch, versus try again.
|
|
8
|
+
*/
|
|
9
|
+
export class RouteNotBuiltError extends Error {
|
|
10
|
+
route;
|
|
11
|
+
estimates;
|
|
12
|
+
routing;
|
|
13
|
+
detail;
|
|
14
|
+
constructor(route, estimates, routing, detail) {
|
|
15
|
+
super(detail || `The "${route}" route is not built yet.`);
|
|
16
|
+
this.route = route;
|
|
17
|
+
this.estimates = estimates;
|
|
18
|
+
this.routing = routing;
|
|
19
|
+
this.detail = detail;
|
|
20
|
+
this.name = "RouteNotBuiltError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A branch that ran and threw, with the estimates that chose it.
|
|
25
|
+
*
|
|
26
|
+
* `detail` is the branch's own message, carried up because the graph underneath
|
|
27
|
+
* catches whatever a branch throws and reports only a failed run. A caller
|
|
28
|
+
* catching this should decide whether the cause is worth retrying; catching
|
|
29
|
+
* `RouteNotBuiltError` never is.
|
|
30
|
+
*/
|
|
31
|
+
export class RouteFailedError extends Error {
|
|
32
|
+
route;
|
|
33
|
+
estimates;
|
|
34
|
+
routing;
|
|
35
|
+
detail;
|
|
36
|
+
constructor(route, estimates, routing, detail) {
|
|
37
|
+
super(detail || `The "${route}" route failed.`);
|
|
38
|
+
this.route = route;
|
|
39
|
+
this.estimates = estimates;
|
|
40
|
+
this.routing = routing;
|
|
41
|
+
this.detail = detail;
|
|
42
|
+
this.name = "RouteFailedError";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** A branch named in the routing table with nothing behind it yet. */
|
|
46
|
+
export const notBuilt = (route) => () => {
|
|
47
|
+
throw new RouteNotBuiltError(route);
|
|
48
|
+
};
|
|
49
|
+
/** A route that has to look something up, with nothing to look it up with. */
|
|
50
|
+
export class NoToolsError extends Error {
|
|
51
|
+
constructor() {
|
|
52
|
+
super("This route looks something up, so it needs a ToolBus in opts.tools.");
|
|
53
|
+
this.name = "NoToolsError";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { NoToolsError, RouteFailedError, RouteNotBuiltError } from "./errors.js";
|
|
2
|
+
export type { Entry, Escalation, Ledger, Recorded, StageId, StageUsage } from "./ledger.js";
|
|
3
|
+
export { newLedger, STAGE_NAMES, STAGES } from "./ledger.js";
|
|
4
|
+
export { run } from "./run.js";
|
|
5
|
+
export type { NoesisStream } from "./stream.js";
|
|
6
|
+
export { runStream } from "./stream.js";
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Emit } from "../events.js";
|
|
2
|
+
import { type PartialUsage, type Usage } from "../usage.js";
|
|
3
|
+
/** Every stage the loop can run, in the order it runs them. */
|
|
4
|
+
export declare const STAGES: readonly ["0", "1", "2", "3A", "3B", "3C", "4", "4A", "4B", "5A", "5B", "5", "5C", "6A", "6B", "6C", "R"];
|
|
5
|
+
export type StageId = (typeof STAGES)[number];
|
|
6
|
+
/** What a stage is called where a person or a model has to read it. */
|
|
7
|
+
export declare const STAGE_NAMES: Record<StageId, string>;
|
|
8
|
+
/** What one stage left behind, and what it cost. */
|
|
9
|
+
export interface Entry extends Usage {
|
|
10
|
+
stage: StageId;
|
|
11
|
+
data: unknown;
|
|
12
|
+
/** More than one where the stage cascaded, so an escalation is not lost. */
|
|
13
|
+
rolloutIds: string[];
|
|
14
|
+
model: string;
|
|
15
|
+
latencyMs: number;
|
|
16
|
+
}
|
|
17
|
+
/** One stage's spend, summed over every time the loop ran it. */
|
|
18
|
+
export interface StageUsage extends Usage {
|
|
19
|
+
stage: StageId;
|
|
20
|
+
name: string;
|
|
21
|
+
/** More than one where a back edge ran the stage again. */
|
|
22
|
+
models: string[];
|
|
23
|
+
rolloutIds: string[];
|
|
24
|
+
latencyMs: number;
|
|
25
|
+
calls: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A stage result, however the stage reports its rollouts.
|
|
29
|
+
*
|
|
30
|
+
* `StageOutcome` carries one and `AnswerResult` carries several, and both are
|
|
31
|
+
* recorded the same way.
|
|
32
|
+
*/
|
|
33
|
+
export type Recorded<T> = {
|
|
34
|
+
data: T;
|
|
35
|
+
model: string;
|
|
36
|
+
latencyMs: number;
|
|
37
|
+
/** What the server will price the call at, still in flight. Settled by `settle()`. */
|
|
38
|
+
pricing?: Promise<number | undefined>;
|
|
39
|
+
} & PartialUsage & ({
|
|
40
|
+
rolloutId: string;
|
|
41
|
+
} | {
|
|
42
|
+
rolloutIds: string[];
|
|
43
|
+
});
|
|
44
|
+
/** A run that stopped and wants a person. */
|
|
45
|
+
export interface Escalation {
|
|
46
|
+
from: StageId;
|
|
47
|
+
reason: string;
|
|
48
|
+
}
|
|
49
|
+
export interface Ledger {
|
|
50
|
+
/** Files a stage's result and hands its data straight back. */
|
|
51
|
+
record<T>(stage: StageId, result: Recorded<T>): T;
|
|
52
|
+
/**
|
|
53
|
+
* Waits for every price still in flight and files it against its stage.
|
|
54
|
+
*
|
|
55
|
+
* Called once, at the end. Each stage hands back a promise rather than
|
|
56
|
+
* waiting on it, so a run pays the report round trip once in parallel instead
|
|
57
|
+
* of fifteen times in series — the money is not read until here anyway.
|
|
58
|
+
*/
|
|
59
|
+
settle(): Promise<void>;
|
|
60
|
+
/** Everything filed, in the order the stages ran. */
|
|
61
|
+
entries(): Entry[];
|
|
62
|
+
/**
|
|
63
|
+
* What 6C compiles from.
|
|
64
|
+
*
|
|
65
|
+
* Keyed by stage id and name together, because the model writes one of them
|
|
66
|
+
* back as `grounds[].source_stage` and either has to lead to the entry.
|
|
67
|
+
*/
|
|
68
|
+
artifacts(): Record<string, unknown>;
|
|
69
|
+
/**
|
|
70
|
+
* Notes a gate sending work backwards, and says whether it may.
|
|
71
|
+
*
|
|
72
|
+
* False once the budget is spent, which is the run's cue to stop looping and
|
|
73
|
+
* ask for a person instead.
|
|
74
|
+
*/
|
|
75
|
+
retry(gate: StageId): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Forgets that stage and everything after it.
|
|
78
|
+
*
|
|
79
|
+
* What it cost stays: tokens were spent and rollouts were recorded, and a
|
|
80
|
+
* retry does not un-spend them. Only the artifacts go, so nothing stale is
|
|
81
|
+
* compiled into the answer.
|
|
82
|
+
*/
|
|
83
|
+
dropFrom(stage: StageId): void;
|
|
84
|
+
/** How many times each gate sent work back. */
|
|
85
|
+
retries(): Partial<Record<StageId, number>>;
|
|
86
|
+
/** Stops the run and asks for a person. */
|
|
87
|
+
escalate(from: StageId, reason: string): void;
|
|
88
|
+
escalated(): Escalation | undefined;
|
|
89
|
+
/** Every token the run was billed for, whatever a back edge later dropped. */
|
|
90
|
+
usage(): Usage;
|
|
91
|
+
/** The same totals split by stage, summed over every time the loop ran it. */
|
|
92
|
+
byStage(): StageUsage[];
|
|
93
|
+
/** And split by model, which is the only split a price list can be applied to. */
|
|
94
|
+
byModel(): Record<string, Usage>;
|
|
95
|
+
rolloutIds: string[];
|
|
96
|
+
inputTokens: number;
|
|
97
|
+
outputTokens: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* What a run leaves behind, as one thing.
|
|
101
|
+
*
|
|
102
|
+
* A branch that keeps its own artifacts hands 6C a different shape from the
|
|
103
|
+
* next branch, and nothing can invalidate a stale one on a retry. Cost is
|
|
104
|
+
* counted here too, because the graph underneath reads a node's tokens off its
|
|
105
|
+
* return value — and a branch that returns text reports none.
|
|
106
|
+
*
|
|
107
|
+
* `maxRetries` is a total across every gate: one gate flapping and five gates
|
|
108
|
+
* failing once each are the same amount of a run going nowhere.
|
|
109
|
+
*/
|
|
110
|
+
export declare function newLedger(maxRetries?: number, emit?: Emit): Ledger;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { addUsage, NO_USAGE, usageOf } from "../usage.js";
|
|
2
|
+
import { digestOne } from "./artifacts.js";
|
|
3
|
+
/** Every stage the loop can run, in the order it runs them. */
|
|
4
|
+
export const STAGES = [
|
|
5
|
+
"0",
|
|
6
|
+
"1",
|
|
7
|
+
"2",
|
|
8
|
+
"3A",
|
|
9
|
+
"3B",
|
|
10
|
+
"3C",
|
|
11
|
+
"4",
|
|
12
|
+
"4A",
|
|
13
|
+
"4B",
|
|
14
|
+
"5A",
|
|
15
|
+
"5B",
|
|
16
|
+
"5",
|
|
17
|
+
"5C",
|
|
18
|
+
"6A",
|
|
19
|
+
"6B",
|
|
20
|
+
"6C",
|
|
21
|
+
"R",
|
|
22
|
+
];
|
|
23
|
+
/** What a stage is called where a person or a model has to read it. */
|
|
24
|
+
export const STAGE_NAMES = {
|
|
25
|
+
"0": "triage",
|
|
26
|
+
"1": "question brief",
|
|
27
|
+
"2": "decomposition",
|
|
28
|
+
"3A": "plan candidates",
|
|
29
|
+
"3B": "premortem",
|
|
30
|
+
"3C": "cqot gate",
|
|
31
|
+
"4": "execution",
|
|
32
|
+
"4A": "think checkpoint",
|
|
33
|
+
"4B": "discriminating test",
|
|
34
|
+
"5A": "verification questions",
|
|
35
|
+
"5B": "verification answers",
|
|
36
|
+
"5": "verification",
|
|
37
|
+
"5C": "standards review",
|
|
38
|
+
"6A": "closure checklist",
|
|
39
|
+
"6B": "red team",
|
|
40
|
+
"6C": "answer",
|
|
41
|
+
R: "root cause",
|
|
42
|
+
};
|
|
43
|
+
const DEFAULT_MAX_RETRIES = 5;
|
|
44
|
+
const rolloutsOf = (result) => "rolloutIds" in result ? result.rolloutIds : [result.rolloutId];
|
|
45
|
+
/**
|
|
46
|
+
* What a run leaves behind, as one thing.
|
|
47
|
+
*
|
|
48
|
+
* A branch that keeps its own artifacts hands 6C a different shape from the
|
|
49
|
+
* next branch, and nothing can invalidate a stale one on a retry. Cost is
|
|
50
|
+
* counted here too, because the graph underneath reads a node's tokens off its
|
|
51
|
+
* return value — and a branch that returns text reports none.
|
|
52
|
+
*
|
|
53
|
+
* `maxRetries` is a total across every gate: one gate flapping and five gates
|
|
54
|
+
* failing once each are the same amount of a run going nowhere.
|
|
55
|
+
*/
|
|
56
|
+
export function newLedger(maxRetries = DEFAULT_MAX_RETRIES, emit) {
|
|
57
|
+
const filed = [];
|
|
58
|
+
const spentOn = [];
|
|
59
|
+
const pending = [];
|
|
60
|
+
const backEdges = {};
|
|
61
|
+
let stopped;
|
|
62
|
+
let spent = 0;
|
|
63
|
+
const ledger = {
|
|
64
|
+
rolloutIds: [],
|
|
65
|
+
inputTokens: 0,
|
|
66
|
+
outputTokens: 0,
|
|
67
|
+
record(stage, result) {
|
|
68
|
+
const rolloutIds = rolloutsOf(result);
|
|
69
|
+
const entry = {
|
|
70
|
+
...usageOf(result),
|
|
71
|
+
stage,
|
|
72
|
+
data: result.data,
|
|
73
|
+
rolloutIds,
|
|
74
|
+
model: result.model,
|
|
75
|
+
latencyMs: result.latencyMs,
|
|
76
|
+
};
|
|
77
|
+
filed.push(entry);
|
|
78
|
+
spentOn.push(entry);
|
|
79
|
+
if (result.pricing)
|
|
80
|
+
pending.push({ entry, pricing: result.pricing });
|
|
81
|
+
ledger.rolloutIds.push(...rolloutIds);
|
|
82
|
+
ledger.inputTokens += entry.inputTokens;
|
|
83
|
+
ledger.outputTokens += entry.outputTokens;
|
|
84
|
+
emit?.({
|
|
85
|
+
type: "stage.finished",
|
|
86
|
+
stage,
|
|
87
|
+
name: STAGE_NAMES[stage],
|
|
88
|
+
artifact: digestOne(entry.data),
|
|
89
|
+
model: entry.model,
|
|
90
|
+
rolloutIds,
|
|
91
|
+
usage: usageOf(entry),
|
|
92
|
+
latencyMs: entry.latencyMs,
|
|
93
|
+
});
|
|
94
|
+
return result.data;
|
|
95
|
+
},
|
|
96
|
+
async settle() {
|
|
97
|
+
const priced = await Promise.all(pending.map((one) => one.pricing));
|
|
98
|
+
priced.forEach((cost, i) => {
|
|
99
|
+
if (cost === undefined)
|
|
100
|
+
return;
|
|
101
|
+
const entry = pending[i]?.entry;
|
|
102
|
+
if (entry)
|
|
103
|
+
entry.costMicros = (entry.costMicros ?? 0) + cost;
|
|
104
|
+
});
|
|
105
|
+
pending.length = 0;
|
|
106
|
+
},
|
|
107
|
+
entries: () => [...filed],
|
|
108
|
+
artifacts: () => Object.fromEntries([...filed]
|
|
109
|
+
.sort((a, b) => STAGES.indexOf(a.stage) - STAGES.indexOf(b.stage))
|
|
110
|
+
.map((entry) => [`${entry.stage} ${STAGE_NAMES[entry.stage]}`, entry.data])),
|
|
111
|
+
retry(gate) {
|
|
112
|
+
backEdges[gate] = (backEdges[gate] ?? 0) + 1;
|
|
113
|
+
spent += 1;
|
|
114
|
+
const granted = spent <= maxRetries;
|
|
115
|
+
emit?.({
|
|
116
|
+
type: "retry.spent",
|
|
117
|
+
gate,
|
|
118
|
+
spent,
|
|
119
|
+
left: Math.max(0, maxRetries - spent),
|
|
120
|
+
budget: maxRetries,
|
|
121
|
+
granted,
|
|
122
|
+
});
|
|
123
|
+
return granted;
|
|
124
|
+
},
|
|
125
|
+
dropFrom(stage) {
|
|
126
|
+
const cutAt = STAGES.indexOf(stage);
|
|
127
|
+
const dropped = [];
|
|
128
|
+
for (let i = filed.length - 1; i >= 0; i -= 1) {
|
|
129
|
+
const entry = filed[i];
|
|
130
|
+
if (entry && STAGES.indexOf(entry.stage) >= cutAt) {
|
|
131
|
+
dropped.push(entry.stage);
|
|
132
|
+
filed.splice(i, 1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (dropped.length) {
|
|
136
|
+
emit?.({ type: "ledger.dropped", from: stage, dropped: [...new Set(dropped)] });
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
retries: () => ({ ...backEdges }),
|
|
140
|
+
usage: () => spentOn.reduce(addUsage, NO_USAGE),
|
|
141
|
+
byStage() {
|
|
142
|
+
const totals = new Map();
|
|
143
|
+
for (const entry of spentOn) {
|
|
144
|
+
const running = totals.get(entry.stage) ?? {
|
|
145
|
+
...NO_USAGE,
|
|
146
|
+
stage: entry.stage,
|
|
147
|
+
name: STAGE_NAMES[entry.stage],
|
|
148
|
+
models: [],
|
|
149
|
+
rolloutIds: [],
|
|
150
|
+
latencyMs: 0,
|
|
151
|
+
calls: 0,
|
|
152
|
+
};
|
|
153
|
+
totals.set(entry.stage, {
|
|
154
|
+
...running,
|
|
155
|
+
...addUsage(running, entry),
|
|
156
|
+
models: running.models.includes(entry.model)
|
|
157
|
+
? running.models
|
|
158
|
+
: [...running.models, entry.model].filter(Boolean),
|
|
159
|
+
rolloutIds: [...running.rolloutIds, ...entry.rolloutIds],
|
|
160
|
+
latencyMs: running.latencyMs + entry.latencyMs,
|
|
161
|
+
calls: running.calls + 1,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return [...totals.values()].sort((a, b) => STAGES.indexOf(a.stage) - STAGES.indexOf(b.stage));
|
|
165
|
+
},
|
|
166
|
+
byModel() {
|
|
167
|
+
const totals = {};
|
|
168
|
+
for (const entry of spentOn) {
|
|
169
|
+
if (!entry.model)
|
|
170
|
+
continue;
|
|
171
|
+
totals[entry.model] = addUsage(totals[entry.model] ?? NO_USAGE, entry);
|
|
172
|
+
}
|
|
173
|
+
return totals;
|
|
174
|
+
},
|
|
175
|
+
escalate(from, reason) {
|
|
176
|
+
if (stopped)
|
|
177
|
+
return;
|
|
178
|
+
stopped = { from, reason };
|
|
179
|
+
emit?.({ type: "run.escalated", from, reason });
|
|
180
|
+
},
|
|
181
|
+
escalated: () => stopped,
|
|
182
|
+
};
|
|
183
|
+
return ledger;
|
|
184
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Llm } from "@spendgraph/llms";
|
|
2
|
+
import type { PromptClient } from "@spendgraph/prompt";
|
|
3
|
+
import type { Draft } from "../draft/index.js";
|
|
4
|
+
import type { NoesisOptions } from "../types.js";
|
|
5
|
+
import type { RunState } from "./state.js";
|
|
6
|
+
export interface Gates {
|
|
7
|
+
/** 5C. Full only. */
|
|
8
|
+
standards: boolean;
|
|
9
|
+
/** 6B. Set from stakes, so a full run reached another way does not pay for it. */
|
|
10
|
+
redTeam: boolean;
|
|
11
|
+
/** Everything up to the draft, run again. */
|
|
12
|
+
reproduce(): Promise<Draft>;
|
|
13
|
+
}
|
|
14
|
+
/** Check the draft, and keep going back until every gate lets it close. */
|
|
15
|
+
export declare function settle(client: PromptClient, llm: Llm, question: string, opts: NoesisOptions, state: RunState, first: Draft, gates: Gates): Promise<void>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { invoke as closureChecklist } from "../gates/closure-checklist/index.js";
|
|
2
|
+
import { invoke as redTeam } from "../gates/red-team/index.js";
|
|
3
|
+
import { invoke as standardsReview } from "../gates/standards-review/index.js";
|
|
4
|
+
import { STAGE_NAMES } from "./ledger.js";
|
|
5
|
+
import { dropPoint, resumeAt } from "./redo.js";
|
|
6
|
+
import { checks, reflect } from "./verify.js";
|
|
7
|
+
/**
|
|
8
|
+
* One return trip: spend a retry, find the root cause, cut the ledger back.
|
|
9
|
+
*
|
|
10
|
+
* The budget is checked before R runs, because a run with nothing left to spend
|
|
11
|
+
* has no use for knowing where it went wrong.
|
|
12
|
+
*
|
|
13
|
+
* The ledger is cut where the loop actually restarts, not where R pointed. A
|
|
14
|
+
* gate is the same gate on the same input, so anything it names redoes the
|
|
15
|
+
* checks — and cutting only from the gate would leave the stale checks filed
|
|
16
|
+
* beside the fresh ones.
|
|
17
|
+
*/
|
|
18
|
+
async function stepBack(client, llm, question, opts, state, gates, gate, reason, hint) {
|
|
19
|
+
if (!state.ledger.retry(gate)) {
|
|
20
|
+
state.ledger.escalate(gate, `Out of retries. ${gate} last said: ${reason}`);
|
|
21
|
+
return { stopped: true };
|
|
22
|
+
}
|
|
23
|
+
const redo = await reflect(client, llm, question, opts, state, gate, reason, hint);
|
|
24
|
+
const again = resumeAt(redo);
|
|
25
|
+
state.ledger.dropFrom(again === "produce" ? dropPoint(redo) : "5A");
|
|
26
|
+
return { stopped: false, draft: again === "produce" ? await gates.reproduce() : undefined };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The gates, in the order they earn their place.
|
|
30
|
+
*
|
|
31
|
+
* 5C reads the answer against its standards, 6A asks the last questions before
|
|
32
|
+
* closing, and 6B attacks what survived both. The first refusal returns, so a
|
|
33
|
+
* gate that fires early saves the later ones the work.
|
|
34
|
+
*/
|
|
35
|
+
async function judge(client, llm, opts, state, checked, gates) {
|
|
36
|
+
if (gates.standards) {
|
|
37
|
+
const review = state.ledger.record("5C", await standardsReview(client, llm, { question: checked, ...opts.context }, opts.stage));
|
|
38
|
+
if (review.overall !== "accept") {
|
|
39
|
+
return {
|
|
40
|
+
stage: "5C",
|
|
41
|
+
reason: `the answer ${review.overall}s on ${review.failed.join(", ")}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
state.emit?.({ type: "gate.passed", stage: "5C", name: STAGE_NAMES["5C"] });
|
|
45
|
+
}
|
|
46
|
+
const closure = state.ledger.record("6A", await closureChecklist(client, llm, { question: checked, ...opts.context }, opts.stage));
|
|
47
|
+
if (closure.gate !== "close") {
|
|
48
|
+
return {
|
|
49
|
+
stage: "6A",
|
|
50
|
+
reason: closure.reason,
|
|
51
|
+
hint: closure.loop_back_to,
|
|
52
|
+
escalate: closure.gate === "escalate",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
state.emit?.({ type: "gate.passed", stage: "6A", name: STAGE_NAMES["6A"] });
|
|
56
|
+
if (gates.redTeam) {
|
|
57
|
+
const attacked = state.ledger.record("6B", await redTeam(client, llm, { question: checked, ...opts.context }, opts.stage));
|
|
58
|
+
if (attacked.verdict !== "survives") {
|
|
59
|
+
return { stage: "6B", reason: `${attacked.verdict}: ${attacked.would_break_it}` };
|
|
60
|
+
}
|
|
61
|
+
state.emit?.({ type: "gate.passed", stage: "6B", name: STAGE_NAMES["6B"] });
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
/** Check the draft, and keep going back until every gate lets it close. */
|
|
66
|
+
export async function settle(client, llm, question, opts, state, first, gates) {
|
|
67
|
+
if (state.ledger.escalated())
|
|
68
|
+
return;
|
|
69
|
+
let draft = first;
|
|
70
|
+
for (;;) {
|
|
71
|
+
const checked = await checks(client, llm, opts, state, draft);
|
|
72
|
+
const refused = await judge(client, llm, opts, state, checked, gates);
|
|
73
|
+
if (!refused)
|
|
74
|
+
return;
|
|
75
|
+
state.emit?.({
|
|
76
|
+
type: "gate.refused",
|
|
77
|
+
stage: refused.stage,
|
|
78
|
+
name: STAGE_NAMES[refused.stage],
|
|
79
|
+
reason: refused.reason,
|
|
80
|
+
hint: refused.hint,
|
|
81
|
+
});
|
|
82
|
+
if (refused.escalate) {
|
|
83
|
+
state.ledger.escalate(refused.stage, refused.reason);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const went = await stepBack(client, llm, question, opts, state, gates, refused.stage, refused.reason, refused.hint);
|
|
87
|
+
if (went.stopped)
|
|
88
|
+
return;
|
|
89
|
+
draft = went.draft ?? draft;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Llm } from "@spendgraph/llms";
|
|
2
|
+
import type { PromptClient } from "@spendgraph/prompt";
|
|
3
|
+
import type { NoesisOptions } from "../types.js";
|
|
4
|
+
import type { RunState } from "./state.js";
|
|
5
|
+
/**
|
|
6
|
+
* Stage 3A, and the audits only `full` pays for.
|
|
7
|
+
*
|
|
8
|
+
* Both long routes plan, because 4A judges each step against the `tolerance` 3A
|
|
9
|
+
* wrote for it and a run with no plan has nothing to check against. What `full`
|
|
10
|
+
* adds is 3B and 3C: the plan assumed dead, then gated.
|
|
11
|
+
*
|
|
12
|
+
* Chained rather than forked, because 3C points at the step at fault and 3B may
|
|
13
|
+
* have patched that step. Run side by side, 3C would gate a plan 3B had already
|
|
14
|
+
* said was wrong. A patch is prose, so `patch_and_proceed` proceeds and the
|
|
15
|
+
* premortem is filed — 3C reads the plan with the patches beside it rather than
|
|
16
|
+
* a plan nobody rewrote.
|
|
17
|
+
*/
|
|
18
|
+
export declare function plan(client: PromptClient, llm: Llm, question: string, opts: NoesisOptions, state: RunState, audits: boolean): Promise<void>;
|