@nklisch/pi-enhanced 0.1.2 → 0.1.4
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/CHANGELOG.md +7 -0
- package/README.md +3 -2
- package/node_modules/@nklisch/pi-clearance/README.md +10 -5
- package/node_modules/@nklisch/pi-clearance/docs/ARCHITECTURE.md +12 -7
- package/node_modules/@nklisch/pi-clearance/docs/CONFIGURATION.md +25 -35
- package/node_modules/@nklisch/pi-clearance/docs/DEVELOPER_GUIDE.md +30 -30
- package/node_modules/@nklisch/pi-clearance/docs/PACK_AUTHORING.md +5 -4
- package/node_modules/@nklisch/pi-clearance/docs/PRINCIPLES.md +14 -10
- package/node_modules/@nklisch/pi-clearance/docs/REFERENCE_PATTERNS.md +1 -1
- package/node_modules/@nklisch/pi-clearance/docs/REVIEWER_PROMPTS.md +12 -8
- package/node_modules/@nklisch/pi-clearance/docs/RULE_PACKS.md +1 -1
- package/node_modules/@nklisch/pi-clearance/docs/SPEC.md +7 -6
- package/node_modules/@nklisch/pi-clearance/docs/TUNE.md +2 -2
- package/node_modules/@nklisch/pi-clearance/docs/USER_GUIDE.md +7 -3
- package/node_modules/@nklisch/pi-clearance/docs/VISION.md +3 -3
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-arm64.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-x64.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-arm64-gnu.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-x64-gnu.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-arm64-msvc.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
- package/node_modules/@nklisch/pi-clearance/package.json +9 -5
- package/node_modules/@nklisch/pi-clearance/src/config/config-command-plans.ts +79 -3
- package/node_modules/@nklisch/pi-clearance/src/config/config-command-writer.ts +7 -1
- package/node_modules/@nklisch/pi-clearance/src/config/defaults.ts +42 -0
- package/node_modules/@nklisch/pi-clearance/src/config/gated-tools.ts +7 -0
- package/node_modules/@nklisch/pi-clearance/src/config/loader.ts +7 -1
- package/node_modules/@nklisch/pi-clearance/src/config/pack-enablement-writer.ts +1 -0
- package/node_modules/@nklisch/pi-clearance/src/config/paths.ts +14 -3
- package/node_modules/@nklisch/pi-clearance/src/config/persistence.ts +135 -0
- package/node_modules/@nklisch/pi-clearance/src/config/schema.ts +74 -26
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCase.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseCategory.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseExpectation.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseResult.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseResultOutcome.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseSource.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialValidationReport.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialValidationStatus.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashBlock.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashCommandShape.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashConditionalArm.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashControlConstruct.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashFlag.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashForLoopKeywordSpans.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashIteratorEntry.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashIteratorEntryKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashListOperator.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashLoopVariableReference.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFact.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactContext.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactProvenance.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactProvenanceEntry.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFacts.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashPipeline.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashStage.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BashStageProgram.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/BlockOperator.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CapturedOutcome.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CommandFamilyKey.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CommandFamilySummary.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CompileError.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CompositionOperator.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundBodyReason.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundFeatureReason.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundForm.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundIteratorReason.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusEntry.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusExpectedLabel.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusFidelity.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQueryModel.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQuerySummary.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQuerySummarySnapshot.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusRecord.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusSource.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/CountByLabel.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/Decision.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionEffect.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionProvenance.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionSource.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/DiagnosticSeverity.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/EffectivePolicy.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/EmbeddedShellProjection.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/EnvironmentAssignment.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/IteratorSourceKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/LoopQuoteKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/LoopVariableUnknownReason.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/MatcherExpr.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/MutationShapeKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/MutationTrustBoundaryClassification.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/MutationTrustBoundaryKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PackCompileResult.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PackWarningLevel.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ParsedEvidence.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ParsedShapeSummary.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathAccess.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactProjectScope.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactsRequirement.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactsResolvedConfig.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathNormalization.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathScope.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathScopeMatcherExpr.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathScopeMatcherMode.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathUnknownReason.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PathUsageKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolOperation.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolPathInput.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolShape.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolSpec.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PiFileMutationToolName.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PiToolMutationFacts.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPack.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackDocLink.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackExample.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackMetadata.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackWarning.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyRule.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ProposalNotRunReason.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ProposalNotRunSeverity.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/QuoteKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/Redirect.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/RedirectStream.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/RedirectTargetKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayCorpus.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDelta.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaBlockedSummary.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaChangedRecord.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaFamily.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaImprovement.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaRegression.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaStatus.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaTransitionCount.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayPathFacts.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayReviewerMode.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayReviewerOutcome.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayStatus.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayedDecision.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ShapeDiagnostic.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/SourceFidelity.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/SourceSpan.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/Substitution.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/SubstitutionKind.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ToolCallIdentity.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathAccess.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFact.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFactContext.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFacts.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathUsage.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/ToolShape.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/UnknownPathBehavior.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/UnknownToolShape.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/contracts/index.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/index.ts +33 -2
- package/node_modules/@nklisch/pi-clearance/src/native/loader.ts +29 -42
- package/node_modules/@nklisch/pi-clearance/src/replay/reviewer-config-proposals.ts +7 -4
- package/node_modules/@nklisch/pi-clearance/src/runtime/allow-request-message.ts +21 -0
- package/node_modules/@nklisch/pi-clearance/src/runtime/auto-reviewer-read-models.ts +165 -8
- package/node_modules/@nklisch/pi-clearance/src/runtime/command-registry.ts +1 -1
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/allow.ts +26 -2
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/packs.ts +6 -6
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/scope.ts +5 -5
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/actions.ts +11 -0
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/dispatcher.ts +274 -1
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/model-options.ts +52 -0
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/native-ui.ts +54 -67
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels/reviewer.ts +19 -3
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels/scope.ts +2 -11
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels.ts +10 -1
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/read-model.ts +47 -13
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings.ts +32 -37
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/status.ts +3 -0
- package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/types.ts +14 -5
- package/node_modules/@nklisch/pi-clearance/src/runtime/handler.ts +36 -0
- package/node_modules/@nklisch/pi-clearance/src/runtime/mode-prompt.ts +0 -1
- package/node_modules/@nklisch/pi-clearance/src/runtime/model-adapter.ts +5 -1
- package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer-context.ts +73 -10
- package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer-prompts.ts +186 -175
- package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer.ts +26 -6
- package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.d.ts +23 -1
- package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.js +46 -50
- package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-package.js +2 -2
- package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-runtime.js +2 -2
- package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-lifecycle.js +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-package.js +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/README.md +41 -31
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/dist/public.d.ts +2 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/FORK-MAINTENANCE.md +17 -11
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/VISION.md +14 -7
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/architecture.md +11 -19
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/comparison-with-upstream.md +58 -53
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/package.json +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/agent-type-resolution.ts +43 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/agent-types.ts +11 -6
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/custom-agents.ts +13 -6
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/default-agents.ts +0 -52
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/thinking-levels.ts +12 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/handlers/interrupt.ts +5 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/handlers/lifecycle.ts +3 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/index.ts +7 -6
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/create-subagent-session.ts +7 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/parent-snapshot.ts +11 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-manager.ts +26 -56
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-session.ts +48 -9
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-state.ts +32 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent.ts +105 -17
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/composite-subagent-observer.ts +4 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/notification.ts +50 -46
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/renderer.ts +1 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/subagent-events-observer.ts +15 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/runtime.ts +2 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service-adapter.ts +35 -6
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service.ts +1 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/model-label.ts +19 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/prompts.ts +24 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/session-config.ts +11 -6
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/settings.ts +70 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/agent-tool.ts +27 -10
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/background-spawner.ts +6 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/foreground-runner.ts +12 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-report.ts +17 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-tool.ts +13 -20
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/result-renderer.ts +6 -1
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/spawn-config.ts +16 -16
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/steer-tool.ts +12 -4
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/types.ts +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/agent-widget.ts +24 -11
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/display.ts +10 -7
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/glyphs.ts +7 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigation.ts +34 -25
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigator.ts +30 -10
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/subagents-settings.ts +58 -13
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/widget-renderer.ts +32 -19
- package/node_modules/@nklisch/pi-plugins/package.json +3 -3
- package/package.json +1 -1
- package/test/verify-bundle.mjs +28 -1
|
@@ -27,29 +27,11 @@ import type { WorkspaceProvider } from "#src/lifecycle/workspace";
|
|
|
27
27
|
import type { RunConfig } from "#src/runtime";
|
|
28
28
|
import type { AgentInvocation, CompactionInfo, ParentSessionInfo, SubagentType, ThinkingLevel } from "#src/types";
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
* A lightweight snapshot of a subagent evicted by the 10-minute cleanup sweep.
|
|
32
|
-
*
|
|
33
|
-
* The sweep frees the heavy in-memory session (its message history included);
|
|
34
|
-
* this descriptor retains only the fields the session navigator needs to label
|
|
35
|
-
* the agent in the picker, plus the persisted `outputFile` to source its
|
|
36
|
-
* transcript from disk. Carries no messages, so memory stays bounded.
|
|
37
|
-
*/
|
|
38
|
-
export interface EvictedSubagent {
|
|
39
|
-
readonly id: string;
|
|
40
|
-
readonly type: SubagentType;
|
|
41
|
-
readonly description: string;
|
|
42
|
-
readonly status: SubagentStatus;
|
|
43
|
-
readonly startedAt: number;
|
|
44
|
-
readonly completedAt: number | undefined;
|
|
45
|
-
readonly toolUses: number;
|
|
46
|
-
readonly outputFile: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
30
|
/** Observer interface for agent lifecycle notifications. */
|
|
50
31
|
export interface SubagentManagerObserver {
|
|
51
32
|
onSubagentStarted(record: Subagent): void;
|
|
52
33
|
onSubagentCompleted(record: Subagent): void;
|
|
34
|
+
onSubagentResumed?(record: Subagent): void;
|
|
53
35
|
onSubagentCompacted(record: Subagent, info: CompactionInfo): void;
|
|
54
36
|
/** Fires synchronously after a background agent record is created (before run). */
|
|
55
37
|
onSubagentCreated(record: Subagent): void;
|
|
@@ -95,8 +77,6 @@ export interface AgentSpawnConfig {
|
|
|
95
77
|
|
|
96
78
|
export class SubagentManager {
|
|
97
79
|
private agents = new Map<string, Subagent>();
|
|
98
|
-
/** Descriptors of agents removed by the cleanup sweep, keyed by id — navigable from disk. */
|
|
99
|
-
private readonly evicted = new Map<string, EvictedSubagent>();
|
|
100
80
|
private cleanupInterval: ReturnType<typeof setInterval>;
|
|
101
81
|
private readonly observer?: SubagentManagerObserver;
|
|
102
82
|
private readonly createSubagentSession: (params: CreateSubagentSessionParams) => Promise<SubagentSession>;
|
|
@@ -117,7 +97,7 @@ export class SubagentManager {
|
|
|
117
97
|
this.baseCwd = options.baseCwd;
|
|
118
98
|
this.observer = options.observer;
|
|
119
99
|
this.getRunConfig = options.getRunConfig;
|
|
120
|
-
//
|
|
100
|
+
// Periodically release heavy terminal sessions according to retention policy.
|
|
121
101
|
this.cleanupInterval = setInterval(() => this.cleanup(), 60_000);
|
|
122
102
|
this.cleanupInterval.unref();
|
|
123
103
|
}
|
|
@@ -160,6 +140,9 @@ export class SubagentManager {
|
|
|
160
140
|
try { this.observer?.onSubagentCompleted(agent); } catch (err) { debugLog("onSubagentCompleted observer", err); }
|
|
161
141
|
}
|
|
162
142
|
},
|
|
143
|
+
onResumedFinished: (agent) => {
|
|
144
|
+
try { this.observer?.onSubagentResumed?.(agent); } catch (err) { debugLog("onSubagentResumed observer", err); }
|
|
145
|
+
},
|
|
163
146
|
onCompacted: (agent, info) => {
|
|
164
147
|
this.observer?.onSubagentCompacted(agent, info);
|
|
165
148
|
},
|
|
@@ -271,19 +254,14 @@ export class SubagentManager {
|
|
|
271
254
|
);
|
|
272
255
|
}
|
|
273
256
|
|
|
274
|
-
/** Descriptors of agents evicted by the cleanup sweep, most recent first. */
|
|
275
|
-
listEvicted(): EvictedSubagent[] {
|
|
276
|
-
return [...this.evicted.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
257
|
abort(id: string): boolean {
|
|
280
258
|
const record = this.agents.get(id);
|
|
281
259
|
if (!record) return false;
|
|
282
260
|
|
|
283
|
-
// A queued agent has not started;
|
|
284
|
-
// becomes a no-op
|
|
261
|
+
// A queued agent has not started; terminate it through the observer funnel.
|
|
262
|
+
// Its scheduled thunk becomes a no-op when its slot eventually opens.
|
|
285
263
|
if (record.status === "queued") {
|
|
286
|
-
record.
|
|
264
|
+
record.stopQueued();
|
|
287
265
|
return true;
|
|
288
266
|
}
|
|
289
267
|
|
|
@@ -297,14 +275,22 @@ export class SubagentManager {
|
|
|
297
275
|
}
|
|
298
276
|
|
|
299
277
|
private cleanup() {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (record.
|
|
307
|
-
|
|
278
|
+
const now = Date.now();
|
|
279
|
+
const config = this.getRunConfig?.();
|
|
280
|
+
const consumedMinutes = config?.consumedSessionRetentionMinutes ?? 10;
|
|
281
|
+
const unconsumedMinutes = config?.unconsumedSessionRetentionMinutes ?? 720;
|
|
282
|
+
|
|
283
|
+
for (const record of this.agents.values()) {
|
|
284
|
+
if (record.isActive() || !record.isSessionReady()) continue;
|
|
285
|
+
const anchor = record.consumed
|
|
286
|
+
? record.consumedAt ?? record.completedAt
|
|
287
|
+
: record.completedAt;
|
|
288
|
+
if (anchor == null) continue;
|
|
289
|
+
const retentionMinutes = record.consumed ? consumedMinutes : unconsumedMinutes;
|
|
290
|
+
if (anchor + retentionMinutes * 60_000 > now) continue;
|
|
291
|
+
// Keep the lightweight terminal record and result for the whole parent
|
|
292
|
+
// session; only release the heavy in-memory child session.
|
|
293
|
+
record.releaseSession();
|
|
308
294
|
}
|
|
309
295
|
}
|
|
310
296
|
|
|
@@ -317,8 +303,7 @@ export class SubagentManager {
|
|
|
317
303
|
if (record.status === "running" || record.status === "queued") continue;
|
|
318
304
|
this.removeRecord(id, record);
|
|
319
305
|
}
|
|
320
|
-
|
|
321
|
-
this.evicted.clear();
|
|
306
|
+
|
|
322
307
|
}
|
|
323
308
|
|
|
324
309
|
/** Whether any agents are still running or queued. */
|
|
@@ -335,7 +320,7 @@ export class SubagentManager {
|
|
|
335
320
|
let count = 0;
|
|
336
321
|
for (const record of this.agents.values()) {
|
|
337
322
|
if (record.status === "queued") {
|
|
338
|
-
record.
|
|
323
|
+
record.stopQueued();
|
|
339
324
|
count++;
|
|
340
325
|
} else if (record.abort()) {
|
|
341
326
|
count++;
|
|
@@ -378,20 +363,5 @@ export class SubagentManager {
|
|
|
378
363
|
record.disposeSession();
|
|
379
364
|
}
|
|
380
365
|
this.agents.clear();
|
|
381
|
-
this.evicted.clear();
|
|
382
366
|
}
|
|
383
367
|
}
|
|
384
|
-
|
|
385
|
-
/** Capture an evicted agent's navigable fields from its record. */
|
|
386
|
-
function toEvictedSubagent(record: Subagent, outputFile: string): EvictedSubagent {
|
|
387
|
-
return {
|
|
388
|
-
id: record.id,
|
|
389
|
-
type: record.type,
|
|
390
|
-
description: record.description,
|
|
391
|
-
status: record.status,
|
|
392
|
-
startedAt: record.startedAt,
|
|
393
|
-
completedAt: record.completedAt,
|
|
394
|
-
toolUses: record.toolUses,
|
|
395
|
-
outputFile,
|
|
396
|
-
};
|
|
397
|
-
}
|
|
@@ -29,6 +29,8 @@ import type { SessionMessage } from "#src/types";
|
|
|
29
29
|
/** Outcome of one turn loop. */
|
|
30
30
|
export interface TurnLoopResult {
|
|
31
31
|
responseText: string;
|
|
32
|
+
/** Normally-resolved provider failure from the final assistant turn. */
|
|
33
|
+
failure?: string;
|
|
32
34
|
/** True if the agent was hard-aborted (max turns + grace exceeded). */
|
|
33
35
|
aborted: boolean;
|
|
34
36
|
/** True if the agent was steered to wrap up (soft turn limit) but finished in time. */
|
|
@@ -121,6 +123,7 @@ export class SubagentSession {
|
|
|
121
123
|
}
|
|
122
124
|
});
|
|
123
125
|
|
|
126
|
+
const startIndex = session.messages.length;
|
|
124
127
|
const collector = collectResponseText(session);
|
|
125
128
|
const cleanupAbort = forwardAbortSignal(session, opts.signal);
|
|
126
129
|
|
|
@@ -135,13 +138,19 @@ export class SubagentSession {
|
|
|
135
138
|
// published immediately after prompt resolution and before extraction.
|
|
136
139
|
await session.prompt(effectivePrompt);
|
|
137
140
|
this.publishCompleted(aborted, softLimitReached);
|
|
138
|
-
const responseText = collector.getText().trim() || getLastAssistantText(session);
|
|
139
|
-
return {
|
|
141
|
+
const responseText = collector.getText().trim() || getLastAssistantText(session, startIndex);
|
|
142
|
+
return {
|
|
143
|
+
responseText,
|
|
144
|
+
failure: finalTurnError(session, startIndex),
|
|
145
|
+
aborted,
|
|
146
|
+
steered: softLimitReached,
|
|
147
|
+
};
|
|
140
148
|
}
|
|
141
149
|
return await this.driveLifecycleTurns(
|
|
142
150
|
effectivePrompt,
|
|
143
151
|
opts.lifecycle,
|
|
144
|
-
() => collector.getText().trim() || getLastAssistantText(session),
|
|
152
|
+
() => collector.getText().trim() || getLastAssistantText(session, startIndex),
|
|
153
|
+
() => finalTurnError(session, startIndex),
|
|
145
154
|
() => ({ aborted, steered: softLimitReached }),
|
|
146
155
|
);
|
|
147
156
|
} finally {
|
|
@@ -152,14 +161,21 @@ export class SubagentSession {
|
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
/** Re-prompt the same session (resume); preserves the released no-provider path. */
|
|
155
|
-
async resumeTurnLoop(
|
|
164
|
+
async resumeTurnLoop(
|
|
165
|
+
prompt: string,
|
|
166
|
+
signal?: AbortSignal,
|
|
167
|
+
): Promise<{ text: string; failure?: string }> {
|
|
156
168
|
const session = this._session;
|
|
169
|
+
const startIndex = session.messages.length;
|
|
157
170
|
const collector = collectResponseText(session);
|
|
158
171
|
const cleanupAbort = forwardAbortSignal(session, signal);
|
|
159
172
|
|
|
160
173
|
try {
|
|
161
174
|
await session.prompt(prompt);
|
|
162
|
-
return
|
|
175
|
+
return {
|
|
176
|
+
text: collector.getText().trim() || getLastAssistantText(session, startIndex),
|
|
177
|
+
failure: finalTurnError(session, startIndex),
|
|
178
|
+
};
|
|
163
179
|
} finally {
|
|
164
180
|
collector.unsubscribe();
|
|
165
181
|
cleanupAbort();
|
|
@@ -175,13 +191,15 @@ export class SubagentSession {
|
|
|
175
191
|
signal: AbortSignal | undefined,
|
|
176
192
|
lifecycle: SubagentTurnLifecycle,
|
|
177
193
|
): Promise<TurnLoopResult> {
|
|
194
|
+
const startIndex = this._session.messages.length;
|
|
178
195
|
const collector = collectResponseText(this._session);
|
|
179
196
|
const cleanupAbort = forwardAbortSignal(this._session, signal);
|
|
180
197
|
try {
|
|
181
198
|
return await this.driveLifecycleTurns(
|
|
182
199
|
prompt,
|
|
183
200
|
lifecycle,
|
|
184
|
-
() => collector.getText().trim() || getLastAssistantText(this._session),
|
|
201
|
+
() => collector.getText().trim() || getLastAssistantText(this._session, startIndex),
|
|
202
|
+
() => finalTurnError(this._session, startIndex),
|
|
185
203
|
() => ({ aborted: false, steered: false }),
|
|
186
204
|
);
|
|
187
205
|
} finally {
|
|
@@ -194,6 +212,7 @@ export class SubagentSession {
|
|
|
194
212
|
initialPrompt: string,
|
|
195
213
|
lifecycle: SubagentTurnLifecycle,
|
|
196
214
|
responseText: () => string,
|
|
215
|
+
failure: () => string | undefined,
|
|
197
216
|
outcome: () => Readonly<{ aborted: boolean; steered: boolean }>,
|
|
198
217
|
): Promise<TurnLoopResult> {
|
|
199
218
|
const start = await lifecycle.beforeStart(initialPrompt);
|
|
@@ -208,6 +227,10 @@ export class SubagentSession {
|
|
|
208
227
|
await this._session.prompt(prompt);
|
|
209
228
|
const turnOutcome = outcome();
|
|
210
229
|
const proposedResult = responseText();
|
|
230
|
+
const turnFailure = failure();
|
|
231
|
+
if (turnFailure) {
|
|
232
|
+
return { responseText: proposedResult, failure: turnFailure, ...turnOutcome };
|
|
233
|
+
}
|
|
211
234
|
const completion = await lifecycle.beforeComplete(
|
|
212
235
|
proposedResult,
|
|
213
236
|
toLifecycleOutcome(turnOutcome),
|
|
@@ -327,7 +350,7 @@ function toLifecycleOutcome(
|
|
|
327
350
|
function collectResponseText(session: AgentSession) {
|
|
328
351
|
let text = "";
|
|
329
352
|
const unsubscribe = session.subscribe((event: AgentSessionEvent) => {
|
|
330
|
-
if (event.type === "message_start") {
|
|
353
|
+
if (event.type === "message_start" && event.message?.role === "assistant") {
|
|
331
354
|
text = "";
|
|
332
355
|
}
|
|
333
356
|
if (
|
|
@@ -341,8 +364,8 @@ function collectResponseText(session: AgentSession) {
|
|
|
341
364
|
}
|
|
342
365
|
|
|
343
366
|
/** Get the last assistant text from the completed session history. */
|
|
344
|
-
function getLastAssistantText(session: AgentSession): string {
|
|
345
|
-
for (let i = session.messages.length - 1; i >=
|
|
367
|
+
function getLastAssistantText(session: AgentSession, startIndex = 0): string {
|
|
368
|
+
for (let i = session.messages.length - 1; i >= startIndex; i--) {
|
|
346
369
|
const msg = session.messages[i];
|
|
347
370
|
if (msg.role !== "assistant") continue;
|
|
348
371
|
const text = extractText(msg.content).trim();
|
|
@@ -351,6 +374,22 @@ function getLastAssistantText(session: AgentSession): string {
|
|
|
351
374
|
return "";
|
|
352
375
|
}
|
|
353
376
|
|
|
377
|
+
/** Classify a normally-resolved failure from this invocation's final assistant turn. */
|
|
378
|
+
function finalTurnError(session: AgentSession, startIndex = 0): string | undefined {
|
|
379
|
+
for (let i = session.messages.length - 1; i >= startIndex; i--) {
|
|
380
|
+
const msg = session.messages[i];
|
|
381
|
+
if (msg.role !== "assistant") continue;
|
|
382
|
+
if (msg.stopReason === "error") {
|
|
383
|
+
return msg.errorMessage?.trim() || "provider error with no output";
|
|
384
|
+
}
|
|
385
|
+
if (msg.stopReason === "length" && !extractText(msg.content).trim()) {
|
|
386
|
+
return "run hit the output token limit before producing any text";
|
|
387
|
+
}
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
|
|
354
393
|
/**
|
|
355
394
|
* Wire an AbortSignal to abort a session.
|
|
356
395
|
* Returns a cleanup function to remove the listener.
|
|
@@ -34,8 +34,12 @@ export interface SubagentStateInit {
|
|
|
34
34
|
status?: SubagentStatus;
|
|
35
35
|
result?: string;
|
|
36
36
|
error?: string;
|
|
37
|
+
/** Whether the limiter never admitted this agent. */
|
|
38
|
+
stoppedWhileQueued?: boolean;
|
|
37
39
|
startedAt?: number;
|
|
38
40
|
completedAt?: number;
|
|
41
|
+
/** Time the parent first collected the terminal outcome. */
|
|
42
|
+
consumedAt?: number;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export class SubagentState {
|
|
@@ -55,6 +59,13 @@ export class SubagentState {
|
|
|
55
59
|
private _completedAt?: number;
|
|
56
60
|
get completedAt(): number | undefined { return this._completedAt; }
|
|
57
61
|
|
|
62
|
+
private _stoppedWhileQueued: boolean;
|
|
63
|
+
get stoppedWhileQueued(): boolean { return this._stoppedWhileQueued; }
|
|
64
|
+
|
|
65
|
+
private _consumedAt?: number;
|
|
66
|
+
get consumedAt(): number | undefined { return this._consumedAt; }
|
|
67
|
+
get consumed(): boolean { return this._consumedAt != null; }
|
|
68
|
+
|
|
58
69
|
// Stats — accumulated via mutation methods, readable via getters
|
|
59
70
|
private _toolUses = 0;
|
|
60
71
|
get toolUses(): number { return this._toolUses; }
|
|
@@ -81,8 +92,10 @@ export class SubagentState {
|
|
|
81
92
|
this._status = init.status ?? "queued";
|
|
82
93
|
this._result = init.result;
|
|
83
94
|
this._error = init.error;
|
|
95
|
+
this._stoppedWhileQueued = init.stoppedWhileQueued ?? false;
|
|
84
96
|
this._startedAt = init.startedAt ?? Date.now();
|
|
85
97
|
this._completedAt = init.completedAt;
|
|
98
|
+
this._consumedAt = init.consumedAt;
|
|
86
99
|
}
|
|
87
100
|
|
|
88
101
|
/** Increment tool use count. Called by record-observer on tool_execution_end. */
|
|
@@ -177,19 +190,36 @@ export class SubagentState {
|
|
|
177
190
|
* Always sets error (formatted) and completedAt (??=). Only changes status if not stopped.
|
|
178
191
|
*/
|
|
179
192
|
markError(error: unknown, completedAt?: number): void {
|
|
193
|
+
this.markFailed(error, undefined, completedAt);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Record a failure together with any partial output produced before it. */
|
|
197
|
+
markFailed(error: unknown, partialResult?: string, completedAt?: number): void {
|
|
180
198
|
this._error = error instanceof Error ? error.message : String(error);
|
|
199
|
+
this._result = partialResult?.trim() ? partialResult : undefined;
|
|
181
200
|
this._completedAt ??= completedAt ?? Date.now();
|
|
182
201
|
if (this._status !== "stopped") {
|
|
183
202
|
this._status = "error";
|
|
184
203
|
}
|
|
185
204
|
}
|
|
186
205
|
|
|
206
|
+
/** Record that the parent collected the outcome. Idempotent. */
|
|
207
|
+
markConsumed(at?: number): void {
|
|
208
|
+
this._consumedAt ??= at ?? Date.now();
|
|
209
|
+
}
|
|
210
|
+
|
|
187
211
|
/** Transition to stopped state. Always valid — no guard. */
|
|
188
212
|
markStopped(completedAt?: number): void {
|
|
189
213
|
this._status = "stopped";
|
|
190
214
|
this._completedAt = completedAt ?? Date.now();
|
|
191
215
|
}
|
|
192
216
|
|
|
217
|
+
/** Stop an agent before the limiter admitted it. */
|
|
218
|
+
stopQueued(completedAt?: number): void {
|
|
219
|
+
if (this._status === "queued") this._stoppedWhileQueued = true;
|
|
220
|
+
this.markStopped(completedAt);
|
|
221
|
+
}
|
|
222
|
+
|
|
193
223
|
/** Reset for resume: running status, new startedAt, clear completedAt/result/error. */
|
|
194
224
|
resetForResume(startedAt: number): void {
|
|
195
225
|
this._status = "running";
|
|
@@ -197,5 +227,7 @@ export class SubagentState {
|
|
|
197
227
|
this._completedAt = undefined;
|
|
198
228
|
this._result = undefined;
|
|
199
229
|
this._error = undefined;
|
|
230
|
+
this._consumedAt = undefined;
|
|
231
|
+
this._stoppedWhileQueued = false;
|
|
200
232
|
}
|
|
201
233
|
}
|
|
@@ -25,6 +25,7 @@ import type { WorkspaceProvider } from "#src/lifecycle/workspace";
|
|
|
25
25
|
import { WorkspaceBracket } from "#src/lifecycle/workspace-bracket";
|
|
26
26
|
import { subscribeSubagentObserver } from "#src/observation/record-observer";
|
|
27
27
|
import type { RunConfig } from "#src/runtime";
|
|
28
|
+
import { formatModelLabel } from "#src/session/model-label";
|
|
28
29
|
import type { AgentInvocation, CompactionInfo, ParentSessionInfo, SessionMessage, SubagentType, ThinkingLevel } from "#src/types";
|
|
29
30
|
|
|
30
31
|
/** Per-subagent lifecycle observer — created by SubagentManager for each spawn. */
|
|
@@ -33,8 +34,10 @@ export interface SubagentLifecycleObserver {
|
|
|
33
34
|
onStarted?(agent: Subagent): void;
|
|
34
35
|
/** Fires once the session is created — the subagent's subagentSession is now available. */
|
|
35
36
|
onSessionCreated?(agent: Subagent): void;
|
|
36
|
-
/** Fires once when the run completes or fails (for concurrency drain). */
|
|
37
|
+
/** Fires once when the initial run completes or fails (for concurrency drain). */
|
|
37
38
|
onRunFinished?(agent: Subagent): void;
|
|
39
|
+
/** Fires once when a resumed turn reaches a terminal state. */
|
|
40
|
+
onResumedFinished?(agent: Subagent): void;
|
|
38
41
|
/** Fires on compaction events during the run. */
|
|
39
42
|
onCompacted?(agent: Subagent, info: CompactionInfo): void;
|
|
40
43
|
}
|
|
@@ -112,6 +115,9 @@ export class Subagent {
|
|
|
112
115
|
get error(): string | undefined { return this.state.error; }
|
|
113
116
|
get startedAt(): number { return this.state.startedAt; }
|
|
114
117
|
get completedAt(): number | undefined { return this.state.completedAt; }
|
|
118
|
+
get stoppedWhileQueued(): boolean { return this.state.stoppedWhileQueued; }
|
|
119
|
+
get consumedAt(): number | undefined { return this.state.consumedAt; }
|
|
120
|
+
get consumed(): boolean { return this.state.consumed; }
|
|
115
121
|
get toolUses(): number { return this.state.toolUses; }
|
|
116
122
|
get lifetimeUsage(): Readonly<LifetimeUsage> { return this.state.lifetimeUsage; }
|
|
117
123
|
get compactionCount(): number { return this.state.compactionCount; }
|
|
@@ -119,8 +125,10 @@ export class Subagent {
|
|
|
119
125
|
get activeTools(): ReadonlyMap<string, string> { return this.state.activeTools; }
|
|
120
126
|
get responseText(): string { return this.state.responseText; }
|
|
121
127
|
get maxTurns(): number | undefined { return this.execution.maxTurns; }
|
|
128
|
+
/** Exact effective model label used by every operator-facing status surface. */
|
|
129
|
+
get modelLabel(): string { return formatModelLabel(this.execution.model ?? this.execution.snapshot.model); }
|
|
122
130
|
|
|
123
|
-
|
|
131
|
+
abortController: AbortController;
|
|
124
132
|
private _promise?: Promise<void>;
|
|
125
133
|
get promise(): Promise<void> | undefined { return this._promise; }
|
|
126
134
|
|
|
@@ -129,6 +137,9 @@ export class Subagent {
|
|
|
129
137
|
private readonly workspaceBracket: WorkspaceBracket;
|
|
130
138
|
|
|
131
139
|
subagentSession?: SubagentSession;
|
|
140
|
+
private releasedOutputFile?: string;
|
|
141
|
+
private _sessionReleased = false;
|
|
142
|
+
get sessionReleased(): boolean { return this._sessionReleased; }
|
|
132
143
|
|
|
133
144
|
// Steer buffer — messages queued before the session is ready
|
|
134
145
|
private _pendingSteers: string[] = [];
|
|
@@ -137,7 +148,7 @@ export class Subagent {
|
|
|
137
148
|
|
|
138
149
|
/** Path to the agent's session JSONL file, or undefined if not yet available. */
|
|
139
150
|
get outputFile(): string | undefined {
|
|
140
|
-
return this.subagentSession?.outputFile;
|
|
151
|
+
return this.subagentSession?.outputFile ?? this.releasedOutputFile;
|
|
141
152
|
}
|
|
142
153
|
|
|
143
154
|
/** The tool call ID that spawned this background agent, if any. */
|
|
@@ -150,6 +161,14 @@ export class Subagent {
|
|
|
150
161
|
return this.subagentSession != null;
|
|
151
162
|
}
|
|
152
163
|
|
|
164
|
+
isActive(): boolean {
|
|
165
|
+
return this.status === "queued" || this.status === "running";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
isRunning(): boolean {
|
|
169
|
+
return this.status === "running";
|
|
170
|
+
}
|
|
171
|
+
|
|
153
172
|
/**
|
|
154
173
|
* Steer a running agent, owning the non-running rejection rule.
|
|
155
174
|
* Returns a `rejected` outcome (with the observed status) when the agent is
|
|
@@ -329,21 +348,35 @@ export class Subagent {
|
|
|
329
348
|
*
|
|
330
349
|
* Requires an existing SubagentSession (set when the original run created it).
|
|
331
350
|
* The returned promise always resolves (errors are captured internally).
|
|
332
|
-
*
|
|
333
|
-
* route through this.abortController.
|
|
351
|
+
* Parent cancellation and manager abort both stop the resumed turn.
|
|
334
352
|
*/
|
|
335
|
-
|
|
353
|
+
resume(prompt: string, signal?: AbortSignal): Promise<void> {
|
|
336
354
|
const subagentSession = this.subagentSession;
|
|
337
355
|
if (!subagentSession) {
|
|
338
|
-
|
|
356
|
+
return Promise.reject(new Error(
|
|
357
|
+
this.sessionReleased
|
|
358
|
+
? "Subagent session was released and can no longer be resumed"
|
|
359
|
+
: "Subagent not configured for resume — missing session",
|
|
360
|
+
));
|
|
339
361
|
}
|
|
362
|
+
this._promise = this.runResume(subagentSession, prompt, signal);
|
|
363
|
+
return this._promise;
|
|
364
|
+
}
|
|
340
365
|
|
|
366
|
+
private async runResume(
|
|
367
|
+
subagentSession: SubagentSession,
|
|
368
|
+
prompt: string,
|
|
369
|
+
signal?: AbortSignal,
|
|
370
|
+
): Promise<void> {
|
|
341
371
|
this.resetForResume(Date.now());
|
|
372
|
+
const executionSignal = signal
|
|
373
|
+
? AbortSignal.any([this.abortController.signal, signal])
|
|
374
|
+
: this.abortController.signal;
|
|
342
375
|
this.listeners.attachObserver(subscribeSubagentObserver(subagentSession, this.state, {
|
|
343
376
|
onCompact: (info) => this.execution.observer?.onCompacted?.(this, info),
|
|
344
377
|
}));
|
|
345
378
|
|
|
346
|
-
const lifecycle = this.createTurnLifecycle("resume",
|
|
379
|
+
const lifecycle = this.createTurnLifecycle("resume", executionSignal);
|
|
347
380
|
try {
|
|
348
381
|
if (lifecycle) {
|
|
349
382
|
const result = await subagentSession.resumeLifecycleTurnLoop(
|
|
@@ -351,20 +384,31 @@ export class Subagent {
|
|
|
351
384
|
lifecycle.signal,
|
|
352
385
|
lifecycle,
|
|
353
386
|
);
|
|
354
|
-
if (result.
|
|
387
|
+
if (result.failure) this.markFailed(result.failure, result.responseText);
|
|
388
|
+
else if (result.aborted) this.markAborted(result.responseText);
|
|
355
389
|
else if (result.steered) this.markSteered(result.responseText);
|
|
356
390
|
else this.markCompleted(result.responseText);
|
|
357
391
|
} else {
|
|
358
|
-
const
|
|
359
|
-
|
|
392
|
+
const resumed = await subagentSession.resumeTurnLoop(prompt, executionSignal);
|
|
393
|
+
const outcome = typeof resumed === "string" ? { text: resumed } : resumed;
|
|
394
|
+
if (outcome.failure) this.markFailed(outcome.failure, outcome.text);
|
|
395
|
+
else this.markCompleted(outcome.text);
|
|
360
396
|
}
|
|
361
397
|
} catch (err) {
|
|
362
398
|
this.markError(err);
|
|
363
399
|
} finally {
|
|
364
400
|
this.listeners.release();
|
|
401
|
+
this.execution.observer?.onResumedFinished?.(this);
|
|
365
402
|
}
|
|
366
403
|
}
|
|
367
404
|
|
|
405
|
+
/** Wait for the current queued, running, or resumed execution without cancelling it. */
|
|
406
|
+
async waitUntilSettled(signal: AbortSignal): Promise<void> {
|
|
407
|
+
const run = this._promise;
|
|
408
|
+
if (!run || !this.isActive() || signal.aborted) return;
|
|
409
|
+
await settleOrAbort(run, signal);
|
|
410
|
+
}
|
|
411
|
+
|
|
368
412
|
/** Build a callback-only lifecycle bridge after the immutable child session exists. */
|
|
369
413
|
private createTurnLifecycle(
|
|
370
414
|
phase: "initial" | "resume",
|
|
@@ -449,11 +493,26 @@ export class Subagent {
|
|
|
449
493
|
this.state.markError(error, completedAt);
|
|
450
494
|
}
|
|
451
495
|
|
|
496
|
+
/** Record a provider failure and preserve any partial output. */
|
|
497
|
+
markFailed(error: unknown, partialResult?: string, completedAt?: number): void {
|
|
498
|
+
this.state.markFailed(error, partialResult, completedAt);
|
|
499
|
+
}
|
|
500
|
+
|
|
452
501
|
/** Transition to stopped state. Always valid — no guard. */
|
|
453
502
|
markStopped(completedAt?: number): void {
|
|
454
503
|
this.state.markStopped(completedAt);
|
|
455
504
|
}
|
|
456
505
|
|
|
506
|
+
markConsumed(at?: number): void {
|
|
507
|
+
this.state.markConsumed(at);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/** Stop a queued agent through the same terminal observer funnel as a run. */
|
|
511
|
+
stopQueued(): void {
|
|
512
|
+
this.state.stopQueued();
|
|
513
|
+
this.execution.observer?.onRunFinished?.(this);
|
|
514
|
+
}
|
|
515
|
+
|
|
457
516
|
/**
|
|
458
517
|
* Abort a running agent: fire AbortController and transition to stopped.
|
|
459
518
|
* Returns false if the agent is not running.
|
|
@@ -488,6 +547,7 @@ export class Subagent {
|
|
|
488
547
|
|
|
489
548
|
/** Reset for resume: running status, new startedAt, clear completedAt/result/error/listeners. */
|
|
490
549
|
resetForResume(startedAt: number): void {
|
|
550
|
+
this.abortController = new AbortController();
|
|
491
551
|
this.state.resetForResume(startedAt);
|
|
492
552
|
this.listeners.release();
|
|
493
553
|
}
|
|
@@ -496,16 +556,19 @@ export class Subagent {
|
|
|
496
556
|
completeRun(result: TurnLoopResult): void {
|
|
497
557
|
this.listeners.release();
|
|
498
558
|
|
|
499
|
-
const finalStatus: SubagentStatus = result.
|
|
500
|
-
? "
|
|
501
|
-
: result.
|
|
502
|
-
? "
|
|
503
|
-
:
|
|
559
|
+
const finalStatus: SubagentStatus = result.failure
|
|
560
|
+
? "error"
|
|
561
|
+
: result.aborted
|
|
562
|
+
? "aborted"
|
|
563
|
+
: result.steered
|
|
564
|
+
? "steered"
|
|
565
|
+
: "completed";
|
|
504
566
|
const finalResult =
|
|
505
567
|
result.responseText +
|
|
506
568
|
this.workspaceBracket.dispose({ status: finalStatus, description: this.description });
|
|
507
569
|
|
|
508
|
-
if (result.
|
|
570
|
+
if (result.failure) this.markFailed(result.failure, finalResult);
|
|
571
|
+
else if (result.aborted) this.markAborted(finalResult);
|
|
509
572
|
else if (result.steered) this.markSteered(finalResult);
|
|
510
573
|
else this.markCompleted(finalResult);
|
|
511
574
|
|
|
@@ -517,6 +580,16 @@ export class Subagent {
|
|
|
517
580
|
this.subagentSession?.dispose();
|
|
518
581
|
}
|
|
519
582
|
|
|
583
|
+
/** Release heavy session state while preserving the transcript pointer and record. */
|
|
584
|
+
releaseSession(): void {
|
|
585
|
+
const session = this.subagentSession;
|
|
586
|
+
if (!session) return;
|
|
587
|
+
this.releasedOutputFile = session.outputFile;
|
|
588
|
+
session.dispose();
|
|
589
|
+
this.subagentSession = undefined;
|
|
590
|
+
this._sessionReleased = true;
|
|
591
|
+
}
|
|
592
|
+
|
|
520
593
|
/** Fail a run: mark error, release listeners, best-effort workspace dispose, notify observer. */
|
|
521
594
|
failRun(err: unknown): void {
|
|
522
595
|
this.markError(err);
|
|
@@ -529,3 +602,18 @@ export class Subagent {
|
|
|
529
602
|
this.execution.observer?.onRunFinished?.(this);
|
|
530
603
|
}
|
|
531
604
|
}
|
|
605
|
+
|
|
606
|
+
/** Resolve when either the run settles or the caller's wait is interrupted. */
|
|
607
|
+
async function settleOrAbort(run: Promise<void>, signal: AbortSignal): Promise<void> {
|
|
608
|
+
await new Promise<void>((resolve) => {
|
|
609
|
+
let done = false;
|
|
610
|
+
const finish = (): void => {
|
|
611
|
+
if (done) return;
|
|
612
|
+
done = true;
|
|
613
|
+
signal.removeEventListener("abort", finish);
|
|
614
|
+
resolve();
|
|
615
|
+
};
|
|
616
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
617
|
+
void run.then(finish, finish);
|
|
618
|
+
});
|
|
619
|
+
}
|
|
@@ -33,6 +33,10 @@ export class CompositeSubagentObserver implements SubagentManagerObserver {
|
|
|
33
33
|
this.dispatch((o) => o.onSubagentCompleted(record), "onSubagentCompleted");
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
onSubagentResumed(record: Subagent): void {
|
|
37
|
+
this.dispatch((o) => o.onSubagentResumed?.(record), "onSubagentResumed");
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
onSubagentCompacted(record: Subagent, info: CompactionInfo): void {
|
|
37
41
|
this.dispatch((o) => o.onSubagentCompacted(record, info), "onSubagentCompacted");
|
|
38
42
|
}
|