@gotgenes/pi-subagents 6.6.0 → 6.8.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.
@@ -7,9 +7,10 @@ import { AgentTypeRegistry } from "../agent-types.js";
7
7
  import { resolveAgentInvocationConfig } from "../invocation-config.js";
8
8
  import { resolveInvocationModel } from "../model-resolver.js";
9
9
 
10
+ import { NotificationState } from "../notification-state.js";
10
11
  import type { AgentInvocation, AgentRecord, SubagentType } from "../types.js";
12
+ import { AgentActivityTracker } from "../ui/agent-activity-tracker.js";
11
13
  import {
12
- type AgentActivity,
13
14
  type AgentDetails,
14
15
  buildInvocationTags,
15
16
  describeActivity,
@@ -26,19 +27,6 @@ import { formatLifetimeTokens, textResult } from "./helpers.js";
26
27
 
27
28
  // ---- Agent-tool-specific helpers ----
28
29
 
29
- /** Create a fresh AgentActivity state for tracking UI progress. */
30
- function createAgentActivity(maxTurns?: number): AgentActivity {
31
- return {
32
- activeTools: new Map(),
33
- toolUses: 0,
34
- turnCount: 1,
35
- maxTurns,
36
- responseText: "",
37
- session: undefined,
38
- lifetimeUsage: { input: 0, output: 0, cacheWrite: 0 },
39
- };
40
- }
41
-
42
30
  /** Parenthetical status note for completed agent result text. */
43
31
  export function getStatusNote(status: string): string {
44
32
  switch (status) {
@@ -66,7 +54,7 @@ export function buildDetails(
66
54
  session?: any;
67
55
  lifetimeUsage: LifetimeUsage;
68
56
  },
69
- activity?: AgentActivity,
57
+ activity?: AgentActivityTracker,
70
58
  overrides?: Partial<AgentDetails>,
71
59
  ): AgentDetails {
72
60
  return {
@@ -106,7 +94,7 @@ export interface AgentToolWidget {
106
94
  export interface AgentToolDeps {
107
95
  manager: AgentToolManager;
108
96
  widget: AgentToolWidget;
109
- agentActivity: Map<string, AgentActivity>;
97
+ agentActivity: Map<string, AgentActivityTracker>;
110
98
  emitEvent: (name: string, data: unknown) => void;
111
99
  registry: AgentTypeRegistry;
112
100
  typeListText: string;
@@ -394,7 +382,7 @@ Guidelines:
394
382
  `Agent not found: "${params.resume}". It may have been cleaned up.`,
395
383
  );
396
384
  }
397
- if (!existing.session) {
385
+ if (!existing.execution?.session) {
398
386
  return textResult(
399
387
  `Agent "${params.resume}" has no active session to resume.`,
400
388
  );
@@ -415,7 +403,7 @@ Guidelines:
415
403
 
416
404
  // Background execution
417
405
  if (runInBackground) {
418
- const bgState = createAgentActivity(effectiveMaxTurns);
406
+ const bgState = new AgentActivityTracker(effectiveMaxTurns);
419
407
 
420
408
  let id: string;
421
409
 
@@ -433,7 +421,7 @@ Guidelines:
433
421
  isolation,
434
422
  invocation: agentInvocation,
435
423
  onSessionCreated: (session: any) => {
436
- bgState.session = session;
424
+ bgState.setSession(session);
437
425
  subscribeUIObserver(session, bgState);
438
426
  },
439
427
  });
@@ -443,7 +431,8 @@ Guidelines:
443
431
 
444
432
  const record = deps.manager.getRecord(id);
445
433
  if (record) {
446
- record.toolCallId = toolCallId;
434
+ // Born complete: notification-state object owns toolCallId + resultConsumed.
435
+ record.notification = new NotificationState(toolCallId);
447
436
  }
448
437
 
449
438
  deps.agentActivity.set(id, bgState);
@@ -464,7 +453,7 @@ Guidelines:
464
453
  `Agent ID: ${id}\n` +
465
454
  `Type: ${displayName}\n` +
466
455
  `Description: ${params.description}\n` +
467
- (record?.outputFile ? `Output file: ${record.outputFile}\n` : "") +
456
+ (record?.execution?.outputFile ? `Output file: ${record.execution.outputFile}\n` : "") +
468
457
  (isQueued
469
458
  ? `Position: queued (max ${deps.manager.getMaxConcurrent()} concurrent)\n`
470
459
  : "") +
@@ -487,7 +476,7 @@ Guidelines:
487
476
  const startedAt = Date.now();
488
477
  let fgId: string | undefined;
489
478
 
490
- const fgState = createAgentActivity(effectiveMaxTurns);
479
+ const fgState = new AgentActivityTracker(effectiveMaxTurns);
491
480
  let unsubUI: (() => void) | undefined;
492
481
 
493
482
  const streamUpdate = () => {
@@ -535,10 +524,10 @@ Guidelines:
535
524
  parentSessionFile: ctx.sessionManager.getSessionFile(),
536
525
  parentSessionId: ctx.sessionManager.getSessionId(),
537
526
  onSessionCreated: (session: any) => {
538
- fgState.session = session;
527
+ fgState.setSession(session);
539
528
  unsubUI = subscribeUIObserver(session, fgState, streamUpdate);
540
529
  for (const a of deps.manager.listAgents()) {
541
- if (a.session === session) {
530
+ if (a.execution?.session === session) {
542
531
  fgId = a.id;
543
532
  deps.agentActivity.set(a.id, fgState);
544
533
  deps.widget.ensureTimer();
@@ -54,7 +54,9 @@ export function createGetResultTool(deps: GetResultDeps) {
54
54
  // (attached earlier at spawn time) and always runs before this await resumes.
55
55
  // Setting the flag here prevents a redundant follow-up notification.
56
56
  if (params.wait && record.status === "running" && record.promise) {
57
- record.resultConsumed = true;
57
+ // Pre-mark consumed BEFORE awaiting — onComplete fires inside .then() and
58
+ // always runs before this await resumes. Prevents a redundant notification.
59
+ record.notification?.markConsumed();
58
60
  deps.cancelNudge(params.agent_id);
59
61
  await record.promise;
60
62
  }
@@ -62,7 +64,7 @@ export function createGetResultTool(deps: GetResultDeps) {
62
64
  const displayName = getDisplayName(record.type, deps.registry);
63
65
  const duration = formatDuration(record.startedAt, record.completedAt);
64
66
  const tokens = formatLifetimeTokens(record);
65
- const contextPercent = getSessionContextPercent(record.session);
67
+ const contextPercent = getSessionContextPercent(record.execution?.session);
66
68
  const statsParts = [`Tool uses: ${record.toolUses}`];
67
69
  if (tokens) statsParts.push(tokens);
68
70
  if (contextPercent !== null) statsParts.push(`Context: ${Math.round(contextPercent)}%`);
@@ -84,13 +86,13 @@ export function createGetResultTool(deps: GetResultDeps) {
84
86
 
85
87
  // Mark result as consumed — suppresses the completion notification
86
88
  if (record.status !== "running" && record.status !== "queued") {
87
- record.resultConsumed = true;
89
+ record.notification?.markConsumed();
88
90
  deps.cancelNudge(params.agent_id);
89
91
  }
90
92
 
91
93
  // Verbose: include full conversation
92
- if (params.verbose && record.session) {
93
- const conversation = deps.getConversation(record.session);
94
+ if (params.verbose && record.execution?.session) {
95
+ const conversation = deps.getConversation(record.execution.session);
94
96
  if (conversation) {
95
97
  output += `\n\n--- Agent Conversation ---\n${conversation}`;
96
98
  }
@@ -9,6 +9,8 @@ export interface SteerToolDeps {
9
9
  getRecord: (id: string) => AgentRecord | undefined;
10
10
  emitEvent: (name: string, data: unknown) => void;
11
11
  steerAgent: (session: AgentSession, message: string) => Promise<void>;
12
+ /** Buffer a steer for an agent whose session isn't ready yet. */
13
+ queueSteer: (id: string, message: string) => boolean;
12
14
  }
13
15
 
14
16
  /** Create the steer_subagent tool definition (without Pi SDK wrapper). */
@@ -46,10 +48,10 @@ export function createSteerTool(deps: SteerToolDeps) {
46
48
  `Agent "${params.agent_id}" is not running (status: ${record.status}). Cannot steer a non-running agent.`,
47
49
  );
48
50
  }
49
- if (!record.session) {
50
- // Session not ready yet — queue the steer for delivery once initialized
51
- if (!record.pendingSteers) record.pendingSteers = [];
52
- record.pendingSteers.push(params.message);
51
+ const session = record.execution?.session;
52
+ if (!session) {
53
+ // Session not ready yet — queue via manager for delivery once initialized
54
+ deps.queueSteer(record.id, params.message);
53
55
  deps.emitEvent("subagents:steered", { id: record.id, message: params.message });
54
56
  return textResult(
55
57
  `Steering message queued for agent ${record.id}. It will be delivered once the session initializes.`,
@@ -57,10 +59,10 @@ export function createSteerTool(deps: SteerToolDeps) {
57
59
  }
58
60
 
59
61
  try {
60
- await deps.steerAgent(record.session, params.message);
62
+ await deps.steerAgent(session, params.message);
61
63
  deps.emitEvent("subagents:steered", { id: record.id, message: params.message });
62
64
  const tokens = formatLifetimeTokens(record);
63
- const contextPercent = getSessionContextPercent(record.session);
65
+ const contextPercent = getSessionContextPercent(session);
64
66
  const stateParts: string[] = [];
65
67
  if (tokens) stateParts.push(tokens);
66
68
  stateParts.push(`${record.toolUses} tool ${record.toolUses === 1 ? "use" : "uses"}`);
@@ -0,0 +1,108 @@
1
+ /**
2
+ * agent-activity-tracker.ts — Per-agent live activity state with explicit transition methods.
3
+ *
4
+ * Replaces the mutable `AgentActivity` interface that was written via output arguments
5
+ * in `ui-observer.ts`. Callers use named transition methods; readers use read-only accessors.
6
+ */
7
+
8
+ import { addUsage, type LifetimeUsage, type SessionLike } from "../usage.js";
9
+
10
+ /** Usage delta accepted by onUsageUpdate — matches the LifetimeUsage accumulator shape. */
11
+ export interface UsageDelta {
12
+ input: number;
13
+ output: number;
14
+ cacheWrite: number;
15
+ }
16
+
17
+ /** Per-agent live activity state with explicit transition methods and read-only accessors. */
18
+ export class AgentActivityTracker {
19
+ private _activeTools = new Map<string, string>();
20
+ private _toolKeySeq = 0;
21
+ private _toolUses = 0;
22
+ private _responseText = "";
23
+ private _session: SessionLike | undefined = undefined;
24
+ private _turnCount = 1;
25
+ private _lifetimeUsage: LifetimeUsage = { input: 0, output: 0, cacheWrite: 0 };
26
+
27
+ constructor(private readonly _maxTurns?: number) {}
28
+
29
+ // ── Transition methods (write surface) ──────────────────────────────────
30
+
31
+ /** Record that a tool has started executing. */
32
+ onToolStart(toolName: string): void {
33
+ this._activeTools.set(toolName + "_" + (++this._toolKeySeq), toolName);
34
+ }
35
+
36
+ /** Record that a tool has finished executing; increments toolUses. No-op when no matching tool is active. */
37
+ onToolEnd(toolName: string): void {
38
+ for (const [key, name] of this._activeTools) {
39
+ if (name === toolName) {
40
+ this._activeTools.delete(key);
41
+ this._toolUses++;
42
+ break;
43
+ }
44
+ }
45
+ }
46
+
47
+ /** Reset the current response text (called at the start of each assistant message). */
48
+ onMessageStart(): void {
49
+ this._responseText = "";
50
+ }
51
+
52
+ /** Append a text delta to the current response text. */
53
+ onMessageUpdate(delta: string): void {
54
+ this._responseText += delta;
55
+ }
56
+
57
+ /** Record that a turn has ended; increments turnCount. */
58
+ onTurnEnd(): void {
59
+ this._turnCount++;
60
+ }
61
+
62
+ /** Accumulate a usage delta into the lifetime usage totals. */
63
+ onUsageUpdate(delta: UsageDelta): void {
64
+ addUsage(this._lifetimeUsage, delta);
65
+ }
66
+
67
+ /** Bind the session reference (called once when the agent session is created). */
68
+ setSession(session: SessionLike): void {
69
+ this._session = session;
70
+ }
71
+
72
+ // ── Read-only accessors ──────────────────────────────────────────────────
73
+
74
+ /** Currently-active tools: key → tool name. Multiple entries for concurrent same-name tools. */
75
+ get activeTools(): ReadonlyMap<string, string> {
76
+ return this._activeTools;
77
+ }
78
+
79
+ /** Total completed tool invocations. */
80
+ get toolUses(): number {
81
+ return this._toolUses;
82
+ }
83
+
84
+ /** The agent's latest partial response text (reset at each message start). */
85
+ get responseText(): string {
86
+ return this._responseText;
87
+ }
88
+
89
+ /** The active SDK session, or undefined before the first session is created. */
90
+ get session(): SessionLike | undefined {
91
+ return this._session;
92
+ }
93
+
94
+ /** Current turn count (starts at 1). */
95
+ get turnCount(): number {
96
+ return this._turnCount;
97
+ }
98
+
99
+ /** Effective max turns for this agent, or undefined for unlimited. */
100
+ get maxTurns(): number | undefined {
101
+ return this._maxTurns;
102
+ }
103
+
104
+ /** Accumulated lifetime token usage (survives compaction). */
105
+ get lifetimeUsage(): Readonly<LifetimeUsage> {
106
+ return this._lifetimeUsage;
107
+ }
108
+ }
@@ -9,7 +9,7 @@ import {
9
9
  } from "../agent-types.js";
10
10
  import type { ModelRegistry } from "../model-resolver.js";
11
11
  import type { AgentConfig, AgentRecord } from "../types.js";
12
- import type { AgentActivity } from "./agent-widget.js";
12
+ import type { AgentActivityTracker } from "./agent-activity-tracker.js";
13
13
  import { formatDuration, getDisplayName } from "./agent-widget.js";
14
14
 
15
15
  // ---- Deps interface ----
@@ -35,7 +35,7 @@ export interface AgentMenuSettings {
35
35
  export interface AgentMenuDeps {
36
36
  manager: AgentMenuManager;
37
37
  registry: AgentTypeRegistry;
38
- agentActivity: Map<string, AgentActivity>;
38
+ agentActivity: Map<string, AgentActivityTracker>;
39
39
  /** Resolve model label for a given agent type + registry. */
40
40
  getModelLabel: (type: string, registry?: ModelRegistry) => string;
41
41
  /** Settings manager — owns in-memory values and persistence. */
@@ -197,7 +197,8 @@ export function createAgentsMenuHandler(deps: AgentMenuDeps) {
197
197
  }
198
198
 
199
199
  async function viewAgentConversation(ctx: ExtensionContext, record: AgentRecord) {
200
- if (!record.session) {
200
+ const session = record.execution?.session;
201
+ if (!session) {
201
202
  ctx.ui.notify(
202
203
  `Agent is ${record.status === "queued" ? "queued" : "expired"} — no session available.`,
203
204
  "info",
@@ -208,7 +209,6 @@ export function createAgentsMenuHandler(deps: AgentMenuDeps) {
208
209
  const { ConversationViewer, VIEWPORT_HEIGHT_PCT } = await import(
209
210
  "./conversation-viewer.js"
210
211
  );
211
- const session = record.session;
212
212
  const activity = deps.agentActivity.get(record.id);
213
213
 
214
214
  await ctx.ui.custom<undefined>(
@@ -9,7 +9,8 @@ import { truncateToWidth } from "@earendil-works/pi-tui";
9
9
  import type { AgentManager } from "../agent-manager.js";
10
10
  import { type AgentConfigLookup, AgentTypeRegistry } from "../agent-types.js";
11
11
  import type { AgentInvocation, SubagentType } from "../types.js";
12
- import { getLifetimeTotal, getSessionContextPercent, type LifetimeUsage, type SessionLike } from "../usage.js";
12
+ import { getLifetimeTotal, getSessionContextPercent } from "../usage.js";
13
+ import type { AgentActivityTracker } from "./agent-activity-tracker.js";
13
14
 
14
15
  // ---- Constants ----
15
16
 
@@ -49,20 +50,6 @@ export type UICtx = {
49
50
  ): void;
50
51
  };
51
52
 
52
- /** Per-agent live activity state. */
53
- export interface AgentActivity {
54
- activeTools: Map<string, string>;
55
- toolUses: number;
56
- responseText: string;
57
- session?: SessionLike;
58
- /** Current turn count. */
59
- turnCount: number;
60
- /** Effective max turns for this agent (undefined = unlimited). */
61
- maxTurns?: number;
62
- /** Lifetime usage breakdown — see LifetimeUsage docs. */
63
- lifetimeUsage: LifetimeUsage;
64
- }
65
-
66
53
  /** Metadata attached to Agent tool results for custom rendering. */
67
54
  export interface AgentDetails {
68
55
  displayName: string;
@@ -177,7 +164,7 @@ function truncateLine(text: string, len = 60): string {
177
164
  }
178
165
 
179
166
  /** Build a human-readable activity string from currently-running tools or response text. */
180
- export function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
167
+ export function describeActivity(activeTools: ReadonlyMap<string, string>, responseText?: string): string {
181
168
  if (activeTools.size > 0) {
182
169
  const groups = new Map<string, number>();
183
170
  for (const toolName of activeTools.values()) {
@@ -224,7 +211,7 @@ export class AgentWidget {
224
211
 
225
212
  constructor(
226
213
  private manager: AgentManager,
227
- private agentActivity: Map<string, AgentActivity>,
214
+ private agentActivity: Map<string, AgentActivityTracker>,
228
215
  private registry: AgentTypeRegistry,
229
216
  ) {}
230
217
 
@@ -11,8 +11,8 @@ import type { AgentConfigLookup } from "../agent-types.js";
11
11
  import { extractText } from "../context.js";
12
12
  import type { AgentRecord } from "../types.js";
13
13
  import { getLifetimeTotal, getSessionContextPercent } from "../usage.js";
14
- import type { Theme } from "./agent-widget.js";
15
- import { type AgentActivity, buildInvocationTags, describeActivity, formatDuration, formatSessionTokens, getDisplayName, getPromptModeLabel } from "./agent-widget.js";
14
+ import type { AgentActivityTracker } from "./agent-activity-tracker.js";
15
+ import { buildInvocationTags, describeActivity, formatDuration, formatSessionTokens, getDisplayName, getPromptModeLabel, type Theme } from "./agent-widget.js";
16
16
 
17
17
  /** Base lines consumed by chrome: top border + header + header sep + footer sep + footer + bottom border. */
18
18
  const CHROME_LINES_BASE = 6;
@@ -31,7 +31,7 @@ export class ConversationViewer implements Component {
31
31
  private tui: TUI,
32
32
  private session: AgentSession,
33
33
  private record: AgentRecord,
34
- private activity: AgentActivity | undefined,
34
+ private activity: AgentActivityTracker | undefined,
35
35
  private theme: Theme,
36
36
  private done: (result: undefined) => void,
37
37
  private registry: AgentConfigLookup,
@@ -1,13 +1,12 @@
1
1
  /**
2
- * ui-observer.ts — Subscribes to session events and updates AgentActivity state.
2
+ * ui-observer.ts — Subscribes to session events and updates AgentActivityTracker state.
3
3
  *
4
4
  * Replaces the callback-based createActivityTracker pattern with a direct
5
5
  * session subscription for streaming UI state (active tools, response text,
6
6
  * turn count, lifetime usage).
7
7
  */
8
8
 
9
- import { addUsage } from "../usage.js";
10
- import type { AgentActivity } from "./agent-widget.js";
9
+ import type { AgentActivityTracker } from "./agent-activity-tracker.js";
11
10
 
12
11
  /** Narrow session interface — only the subscribe method needed by the observer. */
13
12
  interface SubscribableSession {
@@ -15,15 +14,15 @@ interface SubscribableSession {
15
14
  }
16
15
 
17
16
  /**
18
- * Subscribe to session events and stream UI state into an AgentActivity object.
17
+ * Subscribe to session events and stream UI state into an AgentActivityTracker.
19
18
  *
20
19
  * Handles:
21
- * - `tool_execution_start` → add to `state.activeTools`
22
- * - `tool_execution_end` → remove from `state.activeTools`, `state.toolUses++`
23
- * - `message_start` → reset `state.responseText`
24
- * - `message_update` (text_delta) → append to `state.responseText`
25
- * - `turn_end` → `state.turnCount++`
26
- * - `message_end` (assistant, with usage) → `addUsage(state.lifetimeUsage, …)`
20
+ * - `tool_execution_start` → `tracker.onToolStart(name)`
21
+ * - `tool_execution_end` → `tracker.onToolEnd(name)`
22
+ * - `message_start` → `tracker.onMessageStart()`
23
+ * - `message_update` (text_delta) → `tracker.onMessageUpdate(delta)`
24
+ * - `turn_end` → `tracker.onTurnEnd()`
25
+ * - `message_end` (assistant, with usage) → `tracker.onUsageUpdate(usage)`
27
26
  *
28
27
  * Calls `onUpdate?.()` after each state mutation to trigger re-renders.
29
28
  *
@@ -31,47 +30,41 @@ interface SubscribableSession {
31
30
  */
32
31
  export function subscribeUIObserver(
33
32
  session: SubscribableSession,
34
- state: AgentActivity,
33
+ tracker: AgentActivityTracker,
35
34
  onUpdate?: () => void,
36
35
  ): () => void {
37
36
  return session.subscribe((event: any) => {
38
37
  if (event.type === "tool_execution_start") {
39
- state.activeTools.set(event.toolName + "_" + Date.now(), event.toolName);
38
+ tracker.onToolStart(event.toolName);
40
39
  onUpdate?.();
41
40
  }
42
41
 
43
42
  if (event.type === "tool_execution_end") {
44
- for (const [key, name] of state.activeTools) {
45
- if (name === event.toolName) {
46
- state.activeTools.delete(key);
47
- break;
48
- }
49
- }
50
- state.toolUses++;
43
+ tracker.onToolEnd(event.toolName);
51
44
  onUpdate?.();
52
45
  }
53
46
 
54
47
  if (event.type === "message_start") {
55
- state.responseText = "";
48
+ tracker.onMessageStart();
56
49
  }
57
50
 
58
51
  if (
59
52
  event.type === "message_update" &&
60
53
  event.assistantMessageEvent?.type === "text_delta"
61
54
  ) {
62
- state.responseText += event.assistantMessageEvent.delta;
55
+ tracker.onMessageUpdate(event.assistantMessageEvent.delta);
63
56
  onUpdate?.();
64
57
  }
65
58
 
66
59
  if (event.type === "turn_end") {
67
- state.turnCount++;
60
+ tracker.onTurnEnd();
68
61
  onUpdate?.();
69
62
  }
70
63
 
71
64
  if (event.type === "message_end" && event.message?.role === "assistant") {
72
65
  const u = event.message.usage;
73
66
  if (u) {
74
- addUsage(state.lifetimeUsage, {
67
+ tracker.onUsageUpdate({
75
68
  input: u.input ?? 0,
76
69
  output: u.output ?? 0,
77
70
  cacheWrite: u.cacheWrite ?? 0,
@@ -0,0 +1,35 @@
1
+ /**
2
+ * worktree-state.ts — WorktreeState: lifecycle-phase object for worktree-isolated agents.
3
+ *
4
+ * Constructed once when the worktree is set up (before the run begins).
5
+ * Only exists for agents with isolation: "worktree".
6
+ * cleanupResult is recorded once at completion or error — it is not set at construction.
7
+ */
8
+
9
+ import type { WorktreeCleanupResult, WorktreeInfo } from "./worktree.js";
10
+
11
+ export type { WorktreeCleanupResult, WorktreeInfo };
12
+
13
+ export class WorktreeState {
14
+ /** Absolute path to the worktree directory. */
15
+ readonly path: string;
16
+ /** Branch name created for this worktree. */
17
+ readonly branch: string;
18
+
19
+ private _cleanupResult?: WorktreeCleanupResult;
20
+
21
+ constructor(info: WorktreeInfo) {
22
+ this.path = info.path;
23
+ this.branch = info.branch;
24
+ }
25
+
26
+ /** Result of the worktree cleanup — undefined until recordCleanup is called. */
27
+ get cleanupResult(): WorktreeCleanupResult | undefined {
28
+ return this._cleanupResult;
29
+ }
30
+
31
+ /** Record the cleanup result. Called once on agent completion or error. */
32
+ recordCleanup(result: WorktreeCleanupResult): void {
33
+ this._cleanupResult = result;
34
+ }
35
+ }