@yaag/cli 0.8.3 → 0.10.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.
@@ -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;
@@ -8,6 +8,8 @@ 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;
11
13
  readonly sessionDir: string | undefined;
12
14
  }
13
15
  /**
@@ -12,7 +12,7 @@ export interface AskEndOutcome {
12
12
  }
13
13
  /**
14
14
  * Emits the Ask-scoped Lifecycle Events for one exchange, plus the Agent-scoped
15
- * `model_fallback` this exchange's fallback loop reports.
15
+ * `model_fallback` and `agent_model` this exchange's fallback loop reports.
16
16
  */
17
17
  export declare class AskEvents {
18
18
  #private;
@@ -24,6 +24,12 @@ export declare class AskEvents {
24
24
  node: (snapshot: NodeSnapshot) => void;
25
25
  /** Not Ask-scoped: a fallback names the Agent only, like the spawn-time loop. */
26
26
  fallback: (fallback: ModelFallback) => void;
27
+ /**
28
+ * The concrete model the Agent runs after a swap that landed (ADR-0041).
29
+ * Agent-scoped like `fallback`: the pattern side of the story stays in
30
+ * `model_fallback`, and this carries pi's own `provider/id`.
31
+ */
32
+ model: (model: string) => void;
27
33
  /** A `normal` cause is the absent default, so ordinary settlements stay lean. */
28
34
  end(outcome: AskEndOutcome): void;
29
35
  }
@@ -31,6 +31,7 @@ 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>;
34
35
  }>;
35
36
  model: Type.TString;
36
37
  sessionFile: Type.TOptional<Type.TString>;
@@ -72,6 +72,11 @@ export interface CassetteSpawn {
72
72
  * across machines (ADR-0040). Absent when the Agent ran no extension.
73
73
  */
74
74
  readonly declaredExtensions?: readonly string[];
75
+ /**
76
+ * Resolved name of the Agent named as this Agent's parent (Parent Link).
77
+ * Part of spawn identity; absent for a root Agent and for older Cassettes.
78
+ */
79
+ readonly parent?: string;
75
80
  }
76
81
  /** The frames attributed to one Ask marker. */
