@yaag/cli 0.9.0 → 0.11.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.
Files changed (27) hide show
  1. package/assets/types/runtime/agent/agent-compaction.d.ts +29 -0
  2. package/assets/types/runtime/agent/agent-usage.d.ts +8 -0
  3. package/assets/types/runtime/agent/agent.d.ts +11 -2
  4. package/assets/types/runtime/agent/fork.d.ts +43 -0
  5. package/assets/types/runtime/agent/index.d.ts +1 -0
  6. package/assets/types/runtime/agent/spawn-open.d.ts +31 -0
  7. package/assets/types/runtime/agent/spawn-parent.d.ts +11 -0
  8. package/assets/types/runtime/agent/spawn-request.d.ts +9 -1
  9. package/assets/types/runtime/agent/spawn.d.ts +1 -1
  10. package/assets/types/runtime/cassette/cassette-replay.d.ts +9 -1
  11. package/assets/types/runtime/cassette/cassette-schema.d.ts +23 -0
  12. package/assets/types/runtime/cassette/cassette.d.ts +27 -1
  13. package/assets/types/runtime/cassette/index.d.ts +1 -1
  14. package/assets/types/runtime/cassette/replay-divergence.d.ts +3 -2
  15. package/assets/types/runtime/errors.d.ts +1 -1
  16. package/assets/types/runtime/events.d.ts +33 -0
  17. package/assets/types/runtime/index.d.ts +5 -5
  18. package/assets/types/runtime/summary/index.d.ts +1 -1
  19. package/assets/types/runtime/summary/summary-agent.d.ts +29 -2
  20. package/assets/types/runtime/summary/summary.d.ts +3 -1
  21. package/assets/types/runtime/transport/fake-transport.d.ts +8 -1
  22. package/assets/types/runtime/transport/index.d.ts +2 -2
  23. package/assets/types/runtime/transport/pi-state.d.ts +8 -1
  24. package/assets/types/runtime/transport/transport.d.ts +63 -0
  25. package/assets/types/runtime/types.d.ts +51 -1
  26. package/package.json +3 -3
  27. package/src/terminal/render.ts +9 -1
@@ -0,0 +1,29 @@
1
+ import type { EventSink } from "../events.ts";
2
+ import type { AgentTransport, CompactionResult, Connection } from "../transport/index.ts";
3
+ import type { AgentUsage } from "./agent-usage.ts";
4
+ /** One compaction of one Agent's context (ADR-0043). */
5
+ export interface CompactionExchange {
6
+ readonly agent: string;
7
+ readonly connection: Connection;
8
+ readonly transport: AgentTransport;
9
+ /** Monotonic per Agent, from 0. */
10
+ readonly index: number;
11
+ /** Settled Asks of this Agent at the compaction point. */
12
+ readonly afterAsks: number;
13
+ /** pi `customInstructions`; the text never rides the wire (ticket 09). */
14
+ readonly instructions?: string;
15
+ readonly emit: EventSink;
16
+ readonly usage: AgentUsage;
17
+ }
18
+ /**
19
+ * Compacts one Agent's context and reports what the summary call cost.
20
+ *
21
+ * Playback answers from the Cassette and emits the same event, but folds no
22
+ * usage: a replayed Run never reports live accounting.
23
+ */
24
+ export declare function compactAgent(exchange: CompactionExchange): Promise<CompactionResult>;
25
+ /**
26
+ * Identity of one compaction: where it sits in the Agent's conversation, and
27
+ * whether the program steered it. The instruction text is hashed, never stored.
28
+ */
29
+ export declare function compactionHash(afterAsks: number, instructions: string | undefined): string;
@@ -13,4 +13,12 @@ export declare class AgentUsage {
13
13
  constructor(report: (snapshot: AgentUsageSnapshot) => void);
14
14
  /** Observes one raw Agent frame and reports after each valid assistant completion. */
15
15
  observe(frame: Frame): void;
16
+ /**
17
+ * Folds one completion yaag observed outside the frame stream, and reports.
18
+ *
19
+ * A compaction's summary call is the only such completion today: pi answers
20
+ * it on the `compact` response instead of an assistant `message_end`, so the
21
+ * live accounting would lose that spend (ADR-0043).
22
+ */
23
+ add(usage: AgentUsageSnapshot): void;
16
24
  }
@@ -1,8 +1,9 @@
1
1
  import type { Static, TSchema } from "typebox";
2
2
  import type { EventSink } from "../events.ts";
3
3
  import type { ModelErrorHistory, ModelResolution } from "../model/index.ts";
4
- import type { AgentStats, AgentTransport } from "../transport/index.ts";
5
- import type { AskOptions, Handle, ResolvedSpawnOptions, StructuredAskOptions } from "../types.ts";
4
+ import type { AgentStats, AgentTransport, CompactionResult } from "../transport/index.ts";
5
+ import type { AskOptions, ForkOptions, Handle, ResolvedSpawnOptions, StructuredAskOptions } from "../types.ts";
6
+ import type { ForkSpawner } from "./fork.ts";
6
7
  /** The mid-Ask Model Resolution one Agent's spawn handed it (ADR-0038). */
