@caupulican/pi-adaptative 0.81.40 → 0.81.41
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 +25 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +1 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +22 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-paths.d.ts +6 -0
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +12 -0
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/agent-session-contracts.d.ts +293 -0
- package/dist/core/agent-session-contracts.d.ts.map +1 -0
- package/dist/core/agent-session-contracts.js +26 -0
- package/dist/core/agent-session-contracts.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +3 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +26 -405
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +86 -307
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +2 -1
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +3 -1
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +1 -0
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bedrock-sso-login.d.ts +16 -0
- package/dist/core/bedrock-sso-login.d.ts.map +1 -0
- package/dist/core/bedrock-sso-login.js +98 -0
- package/dist/core/bedrock-sso-login.js.map +1 -0
- package/dist/core/compaction-controller.d.ts.map +1 -1
- package/dist/core/compaction-controller.js +1 -1
- package/dist/core/compaction-controller.js.map +1 -1
- package/dist/core/compaction-support.d.ts +3 -8
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +3 -3
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/context-pipeline.d.ts +1 -1
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +7 -33
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/cost/cost-summary.d.ts.map +1 -1
- package/dist/core/cost/cost-summary.js +5 -3
- package/dist/core/cost/cost-summary.js.map +1 -1
- package/dist/core/cost/daily-usage.d.ts.map +1 -1
- package/dist/core/cost/daily-usage.js +4 -5
- package/dist/core/cost/daily-usage.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts.map +1 -1
- package/dist/core/default-tool-surface.js +1 -0
- package/dist/core/default-tool-surface.js.map +1 -1
- package/dist/core/extension-import-authority.d.ts +6 -0
- package/dist/core/extension-import-authority.d.ts.map +1 -0
- package/dist/core/extension-import-authority.js +10 -0
- package/dist/core/extension-import-authority.js.map +1 -0
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +2 -3
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +14 -5
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/stale-context.d.ts +2 -0
- package/dist/core/extensions/stale-context.d.ts.map +1 -0
- package/dist/core/extensions/stale-context.js +2 -0
- package/dist/core/extensions/stale-context.js.map +1 -0
- package/dist/core/extensions/types.d.ts +8 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/foreground-recovery-controller.d.ts +1 -0
- package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
- package/dist/core/foreground-recovery-controller.js +13 -13
- package/dist/core/foreground-recovery-controller.js.map +1 -1
- package/dist/core/goal-loop-controller.d.ts +14 -9
- package/dist/core/goal-loop-controller.d.ts.map +1 -1
- package/dist/core/goal-loop-controller.js +47 -75
- package/dist/core/goal-loop-controller.js.map +1 -1
- package/dist/core/goals/compact-goal-context.d.ts +11 -0
- package/dist/core/goals/compact-goal-context.d.ts.map +1 -0
- package/dist/core/goals/compact-goal-context.js +59 -0
- package/dist/core/goals/compact-goal-context.js.map +1 -0
- package/dist/core/goals/goal-acceptance.d.ts.map +1 -1
- package/dist/core/goals/goal-acceptance.js +2 -0
- package/dist/core/goals/goal-acceptance.js.map +1 -1
- package/dist/core/goals/goal-auto-continue-controller.d.ts +2 -1
- package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-auto-continue-controller.js +5 -8
- package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-controller.d.ts +2 -2
- package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +42 -11
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.d.ts +4 -29
- package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.js +4 -32
- package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts +8 -18
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +9 -179
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/goals/goal-lifecycle.d.ts +35 -12
- package/dist/core/goals/goal-lifecycle.d.ts.map +1 -1
- package/dist/core/goals/goal-lifecycle.js +98 -13
- package/dist/core/goals/goal-lifecycle.js.map +1 -1
- package/dist/core/goals/goal-record.d.ts +17 -0
- package/dist/core/goals/goal-record.d.ts.map +1 -0
- package/dist/core/goals/goal-record.js +31 -0
- package/dist/core/goals/goal-record.js.map +1 -0
- package/dist/core/goals/goal-session-controller.d.ts +42 -0
- package/dist/core/goals/goal-session-controller.d.ts.map +1 -0
- package/dist/core/goals/goal-session-controller.js +128 -0
- package/dist/core/goals/goal-session-controller.js.map +1 -0
- package/dist/core/goals/goal-state.d.ts +45 -25
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +85 -15
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +1 -4
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +49 -34
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/goals/session-goal-state.d.ts +18 -8
- package/dist/core/goals/session-goal-state.d.ts.map +1 -1
- package/dist/core/goals/session-goal-state.js +134 -11
- package/dist/core/goals/session-goal-state.js.map +1 -1
- package/dist/core/human-input-controller.d.ts +47 -0
- package/dist/core/human-input-controller.d.ts.map +1 -0
- package/dist/core/human-input-controller.js +181 -0
- package/dist/core/human-input-controller.js.map +1 -0
- package/dist/core/local-runtime-controller.d.ts +1 -1
- package/dist/core/local-runtime-controller.d.ts.map +1 -1
- package/dist/core/local-runtime-controller.js.map +1 -1
- package/dist/core/model-capability.d.ts +0 -19
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +0 -19
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-registry.d.ts +6 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +13 -5
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +16 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +32 -16
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +1 -1
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/model-selection-controller.d.ts +1 -1
- package/dist/core/model-selection-controller.d.ts.map +1 -1
- package/dist/core/model-selection-controller.js.map +1 -1
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
- package/dist/core/orchestration/delegation-ledger.js +3 -0
- package/dist/core/orchestration/delegation-ledger.js.map +1 -1
- package/dist/core/orchestration/work-state-projection.d.ts.map +1 -1
- package/dist/core/orchestration/work-state-projection.js +1 -1
- package/dist/core/orchestration/work-state-projection.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +20 -3
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/profile-filter-controller.d.ts +8 -1
- package/dist/core/profile-filter-controller.d.ts.map +1 -1
- package/dist/core/profile-filter-controller.js +23 -5
- package/dist/core/profile-filter-controller.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +1 -0
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +18 -29
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +1 -1
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +3 -1
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/request-auth.d.ts +7 -0
- package/dist/core/request-auth.d.ts.map +1 -0
- package/dist/core/request-auth.js +8 -0
- package/dist/core/request-auth.js.map +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +16 -6
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/risk-classifier.d.ts.map +1 -1
- package/dist/core/risk-classifier.js +8 -0
- package/dist/core/risk-classifier.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +15 -3
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +132 -79
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +7 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.d.ts +9 -0
- package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -0
- package/dist/core/secrets/credential-exposure-guard.js +241 -0
- package/dist/core/secrets/credential-exposure-guard.js.map +1 -0
- package/dist/core/secrets/secret-dotenv.d.ts +27 -0
- package/dist/core/secrets/secret-dotenv.d.ts.map +1 -0
- package/dist/core/secrets/secret-dotenv.js +183 -0
- package/dist/core/secrets/secret-dotenv.js.map +1 -0
- package/dist/core/secrets/secret-vault-format.d.ts +112 -0
- package/dist/core/secrets/secret-vault-format.d.ts.map +1 -0
- package/dist/core/secrets/secret-vault-format.js +397 -0
- package/dist/core/secrets/secret-vault-format.js.map +1 -0
- package/dist/core/secrets/secret-vault.d.ts +72 -0
- package/dist/core/secrets/secret-vault.d.ts.map +1 -0
- package/dist/core/secrets/secret-vault.js +671 -0
- package/dist/core/secrets/secret-vault.js.map +1 -0
- package/dist/core/session-analytics.d.ts +4 -23
- package/dist/core/session-analytics.d.ts.map +1 -1
- package/dist/core/session-analytics.js +39 -69
- package/dist/core/session-analytics.js.map +1 -1
- package/dist/core/session-role.d.ts +5 -0
- package/dist/core/session-role.d.ts.map +1 -1
- package/dist/core/session-role.js +14 -1
- package/dist/core/session-role.js.map +1 -1
- package/dist/core/session-tree-navigator.d.ts +4 -6
- package/dist/core/session-tree-navigator.d.ts.map +1 -1
- package/dist/core/session-tree-navigator.js +4 -1
- package/dist/core/session-tree-navigator.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tool-capability-policy.d.ts.map +1 -1
- package/dist/core/tool-capability-policy.js +1 -0
- package/dist/core/tool-capability-policy.js.map +1 -1
- package/dist/core/tool-gate-controller.d.ts.map +1 -1
- package/dist/core/tool-gate-controller.js +8 -2
- package/dist/core/tool-gate-controller.js.map +1 -1
- package/dist/core/tool-protocol-controller.d.ts +2 -4
- package/dist/core/tool-protocol-controller.d.ts.map +1 -1
- package/dist/core/tool-protocol-controller.js.map +1 -1
- package/dist/core/tool-recovery-stats.d.ts +21 -1
- package/dist/core/tool-recovery-stats.d.ts.map +1 -1
- package/dist/core/tool-recovery-stats.js.map +1 -1
- package/dist/core/tools/bash.d.ts +5 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +59 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +6 -7
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +6 -8
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/git-filter.d.ts +1 -8
- package/dist/core/tools/git-filter.d.ts.map +1 -1
- package/dist/core/tools/git-filter.js +2 -63
- package/dist/core/tools/git-filter.js.map +1 -1
- package/dist/core/tools/goal.d.ts +5 -3
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +97 -19
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/orchestration-panel.d.ts +1 -4
- package/dist/core/tools/orchestration-panel.d.ts.map +1 -1
- package/dist/core/tools/orchestration-panel.js +0 -44
- package/dist/core/tools/orchestration-panel.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +11 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +36 -3
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/python.d.ts +2 -0
- package/dist/core/tools/python.d.ts.map +1 -1
- package/dist/core/tools/python.js +3 -1
- package/dist/core/tools/python.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/search-command-guard.d.ts +14 -0
- package/dist/core/tools/search-command-guard.d.ts.map +1 -0
- package/dist/core/tools/search-command-guard.js +333 -0
- package/dist/core/tools/search-command-guard.js.map +1 -0
- package/dist/core/tools/secret-store.d.ts +38 -0
- package/dist/core/tools/secret-store.d.ts.map +1 -0
- package/dist/core/tools/secret-store.js +412 -0
- package/dist/core/tools/secret-store.js.map +1 -0
- package/dist/core/tools/shell-command-parser.d.ts +16 -0
- package/dist/core/tools/shell-command-parser.d.ts.map +1 -0
- package/dist/core/tools/shell-command-parser.js +113 -0
- package/dist/core/tools/shell-command-parser.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +4 -7
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/util/atomic-file.d.ts +6 -2
- package/dist/core/util/atomic-file.d.ts.map +1 -1
- package/dist/core/util/atomic-file.js +4 -4
- package/dist/core/util/atomic-file.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +25 -11
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.d.ts +4 -0
- package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.js +60 -1
- package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
- package/dist/modes/interactive/components/activity-lane.d.ts +55 -0
- package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -0
- package/dist/modes/interactive/components/activity-lane.js +304 -0
- package/dist/modes/interactive/components/activity-lane.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +19 -10
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/expandable-text.d.ts +12 -0
- package/dist/modes/interactive/components/expandable-text.d.ts.map +1 -0
- package/dist/modes/interactive/components/expandable-text.js +17 -0
- package/dist/modes/interactive/components/expandable-text.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +15 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +30 -21
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +6 -1
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +23 -14
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +4 -4
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +19 -11
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +53 -20
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-group.js +3 -0
- package/dist/modes/interactive/components/tool-group.js.map +1 -1
- package/dist/modes/interactive/components/usage-action-selector.d.ts +16 -0
- package/dist/modes/interactive/components/usage-action-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/usage-action-selector.js +46 -0
- package/dist/modes/interactive/components/usage-action-selector.js.map +1 -0
- package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
- package/dist/modes/interactive/extension-ui-host.js +1 -0
- package/dist/modes/interactive/extension-ui-host.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts +15 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +27 -19
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/history-reload-math.d.ts +6 -2
- package/dist/modes/interactive/history-reload-math.d.ts.map +1 -1
- package/dist/modes/interactive/history-reload-math.js +10 -5
- package/dist/modes/interactive/history-reload-math.js.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts +74 -0
- package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-event-controller.js +328 -0
- package/dist/modes/interactive/interactive-event-controller.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +20 -25
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +196 -608
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/loaded-resources-view.d.ts +20 -0
- package/dist/modes/interactive/loaded-resources-view.d.ts.map +1 -0
- package/dist/modes/interactive/loaded-resources-view.js +147 -0
- package/dist/modes/interactive/loaded-resources-view.js.map +1 -0
- package/dist/modes/interactive/resource-display.d.ts.map +1 -1
- package/dist/modes/interactive/resource-display.js +8 -2
- package/dist/modes/interactive/resource-display.js.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.d.ts +1 -5
- package/dist/modes/interactive/resource-shell-commands.d.ts.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.js +0 -5
- package/dist/modes/interactive/resource-shell-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts +10 -6
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +123 -70
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/modes/interactive/usage-commands.d.ts +34 -0
- package/dist/modes/interactive/usage-commands.d.ts.map +1 -0
- package/dist/modes/interactive/usage-commands.js +225 -0
- package/dist/modes/interactive/usage-commands.js.map +1 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +5 -3
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +3 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/UI_POLISH_ORCHESTRATION.md +25 -31
- package/docs/UI_QUALITY_ORCHESTRATION.md +22 -24
- package/docs/providers.md +9 -1
- package/docs/ui-reference/README.md +6 -6
- package/docs/ui-reference/orchestration-after.txt +14 -7
- package/docs/ui-reference/orchestration-before.txt +12 -6
- package/docs/ui-reference/orchestration-reference.txt +15 -5
- package/docs/usage.md +9 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +13 -13
- package/package.json +4 -4
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Agent, ThinkingLevel } from "@caupulican/pi-agent-core";
|
|
11
11
|
import type { SessionManager } from "@caupulican/pi-agent-core/node";
|
|
12
|
+
import { type ExtensionImportAuthority } from "./extension-import-authority.ts";
|
|
12
13
|
import type { Extension } from "./extensions/index.ts";
|
|
13
14
|
import type { ModelRegistry } from "./model-registry.ts";
|
|
14
15
|
import type { ResourceLoader } from "./resource-loader.ts";
|
|
@@ -61,7 +62,13 @@ export declare class ProfileFilterController {
|
|
|
61
62
|
deriveToolProfileFilter(): Required<ResourceProfileFilterSettings>;
|
|
62
63
|
isToolOrCommandAllowedByProfile(name: string): boolean;
|
|
63
64
|
private _hasToolOrCommandProfileGate;
|
|
64
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Decide whether a path has authority to cross the import boundary. Explicit owner loads may
|
|
67
|
+
* proceed without a profile, while discovery-driven loads require an active profile grant.
|
|
68
|
+
* User disables remain a hard stop for both paths.
|
|
69
|
+
*/
|
|
70
|
+
isExtensionPathAllowed(extensionPath: string, authority: ExtensionImportAuthority, baseDir?: string): boolean;
|
|
71
|
+
filterExtensionsForRuntime(extensions: Extension[], explicitLiveExtensionPaths?: ReadonlySet<string>): Extension[];
|
|
65
72
|
/**
|
|
66
73
|
* /context observations for skills/prompts/extensions the active resource profile removed from
|
|
67
74
|
* listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-filter-controller.d.ts","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAEN,KAAK,6BAA6B,EAClC,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,2BAA2B;IAC3C,kBAAkB,IAAI,eAAe,CAAC;IACtC,iBAAiB,IAAI,cAAc,CAAC;IACpC,gBAAgB,IAAI,aAAa,CAAC;IAClC,MAAM,IAAI,MAAM,CAAC;IACjB,QAAQ,IAAI,KAAK,CAAC;IAClB,iBAAiB,IAAI,cAAc,CAAC;IACpC,yEAAyE;IACzE,mBAAmB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAC/C,0CAA0C;IAC1C,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAChD,kFAAkF;IAClF,oBAAoB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,GAAG,SAAS,CAAC;IAC5E,0FAA0F;IAC1F,eAAe,IAAI,OAAO,CAAC;IAC3B,qEAAqE;IACrE,kBAAkB,IAAI,OAAO,CAAC;IAC9B,6DAA6D;IAC7D,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,2BAA2B;IAC3C,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,2BAA2B,EAAE,MAAM,CAAC;IACpC,sBAAsB,EAAE,OAAO,CAAC;IAChC,yBAAyB,EAAE,OAAO,CAAC;CACnC;AAED,qBAAa,uBAAuB;IACnC,qFAAqF;IACrF,OAAO,CAAC,uBAAuB,CAAgB;IAC/C,kGAAkG;IAClG,OAAO,CAAC,4BAA4B,CAAK;IACzC,2GAA2G;IAC3G,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,0BAA0B,CAAS;IAE3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA8B;IAEnD,YAAY,IAAI,EAAE,2BAA2B,EAE5C;IAED,4FAA4F;IAC5F,yBAAyB,IAAI,MAAM,EAAE,CAEpC;IAED,oBAAoB,IAAI,2BAA2B,CAOlD;IAED,qBAAqB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,IAAI,CAKjE;IAED;;;;OAIG;IACH,uBAAuB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,CAGjE;IAED,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUrD;IAED,OAAO,CAAC,4BAA4B;IASpC,0BAA0B,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAuC/D;IAED;;;;;;;;;;;;;OAaG;IACH,iCAAiC,IAAI,MAAM,EAAE,CA0B5C;IAED;;;;;OAKG;IACG,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DvD;CACD","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport interface ProfileFilterReloadSnapshot {\n\tinertExtensionWarnings: string[];\n\tprofileDeniedExtensionCount: number;\n\tprofileControlledModel: boolean;\n\tprofileControlledThinking: boolean;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\t/** Whether the last applied profile generation, rather than the base session selection, owns the field. */\n\tprivate _profileControlledModel = false;\n\tprivate _profileControlledThinking = false;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\tcreateReloadSnapshot(): ProfileFilterReloadSnapshot {\n\t\treturn {\n\t\t\tinertExtensionWarnings: [...this._inertExtensionWarnings],\n\t\t\tprofileDeniedExtensionCount: this._profileDeniedExtensionCount,\n\t\t\tprofileControlledModel: this._profileControlledModel,\n\t\t\tprofileControlledThinking: this._profileControlledThinking,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: ProfileFilterReloadSnapshot): void {\n\t\tthis._inertExtensionWarnings = [...snapshot.inertExtensionWarnings];\n\t\tthis._profileDeniedExtensionCount = snapshot.profileDeniedExtensionCount;\n\t\tthis._profileControlledModel = snapshot.profileControlledModel;\n\t\tthis._profileControlledThinking = snapshot.profileControlledThinking;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[]): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: only inline/SDK extensions load by default. That is the baseline, not\n\t\t\t// a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => extension.sourceInfo.source === \"inline\");\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tsettingsManager.isResourceAllowedByProfile(\"extensions\", extension.path, extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tconst profileSettings =\n\t\t\tactiveProfileNames.length > 0\n\t\t\t\t? resolveProfileModelSettings({\n\t\t\t\t\t\tactiveProfileNames,\n\t\t\t\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\t\t})\n\t\t\t\t: {};\n\t\tif (profileSettings.error) {\n\t\t\tthrow new Error(`Profile model resolution error: ${profileSettings.error}`);\n\t\t}\n\t\tconst mustRestoreBaseModel =\n\t\t\t!this.deps.isExplicitModel() && this._profileControlledModel && !profileSettings.model;\n\t\tconst mustRestoreBaseThinking =\n\t\t\t!this.deps.isExplicitThinking() && this._profileControlledThinking && !profileSettings.thinkingLevel;\n\t\tconst baseSelection =\n\t\t\tmustRestoreBaseModel || mustRestoreBaseThinking\n\t\t\t\t? await findInitialModel({\n\t\t\t\t\t\tscopedModels: [],\n\t\t\t\t\t\tisContinuing: true,\n\t\t\t\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\t\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\t\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t\tlet modelChanged = false;\n\t\tconst nextModel = profileSettings.model ?? (mustRestoreBaseModel ? baseSelection?.model : undefined);\n\t\tif (!this.deps.isExplicitModel() && nextModel) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = nextModel;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t\tmodelChanged = true;\n\t\t\t}\n\t\t}\n\t\tthis._profileControlledModel = !this.deps.isExplicitModel() && Boolean(profileSettings.model);\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t} else if (mustRestoreBaseThinking && baseSelection) {\n\t\t\tthis.deps.setThinkingLevel(baseSelection.thinkingLevel);\n\t\t} else if (modelChanged) {\n\t\t\t// A model-only situation switch still has to clamp the inherited/explicit level. This\n\t\t\t// prevents Sol/Terra Ultra from leaking into Luna or another model that stops at Max.\n\t\t\tthis.deps.setThinkingLevel(this.deps.getAgent().state.thinkingLevel);\n\t\t}\n\t\tthis._profileControlledThinking = !this.deps.isExplicitThinking() && Boolean(profileSettings.thinkingLevel);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"profile-filter-controller.d.ts","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,KAAK,wBAAwB,EAAmC,MAAM,iCAAiC,CAAC;AACjH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAEN,KAAK,6BAA6B,EAClC,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,2BAA2B;IAC3C,kBAAkB,IAAI,eAAe,CAAC;IACtC,iBAAiB,IAAI,cAAc,CAAC;IACpC,gBAAgB,IAAI,aAAa,CAAC;IAClC,MAAM,IAAI,MAAM,CAAC;IACjB,QAAQ,IAAI,KAAK,CAAC;IAClB,iBAAiB,IAAI,cAAc,CAAC;IACpC,yEAAyE;IACzE,mBAAmB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAC/C,0CAA0C;IAC1C,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAChD,kFAAkF;IAClF,oBAAoB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,GAAG,SAAS,CAAC;IAC5E,0FAA0F;IAC1F,eAAe,IAAI,OAAO,CAAC;IAC3B,qEAAqE;IACrE,kBAAkB,IAAI,OAAO,CAAC;IAC9B,6DAA6D;IAC7D,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,2BAA2B;IAC3C,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,2BAA2B,EAAE,MAAM,CAAC;IACpC,sBAAsB,EAAE,OAAO,CAAC;IAChC,yBAAyB,EAAE,OAAO,CAAC;CACnC;AAED,qBAAa,uBAAuB;IACnC,qFAAqF;IACrF,OAAO,CAAC,uBAAuB,CAAgB;IAC/C,kGAAkG;IAClG,OAAO,CAAC,4BAA4B,CAAK;IACzC,2GAA2G;IAC3G,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,0BAA0B,CAAS;IAE3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA8B;IAEnD,YAAY,IAAI,EAAE,2BAA2B,EAE5C;IAED,4FAA4F;IAC5F,yBAAyB,IAAI,MAAM,EAAE,CAEpC;IAED,oBAAoB,IAAI,2BAA2B,CAOlD;IAED,qBAAqB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,IAAI,CAKjE;IAED;;;;OAIG;IACH,uBAAuB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,CAGjE;IAED,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUrD;IAED,OAAO,CAAC,4BAA4B;IASpC;;;;OAIG;IACH,sBAAsB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,wBAAwB,EAAE,OAAO,SAAK,GAAG,OAAO,CAExG;IAED,0BAA0B,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,EAAE,CAkDjH;IAED;;;;;;;;;;;;;OAaG;IACH,iCAAiC,IAAI,MAAM,EAAE,CA0B5C;IAED;;;;;OAKG;IACG,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DvD;CACD","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { type ExtensionImportAuthority, isExtensionPathAllowedForImport } from \"./extension-import-authority.ts\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport interface ProfileFilterReloadSnapshot {\n\tinertExtensionWarnings: string[];\n\tprofileDeniedExtensionCount: number;\n\tprofileControlledModel: boolean;\n\tprofileControlledThinking: boolean;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\t/** Whether the last applied profile generation, rather than the base session selection, owns the field. */\n\tprivate _profileControlledModel = false;\n\tprivate _profileControlledThinking = false;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\tcreateReloadSnapshot(): ProfileFilterReloadSnapshot {\n\t\treturn {\n\t\t\tinertExtensionWarnings: [...this._inertExtensionWarnings],\n\t\t\tprofileDeniedExtensionCount: this._profileDeniedExtensionCount,\n\t\t\tprofileControlledModel: this._profileControlledModel,\n\t\t\tprofileControlledThinking: this._profileControlledThinking,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: ProfileFilterReloadSnapshot): void {\n\t\tthis._inertExtensionWarnings = [...snapshot.inertExtensionWarnings];\n\t\tthis._profileDeniedExtensionCount = snapshot.profileDeniedExtensionCount;\n\t\tthis._profileControlledModel = snapshot.profileControlledModel;\n\t\tthis._profileControlledThinking = snapshot.profileControlledThinking;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\t/**\n\t * Decide whether a path has authority to cross the import boundary. Explicit owner loads may\n\t * proceed without a profile, while discovery-driven loads require an active profile grant.\n\t * User disables remain a hard stop for both paths.\n\t */\n\tisExtensionPathAllowed(extensionPath: string, authority: ExtensionImportAuthority, baseDir = \"\"): boolean {\n\t\treturn isExtensionPathAllowedForImport(this.deps.getSettingsManager(), extensionPath, authority, baseDir);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[], explicitLiveExtensionPaths?: ReadonlySet<string>): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: inline/SDK extensions and exact owner-approved CLI/live loads are the\n\t\t\t// only runtime authorities. Discovery alone must never promote an extension into executable code.\n\t\t\t// This baseline is not a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => {\n\t\t\t\tif (extension.sourceInfo.source === \"inline\") return true;\n\t\t\t\tconst explicitlyApproved =\n\t\t\t\t\textension.sourceInfo.source === \"cli\" ||\n\t\t\t\t\texplicitLiveExtensionPaths?.has(extension.path) === true ||\n\t\t\t\t\texplicitLiveExtensionPaths?.has(extension.resolvedPath) === true;\n\t\t\t\treturn (\n\t\t\t\t\texplicitlyApproved &&\n\t\t\t\t\tthis.isExtensionPathAllowed(extension.path, \"explicit\", extension.sourceInfo.baseDir)\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tthis.isExtensionPathAllowed(extension.path, \"profile\", extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tconst profileSettings =\n\t\t\tactiveProfileNames.length > 0\n\t\t\t\t? resolveProfileModelSettings({\n\t\t\t\t\t\tactiveProfileNames,\n\t\t\t\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\t\t})\n\t\t\t\t: {};\n\t\tif (profileSettings.error) {\n\t\t\tthrow new Error(`Profile model resolution error: ${profileSettings.error}`);\n\t\t}\n\t\tconst mustRestoreBaseModel =\n\t\t\t!this.deps.isExplicitModel() && this._profileControlledModel && !profileSettings.model;\n\t\tconst mustRestoreBaseThinking =\n\t\t\t!this.deps.isExplicitThinking() && this._profileControlledThinking && !profileSettings.thinkingLevel;\n\t\tconst baseSelection =\n\t\t\tmustRestoreBaseModel || mustRestoreBaseThinking\n\t\t\t\t? await findInitialModel({\n\t\t\t\t\t\tscopedModels: [],\n\t\t\t\t\t\tisContinuing: true,\n\t\t\t\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\t\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\t\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t\tlet modelChanged = false;\n\t\tconst nextModel = profileSettings.model ?? (mustRestoreBaseModel ? baseSelection?.model : undefined);\n\t\tif (!this.deps.isExplicitModel() && nextModel) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = nextModel;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t\tmodelChanged = true;\n\t\t\t}\n\t\t}\n\t\tthis._profileControlledModel = !this.deps.isExplicitModel() && Boolean(profileSettings.model);\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t} else if (mustRestoreBaseThinking && baseSelection) {\n\t\t\tthis.deps.setThinkingLevel(baseSelection.thinkingLevel);\n\t\t} else if (modelChanged) {\n\t\t\t// A model-only situation switch still has to clamp the inherited/explicit level. This\n\t\t\t// prevents Sol/Terra Ultra from leaking into Luna or another model that stops at Max.\n\t\t\tthis.deps.setThinkingLevel(this.deps.getAgent().state.thinkingLevel);\n\t\t}\n\t\tthis._profileControlledThinking = !this.deps.isExplicitThinking() && Boolean(profileSettings.thinkingLevel);\n\t}\n}\n"]}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the
|
|
8
8
|
* inert-extension warnings and profile-denied-extension count; everything else is read through deps.
|
|
9
9
|
*/
|
|
10
|
+
import { isExtensionPathAllowedForImport } from "./extension-import-authority.js";
|
|
10
11
|
import { findInitialModel, resolveProfileModelSettings } from "./model-resolver.js";
|
|
11
12
|
import { matchesResourceProfilePattern, } from "./settings-manager.js";
|
|
12
13
|
export class ProfileFilterController {
|
|
@@ -70,7 +71,15 @@ export class ProfileFilterController {
|
|
|
70
71
|
this.deps.getExcludedToolNames() ||
|
|
71
72
|
(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)));
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Decide whether a path has authority to cross the import boundary. Explicit owner loads may
|
|
76
|
+
* proceed without a profile, while discovery-driven loads require an active profile grant.
|
|
77
|
+
* User disables remain a hard stop for both paths.
|
|
78
|
+
*/
|
|
79
|
+
isExtensionPathAllowed(extensionPath, authority, baseDir = "") {
|
|
80
|
+
return isExtensionPathAllowedForImport(this.deps.getSettingsManager(), extensionPath, authority, baseDir);
|
|
81
|
+
}
|
|
82
|
+
filterExtensionsForRuntime(extensions, explicitLiveExtensionPaths) {
|
|
74
83
|
const settingsManager = this.deps.getSettingsManager();
|
|
75
84
|
this._inertExtensionWarnings = [];
|
|
76
85
|
this._profileDeniedExtensionCount = 0;
|
|
@@ -81,12 +90,21 @@ export class ProfileFilterController {
|
|
|
81
90
|
this._profileDeniedExtensionCount = extensions.length;
|
|
82
91
|
return [];
|
|
83
92
|
}
|
|
84
|
-
// No profile in play:
|
|
85
|
-
//
|
|
86
|
-
|
|
93
|
+
// No profile in play: inline/SDK extensions and exact owner-approved CLI/live loads are the
|
|
94
|
+
// only runtime authorities. Discovery alone must never promote an extension into executable code.
|
|
95
|
+
// This baseline is not a profile denial, so it is not counted as withheld.
|
|
96
|
+
return extensions.filter((extension) => {
|
|
97
|
+
if (extension.sourceInfo.source === "inline")
|
|
98
|
+
return true;
|
|
99
|
+
const explicitlyApproved = extension.sourceInfo.source === "cli" ||
|
|
100
|
+
explicitLiveExtensionPaths?.has(extension.path) === true ||
|
|
101
|
+
explicitLiveExtensionPaths?.has(extension.resolvedPath) === true;
|
|
102
|
+
return (explicitlyApproved &&
|
|
103
|
+
this.isExtensionPathAllowed(extension.path, "explicit", extension.sourceInfo.baseDir));
|
|
104
|
+
});
|
|
87
105
|
}
|
|
88
106
|
const hasToolOrCommandGate = this._hasToolOrCommandProfileGate();
|
|
89
|
-
const allowedExtensions = extensions.filter((extension) =>
|
|
107
|
+
const allowedExtensions = extensions.filter((extension) => this.isExtensionPathAllowed(extension.path, "profile", extension.sourceInfo.baseDir));
|
|
90
108
|
this._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;
|
|
91
109
|
return allowedExtensions.map((extension) => {
|
|
92
110
|
if (!hasToolOrCommandGate)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-filter-controller.js","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport interface ProfileFilterReloadSnapshot {\n\tinertExtensionWarnings: string[];\n\tprofileDeniedExtensionCount: number;\n\tprofileControlledModel: boolean;\n\tprofileControlledThinking: boolean;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\t/** Whether the last applied profile generation, rather than the base session selection, owns the field. */\n\tprivate _profileControlledModel = false;\n\tprivate _profileControlledThinking = false;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\tcreateReloadSnapshot(): ProfileFilterReloadSnapshot {\n\t\treturn {\n\t\t\tinertExtensionWarnings: [...this._inertExtensionWarnings],\n\t\t\tprofileDeniedExtensionCount: this._profileDeniedExtensionCount,\n\t\t\tprofileControlledModel: this._profileControlledModel,\n\t\t\tprofileControlledThinking: this._profileControlledThinking,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: ProfileFilterReloadSnapshot): void {\n\t\tthis._inertExtensionWarnings = [...snapshot.inertExtensionWarnings];\n\t\tthis._profileDeniedExtensionCount = snapshot.profileDeniedExtensionCount;\n\t\tthis._profileControlledModel = snapshot.profileControlledModel;\n\t\tthis._profileControlledThinking = snapshot.profileControlledThinking;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[]): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: only inline/SDK extensions load by default. That is the baseline, not\n\t\t\t// a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => extension.sourceInfo.source === \"inline\");\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tsettingsManager.isResourceAllowedByProfile(\"extensions\", extension.path, extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tconst profileSettings =\n\t\t\tactiveProfileNames.length > 0\n\t\t\t\t? resolveProfileModelSettings({\n\t\t\t\t\t\tactiveProfileNames,\n\t\t\t\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\t\t})\n\t\t\t\t: {};\n\t\tif (profileSettings.error) {\n\t\t\tthrow new Error(`Profile model resolution error: ${profileSettings.error}`);\n\t\t}\n\t\tconst mustRestoreBaseModel =\n\t\t\t!this.deps.isExplicitModel() && this._profileControlledModel && !profileSettings.model;\n\t\tconst mustRestoreBaseThinking =\n\t\t\t!this.deps.isExplicitThinking() && this._profileControlledThinking && !profileSettings.thinkingLevel;\n\t\tconst baseSelection =\n\t\t\tmustRestoreBaseModel || mustRestoreBaseThinking\n\t\t\t\t? await findInitialModel({\n\t\t\t\t\t\tscopedModels: [],\n\t\t\t\t\t\tisContinuing: true,\n\t\t\t\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\t\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\t\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t\tlet modelChanged = false;\n\t\tconst nextModel = profileSettings.model ?? (mustRestoreBaseModel ? baseSelection?.model : undefined);\n\t\tif (!this.deps.isExplicitModel() && nextModel) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = nextModel;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t\tmodelChanged = true;\n\t\t\t}\n\t\t}\n\t\tthis._profileControlledModel = !this.deps.isExplicitModel() && Boolean(profileSettings.model);\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t} else if (mustRestoreBaseThinking && baseSelection) {\n\t\t\tthis.deps.setThinkingLevel(baseSelection.thinkingLevel);\n\t\t} else if (modelChanged) {\n\t\t\t// A model-only situation switch still has to clamp the inherited/explicit level. This\n\t\t\t// prevents Sol/Terra Ultra from leaking into Luna or another model that stops at Max.\n\t\t\tthis.deps.setThinkingLevel(this.deps.getAgent().state.thinkingLevel);\n\t\t}\n\t\tthis._profileControlledThinking = !this.deps.isExplicitThinking() && Boolean(profileSettings.thinkingLevel);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"profile-filter-controller.js","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { type ExtensionImportAuthority, isExtensionPathAllowedForImport } from \"./extension-import-authority.ts\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport interface ProfileFilterReloadSnapshot {\n\tinertExtensionWarnings: string[];\n\tprofileDeniedExtensionCount: number;\n\tprofileControlledModel: boolean;\n\tprofileControlledThinking: boolean;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\t/** Whether the last applied profile generation, rather than the base session selection, owns the field. */\n\tprivate _profileControlledModel = false;\n\tprivate _profileControlledThinking = false;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\tcreateReloadSnapshot(): ProfileFilterReloadSnapshot {\n\t\treturn {\n\t\t\tinertExtensionWarnings: [...this._inertExtensionWarnings],\n\t\t\tprofileDeniedExtensionCount: this._profileDeniedExtensionCount,\n\t\t\tprofileControlledModel: this._profileControlledModel,\n\t\t\tprofileControlledThinking: this._profileControlledThinking,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: ProfileFilterReloadSnapshot): void {\n\t\tthis._inertExtensionWarnings = [...snapshot.inertExtensionWarnings];\n\t\tthis._profileDeniedExtensionCount = snapshot.profileDeniedExtensionCount;\n\t\tthis._profileControlledModel = snapshot.profileControlledModel;\n\t\tthis._profileControlledThinking = snapshot.profileControlledThinking;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\t/**\n\t * Decide whether a path has authority to cross the import boundary. Explicit owner loads may\n\t * proceed without a profile, while discovery-driven loads require an active profile grant.\n\t * User disables remain a hard stop for both paths.\n\t */\n\tisExtensionPathAllowed(extensionPath: string, authority: ExtensionImportAuthority, baseDir = \"\"): boolean {\n\t\treturn isExtensionPathAllowedForImport(this.deps.getSettingsManager(), extensionPath, authority, baseDir);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[], explicitLiveExtensionPaths?: ReadonlySet<string>): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: inline/SDK extensions and exact owner-approved CLI/live loads are the\n\t\t\t// only runtime authorities. Discovery alone must never promote an extension into executable code.\n\t\t\t// This baseline is not a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => {\n\t\t\t\tif (extension.sourceInfo.source === \"inline\") return true;\n\t\t\t\tconst explicitlyApproved =\n\t\t\t\t\textension.sourceInfo.source === \"cli\" ||\n\t\t\t\t\texplicitLiveExtensionPaths?.has(extension.path) === true ||\n\t\t\t\t\texplicitLiveExtensionPaths?.has(extension.resolvedPath) === true;\n\t\t\t\treturn (\n\t\t\t\t\texplicitlyApproved &&\n\t\t\t\t\tthis.isExtensionPathAllowed(extension.path, \"explicit\", extension.sourceInfo.baseDir)\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tthis.isExtensionPathAllowed(extension.path, \"profile\", extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tconst profileSettings =\n\t\t\tactiveProfileNames.length > 0\n\t\t\t\t? resolveProfileModelSettings({\n\t\t\t\t\t\tactiveProfileNames,\n\t\t\t\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\t\t})\n\t\t\t\t: {};\n\t\tif (profileSettings.error) {\n\t\t\tthrow new Error(`Profile model resolution error: ${profileSettings.error}`);\n\t\t}\n\t\tconst mustRestoreBaseModel =\n\t\t\t!this.deps.isExplicitModel() && this._profileControlledModel && !profileSettings.model;\n\t\tconst mustRestoreBaseThinking =\n\t\t\t!this.deps.isExplicitThinking() && this._profileControlledThinking && !profileSettings.thinkingLevel;\n\t\tconst baseSelection =\n\t\t\tmustRestoreBaseModel || mustRestoreBaseThinking\n\t\t\t\t? await findInitialModel({\n\t\t\t\t\t\tscopedModels: [],\n\t\t\t\t\t\tisContinuing: true,\n\t\t\t\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\t\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\t\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t\tlet modelChanged = false;\n\t\tconst nextModel = profileSettings.model ?? (mustRestoreBaseModel ? baseSelection?.model : undefined);\n\t\tif (!this.deps.isExplicitModel() && nextModel) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = nextModel;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t\tmodelChanged = true;\n\t\t\t}\n\t\t}\n\t\tthis._profileControlledModel = !this.deps.isExplicitModel() && Boolean(profileSettings.model);\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t} else if (mustRestoreBaseThinking && baseSelection) {\n\t\t\tthis.deps.setThinkingLevel(baseSelection.thinkingLevel);\n\t\t} else if (modelChanged) {\n\t\t\t// A model-only situation switch still has to clamp the inherited/explicit level. This\n\t\t\t// prevents Sol/Terra Ultra from leaking into Luna or another model that stops at Max.\n\t\t\tthis.deps.setThinkingLevel(this.deps.getAgent().state.thinkingLevel);\n\t\t}\n\t\tthis._profileControlledThinking = !this.deps.isExplicitThinking() && Boolean(profileSettings.thinkingLevel);\n\t}\n}\n"]}
|
|
@@ -21,6 +21,7 @@ export declare function parseCommandArgs(argsString: string): string[];
|
|
|
21
21
|
* - $1, $2, ... for positional args
|
|
22
22
|
* - $@ and $ARGUMENTS for all parsed args joined with spaces
|
|
23
23
|
* - $ARGUMENTS_RAW and $RAW_ARGUMENTS for the raw tail after the template name
|
|
24
|
+
* - ${N:-default}, ${@:-default}, and ${ARGUMENTS:-default} for fallback values
|
|
24
25
|
* - ${@:N} for args from Nth onwards (bash-style slicing)
|
|
25
26
|
* - ${@:N:L} for L args starting from Nth
|
|
26
27
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-templates.d.ts","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"AAMA,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B7D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAwCxF;AAoFD,MAAM,WAAW,0BAA0B;IAC1C,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;IACzB,iFAAiF;IACjF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,cAAc,EAAE,CAyEzF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,MAAM,CAgBtF","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { stripResourceProfileBlocks } from \"./resource-profile-blocks.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all parsed args joined with spaces\n * - $ARGUMENTS_RAW and $RAW_ARGUMENTS for the raw tail after the template name\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[], rawArgs?: string): string {\n\tlet result = content;\n\n\t// Replace $1, $2, etc. with positional args FIRST (before wildcards)\n\t// This prevents wildcard replacement values containing $<digit> patterns from being re-substituted\n\tresult = result.replace(/\\$(\\d+)/g, (_, num) => {\n\t\tconst index = parseInt(num, 10) - 1;\n\t\treturn args[index] ?? \"\";\n\t});\n\n\t// Replace ${@:start} or ${@:start:length} with sliced args (bash-style)\n\t// Process BEFORE simple $@ to avoid conflicts\n\tresult = result.replace(/\\$\\{@:(\\d+)(?::(\\d+))?\\}/g, (_, startStr, lengthStr) => {\n\t\tlet start = parseInt(startStr, 10) - 1; // Convert to 0-indexed (user provides 1-indexed)\n\t\t// Treat 0 as 1 (bash convention: args start at 1)\n\t\tif (start < 0) start = 0;\n\n\t\tif (lengthStr) {\n\t\t\tconst length = parseInt(lengthStr, 10);\n\t\t\treturn args.slice(start, start + length).join(\" \");\n\t\t}\n\t\treturn args.slice(start).join(\" \");\n\t});\n\n\t// Pre-compute all args joined (optimization)\n\tconst allArgs = args.join(\" \");\n\n\tconst rawArgumentText = rawArgs ?? allArgs;\n\n\t// Replace raw-argument aliases before $ARGUMENTS so the shared prefix does not partially match.\n\tresult = result.replace(/\\$ARGUMENTS_RAW/g, rawArgumentText);\n\tresult = result.replace(/\\$RAW_ARGUMENTS/g, rawArgumentText);\n\n\t// Replace $ARGUMENTS with all args joined (new syntax, aligns with Claude, Codex, OpenCode)\n\tresult = result.replace(/\\$ARGUMENTS/g, allArgs);\n\n\t// Replace $@ with all args joined (existing syntax)\n\tresult = result.replace(/\\$@/g, allArgs);\n\n\treturn result;\n}\n\nfunction loadTemplateFromFile(filePath: string, sourceInfo: SourceInfo): PromptTemplate | null {\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, body: rawBody } = parseFrontmatter<Record<string, string>>(rawContent);\n\t\tconst body = stripResourceProfileBlocks(rawBody);\n\n\t\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t\t// Get description from frontmatter or first non-empty line\n\t\tlet description = frontmatter.description || \"\";\n\t\tif (!description) {\n\t\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\t\tif (firstLine) {\n\t\t\t\t// Truncate if too long\n\t\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(frontmatter[\"argument-hint\"] && { argumentHint: frontmatter[\"argument-hint\"] }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(\n\tdir: string,\n\tgetSourceInfo: (filePath: string) => SourceInfo,\n\tisPathAllowed?: (path: string) => boolean,\n): PromptTemplate[] {\n\tconst templates: PromptTemplate[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn templates;\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (isPathAllowed && !isPathAllowed(fullPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn templates;\n\t}\n\n\treturn templates;\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n\t/** Profile UAC gate: when provided, files it denies are never read from disk. */\n\tisPathAllowed?: (path: string) => boolean;\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): PromptTemplate[] {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\ttemplates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo, options.isPathAllowed));\n\t\ttemplates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo, options.isPathAllowed));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\ttemplates.push(...loadTemplatesFromDir(resolvedPath, getSourceInfo, options.isPathAllowed));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (options.isPathAllowed && !options.isPathAllowed(resolvedPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read failures\n\t\t}\n\t}\n\n\treturn templates;\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args, argsString);\n\t}\n\n\treturn text;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"prompt-templates.d.ts","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"AAMA,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B7D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBxF;AAoFD,MAAM,WAAW,0BAA0B;IAC1C,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;IACzB,iFAAiF;IACjF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,cAAc,EAAE,CAyEzF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,MAAM,CAgBtF","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { stripResourceProfileBlocks } from \"./resource-profile-blocks.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all parsed args joined with spaces\n * - $ARGUMENTS_RAW and $RAW_ARGUMENTS for the raw tail after the template name\n * - ${N:-default}, ${@:-default}, and ${ARGUMENTS:-default} for fallback values\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[], rawArgs?: string): string {\n\tconst allArgs = args.join(\" \");\n\tconst rawArgumentText = rawArgs ?? allArgs;\n\n\treturn content.replace(\n\t\t/\\$\\{(\\d+|ARGUMENTS|@):-([^}]*)\\}|\\$\\{@:(\\d+)(?::(\\d+))?\\}|\\$(ARGUMENTS_RAW|RAW_ARGUMENTS|ARGUMENTS|@|\\d+)/g,\n\t\t(_match, defaultTarget, defaultValue, sliceStart, sliceLength, simple) => {\n\t\t\tif (defaultTarget) {\n\t\t\t\tconst value =\n\t\t\t\t\tdefaultTarget === \"@\" || defaultTarget === \"ARGUMENTS\" ? allArgs : args[parseInt(defaultTarget, 10) - 1];\n\t\t\t\treturn value || defaultValue;\n\t\t\t}\n\n\t\t\tif (sliceStart) {\n\t\t\t\tconst start = Math.max(0, parseInt(sliceStart, 10) - 1);\n\t\t\t\treturn sliceLength\n\t\t\t\t\t? args.slice(start, start + parseInt(sliceLength, 10)).join(\" \")\n\t\t\t\t\t: args.slice(start).join(\" \");\n\t\t\t}\n\n\t\t\tif (simple === \"ARGUMENTS_RAW\" || simple === \"RAW_ARGUMENTS\") return rawArgumentText;\n\t\t\tif (simple === \"ARGUMENTS\" || simple === \"@\") return allArgs;\n\t\t\treturn args[parseInt(simple, 10) - 1] ?? \"\";\n\t\t},\n\t);\n}\n\nfunction loadTemplateFromFile(filePath: string, sourceInfo: SourceInfo): PromptTemplate | null {\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, body: rawBody } = parseFrontmatter<Record<string, string>>(rawContent);\n\t\tconst body = stripResourceProfileBlocks(rawBody);\n\n\t\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t\t// Get description from frontmatter or first non-empty line\n\t\tlet description = frontmatter.description || \"\";\n\t\tif (!description) {\n\t\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\t\tif (firstLine) {\n\t\t\t\t// Truncate if too long\n\t\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(frontmatter[\"argument-hint\"] && { argumentHint: frontmatter[\"argument-hint\"] }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(\n\tdir: string,\n\tgetSourceInfo: (filePath: string) => SourceInfo,\n\tisPathAllowed?: (path: string) => boolean,\n): PromptTemplate[] {\n\tconst templates: PromptTemplate[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn templates;\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (isPathAllowed && !isPathAllowed(fullPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn templates;\n\t}\n\n\treturn templates;\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n\t/** Profile UAC gate: when provided, files it denies are never read from disk. */\n\tisPathAllowed?: (path: string) => boolean;\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): PromptTemplate[] {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\ttemplates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo, options.isPathAllowed));\n\t\ttemplates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo, options.isPathAllowed));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\ttemplates.push(...loadTemplatesFromDir(resolvedPath, getSourceInfo, options.isPathAllowed));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (options.isPathAllowed && !options.isPathAllowed(resolvedPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read failures\n\t\t}\n\t}\n\n\treturn templates;\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args, argsString);\n\t}\n\n\treturn text;\n}\n"]}
|
|
@@ -47,6 +47,7 @@ export function parseCommandArgs(argsString) {
|
|
|
47
47
|
* - $1, $2, ... for positional args
|
|
48
48
|
* - $@ and $ARGUMENTS for all parsed args joined with spaces
|
|
49
49
|
* - $ARGUMENTS_RAW and $RAW_ARGUMENTS for the raw tail after the template name
|
|
50
|
+
* - ${N:-default}, ${@:-default}, and ${ARGUMENTS:-default} for fallback values
|
|
50
51
|
* - ${@:N} for args from Nth onwards (bash-style slicing)
|
|
51
52
|
* - ${@:N:L} for L args starting from Nth
|
|
52
53
|
*
|
|
@@ -54,37 +55,25 @@ export function parseCommandArgs(argsString) {
|
|
|
54
55
|
* containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.
|
|
55
56
|
*/
|
|
56
57
|
export function substituteArgs(content, args, rawArgs) {
|
|
57
|
-
let result = content;
|
|
58
|
-
// Replace $1, $2, etc. with positional args FIRST (before wildcards)
|
|
59
|
-
// This prevents wildcard replacement values containing $<digit> patterns from being re-substituted
|
|
60
|
-
result = result.replace(/\$(\d+)/g, (_, num) => {
|
|
61
|
-
const index = parseInt(num, 10) - 1;
|
|
62
|
-
return args[index] ?? "";
|
|
63
|
-
});
|
|
64
|
-
// Replace ${@:start} or ${@:start:length} with sliced args (bash-style)
|
|
65
|
-
// Process BEFORE simple $@ to avoid conflicts
|
|
66
|
-
result = result.replace(/\$\{@:(\d+)(?::(\d+))?\}/g, (_, startStr, lengthStr) => {
|
|
67
|
-
let start = parseInt(startStr, 10) - 1; // Convert to 0-indexed (user provides 1-indexed)
|
|
68
|
-
// Treat 0 as 1 (bash convention: args start at 1)
|
|
69
|
-
if (start < 0)
|
|
70
|
-
start = 0;
|
|
71
|
-
if (lengthStr) {
|
|
72
|
-
const length = parseInt(lengthStr, 10);
|
|
73
|
-
return args.slice(start, start + length).join(" ");
|
|
74
|
-
}
|
|
75
|
-
return args.slice(start).join(" ");
|
|
76
|
-
});
|
|
77
|
-
// Pre-compute all args joined (optimization)
|
|
78
58
|
const allArgs = args.join(" ");
|
|
79
59
|
const rawArgumentText = rawArgs ?? allArgs;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
60
|
+
return content.replace(/\$\{(\d+|ARGUMENTS|@):-([^}]*)\}|\$\{@:(\d+)(?::(\d+))?\}|\$(ARGUMENTS_RAW|RAW_ARGUMENTS|ARGUMENTS|@|\d+)/g, (_match, defaultTarget, defaultValue, sliceStart, sliceLength, simple) => {
|
|
61
|
+
if (defaultTarget) {
|
|
62
|
+
const value = defaultTarget === "@" || defaultTarget === "ARGUMENTS" ? allArgs : args[parseInt(defaultTarget, 10) - 1];
|
|
63
|
+
return value || defaultValue;
|
|
64
|
+
}
|
|
65
|
+
if (sliceStart) {
|
|
66
|
+
const start = Math.max(0, parseInt(sliceStart, 10) - 1);
|
|
67
|
+
return sliceLength
|
|
68
|
+
? args.slice(start, start + parseInt(sliceLength, 10)).join(" ")
|
|
69
|
+
: args.slice(start).join(" ");
|
|
70
|
+
}
|
|
71
|
+
if (simple === "ARGUMENTS_RAW" || simple === "RAW_ARGUMENTS")
|
|
72
|
+
return rawArgumentText;
|
|
73
|
+
if (simple === "ARGUMENTS" || simple === "@")
|
|
74
|
+
return allArgs;
|
|
75
|
+
return args[parseInt(simple, 10) - 1] ?? "";
|
|
76
|
+
});
|
|
88
77
|
}
|
|
89
78
|
function loadTemplateFromFile(filePath, sourceInfo) {
|
|
90
79
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-templates.js","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { stripResourceProfileBlocks } from \"./resource-profile-blocks.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all parsed args joined with spaces\n * - $ARGUMENTS_RAW and $RAW_ARGUMENTS for the raw tail after the template name\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[], rawArgs?: string): string {\n\tlet result = content;\n\n\t// Replace $1, $2, etc. with positional args FIRST (before wildcards)\n\t// This prevents wildcard replacement values containing $<digit> patterns from being re-substituted\n\tresult = result.replace(/\\$(\\d+)/g, (_, num) => {\n\t\tconst index = parseInt(num, 10) - 1;\n\t\treturn args[index] ?? \"\";\n\t});\n\n\t// Replace ${@:start} or ${@:start:length} with sliced args (bash-style)\n\t// Process BEFORE simple $@ to avoid conflicts\n\tresult = result.replace(/\\$\\{@:(\\d+)(?::(\\d+))?\\}/g, (_, startStr, lengthStr) => {\n\t\tlet start = parseInt(startStr, 10) - 1; // Convert to 0-indexed (user provides 1-indexed)\n\t\t// Treat 0 as 1 (bash convention: args start at 1)\n\t\tif (start < 0) start = 0;\n\n\t\tif (lengthStr) {\n\t\t\tconst length = parseInt(lengthStr, 10);\n\t\t\treturn args.slice(start, start + length).join(\" \");\n\t\t}\n\t\treturn args.slice(start).join(\" \");\n\t});\n\n\t// Pre-compute all args joined (optimization)\n\tconst allArgs = args.join(\" \");\n\n\tconst rawArgumentText = rawArgs ?? allArgs;\n\n\t// Replace raw-argument aliases before $ARGUMENTS so the shared prefix does not partially match.\n\tresult = result.replace(/\\$ARGUMENTS_RAW/g, rawArgumentText);\n\tresult = result.replace(/\\$RAW_ARGUMENTS/g, rawArgumentText);\n\n\t// Replace $ARGUMENTS with all args joined (new syntax, aligns with Claude, Codex, OpenCode)\n\tresult = result.replace(/\\$ARGUMENTS/g, allArgs);\n\n\t// Replace $@ with all args joined (existing syntax)\n\tresult = result.replace(/\\$@/g, allArgs);\n\n\treturn result;\n}\n\nfunction loadTemplateFromFile(filePath: string, sourceInfo: SourceInfo): PromptTemplate | null {\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, body: rawBody } = parseFrontmatter<Record<string, string>>(rawContent);\n\t\tconst body = stripResourceProfileBlocks(rawBody);\n\n\t\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t\t// Get description from frontmatter or first non-empty line\n\t\tlet description = frontmatter.description || \"\";\n\t\tif (!description) {\n\t\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\t\tif (firstLine) {\n\t\t\t\t// Truncate if too long\n\t\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(frontmatter[\"argument-hint\"] && { argumentHint: frontmatter[\"argument-hint\"] }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(\n\tdir: string,\n\tgetSourceInfo: (filePath: string) => SourceInfo,\n\tisPathAllowed?: (path: string) => boolean,\n): PromptTemplate[] {\n\tconst templates: PromptTemplate[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn templates;\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (isPathAllowed && !isPathAllowed(fullPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn templates;\n\t}\n\n\treturn templates;\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n\t/** Profile UAC gate: when provided, files it denies are never read from disk. */\n\tisPathAllowed?: (path: string) => boolean;\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): PromptTemplate[] {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\ttemplates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo, options.isPathAllowed));\n\t\ttemplates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo, options.isPathAllowed));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\ttemplates.push(...loadTemplatesFromDir(resolvedPath, getSourceInfo, options.isPathAllowed));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (options.isPathAllowed && !options.isPathAllowed(resolvedPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read failures\n\t\t}\n\t}\n\n\treturn templates;\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args, argsString);\n\t}\n\n\treturn text;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"prompt-templates.js","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { stripResourceProfileBlocks } from \"./resource-profile-blocks.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all parsed args joined with spaces\n * - $ARGUMENTS_RAW and $RAW_ARGUMENTS for the raw tail after the template name\n * - ${N:-default}, ${@:-default}, and ${ARGUMENTS:-default} for fallback values\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[], rawArgs?: string): string {\n\tconst allArgs = args.join(\" \");\n\tconst rawArgumentText = rawArgs ?? allArgs;\n\n\treturn content.replace(\n\t\t/\\$\\{(\\d+|ARGUMENTS|@):-([^}]*)\\}|\\$\\{@:(\\d+)(?::(\\d+))?\\}|\\$(ARGUMENTS_RAW|RAW_ARGUMENTS|ARGUMENTS|@|\\d+)/g,\n\t\t(_match, defaultTarget, defaultValue, sliceStart, sliceLength, simple) => {\n\t\t\tif (defaultTarget) {\n\t\t\t\tconst value =\n\t\t\t\t\tdefaultTarget === \"@\" || defaultTarget === \"ARGUMENTS\" ? allArgs : args[parseInt(defaultTarget, 10) - 1];\n\t\t\t\treturn value || defaultValue;\n\t\t\t}\n\n\t\t\tif (sliceStart) {\n\t\t\t\tconst start = Math.max(0, parseInt(sliceStart, 10) - 1);\n\t\t\t\treturn sliceLength\n\t\t\t\t\t? args.slice(start, start + parseInt(sliceLength, 10)).join(\" \")\n\t\t\t\t\t: args.slice(start).join(\" \");\n\t\t\t}\n\n\t\t\tif (simple === \"ARGUMENTS_RAW\" || simple === \"RAW_ARGUMENTS\") return rawArgumentText;\n\t\t\tif (simple === \"ARGUMENTS\" || simple === \"@\") return allArgs;\n\t\t\treturn args[parseInt(simple, 10) - 1] ?? \"\";\n\t\t},\n\t);\n}\n\nfunction loadTemplateFromFile(filePath: string, sourceInfo: SourceInfo): PromptTemplate | null {\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, body: rawBody } = parseFrontmatter<Record<string, string>>(rawContent);\n\t\tconst body = stripResourceProfileBlocks(rawBody);\n\n\t\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t\t// Get description from frontmatter or first non-empty line\n\t\tlet description = frontmatter.description || \"\";\n\t\tif (!description) {\n\t\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\t\tif (firstLine) {\n\t\t\t\t// Truncate if too long\n\t\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(frontmatter[\"argument-hint\"] && { argumentHint: frontmatter[\"argument-hint\"] }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(\n\tdir: string,\n\tgetSourceInfo: (filePath: string) => SourceInfo,\n\tisPathAllowed?: (path: string) => boolean,\n): PromptTemplate[] {\n\tconst templates: PromptTemplate[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn templates;\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (isPathAllowed && !isPathAllowed(fullPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn templates;\n\t}\n\n\treturn templates;\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n\t/** Profile UAC gate: when provided, files it denies are never read from disk. */\n\tisPathAllowed?: (path: string) => boolean;\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): PromptTemplate[] {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\ttemplates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo, options.isPathAllowed));\n\t\ttemplates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo, options.isPathAllowed));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\ttemplates.push(...loadTemplatesFromDir(resolvedPath, getSourceInfo, options.isPathAllowed));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\t// Profile UAC: a denied template file is never read from disk.\n\t\t\t\tif (options.isPathAllowed && !options.isPathAllowed(resolvedPath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst template = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read failures\n\t\t}\n\t}\n\n\treturn templates;\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args, argsString);\n\t}\n\n\treturn text;\n}\n"]}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { type Agent, type ThinkingLevel } from "@caupulican/pi-agent-core";
|
|
13
13
|
import type { SessionManager } from "@caupulican/pi-agent-core/node";
|
|
14
14
|
import { type Api, type Model, type SimpleStreamOptions, type Usage } from "@caupulican/pi-ai";
|
|
15
|
-
import { type IsolatedCompletionOptions, type IsolatedCompletionResult } from "./agent-session.ts";
|
|
15
|
+
import { type IsolatedCompletionOptions, type IsolatedCompletionResult } from "./agent-session-contracts.ts";
|
|
16
16
|
import type { LearningDecision } from "./autonomy/contracts.ts";
|
|
17
17
|
import { type AutonomyTelemetryEvent } from "./autonomy/telemetry-events.ts";
|
|
18
18
|
import { type LearningAuditRecord } from "./learning/learning-audit.ts";
|