@gotgenes/pi-subagents 13.1.0 → 13.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -90,13 +90,7 @@ export class SubagentsServiceAdapter implements SubagentsService {
90
90
  if (record?.status !== "running") {
91
91
  return false;
92
92
  }
93
- const session = record.session;
94
- if (!session) {
95
- // Session not ready yet — buffer on the agent for delivery once initialized
96
- record.queueSteer(message);
97
- return true;
98
- }
99
- await session.steer(message);
93
+ await record.steer(message);
100
94
  return true;
101
95
  }
102
96
 
@@ -108,7 +108,7 @@ export class AgentTool {
108
108
  `Agent not found: "${params.resume}". It may have been cleaned up.`,
109
109
  );
110
110
  }
111
- if (!existing.session) {
111
+ if (!existing.isSessionReady()) {
112
112
  return textResult(
113
113
  `Agent "${params.resume}" has no active session to resume.`,
114
114
  );
@@ -53,9 +53,10 @@ export function spawnBackground(
53
53
  isBackground: true,
54
54
  invocation: execution.agentInvocation,
55
55
  observer: {
56
- onSessionCreated: (_agent, session) => {
57
- bgState.setSession(session);
58
- subscribeUIObserver(session, bgState);
56
+ onSessionCreated: (agent) => {
57
+ const sub = agent.subagentSession!;
58
+ bgState.setSession(sub);
59
+ subscribeUIObserver(sub, bgState);
59
60
  },
60
61
  },
61
62
  });
@@ -108,10 +108,11 @@ export async function runForeground(
108
108
  signal,
109
109
  parentSession: params.parentSession,
110
110
  observer: {
111
- onSessionCreated: (agent, session) => {
112
- fgState.setSession(session);
111
+ onSessionCreated: (agent) => {
112
+ const sub = agent.subagentSession!;
113
+ fgState.setSession(sub);
113
114
  recordRef = agent;
114
- unsubUI = subscribeUIObserver(session, fgState, streamUpdate);
115
+ unsubUI = subscribeUIObserver(sub, fgState, streamUpdate);
115
116
  fgId = agent.id;
116
117
  agentActivity.set(agent.id, fgState);
117
118
  widget.ensureTimer();
@@ -1,8 +1,6 @@
1
1
  import { defineTool } from "@earendil-works/pi-coding-agent";
2
2
  import { Type } from "@sinclair/typebox";
3
3
  import type { AgentConfigLookup } from "#src/config/agent-types";
4
- import { getSessionContextPercent } from "#src/lifecycle/usage";
5
- import { getAgentConversation } from "#src/session/conversation";
6
4
  import { formatLifetimeTokens, textResult } from "#src/tools/helpers";
7
5
  import type { Agent } from "#src/types";
8
6
  import { formatDuration, getDisplayName } from "#src/ui/display";
@@ -53,7 +51,7 @@ export class GetResultTool {
53
51
  const displayName = getDisplayName(record.type, this.registry);
54
52
  const duration = formatDuration(record.startedAt, record.completedAt);
55
53
  const tokens = formatLifetimeTokens(record);
56
- const contextPercent = getSessionContextPercent(record.session);
54
+ const contextPercent = record.getContextPercent();
57
55
  const statsParts = [`Tool uses: ${record.toolUses}`];
58
56
  if (tokens) statsParts.push(tokens);
59
57
  if (contextPercent !== null) statsParts.push(`Context: ${Math.round(contextPercent)}%`);
@@ -80,11 +78,9 @@ export class GetResultTool {
80
78
  }
81
79
 
82
80
  // Verbose: include full conversation
83
- if (params.verbose && record.session) {
84
- const conversation = getAgentConversation(record.session);
85
- if (conversation) {
86
- output += `\n\n--- Agent Conversation ---\n${conversation}`;
87
- }
81
+ const conversation = params.verbose ? record.getConversation() : undefined;
82
+ if (conversation) {
83
+ output += `\n\n--- Agent Conversation ---\n${conversation}`;
88
84
  }
89
85
 
90
86
  return textResult(output);
@@ -1,6 +1,5 @@
1
1
  import { defineTool } from "@earendil-works/pi-coding-agent";
2
2
  import { Type } from "@sinclair/typebox";
3
- import { getSessionContextPercent } from "#src/lifecycle/usage";
4
3
  import { formatLifetimeTokens, textResult } from "#src/tools/helpers";
5
4
  import type { Agent } from "#src/types";
6
5
 
@@ -40,21 +39,16 @@ export class SteerTool {
40
39
  `Agent "${params.agent_id}" is not running (status: ${record.status}). Cannot steer a non-running agent.`,
41
40
  );
42
41
  }
43
- const session = record.session;
44
- if (!session) {
45
- // Session not ready yet — buffer on the agent for delivery once initialized
46
- record.queueSteer(params.message);
47
- this.events.emit("subagents:steered", { id: record.id, message: params.message });
48
- return textResult(
49
- `Steering message queued for agent ${record.id}. It will be delivered once the session initializes.`,
50
- );
51
- }
52
-
53
42
  try {
54
- await session.steer(params.message);
43
+ const delivered = await record.steer(params.message);
55
44
  this.events.emit("subagents:steered", { id: record.id, message: params.message });
45
+ if (!delivered) {
46
+ return textResult(
47
+ `Steering message queued for agent ${record.id}. It will be delivered once the session initializes.`,
48
+ );
49
+ }
56
50
  const tokens = formatLifetimeTokens(record);
57
- const contextPercent = getSessionContextPercent(session);
51
+ const contextPercent = record.getContextPercent();
58
52
  const stateParts: string[] = [];
59
53
  if (tokens) stateParts.push(tokens);
60
54
  stateParts.push(`${record.toolUses} tool ${record.toolUses === 1 ? "use" : "uses"}`);
@@ -252,8 +252,7 @@ export class AgentsMenuHandler {
252
252
  }
253
253
 
254
254
  private async viewAgentConversation(ui: MenuUI, record: Agent): Promise<void> {
255
- const session = record.session;
256
- if (!session) {
255
+ if (!record.isSessionReady()) {
257
256
  ui.notify(
258
257
  `Agent is ${record.status === "queued" ? "queued" : "expired"} — no session available.`,
259
258
  "info",
@@ -270,7 +269,6 @@ export class AgentsMenuHandler {
270
269
  (tui: any, theme: any, _keybindings: any, done: any) => {
271
270
  return new ConversationViewer({
272
271
  tui,
273
- session,
274
272
  record,
275
273
  activity,
276
274
  theme,
@@ -5,10 +5,9 @@
5
5
  * Subscribes to session events for real-time streaming updates.
6
6
  */
7
7
 
8
- import type { AgentSession } from "@earendil-works/pi-coding-agent";
9
8
  import { type Component, matchesKey, type TUI, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
10
9
  import type { AgentConfigLookup } from "#src/config/agent-types";
11
- import { getLifetimeTotal, getSessionContextPercent } from "#src/lifecycle/usage";
10
+ import { getLifetimeTotal } from "#src/lifecycle/usage";
12
11
  import type { Agent } from "#src/types";
13
12
  import type { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
14
13
  import { buildInvocationTags, formatDuration, formatSessionTokens, getDisplayName, getPromptModeLabel, type Theme } from "#src/ui/display";
@@ -24,7 +23,6 @@ export const VIEWPORT_HEIGHT_PCT = 70;
24
23
 
25
24
  export interface ConversationViewerOptions {
26
25
  tui: TUI;
27
- session: AgentSession;
28
26
  record: Agent;
29
27
  activity: AgentActivityTracker | undefined;
30
28
  theme: Theme;
@@ -41,7 +39,6 @@ export class ConversationViewer implements Component {
41
39
  private closed = false;
42
40
 
43
41
  private tui: TUI;
44
- private session: AgentSession;
45
42
  private record: Agent;
46
43
  private activity: AgentActivityTracker | undefined;
47
44
  private theme: Theme;
@@ -51,7 +48,6 @@ export class ConversationViewer implements Component {
51
48
 
52
49
  constructor({
53
50
  tui,
54
- session,
55
51
  record,
56
52
  activity,
57
53
  theme,
@@ -60,14 +56,13 @@ export class ConversationViewer implements Component {
60
56
  wrapText,
61
57
  }: ConversationViewerOptions) {
62
58
  this.tui = tui;
63
- this.session = session;
64
59
  this.record = record;
65
60
  this.activity = activity;
66
61
  this.theme = theme;
67
62
  this.done = done;
68
63
  this.registry = registry;
69
64
  this.wrapText = wrapText;
70
- this.unsubscribe = session.subscribe(() => {
65
+ this.unsubscribe = record.subscribeToUpdates(() => {
71
66
  if (this.closed) return;
72
67
  this.tui.requestRender();
73
68
  });
@@ -142,7 +137,7 @@ export class ConversationViewer implements Component {
142
137
  if (toolUses > 0) headerParts.unshift(`${toolUses} tool${toolUses === 1 ? "" : "s"}`);
143
138
  const tokens = getLifetimeTotal(this.record.lifetimeUsage);
144
139
  if (tokens > 0) {
145
- const percent = getSessionContextPercent(this.record.session);
140
+ const percent = this.record.getContextPercent();
146
141
  headerParts.push(formatSessionTokens(tokens, percent, th, this.record.compactionCount));
147
142
  }
148
143
 
@@ -220,7 +215,7 @@ export class ConversationViewer implements Component {
220
215
 
221
216
  const th = this.theme;
222
217
  const ctx = { theme: th, wrapText: this.wrapText };
223
- const messages = this.session.messages;
218
+ const messages = this.record.messages;
224
219
 
225
220
  if (messages.length === 0) {
226
221
  return [th.fg("dim", "(waiting for first message...)")];
@@ -229,7 +224,7 @@ export class ConversationViewer implements Component {
229
224
  const lines: string[] = [];
230
225
  let needsSeparator = false;
231
226
  for (const msg of messages) {
232
- const formatted = formatMessage(msg as unknown as { role: string; [key: string]: unknown }, width, ctx);
227
+ const formatted = formatMessage(msg as { role: string; [key: string]: unknown }, width, ctx);
233
228
  if (!formatted) continue;
234
229
  if (needsSeparator) lines.push(th.fg("dim", "───"));
235
230
  lines.push(...formatted);