@yaag/cli 0.1.2 → 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/assets/types/runtime/agent.d.ts +4 -2
- package/assets/types/runtime/ask-contract-identity.d.ts +11 -2
- package/assets/types/runtime/ask-exchange-events.d.ts +11 -1
- package/assets/types/runtime/ask-exchange-options.d.ts +7 -2
- package/assets/types/runtime/ask-hash.d.ts +4 -3
- package/assets/types/runtime/ask-limit.d.ts +2 -0
- package/assets/types/runtime/ask-output-steering.d.ts +0 -2
- package/assets/types/runtime/ask-output.d.ts +16 -4
- package/assets/types/runtime/ask-turn.d.ts +5 -0
- package/assets/types/runtime/cassette-loader.d.ts +10 -0
- package/assets/types/runtime/cassette-schema.d.ts +9 -1
- package/assets/types/runtime/cassette.d.ts +4 -2
- package/assets/types/runtime/checkpoint-flush.d.ts +31 -0
- package/assets/types/runtime/connection.d.ts +12 -0
- package/assets/types/runtime/define-agent.d.ts +10 -5
- package/assets/types/runtime/errors.d.ts +7 -1
- package/assets/types/runtime/events.d.ts +24 -2
- package/assets/types/runtime/fake-transport.d.ts +10 -0
- package/assets/types/runtime/frame-queue.d.ts +2 -0
- package/assets/types/runtime/index.d.ts +3 -2
- package/assets/types/runtime/model-resolution.d.ts +45 -0
- package/assets/types/runtime/model-suffix.d.ts +16 -0
- package/assets/types/runtime/pi-state.d.ts +16 -0
- package/assets/types/runtime/report-result-extension.d.ts +44 -0
- package/assets/types/runtime/report-result-output.d.ts +37 -0
- package/assets/types/runtime/report-result-steering.d.ts +35 -0
- package/assets/types/runtime/report-result.d.ts +66 -0
- package/assets/types/runtime/run-checkpoint.d.ts +35 -13
- package/assets/types/runtime/run.d.ts +3 -2
- package/assets/types/runtime/stall-watchdog.d.ts +70 -0
- package/assets/types/runtime/summary-agent.d.ts +7 -1
- package/assets/types/runtime/summary.d.ts +10 -0
- package/assets/types/runtime/thinking-level.d.ts +11 -0
- package/assets/types/runtime/transport.d.ts +41 -4
- package/assets/types/runtime/types.d.ts +49 -14
- package/package.json +5 -5
- package/src/cli.ts +19 -10
- package/src/interactive-run.ts +3 -2
- package/src/output-channel.ts +35 -0
- package/src/presenter.ts +2 -1
- package/src/runtime-alias.ts +6 -8
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TSchema } from "typebox";
|
|
2
|
+
import type { AskOutputResult } from "./ask-output.ts";
|
|
3
|
+
import type { ReportedResult } from "./report-result.ts";
|
|
4
|
+
import type { AskInvalidOutputPlayback } from "./transport.ts";
|
|
5
|
+
/** The one correction an Agent gets when it settles without reporting a result. */
|
|
6
|
+
export declare const REPORT_RESULT_CORRECTION: string;
|
|
7
|
+
/** Appended to a limit wrap-up so the Agent still ends on a reported result. */
|
|
8
|
+
export declare const REPORT_RESULT_WRAP_UP = "End by calling report_result exactly once with the final result.";
|
|
9
|
+
/** What `ASK_INVALID_OUTPUT` carries when no `report_result` call ever arrived. */
|
|
10
|
+
export declare const REPORT_RESULT_MISSING_ERRORS: readonly string[];
|
|
11
|
+
/** What a rethrown recorded failure carries when its recorded value now validates. */
|
|
12
|
+
export declare const REPORT_RESULT_RECORDED_FAILURE_ERRORS: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Validates one reported result against the Ask's schema, without coercion.
|
|
15
|
+
*
|
|
16
|
+
* pi validates and coerces the call arguments first, so a failure here means
|
|
17
|
+
* the recorded or reported value does not match the schema the Ask declared.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateReportedResult(value: unknown, schema: TSchema): AskOutputResult;
|
|
20
|
+
/** One recorded structured Ask, as playback resolves it. */
|
|
21
|
+
export interface PlaybackReportedResult {
|
|
22
|
+
readonly agent: string;
|
|
23
|
+
readonly schema: TSchema;
|
|
24
|
+
/** The call the Cassette recorded, absent when the Ask reported nothing. */
|
|
25
|
+
readonly reported: ReportedResult | undefined;
|
|
26
|
+
/** The invalid-output outcome the recorded Ask surfaced, when it surfaced one. */
|
|
27
|
+
readonly recordedOutcome: AskInvalidOutputPlayback | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolves a replayed structured Ask from the `report_result` call in its frames.
|
|
31
|
+
*
|
|
32
|
+
* A recorded invalid-output outcome rethrows deterministically, exactly as the
|
|
33
|
+
* recorded Run failed (ADR-0032). Otherwise the recorded call's value is
|
|
34
|
+
* re-validated, so playback and live settlement agree. Throws the same
|
|
35
|
+
* recoverable `ASK_INVALID_OUTPUT` error as a live Ask.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolvePlaybackReportedResult(playback: PlaybackReportedResult): unknown;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TSchema } from "typebox";
|
|
2
|
+
import type { ReportedResult } from "./report-result.ts";
|
|
3
|
+
/** What one Ask's report-result settlement needs from its exchange. */
|
|
4
|
+
export interface ReportResultSteeringOptions {
|
|
5
|
+
readonly agent: string;
|
|
6
|
+
readonly schema: TSchema;
|
|
7
|
+
readonly maxSteers: number | undefined;
|
|
8
|
+
/** The accepted `report_result` call of the settled turn, when there is one. */
|
|
9
|
+
readonly reported: () => ReportedResult | undefined;
|
|
10
|
+
/** Atomically enters a correction effort, or false after a limit starts wrap-up. */
|
|
11
|
+
readonly beginCorrection: () => boolean;
|
|
12
|
+
/** True once a soft limit has sent its wrap-up steer. */
|
|
13
|
+
readonly wrappingUp: () => boolean;
|
|
14
|
+
/** Re-arms settlement tracking, sends the correction, and waits for its settlement. */
|
|
15
|
+
readonly correct: (message: string) => Promise<void>;
|
|
16
|
+
/** Re-arms settlement tracking, sends abort, and waits for its settlement. */
|
|
17
|
+
readonly abort: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Settles a schema-bearing Ask from its `report_result` call (ADR-0032).
|
|
21
|
+
*
|
|
22
|
+
* Only a settlement without a valid call costs a steering effort: repairing
|
|
23
|
+
* invalid arguments is pi's own tool-retry loop, bounded by the Ask's outer
|
|
24
|
+
* envelope. A reported value that still fails validation cannot be repaired by
|
|
25
|
+
* a correction prompt, so it fails the Ask directly.
|
|
26
|
+
*
|
|
27
|
+
* Control-command failures propagate from `correct` or `abort` as Agent
|
|
28
|
+
* failures, rather than being reported as invalid-output exhaustion.
|
|
29
|
+
*/
|
|
30
|
+
export declare class ReportResultSteering {
|
|
31
|
+
#private;
|
|
32
|
+
constructor(options: ReportResultSteeringOptions);
|
|
33
|
+
/** Resolves the validated reported value, or rejects after the abort settlement. */
|
|
34
|
+
resolve(): Promise<unknown>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CanonicalJsonObject } from "./ask-contract-identity.ts";
|
|
2
|
+
import type { Frame } from "./transport.ts";
|
|
3
|
+
/** The internal tool a schema-bearing Ask settles from (ADR-0032). */
|
|
4
|
+
export declare const REPORT_RESULT_TOOL_NAME = "report_result";
|
|
5
|
+
/** The pi extension command that carries one Ask's output schema to the Agent. */
|
|
6
|
+
export declare const REPORT_RESULT_COMMAND = "yaag-report-result";
|
|
7
|
+
/** Loadable source path for the private extension that owns `report_result`. */
|
|
8
|
+
export declare const REPORT_RESULT_EXTENSION_PATH: string;
|
|
9
|
+
/**
|
|
10
|
+
* The prompt message that delivers one Ask's schema, or deactivates the tool.
|
|
11
|
+
*
|
|
12
|
+
* pi routes a `/command` prompt to the extension and never to the model, so the
|
|
13
|
+
* Ask's own prompt bytes stay exactly what the Orchestration Program wrote.
|
|
14
|
+
*/
|
|
15
|
+
export declare function reportResultCommandMessage(schema: CanonicalJsonObject | null): string;
|
|
16
|
+
/** True exactly for a prompt frame that carries the private schema command. */
|
|
17
|
+
export declare function isReportResultCommandFrame(frame: Frame): boolean;
|
|
18
|
+
/** One schema command, and the state change the Agent accepting it earns. */
|
|
19
|
+
export interface ReportResultCommand {
|
|
20
|
+
readonly message: string;
|
|
21
|
+
/** Records the new activation state, once the Agent has accepted the command. */
|
|
22
|
+
accepted(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Tracks whether one Agent's `report_result` tool is active across its Asks.
|
|
26
|
+
*
|
|
27
|
+
* The tool is inactive at spawn, so an Agent that never declares an output
|
|
28
|
+
* schema exchanges exactly the frames it did before ADR-0032. A schema Ask
|
|
29
|
+
* always re-delivers its schema, because the schema is the tool's input schema.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ReportResultTool {
|
|
32
|
+
#private;
|
|
33
|
+
/** The command to send before this Ask's prompt, or null when none is needed. */
|
|
34
|
+
begin(schema: CanonicalJsonObject | undefined): ReportResultCommand | null;
|
|
35
|
+
}
|
|
36
|
+
/** The value one accepted `report_result` call reported. */
|
|
37
|
+
export interface ReportedResult {
|
|
38
|
+
readonly value: unknown;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Collects the first accepted `report_result` call from one Ask's frames.
|
|
42
|
+
*
|
|
43
|
+
* The reported value is read from the tool result rather than from the call
|
|
44
|
+
* arguments, because pi coerces arguments before it runs the tool, and the
|
|
45
|
+
* coerced value is the one it validated. A refused call — invalid arguments,
|
|
46
|
+
* or a second call after the result was reported — carries an error and never
|
|
47
|
+
* becomes the Ask's result. Live settlement and Cassette playback read the
|
|
48
|
+
* same frames.
|
|
49
|
+
*/
|
|
50
|
+
export declare class ReportResultCall {
|
|
51
|
+
#private;
|
|
52
|
+
/**
|
|
53
|
+
* Accepts calls immediately, for folding one recorded Ask's own frames.
|
|
54
|
+
*
|
|
55
|
+
* A live Ask uses the ordinary constructor instead, which ignores everything
|
|
56
|
+
* until that Ask sends its prompt: a replayed prefix and an abandoned turn
|
|
57
|
+
* both deliver frames after the Ask they belong to ended, and such a call
|
|
58
|
+
* must never become the next Ask's result.
|
|
59
|
+
*/
|
|
60
|
+
static armed(): ReportResultCall;
|
|
61
|
+
/** Starts accepting calls, from the moment this Ask's prompt is sent. */
|
|
62
|
+
arm(): void;
|
|
63
|
+
observe(frame: Frame): void;
|
|
64
|
+
/** The accepted call, or undefined while the Ask has no reported result. */
|
|
65
|
+
get reported(): ReportedResult | undefined;
|
|
66
|
+
}
|
|
@@ -1,28 +1,50 @@
|
|
|
1
|
-
import type { CassetteCollector } from "./cassette.ts";
|
|
1
|
+
import type { CassetteCollector, CassetteRun } from "./cassette.ts";
|
|
2
2
|
import type { RunOutcome } from "./events.ts";
|
|
3
|
+
/** What a resume is about to re-execute; resolved once, at Run start. */
|
|
4
|
+
export type RunIdentity = Omit<CassetteRun, "outcome">;
|
|
3
5
|
/**
|
|
4
6
|
* One Run's publication request: what settled, what it collected, and where the
|
|
5
|
-
* artifact
|
|
6
|
-
*
|
|
7
|
+
* artifact goes. The destination is fixed at Run start (ADR-0031): the
|
|
8
|
+
* `--record` path itself, or one name in the checkpoint directory.
|
|
7
9
|
*/
|
|
8
10
|
export interface RunCheckpointOptions {
|
|
9
|
-
/** Terminal outcome of the Run; only `stopped`
|
|
11
|
+
/** Terminal outcome of the Run; only `stopped` keeps the artifact without `record`. */
|
|
10
12
|
readonly outcome: RunOutcome;
|
|
11
13
|
readonly collector: CassetteCollector;
|
|
12
|
-
/** Explicit `--record` destination
|
|
14
|
+
/** Explicit `--record` destination, or undefined when the Run only checkpoints. */
|
|
13
15
|
readonly record: string | undefined;
|
|
14
|
-
/**
|
|
15
|
-
readonly
|
|
16
|
-
/**
|
|
17
|
-
readonly
|
|
18
|
-
readonly args: unknown;
|
|
16
|
+
/** Where this Run's artifact lives, in-flight and at settlement. */
|
|
17
|
+
readonly destination: string;
|
|
18
|
+
/** Invocation identity written into the artifact's run block. */
|
|
19
|
+
readonly identity: RunIdentity;
|
|
19
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* What one publication attempt did. `published` covers both a written artifact
|
|
23
|
+
* and a Run that keeps none. `displaced` carries the error that must become the
|
|
24
|
+
* Run's outcome. `lost` reports a Checkpoint the Run asked for and did not get,
|
|
25
|
+
* while a primary program error keeps the outcome.
|
|
26
|
+
*/
|
|
27
|
+
export type RunCheckpointResult = {
|
|
28
|
+
readonly kind: "published";
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: "displaced";
|
|
31
|
+
readonly error: unknown;
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: "lost";
|
|
34
|
+
readonly message: string;
|
|
35
|
+
};
|
|
20
36
|
/**
|
|
21
37
|
* Publishes the collected Cassette when the Run must be restorable: always under
|
|
22
38
|
* `--record`, and additionally into the checkpoint directory when the Run
|
|
23
|
-
* stopped (ADR-0021/0024).
|
|
24
|
-
|
|
39
|
+
* stopped (ADR-0021/0024).
|
|
40
|
+
*/
|
|
41
|
+
export declare function publishRunCheckpoint(options: RunCheckpointOptions): Promise<RunCheckpointResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Creates the checkpoint directory a Run owns. An explicit `--record` path keeps
|
|
44
|
+
* today's rule that its directory must already exist.
|
|
25
45
|
*/
|
|
26
|
-
export declare function
|
|
46
|
+
export declare function ensureCheckpointDirectory(record: string | undefined, destination: string): Promise<void>;
|
|
47
|
+
/** Resolves what a resume re-executes. The program hash is advisory only. */
|
|
48
|
+
export declare function resolveRunIdentity(programFile: string | undefined, args: unknown): Promise<RunIdentity>;
|
|
27
49
|
/** Reports a secondary Cassette publication failure without displacing the Run's primary error. */
|
|
28
50
|
export declare function writeRecordingDiagnostic(error: unknown): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Cassette,
|
|
1
|
+
import { type Cassette, type CassetteSink } from "./cassette.ts";
|
|
2
2
|
import { type OrchestrationProgram } from "./define-run.ts";
|
|
3
3
|
import type { StampedEventSink } from "./events.ts";
|
|
4
4
|
import { type SkillProbeFactory } from "./skill-probe.ts";
|
|
@@ -44,7 +44,8 @@ export declare function executeRun<Args, Result>(program: OrchestrationProgram<A
|
|
|
44
44
|
*/
|
|
45
45
|
export interface FactorySelection {
|
|
46
46
|
readonly options: RunOptions;
|
|
47
|
-
|
|
47
|
+
/** Receives every frame; wrapped so Ask boundaries flush the Checkpoint (ADR-0031). */
|
|
48
|
+
readonly collector: CassetteSink;
|
|
48
49
|
readonly replay: Cassette | null;
|
|
49
50
|
readonly resume: Cassette | null;
|
|
50
51
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CommandResponse } from "./connection.ts";
|
|
2
|
+
import type { AskStalledOutcome } from "./errors.ts";
|
|
3
|
+
import type { Frame } from "./transport.ts";
|
|
4
|
+
/** Silence allowed on a live Ask before the Stall Watchdog probes the Agent. */
|
|
5
|
+
export declare const DEFAULT_STALL_MS = 600000;
|
|
6
|
+
/** Time allowed for the `get_state` probe to answer before yaag kills the Agent. */
|
|
7
|
+
export declare const STALL_PROBE_SETTLE_MS = 45000;
|
|
8
|
+
/** Why an Ask settled: normal frames, watchdog recovery, or a stall rejection. */
|
|
9
|
+
export type SettlementCause = "normal" | "recovered" | "stalled";
|
|
10
|
+
/** Rejection carried by {@link StallWatchdog.failed} for both stall outcomes. */
|
|
11
|
+
export declare class StallSignal extends Error {
|
|
12
|
+
readonly outcome: AskStalledOutcome;
|
|
13
|
+
constructor(outcome: AskStalledOutcome);
|
|
14
|
+
}
|
|
15
|
+
export interface StallWatchdogOptions {
|
|
16
|
+
/** Silence, in milliseconds, that arms the probe. */
|
|
17
|
+
readonly stallMs: number;
|
|
18
|
+
/** Bounded wait for the probe response; defaults to 45s. */
|
|
19
|
+
readonly probeSettleMs?: number;
|
|
20
|
+
/** Sends one command to the Agent and resolves with its response. */
|
|
21
|
+
readonly command: (frame: Frame) => Promise<CommandResponse>;
|
|
22
|
+
/** True when this Ask already observed a terminal assistant `message_end`. */
|
|
23
|
+
readonly terminal: () => boolean;
|
|
24
|
+
/** Settles the Ask from the observed terminal state. */
|
|
25
|
+
readonly recover: () => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Default liveness guard for every live Ask (ADR-0029).
|
|
29
|
+
*
|
|
30
|
+
* It measures silence, not wall time: any Agent frame resets the timer, so a
|
|
31
|
+
* legitimately long Ask is never punished. On expiry it sends one `get_state`
|
|
32
|
+
* probe and takes one of three exits:
|
|
33
|
+
* - the probe answers, the Agent is not working, and this Ask already saw a
|
|
34
|
+
* terminal assistant `message_end` — the Ask settles as `recovered`;
|
|
35
|
+
* - the probe answers and the Agent is still working — the Ask fails with a
|
|
36
|
+
* recoverable `ASK_STALLED` and the Agent stays alive;
|
|
37
|
+
* - the probe stays silent — the Ask fails with a destructive `ASK_STALLED`
|
|
38
|
+
* and the Agent is killed.
|
|
39
|
+
*/
|
|
40
|
+
export declare class StallWatchdog {
|
|
41
|
+
#private;
|
|
42
|
+
constructor(options: StallWatchdogOptions);
|
|
43
|
+
/** Arms the silence timer immediately before the prompt command is sent. */
|
|
44
|
+
start(): void;
|
|
45
|
+
/** Resets the silence timer. Frames that arrive after the probe are ignored. */
|
|
46
|
+
observe(_frame: Frame): void;
|
|
47
|
+
/**
|
|
48
|
+
* Disarms the watchdog for a settled turn, so the commands that follow
|
|
49
|
+
* settlement — `get_last_assistant_text` and output steering — can never be
|
|
50
|
+
* mistaken for Agent silence. Idempotent.
|
|
51
|
+
*/
|
|
52
|
+
settled(): void;
|
|
53
|
+
/** Re-arms the timer for a further effort in the same Ask. Inert after a trip. */
|
|
54
|
+
rearm(): void;
|
|
55
|
+
/** Cancels every timer once the Ask leaves. Idempotent. */
|
|
56
|
+
cleanup(): void;
|
|
57
|
+
/** How this Ask settled, as reported in its `ask_end` Lifecycle Event. */
|
|
58
|
+
get cause(): SettlementCause;
|
|
59
|
+
/** The stalled outcome, once the probe protocol resolved either way. */
|
|
60
|
+
get result(): AskStalledOutcome | null;
|
|
61
|
+
/** Rejects with a {@link StallSignal} on both stall exits. */
|
|
62
|
+
get failed(): Promise<never>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the effective stall budget.
|
|
66
|
+
*
|
|
67
|
+
* `false` and any value that is not above zero disable the watchdog for one
|
|
68
|
+
* Ask. An absent value takes the 10-minute default.
|
|
69
|
+
*/
|
|
70
|
+
export declare function stallBudgetMs(stallMs: number | false | undefined): number | null;
|
|
@@ -96,7 +96,13 @@ export declare function setActivity(current: AgentRecord | undefined, index: num
|
|
|
96
96
|
* Folds an Ask settlement into the idle arm, retaining the settled Ask identity.
|
|
97
97
|
* Older stamped events are ignored and legacy unstamped events use stream order.
|
|
98
98
|
*/
|
|
99
|
-
|
|
99
|
+
/** One Ask settlement as the fold sees it. */
|
|
100
|
+
export interface AskSettlement {
|
|
101
|
+
readonly index: number;
|
|
102
|
+
/** A recovered settlement makes this Agent's accounting a floor, not a total. */
|
|
103
|
+
readonly recovered: boolean;
|
|
104
|
+
}
|
|
105
|
+
export declare function endAsk(current: AgentRecord | undefined, settlement: AskSettlement, at: number | null): AgentRecord;
|
|
100
106
|
/**
|
|
101
107
|
* Folds a cumulative usage snapshot without changing Agent lifecycle state.
|
|
102
108
|
*
|
|
@@ -9,6 +9,11 @@ export type RunState = "running" | "ended";
|
|
|
9
9
|
interface RunSummaryBase {
|
|
10
10
|
readonly program: string;
|
|
11
11
|
readonly startedAt: number | null;
|
|
12
|
+
/**
|
|
13
|
+
* Path of the Run's Checkpoint, as `run_start` named it (ADR-0031). It is
|
|
14
|
+
* null for a Run whose CLI is older than that event field.
|
|
15
|
+
*/
|
|
16
|
+
readonly artifact: string | null;
|
|
12
17
|
readonly agents: Readonly<Record<string, AgentInfo>>;
|
|
13
18
|
readonly asksStarted: number;
|
|
14
19
|
readonly asksSettled: number;
|
|
@@ -31,6 +36,11 @@ export interface EndedRunSummary extends RunSummaryBase {
|
|
|
31
36
|
readonly ok: boolean;
|
|
32
37
|
/** The `run_end.at` event fact used to reject strictly older stamped endings. */
|
|
33
38
|
readonly endedAt: number | null;
|
|
39
|
+
/**
|
|
40
|
+
* The Run asked for a Checkpoint with `--record`, failed, and lost the
|
|
41
|
+
* artifact too. Carries the publication error text (ticket 04).
|
|
42
|
+
*/
|
|
43
|
+
readonly checkpointLost?: string;
|
|
34
44
|
}
|
|
35
45
|
/**
|
|
36
46
|
* The pure, discriminated observer fold of a Run's Lifecycle Events.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi's supported thinking levels.
|
|
3
|
+
*
|
|
4
|
+
* Lives in its own leaf module so both `types.ts` and the Model Resolution
|
|
5
|
+
* modules can depend on it without forming an import cycle.
|
|
6
|
+
*/
|
|
7
|
+
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
8
|
+
/** Every thinking level pi accepts, in documented order. */
|
|
9
|
+
export declare const THINKING_LEVELS: readonly ThinkingLevel[];
|
|
10
|
+
/** True only for one of pi's thinking levels; case-sensitive and total. */
|
|
11
|
+
export declare function isThinkingLevel(value: unknown): value is ThinkingLevel;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CanonicalJsonObject } from "./ask-contract-identity.ts";
|
|
2
2
|
import type { AskLimitOutcome, AskStalledOutcome } from "./errors.ts";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ReportedResult } from "./report-result.ts";
|
|
4
|
+
import type { AskOptions, ResolvedSpawnOptions, ThinkingLevel } from "./types.ts";
|
|
4
5
|
/**
|
|
5
6
|
* The one seam of the runtime (ticket 04). An AgentTransport represents a whole
|
|
6
7
|
* Agent and exchanges raw JSONL frames; it is the only place a process is known.
|
|
@@ -31,8 +32,8 @@ export interface AgentStats {
|
|
|
31
32
|
/** Recorded inputs used to explain an Ask-hash mismatch without changing identity. */
|
|
32
33
|
export interface AskMarkerContext {
|
|
33
34
|
readonly prompt: string;
|
|
34
|
-
readonly spawn: Pick<
|
|
35
|
-
readonly ask: Pick<AskOptions, "maxTurns" | "maxToolCalls" | "maxDurationMs" | "idleMs" | "wrapUpPrompt"> & {
|
|
35
|
+
readonly spawn: Pick<ResolvedSpawnOptions, "cwd" | "model" | "systemPrompt" | "thinking" | "appendSystemPrompt" | "tools" | "disallowedTools" | "skills" | "disallowedSkills" | "worktree">;
|
|
36
|
+
readonly ask: Pick<AskOptions, "maxTurns" | "maxToolCalls" | "maxDurationMs" | "idleMs" | "stallMs" | "wrapUpPrompt"> & {
|
|
36
37
|
readonly outputSchema?: CanonicalJsonObject;
|
|
37
38
|
readonly maxSteers?: number;
|
|
38
39
|
readonly extractionPolicy?: string;
|
|
@@ -60,6 +61,17 @@ export interface AskInvalidOutputPlayback {
|
|
|
60
61
|
readonly kind: "invalid_output";
|
|
61
62
|
readonly steeringEfforts: number;
|
|
62
63
|
}
|
|
64
|
+
/** What one live Ask produced, reported to its transport when the Ask finishes. */
|
|
65
|
+
export interface AskCompletion {
|
|
66
|
+
/** Limit outcome, present only when the Ask rejected with ASK_LIMIT. */
|
|
67
|
+
readonly limit?: AskLimitOutcome;
|
|
68
|
+
/** Stalled outcome, present only when the Ask rejected with ASK_STALLED. */
|
|
69
|
+
readonly stalled?: AskStalledOutcome;
|
|
70
|
+
/** Invalid structured-output result, present only when it surfaced. */
|
|
71
|
+
readonly invalidOutput?: AskInvalidOutputPlayback;
|
|
72
|
+
/** The Stall Watchdog settled this Ask from observed state (ADR-0029). */
|
|
73
|
+
readonly recovered?: true;
|
|
74
|
+
}
|
|
63
75
|
/** Presence identifies Cassette playback, including recorded successful Asks. */
|
|
64
76
|
export interface AskPlayback {
|
|
65
77
|
/** Limit outcome recorded for this Ask, if it rejected with ASK_LIMIT. */
|
|
@@ -68,8 +80,26 @@ export interface AskPlayback {
|
|
|
68
80
|
readonly stalled?: AskStalledOutcome;
|
|
69
81
|
/** Invalid structured-output result recorded for this Ask, if it surfaced. */
|
|
70
82
|
readonly outcome?: AskInvalidOutputPlayback;
|
|
83
|
+
/**
|
|
84
|
+
* The result the recorded Ask reported through `report_result`, read from its
|
|
85
|
+
* frames when the Cassette holds one. Reading it here keeps playback a pure
|
|
86
|
+
* function of the Cassette instead of a race with the replayed frame stream.
|
|
87
|
+
*/
|
|
88
|
+
readonly reported?: ReportedResult;
|
|
71
89
|
/** Whether recorded correction history has an abort settlement after final text. */
|
|
72
90
|
readonly awaitsAbortSettlement?: true;
|
|
91
|
+
/**
|
|
92
|
+
* The recorded Ask settled through Stall Watchdog recovery, so no
|
|
93
|
+
* `agent_settled` frame exists: playback settles on the terminal assistant
|
|
94
|
+
* `message_end` instead.
|
|
95
|
+
*/
|
|
96
|
+
readonly recovered?: true;
|
|
97
|
+
/**
|
|
98
|
+
* False when the recorded Ask holds no `agent_settled` frame. Playback then
|
|
99
|
+
* surfaces the recorded outcome instead of waiting for a settlement that the
|
|
100
|
+
* Cassette does not contain.
|
|
101
|
+
*/
|
|
102
|
+
readonly settles?: false;
|
|
73
103
|
}
|
|
74
104
|
/** One Agent, as the layer above sees it. */
|
|
75
105
|
export interface AgentTransport {
|
|
@@ -85,7 +115,14 @@ export interface AgentTransport {
|
|
|
85
115
|
*/
|
|
86
116
|
beginAsk(marker: AskMarker): AskPlayback | undefined;
|
|
87
117
|
/** Reports surfaced live outcomes; replay ignores completion. */
|
|
88
|
-
finishAsk(
|
|
118
|
+
finishAsk(completion: AskCompletion): void;
|
|
119
|
+
/**
|
|
120
|
+
* The extraction policy recorded for the Ask at `index`, when a Cassette
|
|
121
|
+
* backs it. An Ask recorded under an older policy keeps that policy, so its
|
|
122
|
+
* identity and its settlement behavior stay the ones it was recorded with
|
|
123
|
+
* (ADR-0032). Live transports have nothing recorded and omit this method.
|
|
124
|
+
*/
|
|
125
|
+
recordedExtractionPolicy?(index: number): string | undefined;
|
|
89
126
|
/**
|
|
90
127
|
* Shut the Agent down and report its cost. Idempotent.
|
|
91
128
|
* Owns the whole reap contract, so nothing above this interface knows what a
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import type { Static, TSchema } from "typebox";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import type { ModelSpec, ThinkingSpec } from "./model-resolution.ts";
|
|
3
|
+
import type { ThinkingLevel } from "./thinking-level.ts";
|
|
4
|
+
export type { ThinkingLevel } from "./thinking-level.ts";
|
|
4
5
|
/** Options for spawning one Agent (ADR-0001, ADR-0009, ADR-0026). */
|
|
5
6
|
export interface SpawnOptions {
|
|
6
7
|
/** Working directory. Defaults to the Orchestrator's cwd. */
|
|
7
8
|
readonly cwd?: string;
|
|
8
|
-
/**
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Model pattern, e.g. "anthropic/claude-haiku-4". Unset inherits the user's default.
|
|
11
|
+
*
|
|
12
|
+
* An array is an ordered fallback list and a function picks the next candidate
|
|
13
|
+
* from the failures so far. Any resolved pattern may carry an inline thinking
|
|
14
|
+
* suffix (`"opus-5:medium"`), which wins over `thinking`.
|
|
15
|
+
*/
|
|
16
|
+
readonly model?: ModelSpec;
|
|
10
17
|
/** Replaces the default system prompt (`pi --system-prompt`). */
|
|
11
18
|
readonly systemPrompt?: string;
|
|
12
|
-
/**
|
|
13
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Sets pi's thinking level; omission preserves pi's default. A function picks
|
|
21
|
+
* the level from the settled model, and is not consulted for a model pattern
|
|
22
|
+
* that carries an inline thinking suffix.
|
|
23
|
+
*/
|
|
24
|
+
readonly thinking?: ThinkingSpec;
|
|
14
25
|
/** Appends text to pi's system prompt (`pi --append-system-prompt`). */
|
|
15
26
|
readonly appendSystemPrompt?: string;
|
|
16
27
|
/**
|
|
@@ -46,6 +57,11 @@ export interface SpawnOptions {
|
|
|
46
57
|
/** Request a fresh Git worktree. The requested cwd remains the base until spawn resolves. */
|
|
47
58
|
readonly worktree?: boolean;
|
|
48
59
|
}
|
|
60
|
+
/** Spawn options after Model Resolution has settled one model and thinking level. */
|
|
61
|
+
export interface ResolvedSpawnOptions extends Omit<SpawnOptions, "model" | "thinking"> {
|
|
62
|
+
readonly model?: string;
|
|
63
|
+
readonly thinking?: ThinkingLevel;
|
|
64
|
+
}
|
|
49
65
|
/** Topology-only fields that may change when spawning an Agent Definition. */
|
|
50
66
|
export interface SpawnOverrides {
|
|
51
67
|
/** Log and event label. Defaults to the definition's name; duplicates are suffixed. */
|
|
@@ -81,14 +97,31 @@ export interface AskOptions {
|
|
|
81
97
|
* idle detection (ADR-0020).
|
|
82
98
|
*/
|
|
83
99
|
readonly idleMs?: number;
|
|
100
|
+
/**
|
|
101
|
+
* Silence budget in milliseconds for the Stall Watchdog, which guards every
|
|
102
|
+
* live Ask by default (ADR-0029). On expiry yaag probes the Agent and either
|
|
103
|
+
* recovers a missed settlement or rejects with `ASK_STALLED`. Omission uses
|
|
104
|
+
* the 10-minute default. `false`, and any value that is not above zero,
|
|
105
|
+
* disable the watchdog for this Ask.
|
|
106
|
+
*/
|
|
107
|
+
readonly stallMs?: number | false;
|
|
84
108
|
/** Per-Ask replacement for the runtime's wrap-up steering message. */
|
|
85
109
|
readonly wrapUpPrompt?: string;
|
|
86
110
|
}
|
|
87
111
|
/** Ask options that require a schema and preserve its inferred successful result. */
|
|
88
112
|
export interface StructuredAskOptions<Schema extends TSchema> extends AskOptions {
|
|
89
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* TypeBox object schema the Agent reports its result against, instead of text.
|
|
115
|
+
*
|
|
116
|
+
* It becomes the input schema of one internal tool, so it must describe an
|
|
117
|
+
* object (ADR-0032).
|
|
118
|
+
*/
|
|
90
119
|
readonly outputSchema: Schema;
|
|
91
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Maximum settlements without a reported result that this Ask corrects;
|
|
122
|
+
* defaults to 3 when omitted. Argument repair inside a turn is the Agent's
|
|
123
|
+
* own loop and costs nothing here.
|
|
124
|
+
*/
|
|
92
125
|
readonly maxSteers?: number;
|
|
93
126
|
}
|
|
94
127
|
/**
|
|
@@ -108,12 +141,14 @@ export interface Handle {
|
|
|
108
141
|
* Sends a prompt and resolves after the Agent's turn settles.
|
|
109
142
|
*
|
|
110
143
|
* Schema-free calls resolve with the exact final assistant text. Calls with
|
|
111
|
-
* `outputSchema` resolve with the
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
144
|
+
* `outputSchema` resolve with the TypeBox-validated value the Agent reports
|
|
145
|
+
* through one internal tool call (ADR-0032). Rejects with a YaagError for a
|
|
146
|
+
* failed or empty turn, timeout, `ASK_LIMIT`, or dead Agent. An Ask that
|
|
147
|
+
* settles without a reported result is corrected at most `maxSteers` times
|
|
148
|
+
* (default 3) and then rejects recoverably with `ASK_INVALID_OUTPUT` after an
|
|
149
|
+
* abort settlement; a reported value the schema rejects fails the same way
|
|
150
|
+
* without a correction. These recoverable outcomes leave the Handle reusable.
|
|
151
|
+
* A concurrent call rejects with `AGENT_BUSY`.
|
|
117
152
|
*/
|
|
118
153
|
ask<Schema extends TSchema>(prompt: string, options: StructuredAskOptions<Schema>): Promise<Static<Schema>>;
|
|
119
154
|
ask(prompt: string, options?: AskOptions): Promise<string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaag/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"typecheck": "tsc --noEmit",
|
|
19
|
-
"test": "bun test src scripts",
|
|
20
|
-
"test:e2e": "bun test e2e"
|
|
19
|
+
"test": "bun test --timeout 30000 src scripts",
|
|
20
|
+
"test:e2e": "bun test --timeout 30000 e2e"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@earendil-works/pi-tui": "^0.84.0",
|
|
24
|
-
"@yaag/runtime": "
|
|
25
|
-
"@yaag/tui": "
|
|
24
|
+
"@yaag/runtime": "0.2.0",
|
|
25
|
+
"@yaag/tui": "0.2.0",
|
|
26
26
|
"typebox": "1.3.7"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { writeSync } from "node:fs";
|
|
3
2
|
import { resolve } from "node:path";
|
|
4
3
|
import {
|
|
4
|
+
assertReplayable,
|
|
5
5
|
executeRun,
|
|
6
6
|
isOrchestrationProgram,
|
|
7
7
|
isYaagError,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { interactiveAvailable } from "./alt-screen.ts";
|
|
14
14
|
import { parseArgv } from "./argv.ts";
|
|
15
15
|
import { runInteractive } from "./interactive-run.ts";
|
|
16
|
+
import { writeChannel, writeChannelFd } from "./output-channel.ts";
|
|
16
17
|
import { createPlainPresenter } from "./presenter.ts";
|
|
17
18
|
import { executeOptions, formatResult, type RunFlags } from "./run-invocation.ts";
|
|
18
19
|
import { registerRuntimeAlias } from "./runtime-alias.ts";
|
|
@@ -24,18 +25,22 @@ registerRuntimeAlias();
|
|
|
24
25
|
export async function main(argv: readonly string[]): Promise<number> {
|
|
25
26
|
const parsed = parseArgv(argv);
|
|
26
27
|
if (!parsed.ok) {
|
|
27
|
-
process.stderr
|
|
28
|
+
writeChannel(process.stderr, `${parsed.error}\n`);
|
|
28
29
|
return 2;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
try {
|
|
32
33
|
if (parsed.command === "setup-workspace") {
|
|
33
|
-
process.stdout
|
|
34
|
+
writeChannel(process.stdout, `${(await setupWorkspace(resolve(parsed.dir))).join("\n")}\n`);
|
|
34
35
|
return 0;
|
|
35
36
|
}
|
|
36
|
-
// A bad artifact must fail before importing an arbitrary program module
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// A bad artifact must fail before importing an arbitrary program module,
|
|
38
|
+
// and an interrupted artifact is a bad artifact for strict replay (ADR-0031).
|
|
39
|
+
if (parsed.command === "run" && parsed.replay !== undefined) {
|
|
40
|
+
assertReplayable(await loadCassette(parsed.replay), parsed.replay);
|
|
41
|
+
}
|
|
42
|
+
if (parsed.command === "run" && parsed.resume !== undefined) {
|
|
43
|
+
await loadCassette(parsed.resume);
|
|
39
44
|
}
|
|
40
45
|
const programFile = resolve(parsed.file);
|
|
41
46
|
const program = await loadProgram(programFile);
|
|
@@ -50,14 +55,18 @@ export async function main(argv: readonly string[]): Promise<number> {
|
|
|
50
55
|
quiet: parsed.quiet,
|
|
51
56
|
});
|
|
52
57
|
} catch (error) {
|
|
53
|
-
|
|
58
|
+
writeChannel(
|
|
59
|
+
process.stderr,
|
|
60
|
+
`[yaag] ${error instanceof Error ? error.message : String(error)}\n`,
|
|
61
|
+
);
|
|
54
62
|
return parsed.command === "run" && isYaagError(error) && error.code === "ARGS_INVALID" ? 2 : 1;
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
function describe(program: OrchestrationProgram): number {
|
|
59
67
|
const definition = programDefinition(program);
|
|
60
|
-
|
|
68
|
+
writeChannel(
|
|
69
|
+
process.stdout,
|
|
61
70
|
`${JSON.stringify({
|
|
62
71
|
name: definition.name ?? null,
|
|
63
72
|
description: definition.description ?? null,
|
|
@@ -110,7 +119,7 @@ async function runPlain(program: OrchestrationProgram, flags: RunFlags): Promise
|
|
|
110
119
|
executeOptions(flags, eventSink(flags.eventsFd, presenter.present), stop.signal),
|
|
111
120
|
);
|
|
112
121
|
const output = formatResult(result);
|
|
113
|
-
if (output !== null) process.stdout
|
|
122
|
+
if (output !== null) writeChannel(process.stdout, `${output}\n`);
|
|
114
123
|
return 0;
|
|
115
124
|
} finally {
|
|
116
125
|
process.off("SIGINT", onSignal);
|
|
@@ -128,7 +137,7 @@ async function runPlain(program: OrchestrationProgram, flags: RunFlags): Promise
|
|
|
128
137
|
*/
|
|
129
138
|
function eventSink(fd: number | undefined, present: StampedEventSink): StampedEventSink {
|
|
130
139
|
return (event) => {
|
|
131
|
-
if (fd !== undefined)
|
|
140
|
+
if (fd !== undefined) writeChannelFd(fd, `${JSON.stringify({ v: 1, ...event })}\n`);
|
|
132
141
|
present(event);
|
|
133
142
|
};
|
|
134
143
|
}
|