@agimon-ai/doompi-team 0.0.1-alpha.2 → 0.0.1-alpha.21
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/README.md +33 -17
- package/dist/adapters/asyncJobTracker.cjs +2 -0
- package/dist/adapters/asyncJobTracker.cjs.map +1 -0
- package/dist/adapters/asyncJobTracker.mjs +2 -0
- package/dist/adapters/asyncJobTracker.mjs.map +1 -0
- package/dist/adapters/atomicJson.cjs +2 -0
- package/dist/adapters/atomicJson.cjs.map +1 -0
- package/dist/adapters/atomicJson.mjs +2 -0
- package/dist/adapters/atomicJson.mjs.map +1 -0
- package/dist/adapters/pi/extension.cjs +2 -0
- package/dist/adapters/pi/extension.cjs.map +1 -0
- package/dist/adapters/pi/extension.mjs +2 -0
- package/dist/adapters/pi/extension.mjs.map +1 -0
- package/dist/adapters/pollScheduler.cjs +2 -0
- package/dist/adapters/pollScheduler.cjs.map +1 -0
- package/dist/adapters/pollScheduler.mjs +2 -0
- package/dist/adapters/pollScheduler.mjs.map +1 -0
- package/dist/adapters/process/childTranscript.cjs +5 -0
- package/dist/adapters/process/childTranscript.cjs.map +1 -0
- package/dist/adapters/process/childTranscript.mjs +5 -0
- package/dist/adapters/process/childTranscript.mjs.map +1 -0
- package/dist/adapters/process/env.cjs +2 -0
- package/dist/adapters/process/env.cjs.map +1 -0
- package/dist/adapters/process/env.d.cts +69 -0
- package/dist/adapters/process/env.d.cts.map +1 -0
- package/dist/adapters/process/env.d.mts +69 -0
- package/dist/adapters/process/env.d.mts.map +1 -0
- package/dist/adapters/process/env.mjs +2 -0
- package/dist/adapters/process/env.mjs.map +1 -0
- package/dist/adapters/process/sdkRunnerEntry.cjs +2 -0
- package/dist/adapters/process/sdkRunnerEntry.cjs.map +1 -0
- package/dist/adapters/process/sdkRunnerEntry.d.cts +5 -0
- package/dist/adapters/process/sdkRunnerEntry.d.cts.map +1 -0
- package/dist/adapters/process/sdkRunnerEntry.d.mts +5 -0
- package/dist/adapters/process/sdkRunnerEntry.d.mts.map +1 -0
- package/dist/adapters/process/sdkRunnerEntry.mjs +2 -0
- package/dist/adapters/process/sdkRunnerEntry.mjs.map +1 -0
- package/dist/adapters/processTerminal.cjs +2 -0
- package/dist/adapters/processTerminal.cjs.map +1 -0
- package/dist/adapters/processTerminal.mjs +2 -0
- package/dist/adapters/processTerminal.mjs.map +1 -0
- package/dist/adapters/resultWatcher.cjs +2 -0
- package/dist/adapters/resultWatcher.cjs.map +1 -0
- package/dist/adapters/resultWatcher.mjs +2 -0
- package/dist/adapters/resultWatcher.mjs.map +1 -0
- package/dist/adapters/runIdResolver.cjs +2 -0
- package/dist/adapters/runIdResolver.cjs.map +1 -0
- package/dist/adapters/runIdResolver.mjs +2 -0
- package/dist/adapters/runIdResolver.mjs.map +1 -0
- package/dist/adapters/runRegistry.cjs +2 -0
- package/dist/adapters/runRegistry.cjs.map +1 -0
- package/dist/adapters/runRegistry.mjs +2 -0
- package/dist/adapters/runRegistry.mjs.map +1 -0
- package/dist/adapters/scopeOwner.cjs +2 -0
- package/dist/adapters/scopeOwner.cjs.map +1 -0
- package/dist/adapters/scopeOwner.mjs +2 -0
- package/dist/adapters/scopeOwner.mjs.map +1 -0
- package/dist/adapters/staleRunReconciler.cjs +2 -0
- package/dist/adapters/staleRunReconciler.cjs.map +1 -0
- package/dist/adapters/staleRunReconciler.mjs +2 -0
- package/dist/adapters/staleRunReconciler.mjs.map +1 -0
- package/dist/adapters/statusReader.cjs +2 -0
- package/dist/adapters/statusReader.cjs.map +1 -0
- package/dist/adapters/statusReader.mjs +2 -0
- package/dist/adapters/statusReader.mjs.map +1 -0
- package/dist/adapters/suspendedRuns.cjs +6 -0
- package/dist/adapters/suspendedRuns.cjs.map +1 -0
- package/dist/adapters/suspendedRuns.mjs +6 -0
- package/dist/adapters/suspendedRuns.mjs.map +1 -0
- package/dist/api/backgroundWork.cjs +1 -1
- package/dist/api/backgroundWork.d.cts +2 -2
- package/dist/api/backgroundWork.d.mts +2 -2
- package/dist/api/backgroundWork.mjs +1 -1
- package/dist/api/capabilityCeiling.cjs +1 -1
- package/dist/api/capabilityCeiling.d.cts +3 -3
- package/dist/api/capabilityCeiling.d.mts +3 -3
- package/dist/api/capabilityCeiling.mjs +1 -2
- package/dist/api/delegation.cjs +1 -1
- package/dist/api/delegation.d.cts +1 -1
- package/dist/api/delegation.d.mts +1 -1
- package/dist/api/delegation.mjs +1 -1
- package/dist/api/teamSnapshot.cjs +1 -1
- package/dist/api/teamSnapshot.d.cts +2 -2
- package/dist/api/teamSnapshot.d.mts +2 -2
- package/dist/api/teamSnapshot.mjs +1 -2
- package/dist/commands/slash/chainExpression.cjs.map +1 -0
- package/dist/commands/slash/chainExpression.mjs.map +1 -0
- package/dist/commands/slash/doctor.cjs +5 -0
- package/dist/commands/slash/doctor.cjs.map +1 -0
- package/dist/commands/slash/doctor.mjs +5 -0
- package/dist/{slash/doctor.cjs.map → commands/slash/doctor.mjs.map} +1 -1
- package/dist/{slash → commands/slash}/slashCommands.cjs +1 -1
- package/dist/commands/slash/slashCommands.cjs.map +1 -0
- package/dist/{slash → commands/slash}/slashCommands.mjs +2 -2
- package/dist/commands/slash/slashCommands.mjs.map +1 -0
- package/dist/commands/slash/spawnRequestMapping.cjs.map +1 -0
- package/dist/commands/slash/spawnRequestMapping.mjs.map +1 -0
- package/dist/commands/slash/subagentLaunch.cjs +2 -0
- package/dist/commands/slash/subagentLaunch.cjs.map +1 -0
- package/dist/commands/slash/subagentLaunch.mjs +2 -0
- package/dist/commands/slash/subagentLaunch.mjs.map +1 -0
- package/dist/container/globalBridge.cjs.map +1 -1
- package/dist/container/globalBridge.d.cts +1 -1
- package/dist/container/globalBridge.d.mts +1 -1
- package/dist/container/globalBridge.mjs.map +1 -1
- package/dist/container/index.cjs +1 -1
- package/dist/container/index.cjs.map +1 -1
- package/dist/container/index.d.cts +2 -1
- package/dist/container/index.d.cts.map +1 -1
- package/dist/container/index.d.mts +2 -1
- package/dist/container/index.d.mts.map +1 -1
- package/dist/container/index.mjs +1 -1
- package/dist/container/index.mjs.map +1 -1
- package/dist/container/modules/agents.cjs +1 -1
- package/dist/container/modules/agents.cjs.map +1 -1
- package/dist/container/modules/agents.mjs +1 -1
- package/dist/container/modules/agents.mjs.map +1 -1
- package/dist/container/modules/core.cjs +1 -1
- package/dist/container/modules/core.cjs.map +1 -1
- package/dist/container/modules/core.mjs +1 -1
- package/dist/container/modules/core.mjs.map +1 -1
- package/dist/container/modules/extensions.cjs +1 -1
- package/dist/container/modules/extensions.cjs.map +1 -1
- package/dist/container/modules/extensions.mjs +1 -1
- package/dist/container/modules/extensions.mjs.map +1 -1
- package/dist/container/modules/intercom.cjs +1 -1
- package/dist/container/modules/intercom.cjs.map +1 -1
- package/dist/container/modules/intercom.mjs +1 -1
- package/dist/container/modules/intercom.mjs.map +1 -1
- package/dist/container/modules/runs.cjs +1 -1
- package/dist/container/modules/runs.cjs.map +1 -1
- package/dist/container/modules/runs.mjs +1 -1
- package/dist/container/modules/runs.mjs.map +1 -1
- package/dist/container/types.cjs +1 -1
- package/dist/container/types.cjs.map +1 -1
- package/dist/container/types.d.cts +1 -0
- package/dist/container/types.d.cts.map +1 -1
- package/dist/container/types.d.mts +1 -0
- package/dist/container/types.d.mts.map +1 -1
- package/dist/container/types.mjs +1 -1
- package/dist/container/types.mjs.map +1 -1
- package/dist/env.cjs +1 -2
- package/dist/env.d.cts +2 -65
- package/dist/env.d.mts +2 -65
- package/dist/env.mjs +1 -1
- package/dist/env.mjs.map +1 -1
- package/dist/extensions/config.cjs.map +1 -1
- package/dist/extensions/config.mjs.map +1 -1
- package/dist/extensions/delegationBridge.cjs.map +1 -1
- package/dist/extensions/delegationBridge.mjs.map +1 -1
- package/dist/extensions/doom.cjs +1 -1
- package/dist/extensions/doom.cjs.map +1 -1
- package/dist/extensions/doom.mjs +1 -1
- package/dist/extensions/doom.mjs.map +1 -1
- package/dist/extensions/fablePlanBridge.cjs +1 -1
- package/dist/extensions/fablePlanBridge.cjs.map +1 -1
- package/dist/extensions/fablePlanBridge.mjs +1 -1
- package/dist/extensions/fablePlanBridge.mjs.map +1 -1
- package/dist/extensions/managementActions.cjs +1 -1
- package/dist/extensions/managementActions.cjs.map +1 -1
- package/dist/extensions/managementActions.mjs +1 -1
- package/dist/extensions/managementActions.mjs.map +1 -1
- package/dist/extensions/operationJournal.cjs +1 -1
- package/dist/extensions/operationJournal.cjs.map +1 -1
- package/dist/extensions/operationJournal.mjs +1 -1
- package/dist/extensions/operationJournal.mjs.map +1 -1
- package/dist/extensions/orchestratorPrompt.cjs +5 -6
- package/dist/extensions/orchestratorPrompt.cjs.map +1 -1
- package/dist/extensions/orchestratorPrompt.mjs +5 -6
- package/dist/extensions/orchestratorPrompt.mjs.map +1 -1
- package/dist/extensions/schemas.mjs.map +1 -1
- package/dist/extensions/spawnPlan.cjs +3 -2
- package/dist/extensions/spawnPlan.cjs.map +1 -1
- package/dist/extensions/spawnPlan.mjs +3 -2
- package/dist/extensions/spawnPlan.mjs.map +1 -1
- package/dist/extensions/standard.cjs +1 -1
- package/dist/extensions/standard.cjs.map +1 -1
- package/dist/extensions/standard.mjs +1 -1
- package/dist/extensions/standard.mjs.map +1 -1
- package/dist/extensions/statusViews.cjs +1 -1
- package/dist/extensions/statusViews.cjs.map +1 -1
- package/dist/extensions/statusViews.mjs +2 -2
- package/dist/extensions/statusViews.mjs.map +1 -1
- package/dist/extensions/subagentPromptRuntime.cjs +11 -11
- package/dist/extensions/subagentPromptRuntime.cjs.map +1 -1
- package/dist/extensions/subagentPromptRuntime.d.cts.map +1 -1
- package/dist/extensions/subagentPromptRuntime.d.mts.map +1 -1
- package/dist/extensions/subagentPromptRuntime.mjs +5 -5
- package/dist/extensions/subagentPromptRuntime.mjs.map +1 -1
- package/dist/extensions/subagentTool.cjs +5 -6
- package/dist/extensions/subagentTool.cjs.map +1 -1
- package/dist/extensions/subagentTool.mjs +5 -6
- package/dist/extensions/subagentTool.mjs.map +1 -1
- package/dist/extensions/toolDescription.cjs +1 -1
- package/dist/extensions/toolDescription.cjs.map +1 -1
- package/dist/extensions/toolDescription.mjs +1 -1
- package/dist/extensions/toolDescription.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/runs/background/cliRunnerEntry.cjs +1 -2
- package/dist/runs/background/cliRunnerEntry.mjs +1 -2
- package/dist/runs/sdkRunnerEntry.cjs +1 -2
- package/dist/runs/sdkRunnerEntry.d.cts +2 -1
- package/dist/runs/sdkRunnerEntry.d.mts +2 -1
- package/dist/runs/sdkRunnerEntry.mjs +1 -2
- package/dist/schemas/team/backgroundWork.cjs +2 -0
- package/dist/schemas/team/backgroundWork.cjs.map +1 -0
- package/dist/{protocol → schemas/team}/backgroundWork.d.cts +2 -2
- package/dist/schemas/team/backgroundWork.d.cts.map +1 -0
- package/dist/{protocol → schemas/team}/backgroundWork.d.mts +2 -2
- package/dist/schemas/team/backgroundWork.d.mts.map +1 -0
- package/dist/schemas/team/backgroundWork.mjs +2 -0
- package/dist/{protocol/backgroundWork.cjs.map → schemas/team/backgroundWork.mjs.map} +1 -1
- package/dist/schemas/team/capabilityCeiling.cjs +2 -0
- package/dist/schemas/team/capabilityCeiling.cjs.map +1 -0
- package/dist/{protocol → schemas/team}/capabilityCeiling.d.cts +3 -3
- package/dist/schemas/team/capabilityCeiling.d.cts.map +1 -0
- package/dist/{protocol → schemas/team}/capabilityCeiling.d.mts +3 -3
- package/dist/schemas/team/capabilityCeiling.d.mts.map +1 -0
- package/dist/schemas/team/capabilityCeiling.mjs +2 -0
- package/dist/schemas/team/capabilityCeiling.mjs.map +1 -0
- package/dist/schemas/team/delegation.cjs.map +1 -0
- package/dist/{protocol → schemas/team}/delegation.d.cts +1 -1
- package/dist/schemas/team/delegation.d.cts.map +1 -0
- package/dist/{protocol → schemas/team}/delegation.d.mts +1 -1
- package/dist/schemas/team/delegation.d.mts.map +1 -0
- package/dist/schemas/team/delegation.mjs.map +1 -0
- package/dist/schemas/team/packageConfig.cjs +2 -0
- package/dist/schemas/team/packageConfig.cjs.map +1 -0
- package/dist/schemas/team/packageConfig.mjs +2 -0
- package/dist/schemas/team/packageConfig.mjs.map +1 -0
- package/dist/services/agents/discovery.cjs +2 -0
- package/dist/services/agents/discovery.cjs.map +1 -0
- package/dist/services/agents/discovery.mjs +2 -0
- package/dist/services/agents/discovery.mjs.map +1 -0
- package/dist/services/agents/frontmatter.cjs.map +1 -0
- package/dist/{agents → services/agents}/frontmatter.mjs +1 -1
- package/dist/services/agents/frontmatter.mjs.map +1 -0
- package/dist/services/agents/loader.cjs +2 -0
- package/dist/services/agents/loader.cjs.map +1 -0
- package/dist/services/agents/loader.mjs +2 -0
- package/dist/services/agents/loader.mjs.map +1 -0
- package/dist/services/agents/memory.cjs +3 -0
- package/dist/services/agents/memory.cjs.map +1 -0
- package/dist/{agents → services/agents}/memory.mjs +1 -1
- package/dist/services/agents/memory.mjs.map +1 -0
- package/dist/services/agents/projectRoot.cjs +2 -0
- package/dist/services/agents/projectRoot.cjs.map +1 -0
- package/dist/services/agents/projectRoot.mjs +2 -0
- package/dist/services/agents/projectRoot.mjs.map +1 -0
- package/dist/services/agents/settings.cjs +2 -0
- package/dist/services/agents/settings.cjs.map +1 -0
- package/dist/services/agents/settings.mjs +2 -0
- package/dist/services/agents/settings.mjs.map +1 -0
- package/dist/services/agents/skills.cjs +0 -0
- package/dist/services/agents/skills.cjs.map +1 -0
- package/dist/services/agents/skills.mjs +5 -0
- package/dist/services/agents/skills.mjs.map +1 -0
- package/dist/{agents → services/agents}/types.d.cts +4 -4
- package/dist/services/agents/types.d.cts.map +1 -0
- package/dist/{agents → services/agents}/types.d.mts +4 -4
- package/dist/services/agents/types.d.mts.map +1 -0
- package/dist/services/api/backgroundWork.cjs +1 -0
- package/dist/services/api/backgroundWork.d.cts +1 -0
- package/dist/services/api/backgroundWork.d.mts +1 -0
- package/dist/services/api/backgroundWork.mjs +1 -0
- package/dist/services/api/capabilityCeiling.cjs +1 -0
- package/dist/services/api/capabilityCeiling.d.cts +2 -0
- package/dist/services/api/capabilityCeiling.d.mts +2 -0
- package/dist/services/api/capabilityCeiling.mjs +2 -0
- package/dist/services/api/capabilityCeiling.mjs.map +1 -0
- package/dist/services/api/teamSnapshot.cjs +1 -0
- package/dist/services/api/teamSnapshot.d.cts +1 -0
- package/dist/services/api/teamSnapshot.d.mts +1 -0
- package/dist/services/api/teamSnapshot.mjs +2 -0
- package/dist/services/api/teamSnapshot.mjs.map +1 -0
- package/dist/services/intercom/intercomBridge.cjs.map +1 -0
- package/dist/services/intercom/intercomBridge.mjs.map +1 -0
- package/dist/services/intercom/nativeTeamChannel.cjs +5 -0
- package/dist/services/intercom/nativeTeamChannel.cjs.map +1 -0
- package/dist/{intercom → services/intercom}/nativeTeamChannel.d.cts +1 -1
- package/dist/services/intercom/nativeTeamChannel.d.cts.map +1 -0
- package/dist/{intercom → services/intercom}/nativeTeamChannel.d.mts +1 -1
- package/dist/services/intercom/nativeTeamChannel.d.mts.map +1 -0
- package/dist/services/intercom/nativeTeamChannel.mjs +5 -0
- package/dist/services/intercom/nativeTeamChannel.mjs.map +1 -0
- package/dist/services/intercom/supervisorControlChannel.cjs +2 -0
- package/dist/services/intercom/supervisorControlChannel.cjs.map +1 -0
- package/dist/{intercom → services/intercom}/supervisorControlChannel.mjs +1 -1
- package/dist/services/intercom/supervisorControlChannel.mjs.map +1 -0
- package/dist/services/runs/background/asyncExecution.cjs +2 -0
- package/dist/services/runs/background/asyncExecution.cjs.map +1 -0
- package/dist/services/runs/background/asyncExecution.mjs +2 -0
- package/dist/services/runs/background/asyncExecution.mjs.map +1 -0
- package/dist/services/runs/background/asyncJobTracker.cjs +1 -0
- package/dist/services/runs/background/asyncJobTracker.mjs +2 -0
- package/dist/services/runs/background/asyncJobTracker.mjs.map +1 -0
- package/dist/services/runs/background/cliRunnerEntry.cjs +2 -0
- package/dist/services/runs/background/cliRunnerEntry.cjs.map +1 -0
- package/dist/services/runs/background/cliRunnerEntry.mjs +2 -0
- package/dist/services/runs/background/cliRunnerEntry.mjs.map +1 -0
- package/dist/services/runs/background/completionBatcher.cjs.map +1 -0
- package/dist/services/runs/background/completionBatcher.mjs +2 -0
- package/dist/services/runs/background/completionBatcher.mjs.map +1 -0
- package/dist/{runs → services/runs}/background/notify.cjs +1 -1
- package/dist/services/runs/background/notify.cjs.map +1 -0
- package/dist/{runs → services/runs}/background/notify.mjs +1 -1
- package/dist/services/runs/background/notify.mjs.map +1 -0
- package/dist/services/runs/background/parentWatchdog.cjs.map +1 -0
- package/dist/services/runs/background/parentWatchdog.mjs.map +1 -0
- package/dist/services/runs/background/processTerminal.cjs +1 -0
- package/dist/services/runs/background/processTerminal.mjs +2 -0
- package/dist/services/runs/background/processTerminal.mjs.map +1 -0
- package/dist/services/runs/background/resultWatcher.cjs +1 -0
- package/dist/services/runs/background/resultWatcher.mjs +2 -0
- package/dist/services/runs/background/resultWatcher.mjs.map +1 -0
- package/dist/services/runs/background/runIdResolver.cjs +1 -0
- package/dist/services/runs/background/runIdResolver.mjs +2 -0
- package/dist/services/runs/background/runIdResolver.mjs.map +1 -0
- package/dist/services/runs/background/runnerBootstrap.cjs +2 -0
- package/dist/services/runs/background/runnerBootstrap.cjs.map +1 -0
- package/dist/services/runs/background/runnerBootstrap.mjs +2 -0
- package/dist/services/runs/background/runnerBootstrap.mjs.map +1 -0
- package/dist/services/runs/background/runnerExecution.cjs +2 -0
- package/dist/services/runs/background/runnerExecution.cjs.map +1 -0
- package/dist/services/runs/background/runnerExecution.mjs +2 -0
- package/dist/services/runs/background/runnerExecution.mjs.map +1 -0
- package/dist/{runs → services/runs}/background/runnerReporting.cjs +2 -2
- package/dist/services/runs/background/runnerReporting.cjs.map +1 -0
- package/dist/{runs → services/runs}/background/runnerReporting.mjs +2 -2
- package/dist/services/runs/background/runnerReporting.mjs.map +1 -0
- package/dist/services/runs/background/runnerTeamMembership.cjs +2 -0
- package/dist/services/runs/background/runnerTeamMembership.cjs.map +1 -0
- package/dist/services/runs/background/runnerTeamMembership.mjs +2 -0
- package/dist/services/runs/background/runnerTeamMembership.mjs.map +1 -0
- package/dist/services/runs/background/spawnHandshake.cjs +2 -0
- package/dist/services/runs/background/spawnHandshake.cjs.map +1 -0
- package/dist/services/runs/background/spawnHandshake.mjs +2 -0
- package/dist/services/runs/background/spawnHandshake.mjs.map +1 -0
- package/dist/services/runs/background/staleRunReconciler.cjs +1 -0
- package/dist/services/runs/background/staleRunReconciler.mjs +2 -0
- package/dist/services/runs/background/staleRunReconciler.mjs.map +1 -0
- package/dist/services/runs/background/statusReader.cjs +1 -0
- package/dist/services/runs/background/statusReader.mjs +2 -0
- package/dist/services/runs/background/statusReader.mjs.map +1 -0
- package/dist/services/runs/background/statusWriter.cjs +2 -0
- package/dist/services/runs/background/statusWriter.cjs.map +1 -0
- package/dist/services/runs/background/statusWriter.mjs +2 -0
- package/dist/services/runs/background/statusWriter.mjs.map +1 -0
- package/dist/services/runs/background/subagentWait.cjs +2 -0
- package/dist/services/runs/background/subagentWait.cjs.map +1 -0
- package/dist/services/runs/background/subagentWait.mjs +2 -0
- package/dist/services/runs/background/subagentWait.mjs.map +1 -0
- package/dist/{runs/background/terminalPersistence.mjs → services/runs/background/terminalPersistence.cjs} +2 -2
- package/dist/services/runs/background/terminalPersistence.cjs.map +1 -0
- package/dist/services/runs/background/terminalPersistence.mjs +2 -0
- package/dist/services/runs/background/terminalPersistence.mjs.map +1 -0
- package/dist/services/runs/extensionErrorTelemetry.cjs +2 -0
- package/dist/services/runs/extensionErrorTelemetry.cjs.map +1 -0
- package/dist/services/runs/extensionErrorTelemetry.mjs +2 -0
- package/dist/services/runs/extensionErrorTelemetry.mjs.map +1 -0
- package/dist/services/runs/registry/processGroup.cjs.map +1 -0
- package/dist/services/runs/registry/processGroup.mjs.map +1 -0
- package/dist/services/runs/registry/runRegistry.cjs +1 -0
- package/dist/services/runs/registry/runRegistry.mjs +2 -0
- package/dist/services/runs/registry/runRegistry.mjs.map +1 -0
- package/dist/services/runs/registry/sessionLifecycle.cjs +2 -0
- package/dist/services/runs/registry/sessionLifecycle.cjs.map +1 -0
- package/dist/services/runs/registry/sessionLifecycle.mjs +2 -0
- package/dist/services/runs/registry/sessionLifecycle.mjs.map +1 -0
- package/dist/services/runs/registry/suspendedRuns.cjs +1 -0
- package/dist/services/runs/registry/suspendedRuns.mjs +2 -0
- package/dist/services/runs/registry/suspendedRuns.mjs.map +1 -0
- package/dist/services/runs/sdkRunnerEntry.cjs +1 -0
- package/dist/services/runs/sdkRunnerEntry.mjs +1 -0
- package/dist/services/runs/shared/claudeFableProfile.cjs +4 -0
- package/dist/services/runs/shared/claudeFableProfile.cjs.map +1 -0
- package/dist/services/runs/shared/claudeFableProfile.mjs +4 -0
- package/dist/services/runs/shared/claudeFableProfile.mjs.map +1 -0
- package/dist/services/runs/shared/depthGuard.cjs +2 -0
- package/dist/services/runs/shared/depthGuard.cjs.map +1 -0
- package/dist/services/runs/shared/depthGuard.mjs +2 -0
- package/dist/services/runs/shared/depthGuard.mjs.map +1 -0
- package/dist/services/runs/shared/mcpDirectToolAllowlist.cjs +2 -0
- package/dist/services/runs/shared/mcpDirectToolAllowlist.cjs.map +1 -0
- package/dist/services/runs/shared/mcpDirectToolAllowlist.mjs +2 -0
- package/dist/services/runs/shared/mcpDirectToolAllowlist.mjs.map +1 -0
- package/dist/services/runs/shared/modelFallback.cjs +2 -0
- package/dist/services/runs/shared/modelFallback.cjs.map +1 -0
- package/dist/services/runs/shared/modelFallback.mjs +2 -0
- package/dist/services/runs/shared/modelFallback.mjs.map +1 -0
- package/dist/services/runs/shared/modelScope.cjs +2 -0
- package/dist/services/runs/shared/modelScope.cjs.map +1 -0
- package/dist/services/runs/shared/modelScope.mjs +2 -0
- package/dist/services/runs/shared/modelScope.mjs.map +1 -0
- package/dist/services/runs/shared/nestedPath.cjs +2 -0
- package/dist/services/runs/shared/nestedPath.cjs.map +1 -0
- package/dist/services/runs/shared/nestedPath.mjs +2 -0
- package/dist/services/runs/shared/nestedPath.mjs.map +1 -0
- package/dist/services/runs/shared/piArgs.cjs +2 -0
- package/dist/services/runs/shared/piArgs.cjs.map +1 -0
- package/dist/services/runs/shared/piArgs.mjs +2 -0
- package/dist/services/runs/shared/piArgs.mjs.map +1 -0
- package/dist/services/runs/shared/piSpawn.cjs +2 -0
- package/dist/services/runs/shared/piSpawn.cjs.map +1 -0
- package/dist/services/runs/shared/piSpawn.mjs +2 -0
- package/dist/services/runs/shared/piSpawn.mjs.map +1 -0
- package/dist/services/runs/shared/runWithConcurrency.cjs +2 -0
- package/dist/services/runs/shared/runWithConcurrency.cjs.map +1 -0
- package/dist/services/runs/shared/runWithConcurrency.d.cts +25 -0
- package/dist/services/runs/shared/runWithConcurrency.d.cts.map +1 -0
- package/dist/services/runs/shared/runWithConcurrency.d.mts +25 -0
- package/dist/services/runs/shared/runWithConcurrency.d.mts.map +1 -0
- package/dist/services/runs/shared/runWithConcurrency.mjs +2 -0
- package/dist/services/runs/shared/runWithConcurrency.mjs.map +1 -0
- package/dist/services/runs/shared/runtimeRegistry.cjs +2 -0
- package/dist/services/runs/shared/runtimeRegistry.cjs.map +1 -0
- package/dist/services/runs/shared/runtimeRegistry.mjs +2 -0
- package/dist/services/runs/shared/runtimeRegistry.mjs.map +1 -0
- package/dist/services/runs/shared/structuredOutput.cjs +2 -0
- package/dist/{runs → services/runs}/shared/structuredOutput.cjs.map +1 -1
- package/dist/services/runs/shared/structuredOutput.mjs +2 -0
- package/dist/{runs → services/runs}/shared/structuredOutput.mjs.map +1 -1
- package/dist/services/runs/shared/toolAvailability.cjs +2 -0
- package/dist/services/runs/shared/toolAvailability.cjs.map +1 -0
- package/dist/services/runs/shared/toolAvailability.mjs +2 -0
- package/dist/services/runs/shared/toolAvailability.mjs.map +1 -0
- package/dist/services/runs/shared/toolBudget.cjs +2 -0
- package/dist/services/runs/shared/toolBudget.cjs.map +1 -0
- package/dist/services/runs/shared/toolBudget.mjs +2 -0
- package/dist/services/runs/shared/toolBudget.mjs.map +1 -0
- package/dist/shared/artifacts.cjs.map +1 -1
- package/dist/shared/artifacts.mjs.map +1 -1
- package/dist/shared/atomicJson.cjs +1 -2
- package/dist/shared/atomicJson.mjs +1 -1
- package/dist/shared/atomicJson.mjs.map +1 -1
- package/dist/shared/childTranscript.cjs +1 -5
- package/dist/shared/childTranscript.mjs +1 -4
- package/dist/shared/childTranscript.mjs.map +1 -1
- package/dist/shared/fileSystemRetry.cjs +1 -1
- package/dist/shared/fileSystemRetry.cjs.map +1 -1
- package/dist/shared/fileSystemRetry.mjs +1 -1
- package/dist/shared/fileSystemRetry.mjs.map +1 -1
- package/dist/shared/formatters.cjs.map +1 -1
- package/dist/shared/formatters.mjs.map +1 -1
- package/dist/shared/paths.cjs +1 -1
- package/dist/shared/paths.cjs.map +1 -1
- package/dist/shared/paths.mjs +1 -1
- package/dist/shared/paths.mjs.map +1 -1
- package/dist/shared/pollScheduler.cjs +1 -2
- package/dist/shared/pollScheduler.mjs +1 -1
- package/dist/shared/pollScheduler.mjs.map +1 -1
- package/dist/shared/scopeOwner.cjs +1 -2
- package/dist/shared/scopeOwner.mjs +1 -1
- package/dist/shared/scopeOwner.mjs.map +1 -1
- package/dist/tui/agentCatalog.cjs +1 -1
- package/dist/tui/agentCatalog.cjs.map +1 -1
- package/dist/tui/agentCatalog.mjs +1 -1
- package/dist/tui/agentCatalog.mjs.map +1 -1
- package/dist/tui/agentResourceProjection.cjs +1 -1
- package/dist/tui/agentResourceProjection.cjs.map +1 -1
- package/dist/tui/agentResourceProjection.mjs +1 -1
- package/dist/tui/agentResourceProjection.mjs.map +1 -1
- package/dist/tui/completionNotice.cjs +1 -1
- package/dist/tui/completionNotice.cjs.map +1 -1
- package/dist/tui/completionNotice.mjs +1 -1
- package/dist/tui/completionNotice.mjs.map +1 -1
- package/dist/tui/fleet.cjs +2 -2
- package/dist/tui/fleet.cjs.map +1 -1
- package/dist/tui/fleet.mjs +2 -2
- package/dist/tui/fleet.mjs.map +1 -1
- package/dist/tui/fleetAgentView.cjs.map +1 -1
- package/dist/tui/fleetAgentView.mjs.map +1 -1
- package/dist/tui/fleetStatus.cjs +1 -1
- package/dist/tui/fleetStatus.cjs.map +1 -1
- package/dist/tui/fleetStatus.mjs +1 -1
- package/dist/tui/fleetStatus.mjs.map +1 -1
- package/dist/tui/fleetTranscript.cjs +1 -1
- package/dist/tui/fleetTranscript.cjs.map +1 -1
- package/dist/tui/fleetTranscript.mjs +1 -1
- package/dist/tui/fleetTranscript.mjs.map +1 -1
- package/dist/tui/register.cjs +1 -1
- package/dist/tui/register.cjs.map +1 -1
- package/dist/tui/register.mjs +1 -1
- package/dist/tui/register.mjs.map +1 -1
- package/dist/tui/subagentToolRender.cjs +3 -0
- package/dist/tui/subagentToolRender.cjs.map +1 -0
- package/dist/tui/subagentToolRender.mjs +3 -0
- package/dist/tui/subagentToolRender.mjs.map +1 -0
- package/dist/{types.d.cts → types/index.d.cts} +2 -2
- package/dist/types/index.d.cts.map +1 -0
- package/dist/{types.d.mts → types/index.d.mts} +2 -2
- package/dist/types/index.d.mts.map +1 -0
- package/package.json +23 -16
- package/dist/agents/discovery.cjs +0 -2
- package/dist/agents/discovery.cjs.map +0 -1
- package/dist/agents/discovery.mjs +0 -2
- package/dist/agents/discovery.mjs.map +0 -1
- package/dist/agents/frontmatter.cjs.map +0 -1
- package/dist/agents/frontmatter.mjs.map +0 -1
- package/dist/agents/loader.cjs +0 -2
- package/dist/agents/loader.cjs.map +0 -1
- package/dist/agents/loader.mjs +0 -2
- package/dist/agents/loader.mjs.map +0 -1
- package/dist/agents/memory.cjs +0 -3
- package/dist/agents/memory.cjs.map +0 -1
- package/dist/agents/memory.mjs.map +0 -1
- package/dist/agents/projectRoot.cjs +0 -2
- package/dist/agents/projectRoot.cjs.map +0 -1
- package/dist/agents/projectRoot.mjs +0 -2
- package/dist/agents/projectRoot.mjs.map +0 -1
- package/dist/agents/settings.cjs +0 -2
- package/dist/agents/settings.cjs.map +0 -1
- package/dist/agents/settings.mjs +0 -2
- package/dist/agents/settings.mjs.map +0 -1
- package/dist/agents/skills.cjs +0 -0
- package/dist/agents/skills.cjs.map +0 -1
- package/dist/agents/skills.mjs +0 -5
- package/dist/agents/skills.mjs.map +0 -1
- package/dist/agents/types.d.cts.map +0 -1
- package/dist/agents/types.d.mts.map +0 -1
- package/dist/api/capabilityCeiling.mjs.map +0 -1
- package/dist/api/teamSnapshot.mjs.map +0 -1
- package/dist/env.cjs.map +0 -1
- package/dist/env.d.cts.map +0 -1
- package/dist/env.d.mts.map +0 -1
- package/dist/extension.cjs +0 -2
- package/dist/extension.cjs.map +0 -1
- package/dist/extension.mjs +0 -2
- package/dist/extension.mjs.map +0 -1
- package/dist/intercom/intercomBridge.cjs.map +0 -1
- package/dist/intercom/intercomBridge.mjs.map +0 -1
- package/dist/intercom/nativeTeamChannel.cjs +0 -5
- package/dist/intercom/nativeTeamChannel.cjs.map +0 -1
- package/dist/intercom/nativeTeamChannel.d.cts.map +0 -1
- package/dist/intercom/nativeTeamChannel.d.mts.map +0 -1
- package/dist/intercom/nativeTeamChannel.mjs +0 -5
- package/dist/intercom/nativeTeamChannel.mjs.map +0 -1
- package/dist/intercom/supervisorControlChannel.cjs +0 -2
- package/dist/intercom/supervisorControlChannel.cjs.map +0 -1
- package/dist/intercom/supervisorControlChannel.mjs.map +0 -1
- package/dist/protocol/backgroundWork.cjs +0 -2
- package/dist/protocol/backgroundWork.d.cts.map +0 -1
- package/dist/protocol/backgroundWork.d.mts.map +0 -1
- package/dist/protocol/backgroundWork.mjs +0 -2
- package/dist/protocol/backgroundWork.mjs.map +0 -1
- package/dist/protocol/capabilityCeiling.cjs +0 -2
- package/dist/protocol/capabilityCeiling.cjs.map +0 -1
- package/dist/protocol/capabilityCeiling.d.cts.map +0 -1
- package/dist/protocol/capabilityCeiling.d.mts.map +0 -1
- package/dist/protocol/capabilityCeiling.mjs +0 -2
- package/dist/protocol/capabilityCeiling.mjs.map +0 -1
- package/dist/protocol/delegation.cjs.map +0 -1
- package/dist/protocol/delegation.d.cts.map +0 -1
- package/dist/protocol/delegation.d.mts.map +0 -1
- package/dist/protocol/delegation.mjs.map +0 -1
- package/dist/runs/background/asyncExecution.cjs +0 -2
- package/dist/runs/background/asyncExecution.cjs.map +0 -1
- package/dist/runs/background/asyncExecution.mjs +0 -2
- package/dist/runs/background/asyncExecution.mjs.map +0 -1
- package/dist/runs/background/asyncJobTracker.cjs +0 -2
- package/dist/runs/background/asyncJobTracker.cjs.map +0 -1
- package/dist/runs/background/asyncJobTracker.mjs +0 -2
- package/dist/runs/background/asyncJobTracker.mjs.map +0 -1
- package/dist/runs/background/cliRunnerEntry.cjs.map +0 -1
- package/dist/runs/background/cliRunnerEntry.mjs.map +0 -1
- package/dist/runs/background/completionBatcher.cjs.map +0 -1
- package/dist/runs/background/completionBatcher.mjs +0 -2
- package/dist/runs/background/completionBatcher.mjs.map +0 -1
- package/dist/runs/background/notify.cjs.map +0 -1
- package/dist/runs/background/notify.mjs.map +0 -1
- package/dist/runs/background/parentWatchdog.cjs.map +0 -1
- package/dist/runs/background/parentWatchdog.mjs.map +0 -1
- package/dist/runs/background/processTerminal.cjs +0 -2
- package/dist/runs/background/processTerminal.cjs.map +0 -1
- package/dist/runs/background/processTerminal.mjs +0 -2
- package/dist/runs/background/processTerminal.mjs.map +0 -1
- package/dist/runs/background/resultWatcher.cjs +0 -2
- package/dist/runs/background/resultWatcher.cjs.map +0 -1
- package/dist/runs/background/resultWatcher.mjs +0 -2
- package/dist/runs/background/resultWatcher.mjs.map +0 -1
- package/dist/runs/background/runIdResolver.cjs +0 -2
- package/dist/runs/background/runIdResolver.cjs.map +0 -1
- package/dist/runs/background/runIdResolver.mjs +0 -2
- package/dist/runs/background/runIdResolver.mjs.map +0 -1
- package/dist/runs/background/runnerBootstrap.cjs +0 -2
- package/dist/runs/background/runnerBootstrap.cjs.map +0 -1
- package/dist/runs/background/runnerBootstrap.mjs +0 -2
- package/dist/runs/background/runnerBootstrap.mjs.map +0 -1
- package/dist/runs/background/runnerExecution.cjs +0 -2
- package/dist/runs/background/runnerExecution.cjs.map +0 -1
- package/dist/runs/background/runnerExecution.mjs +0 -2
- package/dist/runs/background/runnerExecution.mjs.map +0 -1
- package/dist/runs/background/runnerReporting.cjs.map +0 -1
- package/dist/runs/background/runnerReporting.mjs.map +0 -1
- package/dist/runs/background/runnerTeamMembership.cjs +0 -2
- package/dist/runs/background/runnerTeamMembership.cjs.map +0 -1
- package/dist/runs/background/runnerTeamMembership.mjs +0 -2
- package/dist/runs/background/runnerTeamMembership.mjs.map +0 -1
- package/dist/runs/background/spawnHandshake.cjs +0 -2
- package/dist/runs/background/spawnHandshake.cjs.map +0 -1
- package/dist/runs/background/spawnHandshake.mjs +0 -2
- package/dist/runs/background/spawnHandshake.mjs.map +0 -1
- package/dist/runs/background/staleRunReconciler.cjs +0 -2
- package/dist/runs/background/staleRunReconciler.cjs.map +0 -1
- package/dist/runs/background/staleRunReconciler.mjs +0 -2
- package/dist/runs/background/staleRunReconciler.mjs.map +0 -1
- package/dist/runs/background/statusReader.cjs +0 -2
- package/dist/runs/background/statusReader.cjs.map +0 -1
- package/dist/runs/background/statusReader.mjs +0 -2
- package/dist/runs/background/statusReader.mjs.map +0 -1
- package/dist/runs/background/statusWriter.cjs +0 -2
- package/dist/runs/background/statusWriter.cjs.map +0 -1
- package/dist/runs/background/statusWriter.mjs +0 -2
- package/dist/runs/background/statusWriter.mjs.map +0 -1
- package/dist/runs/background/subagentWait.cjs +0 -2
- package/dist/runs/background/subagentWait.cjs.map +0 -1
- package/dist/runs/background/subagentWait.mjs +0 -2
- package/dist/runs/background/subagentWait.mjs.map +0 -1
- package/dist/runs/background/terminalPersistence.cjs +0 -2
- package/dist/runs/background/terminalPersistence.cjs.map +0 -1
- package/dist/runs/background/terminalPersistence.mjs.map +0 -1
- package/dist/runs/registry/processGroup.cjs.map +0 -1
- package/dist/runs/registry/processGroup.mjs.map +0 -1
- package/dist/runs/registry/runRegistry.cjs +0 -2
- package/dist/runs/registry/runRegistry.cjs.map +0 -1
- package/dist/runs/registry/runRegistry.mjs +0 -2
- package/dist/runs/registry/runRegistry.mjs.map +0 -1
- package/dist/runs/registry/sessionLifecycle.cjs +0 -2
- package/dist/runs/registry/sessionLifecycle.cjs.map +0 -1
- package/dist/runs/registry/sessionLifecycle.mjs +0 -2
- package/dist/runs/registry/sessionLifecycle.mjs.map +0 -1
- package/dist/runs/registry/suspendedRuns.cjs +0 -4
- package/dist/runs/registry/suspendedRuns.cjs.map +0 -1
- package/dist/runs/registry/suspendedRuns.mjs +0 -4
- package/dist/runs/registry/suspendedRuns.mjs.map +0 -1
- package/dist/runs/sdkRunnerEntry.cjs.map +0 -1
- package/dist/runs/sdkRunnerEntry.mjs.map +0 -1
- package/dist/runs/shared/claudeFableProfile.cjs +0 -4
- package/dist/runs/shared/claudeFableProfile.cjs.map +0 -1
- package/dist/runs/shared/claudeFableProfile.mjs +0 -4
- package/dist/runs/shared/claudeFableProfile.mjs.map +0 -1
- package/dist/runs/shared/depthGuard.cjs +0 -2
- package/dist/runs/shared/depthGuard.cjs.map +0 -1
- package/dist/runs/shared/depthGuard.mjs +0 -2
- package/dist/runs/shared/depthGuard.mjs.map +0 -1
- package/dist/runs/shared/mcpDirectToolAllowlist.cjs +0 -2
- package/dist/runs/shared/mcpDirectToolAllowlist.cjs.map +0 -1
- package/dist/runs/shared/mcpDirectToolAllowlist.mjs +0 -2
- package/dist/runs/shared/mcpDirectToolAllowlist.mjs.map +0 -1
- package/dist/runs/shared/modelFallback.cjs +0 -2
- package/dist/runs/shared/modelFallback.cjs.map +0 -1
- package/dist/runs/shared/modelFallback.mjs +0 -2
- package/dist/runs/shared/modelFallback.mjs.map +0 -1
- package/dist/runs/shared/modelScope.cjs +0 -2
- package/dist/runs/shared/modelScope.cjs.map +0 -1
- package/dist/runs/shared/modelScope.mjs +0 -2
- package/dist/runs/shared/modelScope.mjs.map +0 -1
- package/dist/runs/shared/nestedPath.cjs +0 -2
- package/dist/runs/shared/nestedPath.cjs.map +0 -1
- package/dist/runs/shared/nestedPath.mjs +0 -2
- package/dist/runs/shared/nestedPath.mjs.map +0 -1
- package/dist/runs/shared/piArgs.cjs +0 -2
- package/dist/runs/shared/piArgs.cjs.map +0 -1
- package/dist/runs/shared/piArgs.mjs +0 -2
- package/dist/runs/shared/piArgs.mjs.map +0 -1
- package/dist/runs/shared/piSpawn.cjs +0 -2
- package/dist/runs/shared/piSpawn.cjs.map +0 -1
- package/dist/runs/shared/piSpawn.mjs +0 -2
- package/dist/runs/shared/piSpawn.mjs.map +0 -1
- package/dist/runs/shared/runWithConcurrency.cjs +0 -2
- package/dist/runs/shared/runWithConcurrency.cjs.map +0 -1
- package/dist/runs/shared/runWithConcurrency.mjs +0 -2
- package/dist/runs/shared/runWithConcurrency.mjs.map +0 -1
- package/dist/runs/shared/runtimeRegistry.cjs +0 -2
- package/dist/runs/shared/runtimeRegistry.cjs.map +0 -1
- package/dist/runs/shared/runtimeRegistry.mjs +0 -2
- package/dist/runs/shared/runtimeRegistry.mjs.map +0 -1
- package/dist/runs/shared/structuredOutput.cjs +0 -2
- package/dist/runs/shared/structuredOutput.mjs +0 -2
- package/dist/runs/shared/toolAvailability.cjs +0 -2
- package/dist/runs/shared/toolAvailability.cjs.map +0 -1
- package/dist/runs/shared/toolAvailability.mjs +0 -2
- package/dist/runs/shared/toolAvailability.mjs.map +0 -1
- package/dist/runs/shared/toolBudget.cjs +0 -2
- package/dist/runs/shared/toolBudget.cjs.map +0 -1
- package/dist/runs/shared/toolBudget.mjs +0 -2
- package/dist/runs/shared/toolBudget.mjs.map +0 -1
- package/dist/shared/atomicJson.cjs.map +0 -1
- package/dist/shared/childTranscript.cjs.map +0 -1
- package/dist/shared/pollScheduler.cjs.map +0 -1
- package/dist/shared/scopeOwner.cjs.map +0 -1
- package/dist/slash/chainExpression.cjs.map +0 -1
- package/dist/slash/chainExpression.mjs.map +0 -1
- package/dist/slash/doctor.cjs +0 -5
- package/dist/slash/doctor.mjs +0 -5
- package/dist/slash/doctor.mjs.map +0 -1
- package/dist/slash/slashCommands.cjs.map +0 -1
- package/dist/slash/slashCommands.mjs.map +0 -1
- package/dist/slash/spawnRequestMapping.cjs.map +0 -1
- package/dist/slash/spawnRequestMapping.mjs.map +0 -1
- package/dist/slash/subagentLaunch.cjs +0 -2
- package/dist/slash/subagentLaunch.cjs.map +0 -1
- package/dist/slash/subagentLaunch.mjs +0 -2
- package/dist/slash/subagentLaunch.mjs.map +0 -1
- package/dist/types.d.cts.map +0 -1
- package/dist/types.d.mts.map +0 -1
- /package/dist/{slash → commands/slash}/chainExpression.cjs +0 -0
- /package/dist/{slash → commands/slash}/chainExpression.mjs +0 -0
- /package/dist/{slash → commands/slash}/spawnRequestMapping.cjs +0 -0
- /package/dist/{slash → commands/slash}/spawnRequestMapping.mjs +0 -0
- /package/dist/{protocol → schemas/team}/delegation.cjs +0 -0
- /package/dist/{protocol → schemas/team}/delegation.mjs +0 -0
- /package/dist/{agents → services/agents}/frontmatter.cjs +0 -0
- /package/dist/{intercom → services/intercom}/intercomBridge.cjs +0 -0
- /package/dist/{intercom → services/intercom}/intercomBridge.mjs +0 -0
- /package/dist/{runs → services/runs}/background/completionBatcher.cjs +0 -0
- /package/dist/{runs → services/runs}/background/parentWatchdog.cjs +0 -0
- /package/dist/{runs → services/runs}/background/parentWatchdog.mjs +0 -0
- /package/dist/{runs → services/runs}/registry/processGroup.cjs +0 -0
- /package/dist/{runs → services/runs}/registry/processGroup.mjs +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleetTranscript.cjs","names":["fs","shortenPath","formatDuration","Markdown"],"sources":["../../src/tui/fleetTranscript.ts"],"sourcesContent":["/**\n * Reads and renders a run's child transcript for the fleet detail pane.\n *\n * NOT A PORT:\n * The predecessor's `fleetTranscript.ts` parsed the HOST agent's own session\n * JSONL. `childTranscript.ts` (`../shared/childTranscript.ts`) is a\n * from-scratch, write-only format this package invented for its own reasons\n * (see that module's header: it is the parent's independent record, not the\n * host's session file). Translating the predecessor's parser would build a\n * reader for a format nothing here writes, so this reads `childTranscript.ts`'s\n * actual record shapes (`message`/`tool_start`/`tool_end`/`stdout`/`stderr`/\n * `truncated`) directly.\n *\n * READ THE RECORD, NOT THE SUMMARY:\n * `childTranscript.ts` flattens each message to a `text` field with\n * `extractTextFromContent`, which recognises only `{type:'text'}`. An assistant\n * turn made of a `thinking` block plus `toolCall` blocks therefore flattens to\n * nothing, and the writer omits the field entirely - which is what produced\n * rows of bare `assistant` headers with no content under them. The writer does\n * still persist the whole `message.content` array verbatim, so this module\n * parses THAT: thinking becomes its own event, and a turn with no text emits no\n * assistant event at all. Same reasoning for `argsPayload` (full tool arguments,\n * written all along, previously unread) and for `role: 'toolResult'` records,\n * which used to be dropped so a reader could see what ran but never what came\n * back.\n *\n * INCREMENTAL BY CONSTRUCTION:\n * A transcript is append-only and can reach 50 MiB, while the fleet overlay\n * re-checks the selected run roughly once a second. Re-reading and re-parsing\n * the whole file on every change makes cost grow with run length, so the reader\n * is resumable (`readFleetTranscriptTail`): it reads only the bytes appended\n * since the last call, and the renderer keeps one rendered block per event so\n * an append re-renders only the new and mutated ones. Steady-state cost is\n * proportional to what arrived, not to what the file holds.\n *\n * DESIGN PATTERNS:\n * - Reading is tolerant, not strict: the writer appends lines as a run\n * progresses, so the last line on disk may be a partial write mid-flush.\n * A line that fails to parse is dropped rather than failing the whole read\n * - `tool_start`/`tool_end`/`toolResult` are folded into ONE `tool` event keyed\n * by `toolCallId`, so the pane reads as a sequence of turns rather than three\n * lines per call. Calls interleave and `tool_end` arrives BEFORE its\n * `toolResult`, so the fold map retains an entry for as long as its event is\n * retained rather than closing it out on `tool_end`\n * - A `tool_end` with no matching start (the start fell outside the retained\n * window) still renders on its own rather than being dropped, since it is\n * still evidence of what ran\n *\n * AVOID:\n * - Reusing a `FleetTranscriptTail` after passing it as `previous`. The reader\n * takes ownership of its `events` and fold map instead of copying them, which\n * is the whole point of resuming; the returned tail supersedes it\n */\n\nimport * as fs from 'node:fs';\n\nimport { Markdown, truncateToWidth, wrapTextWithAnsi, type MarkdownTheme } from '@earendil-works/pi-tui';\n\nimport { formatDuration, shortenPath } from '../shared/formatters.ts';\n\nexport type FleetTranscriptEventKind = 'user' | 'assistant' | 'thinking' | 'tool' | 'notice' | 'stdout' | 'stderr';\n\nexport interface FleetTranscriptEvent {\n kind: FleetTranscriptEventKind;\n at: number;\n text: string;\n /** Tool name, present only for `kind: 'tool'`. */\n name?: string;\n /** Present only for `kind: 'tool'`: `'running'` until a matching `tool_end` arrives. */\n status?: 'running' | 'ok' | 'error';\n /** Tool arguments parsed from `argsPayload`, when the writer recorded them. */\n args?: Record<string, unknown>;\n /** Tool output, folded in from the matching `toolResult` record. */\n result?: string;\n /** Set when the writer bounded `result` at its payload cap. */\n resultTruncated?: boolean;\n /** `tool_end` timestamp, for the call's duration. */\n endedAt?: number;\n /** Model that produced an assistant turn, when reported. */\n model?: string;\n}\n\nexport interface FleetTranscript {\n events: FleetTranscriptEvent[];\n warning?: string;\n}\n\n/** One retained tool event and where it sits in `events`, so a later fold can mark it dirty. */\ninterface FleetTranscriptToolEntry {\n event: FleetTranscriptEvent;\n index: number;\n}\n\n/**\n * A resume point for `readFleetTranscriptTail`.\n *\n * `byteOffset` is deliberately the end of the last COMPLETE line rather than\n * the file size: a partial trailing line is left unconsumed so the next read\n * picks it up whole, instead of being dropped as unparseable the way a\n * whole-file read has to.\n */\nexport interface FleetTranscriptTail extends FleetTranscript {\n path: string;\n byteOffset: number;\n /** Size observed at this read. A later shrink means the file was replaced, forcing a full re-read. */\n size: number;\n toolCalls: Map<string, FleetTranscriptToolEntry>;\n /** Earliest index whose content changed in this read. Undefined when only appends happened. */\n firstDirtyIndex: number | undefined;\n /** Cumulative events dropped from the front by the retention cap. */\n droppedEvents: number;\n}\n\n/**\n * Retention cap. Bounds memory and worst-case render cost independently of how\n * long a run goes on; `droppedEvents` keeps the fact that a window was applied\n * visible, so a tail is never mistaken for a whole run.\n */\nexport const MAX_RETAINED_EVENTS = 1000;\n\nconst NEWLINE_BYTE = 0x0a;\n\ninterface RawRecord {\n recordType?: string;\n ts?: number;\n role?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n isError?: boolean;\n argsPreview?: string;\n argsPayload?: string;\n outputTruncated?: boolean;\n model?: string;\n message?: { role?: string; content?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseLine(line: string): RawRecord | undefined {\n const trimmed = line.trim();\n if (!trimmed) return undefined;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n return isRecord(parsed) ? (parsed as RawRecord) : undefined;\n } catch {\n // The writer appends line-by-line; a trailing partial write on the last\n // line is expected while a run is in flight, not evidence of corruption.\n return undefined;\n }\n}\n\n/**\n * Split an assistant turn's raw content into its thinking and its prose.\n *\n * Returns undefined when the record carried no content array, so the caller can\n * fall back to the writer's flattened `text` rather than treating a shape this\n * does not recognise as an empty turn.\n */\nfunction splitAssistantContent(content: unknown): { thinking: string[]; text: string } | undefined {\n if (!Array.isArray(content)) return undefined;\n const thinking: string[] = [];\n const texts: string[] = [];\n for (const part of content) {\n if (!part || typeof part !== 'object') continue;\n const record = part as Record<string, unknown>;\n if (record.type === 'thinking') {\n const value = typeof record.thinking === 'string' ? record.thinking : '';\n if (value.trim()) thinking.push(value);\n continue;\n }\n // Anything else - toolCall in particular - is represented by its own event,\n // so it must not be folded into the assistant turn's text.\n if (record.type === 'text' && typeof record.text === 'string') texts.push(record.text);\n }\n return { thinking, text: texts.join('\\n') };\n}\n\nfunction parseArgsPayload(payload: string | undefined): Record<string, unknown> | undefined {\n if (!payload) return undefined;\n try {\n const parsed: unknown = JSON.parse(payload);\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n // The writer bounds this payload, so a long argument set can land here\n // truncated mid-JSON. The preview still renders; the args just stay absent.\n return undefined;\n }\n}\n\nfunction emptyTail(transcriptPath: string, warning?: string): FleetTranscriptTail {\n return {\n path: transcriptPath,\n events: [],\n byteOffset: 0,\n size: 0,\n toolCalls: new Map(),\n firstDirtyIndex: undefined,\n droppedEvents: 0,\n ...(warning ? { warning } : {}),\n };\n}\n\n/** Read exactly the appended range, so cost tracks what arrived rather than what the file holds. */\nfunction readRange(filePath: string, offset: number, length: number): Buffer {\n const handle = fs.openSync(filePath, 'r');\n try {\n const buffer = Buffer.allocUnsafe(length);\n let read = 0;\n while (read < length) {\n const chunk = fs.readSync(handle, buffer, read, length - read, offset + read);\n if (chunk <= 0) break;\n read += chunk;\n }\n return read === length ? buffer : buffer.subarray(0, read);\n } finally {\n fs.closeSync(handle);\n }\n}\n\nfunction markDirty(tail: FleetTranscriptTail, index: number): void {\n if (tail.firstDirtyIndex === undefined || index < tail.firstDirtyIndex) tail.firstDirtyIndex = index;\n}\n\nfunction pushEvent(tail: FleetTranscriptTail, event: FleetTranscriptEvent): number {\n tail.events.push(event);\n return tail.events.length - 1;\n}\n\n/** Apply the retention cap, keeping every index that survives it consistent. */\nfunction applyRetention(tail: FleetTranscriptTail): void {\n const excess = tail.events.length - MAX_RETAINED_EVENTS;\n if (excess <= 0) return;\n tail.events.splice(0, excess);\n tail.droppedEvents += excess;\n if (tail.firstDirtyIndex !== undefined) tail.firstDirtyIndex = Math.max(0, tail.firstDirtyIndex - excess);\n for (const [callId, entry] of tail.toolCalls) {\n const shifted = entry.index - excess;\n if (shifted < 0) tail.toolCalls.delete(callId);\n else entry.index = shifted;\n }\n}\n\nfunction consumeMessage(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.role === 'toolResult') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n // A result whose call fell outside the retained window has nothing to\n // attach to; the call itself is already gone, so the output would be\n // unattributable rather than useful.\n if (!entry) return;\n entry.event.result = record.text ?? '';\n entry.event.resultTruncated = record.outputTruncated === true;\n if (record.isError === true) entry.event.status = 'error';\n markDirty(tail, entry.index);\n return;\n }\n if (record.role === 'user') {\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'user', at, text });\n return;\n }\n if (record.role === 'assistant') {\n const parts = splitAssistantContent(record.message?.content);\n for (const thinking of parts?.thinking ?? []) pushEvent(tail, { kind: 'thinking', at, text: thinking });\n const text = parts ? parts.text : (record.text ?? '');\n // A turn that only thought and called tools has no prose of its own. Both\n // are already represented, so emitting a header for it says nothing.\n if (text.trim()) {\n pushEvent(tail, { kind: 'assistant', at, text, ...(record.model ? { model: record.model } : {}) });\n }\n return;\n }\n if (record.role === 'custom') {\n // Host-injected notices (skill suggestions, hook output) are part of what\n // the child was told, so they belong in the transcript rather than nowhere.\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'notice', at, text });\n }\n}\n\nfunction consumeRecord(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.recordType === 'message') return consumeMessage(tail, record);\n if (record.recordType === 'tool_start') {\n const args = parseArgsPayload(record.argsPayload);\n const event: FleetTranscriptEvent = {\n kind: 'tool',\n at,\n text: record.argsPreview ?? '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: 'running',\n ...(args ? { args } : {}),\n };\n const index = pushEvent(tail, event);\n if (record.toolCallId) tail.toolCalls.set(record.toolCallId, { event, index });\n return;\n }\n if (record.recordType === 'tool_end') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n if (entry) {\n // An error reported by either half of the call is the call's outcome.\n entry.event.status = record.isError === true || entry.event.status === 'error' ? 'error' : 'ok';\n entry.event.endedAt = at;\n markDirty(tail, entry.index);\n return;\n }\n pushEvent(tail, {\n kind: 'tool',\n at,\n text: '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: record.isError === true ? 'error' : 'ok',\n });\n return;\n }\n if (record.recordType === 'stdout' || record.recordType === 'stderr') {\n pushEvent(tail, { kind: record.recordType, at, text: record.text ?? '' });\n }\n // 'truncated' carries no displayable content of its own; the events\n // recorded before it already show what happened up to the cutoff.\n}\n\n/**\n * Read whatever has been appended since `previous`, or the whole file when no\n * resume point is given.\n *\n * Never throws: a missing or unreadable file is a normal state (the run has\n * not started writing yet, or was cleaned up), reported through `warning`\n * rather than an exception the caller would have to guard against.\n */\nexport function readFleetTranscriptTail(transcriptPath: string, previous?: FleetTranscriptTail): FleetTranscriptTail {\n let size: number;\n try {\n size = fs.statSync(transcriptPath).size;\n } catch (cause) {\n return emptyTail(transcriptPath, cause instanceof Error ? cause.message : String(cause));\n }\n\n // A shrink means the file was replaced rather than appended to, so nothing\n // parsed from the old one can be trusted to still describe it.\n const resumable =\n previous !== undefined &&\n previous.path === transcriptPath &&\n previous.warning === undefined &&\n size >= previous.byteOffset;\n\n const tail: FleetTranscriptTail = resumable\n ? { ...previous, size, firstDirtyIndex: undefined, warning: undefined }\n : emptyTail(transcriptPath);\n tail.size = size;\n if (size <= tail.byteOffset) return tail;\n\n let chunk: Buffer;\n try {\n chunk = readRange(transcriptPath, tail.byteOffset, size - tail.byteOffset);\n } catch (cause) {\n return { ...tail, warning: cause instanceof Error ? cause.message : String(cause) };\n }\n\n // Split at the last newline: 0x0A cannot occur inside a UTF-8 multi-byte\n // sequence, so this is a byte boundary that is also a character boundary.\n const lastNewline = chunk.lastIndexOf(NEWLINE_BYTE);\n const complete = lastNewline === -1 ? undefined : chunk.subarray(0, lastNewline + 1);\n const remainder = chunk.subarray(lastNewline + 1);\n\n // A trailing line with no newline after it is ambiguous: either a write\n // caught mid-flush, or simply the last record of a file the writer did not\n // terminate. Parsing decides - a half-written record cannot be valid JSON,\n // since it is missing its closing brace - so a complete record is never\n // withheld just because nothing followed it, and a partial one is left\n // unconsumed for the next read to pick up whole.\n const pending = remainder.length > 0 ? parseLine(remainder.toString('utf-8')) : undefined;\n tail.byteOffset += (complete?.length ?? 0) + (pending ? remainder.length : 0);\n if (!complete && !pending) return tail;\n\n for (const line of complete ? complete.toString('utf-8').split(/\\r?\\n/) : []) {\n const record = parseLine(line);\n if (record) consumeRecord(tail, record);\n }\n if (pending) consumeRecord(tail, pending);\n applyRetention(tail);\n return tail;\n}\n\n/** Whole-file read, for callers with no incremental state to keep. */\nexport function readFleetTranscript(transcriptPath: string): FleetTranscript {\n const tail = readFleetTranscriptTail(transcriptPath);\n return tail.warning ? { events: tail.events, warning: tail.warning } : { events: tail.events };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\nexport type FleetTranscriptVerbosity = 'compact' | 'full';\n\nexport interface FleetTranscriptTheme {\n fg(color: string, text: string): string;\n bold(text: string): string;\n}\n\n/**\n * What the renderer needs from a tail: the events, and the two facts that say\n * which rendered blocks are still valid. Deliberately narrower than\n * `FleetTranscriptTail` so rendering does not depend on the reader's file\n * bookkeeping.\n */\nexport type FleetTranscriptRenderable = Pick<FleetTranscriptTail, 'events' | 'firstDirtyIndex' | 'droppedEvents'>;\n\nexport interface FleetTranscriptRenderOptions {\n /** Run cwd, so absolute paths render as the part that actually differs between rows. */\n cwd?: string;\n verbosity: FleetTranscriptVerbosity;\n}\n\n/**\n * One rendered block per event, plus their flattened lines.\n *\n * Blocks are kept so an append re-renders only what changed: markdown is by far\n * the most expensive part of this module, and an assistant message's rendering\n * never changes once written.\n */\nexport interface FleetTranscriptRender {\n width: number;\n verbosity: FleetTranscriptVerbosity;\n cwd: string | undefined;\n droppedEvents: number;\n blocks: string[][];\n lines: string[];\n}\n\nconst COMPACT_LINES: Record<'user' | 'thinking' | 'notice' | 'result', number> = {\n user: 8,\n thinking: 6,\n notice: 3,\n result: 3,\n};\n/** Even expanded, one 32 KiB tool result must not be able to bury the pane. */\nconst FULL_RESULT_LINES = 200;\nconst RESULT_GUTTER = ' ⎿ ';\nconst RESULT_CONTINUATION = ' ';\n\n/** Keys that identify what a call actually did, ahead of the ones every call shares. */\nconst PRIMARY_ARG_KEYS = [\n 'command',\n 'pattern',\n 'query',\n 'queries',\n 'prompt',\n 'task',\n 'describe',\n 'workflow',\n 'url',\n 'urls',\n 'old_string',\n 'content',\n 'file_path',\n 'filePath',\n 'path',\n];\nconst PATH_ARG_KEYS = ['path', 'file_path', 'filePath'];\n\nfunction statusGlyph(status: FleetTranscriptEvent['status']): string {\n if (status === 'ok') return '✓';\n if (status === 'error') return '✗';\n return '…';\n}\n\nfunction statusColor(status: FleetTranscriptEvent['status']): string {\n if (status === 'error') return 'error';\n if (status === 'running') return 'accent';\n return 'muted';\n}\n\n/**\n * Render a path as the part that distinguishes it.\n *\n * Every row in a single run shares the run's cwd as a prefix, so keeping it\n * costs the width that would otherwise show which file was touched.\n */\nfunction displayPath(value: string, cwd?: string): string {\n if (cwd) {\n if (value === cwd) return '.';\n const prefix = cwd.endsWith('/') ? cwd : `${cwd}/`;\n if (value.startsWith(prefix)) return value.slice(prefix.length) || '.';\n }\n return shortenPath(value);\n}\n\nfunction argToDisplay(value: unknown, cwd?: string): string | undefined {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const collapsed = trimmed.replace(/\\s+/g, ' ');\n return collapsed.startsWith('/') ? displayPath(collapsed, cwd) : collapsed;\n }\n if (Array.isArray(value)) {\n const parts = value.map((entry) => argToDisplay(entry, cwd)).filter((entry): entry is string => Boolean(entry));\n return parts.length > 0 ? parts.join(', ') : undefined;\n }\n return undefined;\n}\n\n/**\n * The call's headline arguments.\n *\n * Prefers the distinguishing argument over the shared one, then appends the\n * path when it was not already the headline - `grep` reads as\n * `pattern · where`, `read` as just the file.\n */\nfunction toolArgsSummary(event: FleetTranscriptEvent, cwd?: string): string {\n const args = event.args;\n if (!args) return event.text ? displayPath(event.text, cwd) : '';\n let primaryKey: string | undefined;\n let primary: string | undefined;\n for (const key of PRIMARY_ARG_KEYS) {\n const display = argToDisplay(args[key], cwd);\n if (display) {\n primaryKey = key;\n primary = display;\n break;\n }\n }\n if (!primary) {\n for (const [key, value] of Object.entries(args)) {\n const display = argToDisplay(value, cwd);\n if (display) {\n primaryKey = key;\n primary = `${key}=${display}`;\n break;\n }\n }\n }\n if (!primary) return '';\n const pathKey = PATH_ARG_KEYS.find((key) => key !== primaryKey && typeof args[key] === 'string');\n const pathDisplay = pathKey ? argToDisplay(args[pathKey], cwd) : undefined;\n // A path that relativizes to the run's own cwd says nothing the pane does\n // not already know, and every such row would carry the same '.'.\n return pathDisplay && pathDisplay !== '.' ? `${primary} · ${pathDisplay}` : primary;\n}\n\n/** Wrap to `width`, capped, with the number of hidden lines stated rather than implied. */\nfunction cappedLines(text: string, width: number, limit: number, decorate: (line: string) => string): string[] {\n const wrapped = wrapTextWithAnsi(text, Math.max(1, width));\n if (wrapped.length <= limit) return wrapped.map(decorate);\n return [...wrapped.slice(0, limit).map(decorate), decorate(`… +${wrapped.length - limit} lines`)];\n}\n\nfunction renderToolEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const summary = toolArgsSummary(event, options.cwd);\n const duration = event.endedAt !== undefined ? formatDuration(Math.max(0, event.endedAt - event.at)) : undefined;\n const header = [\n theme.fg(statusColor(event.status), statusGlyph(event.status)),\n theme.bold(event.name ?? 'tool'),\n summary ? theme.fg('muted', summary) : undefined,\n duration ? theme.fg('dim', duration) : undefined,\n ]\n .filter((part): part is string => part !== undefined)\n .join(' ');\n const lines = [truncateToWidth(header, width)];\n\n if (options.verbosity === 'full' && event.args) {\n const pretty = JSON.stringify(event.args, null, 2);\n for (const line of pretty.split('\\n')) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}${line}`, width)));\n }\n }\n\n const result = event.result?.trimEnd();\n if (result) {\n const limit = options.verbosity === 'full' ? FULL_RESULT_LINES : COMPACT_LINES.result;\n const color = event.status === 'error' ? 'error' : 'dim';\n const body = result.split('\\n');\n const shown = body.slice(0, limit);\n shown.forEach((line, index) => {\n const prefix = index === 0 ? RESULT_GUTTER : RESULT_CONTINUATION;\n lines.push(theme.fg(color, truncateToWidth(`${prefix}${line}`, width)));\n });\n const hidden = body.length - shown.length;\n if (hidden > 0) lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}… +${hidden} lines`, width)));\n if (event.resultTruncated) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}(output truncated by the recorder)`, width)));\n }\n } else if (event.status === 'running') {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_GUTTER}running…`, width)));\n }\n return lines;\n}\n\nfunction renderEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const safeWidth = Math.max(1, width);\n if (event.kind === 'tool') return [...renderToolEvent(event, safeWidth, theme, options), ''];\n\n if (event.kind === 'assistant') {\n const header = theme.fg('accent', theme.bold('● assistant'));\n // Markdown is the expensive call in this module; the block cache is what\n // keeps it to once per message rather than once per repaint.\n const body = markdownTheme\n ? new Markdown(event.text, 0, 0, markdownTheme).render(safeWidth)\n : wrapTextWithAnsi(event.text, safeWidth);\n return [header, ...body, ''];\n }\n\n if (event.kind === 'thinking') {\n const header = theme.fg('dim', theme.bold('✻ thinking'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.thinking;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => theme.fg('dim', line)), ''];\n }\n\n if (event.kind === 'user') {\n const header = theme.fg('accent', theme.bold('▸ user'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.user;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => line), ''];\n }\n\n if (event.kind === 'notice') {\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.notice;\n return [...cappedLines(`ℹ ${event.text}`, safeWidth, limit, (line) => theme.fg('muted', line)), ''];\n }\n\n const prefix = event.kind === 'stderr' ? theme.fg('warning', 'stderr') : theme.fg('dim', 'stdout');\n return [truncateToWidth(`${prefix} ${event.text}`, safeWidth), ''];\n}\n\n/**\n * Render `tail`'s events, reusing `previous`'s blocks wherever nothing changed.\n *\n * A full render happens only when something global changed - width, verbosity,\n * cwd, or the absence of a usable previous render. An append re-renders from\n * the earliest dirty index onward and reuses every block before it.\n */\nexport function renderFleetTranscript(\n tail: FleetTranscriptRenderable,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n previous?: FleetTranscriptRender,\n): FleetTranscriptRender {\n const reusable =\n previous !== undefined &&\n previous.width === width &&\n previous.verbosity === options.verbosity &&\n previous.cwd === options.cwd &&\n previous.droppedEvents <= tail.droppedEvents;\n\n // Nothing changed at all: hand back the same render rather than rebuilding\n // a flat line array out of blocks that are all still valid. This is the\n // common case - every repaint driven by a keystroke or an unrelated run -\n // and without it the pane still pays a cost proportional to the whole\n // transcript on each one.\n if (\n reusable &&\n previous.droppedEvents === tail.droppedEvents &&\n tail.firstDirtyIndex === undefined &&\n previous.blocks.length === tail.events.length\n ) {\n return previous;\n }\n\n // Retention drops events off the front, so a reused block list has to be\n // realigned by however many went before it can be indexed alongside events.\n const base = reusable ? previous.blocks.slice(tail.droppedEvents - previous.droppedEvents) : [];\n const from = Math.min(tail.firstDirtyIndex ?? base.length, base.length, tail.events.length);\n const blocks = base.slice(0, from);\n for (let index = from; index < tail.events.length; index++) {\n blocks.push(renderEvent(tail.events[index], width, theme, markdownTheme, options));\n }\n\n const lines: string[] = [];\n if (tail.droppedEvents > 0) {\n lines.push(theme.fg('dim', truncateToWidth(`… ${tail.droppedEvents} earlier events not shown`, width)), '');\n }\n for (const block of blocks) lines.push(...block);\n return { width, verbosity: options.verbosity, cwd: options.cwd, droppedEvents: tail.droppedEvents, blocks, lines };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fleetTranscript.cjs","names":["fs","shortenPath","formatDuration","Markdown"],"sources":["../../src/tui/fleetTranscript.ts"],"sourcesContent":["/**\n * Reads and renders a run's child transcript for the fleet detail pane.\n *\n * NOT A PORT:\n * The predecessor's `fleetTranscript.ts` parsed the HOST agent's own session\n * JSONL. `childTranscript.ts` (`../shared/childTranscript.ts`) is a\n * from-scratch, write-only format this package invented for its own reasons\n * (see that module's header: it is the parent's independent record, not the\n * host's session file). Translating the predecessor's parser would build a\n * reader for a format nothing here writes, so this reads `childTranscript.ts`'s\n * actual record shapes (`message`/`tool_start`/`tool_end`/`stdout`/`stderr`/\n * `truncated`) directly.\n *\n * READ THE RECORD, NOT THE SUMMARY:\n * `childTranscript.ts` flattens each message to a `text` field with\n * `extractTextFromContent`, which recognises only `{type:'text'}`. An assistant\n * turn made of a `thinking` block plus `toolCall` blocks therefore flattens to\n * nothing, and the writer omits the field entirely - which is what produced\n * rows of bare `assistant` headers with no content under them. The writer does\n * still persist the whole `message.content` array verbatim, so this module\n * parses THAT: thinking becomes its own event, and a turn with no text emits no\n * assistant event at all. Same reasoning for `argsPayload` (full tool arguments,\n * written all along, previously unread) and for `role: 'toolResult'` records,\n * which used to be dropped so a reader could see what ran but never what came\n * back.\n *\n * INCREMENTAL BY CONSTRUCTION:\n * A transcript is append-only and can reach 50 MiB, while the fleet overlay\n * re-checks the selected run roughly once a second. Re-reading and re-parsing\n * the whole file on every change makes cost grow with run length, so the reader\n * is resumable (`readFleetTranscriptTail`): it reads only the bytes appended\n * since the last call, and the renderer keeps one rendered block per event so\n * an append re-renders only the new and mutated ones. Steady-state cost is\n * proportional to what arrived, not to what the file holds.\n *\n * DESIGN PATTERNS:\n * - Reading is tolerant, not strict: the writer appends lines as a run\n * progresses, so the last line on disk may be a partial write mid-flush.\n * A line that fails to parse is dropped rather than failing the whole read\n * - `tool_start`/`tool_end`/`toolResult` are folded into ONE `tool` event keyed\n * by `toolCallId`, so the pane reads as a sequence of turns rather than three\n * lines per call. Calls interleave and `tool_end` arrives BEFORE its\n * `toolResult`, so the fold map retains an entry for as long as its event is\n * retained rather than closing it out on `tool_end`\n * - A `tool_end` with no matching start (the start fell outside the retained\n * window) still renders on its own rather than being dropped, since it is\n * still evidence of what ran\n *\n * AVOID:\n * - Reusing a `FleetTranscriptTail` after passing it as `previous`. The reader\n * takes ownership of its `events` and fold map instead of copying them, which\n * is the whole point of resuming; the returned tail supersedes it\n */\n\nimport * as fs from 'node:fs';\n\nimport { Markdown, type MarkdownTheme, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';\n\nimport { formatDuration, shortenPath } from '../shared/formatters.ts';\n\nexport type FleetTranscriptEventKind = 'user' | 'assistant' | 'thinking' | 'tool' | 'notice' | 'stdout' | 'stderr';\n\nexport interface FleetTranscriptEvent {\n kind: FleetTranscriptEventKind;\n at: number;\n text: string;\n /** Tool name, present only for `kind: 'tool'`. */\n name?: string;\n /** Present only for `kind: 'tool'`: `'running'` until a matching `tool_end` arrives. */\n status?: 'running' | 'ok' | 'error';\n /** Tool arguments parsed from `argsPayload`, when the writer recorded them. */\n args?: Record<string, unknown>;\n /** Tool output, folded in from the matching `toolResult` record. */\n result?: string;\n /** Set when the writer bounded `result` at its payload cap. */\n resultTruncated?: boolean;\n /** `tool_end` timestamp, for the call's duration. */\n endedAt?: number;\n /** Model that produced an assistant turn, when reported. */\n model?: string;\n}\n\nexport interface FleetTranscript {\n events: FleetTranscriptEvent[];\n warning?: string;\n}\n\n/** One retained tool event and where it sits in `events`, so a later fold can mark it dirty. */\ninterface FleetTranscriptToolEntry {\n event: FleetTranscriptEvent;\n index: number;\n}\n\n/**\n * A resume point for `readFleetTranscriptTail`.\n *\n * `byteOffset` is deliberately the end of the last COMPLETE line rather than\n * the file size: a partial trailing line is left unconsumed so the next read\n * picks it up whole, instead of being dropped as unparseable the way a\n * whole-file read has to.\n */\nexport interface FleetTranscriptTail extends FleetTranscript {\n path: string;\n byteOffset: number;\n /** Size observed at this read. A later shrink means the file was replaced, forcing a full re-read. */\n size: number;\n toolCalls: Map<string, FleetTranscriptToolEntry>;\n /** Earliest index whose content changed in this read. Undefined when only appends happened. */\n firstDirtyIndex: number | undefined;\n /** Cumulative events dropped from the front by the retention cap. */\n droppedEvents: number;\n}\n\n/**\n * Retention cap. Bounds memory and worst-case render cost independently of how\n * long a run goes on; `droppedEvents` keeps the fact that a window was applied\n * visible, so a tail is never mistaken for a whole run.\n */\nexport const MAX_RETAINED_EVENTS = 1000;\n\nconst NEWLINE_BYTE = 0x0a;\n\ninterface RawRecord {\n recordType?: string;\n ts?: number;\n role?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n isError?: boolean;\n argsPreview?: string;\n argsPayload?: string;\n outputTruncated?: boolean;\n model?: string;\n message?: { role?: string; content?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseLine(line: string): RawRecord | undefined {\n const trimmed = line.trim();\n if (!trimmed) return undefined;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n return isRecord(parsed) ? (parsed as RawRecord) : undefined;\n } catch {\n // The writer appends line-by-line; a trailing partial write on the last\n // line is expected while a run is in flight, not evidence of corruption.\n return undefined;\n }\n}\n\n/**\n * Split an assistant turn's raw content into its thinking and its prose.\n *\n * Returns undefined when the record carried no content array, so the caller can\n * fall back to the writer's flattened `text` rather than treating a shape this\n * does not recognise as an empty turn.\n */\nfunction splitAssistantContent(content: unknown): { thinking: string[]; text: string } | undefined {\n if (!Array.isArray(content)) return undefined;\n const thinking: string[] = [];\n const texts: string[] = [];\n for (const part of content) {\n if (!part || typeof part !== 'object') continue;\n const record = part as Record<string, unknown>;\n if (record.type === 'thinking') {\n const value = typeof record.thinking === 'string' ? record.thinking : '';\n if (value.trim()) thinking.push(value);\n continue;\n }\n // Anything else - toolCall in particular - is represented by its own event,\n // so it must not be folded into the assistant turn's text.\n if (record.type === 'text' && typeof record.text === 'string') texts.push(record.text);\n }\n return { thinking, text: texts.join('\\n') };\n}\n\nfunction parseArgsPayload(payload: string | undefined): Record<string, unknown> | undefined {\n if (!payload) return undefined;\n try {\n const parsed: unknown = JSON.parse(payload);\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n // The writer bounds this payload, so a long argument set can land here\n // truncated mid-JSON. The preview still renders; the args just stay absent.\n return undefined;\n }\n}\n\nfunction emptyTail(transcriptPath: string, warning?: string): FleetTranscriptTail {\n return {\n path: transcriptPath,\n events: [],\n byteOffset: 0,\n size: 0,\n toolCalls: new Map(),\n firstDirtyIndex: undefined,\n droppedEvents: 0,\n ...(warning ? { warning } : {}),\n };\n}\n\n/** Read exactly the appended range, so cost tracks what arrived rather than what the file holds. */\nfunction readRange(filePath: string, offset: number, length: number): Buffer {\n const handle = fs.openSync(filePath, 'r');\n try {\n const buffer = Buffer.allocUnsafe(length);\n let read = 0;\n while (read < length) {\n const chunk = fs.readSync(handle, buffer, read, length - read, offset + read);\n if (chunk <= 0) break;\n read += chunk;\n }\n return read === length ? buffer : buffer.subarray(0, read);\n } finally {\n fs.closeSync(handle);\n }\n}\n\nfunction markDirty(tail: FleetTranscriptTail, index: number): void {\n if (tail.firstDirtyIndex === undefined || index < tail.firstDirtyIndex) tail.firstDirtyIndex = index;\n}\n\nfunction pushEvent(tail: FleetTranscriptTail, event: FleetTranscriptEvent): number {\n tail.events.push(event);\n return tail.events.length - 1;\n}\n\n/** Apply the retention cap, keeping every index that survives it consistent. */\nfunction applyRetention(tail: FleetTranscriptTail): void {\n const excess = tail.events.length - MAX_RETAINED_EVENTS;\n if (excess <= 0) return;\n tail.events.splice(0, excess);\n tail.droppedEvents += excess;\n if (tail.firstDirtyIndex !== undefined) tail.firstDirtyIndex = Math.max(0, tail.firstDirtyIndex - excess);\n for (const [callId, entry] of tail.toolCalls) {\n const shifted = entry.index - excess;\n if (shifted < 0) tail.toolCalls.delete(callId);\n else entry.index = shifted;\n }\n}\n\nfunction consumeMessage(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.role === 'toolResult') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n // A result whose call fell outside the retained window has nothing to\n // attach to; the call itself is already gone, so the output would be\n // unattributable rather than useful.\n if (!entry) return;\n entry.event.result = record.text ?? '';\n entry.event.resultTruncated = record.outputTruncated === true;\n if (record.isError === true) entry.event.status = 'error';\n markDirty(tail, entry.index);\n return;\n }\n if (record.role === 'user') {\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'user', at, text });\n return;\n }\n if (record.role === 'assistant') {\n const parts = splitAssistantContent(record.message?.content);\n for (const thinking of parts?.thinking ?? []) pushEvent(tail, { kind: 'thinking', at, text: thinking });\n const text = parts ? parts.text : (record.text ?? '');\n // A turn that only thought and called tools has no prose of its own. Both\n // are already represented, so emitting a header for it says nothing.\n if (text.trim()) {\n pushEvent(tail, { kind: 'assistant', at, text, ...(record.model ? { model: record.model } : {}) });\n }\n return;\n }\n if (record.role === 'custom') {\n // Host-injected notices (skill suggestions, hook output) are part of what\n // the child was told, so they belong in the transcript rather than nowhere.\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'notice', at, text });\n }\n}\n\nfunction consumeRecord(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.recordType === 'message') return consumeMessage(tail, record);\n if (record.recordType === 'tool_start') {\n const args = parseArgsPayload(record.argsPayload);\n const event: FleetTranscriptEvent = {\n kind: 'tool',\n at,\n text: record.argsPreview ?? '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: 'running',\n ...(args ? { args } : {}),\n };\n const index = pushEvent(tail, event);\n if (record.toolCallId) tail.toolCalls.set(record.toolCallId, { event, index });\n return;\n }\n if (record.recordType === 'tool_end') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n if (entry) {\n // An error reported by either half of the call is the call's outcome.\n entry.event.status = record.isError === true || entry.event.status === 'error' ? 'error' : 'ok';\n entry.event.endedAt = at;\n markDirty(tail, entry.index);\n return;\n }\n pushEvent(tail, {\n kind: 'tool',\n at,\n text: '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: record.isError === true ? 'error' : 'ok',\n });\n return;\n }\n if (record.recordType === 'stdout' || record.recordType === 'stderr') {\n pushEvent(tail, { kind: record.recordType, at, text: record.text ?? '' });\n }\n // 'truncated' carries no displayable content of its own; the events\n // recorded before it already show what happened up to the cutoff.\n}\n\n/**\n * Read whatever has been appended since `previous`, or the whole file when no\n * resume point is given.\n *\n * Never throws: a missing or unreadable file is a normal state (the run has\n * not started writing yet, or was cleaned up), reported through `warning`\n * rather than an exception the caller would have to guard against.\n */\nexport function readFleetTranscriptTail(transcriptPath: string, previous?: FleetTranscriptTail): FleetTranscriptTail {\n let size: number;\n try {\n size = fs.statSync(transcriptPath).size;\n } catch (cause) {\n return emptyTail(transcriptPath, cause instanceof Error ? cause.message : String(cause));\n }\n\n // A shrink means the file was replaced rather than appended to, so nothing\n // parsed from the old one can be trusted to still describe it.\n const resumable =\n previous !== undefined &&\n previous.path === transcriptPath &&\n previous.warning === undefined &&\n size >= previous.byteOffset;\n\n const tail: FleetTranscriptTail = resumable\n ? { ...previous, size, firstDirtyIndex: undefined, warning: undefined }\n : emptyTail(transcriptPath);\n tail.size = size;\n if (size <= tail.byteOffset) return tail;\n\n let chunk: Buffer;\n try {\n chunk = readRange(transcriptPath, tail.byteOffset, size - tail.byteOffset);\n } catch (cause) {\n return { ...tail, warning: cause instanceof Error ? cause.message : String(cause) };\n }\n\n // Split at the last newline: 0x0A cannot occur inside a UTF-8 multi-byte\n // sequence, so this is a byte boundary that is also a character boundary.\n const lastNewline = chunk.lastIndexOf(NEWLINE_BYTE);\n const complete = lastNewline === -1 ? undefined : chunk.subarray(0, lastNewline + 1);\n const remainder = chunk.subarray(lastNewline + 1);\n\n // A trailing line with no newline after it is ambiguous: either a write\n // caught mid-flush, or simply the last record of a file the writer did not\n // terminate. Parsing decides - a half-written record cannot be valid JSON,\n // since it is missing its closing brace - so a complete record is never\n // withheld just because nothing followed it, and a partial one is left\n // unconsumed for the next read to pick up whole.\n const pending = remainder.length > 0 ? parseLine(remainder.toString('utf-8')) : undefined;\n tail.byteOffset += (complete?.length ?? 0) + (pending ? remainder.length : 0);\n if (!complete && !pending) return tail;\n\n for (const line of complete ? complete.toString('utf-8').split(/\\r?\\n/) : []) {\n const record = parseLine(line);\n if (record) consumeRecord(tail, record);\n }\n if (pending) consumeRecord(tail, pending);\n applyRetention(tail);\n return tail;\n}\n\n/** Whole-file read, for callers with no incremental state to keep. */\nexport function readFleetTranscript(transcriptPath: string): FleetTranscript {\n const tail = readFleetTranscriptTail(transcriptPath);\n return tail.warning ? { events: tail.events, warning: tail.warning } : { events: tail.events };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\nexport type FleetTranscriptVerbosity = 'compact' | 'full';\n\nexport interface FleetTranscriptTheme {\n fg(color: string, text: string): string;\n bold(text: string): string;\n}\n\n/**\n * What the renderer needs from a tail: the events, and the two facts that say\n * which rendered blocks are still valid. Deliberately narrower than\n * `FleetTranscriptTail` so rendering does not depend on the reader's file\n * bookkeeping.\n */\nexport type FleetTranscriptRenderable = Pick<FleetTranscriptTail, 'events' | 'firstDirtyIndex' | 'droppedEvents'>;\n\nexport interface FleetTranscriptRenderOptions {\n /** Run cwd, so absolute paths render as the part that actually differs between rows. */\n cwd?: string;\n verbosity: FleetTranscriptVerbosity;\n}\n\n/**\n * One rendered block per event, plus their flattened lines.\n *\n * Blocks are kept so an append re-renders only what changed: markdown is by far\n * the most expensive part of this module, and an assistant message's rendering\n * never changes once written.\n */\nexport interface FleetTranscriptRender {\n width: number;\n verbosity: FleetTranscriptVerbosity;\n cwd: string | undefined;\n droppedEvents: number;\n blocks: string[][];\n lines: string[];\n}\n\nconst COMPACT_LINES: Record<'user' | 'thinking' | 'notice' | 'result', number> = {\n user: 8,\n thinking: 6,\n notice: 3,\n result: 3,\n};\n/** Even expanded, one 32 KiB tool result must not be able to bury the pane. */\nconst FULL_RESULT_LINES = 200;\nconst RESULT_GUTTER = ' ⎿ ';\nconst RESULT_CONTINUATION = ' ';\n\n/** Keys that identify what a call actually did, ahead of the ones every call shares. */\nconst PRIMARY_ARG_KEYS = [\n 'command',\n 'pattern',\n 'query',\n 'queries',\n 'prompt',\n 'task',\n 'describe',\n 'workflow',\n 'url',\n 'urls',\n 'old_string',\n 'content',\n 'file_path',\n 'filePath',\n 'path',\n];\nconst PATH_ARG_KEYS = ['path', 'file_path', 'filePath'];\n\nfunction statusGlyph(status: FleetTranscriptEvent['status']): string {\n if (status === 'ok') return '✓';\n if (status === 'error') return '✗';\n return '…';\n}\n\nfunction statusColor(status: FleetTranscriptEvent['status']): string {\n if (status === 'error') return 'error';\n if (status === 'running') return 'accent';\n return 'success';\n}\n\n/**\n * Render a path as the part that distinguishes it.\n *\n * Every row in a single run shares the run's cwd as a prefix, so keeping it\n * costs the width that would otherwise show which file was touched.\n */\nfunction displayPath(value: string, cwd?: string): string {\n if (cwd) {\n if (value === cwd) return '.';\n const prefix = cwd.endsWith('/') ? cwd : `${cwd}/`;\n if (value.startsWith(prefix)) return value.slice(prefix.length) || '.';\n }\n return shortenPath(value);\n}\n\nfunction argToDisplay(value: unknown, cwd?: string): string | undefined {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const collapsed = trimmed.replace(/\\s+/g, ' ');\n return collapsed.startsWith('/') ? displayPath(collapsed, cwd) : collapsed;\n }\n if (Array.isArray(value)) {\n const parts = value.map((entry) => argToDisplay(entry, cwd)).filter((entry): entry is string => Boolean(entry));\n return parts.length > 0 ? parts.join(', ') : undefined;\n }\n return undefined;\n}\n\n/**\n * The call's headline arguments.\n *\n * Prefers the distinguishing argument over the shared one, then appends the\n * path when it was not already the headline - `grep` reads as\n * `pattern · where`, `read` as just the file.\n */\nfunction toolArgsSummary(event: FleetTranscriptEvent, cwd?: string): string {\n const args = event.args;\n if (!args) return event.text ? displayPath(event.text, cwd) : '';\n let primaryKey: string | undefined;\n let primary: string | undefined;\n for (const key of PRIMARY_ARG_KEYS) {\n const display = argToDisplay(args[key], cwd);\n if (display) {\n primaryKey = key;\n primary = display;\n break;\n }\n }\n if (!primary) {\n for (const [key, value] of Object.entries(args)) {\n const display = argToDisplay(value, cwd);\n if (display) {\n primaryKey = key;\n primary = `${key}=${display}`;\n break;\n }\n }\n }\n if (!primary) return '';\n const pathKey = PATH_ARG_KEYS.find((key) => key !== primaryKey && typeof args[key] === 'string');\n const pathDisplay = pathKey ? argToDisplay(args[pathKey], cwd) : undefined;\n // A path that relativizes to the run's own cwd says nothing the pane does\n // not already know, and every such row would carry the same '.'.\n return pathDisplay && pathDisplay !== '.' ? `${primary} · ${pathDisplay}` : primary;\n}\n\n/** Wrap to `width`, capped, with the number of hidden lines stated rather than implied. */\nfunction cappedLines(text: string, width: number, limit: number, decorate: (line: string) => string): string[] {\n const wrapped = wrapTextWithAnsi(text, Math.max(1, width));\n if (wrapped.length <= limit) return wrapped.map(decorate);\n return [...wrapped.slice(0, limit).map(decorate), decorate(`… +${wrapped.length - limit} lines`)];\n}\n\nfunction renderToolEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const summary = toolArgsSummary(event, options.cwd);\n const duration = event.endedAt !== undefined ? formatDuration(Math.max(0, event.endedAt - event.at)) : undefined;\n const header = [\n theme.fg(statusColor(event.status), statusGlyph(event.status)),\n theme.bold(event.name ?? 'tool'),\n summary ? theme.fg('muted', summary) : undefined,\n duration ? theme.fg('dim', duration) : undefined,\n ]\n .filter((part): part is string => part !== undefined)\n .join(' ');\n const lines = [truncateToWidth(header, width)];\n\n if (options.verbosity === 'full' && event.args) {\n const pretty = JSON.stringify(event.args, null, 2);\n for (const line of pretty.split('\\n')) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}${line}`, width)));\n }\n }\n\n const result = event.result?.trimEnd();\n if (result) {\n const limit = options.verbosity === 'full' ? FULL_RESULT_LINES : COMPACT_LINES.result;\n const color = event.status === 'error' ? 'error' : 'dim';\n const body = result.split('\\n');\n const shown = body.slice(0, limit);\n shown.forEach((line, index) => {\n const prefix = index === 0 ? RESULT_GUTTER : RESULT_CONTINUATION;\n lines.push(theme.fg(color, truncateToWidth(`${prefix}${line}`, width)));\n });\n const hidden = body.length - shown.length;\n if (hidden > 0) lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}… +${hidden} lines`, width)));\n if (event.resultTruncated) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}(output truncated by the recorder)`, width)));\n }\n } else if (event.status === 'running') {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_GUTTER}running…`, width)));\n }\n return lines;\n}\n\nfunction renderEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const safeWidth = Math.max(1, width);\n if (event.kind === 'tool') return [...renderToolEvent(event, safeWidth, theme, options), ''];\n\n if (event.kind === 'assistant') {\n const header = theme.fg('accent', theme.bold('● assistant'));\n // Markdown is the expensive call in this module; the block cache is what\n // keeps it to once per message rather than once per repaint.\n const body = markdownTheme\n ? new Markdown(event.text, 0, 0, markdownTheme).render(safeWidth)\n : wrapTextWithAnsi(event.text, safeWidth);\n return [header, ...body, ''];\n }\n\n if (event.kind === 'thinking') {\n const header = theme.fg('dim', theme.bold('✻ thinking'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.thinking;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => theme.fg('dim', line)), ''];\n }\n\n if (event.kind === 'user') {\n const header = theme.fg('accent', theme.bold('▸ user'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.user;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => line), ''];\n }\n\n if (event.kind === 'notice') {\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.notice;\n return [...cappedLines(`ℹ ${event.text}`, safeWidth, limit, (line) => theme.fg('muted', line)), ''];\n }\n\n const prefix = event.kind === 'stderr' ? theme.fg('warning', 'stderr') : theme.fg('dim', 'stdout');\n return [truncateToWidth(`${prefix} ${event.text}`, safeWidth), ''];\n}\n\n/**\n * Render `tail`'s events, reusing `previous`'s blocks wherever nothing changed.\n *\n * A full render happens only when something global changed - width, verbosity,\n * cwd, or the absence of a usable previous render. An append re-renders from\n * the earliest dirty index onward and reuses every block before it.\n */\nexport function renderFleetTranscript(\n tail: FleetTranscriptRenderable,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n previous?: FleetTranscriptRender,\n): FleetTranscriptRender {\n const reusable =\n previous !== undefined &&\n previous.width === width &&\n previous.verbosity === options.verbosity &&\n previous.cwd === options.cwd &&\n previous.droppedEvents <= tail.droppedEvents;\n\n // Nothing changed at all: hand back the same render rather than rebuilding\n // a flat line array out of blocks that are all still valid. This is the\n // common case - every repaint driven by a keystroke or an unrelated run -\n // and without it the pane still pays a cost proportional to the whole\n // transcript on each one.\n if (\n reusable &&\n previous.droppedEvents === tail.droppedEvents &&\n tail.firstDirtyIndex === undefined &&\n previous.blocks.length === tail.events.length\n ) {\n return previous;\n }\n\n // Retention drops events off the front, so a reused block list has to be\n // realigned by however many went before it can be indexed alongside events.\n const base = reusable ? previous.blocks.slice(tail.droppedEvents - previous.droppedEvents) : [];\n const from = Math.min(tail.firstDirtyIndex ?? base.length, base.length, tail.events.length);\n const blocks = base.slice(0, from);\n for (let index = from; index < tail.events.length; index++) {\n blocks.push(renderEvent(tail.events[index], width, theme, markdownTheme, options));\n }\n\n const lines: string[] = [];\n if (tail.droppedEvents > 0) {\n lines.push(theme.fg('dim', truncateToWidth(`… ${tail.droppedEvents} earlier events not shown`, width)), '');\n }\n for (const block of blocks) lines.push(...block);\n return { width, verbosity: options.verbosity, cwd: options.cwd, droppedEvents: tail.droppedEvents, blocks, lines };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{formatDuration as t,init_formatters as n,shortenPath as r}from"../shared/formatters.mjs";import*as i from"node:fs";import{Markdown as a,truncateToWidth as o,wrapTextWithAnsi as s}from"@earendil-works/pi-tui";function c(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function l(e){let t=e.trim();if(t)try{let e=JSON.parse(t);return c(e)?e:void 0}catch{return}}function u(e){if(!Array.isArray(e))return;let t=[],n=[];for(let r of e){if(!r||typeof r!=`object`)continue;let e=r;if(e.type===`thinking`){let n=typeof e.thinking==`string`?e.thinking:``;n.trim()&&t.push(n);continue}e.type===`text`&&typeof e.text==`string`&&n.push(e.text)}return{thinking:t,text:n.join(`
|
|
2
|
-
`)}}function d(e){if(e)try{let t=JSON.parse(e);return c(t)?t:void 0}catch{return}}function f(e,t){return{path:e,events:[],byteOffset:0,size:0,toolCalls:new Map,firstDirtyIndex:void 0,droppedEvents:0,...t?{warning:t}:{}}}function p(e,t,n){let r=i.openSync(e,`r`);try{let e=Buffer.allocUnsafe(n),a=0;for(;a<n;){let o=i.readSync(r,e,a,n-a,t+a);if(o<=0)break;a+=o}return a===n?e:e.subarray(0,a)}finally{i.closeSync(r)}}function m(e,t){(e.firstDirtyIndex===void 0||t<e.firstDirtyIndex)&&(e.firstDirtyIndex=t)}function h(e,t){return e.events.push(t),e.events.length-1}function g(e){let t=e.events.length-A;if(!(t<=0)){e.events.splice(0,t),e.droppedEvents+=t,e.firstDirtyIndex!==void 0&&(e.firstDirtyIndex=Math.max(0,e.firstDirtyIndex-t));for(let[n,r]of e.toolCalls){let i=r.index-t;i<0?e.toolCalls.delete(n):r.index=i}}}function _(e,t){let n=t.ts??0;if(t.role===`toolResult`){let n=t.toolCallId?e.toolCalls.get(t.toolCallId):void 0;if(!n)return;n.event.result=t.text??``,n.event.resultTruncated=t.outputTruncated===!0,t.isError===!0&&(n.event.status=`error`),m(e,n.index);return}if(t.role===`user`){let r=t.text??``;r.trim()&&h(e,{kind:`user`,at:n,text:r});return}if(t.role===`assistant`){let r=u(t.message?.content);for(let t of r?.thinking??[])h(e,{kind:`thinking`,at:n,text:t});let i=r?r.text:t.text??``;i.trim()&&h(e,{kind:`assistant`,at:n,text:i,...t.model?{model:t.model}:{}});return}if(t.role===`custom`){let r=t.text??``;r.trim()&&h(e,{kind:`notice`,at:n,text:r})}}function v(e,t){let n=t.ts??0;if(t.recordType===`message`)return _(e,t);if(t.recordType===`tool_start`){let r=d(t.argsPayload),i={kind:`tool`,at:n,text:t.argsPreview??``,...t.toolName?{name:t.toolName}:{},status:`running`,...r?{args:r}:{}},a=h(e,i);t.toolCallId&&e.toolCalls.set(t.toolCallId,{event:i,index:a});return}if(t.recordType===`tool_end`){let r=t.toolCallId?e.toolCalls.get(t.toolCallId):void 0;if(r){r.event.status=t.isError===!0||r.event.status===`error`?`error`:`ok`,r.event.endedAt=n,m(e,r.index);return}h(e,{kind:`tool`,at:n,text:``,...t.toolName?{name:t.toolName}:{},status:t.isError===!0?`error`:`ok`});return}(t.recordType===`stdout`||t.recordType===`stderr`)&&h(e,{kind:t.recordType,at:n,text:t.text??``})}function y(e,t){let n;try{n=i.statSync(e).size}catch(t){return f(e,t instanceof Error?t.message:String(t))}let r=t!==void 0&&t.path===e&&t.warning===void 0&&n>=t.byteOffset?{...t,size:n,firstDirtyIndex:void 0,warning:void 0}:f(e);if(r.size=n,n<=r.byteOffset)return r;let a;try{a=p(e,r.byteOffset,n-r.byteOffset)}catch(e){return{...r,warning:e instanceof Error?e.message:String(e)}}let o=a.lastIndexOf(j),s=o===-1?void 0:a.subarray(0,o+1),c=a.subarray(o+1),u=c.length>0?l(c.toString(`utf-8`)):void 0;if(r.byteOffset+=(s?.length??0)+(u?c.length:0),!s&&!u)return r;for(let e of s?s.toString(`utf-8`).split(/\r?\n/):[]){let t=l(e);t&&v(r,t)}return u&&v(r,u),g(r),r}function b(e){let t=y(e);return t.warning?{events:t.events,warning:t.warning}:{events:t.events}}function x(e){return e===`ok`?`✓`:e===`error`?`✗`:`…`}function S(e){return e===`error`?`error`:e===`running`?`accent`:`
|
|
2
|
+
`)}}function d(e){if(e)try{let t=JSON.parse(e);return c(t)?t:void 0}catch{return}}function f(e,t){return{path:e,events:[],byteOffset:0,size:0,toolCalls:new Map,firstDirtyIndex:void 0,droppedEvents:0,...t?{warning:t}:{}}}function p(e,t,n){let r=i.openSync(e,`r`);try{let e=Buffer.allocUnsafe(n),a=0;for(;a<n;){let o=i.readSync(r,e,a,n-a,t+a);if(o<=0)break;a+=o}return a===n?e:e.subarray(0,a)}finally{i.closeSync(r)}}function m(e,t){(e.firstDirtyIndex===void 0||t<e.firstDirtyIndex)&&(e.firstDirtyIndex=t)}function h(e,t){return e.events.push(t),e.events.length-1}function g(e){let t=e.events.length-A;if(!(t<=0)){e.events.splice(0,t),e.droppedEvents+=t,e.firstDirtyIndex!==void 0&&(e.firstDirtyIndex=Math.max(0,e.firstDirtyIndex-t));for(let[n,r]of e.toolCalls){let i=r.index-t;i<0?e.toolCalls.delete(n):r.index=i}}}function _(e,t){let n=t.ts??0;if(t.role===`toolResult`){let n=t.toolCallId?e.toolCalls.get(t.toolCallId):void 0;if(!n)return;n.event.result=t.text??``,n.event.resultTruncated=t.outputTruncated===!0,t.isError===!0&&(n.event.status=`error`),m(e,n.index);return}if(t.role===`user`){let r=t.text??``;r.trim()&&h(e,{kind:`user`,at:n,text:r});return}if(t.role===`assistant`){let r=u(t.message?.content);for(let t of r?.thinking??[])h(e,{kind:`thinking`,at:n,text:t});let i=r?r.text:t.text??``;i.trim()&&h(e,{kind:`assistant`,at:n,text:i,...t.model?{model:t.model}:{}});return}if(t.role===`custom`){let r=t.text??``;r.trim()&&h(e,{kind:`notice`,at:n,text:r})}}function v(e,t){let n=t.ts??0;if(t.recordType===`message`)return _(e,t);if(t.recordType===`tool_start`){let r=d(t.argsPayload),i={kind:`tool`,at:n,text:t.argsPreview??``,...t.toolName?{name:t.toolName}:{},status:`running`,...r?{args:r}:{}},a=h(e,i);t.toolCallId&&e.toolCalls.set(t.toolCallId,{event:i,index:a});return}if(t.recordType===`tool_end`){let r=t.toolCallId?e.toolCalls.get(t.toolCallId):void 0;if(r){r.event.status=t.isError===!0||r.event.status===`error`?`error`:`ok`,r.event.endedAt=n,m(e,r.index);return}h(e,{kind:`tool`,at:n,text:``,...t.toolName?{name:t.toolName}:{},status:t.isError===!0?`error`:`ok`});return}(t.recordType===`stdout`||t.recordType===`stderr`)&&h(e,{kind:t.recordType,at:n,text:t.text??``})}function y(e,t){let n;try{n=i.statSync(e).size}catch(t){return f(e,t instanceof Error?t.message:String(t))}let r=t!==void 0&&t.path===e&&t.warning===void 0&&n>=t.byteOffset?{...t,size:n,firstDirtyIndex:void 0,warning:void 0}:f(e);if(r.size=n,n<=r.byteOffset)return r;let a;try{a=p(e,r.byteOffset,n-r.byteOffset)}catch(e){return{...r,warning:e instanceof Error?e.message:String(e)}}let o=a.lastIndexOf(j),s=o===-1?void 0:a.subarray(0,o+1),c=a.subarray(o+1),u=c.length>0?l(c.toString(`utf-8`)):void 0;if(r.byteOffset+=(s?.length??0)+(u?c.length:0),!s&&!u)return r;for(let e of s?s.toString(`utf-8`).split(/\r?\n/):[]){let t=l(e);t&&v(r,t)}return u&&v(r,u),g(r),r}function b(e){let t=y(e);return t.warning?{events:t.events,warning:t.warning}:{events:t.events}}function x(e){return e===`ok`?`✓`:e===`error`?`✗`:`…`}function S(e){return e===`error`?`error`:e===`running`?`accent`:`success`}function C(e,t){if(t){if(e===t)return`.`;let n=t.endsWith(`/`)?t:`${t}/`;if(e.startsWith(n))return e.slice(n.length)||`.`}return r(e)}function w(e,t){if(typeof e==`string`){let n=e.trim();if(!n)return;let r=n.replace(/\s+/g,` `);return r.startsWith(`/`)?C(r,t):r}if(Array.isArray(e)){let n=e.map(e=>w(e,t)).filter(e=>!!e);return n.length>0?n.join(`, `):void 0}}function T(e,t){let n=e.args;if(!n)return e.text?C(e.text,t):``;let r,i;for(let e of I){let a=w(n[e],t);if(a){r=e,i=a;break}}if(!i)for(let[e,a]of Object.entries(n)){let n=w(a,t);if(n){r=e,i=`${e}=${n}`;break}}if(!i)return``;let a=L.find(e=>e!==r&&typeof n[e]==`string`),o=a?w(n[a],t):void 0;return o&&o!==`.`?`${i} · ${o}`:i}function E(e,t,n,r){let i=s(e,Math.max(1,t));return i.length<=n?i.map(r):[...i.slice(0,n).map(r),r(`… +${i.length-n} lines`)]}function D(e,n,r,i){let a=T(e,i.cwd),s=e.endedAt===void 0?void 0:t(Math.max(0,e.endedAt-e.at)),c=[o([r.fg(S(e.status),x(e.status)),r.bold(e.name??`tool`),a?r.fg(`muted`,a):void 0,s?r.fg(`dim`,s):void 0].filter(e=>e!==void 0).join(` `),n)];if(i.verbosity===`full`&&e.args){let t=JSON.stringify(e.args,null,2);for(let e of t.split(`
|
|
3
3
|
`))c.push(r.fg(`dim`,o(`${F}${e}`,n)))}let l=e.result?.trimEnd();if(l){let t=i.verbosity===`full`?N:M.result,a=e.status===`error`?`error`:`dim`,s=l.split(`
|
|
4
4
|
`),u=s.slice(0,t);u.forEach((e,t)=>{let i=t===0?P:F;c.push(r.fg(a,o(`${i}${e}`,n)))});let d=s.length-u.length;d>0&&c.push(r.fg(`dim`,o(`${F}… +${d} lines`,n))),e.resultTruncated&&c.push(r.fg(`dim`,o(`${F}(output truncated by the recorder)`,n)))}else e.status===`running`&&c.push(r.fg(`dim`,o(`${P}running…`,n)));return c}function O(e,t,n,r,i){let c=Math.max(1,t);if(e.kind===`tool`)return[...D(e,c,n,i),``];if(e.kind===`assistant`)return[n.fg(`accent`,n.bold(`● assistant`)),...r?new a(e.text,0,0,r).render(c):s(e.text,c),``];if(e.kind===`thinking`){let t=n.fg(`dim`,n.bold(`✻ thinking`)),r=i.verbosity===`full`?1/0:M.thinking;return[t,...E(e.text,c,r,e=>n.fg(`dim`,e)),``]}if(e.kind===`user`){let t=n.fg(`accent`,n.bold(`▸ user`)),r=i.verbosity===`full`?1/0:M.user;return[t,...E(e.text,c,r,e=>e),``]}if(e.kind===`notice`){let t=i.verbosity===`full`?1/0:M.notice;return[...E(`ℹ ${e.text}`,c,t,e=>n.fg(`muted`,e)),``]}return[o(`${e.kind===`stderr`?n.fg(`warning`,`stderr`):n.fg(`dim`,`stdout`)} ${e.text}`,c),``]}function k(e,t,n,r,i,a){let s=a!==void 0&&a.width===t&&a.verbosity===i.verbosity&&a.cwd===i.cwd&&a.droppedEvents<=e.droppedEvents;if(s&&a.droppedEvents===e.droppedEvents&&e.firstDirtyIndex===void 0&&a.blocks.length===e.events.length)return a;let c=s?a.blocks.slice(e.droppedEvents-a.droppedEvents):[],l=Math.min(e.firstDirtyIndex??c.length,c.length,e.events.length),u=c.slice(0,l);for(let a=l;a<e.events.length;a++)u.push(O(e.events[a],t,n,r,i));let d=[];e.droppedEvents>0&&d.push(n.fg(`dim`,o(`… ${e.droppedEvents} earlier events not shown`,t)),``);for(let e of u)d.push(...e);return{width:t,verbosity:i.verbosity,cwd:i.cwd,droppedEvents:e.droppedEvents,blocks:u,lines:d}}var A,j,M,N,P,F,I,L,R=e((()=>{n(),A=1e3,j=10,M={user:8,thinking:6,notice:3,result:3},N=200,P=` ⎿ `,F=` `,I=[`command`,`pattern`,`query`,`queries`,`prompt`,`task`,`describe`,`workflow`,`url`,`urls`,`old_string`,`content`,`file_path`,`filePath`,`path`],L=[`path`,`file_path`,`filePath`]}));R();export{R as init_fleetTranscript,b as readFleetTranscript,y as readFleetTranscriptTail,k as renderFleetTranscript};
|
|
5
5
|
//# sourceMappingURL=fleetTranscript.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleetTranscript.mjs","names":[],"sources":["../../src/tui/fleetTranscript.ts"],"sourcesContent":["/**\n * Reads and renders a run's child transcript for the fleet detail pane.\n *\n * NOT A PORT:\n * The predecessor's `fleetTranscript.ts` parsed the HOST agent's own session\n * JSONL. `childTranscript.ts` (`../shared/childTranscript.ts`) is a\n * from-scratch, write-only format this package invented for its own reasons\n * (see that module's header: it is the parent's independent record, not the\n * host's session file). Translating the predecessor's parser would build a\n * reader for a format nothing here writes, so this reads `childTranscript.ts`'s\n * actual record shapes (`message`/`tool_start`/`tool_end`/`stdout`/`stderr`/\n * `truncated`) directly.\n *\n * READ THE RECORD, NOT THE SUMMARY:\n * `childTranscript.ts` flattens each message to a `text` field with\n * `extractTextFromContent`, which recognises only `{type:'text'}`. An assistant\n * turn made of a `thinking` block plus `toolCall` blocks therefore flattens to\n * nothing, and the writer omits the field entirely - which is what produced\n * rows of bare `assistant` headers with no content under them. The writer does\n * still persist the whole `message.content` array verbatim, so this module\n * parses THAT: thinking becomes its own event, and a turn with no text emits no\n * assistant event at all. Same reasoning for `argsPayload` (full tool arguments,\n * written all along, previously unread) and for `role: 'toolResult'` records,\n * which used to be dropped so a reader could see what ran but never what came\n * back.\n *\n * INCREMENTAL BY CONSTRUCTION:\n * A transcript is append-only and can reach 50 MiB, while the fleet overlay\n * re-checks the selected run roughly once a second. Re-reading and re-parsing\n * the whole file on every change makes cost grow with run length, so the reader\n * is resumable (`readFleetTranscriptTail`): it reads only the bytes appended\n * since the last call, and the renderer keeps one rendered block per event so\n * an append re-renders only the new and mutated ones. Steady-state cost is\n * proportional to what arrived, not to what the file holds.\n *\n * DESIGN PATTERNS:\n * - Reading is tolerant, not strict: the writer appends lines as a run\n * progresses, so the last line on disk may be a partial write mid-flush.\n * A line that fails to parse is dropped rather than failing the whole read\n * - `tool_start`/`tool_end`/`toolResult` are folded into ONE `tool` event keyed\n * by `toolCallId`, so the pane reads as a sequence of turns rather than three\n * lines per call. Calls interleave and `tool_end` arrives BEFORE its\n * `toolResult`, so the fold map retains an entry for as long as its event is\n * retained rather than closing it out on `tool_end`\n * - A `tool_end` with no matching start (the start fell outside the retained\n * window) still renders on its own rather than being dropped, since it is\n * still evidence of what ran\n *\n * AVOID:\n * - Reusing a `FleetTranscriptTail` after passing it as `previous`. The reader\n * takes ownership of its `events` and fold map instead of copying them, which\n * is the whole point of resuming; the returned tail supersedes it\n */\n\nimport * as fs from 'node:fs';\n\nimport { Markdown, truncateToWidth, wrapTextWithAnsi, type MarkdownTheme } from '@earendil-works/pi-tui';\n\nimport { formatDuration, shortenPath } from '../shared/formatters.ts';\n\nexport type FleetTranscriptEventKind = 'user' | 'assistant' | 'thinking' | 'tool' | 'notice' | 'stdout' | 'stderr';\n\nexport interface FleetTranscriptEvent {\n kind: FleetTranscriptEventKind;\n at: number;\n text: string;\n /** Tool name, present only for `kind: 'tool'`. */\n name?: string;\n /** Present only for `kind: 'tool'`: `'running'` until a matching `tool_end` arrives. */\n status?: 'running' | 'ok' | 'error';\n /** Tool arguments parsed from `argsPayload`, when the writer recorded them. */\n args?: Record<string, unknown>;\n /** Tool output, folded in from the matching `toolResult` record. */\n result?: string;\n /** Set when the writer bounded `result` at its payload cap. */\n resultTruncated?: boolean;\n /** `tool_end` timestamp, for the call's duration. */\n endedAt?: number;\n /** Model that produced an assistant turn, when reported. */\n model?: string;\n}\n\nexport interface FleetTranscript {\n events: FleetTranscriptEvent[];\n warning?: string;\n}\n\n/** One retained tool event and where it sits in `events`, so a later fold can mark it dirty. */\ninterface FleetTranscriptToolEntry {\n event: FleetTranscriptEvent;\n index: number;\n}\n\n/**\n * A resume point for `readFleetTranscriptTail`.\n *\n * `byteOffset` is deliberately the end of the last COMPLETE line rather than\n * the file size: a partial trailing line is left unconsumed so the next read\n * picks it up whole, instead of being dropped as unparseable the way a\n * whole-file read has to.\n */\nexport interface FleetTranscriptTail extends FleetTranscript {\n path: string;\n byteOffset: number;\n /** Size observed at this read. A later shrink means the file was replaced, forcing a full re-read. */\n size: number;\n toolCalls: Map<string, FleetTranscriptToolEntry>;\n /** Earliest index whose content changed in this read. Undefined when only appends happened. */\n firstDirtyIndex: number | undefined;\n /** Cumulative events dropped from the front by the retention cap. */\n droppedEvents: number;\n}\n\n/**\n * Retention cap. Bounds memory and worst-case render cost independently of how\n * long a run goes on; `droppedEvents` keeps the fact that a window was applied\n * visible, so a tail is never mistaken for a whole run.\n */\nexport const MAX_RETAINED_EVENTS = 1000;\n\nconst NEWLINE_BYTE = 0x0a;\n\ninterface RawRecord {\n recordType?: string;\n ts?: number;\n role?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n isError?: boolean;\n argsPreview?: string;\n argsPayload?: string;\n outputTruncated?: boolean;\n model?: string;\n message?: { role?: string; content?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseLine(line: string): RawRecord | undefined {\n const trimmed = line.trim();\n if (!trimmed) return undefined;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n return isRecord(parsed) ? (parsed as RawRecord) : undefined;\n } catch {\n // The writer appends line-by-line; a trailing partial write on the last\n // line is expected while a run is in flight, not evidence of corruption.\n return undefined;\n }\n}\n\n/**\n * Split an assistant turn's raw content into its thinking and its prose.\n *\n * Returns undefined when the record carried no content array, so the caller can\n * fall back to the writer's flattened `text` rather than treating a shape this\n * does not recognise as an empty turn.\n */\nfunction splitAssistantContent(content: unknown): { thinking: string[]; text: string } | undefined {\n if (!Array.isArray(content)) return undefined;\n const thinking: string[] = [];\n const texts: string[] = [];\n for (const part of content) {\n if (!part || typeof part !== 'object') continue;\n const record = part as Record<string, unknown>;\n if (record.type === 'thinking') {\n const value = typeof record.thinking === 'string' ? record.thinking : '';\n if (value.trim()) thinking.push(value);\n continue;\n }\n // Anything else - toolCall in particular - is represented by its own event,\n // so it must not be folded into the assistant turn's text.\n if (record.type === 'text' && typeof record.text === 'string') texts.push(record.text);\n }\n return { thinking, text: texts.join('\\n') };\n}\n\nfunction parseArgsPayload(payload: string | undefined): Record<string, unknown> | undefined {\n if (!payload) return undefined;\n try {\n const parsed: unknown = JSON.parse(payload);\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n // The writer bounds this payload, so a long argument set can land here\n // truncated mid-JSON. The preview still renders; the args just stay absent.\n return undefined;\n }\n}\n\nfunction emptyTail(transcriptPath: string, warning?: string): FleetTranscriptTail {\n return {\n path: transcriptPath,\n events: [],\n byteOffset: 0,\n size: 0,\n toolCalls: new Map(),\n firstDirtyIndex: undefined,\n droppedEvents: 0,\n ...(warning ? { warning } : {}),\n };\n}\n\n/** Read exactly the appended range, so cost tracks what arrived rather than what the file holds. */\nfunction readRange(filePath: string, offset: number, length: number): Buffer {\n const handle = fs.openSync(filePath, 'r');\n try {\n const buffer = Buffer.allocUnsafe(length);\n let read = 0;\n while (read < length) {\n const chunk = fs.readSync(handle, buffer, read, length - read, offset + read);\n if (chunk <= 0) break;\n read += chunk;\n }\n return read === length ? buffer : buffer.subarray(0, read);\n } finally {\n fs.closeSync(handle);\n }\n}\n\nfunction markDirty(tail: FleetTranscriptTail, index: number): void {\n if (tail.firstDirtyIndex === undefined || index < tail.firstDirtyIndex) tail.firstDirtyIndex = index;\n}\n\nfunction pushEvent(tail: FleetTranscriptTail, event: FleetTranscriptEvent): number {\n tail.events.push(event);\n return tail.events.length - 1;\n}\n\n/** Apply the retention cap, keeping every index that survives it consistent. */\nfunction applyRetention(tail: FleetTranscriptTail): void {\n const excess = tail.events.length - MAX_RETAINED_EVENTS;\n if (excess <= 0) return;\n tail.events.splice(0, excess);\n tail.droppedEvents += excess;\n if (tail.firstDirtyIndex !== undefined) tail.firstDirtyIndex = Math.max(0, tail.firstDirtyIndex - excess);\n for (const [callId, entry] of tail.toolCalls) {\n const shifted = entry.index - excess;\n if (shifted < 0) tail.toolCalls.delete(callId);\n else entry.index = shifted;\n }\n}\n\nfunction consumeMessage(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.role === 'toolResult') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n // A result whose call fell outside the retained window has nothing to\n // attach to; the call itself is already gone, so the output would be\n // unattributable rather than useful.\n if (!entry) return;\n entry.event.result = record.text ?? '';\n entry.event.resultTruncated = record.outputTruncated === true;\n if (record.isError === true) entry.event.status = 'error';\n markDirty(tail, entry.index);\n return;\n }\n if (record.role === 'user') {\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'user', at, text });\n return;\n }\n if (record.role === 'assistant') {\n const parts = splitAssistantContent(record.message?.content);\n for (const thinking of parts?.thinking ?? []) pushEvent(tail, { kind: 'thinking', at, text: thinking });\n const text = parts ? parts.text : (record.text ?? '');\n // A turn that only thought and called tools has no prose of its own. Both\n // are already represented, so emitting a header for it says nothing.\n if (text.trim()) {\n pushEvent(tail, { kind: 'assistant', at, text, ...(record.model ? { model: record.model } : {}) });\n }\n return;\n }\n if (record.role === 'custom') {\n // Host-injected notices (skill suggestions, hook output) are part of what\n // the child was told, so they belong in the transcript rather than nowhere.\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'notice', at, text });\n }\n}\n\nfunction consumeRecord(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.recordType === 'message') return consumeMessage(tail, record);\n if (record.recordType === 'tool_start') {\n const args = parseArgsPayload(record.argsPayload);\n const event: FleetTranscriptEvent = {\n kind: 'tool',\n at,\n text: record.argsPreview ?? '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: 'running',\n ...(args ? { args } : {}),\n };\n const index = pushEvent(tail, event);\n if (record.toolCallId) tail.toolCalls.set(record.toolCallId, { event, index });\n return;\n }\n if (record.recordType === 'tool_end') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n if (entry) {\n // An error reported by either half of the call is the call's outcome.\n entry.event.status = record.isError === true || entry.event.status === 'error' ? 'error' : 'ok';\n entry.event.endedAt = at;\n markDirty(tail, entry.index);\n return;\n }\n pushEvent(tail, {\n kind: 'tool',\n at,\n text: '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: record.isError === true ? 'error' : 'ok',\n });\n return;\n }\n if (record.recordType === 'stdout' || record.recordType === 'stderr') {\n pushEvent(tail, { kind: record.recordType, at, text: record.text ?? '' });\n }\n // 'truncated' carries no displayable content of its own; the events\n // recorded before it already show what happened up to the cutoff.\n}\n\n/**\n * Read whatever has been appended since `previous`, or the whole file when no\n * resume point is given.\n *\n * Never throws: a missing or unreadable file is a normal state (the run has\n * not started writing yet, or was cleaned up), reported through `warning`\n * rather than an exception the caller would have to guard against.\n */\nexport function readFleetTranscriptTail(transcriptPath: string, previous?: FleetTranscriptTail): FleetTranscriptTail {\n let size: number;\n try {\n size = fs.statSync(transcriptPath).size;\n } catch (cause) {\n return emptyTail(transcriptPath, cause instanceof Error ? cause.message : String(cause));\n }\n\n // A shrink means the file was replaced rather than appended to, so nothing\n // parsed from the old one can be trusted to still describe it.\n const resumable =\n previous !== undefined &&\n previous.path === transcriptPath &&\n previous.warning === undefined &&\n size >= previous.byteOffset;\n\n const tail: FleetTranscriptTail = resumable\n ? { ...previous, size, firstDirtyIndex: undefined, warning: undefined }\n : emptyTail(transcriptPath);\n tail.size = size;\n if (size <= tail.byteOffset) return tail;\n\n let chunk: Buffer;\n try {\n chunk = readRange(transcriptPath, tail.byteOffset, size - tail.byteOffset);\n } catch (cause) {\n return { ...tail, warning: cause instanceof Error ? cause.message : String(cause) };\n }\n\n // Split at the last newline: 0x0A cannot occur inside a UTF-8 multi-byte\n // sequence, so this is a byte boundary that is also a character boundary.\n const lastNewline = chunk.lastIndexOf(NEWLINE_BYTE);\n const complete = lastNewline === -1 ? undefined : chunk.subarray(0, lastNewline + 1);\n const remainder = chunk.subarray(lastNewline + 1);\n\n // A trailing line with no newline after it is ambiguous: either a write\n // caught mid-flush, or simply the last record of a file the writer did not\n // terminate. Parsing decides - a half-written record cannot be valid JSON,\n // since it is missing its closing brace - so a complete record is never\n // withheld just because nothing followed it, and a partial one is left\n // unconsumed for the next read to pick up whole.\n const pending = remainder.length > 0 ? parseLine(remainder.toString('utf-8')) : undefined;\n tail.byteOffset += (complete?.length ?? 0) + (pending ? remainder.length : 0);\n if (!complete && !pending) return tail;\n\n for (const line of complete ? complete.toString('utf-8').split(/\\r?\\n/) : []) {\n const record = parseLine(line);\n if (record) consumeRecord(tail, record);\n }\n if (pending) consumeRecord(tail, pending);\n applyRetention(tail);\n return tail;\n}\n\n/** Whole-file read, for callers with no incremental state to keep. */\nexport function readFleetTranscript(transcriptPath: string): FleetTranscript {\n const tail = readFleetTranscriptTail(transcriptPath);\n return tail.warning ? { events: tail.events, warning: tail.warning } : { events: tail.events };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\nexport type FleetTranscriptVerbosity = 'compact' | 'full';\n\nexport interface FleetTranscriptTheme {\n fg(color: string, text: string): string;\n bold(text: string): string;\n}\n\n/**\n * What the renderer needs from a tail: the events, and the two facts that say\n * which rendered blocks are still valid. Deliberately narrower than\n * `FleetTranscriptTail` so rendering does not depend on the reader's file\n * bookkeeping.\n */\nexport type FleetTranscriptRenderable = Pick<FleetTranscriptTail, 'events' | 'firstDirtyIndex' | 'droppedEvents'>;\n\nexport interface FleetTranscriptRenderOptions {\n /** Run cwd, so absolute paths render as the part that actually differs between rows. */\n cwd?: string;\n verbosity: FleetTranscriptVerbosity;\n}\n\n/**\n * One rendered block per event, plus their flattened lines.\n *\n * Blocks are kept so an append re-renders only what changed: markdown is by far\n * the most expensive part of this module, and an assistant message's rendering\n * never changes once written.\n */\nexport interface FleetTranscriptRender {\n width: number;\n verbosity: FleetTranscriptVerbosity;\n cwd: string | undefined;\n droppedEvents: number;\n blocks: string[][];\n lines: string[];\n}\n\nconst COMPACT_LINES: Record<'user' | 'thinking' | 'notice' | 'result', number> = {\n user: 8,\n thinking: 6,\n notice: 3,\n result: 3,\n};\n/** Even expanded, one 32 KiB tool result must not be able to bury the pane. */\nconst FULL_RESULT_LINES = 200;\nconst RESULT_GUTTER = ' ⎿ ';\nconst RESULT_CONTINUATION = ' ';\n\n/** Keys that identify what a call actually did, ahead of the ones every call shares. */\nconst PRIMARY_ARG_KEYS = [\n 'command',\n 'pattern',\n 'query',\n 'queries',\n 'prompt',\n 'task',\n 'describe',\n 'workflow',\n 'url',\n 'urls',\n 'old_string',\n 'content',\n 'file_path',\n 'filePath',\n 'path',\n];\nconst PATH_ARG_KEYS = ['path', 'file_path', 'filePath'];\n\nfunction statusGlyph(status: FleetTranscriptEvent['status']): string {\n if (status === 'ok') return '✓';\n if (status === 'error') return '✗';\n return '…';\n}\n\nfunction statusColor(status: FleetTranscriptEvent['status']): string {\n if (status === 'error') return 'error';\n if (status === 'running') return 'accent';\n return 'muted';\n}\n\n/**\n * Render a path as the part that distinguishes it.\n *\n * Every row in a single run shares the run's cwd as a prefix, so keeping it\n * costs the width that would otherwise show which file was touched.\n */\nfunction displayPath(value: string, cwd?: string): string {\n if (cwd) {\n if (value === cwd) return '.';\n const prefix = cwd.endsWith('/') ? cwd : `${cwd}/`;\n if (value.startsWith(prefix)) return value.slice(prefix.length) || '.';\n }\n return shortenPath(value);\n}\n\nfunction argToDisplay(value: unknown, cwd?: string): string | undefined {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const collapsed = trimmed.replace(/\\s+/g, ' ');\n return collapsed.startsWith('/') ? displayPath(collapsed, cwd) : collapsed;\n }\n if (Array.isArray(value)) {\n const parts = value.map((entry) => argToDisplay(entry, cwd)).filter((entry): entry is string => Boolean(entry));\n return parts.length > 0 ? parts.join(', ') : undefined;\n }\n return undefined;\n}\n\n/**\n * The call's headline arguments.\n *\n * Prefers the distinguishing argument over the shared one, then appends the\n * path when it was not already the headline - `grep` reads as\n * `pattern · where`, `read` as just the file.\n */\nfunction toolArgsSummary(event: FleetTranscriptEvent, cwd?: string): string {\n const args = event.args;\n if (!args) return event.text ? displayPath(event.text, cwd) : '';\n let primaryKey: string | undefined;\n let primary: string | undefined;\n for (const key of PRIMARY_ARG_KEYS) {\n const display = argToDisplay(args[key], cwd);\n if (display) {\n primaryKey = key;\n primary = display;\n break;\n }\n }\n if (!primary) {\n for (const [key, value] of Object.entries(args)) {\n const display = argToDisplay(value, cwd);\n if (display) {\n primaryKey = key;\n primary = `${key}=${display}`;\n break;\n }\n }\n }\n if (!primary) return '';\n const pathKey = PATH_ARG_KEYS.find((key) => key !== primaryKey && typeof args[key] === 'string');\n const pathDisplay = pathKey ? argToDisplay(args[pathKey], cwd) : undefined;\n // A path that relativizes to the run's own cwd says nothing the pane does\n // not already know, and every such row would carry the same '.'.\n return pathDisplay && pathDisplay !== '.' ? `${primary} · ${pathDisplay}` : primary;\n}\n\n/** Wrap to `width`, capped, with the number of hidden lines stated rather than implied. */\nfunction cappedLines(text: string, width: number, limit: number, decorate: (line: string) => string): string[] {\n const wrapped = wrapTextWithAnsi(text, Math.max(1, width));\n if (wrapped.length <= limit) return wrapped.map(decorate);\n return [...wrapped.slice(0, limit).map(decorate), decorate(`… +${wrapped.length - limit} lines`)];\n}\n\nfunction renderToolEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const summary = toolArgsSummary(event, options.cwd);\n const duration = event.endedAt !== undefined ? formatDuration(Math.max(0, event.endedAt - event.at)) : undefined;\n const header = [\n theme.fg(statusColor(event.status), statusGlyph(event.status)),\n theme.bold(event.name ?? 'tool'),\n summary ? theme.fg('muted', summary) : undefined,\n duration ? theme.fg('dim', duration) : undefined,\n ]\n .filter((part): part is string => part !== undefined)\n .join(' ');\n const lines = [truncateToWidth(header, width)];\n\n if (options.verbosity === 'full' && event.args) {\n const pretty = JSON.stringify(event.args, null, 2);\n for (const line of pretty.split('\\n')) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}${line}`, width)));\n }\n }\n\n const result = event.result?.trimEnd();\n if (result) {\n const limit = options.verbosity === 'full' ? FULL_RESULT_LINES : COMPACT_LINES.result;\n const color = event.status === 'error' ? 'error' : 'dim';\n const body = result.split('\\n');\n const shown = body.slice(0, limit);\n shown.forEach((line, index) => {\n const prefix = index === 0 ? RESULT_GUTTER : RESULT_CONTINUATION;\n lines.push(theme.fg(color, truncateToWidth(`${prefix}${line}`, width)));\n });\n const hidden = body.length - shown.length;\n if (hidden > 0) lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}… +${hidden} lines`, width)));\n if (event.resultTruncated) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}(output truncated by the recorder)`, width)));\n }\n } else if (event.status === 'running') {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_GUTTER}running…`, width)));\n }\n return lines;\n}\n\nfunction renderEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const safeWidth = Math.max(1, width);\n if (event.kind === 'tool') return [...renderToolEvent(event, safeWidth, theme, options), ''];\n\n if (event.kind === 'assistant') {\n const header = theme.fg('accent', theme.bold('● assistant'));\n // Markdown is the expensive call in this module; the block cache is what\n // keeps it to once per message rather than once per repaint.\n const body = markdownTheme\n ? new Markdown(event.text, 0, 0, markdownTheme).render(safeWidth)\n : wrapTextWithAnsi(event.text, safeWidth);\n return [header, ...body, ''];\n }\n\n if (event.kind === 'thinking') {\n const header = theme.fg('dim', theme.bold('✻ thinking'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.thinking;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => theme.fg('dim', line)), ''];\n }\n\n if (event.kind === 'user') {\n const header = theme.fg('accent', theme.bold('▸ user'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.user;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => line), ''];\n }\n\n if (event.kind === 'notice') {\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.notice;\n return [...cappedLines(`ℹ ${event.text}`, safeWidth, limit, (line) => theme.fg('muted', line)), ''];\n }\n\n const prefix = event.kind === 'stderr' ? theme.fg('warning', 'stderr') : theme.fg('dim', 'stdout');\n return [truncateToWidth(`${prefix} ${event.text}`, safeWidth), ''];\n}\n\n/**\n * Render `tail`'s events, reusing `previous`'s blocks wherever nothing changed.\n *\n * A full render happens only when something global changed - width, verbosity,\n * cwd, or the absence of a usable previous render. An append re-renders from\n * the earliest dirty index onward and reuses every block before it.\n */\nexport function renderFleetTranscript(\n tail: FleetTranscriptRenderable,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n previous?: FleetTranscriptRender,\n): FleetTranscriptRender {\n const reusable =\n previous !== undefined &&\n previous.width === width &&\n previous.verbosity === options.verbosity &&\n previous.cwd === options.cwd &&\n previous.droppedEvents <= tail.droppedEvents;\n\n // Nothing changed at all: hand back the same render rather than rebuilding\n // a flat line array out of blocks that are all still valid. This is the\n // common case - every repaint driven by a keystroke or an unrelated run -\n // and without it the pane still pays a cost proportional to the whole\n // transcript on each one.\n if (\n reusable &&\n previous.droppedEvents === tail.droppedEvents &&\n tail.firstDirtyIndex === undefined &&\n previous.blocks.length === tail.events.length\n ) {\n return previous;\n }\n\n // Retention drops events off the front, so a reused block list has to be\n // realigned by however many went before it can be indexed alongside events.\n const base = reusable ? previous.blocks.slice(tail.droppedEvents - previous.droppedEvents) : [];\n const from = Math.min(tail.firstDirtyIndex ?? base.length, base.length, tail.events.length);\n const blocks = base.slice(0, from);\n for (let index = from; index < tail.events.length; index++) {\n blocks.push(renderEvent(tail.events[index], width, theme, markdownTheme, options));\n }\n\n const lines: string[] = [];\n if (tail.droppedEvents > 0) {\n lines.push(theme.fg('dim', truncateToWidth(`… ${tail.droppedEvents} earlier events not shown`, width)), '');\n }\n for (const block of blocks) lines.push(...block);\n return { width, verbosity: options.verbosity, cwd: options.cwd, droppedEvents: tail.droppedEvents, blocks, lines };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fleetTranscript.mjs","names":[],"sources":["../../src/tui/fleetTranscript.ts"],"sourcesContent":["/**\n * Reads and renders a run's child transcript for the fleet detail pane.\n *\n * NOT A PORT:\n * The predecessor's `fleetTranscript.ts` parsed the HOST agent's own session\n * JSONL. `childTranscript.ts` (`../shared/childTranscript.ts`) is a\n * from-scratch, write-only format this package invented for its own reasons\n * (see that module's header: it is the parent's independent record, not the\n * host's session file). Translating the predecessor's parser would build a\n * reader for a format nothing here writes, so this reads `childTranscript.ts`'s\n * actual record shapes (`message`/`tool_start`/`tool_end`/`stdout`/`stderr`/\n * `truncated`) directly.\n *\n * READ THE RECORD, NOT THE SUMMARY:\n * `childTranscript.ts` flattens each message to a `text` field with\n * `extractTextFromContent`, which recognises only `{type:'text'}`. An assistant\n * turn made of a `thinking` block plus `toolCall` blocks therefore flattens to\n * nothing, and the writer omits the field entirely - which is what produced\n * rows of bare `assistant` headers with no content under them. The writer does\n * still persist the whole `message.content` array verbatim, so this module\n * parses THAT: thinking becomes its own event, and a turn with no text emits no\n * assistant event at all. Same reasoning for `argsPayload` (full tool arguments,\n * written all along, previously unread) and for `role: 'toolResult'` records,\n * which used to be dropped so a reader could see what ran but never what came\n * back.\n *\n * INCREMENTAL BY CONSTRUCTION:\n * A transcript is append-only and can reach 50 MiB, while the fleet overlay\n * re-checks the selected run roughly once a second. Re-reading and re-parsing\n * the whole file on every change makes cost grow with run length, so the reader\n * is resumable (`readFleetTranscriptTail`): it reads only the bytes appended\n * since the last call, and the renderer keeps one rendered block per event so\n * an append re-renders only the new and mutated ones. Steady-state cost is\n * proportional to what arrived, not to what the file holds.\n *\n * DESIGN PATTERNS:\n * - Reading is tolerant, not strict: the writer appends lines as a run\n * progresses, so the last line on disk may be a partial write mid-flush.\n * A line that fails to parse is dropped rather than failing the whole read\n * - `tool_start`/`tool_end`/`toolResult` are folded into ONE `tool` event keyed\n * by `toolCallId`, so the pane reads as a sequence of turns rather than three\n * lines per call. Calls interleave and `tool_end` arrives BEFORE its\n * `toolResult`, so the fold map retains an entry for as long as its event is\n * retained rather than closing it out on `tool_end`\n * - A `tool_end` with no matching start (the start fell outside the retained\n * window) still renders on its own rather than being dropped, since it is\n * still evidence of what ran\n *\n * AVOID:\n * - Reusing a `FleetTranscriptTail` after passing it as `previous`. The reader\n * takes ownership of its `events` and fold map instead of copying them, which\n * is the whole point of resuming; the returned tail supersedes it\n */\n\nimport * as fs from 'node:fs';\n\nimport { Markdown, type MarkdownTheme, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';\n\nimport { formatDuration, shortenPath } from '../shared/formatters.ts';\n\nexport type FleetTranscriptEventKind = 'user' | 'assistant' | 'thinking' | 'tool' | 'notice' | 'stdout' | 'stderr';\n\nexport interface FleetTranscriptEvent {\n kind: FleetTranscriptEventKind;\n at: number;\n text: string;\n /** Tool name, present only for `kind: 'tool'`. */\n name?: string;\n /** Present only for `kind: 'tool'`: `'running'` until a matching `tool_end` arrives. */\n status?: 'running' | 'ok' | 'error';\n /** Tool arguments parsed from `argsPayload`, when the writer recorded them. */\n args?: Record<string, unknown>;\n /** Tool output, folded in from the matching `toolResult` record. */\n result?: string;\n /** Set when the writer bounded `result` at its payload cap. */\n resultTruncated?: boolean;\n /** `tool_end` timestamp, for the call's duration. */\n endedAt?: number;\n /** Model that produced an assistant turn, when reported. */\n model?: string;\n}\n\nexport interface FleetTranscript {\n events: FleetTranscriptEvent[];\n warning?: string;\n}\n\n/** One retained tool event and where it sits in `events`, so a later fold can mark it dirty. */\ninterface FleetTranscriptToolEntry {\n event: FleetTranscriptEvent;\n index: number;\n}\n\n/**\n * A resume point for `readFleetTranscriptTail`.\n *\n * `byteOffset` is deliberately the end of the last COMPLETE line rather than\n * the file size: a partial trailing line is left unconsumed so the next read\n * picks it up whole, instead of being dropped as unparseable the way a\n * whole-file read has to.\n */\nexport interface FleetTranscriptTail extends FleetTranscript {\n path: string;\n byteOffset: number;\n /** Size observed at this read. A later shrink means the file was replaced, forcing a full re-read. */\n size: number;\n toolCalls: Map<string, FleetTranscriptToolEntry>;\n /** Earliest index whose content changed in this read. Undefined when only appends happened. */\n firstDirtyIndex: number | undefined;\n /** Cumulative events dropped from the front by the retention cap. */\n droppedEvents: number;\n}\n\n/**\n * Retention cap. Bounds memory and worst-case render cost independently of how\n * long a run goes on; `droppedEvents` keeps the fact that a window was applied\n * visible, so a tail is never mistaken for a whole run.\n */\nexport const MAX_RETAINED_EVENTS = 1000;\n\nconst NEWLINE_BYTE = 0x0a;\n\ninterface RawRecord {\n recordType?: string;\n ts?: number;\n role?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n isError?: boolean;\n argsPreview?: string;\n argsPayload?: string;\n outputTruncated?: boolean;\n model?: string;\n message?: { role?: string; content?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseLine(line: string): RawRecord | undefined {\n const trimmed = line.trim();\n if (!trimmed) return undefined;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n return isRecord(parsed) ? (parsed as RawRecord) : undefined;\n } catch {\n // The writer appends line-by-line; a trailing partial write on the last\n // line is expected while a run is in flight, not evidence of corruption.\n return undefined;\n }\n}\n\n/**\n * Split an assistant turn's raw content into its thinking and its prose.\n *\n * Returns undefined when the record carried no content array, so the caller can\n * fall back to the writer's flattened `text` rather than treating a shape this\n * does not recognise as an empty turn.\n */\nfunction splitAssistantContent(content: unknown): { thinking: string[]; text: string } | undefined {\n if (!Array.isArray(content)) return undefined;\n const thinking: string[] = [];\n const texts: string[] = [];\n for (const part of content) {\n if (!part || typeof part !== 'object') continue;\n const record = part as Record<string, unknown>;\n if (record.type === 'thinking') {\n const value = typeof record.thinking === 'string' ? record.thinking : '';\n if (value.trim()) thinking.push(value);\n continue;\n }\n // Anything else - toolCall in particular - is represented by its own event,\n // so it must not be folded into the assistant turn's text.\n if (record.type === 'text' && typeof record.text === 'string') texts.push(record.text);\n }\n return { thinking, text: texts.join('\\n') };\n}\n\nfunction parseArgsPayload(payload: string | undefined): Record<string, unknown> | undefined {\n if (!payload) return undefined;\n try {\n const parsed: unknown = JSON.parse(payload);\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n // The writer bounds this payload, so a long argument set can land here\n // truncated mid-JSON. The preview still renders; the args just stay absent.\n return undefined;\n }\n}\n\nfunction emptyTail(transcriptPath: string, warning?: string): FleetTranscriptTail {\n return {\n path: transcriptPath,\n events: [],\n byteOffset: 0,\n size: 0,\n toolCalls: new Map(),\n firstDirtyIndex: undefined,\n droppedEvents: 0,\n ...(warning ? { warning } : {}),\n };\n}\n\n/** Read exactly the appended range, so cost tracks what arrived rather than what the file holds. */\nfunction readRange(filePath: string, offset: number, length: number): Buffer {\n const handle = fs.openSync(filePath, 'r');\n try {\n const buffer = Buffer.allocUnsafe(length);\n let read = 0;\n while (read < length) {\n const chunk = fs.readSync(handle, buffer, read, length - read, offset + read);\n if (chunk <= 0) break;\n read += chunk;\n }\n return read === length ? buffer : buffer.subarray(0, read);\n } finally {\n fs.closeSync(handle);\n }\n}\n\nfunction markDirty(tail: FleetTranscriptTail, index: number): void {\n if (tail.firstDirtyIndex === undefined || index < tail.firstDirtyIndex) tail.firstDirtyIndex = index;\n}\n\nfunction pushEvent(tail: FleetTranscriptTail, event: FleetTranscriptEvent): number {\n tail.events.push(event);\n return tail.events.length - 1;\n}\n\n/** Apply the retention cap, keeping every index that survives it consistent. */\nfunction applyRetention(tail: FleetTranscriptTail): void {\n const excess = tail.events.length - MAX_RETAINED_EVENTS;\n if (excess <= 0) return;\n tail.events.splice(0, excess);\n tail.droppedEvents += excess;\n if (tail.firstDirtyIndex !== undefined) tail.firstDirtyIndex = Math.max(0, tail.firstDirtyIndex - excess);\n for (const [callId, entry] of tail.toolCalls) {\n const shifted = entry.index - excess;\n if (shifted < 0) tail.toolCalls.delete(callId);\n else entry.index = shifted;\n }\n}\n\nfunction consumeMessage(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.role === 'toolResult') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n // A result whose call fell outside the retained window has nothing to\n // attach to; the call itself is already gone, so the output would be\n // unattributable rather than useful.\n if (!entry) return;\n entry.event.result = record.text ?? '';\n entry.event.resultTruncated = record.outputTruncated === true;\n if (record.isError === true) entry.event.status = 'error';\n markDirty(tail, entry.index);\n return;\n }\n if (record.role === 'user') {\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'user', at, text });\n return;\n }\n if (record.role === 'assistant') {\n const parts = splitAssistantContent(record.message?.content);\n for (const thinking of parts?.thinking ?? []) pushEvent(tail, { kind: 'thinking', at, text: thinking });\n const text = parts ? parts.text : (record.text ?? '');\n // A turn that only thought and called tools has no prose of its own. Both\n // are already represented, so emitting a header for it says nothing.\n if (text.trim()) {\n pushEvent(tail, { kind: 'assistant', at, text, ...(record.model ? { model: record.model } : {}) });\n }\n return;\n }\n if (record.role === 'custom') {\n // Host-injected notices (skill suggestions, hook output) are part of what\n // the child was told, so they belong in the transcript rather than nowhere.\n const text = record.text ?? '';\n if (text.trim()) pushEvent(tail, { kind: 'notice', at, text });\n }\n}\n\nfunction consumeRecord(tail: FleetTranscriptTail, record: RawRecord): void {\n const at = record.ts ?? 0;\n if (record.recordType === 'message') return consumeMessage(tail, record);\n if (record.recordType === 'tool_start') {\n const args = parseArgsPayload(record.argsPayload);\n const event: FleetTranscriptEvent = {\n kind: 'tool',\n at,\n text: record.argsPreview ?? '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: 'running',\n ...(args ? { args } : {}),\n };\n const index = pushEvent(tail, event);\n if (record.toolCallId) tail.toolCalls.set(record.toolCallId, { event, index });\n return;\n }\n if (record.recordType === 'tool_end') {\n const entry = record.toolCallId ? tail.toolCalls.get(record.toolCallId) : undefined;\n if (entry) {\n // An error reported by either half of the call is the call's outcome.\n entry.event.status = record.isError === true || entry.event.status === 'error' ? 'error' : 'ok';\n entry.event.endedAt = at;\n markDirty(tail, entry.index);\n return;\n }\n pushEvent(tail, {\n kind: 'tool',\n at,\n text: '',\n ...(record.toolName ? { name: record.toolName } : {}),\n status: record.isError === true ? 'error' : 'ok',\n });\n return;\n }\n if (record.recordType === 'stdout' || record.recordType === 'stderr') {\n pushEvent(tail, { kind: record.recordType, at, text: record.text ?? '' });\n }\n // 'truncated' carries no displayable content of its own; the events\n // recorded before it already show what happened up to the cutoff.\n}\n\n/**\n * Read whatever has been appended since `previous`, or the whole file when no\n * resume point is given.\n *\n * Never throws: a missing or unreadable file is a normal state (the run has\n * not started writing yet, or was cleaned up), reported through `warning`\n * rather than an exception the caller would have to guard against.\n */\nexport function readFleetTranscriptTail(transcriptPath: string, previous?: FleetTranscriptTail): FleetTranscriptTail {\n let size: number;\n try {\n size = fs.statSync(transcriptPath).size;\n } catch (cause) {\n return emptyTail(transcriptPath, cause instanceof Error ? cause.message : String(cause));\n }\n\n // A shrink means the file was replaced rather than appended to, so nothing\n // parsed from the old one can be trusted to still describe it.\n const resumable =\n previous !== undefined &&\n previous.path === transcriptPath &&\n previous.warning === undefined &&\n size >= previous.byteOffset;\n\n const tail: FleetTranscriptTail = resumable\n ? { ...previous, size, firstDirtyIndex: undefined, warning: undefined }\n : emptyTail(transcriptPath);\n tail.size = size;\n if (size <= tail.byteOffset) return tail;\n\n let chunk: Buffer;\n try {\n chunk = readRange(transcriptPath, tail.byteOffset, size - tail.byteOffset);\n } catch (cause) {\n return { ...tail, warning: cause instanceof Error ? cause.message : String(cause) };\n }\n\n // Split at the last newline: 0x0A cannot occur inside a UTF-8 multi-byte\n // sequence, so this is a byte boundary that is also a character boundary.\n const lastNewline = chunk.lastIndexOf(NEWLINE_BYTE);\n const complete = lastNewline === -1 ? undefined : chunk.subarray(0, lastNewline + 1);\n const remainder = chunk.subarray(lastNewline + 1);\n\n // A trailing line with no newline after it is ambiguous: either a write\n // caught mid-flush, or simply the last record of a file the writer did not\n // terminate. Parsing decides - a half-written record cannot be valid JSON,\n // since it is missing its closing brace - so a complete record is never\n // withheld just because nothing followed it, and a partial one is left\n // unconsumed for the next read to pick up whole.\n const pending = remainder.length > 0 ? parseLine(remainder.toString('utf-8')) : undefined;\n tail.byteOffset += (complete?.length ?? 0) + (pending ? remainder.length : 0);\n if (!complete && !pending) return tail;\n\n for (const line of complete ? complete.toString('utf-8').split(/\\r?\\n/) : []) {\n const record = parseLine(line);\n if (record) consumeRecord(tail, record);\n }\n if (pending) consumeRecord(tail, pending);\n applyRetention(tail);\n return tail;\n}\n\n/** Whole-file read, for callers with no incremental state to keep. */\nexport function readFleetTranscript(transcriptPath: string): FleetTranscript {\n const tail = readFleetTranscriptTail(transcriptPath);\n return tail.warning ? { events: tail.events, warning: tail.warning } : { events: tail.events };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\nexport type FleetTranscriptVerbosity = 'compact' | 'full';\n\nexport interface FleetTranscriptTheme {\n fg(color: string, text: string): string;\n bold(text: string): string;\n}\n\n/**\n * What the renderer needs from a tail: the events, and the two facts that say\n * which rendered blocks are still valid. Deliberately narrower than\n * `FleetTranscriptTail` so rendering does not depend on the reader's file\n * bookkeeping.\n */\nexport type FleetTranscriptRenderable = Pick<FleetTranscriptTail, 'events' | 'firstDirtyIndex' | 'droppedEvents'>;\n\nexport interface FleetTranscriptRenderOptions {\n /** Run cwd, so absolute paths render as the part that actually differs between rows. */\n cwd?: string;\n verbosity: FleetTranscriptVerbosity;\n}\n\n/**\n * One rendered block per event, plus their flattened lines.\n *\n * Blocks are kept so an append re-renders only what changed: markdown is by far\n * the most expensive part of this module, and an assistant message's rendering\n * never changes once written.\n */\nexport interface FleetTranscriptRender {\n width: number;\n verbosity: FleetTranscriptVerbosity;\n cwd: string | undefined;\n droppedEvents: number;\n blocks: string[][];\n lines: string[];\n}\n\nconst COMPACT_LINES: Record<'user' | 'thinking' | 'notice' | 'result', number> = {\n user: 8,\n thinking: 6,\n notice: 3,\n result: 3,\n};\n/** Even expanded, one 32 KiB tool result must not be able to bury the pane. */\nconst FULL_RESULT_LINES = 200;\nconst RESULT_GUTTER = ' ⎿ ';\nconst RESULT_CONTINUATION = ' ';\n\n/** Keys that identify what a call actually did, ahead of the ones every call shares. */\nconst PRIMARY_ARG_KEYS = [\n 'command',\n 'pattern',\n 'query',\n 'queries',\n 'prompt',\n 'task',\n 'describe',\n 'workflow',\n 'url',\n 'urls',\n 'old_string',\n 'content',\n 'file_path',\n 'filePath',\n 'path',\n];\nconst PATH_ARG_KEYS = ['path', 'file_path', 'filePath'];\n\nfunction statusGlyph(status: FleetTranscriptEvent['status']): string {\n if (status === 'ok') return '✓';\n if (status === 'error') return '✗';\n return '…';\n}\n\nfunction statusColor(status: FleetTranscriptEvent['status']): string {\n if (status === 'error') return 'error';\n if (status === 'running') return 'accent';\n return 'success';\n}\n\n/**\n * Render a path as the part that distinguishes it.\n *\n * Every row in a single run shares the run's cwd as a prefix, so keeping it\n * costs the width that would otherwise show which file was touched.\n */\nfunction displayPath(value: string, cwd?: string): string {\n if (cwd) {\n if (value === cwd) return '.';\n const prefix = cwd.endsWith('/') ? cwd : `${cwd}/`;\n if (value.startsWith(prefix)) return value.slice(prefix.length) || '.';\n }\n return shortenPath(value);\n}\n\nfunction argToDisplay(value: unknown, cwd?: string): string | undefined {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const collapsed = trimmed.replace(/\\s+/g, ' ');\n return collapsed.startsWith('/') ? displayPath(collapsed, cwd) : collapsed;\n }\n if (Array.isArray(value)) {\n const parts = value.map((entry) => argToDisplay(entry, cwd)).filter((entry): entry is string => Boolean(entry));\n return parts.length > 0 ? parts.join(', ') : undefined;\n }\n return undefined;\n}\n\n/**\n * The call's headline arguments.\n *\n * Prefers the distinguishing argument over the shared one, then appends the\n * path when it was not already the headline - `grep` reads as\n * `pattern · where`, `read` as just the file.\n */\nfunction toolArgsSummary(event: FleetTranscriptEvent, cwd?: string): string {\n const args = event.args;\n if (!args) return event.text ? displayPath(event.text, cwd) : '';\n let primaryKey: string | undefined;\n let primary: string | undefined;\n for (const key of PRIMARY_ARG_KEYS) {\n const display = argToDisplay(args[key], cwd);\n if (display) {\n primaryKey = key;\n primary = display;\n break;\n }\n }\n if (!primary) {\n for (const [key, value] of Object.entries(args)) {\n const display = argToDisplay(value, cwd);\n if (display) {\n primaryKey = key;\n primary = `${key}=${display}`;\n break;\n }\n }\n }\n if (!primary) return '';\n const pathKey = PATH_ARG_KEYS.find((key) => key !== primaryKey && typeof args[key] === 'string');\n const pathDisplay = pathKey ? argToDisplay(args[pathKey], cwd) : undefined;\n // A path that relativizes to the run's own cwd says nothing the pane does\n // not already know, and every such row would carry the same '.'.\n return pathDisplay && pathDisplay !== '.' ? `${primary} · ${pathDisplay}` : primary;\n}\n\n/** Wrap to `width`, capped, with the number of hidden lines stated rather than implied. */\nfunction cappedLines(text: string, width: number, limit: number, decorate: (line: string) => string): string[] {\n const wrapped = wrapTextWithAnsi(text, Math.max(1, width));\n if (wrapped.length <= limit) return wrapped.map(decorate);\n return [...wrapped.slice(0, limit).map(decorate), decorate(`… +${wrapped.length - limit} lines`)];\n}\n\nfunction renderToolEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const summary = toolArgsSummary(event, options.cwd);\n const duration = event.endedAt !== undefined ? formatDuration(Math.max(0, event.endedAt - event.at)) : undefined;\n const header = [\n theme.fg(statusColor(event.status), statusGlyph(event.status)),\n theme.bold(event.name ?? 'tool'),\n summary ? theme.fg('muted', summary) : undefined,\n duration ? theme.fg('dim', duration) : undefined,\n ]\n .filter((part): part is string => part !== undefined)\n .join(' ');\n const lines = [truncateToWidth(header, width)];\n\n if (options.verbosity === 'full' && event.args) {\n const pretty = JSON.stringify(event.args, null, 2);\n for (const line of pretty.split('\\n')) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}${line}`, width)));\n }\n }\n\n const result = event.result?.trimEnd();\n if (result) {\n const limit = options.verbosity === 'full' ? FULL_RESULT_LINES : COMPACT_LINES.result;\n const color = event.status === 'error' ? 'error' : 'dim';\n const body = result.split('\\n');\n const shown = body.slice(0, limit);\n shown.forEach((line, index) => {\n const prefix = index === 0 ? RESULT_GUTTER : RESULT_CONTINUATION;\n lines.push(theme.fg(color, truncateToWidth(`${prefix}${line}`, width)));\n });\n const hidden = body.length - shown.length;\n if (hidden > 0) lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}… +${hidden} lines`, width)));\n if (event.resultTruncated) {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_CONTINUATION}(output truncated by the recorder)`, width)));\n }\n } else if (event.status === 'running') {\n lines.push(theme.fg('dim', truncateToWidth(`${RESULT_GUTTER}running…`, width)));\n }\n return lines;\n}\n\nfunction renderEvent(\n event: FleetTranscriptEvent,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n): string[] {\n const safeWidth = Math.max(1, width);\n if (event.kind === 'tool') return [...renderToolEvent(event, safeWidth, theme, options), ''];\n\n if (event.kind === 'assistant') {\n const header = theme.fg('accent', theme.bold('● assistant'));\n // Markdown is the expensive call in this module; the block cache is what\n // keeps it to once per message rather than once per repaint.\n const body = markdownTheme\n ? new Markdown(event.text, 0, 0, markdownTheme).render(safeWidth)\n : wrapTextWithAnsi(event.text, safeWidth);\n return [header, ...body, ''];\n }\n\n if (event.kind === 'thinking') {\n const header = theme.fg('dim', theme.bold('✻ thinking'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.thinking;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => theme.fg('dim', line)), ''];\n }\n\n if (event.kind === 'user') {\n const header = theme.fg('accent', theme.bold('▸ user'));\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.user;\n return [header, ...cappedLines(event.text, safeWidth, limit, (line) => line), ''];\n }\n\n if (event.kind === 'notice') {\n const limit = options.verbosity === 'full' ? Number.POSITIVE_INFINITY : COMPACT_LINES.notice;\n return [...cappedLines(`ℹ ${event.text}`, safeWidth, limit, (line) => theme.fg('muted', line)), ''];\n }\n\n const prefix = event.kind === 'stderr' ? theme.fg('warning', 'stderr') : theme.fg('dim', 'stdout');\n return [truncateToWidth(`${prefix} ${event.text}`, safeWidth), ''];\n}\n\n/**\n * Render `tail`'s events, reusing `previous`'s blocks wherever nothing changed.\n *\n * A full render happens only when something global changed - width, verbosity,\n * cwd, or the absence of a usable previous render. An append re-renders from\n * the earliest dirty index onward and reuses every block before it.\n */\nexport function renderFleetTranscript(\n tail: FleetTranscriptRenderable,\n width: number,\n theme: FleetTranscriptTheme,\n markdownTheme: MarkdownTheme | undefined,\n options: FleetTranscriptRenderOptions,\n previous?: FleetTranscriptRender,\n): FleetTranscriptRender {\n const reusable =\n previous !== undefined &&\n previous.width === width &&\n previous.verbosity === options.verbosity &&\n previous.cwd === options.cwd &&\n previous.droppedEvents <= tail.droppedEvents;\n\n // Nothing changed at all: hand back the same render rather than rebuilding\n // a flat line array out of blocks that are all still valid. This is the\n // common case - every repaint driven by a keystroke or an unrelated run -\n // and without it the pane still pays a cost proportional to the whole\n // transcript on each one.\n if (\n reusable &&\n previous.droppedEvents === tail.droppedEvents &&\n tail.firstDirtyIndex === undefined &&\n previous.blocks.length === tail.events.length\n ) {\n return previous;\n }\n\n // Retention drops events off the front, so a reused block list has to be\n // realigned by however many went before it can be indexed alongside events.\n const base = reusable ? previous.blocks.slice(tail.droppedEvents - previous.droppedEvents) : [];\n const from = Math.min(tail.firstDirtyIndex ?? base.length, base.length, tail.events.length);\n const blocks = base.slice(0, from);\n for (let index = from; index < tail.events.length; index++) {\n blocks.push(renderEvent(tail.events[index], width, theme, markdownTheme, options));\n }\n\n const lines: string[] = [];\n if (tail.droppedEvents > 0) {\n lines.push(theme.fg('dim', truncateToWidth(`… ${tail.droppedEvents} earlier events not shown`, width)), '');\n }\n for (const block of blocks) lines.push(...block);\n return { width, verbosity: options.verbosity, cwd: options.cwd, droppedEvents: tail.droppedEvents, blocks, lines };\n}\n"],"mappings":"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"}
|
package/dist/tui/register.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../agents/discovery.cjs`),t=require(`../runs/background/asyncJobTracker.cjs`)
|
|
1
|
+
require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../services/agents/discovery.cjs`),t=require(`../adapters/asyncJobTracker.cjs`);require(`../services/runs/background/asyncJobTracker.cjs`);const n=require(`./agentCatalog.cjs`),r=require(`./agentResourceProjection.cjs`),i=require(`./fleet.cjs`),a=require(`./fleetStatus.cjs`);let o=require(`@agimon-ai/doompi-ui/footer`),s=require(`@agimon-ai/doompi-ui/leader`);const c=`subagents-fleet`,l=`subagents-list`,u=`@agimon-ai/doompi-team`,d={key:`a`,label:`agents`,detail:`subagent resources and runs`,order:15},f=`requested`;function p(e){return(0,s.registerDoomLeaderContribution)(e,{source:u,bindings:[{id:`subagents.fleet`,path:[d,{key:`r`,label:`runs`,detail:`current-session runs`}],command:{name:c}},{id:`subagents.list`,path:[d,{key:`l`,label:`list`,detail:`available agents`}],command:{name:l}}]})}function m(t,i){t.registerCommand(l,{description:`Browse agents available from this session cwd and inspect their projected launch resources`,handler:async(t,a)=>{let o=i.discovery.discover(a.cwd,`both`).agents,s=e.resolveActiveTeamPackageConfig(),c=i.skills.discoverAvailableSkills(a.cwd);await n.openAgentCatalog(a,r.buildAgentCatalogEntries(o,{cwd:a.cwd,skills:{resolveSkillsWithFallback:(...e)=>i.skills.resolveSkillsWithFallback(...e),discoverAvailableSkills:()=>c},capabilityCeiling:i.policies.resolve(),...s?.config.excludeTools?{excludeTools:s.config.excludeTools,exclusionSource:s.path}:{},environment:{...process.env}}))}})}function h(e,n){return async r=>{let i=t.resolveTrackedRunId(n,r.id);if(r.action===`interrupt`)return e.interrupt(i,r.message),{status:f};if(r.action===`stop`)return e.stop(i,r.message),{status:f};if(r.action===`steer`){let t=await e.steer(i,r.message??``);return{status:t.state,detail:t.message}}throw Error(`Resume is not supported by the current subagent runtime.`)}}function g(e,t){let n,r=!1,i=0,s=(0,o.registerDoomFooterContribution)(e,{source:u,id:`agent-count`,order:20}),c=(e=!1)=>{if(!n)return!1;let t=a.agentFleetStatus(n.jobs,i),r=t?.fingerprint;return!e&&r===n.fingerprint?!1:(s.update(t?.footer),n.ctx.hasUI&&n.ctx.ui.setStatus(a.FLEET_STATUS_KEY,t?.text),n.fingerprint=r,!0)},l=t.scheduler.register({id:`doom-team-agent-status`,intervalMs:250,run:()=>(i=(i+1)%a.AGENT_PULSE_FRAMES.length,c())});return e.on(`session_start`,(e,i)=>{r||(n={ctx:i,jobs:t.tracker.forSession(i.sessionManager.getSessionId()),fingerprint:void 0},c(!0))}),()=>{r||(r=!0,l(),s.dispose(),n?.ctx.hasUI&&n.ctx.ui.setStatus(a.FLEET_STATUS_KEY,void 0),n=void 0)}}function _(e,t){e.registerCommand(c,{description:`Open the live agent runs overlay: inspect current-session runs and apply runtime controls`,handler:async(e,n)=>{let r=t.tracker.forSession(n.sessionManager.getSessionId()),a=t.dispatchAction??(t.management?h(t.management,r):void 0);await i.openSubagentFleet(n,t.scheduler,r,{dispatchAction:a})}})}exports.SUBAGENT_LEADER_SOURCE=u,exports.registerAgentListCommand=m,exports.registerAgentStatus=g,exports.registerFleetCommand=_,exports.registerSubagentLeaderContribution=p;
|
|
2
2
|
//# sourceMappingURL=register.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.cjs","names":["resolveActiveLayerSubagentConfig","openAgentCatalog","buildAgentCatalogEntries","resolveTrackedRunId","agentFleetStatus","FLEET_STATUS_KEY","AGENT_PULSE_FRAMES","openSubagentFleet"],"sources":["../../src/tui/register.ts"],"sourcesContent":["/**\n * Wires the agent catalog, run fleet, and compact footer status into the host.\n *\n * DESIGN PATTERNS:\n * - Wiring only: display decisions live in `agentCatalog.ts`, `fleet.ts`, and\n * `fleetStatus.ts`; this file connects those surfaces to host services.\n */\n\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerDoomFooterContribution } from '@agimon-ai/doompi-ui/footer';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\n\nimport {\n resolveTrackedRunId,\n type IAsyncJobTracker,\n type ITrackedAsyncJobs,\n} from '../runs/background/asyncJobTracker.ts';\nimport { resolveActiveLayerSubagentConfig } from '../agents/discovery.ts';\nimport type { ISkillDiscoveryService } from '../agents/skills.ts';\nimport type { IAgentDiscoveryService } from '../agents/types.ts';\nimport type { IManagementActions } from '../extensions/managementActions.ts';\nimport type { SubagentCapabilityPolicyStore } from '../protocol/capabilityCeiling.ts';\nimport type { IPollScheduler } from '../shared/pollScheduler.ts';\nimport { openAgentCatalog } from './agentCatalog.ts';\nimport { buildAgentCatalogEntries } from './agentResourceProjection.ts';\nimport { openSubagentFleet, type FleetActionDispatcher } from './fleet.ts';\nimport { AGENT_PULSE_FRAMES, agentFleetStatus, FLEET_STATUS_KEY } from './fleetStatus.ts';\n\nexport const SUBAGENT_FLEET_COMMAND = 'subagents-fleet';\nexport const SUBAGENT_LIST_COMMAND = 'subagents-list';\nexport const SUBAGENT_LEADER_SOURCE = '@agimon-ai/doompi-team';\nconst SUBAGENT_LEADER_ORDER = 15;\nconst SUBAGENT_LEADER_SEGMENT = {\n key: 'a',\n label: 'agents',\n detail: 'subagent resources and runs',\n order: SUBAGENT_LEADER_ORDER,\n} as const;\nconst AGENT_FOOTER_ORDER = 20;\nconst AGENT_STATUS_POLL_INTERVAL_MS = 250;\nconst REQUESTED_STATUS = 'requested';\n\n/** Publishes the nested SPC a menu for available agents and current-session runs. */\nexport function registerSubagentLeaderContribution(pi: Pick<ExtensionAPI, 'events' | 'on'>): () => void {\n return registerDoomLeaderContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n bindings: [\n {\n id: 'subagents.fleet',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'r', label: 'runs', detail: 'current-session runs' }],\n command: { name: SUBAGENT_FLEET_COMMAND },\n },\n {\n id: 'subagents.list',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'l', label: 'list', detail: 'available agents' }],\n command: { name: SUBAGENT_LIST_COMMAND },\n },\n ],\n });\n}\n\nexport interface RegisterAgentListCommandDeps {\n discovery: IAgentDiscoveryService;\n skills: ISkillDiscoveryService;\n policies: SubagentCapabilityPolicyStore;\n}\n\nexport function registerAgentListCommand(pi: ExtensionAPI, deps: RegisterAgentListCommandDeps): void {\n pi.registerCommand(SUBAGENT_LIST_COMMAND, {\n description: 'Browse agents available from this session cwd and inspect their projected launch resources',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const agents = deps.discovery.discover(ctx.cwd, 'both').agents;\n const activeLayer = resolveActiveLayerSubagentConfig();\n const availableSkills = deps.skills.discoverAvailableSkills(ctx.cwd);\n const skillSnapshot = {\n resolveSkillsWithFallback: (\n ...args: Parameters<ISkillDiscoveryService['resolveSkillsWithFallback']>\n ): ReturnType<ISkillDiscoveryService['resolveSkillsWithFallback']> =>\n deps.skills.resolveSkillsWithFallback(...args),\n discoverAvailableSkills: (): ReturnType<ISkillDiscoveryService['discoverAvailableSkills']> => availableSkills,\n };\n const entries = buildAgentCatalogEntries(agents, {\n cwd: ctx.cwd,\n skills: skillSnapshot,\n capabilityCeiling: deps.policies.resolve(),\n ...(activeLayer?.config.excludeTools\n ? { excludeTools: activeLayer.config.excludeTools, exclusionSource: activeLayer.path }\n : {}),\n environment: { ...process.env },\n });\n await openAgentCatalog(ctx, entries);\n },\n });\n}\n\nexport interface RegisterFleetCommandDeps {\n scheduler: IPollScheduler;\n tracker: IAsyncJobTracker;\n /** Optional: absent until a composition root wires runtime controls to a real dispatcher. */\n dispatchAction?: FleetActionDispatcher;\n /** Builds context-scoped controls when a static test dispatcher is not supplied. */\n management?: IManagementActions;\n}\n\nexport function createFleetActionDispatcher(\n management: IManagementActions,\n jobs: ITrackedAsyncJobs,\n): FleetActionDispatcher {\n return async (request) => {\n const runId = resolveTrackedRunId(jobs, request.id);\n if (request.action === 'interrupt') {\n management.interrupt(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'stop') {\n management.stop(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'steer') {\n const result = await management.steer(runId, request.message ?? '');\n return { status: result.state, detail: result.message };\n }\n throw new Error('Resume is not supported by the current subagent runtime.');\n };\n}\n\nexport function registerAgentStatus(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): () => void {\n let current: { ctx: ExtensionContext; jobs: ITrackedAsyncJobs; fingerprint: string | undefined } | undefined;\n let disposed = false;\n let frame = 0;\n const footerContribution = registerDoomFooterContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n id: 'agent-count',\n order: AGENT_FOOTER_ORDER,\n });\n\n const publish = (force = false): boolean => {\n if (!current) return false;\n const status = agentFleetStatus(current.jobs, frame);\n const fingerprint = status?.fingerprint;\n if (!force && fingerprint === current.fingerprint) return false;\n footerContribution.update(status?.footer);\n if (current.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, status?.text);\n current.fingerprint = fingerprint;\n return true;\n };\n\n const unregisterPoll = deps.scheduler.register({\n id: 'doom-team-agent-status',\n intervalMs: AGENT_STATUS_POLL_INTERVAL_MS,\n run: () => {\n frame = (frame + 1) % AGENT_PULSE_FRAMES.length;\n return publish();\n },\n });\n\n pi.on('session_start', (_event, ctx: ExtensionContext) => {\n if (disposed) return;\n current = {\n ctx,\n jobs: deps.tracker.forSession(ctx.sessionManager.getSessionId()),\n fingerprint: undefined,\n };\n publish(true);\n });\n\n return () => {\n if (disposed) return;\n disposed = true;\n unregisterPoll();\n footerContribution.dispose();\n if (current?.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, undefined);\n current = undefined;\n };\n}\n\nexport function registerFleetCommand(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): void {\n pi.registerCommand(SUBAGENT_FLEET_COMMAND, {\n description: 'Open the live agent runs overlay: inspect current-session runs and apply runtime controls',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const jobs = deps.tracker.forSession(ctx.sessionManager.getSessionId());\n const dispatchAction =\n deps.dispatchAction ?? (deps.management ? createFleetActionDispatcher(deps.management, jobs) : undefined);\n await openSubagentFleet(ctx, deps.scheduler, jobs, { dispatchAction });\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"register.cjs","names":["resolveActiveTeamPackageConfig","openAgentCatalog","buildAgentCatalogEntries","resolveTrackedRunId","agentFleetStatus","FLEET_STATUS_KEY","AGENT_PULSE_FRAMES","openSubagentFleet"],"sources":["../../src/tui/register.ts"],"sourcesContent":["/**\n * Wires the agent catalog, run fleet, and compact footer status into the host.\n *\n * DESIGN PATTERNS:\n * - Wiring only: display decisions live in `agentCatalog.ts`, `fleet.ts`, and\n * `fleetStatus.ts`; this file connects those surfaces to host services.\n */\n\nimport { registerDoomFooterContribution } from '@agimon-ai/doompi-ui/footer';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { IManagementActions } from '../extensions/managementActions.ts';\nimport type { SubagentCapabilityPolicyStore } from '../schemas/team/capabilityCeiling.ts';\nimport { resolveActiveTeamPackageConfig } from '../services/agents/discovery.ts';\nimport type { ISkillDiscoveryService } from '../services/agents/skills.ts';\nimport type { IAgentDiscoveryService } from '../services/agents/types.ts';\nimport {\n type IAsyncJobTracker,\n type ITrackedAsyncJobs,\n resolveTrackedRunId,\n} from '../services/runs/background/asyncJobTracker.ts';\nimport type { IPollScheduler } from '../shared/pollScheduler.ts';\nimport { openAgentCatalog } from './agentCatalog.ts';\nimport { buildAgentCatalogEntries } from './agentResourceProjection.ts';\nimport { type FleetActionDispatcher, openSubagentFleet } from './fleet.ts';\nimport { AGENT_PULSE_FRAMES, agentFleetStatus, FLEET_STATUS_KEY } from './fleetStatus.ts';\n\nexport const SUBAGENT_FLEET_COMMAND = 'subagents-fleet';\nexport const SUBAGENT_LIST_COMMAND = 'subagents-list';\nexport const SUBAGENT_LEADER_SOURCE = '@agimon-ai/doompi-team';\nconst SUBAGENT_LEADER_ORDER = 15;\nconst SUBAGENT_LEADER_SEGMENT = {\n key: 'a',\n label: 'agents',\n detail: 'subagent resources and runs',\n order: SUBAGENT_LEADER_ORDER,\n} as const;\nconst AGENT_FOOTER_ORDER = 20;\nconst AGENT_STATUS_POLL_INTERVAL_MS = 250;\nconst REQUESTED_STATUS = 'requested';\n\n/** Publishes the nested SPC a menu for available agents and current-session runs. */\nexport function registerSubagentLeaderContribution(pi: Pick<ExtensionAPI, 'events' | 'on'>): () => void {\n return registerDoomLeaderContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n bindings: [\n {\n id: 'subagents.fleet',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'r', label: 'runs', detail: 'current-session runs' }],\n command: { name: SUBAGENT_FLEET_COMMAND },\n },\n {\n id: 'subagents.list',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'l', label: 'list', detail: 'available agents' }],\n command: { name: SUBAGENT_LIST_COMMAND },\n },\n ],\n });\n}\n\nexport interface RegisterAgentListCommandDeps {\n discovery: IAgentDiscoveryService;\n skills: ISkillDiscoveryService;\n policies: SubagentCapabilityPolicyStore;\n}\n\nexport function registerAgentListCommand(pi: ExtensionAPI, deps: RegisterAgentListCommandDeps): void {\n pi.registerCommand(SUBAGENT_LIST_COMMAND, {\n description: 'Browse agents available from this session cwd and inspect their projected launch resources',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const agents = deps.discovery.discover(ctx.cwd, 'both').agents;\n const teamPackage = resolveActiveTeamPackageConfig();\n const availableSkills = deps.skills.discoverAvailableSkills(ctx.cwd);\n const skillSnapshot = {\n resolveSkillsWithFallback: (\n ...args: Parameters<ISkillDiscoveryService['resolveSkillsWithFallback']>\n ): ReturnType<ISkillDiscoveryService['resolveSkillsWithFallback']> =>\n deps.skills.resolveSkillsWithFallback(...args),\n discoverAvailableSkills: (): ReturnType<ISkillDiscoveryService['discoverAvailableSkills']> => availableSkills,\n };\n const entries = buildAgentCatalogEntries(agents, {\n cwd: ctx.cwd,\n skills: skillSnapshot,\n capabilityCeiling: deps.policies.resolve(),\n ...(teamPackage?.config.excludeTools\n ? { excludeTools: teamPackage.config.excludeTools, exclusionSource: teamPackage.path }\n : {}),\n environment: { ...process.env },\n });\n await openAgentCatalog(ctx, entries);\n },\n });\n}\n\nexport interface RegisterFleetCommandDeps {\n scheduler: IPollScheduler;\n tracker: IAsyncJobTracker;\n /** Optional: absent until a composition root wires runtime controls to a real dispatcher. */\n dispatchAction?: FleetActionDispatcher;\n /** Builds context-scoped controls when a static test dispatcher is not supplied. */\n management?: IManagementActions;\n}\n\nexport function createFleetActionDispatcher(\n management: IManagementActions,\n jobs: ITrackedAsyncJobs,\n): FleetActionDispatcher {\n return async (request) => {\n const runId = resolveTrackedRunId(jobs, request.id);\n if (request.action === 'interrupt') {\n management.interrupt(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'stop') {\n management.stop(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'steer') {\n const result = await management.steer(runId, request.message ?? '');\n return { status: result.state, detail: result.message };\n }\n throw new Error('Resume is not supported by the current subagent runtime.');\n };\n}\n\nexport function registerAgentStatus(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): () => void {\n let current: { ctx: ExtensionContext; jobs: ITrackedAsyncJobs; fingerprint: string | undefined } | undefined;\n let disposed = false;\n let frame = 0;\n const footerContribution = registerDoomFooterContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n id: 'agent-count',\n order: AGENT_FOOTER_ORDER,\n });\n\n const publish = (force = false): boolean => {\n if (!current) return false;\n const status = agentFleetStatus(current.jobs, frame);\n const fingerprint = status?.fingerprint;\n if (!force && fingerprint === current.fingerprint) return false;\n footerContribution.update(status?.footer);\n if (current.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, status?.text);\n current.fingerprint = fingerprint;\n return true;\n };\n\n const unregisterPoll = deps.scheduler.register({\n id: 'doom-team-agent-status',\n intervalMs: AGENT_STATUS_POLL_INTERVAL_MS,\n run: () => {\n frame = (frame + 1) % AGENT_PULSE_FRAMES.length;\n return publish();\n },\n });\n\n pi.on('session_start', (_event, ctx: ExtensionContext) => {\n if (disposed) return;\n current = {\n ctx,\n jobs: deps.tracker.forSession(ctx.sessionManager.getSessionId()),\n fingerprint: undefined,\n };\n publish(true);\n });\n\n return () => {\n if (disposed) return;\n disposed = true;\n unregisterPoll();\n footerContribution.dispose();\n if (current?.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, undefined);\n current = undefined;\n };\n}\n\nexport function registerFleetCommand(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): void {\n pi.registerCommand(SUBAGENT_FLEET_COMMAND, {\n description: 'Open the live agent runs overlay: inspect current-session runs and apply runtime controls',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const jobs = deps.tracker.forSession(ctx.sessionManager.getSessionId());\n const dispatchAction =\n deps.dispatchAction ?? (deps.management ? createFleetActionDispatcher(deps.management, jobs) : undefined);\n await openSubagentFleet(ctx, deps.scheduler, jobs, { dispatchAction });\n },\n });\n}\n"],"mappings":"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"}
|
package/dist/tui/register.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{init_discovery as e,
|
|
1
|
+
import{init_discovery as e,resolveActiveTeamPackageConfig as t}from"../services/agents/discovery.mjs";import{resolveTrackedRunId as n}from"../adapters/asyncJobTracker.mjs";import{init_asyncJobTracker as r}from"../services/runs/background/asyncJobTracker.mjs";import{openAgentCatalog as i}from"./agentCatalog.mjs";import{buildAgentCatalogEntries as a}from"./agentResourceProjection.mjs";import{openSubagentFleet as o}from"./fleet.mjs";import{AGENT_PULSE_FRAMES as s,FLEET_STATUS_KEY as c,agentFleetStatus as l}from"./fleetStatus.mjs";import{registerDoomFooterContribution as u}from"@agimon-ai/doompi-ui/footer";import{registerDoomLeaderContribution as d}from"@agimon-ai/doompi-ui/leader";e(),r();const f=`subagents-fleet`,p=`subagents-list`,m=`@agimon-ai/doompi-team`,h={key:`a`,label:`agents`,detail:`subagent resources and runs`,order:15},g=`requested`;function _(e){return d(e,{source:m,bindings:[{id:`subagents.fleet`,path:[h,{key:`r`,label:`runs`,detail:`current-session runs`}],command:{name:f}},{id:`subagents.list`,path:[h,{key:`l`,label:`list`,detail:`available agents`}],command:{name:p}}]})}function v(e,n){e.registerCommand(p,{description:`Browse agents available from this session cwd and inspect their projected launch resources`,handler:async(e,r)=>{let o=n.discovery.discover(r.cwd,`both`).agents,s=t(),c=n.skills.discoverAvailableSkills(r.cwd);await i(r,a(o,{cwd:r.cwd,skills:{resolveSkillsWithFallback:(...e)=>n.skills.resolveSkillsWithFallback(...e),discoverAvailableSkills:()=>c},capabilityCeiling:n.policies.resolve(),...s?.config.excludeTools?{excludeTools:s.config.excludeTools,exclusionSource:s.path}:{},environment:{...process.env}}))}})}function y(e,t){return async r=>{let i=n(t,r.id);if(r.action===`interrupt`)return e.interrupt(i,r.message),{status:g};if(r.action===`stop`)return e.stop(i,r.message),{status:g};if(r.action===`steer`){let t=await e.steer(i,r.message??``);return{status:t.state,detail:t.message}}throw Error(`Resume is not supported by the current subagent runtime.`)}}function b(e,t){let n,r=!1,i=0,a=u(e,{source:m,id:`agent-count`,order:20}),o=(e=!1)=>{if(!n)return!1;let t=l(n.jobs,i),r=t?.fingerprint;return!e&&r===n.fingerprint?!1:(a.update(t?.footer),n.ctx.hasUI&&n.ctx.ui.setStatus(c,t?.text),n.fingerprint=r,!0)},d=t.scheduler.register({id:`doom-team-agent-status`,intervalMs:250,run:()=>(i=(i+1)%s.length,o())});return e.on(`session_start`,(e,i)=>{r||(n={ctx:i,jobs:t.tracker.forSession(i.sessionManager.getSessionId()),fingerprint:void 0},o(!0))}),()=>{r||(r=!0,d(),a.dispose(),n?.ctx.hasUI&&n.ctx.ui.setStatus(c,void 0),n=void 0)}}function x(e,t){e.registerCommand(f,{description:`Open the live agent runs overlay: inspect current-session runs and apply runtime controls`,handler:async(e,n)=>{let r=t.tracker.forSession(n.sessionManager.getSessionId()),i=t.dispatchAction??(t.management?y(t.management,r):void 0);await o(n,t.scheduler,r,{dispatchAction:i})}})}export{m as SUBAGENT_LEADER_SOURCE,v as registerAgentListCommand,b as registerAgentStatus,x as registerFleetCommand,_ as registerSubagentLeaderContribution};
|
|
2
2
|
//# sourceMappingURL=register.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.mjs","names":[],"sources":["../../src/tui/register.ts"],"sourcesContent":["/**\n * Wires the agent catalog, run fleet, and compact footer status into the host.\n *\n * DESIGN PATTERNS:\n * - Wiring only: display decisions live in `agentCatalog.ts`, `fleet.ts`, and\n * `fleetStatus.ts`; this file connects those surfaces to host services.\n */\n\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerDoomFooterContribution } from '@agimon-ai/doompi-ui/footer';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\n\nimport {\n resolveTrackedRunId,\n type IAsyncJobTracker,\n type ITrackedAsyncJobs,\n} from '../runs/background/asyncJobTracker.ts';\nimport { resolveActiveLayerSubagentConfig } from '../agents/discovery.ts';\nimport type { ISkillDiscoveryService } from '../agents/skills.ts';\nimport type { IAgentDiscoveryService } from '../agents/types.ts';\nimport type { IManagementActions } from '../extensions/managementActions.ts';\nimport type { SubagentCapabilityPolicyStore } from '../protocol/capabilityCeiling.ts';\nimport type { IPollScheduler } from '../shared/pollScheduler.ts';\nimport { openAgentCatalog } from './agentCatalog.ts';\nimport { buildAgentCatalogEntries } from './agentResourceProjection.ts';\nimport { openSubagentFleet, type FleetActionDispatcher } from './fleet.ts';\nimport { AGENT_PULSE_FRAMES, agentFleetStatus, FLEET_STATUS_KEY } from './fleetStatus.ts';\n\nexport const SUBAGENT_FLEET_COMMAND = 'subagents-fleet';\nexport const SUBAGENT_LIST_COMMAND = 'subagents-list';\nexport const SUBAGENT_LEADER_SOURCE = '@agimon-ai/doompi-team';\nconst SUBAGENT_LEADER_ORDER = 15;\nconst SUBAGENT_LEADER_SEGMENT = {\n key: 'a',\n label: 'agents',\n detail: 'subagent resources and runs',\n order: SUBAGENT_LEADER_ORDER,\n} as const;\nconst AGENT_FOOTER_ORDER = 20;\nconst AGENT_STATUS_POLL_INTERVAL_MS = 250;\nconst REQUESTED_STATUS = 'requested';\n\n/** Publishes the nested SPC a menu for available agents and current-session runs. */\nexport function registerSubagentLeaderContribution(pi: Pick<ExtensionAPI, 'events' | 'on'>): () => void {\n return registerDoomLeaderContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n bindings: [\n {\n id: 'subagents.fleet',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'r', label: 'runs', detail: 'current-session runs' }],\n command: { name: SUBAGENT_FLEET_COMMAND },\n },\n {\n id: 'subagents.list',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'l', label: 'list', detail: 'available agents' }],\n command: { name: SUBAGENT_LIST_COMMAND },\n },\n ],\n });\n}\n\nexport interface RegisterAgentListCommandDeps {\n discovery: IAgentDiscoveryService;\n skills: ISkillDiscoveryService;\n policies: SubagentCapabilityPolicyStore;\n}\n\nexport function registerAgentListCommand(pi: ExtensionAPI, deps: RegisterAgentListCommandDeps): void {\n pi.registerCommand(SUBAGENT_LIST_COMMAND, {\n description: 'Browse agents available from this session cwd and inspect their projected launch resources',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const agents = deps.discovery.discover(ctx.cwd, 'both').agents;\n const activeLayer = resolveActiveLayerSubagentConfig();\n const availableSkills = deps.skills.discoverAvailableSkills(ctx.cwd);\n const skillSnapshot = {\n resolveSkillsWithFallback: (\n ...args: Parameters<ISkillDiscoveryService['resolveSkillsWithFallback']>\n ): ReturnType<ISkillDiscoveryService['resolveSkillsWithFallback']> =>\n deps.skills.resolveSkillsWithFallback(...args),\n discoverAvailableSkills: (): ReturnType<ISkillDiscoveryService['discoverAvailableSkills']> => availableSkills,\n };\n const entries = buildAgentCatalogEntries(agents, {\n cwd: ctx.cwd,\n skills: skillSnapshot,\n capabilityCeiling: deps.policies.resolve(),\n ...(activeLayer?.config.excludeTools\n ? { excludeTools: activeLayer.config.excludeTools, exclusionSource: activeLayer.path }\n : {}),\n environment: { ...process.env },\n });\n await openAgentCatalog(ctx, entries);\n },\n });\n}\n\nexport interface RegisterFleetCommandDeps {\n scheduler: IPollScheduler;\n tracker: IAsyncJobTracker;\n /** Optional: absent until a composition root wires runtime controls to a real dispatcher. */\n dispatchAction?: FleetActionDispatcher;\n /** Builds context-scoped controls when a static test dispatcher is not supplied. */\n management?: IManagementActions;\n}\n\nexport function createFleetActionDispatcher(\n management: IManagementActions,\n jobs: ITrackedAsyncJobs,\n): FleetActionDispatcher {\n return async (request) => {\n const runId = resolveTrackedRunId(jobs, request.id);\n if (request.action === 'interrupt') {\n management.interrupt(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'stop') {\n management.stop(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'steer') {\n const result = await management.steer(runId, request.message ?? '');\n return { status: result.state, detail: result.message };\n }\n throw new Error('Resume is not supported by the current subagent runtime.');\n };\n}\n\nexport function registerAgentStatus(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): () => void {\n let current: { ctx: ExtensionContext; jobs: ITrackedAsyncJobs; fingerprint: string | undefined } | undefined;\n let disposed = false;\n let frame = 0;\n const footerContribution = registerDoomFooterContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n id: 'agent-count',\n order: AGENT_FOOTER_ORDER,\n });\n\n const publish = (force = false): boolean => {\n if (!current) return false;\n const status = agentFleetStatus(current.jobs, frame);\n const fingerprint = status?.fingerprint;\n if (!force && fingerprint === current.fingerprint) return false;\n footerContribution.update(status?.footer);\n if (current.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, status?.text);\n current.fingerprint = fingerprint;\n return true;\n };\n\n const unregisterPoll = deps.scheduler.register({\n id: 'doom-team-agent-status',\n intervalMs: AGENT_STATUS_POLL_INTERVAL_MS,\n run: () => {\n frame = (frame + 1) % AGENT_PULSE_FRAMES.length;\n return publish();\n },\n });\n\n pi.on('session_start', (_event, ctx: ExtensionContext) => {\n if (disposed) return;\n current = {\n ctx,\n jobs: deps.tracker.forSession(ctx.sessionManager.getSessionId()),\n fingerprint: undefined,\n };\n publish(true);\n });\n\n return () => {\n if (disposed) return;\n disposed = true;\n unregisterPoll();\n footerContribution.dispose();\n if (current?.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, undefined);\n current = undefined;\n };\n}\n\nexport function registerFleetCommand(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): void {\n pi.registerCommand(SUBAGENT_FLEET_COMMAND, {\n description: 'Open the live agent runs overlay: inspect current-session runs and apply runtime controls',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const jobs = deps.tracker.forSession(ctx.sessionManager.getSessionId());\n const dispatchAction =\n deps.dispatchAction ?? (deps.management ? createFleetActionDispatcher(deps.management, jobs) : undefined);\n await openSubagentFleet(ctx, deps.scheduler, jobs, { dispatchAction });\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"register.mjs","names":[],"sources":["../../src/tui/register.ts"],"sourcesContent":["/**\n * Wires the agent catalog, run fleet, and compact footer status into the host.\n *\n * DESIGN PATTERNS:\n * - Wiring only: display decisions live in `agentCatalog.ts`, `fleet.ts`, and\n * `fleetStatus.ts`; this file connects those surfaces to host services.\n */\n\nimport { registerDoomFooterContribution } from '@agimon-ai/doompi-ui/footer';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { IManagementActions } from '../extensions/managementActions.ts';\nimport type { SubagentCapabilityPolicyStore } from '../schemas/team/capabilityCeiling.ts';\nimport { resolveActiveTeamPackageConfig } from '../services/agents/discovery.ts';\nimport type { ISkillDiscoveryService } from '../services/agents/skills.ts';\nimport type { IAgentDiscoveryService } from '../services/agents/types.ts';\nimport {\n type IAsyncJobTracker,\n type ITrackedAsyncJobs,\n resolveTrackedRunId,\n} from '../services/runs/background/asyncJobTracker.ts';\nimport type { IPollScheduler } from '../shared/pollScheduler.ts';\nimport { openAgentCatalog } from './agentCatalog.ts';\nimport { buildAgentCatalogEntries } from './agentResourceProjection.ts';\nimport { type FleetActionDispatcher, openSubagentFleet } from './fleet.ts';\nimport { AGENT_PULSE_FRAMES, agentFleetStatus, FLEET_STATUS_KEY } from './fleetStatus.ts';\n\nexport const SUBAGENT_FLEET_COMMAND = 'subagents-fleet';\nexport const SUBAGENT_LIST_COMMAND = 'subagents-list';\nexport const SUBAGENT_LEADER_SOURCE = '@agimon-ai/doompi-team';\nconst SUBAGENT_LEADER_ORDER = 15;\nconst SUBAGENT_LEADER_SEGMENT = {\n key: 'a',\n label: 'agents',\n detail: 'subagent resources and runs',\n order: SUBAGENT_LEADER_ORDER,\n} as const;\nconst AGENT_FOOTER_ORDER = 20;\nconst AGENT_STATUS_POLL_INTERVAL_MS = 250;\nconst REQUESTED_STATUS = 'requested';\n\n/** Publishes the nested SPC a menu for available agents and current-session runs. */\nexport function registerSubagentLeaderContribution(pi: Pick<ExtensionAPI, 'events' | 'on'>): () => void {\n return registerDoomLeaderContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n bindings: [\n {\n id: 'subagents.fleet',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'r', label: 'runs', detail: 'current-session runs' }],\n command: { name: SUBAGENT_FLEET_COMMAND },\n },\n {\n id: 'subagents.list',\n path: [SUBAGENT_LEADER_SEGMENT, { key: 'l', label: 'list', detail: 'available agents' }],\n command: { name: SUBAGENT_LIST_COMMAND },\n },\n ],\n });\n}\n\nexport interface RegisterAgentListCommandDeps {\n discovery: IAgentDiscoveryService;\n skills: ISkillDiscoveryService;\n policies: SubagentCapabilityPolicyStore;\n}\n\nexport function registerAgentListCommand(pi: ExtensionAPI, deps: RegisterAgentListCommandDeps): void {\n pi.registerCommand(SUBAGENT_LIST_COMMAND, {\n description: 'Browse agents available from this session cwd and inspect their projected launch resources',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const agents = deps.discovery.discover(ctx.cwd, 'both').agents;\n const teamPackage = resolveActiveTeamPackageConfig();\n const availableSkills = deps.skills.discoverAvailableSkills(ctx.cwd);\n const skillSnapshot = {\n resolveSkillsWithFallback: (\n ...args: Parameters<ISkillDiscoveryService['resolveSkillsWithFallback']>\n ): ReturnType<ISkillDiscoveryService['resolveSkillsWithFallback']> =>\n deps.skills.resolveSkillsWithFallback(...args),\n discoverAvailableSkills: (): ReturnType<ISkillDiscoveryService['discoverAvailableSkills']> => availableSkills,\n };\n const entries = buildAgentCatalogEntries(agents, {\n cwd: ctx.cwd,\n skills: skillSnapshot,\n capabilityCeiling: deps.policies.resolve(),\n ...(teamPackage?.config.excludeTools\n ? { excludeTools: teamPackage.config.excludeTools, exclusionSource: teamPackage.path }\n : {}),\n environment: { ...process.env },\n });\n await openAgentCatalog(ctx, entries);\n },\n });\n}\n\nexport interface RegisterFleetCommandDeps {\n scheduler: IPollScheduler;\n tracker: IAsyncJobTracker;\n /** Optional: absent until a composition root wires runtime controls to a real dispatcher. */\n dispatchAction?: FleetActionDispatcher;\n /** Builds context-scoped controls when a static test dispatcher is not supplied. */\n management?: IManagementActions;\n}\n\nexport function createFleetActionDispatcher(\n management: IManagementActions,\n jobs: ITrackedAsyncJobs,\n): FleetActionDispatcher {\n return async (request) => {\n const runId = resolveTrackedRunId(jobs, request.id);\n if (request.action === 'interrupt') {\n management.interrupt(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'stop') {\n management.stop(runId, request.message);\n return { status: REQUESTED_STATUS };\n }\n if (request.action === 'steer') {\n const result = await management.steer(runId, request.message ?? '');\n return { status: result.state, detail: result.message };\n }\n throw new Error('Resume is not supported by the current subagent runtime.');\n };\n}\n\nexport function registerAgentStatus(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): () => void {\n let current: { ctx: ExtensionContext; jobs: ITrackedAsyncJobs; fingerprint: string | undefined } | undefined;\n let disposed = false;\n let frame = 0;\n const footerContribution = registerDoomFooterContribution(pi, {\n source: SUBAGENT_LEADER_SOURCE,\n id: 'agent-count',\n order: AGENT_FOOTER_ORDER,\n });\n\n const publish = (force = false): boolean => {\n if (!current) return false;\n const status = agentFleetStatus(current.jobs, frame);\n const fingerprint = status?.fingerprint;\n if (!force && fingerprint === current.fingerprint) return false;\n footerContribution.update(status?.footer);\n if (current.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, status?.text);\n current.fingerprint = fingerprint;\n return true;\n };\n\n const unregisterPoll = deps.scheduler.register({\n id: 'doom-team-agent-status',\n intervalMs: AGENT_STATUS_POLL_INTERVAL_MS,\n run: () => {\n frame = (frame + 1) % AGENT_PULSE_FRAMES.length;\n return publish();\n },\n });\n\n pi.on('session_start', (_event, ctx: ExtensionContext) => {\n if (disposed) return;\n current = {\n ctx,\n jobs: deps.tracker.forSession(ctx.sessionManager.getSessionId()),\n fingerprint: undefined,\n };\n publish(true);\n });\n\n return () => {\n if (disposed) return;\n disposed = true;\n unregisterPoll();\n footerContribution.dispose();\n if (current?.ctx.hasUI) current.ctx.ui.setStatus(FLEET_STATUS_KEY, undefined);\n current = undefined;\n };\n}\n\nexport function registerFleetCommand(pi: ExtensionAPI, deps: RegisterFleetCommandDeps): void {\n pi.registerCommand(SUBAGENT_FLEET_COMMAND, {\n description: 'Open the live agent runs overlay: inspect current-session runs and apply runtime controls',\n handler: async (_args: string, ctx: ExtensionContext) => {\n const jobs = deps.tracker.forSession(ctx.sessionManager.getSessionId());\n const dispatchAction =\n deps.dispatchAction ?? (deps.management ? createFleetActionDispatcher(deps.management, jobs) : undefined);\n await openSubagentFleet(ctx, deps.scheduler, jobs, { dispatchAction });\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
require(`../_virtual/_rolldown/runtime.cjs`);let e=require(`@agimon-ai/doompi-ui/toolChrome`),t=require(`@earendil-works/pi-tui`);var n=class{constructor(e,t,n){this.lines=e,this.layout=t,this.theme=n}render(e){if(e<=0)return[];if(e<3)return this.lines.map(n=>(0,t.truncateToWidth)(n,e,`…`));let n=e-2,r=this.layout.wrap?this.lines.flatMap(e=>(0,t.wrapTextWithAnsi)(e,n)):this.lines.map(e=>(0,t.truncateToWidth)(e,n,`…`));this.layout.frameColor&&r.unshift(this.theme.fg(this.layout.frameColor,`─`.repeat(n)));let i=r.map(e=>` ${e}`);return this.layout.trailingBlank&&i.push(``),i}invalidate(){}};function r(e,t){switch(e.action){case`agents`:return e.name?t.fg(`accent`,e.name):``;case`run`:{let n=e.requests.map(e=>e.agent),r=`${n.length} agent${n.length===1?``:`s`}`;return t.fg(`muted`,`${r} · ${n.join(`, `)}`)}case`status`:return t.fg(`accent`,`id`in e?e.id:`fleet`);case`steer`:case`stop`:case`restore`:return t.fg(`accent`,e.id);case`suspended`:return t.fg(`muted`,`runs`)}}function i(t,n){let i=(0,e.renderToolHeading)(`subagent`,t.action,n),a=r(t,n);return new e.DoomToolCall(`${i}${a?` ${a}`:``}`)}function a(e){let t=(e.content??[]).map(e=>e.text??``).join(``).split(`
|
|
2
|
+
`);for(;t.length>0&&t.at(-1)?.trim()===``;)t.pop();return t}function o(e,t,r){return new n(e,{wrap:t,frameColor:`borderMuted`,trailingBlank:!0},r)}function s(e,t,n){let r=a(e);if(t.isPartial)return o([...r.slice(-12).map(e=>n.fg(`toolOutput`,e)),n.fg(`warning`,`◐`)+n.fg(`dim`,` running`)],!1,n);let i=e.isError===!0,s=t.expanded?r:r.slice(0,12),c=s.map(e=>n.fg(`toolOutput`,e));return i?c.push(n.fg(`error`,`✗`)+n.fg(`dim`,` failed`)):!t.expanded&&r.length>s.length?c.push(n.fg(`dim`,`… ctrl+o`)):c.length===0&&c.push(n.fg(`success`,`✓`)+n.fg(`dim`,` done`)),o(c,t.expanded,n)}exports.renderSubagentCall=i,exports.renderSubagentResult=s;
|
|
3
|
+
//# sourceMappingURL=subagentToolRender.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagentToolRender.cjs","names":["DoomToolCall"],"sources":["../../src/tui/subagentToolRender.ts"],"sourcesContent":["import type { SubagentToolParams } from '@agimon-ai/doompi-extension-contracts/subagent-tool';\nimport { DoomToolCall, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\nimport { type Component, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';\n\nconst COLLAPSED_RESULT_LINES = 12;\nconst ELLIPSIS = '…';\n\ninterface SubagentToolTextLayout {\n wrap: boolean;\n frameColor?: ThemeColor;\n trailingBlank?: boolean;\n}\n\n/** Width-aware text for the self-owned subagent tool shell. */\nclass SubagentToolText implements Component {\n constructor(\n private readonly lines: readonly string[],\n private readonly layout: SubagentToolTextLayout,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n if (width < 3) return this.lines.map((line) => truncateToWidth(line, width, ELLIPSIS));\n\n const contentWidth = width - 2;\n const rendered = this.layout.wrap\n ? this.lines.flatMap((line) => wrapTextWithAnsi(line, contentWidth))\n : this.lines.map((line) => truncateToWidth(line, contentWidth, ELLIPSIS));\n if (this.layout.frameColor) {\n rendered.unshift(this.theme.fg(this.layout.frameColor, '─'.repeat(contentWidth)));\n }\n const padded = rendered.map((line) => ` ${line}`);\n if (this.layout.trailingBlank) padded.push('');\n return padded;\n }\n\n invalidate(): void {\n // The host rebuilds tool renderers when the theme changes.\n }\n}\n\nfunction callDetails(params: SubagentToolParams, theme: Theme): string {\n switch (params.action) {\n case 'agents':\n return params.name ? theme.fg('accent', params.name) : '';\n case 'run': {\n const agents = params.requests.map((request) => request.agent);\n const count = `${agents.length} agent${agents.length === 1 ? '' : 's'}`;\n return theme.fg('muted', `${count} · ${agents.join(', ')}`);\n }\n case 'status':\n return theme.fg('accent', 'id' in params ? params.id : 'fleet');\n case 'steer':\n case 'stop':\n case 'restore':\n return theme.fg('accent', params.id);\n case 'suspended':\n return theme.fg('muted', 'runs');\n }\n}\n\nexport function renderSubagentCall(params: SubagentToolParams, theme: Theme): Component {\n const heading = renderToolHeading('subagent', params.action, theme);\n const detail = callDetails(params, theme);\n return new DoomToolCall(`${heading}${detail ? ` ${detail}` : ''}`);\n}\n\nexport interface SubagentResultLike {\n content?: Array<{ type: string; text?: string }>;\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface SubagentResultOptions {\n expanded: boolean;\n isPartial: boolean;\n}\n\nfunction contentLines(result: SubagentResultLike): string[] {\n const text = (result.content ?? []).map((block) => block.text ?? '').join('');\n const lines = text.split('\\n');\n while (lines.length > 0 && lines.at(-1)?.trim() === '') lines.pop();\n return lines;\n}\n\nfunction framedResult(lines: readonly string[], wrap: boolean, theme: Theme): Component {\n return new SubagentToolText(lines, { wrap, frameColor: 'borderMuted', trailingBlank: true }, theme);\n}\n\nexport function renderSubagentResult(\n result: SubagentResultLike,\n options: SubagentResultOptions,\n theme: Theme,\n): Component {\n const all = contentLines(result);\n if (options.isPartial) {\n const body = all.slice(-COLLAPSED_RESULT_LINES).map((line) => theme.fg('toolOutput', line));\n return framedResult([...body, theme.fg('warning', '◐') + theme.fg('dim', ' running')], false, theme);\n }\n\n const failed = result.isError === true;\n const shown = options.expanded ? all : all.slice(0, COLLAPSED_RESULT_LINES);\n const body = shown.map((line) => theme.fg('toolOutput', line));\n if (failed) {\n body.push(theme.fg('error', '✗') + theme.fg('dim', ' failed'));\n } else if (!options.expanded && all.length > shown.length) {\n body.push(theme.fg('dim', '… ctrl+o'));\n } else if (body.length === 0) {\n body.push(theme.fg('success', '✓') + theme.fg('dim', ' done'));\n }\n return framedResult(body, options.expanded, theme);\n}\n"],"mappings":"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"}
|