π File detail
tasks/LocalShellTask/killShellTasks.ts
π― Use case
This module is a βLocalShellTaskβ task implementation β concrete work units the task runner schedules and monitors. On the API surface it exposes killTask and killShellTasksForAgent β mainly functions, hooks, or classes. It composes internal code from state, types, utils, and guards (relative imports). What the file header says: Pure (non-React) kill helpers for LocalShellTask. Extracted so runAgent.ts can kill agent-scoped bash tasks without pulling React/Ink into its module graph (same rationale as guards.ts).
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
Pure (non-React) kill helpers for LocalShellTask. Extracted so runAgent.ts can kill agent-scoped bash tasks without pulling React/Ink into its module graph (same rationale as guards.ts).
π€ Exports (heuristic)
killTaskkillShellTasksForAgent
π₯οΈ Source preview
// Pure (non-React) kill helpers for LocalShellTask.
// Extracted so runAgent.ts can kill agent-scoped bash tasks without pulling
// React/Ink into its module graph (same rationale as guards.ts).
import type { AppState } from '../../state/AppState.js'
import type { AgentId } from '../../types/ids.js'
import { logForDebugging } from '../../utils/debug.js'
import { logError } from '../../utils/log.js'
import { dequeueAllMatching } from '../../utils/messageQueueManager.js'
import { evictTaskOutput } from '../../utils/task/diskOutput.js'
import { updateTaskState } from '../../utils/task/framework.js'
import { isLocalShellTask } from './guards.js'
type SetAppStateFn = (updater: (prev: AppState) => AppState) => void
export function killTask(taskId: string, setAppState: SetAppStateFn): void {
updateTaskState(taskId, setAppState, task => {
if (task.status !== 'running' || !isLocalShellTask(task)) {
return task
}
try {
logForDebugging(`LocalShellTask ${taskId} kill requested`)
task.shellCommand?.kill()
task.shellCommand?.cleanup()
} catch (error) {
logError(error)
}
task.unregisterCleanup?.()
if (task.cleanupTimeoutId) {
clearTimeout(task.cleanupTimeoutId)
}
return {
...task,
status: 'killed',
notified: true,
shellCommand: null,
unregisterCleanup: undefined,
cleanupTimeoutId: undefined,
endTime: Date.now(),
}
})
void evictTaskOutput(taskId)
}
/**
* Kill all running bash tasks spawned by a given agent.
* Called from runAgent.ts finally block so background processes don't outlive
* the agent that started them (prevents 10-day fake-logs.sh zombies).
*/
export function killShellTasksForAgent(
agentId: AgentId,
getAppState: () => AppState,
setAppState: SetAppStateFn,
): void {
const tasks = getAppState().tasks ?? {}
for (const [taskId, task] of Object.entries(tasks)) {
if (
isLocalShellTask(task) &&
task.agentId === agentId &&
task.status === 'running'
) {
logForDebugging(
`killShellTasksForAgent: killing orphaned shell task ${taskId} (agent ${agentId} exiting)`,
)
killTask(taskId, setAppState)
}
}
// Purge any queued notifications addressed to this agent β its query loop
// has exited and won't drain them. killTask fires 'killed' notifications
// asynchronously; drop the ones already queued and any that land later sit
// harmlessly (no consumer matches a dead agentId).
dequeueAllMatching(cmd => cmd.agentId === agentId)
}