7
8
  export interface AgentModelFallback {
8
9
  readonly resolution: ModelResolution;
@@ -24,6 +25,10 @@ export interface AgentOptions {
24
25
  * Absent when the spawn named no candidate: there is nothing to fall back from.
25
26
  */
26
27
  readonly modelFallback?: AgentModelFallback;
28
+ /** pi's session file for this Agent; a fork copies it. Absent during playback. */
29
+ readonly sessionFile?: string;
30
+ /** Opens a fork of this Agent through the Run's spawn gate (ADR-0044). */
31
+ readonly fork?: ForkSpawner;
27
32
  /** Definition-owned defaults merged below explicit per-Ask options. */
28
33
  readonly askDefaults?: AskOptions;
29
34
  /** Definition identity recorded on its Asks, outside replay identity. */
@@ -46,6 +51,10 @@ export declare class Agent implements Handle {
46
51
  get model(): string;
47
52
  ask<Schema extends TSchema>(prompt: string, options: StructuredAskOptions<Schema>): Promise<Static<Schema>>;
48
53
  ask(prompt: string, options?: AskOptions): Promise<string>;
54
+ /** Replaces this Agent's context with a summary of it (ADR-0043). */
55
+ compact(instructions?: string): Promise<CompactionResult>;
56
+ /** Spawns a new Agent from a copy of this Agent's session (ADR-0044). */
57
+ fork(overrides?: ForkOptions): Promise<Handle>;
49
58
  /** True when the Agent was killed mid-Ask, so its cost is a floor (ADR-0012). */
50
59
  get incomplete(): boolean;
51
60
  /** Shuts the Agent down and reports its cost. Idempotent. */
@@ -0,0 +1,43 @@
1
+ import type { WorktreeResolution } from "../transport/index.ts";
2
+ import type { ForkOptions, Handle, ResolvedSpawnOptions, SpawnOptions } from "../types.ts";
3
+ /** What one Agent hands the spawn gate when it is forked (ADR-0044). */
4
+ export interface ForkSource {
5
+ readonly name: string;
6
+ /** The options the source settled on; the verbatim inheritance base. */
7
+ readonly spawnOptions: ResolvedSpawnOptions;
8
+ /** pi's session file for the source; a fork without one is refused. */
9
+ readonly sessionFile: string | undefined;
10
+ readonly worktree: WorktreeResolution | undefined;
11
+ /** Settled Asks of the source at the fork point. */
12
+ readonly settledAsks: number;
13
+ /** The source Handle itself, which becomes the fork's default Parent Link. */
14
+ readonly handle: Handle;
15
+ }
16
+ /** Opens one fork. The spawn gate owns it, so a fork walks the normal spawn path. */
17
+ export type ForkSpawner = (source: ForkSource, overrides?: ForkOptions) => Promise<Handle>;
18
+ /** Spawn identity and launch facts that only a fork carries. */
19
+ export interface ForkIdentity {
20
+ readonly forkOf: string;
21
+ readonly forkAsks: number;
22
+ /** The session pi copies with `--fork`; machine-specific, never identity. */
23
+ readonly forkSession: string;
24
+ readonly worktreeFrom?: WorktreeResolution;
25
+ }
26
+ /** One fork request: what to spawn, whose child it is, and whether to compact it. */
27
+ export interface ForkRequest {
28
+ /** Inherited options with the overrides applied; never carries `name`. */
29
+ readonly spawnOptions: SpawnOptions;
30
+ /** The raw Parent Link, defaulting to the fork source. */
31
+ readonly parent: unknown;
32
+ readonly fork: ForkIdentity;
33
+ readonly compact: boolean | string | undefined;
34
+ }
35
+ /**
36
+ * Merges a fork source with explicit overrides into one spawn request.
37
+ *
38
+ * Inheritance is verbatim, with two deliberate exceptions: `name` is dropped,
39
+ * so the child gets a fresh auto-name unless the overrides give it one, and
40
+ * `compact` never reaches the spawn options, because it is a fork verb rather
41
+ * than a property of the Agent.
42
+ */
43
+ export declare function forkRequest(source: ForkSource, overrides?: ForkOptions): ForkRequest;
@@ -4,4 +4,5 @@
4
4
  */
5
5
  export { Agent } from "./agent.ts";
6
6
  export { type AgentConfig, type AgentDefinition, agentDefinitionConfig, defineAgent, isAgentDefinition, } from "./define-agent.ts";
7
+ export { type ForkSource, type ForkSpawner, forkRequest } from "./fork.ts";
7
8
  export { makeSpawn } from "./spawn.ts";
@@ -0,0 +1,31 @@
1
+ import type { AskOptions, Handle, SpawnOptions } from "../types.ts";
2
+ import type { ForkIdentity, ForkSpawner } from "./fork.ts";
3
+ import type { SpawnDependencies } from "./spawn.ts";
4
+ /** One request the open path serves: a plain spawn, or a fork of another Agent. */
5
+ export interface OpenAgentRequest {
6
+ /** Never carries `parent`: a Handle must not reach the recorded options. */
7
+ readonly spawnOptions: SpawnOptions;
8
+ /** The raw Parent Link option, validated by `resolveParent`. */
9
+ readonly parent: unknown;
10
+ readonly askDefaults: AskOptions | undefined;
11
+ /** Definition identity stays separate from a topology-overridden Agent name. */
12
+ readonly definitionName: string | undefined;
13
+ /** Present only for a fork; adds `origin: "fork"` and the `--fork` source. */
14
+ readonly fork?: ForkIdentity;
15
+ }
16
+ /** What the open path needs beyond the Run's spawn dependencies. */
17
+ export interface OpenAgentContext extends SpawnDependencies {
18
+ /** Agent names already allocated in this Run. */
19
+ readonly taken: Set<string>;
20
+ /** How an opened Agent forks itself later. */
21
+ readonly forkSpawner: ForkSpawner;
22
+ }
23
+ /**
24
+ * Opens one Agent: resolves extensions and a model, starts its transport, and
25
+ * emits `agent_spawn`.
26
+ *
27
+ * A fork walks this very path, so the Tool Contract probe, Model Resolution,
28
+ * the worktree wrapper and the Cassette identity cannot drift between a spawn
29
+ * and a fork (ADR-0044).
30
+ */
31
+ export declare function openAgent(deps: OpenAgentContext, request: OpenAgentRequest): Promise<Handle>;
@@ -0,0 +1,11 @@
1
+ import type { Agent } from "./agent.ts";
2
+ /**
3
+ * Resolves a Parent Link to the parent Agent's name.
4
+ *
5
+ * Only a Handle this Run already spawned is accepted, which is what makes a
6
+ * lineage cycle impossible by construction: a Handle exists only after its own
7
+ * spawn resolved. Liveness is deliberately not checked — a cleanly exited Agent
8
+ * remains a valid parent, because the link states tree position, not a
9
+ * dependency on a live process.
10
+ */
11
+ export declare function resolveParent(parent: unknown, agents: readonly Agent[]): string | undefined;
@@ -1,5 +1,5 @@
1
1
  import type { ModelSelection } from "../model/index.ts";
2
- import type { OpenOptions } from "../transport/index.ts";
2
+ import type { OpenOptions, WorktreeResolution } from "../transport/index.ts";
3
3
  import type { ResolvedSpawnOptions, SpawnOptions } from "../types.ts";
4
4
  /** Everything one spawn needs to build its open request, before a model settles. */
5
5
  export interface OpenRequestOptions {
@@ -8,6 +8,14 @@ export interface OpenRequestOptions {
8
8
  readonly spawnOptions: SpawnOptions;
9
9
  readonly resolvedExtensionPaths?: readonly string[];
10
10
  readonly declaredExtensions?: readonly string[];
11
+ /** Resolved parent Agent name (Parent Link); spawn identity only, never argv. */
12
+ readonly parent?: string;
13
+ /** Fork facts (ADR-0044); the first three are spawn identity, the rest are not. */
14
+ readonly origin?: "fork";
15
+ readonly forkOf?: string;
16
+ readonly forkAsks?: number;
17
+ readonly forkSession?: string;
18
+ readonly worktreeFrom?: WorktreeResolution;
11
19
  readonly sessionDir: string | undefined;
12
20
  }
13
21
  /**
@@ -2,7 +2,7 @@ import type { ConfigExtension } from "../config/index.ts";
2
2
  import type { EventSink } from "../events.ts";
3
3
  import type { RunContext } from "../run/index.ts";
4
4
  import type { TransportFactory } from "../transport/index.ts";
5
- import { Agent } from "./agent.ts";
5
+ import type { Agent } from "./agent.ts";
6
6
  /** Dependencies for one Run's Agent-spawn gate. */
7
7
  export interface SpawnDependencies {
8
8
  readonly factory: TransportFactory;
@@ -1,4 +1,4 @@
1
- import type { AgentStats, AskMarker, AskPlayback, Frame } from "../transport/index.ts";
1
+ import type { AgentStats, AskMarker, AskPlayback, CompactionMarker, CompactionPlayback, Frame } from "../transport/index.ts";
2
2
  import type { CassetteAgent } from "./cassette.ts";
3
3
  /**
4
4
  * Replays one Cassette Agent's frame streams at the transport seam.
@@ -19,6 +19,14 @@ export declare class CassetteReplay {
19
19
  frames(): AsyncIterable<Frame>;
20
20
  /** Moves playback to an Ask that its caller has already identity-checked. */
21
21
  beginAsk(marker: AskMarker): AskPlayback;
22
+ /**
23
+ * Moves playback to a compaction its caller has already identity-checked.
24
+ *
25
+ * A compaction lives between two Asks, and `beginAsk` never switches the
26
+ * cursors back to the Agent-level streams, so the recorded compaction owns
27
+ * its own streams (ADR-0043).
28
+ */
29
+ beginCompaction(marker: CompactionMarker): CompactionPlayback;
22
30
  /** Ends the stream cleanly, allowing a caller to switch to another source. */
23
31
  finish(): void;
24
32
  }
@@ -31,6 +31,10 @@ export declare const CassetteSchema: Type.TObject<{
31
31
  cwd: Type.TString;
32
32
  worktree: Type.TOptional<Type.TLiteral<true>>;
33
33
  declaredExtensions: Type.TOptional<Type.TArray<Type.TString>>;
34
+ parent: Type.TOptional<Type.TString>;
35
+ origin: Type.TOptional<Type.TLiteral<"fork">>;
36
+ forkOf: Type.TOptional<Type.TString>;
37
+ forkAsks: Type.TOptional<Type.TNumber>;
34
38
  }>;
35
39
  model: Type.TString;
36
40
  sessionFile: Type.TOptional<Type.TString>;
@@ -99,6 +103,25 @@ export declare const CassetteSchema: Type.TObject<{
99
103
  }>>;
100
104
  recovered: Type.TOptional<Type.TLiteral<true>>;
101
105
  }>>;
106
+ compactions: Type.TOptional<Type.TArray<Type.TObject<{
107
+ index: Type.TNumber;
108
+ hash: Type.TString;
109
+ afterAsks: Type.TNumber;
110
+ result: Type.TOptional<Type.TObject<{
111
+ tokensBefore: Type.TUnion<[Type.TNull, Type.TNumber]>;
112
+ tokensAfter: Type.TUnion<[Type.TNull, Type.TNumber]>;
113
+ tokens: Type.TUnion<[Type.TNull, Type.TObject<{
114
+ input: Type.TNumber;
115
+ output: Type.TNumber;
116
+ cacheRead: Type.TNumber;
117
+ cacheWrite: Type.TNumber;
118
+ total: Type.TNumber;
119
+ }>]>;
120
+ cost: Type.TUnion<[Type.TNull, Type.TNumber]>;
121
+ }>>;
122
+ sentFrames: Type.TArray<Type.TUnsafe<Frame>>;
123
+ receivedFrames: Type.TArray<Type.TUnsafe<Frame>>;
124
+ }>>>;
102
125
  stats: Type.TObject<{
103
126
  tokens: Type.TUnion<[Type.TNull, Type.TObject<{
104
127
  input: Type.TNumber;
@@ -1,7 +1,7 @@
1
1
  import type { CanonicalJsonObject } from "../ask-contract/index.ts";
2
2
  import type { AskLimitOutcome, AskStalledOutcome } from "../errors.ts";
3
3
  import type { RunOutcome } from "../events.ts";
4
- import type { AgentStats, AskCompletion, AskInvalidOutputPlayback, AskMarker, AskMarkerContext, Frame, OpenOptions, WorktreeResolution } from "../transport/index.ts";
4
+ import type { AgentStats, AskCompletion, AskInvalidOutputPlayback, AskMarker, AskMarkerContext, CompactionMarker, CompactionResult, Frame, OpenOptions, WorktreeResolution } from "../transport/index.ts";
5
5
  import type { ThinkingLevel } from "../types.ts";
6
6
  /** The Cassette format this runtime writes; the loader also reads version 1. */
7
7
  export declare const CASSETTE_VERSION: 2;
@@ -45,8 +45,21 @@ export interface CassetteAgent {
45
45
  readonly sentFrames: readonly Frame[];
46
46
  readonly receivedFrames: readonly Frame[];
47
47
  readonly asks: readonly CassetteAsk[];
48
+ /** Compactions between this Agent's Asks; absent for an Agent that compacted none. */
49
+ readonly compactions?: readonly CassetteCompaction[];
48
50
  readonly stats: AgentStats;
49
51
  }
52
+ /** The frames attributed to one compaction marker (ADR-0043). */
53
+ export interface CassetteCompaction {
54
+ readonly index: number;
55
+ readonly hash: string;
56
+ /** Settled Asks of this Agent at the compaction point. */
57
+ readonly afterAsks: number;
58
+ /** What the recorded compaction reported; absent when the exchange failed. */
59
+ readonly result?: CompactionResult;
60
+ readonly sentFrames: readonly Frame[];
61
+ readonly receivedFrames: readonly Frame[];
62
+ }
50
63
  /** Behavioural identity supplied when opening a recorded Agent. */
51
64
  export interface CassetteSpawn {
52
65
  readonly name: string;
@@ -72,6 +85,17 @@ export interface CassetteSpawn {
72
85
  * across machines (ADR-0040). Absent when the Agent ran no extension.
73
86
  */
74
87
  readonly declaredExtensions?: readonly string[];
88
+ /**
89
+ * Resolved name of the Agent named as this Agent's parent (Parent Link).
90
+ * Part of spawn identity; absent for a root Agent and for older Cassettes.
91
+ */
92
+ readonly parent?: string;
93
+ /** How the Agent came to be; absent means an ordinary spawn. Spawn identity. */
94
+ readonly origin?: "fork";
95
+ /** Name of the Agent this one was forked from. Spawn identity. */
96
+ readonly forkOf?: string;
97
+ /** Settled Asks of the fork source at the fork point. Spawn identity. */
98
+ readonly forkAsks?: number;
75
99
  }
76
100
  /** The frames attributed to one Ask marker. */
77
101
  export interface CassetteAsk {
@@ -117,6 +141,8 @@ export interface CassetteRecorder {
117
141
  received(frame: Frame): void;
118
142
  beginAsk(marker: AskMarker): void;
119
143
  finishAsk(completion: AskCompletion): void;
144
+ beginCompaction(marker: CompactionMarker): void;
145
+ finishCompaction(result: CompactionResult | undefined): void;
120
146
  closed(stats: AgentStats): void;
121
147
  }
122
148
  /** Collects a Cassette in memory; `executeRun` serializes it at Run settlement. */
@@ -2,7 +2,7 @@
2
2
  * Public surface of the `cassette/` module: record, replay, checkpoint, and resume.
3
3
  * Files inside this directory import each other directly.
4
4
  */
5
- export { CASSETTE_VERSION, type Cassette, type CassetteAgent, type CassetteArtifact, type CassetteAsk, CassetteCollector, type CassetteGit, type CassetteRun, type CassetteSink, type CassetteSpawn, } from "./cassette.ts";
5
+ export { CASSETTE_VERSION, type Cassette, type CassetteAgent, type CassetteArtifact, type CassetteAsk, CassetteCollector, type CassetteCompaction, type CassetteGit, type CassetteRun, type CassetteSink, type CassetteSpawn, } from "./cassette.ts";
6
6
  export { assertReplayable, interruptedResumeWarning, loadCassette } from "./cassette-loader.ts";
7
7
  export { publishCassette } from "./cassette-publish.ts";
8
8
  export { checkpointFileName, cleanStaleCheckpointTemp, resolveCheckpointDirectory, } from "./checkpoint-dir.ts";
@@ -1,8 +1,8 @@
1
- import type { AskMarker, OpenOptions, RecordedSpawnSelection } from "../transport/index.ts";
1
+ import type { AskMarker, CompactionMarker, OpenOptions, RecordedSpawnSelection } from "../transport/index.ts";
2
2
  import type { CassetteAgent, CassetteSpawn } from "./cassette.ts";
3
3
  /** A pure description of the first strict replay identity mismatch. */
4
4
  export interface ReplayMismatch {
5
- readonly kind: "unexpected-spawn" | "spawn-options" | "changed-ask" | "extra-ask";
5
+ readonly kind: "unexpected-spawn" | "spawn-options" | "changed-ask" | "extra-ask" | "changed-compaction" | "extra-compaction";
6
6
  readonly agent: string;
7
7
  readonly index?: number;
8
8
  readonly expectedHash: string;
@@ -14,6 +14,7 @@ export interface ReplayMismatch {
14
14
  export declare const replayMismatch: {
15
15
  spawn(expected: CassetteAgent | null, actual: OpenOptions): ReplayMismatch | null;
16
16
  ask(agent: CassetteAgent, cursor: number, actual: AskMarker): ReplayMismatch | null;
17
+ compaction(agent: CassetteAgent, cursor: number, actual: CompactionMarker): ReplayMismatch | null;
17
18
  };
18
19
  /** Converts a detected mismatch into strict replay's public failure. */
19
20
  export declare function strictReplay(mismatch: ReplayMismatch): never;
@@ -30,7 +30,7 @@ export interface ModelResolutionOutcome {
30
30
  readonly modelErrors: readonly ModelError[];
31
31
  }
32
32
  /** Why an Ask, spawn, or Run failed. Programs may branch on this; most won't. */
33
- export type YaagErrorCode = "AGENT_FAILED" | "AGENT_DIED" | "AGENT_BUSY" | "ASK_TIMEOUT" | "ASK_LIMIT" | "ASK_STALLED" | "ASK_INVALID_OUTPUT" | "ARGS_INVALID" | "CONFIG_INVALID" | "OPTIONS_CONFLICT" | "RECORD_PATH_UNUSABLE" | "REPLAY_DIVERGED" | "RESUME_REFUSED" | "RUN_CLOSED" | "RUN_STOPPED" | "WORKTREE_REFUSED" | "MODEL_RESOLUTION_FAILED" | "SPAWN_FAILED";
33
+ export type YaagErrorCode = "AGENT_FAILED" | "AGENT_DIED" | "AGENT_BUSY" | "ASK_TIMEOUT" | "ASK_LIMIT" | "ASK_STALLED" | "COMPACT_DURING_ASK" | "COMPACT_FAILED" | "FORK_DURING_ASK" | "FORK_REFUSED" | "ASK_INVALID_OUTPUT" | "ARGS_INVALID" | "CONFIG_INVALID" | "OPTIONS_CONFLICT" | "RECORD_PATH_UNUSABLE" | "REPLAY_DIVERGED" | "RESUME_REFUSED" | "RUN_CLOSED" | "RUN_STOPPED" | "WORKTREE_REFUSED" | "MODEL_RESOLUTION_FAILED" | "SPAWN_FAILED";
34
34
  /** The single error class of the runtime (ADR-0003). */
35
35
  export declare class YaagError extends Error {
36
36
  readonly code: YaagErrorCode;
@@ -17,6 +17,11 @@ export type { ModelErrorReason } from "./model/index.ts";
17
17
  * settled Run never writes it (ADR-0031).
18
18
  */
19
19
  export type RunOutcome = "completed" | "failed" | "stopped" | "paused" | "interrupted";
20
+ /**
21
+ * How an Agent came to be. An absent value on an event means "spawn"; "fork"
22
+ * marks an Agent created from a copy of another Agent's session (ADR-0044).
23
+ */
24
+ export type SpawnOrigin = "spawn" | "fork";
20
25
  /** The current, Ask-scoped observer projection derived from Agent frames. */
21
26
  export type AgentActivity = {
22
27
  readonly type: "thinking";
@@ -65,6 +70,10 @@ export type LifecycleEventBody = {
65
70
  * Cassette-playback Agents and events from older CLIs.
66
71
  */
67
72
  readonly sessionFile?: string;
73
+ /** Resolved name of the Agent named as this Agent's parent (Parent Link). */
74
+ readonly parent?: string;
75
+ /** How the Agent came to be; absent means "spawn". */
76
+ readonly origin?: SpawnOrigin;
68
77
  } | {
69
78
  readonly type: "ask_start";
70
79
  readonly agent: string;
@@ -150,6 +159,30 @@ export type LifecycleEventBody = {
150
159
  readonly tokens: TokenBreakdown;
151
160
  /** Cumulative assistant-completion cost observed so far. */
152
161
  readonly cost: number;
162
+ } | {
163
+ /**
164
+ * One compaction of an Agent's context (ADR-0043).
165
+ *
166
+ * It carries the summary call's own spend, so a Run Summary does not
167
+ * silently lose it. `agent_exit` stays authoritative for the Agent's
168
+ * total: pi's `get_session_stats` already includes compaction spend.
169
+ * The custom instructions never ride the wire (ticket 09); only the
170
+ * `custom` flag says that the program supplied some.
171
+ */
172
+ readonly type: "agent_compaction";
173
+ readonly agent: string;
174
+ /** Monotonic per Agent, from 0. */
175
+ readonly index: number;
176
+ /** Context tokens before the summary replaced the transcript; null when unknown. */
177
+ readonly tokensBefore: number | null;
178
+ /** Estimated context tokens after the summary; null when unknown. */
179
+ readonly tokensAfter: number | null;
180
+ /** The summary call's own token breakdown; null when unknown. */
181
+ readonly tokens: TokenBreakdown | null;
182
+ /** The summary call's own cost; null when unknown. */
183
+ readonly cost: number | null;
184
+ /** The program supplied custom compaction instructions. */
185
+ readonly custom: boolean;
153
186
  } | {
154
187
  readonly type: "agent_exit";
155
188
  readonly agent: string;
@@ -1,21 +1,21 @@
1
1
  export type { AgentConfig, AgentDefinition } from "./agent/index.ts";
2
2
  export { agentDefinitionConfig, defineAgent, isAgentDefinition } from "./agent/index.ts";
3
- export type { Cassette, CassetteAgent, CassetteArtifact, CassetteAsk, CassetteGit, CassetteRun, CassetteSink, CassetteSpawn, } from "./cassette/index.ts";
3
+ export type { Cassette, CassetteAgent, CassetteArtifact, CassetteAsk, CassetteCompaction, CassetteGit, CassetteRun, CassetteSink, CassetteSpawn, } from "./cassette/index.ts";
4
4
  export { assertReplayable, CASSETTE_VERSION, loadCassette, recordingTransport, replayTransport, resumeTransport, } from "./cassette/index.ts";
5
5
  export type { ConfigEnvironment, ConfigExtension, ConfigLayerName, EffectiveConfig, EffectiveConfigRequest, } from "./config/index.ts";
6
6
  export { EMPTY_EFFECTIVE_CONFIG, loadEffectiveConfig, PROJECT_CONFIG_DIR, } from "./config/index.ts";
7
7
  export type { AskInvalidOutputOutcome, AskLimitKind, AskLimitOutcome, AskStalledOutcome, ModelResolutionOutcome, YaagErrorCode, } from "./errors.ts";
8
8
  export { isYaagError, YaagError } from "./errors.ts";
9
- export type { AgentActivity, AskOutputChannel, EventSink, LifecycleEvent, LifecycleEventBody, NodeState, NodeUsage, StampedEventSink, } from "./events.ts";
9
+ export type { AgentActivity, AskOutputChannel, EventSink, LifecycleEvent, LifecycleEventBody, NodeState, NodeUsage, SpawnOrigin, StampedEventSink, } from "./events.ts";
10
10
  export type { ModelError, ModelErrorReason, ModelResolver, ModelSelection, ModelSpec, ThinkingResolver, ThinkingSpec, } from "./model/index.ts";
11
11
  export type { DecodedNode, NodeDecoder, NodePath, NodeSnapshot } from "./node/index.ts";
12
12
  export { agentAskPath, childPath, DEFAULT_NODE_DECODERS, NodeTracker, sanitizeNodeName, } from "./node/index.ts";
13
13
  export { prompt, promptGist } from "./prompt/index.ts";
14
14
  export type { OrchestrationProgram, ProgramDefinition, RunContext, RunOptions, } from "./run/index.ts";
15
15
  export { defineRun, executeRun, isOrchestrationProgram, programDefinition } from "./run/index.ts";
16
- export type { AgentInfo, AgentState, AskingAgentInfo, EndedRunSummary, ExitedAgentInfo, IdleAgentInfo, ModelFallbackInfo, NodeInfo, RunningRunSummary, RunOutcome, RunState, RunSummary, } from "./summary/index.ts";
16
+ export type { AgentInfo, AgentState, AskingAgentInfo, CompactionInfo, EndedRunSummary, ExitedAgentInfo, IdleAgentInfo, ModelFallbackInfo, NodeInfo, RunningRunSummary, RunOutcome, RunState, RunSummary, } from "./summary/index.ts";
17
17
  export { applyEvent, initialSummary } from "./summary/index.ts";
18
- export type { AgentStats, AgentTransport, AskMarker, AskMarkerContext, AskPlayback, DiscoveredSkill, Frame, ReapPath, ReapTarget, SkillProbeFactory, TokenBreakdown, TransportFactory, TransportStartup, TransportStartupObserver, WorktreeResolution, } from "./transport/index.ts";
18
+ export type { AgentStats, AgentTransport, AskMarker, AskMarkerContext, AskPlayback, CompactionMarker, CompactionPlayback, CompactionResult, DiscoveredSkill, Frame, ReapPath, ReapTarget, SkillProbeFactory, TokenBreakdown, TransportFactory, TransportStartup, TransportStartupObserver, WorktreeResolution, } from "./transport/index.ts";
19
19
  export { readSystemPromptSidecar, reap, worktreeTransport } from "./transport/index.ts";
20
- export type { AskOptions, Handle, ResolvedSpawnOptions, SpawnOptions, SpawnOverrides, StructuredAskOptions, ThinkingLevel, } from "./types.ts";
20
+ export type { AskOptions, ForkOptions, Handle, ResolvedSpawnOptions, SpawnOptions, SpawnOverrides, StructuredAskOptions, ThinkingLevel, } from "./types.ts";
21
21
  export { AGENT_NODE_TABLE_MAX, ASK_OUTPUT_FLUSH_INTERVAL_MS, ASK_OUTPUT_MAX_BYTES, ASK_OUTPUT_TRUNCATION_MARKER, NODE_GIST_MAX_CHARS, PROMPT_GIST_MAX_CHARS, TOOL_ARGS_GIST_MAX_CHARS, } from "./wire-constants.ts";
@@ -3,4 +3,4 @@
3
3
  * Files inside this directory import each other directly.
4
4
  */
5
5
  export { applyEvent, type EndedRunSummary, initialSummary, type RunningRunSummary, type RunOutcome, type RunState, type RunSummary, } from "./summary.ts";
6
- export type { AgentInfo, AgentState, AskingAgentInfo, ExitedAgentInfo, IdleAgentInfo, ModelFallbackInfo, NodeInfo, } from "./summary-agent.ts";
6
+ export type { AgentInfo, AgentState, AskingAgentInfo, CompactionInfo, ExitedAgentInfo, IdleAgentInfo, ModelFallbackInfo, NodeInfo, SpawnOrigin, } from "./summary-agent.ts";
@@ -1,10 +1,18 @@
1
- import type { AgentActivity } from "../events.ts";
1
+ import type { AgentActivity, SpawnOrigin } from "../events.ts";
2
2
  import type { TokenBreakdown, WorktreeResolution } from "../transport/index.ts";
3
3
  import type { ModelFallbackInfo } from "./summary-fallbacks.ts";
4
4
  import type { NodeInfo } from "./summary-nodes.ts";
5
- export type { AgentActivity } from "../events.ts";
5
+ export type { AgentActivity, SpawnOrigin } from "../events.ts";
6
6
  export type { ModelFallbackInfo } from "./summary-fallbacks.ts";
7
7
  export type { NodeInfo } from "./summary-nodes.ts";
8
+ /** What one compaction of an Agent's context reported (ADR-0043). */
9
+ export interface CompactionInfo {
10
+ readonly tokensBefore: number | null;
11
+ readonly tokensAfter: number | null;
12
+ /** The summary call's own cost, already included in `agent_exit` accounting. */
13
+ readonly cost: number | null;
14
+ readonly at: number | null;
15
+ }
8
16
  /** The observer-facing lifecycle state of an Agent. */
9
17
  export type AgentState = "idle" | "asking" | "exited";
10
18
  /** Identity and accounting facts that apply in every observer Agent state. */
@@ -19,6 +27,10 @@ interface AgentInfoBase {
19
27
  readonly branch: string | null;
20
28
  /** pi's session file for this Agent, when the spawn reported one; a Peek reads it. */
21
29
  readonly sessionFile: string | null;
30
+ /** The Agent named as this one's parent (Parent Link), or null for a root Agent. */
31
+ readonly parent: string | null;
32
+ /** How the Agent came to be; "spawn" until forking ships. */
33
+ readonly origin: SpawnOrigin;
22
34
  readonly activity: AgentActivity | null;
23
35
  readonly tokens: TokenBreakdown | null;
24
36
  readonly cost: number | null;
@@ -27,6 +39,10 @@ interface AgentInfoBase {
27
39
  /** Timestamp ordering only cumulative live accounting, never lifecycle state. */
28
40
  readonly usageUpdatedAt: number | null;
29
41
  readonly askStartedAt: number | null;
42
+ /** Compactions of this Agent's context so far (ADR-0043). */
43
+ readonly compactions: number;
44
+ /** What the newest compaction reported, or null when the Agent compacted none. */
45
+ readonly lastCompaction: CompactionInfo | null;
30
46
  /** This Agent's bounded Nested Node table, in first-seen order (spec §3). */
31
47
  readonly nodes: readonly NodeInfo[];
32
48
  /** Exited Nested Nodes dropped to keep the table bounded. */
@@ -90,6 +106,8 @@ export declare function spawnAgent(current: AgentRecord | undefined, identity: {
90
106
  readonly cwd: string;
91
107
  readonly branch?: string;
92
108
  readonly sessionFile?: string;
109
+ readonly parent?: string;
110
+ readonly origin?: SpawnOrigin;
93
111
  }, at: number | null): AgentRecord;
94
112
  /**
95
113
  * Folds an Ask start into the asking arm when it is not older than the folded
@@ -123,6 +141,15 @@ export declare function endAsk(current: AgentRecord | undefined, settlement: Ask
123
141
  * stream order while older stamped observations are ignored.
124
142
  */
125
143
  export declare function setUsage(current: AgentRecord | undefined, observation: AgentUsageObservation, at: number | null): AgentRecord;
144
+ /**
145
+ * Counts one compaction and keeps what it reported.
146
+ *
147
+ * It folds no cost: the summary call's spend reaches the Summary through
148
+ * `agent_usage`, and `agent_exit` stays authoritative (ADR-0043). A terminal
149
+ * Agent still counts a late compaction, because the count is a fact about the
150
+ * conversation rather than a lifecycle state.
151
+ */
152
+ export declare function compactAgent(current: AgentRecord | undefined, observation: CompactionInfo, at: number | null): AgentRecord;
126
153
  /**
127
154
  * Folds authoritative shutdown accounting into the terminal Agent state.
128
155
  *
@@ -2,7 +2,7 @@ import type { LifecycleEvent, LifecycleEventBody, RunOutcome } from "../events.t
2
2
  import type { TokenBreakdown } from "../transport/index.ts";
3
3
  import type { AgentInfo } from "./summary-agent.ts";
4
4
  export type { NodeState, NodeUsage, RunOutcome } from "../events.ts";
5
- export type { AgentActivity, AgentInfo, AgentState, AskingAgentInfo, ExitedAgentInfo, IdleAgentInfo, ModelFallbackInfo, NodeInfo, } from "./summary-agent.ts";
5
+ export type { AgentActivity, AgentInfo, AgentState, AskingAgentInfo, CompactionInfo, ExitedAgentInfo, IdleAgentInfo, ModelFallbackInfo, NodeInfo, SpawnOrigin, } from "./summary-agent.ts";
6
6
  /** The observer-facing lifecycle state of a Run. */
7
7
  export type RunState = "running" | "ended";
8
8
  /** Accounting and identity facts shared by all Run observer states. */
@@ -24,6 +24,8 @@ interface RunSummaryBase {
24
24
  readonly worstFrameGapMs: number;
25
25
  /** Run-wide Model Resolution fallbacks, pruned per-Agent entries included. */
26
26
  readonly modelFallbacks: number;
27
+ /** Compactions across every Agent of this Run (ADR-0043). */
28
+ readonly compactions: number;
27
29
  }
28
30
  /** A Run that has not settled; it has neither outcome nor compatibility result. */
29
31
  export interface RunningRunSummary extends RunSummaryBase {
@@ -1,5 +1,5 @@
1
1
  import type { AvailableModel } from "../model/index.ts";
2
- import type { AgentStats, AgentTransport, AskMarker, Frame } from "./transport.ts";
2
+ import type { AgentStats, AgentTransport, AskMarker, CompactionMarker, Frame } from "./transport.ts";
3
3
  /**
4
4
  * Scripted frame playback for one prompt sent through a FakeTransport.
5
5
  *
@@ -41,6 +41,10 @@ export interface FakeTransportOptions extends FakePromptScript {
41
41
  readonly steerError?: string;
42
42
  /** Makes an abort RPC response fail without embedding a limit decision in playback. */
43
43
  readonly abortError?: string;
44
+ /** Payload of a `compact` command response; omission answers a bare success. */
45
+ readonly compaction?: Record<string, unknown>;
46
+ /** Makes a `compact` command response fail, as pi does when it cannot summarize. */
47
+ readonly compactError?: string;
44
48
  /** Makes the private report_result schema command fail. */
45
49
  readonly schemaCommandError?: string;
46
50
  /** The snapshot answered to `get_available_models`; defaults to this fake's own model. */
@@ -73,11 +77,14 @@ export declare class FakeTransport implements AgentTransport {
73
77
  /** Everything the runtime wrote, in order. */
74
78
  readonly sent: Frame[];
75
79
  readonly asks: AskMarker[];
80
+ readonly compactions: CompactionMarker[];
76
81
  constructor(options?: FakeTransportOptions);
77
82
  send(frame: Frame): void;
78
83
  frames(): AsyncIterable<Frame>;
79
84
  beginAsk(marker: AskMarker): undefined;
80
85
  finishAsk(): void;
86
+ beginCompaction(marker: CompactionMarker): undefined;
87
+ finishCompaction(): void;
81
88
  /** True once the runtime shut this Agent down. */
82
89
  get closed(): boolean;
83
90
  /** Number of times this fake actually began close work. */
@@ -9,11 +9,11 @@ export { FrameGapTracker } from "./frame-gap.ts";
9
9
  export { FrameQueue } from "./frame-queue.ts";
10
10
  export { decodeFrames } from "./jsonl.ts";
11
11
  export { liveTransport } from "./live-transport.ts";
12
- export { type AgentProgress, piCommand, readAgentProgress } from "./pi-state.ts";
12
+ export { type AgentProgress, piCommand, readAgentProgress, readCompaction, } from "./pi-state.ts";
13
13
  export { type ReapPath, type ReapTarget, reap } from "./reap.ts";
14
14
  export { type DiscoveredSkill, liveSkillProbe, type SkillProbeFactory } from "./skill-probe.ts";
15
15
  export { skillRestrictionTransport } from "./skill-restriction-transport.ts";
16
16
  export { readSystemPromptSidecar } from "./system-prompt-recorder.ts";
17
17
  export { SYSTEM_PROMPT_SIDECAR_SUFFIX, systemPromptSidecarPath, } from "./system-prompt-recorder-extension.ts";
18
- export type { AgentStats, AgentTransport, AskCompletion, AskInvalidOutputPlayback, AskMarker, AskMarkerContext, AskPlayback, Frame, OpenOptions, TokenBreakdown, TransportFactory, TransportStartup, TransportStartupObserver, WorktreeResolution, } from "./transport.ts";
18
+ export type { AgentStats, AgentTransport, AskCompletion, AskInvalidOutputPlayback, AskMarker, AskMarkerContext, AskPlayback, CompactionMarker, CompactionPlayback, CompactionResult, Frame, OpenOptions, TokenBreakdown, TransportFactory, TransportStartup, TransportStartupObserver, WorktreeResolution, } from "./transport.ts";
19
19
  export { worktreeTransport } from "./worktree-transport.ts";
@@ -1,4 +1,4 @@
1
- import type { AgentStats, Frame, OpenOptions } from "./transport.ts";
1
+ import type { AgentStats, CompactionResult, Frame, OpenOptions } from "./transport.ts";
2
2
  /** What the Agent reported about its own work, read from a `get_state` probe. */
3
3
  export interface AgentProgress {
4
4
  /** True while pi streams a completion, compacts its context, or holds a queued prompt. */
@@ -49,3 +49,10 @@ export declare function readSessionFile(response: Frame): string | null;
49
49
  * distinguishable (ADR-0012); a partial breakdown is treated as unknown.
50
50
  */
51
51
  export declare function readStats(response: Frame): AgentStats;
52
+ /**
53
+ * What a `compact` response reports (ADR-0043).
54
+ *
55
+ * A missing field becomes null rather than 0, so "unknown" and "free" stay
56
+ * distinguishable: a custom compaction handler may answer without usage.
57
+ */
58
+ export declare function readCompaction(response: Frame): CompactionResult;
@@ -29,6 +29,22 @@ export interface AgentStats {
29
29
  readonly tokens: TokenBreakdown | null;
30
30
  readonly cost: number | null;
31
31
  }
32
+ /**
33
+ * What one compaction of an Agent's context reported (ADR-0043).
34
+ *
35
+ * Every field is null when pi reported nothing for it: a custom compaction
36
+ * handler may answer without usage, and "unknown" must stay distinct from 0.
37
+ */
38
+ export interface CompactionResult {
39
+ /** Context tokens before the summary replaced the transcript. */
40
+ readonly tokensBefore: number | null;
41
+ /** Estimated context tokens after the summary replaced the transcript. */
42
+ readonly tokensAfter: number | null;
43
+ /** Token breakdown of the summary call itself. */
44
+ readonly tokens: TokenBreakdown | null;
45
+ /** Cost of the summary call itself. */
46
+ readonly cost: number | null;
47
+ }
32
48
  /** Recorded inputs used to explain an Ask-hash mismatch without changing identity. */
33
49
  export interface AskMarkerContext {
34
50
  readonly prompt: string;
@@ -72,6 +88,25 @@ export interface AskCompletion {
72
88
  /** The Stall Watchdog settled this Ask from observed state (ADR-0029). */
73
89
  readonly recovered?: true;
74
90
  }
91
+ /**
92
+ * Opaque marker naming one compaction of an Agent's context.
93
+ *
94
+ * A compaction sits between two Asks, so it cannot ride the Agent-level frame
95
+ * streams: a Cassette replay switches those cursors to an Ask and never
96
+ * switches back. The marker gives the exchange its own recorded streams.
97
+ */
98
+ export interface CompactionMarker {
99
+ /** Monotonic per Agent, from 0. */
100
+ readonly index: number;
101
+ /** sha256 of the settled-Ask count and the custom instructions, when given. */
102
+ readonly hash: string;
103
+ /** Settled Asks of this Agent at the compaction point. */
104
+ readonly afterAsks: number;
105
+ }
106
+ /** Presence identifies Cassette playback of one compaction. */
107
+ export interface CompactionPlayback {
108
+ readonly result: CompactionResult;
109
+ }
75
110
  /** Presence identifies Cassette playback, including recorded successful Asks. */
76
111
  export interface AskPlayback {
77
112
  /** Limit outcome recorded for this Ask, if it rejected with ASK_LIMIT. */
@@ -116,6 +151,13 @@ export interface AgentTransport {
116
151
  beginAsk(marker: AskMarker): AskPlayback | undefined;
117
152
  /** Reports surfaced live outcomes; replay ignores completion. */
118
153
  finishAsk(completion: AskCompletion): void;
154
+ /**
155
+ * Begins one compaction. Live transports return undefined; replay transports
156
+ * return the recorded result.
157
+ */
158
+ beginCompaction(marker: CompactionMarker): CompactionPlayback | undefined;
159
+ /** Ends the compaction exchange, so later frames belong to the Agent again. */
160
+ finishCompaction(result: CompactionResult | undefined): void;
119
161
  /**
120
162
  * The extraction policy recorded for the Ask at `index`, when a Cassette
121
163
  * backs it. An Ask recorded under an older policy keeps that policy, so its
@@ -162,10 +204,31 @@ export interface OpenOptions {
162
204
  * Spawn identity only: it never becomes argv, and it is absent when empty.
163
205
  */
164
206
  readonly declaredExtensions?: readonly string[];
207
+ /**
208
+ * Resolved name of the Agent named as this Agent's parent (Parent Link).
209
+ * Spawn identity only: it never becomes argv.
210
+ */
211
+ readonly parent?: string;
165
212
  /** Session storage directory. Used by the e2e suite to stay out of ~/.pi (ticket 06). */
166
213
  readonly sessionDir?: string;
167
214
  /** Resumes an existing pi session, translated to `--session <path>`. */
168
215
  readonly sessionFile?: string;
216
+ /** How the Agent came to be; absent means an ordinary spawn. Spawn identity. */
217
+ readonly origin?: "fork";
218
+ /** Name of the Agent this one was forked from. Spawn identity; never argv. */
219
+ readonly forkOf?: string;
220
+ /** Settled Asks of the fork source at the fork point. Spawn identity. */
221
+ readonly forkAsks?: number;
222
+ /**
223
+ * The fork source's session file, translated to `pi --fork <path>`.
224
+ * Never spawn identity: the path is machine-specific.
225
+ */
226
+ readonly forkSession?: string;
227
+ /**
228
+ * The fork source's worktree, so the fork branches from it instead of from a
229
+ * clean base tree. Never spawn identity.
230
+ */
231
+ readonly worktreeFrom?: WorktreeResolution;
169
232
  }
170
233
  /** Nondeterministic worktree identity resolved below the transport seam. */
171
234
  export interface WorktreeResolution {
@@ -1,6 +1,8 @@
1
1
  import type { Static, TSchema } from "typebox";
2
2
  import type { ModelSpec, ThinkingLevel, ThinkingSpec } from "./model/index.ts";
3
+ import type { CompactionResult } from "./transport/index.ts";
3
4
  export type { ThinkingLevel } from "./model/index.ts";
5
+ export type { CompactionResult } from "./transport/index.ts";
4
6
  /** Options for spawning one Agent (ADR-0001, ADR-0009, ADR-0026). */
5
7
  export interface SpawnOptions {
6
8
  /** Working directory. Defaults to the Orchestrator's cwd. */
@@ -64,17 +66,44 @@ export interface SpawnOptions {
64
66
  readonly name?: string;
65
67
  /** Request a fresh Git worktree. The requested cwd remains the base until spawn resolves. */
66
68
  readonly worktree?: boolean;
69
+ /**
70
+ * Names this Agent's parent in the Run tree (Parent Link). Data only: it is
71
+ * no conversation channel and no lifetime rule.
72
+ */
73
+ readonly parent?: Handle;
67
74
  }
68
75
  /**
69
76
  * Spawn options after Model Resolution has settled one model and thinking level.
70
77
  *
71
78
  * This is the settled shape, and it is what the Cassette identity hashes
72
79
  * (ADR-0039).
80
+ *
81
+ * `parent` is omitted on purpose: a Handle must never reach this shape, because
82
+ * an Ask records these options into the Cassette. Spawn resolves the Parent
83
+ * Link to a name, which travels in `OpenOptions` and stays out of Ask identity.
73
84
  */
74
- export interface ResolvedSpawnOptions extends Omit<SpawnOptions, "model" | "thinking"> {
85
+ export interface ResolvedSpawnOptions extends Omit<SpawnOptions, "model" | "thinking" | "parent"> {
75
86
  readonly model?: string;
76
87
  readonly thinking?: ThinkingLevel;
77
88
  }
89
+ /**
90
+ * What a fork may change about the Agent it copies (ADR-0044).
91
+ *
92
+ * A fork inherits the source's resolved spawn options verbatim: model,
93
+ * thinking, tools, skills, system prompt, extensions and Ask defaults. `name`
94
+ * is the exception — the child always gets a fresh auto-name unless this object
95
+ * names one. The fork source becomes the child's default `parent`.
96
+ *
97
+ * The inherited model is the concrete one the source settled on, so the child
98
+ * never re-runs the source's fallback list; `fork({ model })` is the way out.
99
+ * A fork of a Worktree Agent gets its own fresh worktree, branched from the
100
+ * source's branch, so only committed state transfers. The child re-runs the
101
+ * ADR-0026 Tool Contract probe against its own effective options.
102
+ */
103
+ export interface ForkOptions extends SpawnOptions {
104
+ /** true = pi's default compaction; a string = pi custom instructions. */
105
+ readonly compact?: boolean | string;
106
+ }
78
107
  /** Topology-only fields that may change when spawning an Agent Definition. */
79
108
  export interface SpawnOverrides {
80
109
  /** Log and event label. Defaults to the definition's name; duplicates are suffixed. */
@@ -83,6 +112,8 @@ export interface SpawnOverrides {
83
112
  readonly cwd?: string;
84
113
  /** Request a fresh Git worktree. */
85
114
  readonly worktree?: boolean;
115
+ /** Names this Agent's parent in the Run tree (Parent Link). Data only. */
116
+ readonly parent?: Handle;
86
117
  }
87
118
  /**
88
119
  * Options for one Ask (ADR-0003).
@@ -179,4 +210,23 @@ export interface Handle {
179
210
  */
180
211
  ask<Schema extends TSchema>(prompt: string, options: StructuredAskOptions<Schema>): Promise<Static<Schema>>;
181
212
  ask(prompt: string, options?: AskOptions): Promise<string>;
213
+ /**
214
+ * Spawns a new Agent from a copy of this Agent's session (ADR-0044).
215
+ *
216
+ * The fork point is a settled Ask boundary: a call while an Ask is in flight
217
+ * rejects with `FORK_DURING_ASK`. An Agent that died mid-Ask, or that holds
218
+ * no session file, rejects with `FORK_REFUSED`. An Agent that exited cleanly
219
+ * stays forkable, so a program can build context, let the Agent exit, then
220
+ * fork its final state many times.
221
+ */
222
+ fork(overrides?: ForkOptions): Promise<Handle>;
223
+ /**
224
+ * Replaces this Agent's context with a summary of it (ADR-0043).
225
+ *
226
+ * `instructions` becomes pi's `customInstructions`. A call while an Ask is in
227
+ * flight rejects with `COMPACT_DURING_ASK`; a refusal from pi rejects with
228
+ * `COMPACT_FAILED`. The result reports what the summary call cost; each field
229
+ * is null when pi reported nothing for it.
230
+ */
231
+ compact(instructions?: string): Promise<CompactionResult>;
182
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaag/cli",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,8 +21,8 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@earendil-works/pi-tui": "^0.84.0",
24
- "@yaag/runtime": "0.9.0",
25
- "@yaag/tui": "0.9.0",
24
+ "@yaag/runtime": "0.11.0",
25
+ "@yaag/tui": "0.11.0",
26
26
  "typebox": "1.3.7"
27
27
  }
28
28
  }
@@ -24,7 +24,7 @@ function renderPlainEvent(event: LifecycleEventBody, mode: PlainRenderMode): str
24
24
  case "run_start":
25
25
  return `[yaag] run ${event.program}`;
26
26
  case "agent_spawn":
27
- return `[${event.agent}] spawned ${event.model} in ${event.cwd}`;
27
+ return `[${event.agent}] ${event.origin === "fork" ? "forked" : "spawned"} ${event.model} in ${event.cwd}${event.parent === undefined ? "" : ` under ${event.parent}`}`;
28
28
  case "ask_start":
29
29
  return `[${event.agent}] ask #${event.index + 1}${event.replayed === true ? " (replayed)" : ""}${event.promptGist === "" ? "" : ` — ${event.promptGist}`}`;
30
30
  case "ask_activity":
@@ -43,6 +43,8 @@ function renderPlainEvent(event: LifecycleEventBody, mode: PlainRenderMode): str
43
43
  return `[${event.agent}] model ${event.failedModel} ${event.reason} — falling back to ${event.resolvedModel}`;
44
44
  case "agent_model":
45
45
  return `[${event.agent}] model \u2014 now running ${event.model}`;
46
+ case "agent_compaction":
47
+ return `[${event.agent}] compacted ${contextTokens(event.tokensBefore)} → ${contextTokens(event.tokensAfter)}${event.custom ? " (custom)" : ""} ${cost(event.cost)}`;
46
48
  case "agent_exit":
47
49
  return `[${event.agent}] exit ${tokens(event.tokens)} ${cost(event.cost)}${event.incomplete ? " (killed mid-ask, cost incomplete)" : ""}`;
48
50
  case "run_end": {
@@ -64,6 +66,7 @@ function minimalEvent(type: LifecycleEventBody["type"]): boolean {
64
66
  type === "ask_end" ||
65
67
  type === "model_fallback" ||
66
68
  type === "agent_model" ||
69
+ type === "agent_compaction" ||
67
70
  type === "agent_exit" ||
68
71
  type === "run_end"
69
72
  );
@@ -97,6 +100,11 @@ function tokens(value: TokenBreakdown | null): string {
97
100
  return value === null ? "tokens unknown" : `${value.total} tokens`;
98
101
  }
99
102
 
103
+ /** Context size at one end of a compaction; a compaction may report neither. */
104
+ function contextTokens(value: number | null): string {
105
+ return value === null ? "?" : `${value} tokens`;
106
+ }
107
+
100
108
  function cost(value: number | null): string {
101
109
  return value === null ? "cost unknown" : `$${value.toFixed(4)}`;
102
110
  }