@diffexai/diffex 0.2.7 → 0.3.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 (47) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/core/agent-session.d.ts +5 -0
  3. package/dist/core/agent-session.js +58 -8
  4. package/dist/core/compaction/compaction.js +3 -1
  5. package/dist/core/sdk.d.ts +2 -1
  6. package/dist/core/sdk.js +18 -6
  7. package/dist/core/subagents/controller.d.ts +4 -3
  8. package/dist/core/subagents/controller.js +173 -37
  9. package/dist/core/subagents/types.d.ts +13 -0
  10. package/dist/core/tools/bash.d.ts +4 -0
  11. package/dist/core/tools/bash.js +14 -5
  12. package/dist/core/tools/subagents.d.ts +12 -10
  13. package/dist/core/tools/subagents.js +61 -49
  14. package/dist/node_modules/@diffexai/diffex-agent-core/distribution-components.json +4 -4
  15. package/dist/node_modules/@diffexai/diffex-agent-core/distribution-files.json +1 -1
  16. package/dist/node_modules/@diffexai/diffex-agent-core/package.json +1 -1
  17. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/.manifest.json +1 -1
  18. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/opencode.json +1 -1
  19. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/openrouter.json +1 -1
  20. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  21. package/dist/node_modules/@diffexai/diffex-ai/distribution-components.json +3 -3
  22. package/dist/node_modules/@diffexai/diffex-ai/distribution-files.json +5 -5
  23. package/dist/node_modules/@diffexai/diffex-ai/package.json +1 -1
  24. package/dist/node_modules/@diffexai/diffex-client/distribution-components.json +3 -3
  25. package/dist/node_modules/@diffexai/diffex-client/distribution-files.json +1 -1
  26. package/dist/node_modules/@diffexai/diffex-client/package.json +1 -1
  27. package/dist/node_modules/@diffexai/diffex-harness-state/distribution-components.json +2 -2
  28. package/dist/node_modules/@diffexai/diffex-harness-state/distribution-files.json +1 -1
  29. package/dist/node_modules/@diffexai/diffex-harness-state/package.json +1 -1
  30. package/dist/node_modules/@diffexai/diffex-protocol/distribution-components.json +2 -2
  31. package/dist/node_modules/@diffexai/diffex-protocol/distribution-files.json +1 -1
  32. package/dist/node_modules/@diffexai/diffex-protocol/package.json +1 -1
  33. package/dist/node_modules/@diffexai/diffex-telemetry/distribution-components.json +2 -2
  34. package/dist/node_modules/@diffexai/diffex-telemetry/distribution-files.json +1 -1
  35. package/dist/node_modules/@diffexai/diffex-telemetry/package.json +1 -1
  36. package/dist/node_modules/@diffexai/diffex-tui/distribution-components.json +2 -2
  37. package/dist/node_modules/@diffexai/diffex-tui/distribution-files.json +1 -1
  38. package/dist/node_modules/@diffexai/diffex-tui/package.json +1 -1
  39. package/dist/utils/owned-shell-processes.d.ts +8 -0
  40. package/dist/utils/owned-shell-processes.js +61 -0
  41. package/distribution-components.json +11 -11
  42. package/distribution-files.json +46 -38
  43. package/npm-shrinkwrap.json +2 -2
  44. package/package.json +1 -1
  45. package/release/distribution-manifest.json +4 -4
  46. package/release/install-package-lock.json +5 -5
  47. package/release/install-package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0] - 2026-09-20
