@agimon-ai/doompi-team 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +25 -0
- package/README.md +46 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.cjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.mjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.cjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.mjs +1 -0
- package/dist/_virtual/_rolldown/runtime.cjs +1 -0
- package/dist/_virtual/_rolldown/runtime.mjs +1 -0
- package/dist/agents/discovery.cjs +2 -0
- package/dist/agents/discovery.cjs.map +1 -0
- package/dist/agents/discovery.mjs +2 -0
- package/dist/agents/discovery.mjs.map +1 -0
- package/dist/agents/frontmatter.cjs +10 -0
- package/dist/agents/frontmatter.cjs.map +1 -0
- package/dist/agents/frontmatter.mjs +10 -0
- package/dist/agents/frontmatter.mjs.map +1 -0
- package/dist/agents/loader.cjs +2 -0
- package/dist/agents/loader.cjs.map +1 -0
- package/dist/agents/loader.mjs +2 -0
- package/dist/agents/loader.mjs.map +1 -0
- package/dist/agents/memory.cjs +3 -0
- package/dist/agents/memory.cjs.map +1 -0
- package/dist/agents/memory.mjs +3 -0
- package/dist/agents/memory.mjs.map +1 -0
- package/dist/agents/projectRoot.cjs +2 -0
- package/dist/agents/projectRoot.cjs.map +1 -0
- package/dist/agents/projectRoot.mjs +2 -0
- package/dist/agents/projectRoot.mjs.map +1 -0
- package/dist/agents/settings.cjs +2 -0
- package/dist/agents/settings.cjs.map +1 -0
- package/dist/agents/settings.mjs +2 -0
- package/dist/agents/settings.mjs.map +1 -0
- package/dist/agents/skills.cjs +0 -0
- package/dist/agents/skills.cjs.map +1 -0
- package/dist/agents/skills.mjs +5 -0
- package/dist/agents/skills.mjs.map +1 -0
- package/dist/agents/types.d.cts +121 -0
- package/dist/agents/types.d.cts.map +1 -0
- package/dist/agents/types.d.mts +121 -0
- package/dist/agents/types.d.mts.map +1 -0
- package/dist/api/backgroundWork.cjs +1 -0
- package/dist/api/backgroundWork.d.cts +2 -0
- package/dist/api/backgroundWork.d.mts +2 -0
- package/dist/api/backgroundWork.mjs +1 -0
- package/dist/api/capabilityCeiling.cjs +1 -0
- package/dist/api/capabilityCeiling.d.cts +3 -0
- package/dist/api/capabilityCeiling.d.mts +3 -0
- package/dist/api/capabilityCeiling.mjs +2 -0
- package/dist/api/capabilityCeiling.mjs.map +1 -0
- package/dist/api/delegation.cjs +1 -0
- package/dist/api/delegation.d.cts +2 -0
- package/dist/api/delegation.d.mts +2 -0
- package/dist/api/delegation.mjs +1 -0
- package/dist/api/teamSnapshot.cjs +1 -0
- package/dist/api/teamSnapshot.d.cts +2 -0
- package/dist/api/teamSnapshot.d.mts +2 -0
- package/dist/api/teamSnapshot.mjs +2 -0
- package/dist/api/teamSnapshot.mjs.map +1 -0
- package/dist/container/bindOrRebind.cjs +2 -0
- package/dist/container/bindOrRebind.cjs.map +1 -0
- package/dist/container/bindOrRebind.mjs +2 -0
- package/dist/container/bindOrRebind.mjs.map +1 -0
- package/dist/container/globalBridge.cjs +2 -0
- package/dist/container/globalBridge.cjs.map +1 -0
- package/dist/container/globalBridge.d.cts +19 -0
- package/dist/container/globalBridge.d.cts.map +1 -0
- package/dist/container/globalBridge.d.mts +19 -0
- package/dist/container/globalBridge.d.mts.map +1 -0
- package/dist/container/globalBridge.mjs +2 -0
- package/dist/container/globalBridge.mjs.map +1 -0
- package/dist/container/index.cjs +2 -0
- package/dist/container/index.cjs.map +1 -0
- package/dist/container/index.d.cts +38 -0
- package/dist/container/index.d.cts.map +1 -0
- package/dist/container/index.d.mts +38 -0
- package/dist/container/index.d.mts.map +1 -0
- package/dist/container/index.mjs +2 -0
- package/dist/container/index.mjs.map +1 -0
- package/dist/container/modules/agents.cjs +2 -0
- package/dist/container/modules/agents.cjs.map +1 -0
- package/dist/container/modules/agents.d.cts +1 -0
- package/dist/container/modules/agents.d.mts +1 -0
- package/dist/container/modules/agents.mjs +2 -0
- package/dist/container/modules/agents.mjs.map +1 -0
- package/dist/container/modules/core.cjs +2 -0
- package/dist/container/modules/core.cjs.map +1 -0
- package/dist/container/modules/core.d.cts +1 -0
- package/dist/container/modules/core.d.mts +1 -0
- package/dist/container/modules/core.mjs +2 -0
- package/dist/container/modules/core.mjs.map +1 -0
- package/dist/container/modules/extensions.cjs +2 -0
- package/dist/container/modules/extensions.cjs.map +1 -0
- package/dist/container/modules/extensions.mjs +2 -0
- package/dist/container/modules/extensions.mjs.map +1 -0
- package/dist/container/modules/intercom.cjs +2 -0
- package/dist/container/modules/intercom.cjs.map +1 -0
- package/dist/container/modules/intercom.mjs +2 -0
- package/dist/container/modules/intercom.mjs.map +1 -0
- package/dist/container/modules/runs.cjs +2 -0
- package/dist/container/modules/runs.cjs.map +1 -0
- package/dist/container/modules/runs.mjs +2 -0
- package/dist/container/modules/runs.mjs.map +1 -0
- package/dist/container/types.cjs +2 -0
- package/dist/container/types.cjs.map +1 -0
- package/dist/container/types.d.cts +45 -0
- package/dist/container/types.d.cts.map +1 -0
- package/dist/container/types.d.mts +45 -0
- package/dist/container/types.d.mts.map +1 -0
- package/dist/container/types.mjs +2 -0
- package/dist/container/types.mjs.map +1 -0
- package/dist/env.cjs +2 -0
- package/dist/env.cjs.map +1 -0
- package/dist/env.d.cts +65 -0
- package/dist/env.d.cts.map +1 -0
- package/dist/env.d.mts +65 -0
- package/dist/env.d.mts.map +1 -0
- package/dist/env.mjs +2 -0
- package/dist/env.mjs.map +1 -0
- package/dist/extension.cjs +2 -0
- package/dist/extension.cjs.map +1 -0
- package/dist/extension.mjs +2 -0
- package/dist/extension.mjs.map +1 -0
- package/dist/extensions/config.cjs +2 -0
- package/dist/extensions/config.cjs.map +1 -0
- package/dist/extensions/config.mjs +2 -0
- package/dist/extensions/config.mjs.map +1 -0
- package/dist/extensions/delegationBridge.cjs +2 -0
- package/dist/extensions/delegationBridge.cjs.map +1 -0
- package/dist/extensions/delegationBridge.mjs +2 -0
- package/dist/extensions/delegationBridge.mjs.map +1 -0
- package/dist/extensions/doom.cjs +2 -0
- package/dist/extensions/doom.cjs.map +1 -0
- package/dist/extensions/doom.d.cts +8 -0
- package/dist/extensions/doom.d.cts.map +1 -0
- package/dist/extensions/doom.d.mts +8 -0
- package/dist/extensions/doom.d.mts.map +1 -0
- package/dist/extensions/doom.mjs +2 -0
- package/dist/extensions/doom.mjs.map +1 -0
- package/dist/extensions/fablePlanBridge.cjs +2 -0
- package/dist/extensions/fablePlanBridge.cjs.map +1 -0
- package/dist/extensions/fablePlanBridge.mjs +2 -0
- package/dist/extensions/fablePlanBridge.mjs.map +1 -0
- package/dist/extensions/managementActions.cjs +2 -0
- package/dist/extensions/managementActions.cjs.map +1 -0
- package/dist/extensions/managementActions.mjs +2 -0
- package/dist/extensions/managementActions.mjs.map +1 -0
- package/dist/extensions/operationJournal.cjs +2 -0
- package/dist/extensions/operationJournal.cjs.map +1 -0
- package/dist/extensions/operationJournal.mjs +2 -0
- package/dist/extensions/operationJournal.mjs.map +1 -0
- package/dist/extensions/orchestratorPrompt.cjs +35 -0
- package/dist/extensions/orchestratorPrompt.cjs.map +1 -0
- package/dist/extensions/orchestratorPrompt.mjs +35 -0
- package/dist/extensions/orchestratorPrompt.mjs.map +1 -0
- package/dist/extensions/pi.cjs +2 -0
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.cts +2 -0
- package/dist/extensions/pi.d.mts +2 -0
- package/dist/extensions/pi.mjs +2 -0
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/schemas.cjs +1 -0
- package/dist/extensions/schemas.mjs +2 -0
- package/dist/extensions/schemas.mjs.map +1 -0
- package/dist/extensions/spawnPlan.cjs +3 -0
- package/dist/extensions/spawnPlan.cjs.map +1 -0
- package/dist/extensions/spawnPlan.mjs +3 -0
- package/dist/extensions/spawnPlan.mjs.map +1 -0
- package/dist/extensions/standard.cjs +2 -0
- package/dist/extensions/standard.cjs.map +1 -0
- package/dist/extensions/standard.d.cts +8 -0
- package/dist/extensions/standard.d.cts.map +1 -0
- package/dist/extensions/standard.d.mts +8 -0
- package/dist/extensions/standard.d.mts.map +1 -0
- package/dist/extensions/standard.mjs +2 -0
- package/dist/extensions/standard.mjs.map +1 -0
- package/dist/extensions/statusViews.cjs +7 -0
- package/dist/extensions/statusViews.cjs.map +1 -0
- package/dist/extensions/statusViews.mjs +7 -0
- package/dist/extensions/statusViews.mjs.map +1 -0
- package/dist/extensions/subagentPromptRuntime.cjs +18 -0
- package/dist/extensions/subagentPromptRuntime.cjs.map +1 -0
- package/dist/extensions/subagentPromptRuntime.d.cts +58 -0
- package/dist/extensions/subagentPromptRuntime.d.cts.map +1 -0
- package/dist/extensions/subagentPromptRuntime.d.mts +58 -0
- package/dist/extensions/subagentPromptRuntime.d.mts.map +1 -0
- package/dist/extensions/subagentPromptRuntime.mjs +18 -0
- package/dist/extensions/subagentPromptRuntime.mjs.map +1 -0
- package/dist/extensions/subagentPromptRuntimeEntry.cjs +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.cjs.map +1 -0
- package/dist/extensions/subagentPromptRuntimeEntry.d.cts +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.d.mts +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.mjs +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.mjs.map +1 -0
- package/dist/extensions/subagentTool.cjs +7 -0
- package/dist/extensions/subagentTool.cjs.map +1 -0
- package/dist/extensions/subagentTool.mjs +7 -0
- package/dist/extensions/subagentTool.mjs.map +1 -0
- package/dist/extensions/toolDescription.cjs +2 -0
- package/dist/extensions/toolDescription.cjs.map +1 -0
- package/dist/extensions/toolDescription.mjs +2 -0
- package/dist/extensions/toolDescription.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +34 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +34 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/intercom/intercomBridge.cjs +2 -0
- package/dist/intercom/intercomBridge.cjs.map +1 -0
- package/dist/intercom/intercomBridge.mjs +2 -0
- package/dist/intercom/intercomBridge.mjs.map +1 -0
- package/dist/intercom/nativeTeamChannel.cjs +5 -0
- package/dist/intercom/nativeTeamChannel.cjs.map +1 -0
- package/dist/intercom/nativeTeamChannel.d.cts +22 -0
- package/dist/intercom/nativeTeamChannel.d.cts.map +1 -0
- package/dist/intercom/nativeTeamChannel.d.mts +22 -0
- package/dist/intercom/nativeTeamChannel.d.mts.map +1 -0
- package/dist/intercom/nativeTeamChannel.mjs +5 -0
- package/dist/intercom/nativeTeamChannel.mjs.map +1 -0
- package/dist/intercom/supervisorControlChannel.cjs +2 -0
- package/dist/intercom/supervisorControlChannel.cjs.map +1 -0
- package/dist/intercom/supervisorControlChannel.mjs +2 -0
- package/dist/intercom/supervisorControlChannel.mjs.map +1 -0
- package/dist/protocol/backgroundWork.cjs +2 -0
- package/dist/protocol/backgroundWork.cjs.map +1 -0
- package/dist/protocol/backgroundWork.d.cts +52 -0
- package/dist/protocol/backgroundWork.d.cts.map +1 -0
- package/dist/protocol/backgroundWork.d.mts +52 -0
- package/dist/protocol/backgroundWork.d.mts.map +1 -0
- package/dist/protocol/backgroundWork.mjs +2 -0
- package/dist/protocol/backgroundWork.mjs.map +1 -0
- package/dist/protocol/capabilityCeiling.cjs +2 -0
- package/dist/protocol/capabilityCeiling.cjs.map +1 -0
- package/dist/protocol/capabilityCeiling.d.cts +51 -0
- package/dist/protocol/capabilityCeiling.d.cts.map +1 -0
- package/dist/protocol/capabilityCeiling.d.mts +51 -0
- package/dist/protocol/capabilityCeiling.d.mts.map +1 -0
- package/dist/protocol/capabilityCeiling.mjs +2 -0
- package/dist/protocol/capabilityCeiling.mjs.map +1 -0
- package/dist/protocol/delegation.cjs +2 -0
- package/dist/protocol/delegation.cjs.map +1 -0
- package/dist/protocol/delegation.d.cts +269 -0
- package/dist/protocol/delegation.d.cts.map +1 -0
- package/dist/protocol/delegation.d.mts +269 -0
- package/dist/protocol/delegation.d.mts.map +1 -0
- package/dist/protocol/delegation.mjs +2 -0
- package/dist/protocol/delegation.mjs.map +1 -0
- package/dist/runs/background/asyncExecution.cjs +2 -0
- package/dist/runs/background/asyncExecution.cjs.map +1 -0
- package/dist/runs/background/asyncExecution.mjs +2 -0
- package/dist/runs/background/asyncExecution.mjs.map +1 -0
- package/dist/runs/background/asyncJobTracker.cjs +2 -0
- package/dist/runs/background/asyncJobTracker.cjs.map +1 -0
- package/dist/runs/background/asyncJobTracker.mjs +2 -0
- package/dist/runs/background/asyncJobTracker.mjs.map +1 -0
- package/dist/runs/background/cliRunnerEntry.cjs +2 -0
- package/dist/runs/background/cliRunnerEntry.cjs.map +1 -0
- package/dist/runs/background/cliRunnerEntry.d.cts +1 -0
- package/dist/runs/background/cliRunnerEntry.d.mts +1 -0
- package/dist/runs/background/cliRunnerEntry.mjs +2 -0
- package/dist/runs/background/cliRunnerEntry.mjs.map +1 -0
- package/dist/runs/background/completionBatcher.cjs +2 -0
- package/dist/runs/background/completionBatcher.cjs.map +1 -0
- package/dist/runs/background/completionBatcher.mjs +2 -0
- package/dist/runs/background/completionBatcher.mjs.map +1 -0
- package/dist/runs/background/notify.cjs +4 -0
- package/dist/runs/background/notify.cjs.map +1 -0
- package/dist/runs/background/notify.mjs +4 -0
- package/dist/runs/background/notify.mjs.map +1 -0
- package/dist/runs/background/parentWatchdog.cjs +2 -0
- package/dist/runs/background/parentWatchdog.cjs.map +1 -0
- package/dist/runs/background/parentWatchdog.mjs +2 -0
- package/dist/runs/background/parentWatchdog.mjs.map +1 -0
- package/dist/runs/background/processTerminal.cjs +2 -0
- package/dist/runs/background/processTerminal.cjs.map +1 -0
- package/dist/runs/background/processTerminal.mjs +2 -0
- package/dist/runs/background/processTerminal.mjs.map +1 -0
- package/dist/runs/background/resultWatcher.cjs +2 -0
- package/dist/runs/background/resultWatcher.cjs.map +1 -0
- package/dist/runs/background/resultWatcher.mjs +2 -0
- package/dist/runs/background/resultWatcher.mjs.map +1 -0
- package/dist/runs/background/runIdResolver.cjs +2 -0
- package/dist/runs/background/runIdResolver.cjs.map +1 -0
- package/dist/runs/background/runIdResolver.mjs +2 -0
- package/dist/runs/background/runIdResolver.mjs.map +1 -0
- package/dist/runs/background/runnerBootstrap.cjs +2 -0
- package/dist/runs/background/runnerBootstrap.cjs.map +1 -0
- package/dist/runs/background/runnerBootstrap.mjs +2 -0
- package/dist/runs/background/runnerBootstrap.mjs.map +1 -0
- package/dist/runs/background/runnerExecution.cjs +2 -0
- package/dist/runs/background/runnerExecution.cjs.map +1 -0
- package/dist/runs/background/runnerExecution.mjs +2 -0
- package/dist/runs/background/runnerExecution.mjs.map +1 -0
- package/dist/runs/background/runnerReporting.cjs +3 -0
- package/dist/runs/background/runnerReporting.cjs.map +1 -0
- package/dist/runs/background/runnerReporting.mjs +3 -0
- package/dist/runs/background/runnerReporting.mjs.map +1 -0
- package/dist/runs/background/runnerTeamMembership.cjs +2 -0
- package/dist/runs/background/runnerTeamMembership.cjs.map +1 -0
- package/dist/runs/background/runnerTeamMembership.mjs +2 -0
- package/dist/runs/background/runnerTeamMembership.mjs.map +1 -0
- package/dist/runs/background/spawnHandshake.cjs +2 -0
- package/dist/runs/background/spawnHandshake.cjs.map +1 -0
- package/dist/runs/background/spawnHandshake.mjs +2 -0
- package/dist/runs/background/spawnHandshake.mjs.map +1 -0
- package/dist/runs/background/staleRunReconciler.cjs +2 -0
- package/dist/runs/background/staleRunReconciler.cjs.map +1 -0
- package/dist/runs/background/staleRunReconciler.mjs +2 -0
- package/dist/runs/background/staleRunReconciler.mjs.map +1 -0
- package/dist/runs/background/statusReader.cjs +2 -0
- package/dist/runs/background/statusReader.cjs.map +1 -0
- package/dist/runs/background/statusReader.mjs +2 -0
- package/dist/runs/background/statusReader.mjs.map +1 -0
- package/dist/runs/background/statusWriter.cjs +2 -0
- package/dist/runs/background/statusWriter.cjs.map +1 -0
- package/dist/runs/background/statusWriter.mjs +2 -0
- package/dist/runs/background/statusWriter.mjs.map +1 -0
- package/dist/runs/background/subagentWait.cjs +2 -0
- package/dist/runs/background/subagentWait.cjs.map +1 -0
- package/dist/runs/background/subagentWait.mjs +2 -0
- package/dist/runs/background/subagentWait.mjs.map +1 -0
- package/dist/runs/background/terminalPersistence.cjs +2 -0
- package/dist/runs/background/terminalPersistence.cjs.map +1 -0
- package/dist/runs/background/terminalPersistence.mjs +2 -0
- package/dist/runs/background/terminalPersistence.mjs.map +1 -0
- package/dist/runs/registry/processGroup.cjs +2 -0
- package/dist/runs/registry/processGroup.cjs.map +1 -0
- package/dist/runs/registry/processGroup.mjs +2 -0
- package/dist/runs/registry/processGroup.mjs.map +1 -0
- package/dist/runs/registry/runRegistry.cjs +2 -0
- package/dist/runs/registry/runRegistry.cjs.map +1 -0
- package/dist/runs/registry/runRegistry.mjs +2 -0
- package/dist/runs/registry/runRegistry.mjs.map +1 -0
- package/dist/runs/registry/sessionLifecycle.cjs +2 -0
- package/dist/runs/registry/sessionLifecycle.cjs.map +1 -0
- package/dist/runs/registry/sessionLifecycle.mjs +2 -0
- package/dist/runs/registry/sessionLifecycle.mjs.map +1 -0
- package/dist/runs/registry/suspendedRuns.cjs +4 -0
- package/dist/runs/registry/suspendedRuns.cjs.map +1 -0
- package/dist/runs/registry/suspendedRuns.mjs +4 -0
- package/dist/runs/registry/suspendedRuns.mjs.map +1 -0
- package/dist/runs/sdkRunnerEntry.cjs +2 -0
- package/dist/runs/sdkRunnerEntry.cjs.map +1 -0
- package/dist/runs/sdkRunnerEntry.d.cts +1 -0
- package/dist/runs/sdkRunnerEntry.d.mts +1 -0
- package/dist/runs/sdkRunnerEntry.mjs +2 -0
- package/dist/runs/sdkRunnerEntry.mjs.map +1 -0
- package/dist/runs/shared/claudeFableProfile.cjs +4 -0
- package/dist/runs/shared/claudeFableProfile.cjs.map +1 -0
- package/dist/runs/shared/claudeFableProfile.mjs +4 -0
- package/dist/runs/shared/claudeFableProfile.mjs.map +1 -0
- package/dist/runs/shared/depthGuard.cjs +2 -0
- package/dist/runs/shared/depthGuard.cjs.map +1 -0
- package/dist/runs/shared/depthGuard.mjs +2 -0
- package/dist/runs/shared/depthGuard.mjs.map +1 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.cjs +2 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.cjs.map +1 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.mjs +2 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.mjs.map +1 -0
- package/dist/runs/shared/modelFallback.cjs +2 -0
- package/dist/runs/shared/modelFallback.cjs.map +1 -0
- package/dist/runs/shared/modelFallback.mjs +2 -0
- package/dist/runs/shared/modelFallback.mjs.map +1 -0
- package/dist/runs/shared/modelScope.cjs +2 -0
- package/dist/runs/shared/modelScope.cjs.map +1 -0
- package/dist/runs/shared/modelScope.mjs +2 -0
- package/dist/runs/shared/modelScope.mjs.map +1 -0
- package/dist/runs/shared/nestedPath.cjs +2 -0
- package/dist/runs/shared/nestedPath.cjs.map +1 -0
- package/dist/runs/shared/nestedPath.mjs +2 -0
- package/dist/runs/shared/nestedPath.mjs.map +1 -0
- package/dist/runs/shared/piArgs.cjs +2 -0
- package/dist/runs/shared/piArgs.cjs.map +1 -0
- package/dist/runs/shared/piArgs.mjs +2 -0
- package/dist/runs/shared/piArgs.mjs.map +1 -0
- package/dist/runs/shared/piSpawn.cjs +2 -0
- package/dist/runs/shared/piSpawn.cjs.map +1 -0
- package/dist/runs/shared/piSpawn.mjs +2 -0
- package/dist/runs/shared/piSpawn.mjs.map +1 -0
- package/dist/runs/shared/runWithConcurrency.cjs +2 -0
- package/dist/runs/shared/runWithConcurrency.cjs.map +1 -0
- package/dist/runs/shared/runWithConcurrency.mjs +2 -0
- package/dist/runs/shared/runWithConcurrency.mjs.map +1 -0
- package/dist/runs/shared/runtimeRegistry.cjs +2 -0
- package/dist/runs/shared/runtimeRegistry.cjs.map +1 -0
- package/dist/runs/shared/runtimeRegistry.mjs +2 -0
- package/dist/runs/shared/runtimeRegistry.mjs.map +1 -0
- package/dist/runs/shared/structuredOutput.cjs +2 -0
- package/dist/runs/shared/structuredOutput.cjs.map +1 -0
- package/dist/runs/shared/structuredOutput.mjs +2 -0
- package/dist/runs/shared/structuredOutput.mjs.map +1 -0
- package/dist/runs/shared/toolAvailability.cjs +2 -0
- package/dist/runs/shared/toolAvailability.cjs.map +1 -0
- package/dist/runs/shared/toolAvailability.mjs +2 -0
- package/dist/runs/shared/toolAvailability.mjs.map +1 -0
- package/dist/runs/shared/toolBudget.cjs +2 -0
- package/dist/runs/shared/toolBudget.cjs.map +1 -0
- package/dist/runs/shared/toolBudget.mjs +2 -0
- package/dist/runs/shared/toolBudget.mjs.map +1 -0
- package/dist/shared/artifacts.cjs +2 -0
- package/dist/shared/artifacts.cjs.map +1 -0
- package/dist/shared/artifacts.mjs +2 -0
- package/dist/shared/artifacts.mjs.map +1 -0
- package/dist/shared/atomicJson.cjs +2 -0
- package/dist/shared/atomicJson.cjs.map +1 -0
- package/dist/shared/atomicJson.mjs +2 -0
- package/dist/shared/atomicJson.mjs.map +1 -0
- package/dist/shared/boundedKeySet.cjs +2 -0
- package/dist/shared/boundedKeySet.cjs.map +1 -0
- package/dist/shared/boundedKeySet.mjs +2 -0
- package/dist/shared/boundedKeySet.mjs.map +1 -0
- package/dist/shared/childTranscript.cjs +5 -0
- package/dist/shared/childTranscript.cjs.map +1 -0
- package/dist/shared/childTranscript.mjs +5 -0
- package/dist/shared/childTranscript.mjs.map +1 -0
- package/dist/shared/configDir.cjs +2 -0
- package/dist/shared/configDir.cjs.map +1 -0
- package/dist/shared/configDir.mjs +2 -0
- package/dist/shared/configDir.mjs.map +1 -0
- package/dist/shared/errors.cjs +2 -0
- package/dist/shared/errors.cjs.map +1 -0
- package/dist/shared/errors.mjs +2 -0
- package/dist/shared/errors.mjs.map +1 -0
- package/dist/shared/fileSystemRetry.cjs +2 -0
- package/dist/shared/fileSystemRetry.cjs.map +1 -0
- package/dist/shared/fileSystemRetry.mjs +2 -0
- package/dist/shared/fileSystemRetry.mjs.map +1 -0
- package/dist/shared/formatters.cjs +2 -0
- package/dist/shared/formatters.cjs.map +1 -0
- package/dist/shared/formatters.mjs +2 -0
- package/dist/shared/formatters.mjs.map +1 -0
- package/dist/shared/lruCache.cjs +2 -0
- package/dist/shared/lruCache.cjs.map +1 -0
- package/dist/shared/lruCache.mjs +2 -0
- package/dist/shared/lruCache.mjs.map +1 -0
- package/dist/shared/modelInfo.cjs +2 -0
- package/dist/shared/modelInfo.cjs.map +1 -0
- package/dist/shared/modelInfo.mjs +2 -0
- package/dist/shared/modelInfo.mjs.map +1 -0
- package/dist/shared/modelResolution.cjs +2 -0
- package/dist/shared/modelResolution.cjs.map +1 -0
- package/dist/shared/modelResolution.mjs +2 -0
- package/dist/shared/modelResolution.mjs.map +1 -0
- package/dist/shared/paths.cjs +2 -0
- package/dist/shared/paths.cjs.map +1 -0
- package/dist/shared/paths.mjs +2 -0
- package/dist/shared/paths.mjs.map +1 -0
- package/dist/shared/pollScheduler.cjs +2 -0
- package/dist/shared/pollScheduler.cjs.map +1 -0
- package/dist/shared/pollScheduler.mjs +2 -0
- package/dist/shared/pollScheduler.mjs.map +1 -0
- package/dist/shared/scopeOwner.cjs +2 -0
- package/dist/shared/scopeOwner.cjs.map +1 -0
- package/dist/shared/scopeOwner.mjs +2 -0
- package/dist/shared/scopeOwner.mjs.map +1 -0
- package/dist/shared/versioned.cjs +2 -0
- package/dist/shared/versioned.cjs.map +1 -0
- package/dist/shared/versioned.mjs +2 -0
- package/dist/shared/versioned.mjs.map +1 -0
- package/dist/slash/chainExpression.cjs +2 -0
- package/dist/slash/chainExpression.cjs.map +1 -0
- package/dist/slash/chainExpression.mjs +2 -0
- package/dist/slash/chainExpression.mjs.map +1 -0
- package/dist/slash/doctor.cjs +5 -0
- package/dist/slash/doctor.cjs.map +1 -0
- package/dist/slash/doctor.mjs +5 -0
- package/dist/slash/doctor.mjs.map +1 -0
- package/dist/slash/slashCommands.cjs +4 -0
- package/dist/slash/slashCommands.cjs.map +1 -0
- package/dist/slash/slashCommands.mjs +4 -0
- package/dist/slash/slashCommands.mjs.map +1 -0
- package/dist/slash/spawnRequestMapping.cjs +2 -0
- package/dist/slash/spawnRequestMapping.cjs.map +1 -0
- package/dist/slash/spawnRequestMapping.mjs +2 -0
- package/dist/slash/spawnRequestMapping.mjs.map +1 -0
- package/dist/slash/subagentLaunch.cjs +2 -0
- package/dist/slash/subagentLaunch.cjs.map +1 -0
- package/dist/slash/subagentLaunch.mjs +2 -0
- package/dist/slash/subagentLaunch.mjs.map +1 -0
- package/dist/tui/agentCatalog.cjs +2 -0
- package/dist/tui/agentCatalog.cjs.map +1 -0
- package/dist/tui/agentCatalog.mjs +2 -0
- package/dist/tui/agentCatalog.mjs.map +1 -0
- package/dist/tui/agentResourceProjection.cjs +2 -0
- package/dist/tui/agentResourceProjection.cjs.map +1 -0
- package/dist/tui/agentResourceProjection.mjs +2 -0
- package/dist/tui/agentResourceProjection.mjs.map +1 -0
- package/dist/tui/completionNotice.cjs +5 -0
- package/dist/tui/completionNotice.cjs.map +1 -0
- package/dist/tui/completionNotice.mjs +5 -0
- package/dist/tui/completionNotice.mjs.map +1 -0
- package/dist/tui/fleet.cjs +3 -0
- package/dist/tui/fleet.cjs.map +1 -0
- package/dist/tui/fleet.mjs +3 -0
- package/dist/tui/fleet.mjs.map +1 -0
- package/dist/tui/fleetAgentView.cjs +2 -0
- package/dist/tui/fleetAgentView.cjs.map +1 -0
- package/dist/tui/fleetAgentView.mjs +2 -0
- package/dist/tui/fleetAgentView.mjs.map +1 -0
- package/dist/tui/fleetStatus.cjs +2 -0
- package/dist/tui/fleetStatus.cjs.map +1 -0
- package/dist/tui/fleetStatus.mjs +2 -0
- package/dist/tui/fleetStatus.mjs.map +1 -0
- package/dist/tui/fleetTranscript.cjs +5 -0
- package/dist/tui/fleetTranscript.cjs.map +1 -0
- package/dist/tui/fleetTranscript.mjs +5 -0
- package/dist/tui/fleetTranscript.mjs.map +1 -0
- package/dist/tui/register.cjs +2 -0
- package/dist/tui/register.cjs.map +1 -0
- package/dist/tui/register.mjs +2 -0
- package/dist/tui/register.mjs.map +1 -0
- package/dist/types.d.cts +59 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +59 -0
- package/dist/types.d.mts.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runWithConcurrency.mjs","names":[],"sources":["../../../src/runs/shared/runWithConcurrency.ts"],"sourcesContent":["/**\n * Runs a batch of async operations with at most N in flight at once, and\n * always settles all of them - one rejection never aborts its peers.\n *\n * WHY THIS EXISTS, RATHER THAN BEING INLINED INTO ITS ONE CALLER:\n * `spawnPlan.ts`'s PARALLEL mode needs exactly this shape (fire up to\n * `concurrency` tasks at once, wait for a slot to free before starting the\n * next, return every outcome regardless of individual failure), and it is a\n * property worth testing directly rather than only through spawn-plan's own\n * fixtures: \"at most N in flight at any moment\" and \"one rejection does not\n * abort its peers\" are each independently verifiable here, which is more\n * precise than inferring them from a higher-level test that also exercises\n * agent resolution, depth checks, and status writes.\n *\n * DELIBERATELY MINIMAL:\n * Bounded concurrency and settle-all, nothing else - no priority ordering\n * beyond FIFO start order, no cancellation, no `AbortSignal`. Add any of\n * those only when a real caller needs them, not speculatively.\n */\n\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nconst telemetry = createDoomTelemetry({\n serviceName: 'doom-team-concurrency',\n packageName: '@agimon-ai/doompi-team',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n});\n\nexport type ConcurrencyOutcome<T> = { status: 'fulfilled'; value: T } | { status: 'rejected'; reason: unknown };\n\n/**\n * Runs `factories` (each called at most once, lazily, only once a slot is\n * free) with at most `concurrency` in flight at a time. Resolves once every\n * factory has settled, in the SAME ORDER as `factories` regardless of which\n * one finished first - a caller zipping results back against its own input\n * list does not need to track indices itself.\n */\nexport async function runWithConcurrency<T>(\n factories: ReadonlyArray<() => Promise<T>>,\n concurrency: number,\n): Promise<ConcurrencyOutcome<T>[]> {\n const startedAt = Date.now();\n const results: ConcurrencyOutcome<T>[] = Array.from({ length: factories.length });\n const effectiveConcurrency = Math.max(1, Math.min(concurrency, factories.length || 1));\n let nextIndex = 0;\n\n async function worker(): Promise<void> {\n for (;;) {\n const index = nextIndex;\n nextIndex += 1;\n if (index >= factories.length) return;\n try {\n results[index] = { status: 'fulfilled', value: await factories[index]!() };\n } catch (reason) {\n results[index] = { status: 'rejected', reason };\n }\n }\n }\n\n void telemetry.recordEvent('doom_team.concurrency_started', {\n 'team.task_count': factories.length,\n 'team.concurrency': effectiveConcurrency,\n });\n await Promise.all(Array.from({ length: effectiveConcurrency }, () => worker()));\n void telemetry.recordEvent('doom_team.concurrency_finished', {\n 'team.task_count': factories.length,\n 'team.concurrency': effectiveConcurrency,\n 'team.failure_count': results.filter((result) => result?.status === 'rejected').length,\n duration_ms: Date.now() - startedAt,\n outcome: 'completed',\n });\n return results;\n}\n"],"mappings":"kIAuCA,eAAsB,EACpB,EACA,EACkC,CAClC,IAAM,EAAY,KAAK,KAAK,CACtB,EAAmC,MAAM,KAAK,CAAE,OAAQ,EAAU,OAAQ,CAAC,CAC3E,EAAuB,KAAK,IAAI,EAAG,KAAK,IAAI,EAAa,EAAU,QAAU,EAAE,CAAC,CAClF,EAAY,EAEhB,eAAe,GAAwB,CACrC,OAAS,CACP,IAAM,EAAQ,EAEd,GADA,GAAa,EACT,GAAS,EAAU,OAAQ,OAC/B,GAAI,CACF,EAAQ,GAAS,CAAE,OAAQ,YAAa,MAAO,MAAM,EAAU,IAAS,CAAE,OACnE,EAAQ,CACf,EAAQ,GAAS,CAAE,OAAQ,WAAY,SAAQ,GAiBrD,OAZK,EAAU,YAAY,gCAAiC,CAC1D,kBAAmB,EAAU,OAC7B,mBAAoB,EACrB,CAAC,CACF,MAAM,QAAQ,IAAI,MAAM,KAAK,CAAE,OAAQ,EAAsB,KAAQ,GAAQ,CAAC,CAAC,CAC1E,EAAU,YAAY,iCAAkC,CAC3D,kBAAmB,EAAU,OAC7B,mBAAoB,EACpB,qBAAsB,EAAQ,OAAQ,GAAW,GAAQ,SAAW,WAAW,CAAC,OAChF,YAAa,KAAK,KAAK,CAAG,EAC1B,QAAS,YACV,CAAC,CACK,kBAnDH,EAAY,EAAoB,CACpC,YAAa,wBACb,YAAa,yBACb,IAAK,QAAQ,IACb,WAAY,GACZ,aAAc,GACf,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../../env.cjs`),t={claude:{command:`claude`,args:[`-p`,`{prompt}`]},antigravity:{command:`antigravity`,args:[`{prompt}`]}};function n(e){return(e??`pi`).trim()===`pi`}function r(e){return e?{...t,...e}:{...t}}function i(t,n,r){return r[e.runtimeBinaryEnvVar(t)]?.trim()||n.command}function a(e,t){return e.replaceAll(`{prompt}`,t.prompt).replaceAll(`{model}`,t.model??``).replaceAll(`{cwd}`,t.cwd)}function o(e,t,n,r=process.env){let o=t[e];if(!o){let n=Object.keys(t).sort().join(`, `)||`(none configured)`;throw Error(`Unknown subagent runtime '${e}'. Configured runtimes: ${n}.`)}return{runtime:e,command:i(e,o,r),args:o.args.map(e=>a(e,n)).filter(e=>e.length>0)}}exports.isPiRuntime=n,exports.resolveRuntimeLaunch=o,exports.resolveRuntimeTable=r;
|
|
2
|
+
//# sourceMappingURL=runtimeRegistry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeRegistry.cjs","names":["runtimeBinaryEnvVar"],"sources":["../../../src/runs/shared/runtimeRegistry.ts"],"sourcesContent":["/**\n * Which agent runtime executes a run, and how to invoke it.\n *\n * WHY THIS IS CONFIG AND NOT A CLASS PER VENDOR:\n * Every external agent CLI is the same shape from here: a binary, an argv with\n * the prompt substituted in, a working directory, and whatever it prints. There\n * is no per-vendor behaviour worth a per-vendor module, and inventing an\n * adapter interface would mean a new file, a new registration and a new test\n * suite every time someone wants to point a run at a different command. Adding\n * or retargeting a CLI is a config edit.\n *\n * WHAT THIS DELIBERATELY DOES NOT DO:\n * It does not parse vendor output. A generic runner cannot know what a given\n * CLI's JSON stream means, and pretending to would either lock the config down\n * to the two CLIs someone happened to test, or silently mis-report a third. The\n * run's final output is its output; status comes from the run lifecycle, which\n * already knows queued/running/terminal without reading a word of it.\n *\n * `pi` IS NOT IN THIS TABLE:\n * It is not an external command - it runs in-process through the SDK\n * (`runs/sdkRunnerEntry.ts`) and gets the team channel, steering and\n * structured output that an external one-shot cannot. Treating it as one more\n * row here would advertise capabilities the external path does not have.\n *\n * AVOID:\n * - Putting a secret in an argv template; argv is visible in the process table\n * - Adding a placeholder without substituting it. An unsubstituted `{prompt}`\n * reaches the CLI literally\n */\n\nimport { PI_RUNTIME_NAME, runtimeBinaryEnvVar } from '../../env.ts';\n\n/** One external CLI, as configured. */\nexport interface RuntimeDefinition {\n /** Binary to execute. Resolved against PATH unless overridden by env. */\n command: string;\n /** Argv template. `{prompt}`, `{model}` and `{cwd}` are substituted per run. */\n args: string[];\n}\n\nexport type RuntimeTable = Record<string, RuntimeDefinition>;\n\n/**\n * Shipped defaults, overridable per entry by config.\n *\n * Both are headless one-shot invocations: the prompt goes in, the final answer\n * comes out on stdout, and the process exits. Neither is asked for a structured\n * stream, because nothing here would read it.\n */\nexport const DEFAULT_RUNTIMES: RuntimeTable = {\n claude: { command: 'claude', args: ['-p', '{prompt}'] },\n antigravity: { command: 'antigravity', args: ['{prompt}'] },\n};\n\nexport interface ResolvedRuntimeLaunch {\n runtime: string;\n command: string;\n args: string[];\n}\n\nexport interface RuntimeSubstitutions {\n prompt: string;\n model?: string;\n cwd: string;\n}\n\n/** True for the in-process SDK path, which this module does not describe. */\nexport function isPiRuntime(runtime: string | undefined): boolean {\n return (runtime ?? PI_RUNTIME_NAME).trim() === PI_RUNTIME_NAME;\n}\n\nexport function resolveRuntimeTable(configured: RuntimeTable | undefined): RuntimeTable {\n if (!configured) return { ...DEFAULT_RUNTIMES };\n return { ...DEFAULT_RUNTIMES, ...configured };\n}\n\n/**\n * The binary to run, most explicit source first.\n *\n * The env override exists for the same reason `piSpawn.ts` has one: a\n * developer testing against a locally built CLI must be able to point a run at\n * it without editing shared config.\n */\nfunction resolveCommand(runtime: string, definition: RuntimeDefinition, env: NodeJS.ProcessEnv): string {\n const override = env[runtimeBinaryEnvVar(runtime)]?.trim();\n return override || definition.command;\n}\n\nfunction substitute(template: string, values: RuntimeSubstitutions): string {\n return template\n .replaceAll('{prompt}', values.prompt)\n .replaceAll('{model}', values.model ?? '')\n .replaceAll('{cwd}', values.cwd);\n}\n\n/**\n * Resolve one run's command line.\n *\n * An argument that reduces to the empty string is dropped rather than passed:\n * a template carrying `--model {model}` for a run with no model override would\n * otherwise hand the CLI a bare `--model` with nothing after it.\n */\nexport function resolveRuntimeLaunch(\n runtime: string,\n table: RuntimeTable,\n values: RuntimeSubstitutions,\n env: NodeJS.ProcessEnv = process.env,\n): ResolvedRuntimeLaunch {\n const definition = table[runtime];\n if (!definition) {\n const known = Object.keys(table).sort().join(', ') || '(none configured)';\n throw new Error(`Unknown subagent runtime '${runtime}'. Configured runtimes: ${known}.`);\n }\n return {\n runtime,\n command: resolveCommand(runtime, definition, env),\n args: definition.args.map((arg) => substitute(arg, values)).filter((arg) => arg.length > 0),\n };\n}\n"],"mappings":"iCAiDa,EAAiC,CAC5C,OAAQ,CAAE,QAAS,SAAU,KAAM,CAAC,KAAM,WAAW,CAAE,CACvD,YAAa,CAAE,QAAS,cAAe,KAAM,CAAC,WAAW,CAAE,CAC5D,CAeD,SAAgB,EAAY,EAAsC,CAChE,OAAQ,GAAA,MAA4B,MAAM,GAAA,KAG5C,SAAgB,EAAoB,EAAoD,CAEtF,OADK,EACE,CAAE,GAAG,EAAkB,GAAG,EAAY,CADrB,CAAE,GAAG,EAAkB,CAWjD,SAAS,EAAe,EAAiB,EAA+B,EAAgC,CAEtG,OADiB,EAAIA,EAAAA,oBAAoB,EAAQ,GAAG,MAAM,EACvC,EAAW,QAGhC,SAAS,EAAW,EAAkB,EAAsC,CAC1E,OAAO,EACJ,WAAW,WAAY,EAAO,OAAO,CACrC,WAAW,UAAW,EAAO,OAAS,GAAG,CACzC,WAAW,QAAS,EAAO,IAAI,CAUpC,SAAgB,EACd,EACA,EACA,EACA,EAAyB,QAAQ,IACV,CACvB,IAAM,EAAa,EAAM,GACzB,GAAI,CAAC,EAAY,CACf,IAAM,EAAQ,OAAO,KAAK,EAAM,CAAC,MAAM,CAAC,KAAK,KAAK,EAAI,oBACtD,MAAU,MAAM,6BAA6B,EAAQ,0BAA0B,EAAM,GAAG,CAE1F,MAAO,CACL,UACA,QAAS,EAAe,EAAS,EAAY,EAAI,CACjD,KAAM,EAAW,KAAK,IAAK,GAAQ,EAAW,EAAK,EAAO,CAAC,CAAC,OAAQ,GAAQ,EAAI,OAAS,EAAE,CAC5F"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../../_virtual/_rolldown/runtime.mjs";import{init_env as t,runtimeBinaryEnvVar as n}from"../../env.mjs";function r(e){return(e??`pi`).trim()===`pi`}function i(e){return e?{...c,...e}:{...c}}function a(e,t,r){return r[n(e)]?.trim()||t.command}function o(e,t){return e.replaceAll(`{prompt}`,t.prompt).replaceAll(`{model}`,t.model??``).replaceAll(`{cwd}`,t.cwd)}function s(e,t,n,r=process.env){let i=t[e];if(!i){let n=Object.keys(t).sort().join(`, `)||`(none configured)`;throw Error(`Unknown subagent runtime '${e}'. Configured runtimes: ${n}.`)}return{runtime:e,command:a(e,i,r),args:i.args.map(e=>o(e,n)).filter(e=>e.length>0)}}var c,l=e((()=>{t(),c={claude:{command:`claude`,args:[`-p`,`{prompt}`]},antigravity:{command:`antigravity`,args:[`{prompt}`]}}}));l();export{l as init_runtimeRegistry,r as isPiRuntime,s as resolveRuntimeLaunch,i as resolveRuntimeTable};
|
|
2
|
+
//# sourceMappingURL=runtimeRegistry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeRegistry.mjs","names":[],"sources":["../../../src/runs/shared/runtimeRegistry.ts"],"sourcesContent":["/**\n * Which agent runtime executes a run, and how to invoke it.\n *\n * WHY THIS IS CONFIG AND NOT A CLASS PER VENDOR:\n * Every external agent CLI is the same shape from here: a binary, an argv with\n * the prompt substituted in, a working directory, and whatever it prints. There\n * is no per-vendor behaviour worth a per-vendor module, and inventing an\n * adapter interface would mean a new file, a new registration and a new test\n * suite every time someone wants to point a run at a different command. Adding\n * or retargeting a CLI is a config edit.\n *\n * WHAT THIS DELIBERATELY DOES NOT DO:\n * It does not parse vendor output. A generic runner cannot know what a given\n * CLI's JSON stream means, and pretending to would either lock the config down\n * to the two CLIs someone happened to test, or silently mis-report a third. The\n * run's final output is its output; status comes from the run lifecycle, which\n * already knows queued/running/terminal without reading a word of it.\n *\n * `pi` IS NOT IN THIS TABLE:\n * It is not an external command - it runs in-process through the SDK\n * (`runs/sdkRunnerEntry.ts`) and gets the team channel, steering and\n * structured output that an external one-shot cannot. Treating it as one more\n * row here would advertise capabilities the external path does not have.\n *\n * AVOID:\n * - Putting a secret in an argv template; argv is visible in the process table\n * - Adding a placeholder without substituting it. An unsubstituted `{prompt}`\n * reaches the CLI literally\n */\n\nimport { PI_RUNTIME_NAME, runtimeBinaryEnvVar } from '../../env.ts';\n\n/** One external CLI, as configured. */\nexport interface RuntimeDefinition {\n /** Binary to execute. Resolved against PATH unless overridden by env. */\n command: string;\n /** Argv template. `{prompt}`, `{model}` and `{cwd}` are substituted per run. */\n args: string[];\n}\n\nexport type RuntimeTable = Record<string, RuntimeDefinition>;\n\n/**\n * Shipped defaults, overridable per entry by config.\n *\n * Both are headless one-shot invocations: the prompt goes in, the final answer\n * comes out on stdout, and the process exits. Neither is asked for a structured\n * stream, because nothing here would read it.\n */\nexport const DEFAULT_RUNTIMES: RuntimeTable = {\n claude: { command: 'claude', args: ['-p', '{prompt}'] },\n antigravity: { command: 'antigravity', args: ['{prompt}'] },\n};\n\nexport interface ResolvedRuntimeLaunch {\n runtime: string;\n command: string;\n args: string[];\n}\n\nexport interface RuntimeSubstitutions {\n prompt: string;\n model?: string;\n cwd: string;\n}\n\n/** True for the in-process SDK path, which this module does not describe. */\nexport function isPiRuntime(runtime: string | undefined): boolean {\n return (runtime ?? PI_RUNTIME_NAME).trim() === PI_RUNTIME_NAME;\n}\n\nexport function resolveRuntimeTable(configured: RuntimeTable | undefined): RuntimeTable {\n if (!configured) return { ...DEFAULT_RUNTIMES };\n return { ...DEFAULT_RUNTIMES, ...configured };\n}\n\n/**\n * The binary to run, most explicit source first.\n *\n * The env override exists for the same reason `piSpawn.ts` has one: a\n * developer testing against a locally built CLI must be able to point a run at\n * it without editing shared config.\n */\nfunction resolveCommand(runtime: string, definition: RuntimeDefinition, env: NodeJS.ProcessEnv): string {\n const override = env[runtimeBinaryEnvVar(runtime)]?.trim();\n return override || definition.command;\n}\n\nfunction substitute(template: string, values: RuntimeSubstitutions): string {\n return template\n .replaceAll('{prompt}', values.prompt)\n .replaceAll('{model}', values.model ?? '')\n .replaceAll('{cwd}', values.cwd);\n}\n\n/**\n * Resolve one run's command line.\n *\n * An argument that reduces to the empty string is dropped rather than passed:\n * a template carrying `--model {model}` for a run with no model override would\n * otherwise hand the CLI a bare `--model` with nothing after it.\n */\nexport function resolveRuntimeLaunch(\n runtime: string,\n table: RuntimeTable,\n values: RuntimeSubstitutions,\n env: NodeJS.ProcessEnv = process.env,\n): ResolvedRuntimeLaunch {\n const definition = table[runtime];\n if (!definition) {\n const known = Object.keys(table).sort().join(', ') || '(none configured)';\n throw new Error(`Unknown subagent runtime '${runtime}'. Configured runtimes: ${known}.`);\n }\n return {\n runtime,\n command: resolveCommand(runtime, definition, env),\n args: definition.args.map((arg) => substitute(arg, values)).filter((arg) => arg.length > 0),\n };\n}\n"],"mappings":"kIAmEA,SAAgB,EAAY,EAAsC,CAChE,OAAQ,GAAA,MAA4B,MAAM,GAAA,KAG5C,SAAgB,EAAoB,EAAoD,CAEtF,OADK,EACE,CAAE,GAAG,EAAkB,GAAG,EAAY,CADrB,CAAE,GAAG,EAAkB,CAWjD,SAAS,EAAe,EAAiB,EAA+B,EAAgC,CAEtG,OADiB,EAAI,EAAoB,EAAQ,GAAG,MAAM,EACvC,EAAW,QAGhC,SAAS,EAAW,EAAkB,EAAsC,CAC1E,OAAO,EACJ,WAAW,WAAY,EAAO,OAAO,CACrC,WAAW,UAAW,EAAO,OAAS,GAAG,CACzC,WAAW,QAAS,EAAO,IAAI,CAUpC,SAAgB,EACd,EACA,EACA,EACA,EAAyB,QAAQ,IACV,CACvB,IAAM,EAAa,EAAM,GACzB,GAAI,CAAC,EAAY,CACf,IAAM,EAAQ,OAAO,KAAK,EAAM,CAAC,MAAM,CAAC,KAAK,KAAK,EAAI,oBACtD,MAAU,MAAM,6BAA6B,EAAQ,0BAA0B,EAAM,GAAG,CAE1F,MAAO,CACL,UACA,QAAS,EAAe,EAAS,EAAY,EAAI,CACjD,KAAM,EAAW,KAAK,IAAK,GAAQ,EAAW,EAAK,EAAO,CAAC,CAAC,OAAQ,GAAQ,EAAI,OAAS,EAAE,CAC5F,oBAvFiE,CAmBvD,EAAiC,CAC5C,OAAQ,CAAE,QAAS,SAAU,KAAM,CAAC,KAAM,WAAW,CAAE,CACvD,YAAa,CAAE,QAAS,cAAe,KAAM,CAAC,WAAW,CAAE,CAC5D"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../../_virtual/_rolldown/runtime.cjs`),t=require(`../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs`),n=require(`../../env.cjs`);require(`../../shared/paths.cjs`);let r=require(`node:fs`);r=e.__toESM(r,1);let i=require(`node:path`);i=e.__toESM(i,1);let a=require(`inversify`),o=require(`node:url`),s=require(`node:module`);const c=`value`,l=`#/properties/${c}`,u=[`properties`,`patternProperties`,`$defs`,`definitions`,`dependentSchemas`],d=[`additionalItems`,`additionalProperties`,`contains`,`not`,`propertyNames`,`if`,`then`,`else`,`unevaluatedItems`,`unevaluatedProperties`,`contentSchema`],f=[`allOf`,`anyOf`,`oneOf`,`prefixItems`],p=[`$ref`,`$dynamicRef`,`$recursiveRef`];function m(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function h(e,t,n=!0){if(typeof e==`boolean`||!m(e))return e;let r=e,i={...r},a=n&&typeof r.$id!=`string`;if(a)for(let e of p){let n=r[e];n===`#`?i[e]=t:typeof n==`string`&&n.startsWith(`#/`)&&(i[e]=`${t}${n.slice(1)}`)}for(let e of u){let n=r[e];m(n)&&(i[e]=Object.fromEntries(Object.entries(n).map(([e,n])=>[e,h(n,t,a)])))}let o=r.items;Array.isArray(o)?i.items=o.map(e=>h(e,t,a)):o!==void 0&&(i.items=h(o,t,a));for(let e of d)r[e]!==void 0&&(i[e]=h(r[e],t,a));for(let e of f){let n=r[e];Array.isArray(n)&&(i[e]=n.map(e=>h(e,t,a)))}let s=r.dependencies;return m(s)&&(i.dependencies=Object.fromEntries(Object.entries(s).map(([e,n])=>[e,Array.isArray(n)?n:h(n,t,a)]))),i}function g(e){return{type:`object`,properties:{[c]:h(e,l)},required:[c],additionalProperties:!1}}const _=`typebox/compile`,v=`Compile`;function y(e){return e instanceof Error?e.message:String(e)}function b(e){if(!m(e))return;let t=e[v];return typeof t==`function`?t:void 0}async function x(e){return b(await import((0,o.pathToFileURL)((0,s.createRequire)(i.join(e,`package.json`)).resolve(_)).href))}let S=class{cachedCompile;importCompileModule(){return import(_)}importCompileFromPackageRoot(e){return x(e)}async importCompile(){let e=[];try{let t=b(await this.importCompileModule());if(t)return t;e.push(`${_} did not export a ${v} function`)}catch(t){e.push(`direct import failed: ${y(t)}`)}let t=process.env[n.PI_CODING_AGENT_PACKAGE_ROOT_ENV];if(t)try{let n=await this.importCompileFromPackageRoot(t);if(n)return n;e.push(`Pi package root ${_} did not export a ${v} function`)}catch(t){e.push(`Pi package root import failed: ${y(t)}`)}else e.push(`${n.PI_CODING_AGENT_PACKAGE_ROOT_ENV} is not set`);throw Error(`Cannot load ${_} for structured output validation (${e.join(`; `)})`)}loadCompile(){return this.cachedCompile||=this.importCompile().catch(e=>{throw this.cachedCompile=void 0,e}),this.cachedCompile}reset(){this.cachedCompile=void 0}async validateValue(e,t){let n=await this.loadCompile(),r;try{r=n(e)}catch(e){return{status:`invalid`,message:`invalid outputSchema: ${y(e)}`}}return r.Check(t)?{status:`valid`}:{status:`invalid`,message:[...r.Errors(t)].slice(0,8).map(e=>`${e.instancePath?e.instancePath.replace(/^\//,``).replace(/\//g,`.`):`root`}: ${e.message}`).join(`; `)||`schema validation failed`}}async readStructuredOutput(e){if(!r.existsSync(e.outputPath))return{error:`Missing structured_output call; this step has outputSchema and must finish by calling structured_output.`};let t;try{t=JSON.parse(r.readFileSync(e.outputPath,`utf-8`))}catch(e){return{error:`Failed to read structured output: ${y(e)}`}}try{let n=await this.validateValue(e.schema,t);if(n.status===`invalid`)return{error:`Structured output validation failed: ${n.message}`}}catch(e){return{error:`Failed to validate structured output: ${y(e)}`}}return{value:t}}};S=t.__decorate([(0,a.injectable)()],S),Object.defineProperty(exports,`StructuredOutputValidator`,{enumerable:!0,get:function(){return S}}),exports.createStructuredOutputToolParameters=g;
|
|
2
|
+
//# sourceMappingURL=structuredOutput.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structuredOutput.cjs","names":["path","PI_CODING_AGENT_PACKAGE_ROOT_ENV","fs"],"sources":["../../../src/runs/shared/structuredOutput.ts"],"sourcesContent":["/**\n * Structured output: the schema a step must satisfy, and the file it answers in.\n *\n * A step with an `outputSchema` may not finish in prose. The runtime hands the\n * child a `structured_output` tool whose parameters are derived from the schema,\n * writes the schema to a private temp directory, and validates whatever the child\n * wrote back before the run is allowed to succeed.\n *\n * DESIGN PATTERNS:\n * - The author's schema is nested under a `value` property rather than used as\n * the tool parameters directly, because a tool's parameters must be an object\n * and an author is entitled to declare a top-level array or string\n * - Local JSON pointers are rewritten during that nesting. `#/$defs/x` addressed\n * the author's schema root, which is now one level down, so an unrewritten\n * pointer would resolve against the wrapper and silently validate nothing\n * - Every failure is returned as a message, never thrown. An invalid schema is an\n * authoring mistake to report against the run, not a crash of the parent\n *\n * PERFORMANCE:\n * `typebox/compile` is loaded once per process and memoised. It is a heavy\n * import and every validated step would otherwise pay for it. A failed load\n * clears the memo so a transient resolution failure is retried rather than\n * cached forever.\n *\n * AVOID:\n * - Joining `os.tmpdir()` here; the temp root belongs to `shared/paths.ts`\n * - Treating an absent output file as a validation failure. It means the child\n * never called the tool, which is a different diagnosis for the operator\n */\n\nimport * as fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport * as path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { injectable } from 'inversify';\n\nimport { PI_CODING_AGENT_PACKAGE_ROOT_ENV } from '../../env.ts';\nimport { TEMP_ROOT_DIR } from '../../shared/paths.ts';\nimport type { JsonSchemaObject } from '../../types.ts';\n\nexport { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV } from '../../env.ts';\n\nexport const MISSING_STRUCTURED_OUTPUT_CALL_ERROR =\n 'Missing structured_output call; this step has outputSchema and must finish by calling structured_output.';\n\n/** Where the schema and the child's answer live for one run. */\nexport interface StructuredOutputRuntime {\n schema: JsonSchemaObject;\n schemaPath: string;\n outputPath: string;\n}\n\n// ============================================================================\n// Wrapping an authored schema as tool parameters\n// ============================================================================\n\n/** The property the author's schema is nested under inside the tool parameters. */\nconst STRUCTURED_OUTPUT_VALUE_PROPERTY = 'value';\n/** JSON pointer that addresses the nested schema from the wrapper root. */\nconst STRUCTURED_OUTPUT_VALUE_POINTER = `#/properties/${STRUCTURED_OUTPUT_VALUE_PROPERTY}`;\n\n/** Keywords whose value is a map of names to subschemas. */\nconst SCHEMA_MAP_KEYWORDS = ['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas'] as const;\n/** Keywords whose value is a single subschema. */\nconst SCHEMA_SINGLE_KEYWORDS = [\n 'additionalItems',\n 'additionalProperties',\n 'contains',\n 'not',\n 'propertyNames',\n 'if',\n 'then',\n 'else',\n 'unevaluatedItems',\n 'unevaluatedProperties',\n 'contentSchema',\n] as const;\n/** Keywords whose value is an array of subschemas. */\nconst SCHEMA_ARRAY_KEYWORDS = ['allOf', 'anyOf', 'oneOf', 'prefixItems'] as const;\n\n/** Reference keywords that can carry a local pointer needing a rebase. */\nconst SCHEMA_REF_KEYWORDS = ['$ref', '$dynamicRef', '$recursiveRef'] as const;\n\n/** A pointer to the schema resource root, which moves when the schema is nested. */\nconst ROOT_POINTER = '#';\nconst LOCAL_POINTER_PREFIX = '#/';\n\nfunction isSchemaRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Rebase local JSON pointers so they still address the author's schema once it\n * has been nested under the wrapper.\n *\n * `$id` stops the descent: a subschema declaring one starts a new resource, so\n * its local pointers address itself and were never relative to the wrapper.\n * Boolean schemas (`true`/`false`) are legal JSON Schema and pass through.\n */\nfunction rewriteLocalJsonPointerRefs(schema: unknown, pointerPrefix: string, inheritsWrapperResource = true): unknown {\n if (typeof schema === 'boolean' || !isSchemaRecord(schema)) return schema;\n const source = schema;\n const rewritten: Record<string, unknown> = { ...source };\n const sharesWrapperResource = inheritsWrapperResource && typeof source.$id !== 'string';\n if (sharesWrapperResource) {\n for (const keyword of SCHEMA_REF_KEYWORDS) {\n const ref = source[keyword];\n if (ref === ROOT_POINTER) rewritten[keyword] = pointerPrefix;\n else if (typeof ref === 'string' && ref.startsWith(LOCAL_POINTER_PREFIX)) {\n rewritten[keyword] = `${pointerPrefix}${ref.slice(ROOT_POINTER.length)}`;\n }\n }\n }\n for (const keyword of SCHEMA_MAP_KEYWORDS) {\n const entries = source[keyword];\n if (!isSchemaRecord(entries)) continue;\n rewritten[keyword] = Object.fromEntries(\n Object.entries(entries).map(([name, nested]) => [\n name,\n rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource),\n ]),\n );\n }\n const items = source.items;\n if (Array.isArray(items)) {\n rewritten.items = items.map((nested) => rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource));\n } else if (items !== undefined) {\n rewritten.items = rewriteLocalJsonPointerRefs(items, pointerPrefix, sharesWrapperResource);\n }\n for (const keyword of SCHEMA_SINGLE_KEYWORDS) {\n if (source[keyword] !== undefined) {\n rewritten[keyword] = rewriteLocalJsonPointerRefs(source[keyword], pointerPrefix, sharesWrapperResource);\n }\n }\n for (const keyword of SCHEMA_ARRAY_KEYWORDS) {\n const branches = source[keyword];\n if (Array.isArray(branches)) {\n rewritten[keyword] = branches.map((nested) =>\n rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource),\n );\n }\n }\n const dependencies = source.dependencies;\n if (isSchemaRecord(dependencies)) {\n // Draft-07 `dependencies` is either a property-name list or a subschema.\n rewritten.dependencies = Object.fromEntries(\n Object.entries(dependencies).map(([name, nested]) => [\n name,\n Array.isArray(nested) ? nested : rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource),\n ]),\n );\n }\n return rewritten;\n}\n\n/** Parameters for the `structured_output` tool that satisfies `schema`. */\nexport function createStructuredOutputToolParameters(schema: JsonSchemaObject): JsonSchemaObject {\n return {\n type: 'object',\n properties: {\n [STRUCTURED_OUTPUT_VALUE_PROPERTY]: rewriteLocalJsonPointerRefs(schema, STRUCTURED_OUTPUT_VALUE_POINTER),\n },\n required: [STRUCTURED_OUTPUT_VALUE_PROPERTY],\n additionalProperties: false,\n };\n}\n\n// ============================================================================\n// Runtime files\n// ============================================================================\n\nconst STRUCTURED_OUTPUT_DIR_PREFIX = 'doom-team-structured-';\nconst SCHEMA_FILE_NAME = 'schema.json';\nconst OUTPUT_FILE_NAME = 'output.json';\n/** The schema can carry task detail, so it is readable only by its own user. */\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport function assertJsonSchemaObject(schema: unknown, label = 'outputSchema'): asserts schema is JsonSchemaObject {\n if (!isSchemaRecord(schema)) {\n throw new Error(`${label} must be a JSON Schema object.`);\n }\n}\n\n/** Stage the schema on disk and reserve the path the child answers in. */\nexport function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?: string): StructuredOutputRuntime {\n assertJsonSchemaObject(schema);\n const rootDir = baseDir ?? TEMP_ROOT_DIR;\n fs.mkdirSync(rootDir, { recursive: true });\n const dir = fs.mkdtempSync(path.join(rootDir, STRUCTURED_OUTPUT_DIR_PREFIX));\n const schemaPath = path.join(dir, SCHEMA_FILE_NAME);\n const outputPath = path.join(dir, OUTPUT_FILE_NAME);\n fs.writeFileSync(schemaPath, JSON.stringify(schema), { mode: PRIVATE_FILE_MODE });\n return { schema, schemaPath, outputPath };\n}\n\n/**\n * Remove the run's schema and output directory.\n *\n * The failure is returned rather than thrown or logged: the directory is under\n * the per-user temp root, so a leftover is harmless and must not fail a run that\n * has already produced its result. Returning it still lets a caller that cares,\n * such as a teardown check, see what happened instead of it vanishing.\n */\nexport function cleanupStructuredOutputRuntime(runtime: StructuredOutputRuntime | undefined): {\n removed: boolean;\n error?: string;\n} {\n if (!runtime) return { removed: false };\n try {\n fs.rmSync(path.dirname(runtime.schemaPath), { recursive: true, force: true });\n return { removed: true };\n } catch (error) {\n return { removed: false, error: toMessage(error) };\n }\n}\n\n// ============================================================================\n// Validation\n// ============================================================================\n\n/** The compiled-validator surface this module uses out of `typebox/compile`. */\ninterface CompiledJsonSchema {\n Check(value: unknown): boolean;\n Errors(value: unknown): Iterable<{ instancePath?: string; message?: string }>;\n}\n\ntype CompileJsonSchema = (schema: unknown) => CompiledJsonSchema;\n\nconst TYPEBOX_COMPILE_SPECIFIER = 'typebox/compile';\nconst COMPILE_EXPORT_NAME = 'Compile';\n/** How many schema violations are reported before the message is truncated. */\nconst MAX_REPORTED_SCHEMA_ERRORS = 8;\n/** Path shown for a violation at the document root. */\nconst ROOT_INSTANCE_PATH_LABEL = 'root';\n\nexport type StructuredOutputValidation = { status: 'valid' } | { status: 'invalid'; message: string };\n\nfunction toMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction readCompileExport(mod: unknown): CompileJsonSchema | undefined {\n if (!isSchemaRecord(mod)) return undefined;\n const compile = mod[COMPILE_EXPORT_NAME];\n return typeof compile === 'function' ? (compile as CompileJsonSchema) : undefined;\n}\n\n/**\n * Load `typebox/compile` out of the host Pi installation.\n *\n * Exported because the host package root is the only place the dependency is\n * guaranteed to resolve when this extension is loaded from outside the host's\n * own `node_modules` tree.\n */\nexport async function resolveCompileFromPackageRoot(packageRoot: string): Promise<CompileJsonSchema | undefined> {\n const requireFromRoot = createRequire(path.join(packageRoot, 'package.json'));\n const resolved = requireFromRoot.resolve(TYPEBOX_COMPILE_SPECIFIER);\n const mod: unknown = await import(pathToFileURL(resolved).href);\n return readCompileExport(mod);\n}\n\n/**\n * Validates structured output against an authored JSON Schema.\n *\n * WHY THIS IS A SERVICE AND NOT A FUNCTION:\n * The compiler import is memoised for the life of the process, and that memo is\n * mutable state that needs an owner. As a module-level variable it also could\n * not be reset between tests, which made a single failed-load test poison every\n * later one.\n */\n@injectable()\nexport class StructuredOutputValidator {\n private cachedCompile: Promise<CompileJsonSchema> | undefined;\n\n /** Import seam: ESM namespaces are frozen, so a test cannot spy on the import. */\n protected importCompileModule(): Promise<unknown> {\n return import(TYPEBOX_COMPILE_SPECIFIER);\n }\n\n /** Import seam for the host-package-root fallback. */\n protected importCompileFromPackageRoot(packageRoot: string): Promise<CompileJsonSchema | undefined> {\n return resolveCompileFromPackageRoot(packageRoot);\n }\n\n /**\n * Resolve the compiler, reporting every attempt that failed.\n *\n * Both routes are named in the error because which one failed is what tells\n * an operator whether the extension is mis-installed or the host env is unset.\n */\n private async importCompile(): Promise<CompileJsonSchema> {\n const failures: string[] = [];\n try {\n const compile = readCompileExport(await this.importCompileModule());\n if (compile) return compile;\n failures.push(`${TYPEBOX_COMPILE_SPECIFIER} did not export a ${COMPILE_EXPORT_NAME} function`);\n } catch (error) {\n failures.push(`direct import failed: ${toMessage(error)}`);\n }\n const packageRoot = process.env[PI_CODING_AGENT_PACKAGE_ROOT_ENV];\n if (packageRoot) {\n try {\n const compile = await this.importCompileFromPackageRoot(packageRoot);\n if (compile) return compile;\n failures.push(`Pi package root ${TYPEBOX_COMPILE_SPECIFIER} did not export a ${COMPILE_EXPORT_NAME} function`);\n } catch (error) {\n failures.push(`Pi package root import failed: ${toMessage(error)}`);\n }\n } else {\n failures.push(`${PI_CODING_AGENT_PACKAGE_ROOT_ENV} is not set`);\n }\n throw new Error(\n `Cannot load ${TYPEBOX_COMPILE_SPECIFIER} for structured output validation (${failures.join('; ')})`,\n );\n }\n\n /** Memoised compiler. A rejected load drops the memo so it can be retried. */\n private loadCompile(): Promise<CompileJsonSchema> {\n if (!this.cachedCompile) {\n this.cachedCompile = this.importCompile().catch((error: unknown) => {\n this.cachedCompile = undefined;\n throw error;\n });\n }\n return this.cachedCompile;\n }\n\n /** Drop the memoised compiler. Intended for tests and teardown. */\n reset(): void {\n this.cachedCompile = undefined;\n }\n\n async validateValue(schema: JsonSchemaObject, value: unknown): Promise<StructuredOutputValidation> {\n const compile = await this.loadCompile();\n let validator: CompiledJsonSchema;\n try {\n validator = compile(schema);\n } catch (error) {\n // A schema that will not compile is the author's mistake, not a runtime\n // fault, so it is reported against the run like any other invalid output.\n return { status: 'invalid', message: `invalid outputSchema: ${toMessage(error)}` };\n }\n if (validator.Check(value)) return { status: 'valid' };\n const errors = [...validator.Errors(value)].slice(0, MAX_REPORTED_SCHEMA_ERRORS).map((error) => {\n const pathText = error.instancePath\n ? error.instancePath.replace(/^\\//, '').replace(/\\//g, '.')\n : ROOT_INSTANCE_PATH_LABEL;\n return `${pathText}: ${error.message}`;\n });\n return { status: 'invalid', message: errors.join('; ') || 'schema validation failed' };\n }\n\n /**\n * Read and validate what the child wrote.\n *\n * An absent file is reported as a missing tool call rather than as invalid\n * output: the child never answered at all, which is a different fix.\n */\n async readStructuredOutput(runtime: StructuredOutputRuntime): Promise<{ value?: unknown; error?: string }> {\n if (!fs.existsSync(runtime.outputPath)) {\n return { error: MISSING_STRUCTURED_OUTPUT_CALL_ERROR };\n }\n let value: unknown;\n try {\n value = JSON.parse(fs.readFileSync(runtime.outputPath, 'utf-8')) as unknown;\n } catch (error) {\n return { error: `Failed to read structured output: ${toMessage(error)}` };\n }\n try {\n const validation = await this.validateValue(runtime.schema, value);\n if (validation.status === 'invalid') {\n return { error: `Structured output validation failed: ${validation.message}` };\n }\n } catch (error) {\n // The compiler itself failed to load. That is an environment problem, and\n // reporting it against the run beats crashing the parent mid-orchestration.\n return { error: `Failed to validate structured output: ${toMessage(error)}` };\n }\n return { value };\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../../_virtual/_rolldown/runtime.mjs";import{__decorate as t,init_decorate as n}from"../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs";import{PI_CODING_AGENT_PACKAGE_ROOT_ENV as r,init_env as i}from"../../env.mjs";import{init_paths as a}from"../../shared/paths.mjs";import{createRequire as o}from"node:module";import*as s from"node:fs";import*as c from"node:path";import{injectable as l}from"inversify";import{pathToFileURL as u}from"node:url";function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function f(e,t,n=!0){if(typeof e==`boolean`||!d(e))return e;let r=e,i={...r},a=n&&typeof r.$id!=`string`;if(a)for(let e of C){let n=r[e];n===w?i[e]=t:typeof n==`string`&&n.startsWith(T)&&(i[e]=`${t}${n.slice(1)}`)}for(let e of b){let n=r[e];d(n)&&(i[e]=Object.fromEntries(Object.entries(n).map(([e,n])=>[e,f(n,t,a)])))}let o=r.items;Array.isArray(o)?i.items=o.map(e=>f(e,t,a)):o!==void 0&&(i.items=f(o,t,a));for(let e of x)r[e]!==void 0&&(i[e]=f(r[e],t,a));for(let e of S){let n=r[e];Array.isArray(n)&&(i[e]=n.map(e=>f(e,t,a)))}let s=r.dependencies;return d(s)&&(i.dependencies=Object.fromEntries(Object.entries(s).map(([e,n])=>[e,Array.isArray(n)?n:f(n,t,a)]))),i}function p(e){return{type:`object`,properties:{[v]:f(e,y)},required:[v],additionalProperties:!1}}function m(e){return e instanceof Error?e.message:String(e)}function h(e){if(!d(e))return;let t=e[D];return typeof t==`function`?t:void 0}async function g(e){return h(await import(u(o(c.join(e,`package.json`)).resolve(E)).href))}var _,v,y,b,x,S,C,w,T,E,D,O,k,A,j=e((()=>{i(),a(),n(),_=`Missing structured_output call; this step has outputSchema and must finish by calling structured_output.`,v=`value`,y=`#/properties/${v}`,b=[`properties`,`patternProperties`,`$defs`,`definitions`,`dependentSchemas`],x=[`additionalItems`,`additionalProperties`,`contains`,`not`,`propertyNames`,`if`,`then`,`else`,`unevaluatedItems`,`unevaluatedProperties`,`contentSchema`],S=[`allOf`,`anyOf`,`oneOf`,`prefixItems`],C=[`$ref`,`$dynamicRef`,`$recursiveRef`],w=`#`,T=`#/`,E=`typebox/compile`,D=`Compile`,O=8,k=`root`,A=class{cachedCompile;importCompileModule(){return import(E)}importCompileFromPackageRoot(e){return g(e)}async importCompile(){let e=[];try{let t=h(await this.importCompileModule());if(t)return t;e.push(`${E} did not export a ${D} function`)}catch(t){e.push(`direct import failed: ${m(t)}`)}let t=process.env[r];if(t)try{let n=await this.importCompileFromPackageRoot(t);if(n)return n;e.push(`Pi package root ${E} did not export a ${D} function`)}catch(t){e.push(`Pi package root import failed: ${m(t)}`)}else e.push(`${r} is not set`);throw Error(`Cannot load ${E} for structured output validation (${e.join(`; `)})`)}loadCompile(){return this.cachedCompile||=this.importCompile().catch(e=>{throw this.cachedCompile=void 0,e}),this.cachedCompile}reset(){this.cachedCompile=void 0}async validateValue(e,t){let n=await this.loadCompile(),r;try{r=n(e)}catch(e){return{status:`invalid`,message:`invalid outputSchema: ${m(e)}`}}return r.Check(t)?{status:`valid`}:{status:`invalid`,message:[...r.Errors(t)].slice(0,O).map(e=>`${e.instancePath?e.instancePath.replace(/^\//,``).replace(/\//g,`.`):k}: ${e.message}`).join(`; `)||`schema validation failed`}}async readStructuredOutput(e){if(!s.existsSync(e.outputPath))return{error:_};let t;try{t=JSON.parse(s.readFileSync(e.outputPath,`utf-8`))}catch(e){return{error:`Failed to read structured output: ${m(e)}`}}try{let n=await this.validateValue(e.schema,t);if(n.status===`invalid`)return{error:`Structured output validation failed: ${n.message}`}}catch(e){return{error:`Failed to validate structured output: ${m(e)}`}}return{value:t}}},A=t([l()],A)}));j();export{A as StructuredOutputValidator,p as createStructuredOutputToolParameters,j as init_structuredOutput};
|
|
2
|
+
//# sourceMappingURL=structuredOutput.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structuredOutput.mjs","names":[],"sources":["../../../src/runs/shared/structuredOutput.ts"],"sourcesContent":["/**\n * Structured output: the schema a step must satisfy, and the file it answers in.\n *\n * A step with an `outputSchema` may not finish in prose. The runtime hands the\n * child a `structured_output` tool whose parameters are derived from the schema,\n * writes the schema to a private temp directory, and validates whatever the child\n * wrote back before the run is allowed to succeed.\n *\n * DESIGN PATTERNS:\n * - The author's schema is nested under a `value` property rather than used as\n * the tool parameters directly, because a tool's parameters must be an object\n * and an author is entitled to declare a top-level array or string\n * - Local JSON pointers are rewritten during that nesting. `#/$defs/x` addressed\n * the author's schema root, which is now one level down, so an unrewritten\n * pointer would resolve against the wrapper and silently validate nothing\n * - Every failure is returned as a message, never thrown. An invalid schema is an\n * authoring mistake to report against the run, not a crash of the parent\n *\n * PERFORMANCE:\n * `typebox/compile` is loaded once per process and memoised. It is a heavy\n * import and every validated step would otherwise pay for it. A failed load\n * clears the memo so a transient resolution failure is retried rather than\n * cached forever.\n *\n * AVOID:\n * - Joining `os.tmpdir()` here; the temp root belongs to `shared/paths.ts`\n * - Treating an absent output file as a validation failure. It means the child\n * never called the tool, which is a different diagnosis for the operator\n */\n\nimport * as fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport * as path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { injectable } from 'inversify';\n\nimport { PI_CODING_AGENT_PACKAGE_ROOT_ENV } from '../../env.ts';\nimport { TEMP_ROOT_DIR } from '../../shared/paths.ts';\nimport type { JsonSchemaObject } from '../../types.ts';\n\nexport { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV } from '../../env.ts';\n\nexport const MISSING_STRUCTURED_OUTPUT_CALL_ERROR =\n 'Missing structured_output call; this step has outputSchema and must finish by calling structured_output.';\n\n/** Where the schema and the child's answer live for one run. */\nexport interface StructuredOutputRuntime {\n schema: JsonSchemaObject;\n schemaPath: string;\n outputPath: string;\n}\n\n// ============================================================================\n// Wrapping an authored schema as tool parameters\n// ============================================================================\n\n/** The property the author's schema is nested under inside the tool parameters. */\nconst STRUCTURED_OUTPUT_VALUE_PROPERTY = 'value';\n/** JSON pointer that addresses the nested schema from the wrapper root. */\nconst STRUCTURED_OUTPUT_VALUE_POINTER = `#/properties/${STRUCTURED_OUTPUT_VALUE_PROPERTY}`;\n\n/** Keywords whose value is a map of names to subschemas. */\nconst SCHEMA_MAP_KEYWORDS = ['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas'] as const;\n/** Keywords whose value is a single subschema. */\nconst SCHEMA_SINGLE_KEYWORDS = [\n 'additionalItems',\n 'additionalProperties',\n 'contains',\n 'not',\n 'propertyNames',\n 'if',\n 'then',\n 'else',\n 'unevaluatedItems',\n 'unevaluatedProperties',\n 'contentSchema',\n] as const;\n/** Keywords whose value is an array of subschemas. */\nconst SCHEMA_ARRAY_KEYWORDS = ['allOf', 'anyOf', 'oneOf', 'prefixItems'] as const;\n\n/** Reference keywords that can carry a local pointer needing a rebase. */\nconst SCHEMA_REF_KEYWORDS = ['$ref', '$dynamicRef', '$recursiveRef'] as const;\n\n/** A pointer to the schema resource root, which moves when the schema is nested. */\nconst ROOT_POINTER = '#';\nconst LOCAL_POINTER_PREFIX = '#/';\n\nfunction isSchemaRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Rebase local JSON pointers so they still address the author's schema once it\n * has been nested under the wrapper.\n *\n * `$id` stops the descent: a subschema declaring one starts a new resource, so\n * its local pointers address itself and were never relative to the wrapper.\n * Boolean schemas (`true`/`false`) are legal JSON Schema and pass through.\n */\nfunction rewriteLocalJsonPointerRefs(schema: unknown, pointerPrefix: string, inheritsWrapperResource = true): unknown {\n if (typeof schema === 'boolean' || !isSchemaRecord(schema)) return schema;\n const source = schema;\n const rewritten: Record<string, unknown> = { ...source };\n const sharesWrapperResource = inheritsWrapperResource && typeof source.$id !== 'string';\n if (sharesWrapperResource) {\n for (const keyword of SCHEMA_REF_KEYWORDS) {\n const ref = source[keyword];\n if (ref === ROOT_POINTER) rewritten[keyword] = pointerPrefix;\n else if (typeof ref === 'string' && ref.startsWith(LOCAL_POINTER_PREFIX)) {\n rewritten[keyword] = `${pointerPrefix}${ref.slice(ROOT_POINTER.length)}`;\n }\n }\n }\n for (const keyword of SCHEMA_MAP_KEYWORDS) {\n const entries = source[keyword];\n if (!isSchemaRecord(entries)) continue;\n rewritten[keyword] = Object.fromEntries(\n Object.entries(entries).map(([name, nested]) => [\n name,\n rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource),\n ]),\n );\n }\n const items = source.items;\n if (Array.isArray(items)) {\n rewritten.items = items.map((nested) => rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource));\n } else if (items !== undefined) {\n rewritten.items = rewriteLocalJsonPointerRefs(items, pointerPrefix, sharesWrapperResource);\n }\n for (const keyword of SCHEMA_SINGLE_KEYWORDS) {\n if (source[keyword] !== undefined) {\n rewritten[keyword] = rewriteLocalJsonPointerRefs(source[keyword], pointerPrefix, sharesWrapperResource);\n }\n }\n for (const keyword of SCHEMA_ARRAY_KEYWORDS) {\n const branches = source[keyword];\n if (Array.isArray(branches)) {\n rewritten[keyword] = branches.map((nested) =>\n rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource),\n );\n }\n }\n const dependencies = source.dependencies;\n if (isSchemaRecord(dependencies)) {\n // Draft-07 `dependencies` is either a property-name list or a subschema.\n rewritten.dependencies = Object.fromEntries(\n Object.entries(dependencies).map(([name, nested]) => [\n name,\n Array.isArray(nested) ? nested : rewriteLocalJsonPointerRefs(nested, pointerPrefix, sharesWrapperResource),\n ]),\n );\n }\n return rewritten;\n}\n\n/** Parameters for the `structured_output` tool that satisfies `schema`. */\nexport function createStructuredOutputToolParameters(schema: JsonSchemaObject): JsonSchemaObject {\n return {\n type: 'object',\n properties: {\n [STRUCTURED_OUTPUT_VALUE_PROPERTY]: rewriteLocalJsonPointerRefs(schema, STRUCTURED_OUTPUT_VALUE_POINTER),\n },\n required: [STRUCTURED_OUTPUT_VALUE_PROPERTY],\n additionalProperties: false,\n };\n}\n\n// ============================================================================\n// Runtime files\n// ============================================================================\n\nconst STRUCTURED_OUTPUT_DIR_PREFIX = 'doom-team-structured-';\nconst SCHEMA_FILE_NAME = 'schema.json';\nconst OUTPUT_FILE_NAME = 'output.json';\n/** The schema can carry task detail, so it is readable only by its own user. */\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport function assertJsonSchemaObject(schema: unknown, label = 'outputSchema'): asserts schema is JsonSchemaObject {\n if (!isSchemaRecord(schema)) {\n throw new Error(`${label} must be a JSON Schema object.`);\n }\n}\n\n/** Stage the schema on disk and reserve the path the child answers in. */\nexport function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?: string): StructuredOutputRuntime {\n assertJsonSchemaObject(schema);\n const rootDir = baseDir ?? TEMP_ROOT_DIR;\n fs.mkdirSync(rootDir, { recursive: true });\n const dir = fs.mkdtempSync(path.join(rootDir, STRUCTURED_OUTPUT_DIR_PREFIX));\n const schemaPath = path.join(dir, SCHEMA_FILE_NAME);\n const outputPath = path.join(dir, OUTPUT_FILE_NAME);\n fs.writeFileSync(schemaPath, JSON.stringify(schema), { mode: PRIVATE_FILE_MODE });\n return { schema, schemaPath, outputPath };\n}\n\n/**\n * Remove the run's schema and output directory.\n *\n * The failure is returned rather than thrown or logged: the directory is under\n * the per-user temp root, so a leftover is harmless and must not fail a run that\n * has already produced its result. Returning it still lets a caller that cares,\n * such as a teardown check, see what happened instead of it vanishing.\n */\nexport function cleanupStructuredOutputRuntime(runtime: StructuredOutputRuntime | undefined): {\n removed: boolean;\n error?: string;\n} {\n if (!runtime) return { removed: false };\n try {\n fs.rmSync(path.dirname(runtime.schemaPath), { recursive: true, force: true });\n return { removed: true };\n } catch (error) {\n return { removed: false, error: toMessage(error) };\n }\n}\n\n// ============================================================================\n// Validation\n// ============================================================================\n\n/** The compiled-validator surface this module uses out of `typebox/compile`. */\ninterface CompiledJsonSchema {\n Check(value: unknown): boolean;\n Errors(value: unknown): Iterable<{ instancePath?: string; message?: string }>;\n}\n\ntype CompileJsonSchema = (schema: unknown) => CompiledJsonSchema;\n\nconst TYPEBOX_COMPILE_SPECIFIER = 'typebox/compile';\nconst COMPILE_EXPORT_NAME = 'Compile';\n/** How many schema violations are reported before the message is truncated. */\nconst MAX_REPORTED_SCHEMA_ERRORS = 8;\n/** Path shown for a violation at the document root. */\nconst ROOT_INSTANCE_PATH_LABEL = 'root';\n\nexport type StructuredOutputValidation = { status: 'valid' } | { status: 'invalid'; message: string };\n\nfunction toMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction readCompileExport(mod: unknown): CompileJsonSchema | undefined {\n if (!isSchemaRecord(mod)) return undefined;\n const compile = mod[COMPILE_EXPORT_NAME];\n return typeof compile === 'function' ? (compile as CompileJsonSchema) : undefined;\n}\n\n/**\n * Load `typebox/compile` out of the host Pi installation.\n *\n * Exported because the host package root is the only place the dependency is\n * guaranteed to resolve when this extension is loaded from outside the host's\n * own `node_modules` tree.\n */\nexport async function resolveCompileFromPackageRoot(packageRoot: string): Promise<CompileJsonSchema | undefined> {\n const requireFromRoot = createRequire(path.join(packageRoot, 'package.json'));\n const resolved = requireFromRoot.resolve(TYPEBOX_COMPILE_SPECIFIER);\n const mod: unknown = await import(pathToFileURL(resolved).href);\n return readCompileExport(mod);\n}\n\n/**\n * Validates structured output against an authored JSON Schema.\n *\n * WHY THIS IS A SERVICE AND NOT A FUNCTION:\n * The compiler import is memoised for the life of the process, and that memo is\n * mutable state that needs an owner. As a module-level variable it also could\n * not be reset between tests, which made a single failed-load test poison every\n * later one.\n */\n@injectable()\nexport class StructuredOutputValidator {\n private cachedCompile: Promise<CompileJsonSchema> | undefined;\n\n /** Import seam: ESM namespaces are frozen, so a test cannot spy on the import. */\n protected importCompileModule(): Promise<unknown> {\n return import(TYPEBOX_COMPILE_SPECIFIER);\n }\n\n /** Import seam for the host-package-root fallback. */\n protected importCompileFromPackageRoot(packageRoot: string): Promise<CompileJsonSchema | undefined> {\n return resolveCompileFromPackageRoot(packageRoot);\n }\n\n /**\n * Resolve the compiler, reporting every attempt that failed.\n *\n * Both routes are named in the error because which one failed is what tells\n * an operator whether the extension is mis-installed or the host env is unset.\n */\n private async importCompile(): Promise<CompileJsonSchema> {\n const failures: string[] = [];\n try {\n const compile = readCompileExport(await this.importCompileModule());\n if (compile) return compile;\n failures.push(`${TYPEBOX_COMPILE_SPECIFIER} did not export a ${COMPILE_EXPORT_NAME} function`);\n } catch (error) {\n failures.push(`direct import failed: ${toMessage(error)}`);\n }\n const packageRoot = process.env[PI_CODING_AGENT_PACKAGE_ROOT_ENV];\n if (packageRoot) {\n try {\n const compile = await this.importCompileFromPackageRoot(packageRoot);\n if (compile) return compile;\n failures.push(`Pi package root ${TYPEBOX_COMPILE_SPECIFIER} did not export a ${COMPILE_EXPORT_NAME} function`);\n } catch (error) {\n failures.push(`Pi package root import failed: ${toMessage(error)}`);\n }\n } else {\n failures.push(`${PI_CODING_AGENT_PACKAGE_ROOT_ENV} is not set`);\n }\n throw new Error(\n `Cannot load ${TYPEBOX_COMPILE_SPECIFIER} for structured output validation (${failures.join('; ')})`,\n );\n }\n\n /** Memoised compiler. A rejected load drops the memo so it can be retried. */\n private loadCompile(): Promise<CompileJsonSchema> {\n if (!this.cachedCompile) {\n this.cachedCompile = this.importCompile().catch((error: unknown) => {\n this.cachedCompile = undefined;\n throw error;\n });\n }\n return this.cachedCompile;\n }\n\n /** Drop the memoised compiler. Intended for tests and teardown. */\n reset(): void {\n this.cachedCompile = undefined;\n }\n\n async validateValue(schema: JsonSchemaObject, value: unknown): Promise<StructuredOutputValidation> {\n const compile = await this.loadCompile();\n let validator: CompiledJsonSchema;\n try {\n validator = compile(schema);\n } catch (error) {\n // A schema that will not compile is the author's mistake, not a runtime\n // fault, so it is reported against the run like any other invalid output.\n return { status: 'invalid', message: `invalid outputSchema: ${toMessage(error)}` };\n }\n if (validator.Check(value)) return { status: 'valid' };\n const errors = [...validator.Errors(value)].slice(0, MAX_REPORTED_SCHEMA_ERRORS).map((error) => {\n const pathText = error.instancePath\n ? error.instancePath.replace(/^\\//, '').replace(/\\//g, '.')\n : ROOT_INSTANCE_PATH_LABEL;\n return `${pathText}: ${error.message}`;\n });\n return { status: 'invalid', message: errors.join('; ') || 'schema validation failed' };\n }\n\n /**\n * Read and validate what the child wrote.\n *\n * An absent file is reported as a missing tool call rather than as invalid\n * output: the child never answered at all, which is a different fix.\n */\n async readStructuredOutput(runtime: StructuredOutputRuntime): Promise<{ value?: unknown; error?: string }> {\n if (!fs.existsSync(runtime.outputPath)) {\n return { error: MISSING_STRUCTURED_OUTPUT_CALL_ERROR };\n }\n let value: unknown;\n try {\n value = JSON.parse(fs.readFileSync(runtime.outputPath, 'utf-8')) as unknown;\n } catch (error) {\n return { error: `Failed to read structured output: ${toMessage(error)}` };\n }\n try {\n const validation = await this.validateValue(runtime.schema, value);\n if (validation.status === 'invalid') {\n return { error: `Structured output validation failed: ${validation.message}` };\n }\n } catch (error) {\n // The compiler itself failed to load. That is an environment problem, and\n // reporting it against the run beats crashing the parent mid-orchestration.\n return { error: `Failed to validate structured output: ${toMessage(error)}` };\n }\n return { value };\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../../_virtual/_rolldown/runtime.cjs`),t=require(`../../shared/atomicJson.cjs`);let n=require(`node:fs`);n=e.__toESM(n,1);const r=new Set([`bash`,`edit`,`find`,`grep`,`ls`,`read`,`write`]);function i(e,i,a,o,s){let c=new Set([...a,...r]),l=i.filter(e=>!c.has(e));if(l.length===0){n.rmSync(e,{force:!0});return}let u=s?.length?l.filter(e=>s.includes(e)):[],d={...o===void 0?{}:{agent:o},required:i,available:a,missing:l,...u.length>0?{missingMcpDirectTools:u}:{}};return t.writePrivateAtomicJson(e,d),d}exports.writeChildToolDiagnostic=i;
|
|
2
|
+
//# sourceMappingURL=toolAvailability.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolAvailability.cjs","names":[],"sources":["../../../src/runs/shared/toolAvailability.ts"],"sourcesContent":["/**\n * Diagnostics for child tools an agent asked for that the child never registered.\n *\n * The `tools` field on an agent is a strict allowlist: it selects from what the\n * child process already has, it does not cause extension code to load. When the\n * two disagree the child notices, not the parent, and the child's stderr is not\n * where a user looks. So the child drops a small file the parent reads after the\n * run and turns into an actionable message.\n *\n * DESIGN PATTERNS:\n * - The file is a negative signal only. A run with nothing missing removes it,\n * so a stale file from an earlier run can never be reported against a later one\n * - Written 0600 and atomically, because it is parsed by a different process and\n * a torn read is indistinguishable from a corrupt file\n * - Reads validate every field. The file is JSON on disk that any process could\n * have written, so nothing is trusted on shape alone\n *\n * AVOID:\n * - Treating a malformed diagnostic as \"nothing missing\"; it is reported as a\n * read failure instead, because a silent empty result hides the tool gap\n */\n\nimport * as fs from 'node:fs';\nimport { writePrivateAtomicJson } from '../../shared/atomicJson.ts';\n\nexport interface ChildToolDiagnostic {\n agent?: string;\n required: string[];\n available: string[];\n missing: string[];\n missingMcpDirectTools?: string[];\n}\n\n/**\n * Tools every child has regardless of what it reported.\n *\n * The child registry snapshot can be taken before the builtins finish\n * registering, so treating these as always available avoids a false alarm.\n */\nconst CORE_CHILD_TOOLS = new Set(['bash', 'edit', 'find', 'grep', 'ls', 'read', 'write']);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === 'string' && entry.length > 0);\n}\n\n/**\n * Record the gap between requested and registered tools, or clear a stale record.\n *\n * Returns the diagnostic when there is one so the caller can report it without\n * reading back what it just wrote.\n */\nexport function writeChildToolDiagnostic(\n filePath: string,\n required: string[],\n available: string[],\n agent?: string,\n mcpDirectTools?: string[],\n): ChildToolDiagnostic | undefined {\n const availableNames = new Set([...available, ...CORE_CHILD_TOOLS]);\n const missing = required.filter((name) => !availableNames.has(name));\n if (missing.length === 0) {\n fs.rmSync(filePath, { force: true });\n return undefined;\n }\n\n const missingMcpDirectTools = mcpDirectTools?.length ? missing.filter((name) => mcpDirectTools.includes(name)) : [];\n const diagnostic: ChildToolDiagnostic = {\n ...(agent !== undefined ? { agent } : {}),\n required,\n available,\n missing,\n ...(missingMcpDirectTools.length > 0 ? { missingMcpDirectTools } : {}),\n };\n writePrivateAtomicJson(filePath, diagnostic);\n return diagnostic;\n}\n\n/** Read a diagnostic written by a child. Throws when the file exists but is not one. */\nexport function readChildToolDiagnostic(filePath: string | undefined): ChildToolDiagnostic | undefined {\n if (!filePath || !fs.existsSync(filePath)) return undefined;\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n if (\n !isRecord(parsed) ||\n !isNonEmptyStringArray(parsed.required) ||\n !isNonEmptyStringArray(parsed.available) ||\n !isNonEmptyStringArray(parsed.missing) ||\n (parsed.agent !== undefined && typeof parsed.agent !== 'string') ||\n (parsed.missingMcpDirectTools !== undefined && !isNonEmptyStringArray(parsed.missingMcpDirectTools))\n ) {\n throw new Error(`Malformed child tool diagnostic at '${filePath}'.`);\n }\n return {\n ...(parsed.agent ? { agent: parsed.agent } : {}),\n required: parsed.required,\n available: parsed.available,\n missing: parsed.missing,\n ...(parsed.missingMcpDirectTools ? { missingMcpDirectTools: parsed.missingMcpDirectTools } : {}),\n };\n}\n\nexport function formatChildToolDiagnostic(diagnostic: ChildToolDiagnostic): string {\n const subject = diagnostic.agent ? `Agent '${diagnostic.agent}'` : 'Subagent';\n return [\n `${subject} requested unavailable child tools: ${diagnostic.missing.join(', ')}.`,\n 'The `tools` field is a strict allowlist; it does not load extension code.',\n ...(diagnostic.missingMcpDirectTools?.length\n ? [\n `Resolved MCP direct tools missing from the child registry: ${diagnostic.missingMcpDirectTools.join(', ')}. This indicates a doom-mcp registration problem, not a tool-call failure.`,\n ]\n : []),\n 'For extension tools, add the provider path to `subagentOnlyExtensions` (child-only), `extensions`, or as a path-like entry in `tools`, while keeping each registered tool name in `tools`.',\n 'For MCP tools, check `/mcp` in the parent session for the server state, and verify the selected tool names. For builtin tools, verify the name against the installed Pi version.',\n ].join('\\n');\n}\n\n/**\n * The reportable message for a run, or undefined when nothing was missing.\n *\n * A read failure is surfaced as its own message rather than swallowed: the file\n * existing at all means the child had a tool gap, so returning undefined here\n * would report a broken run as a clean one.\n */\nexport function readChildToolDiagnosticError(filePath: string | undefined): string | undefined {\n try {\n const diagnostic = readChildToolDiagnostic(filePath);\n return diagnostic ? formatChildToolDiagnostic(diagnostic) : undefined;\n } catch (error) {\n return `Failed to read child tool availability diagnostic: ${error instanceof Error ? error.message : String(error)}`;\n }\n}\n"],"mappings":"2IAuCA,MAAM,EAAmB,IAAI,IAAI,CAAC,OAAQ,OAAQ,OAAQ,OAAQ,KAAM,OAAQ,QAAQ,CAAC,CAgBzF,SAAgB,EACd,EACA,EACA,EACA,EACA,EACiC,CACjC,IAAM,EAAiB,IAAI,IAAI,CAAC,GAAG,EAAW,GAAG,EAAiB,CAAC,CAC7D,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAe,IAAI,EAAK,CAAC,CACpE,GAAI,EAAQ,SAAW,EAAG,CACxB,EAAG,OAAO,EAAU,CAAE,MAAO,GAAM,CAAC,CACpC,OAGF,IAAM,EAAwB,GAAgB,OAAS,EAAQ,OAAQ,GAAS,EAAe,SAAS,EAAK,CAAC,CAAG,EAAE,CAC7G,EAAkC,CACtC,GAAI,IAAU,IAAA,GAAwB,EAAE,CAAd,CAAE,QAAO,CACnC,WACA,YACA,UACA,GAAI,EAAsB,OAAS,EAAI,CAAE,wBAAuB,CAAG,EAAE,CACtE,CAED,OADA,EAAA,uBAAuB,EAAU,EAAW,CACrC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../../_virtual/_rolldown/runtime.mjs";import{init_atomicJson as t,writePrivateAtomicJson as n}from"../../shared/atomicJson.mjs";import*as r from"node:fs";function i(e,t,i,o,s){let c=new Set([...i,...a]),l=t.filter(e=>!c.has(e));if(l.length===0){r.rmSync(e,{force:!0});return}let u=s?.length?l.filter(e=>s.includes(e)):[],d={...o===void 0?{}:{agent:o},required:t,available:i,missing:l,...u.length>0?{missingMcpDirectTools:u}:{}};return n(e,d),d}var a,o=e((()=>{t(),a=new Set([`bash`,`edit`,`find`,`grep`,`ls`,`read`,`write`])}));o();export{o as init_toolAvailability,i as writeChildToolDiagnostic};
|
|
2
|
+
//# sourceMappingURL=toolAvailability.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolAvailability.mjs","names":[],"sources":["../../../src/runs/shared/toolAvailability.ts"],"sourcesContent":["/**\n * Diagnostics for child tools an agent asked for that the child never registered.\n *\n * The `tools` field on an agent is a strict allowlist: it selects from what the\n * child process already has, it does not cause extension code to load. When the\n * two disagree the child notices, not the parent, and the child's stderr is not\n * where a user looks. So the child drops a small file the parent reads after the\n * run and turns into an actionable message.\n *\n * DESIGN PATTERNS:\n * - The file is a negative signal only. A run with nothing missing removes it,\n * so a stale file from an earlier run can never be reported against a later one\n * - Written 0600 and atomically, because it is parsed by a different process and\n * a torn read is indistinguishable from a corrupt file\n * - Reads validate every field. The file is JSON on disk that any process could\n * have written, so nothing is trusted on shape alone\n *\n * AVOID:\n * - Treating a malformed diagnostic as \"nothing missing\"; it is reported as a\n * read failure instead, because a silent empty result hides the tool gap\n */\n\nimport * as fs from 'node:fs';\nimport { writePrivateAtomicJson } from '../../shared/atomicJson.ts';\n\nexport interface ChildToolDiagnostic {\n agent?: string;\n required: string[];\n available: string[];\n missing: string[];\n missingMcpDirectTools?: string[];\n}\n\n/**\n * Tools every child has regardless of what it reported.\n *\n * The child registry snapshot can be taken before the builtins finish\n * registering, so treating these as always available avoids a false alarm.\n */\nconst CORE_CHILD_TOOLS = new Set(['bash', 'edit', 'find', 'grep', 'ls', 'read', 'write']);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === 'string' && entry.length > 0);\n}\n\n/**\n * Record the gap between requested and registered tools, or clear a stale record.\n *\n * Returns the diagnostic when there is one so the caller can report it without\n * reading back what it just wrote.\n */\nexport function writeChildToolDiagnostic(\n filePath: string,\n required: string[],\n available: string[],\n agent?: string,\n mcpDirectTools?: string[],\n): ChildToolDiagnostic | undefined {\n const availableNames = new Set([...available, ...CORE_CHILD_TOOLS]);\n const missing = required.filter((name) => !availableNames.has(name));\n if (missing.length === 0) {\n fs.rmSync(filePath, { force: true });\n return undefined;\n }\n\n const missingMcpDirectTools = mcpDirectTools?.length ? missing.filter((name) => mcpDirectTools.includes(name)) : [];\n const diagnostic: ChildToolDiagnostic = {\n ...(agent !== undefined ? { agent } : {}),\n required,\n available,\n missing,\n ...(missingMcpDirectTools.length > 0 ? { missingMcpDirectTools } : {}),\n };\n writePrivateAtomicJson(filePath, diagnostic);\n return diagnostic;\n}\n\n/** Read a diagnostic written by a child. Throws when the file exists but is not one. */\nexport function readChildToolDiagnostic(filePath: string | undefined): ChildToolDiagnostic | undefined {\n if (!filePath || !fs.existsSync(filePath)) return undefined;\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n if (\n !isRecord(parsed) ||\n !isNonEmptyStringArray(parsed.required) ||\n !isNonEmptyStringArray(parsed.available) ||\n !isNonEmptyStringArray(parsed.missing) ||\n (parsed.agent !== undefined && typeof parsed.agent !== 'string') ||\n (parsed.missingMcpDirectTools !== undefined && !isNonEmptyStringArray(parsed.missingMcpDirectTools))\n ) {\n throw new Error(`Malformed child tool diagnostic at '${filePath}'.`);\n }\n return {\n ...(parsed.agent ? { agent: parsed.agent } : {}),\n required: parsed.required,\n available: parsed.available,\n missing: parsed.missing,\n ...(parsed.missingMcpDirectTools ? { missingMcpDirectTools: parsed.missingMcpDirectTools } : {}),\n };\n}\n\nexport function formatChildToolDiagnostic(diagnostic: ChildToolDiagnostic): string {\n const subject = diagnostic.agent ? `Agent '${diagnostic.agent}'` : 'Subagent';\n return [\n `${subject} requested unavailable child tools: ${diagnostic.missing.join(', ')}.`,\n 'The `tools` field is a strict allowlist; it does not load extension code.',\n ...(diagnostic.missingMcpDirectTools?.length\n ? [\n `Resolved MCP direct tools missing from the child registry: ${diagnostic.missingMcpDirectTools.join(', ')}. This indicates a doom-mcp registration problem, not a tool-call failure.`,\n ]\n : []),\n 'For extension tools, add the provider path to `subagentOnlyExtensions` (child-only), `extensions`, or as a path-like entry in `tools`, while keeping each registered tool name in `tools`.',\n 'For MCP tools, check `/mcp` in the parent session for the server state, and verify the selected tool names. For builtin tools, verify the name against the installed Pi version.',\n ].join('\\n');\n}\n\n/**\n * The reportable message for a run, or undefined when nothing was missing.\n *\n * A read failure is surfaced as its own message rather than swallowed: the file\n * existing at all means the child had a tool gap, so returning undefined here\n * would report a broken run as a clean one.\n */\nexport function readChildToolDiagnosticError(filePath: string | undefined): string | undefined {\n try {\n const diagnostic = readChildToolDiagnostic(filePath);\n return diagnostic ? formatChildToolDiagnostic(diagnostic) : undefined;\n } catch (error) {\n return `Failed to read child tool availability diagnostic: ${error instanceof Error ? error.message : String(error)}`;\n }\n}\n"],"mappings":"oLAuDA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACiC,CACjC,IAAM,EAAiB,IAAI,IAAI,CAAC,GAAG,EAAW,GAAG,EAAiB,CAAC,CAC7D,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAe,IAAI,EAAK,CAAC,CACpE,GAAI,EAAQ,SAAW,EAAG,CACxB,EAAG,OAAO,EAAU,CAAE,MAAO,GAAM,CAAC,CACpC,OAGF,IAAM,EAAwB,GAAgB,OAAS,EAAQ,OAAQ,GAAS,EAAe,SAAS,EAAK,CAAC,CAAG,EAAE,CAC7G,EAAkC,CACtC,GAAI,IAAU,IAAA,GAAwB,EAAE,CAAd,CAAE,QAAO,CACnC,WACA,YACA,UACA,GAAI,EAAsB,OAAS,EAAI,CAAE,wBAAuB,CAAG,EAAE,CACtE,CAED,OADA,EAAuB,EAAU,EAAW,CACrC,qBAvD2D,CAgB9D,EAAmB,IAAI,IAAI,CAAC,OAAQ,OAAQ,OAAQ,OAAQ,KAAM,OAAQ,QAAQ,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../../env.cjs`),t=[`read`,`grep`,`find`,`ls`];function n(e){return e===`*`?`*`:e===void 0?[...t]:[...new Set(e.map(e=>e.trim()).filter(Boolean))]}function r(e,t=`toolBudget`,r={}){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))return{error:`${t} must be an object with hard and optional soft/block.`};let i=e,a=r.minimumHard??1,o=i.hard;if(typeof o!=`number`||!Number.isInteger(o)||o<a)return{error:`${t}.hard must be an integer >= ${a}.`};let s=i.soft;if(s!==void 0&&(typeof s!=`number`||!Number.isInteger(s)||s<1))return{error:`${t}.soft must be an integer >= 1 when provided.`};if(s!==void 0&&s>o)return{error:`${t}.soft must be <= ${t}.hard.`};let c=i.block,l;if(c!==void 0&&c!==`*`){if(!Array.isArray(c))return{error:`${t}.block must be "*" or an array of tool names.`};if(c.length===0)return{error:`${t}.block must contain at least one tool name.`};for(let e of c)if(typeof e!=`string`||!e.trim())return{error:`${t}.block must contain non-empty tool names.`};l=c}else l=c;return{budget:{hard:o,...s===void 0?{}:{soft:s},block:n(l)}}}function i(e,t,n){return n<=e.hard?!1:e.block===`*`||e.block.includes(t)}function a(e,t){return`Tool budget soft limit reached after ${t} tool call${t===1?``:`s`} (soft ${e.soft}, hard ${e.hard}). Stop starting new browsing/search work and finalize from the context you already have.`}function o(e,t,n){return`Tool budget hard limit reached after ${n} tool call${n===1?``:`s`} (hard ${e.hard}). The '${t}' tool is blocked so you can finalize from the context you already have.`}function s(e){return e?JSON.stringify(e):void 0}function c(t,n={}){if(!t?.trim())return;let i=r(JSON.parse(t),e.TOOL_BUDGET_ENV,n.allowZero?{minimumHard:0}:void 0);if(i.error)throw Error(i.error);return i.budget}exports.decodeToolBudgetEnv=c,exports.encodeToolBudgetEnv=s,exports.shouldBlockToolForBudget=i,exports.toolBudgetBlockedMessage=o,exports.toolBudgetSoftNudge=a;
|
|
2
|
+
//# sourceMappingURL=toolBudget.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolBudget.cjs","names":["TOOL_BUDGET_ENV"],"sources":["../../../src/runs/shared/toolBudget.ts"],"sourcesContent":["/**\n * Tool-call budget validation, enforcement predicates and its process handoff.\n *\n * DESIGN PATTERNS:\n * - Pure functions over a resolved budget; the running tool count is the\n * caller's, so this module has nothing to reset between runs\n * - The budget crosses into the child as one JSON environment value, encoded and\n * decoded here, so parent and child can never disagree about its shape\n *\n * WHY BLOCKING IS NARROWED TO A TOOL LIST:\n * The point of the hard limit is to stop open-ended browsing and searching, not\n * to strand a child that still has to write its answer. Blocking only the\n * read-style tools leaves `edit`, `write` and the reporting path available past\n * the limit, so an over-budget child finishes rather than fails.\n *\n * AVOID:\n * - Blocking on the call that reaches the hard limit; the count must exceed it\n * first, so the budgeted number of calls actually happens\n * - Reading the environment variable name from anywhere but `src/env.ts`\n */\n\nimport { TOOL_BUDGET_ENV } from '../../env.ts';\nimport type { ToolBudgetConfig } from '../../types.ts';\n\n/** A tool budget with its block list settled. */\nexport interface ResolvedToolBudget {\n soft?: number;\n hard: number;\n block: string[] | '*';\n}\n\n/** How a run stands against its tool budget. */\nexport type ToolBudgetOutcome = 'within-budget' | 'soft-reached' | 'hard-blocked';\n\n/** The budget plus everything observed about it during one run. */\nexport interface ToolBudgetState extends ResolvedToolBudget {\n outcome: ToolBudgetOutcome;\n toolCount: number;\n softReachedAt?: number;\n hardReachedAt?: number;\n blockedTool?: string;\n}\n\n/**\n * Blocked by default: the read-style tools a child loops on when it is stuck.\n *\n * Deliberately excludes `edit` and `write` so an over-budget child can still\n * land the work it has already reasoned about.\n */\nexport const DEFAULT_TOOL_BUDGET_BLOCK = ['read', 'grep', 'find', 'ls'] as const;\n\nconst BLOCK_ALL_TOOLS = '*';\nconst MIN_SOFT_LIMIT = 1;\nconst DEFAULT_MINIMUM_HARD_LIMIT = 1;\n\n/** Deduplicate and trim an authored block list, or pass through the block-all marker. */\nexport function normalizeToolBudgetBlock(block: ToolBudgetConfig['block'] | undefined): '*' | string[] {\n if (block === BLOCK_ALL_TOOLS) return BLOCK_ALL_TOOLS;\n if (block === undefined) return [...DEFAULT_TOOL_BUDGET_BLOCK];\n return [...new Set(block.map((tool) => tool.trim()).filter(Boolean))];\n}\n\n/**\n * Validate an authored tool budget and settle its block list.\n *\n * `minimumHard` is 0 only for the zero-authorised path, where a caller has\n * explicitly asked for a budget that blocks every tool from the first call.\n */\nexport function validateToolBudgetConfig(\n raw: unknown,\n label = 'toolBudget',\n options: { minimumHard?: 0 | 1 } = {},\n): { budget?: ResolvedToolBudget; error?: string } {\n if (raw === undefined) return {};\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return { error: `${label} must be an object with hard and optional soft/block.` };\n }\n const value: Record<string, unknown> = raw as Record<string, unknown>;\n const minimumHard = options.minimumHard ?? DEFAULT_MINIMUM_HARD_LIMIT;\n\n const hard = value.hard;\n if (typeof hard !== 'number' || !Number.isInteger(hard) || hard < minimumHard) {\n return { error: `${label}.hard must be an integer >= ${minimumHard}.` };\n }\n const soft = value.soft;\n if (soft !== undefined && (typeof soft !== 'number' || !Number.isInteger(soft) || soft < MIN_SOFT_LIMIT)) {\n return { error: `${label}.soft must be an integer >= ${MIN_SOFT_LIMIT} when provided.` };\n }\n if (soft !== undefined && soft > hard) {\n return { error: `${label}.soft must be <= ${label}.hard.` };\n }\n\n const rawBlock = value.block;\n let block: ToolBudgetConfig['block'] | undefined;\n if (rawBlock !== undefined && rawBlock !== BLOCK_ALL_TOOLS) {\n if (!Array.isArray(rawBlock)) return { error: `${label}.block must be \"*\" or an array of tool names.` };\n if (rawBlock.length === 0) return { error: `${label}.block must contain at least one tool name.` };\n for (const item of rawBlock) {\n if (typeof item !== 'string' || !item.trim()) {\n return { error: `${label}.block must contain non-empty tool names.` };\n }\n }\n block = rawBlock as string[];\n } else {\n block = rawBlock;\n }\n\n return {\n budget: {\n hard,\n ...(soft !== undefined ? { soft } : {}),\n block: normalizeToolBudgetBlock(block),\n },\n };\n}\n\nexport function initialToolBudgetState(budget: ResolvedToolBudget): ToolBudgetState {\n return { ...budget, toolCount: 0, outcome: 'within-budget' };\n}\n\nexport function toolBudgetState(budget: ResolvedToolBudget, toolCount: number, blockedTool?: string): ToolBudgetState {\n const overHard = toolCount > budget.hard;\n const overSoft = budget.soft !== undefined && toolCount >= budget.soft;\n return {\n ...budget,\n toolCount,\n outcome: overHard ? 'hard-blocked' : overSoft ? 'soft-reached' : 'within-budget',\n ...(overSoft ? { softReachedAt: budget.soft } : {}),\n ...(overHard ? { hardReachedAt: budget.hard, blockedTool } : {}),\n };\n}\n\n/**\n * Whether this call must be refused.\n *\n * `nextToolCount` is the count including the call being decided, so the budgeted\n * number of calls runs and only the one after it is refused.\n */\nexport function shouldBlockToolForBudget(budget: ResolvedToolBudget, toolName: string, nextToolCount: number): boolean {\n if (nextToolCount <= budget.hard) return false;\n return budget.block === BLOCK_ALL_TOOLS || budget.block.includes(toolName);\n}\n\nexport function toolBudgetSoftNudge(budget: ResolvedToolBudget, toolCount: number): string {\n return `Tool budget soft limit reached after ${toolCount} tool call${toolCount === 1 ? '' : 's'} (soft ${budget.soft}, hard ${budget.hard}). Stop starting new browsing/search work and finalize from the context you already have.`;\n}\n\nexport function toolBudgetBlockedMessage(budget: ResolvedToolBudget, toolName: string, toolCount: number): string {\n return `Tool budget hard limit reached after ${toolCount} tool call${toolCount === 1 ? '' : 's'} (hard ${budget.hard}). The '${toolName}' tool is blocked so you can finalize from the context you already have.`;\n}\n\nexport function encodeToolBudgetEnv(budget: ResolvedToolBudget | undefined): string | undefined {\n return budget ? JSON.stringify(budget) : undefined;\n}\n\n/**\n * Read the budget a parent handed to this child.\n *\n * Throws rather than returning an error string: an unreadable budget means the\n * child cannot know what it is allowed to do, and running unbudgeted would\n * silently defeat the cap the parent set.\n */\nexport function decodeToolBudgetEnv(\n value: string | undefined,\n options: { allowZero?: boolean } = {},\n): ResolvedToolBudget | undefined {\n if (!value?.trim()) return undefined;\n const parsed: unknown = JSON.parse(value);\n const normalized = validateToolBudgetConfig(\n parsed,\n TOOL_BUDGET_ENV,\n options.allowZero ? { minimumHard: 0 } : undefined,\n );\n if (normalized.error) throw new Error(normalized.error);\n return normalized.budget;\n}\n"],"mappings":"iCAiDa,EAA4B,CAAC,OAAQ,OAAQ,OAAQ,KAAK,CAOvE,SAAgB,EAAyB,EAA8D,CAGrG,OAFI,IAAU,IAAwB,IAClC,IAAU,IAAA,GAAkB,CAAC,GAAG,EAA0B,CACvD,CAAC,GAAG,IAAI,IAAI,EAAM,IAAK,GAAS,EAAK,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,CASvE,SAAgB,EACd,EACA,EAAQ,aACR,EAAmC,EAAE,CACY,CACjD,GAAI,IAAQ,IAAA,GAAW,MAAO,EAAE,CAChC,GAAI,OAAO,GAAQ,WAAY,GAAgB,MAAM,QAAQ,EAAI,CAC/D,MAAO,CAAE,MAAO,GAAG,EAAM,uDAAwD,CAEnF,IAAM,EAAiC,EACjC,EAAc,EAAQ,aAAe,EAErC,EAAO,EAAM,KACnB,GAAI,OAAO,GAAS,UAAY,CAAC,OAAO,UAAU,EAAK,EAAI,EAAO,EAChE,MAAO,CAAE,MAAO,GAAG,EAAM,8BAA8B,EAAY,GAAI,CAEzE,IAAM,EAAO,EAAM,KACnB,GAAI,IAAS,IAAA,KAAc,OAAO,GAAS,UAAY,CAAC,OAAO,UAAU,EAAK,EAAI,EAAO,GACvF,MAAO,CAAE,MAAO,GAAG,EAAM,8CAA+D,CAE1F,GAAI,IAAS,IAAA,IAAa,EAAO,EAC/B,MAAO,CAAE,MAAO,GAAG,EAAM,mBAAmB,EAAM,QAAS,CAG7D,IAAM,EAAW,EAAM,MACnB,EACJ,GAAI,IAAa,IAAA,IAAa,IAAa,IAAiB,CAC1D,GAAI,CAAC,MAAM,QAAQ,EAAS,CAAE,MAAO,CAAE,MAAO,GAAG,EAAM,+CAAgD,CACvG,GAAI,EAAS,SAAW,EAAG,MAAO,CAAE,MAAO,GAAG,EAAM,6CAA8C,CAClG,IAAK,IAAM,KAAQ,EACjB,GAAI,OAAO,GAAS,UAAY,CAAC,EAAK,MAAM,CAC1C,MAAO,CAAE,MAAO,GAAG,EAAM,2CAA4C,CAGzE,EAAQ,OAER,EAAQ,EAGV,MAAO,CACL,OAAQ,CACN,OACA,GAAI,IAAS,IAAA,GAAuB,EAAE,CAAb,CAAE,OAAM,CACjC,MAAO,EAAyB,EAAM,CACvC,CACF,CAyBH,SAAgB,EAAyB,EAA4B,EAAkB,EAAgC,CAErH,OADI,GAAiB,EAAO,KAAa,GAClC,EAAO,QAAU,KAAmB,EAAO,MAAM,SAAS,EAAS,CAG5E,SAAgB,EAAoB,EAA4B,EAA2B,CACzF,MAAO,wCAAwC,EAAU,YAAY,IAAc,EAAI,GAAK,IAAI,SAAS,EAAO,KAAK,SAAS,EAAO,KAAK,2FAG5I,SAAgB,EAAyB,EAA4B,EAAkB,EAA2B,CAChH,MAAO,wCAAwC,EAAU,YAAY,IAAc,EAAI,GAAK,IAAI,SAAS,EAAO,KAAK,UAAU,EAAS,0EAG1I,SAAgB,EAAoB,EAA4D,CAC9F,OAAO,EAAS,KAAK,UAAU,EAAO,CAAG,IAAA,GAU3C,SAAgB,EACd,EACA,EAAmC,EAAE,CACL,CAChC,GAAI,CAAC,GAAO,MAAM,CAAE,OAEpB,IAAM,EAAa,EADK,KAAK,MAAM,EAE3B,CACNA,EAAAA,gBACA,EAAQ,UAAY,CAAE,YAAa,EAAG,CAAG,IAAA,GAC1C,CACD,GAAI,EAAW,MAAO,MAAU,MAAM,EAAW,MAAM,CACvD,OAAO,EAAW"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../../_virtual/_rolldown/runtime.mjs";import{TOOL_BUDGET_ENV as t,init_env as n}from"../../env.mjs";function r(e){return e===d?d:e===void 0?[...u]:[...new Set(e.map(e=>e.trim()).filter(Boolean))]}function i(e,t=`toolBudget`,n={}){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))return{error:`${t} must be an object with hard and optional soft/block.`};let i=e,a=n.minimumHard??p,o=i.hard;if(typeof o!=`number`||!Number.isInteger(o)||o<a)return{error:`${t}.hard must be an integer >= ${a}.`};let s=i.soft;if(s!==void 0&&(typeof s!=`number`||!Number.isInteger(s)||s<f))return{error:`${t}.soft must be an integer >= ${f} when provided.`};if(s!==void 0&&s>o)return{error:`${t}.soft must be <= ${t}.hard.`};let c=i.block,l;if(c!==void 0&&c!==d){if(!Array.isArray(c))return{error:`${t}.block must be "*" or an array of tool names.`};if(c.length===0)return{error:`${t}.block must contain at least one tool name.`};for(let e of c)if(typeof e!=`string`||!e.trim())return{error:`${t}.block must contain non-empty tool names.`};l=c}else l=c;return{budget:{hard:o,...s===void 0?{}:{soft:s},block:r(l)}}}function a(e,t,n){return n<=e.hard?!1:e.block===d||e.block.includes(t)}function o(e,t){return`Tool budget soft limit reached after ${t} tool call${t===1?``:`s`} (soft ${e.soft}, hard ${e.hard}). Stop starting new browsing/search work and finalize from the context you already have.`}function s(e,t,n){return`Tool budget hard limit reached after ${n} tool call${n===1?``:`s`} (hard ${e.hard}). The '${t}' tool is blocked so you can finalize from the context you already have.`}function c(e){return e?JSON.stringify(e):void 0}function l(e,n={}){if(!e?.trim())return;let r=i(JSON.parse(e),t,n.allowZero?{minimumHard:0}:void 0);if(r.error)throw Error(r.error);return r.budget}var u,d,f,p,m=e((()=>{n(),u=[`read`,`grep`,`find`,`ls`],d=`*`,f=1,p=1}));m();export{l as decodeToolBudgetEnv,c as encodeToolBudgetEnv,m as init_toolBudget,a as shouldBlockToolForBudget,s as toolBudgetBlockedMessage,o as toolBudgetSoftNudge};
|
|
2
|
+
//# sourceMappingURL=toolBudget.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolBudget.mjs","names":[],"sources":["../../../src/runs/shared/toolBudget.ts"],"sourcesContent":["/**\n * Tool-call budget validation, enforcement predicates and its process handoff.\n *\n * DESIGN PATTERNS:\n * - Pure functions over a resolved budget; the running tool count is the\n * caller's, so this module has nothing to reset between runs\n * - The budget crosses into the child as one JSON environment value, encoded and\n * decoded here, so parent and child can never disagree about its shape\n *\n * WHY BLOCKING IS NARROWED TO A TOOL LIST:\n * The point of the hard limit is to stop open-ended browsing and searching, not\n * to strand a child that still has to write its answer. Blocking only the\n * read-style tools leaves `edit`, `write` and the reporting path available past\n * the limit, so an over-budget child finishes rather than fails.\n *\n * AVOID:\n * - Blocking on the call that reaches the hard limit; the count must exceed it\n * first, so the budgeted number of calls actually happens\n * - Reading the environment variable name from anywhere but `src/env.ts`\n */\n\nimport { TOOL_BUDGET_ENV } from '../../env.ts';\nimport type { ToolBudgetConfig } from '../../types.ts';\n\n/** A tool budget with its block list settled. */\nexport interface ResolvedToolBudget {\n soft?: number;\n hard: number;\n block: string[] | '*';\n}\n\n/** How a run stands against its tool budget. */\nexport type ToolBudgetOutcome = 'within-budget' | 'soft-reached' | 'hard-blocked';\n\n/** The budget plus everything observed about it during one run. */\nexport interface ToolBudgetState extends ResolvedToolBudget {\n outcome: ToolBudgetOutcome;\n toolCount: number;\n softReachedAt?: number;\n hardReachedAt?: number;\n blockedTool?: string;\n}\n\n/**\n * Blocked by default: the read-style tools a child loops on when it is stuck.\n *\n * Deliberately excludes `edit` and `write` so an over-budget child can still\n * land the work it has already reasoned about.\n */\nexport const DEFAULT_TOOL_BUDGET_BLOCK = ['read', 'grep', 'find', 'ls'] as const;\n\nconst BLOCK_ALL_TOOLS = '*';\nconst MIN_SOFT_LIMIT = 1;\nconst DEFAULT_MINIMUM_HARD_LIMIT = 1;\n\n/** Deduplicate and trim an authored block list, or pass through the block-all marker. */\nexport function normalizeToolBudgetBlock(block: ToolBudgetConfig['block'] | undefined): '*' | string[] {\n if (block === BLOCK_ALL_TOOLS) return BLOCK_ALL_TOOLS;\n if (block === undefined) return [...DEFAULT_TOOL_BUDGET_BLOCK];\n return [...new Set(block.map((tool) => tool.trim()).filter(Boolean))];\n}\n\n/**\n * Validate an authored tool budget and settle its block list.\n *\n * `minimumHard` is 0 only for the zero-authorised path, where a caller has\n * explicitly asked for a budget that blocks every tool from the first call.\n */\nexport function validateToolBudgetConfig(\n raw: unknown,\n label = 'toolBudget',\n options: { minimumHard?: 0 | 1 } = {},\n): { budget?: ResolvedToolBudget; error?: string } {\n if (raw === undefined) return {};\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return { error: `${label} must be an object with hard and optional soft/block.` };\n }\n const value: Record<string, unknown> = raw as Record<string, unknown>;\n const minimumHard = options.minimumHard ?? DEFAULT_MINIMUM_HARD_LIMIT;\n\n const hard = value.hard;\n if (typeof hard !== 'number' || !Number.isInteger(hard) || hard < minimumHard) {\n return { error: `${label}.hard must be an integer >= ${minimumHard}.` };\n }\n const soft = value.soft;\n if (soft !== undefined && (typeof soft !== 'number' || !Number.isInteger(soft) || soft < MIN_SOFT_LIMIT)) {\n return { error: `${label}.soft must be an integer >= ${MIN_SOFT_LIMIT} when provided.` };\n }\n if (soft !== undefined && soft > hard) {\n return { error: `${label}.soft must be <= ${label}.hard.` };\n }\n\n const rawBlock = value.block;\n let block: ToolBudgetConfig['block'] | undefined;\n if (rawBlock !== undefined && rawBlock !== BLOCK_ALL_TOOLS) {\n if (!Array.isArray(rawBlock)) return { error: `${label}.block must be \"*\" or an array of tool names.` };\n if (rawBlock.length === 0) return { error: `${label}.block must contain at least one tool name.` };\n for (const item of rawBlock) {\n if (typeof item !== 'string' || !item.trim()) {\n return { error: `${label}.block must contain non-empty tool names.` };\n }\n }\n block = rawBlock as string[];\n } else {\n block = rawBlock;\n }\n\n return {\n budget: {\n hard,\n ...(soft !== undefined ? { soft } : {}),\n block: normalizeToolBudgetBlock(block),\n },\n };\n}\n\nexport function initialToolBudgetState(budget: ResolvedToolBudget): ToolBudgetState {\n return { ...budget, toolCount: 0, outcome: 'within-budget' };\n}\n\nexport function toolBudgetState(budget: ResolvedToolBudget, toolCount: number, blockedTool?: string): ToolBudgetState {\n const overHard = toolCount > budget.hard;\n const overSoft = budget.soft !== undefined && toolCount >= budget.soft;\n return {\n ...budget,\n toolCount,\n outcome: overHard ? 'hard-blocked' : overSoft ? 'soft-reached' : 'within-budget',\n ...(overSoft ? { softReachedAt: budget.soft } : {}),\n ...(overHard ? { hardReachedAt: budget.hard, blockedTool } : {}),\n };\n}\n\n/**\n * Whether this call must be refused.\n *\n * `nextToolCount` is the count including the call being decided, so the budgeted\n * number of calls runs and only the one after it is refused.\n */\nexport function shouldBlockToolForBudget(budget: ResolvedToolBudget, toolName: string, nextToolCount: number): boolean {\n if (nextToolCount <= budget.hard) return false;\n return budget.block === BLOCK_ALL_TOOLS || budget.block.includes(toolName);\n}\n\nexport function toolBudgetSoftNudge(budget: ResolvedToolBudget, toolCount: number): string {\n return `Tool budget soft limit reached after ${toolCount} tool call${toolCount === 1 ? '' : 's'} (soft ${budget.soft}, hard ${budget.hard}). Stop starting new browsing/search work and finalize from the context you already have.`;\n}\n\nexport function toolBudgetBlockedMessage(budget: ResolvedToolBudget, toolName: string, toolCount: number): string {\n return `Tool budget hard limit reached after ${toolCount} tool call${toolCount === 1 ? '' : 's'} (hard ${budget.hard}). The '${toolName}' tool is blocked so you can finalize from the context you already have.`;\n}\n\nexport function encodeToolBudgetEnv(budget: ResolvedToolBudget | undefined): string | undefined {\n return budget ? JSON.stringify(budget) : undefined;\n}\n\n/**\n * Read the budget a parent handed to this child.\n *\n * Throws rather than returning an error string: an unreadable budget means the\n * child cannot know what it is allowed to do, and running unbudgeted would\n * silently defeat the cap the parent set.\n */\nexport function decodeToolBudgetEnv(\n value: string | undefined,\n options: { allowZero?: boolean } = {},\n): ResolvedToolBudget | undefined {\n if (!value?.trim()) return undefined;\n const parsed: unknown = JSON.parse(value);\n const normalized = validateToolBudgetConfig(\n parsed,\n TOOL_BUDGET_ENV,\n options.allowZero ? { minimumHard: 0 } : undefined,\n );\n if (normalized.error) throw new Error(normalized.error);\n return normalized.budget;\n}\n"],"mappings":"8HAwDA,SAAgB,EAAyB,EAA8D,CAGrG,OAFI,IAAU,EAAwB,EAClC,IAAU,IAAA,GAAkB,CAAC,GAAG,EAA0B,CACvD,CAAC,GAAG,IAAI,IAAI,EAAM,IAAK,GAAS,EAAK,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,CASvE,SAAgB,EACd,EACA,EAAQ,aACR,EAAmC,EAAE,CACY,CACjD,GAAI,IAAQ,IAAA,GAAW,MAAO,EAAE,CAChC,GAAI,OAAO,GAAQ,WAAY,GAAgB,MAAM,QAAQ,EAAI,CAC/D,MAAO,CAAE,MAAO,GAAG,EAAM,uDAAwD,CAEnF,IAAM,EAAiC,EACjC,EAAc,EAAQ,aAAe,EAErC,EAAO,EAAM,KACnB,GAAI,OAAO,GAAS,UAAY,CAAC,OAAO,UAAU,EAAK,EAAI,EAAO,EAChE,MAAO,CAAE,MAAO,GAAG,EAAM,8BAA8B,EAAY,GAAI,CAEzE,IAAM,EAAO,EAAM,KACnB,GAAI,IAAS,IAAA,KAAc,OAAO,GAAS,UAAY,CAAC,OAAO,UAAU,EAAK,EAAI,EAAO,GACvF,MAAO,CAAE,MAAO,GAAG,EAAM,8BAA8B,EAAe,iBAAkB,CAE1F,GAAI,IAAS,IAAA,IAAa,EAAO,EAC/B,MAAO,CAAE,MAAO,GAAG,EAAM,mBAAmB,EAAM,QAAS,CAG7D,IAAM,EAAW,EAAM,MACnB,EACJ,GAAI,IAAa,IAAA,IAAa,IAAa,EAAiB,CAC1D,GAAI,CAAC,MAAM,QAAQ,EAAS,CAAE,MAAO,CAAE,MAAO,GAAG,EAAM,+CAAgD,CACvG,GAAI,EAAS,SAAW,EAAG,MAAO,CAAE,MAAO,GAAG,EAAM,6CAA8C,CAClG,IAAK,IAAM,KAAQ,EACjB,GAAI,OAAO,GAAS,UAAY,CAAC,EAAK,MAAM,CAC1C,MAAO,CAAE,MAAO,GAAG,EAAM,2CAA4C,CAGzE,EAAQ,OAER,EAAQ,EAGV,MAAO,CACL,OAAQ,CACN,OACA,GAAI,IAAS,IAAA,GAAuB,EAAE,CAAb,CAAE,OAAM,CACjC,MAAO,EAAyB,EAAM,CACvC,CACF,CAyBH,SAAgB,EAAyB,EAA4B,EAAkB,EAAgC,CAErH,OADI,GAAiB,EAAO,KAAa,GAClC,EAAO,QAAU,GAAmB,EAAO,MAAM,SAAS,EAAS,CAG5E,SAAgB,EAAoB,EAA4B,EAA2B,CACzF,MAAO,wCAAwC,EAAU,YAAY,IAAc,EAAI,GAAK,IAAI,SAAS,EAAO,KAAK,SAAS,EAAO,KAAK,2FAG5I,SAAgB,EAAyB,EAA4B,EAAkB,EAA2B,CAChH,MAAO,wCAAwC,EAAU,YAAY,IAAc,EAAI,GAAK,IAAI,SAAS,EAAO,KAAK,UAAU,EAAS,0EAG1I,SAAgB,EAAoB,EAA4D,CAC9F,OAAO,EAAS,KAAK,UAAU,EAAO,CAAG,IAAA,GAU3C,SAAgB,EACd,EACA,EAAmC,EAAE,CACL,CAChC,GAAI,CAAC,GAAO,MAAM,CAAE,OAEpB,IAAM,EAAa,EADK,KAAK,MAAM,EAE3B,CACN,EACA,EAAQ,UAAY,CAAE,YAAa,EAAG,CAAG,IAAA,GAC1C,CACD,GAAI,EAAW,MAAO,MAAU,MAAM,EAAW,MAAM,CACvD,OAAO,EAAW,gCAzJ2B,CA4BlC,EAA4B,CAAC,OAAQ,OAAQ,OAAQ,KAAK,CAEjE,EAAkB,IAClB,EAAiB,EACjB,EAA6B"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`);require(`./atomicJson.cjs`);const t=require(`./paths.cjs`);let n=require(`node:fs`);n=e.__toESM(n,1);let r=require(`node:path`);r=e.__toESM(r,1);const i=`subagent-artifacts`,a=/[^\w.-]/g;function o(e){return r.join(e,`.doom-team`)}function s(e){return r.join(o(e),`artifacts`)}function c(e,n,a=`session`){switch(a){case`session`:if(e){let t=r.dirname(e);return r.join(t,i)}return t.TEMP_ARTIFACTS_DIR;case`temp`:return t.TEMP_ARTIFACTS_DIR;case`project`:if(n)return s(n);if(e){let t=r.dirname(e);return r.join(t,i)}return t.TEMP_ARTIFACTS_DIR;default:throw Error(`Unsupported artifactDir ${JSON.stringify(a)}; expected "project", "session", or "temp".`)}}function l(e,t,n,i){let o=i===void 0?``:`_${i}`,s=`${t}_${n.replace(a,`_`)}${o}`;return{inputPath:r.join(e,`${s}_input.md`),outputPath:r.join(e,`${s}_output.md`),jsonlPath:r.join(e,`${s}.jsonl`),transcriptPath:r.join(e,`${s}_transcript.jsonl`),metadataPath:r.join(e,`${s}_meta.json`)}}function u(e,t){n.appendFileSync(e,`${t}\n`)}exports.appendJsonl=u,exports.getArtifactPaths=l,exports.getArtifactsDir=c;
|
|
2
|
+
//# sourceMappingURL=artifacts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.cjs","names":["path","TEMP_ARTIFACTS_DIR"],"sources":["../../src/shared/artifacts.ts"],"sourcesContent":["/**\n * Artifact file layout and retention for subagent runs.\n *\n * DESIGN PATTERNS:\n * - Path shape is derived, never stored: `getArtifactPaths` is pure so a reader\n * can reconstruct every file for a run from the run id and agent name alone\n * - The agent name is sanitised into the filename because it comes from\n * user-authored frontmatter and would otherwise let a run write outside the\n * artifacts directory\n * - Cleanup is rate-limited by a marker file rather than by an in-process timer,\n * so several concurrent extension hosts do not each sweep on every startup\n *\n * AVOID:\n * - Failing a run because housekeeping failed; every cleanup error is swallowed\n * deliberately and the sweep continues with the next entry\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { ArtifactDirPreference, ArtifactPaths } from '../types.ts';\nimport { writeAtomicJson } from './atomicJson.ts';\nimport { getAgentDir } from './configDir.ts';\nimport { TEMP_ARTIFACTS_DIR } from './paths.ts';\n\nconst CLEANUP_MARKER_FILE = '.last-cleanup';\n/**\n * In-project artifact root, named for this package rather than its predecessor.\n *\n * Both packages can be installed at once during cutover, and both sweep their\n * root on a retention timer. A shared name would let one package's cleanup delete\n * the other's live artifacts, so the roots are kept disjoint.\n */\nconst PROJECT_ARTIFACT_ROOT = '.doom-team';\nconst PROJECT_ARTIFACTS_DIR_NAME = 'artifacts';\nconst PROJECT_CHAIN_RUNS_DIR_NAME = 'chain-runs';\nconst SESSION_ARTIFACTS_DIR_NAME = 'subagent-artifacts';\nconst SESSIONS_DIR_NAME = 'sessions';\nconst UNSAFE_AGENT_NAME_CHARS = /[^\\w.-]/g;\nconst AGENT_NAME_REPLACEMENT = '_';\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\nconst CLEANUP_INTERVAL_MS = MS_PER_DAY;\n\nexport function getProjectSubagentsDir(cwd: string): string {\n return path.join(cwd, PROJECT_ARTIFACT_ROOT);\n}\n\nexport function getProjectArtifactsDir(cwd: string): string {\n return path.join(getProjectSubagentsDir(cwd), PROJECT_ARTIFACTS_DIR_NAME);\n}\n\nexport function getProjectChainRunsDir(cwd: string): string {\n return path.join(getProjectSubagentsDir(cwd), PROJECT_CHAIN_RUNS_DIR_NAME);\n}\n\nexport function getArtifactsDir(\n sessionFile: string | null,\n projectCwd?: string,\n dirPreference: ArtifactDirPreference = 'session',\n): string {\n switch (dirPreference) {\n case 'session':\n if (sessionFile) {\n const sessionDir = path.dirname(sessionFile);\n return path.join(sessionDir, SESSION_ARTIFACTS_DIR_NAME);\n }\n return TEMP_ARTIFACTS_DIR;\n case 'temp':\n return TEMP_ARTIFACTS_DIR;\n case 'project':\n if (projectCwd) return getProjectArtifactsDir(projectCwd);\n if (sessionFile) {\n const sessionDir = path.dirname(sessionFile);\n return path.join(sessionDir, SESSION_ARTIFACTS_DIR_NAME);\n }\n return TEMP_ARTIFACTS_DIR;\n default:\n throw new Error(\n `Unsupported artifactDir ${JSON.stringify(dirPreference)}; expected \"project\", \"session\", or \"temp\".`,\n );\n }\n}\n\nexport function getArtifactPaths(artifactsDir: string, runId: string, agent: string, index?: number): ArtifactPaths {\n const suffix = index !== undefined ? `_${index}` : '';\n const safeAgent = agent.replace(UNSAFE_AGENT_NAME_CHARS, AGENT_NAME_REPLACEMENT);\n const base = `${runId}_${safeAgent}${suffix}`;\n return {\n inputPath: path.join(artifactsDir, `${base}_input.md`),\n outputPath: path.join(artifactsDir, `${base}_output.md`),\n jsonlPath: path.join(artifactsDir, `${base}.jsonl`),\n transcriptPath: path.join(artifactsDir, `${base}_transcript.jsonl`),\n metadataPath: path.join(artifactsDir, `${base}_meta.json`),\n };\n}\n\nexport function ensureArtifactsDir(dir: string): void {\n fs.mkdirSync(dir, { recursive: true });\n}\n\nexport function writeArtifact(filePath: string, content: string): void {\n fs.writeFileSync(filePath, content, 'utf-8');\n}\n\nexport function formatOutputArtifactContent(input: {\n output: string;\n error?: string;\n transcriptPath?: string;\n metadataPath?: string;\n}): string {\n // An empty output artifact tells the reader nothing about why a run produced\n // nothing, so a failure with no output is replaced by the error and its evidence.\n if (input.output.trim() || !input.error) return input.output;\n const lines = ['Subagent run failed before producing output.', '', 'Error:', input.error];\n if (input.transcriptPath) lines.push('', `Transcript: ${input.transcriptPath}`);\n if (input.metadataPath) lines.push(`Metadata: ${input.metadataPath}`);\n return lines.join('\\n');\n}\n\n/**\n * Publish a run's metadata sidecar.\n *\n * Written atomically because the parent polls this file while the run is still\n * producing it, and a direct write lets that poll observe a half-serialised object.\n */\nexport function writeMetadata(filePath: string, metadata: object): void {\n writeAtomicJson(filePath, metadata);\n}\n\nexport function appendJsonl(filePath: string, line: string): void {\n fs.appendFileSync(filePath, `${line}\\n`);\n}\n\nexport function cleanupOldArtifacts(dir: string, maxAgeDays: number): void {\n if (!fs.existsSync(dir)) return;\n\n const markerPath = path.join(dir, CLEANUP_MARKER_FILE);\n const now = Date.now();\n\n if (fs.existsSync(markerPath)) {\n if (now - fs.statSync(markerPath).mtimeMs < CLEANUP_INTERVAL_MS) return;\n }\n\n const maxAgeMs = maxAgeDays * MS_PER_DAY;\n const cutoff = now - maxAgeMs;\n\n for (const file of fs.readdirSync(dir)) {\n if (file === CLEANUP_MARKER_FILE) continue;\n const filePath = path.join(dir, file);\n try {\n const stat = fs.statSync(filePath);\n if (stat.mtimeMs < cutoff) {\n fs.unlinkSync(filePath);\n }\n } catch {\n // Artifact cleanup is best-effort housekeeping. Skip files that disappear\n // or become unreadable while scanning so one bad entry does not block the rest.\n }\n }\n\n fs.writeFileSync(markerPath, String(now));\n}\n\nexport function cleanupAllArtifactDirs(maxAgeDays: number): void {\n cleanupOldArtifacts(TEMP_ARTIFACTS_DIR, maxAgeDays);\n\n const sessionsBase = path.join(getAgentDir(), SESSIONS_DIR_NAME);\n if (!fs.existsSync(sessionsBase)) return;\n\n let dirs: string[];\n try {\n dirs = fs.readdirSync(sessionsBase);\n } catch {\n // Session artifact cleanup is best-effort. If the sessions root cannot be read,\n // skip cleanup instead of failing extension startup.\n return;\n }\n\n for (const dir of dirs) {\n const artifactsDir = path.join(sessionsBase, dir, SESSION_ARTIFACTS_DIR_NAME);\n try {\n cleanupOldArtifacts(artifactsDir, maxAgeDays);\n } catch {\n // Session cleanup is best-effort. Keep going so one unreadable session dir\n // does not block cleanup for the rest.\n }\n }\n}\n"],"mappings":"sMAgCA,MAGM,EAA6B,qBAE7B,EAA0B,WAKhC,SAAgB,EAAuB,EAAqB,CAC1D,OAAOA,EAAK,KAAK,EAAK,aAAsB,CAG9C,SAAgB,EAAuB,EAAqB,CAC1D,OAAOA,EAAK,KAAK,EAAuB,EAAI,CAAE,YAA2B,CAO3E,SAAgB,EACd,EACA,EACA,EAAuC,UAC/B,CACR,OAAQ,EAAR,CACE,IAAK,UACH,GAAI,EAAa,CACf,IAAM,EAAaA,EAAK,QAAQ,EAAY,CAC5C,OAAOA,EAAK,KAAK,EAAY,EAA2B,CAE1D,OAAOC,EAAAA,mBACT,IAAK,OACH,OAAOA,EAAAA,mBACT,IAAK,UACH,GAAI,EAAY,OAAO,EAAuB,EAAW,CACzD,GAAI,EAAa,CACf,IAAM,EAAaD,EAAK,QAAQ,EAAY,CAC5C,OAAOA,EAAK,KAAK,EAAY,EAA2B,CAE1D,OAAOC,EAAAA,mBACT,QACE,MAAU,MACR,2BAA2B,KAAK,UAAU,EAAc,CAAC,6CAC1D,EAIP,SAAgB,EAAiB,EAAsB,EAAe,EAAe,EAA+B,CAClH,IAAM,EAAS,IAAU,IAAA,GAA0B,GAAd,IAAI,IAEnC,EAAO,GAAG,EAAM,GADJ,EAAM,QAAQ,EAAyB,IACvB,GAAG,IACrC,MAAO,CACL,UAAWD,EAAK,KAAK,EAAc,GAAG,EAAK,WAAW,CACtD,WAAYA,EAAK,KAAK,EAAc,GAAG,EAAK,YAAY,CACxD,UAAWA,EAAK,KAAK,EAAc,GAAG,EAAK,QAAQ,CACnD,eAAgBA,EAAK,KAAK,EAAc,GAAG,EAAK,mBAAmB,CACnE,aAAcA,EAAK,KAAK,EAAc,GAAG,EAAK,YAAY,CAC3D,CAoCH,SAAgB,EAAY,EAAkB,EAAoB,CAChE,EAAG,eAAe,EAAU,GAAG,EAAK,IAAI"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{init_atomicJson as t}from"./atomicJson.mjs";import{TEMP_ARTIFACTS_DIR as n,init_paths as r}from"./paths.mjs";import*as i from"node:fs";import*as a from"node:path";function o(e){return a.join(e,d)}function s(e){return a.join(o(e),f)}function c(e,t,r=`session`){switch(r){case`session`:if(e){let t=a.dirname(e);return a.join(t,p)}return n;case`temp`:return n;case`project`:if(t)return s(t);if(e){let t=a.dirname(e);return a.join(t,p)}return n;default:throw Error(`Unsupported artifactDir ${JSON.stringify(r)}; expected "project", "session", or "temp".`)}}function l(e,t,n,r){let i=r===void 0?``:`_${r}`,o=`${t}_${n.replace(m,h)}${i}`;return{inputPath:a.join(e,`${o}_input.md`),outputPath:a.join(e,`${o}_output.md`),jsonlPath:a.join(e,`${o}.jsonl`),transcriptPath:a.join(e,`${o}_transcript.jsonl`),metadataPath:a.join(e,`${o}_meta.json`)}}function u(e,t){i.appendFileSync(e,`${t}\n`)}var d,f,p,m,h,g=e((()=>{t(),r(),d=`.doom-team`,f=`artifacts`,p=`subagent-artifacts`,m=/[^\w.-]/g,h=`_`}));g();export{u as appendJsonl,l as getArtifactPaths,c as getArtifactsDir,g as init_artifacts};
|
|
2
|
+
//# sourceMappingURL=artifacts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.mjs","names":[],"sources":["../../src/shared/artifacts.ts"],"sourcesContent":["/**\n * Artifact file layout and retention for subagent runs.\n *\n * DESIGN PATTERNS:\n * - Path shape is derived, never stored: `getArtifactPaths` is pure so a reader\n * can reconstruct every file for a run from the run id and agent name alone\n * - The agent name is sanitised into the filename because it comes from\n * user-authored frontmatter and would otherwise let a run write outside the\n * artifacts directory\n * - Cleanup is rate-limited by a marker file rather than by an in-process timer,\n * so several concurrent extension hosts do not each sweep on every startup\n *\n * AVOID:\n * - Failing a run because housekeeping failed; every cleanup error is swallowed\n * deliberately and the sweep continues with the next entry\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { ArtifactDirPreference, ArtifactPaths } from '../types.ts';\nimport { writeAtomicJson } from './atomicJson.ts';\nimport { getAgentDir } from './configDir.ts';\nimport { TEMP_ARTIFACTS_DIR } from './paths.ts';\n\nconst CLEANUP_MARKER_FILE = '.last-cleanup';\n/**\n * In-project artifact root, named for this package rather than its predecessor.\n *\n * Both packages can be installed at once during cutover, and both sweep their\n * root on a retention timer. A shared name would let one package's cleanup delete\n * the other's live artifacts, so the roots are kept disjoint.\n */\nconst PROJECT_ARTIFACT_ROOT = '.doom-team';\nconst PROJECT_ARTIFACTS_DIR_NAME = 'artifacts';\nconst PROJECT_CHAIN_RUNS_DIR_NAME = 'chain-runs';\nconst SESSION_ARTIFACTS_DIR_NAME = 'subagent-artifacts';\nconst SESSIONS_DIR_NAME = 'sessions';\nconst UNSAFE_AGENT_NAME_CHARS = /[^\\w.-]/g;\nconst AGENT_NAME_REPLACEMENT = '_';\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\nconst CLEANUP_INTERVAL_MS = MS_PER_DAY;\n\nexport function getProjectSubagentsDir(cwd: string): string {\n return path.join(cwd, PROJECT_ARTIFACT_ROOT);\n}\n\nexport function getProjectArtifactsDir(cwd: string): string {\n return path.join(getProjectSubagentsDir(cwd), PROJECT_ARTIFACTS_DIR_NAME);\n}\n\nexport function getProjectChainRunsDir(cwd: string): string {\n return path.join(getProjectSubagentsDir(cwd), PROJECT_CHAIN_RUNS_DIR_NAME);\n}\n\nexport function getArtifactsDir(\n sessionFile: string | null,\n projectCwd?: string,\n dirPreference: ArtifactDirPreference = 'session',\n): string {\n switch (dirPreference) {\n case 'session':\n if (sessionFile) {\n const sessionDir = path.dirname(sessionFile);\n return path.join(sessionDir, SESSION_ARTIFACTS_DIR_NAME);\n }\n return TEMP_ARTIFACTS_DIR;\n case 'temp':\n return TEMP_ARTIFACTS_DIR;\n case 'project':\n if (projectCwd) return getProjectArtifactsDir(projectCwd);\n if (sessionFile) {\n const sessionDir = path.dirname(sessionFile);\n return path.join(sessionDir, SESSION_ARTIFACTS_DIR_NAME);\n }\n return TEMP_ARTIFACTS_DIR;\n default:\n throw new Error(\n `Unsupported artifactDir ${JSON.stringify(dirPreference)}; expected \"project\", \"session\", or \"temp\".`,\n );\n }\n}\n\nexport function getArtifactPaths(artifactsDir: string, runId: string, agent: string, index?: number): ArtifactPaths {\n const suffix = index !== undefined ? `_${index}` : '';\n const safeAgent = agent.replace(UNSAFE_AGENT_NAME_CHARS, AGENT_NAME_REPLACEMENT);\n const base = `${runId}_${safeAgent}${suffix}`;\n return {\n inputPath: path.join(artifactsDir, `${base}_input.md`),\n outputPath: path.join(artifactsDir, `${base}_output.md`),\n jsonlPath: path.join(artifactsDir, `${base}.jsonl`),\n transcriptPath: path.join(artifactsDir, `${base}_transcript.jsonl`),\n metadataPath: path.join(artifactsDir, `${base}_meta.json`),\n };\n}\n\nexport function ensureArtifactsDir(dir: string): void {\n fs.mkdirSync(dir, { recursive: true });\n}\n\nexport function writeArtifact(filePath: string, content: string): void {\n fs.writeFileSync(filePath, content, 'utf-8');\n}\n\nexport function formatOutputArtifactContent(input: {\n output: string;\n error?: string;\n transcriptPath?: string;\n metadataPath?: string;\n}): string {\n // An empty output artifact tells the reader nothing about why a run produced\n // nothing, so a failure with no output is replaced by the error and its evidence.\n if (input.output.trim() || !input.error) return input.output;\n const lines = ['Subagent run failed before producing output.', '', 'Error:', input.error];\n if (input.transcriptPath) lines.push('', `Transcript: ${input.transcriptPath}`);\n if (input.metadataPath) lines.push(`Metadata: ${input.metadataPath}`);\n return lines.join('\\n');\n}\n\n/**\n * Publish a run's metadata sidecar.\n *\n * Written atomically because the parent polls this file while the run is still\n * producing it, and a direct write lets that poll observe a half-serialised object.\n */\nexport function writeMetadata(filePath: string, metadata: object): void {\n writeAtomicJson(filePath, metadata);\n}\n\nexport function appendJsonl(filePath: string, line: string): void {\n fs.appendFileSync(filePath, `${line}\\n`);\n}\n\nexport function cleanupOldArtifacts(dir: string, maxAgeDays: number): void {\n if (!fs.existsSync(dir)) return;\n\n const markerPath = path.join(dir, CLEANUP_MARKER_FILE);\n const now = Date.now();\n\n if (fs.existsSync(markerPath)) {\n if (now - fs.statSync(markerPath).mtimeMs < CLEANUP_INTERVAL_MS) return;\n }\n\n const maxAgeMs = maxAgeDays * MS_PER_DAY;\n const cutoff = now - maxAgeMs;\n\n for (const file of fs.readdirSync(dir)) {\n if (file === CLEANUP_MARKER_FILE) continue;\n const filePath = path.join(dir, file);\n try {\n const stat = fs.statSync(filePath);\n if (stat.mtimeMs < cutoff) {\n fs.unlinkSync(filePath);\n }\n } catch {\n // Artifact cleanup is best-effort housekeeping. Skip files that disappear\n // or become unreadable while scanning so one bad entry does not block the rest.\n }\n }\n\n fs.writeFileSync(markerPath, String(now));\n}\n\nexport function cleanupAllArtifactDirs(maxAgeDays: number): void {\n cleanupOldArtifacts(TEMP_ARTIFACTS_DIR, maxAgeDays);\n\n const sessionsBase = path.join(getAgentDir(), SESSIONS_DIR_NAME);\n if (!fs.existsSync(sessionsBase)) return;\n\n let dirs: string[];\n try {\n dirs = fs.readdirSync(sessionsBase);\n } catch {\n // Session artifact cleanup is best-effort. If the sessions root cannot be read,\n // skip cleanup instead of failing extension startup.\n return;\n }\n\n for (const dir of dirs) {\n const artifactsDir = path.join(sessionsBase, dir, SESSION_ARTIFACTS_DIR_NAME);\n try {\n cleanupOldArtifacts(artifactsDir, maxAgeDays);\n } catch {\n // Session cleanup is best-effort. Keep going so one unreadable session dir\n // does not block cleanup for the rest.\n }\n }\n}\n"],"mappings":"uOA0CA,SAAgB,EAAuB,EAAqB,CAC1D,OAAO,EAAK,KAAK,EAAK,EAAsB,CAG9C,SAAgB,EAAuB,EAAqB,CAC1D,OAAO,EAAK,KAAK,EAAuB,EAAI,CAAE,EAA2B,CAO3E,SAAgB,EACd,EACA,EACA,EAAuC,UAC/B,CACR,OAAQ,EAAR,CACE,IAAK,UACH,GAAI,EAAa,CACf,IAAM,EAAa,EAAK,QAAQ,EAAY,CAC5C,OAAO,EAAK,KAAK,EAAY,EAA2B,CAE1D,OAAO,EACT,IAAK,OACH,OAAO,EACT,IAAK,UACH,GAAI,EAAY,OAAO,EAAuB,EAAW,CACzD,GAAI,EAAa,CACf,IAAM,EAAa,EAAK,QAAQ,EAAY,CAC5C,OAAO,EAAK,KAAK,EAAY,EAA2B,CAE1D,OAAO,EACT,QACE,MAAU,MACR,2BAA2B,KAAK,UAAU,EAAc,CAAC,6CAC1D,EAIP,SAAgB,EAAiB,EAAsB,EAAe,EAAe,EAA+B,CAClH,IAAM,EAAS,IAAU,IAAA,GAA0B,GAAd,IAAI,IAEnC,EAAO,GAAG,EAAM,GADJ,EAAM,QAAQ,EAAyB,EACvB,GAAG,IACrC,MAAO,CACL,UAAW,EAAK,KAAK,EAAc,GAAG,EAAK,WAAW,CACtD,WAAY,EAAK,KAAK,EAAc,GAAG,EAAK,YAAY,CACxD,UAAW,EAAK,KAAK,EAAc,GAAG,EAAK,QAAQ,CACnD,eAAgB,EAAK,KAAK,EAAc,GAAG,EAAK,mBAAmB,CACnE,aAAc,EAAK,KAAK,EAAc,GAAG,EAAK,YAAY,CAC3D,CAoCH,SAAgB,EAAY,EAAkB,EAAoB,CAChE,EAAG,eAAe,EAAU,GAAG,EAAK,IAAI,4BA7GQ,IAEF,CAU1C,EAAwB,aACxB,EAA6B,YAE7B,EAA6B,qBAE7B,EAA0B,WAC1B,EAAyB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`./fileSystemRetry.cjs`);let n=require(`node:fs`);n=e.__toESM(n,1);let r=require(`node:path`);r=e.__toESM(r,1);function i(e={}){let i=e.fs??n,a=e.now??Date.now,o=e.pid??process.pid,s=e.random??Math.random,c=e.mode,l=e.retryRenameErrors??process.platform===`win32`,u=e.retryDirectoryErrors??l,d=e.retryDelaysMs??t.DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS,f=l?d:[],p=u?d:[],m=e.wait??t.waitForFileSystemRetry;return(e,n)=>{t.runFileSystemOperationWithRetry(()=>{i.mkdirSync(r.dirname(e),{recursive:!0})},{retryDelaysMs:p,wait:m});let l=r.join(r.dirname(e),`.${r.basename(e)}.${o}.${a()}.${s().toString(36).slice(2)}.tmp`);try{i.writeFileSync(l,JSON.stringify(n,null,2),c===void 0?`utf-8`:{encoding:`utf-8`,mode:c}),t.runFileSystemOperationWithRetry(()=>{i.renameSync(l,e)},{retryDelaysMs:f,wait:m})}finally{i.rmSync(l,{force:!0})}}}const a=i(),o=i({mode:384});exports.writeAtomicJson=a,exports.writePrivateAtomicJson=o;
|
|
2
|
+
//# sourceMappingURL=atomicJson.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomicJson.cjs","names":["fs","DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS","waitForFileSystemRetry","path"],"sources":["../../src/shared/atomicJson.ts"],"sourcesContent":["/**\n * Atomic JSON writes.\n *\n * DESIGN PATTERNS:\n * - Write to a uniquely named temp file in the destination directory, then\n * rename onto the target. Rename is atomic within a filesystem, so a reader\n * sees either the previous file or the complete new one, never a partial write\n * - The temp name carries pid, timestamp and a random suffix so two processes\n * writing the same target never collide on the temp file\n *\n * WHAT THIS DOES NOT DO:\n * This prevents torn reads. It does NOT prevent lost updates: two writers doing\n * read-modify-write still clobber each other, because the rename only publishes\n * whatever the caller already computed. Anything needing mutual exclusion has to\n * take a lease or claim, not rely on atomic writes.\n *\n * AVOID:\n * - Treating this as a lock\n * - Using the plain writer for anything sensitive; prefer writePrivateAtomicJson,\n * which creates the file 0600 so a shared temp directory does not leak prompts\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS,\n runFileSystemOperationWithRetry,\n waitForFileSystemRetry,\n} from './fileSystemRetry.ts';\n\nconst PRIVATE_FILE_MODE = 0o600;\nconst JSON_INDENT = 2;\n\ntype AtomicJsonFs = Pick<typeof fs, 'mkdirSync' | 'writeFileSync' | 'renameSync' | 'rmSync'>;\n\ntype AtomicJsonWriterOptions = {\n fs?: AtomicJsonFs;\n now?: () => number;\n pid?: number;\n random?: () => number;\n mode?: number;\n retryRenameErrors?: boolean;\n retryDirectoryErrors?: boolean;\n retryDelaysMs?: readonly number[];\n wait?: (delayMs: number) => void;\n};\n\nexport function createAtomicJsonWriter(\n options: AtomicJsonWriterOptions = {},\n): (filePath: string, payload: object) => void {\n const fsImpl = options.fs ?? fs;\n const now = options.now ?? Date.now;\n const pid = options.pid ?? process.pid;\n const random = options.random ?? Math.random;\n const mode = options.mode;\n // Retries exist for Windows directory and rename locks; other platforms fail fast.\n const retryRenameErrors = options.retryRenameErrors ?? process.platform === 'win32';\n const retryDirectoryErrors = options.retryDirectoryErrors ?? retryRenameErrors;\n const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS;\n const renameRetryDelaysMs = retryRenameErrors ? retryDelaysMs : [];\n const directoryRetryDelaysMs = retryDirectoryErrors ? retryDelaysMs : [];\n const wait = options.wait ?? waitForFileSystemRetry;\n\n return (filePath: string, payload: object): void => {\n runFileSystemOperationWithRetry(\n () => {\n fsImpl.mkdirSync(path.dirname(filePath), { recursive: true });\n },\n { retryDelaysMs: directoryRetryDelaysMs, wait },\n );\n const tempPath = path.join(\n path.dirname(filePath),\n `.${path.basename(filePath)}.${pid}.${now()}.${random().toString(36).slice(2)}.tmp`,\n );\n try {\n fsImpl.writeFileSync(\n tempPath,\n JSON.stringify(payload, null, JSON_INDENT),\n mode === undefined ? 'utf-8' : { encoding: 'utf-8', mode },\n );\n runFileSystemOperationWithRetry(\n () => {\n fsImpl.renameSync(tempPath, filePath);\n },\n { retryDelaysMs: renameRetryDelaysMs, wait },\n );\n } finally {\n // Best effort: on the happy path the rename already consumed the temp file.\n fsImpl.rmSync(tempPath, { force: true });\n }\n };\n}\n\nexport const writeAtomicJson = createAtomicJsonWriter();\nexport const writePrivateAtomicJson = createAtomicJsonWriter({ mode: PRIVATE_FILE_MODE });\n"],"mappings":"8KA+CA,SAAgB,EACd,EAAmC,EAAE,CACQ,CAC7C,IAAM,EAAS,EAAQ,IAAMA,EACvB,EAAM,EAAQ,KAAO,KAAK,IAC1B,EAAM,EAAQ,KAAO,QAAQ,IAC7B,EAAS,EAAQ,QAAU,KAAK,OAChC,EAAO,EAAQ,KAEf,EAAoB,EAAQ,mBAAqB,QAAQ,WAAa,QACtE,EAAuB,EAAQ,sBAAwB,EACvD,EAAgB,EAAQ,eAAiBC,EAAAA,oCACzC,EAAsB,EAAoB,EAAgB,EAAE,CAC5D,EAAyB,EAAuB,EAAgB,EAAE,CAClE,EAAO,EAAQ,MAAQC,EAAAA,uBAE7B,OAAQ,EAAkB,IAA0B,CAClD,EAAA,oCACQ,CACJ,EAAO,UAAUC,EAAK,QAAQ,EAAS,CAAE,CAAE,UAAW,GAAM,CAAC,EAE/D,CAAE,cAAe,EAAwB,OAAM,CAChD,CACD,IAAM,EAAWA,EAAK,KACpBA,EAAK,QAAQ,EAAS,CACtB,IAAIA,EAAK,SAAS,EAAS,CAAC,GAAG,EAAI,GAAG,GAAK,CAAC,GAAG,GAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,MAC/E,CACD,GAAI,CACF,EAAO,cACL,EACA,KAAK,UAAU,EAAS,KAAM,EAAY,CAC1C,IAAS,IAAA,GAAY,QAAU,CAAE,SAAU,QAAS,OAAM,CAC3D,CACD,EAAA,oCACQ,CACJ,EAAO,WAAW,EAAU,EAAS,EAEvC,CAAE,cAAe,EAAqB,OAAM,CAC7C,QACO,CAER,EAAO,OAAO,EAAU,CAAE,MAAO,GAAM,CAAC,GAK9C,MAAa,EAAkB,GAAwB,CAC1C,EAAyB,EAAuB,CAAE,KAAM,IAAmB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS as t,init_fileSystemRetry as n,runFileSystemOperationWithRetry as r,waitForFileSystemRetry as i}from"./fileSystemRetry.mjs";import*as a from"node:fs";import*as o from"node:path";function s(e={}){let n=e.fs??a,s=e.now??Date.now,c=e.pid??process.pid,u=e.random??Math.random,d=e.mode,f=e.retryRenameErrors??process.platform===`win32`,p=e.retryDirectoryErrors??f,m=e.retryDelaysMs??t,h=f?m:[],g=p?m:[],_=e.wait??i;return(e,t)=>{r(()=>{n.mkdirSync(o.dirname(e),{recursive:!0})},{retryDelaysMs:g,wait:_});let i=o.join(o.dirname(e),`.${o.basename(e)}.${c}.${s()}.${u().toString(36).slice(2)}.tmp`);try{n.writeFileSync(i,JSON.stringify(t,null,l),d===void 0?`utf-8`:{encoding:`utf-8`,mode:d}),r(()=>{n.renameSync(i,e)},{retryDelaysMs:h,wait:_})}finally{n.rmSync(i,{force:!0})}}}var c,l,u,d,f=e((()=>{n(),c=384,l=2,u=s(),d=s({mode:c})}));f();export{f as init_atomicJson,u as writeAtomicJson,d as writePrivateAtomicJson};
|
|
2
|
+
//# sourceMappingURL=atomicJson.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomicJson.mjs","names":[],"sources":["../../src/shared/atomicJson.ts"],"sourcesContent":["/**\n * Atomic JSON writes.\n *\n * DESIGN PATTERNS:\n * - Write to a uniquely named temp file in the destination directory, then\n * rename onto the target. Rename is atomic within a filesystem, so a reader\n * sees either the previous file or the complete new one, never a partial write\n * - The temp name carries pid, timestamp and a random suffix so two processes\n * writing the same target never collide on the temp file\n *\n * WHAT THIS DOES NOT DO:\n * This prevents torn reads. It does NOT prevent lost updates: two writers doing\n * read-modify-write still clobber each other, because the rename only publishes\n * whatever the caller already computed. Anything needing mutual exclusion has to\n * take a lease or claim, not rely on atomic writes.\n *\n * AVOID:\n * - Treating this as a lock\n * - Using the plain writer for anything sensitive; prefer writePrivateAtomicJson,\n * which creates the file 0600 so a shared temp directory does not leak prompts\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS,\n runFileSystemOperationWithRetry,\n waitForFileSystemRetry,\n} from './fileSystemRetry.ts';\n\nconst PRIVATE_FILE_MODE = 0o600;\nconst JSON_INDENT = 2;\n\ntype AtomicJsonFs = Pick<typeof fs, 'mkdirSync' | 'writeFileSync' | 'renameSync' | 'rmSync'>;\n\ntype AtomicJsonWriterOptions = {\n fs?: AtomicJsonFs;\n now?: () => number;\n pid?: number;\n random?: () => number;\n mode?: number;\n retryRenameErrors?: boolean;\n retryDirectoryErrors?: boolean;\n retryDelaysMs?: readonly number[];\n wait?: (delayMs: number) => void;\n};\n\nexport function createAtomicJsonWriter(\n options: AtomicJsonWriterOptions = {},\n): (filePath: string, payload: object) => void {\n const fsImpl = options.fs ?? fs;\n const now = options.now ?? Date.now;\n const pid = options.pid ?? process.pid;\n const random = options.random ?? Math.random;\n const mode = options.mode;\n // Retries exist for Windows directory and rename locks; other platforms fail fast.\n const retryRenameErrors = options.retryRenameErrors ?? process.platform === 'win32';\n const retryDirectoryErrors = options.retryDirectoryErrors ?? retryRenameErrors;\n const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS;\n const renameRetryDelaysMs = retryRenameErrors ? retryDelaysMs : [];\n const directoryRetryDelaysMs = retryDirectoryErrors ? retryDelaysMs : [];\n const wait = options.wait ?? waitForFileSystemRetry;\n\n return (filePath: string, payload: object): void => {\n runFileSystemOperationWithRetry(\n () => {\n fsImpl.mkdirSync(path.dirname(filePath), { recursive: true });\n },\n { retryDelaysMs: directoryRetryDelaysMs, wait },\n );\n const tempPath = path.join(\n path.dirname(filePath),\n `.${path.basename(filePath)}.${pid}.${now()}.${random().toString(36).slice(2)}.tmp`,\n );\n try {\n fsImpl.writeFileSync(\n tempPath,\n JSON.stringify(payload, null, JSON_INDENT),\n mode === undefined ? 'utf-8' : { encoding: 'utf-8', mode },\n );\n runFileSystemOperationWithRetry(\n () => {\n fsImpl.renameSync(tempPath, filePath);\n },\n { retryDelaysMs: renameRetryDelaysMs, wait },\n );\n } finally {\n // Best effort: on the happy path the rename already consumed the temp file.\n fsImpl.rmSync(tempPath, { force: true });\n }\n };\n}\n\nexport const writeAtomicJson = createAtomicJsonWriter();\nexport const writePrivateAtomicJson = createAtomicJsonWriter({ mode: PRIVATE_FILE_MODE });\n"],"mappings":"0RA+CA,SAAgB,EACd,EAAmC,EAAE,CACQ,CAC7C,IAAM,EAAS,EAAQ,IAAM,EACvB,EAAM,EAAQ,KAAO,KAAK,IAC1B,EAAM,EAAQ,KAAO,QAAQ,IAC7B,EAAS,EAAQ,QAAU,KAAK,OAChC,EAAO,EAAQ,KAEf,EAAoB,EAAQ,mBAAqB,QAAQ,WAAa,QACtE,EAAuB,EAAQ,sBAAwB,EACvD,EAAgB,EAAQ,eAAiB,EACzC,EAAsB,EAAoB,EAAgB,EAAE,CAC5D,EAAyB,EAAuB,EAAgB,EAAE,CAClE,EAAO,EAAQ,MAAQ,EAE7B,OAAQ,EAAkB,IAA0B,CAClD,MACQ,CACJ,EAAO,UAAU,EAAK,QAAQ,EAAS,CAAE,CAAE,UAAW,GAAM,CAAC,EAE/D,CAAE,cAAe,EAAwB,OAAM,CAChD,CACD,IAAM,EAAW,EAAK,KACpB,EAAK,QAAQ,EAAS,CACtB,IAAI,EAAK,SAAS,EAAS,CAAC,GAAG,EAAI,GAAG,GAAK,CAAC,GAAG,GAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,MAC/E,CACD,GAAI,CACF,EAAO,cACL,EACA,KAAK,UAAU,EAAS,KAAM,EAAY,CAC1C,IAAS,IAAA,GAAY,QAAU,CAAE,SAAU,QAAS,OAAM,CAC3D,CACD,MACQ,CACJ,EAAO,WAAW,EAAU,EAAS,EAEvC,CAAE,cAAe,EAAqB,OAAM,CAC7C,QACO,CAER,EAAO,OAAO,EAAU,CAAE,MAAO,GAAM,CAAC,4BA5DhB,CAExB,EAAoB,IACpB,EAAc,EA8DP,EAAkB,GAAwB,CAC1C,EAAyB,EAAuB,CAAE,KAAM,EAAmB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=class{keys=new Set;capacity;constructor(e){if(!Number.isInteger(e)||e<1)throw Error(`BoundedKeySet capacity must be an integer of at least 1.`);this.capacity=e}get size(){return this.keys.size}has(e){return this.keys.has(e)}add(e){if(this.keys.has(e))return!1;for(this.keys.add(e);this.keys.size>this.capacity;){let e=this.keys.values().next();if(e.done===!0)break;this.keys.delete(e.value)}return!0}delete(e){return this.keys.delete(e)}clear(){this.keys.clear()}};exports.BoundedKeySet=e;
|
|
2
|
+
//# sourceMappingURL=boundedKeySet.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundedKeySet.cjs","names":[],"sources":["../../src/shared/boundedKeySet.ts"],"sourcesContent":["/**\n * BoundedKeySet - a \"have I already seen this?\" set that cannot grow forever.\n *\n * DESIGN PATTERNS:\n * - Insertion-ordered eviction: the oldest key goes when the cap is reached\n *\n * WHY THIS EXISTS:\n * The predecessor package tracked delivered messages, emitted control events and\n * seen request files in plain Sets that were never pruned, so a long session\n * accumulated one entry per event for its whole lifetime. Dedupe only needs a\n * recent window: once an event is old enough to be evicted, the file or record\n * that would have re-triggered it is long gone.\n *\n * AVOID:\n * - Sizing the cap below the number of events that can be in flight at once,\n * which would let a duplicate slip through\n */\n\nconst MINIMUM_CAPACITY = 1;\n\nexport class BoundedKeySet<K> {\n private readonly keys = new Set<K>();\n private readonly capacity: number;\n\n constructor(capacity: number) {\n if (!Number.isInteger(capacity) || capacity < MINIMUM_CAPACITY) {\n throw new Error(`BoundedKeySet capacity must be an integer of at least ${MINIMUM_CAPACITY}.`);\n }\n this.capacity = capacity;\n }\n\n get size(): number {\n return this.keys.size;\n }\n\n has(key: K): boolean {\n return this.keys.has(key);\n }\n\n /** Record a key. Returns true when it was newly added. */\n add(key: K): boolean {\n if (this.keys.has(key)) return false;\n this.keys.add(key);\n while (this.keys.size > this.capacity) {\n const oldest = this.keys.values().next();\n if (oldest.done === true) break;\n this.keys.delete(oldest.value);\n }\n return true;\n }\n\n delete(key: K): boolean {\n return this.keys.delete(key);\n }\n\n clear(): void {\n this.keys.clear();\n }\n}\n"],"mappings":"AAoBA,IAAa,EAAb,KAA8B,CAC5B,KAAwB,IAAI,IAC5B,SAEA,YAAY,EAAkB,CAC5B,GAAI,CAAC,OAAO,UAAU,EAAS,EAAI,EAAW,EAC5C,MAAU,MAAM,2DAA6E,CAE/F,KAAK,SAAW,EAGlB,IAAI,MAAe,CACjB,OAAO,KAAK,KAAK,KAGnB,IAAI,EAAiB,CACnB,OAAO,KAAK,KAAK,IAAI,EAAI,CAI3B,IAAI,EAAiB,CACnB,GAAI,KAAK,KAAK,IAAI,EAAI,CAAE,MAAO,GAE/B,IADA,KAAK,KAAK,IAAI,EAAI,CACX,KAAK,KAAK,KAAO,KAAK,UAAU,CACrC,IAAM,EAAS,KAAK,KAAK,QAAQ,CAAC,MAAM,CACxC,GAAI,EAAO,OAAS,GAAM,MAC1B,KAAK,KAAK,OAAO,EAAO,MAAM,CAEhC,MAAO,GAGT,OAAO,EAAiB,CACtB,OAAO,KAAK,KAAK,OAAO,EAAI,CAG9B,OAAc,CACZ,KAAK,KAAK,OAAO"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";var t,n,r=e((()=>{t=1,n=class{keys=new Set;capacity;constructor(e){if(!Number.isInteger(e)||e<t)throw Error(`BoundedKeySet capacity must be an integer of at least ${t}.`);this.capacity=e}get size(){return this.keys.size}has(e){return this.keys.has(e)}add(e){if(this.keys.has(e))return!1;for(this.keys.add(e);this.keys.size>this.capacity;){let e=this.keys.values().next();if(e.done===!0)break;this.keys.delete(e.value)}return!0}delete(e){return this.keys.delete(e)}clear(){this.keys.clear()}}}));r();export{n as BoundedKeySet,r as init_boundedKeySet};
|
|
2
|
+
//# sourceMappingURL=boundedKeySet.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundedKeySet.mjs","names":[],"sources":["../../src/shared/boundedKeySet.ts"],"sourcesContent":["/**\n * BoundedKeySet - a \"have I already seen this?\" set that cannot grow forever.\n *\n * DESIGN PATTERNS:\n * - Insertion-ordered eviction: the oldest key goes when the cap is reached\n *\n * WHY THIS EXISTS:\n * The predecessor package tracked delivered messages, emitted control events and\n * seen request files in plain Sets that were never pruned, so a long session\n * accumulated one entry per event for its whole lifetime. Dedupe only needs a\n * recent window: once an event is old enough to be evicted, the file or record\n * that would have re-triggered it is long gone.\n *\n * AVOID:\n * - Sizing the cap below the number of events that can be in flight at once,\n * which would let a duplicate slip through\n */\n\nconst MINIMUM_CAPACITY = 1;\n\nexport class BoundedKeySet<K> {\n private readonly keys = new Set<K>();\n private readonly capacity: number;\n\n constructor(capacity: number) {\n if (!Number.isInteger(capacity) || capacity < MINIMUM_CAPACITY) {\n throw new Error(`BoundedKeySet capacity must be an integer of at least ${MINIMUM_CAPACITY}.`);\n }\n this.capacity = capacity;\n }\n\n get size(): number {\n return this.keys.size;\n }\n\n has(key: K): boolean {\n return this.keys.has(key);\n }\n\n /** Record a key. Returns true when it was newly added. */\n add(key: K): boolean {\n if (this.keys.has(key)) return false;\n this.keys.add(key);\n while (this.keys.size > this.capacity) {\n const oldest = this.keys.values().next();\n if (oldest.done === true) break;\n this.keys.delete(oldest.value);\n }\n return true;\n }\n\n delete(key: K): boolean {\n return this.keys.delete(key);\n }\n\n clear(): void {\n this.keys.clear();\n }\n}\n"],"mappings":"+EAkBM,EAAmB,EAEZ,EAAb,KAA8B,CAC5B,KAAwB,IAAI,IAC5B,SAEA,YAAY,EAAkB,CAC5B,GAAI,CAAC,OAAO,UAAU,EAAS,EAAI,EAAW,EAC5C,MAAU,MAAM,yDAAyD,EAAiB,GAAG,CAE/F,KAAK,SAAW,EAGlB,IAAI,MAAe,CACjB,OAAO,KAAK,KAAK,KAGnB,IAAI,EAAiB,CACnB,OAAO,KAAK,KAAK,IAAI,EAAI,CAI3B,IAAI,EAAiB,CACnB,GAAI,KAAK,KAAK,IAAI,EAAI,CAAE,MAAO,GAE/B,IADA,KAAK,KAAK,IAAI,EAAI,CACX,KAAK,KAAK,KAAO,KAAK,UAAU,CACrC,IAAM,EAAS,KAAK,KAAK,QAAQ,CAAC,MAAM,CACxC,GAAI,EAAO,OAAS,GAAM,MAC1B,KAAK,KAAK,OAAO,EAAO,MAAM,CAEhC,MAAO,GAGT,OAAO,EAAiB,CACtB,OAAO,KAAK,KAAK,OAAO,EAAI,CAG9B,OAAc,CACZ,KAAK,KAAK,OAAO"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`);let t=require(`node:fs`);t=e.__toESM(t,1);let n=require(`node:path`);n=e.__toESM(n,1);const r=1024;1024*r,32*r;const i=`
|
|
2
|
+
|
|
3
|
+
… payload truncated`,a=[`command`,`path`,`file_path`,`pattern`,`query`,`url`,`task`,`describe`,`search`];function o(e){return e instanceof Error?e.message:String(e)}function s(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function c(e,t=32768){let n;if(typeof e==`string`)n=e;else try{let t=JSON.stringify(e,null,2);if(t===void 0)return;n=t}catch{return}if(!n.trim())return;let r=Buffer.from(n,`utf-8`);if(r.length<=t)return{text:n,truncated:!1};let a=Buffer.byteLength(i,`utf-8`),o=Math.max(0,t-a);for(;o>0&&((r[o]??0)&192)==128;)o--;return{text:`${r.subarray(0,o).toString(`utf-8`)}${i}`,truncated:!0}}function l(e){if(!e||typeof e!=`object`)return;let t=e,n=t.cost,r=n&&typeof n==`object`?s(n.total)??0:s(n)??0;return{input:s(t.input)??s(t.inputTokens)??0,output:s(t.output)??s(t.outputTokens)??0,cacheRead:s(t.cacheRead)??0,cacheWrite:s(t.cacheWrite)??0,cost:r}}function u(e){let t=l(e.usage);if(t)return t.input+t.output+t.cacheRead+t.cacheWrite}function d(e,t){return e.length>t?`${e.slice(0,t-3)}...`:e}function f(e){if(typeof e==`string`&&e.trim().length>0)return e;if(typeof e==`number`||typeof e==`boolean`)return String(e)}function p(e){if(!Array.isArray(e)||e.length===0)return;let t=f(e[0]);if(t)return`${t}${e.length>1?` (+${e.length-1} more)`:``}`}function m(e){if(typeof e.tool==`string`&&e.tool){let t=typeof e.server==`string`&&e.server?`${e.server}/`:``,n=typeof e.args==`string`&&e.args?` ${e.args.slice(0,40)}`:``;return`${t}${e.tool}${n}`}let t=p(e.queries);if(t)return d(t,60);if(typeof e.query==`string`&&e.query.trim().length>0)return d(e.query,60);if(typeof e.workflow==`string`&&e.workflow.trim().length>0)return`workflow=${d(e.workflow,48)}`;if(typeof e.url==`string`&&e.url.trim().length>0)return d(e.url,60);let n=p(e.urls);if(n)return d(n,60);if(typeof e.prompt==`string`&&e.prompt.trim().length>0)return d(e.prompt,60);for(let t of a){let n=e[t];if(typeof n==`string`&&n.length>0)return d(n,60)}for(let[t,n]of Object.entries(e)){let e=p(n);if(e)return`${t}=${d(e,50)}`;if(typeof n==`string`&&n.length>0)return`${t}=${d(n,50)}`}return``}function h(e){if(!e)return``;if(typeof e==`string`)return e;if(!Array.isArray(e))return``;let t=[];for(let n of e){if(!n||typeof n!=`object`)continue;let e=n;if(e.type===`text`&&`text`in e)t.push(String(e.text));else if(e.type===`tool_result`&&`content`in e){let n=h(e.content);n&&t.push(n)}else `text`in e&&t.push(String(e.text))}return t.join(`
|
|
4
|
+
`)}function g(e){return e.args&&typeof e.args==`object`&&!Array.isArray(e.args)?e.args:{}}function _(e){let r=0,i,a=!1,s=e.maxBytes??52428800,u=t=>{let n=Date.now();return{version:1,recordType:t,source:e.source,runId:e.runId,agent:e.agent,...e.childIndex===void 0?{}:{childIndex:e.childIndex},cwd:e.cwd,ts:n,timestamp:new Date(n).toISOString()}},d=()=>`${JSON.stringify({...u(`truncated`),maxBytes:s,message:`Child transcript exceeded ${s} bytes; further records were omitted.`})}\n`,f=Buffer.byteLength(d(),`utf-8`),p=()=>{a=!0;let n=d(),c=Buffer.byteLength(n,`utf-8`);if(!(r+c>s))try{t.appendFileSync(e.transcriptPath,n,`utf-8`),r+=c}catch(t){i=`Failed to write child transcript '${e.transcriptPath}': ${o(t)}`}},_=n=>{if(i||a)return;let c=`${JSON.stringify(n)}\n`,l=Buffer.byteLength(c,`utf-8`);if(r+l+f>s){p();return}try{t.appendFileSync(e.transcriptPath,c,`utf-8`),r+=l}catch(t){i=`Failed to write child transcript '${e.transcriptPath}': ${o(t)}`}};try{t.mkdirSync(n.dirname(e.transcriptPath),{recursive:!0}),t.writeFileSync(e.transcriptPath,``,`utf-8`)}catch(t){i=`Failed to initialize child transcript '${e.transcriptPath}': ${o(t)}`}let v=(e,t)=>{let n=h(t.content),r=l(t.usage);if(t.role===`toolResult`){let r=c(n);_({...u(`message`),sourceEventType:e,role:t.role,toolCallId:t.toolCallId,toolName:t.toolName,isError:t.isError,...r?{text:r.text,outputTruncated:r.truncated}:{},message:{role:t.role,toolCallId:t.toolCallId,toolName:t.toolName,isError:t.isError,content:r?[{type:`text`,text:r.text}]:[],...t.timestamp===void 0?{}:{timestamp:t.timestamp}}});return}_({...u(`message`),sourceEventType:e,role:t.role,...n?{text:n}:{},...t.model?{model:t.model}:{},...t.stopReason?{stopReason:t.stopReason}:{},...t.errorMessage?{errorMessage:t.errorMessage}:{},...r?{usage:r}:{},message:t})},y=e=>{e.trim()&&_({...u(`stderr`),text:e})};return{path:e.transcriptPath,writeInitialUserMessage(e){_({...u(`message`),sourceEventType:`initial_prompt`,role:`user`,text:e,message:{role:`user`,content:[{type:`text`,text:e}]}})},writeChildEvent(e){if((e.type===`message_end`||e.type===`tool_result_end`)&&e.message){v(e.type,e.message);return}if(e.type===`tool_execution_start`&&e.toolName){let t=g(e),n=c(t);_({...u(`tool_start`),sourceEventType:e.type,...e.toolCallId?{toolCallId:e.toolCallId}:{},toolName:e.toolName,...Object.keys(t).length>0?{argsPreview:m(t)}:{},...n?{argsPayload:n.text}:{}});return}e.type===`tool_execution_end`&&_({...u(`tool_end`),sourceEventType:e.type,...e.toolCallId?{toolCallId:e.toolCallId}:{},...e.toolName?{toolName:e.toolName}:{},...typeof e.isError==`boolean`?{isError:e.isError}:{}})},writeStdoutLine(e){e.trim()&&_({...u(`stdout`),text:e})},writeStderrLine:y,writeStderrText(e){for(let t of e.split(/\r?\n/))y(t)},getError(){return i}}}exports.createChildTranscriptWriter=_,exports.getMessageUsageTokens=u;
|
|
5
|
+
//# sourceMappingURL=childTranscript.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"childTranscript.cjs","names":["path"],"sources":["../../src/shared/childTranscript.ts"],"sourcesContent":["/**\n * Append-only JSONL transcript of one child run, written by the parent.\n *\n * WHY THE PARENT WRITES ITS OWN:\n * The child's session transcript lives in the host agent's config directory and\n * is keyed by a session id the parent does not learn until the run ends. This\n * file is the parent's independent record: it exists from the first prompt, it\n * survives a child that never started, and it carries the stdout and stderr the\n * child's own transcript has no place for.\n *\n * DESIGN PATTERNS:\n * - Two independent byte budgets. Each tool payload is capped so one huge result\n * cannot consume the file, and the file itself is capped so a runaway child\n * cannot fill the disk\n * - The file budget reserves room for the truncation marker up front, so the\n * record that trips the limit is dropped in favour of a record explaining why\n * - Write failures latch into one reported error instead of throwing. This file\n * is evidence about a run, so losing it must never end the run it describes\n * - Truncation is carried as a flag beside the text. The predecessor rediscovered\n * it by searching the output for the marker substring, which any payload\n * quoting that phrase would have tripped\n *\n * PERFORMANCE:\n * The marker's byte size is measured once. Every field feeding it is fixed for\n * the writer's lifetime and both timestamps are fixed width, so the predecessor's\n * re-serialisation of the marker on every single record bought nothing.\n *\n * AVOID:\n * - Throwing from any writer method\n * - Cutting a payload mid-character; the cut walks back off UTF-8 continuation\n * bytes before slicing\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport const CHILD_TRANSCRIPT_ARTIFACT_VERSION = 1;\n\nconst BYTES_PER_KIB = 1024;\nconst BYTES_PER_MIB = 1024 * BYTES_PER_KIB;\nconst MAX_TOOL_PAYLOAD_BYTES = 32 * BYTES_PER_KIB;\nconst DEFAULT_MAX_CHILD_TRANSCRIPT_BYTES = 50 * BYTES_PER_MIB;\nconst TOOL_PAYLOAD_TRUNCATION_MARKER = '\\n\\n… payload truncated';\nconst JSON_INDENT = 2;\n\n/** High bits identifying a UTF-8 continuation byte (10xxxxxx). */\nconst UTF8_CONTINUATION_MASK = 0xc0;\nconst UTF8_CONTINUATION_BITS = 0x80;\n\nconst PREVIEW_ELLIPSIS = '...';\nconst PREVIEW_MAX_LENGTH = 60;\nconst FALLBACK_PREVIEW_MAX_LENGTH = 50;\nconst WORKFLOW_PREVIEW_MAX_LENGTH = 48;\nconst MCP_ARGS_PREVIEW_MAX_LENGTH = 40;\n\n/** Argument keys worth previewing verbatim, in the order they are preferred. */\nconst PREVIEW_KEYS = ['command', 'path', 'file_path', 'pattern', 'query', 'url', 'task', 'describe', 'search'] as const;\n\n/**\n * Which execution path produced the records in a file.\n *\n * Only background runs exist, so this is single-valued today. It is still\n * written into every record because the file is read by tools that must be able\n * to tell what produced it without being told out of band, and because a second\n * variant is cheaper to add than to retrofit onto files already on disk.\n */\nexport type ChildTranscriptSource = 'async';\n\nexport type ChildTranscriptRecordType = 'message' | 'tool_start' | 'tool_end' | 'stdout' | 'stderr' | 'truncated';\n\n/**\n * The message fields this transcript records.\n *\n * Declared structurally rather than imported from the host agent's message type:\n * the writer only reads these fields, and a shared leaf should not take on an\n * optional peer dependency to describe them.\n */\nexport interface ChildTranscriptMessage {\n role: string;\n content?: unknown;\n toolCallId?: string;\n toolName?: string;\n isError?: boolean;\n timestamp?: unknown;\n model?: string;\n errorMessage?: string;\n stopReason?: string;\n usage?: unknown;\n}\n\nexport interface ChildTranscriptEvent {\n type?: string;\n message?: ChildTranscriptMessage;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n isError?: boolean;\n}\n\nexport interface ChildTranscriptWriterInput {\n transcriptPath: string;\n source: ChildTranscriptSource;\n runId: string;\n agent: string;\n childIndex?: number;\n cwd: string;\n maxBytes?: number;\n}\n\nexport interface ChildTranscriptWriter {\n path: string;\n writeInitialUserMessage(prompt: string): void;\n writeChildEvent(event: ChildTranscriptEvent): void;\n writeStdoutLine(line: string): void;\n writeStderrLine(line: string): void;\n writeStderrText(text: string): void;\n getError(): string | undefined;\n}\n\ninterface BoundedPayload {\n text: string;\n truncated: boolean;\n}\n\ninterface NormalizedUsage {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n cost: number;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction finiteNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n/**\n * Render `value` as text no larger than `maxBytes`, or undefined if it carries\n * nothing worth recording.\n *\n * A value that cannot be serialised (a cycle, a BigInt) is dropped rather than\n * reported, because a tool payload is context and not part of the run's result.\n */\nfunction boundedPayload(value: unknown, maxBytes = MAX_TOOL_PAYLOAD_BYTES): BoundedPayload | undefined {\n let text: string;\n if (typeof value === 'string') {\n text = value;\n } else {\n try {\n const serialized = JSON.stringify(value, null, JSON_INDENT);\n if (serialized === undefined) return undefined;\n text = serialized;\n } catch {\n // Cyclic or non-serialisable payloads have no textual form to record.\n return undefined;\n }\n }\n if (!text.trim()) return undefined;\n\n const payload = Buffer.from(text, 'utf-8');\n if (payload.length <= maxBytes) return { text, truncated: false };\n\n const markerBytes = Buffer.byteLength(TOOL_PAYLOAD_TRUNCATION_MARKER, 'utf-8');\n let end = Math.max(0, maxBytes - markerBytes);\n // Back off any continuation bytes so the cut lands on a character boundary.\n while (end > 0 && ((payload[end] ?? 0) & UTF8_CONTINUATION_MASK) === UTF8_CONTINUATION_BITS) end--;\n return {\n text: `${payload.subarray(0, end).toString('utf-8')}${TOOL_PAYLOAD_TRUNCATION_MARKER}`,\n truncated: true,\n };\n}\n\n/**\n * Read a child's usage counters, tolerating both field namings the host has used.\n *\n * Cost arrives either as a number or as an object with a `total`, so both are\n * accepted; anything else counts as zero rather than as missing, because a run\n * with unreadable cost still has a real elapsed usage record.\n */\nfunction normalizeUsage(value: unknown): NormalizedUsage | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const raw = value as Record<string, unknown>;\n const rawCost = raw.cost;\n const cost =\n rawCost && typeof rawCost === 'object'\n ? (finiteNumber((rawCost as { total?: unknown }).total) ?? 0)\n : (finiteNumber(rawCost) ?? 0);\n return {\n input: finiteNumber(raw.input) ?? finiteNumber(raw.inputTokens) ?? 0,\n output: finiteNumber(raw.output) ?? finiteNumber(raw.outputTokens) ?? 0,\n cacheRead: finiteNumber(raw.cacheRead) ?? 0,\n cacheWrite: finiteNumber(raw.cacheWrite) ?? 0,\n cost,\n };\n}\n\n/**\n * Return the token total observed on one finalized child message.\n *\n * Missing usage means that this message carried no observation and therefore\n * returns undefined. An actual usage object with empty or zero buckets returns\n * zero, which is a meaningful observation for live progress.\n */\nexport function getMessageUsageTokens(message: ChildTranscriptMessage): number | undefined {\n const usage = normalizeUsage(message.usage);\n if (!usage) return undefined;\n return usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n}\n\nfunction truncatePreview(value: string, maxLength: number): string {\n return value.length > maxLength ? `${value.slice(0, maxLength - PREVIEW_ELLIPSIS.length)}${PREVIEW_ELLIPSIS}` : value;\n}\n\nfunction stringifyPreviewValue(value: unknown): string | undefined {\n if (typeof value === 'string' && value.trim().length > 0) return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n return undefined;\n}\n\nfunction previewArray(value: unknown): string | undefined {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n const first = stringifyPreviewValue(value[0]);\n if (!first) return undefined;\n const suffix = value.length > 1 ? ` (+${value.length - 1} more)` : '';\n return `${first}${suffix}`;\n}\n\n/**\n * One short line describing what a tool call was asked to do.\n *\n * The ladder is ordered by how much the key tells a reader, not alphabetically:\n * an MCP call is named by server and tool, a search by its query, and everything\n * else falls back to the first stringable argument so the line is never empty\n * when the call had any arguments at all.\n */\nfunction extractToolArgsPreview(args: Record<string, unknown>): string {\n if (typeof args.tool === 'string' && args.tool) {\n const server = typeof args.server === 'string' && args.server ? `${args.server}/` : '';\n const toolArgs =\n typeof args.args === 'string' && args.args ? ` ${args.args.slice(0, MCP_ARGS_PREVIEW_MAX_LENGTH)}` : '';\n return `${server}${args.tool}${toolArgs}`;\n }\n\n const queriesPreview = previewArray(args.queries);\n if (queriesPreview) return truncatePreview(queriesPreview, PREVIEW_MAX_LENGTH);\n if (typeof args.query === 'string' && args.query.trim().length > 0) {\n return truncatePreview(args.query, PREVIEW_MAX_LENGTH);\n }\n if (typeof args.workflow === 'string' && args.workflow.trim().length > 0) {\n return `workflow=${truncatePreview(args.workflow, WORKFLOW_PREVIEW_MAX_LENGTH)}`;\n }\n if (typeof args.url === 'string' && args.url.trim().length > 0) return truncatePreview(args.url, PREVIEW_MAX_LENGTH);\n const urlsPreview = previewArray(args.urls);\n if (urlsPreview) return truncatePreview(urlsPreview, PREVIEW_MAX_LENGTH);\n if (typeof args.prompt === 'string' && args.prompt.trim().length > 0) {\n return truncatePreview(args.prompt, PREVIEW_MAX_LENGTH);\n }\n\n for (const key of PREVIEW_KEYS) {\n const value = args[key];\n if (typeof value === 'string' && value.length > 0) return truncatePreview(value, PREVIEW_MAX_LENGTH);\n }\n\n for (const [key, value] of Object.entries(args)) {\n const arrayPreview = previewArray(value);\n if (arrayPreview) return `${key}=${truncatePreview(arrayPreview, FALLBACK_PREVIEW_MAX_LENGTH)}`;\n if (typeof value === 'string' && value.length > 0) {\n return `${key}=${truncatePreview(value, FALLBACK_PREVIEW_MAX_LENGTH)}`;\n }\n }\n return '';\n}\n\n/**\n * Flatten a message's content into plain text.\n *\n * Content arrives as unknown JSON in four shapes the host has used over time, so\n * every hop is checked. Anything unrecognised contributes nothing rather than\n * being stringified, which would put `[object Object]` into the transcript.\n */\nfunction extractTextFromContent(content: unknown): string {\n if (!content) return '';\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n\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 === 'text' && 'text' in record) {\n texts.push(String(record.text));\n } else if (record.type === 'tool_result' && 'content' in record) {\n const inner = extractTextFromContent(record.content);\n if (inner) texts.push(inner);\n } else if ('text' in record) {\n texts.push(String(record.text));\n }\n }\n return texts.join('\\n');\n}\n\nfunction eventArgs(event: ChildTranscriptEvent): Record<string, unknown> {\n return event.args && typeof event.args === 'object' && !Array.isArray(event.args)\n ? (event.args as Record<string, unknown>)\n : {};\n}\n\nexport function createChildTranscriptWriter(input: ChildTranscriptWriterInput): ChildTranscriptWriter {\n let bytesWritten = 0;\n let writeError: string | undefined;\n let truncated = false;\n const maxBytes = input.maxBytes ?? DEFAULT_MAX_CHILD_TRANSCRIPT_BYTES;\n\n const baseRecord = (recordType: ChildTranscriptRecordType) => {\n const ts = Date.now();\n return {\n version: CHILD_TRANSCRIPT_ARTIFACT_VERSION,\n recordType,\n source: input.source,\n runId: input.runId,\n agent: input.agent,\n ...(input.childIndex !== undefined ? { childIndex: input.childIndex } : {}),\n cwd: input.cwd,\n ts,\n timestamp: new Date(ts).toISOString(),\n };\n };\n\n const truncationMarkerLine = (): string =>\n `${JSON.stringify({\n ...baseRecord('truncated'),\n maxBytes,\n message: `Child transcript exceeded ${maxBytes} bytes; further records were omitted.`,\n })}\\n`;\n\n // Fixed for this writer: every field is constant except the two timestamps,\n // which are fixed width. Measuring once is why writeRecord stays allocation-free.\n const reservedMarkerBytes = Buffer.byteLength(truncationMarkerLine(), 'utf-8');\n\n const writeTruncatedMarker = () => {\n truncated = true;\n const marker = truncationMarkerLine();\n const markerBytes = Buffer.byteLength(marker, 'utf-8');\n if (bytesWritten + markerBytes > maxBytes) return;\n try {\n fs.appendFileSync(input.transcriptPath, marker, 'utf-8');\n bytesWritten += markerBytes;\n } catch (error) {\n writeError = `Failed to write child transcript '${input.transcriptPath}': ${errorMessage(error)}`;\n }\n };\n\n const writeRecord = (record: Record<string, unknown>) => {\n if (writeError || truncated) return;\n const line = `${JSON.stringify(record)}\\n`;\n const bytes = Buffer.byteLength(line, 'utf-8');\n // The marker's room is reserved, so the file can always state why it ended.\n if (bytesWritten + bytes + reservedMarkerBytes > maxBytes) {\n writeTruncatedMarker();\n return;\n }\n try {\n fs.appendFileSync(input.transcriptPath, line, 'utf-8');\n bytesWritten += bytes;\n } catch (error) {\n writeError = `Failed to write child transcript '${input.transcriptPath}': ${errorMessage(error)}`;\n }\n };\n\n try {\n fs.mkdirSync(path.dirname(input.transcriptPath), { recursive: true });\n fs.writeFileSync(input.transcriptPath, '', 'utf-8');\n } catch (error) {\n writeError = `Failed to initialize child transcript '${input.transcriptPath}': ${errorMessage(error)}`;\n }\n\n const writeMessage = (sourceEventType: string, message: ChildTranscriptMessage) => {\n const text = extractTextFromContent(message.content);\n const usage = normalizeUsage(message.usage);\n if (message.role === 'toolResult') {\n const output = boundedPayload(text);\n writeRecord({\n ...baseRecord('message'),\n sourceEventType,\n role: message.role,\n toolCallId: message.toolCallId,\n toolName: message.toolName,\n isError: message.isError,\n ...(output ? { text: output.text, outputTruncated: output.truncated } : {}),\n message: {\n role: message.role,\n toolCallId: message.toolCallId,\n toolName: message.toolName,\n isError: message.isError,\n content: output ? [{ type: 'text', text: output.text }] : [],\n ...(message.timestamp !== undefined ? { timestamp: message.timestamp } : {}),\n },\n });\n return;\n }\n writeRecord({\n ...baseRecord('message'),\n sourceEventType,\n role: message.role,\n ...(text ? { text } : {}),\n ...(message.model ? { model: message.model } : {}),\n ...(message.stopReason ? { stopReason: message.stopReason } : {}),\n ...(message.errorMessage ? { errorMessage: message.errorMessage } : {}),\n ...(usage ? { usage } : {}),\n message,\n });\n };\n\n const writeStderrLine = (line: string) => {\n if (!line.trim()) return;\n writeRecord({ ...baseRecord('stderr'), text: line });\n };\n\n return {\n path: input.transcriptPath,\n writeInitialUserMessage(prompt: string) {\n writeRecord({\n ...baseRecord('message'),\n sourceEventType: 'initial_prompt',\n role: 'user',\n text: prompt,\n message: { role: 'user', content: [{ type: 'text', text: prompt }] },\n });\n },\n writeChildEvent(event: ChildTranscriptEvent) {\n if ((event.type === 'message_end' || event.type === 'tool_result_end') && event.message) {\n writeMessage(event.type, event.message);\n return;\n }\n if (event.type === 'tool_execution_start' && event.toolName) {\n const args = eventArgs(event);\n const argsPayload = boundedPayload(args);\n writeRecord({\n ...baseRecord('tool_start'),\n sourceEventType: event.type,\n ...(event.toolCallId ? { toolCallId: event.toolCallId } : {}),\n toolName: event.toolName,\n ...(Object.keys(args).length > 0 ? { argsPreview: extractToolArgsPreview(args) } : {}),\n ...(argsPayload ? { argsPayload: argsPayload.text } : {}),\n });\n return;\n }\n if (event.type === 'tool_execution_end') {\n writeRecord({\n ...baseRecord('tool_end'),\n sourceEventType: event.type,\n ...(event.toolCallId ? { toolCallId: event.toolCallId } : {}),\n ...(event.toolName ? { toolName: event.toolName } : {}),\n ...(typeof event.isError === 'boolean' ? { isError: event.isError } : {}),\n });\n }\n },\n writeStdoutLine(line: string) {\n if (!line.trim()) return;\n writeRecord({ ...baseRecord('stdout'), text: line });\n },\n writeStderrLine,\n writeStderrText(text: string) {\n for (const line of text.split(/\\r?\\n/)) writeStderrLine(line);\n },\n getError() {\n return writeError;\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import*as t from"node:fs";import*as n from"node:path";function r(e){return e instanceof Error?e.message:String(e)}function i(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function a(e,t=_){let n;if(typeof e==`string`)n=e;else try{let t=JSON.stringify(e,null,b);if(t===void 0)return;n=t}catch{return}if(!n.trim())return;let r=Buffer.from(n,`utf-8`);if(r.length<=t)return{text:n,truncated:!1};let i=Buffer.byteLength(y,`utf-8`),a=Math.max(0,t-i);for(;a>0&&((r[a]??0)&x)===S;)a--;return{text:`${r.subarray(0,a).toString(`utf-8`)}${y}`,truncated:!0}}function o(e){if(!e||typeof e!=`object`)return;let t=e,n=t.cost,r=n&&typeof n==`object`?i(n.total)??0:i(n)??0;return{input:i(t.input)??i(t.inputTokens)??0,output:i(t.output)??i(t.outputTokens)??0,cacheRead:i(t.cacheRead)??0,cacheWrite:i(t.cacheWrite)??0,cost:r}}function s(e){let t=o(e.usage);if(t)return t.input+t.output+t.cacheRead+t.cacheWrite}function c(e,t){return e.length>t?`${e.slice(0,t-3)}${C}`:e}function l(e){if(typeof e==`string`&&e.trim().length>0)return e;if(typeof e==`number`||typeof e==`boolean`)return String(e)}function u(e){if(!Array.isArray(e)||e.length===0)return;let t=l(e[0]);if(t)return`${t}${e.length>1?` (+${e.length-1} more)`:``}`}function d(e){if(typeof e.tool==`string`&&e.tool){let t=typeof e.server==`string`&&e.server?`${e.server}/`:``,n=typeof e.args==`string`&&e.args?` ${e.args.slice(0,D)}`:``;return`${t}${e.tool}${n}`}let t=u(e.queries);if(t)return c(t,w);if(typeof e.query==`string`&&e.query.trim().length>0)return c(e.query,w);if(typeof e.workflow==`string`&&e.workflow.trim().length>0)return`workflow=${c(e.workflow,E)}`;if(typeof e.url==`string`&&e.url.trim().length>0)return c(e.url,w);let n=u(e.urls);if(n)return c(n,w);if(typeof e.prompt==`string`&&e.prompt.trim().length>0)return c(e.prompt,w);for(let t of O){let n=e[t];if(typeof n==`string`&&n.length>0)return c(n,w)}for(let[t,n]of Object.entries(e)){let e=u(n);if(e)return`${t}=${c(e,T)}`;if(typeof n==`string`&&n.length>0)return`${t}=${c(n,T)}`}return``}function f(e){if(!e)return``;if(typeof e==`string`)return e;if(!Array.isArray(e))return``;let t=[];for(let n of e){if(!n||typeof n!=`object`)continue;let e=n;if(e.type===`text`&&`text`in e)t.push(String(e.text));else if(e.type===`tool_result`&&`content`in e){let n=f(e.content);n&&t.push(n)}else `text`in e&&t.push(String(e.text))}return t.join(`
|
|
2
|
+
`)}function p(e){return e.args&&typeof e.args==`object`&&!Array.isArray(e.args)?e.args:{}}function m(e){let i=0,s,c=!1,l=e.maxBytes??v,u=t=>{let n=Date.now();return{version:1,recordType:t,source:e.source,runId:e.runId,agent:e.agent,...e.childIndex===void 0?{}:{childIndex:e.childIndex},cwd:e.cwd,ts:n,timestamp:new Date(n).toISOString()}},m=()=>`${JSON.stringify({...u(`truncated`),maxBytes:l,message:`Child transcript exceeded ${l} bytes; further records were omitted.`})}\n`,h=Buffer.byteLength(m(),`utf-8`),g=()=>{c=!0;let n=m(),a=Buffer.byteLength(n,`utf-8`);if(!(i+a>l))try{t.appendFileSync(e.transcriptPath,n,`utf-8`),i+=a}catch(t){s=`Failed to write child transcript '${e.transcriptPath}': ${r(t)}`}},_=n=>{if(s||c)return;let a=`${JSON.stringify(n)}\n`,o=Buffer.byteLength(a,`utf-8`);if(i+o+h>l){g();return}try{t.appendFileSync(e.transcriptPath,a,`utf-8`),i+=o}catch(t){s=`Failed to write child transcript '${e.transcriptPath}': ${r(t)}`}};try{t.mkdirSync(n.dirname(e.transcriptPath),{recursive:!0}),t.writeFileSync(e.transcriptPath,``,`utf-8`)}catch(t){s=`Failed to initialize child transcript '${e.transcriptPath}': ${r(t)}`}let y=(e,t)=>{let n=f(t.content),r=o(t.usage);if(t.role===`toolResult`){let r=a(n);_({...u(`message`),sourceEventType:e,role:t.role,toolCallId:t.toolCallId,toolName:t.toolName,isError:t.isError,...r?{text:r.text,outputTruncated:r.truncated}:{},message:{role:t.role,toolCallId:t.toolCallId,toolName:t.toolName,isError:t.isError,content:r?[{type:`text`,text:r.text}]:[],...t.timestamp===void 0?{}:{timestamp:t.timestamp}}});return}_({...u(`message`),sourceEventType:e,role:t.role,...n?{text:n}:{},...t.model?{model:t.model}:{},...t.stopReason?{stopReason:t.stopReason}:{},...t.errorMessage?{errorMessage:t.errorMessage}:{},...r?{usage:r}:{},message:t})},b=e=>{e.trim()&&_({...u(`stderr`),text:e})};return{path:e.transcriptPath,writeInitialUserMessage(e){_({...u(`message`),sourceEventType:`initial_prompt`,role:`user`,text:e,message:{role:`user`,content:[{type:`text`,text:e}]}})},writeChildEvent(e){if((e.type===`message_end`||e.type===`tool_result_end`)&&e.message){y(e.type,e.message);return}if(e.type===`tool_execution_start`&&e.toolName){let t=p(e),n=a(t);_({...u(`tool_start`),sourceEventType:e.type,...e.toolCallId?{toolCallId:e.toolCallId}:{},toolName:e.toolName,...Object.keys(t).length>0?{argsPreview:d(t)}:{},...n?{argsPayload:n.text}:{}});return}e.type===`tool_execution_end`&&_({...u(`tool_end`),sourceEventType:e.type,...e.toolCallId?{toolCallId:e.toolCallId}:{},...e.toolName?{toolName:e.toolName}:{},...typeof e.isError==`boolean`?{isError:e.isError}:{}})},writeStdoutLine(e){e.trim()&&_({...u(`stdout`),text:e})},writeStderrLine:b,writeStderrText(e){for(let t of e.split(/\r?\n/))b(t)},getError(){return s}}}var h,g,_,v,y,b,x,S,C,w,T,E,D,O,k=e((()=>{h=1024,g=1024*h,_=32*h,v=50*g,y=`
|
|
3
|
+
|
|
4
|
+
… payload truncated`,b=2,x=192,S=128,C=`...`,w=60,T=50,E=48,D=40,O=[`command`,`path`,`file_path`,`pattern`,`query`,`url`,`task`,`describe`,`search`]}));k();export{m as createChildTranscriptWriter,s as getMessageUsageTokens,k as init_childTranscript};
|
|
5
|
+
//# sourceMappingURL=childTranscript.mjs.map
|