77
82
  export interface CassetteAsk {
@@ -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
+ * arrives with the forking spec.
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;
@@ -130,6 +139,19 @@ export type LifecycleEventBody = {
130
139
  readonly attempt: number;
131
140
  /** The candidate resolution picked next. */
132
141
  readonly resolvedModel: string;
142
+ } | {
143
+ /**
144
+ * The concrete model an Agent runs from now on, reported when it changes
145
+ * after spawn. Today the only producer is a successful mid-Ask model swap
146
+ * (ADR-0038, ADR-0041): `model_fallback` names the pattern the resolver
147
+ * picked, this names the `provider/id` pi landed on, so `agent.model`
148
+ * stays concrete. Spawn needs none: `agent_spawn.model` is already
149
+ * concrete.
150
+ */
151
+ readonly type: "agent_model";
152
+ readonly agent: string;
153
+ /** pi's concrete `provider/id` for the model the Agent runs now. */
154
+ readonly model: string;
133
155
  } | {
134
156
  readonly type: "agent_usage";
135
157
  readonly agent: string;
@@ -6,7 +6,7 @@ export type { ConfigEnvironment, ConfigExtension, ConfigLayerName, EffectiveConf
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";
@@ -6,7 +6,10 @@ export interface ModelFallback {
6
6
  readonly reason: ModelErrorReason;
7
7
  /** 0-based index of the failed attempt in the Agent's shared history. */
8
8
  readonly attempt: number;
9
- /** The candidate the resolver picked next. */
9
+ /**
10
+ * The candidate the resolver picked next. It is a yaag pattern, not a
11
+ * concrete `provider/id`, so it is never an Agent's model (ADR-0041).
12
+ */
10
13
  readonly resolvedModel: string;
11
14
  }
12
15
  /** Where a Model Resolution loop reports each fallback it takes. */
@@ -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, ExitedAgentInfo, IdleAgentInfo, ModelFallbackInfo, NodeInfo, SpawnOrigin, } from "./summary-agent.ts";
@@ -1,19 +1,28 @@
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
8
  /** The observer-facing lifecycle state of an Agent. */
9
9
  export type AgentState = "idle" | "asking" | "exited";
10
10
  /** Identity and accounting facts that apply in every observer Agent state. */
11
11
  interface AgentInfoBase {
12
+ /**
13
+ * Always the concrete `provider/id` pi reports — from `agent_spawn` at spawn
14
+ * and from `agent_model` after every Model Fallback that landed (ADR-0041).
15
+ * A `resolvedModel` pattern never lands here.
16
+ */
12
17
  readonly model: string | null;
13
18
  readonly cwd: string | null;
14
19
  readonly branch: string | null;
15
20
  /** pi's session file for this Agent, when the spawn reported one; a Peek reads it. */
16
21
  readonly sessionFile: string | null;
22
+ /** The Agent named as this one's parent (Parent Link), or null for a root Agent. */
23
+ readonly parent: string | null;
24
+ /** How the Agent came to be; "spawn" until forking ships. */
25
+ readonly origin: SpawnOrigin;
17
26
  readonly activity: AgentActivity | null;
18
27
  readonly tokens: TokenBreakdown | null;
19
28
  readonly cost: number | null;
@@ -85,6 +94,8 @@ export declare function spawnAgent(current: AgentRecord | undefined, identity: {
85
94
  readonly cwd: string;
86
95
  readonly branch?: string;
87
96
  readonly sessionFile?: string;
97
+ readonly parent?: string;
98
+ readonly origin?: SpawnOrigin;
88
99
  }, at: number | null): AgentRecord;
89
100
  /**
90
101
  * Folds an Ask start into the asking arm when it is not older than the folded
@@ -22,9 +22,8 @@ type ModelFallbackEvent = Extract<LifecycleEventBody, {
22
22
  * The Agent's `model` is left alone. `resolvedModel` is a yaag pattern, which
23
23
  * can be partial, and the event reports it before the swap is applied, while
24
24
  * `model` is pi's resolved `provider/id` for the model the Agent really runs.
25
- * `agent_spawn` is the only writer of that field: a mid-Ask swap updates the
26
- * Handle alone and reports no Lifecycle Event, so the Summary keeps the
27
- * spawn-time id.
25
+ * Only `agent_spawn` and `agent_model` write that field: a mid-Ask swap that
26
+ * lands reports the concrete model in its own `agent_model` event (ADR-0041).
28
27
  */
29
28
  export declare function applyModelFallback(agent: AgentRecord, event: ModelFallbackEvent, at: number | null): AgentRecord;
30
29
  export {};
@@ -0,0 +1,16 @@
1
+ import type { LifecycleEventBody } from "../events.ts";
2
+ import type { AgentRecord } from "./summary-agent.ts";
3
+ type AgentModelEvent = Extract<LifecycleEventBody, {
4
+ readonly type: "agent_model";
5
+ }>;
6
+ /**
7
+ * Folds the concrete model an Agent runs now (ADR-0041).
8
+ *
9
+ * The event reports pi's `provider/id` after a swap landed, so it replaces
10
+ * `model` and nothing else: a model change is not a lifecycle state change, and
11
+ * `state`, `askIndex`, `activity`, `stateChangedAt` and the bounded fallback
12
+ * table stay as they are. An exited Agent is skipped, because its model is
13
+ * frozen together with its final accounting.
14
+ */
15
+ export declare function applyAgentModel(agent: AgentRecord, event: AgentModelEvent): AgentRecord;
16
+ export {};
@@ -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, 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. */
@@ -45,6 +45,8 @@ export interface FakeTransportOptions extends FakePromptScript {
45
45
  readonly schemaCommandError?: string;
46
46
  /** The snapshot answered to `get_available_models`; defaults to this fake's own model. */
47
47
  readonly models?: readonly AvailableModel[];
48
+ /** The model this fake reports before any swap; defaults to a placeholder id. */
49
+ readonly model?: string;
48
50
  /** Makes a `set_model` command fail, as pi does for a pair it does not know. */
49
51
  readonly setModelError?: string;
50
52
  /** Makes a `set_thinking_level` command fail. */
@@ -162,6 +162,11 @@ export interface OpenOptions {
162
162
  * Spawn identity only: it never becomes argv, and it is absent when empty.
163
163
  */
164
164
  readonly declaredExtensions?: readonly string[];
165
+ /**
166
+ * Resolved name of the Agent named as this Agent's parent (Parent Link).
167
+ * Spawn identity only: it never becomes argv.
168
+ */
169
+ readonly parent?: string;
165
170
  /** Session storage directory. Used by the e2e suite to stay out of ~/.pi (ticket 06). */
166
171
  readonly sessionDir?: string;
167
172
  /** Resumes an existing pi session, translated to `--session <path>`. */
@@ -64,14 +64,23 @@ export interface SpawnOptions {
64
64
  readonly name?: string;
65
65
  /** Request a fresh Git worktree. The requested cwd remains the base until spawn resolves. */
66
66
  readonly worktree?: boolean;
67
+ /**
68
+ * Names this Agent's parent in the Run tree (Parent Link). Data only: it is
69
+ * no conversation channel and no lifetime rule.
70
+ */
71
+ readonly parent?: Handle;
67
72
  }
68
73
  /**
69
74
  * Spawn options after Model Resolution has settled one model and thinking level.
70
75
  *
71
76
  * This is the settled shape, and it is what the Cassette identity hashes
72
77
  * (ADR-0039).
78
+ *
79
+ * `parent` is omitted on purpose: a Handle must never reach this shape, because
80
+ * an Ask records these options into the Cassette. Spawn resolves the Parent
81
+ * Link to a name, which travels in `OpenOptions` and stays out of Ask identity.
73
82
  */
74
- export interface ResolvedSpawnOptions extends Omit<SpawnOptions, "model" | "thinking"> {
83
+ export interface ResolvedSpawnOptions extends Omit<SpawnOptions, "model" | "thinking" | "parent"> {
75
84
  readonly model?: string;
76
85
  readonly thinking?: ThinkingLevel;
77
86
  }
@@ -83,6 +92,8 @@ export interface SpawnOverrides {
83
92
  readonly cwd?: string;
84
93
  /** Request a fresh Git worktree. */
85
94
  readonly worktree?: boolean;
95
+ /** Names this Agent's parent in the Run tree (Parent Link). Data only. */
96
+ readonly parent?: Handle;
86
97
  }
87
98
  /**
88
99
  * Options for one Ask (ADR-0003).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaag/cli",
3
- "version": "0.8.3",
3
+ "version": "0.10.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.8.3",
25
- "@yaag/tui": "0.8.3",
24
+ "@yaag/runtime": "0.10.0",
25
+ "@yaag/tui": "0.10.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}] 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":
@@ -41,6 +41,8 @@ function renderPlainEvent(event: LifecycleEventBody, mode: PlainRenderMode): str
41
41
  }
42
42
  case "model_fallback":
43
43
  return `[${event.agent}] model ${event.failedModel} ${event.reason} — falling back to ${event.resolvedModel}`;
44
+ case "agent_model":
45
+ return `[${event.agent}] model \u2014 now running ${event.model}`;
44
46
  case "agent_exit":
45
47
  return `[${event.agent}] exit ${tokens(event.tokens)} ${cost(event.cost)}${event.incomplete ? " (killed mid-ask, cost incomplete)" : ""}`;
46
48
  case "run_end": {
@@ -61,6 +63,7 @@ function minimalEvent(type: LifecycleEventBody["type"]): boolean {
61
63
  type === "ask_start" ||
62
64
  type === "ask_end" ||
63
65
  type === "model_fallback" ||
66
+ type === "agent_model" ||
64
67
  type === "agent_exit" ||
65
68
  type === "run_end"
66
69
  );