4
+
5
+ ### New Features
6
+
7
+ - Added nonblocking `agent_status` for immediate child-status and result inspection, with automatic coordination for child completion. See [Automatic Sub-agent Coordination](docs/usage.md#automatic-sub-agent-coordination) ([#234](https://github.com/diffexai/diffex/pull/234)).
8
+ - Added automatic subagent supervision with renewable work allowances and parent-deadline bounds. See [SDK tools](docs/sdk.md#tools) ([#233](https://github.com/diffexai/diffex/pull/233)).
9
+
10
+ ### Breaking Changes
11
+
12
+ - Replaced the blocking `wait_for_agents` tool with nonblocking `agent_status`; rely on automatic child-result coordination instead of tool-managed waits ([#234](https://github.com/diffexai/diffex/pull/234)).
13
+
14
+ ### Fixed
15
+
16
+ - Fixed automatic compaction to check complete tool-result batches ([#233](https://github.com/diffexai/diffex/pull/233)).
17
+ - Fixed subagent completion and cancellation, including renewable work-allowance supervision ([#233](https://github.com/diffexai/diffex/pull/233)).
18
+ - Fixed session and subagent cancellation to terminate owned background shell processes ([#233](https://github.com/diffexai/diffex/pull/233)).
19
+
3
20
  ## [0.2.7] - 2026-09-18
4
21
 
5
22
  ### Fixed
@@ -263,6 +263,8 @@ export declare class AgentSession {
263
263
  private readonly _bashAbortControllers;
264
264
  private _pendingBashMessages;
265
265
  private readonly _ownedOperations;
266
+ private readonly _shellProcesses;
267
+ private readonly _bashOperations;
266
268
  private _extensionRunner;
267
269
  private _turnIndex;
268
270
  private _resourceLoader;
@@ -561,6 +563,9 @@ export declare class AgentSession {
561
563
  */
562
564
  abort(): Promise<void>;
563
565
  private _abort;
566
+ /** Stop and verify all shell processes owned by this session, including background jobs. */
567
+ terminateOwnedShellProcesses(): Promise<void>;
568
+ get ownedShellProcessCount(): number;
564
569
  waitForIdle(): Promise<void>;
565
570
  private _emitModelSelect;
566
571
  /**
@@ -18,6 +18,7 @@ import { NOOP_TELEMETRY_CONTEXT, uuidv7, } from "@diffexai/diffex-agent-core";
18
18
  import { contentText } from "@diffexai/diffex-ai";
19
19
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@diffexai/diffex-ai/compat";
20
20
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
21
+ import { OwnedShellProcesses } from "../utils/owned-shell-processes.js";
21
22
  import { canonicalizePath, resolvePath } from "../utils/paths.js";
22
23
  import { sleep } from "../utils/sleep.js";
23
24
  import { normalizeToolResultImages } from "../utils/tool-result-images.js";
@@ -161,6 +162,8 @@ export class AgentSession {
161
162
  _bashAbortControllers = new Set();
162
163
  _pendingBashMessages = [];
163
164
  _ownedOperations = new Set();
165
+ _shellProcesses = new OwnedShellProcesses();
166
+ _bashOperations = new Set();
164
167
  // Extension system
165
168
  _extensionRunner;
166
169
  _turnIndex = 0;
@@ -402,10 +405,25 @@ export class AgentSession {
402
405
  this.agent.prepareNextTurnWithContext = async (turn, signal) => {
403
406
  const previousSnapshot = await previousPrepareNextTurnWithContext?.(turn, signal);
404
407
  const previousContext = previousSnapshot?.context ?? turn.context;
408
+ let messages = previousContext.messages;
409
+ // Tool output is part of the next request even though the model's usage
410
+ // only describes the request before that output. Check every complete batch.
411
+ if (turn.toolResults.length > 0 && !signal?.aborted && this.model) {
412
+ const settings = this.settingsManager.getCompactionSettings();
413
+ if (shouldCompact(estimateContextTokens(messages).tokens, this.model.contextWindow, settings)) {
414
+ const beforeCompaction = this.agent.state.messages;
415
+ await this._runAutoCompaction("threshold", true, signal);
416
+ if (this.agent.state.messages !== beforeCompaction) {
417
+ messages = this.agent.state.messages;
418
+ }
419
+ }
420
+ }
421
+ signal?.throwIfAborted();
405
422
  return {
406
423
  ...previousSnapshot,
407
424
  context: {
408
425
  ...previousContext,
426
+ messages,
409
427
  systemPrompt: this._activeRunCollaborationState?.effectiveSystemPrompt ??
410
428
  this._deriveEffectivePrompt(this._systemPromptOverride ?? this._baseSystemPrompt, this._activeRunCollaborationMode ?? this._collaborationMode),
411
429
  tools: (this._activeRunCollaborationState?.tools ?? this.agent.state.tools).slice(),
@@ -836,7 +854,7 @@ export class AgentSession {
836
854
  failures.push(error);
837
855
  }
838
856
  const ownedOperationsIdle = this._waitForOwnedOperations();
839
- const cleanupResults = await Promise.allSettled([childCleanup, rootIdle, ownedOperationsIdle].filter((promise) => promise !== undefined));
857
+ const cleanupResults = await Promise.allSettled([childCleanup, rootIdle, ownedOperationsIdle, this.terminateOwnedShellProcesses()].filter((promise) => promise !== undefined));
840
858
  for (const result of cleanupResults) {
841
859
  if (result.status !== "rejected")
842
860
  continue;
@@ -1651,6 +1669,22 @@ export class AgentSession {
1651
1669
  if (coordination.exitReason === "cancellation" || this._agentRunAborted) {
1652
1670
  this._exitSubagentCoordination("cancellation");
1653
1671
  }
1672
+ else if (!coordination.completionChecked) {
1673
+ coordination.completionChecked = true;
1674
+ const active = this.getSubagents().filter((child) => coordination.names.includes(child.name) && (child.status === "running" || child.status === "queued"));
1675
+ if (active.length > 0) {
1676
+ await this.sendCustomMessage({
1677
+ customType: "subagent_completion_check",
1678
+ content: JSON.stringify({
1679
+ type: "subagent_completion_check",
1680
+ agents: active,
1681
+ instruction: "Review active children before finishing: cancel obsolete work with agent_action interrupt. If needed work remains, end your response to wait automatically. Renew only justified work.",
1682
+ }),
1683
+ display: false,
1684
+ }, { deliverAs: "steer" });
1685
+ return "continue";
1686
+ }
1687
+ }
1654
1688
  const delivery = await this._waitForSubagentCoordination(coordination);
1655
1689
  if (delivery.reason === "cancellation")
1656
1690
  return "settled";
@@ -2354,11 +2388,21 @@ export class AgentSession {
2354
2388
  }
2355
2389
  async _abort() {
2356
2390
  this.abortRetry();
2391
+ this.abortCompaction();
2392
+ this.abortBranchSummary();
2393
+ this.abortBash();
2357
2394
  if (this._isAgentRunActive)
2358
2395
  this._agentRunAborted = true;
2359
2396
  this._exitSubagentCoordination("cancellation");
2360
2397
  this.agent.abort();
2361
- await this.waitForIdle();
2398
+ await Promise.all([this.waitForIdle(), this.terminateOwnedShellProcesses(), ...this._bashOperations]);
2399
+ }
2400
+ /** Stop and verify all shell processes owned by this session, including background jobs. */
2401
+ async terminateOwnedShellProcesses() {
2402
+ await this._shellProcesses.terminateAll();
2403
+ }
2404
+ get ownedShellProcessCount() {
2405
+ return this._shellProcesses.count;
2362
2406
  }
2363
2407
  async waitForIdle() {
2364
2408
  if (this.isIdle) {
@@ -2847,6 +2891,9 @@ export class AgentSession {
2847
2891
  return false;
2848
2892
  }
2849
2893
  this._autoCompactionAbortController = new AbortController();
2894
+ const compactionSignal = signal
2895
+ ? AbortSignal.any([signal, this._autoCompactionAbortController.signal])
2896
+ : this._autoCompactionAbortController.signal;
2850
2897
  this._emit({ type: "compaction_start", reason });
2851
2898
  started = true;
2852
2899
  let extensionCompaction;
@@ -2859,7 +2906,7 @@ export class AgentSession {
2859
2906
  customInstructions: undefined,
2860
2907
  reason,
2861
2908
  willRetry,
2862
- signal: this._autoCompactionAbortController.signal,
2909
+ signal: compactionSignal,
2863
2910
  }));
2864
2911
  if (extensionResult?.cancel) {
2865
2912
  this._emit({
@@ -2891,14 +2938,14 @@ export class AgentSession {
2891
2938
  }
2892
2939
  else {
2893
2940
  // Generate compaction result
2894
- const compactResult = await compact(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }, telemetryContext), telemetryContext);
2941
+ const compactResult = await compact(preparation, requestModel, apiKey, headers, undefined, compactionSignal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }, telemetryContext), telemetryContext);
2895
2942
  summary = compactResult.summary;
2896
2943
  firstKeptEntryId = compactResult.firstKeptEntryId;
2897
2944
  tokensBefore = compactResult.tokensBefore;
2898
2945
  usage = compactResult.usage;
2899
2946
  details = compactResult.details;
2900
2947
  }
2901
- if (this._autoCompactionAbortController.signal.aborted) {
2948
+ if (compactionSignal.aborted) {
2902
2949
  this._emit({
2903
2950
  type: "compaction_end",
2904
2951
  reason,
@@ -3257,7 +3304,7 @@ export class AgentSession {
3257
3304
  : {
3258
3305
  ...createAllToolDefinitions(this._cwd, {
3259
3306
  read: { autoResizeImages },
3260
- bash: { commandPrefix: shellCommandPrefix, shellPath },
3307
+ bash: { commandPrefix: shellCommandPrefix, shellPath, owner: this._shellProcesses },
3261
3308
  }),
3262
3309
  update_plan: createSessionUpdatePlanToolDefinition(this.sessionManager),
3263
3310
  };
@@ -3458,7 +3505,7 @@ export class AgentSession {
3458
3505
  * @param options.operations Custom BashOperations for remote execution
3459
3506
  */
3460
3507
  executeBash(command, onChunk, options) {
3461
- return this._runOwnedOperation(() => this._traceOperation("bash", () => startCodingAgentSpan(this.agent.telemetryContext, "diffex.coding_agent.terminal", { "diffex.session.id": this.sessionId }, async (span) => {
3508
+ const operation = this._runOwnedOperation(() => this._traceOperation("bash", () => startCodingAgentSpan(this.agent.telemetryContext, "diffex.coding_agent.terminal", { "diffex.session.id": this.sessionId }, async (span) => {
3462
3509
  const result = await this._executeBash(command, onChunk, options);
3463
3510
  const outcome = result.cancelled ? "cancelled" : result.exitCode === 0 ? "completed" : "failed";
3464
3511
  span.setAttributes({
@@ -3469,6 +3516,9 @@ export class AgentSession {
3469
3516
  span.setStatus({ status: "error" });
3470
3517
  return result;
3471
3518
  })));
3519
+ this._bashOperations.add(operation);
3520
+ void operation.then(() => this._bashOperations.delete(operation), () => this._bashOperations.delete(operation));
3521
+ return operation;
3472
3522
  }
3473
3523
  async _executeBash(command, onChunk, options) {
3474
3524
  const abortController = new AbortController();
@@ -3478,7 +3528,7 @@ export class AgentSession {
3478
3528
  const shellPath = this.settingsManager.getShellPath();
3479
3529
  const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
3480
3530
  try {
3481
- const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), {
3531
+ const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath, owner: this._shellProcesses }), {
3482
3532
  onChunk: (delta) => {
3483
3533
  onChunk?.(delta);
3484
3534
  this._emit({ type: "bash_execution_update", id: options?.id, delta });
@@ -322,7 +322,9 @@ export function findCutPoint(entries, startIndex, endIndex, keepRecentTokens) {
322
322
  accumulatedTokens += messageTokens;
323
323
  // Check if we've exceeded the budget
324
324
  if (accumulatedTokens >= keepRecentTokens) {
325
- // Find the closest valid cut point at or after this entry
325
+ // A completed tool batch can end the transcript. If no cut point follows
326
+ // its results, keep the batch's assistant message and all paired results.
327
+ cutIndex = cutPoints[cutPoints.length - 1];
326
328
  for (let c = 0; c < cutPoints.length; c++) {
327
329
  if (cutPoints[c] >= i) {
328
330
  cutIndex = cutPoints[c];
@@ -8,8 +8,9 @@ import { ModelRuntime } from "./model-runtime.ts";
8
8
  import type { ResourceLoader } from "./resource-loader.ts";
9
9
  import { SessionManager } from "./session-manager.ts";
10
10
  import { SettingsManager } from "./settings-manager.ts";
11
+ import type { SubagentSupervisionOptions } from "./subagents/types.ts";
11
12
  import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createUpdatePlanTool, createWriteTool, withFileMutationQueue } from "./tools/index.ts";
12
- export interface SubagentOptions {
13
+ export interface SubagentOptions extends SubagentSupervisionOptions {
13
14
  /** Maximum number of child agents that may run concurrently. Default: 4 */
14
15
  maxConcurrent?: number;
15
16
  }
package/dist/core/sdk.js CHANGED
@@ -29,7 +29,7 @@ import { createBashTool, createCodingTools, createEditTool, createFindTool, crea
29
29
  // provider-agnostic and does not import Diffex AI compat itself.
30
30
  setDefaultStreamFn(streamSimple);
31
31
  const DEFAULT_SUBAGENT_CONCURRENCY = 4;
32
- const RESERVED_SUBAGENT_TOOL_NAMES = new Set(["spawn_agent", "agent_action", "wait_for_agents", "escalate_to_parent"]);
32
+ const RESERVED_SUBAGENT_TOOL_NAMES = new Set(["spawn_agent", "agent_action", "agent_status", "escalate_to_parent"]);
33
33
  // Re-exports
34
34
  export * from "./agent-session-runtime.js";
35
35
  export { withFileMutationQueue,
@@ -48,7 +48,7 @@ function normalizeSubagentOptions(value) {
48
48
  const options = value;
49
49
  if (options) {
50
50
  for (const key of Object.keys(options)) {
51
- if (key !== "maxConcurrent") {
51
+ if (!["maxConcurrent", "leaseMs", "supervisionIntervalMs", "parentDeadline"].includes(key)) {
52
52
  throw new Error(`Unknown subagents option: ${key}`);
53
53
  }
54
54
  }
@@ -57,7 +57,19 @@ function normalizeSubagentOptions(value) {
57
57
  if (!Number.isInteger(maxConcurrent) || maxConcurrent <= 0) {
58
58
  throw new RangeError("subagents.maxConcurrent must be a positive integer");
59
59
  }
60
- return { maxConcurrent };
60
+ return {
61
+ maxConcurrent,
62
+ leaseMs: options?.leaseMs ??
63
+ (process.env.DIFFEX_SUBAGENT_LEASE_MS === undefined
64
+ ? undefined
65
+ : Number(process.env.DIFFEX_SUBAGENT_LEASE_MS)),
66
+ supervisionIntervalMs: options?.supervisionIntervalMs ??
67
+ (process.env.DIFFEX_SUBAGENT_SUPERVISION_MS === undefined
68
+ ? undefined
69
+ : Number(process.env.DIFFEX_SUBAGENT_SUPERVISION_MS)),
70
+ parentDeadline: options?.parentDeadline ??
71
+ (process.env.DIFFEX_TASK_DEADLINE_MS === undefined ? undefined : Number(process.env.DIFFEX_TASK_DEADLINE_MS)),
72
+ };
61
73
  }
62
74
  function assertNoReservedSubagentTools(customTools) {
63
75
  for (const tool of customTools ?? []) {
@@ -66,10 +78,10 @@ function assertNoReservedSubagentTools(customTools) {
66
78
  }
67
79
  }
68
80
  }
69
- function createRootSubagentController(maxConcurrent, agentDir, telemetryContext, parentSessionManager) {
81
+ function createRootSubagentController(options, agentDir, telemetryContext, parentSessionManager) {
70
82
  const sessionManagerFactory = new RootLocalEphemeralSubagentSessionManagerFactory();
71
83
  return new SubagentController({
72
- maxConcurrent,
84
+ ...options,
73
85
  telemetryContext,
74
86
  restoreRoster: (controller, parent) => restoreRootSubagents(controller, parent, agentDir, parentSessionManager),
75
87
  captureSession: (parent, childName, origin) => {
@@ -239,7 +251,7 @@ export async function createInternalAgentSession(options = {}, internalCustomToo
239
251
  const sessionManager = options.sessionManager ?? SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));
240
252
  const subagentController = subagentOptions === false
241
253
  ? undefined
242
- : createRootSubagentController(subagentOptions.maxConcurrent, agentDir, telemetryContext, sessionManager);
254
+ : createRootSubagentController(subagentOptions, agentDir, telemetryContext, sessionManager);
243
255
  const customTools = [
244
256
  ...(options.customTools ?? []),
245
257
  ...(subagentController?.createToolDefinitions() ?? []),
@@ -2,14 +2,14 @@ import { type TelemetryContext } from "@diffexai/diffex-agent-core";
2
2
  import type { AgentSession } from "../agent-session.ts";
3
3
  import type { ToolDefinition } from "../extensions/types.ts";
4
4
  import type { ChildSnapshotV1 } from "./persistence.ts";
5
- import type { CapturedSubagentSession, CaptureSubagentSession, CreateSubagentSession, SubagentActionResult, SubagentBatchResult, SubagentBatchWaitOptions, SubagentControllerSnapshot, SubagentDeliveryBatch, SubagentLifecycleListener, SubagentPermit, SubagentQueryOptions, SubagentQueryResult, SubagentSpawnOrigin, SubagentSummary, SubagentWaitOptions, SubagentWaitResult } from "./types.ts";
5
+ import type { CapturedSubagentSession, CaptureSubagentSession, CreateSubagentSession, SubagentActionResult, SubagentBatchResult, SubagentBatchWaitOptions, SubagentControllerSnapshot, SubagentDeliveryBatch, SubagentLifecycleListener, SubagentPermit, SubagentQueryOptions, SubagentQueryResult, SubagentSpawnOrigin, SubagentSummary, SubagentSupervisionOptions, SubagentWaitOptions, SubagentWaitResult } from "./types.ts";
6
6
  export interface PreparedSubagentWake extends CapturedSubagentSession {
7
7
  readonly warning?: string;
8
8
  }
9
9
  export type PrepareSubagentWake = () => Promise<PreparedSubagentWake>;
10
10
  export declare class SubagentController {
11
11
  #private;
12
- constructor(options: {
12
+ constructor(options: SubagentSupervisionOptions & {
13
13
  maxConcurrent: number;
14
14
  captureSession?: CaptureSubagentSession;
15
15
  telemetryContext?: TelemetryContext;
@@ -23,8 +23,9 @@ export declare class SubagentController {
23
23
  restore(snapshot: ChildSnapshotV1, prepareWake: PrepareSubagentWake, isolationError?: string): void;
24
24
  restoreIsolated(name: string, error: string, prepareWake: PrepareSubagentWake): void;
25
25
  spawn(name: string, task: string, createSession: CreateSubagentSession, origin?: SubagentSpawnOrigin, telemetryContext?: TelemetryContext, captured?: CapturedSubagentSession): void;
26
- message(name: string, message: string, wake?: boolean, coordinated?: boolean): Promise<SubagentActionResult>;
26
+ message(name: string, message: string, wake?: boolean, coordinated?: boolean, delivery?: "steer" | "follow_up"): Promise<SubagentActionResult>;
27
27
  interrupt(name: string): Promise<SubagentActionResult>;
28
+ renew(name: string, reason: string): Promise<SubagentActionResult>;
28
29
  attachSession(name: string, session: AgentSession): void;
29
30
  trackRun(name: string, run: Promise<void>): void;
30
31
  markRunning(name: string): void;