@opencow-ai/opencow-agent-sdk 0.4.18 → 0.4.19

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.
package/dist/cli.mjs CHANGED
@@ -94620,7 +94620,7 @@ function printStartupScreen() {
94620
94620
  const sLen = ` ● ${sL} Ready — type /help to begin`.length;
94621
94621
  out.push(boxRow(sRow, W2, sLen));
94622
94622
  out.push(`${rgb(...BORDER)}╚${"═".repeat(W2 - 2)}╝${RESET}`);
94623
- out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.18"}${RESET}`);
94623
+ out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.19"}${RESET}`);
94624
94624
  out.push("");
94625
94625
  process.stdout.write(out.join(`
94626
94626
  `) + `
@@ -241530,6 +241530,9 @@ async function checkPermissionsAndCallTool(tool, toolUseID, input, toolUseContex
241530
241530
  logError(hookError);
241531
241531
  }
241532
241532
  }
241533
+ if (toolUseContext.abortController.signal.aborted) {
241534
+ throw new AbortError;
241535
+ }
241533
241536
  try {
241534
241537
  const result = await tool.call(callInput, {
241535
241538
  ...toolUseContext,
@@ -244610,7 +244613,7 @@ function getAnthropicEnvMetadata() {
244610
244613
  function getBuildAgeMinutes() {
244611
244614
  if (false)
244612
244615
  ;
244613
- const buildTime = new Date("2026-07-16T03:27:09.740Z").getTime();
244616
+ const buildTime = new Date("2026-07-20T09:45:03.714Z").getTime();
244614
244617
  if (isNaN(buildTime))
244615
244618
  return;
244616
244619
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -247203,18 +247206,74 @@ var init_registerFrontmatterHooks = __esm(() => {
247203
247206
  });
247204
247207
 
247205
247208
  // src/session/backgroundAbortRegistry.ts
247206
- function registerBackgroundAgentAbort(agentId, controller) {
247207
- runs.set(agentId, { controller, stopFired: false });
247209
+ function registerBackgroundAgentAbort(agentId, actions) {
247210
+ if (runs.has(agentId)) {
247211
+ throw new Error(`Background agent '${agentId}' is already registered`);
247212
+ }
247213
+ let resolveSettled = () => {};
247214
+ const settled = new Promise((resolve18) => {
247215
+ resolveSettled = resolve18;
247216
+ });
247217
+ runs.set(agentId, {
247218
+ ...actions,
247219
+ stopFired: false,
247220
+ stopPromise: undefined,
247221
+ settled,
247222
+ resolveSettled,
247223
+ shellsStopped: false
247224
+ });
247208
247225
  }
247209
247226
  function unregisterBackgroundAgentAbort(agentId) {
247227
+ const run = runs.get(agentId);
247228
+ if (!run)
247229
+ return;
247210
247230
  runs.delete(agentId);
247231
+ run.resolveSettled();
247211
247232
  }
247212
247233
  function abortBackgroundAgentById(agentId) {
247213
247234
  const run = runs.get(agentId);
247214
247235
  if (!run)
247215
- return false;
247216
- run.controller.abort();
247217
- return true;
247236
+ return Promise.resolve();
247237
+ if (!run.stopPromise) {
247238
+ run.stopPromise = (async () => {
247239
+ const errors4 = [];
247240
+ let abortFailed = false;
247241
+ try {
247242
+ run.abort();
247243
+ } catch (error41) {
247244
+ abortFailed = true;
247245
+ errors4.push(error41);
247246
+ }
247247
+ try {
247248
+ stopBackgroundAgentShells(run);
247249
+ } catch (error41) {
247250
+ errors4.push(error41);
247251
+ }
247252
+ if (abortFailed) {
247253
+ throw new AggregateError(errors4, `Failed to stop background agent '${agentId}'`);
247254
+ }
247255
+ await run.settled;
247256
+ if (errors4.length > 0) {
247257
+ throw new AggregateError(errors4, `Failed to stop background agent '${agentId}'`);
247258
+ }
247259
+ })();
247260
+ }
247261
+ return run.stopPromise;
247262
+ }
247263
+ function hasBackgroundAgent(agentId) {
247264
+ return runs.has(agentId);
247265
+ }
247266
+ function stopBackgroundAgentShellsById(agentId) {
247267
+ const run = runs.get(agentId);
247268
+ if (run) {
247269
+ stopBackgroundAgentShells(run);
247270
+ }
247271
+ }
247272
+ function stopBackgroundAgentShells(run) {
247273
+ if (run.shellsStopped)
247274
+ return;
247275
+ run.shellsStopped = true;
247276
+ run.stopShells();
247218
247277
  }
247219
247278
  function markSubagentStopFired(agentId) {
247220
247279
  const run = runs.get(agentId);
@@ -248069,6 +248128,23 @@ var init_formatting = __esm(() => {
248069
248128
  init_xml();
248070
248129
  });
248071
248130
 
248131
+ // src/capabilities/tools/AgentTool/agentLifecycleFinalizer.ts
248132
+ function createAgentLifecycleFinalizer(onCleanupError) {
248133
+ let finalization;
248134
+ return (cleanups) => {
248135
+ finalization ??= (async () => {
248136
+ for (const cleanup of cleanups) {
248137
+ try {
248138
+ await cleanup();
248139
+ } catch (error41) {
248140
+ onCleanupError(error41);
248141
+ }
248142
+ }
248143
+ })();
248144
+ return finalization;
248145
+ };
248146
+ }
248147
+
248072
248148
  // src/capabilities/tools/AgentTool/runAgent.ts
248073
248149
  import { randomUUID as randomUUID10 } from "crypto";
248074
248150
  async function initializeAgentMcpServers(agentDefinition, parentClients) {
@@ -248091,44 +248167,6 @@ async function initializeAgentMcpServers(agentDefinition, parentClients) {
248091
248167
  const agentClients = [];
248092
248168
  const newlyCreatedClients = [];
248093
248169
  const agentTools = [];
248094
- for (const spec of agentDefinition.mcpServers) {
248095
- let config2 = null;
248096
- let name;
248097
- let isNewlyCreated = false;
248098
- if (typeof spec === "string") {
248099
- name = spec;
248100
- config2 = getMcpConfigByName(spec);
248101
- if (!config2) {
248102
- logForDebugging2(`[Agent: ${agentDefinition.agentType}] MCP server not found: ${spec}`, { level: "warn" });
248103
- continue;
248104
- }
248105
- } else {
248106
- const entries = Object.entries(spec);
248107
- if (entries.length !== 1) {
248108
- logForDebugging2(`[Agent: ${agentDefinition.agentType}] Invalid MCP server spec: expected exactly one key`, { level: "warn" });
248109
- continue;
248110
- }
248111
- const [serverName, serverConfig] = entries[0];
248112
- name = serverName;
248113
- config2 = {
248114
- ...serverConfig,
248115
- scope: "dynamic"
248116
- };
248117
- isNewlyCreated = true;
248118
- }
248119
- const client = await connectToServer(name, config2);
248120
- agentClients.push(client);
248121
- if (isNewlyCreated) {
248122
- newlyCreatedClients.push(client);
248123
- }
248124
- if (client.type === "connected") {
248125
- const tools = await fetchToolsForClient(client);
248126
- agentTools.push(...tools);
248127
- logForDebugging2(`[Agent: ${agentDefinition.agentType}] Connected to MCP server '${name}' with ${tools.length} tools`);
248128
- } else {
248129
- logForDebugging2(`[Agent: ${agentDefinition.agentType}] Failed to connect to MCP server '${name}': ${client.type}`, { level: "warn" });
248130
- }
248131
- }
248132
248170
  const cleanup = async () => {
248133
248171
  for (const client of newlyCreatedClients) {
248134
248172
  if (client.type === "connected") {
@@ -248140,6 +248178,49 @@ async function initializeAgentMcpServers(agentDefinition, parentClients) {
248140
248178
  }
248141
248179
  }
248142
248180
  };
248181
+ try {
248182
+ for (const spec of agentDefinition.mcpServers) {
248183
+ let config2 = null;
248184
+ let name;
248185
+ let isNewlyCreated = false;
248186
+ if (typeof spec === "string") {
248187
+ name = spec;
248188
+ config2 = getMcpConfigByName(spec);
248189
+ if (!config2) {
248190
+ logForDebugging2(`[Agent: ${agentDefinition.agentType}] MCP server not found: ${spec}`, { level: "warn" });
248191
+ continue;
248192
+ }
248193
+ } else {
248194
+ const entries = Object.entries(spec);
248195
+ if (entries.length !== 1) {
248196
+ logForDebugging2(`[Agent: ${agentDefinition.agentType}] Invalid MCP server spec: expected exactly one key`, { level: "warn" });
248197
+ continue;
248198
+ }
248199
+ const [serverName, serverConfig] = entries[0];
248200
+ name = serverName;
248201
+ config2 = {
248202
+ ...serverConfig,
248203
+ scope: "dynamic"
248204
+ };
248205
+ isNewlyCreated = true;
248206
+ }
248207
+ const client = await connectToServer(name, config2);
248208
+ agentClients.push(client);
248209
+ if (isNewlyCreated) {
248210
+ newlyCreatedClients.push(client);
248211
+ }
248212
+ if (client.type === "connected") {
248213
+ const tools = await fetchToolsForClient(client);
248214
+ agentTools.push(...tools);
248215
+ logForDebugging2(`[Agent: ${agentDefinition.agentType}] Connected to MCP server '${name}' with ${tools.length} tools`);
248216
+ } else {
248217
+ logForDebugging2(`[Agent: ${agentDefinition.agentType}] Failed to connect to MCP server '${name}': ${client.type}`, { level: "warn" });
248218
+ }
248219
+ }
248220
+ } catch (error41) {
248221
+ await cleanup();
248222
+ throw error41;
248223
+ }
248143
248224
  return {
248144
248225
  clients: [...parentClients, ...agentClients],
248145
248226
  tools: agentTools,
@@ -248292,121 +248373,140 @@ async function* runAgent({
248292
248373
  const additionalWorkingDirectories = Array.from(appState.toolPermissionContext.additionalWorkingDirectories.keys());
248293
248374
  const agentSystemPrompt = override?.systemPrompt ? override.systemPrompt : asSystemPrompt(await getAgentSystemPrompt(agentDefinition, toolUseContext, resolvedAgentModel, additionalWorkingDirectories, resolvedTools));
248294
248375
  const agentAbortController = override?.abortController ? override.abortController : isAsync2 ? new AbortController : toolUseContext.abortController;
248376
+ const stopOwnedShells = () => {
248377
+ killShellTasksForAgent(agentId, toolUseContext.getAppState, rootSetAppState);
248378
+ };
248295
248379
  if (isAsync2) {
248296
- registerBackgroundAgentAbort(agentId, agentAbortController);
248380
+ registerBackgroundAgentAbort(agentId, {
248381
+ abort: () => {
248382
+ killAsyncAgent(agentId, rootSetAppState);
248383
+ if (!agentAbortController.signal.aborted) {
248384
+ agentAbortController.abort();
248385
+ }
248386
+ },
248387
+ stopShells: stopOwnedShells
248388
+ });
248297
248389
  }
248298
- const additionalContexts = [];
248299
- for await (const hookResult of executeSubagentStartHooks(agentId, agentDefinition.agentType, agentAbortController.signal, undefined, toolUseContext.toolUseId, isAsync2)) {
248300
- if (hookResult.additionalContexts && hookResult.additionalContexts.length > 0) {
248301
- additionalContexts.push(...hookResult.additionalContexts);
248390
+ let agentToolRuntimeContext;
248391
+ let mcpCleanup = async () => {};
248392
+ let lastAssistantForFallback = null;
248393
+ let agentRunError;
248394
+ const finalizeLifecycle = createAgentLifecycleFinalizer((error41) => {
248395
+ logForDebugging2(`Agent ${agentId} cleanup failed: ${error41}`, {
248396
+ level: "warn"
248397
+ });
248398
+ });
248399
+ try {
248400
+ const additionalContexts = [];
248401
+ for await (const hookResult of executeSubagentStartHooks(agentId, agentDefinition.agentType, agentAbortController.signal, undefined, toolUseContext.toolUseId, isAsync2)) {
248402
+ if (hookResult.additionalContexts && hookResult.additionalContexts.length > 0) {
248403
+ additionalContexts.push(...hookResult.additionalContexts);
248404
+ }
248302
248405
  }
248303
- }
248304
- if (additionalContexts.length > 0) {
248305
- const contextMessage = createAttachmentMessage({
248306
- type: "hook_additional_context",
248307
- content: additionalContexts,
248308
- hookName: "SubagentStart",
248309
- toolUseID: randomUUID10(),
248310
- hookEvent: "SubagentStart"
248311
- });
248312
- initialMessages.push(contextMessage);
248313
- }
248314
- const hooksAllowedForThisAgent = !isRestrictedToPluginOnly("hooks") || isSourceAdminTrusted(agentDefinition.source);
248315
- if (agentDefinition.hooks && hooksAllowedForThisAgent) {
248316
- registerFrontmatterHooks(rootSetAppState, agentId, agentDefinition.hooks, `agent '${agentDefinition.agentType}'`, true);
248317
- }
248318
- const skillsToPreload = agentDefinition.skills ?? [];
248319
- if (skillsToPreload.length > 0) {
248320
- const allSkills = await getSkillToolCommands(getProjectRoot());
248321
- const validSkills = [];
248322
- for (const skillName of skillsToPreload) {
248323
- const resolvedName = resolveSkillName(skillName, allSkills, agentDefinition);
248324
- if (!resolvedName) {
248325
- logForDebugging2(`[Agent: ${agentDefinition.agentType}] Warning: Skill '${skillName}' specified in frontmatter was not found`, { level: "warn" });
248326
- continue;
248406
+ if (additionalContexts.length > 0) {
248407
+ const contextMessage = createAttachmentMessage({
248408
+ type: "hook_additional_context",
248409
+ content: additionalContexts,
248410
+ hookName: "SubagentStart",
248411
+ toolUseID: randomUUID10(),
248412
+ hookEvent: "SubagentStart"
248413
+ });
248414
+ initialMessages.push(contextMessage);
248415
+ }
248416
+ const hooksAllowedForThisAgent = !isRestrictedToPluginOnly("hooks") || isSourceAdminTrusted(agentDefinition.source);
248417
+ if (agentDefinition.hooks && hooksAllowedForThisAgent) {
248418
+ registerFrontmatterHooks(rootSetAppState, agentId, agentDefinition.hooks, `agent '${agentDefinition.agentType}'`, true);
248419
+ }
248420
+ const skillsToPreload = agentDefinition.skills ?? [];
248421
+ if (skillsToPreload.length > 0) {
248422
+ const allSkills = await getSkillToolCommands(getProjectRoot());
248423
+ const validSkills = [];
248424
+ for (const skillName of skillsToPreload) {
248425
+ const resolvedName = resolveSkillName(skillName, allSkills, agentDefinition);
248426
+ if (!resolvedName) {
248427
+ logForDebugging2(`[Agent: ${agentDefinition.agentType}] Warning: Skill '${skillName}' specified in frontmatter was not found`, { level: "warn" });
248428
+ continue;
248429
+ }
248430
+ const skill = getCommand2(resolvedName, allSkills);
248431
+ if (skill.type !== "prompt") {
248432
+ logForDebugging2(`[Agent: ${agentDefinition.agentType}] Warning: Skill '${skillName}' is not a prompt-based skill`, { level: "warn" });
248433
+ continue;
248434
+ }
248435
+ validSkills.push({ skillName, skill });
248327
248436
  }
248328
- const skill = getCommand2(resolvedName, allSkills);
248329
- if (skill.type !== "prompt") {
248330
- logForDebugging2(`[Agent: ${agentDefinition.agentType}] Warning: Skill '${skillName}' is not a prompt-based skill`, { level: "warn" });
248331
- continue;
248437
+ const loaded = await Promise.all(validSkills.map(async ({ skillName, skill }) => ({
248438
+ skillName,
248439
+ skill,
248440
+ content: await skill.getPromptForCommand("", toolUseContext)
248441
+ })));
248442
+ for (const { skillName, skill, content } of loaded) {
248443
+ logForDebugging2(`[Agent: ${agentDefinition.agentType}] Preloaded skill '${skillName}'`);
248444
+ const metadata = formatSkillLoadingMetadata(skillName, skill.progressMessage);
248445
+ initialMessages.push(createUserMessage({
248446
+ content: [{ type: "text", text: metadata }, ...content],
248447
+ isMeta: true
248448
+ }));
248332
248449
  }
248333
- validSkills.push({ skillName, skill });
248334
- }
248335
- const loaded = await Promise.all(validSkills.map(async ({ skillName, skill }) => ({
248336
- skillName,
248337
- skill,
248338
- content: await skill.getPromptForCommand("", toolUseContext)
248339
- })));
248340
- for (const { skillName, skill, content } of loaded) {
248341
- logForDebugging2(`[Agent: ${agentDefinition.agentType}] Preloaded skill '${skillName}'`);
248342
- const metadata = formatSkillLoadingMetadata(skillName, skill.progressMessage);
248343
- initialMessages.push(createUserMessage({
248344
- content: [{ type: "text", text: metadata }, ...content],
248345
- isMeta: true
248346
- }));
248347
248450
  }
248348
- }
248349
- const {
248350
- clients: mergedMcpClients,
248351
- tools: agentMcpTools,
248352
- cleanup: mcpCleanup
248353
- } = await initializeAgentMcpServers(agentDefinition, toolUseContext.options.mcpClients);
248354
- const allTools = agentMcpTools.length > 0 ? uniqBy_default([...resolvedTools, ...agentMcpTools], "name") : resolvedTools;
248355
- const agentOptions = {
248356
- isNonInteractiveSession: useExactTools ? toolUseContext.options.isNonInteractiveSession : isAsync2 ? true : toolUseContext.options.isNonInteractiveSession ?? false,
248357
- appendSystemPrompt: toolUseContext.options.appendSystemPrompt,
248358
- tools: allTools,
248359
- commands: [],
248360
- debug: toolUseContext.options.debug,
248361
- verbose: toolUseContext.options.verbose,
248362
- mainLoopModel: effectiveModel,
248363
- providerOverride: providerOverride ?? undefined,
248364
- thinkingConfig: toolUseContext.options.thinkingConfig,
248365
- mcpClients: mergedMcpClients,
248366
- mcpResources: toolUseContext.options.mcpResources,
248367
- agentDefinitions: toolUseContext.options.agentDefinitions,
248368
- subagentDisallowedTools: toolUseContext.options.subagentDisallowedTools,
248369
- ...useExactTools && { querySource }
248370
- };
248371
- const agentToolRuntimeContext = createSubagentContext(toolUseContext, {
248372
- options: agentOptions,
248373
- agentId,
248374
- agentType: agentDefinition.agentType,
248375
- messages: initialMessages,
248376
- readFileState: agentReadFileState,
248377
- abortController: agentAbortController,
248378
- getAppState: agentGetAppState,
248379
- shareSetAppState: !isAsync2,
248380
- shareSetResponseLength: true,
248381
- criticalSystemReminder_EXPERIMENTAL: agentDefinition.criticalSystemReminder_EXPERIMENTAL,
248382
- contentReplacementState
248383
- });
248384
- if (preserveToolUseResults) {
248385
- agentToolRuntimeContext.preserveToolUseResults = true;
248386
- }
248387
- if (onCacheSafeParams) {
248388
- onCacheSafeParams({
248389
- systemPrompt: agentSystemPrompt,
248390
- userContext: resolvedUserContext,
248391
- systemContext: resolvedSystemContext,
248392
- toolUseContext: agentToolRuntimeContext,
248393
- forkContextMessages: initialMessages
248394
- });
248395
- }
248396
- recordSidechainTranscript(initialMessages, agentId).catch((_err) => logForDebugging2(`Failed to record sidechain transcript: ${_err}`));
248397
- writeAgentMetadata(agentId, {
248398
- agentType: agentDefinition.agentType,
248399
- ...worktreePath && { worktreePath },
248400
- ...description && { description }
248401
- }).catch((_err) => logForDebugging2(`Failed to write agent metadata: ${_err}`));
248402
- let lastRecordedUuid = initialMessages.at(-1)?.uuid ?? null;
248403
- let partialBaseMessage = null;
248404
- const partialTextByIndex = new Map;
248405
- const PARTIAL_PROGRESS_HOOK_INTERVAL_MS = 100;
248406
- let lastPartialProgressHookAt = 0;
248407
- let lastAssistantForFallback = null;
248408
- let agentRunError;
248409
- try {
248451
+ const mcp = await initializeAgentMcpServers(agentDefinition, toolUseContext.options.mcpClients);
248452
+ const {
248453
+ clients: mergedMcpClients,
248454
+ tools: agentMcpTools
248455
+ } = mcp;
248456
+ mcpCleanup = mcp.cleanup;
248457
+ const allTools = agentMcpTools.length > 0 ? uniqBy_default([...resolvedTools, ...agentMcpTools], "name") : resolvedTools;
248458
+ const agentOptions = {
248459
+ isNonInteractiveSession: useExactTools ? toolUseContext.options.isNonInteractiveSession : isAsync2 ? true : toolUseContext.options.isNonInteractiveSession ?? false,
248460
+ appendSystemPrompt: toolUseContext.options.appendSystemPrompt,
248461
+ tools: allTools,
248462
+ commands: [],
248463
+ debug: toolUseContext.options.debug,
248464
+ verbose: toolUseContext.options.verbose,
248465
+ mainLoopModel: effectiveModel,
248466
+ providerOverride: providerOverride ?? undefined,
248467
+ thinkingConfig: toolUseContext.options.thinkingConfig,
248468
+ mcpClients: mergedMcpClients,
248469
+ mcpResources: toolUseContext.options.mcpResources,
248470
+ agentDefinitions: toolUseContext.options.agentDefinitions,
248471
+ subagentDisallowedTools: toolUseContext.options.subagentDisallowedTools,
248472
+ ...useExactTools && { querySource }
248473
+ };
248474
+ agentToolRuntimeContext = createSubagentContext(toolUseContext, {
248475
+ options: agentOptions,
248476
+ agentId,
248477
+ agentType: agentDefinition.agentType,
248478
+ messages: initialMessages,
248479
+ readFileState: agentReadFileState,
248480
+ abortController: agentAbortController,
248481
+ getAppState: agentGetAppState,
248482
+ shareSetAppState: !isAsync2,
248483
+ shareSetResponseLength: true,
248484
+ criticalSystemReminder_EXPERIMENTAL: agentDefinition.criticalSystemReminder_EXPERIMENTAL,
248485
+ contentReplacementState
248486
+ });
248487
+ if (preserveToolUseResults) {
248488
+ agentToolRuntimeContext.preserveToolUseResults = true;
248489
+ }
248490
+ if (onCacheSafeParams) {
248491
+ onCacheSafeParams({
248492
+ systemPrompt: agentSystemPrompt,
248493
+ userContext: resolvedUserContext,
248494
+ systemContext: resolvedSystemContext,
248495
+ toolUseContext: agentToolRuntimeContext,
248496
+ forkContextMessages: initialMessages
248497
+ });
248498
+ }
248499
+ recordSidechainTranscript(initialMessages, agentId).catch((_err) => logForDebugging2(`Failed to record sidechain transcript: ${_err}`));
248500
+ writeAgentMetadata(agentId, {
248501
+ agentType: agentDefinition.agentType,
248502
+ ...worktreePath && { worktreePath },
248503
+ ...description && { description }
248504
+ }).catch((_err) => logForDebugging2(`Failed to write agent metadata: ${_err}`));
248505
+ let lastRecordedUuid = initialMessages.at(-1)?.uuid ?? null;
248506
+ let partialBaseMessage = null;
248507
+ const partialTextByIndex = new Map;
248508
+ const PARTIAL_PROGRESS_HOOK_INTERVAL_MS = 100;
248509
+ let lastPartialProgressHookAt = 0;
248410
248510
  for await (const message of query({
248411
248511
  messages: initialMessages,
248412
248512
  systemPrompt: agentSystemPrompt,
@@ -248496,43 +248596,65 @@ async function* runAgent({
248496
248596
  agentRunError = err2;
248497
248597
  throw err2;
248498
248598
  } finally {
248499
- const naturalStopAlreadyFired = isAsync2 && hasSubagentStopFired(agentId);
248500
- if (isAsync2) {
248501
- unregisterBackgroundAgentAbort(agentId);
248502
- }
248503
- if (isAsync2 && agentRunError !== undefined) {
248504
- logForDebugging2(`Background agent ${agentId} crashed: ${errorMessage(agentRunError)}
248599
+ await finalizeLifecycle([
248600
+ async () => {
248601
+ const naturalStopAlreadyFired = isAsync2 && hasSubagentStopFired(agentId);
248602
+ if (isAsync2 && agentRunError !== undefined) {
248603
+ logForDebugging2(`Background agent ${agentId} crashed: ${errorMessage(agentRunError)}
248505
248604
  ${agentRunError instanceof Error ? agentRunError.stack ?? "" : ""}`, { level: "error" });
248506
- }
248507
- if (isAsync2 && !naturalStopAlreadyFired) {
248508
- try {
248509
- const fallbackAppState = agentToolRuntimeContext.getAppState();
248510
- const terminal = agentAbortController.signal.aborted ? { status: "stopped" } : agentRunError !== undefined ? { status: "failed", errorMessage: errorMessage(agentRunError) } : { status: "completed" };
248511
- const fallbackStop = executeStopHooks(fallbackAppState.toolPermissionContext.mode, undefined, undefined, false, agentId, agentToolRuntimeContext, lastAssistantForFallback ? [lastAssistantForFallback] : undefined, agentDefinition.agentType, undefined, terminal);
248512
- for await (const evt of fallbackStop) {}
248513
- } catch (err2) {
248514
- logForDebugging2(`Fallback SubagentStop failed for ${agentId}: ${err2}`, {
248515
- level: "warn"
248605
+ }
248606
+ if (isAsync2 && !naturalStopAlreadyFired) {
248607
+ try {
248608
+ const terminalContext = agentToolRuntimeContext ?? toolUseContext;
248609
+ const fallbackAppState = terminalContext.getAppState();
248610
+ const terminal = agentAbortController.signal.aborted ? { status: "stopped" } : agentRunError !== undefined ? { status: "failed", errorMessage: errorMessage(agentRunError) } : { status: "completed" };
248611
+ const fallbackStop = executeStopHooks(fallbackAppState.toolPermissionContext.mode, undefined, undefined, false, agentId, terminalContext, lastAssistantForFallback ? [lastAssistantForFallback] : undefined, agentDefinition.agentType, undefined, terminal);
248612
+ for await (const evt of fallbackStop) {}
248613
+ } catch (err2) {
248614
+ logForDebugging2(`Fallback SubagentStop failed for ${agentId}: ${err2}`, {
248615
+ level: "warn"
248616
+ });
248617
+ }
248618
+ }
248619
+ },
248620
+ mcpCleanup,
248621
+ () => {
248622
+ if (agentDefinition.hooks) {
248623
+ clearSessionHooks(rootSetAppState, agentId);
248624
+ }
248625
+ },
248626
+ () => {
248627
+ if (false) {}
248628
+ },
248629
+ () => agentToolRuntimeContext?.readFileState.clear(),
248630
+ () => {
248631
+ initialMessages.length = 0;
248632
+ },
248633
+ () => unregisterAgent(agentId),
248634
+ () => clearAgentTranscriptSubdir(agentId),
248635
+ () => {
248636
+ rootSetAppState((prev) => {
248637
+ if (!(agentId in prev.todos))
248638
+ return prev;
248639
+ const { [agentId]: _removed, ...todos } = prev.todos;
248640
+ return { ...prev, todos };
248516
248641
  });
248642
+ },
248643
+ () => {
248644
+ if (isAsync2) {
248645
+ stopBackgroundAgentShellsById(agentId);
248646
+ } else {
248647
+ stopOwnedShells();
248648
+ }
248649
+ },
248650
+ () => {
248651
+ if (false) {}
248652
+ },
248653
+ () => {
248654
+ if (isAsync2)
248655
+ unregisterBackgroundAgentAbort(agentId);
248517
248656
  }
248518
- }
248519
- await mcpCleanup();
248520
- if (agentDefinition.hooks) {
248521
- clearSessionHooks(rootSetAppState, agentId);
248522
- }
248523
- if (false) {}
248524
- agentToolRuntimeContext.readFileState.clear();
248525
- initialMessages.length = 0;
248526
- unregisterAgent(agentId);
248527
- clearAgentTranscriptSubdir(agentId);
248528
- rootSetAppState((prev) => {
248529
- if (!(agentId in prev.todos))
248530
- return prev;
248531
- const { [agentId]: _removed, ...todos } = prev.todos;
248532
- return { ...prev, todos };
248533
- });
248534
- killShellTasksForAgent(agentId, toolUseContext.getAppState, rootSetAppState);
248535
- if (false) {}
248657
+ ]);
248536
248658
  }
248537
248659
  }
248538
248660
  function filterIncompleteToolCalls(messages) {
@@ -248604,6 +248726,7 @@ var init_runAgent = __esm(() => {
248604
248726
  init_config7();
248605
248727
  init_permissions2();
248606
248728
  init_killShellTasks();
248729
+ init_LocalAgentTask();
248607
248730
  init_attachments();
248608
248731
  init_errors2();
248609
248732
  init_file();
@@ -275179,6 +275302,67 @@ var init_LocalAgentTask = __esm(() => {
275179
275302
  backgroundSignalResolvers = new Map;
275180
275303
  });
275181
275304
 
275305
+ // src/session/backgroundTaskScope.ts
275306
+ class BackgroundTaskScope {
275307
+ tasks = new Set;
275308
+ stopPromise;
275309
+ stopped = false;
275310
+ register(task) {
275311
+ let registered = true;
275312
+ const unregister = () => {
275313
+ if (!registered)
275314
+ return;
275315
+ registered = false;
275316
+ this.tasks.delete(task);
275317
+ };
275318
+ this.tasks.add(task);
275319
+ task.settled.then(unregister, unregister);
275320
+ if (this.stopped) {
275321
+ this.stopTask(task).finally(unregister).catch(() => {});
275322
+ }
275323
+ return unregister;
275324
+ }
275325
+ stop() {
275326
+ if (!this.stopPromise) {
275327
+ this.stopPromise = this.drain().finally(() => {
275328
+ this.stopped = true;
275329
+ });
275330
+ }
275331
+ return this.stopPromise;
275332
+ }
275333
+ async drain() {
275334
+ const failures = [];
275335
+ while (this.tasks.size > 0) {
275336
+ const tasks = [...this.tasks];
275337
+ const results = await Promise.allSettled(tasks.map((task) => this.stopTask(task)));
275338
+ for (const result of results) {
275339
+ if (result.status === "rejected")
275340
+ failures.push(result.reason);
275341
+ }
275342
+ for (const task of tasks) {
275343
+ this.tasks.delete(task);
275344
+ }
275345
+ }
275346
+ if (failures.length > 0) {
275347
+ throw new AggregateError(failures, "BackgroundTaskScope cleanup failed");
275348
+ }
275349
+ }
275350
+ async stopTask(task) {
275351
+ try {
275352
+ await task.stop();
275353
+ } finally {
275354
+ await task.settled;
275355
+ }
275356
+ }
275357
+ }
275358
+ function getBackgroundTaskScope(setAppState) {
275359
+ return scopesBySetAppState.get(setAppState);
275360
+ }
275361
+ var scopesBySetAppState;
275362
+ var init_backgroundTaskScope = __esm(() => {
275363
+ scopesBySetAppState = new WeakMap;
275364
+ });
275365
+
275182
275366
  // src/tasks/LocalShellTask/LocalShellTask.ts
275183
275367
  function looksLikePrompt(tail) {
275184
275368
  const lastLine = tail.trimEnd().split(`
@@ -275307,26 +275491,9 @@ async function spawnShellTask(input, context4) {
275307
275491
  taskOutput
275308
275492
  } = shellCommand;
275309
275493
  const taskId = taskOutput.taskId;
275310
- const unregisterCleanup = registerCleanup(async () => {
275311
- killTask(taskId, setAppState);
275312
- });
275313
- const taskState = {
275314
- ...createTaskStateBase(taskId, "local_bash", description, toolUseId),
275315
- type: "local_bash",
275316
- status: "running",
275317
- command,
275318
- completionStatusSentInAttachment: false,
275319
- shellCommand,
275320
- unregisterCleanup,
275321
- lastReportedTotalLines: 0,
275322
- isBackgrounded: true,
275323
- agentId,
275324
- kind
275325
- };
275326
- registerTask(taskState, setAppState);
275327
- shellCommand.background(taskId);
275328
- const cancelStallWatchdog = startStallWatchdog(taskId, description, kind, toolUseId, agentId);
275329
- shellCommand.result.then(async (result) => {
275494
+ let unregisterCleanup = () => {};
275495
+ let cancelStallWatchdog = () => {};
275496
+ const settled = shellCommand.result.then(async (result) => {
275330
275497
  cancelStallWatchdog();
275331
275498
  await flushAndCleanup(shellCommand);
275332
275499
  let wasKilled = false;
@@ -275349,12 +275516,43 @@ async function spawnShellTask(input, context4) {
275349
275516
  });
275350
275517
  enqueueShellNotification(taskId, description, wasKilled ? "killed" : result.code === 0 ? "completed" : "failed", result.code, setAppState, toolUseId, kind, agentId);
275351
275518
  evictTaskOutput(taskId);
275519
+ }).catch((error41) => {
275520
+ cancelStallWatchdog();
275521
+ logError(error41);
275522
+ }).finally(() => {
275523
+ unregisterCleanup();
275352
275524
  });
275525
+ const stopAndSettle = async () => {
275526
+ killTask(taskId, setAppState);
275527
+ await settled;
275528
+ };
275529
+ const cleanupRegistration = () => {
275530
+ unregisterCleanup();
275531
+ };
275532
+ const taskState = {
275533
+ ...createTaskStateBase(taskId, "local_bash", description, toolUseId),
275534
+ type: "local_bash",
275535
+ status: "running",
275536
+ command,
275537
+ completionStatusSentInAttachment: false,
275538
+ shellCommand,
275539
+ unregisterCleanup: cleanupRegistration,
275540
+ lastReportedTotalLines: 0,
275541
+ isBackgrounded: true,
275542
+ agentId,
275543
+ kind
275544
+ };
275545
+ registerTask(taskState, setAppState);
275546
+ const owner = getBackgroundTaskScope(setAppState);
275547
+ unregisterCleanup = owner ? owner.register({
275548
+ stop: stopAndSettle,
275549
+ settled
275550
+ }) : registerCleanup(stopAndSettle);
275551
+ shellCommand.background(taskId);
275552
+ cancelStallWatchdog = startStallWatchdog(taskId, description, kind, toolUseId, agentId);
275353
275553
  return {
275354
275554
  taskId,
275355
- cleanup: () => {
275356
- unregisterCleanup();
275357
- }
275555
+ cleanup: cleanupRegistration
275358
275556
  };
275359
275557
  }
275360
275558
  function registerForeground(input, setAppState, toolUseId) {
@@ -275578,6 +275776,7 @@ var init_LocalShellTask = __esm(() => {
275578
275776
  init_LocalAgentTask();
275579
275777
  init_killShellTasks();
275580
275778
  init_fsOperations();
275779
+ init_backgroundTaskScope();
275581
275780
  PROMPT_PATTERNS = [
275582
275781
  /\(y\/n\)/i,
275583
275782
  /\[y\/n\]/i,
@@ -382400,7 +382599,8 @@ async function stopTask(taskId, context4) {
382400
382599
  const appState = getAppState();
382401
382600
  const task = appState.tasks?.[taskId];
382402
382601
  if (!task) {
382403
- if (abortBackgroundAgentById(taskId)) {
382602
+ if (hasBackgroundAgent(taskId)) {
382603
+ await abortBackgroundAgentById(taskId);
382404
382604
  return { taskId, taskType: "local_agent", command: undefined };
382405
382605
  }
382406
382606
  throw new StopTaskError(`No task found with ID: ${taskId}`, "not_found");
@@ -382412,7 +382612,11 @@ async function stopTask(taskId, context4) {
382412
382612
  if (!taskImpl) {
382413
382613
  throw new StopTaskError(`Unsupported task type: ${task.type}`, "unsupported_type");
382414
382614
  }
382415
- await taskImpl.kill(taskId, setAppState);
382615
+ if (task.type === "local_agent" && hasBackgroundAgent(taskId)) {
382616
+ await abortBackgroundAgentById(taskId);
382617
+ } else {
382618
+ await taskImpl.kill(taskId, setAppState);
382619
+ }
382416
382620
  if (isLocalShellTask(task)) {
382417
382621
  let suppressed = false;
382418
382622
  setAppState((prev) => {
@@ -382470,6 +382674,7 @@ var init_TaskStopTool = __esm(() => {
382470
382674
  init_stopTask();
382471
382675
  init_slowOperations();
382472
382676
  init_toolUIRegistry();
382677
+ init_backgroundAbortRegistry();
382473
382678
  ({
382474
382679
  renderToolResultMessage: renderToolResultMessage15,
382475
382680
  renderToolUseMessage: renderToolUseMessage15
@@ -382515,6 +382720,9 @@ var init_TaskStopTool = __esm(() => {
382515
382720
  const appState = getAppState();
382516
382721
  const task = appState.tasks?.[id];
382517
382722
  if (!task) {
382723
+ if (hasBackgroundAgent(id)) {
382724
+ return { result: true };
382725
+ }
382518
382726
  return {
382519
382727
  result: false,
382520
382728
  message: `No task found with ID: ${id}`,
@@ -491619,7 +491827,7 @@ function buildPrimarySection() {
491619
491827
  }, undefined, false, undefined, this);
491620
491828
  return [{
491621
491829
  label: "Version",
491622
- value: "0.4.18"
491830
+ value: "0.4.19"
491623
491831
  }, {
491624
491832
  label: "Session name",
491625
491833
  value: nameValue
@@ -539762,7 +539970,7 @@ var init_bridge_kick = __esm(() => {
539762
539970
  var call58 = async () => {
539763
539971
  return {
539764
539972
  type: "text",
539765
- value: `${"99.0.0"} (built ${"2026-07-16T03:27:09.740Z"})`
539973
+ value: `${"99.0.0"} (built ${"2026-07-20T09:45:03.714Z"})`
539766
539974
  };
539767
539975
  }, version2, version_default;
539768
539976
  var init_version = __esm(() => {
@@ -557529,7 +557737,7 @@ function WelcomeV2() {
557529
557737
  dimColor: true,
557530
557738
  children: [
557531
557739
  "v",
557532
- "0.4.18",
557740
+ "0.4.19",
557533
557741
  " "
557534
557742
  ]
557535
557743
  }, undefined, true, undefined, this)
@@ -557729,7 +557937,7 @@ function WelcomeV2() {
557729
557937
  dimColor: true,
557730
557938
  children: [
557731
557939
  "v",
557732
- "0.4.18",
557940
+ "0.4.19",
557733
557941
  " "
557734
557942
  ]
557735
557943
  }, undefined, true, undefined, this)
@@ -557955,7 +558163,7 @@ function AppleTerminalWelcomeV2(t0) {
557955
558163
  dimColor: true,
557956
558164
  children: [
557957
558165
  "v",
557958
- "0.4.18",
558166
+ "0.4.19",
557959
558167
  " "
557960
558168
  ]
557961
558169
  }, undefined, true, undefined, this);
@@ -558209,7 +558417,7 @@ function AppleTerminalWelcomeV2(t0) {
558209
558417
  dimColor: true,
558210
558418
  children: [
558211
558419
  "v",
558212
- "0.4.18",
558420
+ "0.4.19",
558213
558421
  " "
558214
558422
  ]
558215
558423
  }, undefined, true, undefined, this);
@@ -579191,7 +579399,7 @@ Usage: claude --remote "your task description"`, () => gracefulShutdown(1));
579191
579399
  pendingHookMessages
579192
579400
  }, renderAndRun);
579193
579401
  }
579194
- }).version("0.4.18 (OpenCow)", "-v, --version", "Output the version number");
579402
+ }).version("0.4.19 (OpenCow)", "-v, --version", "Output the version number");
579195
579403
  program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
579196
579404
  program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
579197
579405
  if (canUserConfigureAdvisor()) {
@@ -579836,7 +580044,7 @@ if (false) {}
579836
580044
  async function main2() {
579837
580045
  const args = process.argv.slice(2);
579838
580046
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-v" || args[0] === "-V")) {
579839
- console.log(`${"0.4.18"} (OpenCow)`);
580047
+ console.log(`${"0.4.19"} (OpenCow)`);
579840
580048
  return;
579841
580049
  }
579842
580050
  if (args.includes("--provider")) {
@@ -579954,4 +580162,4 @@ async function main2() {
579954
580162
  }
579955
580163
  main2();
579956
580164
 
579957
- //# debugId=B28B6A1F66E05E3B64756E2164756E21
580165
+ //# debugId=194C54719A60782464756E2164756E21