open-multi-agent-kit 0.96.2 → 0.98.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/README.md +43 -9
- package/dist/cli/args.d.ts +2 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +13 -189
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.d.ts +3 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +194 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js +2 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/resource-paths.d.ts +4 -0
- package/dist/cli/resource-paths.d.ts.map +1 -0
- package/dist/cli/resource-paths.js +59 -0
- package/dist/cli/resource-paths.js.map +1 -0
- package/dist/commands/adaptorch-doctor-cli.d.ts +13 -0
- package/dist/commands/adaptorch-doctor-cli.d.ts.map +1 -0
- package/dist/commands/adaptorch-doctor-cli.js +115 -0
- package/dist/commands/adaptorch-doctor-cli.js.map +1 -0
- package/dist/commands/resource-doctor-cli.d.ts +4 -0
- package/dist/commands/resource-doctor-cli.d.ts.map +1 -1
- package/dist/commands/resource-doctor-cli.js +35 -5
- package/dist/commands/resource-doctor-cli.js.map +1 -1
- package/dist/core/adaptorch-bridge.d.ts +11 -13
- package/dist/core/adaptorch-bridge.d.ts.map +1 -1
- package/dist/core/adaptorch-bridge.js +11 -13
- package/dist/core/adaptorch-bridge.js.map +1 -1
- package/dist/core/agent-session-snapshot.d.ts +16 -0
- package/dist/core/agent-session-snapshot.d.ts.map +1 -0
- package/dist/core/agent-session-snapshot.js +111 -0
- package/dist/core/agent-session-snapshot.js.map +1 -0
- package/dist/core/agent-session.d.ts +14 -10
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +102 -217
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agent-tool-settings.d.ts +10 -0
- package/dist/core/agent-tool-settings.d.ts.map +1 -1
- package/dist/core/agent-tool-settings.js +10 -1
- package/dist/core/agent-tool-settings.js.map +1 -1
- package/dist/core/attachment-store.d.ts +39 -0
- package/dist/core/attachment-store.d.ts.map +1 -0
- package/dist/core/attachment-store.js +81 -0
- package/dist/core/attachment-store.js.map +1 -0
- package/dist/core/command-safety.d.ts +8 -0
- package/dist/core/command-safety.d.ts.map +1 -1
- package/dist/core/command-safety.js +140 -14
- package/dist/core/command-safety.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +13 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +60 -62
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts +1 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +1 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/compaction/knowledge-triage-format.d.ts +9 -0
- package/dist/core/compaction/knowledge-triage-format.d.ts.map +1 -0
- package/dist/core/compaction/knowledge-triage-format.js +71 -0
- package/dist/core/compaction/knowledge-triage-format.js.map +1 -0
- package/dist/core/compaction/knowledge-triage-types.d.ts +7 -0
- package/dist/core/compaction/knowledge-triage-types.d.ts.map +1 -0
- package/dist/core/compaction/knowledge-triage-types.js +2 -0
- package/dist/core/compaction/knowledge-triage-types.js.map +1 -0
- package/dist/core/compaction/knowledge-triage.d.ts +29 -0
- package/dist/core/compaction/knowledge-triage.d.ts.map +1 -0
- package/dist/core/compaction/knowledge-triage.js +189 -0
- package/dist/core/compaction/knowledge-triage.js.map +1 -0
- package/dist/core/compaction/transaction.d.ts.map +1 -1
- package/dist/core/compaction/transaction.js +1 -1
- package/dist/core/compaction/transaction.js.map +1 -1
- package/dist/core/compaction/utils.d.ts +4 -0
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +37 -0
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/completion-sound-io.d.ts +14 -0
- package/dist/core/completion-sound-io.d.ts.map +1 -0
- package/dist/core/completion-sound-io.js +64 -0
- package/dist/core/completion-sound-io.js.map +1 -0
- package/dist/core/completion-sound.d.ts +4 -10
- package/dist/core/completion-sound.d.ts.map +1 -1
- package/dist/core/completion-sound.js +28 -58
- package/dist/core/completion-sound.js.map +1 -1
- package/dist/core/context-budget-governor-v2.d.ts +1 -1
- package/dist/core/context-budget-governor-v2.d.ts.map +1 -1
- package/dist/core/context-budget-governor-v2.js +1 -1
- package/dist/core/context-budget-governor-v2.js.map +1 -1
- package/dist/core/context-budget-relevance.d.ts +15 -2
- package/dist/core/context-budget-relevance.d.ts.map +1 -1
- package/dist/core/context-budget-relevance.js +21 -9
- package/dist/core/context-budget-relevance.js.map +1 -1
- package/dist/core/context-budget-system-prompt-items.d.ts +1 -1
- package/dist/core/context-budget-system-prompt-items.d.ts.map +1 -1
- package/dist/core/context-budget-system-prompt-items.js.map +1 -1
- package/dist/core/context-budget-system-prompt.d.ts +1 -1
- package/dist/core/context-budget-system-prompt.d.ts.map +1 -1
- package/dist/core/context-budget-system-prompt.js.map +1 -1
- package/dist/core/context-budget-v2-cache-keys.d.ts +3 -0
- package/dist/core/context-budget-v2-cache-keys.d.ts.map +1 -1
- package/dist/core/context-budget-v2-cache-keys.js +2 -0
- package/dist/core/context-budget-v2-cache-keys.js.map +1 -1
- package/dist/core/context-budget-v2-plan-cache-keys.d.ts.map +1 -1
- package/dist/core/context-budget-v2-plan-cache-keys.js +3 -0
- package/dist/core/context-budget-v2-plan-cache-keys.js.map +1 -1
- package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
- package/dist/core/context-budget-v2-planner.js +2 -1
- package/dist/core/context-budget-v2-planner.js.map +1 -1
- package/dist/core/context-budget-v2-scoring.d.ts +17 -0
- package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
- package/dist/core/context-budget-v2-scoring.js +25 -8
- package/dist/core/context-budget-v2-scoring.js.map +1 -1
- package/dist/core/context-budget-v2-selection.d.ts +2 -2
- package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
- package/dist/core/context-budget-v2-selection.js +18 -1
- package/dist/core/context-budget-v2-selection.js.map +1 -1
- package/dist/core/context-budget-v2-types.d.ts +17 -0
- package/dist/core/context-budget-v2-types.d.ts.map +1 -1
- package/dist/core/context-budget-v2-types.js +15 -0
- package/dist/core/context-budget-v2-types.js.map +1 -1
- package/dist/core/context-file.d.ts +23 -0
- package/dist/core/context-file.d.ts.map +1 -0
- package/dist/core/context-file.js +17 -0
- package/dist/core/context-file.js.map +1 -0
- package/dist/core/extensions/builtin/command-safety-gate.d.ts +3 -1
- package/dist/core/extensions/builtin/command-safety-gate.d.ts.map +1 -1
- package/dist/core/extensions/builtin/command-safety-gate.js +7 -0
- package/dist/core/extensions/builtin/command-safety-gate.js.map +1 -1
- package/dist/core/extensions/types.d.ts +12 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +6 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-prompt-policy.d.ts +1 -1
- package/dist/core/model-prompt-policy.d.ts.map +1 -1
- package/dist/core/model-prompt-policy.js.map +1 -1
- package/dist/core/model-registry.d.ts +8 -3
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +71 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-doctor-source-scan.d.ts +9 -0
- package/dist/core/package-doctor-source-scan.d.ts.map +1 -1
- package/dist/core/package-doctor-source-scan.js +77 -4
- package/dist/core/package-doctor-source-scan.js.map +1 -1
- package/dist/core/prompt-attachment.d.ts +51 -0
- package/dist/core/prompt-attachment.d.ts.map +1 -0
- package/dist/core/prompt-attachment.js +186 -0
- package/dist/core/prompt-attachment.js.map +1 -0
- package/dist/core/prompt-settlement.d.ts +6 -3
- package/dist/core/prompt-settlement.d.ts.map +1 -1
- package/dist/core/prompt-settlement.js +10 -14
- package/dist/core/prompt-settlement.js.map +1 -1
- package/dist/core/provider-resilience.d.ts +28 -0
- package/dist/core/provider-resilience.d.ts.map +1 -1
- package/dist/core/provider-resilience.js +50 -0
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/provider-retry.d.ts +1 -1
- package/dist/core/provider-retry.d.ts.map +1 -1
- package/dist/core/provider-retry.js +1 -1
- package/dist/core/provider-retry.js.map +1 -1
- package/dist/core/provider-usage.d.ts.map +1 -1
- package/dist/core/provider-usage.js +68 -2
- package/dist/core/provider-usage.js.map +1 -1
- package/dist/core/reasoning-router-promotion.d.ts +124 -0
- package/dist/core/reasoning-router-promotion.d.ts.map +1 -0
- package/dist/core/reasoning-router-promotion.js +121 -0
- package/dist/core/reasoning-router-promotion.js.map +1 -0
- package/dist/core/reasoning-router-replay-stability.d.ts +65 -0
- package/dist/core/reasoning-router-replay-stability.d.ts.map +1 -0
- package/dist/core/reasoning-router-replay-stability.js +67 -0
- package/dist/core/reasoning-router-replay-stability.js.map +1 -0
- package/dist/core/reasoning-router-v4-weights.d.ts +6 -6
- package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4-weights.js +3 -3
- package/dist/core/reasoning-router-v4-weights.js.map +1 -1
- package/dist/core/reasoning-router-v4.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4.js +6 -6
- package/dist/core/reasoning-router-v4.js.map +1 -1
- package/dist/core/redaction.d.ts.map +1 -1
- package/dist/core/redaction.js +1 -1
- package/dist/core/redaction.js.map +1 -1
- package/dist/core/resource-admission.d.ts +2 -1
- package/dist/core/resource-admission.d.ts.map +1 -1
- package/dist/core/resource-admission.js +11 -0
- package/dist/core/resource-admission.js.map +1 -1
- package/dist/core/resource-governor-format.d.ts +1 -1
- package/dist/core/resource-governor-format.d.ts.map +1 -1
- package/dist/core/resource-governor-format.js.map +1 -1
- package/dist/core/resource-governor-settings.d.ts +6 -12
- package/dist/core/resource-governor-settings.d.ts.map +1 -1
- package/dist/core/resource-governor-settings.js.map +1 -1
- package/dist/core/resource-governor-types.d.ts +24 -0
- package/dist/core/resource-governor-types.d.ts.map +1 -0
- package/dist/core/resource-governor-types.js +15 -0
- package/dist/core/resource-governor-types.js.map +1 -0
- package/dist/core/resource-loader.d.ts +1 -7
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/resource-observation-journal.d.ts +2 -1
- package/dist/core/resource-observation-journal.d.ts.map +1 -1
- package/dist/core/resource-observation-journal.js +4 -3
- package/dist/core/resource-observation-journal.js.map +1 -1
- package/dist/core/resource-observation-report-reader.d.ts +7 -0
- package/dist/core/resource-observation-report-reader.d.ts.map +1 -0
- package/dist/core/resource-observation-report-reader.js +157 -0
- package/dist/core/resource-observation-report-reader.js.map +1 -0
- package/dist/core/resource-observation-report.d.ts +24 -0
- package/dist/core/resource-observation-report.d.ts.map +1 -0
- package/dist/core/resource-observation-report.js +101 -0
- package/dist/core/resource-observation-report.js.map +1 -0
- package/dist/core/router-feedback-collector.d.ts +1 -1
- package/dist/core/router-feedback-collector.d.ts.map +1 -1
- package/dist/core/router-feedback-collector.js.map +1 -1
- package/dist/core/session-bash-service.d.ts.map +1 -1
- package/dist/core/session-bash-service.js +4 -3
- package/dist/core/session-bash-service.js.map +1 -1
- package/dist/core/session-failure-cause.d.ts +33 -0
- package/dist/core/session-failure-cause.d.ts.map +1 -0
- package/dist/core/session-failure-cause.js +141 -0
- package/dist/core/session-failure-cause.js.map +1 -0
- package/dist/core/session-system-prompt.d.ts +1 -1
- package/dist/core/session-system-prompt.d.ts.map +1 -1
- package/dist/core/session-system-prompt.js.map +1 -1
- package/dist/core/session-termination.d.ts +1 -1
- package/dist/core/session-termination.d.ts.map +1 -1
- package/dist/core/session-termination.js +11 -2
- package/dist/core/session-termination.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.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +10 -14
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +13 -48
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -34
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/workload-classifier.d.ts +2 -10
- package/dist/core/workload-classifier.d.ts.map +1 -1
- package/dist/core/workload-classifier.js +17 -0
- package/dist/core/workload-classifier.js.map +1 -1
- package/dist/core/workload-family-matcher.d.ts +1 -1
- package/dist/core/workload-family-matcher.d.ts.map +1 -1
- package/dist/core/workload-family-matcher.js.map +1 -1
- package/dist/core/workload-shard-execution-types.d.ts +40 -0
- package/dist/core/workload-shard-execution-types.d.ts.map +1 -0
- package/dist/core/workload-shard-execution-types.js +14 -0
- package/dist/core/workload-shard-execution-types.js.map +1 -0
- package/dist/core/workload-shard-executor.d.ts +3 -27
- package/dist/core/workload-shard-executor.d.ts.map +1 -1
- package/dist/core/workload-shard-executor.js.map +1 -1
- package/dist/core/workload-shard-runner.d.ts +1 -1
- package/dist/core/workload-shard-runner.d.ts.map +1 -1
- package/dist/core/workload-shard-runner.js.map +1 -1
- package/dist/core/workload-types.d.ts +24 -0
- package/dist/core/workload-types.d.ts.map +1 -0
- package/dist/core/workload-types.js +15 -0
- package/dist/core/workload-types.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +27 -47
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/attachment-strip.d.ts +21 -0
- package/dist/modes/interactive/components/attachment-strip.d.ts.map +1 -0
- package/dist/modes/interactive/components/attachment-strip.js +136 -0
- package/dist/modes/interactive/components/attachment-strip.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +6 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +26 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/status-sidebar.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-sidebar.js +12 -1
- package/dist/modes/interactive/components/status-sidebar.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +23 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +174 -53
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/omk-control-panel.json +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +42 -33
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +14 -2
- package/dist/utils/clipboard.js.map +1 -1
- package/docs/adaptorch-preview-spec.md +20 -18
- package/docs/adaptorch-preview.md +9 -9
- package/docs/compaction.md +25 -4
- package/docs/correctness-wall.md +10 -10
- package/docs/docs.json +12 -0
- package/docs/environment-variables.md +9 -2
- package/docs/extensions.md +12 -3
- package/docs/index.md +7 -1
- package/docs/metrics.md +74 -2
- package/docs/provider-resilience.md +11 -0
- package/docs/runtime-algorithms.md +359 -0
- package/docs/sdk.md +2 -2
- package/docs/settings.md +11 -10
- package/docs/usage.md +19 -3
- 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/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/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 +18 -18
- package/package.json +7 -7
- package/dist/core/context-budget-governor.d.ts +0 -67
- package/dist/core/context-budget-governor.d.ts.map +0 -1
- package/dist/core/context-budget-governor.js +0 -224
- package/dist/core/context-budget-governor.js.map +0 -1
- package/dist/core/lean-ctx.d.ts +0 -99
- package/dist/core/lean-ctx.d.ts.map +0 -1
- package/dist/core/lean-ctx.js +0 -225
- package/dist/core/lean-ctx.js.map +0 -1
- package/dist/core/read-anchors.d.ts +0 -110
- package/dist/core/read-anchors.d.ts.map +0 -1
- package/dist/core/read-anchors.js +0 -193
- package/dist/core/read-anchors.js.map +0 -1
- package/dist/core/recovery-checkpoint.d.ts +0 -196
- package/dist/core/recovery-checkpoint.d.ts.map +0 -1
- package/dist/core/recovery-checkpoint.js +0 -382
- package/dist/core/recovery-checkpoint.js.map +0 -1
- package/dist/core/sandbox-policy-v2.d.ts +0 -49
- package/dist/core/sandbox-policy-v2.d.ts.map +0 -1
- package/dist/core/sandbox-policy-v2.js +0 -368
- package/dist/core/sandbox-policy-v2.js.map +0 -1
- package/dist/core/tools/omp-pure-seams.d.ts +0 -41
- package/dist/core/tools/omp-pure-seams.d.ts.map +0 -1
- package/dist/core/tools/omp-pure-seams.js +0 -142
- package/dist/core/tools/omp-pure-seams.js.map +0 -1
- package/dist/core/tools/omp-seam-runtime.d.ts +0 -100
- package/dist/core/tools/omp-seam-runtime.d.ts.map +0 -1
- package/dist/core/tools/omp-seam-runtime.js +0 -40
- package/dist/core/tools/omp-seam-runtime.js.map +0 -1
- package/dist/guardrails/audit-logger.d.ts +0 -4
- package/dist/guardrails/audit-logger.d.ts.map +0 -1
- package/dist/guardrails/audit-logger.js +0 -28
- package/dist/guardrails/audit-logger.js.map +0 -1
- package/dist/guardrails/freedomd-adapter.d.ts +0 -16
- package/dist/guardrails/freedomd-adapter.d.ts.map +0 -1
- package/dist/guardrails/freedomd-adapter.js +0 -87
- package/dist/guardrails/freedomd-adapter.js.map +0 -1
- package/dist/orchestration/lane-grant-auditor.d.ts +0 -4
- package/dist/orchestration/lane-grant-auditor.d.ts.map +0 -1
- package/dist/orchestration/lane-grant-auditor.js +0 -87
- package/dist/orchestration/lane-grant-auditor.js.map +0 -1
- package/dist/types/guardrails.d.ts +0 -61
- package/dist/types/guardrails.d.ts.map +0 -1
- package/dist/types/guardrails.js +0 -9
- package/dist/types/guardrails.js.map +0 -1
- package/dist/types/lane-grant.d.ts +0 -58
- package/dist/types/lane-grant.d.ts.map +0 -1
- package/dist/types/lane-grant.js +0 -9
- package/dist/types/lane-grant.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-relevance.js","sourceRoot":"","sources":["../../src/core/context-budget-relevance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,8EAA8E;AAC9E,mDAAiD;AACjD,8EAA8E;AAE9E,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;IAC/C,4BAA4B;IAC5B,KAAK;IACL,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,KAAK;IACL,MAAM;IACN,IAAI;IACJ,MAAM;IACN,OAAO;IACP,MAAM;IACN,KAAK;IACL,KAAK;IACL,IAAI;IACJ,MAAM;IACN,KAAK;IACL,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;IACR,KAAK;IACL,OAAO;IACP,OAAO;IACP,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,KAAK;IACL,IAAI;IACJ,MAAM;IACN,KAAK;IACL,GAAG;IACH,KAAK;IACL,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,KAAK;IACL,KAAK;IACL,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,KAAK;IACL,MAAM;IACN,OAAO;IACP,MAAM;IACN,KAAK;IACL,KAAK;IACL,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,KAAK;IACL,MAAM;IACN,IAAI;IACJ,KAAK;IACL,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,KAAK;IACL,IAAI;IACJ,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,IAAI;IACJ,MAAM;IACN,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,SAAS;IACT,QAAQ;IACR,MAAM;IACN,KAAK;IACL,SAAS;IACT,MAAM;IACN,MAAM;IACN,OAAO;IACP,4BAAwB;IACxB,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,KAAG;IACH,QAAI;IACJ,QAAI;IACJ,QAAI;IACJ,QAAI;IACJ,KAAG;IACH,QAAI;IACJ,KAAG;IACH,QAAI;IACJ,QAAI;IACJ,QAAI;IACJ,KAAG;IACH,QAAI;IACJ,QAAI;IACJ,KAAG;CACH,CAAC,CAAC;AAEH,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAqB;IACrE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACvD,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;IACX,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACzB,kBAAkB;QAClB,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,SAAS;QACV,CAAC;QACD,qDAAqD;QACrD,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACpD,SAAS;QACV,CAAC;QACD,sCAAsC;QACtC,IAAI,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YACD,SAAS;QACV,CAAC;QACD,cAAc;QACd,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AAAA,CACd;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,eAAe,CACvB,WAA8B,EAC9B,YAAiC,EACjC,gBAA8C,EACrC;IACT,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,CAAC;IACV,CAAC;IAED,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC9B,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1B,WAAW,IAAI,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;IACF,CAAC;IAED,qEAAqE;IACrE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAAA,CACrD;AAED;;GAEG;AACH,SAAS,qBAAqB,CAC7B,QAAgB,EAChB,cAAkC,EAClC,MAAc,EAC0C;IACxD,MAAM,UAAU,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE9E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC5B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnB,CAAC;IACF,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC5B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAAA,CACrC;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAClC,KAA8D,EAC9D,YAAgC,EACvB;IACT,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjD,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,MAAM,WAAW,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IACvD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,wEAAwE;IACxE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,qBAAqB,CAC7E,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,IAAI,EACV,iBAAiB,CACjB,CAAC;IAEF,IAAI,KAAK,GAAG,eAAe,CAAC,WAAW,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC;IAEtE,4EAA4E;IAC5E,MAAM,UAAU,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IAC9C,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QAC7E,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,gBAAgB,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CACtB;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAElC;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CACxC,WAA4F,EAC5F,YAAgC,EACvB;IACT,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjD,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB,CAAC;IAC9E,CAAC;IAED,MAAM,WAAW,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IACvD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB,CAAC;IAC9E,CAAC;IAED,oCAAoC;IACpC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAEvD,iCAAiC;IACjC,MAAM,aAAa,GAClB,WAAW,CAAC,OAAO,CAAC,MAAM,GAAG,oBAAoB;QAChD,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC;QACpD,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC;IAExB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,qBAAqB,CAC3E,aAAa,EACb,QAAQ,EACR,iBAAiB,CACjB,CAAC;IAEF,IAAI,KAAK,GAAG,eAAe,CAAC,WAAW,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;IAEpE,oBAAoB;IACpB,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CACtB;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;GAEG;AACH,SAAS,mBAAmB,CAAC,IAAY,EAAU;IAClD,OAAO,IAAI;SACT,WAAW,EAAE;SACb,KAAK,CAAC,WAAW,CAAC;SAClB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;SAC/B,IAAI,CAAC,GAAG,CAAC,CAAC;AAAA,CACZ;AAED,SAAS,OAAO,CAAC,KAAa,EAAU;IACvC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AAAA,CACvC","sourcesContent":["/**\n * Query-aware relevance scoring for context budget items.\n *\n * Replaces heuristic proxies (description length, isGlobal hardcoding)\n * with token-based coverage scoring that measures semantic overlap\n * between the current query context and each resource item.\n *\n * Design: Coverage-based IR (not Jaccard) — avoids penalizing long\n * descriptions whose large union would dilute the score.\n */\n\n// ---------------------------------------------------------------------------\n// Stop words — English + common Korean particles\n// ---------------------------------------------------------------------------\n\nconst STOP_WORDS: ReadonlySet<string> = new Set([\n\t// English common stop words\n\t\"the\",\n\t\"a\",\n\t\"an\",\n\t\"is\",\n\t\"are\",\n\t\"was\",\n\t\"were\",\n\t\"be\",\n\t\"been\",\n\t\"being\",\n\t\"have\",\n\t\"has\",\n\t\"had\",\n\t\"do\",\n\t\"does\",\n\t\"did\",\n\t\"will\",\n\t\"would\",\n\t\"could\",\n\t\"should\",\n\t\"may\",\n\t\"might\",\n\t\"shall\",\n\t\"can\",\n\t\"to\",\n\t\"of\",\n\t\"in\",\n\t\"on\",\n\t\"for\",\n\t\"and\",\n\t\"or\",\n\t\"with\",\n\t\"how\",\n\t\"i\",\n\t\"you\",\n\t\"he\",\n\t\"she\",\n\t\"it\",\n\t\"we\",\n\t\"they\",\n\t\"me\",\n\t\"him\",\n\t\"her\",\n\t\"us\",\n\t\"them\",\n\t\"my\",\n\t\"your\",\n\t\"his\",\n\t\"its\",\n\t\"our\",\n\t\"their\",\n\t\"this\",\n\t\"that\",\n\t\"these\",\n\t\"those\",\n\t\"what\",\n\t\"which\",\n\t\"who\",\n\t\"whom\",\n\t\"where\",\n\t\"when\",\n\t\"why\",\n\t\"not\",\n\t\"no\",\n\t\"nor\",\n\t\"so\",\n\t\"if\",\n\t\"then\",\n\t\"than\",\n\t\"too\",\n\t\"very\",\n\t\"just\",\n\t\"about\",\n\t\"above\",\n\t\"after\",\n\t\"again\",\n\t\"all\",\n\t\"also\",\n\t\"am\",\n\t\"any\",\n\t\"because\",\n\t\"before\",\n\t\"between\",\n\t\"both\",\n\t\"but\",\n\t\"by\",\n\t\"from\",\n\t\"into\",\n\t\"more\",\n\t\"most\",\n\t\"other\",\n\t\"out\",\n\t\"over\",\n\t\"own\",\n\t\"same\",\n\t\"some\",\n\t\"such\",\n\t\"there\",\n\t\"up\",\n\t\"down\",\n\t\"here\",\n\t\"at\",\n\t\"as\",\n\t\"while\",\n\t\"through\",\n\t\"during\",\n\t\"each\",\n\t\"few\",\n\t\"further\",\n\t\"once\",\n\t\"only\",\n\t\"until\",\n\t// Korean particles (조사)\n\t\"은\",\n\t\"는\",\n\t\"이\",\n\t\"가\",\n\t\"을\",\n\t\"를\",\n\t\"에\",\n\t\"의\",\n\t\"와\",\n\t\"과\",\n\t\"도\",\n\t\"로\",\n\t\"으로\",\n\t\"에서\",\n\t\"까지\",\n\t\"부터\",\n\t\"만\",\n\t\"라도\",\n\t\"나\",\n\t\"이며\",\n\t\"이고\",\n\t\"이나\",\n\t\"라\",\n\t\"이다\",\n\t\"하다\",\n\t\"된\",\n]);\n\n// ---------------------------------------------------------------------------\n// Tokenizer\n// ---------------------------------------------------------------------------\n\n/**\n * Tokenize text for relevance scoring.\n *\n * 1. Lowercase\n * 2. Split: Latin words via regex, Korean syllable groups via regex\n * 3. Remove stop words\n * 4. Remove short Latin tokens (<=2 chars)\n * 5. Deduplicate\n */\nexport function tokenizeForRelevance(text: string): readonly string[] {\n\tconst lower = text.toLowerCase();\n\tconst raw = lower.match(/[\\uac00-\\ud7a3]+|[a-z0-9]+/g);\n\tif (!raw) {\n\t\treturn [];\n\t}\n\n\tconst seen = new Set<string>();\n\tconst tokens: string[] = [];\n\n\tfor (const token of raw) {\n\t\t// Skip stop words\n\t\tif (STOP_WORDS.has(token)) {\n\t\t\tcontinue;\n\t\t}\n\t\t// For Latin tokens, skip very short ones (<=2 chars)\n\t\tif (/^[a-z0-9]+$/.test(token) && token.length <= 2) {\n\t\t\tcontinue;\n\t\t}\n\t\t// For Korean, keep 2+ syllable groups\n\t\tif (/^[\\uac00-\\ud7a3]+$/.test(token)) {\n\t\t\tif (token.length >= 2 && !seen.has(token)) {\n\t\t\t\tseen.add(token);\n\t\t\t\ttokens.push(token);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Deduplicate\n\t\tif (!seen.has(token)) {\n\t\t\tseen.add(token);\n\t\t\ttokens.push(token);\n\t\t}\n\t}\n\n\treturn tokens;\n}\n\n// ---------------------------------------------------------------------------\n// Coverage scoring\n// ---------------------------------------------------------------------------\n\n/**\n * Compute coverage: fraction of query tokens found in the item token set.\n * Optional per-token weights boost high-value matches (name, path).\n */\nfunction computeCoverage(\n\tqueryTokens: readonly string[],\n\titemTokenSet: ReadonlySet<string>,\n\titemTokenWeights?: ReadonlyMap<string, number>,\n): number {\n\tif (queryTokens.length === 0) {\n\t\treturn 0;\n\t}\n\n\tlet matchWeight = 0;\n\tfor (const qt of queryTokens) {\n\t\tif (itemTokenSet.has(qt)) {\n\t\t\tmatchWeight += itemTokenWeights?.get(qt) ?? 1;\n\t\t}\n\t}\n\n\t// Normalize: each query token contributes at most 1 to the numerator\n\treturn Math.min(1, matchWeight / queryTokens.length);\n}\n\n/**\n * Build a token set with optional high-weight tokens.\n */\nfunction buildWeightedTokenSet(\n\tbaseText: string,\n\thighWeightText: string | undefined,\n\tweight: number,\n): { tokens: Set<string>; weights: Map<string, number> } {\n\tconst baseTokens = tokenizeForRelevance(baseText);\n\tconst highTokens = highWeightText ? tokenizeForRelevance(highWeightText) : [];\n\n\tconst tokenSet = new Set<string>();\n\tconst weights = new Map<string, number>();\n\n\tfor (const t of baseTokens) {\n\t\ttokenSet.add(t);\n\t\tif (!weights.has(t)) {\n\t\t\tweights.set(t, 1);\n\t\t}\n\t}\n\tfor (const t of highTokens) {\n\t\ttokenSet.add(t);\n\t\tweights.set(t, Math.max(weights.get(t) ?? 0, weight));\n\t}\n\n\treturn { tokens: tokenSet, weights };\n}\n\n// ---------------------------------------------------------------------------\n// Skill relevance\n// ---------------------------------------------------------------------------\n\nconst SKILL_NEUTRAL_SCORE = 0.3;\nconst NAME_MATCH_BONUS = 0.2;\nconst NAME_TOKEN_WEIGHT = 3;\n\n/**\n * Score skill relevance against a query context.\n *\n * - queryContext undefined or empty -> 0.3 (neutral)\n * - Coverage of query tokens against skill name+description\n * - Name tokens weighted 3x\n * - Name substring match bonus: +0.2 (capped at 1.0)\n */\nexport function scoreSkillRelevance(\n\tskill: { readonly name: string; readonly description: string },\n\tqueryContext: string | undefined,\n): number {\n\tif (!queryContext || queryContext.trim() === \"\") {\n\t\treturn SKILL_NEUTRAL_SCORE;\n\t}\n\n\tconst queryTokens = tokenizeForRelevance(queryContext);\n\tif (queryTokens.length === 0) {\n\t\treturn SKILL_NEUTRAL_SCORE;\n\t}\n\n\t// Build skill token set: description is base, name tokens get 3x weight\n\tconst { tokens: skillTokenSet, weights: skillWeights } = buildWeightedTokenSet(\n\t\tskill.description,\n\t\tskill.name,\n\t\tNAME_TOKEN_WEIGHT,\n\t);\n\n\tlet score = computeCoverage(queryTokens, skillTokenSet, skillWeights);\n\n\t// Name substring bonus: if skill.name appears in query text (or vice versa)\n\tconst queryLower = queryContext.toLowerCase();\n\tconst nameLower = skill.name.toLowerCase();\n\tif (queryLower.includes(nameLower) || nameLower.includes(queryLower.trim())) {\n\t\tscore = Math.min(1, score + NAME_MATCH_BONUS);\n\t}\n\n\treturn clamp01(score);\n}\n\n// ---------------------------------------------------------------------------\n// Context file relevance\n// ---------------------------------------------------------------------------\n\nconst CONTEXT_NEUTRAL_GLOBAL = 0.9;\nconst CONTEXT_NEUTRAL_LOCAL = 0.6;\nconst PATH_TOKEN_WEIGHT = 2;\nconst GLOBAL_BONUS = 0.1;\nconst CONTENT_SAMPLE_CHARS = 2000;\n\n/**\n * Score context file relevance against a query context.\n *\n * - queryContext undefined -> isGlobal ? 0.9 : 0.6\n * - Coverage of query tokens against path segments + content sample\n * - Path tokens weighted 2x\n * - isGlobal bonus: +0.1\n */\nexport function scoreContextFileRelevance(\n\tcontextFile: { readonly path: string; readonly content: string; readonly isGlobal: boolean },\n\tqueryContext: string | undefined,\n): number {\n\tif (!queryContext || queryContext.trim() === \"\") {\n\t\treturn contextFile.isGlobal ? CONTEXT_NEUTRAL_GLOBAL : CONTEXT_NEUTRAL_LOCAL;\n\t}\n\n\tconst queryTokens = tokenizeForRelevance(queryContext);\n\tif (queryTokens.length === 0) {\n\t\treturn contextFile.isGlobal ? CONTEXT_NEUTRAL_GLOBAL : CONTEXT_NEUTRAL_LOCAL;\n\t}\n\n\t// Path segments as high-weight text\n\tconst pathText = extractPathSegments(contextFile.path);\n\n\t// Content sample for performance\n\tconst contentSample =\n\t\tcontextFile.content.length > CONTENT_SAMPLE_CHARS\n\t\t\t? contextFile.content.slice(0, CONTENT_SAMPLE_CHARS)\n\t\t\t: contextFile.content;\n\n\t// Build token set: content is base, path tokens get 2x weight\n\tconst { tokens: fileTokenSet, weights: fileWeights } = buildWeightedTokenSet(\n\t\tcontentSample,\n\t\tpathText,\n\t\tPATH_TOKEN_WEIGHT,\n\t);\n\n\tlet score = computeCoverage(queryTokens, fileTokenSet, fileWeights);\n\n\t// Global file bonus\n\tif (contextFile.isGlobal) {\n\t\tscore = Math.min(1, score + GLOBAL_BONUS);\n\t}\n\n\treturn clamp01(score);\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract meaningful segments from a file path for token matching.\n */\nfunction extractPathSegments(path: string): string {\n\treturn path\n\t\t.toLowerCase()\n\t\t.split(/[/\\\\._-]+/)\n\t\t.filter((seg) => seg.length > 0)\n\t\t.join(\" \");\n}\n\nfunction clamp01(value: number): number {\n\treturn Math.max(0, Math.min(1, value));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-relevance.js","sourceRoot":"","sources":["../../src/core/context-budget-relevance.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Query-aware relevance scoring for context budget items.\n *\n * Replaces heuristic proxies (description length, isGlobal hardcoding)\n * with token-based coverage scoring that measures semantic overlap\n * between the current query context and each resource item.\n *\n * Design: Coverage-based IR (not Jaccard) — avoids penalizing long\n * descriptions whose large union would dilute the score.\n */\n\n// ---------------------------------------------------------------------------\n// Stop words — English + common Korean particles\n// ---------------------------------------------------------------------------\n\nconst STOP_WORDS: ReadonlySet<string> = new Set([\n\t// English common stop words\n\t\"the\",\n\t\"a\",\n\t\"an\",\n\t\"is\",\n\t\"are\",\n\t\"was\",\n\t\"were\",\n\t\"be\",\n\t\"been\",\n\t\"being\",\n\t\"have\",\n\t\"has\",\n\t\"had\",\n\t\"do\",\n\t\"does\",\n\t\"did\",\n\t\"will\",\n\t\"would\",\n\t\"could\",\n\t\"should\",\n\t\"may\",\n\t\"might\",\n\t\"shall\",\n\t\"can\",\n\t\"to\",\n\t\"of\",\n\t\"in\",\n\t\"on\",\n\t\"for\",\n\t\"and\",\n\t\"or\",\n\t\"with\",\n\t\"how\",\n\t\"i\",\n\t\"you\",\n\t\"he\",\n\t\"she\",\n\t\"it\",\n\t\"we\",\n\t\"they\",\n\t\"me\",\n\t\"him\",\n\t\"her\",\n\t\"us\",\n\t\"them\",\n\t\"my\",\n\t\"your\",\n\t\"his\",\n\t\"its\",\n\t\"our\",\n\t\"their\",\n\t\"this\",\n\t\"that\",\n\t\"these\",\n\t\"those\",\n\t\"what\",\n\t\"which\",\n\t\"who\",\n\t\"whom\",\n\t\"where\",\n\t\"when\",\n\t\"why\",\n\t\"not\",\n\t\"no\",\n\t\"nor\",\n\t\"so\",\n\t\"if\",\n\t\"then\",\n\t\"than\",\n\t\"too\",\n\t\"very\",\n\t\"just\",\n\t\"about\",\n\t\"above\",\n\t\"after\",\n\t\"again\",\n\t\"all\",\n\t\"also\",\n\t\"am\",\n\t\"any\",\n\t\"because\",\n\t\"before\",\n\t\"between\",\n\t\"both\",\n\t\"but\",\n\t\"by\",\n\t\"from\",\n\t\"into\",\n\t\"more\",\n\t\"most\",\n\t\"other\",\n\t\"out\",\n\t\"over\",\n\t\"own\",\n\t\"same\",\n\t\"some\",\n\t\"such\",\n\t\"there\",\n\t\"up\",\n\t\"down\",\n\t\"here\",\n\t\"at\",\n\t\"as\",\n\t\"while\",\n\t\"through\",\n\t\"during\",\n\t\"each\",\n\t\"few\",\n\t\"further\",\n\t\"once\",\n\t\"only\",\n\t\"until\",\n\t// Korean particles (조사)\n\t\"은\",\n\t\"는\",\n\t\"이\",\n\t\"가\",\n\t\"을\",\n\t\"를\",\n\t\"에\",\n\t\"의\",\n\t\"와\",\n\t\"과\",\n\t\"도\",\n\t\"로\",\n\t\"으로\",\n\t\"에서\",\n\t\"까지\",\n\t\"부터\",\n\t\"만\",\n\t\"라도\",\n\t\"나\",\n\t\"이며\",\n\t\"이고\",\n\t\"이나\",\n\t\"라\",\n\t\"이다\",\n\t\"하다\",\n\t\"된\",\n]);\n\n// ---------------------------------------------------------------------------\n// Tokenizer\n// ---------------------------------------------------------------------------\n\n/**\n * Tokenize text for relevance scoring.\n *\n * 1. Lowercase\n * 2. Split: Latin words via regex, Korean syllable groups via regex\n * 3. Remove stop words\n * 4. Remove short Latin tokens (<=2 chars)\n * 5. Deduplicate\n */\nexport function tokenizeForRelevance(text: string): readonly string[] {\n\tconst lower = text.toLowerCase();\n\tconst raw = lower.match(/[\\uac00-\\ud7a3]+|[a-z0-9]+/g);\n\tif (!raw) {\n\t\treturn [];\n\t}\n\n\tconst seen = new Set<string>();\n\tconst tokens: string[] = [];\n\n\tfor (const token of raw) {\n\t\t// Skip stop words\n\t\tif (STOP_WORDS.has(token)) {\n\t\t\tcontinue;\n\t\t}\n\t\t// For Latin tokens, skip very short ones (<=2 chars)\n\t\tif (/^[a-z0-9]+$/.test(token) && token.length <= 2) {\n\t\t\tcontinue;\n\t\t}\n\t\t// For Korean, keep 2+ syllable groups\n\t\tif (/^[\\uac00-\\ud7a3]+$/.test(token)) {\n\t\t\tif (token.length >= 2 && !seen.has(token)) {\n\t\t\t\tseen.add(token);\n\t\t\t\ttokens.push(token);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Deduplicate\n\t\tif (!seen.has(token)) {\n\t\t\tseen.add(token);\n\t\t\ttokens.push(token);\n\t\t}\n\t}\n\n\treturn tokens;\n}\n\n// ---------------------------------------------------------------------------\n// Coverage scoring\n// ---------------------------------------------------------------------------\n\n/**\n * Compute coverage: fraction of query tokens found in the item token set.\n * Optional per-token weights boost high-value matches (name, path).\n */\nfunction computeCoverage(\n\tqueryTokens: readonly string[],\n\titemTokenSet: ReadonlySet<string>,\n\titemTokenWeights?: ReadonlyMap<string, number>,\n): number {\n\tif (queryTokens.length === 0) {\n\t\treturn 0;\n\t}\n\n\tlet matchWeight = 0;\n\tfor (const qt of queryTokens) {\n\t\tif (itemTokenSet.has(qt)) {\n\t\t\tmatchWeight += itemTokenWeights?.get(qt) ?? 1;\n\t\t}\n\t}\n\n\t// Normalize: each query token contributes at most 1 to the numerator\n\treturn Math.min(1, matchWeight / queryTokens.length);\n}\n\n/**\n * Build a token set with optional high-weight tokens.\n */\nfunction buildWeightedTokenSet(\n\tbaseText: string,\n\thighWeightText: string | undefined,\n\tweight: number,\n): { tokens: Set<string>; weights: Map<string, number> } {\n\tconst baseTokens = tokenizeForRelevance(baseText);\n\tconst highTokens = highWeightText ? tokenizeForRelevance(highWeightText) : [];\n\n\tconst tokenSet = new Set<string>();\n\tconst weights = new Map<string, number>();\n\n\tfor (const t of baseTokens) {\n\t\ttokenSet.add(t);\n\t\tif (!weights.has(t)) {\n\t\t\tweights.set(t, 1);\n\t\t}\n\t}\n\tfor (const t of highTokens) {\n\t\ttokenSet.add(t);\n\t\tweights.set(t, Math.max(weights.get(t) ?? 0, weight));\n\t}\n\n\treturn { tokens: tokenSet, weights };\n}\n\n// ---------------------------------------------------------------------------\n// Skill relevance\n// ---------------------------------------------------------------------------\n\nconst SKILL_NEUTRAL_SCORE = 0.3;\nconst NAME_MATCH_BONUS = 0.2;\nconst NAME_TOKEN_WEIGHT = 3;\n\n/**\n * Score skill relevance against a query context.\n *\n * - queryContext undefined or empty -> 0.3 (neutral)\n * - Coverage of query tokens against skill name+description\n * - Name tokens weighted 3x\n * - Name substring match bonus: +0.2 (capped at 1.0)\n */\nexport function scoreSkillRelevance(\n\tskill: { readonly name: string; readonly description: string },\n\tqueryContext: string | undefined,\n): number {\n\tif (!queryContext || queryContext.trim() === \"\") {\n\t\treturn SKILL_NEUTRAL_SCORE;\n\t}\n\n\tconst queryTokens = tokenizeForRelevance(queryContext);\n\tif (queryTokens.length === 0) {\n\t\treturn SKILL_NEUTRAL_SCORE;\n\t}\n\n\t// Build skill token set: description is base, name tokens get 3x weight\n\tconst { tokens: skillTokenSet, weights: skillWeights } = buildWeightedTokenSet(\n\t\tskill.description,\n\t\tskill.name,\n\t\tNAME_TOKEN_WEIGHT,\n\t);\n\n\tlet score = computeCoverage(queryTokens, skillTokenSet, skillWeights);\n\n\t// Name substring bonus: if skill.name appears in query text (or vice versa)\n\tconst queryLower = queryContext.toLowerCase();\n\tconst nameLower = skill.name.toLowerCase();\n\tif (queryLower.includes(nameLower) || nameLower.includes(queryLower.trim())) {\n\t\tscore = Math.min(1, score + NAME_MATCH_BONUS);\n\t}\n\n\treturn clamp01(score);\n}\n\n// ---------------------------------------------------------------------------\n// Context file relevance\n// ---------------------------------------------------------------------------\n\nconst CONTEXT_NEUTRAL_GLOBAL = 0.9;\nconst CONTEXT_NEUTRAL_LOCAL = 0.6;\nconst PATH_TOKEN_WEIGHT = 2;\nconst CONTENT_SAMPLE_CHARS = 2000;\n\n/**\n * Score context file relevance against a query context.\n *\n * - Baseline (no query evidence) -> isGlobal ? 0.9 : 0.6\n * - Coverage of query tokens against path segments + content sample\n * - Path tokens weighted 2x\n * - Coverage allocates the headroom ABOVE the baseline; it never cuts into it\n *\n * A context file is not retrieved reference material — it is the standing\n * instruction set the agent is required to follow, and it applies whether or\n * not the user's current sentence reuses its vocabulary. Scoring it purely by\n * lexical overlap made the rules rank LOWEST exactly when the agent was doing\n * work: this repository's own AGENTS.md scored 0.9 idle but 0.1 under queries\n * like \"rename this variable\". Coverage therefore raises the score from the\n * baseline toward 1 instead of replacing it, which also reconciles the two\n * readings the function used to have — an absent query and a present-but-\n * non-overlapping query now agree that there is simply no evidence to add.\n *\n * Lexical scoring stays the right metric for skills, which really are\n * topic-scoped; see scoreSkillRelevance.\n */\nexport function scoreContextFileRelevance(\n\tcontextFile: { readonly path: string; readonly content: string; readonly isGlobal: boolean },\n\tqueryContext: string | undefined,\n): number {\n\tif (!queryContext || queryContext.trim() === \"\") {\n\t\treturn contextFile.isGlobal ? CONTEXT_NEUTRAL_GLOBAL : CONTEXT_NEUTRAL_LOCAL;\n\t}\n\n\tconst queryTokens = tokenizeForRelevance(queryContext);\n\tif (queryTokens.length === 0) {\n\t\treturn contextFile.isGlobal ? CONTEXT_NEUTRAL_GLOBAL : CONTEXT_NEUTRAL_LOCAL;\n\t}\n\n\t// Path segments as high-weight text\n\tconst pathText = extractPathSegments(contextFile.path);\n\n\t// Content sample for performance\n\tconst contentSample =\n\t\tcontextFile.content.length > CONTENT_SAMPLE_CHARS\n\t\t\t? contextFile.content.slice(0, CONTENT_SAMPLE_CHARS)\n\t\t\t: contextFile.content;\n\n\t// Build token set: content is base, path tokens get 2x weight\n\tconst { tokens: fileTokenSet, weights: fileWeights } = buildWeightedTokenSet(\n\t\tcontentSample,\n\t\tpathText,\n\t\tPATH_TOKEN_WEIGHT,\n\t);\n\n\tconst coverage = computeCoverage(queryTokens, fileTokenSet, fileWeights);\n\n\t// The baseline is a floor, not a starting guess: query evidence distributes\n\t// the remaining headroom above it. Zero coverage returns the baseline exactly,\n\t// matching the no-query branch above.\n\tconst baseline = contextFile.isGlobal ? CONTEXT_NEUTRAL_GLOBAL : CONTEXT_NEUTRAL_LOCAL;\n\treturn clamp01(baseline + (1 - baseline) * clamp01(coverage));\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract meaningful segments from a file path for token matching.\n */\nfunction extractPathSegments(path: string): string {\n\treturn path\n\t\t.toLowerCase()\n\t\t.split(/[/\\\\._-]+/)\n\t\t.filter((seg) => seg.length > 0)\n\t\t.join(\" \");\n}\n\nfunction clamp01(value: number): number {\n\treturn Math.max(0, Math.min(1, value));\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ContextBudgetItemV2 } from "./context-budget-governor-v2.ts";
|
|
2
|
-
import type { ContextFile } from "./
|
|
2
|
+
import type { ContextFile } from "./context-file.ts";
|
|
3
3
|
import type { Skill } from "./skills.ts";
|
|
4
4
|
interface SystemPromptBudgetItemInput {
|
|
5
5
|
readonly contextFiles: readonly ContextFile[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-system-prompt-items.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt-items.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,mBAAmB,EAAY,MAAM,iCAAiC,CAAC;AAErF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"context-budget-system-prompt-items.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt-items.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,mBAAmB,EAAY,MAAM,iCAAiC,CAAC;AAErF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,UAAU,2BAA2B;IACpC,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE;QACjB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC9C,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;QACzC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;QAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;KAC/B,CAAC;CACF;AAQD,wBAAgB,6BAA6B,CAC5C,KAAK,EAAE,2BAA2B,EAClC,cAAc,EAAE,MAAM,GACpB,mBAAmB,EAAE,CAqBvB;AAyID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C","sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport { type ContextBudgetItemV2, fnv1aHex } from \"./context-budget-governor-v2.ts\";\nimport { scoreContextFileRelevance, scoreSkillRelevance } from \"./context-budget-relevance.ts\";\nimport type { ContextFile } from \"./context-file.ts\";\nimport type { Skill } from \"./skills.ts\";\n\ninterface SystemPromptBudgetItemInput {\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly Skill[];\n\treadonly includeSkills: boolean;\n\treadonly options: {\n\t\treadonly activeSkillNames?: readonly string[];\n\t\treadonly includeSkillInventory?: boolean;\n\t\treadonly includeFullContextFiles?: boolean;\n\t\treadonly maxInactiveSkills?: number;\n\t\treadonly queryContext?: string;\n\t};\n}\n\nconst CONTEXT_EXCERPT_CHARS = 1200;\nconst MIN_EXCERPT_CHARS = 400;\nconst MAX_EXCERPT_CHARS = 3000;\nconst MAX_INACTIVE_SKILLS = 15;\nconst CONTEXT_FULL_BUDGET_RATIO = 0.5;\n\nexport function createSystemPromptBudgetItems(\n\tinput: SystemPromptBudgetItemInput,\n\tresourceBudget: number,\n): ContextBudgetItemV2[] {\n\tconst items: ContextBudgetItemV2[] = [];\n\tconst contextFullCount = input.options.includeFullContextFiles !== false ? input.contextFiles.length : 0;\n\tconst excerptChars = computeExcerptChars(resourceBudget, contextFullCount);\n\n\tfor (const contextFile of input.contextFiles) {\n\t\tconst queryScore = scoreContextFileRelevance(\n\t\t\t{ path: contextFile.path, content: contextFile.content, isGlobal: contextFile.isGlobal ?? false },\n\t\t\tinput.options.queryContext,\n\t\t);\n\t\tpushContextItems(items, contextFile, queryScore, excerptChars, input.options.includeFullContextFiles !== false);\n\t}\n\n\tconst activeSkillNames = new Set(input.options.activeSkillNames ?? []);\n\tconst visibleSkills = input.skills.filter(\n\t\t(skill) => !skill.disableModelInvocation || activeSkillNames.has(skill.name),\n\t);\n\tif (input.includeSkills && input.options.includeSkillInventory !== false && visibleSkills.length > 0) {\n\t\tpushSkillItems(items, visibleSkills, input.options);\n\t}\n\treturn items;\n}\n\nfunction pushContextItems(\n\titems: ContextBudgetItemV2[],\n\tcontextFile: ContextFile,\n\tqueryScore: number,\n\texcerptChars: number,\n\tincludeFullContextFiles: boolean,\n): void {\n\tconst scope = contextFile.isGlobal ? \"parent\" : \"project\";\n\tconst tagName = contextFile.isGlobal ? \"parent_instructions\" : \"project_instructions\";\n\titems.push({\n\t\tid: `context-pointer:${contextFile.path}`,\n\t\ttier: contextFile.isGlobal ? \"system\" : \"current-files\",\n\t\tpriority: contextFile.isGlobal ? \"hard\" : \"high\",\n\t\trelevance: (contextFile.isGlobal ? 1 : 0.8) * 0.4 + queryScore * 0.6,\n\t\ttext: renderContextPointer(contextFile, scope),\n\t});\n\tif (!includeFullContextFiles) return;\n\titems.push({\n\t\tid: `context-full:${contextFile.path}`,\n\t\ttier: contextFile.isGlobal ? \"system\" : \"current-files\",\n\t\tpriority: contextFile.isGlobal ? \"high\" : \"medium\",\n\t\trelevance: (contextFile.isGlobal ? 0.9 : 0.6) * 0.4 + queryScore * 0.6,\n\t\tredundancyKey: contextFile.path,\n\t\tsourceRef: {\n\t\t\turi: pathToFileURL(contextFile.path).href,\n\t\t\tcontentHash: fnv1aHex(contextFile.content),\n\t\t\tretrievable: true,\n\t\t},\n\t\ttext: renderContextFull(contextFile, tagName, excerptChars),\n\t});\n}\n\nfunction pushSkillItems(\n\titems: ContextBudgetItemV2[],\n\tvisibleSkills: readonly Skill[],\n\toptions: SystemPromptBudgetItemInput[\"options\"],\n): void {\n\titems.push({ id: \"skill-header\", tier: \"skills\", priority: \"hard\", text: renderSkillHeader() });\n\tconst activeSkillNames = new Set(options.activeSkillNames ?? []);\n\tconst maxInactive = options.maxInactiveSkills ?? MAX_INACTIVE_SKILLS;\n\tfor (const skill of visibleSkills.filter((skill) => activeSkillNames.has(skill.name))) {\n\t\titems.push(renderSkillBudgetItem(skill, \"hard\", 1));\n\t}\n\n\tconst sortedInactive = visibleSkills\n\t\t.filter((skill) => !activeSkillNames.has(skill.name))\n\t\t.sort((a, b) => scoreSkillRelevance(b, options.queryContext) - scoreSkillRelevance(a, options.queryContext));\n\tfor (const skill of sortedInactive.slice(0, maxInactive)) {\n\t\titems.push(renderSkillBudgetItem(skill, \"low\", Math.max(0.25, scoreSkillRelevance(skill, options.queryContext))));\n\t}\n\titems.push({ id: \"skill-footer\", tier: \"skills\", priority: \"hard\", text: \"</available_skills>\" });\n\n\tconst omittedInactiveCount = sortedInactive.length - Math.min(sortedInactive.length, maxInactive);\n\tif (omittedInactiveCount > 0) {\n\t\titems.push({\n\t\t\tid: \"skill-omitted-summary\",\n\t\t\ttier: \"skills\",\n\t\t\tpriority: \"low\",\n\t\t\trelevance: 0.1,\n\t\t\ttext: `<!-- ${omittedInactiveCount} additional skills available. Use 'read' to load a specific skill file when a task matches. -->`,\n\t\t});\n\t}\n}\n\nfunction renderSkillBudgetItem(skill: Skill, priority: \"hard\" | \"low\", relevance: number): ContextBudgetItemV2 {\n\treturn {\n\t\tid: `skill:${skill.name}`,\n\t\ttier: \"skills\",\n\t\tpriority,\n\t\trelevance,\n\t\tredundancyKey: skill.name,\n\t\ttext: renderSkillEntry(skill, priority === \"hard\"),\n\t};\n}\n\nfunction computeExcerptChars(resourceBudget: number, contextFullCount: number): number {\n\tif (contextFullCount <= 0) {\n\t\treturn CONTEXT_EXCERPT_CHARS;\n\t}\n\tconst allocatedTokens = Math.floor(resourceBudget * CONTEXT_FULL_BUDGET_RATIO) / contextFullCount;\n\tconst estimatedChars = Math.floor(allocatedTokens * 4);\n\treturn Math.max(MIN_EXCERPT_CHARS, Math.min(MAX_EXCERPT_CHARS, estimatedChars));\n}\n\nfunction renderContextPointer(contextFile: ContextFile, scope: string): string {\n\tconst marker = contextFile.containsJailbreak ? \" sanitized=true\" : \"\";\n\treturn `<context_file_pointer scope=\"${escapeXml(scope)}\" path=\"${escapeXml(contextFile.path)}\"${marker} chars=\"${contextFile.content.length}\" />`;\n}\n\nfunction renderContextFull(contextFile: ContextFile, tagName: string, excerptChars = CONTEXT_EXCERPT_CHARS): string {\n\tconst content =\n\t\tcontextFile.content.length > excerptChars\n\t\t\t? boundedExcerpt(contextFile.content, excerptChars)\n\t\t\t: contextFile.content;\n\tconst truncated = content.length < contextFile.content.length ? \" truncated=true\" : \"\";\n\treturn `<${tagName} path=\"${escapeXml(contextFile.path)}\"${truncated}>\\n${escapeXmlText(content)}\\n</${tagName}>`;\n}\n\nfunction boundedExcerpt(content: string, excerptChars = CONTEXT_EXCERPT_CHARS): string {\n\tconst headChars = Math.floor(excerptChars * 0.7);\n\tconst tailChars = excerptChars - headChars;\n\tlet head = content.slice(0, headChars);\n\tconst lastSpace = head.search(/\\s+\\S*$/);\n\tif (lastSpace > headChars * 0.5) {\n\t\thead = head.slice(0, lastSpace);\n\t}\n\tlet tail = content.slice(-tailChars);\n\tconst firstSpace = tail.search(/\\S+\\s+/);\n\tif (firstSpace > 0 && firstSpace < tailChars * 0.5) {\n\t\ttail = tail.slice(firstSpace).trimStart();\n\t}\n\treturn `${head}\\n\\n[...context-budget excerpt omitted ${content.length - excerptChars} chars...]\\n\\n${tail}`;\n}\n\nfunction renderSkillHeader(): string {\n\treturn [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t].join(\"\\n\");\n}\n\nfunction renderSkillEntry(skill: Skill, active = false): string {\n\treturn [\n\t\t\" <skill>\",\n\t\t` <name>${escapeXml(skill.name)}</name>`,\n\t\t` <description>${escapeXml(skill.description)}</description>`,\n\t\t...(active ? [\" <activation>active</activation>\"] : []),\n\t\t` <location>${escapeXml(skill.filePath)}</location>`,\n\t\t\" </skill>\",\n\t].join(\"\\n\");\n}\n\nexport function escapeXmlText(str: string): string {\n\treturn str.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function escapeXml(str: string): string {\n\treturn escapeXmlText(str).replace(/\"/g, \""\").replace(/'/g, \"'\");\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-system-prompt-items.js","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt-items.ts"],"names":[],"mappings":"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","sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport { type ContextBudgetItemV2, fnv1aHex } from \"./context-budget-governor-v2.ts\";\nimport { scoreContextFileRelevance, scoreSkillRelevance } from \"./context-budget-relevance.ts\";\nimport type { ContextFile } from \"./resource-loader.ts\";\nimport type { Skill } from \"./skills.ts\";\n\ninterface SystemPromptBudgetItemInput {\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly Skill[];\n\treadonly includeSkills: boolean;\n\treadonly options: {\n\t\treadonly activeSkillNames?: readonly string[];\n\t\treadonly includeSkillInventory?: boolean;\n\t\treadonly includeFullContextFiles?: boolean;\n\t\treadonly maxInactiveSkills?: number;\n\t\treadonly queryContext?: string;\n\t};\n}\n\nconst CONTEXT_EXCERPT_CHARS = 1200;\nconst MIN_EXCERPT_CHARS = 400;\nconst MAX_EXCERPT_CHARS = 3000;\nconst MAX_INACTIVE_SKILLS = 15;\nconst CONTEXT_FULL_BUDGET_RATIO = 0.5;\n\nexport function createSystemPromptBudgetItems(\n\tinput: SystemPromptBudgetItemInput,\n\tresourceBudget: number,\n): ContextBudgetItemV2[] {\n\tconst items: ContextBudgetItemV2[] = [];\n\tconst contextFullCount = input.options.includeFullContextFiles !== false ? input.contextFiles.length : 0;\n\tconst excerptChars = computeExcerptChars(resourceBudget, contextFullCount);\n\n\tfor (const contextFile of input.contextFiles) {\n\t\tconst queryScore = scoreContextFileRelevance(\n\t\t\t{ path: contextFile.path, content: contextFile.content, isGlobal: contextFile.isGlobal ?? false },\n\t\t\tinput.options.queryContext,\n\t\t);\n\t\tpushContextItems(items, contextFile, queryScore, excerptChars, input.options.includeFullContextFiles !== false);\n\t}\n\n\tconst activeSkillNames = new Set(input.options.activeSkillNames ?? []);\n\tconst visibleSkills = input.skills.filter(\n\t\t(skill) => !skill.disableModelInvocation || activeSkillNames.has(skill.name),\n\t);\n\tif (input.includeSkills && input.options.includeSkillInventory !== false && visibleSkills.length > 0) {\n\t\tpushSkillItems(items, visibleSkills, input.options);\n\t}\n\treturn items;\n}\n\nfunction pushContextItems(\n\titems: ContextBudgetItemV2[],\n\tcontextFile: ContextFile,\n\tqueryScore: number,\n\texcerptChars: number,\n\tincludeFullContextFiles: boolean,\n): void {\n\tconst scope = contextFile.isGlobal ? \"parent\" : \"project\";\n\tconst tagName = contextFile.isGlobal ? \"parent_instructions\" : \"project_instructions\";\n\titems.push({\n\t\tid: `context-pointer:${contextFile.path}`,\n\t\ttier: contextFile.isGlobal ? \"system\" : \"current-files\",\n\t\tpriority: contextFile.isGlobal ? \"hard\" : \"high\",\n\t\trelevance: (contextFile.isGlobal ? 1 : 0.8) * 0.4 + queryScore * 0.6,\n\t\ttext: renderContextPointer(contextFile, scope),\n\t});\n\tif (!includeFullContextFiles) return;\n\titems.push({\n\t\tid: `context-full:${contextFile.path}`,\n\t\ttier: contextFile.isGlobal ? \"system\" : \"current-files\",\n\t\tpriority: contextFile.isGlobal ? \"high\" : \"medium\",\n\t\trelevance: (contextFile.isGlobal ? 0.9 : 0.6) * 0.4 + queryScore * 0.6,\n\t\tredundancyKey: contextFile.path,\n\t\tsourceRef: {\n\t\t\turi: pathToFileURL(contextFile.path).href,\n\t\t\tcontentHash: fnv1aHex(contextFile.content),\n\t\t\tretrievable: true,\n\t\t},\n\t\ttext: renderContextFull(contextFile, tagName, excerptChars),\n\t});\n}\n\nfunction pushSkillItems(\n\titems: ContextBudgetItemV2[],\n\tvisibleSkills: readonly Skill[],\n\toptions: SystemPromptBudgetItemInput[\"options\"],\n): void {\n\titems.push({ id: \"skill-header\", tier: \"skills\", priority: \"hard\", text: renderSkillHeader() });\n\tconst activeSkillNames = new Set(options.activeSkillNames ?? []);\n\tconst maxInactive = options.maxInactiveSkills ?? MAX_INACTIVE_SKILLS;\n\tfor (const skill of visibleSkills.filter((skill) => activeSkillNames.has(skill.name))) {\n\t\titems.push(renderSkillBudgetItem(skill, \"hard\", 1));\n\t}\n\n\tconst sortedInactive = visibleSkills\n\t\t.filter((skill) => !activeSkillNames.has(skill.name))\n\t\t.sort((a, b) => scoreSkillRelevance(b, options.queryContext) - scoreSkillRelevance(a, options.queryContext));\n\tfor (const skill of sortedInactive.slice(0, maxInactive)) {\n\t\titems.push(renderSkillBudgetItem(skill, \"low\", Math.max(0.25, scoreSkillRelevance(skill, options.queryContext))));\n\t}\n\titems.push({ id: \"skill-footer\", tier: \"skills\", priority: \"hard\", text: \"</available_skills>\" });\n\n\tconst omittedInactiveCount = sortedInactive.length - Math.min(sortedInactive.length, maxInactive);\n\tif (omittedInactiveCount > 0) {\n\t\titems.push({\n\t\t\tid: \"skill-omitted-summary\",\n\t\t\ttier: \"skills\",\n\t\t\tpriority: \"low\",\n\t\t\trelevance: 0.1,\n\t\t\ttext: `<!-- ${omittedInactiveCount} additional skills available. Use 'read' to load a specific skill file when a task matches. -->`,\n\t\t});\n\t}\n}\n\nfunction renderSkillBudgetItem(skill: Skill, priority: \"hard\" | \"low\", relevance: number): ContextBudgetItemV2 {\n\treturn {\n\t\tid: `skill:${skill.name}`,\n\t\ttier: \"skills\",\n\t\tpriority,\n\t\trelevance,\n\t\tredundancyKey: skill.name,\n\t\ttext: renderSkillEntry(skill, priority === \"hard\"),\n\t};\n}\n\nfunction computeExcerptChars(resourceBudget: number, contextFullCount: number): number {\n\tif (contextFullCount <= 0) {\n\t\treturn CONTEXT_EXCERPT_CHARS;\n\t}\n\tconst allocatedTokens = Math.floor(resourceBudget * CONTEXT_FULL_BUDGET_RATIO) / contextFullCount;\n\tconst estimatedChars = Math.floor(allocatedTokens * 4);\n\treturn Math.max(MIN_EXCERPT_CHARS, Math.min(MAX_EXCERPT_CHARS, estimatedChars));\n}\n\nfunction renderContextPointer(contextFile: ContextFile, scope: string): string {\n\tconst marker = contextFile.containsJailbreak ? \" sanitized=true\" : \"\";\n\treturn `<context_file_pointer scope=\"${escapeXml(scope)}\" path=\"${escapeXml(contextFile.path)}\"${marker} chars=\"${contextFile.content.length}\" />`;\n}\n\nfunction renderContextFull(contextFile: ContextFile, tagName: string, excerptChars = CONTEXT_EXCERPT_CHARS): string {\n\tconst content =\n\t\tcontextFile.content.length > excerptChars\n\t\t\t? boundedExcerpt(contextFile.content, excerptChars)\n\t\t\t: contextFile.content;\n\tconst truncated = content.length < contextFile.content.length ? \" truncated=true\" : \"\";\n\treturn `<${tagName} path=\"${escapeXml(contextFile.path)}\"${truncated}>\\n${escapeXmlText(content)}\\n</${tagName}>`;\n}\n\nfunction boundedExcerpt(content: string, excerptChars = CONTEXT_EXCERPT_CHARS): string {\n\tconst headChars = Math.floor(excerptChars * 0.7);\n\tconst tailChars = excerptChars - headChars;\n\tlet head = content.slice(0, headChars);\n\tconst lastSpace = head.search(/\\s+\\S*$/);\n\tif (lastSpace > headChars * 0.5) {\n\t\thead = head.slice(0, lastSpace);\n\t}\n\tlet tail = content.slice(-tailChars);\n\tconst firstSpace = tail.search(/\\S+\\s+/);\n\tif (firstSpace > 0 && firstSpace < tailChars * 0.5) {\n\t\ttail = tail.slice(firstSpace).trimStart();\n\t}\n\treturn `${head}\\n\\n[...context-budget excerpt omitted ${content.length - excerptChars} chars...]\\n\\n${tail}`;\n}\n\nfunction renderSkillHeader(): string {\n\treturn [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t].join(\"\\n\");\n}\n\nfunction renderSkillEntry(skill: Skill, active = false): string {\n\treturn [\n\t\t\" <skill>\",\n\t\t` <name>${escapeXml(skill.name)}</name>`,\n\t\t` <description>${escapeXml(skill.description)}</description>`,\n\t\t...(active ? [\" <activation>active</activation>\"] : []),\n\t\t` <location>${escapeXml(skill.filePath)}</location>`,\n\t\t\" </skill>\",\n\t].join(\"\\n\");\n}\n\nexport function escapeXmlText(str: string): string {\n\treturn str.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function escapeXml(str: string): string {\n\treturn escapeXmlText(str).replace(/\"/g, \""\").replace(/'/g, \"'\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-system-prompt-items.js","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt-items.ts"],"names":[],"mappings":"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","sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport { type ContextBudgetItemV2, fnv1aHex } from \"./context-budget-governor-v2.ts\";\nimport { scoreContextFileRelevance, scoreSkillRelevance } from \"./context-budget-relevance.ts\";\nimport type { ContextFile } from \"./context-file.ts\";\nimport type { Skill } from \"./skills.ts\";\n\ninterface SystemPromptBudgetItemInput {\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly Skill[];\n\treadonly includeSkills: boolean;\n\treadonly options: {\n\t\treadonly activeSkillNames?: readonly string[];\n\t\treadonly includeSkillInventory?: boolean;\n\t\treadonly includeFullContextFiles?: boolean;\n\t\treadonly maxInactiveSkills?: number;\n\t\treadonly queryContext?: string;\n\t};\n}\n\nconst CONTEXT_EXCERPT_CHARS = 1200;\nconst MIN_EXCERPT_CHARS = 400;\nconst MAX_EXCERPT_CHARS = 3000;\nconst MAX_INACTIVE_SKILLS = 15;\nconst CONTEXT_FULL_BUDGET_RATIO = 0.5;\n\nexport function createSystemPromptBudgetItems(\n\tinput: SystemPromptBudgetItemInput,\n\tresourceBudget: number,\n): ContextBudgetItemV2[] {\n\tconst items: ContextBudgetItemV2[] = [];\n\tconst contextFullCount = input.options.includeFullContextFiles !== false ? input.contextFiles.length : 0;\n\tconst excerptChars = computeExcerptChars(resourceBudget, contextFullCount);\n\n\tfor (const contextFile of input.contextFiles) {\n\t\tconst queryScore = scoreContextFileRelevance(\n\t\t\t{ path: contextFile.path, content: contextFile.content, isGlobal: contextFile.isGlobal ?? false },\n\t\t\tinput.options.queryContext,\n\t\t);\n\t\tpushContextItems(items, contextFile, queryScore, excerptChars, input.options.includeFullContextFiles !== false);\n\t}\n\n\tconst activeSkillNames = new Set(input.options.activeSkillNames ?? []);\n\tconst visibleSkills = input.skills.filter(\n\t\t(skill) => !skill.disableModelInvocation || activeSkillNames.has(skill.name),\n\t);\n\tif (input.includeSkills && input.options.includeSkillInventory !== false && visibleSkills.length > 0) {\n\t\tpushSkillItems(items, visibleSkills, input.options);\n\t}\n\treturn items;\n}\n\nfunction pushContextItems(\n\titems: ContextBudgetItemV2[],\n\tcontextFile: ContextFile,\n\tqueryScore: number,\n\texcerptChars: number,\n\tincludeFullContextFiles: boolean,\n): void {\n\tconst scope = contextFile.isGlobal ? \"parent\" : \"project\";\n\tconst tagName = contextFile.isGlobal ? \"parent_instructions\" : \"project_instructions\";\n\titems.push({\n\t\tid: `context-pointer:${contextFile.path}`,\n\t\ttier: contextFile.isGlobal ? \"system\" : \"current-files\",\n\t\tpriority: contextFile.isGlobal ? \"hard\" : \"high\",\n\t\trelevance: (contextFile.isGlobal ? 1 : 0.8) * 0.4 + queryScore * 0.6,\n\t\ttext: renderContextPointer(contextFile, scope),\n\t});\n\tif (!includeFullContextFiles) return;\n\titems.push({\n\t\tid: `context-full:${contextFile.path}`,\n\t\ttier: contextFile.isGlobal ? \"system\" : \"current-files\",\n\t\tpriority: contextFile.isGlobal ? \"high\" : \"medium\",\n\t\trelevance: (contextFile.isGlobal ? 0.9 : 0.6) * 0.4 + queryScore * 0.6,\n\t\tredundancyKey: contextFile.path,\n\t\tsourceRef: {\n\t\t\turi: pathToFileURL(contextFile.path).href,\n\t\t\tcontentHash: fnv1aHex(contextFile.content),\n\t\t\tretrievable: true,\n\t\t},\n\t\ttext: renderContextFull(contextFile, tagName, excerptChars),\n\t});\n}\n\nfunction pushSkillItems(\n\titems: ContextBudgetItemV2[],\n\tvisibleSkills: readonly Skill[],\n\toptions: SystemPromptBudgetItemInput[\"options\"],\n): void {\n\titems.push({ id: \"skill-header\", tier: \"skills\", priority: \"hard\", text: renderSkillHeader() });\n\tconst activeSkillNames = new Set(options.activeSkillNames ?? []);\n\tconst maxInactive = options.maxInactiveSkills ?? MAX_INACTIVE_SKILLS;\n\tfor (const skill of visibleSkills.filter((skill) => activeSkillNames.has(skill.name))) {\n\t\titems.push(renderSkillBudgetItem(skill, \"hard\", 1));\n\t}\n\n\tconst sortedInactive = visibleSkills\n\t\t.filter((skill) => !activeSkillNames.has(skill.name))\n\t\t.sort((a, b) => scoreSkillRelevance(b, options.queryContext) - scoreSkillRelevance(a, options.queryContext));\n\tfor (const skill of sortedInactive.slice(0, maxInactive)) {\n\t\titems.push(renderSkillBudgetItem(skill, \"low\", Math.max(0.25, scoreSkillRelevance(skill, options.queryContext))));\n\t}\n\titems.push({ id: \"skill-footer\", tier: \"skills\", priority: \"hard\", text: \"</available_skills>\" });\n\n\tconst omittedInactiveCount = sortedInactive.length - Math.min(sortedInactive.length, maxInactive);\n\tif (omittedInactiveCount > 0) {\n\t\titems.push({\n\t\t\tid: \"skill-omitted-summary\",\n\t\t\ttier: \"skills\",\n\t\t\tpriority: \"low\",\n\t\t\trelevance: 0.1,\n\t\t\ttext: `<!-- ${omittedInactiveCount} additional skills available. Use 'read' to load a specific skill file when a task matches. -->`,\n\t\t});\n\t}\n}\n\nfunction renderSkillBudgetItem(skill: Skill, priority: \"hard\" | \"low\", relevance: number): ContextBudgetItemV2 {\n\treturn {\n\t\tid: `skill:${skill.name}`,\n\t\ttier: \"skills\",\n\t\tpriority,\n\t\trelevance,\n\t\tredundancyKey: skill.name,\n\t\ttext: renderSkillEntry(skill, priority === \"hard\"),\n\t};\n}\n\nfunction computeExcerptChars(resourceBudget: number, contextFullCount: number): number {\n\tif (contextFullCount <= 0) {\n\t\treturn CONTEXT_EXCERPT_CHARS;\n\t}\n\tconst allocatedTokens = Math.floor(resourceBudget * CONTEXT_FULL_BUDGET_RATIO) / contextFullCount;\n\tconst estimatedChars = Math.floor(allocatedTokens * 4);\n\treturn Math.max(MIN_EXCERPT_CHARS, Math.min(MAX_EXCERPT_CHARS, estimatedChars));\n}\n\nfunction renderContextPointer(contextFile: ContextFile, scope: string): string {\n\tconst marker = contextFile.containsJailbreak ? \" sanitized=true\" : \"\";\n\treturn `<context_file_pointer scope=\"${escapeXml(scope)}\" path=\"${escapeXml(contextFile.path)}\"${marker} chars=\"${contextFile.content.length}\" />`;\n}\n\nfunction renderContextFull(contextFile: ContextFile, tagName: string, excerptChars = CONTEXT_EXCERPT_CHARS): string {\n\tconst content =\n\t\tcontextFile.content.length > excerptChars\n\t\t\t? boundedExcerpt(contextFile.content, excerptChars)\n\t\t\t: contextFile.content;\n\tconst truncated = content.length < contextFile.content.length ? \" truncated=true\" : \"\";\n\treturn `<${tagName} path=\"${escapeXml(contextFile.path)}\"${truncated}>\\n${escapeXmlText(content)}\\n</${tagName}>`;\n}\n\nfunction boundedExcerpt(content: string, excerptChars = CONTEXT_EXCERPT_CHARS): string {\n\tconst headChars = Math.floor(excerptChars * 0.7);\n\tconst tailChars = excerptChars - headChars;\n\tlet head = content.slice(0, headChars);\n\tconst lastSpace = head.search(/\\s+\\S*$/);\n\tif (lastSpace > headChars * 0.5) {\n\t\thead = head.slice(0, lastSpace);\n\t}\n\tlet tail = content.slice(-tailChars);\n\tconst firstSpace = tail.search(/\\S+\\s+/);\n\tif (firstSpace > 0 && firstSpace < tailChars * 0.5) {\n\t\ttail = tail.slice(firstSpace).trimStart();\n\t}\n\treturn `${head}\\n\\n[...context-budget excerpt omitted ${content.length - excerptChars} chars...]\\n\\n${tail}`;\n}\n\nfunction renderSkillHeader(): string {\n\treturn [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t].join(\"\\n\");\n}\n\nfunction renderSkillEntry(skill: Skill, active = false): string {\n\treturn [\n\t\t\" <skill>\",\n\t\t` <name>${escapeXml(skill.name)}</name>`,\n\t\t` <description>${escapeXml(skill.description)}</description>`,\n\t\t...(active ? [\" <activation>active</activation>\"] : []),\n\t\t` <location>${escapeXml(skill.filePath)}</location>`,\n\t\t\" </skill>\",\n\t].join(\"\\n\");\n}\n\nexport function escapeXmlText(str: string): string {\n\treturn str.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nexport function escapeXml(str: string): string {\n\treturn escapeXmlText(str).replace(/\"/g, \""\").replace(/'/g, \"'\");\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ContextBudgetCacheProviderV2, type PromptContextBudgetPlanV2 } from "./context-budget-governor-v2.ts";
|
|
2
2
|
import { type ContextBudgetTokenizerMode, type TokenCounterAdapter } from "./context-budget-token-counter.ts";
|
|
3
|
-
import type { ContextFile } from "./
|
|
3
|
+
import type { ContextFile } from "./context-file.ts";
|
|
4
4
|
import type { Skill } from "./skills.ts";
|
|
5
5
|
export interface SystemPromptContextBudgetOptions {
|
|
6
6
|
readonly maxPromptTokens: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,4BAA4B,EAEjC,KAAK,yBAAyB,EAE9B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACN,KAAK,0BAA0B,EAE/B,KAAK,mBAAmB,EACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"context-budget-system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,4BAA4B,EAEjC,KAAK,yBAAyB,EAE9B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACN,KAAK,0BAA0B,EAE/B,KAAK,mBAAmB,EACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,gCAAgC;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,0BAA0B,CAAC;IACpD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,QAAQ,CAAC,aAAa,CAAC,EAAE,4BAA4B,CAAC;IACtD,uFAAuF;IACvF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,iEAAiE;IACjE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,kCAAkC;IAClD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,gCAAgC,CAAC;CACnD;AAED,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;CACzC;AAED,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,kCAAkC,GACvC,6BAA6B,CAiC/B","sourcesContent":["import {\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\ttype ContextBudgetCacheProviderV2,\n\ttype ContextBudgetItemV2,\n\ttype PromptContextBudgetPlanV2,\n\tplanPromptContextBudgetV2,\n} from \"./context-budget-governor-v2.ts\";\nimport { createSystemPromptBudgetItems, escapeXml } from \"./context-budget-system-prompt-items.ts\";\nimport {\n\ttype ContextBudgetTokenizerMode,\n\tcreateTokenCounterForMode,\n\ttype TokenCounterAdapter,\n} from \"./context-budget-token-counter.ts\";\nimport type { ContextFile } from \"./context-file.ts\";\nimport type { Skill } from \"./skills.ts\";\n\nexport interface SystemPromptContextBudgetOptions {\n\treadonly maxPromptTokens: number;\n\treadonly responseReserveTokens?: number;\n\treadonly modelId?: string;\n\treadonly tokenizerMode?: ContextBudgetTokenizerMode;\n\treadonly activeSkillNames?: readonly string[];\n\treadonly includeSkillInventory?: boolean;\n\treadonly includeFullContextFiles?: boolean;\n\treadonly tokenCounter?: TokenCounterAdapter;\n\treadonly cacheProvider?: ContextBudgetCacheProviderV2;\n\t/** Maximum number of inactive (non-active) skills to include as items. Default: 15. */\n\treadonly maxInactiveSkills?: number;\n\t/** Current user query text for relevance-aware skill ranking. */\n\treadonly queryContext?: string;\n}\n\nexport interface SystemPromptBudgetedResourcesInput {\n\treadonly basePrompt: string;\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly Skill[];\n\treadonly includeSkills: boolean;\n\treadonly options: SystemPromptContextBudgetOptions;\n}\n\nexport interface SystemPromptBudgetedResources {\n\treadonly text: string;\n\treadonly plan: PromptContextBudgetPlanV2;\n}\n\nexport function renderSystemPromptBudgetedResources(\n\tinput: SystemPromptBudgetedResourcesInput,\n): SystemPromptBudgetedResources {\n\tconst tokenCounter =\n\t\tinput.options.tokenCounter ?? createTokenCounterForMode(input.options.tokenizerMode ?? \"fallback\");\n\tconst modelId = input.options.modelId ?? \"unknown\";\n\tconst baseTokens = tokenCounter.countText(input.basePrompt, modelId).tokens;\n\tconst resourceBudget = Math.max(0, input.options.maxPromptTokens - baseTokens);\n\tconst items = createSystemPromptBudgetItems(input, resourceBudget);\n\tconst plan = planPromptContextBudgetV2({\n\t\tmaxTokens: resourceBudget,\n\t\tresponseReserveTokens: input.options.responseReserveTokens ?? 0,\n\t\tmodelId,\n\t\tpolicyVersion: CONTEXT_BUDGET_POLICY_VERSION_V2,\n\t\tquery: input.options.queryContext,\n\t\titems,\n\t\ttokenCounter,\n\t\tcacheProvider: input.options.cacheProvider,\n\t});\n\tif (items.length === 0 && !plan.emergency && plan.observability.diagnosticReasons.length === 0) {\n\t\treturn { text: renderEmptyBudgetNote(plan), plan };\n\t}\n\n\tconst included = new Set(plan.includedItemIds);\n\tconst filtered = deduplicatePointerFull(items, included);\n\tconst text = filtered\n\t\t.filter((item) => included.has(item.id))\n\t\t.map(\n\t\t\t(item) =>\n\t\t\t\tplan.selectedRepresentations.find((representation) => representation.itemId === item.id)?.text ?? item.text,\n\t\t)\n\t\t.join(\"\\n\")\n\t\t.trimEnd();\n\tconst note = renderBudgetNote(plan, baseTokens);\n\treturn { text: text ? `${text}\\n${note}` : note, plan };\n}\n\nfunction renderEmptyBudgetNote(plan: PromptContextBudgetPlanV2): string {\n\treturn [\n\t\t\"<context_budget>\",\n\t\t` <policy>${escapeXml(plan.policyVersion)}</policy>`,\n\t\t` <cache_decision plan_hit=\"${plan.observability.cache.planCache.hit ? \"true\" : \"false\"}\" />`,\n\t\trenderTokenOptimizerStatus(plan, \" \"),\n\t\t\"</context_budget>\",\n\t].join(\"\\n\");\n}\n\nfunction renderBudgetNote(plan: PromptContextBudgetPlanV2, baseTokens: number): string {\n\tconst omitted = plan.omittedItemIds.length;\n\tconst { cache, counts, diagnosticReasons, tokens } = plan.observability;\n\tconst selectedCacheHits =\n\t\tcache.representationCache.exactHits +\n\t\tcache.representationCache.semanticHits +\n\t\tcache.representationCache.pointerHits;\n\tconst renderedDiagnosticReasons =\n\t\tdiagnosticReasons.length === 0\n\t\t\t? ' <diagnostic_reasons count=\"0\" />'\n\t\t\t: [\n\t\t\t\t\t` <diagnostic_reasons count=\"${formatObservableInteger(diagnosticReasons.length)}\">`,\n\t\t\t\t\t...diagnosticReasons.map((reason) => ` <reason>${escapeXml(reason)}</reason>`),\n\t\t\t\t\t\" </diagnostic_reasons>\",\n\t\t\t\t].join(\"\\n\");\n\treturn [\n\t\t\"<context_budget>\",\n\t\t` <policy>${escapeXml(plan.policyVersion)}</policy>`,\n\t\t` <plan_hash>${escapeXml(plan.planHash)}</plan_hash>`,\n\t\t` <base_prompt_tokens>${formatObservableInteger(baseTokens)}</base_prompt_tokens>`,\n\t\t` <resource_tokens_used>${formatObservableInteger(plan.usedTokens)}</resource_tokens_used>`,\n\t\t` <resource_tokens_omitted>${formatObservableInteger(plan.omittedTokens)}</resource_tokens_omitted>`,\n\t\t` <omitted_items>${formatObservableInteger(omitted)}</omitted_items>`,\n\t\t` <emergency>${plan.emergency ? \"true\" : \"false\"}</emergency>`,\n\t\t\" <decision_observability>\",\n\t\t` <counts selected=\"${formatObservableInteger(counts.selected)}\" omitted=\"${formatObservableInteger(counts.omitted)}\" pointer=\"${formatObservableInteger(counts.pointer)}\" compressed=\"${formatObservableInteger(counts.compressed)}\" full=\"${formatObservableInteger(counts.full)}\" retrieval_fallbacks=\"${formatObservableInteger(counts.retrievalFallback)}\" />`,\n\t\t` <tokens available=\"${formatObservableInteger(tokens.available)}\" used=\"${formatObservableInteger(tokens.used)}\" raw=\"${formatObservableInteger(tokens.raw)}\" omitted=\"${formatObservableInteger(tokens.omitted)}\" token_savings=\"${formatObservableInteger(tokens.tokenSavings)}\" />`,\n\t\t` <cache_decision plan_hit=\"${cache.planCache.hit ? \"true\" : \"false\"}\" selected_hits=\"${formatObservableInteger(selectedCacheHits)}\" misses=\"${formatObservableInteger(cache.representationCache.misses)}\" stale_rejects=\"${formatObservableInteger(cache.representationCache.staleRejects)}\" negative_hits=\"${formatObservableInteger(cache.representationCache.negativeHits)}\" writes=\"${formatObservableInteger(cache.representationCache.writes)}\" />`,\n\t\trenderedDiagnosticReasons,\n\t\trenderTokenOptimizerStatus(plan, \" \"),\n\t\t\" </decision_observability>\",\n\t\t\" <note>Some low-priority resource inventory may be represented by pointers. Use read on referenced paths when needed.</note>\",\n\t\t\"</context_budget>\",\n\t].join(\"\\n\");\n}\n\nfunction renderTokenOptimizerStatus(plan: PromptContextBudgetPlanV2, indent: string): string {\n\tconst { tokenOptimizer } = plan.observability;\n\treturn `${indent}<token_optimizer optimizer_id=\"${escapeXml(tokenOptimizer.optimizerId)}\" status=\"${escapeXml(tokenOptimizer.status)}\" active=\"${tokenOptimizer.active ? \"true\" : \"false\"}\" active_context_budget_optimizer=\"${escapeXml(tokenOptimizer.activeContextBudgetOptimizer)}\" compatibility_only=\"${tokenOptimizer.compatibilityOnly ? \"true\" : \"false\"}\" />`;\n}\n\nfunction formatObservableInteger(value: number): string {\n\tif (!Number.isFinite(value)) {\n\t\treturn \"0\";\n\t}\n\treturn String(Math.max(0, Math.floor(value)));\n}\n\n/**\n * R3: When a context-full item is included, exclude the matching pointer\n * so the same file does not consume tokens twice.\n */\nfunction deduplicatePointerFull(\n\titems: readonly ContextBudgetItemV2[],\n\tincluded: ReadonlySet<string>,\n): readonly ContextBudgetItemV2[] {\n\tconst includedFullPaths = new Set<string>();\n\tfor (const item of items) {\n\t\tif (item.id.startsWith(\"context-full:\") && included.has(item.id)) {\n\t\t\tconst path = item.id.slice(\"context-full:\".length);\n\t\t\tincludedFullPaths.add(path);\n\t\t}\n\t}\n\tif (includedFullPaths.size === 0) {\n\t\treturn items;\n\t}\n\treturn items.filter((item) => {\n\t\tif (!item.id.startsWith(\"context-pointer:\")) {\n\t\t\treturn true;\n\t\t}\n\t\tconst path = item.id.slice(\"context-pointer:\".length);\n\t\treturn !includedFullPaths.has(path);\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-system-prompt.js","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\ttype ContextBudgetCacheProviderV2,\n\ttype ContextBudgetItemV2,\n\ttype PromptContextBudgetPlanV2,\n\tplanPromptContextBudgetV2,\n} from \"./context-budget-governor-v2.ts\";\nimport { createSystemPromptBudgetItems, escapeXml } from \"./context-budget-system-prompt-items.ts\";\nimport {\n\ttype ContextBudgetTokenizerMode,\n\tcreateTokenCounterForMode,\n\ttype TokenCounterAdapter,\n} from \"./context-budget-token-counter.ts\";\nimport type { ContextFile } from \"./resource-loader.ts\";\nimport type { Skill } from \"./skills.ts\";\n\nexport interface SystemPromptContextBudgetOptions {\n\treadonly maxPromptTokens: number;\n\treadonly responseReserveTokens?: number;\n\treadonly modelId?: string;\n\treadonly tokenizerMode?: ContextBudgetTokenizerMode;\n\treadonly activeSkillNames?: readonly string[];\n\treadonly includeSkillInventory?: boolean;\n\treadonly includeFullContextFiles?: boolean;\n\treadonly tokenCounter?: TokenCounterAdapter;\n\treadonly cacheProvider?: ContextBudgetCacheProviderV2;\n\t/** Maximum number of inactive (non-active) skills to include as items. Default: 15. */\n\treadonly maxInactiveSkills?: number;\n\t/** Current user query text for relevance-aware skill ranking. */\n\treadonly queryContext?: string;\n}\n\nexport interface SystemPromptBudgetedResourcesInput {\n\treadonly basePrompt: string;\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly Skill[];\n\treadonly includeSkills: boolean;\n\treadonly options: SystemPromptContextBudgetOptions;\n}\n\nexport interface SystemPromptBudgetedResources {\n\treadonly text: string;\n\treadonly plan: PromptContextBudgetPlanV2;\n}\n\nexport function renderSystemPromptBudgetedResources(\n\tinput: SystemPromptBudgetedResourcesInput,\n): SystemPromptBudgetedResources {\n\tconst tokenCounter =\n\t\tinput.options.tokenCounter ?? createTokenCounterForMode(input.options.tokenizerMode ?? \"fallback\");\n\tconst modelId = input.options.modelId ?? \"unknown\";\n\tconst baseTokens = tokenCounter.countText(input.basePrompt, modelId).tokens;\n\tconst resourceBudget = Math.max(0, input.options.maxPromptTokens - baseTokens);\n\tconst items = createSystemPromptBudgetItems(input, resourceBudget);\n\tconst plan = planPromptContextBudgetV2({\n\t\tmaxTokens: resourceBudget,\n\t\tresponseReserveTokens: input.options.responseReserveTokens ?? 0,\n\t\tmodelId,\n\t\tpolicyVersion: CONTEXT_BUDGET_POLICY_VERSION_V2,\n\t\tquery: input.options.queryContext,\n\t\titems,\n\t\ttokenCounter,\n\t\tcacheProvider: input.options.cacheProvider,\n\t});\n\tif (items.length === 0 && !plan.emergency && plan.observability.diagnosticReasons.length === 0) {\n\t\treturn { text: renderEmptyBudgetNote(plan), plan };\n\t}\n\n\tconst included = new Set(plan.includedItemIds);\n\tconst filtered = deduplicatePointerFull(items, included);\n\tconst text = filtered\n\t\t.filter((item) => included.has(item.id))\n\t\t.map(\n\t\t\t(item) =>\n\t\t\t\tplan.selectedRepresentations.find((representation) => representation.itemId === item.id)?.text ?? item.text,\n\t\t)\n\t\t.join(\"\\n\")\n\t\t.trimEnd();\n\tconst note = renderBudgetNote(plan, baseTokens);\n\treturn { text: text ? `${text}\\n${note}` : note, plan };\n}\n\nfunction renderEmptyBudgetNote(plan: PromptContextBudgetPlanV2): string {\n\treturn [\n\t\t\"<context_budget>\",\n\t\t` <policy>${escapeXml(plan.policyVersion)}</policy>`,\n\t\t` <cache_decision plan_hit=\"${plan.observability.cache.planCache.hit ? \"true\" : \"false\"}\" />`,\n\t\trenderTokenOptimizerStatus(plan, \" \"),\n\t\t\"</context_budget>\",\n\t].join(\"\\n\");\n}\n\nfunction renderBudgetNote(plan: PromptContextBudgetPlanV2, baseTokens: number): string {\n\tconst omitted = plan.omittedItemIds.length;\n\tconst { cache, counts, diagnosticReasons, tokens } = plan.observability;\n\tconst selectedCacheHits =\n\t\tcache.representationCache.exactHits +\n\t\tcache.representationCache.semanticHits +\n\t\tcache.representationCache.pointerHits;\n\tconst renderedDiagnosticReasons =\n\t\tdiagnosticReasons.length === 0\n\t\t\t? ' <diagnostic_reasons count=\"0\" />'\n\t\t\t: [\n\t\t\t\t\t` <diagnostic_reasons count=\"${formatObservableInteger(diagnosticReasons.length)}\">`,\n\t\t\t\t\t...diagnosticReasons.map((reason) => ` <reason>${escapeXml(reason)}</reason>`),\n\t\t\t\t\t\" </diagnostic_reasons>\",\n\t\t\t\t].join(\"\\n\");\n\treturn [\n\t\t\"<context_budget>\",\n\t\t` <policy>${escapeXml(plan.policyVersion)}</policy>`,\n\t\t` <plan_hash>${escapeXml(plan.planHash)}</plan_hash>`,\n\t\t` <base_prompt_tokens>${formatObservableInteger(baseTokens)}</base_prompt_tokens>`,\n\t\t` <resource_tokens_used>${formatObservableInteger(plan.usedTokens)}</resource_tokens_used>`,\n\t\t` <resource_tokens_omitted>${formatObservableInteger(plan.omittedTokens)}</resource_tokens_omitted>`,\n\t\t` <omitted_items>${formatObservableInteger(omitted)}</omitted_items>`,\n\t\t` <emergency>${plan.emergency ? \"true\" : \"false\"}</emergency>`,\n\t\t\" <decision_observability>\",\n\t\t` <counts selected=\"${formatObservableInteger(counts.selected)}\" omitted=\"${formatObservableInteger(counts.omitted)}\" pointer=\"${formatObservableInteger(counts.pointer)}\" compressed=\"${formatObservableInteger(counts.compressed)}\" full=\"${formatObservableInteger(counts.full)}\" retrieval_fallbacks=\"${formatObservableInteger(counts.retrievalFallback)}\" />`,\n\t\t` <tokens available=\"${formatObservableInteger(tokens.available)}\" used=\"${formatObservableInteger(tokens.used)}\" raw=\"${formatObservableInteger(tokens.raw)}\" omitted=\"${formatObservableInteger(tokens.omitted)}\" token_savings=\"${formatObservableInteger(tokens.tokenSavings)}\" />`,\n\t\t` <cache_decision plan_hit=\"${cache.planCache.hit ? \"true\" : \"false\"}\" selected_hits=\"${formatObservableInteger(selectedCacheHits)}\" misses=\"${formatObservableInteger(cache.representationCache.misses)}\" stale_rejects=\"${formatObservableInteger(cache.representationCache.staleRejects)}\" negative_hits=\"${formatObservableInteger(cache.representationCache.negativeHits)}\" writes=\"${formatObservableInteger(cache.representationCache.writes)}\" />`,\n\t\trenderedDiagnosticReasons,\n\t\trenderTokenOptimizerStatus(plan, \" \"),\n\t\t\" </decision_observability>\",\n\t\t\" <note>Some low-priority resource inventory may be represented by pointers. Use read on referenced paths when needed.</note>\",\n\t\t\"</context_budget>\",\n\t].join(\"\\n\");\n}\n\nfunction renderTokenOptimizerStatus(plan: PromptContextBudgetPlanV2, indent: string): string {\n\tconst { tokenOptimizer } = plan.observability;\n\treturn `${indent}<token_optimizer optimizer_id=\"${escapeXml(tokenOptimizer.optimizerId)}\" status=\"${escapeXml(tokenOptimizer.status)}\" active=\"${tokenOptimizer.active ? \"true\" : \"false\"}\" active_context_budget_optimizer=\"${escapeXml(tokenOptimizer.activeContextBudgetOptimizer)}\" compatibility_only=\"${tokenOptimizer.compatibilityOnly ? \"true\" : \"false\"}\" />`;\n}\n\nfunction formatObservableInteger(value: number): string {\n\tif (!Number.isFinite(value)) {\n\t\treturn \"0\";\n\t}\n\treturn String(Math.max(0, Math.floor(value)));\n}\n\n/**\n * R3: When a context-full item is included, exclude the matching pointer\n * so the same file does not consume tokens twice.\n */\nfunction deduplicatePointerFull(\n\titems: readonly ContextBudgetItemV2[],\n\tincluded: ReadonlySet<string>,\n): readonly ContextBudgetItemV2[] {\n\tconst includedFullPaths = new Set<string>();\n\tfor (const item of items) {\n\t\tif (item.id.startsWith(\"context-full:\") && included.has(item.id)) {\n\t\t\tconst path = item.id.slice(\"context-full:\".length);\n\t\t\tincludedFullPaths.add(path);\n\t\t}\n\t}\n\tif (includedFullPaths.size === 0) {\n\t\treturn items;\n\t}\n\treturn items.filter((item) => {\n\t\tif (!item.id.startsWith(\"context-pointer:\")) {\n\t\t\treturn true;\n\t\t}\n\t\tconst path = item.id.slice(\"context-pointer:\".length);\n\t\treturn !includedFullPaths.has(path);\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-system-prompt.js","sourceRoot":"","sources":["../../src/core/context-budget-system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\ttype ContextBudgetCacheProviderV2,\n\ttype ContextBudgetItemV2,\n\ttype PromptContextBudgetPlanV2,\n\tplanPromptContextBudgetV2,\n} from \"./context-budget-governor-v2.ts\";\nimport { createSystemPromptBudgetItems, escapeXml } from \"./context-budget-system-prompt-items.ts\";\nimport {\n\ttype ContextBudgetTokenizerMode,\n\tcreateTokenCounterForMode,\n\ttype TokenCounterAdapter,\n} from \"./context-budget-token-counter.ts\";\nimport type { ContextFile } from \"./context-file.ts\";\nimport type { Skill } from \"./skills.ts\";\n\nexport interface SystemPromptContextBudgetOptions {\n\treadonly maxPromptTokens: number;\n\treadonly responseReserveTokens?: number;\n\treadonly modelId?: string;\n\treadonly tokenizerMode?: ContextBudgetTokenizerMode;\n\treadonly activeSkillNames?: readonly string[];\n\treadonly includeSkillInventory?: boolean;\n\treadonly includeFullContextFiles?: boolean;\n\treadonly tokenCounter?: TokenCounterAdapter;\n\treadonly cacheProvider?: ContextBudgetCacheProviderV2;\n\t/** Maximum number of inactive (non-active) skills to include as items. Default: 15. */\n\treadonly maxInactiveSkills?: number;\n\t/** Current user query text for relevance-aware skill ranking. */\n\treadonly queryContext?: string;\n}\n\nexport interface SystemPromptBudgetedResourcesInput {\n\treadonly basePrompt: string;\n\treadonly contextFiles: readonly ContextFile[];\n\treadonly skills: readonly Skill[];\n\treadonly includeSkills: boolean;\n\treadonly options: SystemPromptContextBudgetOptions;\n}\n\nexport interface SystemPromptBudgetedResources {\n\treadonly text: string;\n\treadonly plan: PromptContextBudgetPlanV2;\n}\n\nexport function renderSystemPromptBudgetedResources(\n\tinput: SystemPromptBudgetedResourcesInput,\n): SystemPromptBudgetedResources {\n\tconst tokenCounter =\n\t\tinput.options.tokenCounter ?? createTokenCounterForMode(input.options.tokenizerMode ?? \"fallback\");\n\tconst modelId = input.options.modelId ?? \"unknown\";\n\tconst baseTokens = tokenCounter.countText(input.basePrompt, modelId).tokens;\n\tconst resourceBudget = Math.max(0, input.options.maxPromptTokens - baseTokens);\n\tconst items = createSystemPromptBudgetItems(input, resourceBudget);\n\tconst plan = planPromptContextBudgetV2({\n\t\tmaxTokens: resourceBudget,\n\t\tresponseReserveTokens: input.options.responseReserveTokens ?? 0,\n\t\tmodelId,\n\t\tpolicyVersion: CONTEXT_BUDGET_POLICY_VERSION_V2,\n\t\tquery: input.options.queryContext,\n\t\titems,\n\t\ttokenCounter,\n\t\tcacheProvider: input.options.cacheProvider,\n\t});\n\tif (items.length === 0 && !plan.emergency && plan.observability.diagnosticReasons.length === 0) {\n\t\treturn { text: renderEmptyBudgetNote(plan), plan };\n\t}\n\n\tconst included = new Set(plan.includedItemIds);\n\tconst filtered = deduplicatePointerFull(items, included);\n\tconst text = filtered\n\t\t.filter((item) => included.has(item.id))\n\t\t.map(\n\t\t\t(item) =>\n\t\t\t\tplan.selectedRepresentations.find((representation) => representation.itemId === item.id)?.text ?? item.text,\n\t\t)\n\t\t.join(\"\\n\")\n\t\t.trimEnd();\n\tconst note = renderBudgetNote(plan, baseTokens);\n\treturn { text: text ? `${text}\\n${note}` : note, plan };\n}\n\nfunction renderEmptyBudgetNote(plan: PromptContextBudgetPlanV2): string {\n\treturn [\n\t\t\"<context_budget>\",\n\t\t` <policy>${escapeXml(plan.policyVersion)}</policy>`,\n\t\t` <cache_decision plan_hit=\"${plan.observability.cache.planCache.hit ? \"true\" : \"false\"}\" />`,\n\t\trenderTokenOptimizerStatus(plan, \" \"),\n\t\t\"</context_budget>\",\n\t].join(\"\\n\");\n}\n\nfunction renderBudgetNote(plan: PromptContextBudgetPlanV2, baseTokens: number): string {\n\tconst omitted = plan.omittedItemIds.length;\n\tconst { cache, counts, diagnosticReasons, tokens } = plan.observability;\n\tconst selectedCacheHits =\n\t\tcache.representationCache.exactHits +\n\t\tcache.representationCache.semanticHits +\n\t\tcache.representationCache.pointerHits;\n\tconst renderedDiagnosticReasons =\n\t\tdiagnosticReasons.length === 0\n\t\t\t? ' <diagnostic_reasons count=\"0\" />'\n\t\t\t: [\n\t\t\t\t\t` <diagnostic_reasons count=\"${formatObservableInteger(diagnosticReasons.length)}\">`,\n\t\t\t\t\t...diagnosticReasons.map((reason) => ` <reason>${escapeXml(reason)}</reason>`),\n\t\t\t\t\t\" </diagnostic_reasons>\",\n\t\t\t\t].join(\"\\n\");\n\treturn [\n\t\t\"<context_budget>\",\n\t\t` <policy>${escapeXml(plan.policyVersion)}</policy>`,\n\t\t` <plan_hash>${escapeXml(plan.planHash)}</plan_hash>`,\n\t\t` <base_prompt_tokens>${formatObservableInteger(baseTokens)}</base_prompt_tokens>`,\n\t\t` <resource_tokens_used>${formatObservableInteger(plan.usedTokens)}</resource_tokens_used>`,\n\t\t` <resource_tokens_omitted>${formatObservableInteger(plan.omittedTokens)}</resource_tokens_omitted>`,\n\t\t` <omitted_items>${formatObservableInteger(omitted)}</omitted_items>`,\n\t\t` <emergency>${plan.emergency ? \"true\" : \"false\"}</emergency>`,\n\t\t\" <decision_observability>\",\n\t\t` <counts selected=\"${formatObservableInteger(counts.selected)}\" omitted=\"${formatObservableInteger(counts.omitted)}\" pointer=\"${formatObservableInteger(counts.pointer)}\" compressed=\"${formatObservableInteger(counts.compressed)}\" full=\"${formatObservableInteger(counts.full)}\" retrieval_fallbacks=\"${formatObservableInteger(counts.retrievalFallback)}\" />`,\n\t\t` <tokens available=\"${formatObservableInteger(tokens.available)}\" used=\"${formatObservableInteger(tokens.used)}\" raw=\"${formatObservableInteger(tokens.raw)}\" omitted=\"${formatObservableInteger(tokens.omitted)}\" token_savings=\"${formatObservableInteger(tokens.tokenSavings)}\" />`,\n\t\t` <cache_decision plan_hit=\"${cache.planCache.hit ? \"true\" : \"false\"}\" selected_hits=\"${formatObservableInteger(selectedCacheHits)}\" misses=\"${formatObservableInteger(cache.representationCache.misses)}\" stale_rejects=\"${formatObservableInteger(cache.representationCache.staleRejects)}\" negative_hits=\"${formatObservableInteger(cache.representationCache.negativeHits)}\" writes=\"${formatObservableInteger(cache.representationCache.writes)}\" />`,\n\t\trenderedDiagnosticReasons,\n\t\trenderTokenOptimizerStatus(plan, \" \"),\n\t\t\" </decision_observability>\",\n\t\t\" <note>Some low-priority resource inventory may be represented by pointers. Use read on referenced paths when needed.</note>\",\n\t\t\"</context_budget>\",\n\t].join(\"\\n\");\n}\n\nfunction renderTokenOptimizerStatus(plan: PromptContextBudgetPlanV2, indent: string): string {\n\tconst { tokenOptimizer } = plan.observability;\n\treturn `${indent}<token_optimizer optimizer_id=\"${escapeXml(tokenOptimizer.optimizerId)}\" status=\"${escapeXml(tokenOptimizer.status)}\" active=\"${tokenOptimizer.active ? \"true\" : \"false\"}\" active_context_budget_optimizer=\"${escapeXml(tokenOptimizer.activeContextBudgetOptimizer)}\" compatibility_only=\"${tokenOptimizer.compatibilityOnly ? \"true\" : \"false\"}\" />`;\n}\n\nfunction formatObservableInteger(value: number): string {\n\tif (!Number.isFinite(value)) {\n\t\treturn \"0\";\n\t}\n\treturn String(Math.max(0, Math.floor(value)));\n}\n\n/**\n * R3: When a context-full item is included, exclude the matching pointer\n * so the same file does not consume tokens twice.\n */\nfunction deduplicatePointerFull(\n\titems: readonly ContextBudgetItemV2[],\n\tincluded: ReadonlySet<string>,\n): readonly ContextBudgetItemV2[] {\n\tconst includedFullPaths = new Set<string>();\n\tfor (const item of items) {\n\t\tif (item.id.startsWith(\"context-full:\") && included.has(item.id)) {\n\t\t\tconst path = item.id.slice(\"context-full:\".length);\n\t\t\tincludedFullPaths.add(path);\n\t\t}\n\t}\n\tif (includedFullPaths.size === 0) {\n\t\treturn items;\n\t}\n\treturn items.filter((item) => {\n\t\tif (!item.id.startsWith(\"context-pointer:\")) {\n\t\t\treturn true;\n\t\t}\n\t\tconst path = item.id.slice(\"context-pointer:\".length);\n\t\treturn !includedFullPaths.has(path);\n\t});\n}\n"]}
|
|
@@ -26,6 +26,8 @@ export interface ContextBudgetCacheKeyBaseV2 {
|
|
|
26
26
|
readonly modelId: string;
|
|
27
27
|
readonly tokenizerId: string;
|
|
28
28
|
readonly policyVersion: string;
|
|
29
|
+
/** Selection-policy token; separate from `policyVersion`, which is public. */
|
|
30
|
+
readonly selectionPolicyVersion: string;
|
|
29
31
|
readonly queryIntentHash: string;
|
|
30
32
|
readonly queryIntentCluster?: string;
|
|
31
33
|
readonly budgetBucket: string;
|
|
@@ -40,6 +42,7 @@ export declare function createContextBudgetCacheKeyBaseV2(input: {
|
|
|
40
42
|
readonly modelId: string;
|
|
41
43
|
readonly tokenizerId?: string;
|
|
42
44
|
readonly policyVersion: string;
|
|
45
|
+
readonly selectionPolicyVersion?: string;
|
|
43
46
|
readonly query?: string;
|
|
44
47
|
readonly queryIntentHash?: string;
|
|
45
48
|
readonly queryIntentCluster?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-v2-cache-keys.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-keys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContextRepresentationCandidateV2, ContextRepresentationKindV2 } from \"./context-budget-headroom.ts\";\nimport { sha256Canonical, sha256Hex } from \"./context-budget-v2-cache-hash.ts\";\nimport {\n\ttype ContextCacheInvalidationSnapshot,\n\tserializeContextCacheSnapshot,\n} from \"./context-budget-v2-cache-invalidation.ts\";\n\nconst DEFAULT_CACHE_NAMESPACE = \"context-budget-v2\";\nconst DEFAULT_TOKENIZER_ID = \"heuristic-v1\";\nconst DEFAULT_REDACTION_POLICY_HASH = \"none\";\nconst DEFAULT_SAFETY_PROFILE_HASH = \"default\";\nconst NO_COMPRESSOR_ID = \"none\";\nexport const DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2 = \"context-budget-v2-default-compressor\";\nexport const DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2 = \"context-budget-v2-default-quality\";\nconst MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE = 100;\n\ntype ContextBudgetRepresentationFingerprintInputV2 = Pick<\n\tContextRepresentationCandidateV2,\n\t\"compressorId\" | \"estimatedTokens\" | \"fidelity\" | \"kind\" | \"sourceRef\" | \"summaryHash\" | \"text\"\n>;\n\n/**\n * Cache-key inputs shared by the plan cache and the representation cache.\n *\n * Scope contract (deliberate asymmetry — see the two builders below):\n * - **Plan keys are session-scoped.** A plan is a selection over the whole\n * transcript and runtime state, so {@link invalidationSnapshotHash} (fork id,\n * worktree fingerprint, steering/repair/settings counters) is part of the key.\n * - **Representation keys are content-addressed.** A rendered representation is\n * fully identified by its source content plus the rendering policy, so the\n * invalidation snapshot is deliberately *excluded*. Folding a per-session\n * `forkId` into representation keys made every new session miss every entry\n * (structurally unreachable reuse, not a cold cache), while adding no safety:\n * `validateContextBudgetRepresentationCacheEntryV2` independently re-checks\n * source hash, fingerprint, model, tokenizer, policy version, and\n * TTL on every read, and worktree/model/redaction/safety changes already move\n * the key through `sourceHash` / `modelId` / `redactionPolicyHash` /\n * `safetyProfileHash`.\n */\nexport interface ContextBudgetCacheKeyBaseV2 {\n\treadonly namespace: string;\n\treadonly modelId: string;\n\treadonly tokenizerId: string;\n\treadonly policyVersion: string;\n\treadonly queryIntentHash: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket: string;\n\treadonly redactionPolicyHash: string;\n\treadonly safetyProfileHash: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly invalidationSnapshotHash?: string;\n}\n\nexport function createContextBudgetCacheKeyBaseV2(input: {\n\treadonly namespace?: string;\n\treadonly modelId: string;\n\treadonly tokenizerId?: string;\n\treadonly policyVersion: string;\n\treadonly query?: string;\n\treadonly queryIntentHash?: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket?: string;\n\treadonly redactionPolicyHash?: string;\n\treadonly safetyProfileHash?: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly cacheInvalidationSnapshot?: ContextCacheInvalidationSnapshot;\n}): ContextBudgetCacheKeyBaseV2 {\n\tconst queryIntentHash = input.queryIntentHash ?? computeContextBudgetQueryIntentHashV2(input.query);\n\treturn {\n\t\tnamespace: input.namespace ?? DEFAULT_CACHE_NAMESPACE,\n\t\tmodelId: input.modelId,\n\t\ttokenizerId: input.tokenizerId ?? DEFAULT_TOKENIZER_ID,\n\t\tpolicyVersion: input.policyVersion,\n\t\tqueryIntentHash,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? queryIntentHash,\n\t\tbudgetBucket: input.budgetBucket ?? \"unknown\",\n\t\tredactionPolicyHash: input.redactionPolicyHash ?? DEFAULT_REDACTION_POLICY_HASH,\n\t\tsafetyProfileHash: input.safetyProfileHash ?? DEFAULT_SAFETY_PROFILE_HASH,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tinvalidationSnapshotHash:\n\t\t\tinput.cacheInvalidationSnapshot === undefined\n\t\t\t\t? \"none\"\n\t\t\t\t: sha256Hex(serializeContextCacheSnapshot(input.cacheInvalidationSnapshot)),\n\t};\n}\n\nexport function computeContextBudgetQueryIntentHashV2(query: string | undefined): string {\n\tconst normalized = (query ?? \"\")\n\t\t.toLowerCase()\n\t\t.split(/[\\s,.;:!?()[\\]{}\"'`]+/u)\n\t\t.map((term) => term.trim())\n\t\t.filter((term) => term.length > 0)\n\t\t.sort()\n\t\t.join(\" \");\n\treturn sha256Hex(normalized.length > 0 ? normalized : \"none\");\n}\n\nexport function buildContextBudgetRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2(input);\n}\n\n/**\n * Exact representation key. Content-addressed: identical content + rendering\n * policy yields one key across queries, budgets, sessions, forks, and worktrees.\n */\nexport function buildContextBudgetExactRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn `context-representation-exact:${sha256Canonical({\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationFingerprint: input.representationFingerprint ?? \"none\",\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function buildContextBudgetMaterializedRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly compressorId?: string;\n\t\treadonly targetTokenBucket: number;\n\t},\n): string {\n\treturn `context-representation-materialized:${sha256Canonical({\n\t\tbudgetBucket: input.budgetBucket,\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? input.queryIntentHash,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttargetTokenBucket: input.targetTokenBucket,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function computeContextBudgetRepresentationFingerprintV2(\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n): string {\n\treturn sha256Canonical({\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\testimatedTokens: candidate.estimatedTokens,\n\t\tfidelity: candidate.fidelity,\n\t\tkind: candidate.kind,\n\t\tsourceRef: fingerprintSourceRef(candidate.sourceRef),\n\t\tsummaryHash: candidate.summaryHash ?? null,\n\t\ttextHash: sha256Hex(candidate.text),\n\t});\n}\n\nexport function buildContextBudgetRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationFingerprint: computeContextBudgetRepresentationFingerprintV2(candidate),\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t});\n}\n\nexport function buildContextBudgetMaterializedRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetMaterializedRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t\ttargetTokenBucket: computeContextBudgetTargetTokenBucketV2(candidate.estimatedTokens),\n\t});\n}\n\nexport function computeContextBudgetTargetTokenBucketV2(tokens: number): number {\n\tif (!Number.isFinite(tokens) || tokens <= 0) {\n\t\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE;\n\t}\n\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE * Math.ceil(tokens / MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE);\n}\n\nexport function computeContextBudgetOutputTextHashV2(text: string): string {\n\treturn sha256Hex(text);\n}\n\nexport function fingerprintSourceRef(sourceRef: ContextRepresentationCandidateV2[\"sourceRef\"]): unknown {\n\tif (!sourceRef) return null;\n\treturn {\n\t\tcontentHash: sourceRef.contentHash,\n\t\trange: sourceRef.range\n\t\t\t? {\n\t\t\t\t\tendLine: sourceRef.range.endLine,\n\t\t\t\t\tstartLine: sourceRef.range.startLine,\n\t\t\t\t}\n\t\t\t: null,\n\t\tretrievable: sourceRef.retrievable,\n\t\tsymbol: sourceRef.symbol ?? null,\n\t\turi: sourceRef.uri,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-v2-cache-keys.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-keys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContextRepresentationCandidateV2, ContextRepresentationKindV2 } from \"./context-budget-headroom.ts\";\nimport { sha256Canonical, sha256Hex } from \"./context-budget-v2-cache-hash.ts\";\nimport {\n\ttype ContextCacheInvalidationSnapshot,\n\tserializeContextCacheSnapshot,\n} from \"./context-budget-v2-cache-invalidation.ts\";\nimport { CONTEXT_BUDGET_SELECTION_POLICY_V2 } from \"./context-budget-v2-types.ts\";\n\nconst DEFAULT_CACHE_NAMESPACE = \"context-budget-v2\";\nconst DEFAULT_TOKENIZER_ID = \"heuristic-v1\";\nconst DEFAULT_REDACTION_POLICY_HASH = \"none\";\nconst DEFAULT_SAFETY_PROFILE_HASH = \"default\";\nconst NO_COMPRESSOR_ID = \"none\";\nexport const DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2 = \"context-budget-v2-default-compressor\";\nexport const DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2 = \"context-budget-v2-default-quality\";\nconst MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE = 100;\n\ntype ContextBudgetRepresentationFingerprintInputV2 = Pick<\n\tContextRepresentationCandidateV2,\n\t\"compressorId\" | \"estimatedTokens\" | \"fidelity\" | \"kind\" | \"sourceRef\" | \"summaryHash\" | \"text\"\n>;\n\n/**\n * Cache-key inputs shared by the plan cache and the representation cache.\n *\n * Scope contract (deliberate asymmetry — see the two builders below):\n * - **Plan keys are session-scoped.** A plan is a selection over the whole\n * transcript and runtime state, so {@link invalidationSnapshotHash} (fork id,\n * worktree fingerprint, steering/repair/settings counters) is part of the key.\n * - **Representation keys are content-addressed.** A rendered representation is\n * fully identified by its source content plus the rendering policy, so the\n * invalidation snapshot is deliberately *excluded*. Folding a per-session\n * `forkId` into representation keys made every new session miss every entry\n * (structurally unreachable reuse, not a cold cache), while adding no safety:\n * `validateContextBudgetRepresentationCacheEntryV2` independently re-checks\n * source hash, fingerprint, model, tokenizer, policy version, and\n * TTL on every read, and worktree/model/redaction/safety changes already move\n * the key through `sourceHash` / `modelId` / `redactionPolicyHash` /\n * `safetyProfileHash`.\n */\nexport interface ContextBudgetCacheKeyBaseV2 {\n\treadonly namespace: string;\n\treadonly modelId: string;\n\treadonly tokenizerId: string;\n\treadonly policyVersion: string;\n\t/** Selection-policy token; separate from `policyVersion`, which is public. */\n\treadonly selectionPolicyVersion: string;\n\treadonly queryIntentHash: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket: string;\n\treadonly redactionPolicyHash: string;\n\treadonly safetyProfileHash: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly invalidationSnapshotHash?: string;\n}\n\nexport function createContextBudgetCacheKeyBaseV2(input: {\n\treadonly namespace?: string;\n\treadonly modelId: string;\n\treadonly tokenizerId?: string;\n\treadonly policyVersion: string;\n\treadonly selectionPolicyVersion?: string;\n\treadonly query?: string;\n\treadonly queryIntentHash?: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket?: string;\n\treadonly redactionPolicyHash?: string;\n\treadonly safetyProfileHash?: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly cacheInvalidationSnapshot?: ContextCacheInvalidationSnapshot;\n}): ContextBudgetCacheKeyBaseV2 {\n\tconst queryIntentHash = input.queryIntentHash ?? computeContextBudgetQueryIntentHashV2(input.query);\n\treturn {\n\t\tnamespace: input.namespace ?? DEFAULT_CACHE_NAMESPACE,\n\t\tmodelId: input.modelId,\n\t\ttokenizerId: input.tokenizerId ?? DEFAULT_TOKENIZER_ID,\n\t\tpolicyVersion: input.policyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion ?? CONTEXT_BUDGET_SELECTION_POLICY_V2,\n\t\tqueryIntentHash,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? queryIntentHash,\n\t\tbudgetBucket: input.budgetBucket ?? \"unknown\",\n\t\tredactionPolicyHash: input.redactionPolicyHash ?? DEFAULT_REDACTION_POLICY_HASH,\n\t\tsafetyProfileHash: input.safetyProfileHash ?? DEFAULT_SAFETY_PROFILE_HASH,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tinvalidationSnapshotHash:\n\t\t\tinput.cacheInvalidationSnapshot === undefined\n\t\t\t\t? \"none\"\n\t\t\t\t: sha256Hex(serializeContextCacheSnapshot(input.cacheInvalidationSnapshot)),\n\t};\n}\n\nexport function computeContextBudgetQueryIntentHashV2(query: string | undefined): string {\n\tconst normalized = (query ?? \"\")\n\t\t.toLowerCase()\n\t\t.split(/[\\s,.;:!?()[\\]{}\"'`]+/u)\n\t\t.map((term) => term.trim())\n\t\t.filter((term) => term.length > 0)\n\t\t.sort()\n\t\t.join(\" \");\n\treturn sha256Hex(normalized.length > 0 ? normalized : \"none\");\n}\n\nexport function buildContextBudgetRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2(input);\n}\n\n/**\n * Exact representation key. Content-addressed: identical content + rendering\n * policy yields one key across queries, budgets, sessions, forks, and worktrees.\n */\nexport function buildContextBudgetExactRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn `context-representation-exact:${sha256Canonical({\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationFingerprint: input.representationFingerprint ?? \"none\",\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function buildContextBudgetMaterializedRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly compressorId?: string;\n\t\treadonly targetTokenBucket: number;\n\t},\n): string {\n\treturn `context-representation-materialized:${sha256Canonical({\n\t\tbudgetBucket: input.budgetBucket,\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? input.queryIntentHash,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttargetTokenBucket: input.targetTokenBucket,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function computeContextBudgetRepresentationFingerprintV2(\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n): string {\n\treturn sha256Canonical({\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\testimatedTokens: candidate.estimatedTokens,\n\t\tfidelity: candidate.fidelity,\n\t\tkind: candidate.kind,\n\t\tsourceRef: fingerprintSourceRef(candidate.sourceRef),\n\t\tsummaryHash: candidate.summaryHash ?? null,\n\t\ttextHash: sha256Hex(candidate.text),\n\t});\n}\n\nexport function buildContextBudgetRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationFingerprint: computeContextBudgetRepresentationFingerprintV2(candidate),\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t});\n}\n\nexport function buildContextBudgetMaterializedRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetMaterializedRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t\ttargetTokenBucket: computeContextBudgetTargetTokenBucketV2(candidate.estimatedTokens),\n\t});\n}\n\nexport function computeContextBudgetTargetTokenBucketV2(tokens: number): number {\n\tif (!Number.isFinite(tokens) || tokens <= 0) {\n\t\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE;\n\t}\n\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE * Math.ceil(tokens / MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE);\n}\n\nexport function computeContextBudgetOutputTextHashV2(text: string): string {\n\treturn sha256Hex(text);\n}\n\nexport function fingerprintSourceRef(sourceRef: ContextRepresentationCandidateV2[\"sourceRef\"]): unknown {\n\tif (!sourceRef) return null;\n\treturn {\n\t\tcontentHash: sourceRef.contentHash,\n\t\trange: sourceRef.range\n\t\t\t? {\n\t\t\t\t\tendLine: sourceRef.range.endLine,\n\t\t\t\t\tstartLine: sourceRef.range.startLine,\n\t\t\t\t}\n\t\t\t: null,\n\t\tretrievable: sourceRef.retrievable,\n\t\tsymbol: sourceRef.symbol ?? null,\n\t\turi: sourceRef.uri,\n\t};\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { sha256Canonical, sha256Hex } from "./context-budget-v2-cache-hash.js";
|
|
2
2
|
import { serializeContextCacheSnapshot, } from "./context-budget-v2-cache-invalidation.js";
|
|
3
|
+
import { CONTEXT_BUDGET_SELECTION_POLICY_V2 } from "./context-budget-v2-types.js";
|
|
3
4
|
const DEFAULT_CACHE_NAMESPACE = "context-budget-v2";
|
|
4
5
|
const DEFAULT_TOKENIZER_ID = "heuristic-v1";
|
|
5
6
|
const DEFAULT_REDACTION_POLICY_HASH = "none";
|
|
@@ -15,6 +16,7 @@ export function createContextBudgetCacheKeyBaseV2(input) {
|
|
|
15
16
|
modelId: input.modelId,
|
|
16
17
|
tokenizerId: input.tokenizerId ?? DEFAULT_TOKENIZER_ID,
|
|
17
18
|
policyVersion: input.policyVersion,
|
|
19
|
+
selectionPolicyVersion: input.selectionPolicyVersion ?? CONTEXT_BUDGET_SELECTION_POLICY_V2,
|
|
18
20
|
queryIntentHash,
|
|
19
21
|
queryIntentCluster: input.queryIntentCluster ?? queryIntentHash,
|
|
20
22
|
budgetBucket: input.budgetBucket ?? "unknown",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-v2-cache-keys.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-keys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContextRepresentationCandidateV2, ContextRepresentationKindV2 } from \"./context-budget-headroom.ts\";\nimport { sha256Canonical, sha256Hex } from \"./context-budget-v2-cache-hash.ts\";\nimport {\n\ttype ContextCacheInvalidationSnapshot,\n\tserializeContextCacheSnapshot,\n} from \"./context-budget-v2-cache-invalidation.ts\";\n\nconst DEFAULT_CACHE_NAMESPACE = \"context-budget-v2\";\nconst DEFAULT_TOKENIZER_ID = \"heuristic-v1\";\nconst DEFAULT_REDACTION_POLICY_HASH = \"none\";\nconst DEFAULT_SAFETY_PROFILE_HASH = \"default\";\nconst NO_COMPRESSOR_ID = \"none\";\nexport const DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2 = \"context-budget-v2-default-compressor\";\nexport const DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2 = \"context-budget-v2-default-quality\";\nconst MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE = 100;\n\ntype ContextBudgetRepresentationFingerprintInputV2 = Pick<\n\tContextRepresentationCandidateV2,\n\t\"compressorId\" | \"estimatedTokens\" | \"fidelity\" | \"kind\" | \"sourceRef\" | \"summaryHash\" | \"text\"\n>;\n\n/**\n * Cache-key inputs shared by the plan cache and the representation cache.\n *\n * Scope contract (deliberate asymmetry — see the two builders below):\n * - **Plan keys are session-scoped.** A plan is a selection over the whole\n * transcript and runtime state, so {@link invalidationSnapshotHash} (fork id,\n * worktree fingerprint, steering/repair/settings counters) is part of the key.\n * - **Representation keys are content-addressed.** A rendered representation is\n * fully identified by its source content plus the rendering policy, so the\n * invalidation snapshot is deliberately *excluded*. Folding a per-session\n * `forkId` into representation keys made every new session miss every entry\n * (structurally unreachable reuse, not a cold cache), while adding no safety:\n * `validateContextBudgetRepresentationCacheEntryV2` independently re-checks\n * source hash, fingerprint, model, tokenizer, policy version, and\n * TTL on every read, and worktree/model/redaction/safety changes already move\n * the key through `sourceHash` / `modelId` / `redactionPolicyHash` /\n * `safetyProfileHash`.\n */\nexport interface ContextBudgetCacheKeyBaseV2 {\n\treadonly namespace: string;\n\treadonly modelId: string;\n\treadonly tokenizerId: string;\n\treadonly policyVersion: string;\n\treadonly queryIntentHash: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket: string;\n\treadonly redactionPolicyHash: string;\n\treadonly safetyProfileHash: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly invalidationSnapshotHash?: string;\n}\n\nexport function createContextBudgetCacheKeyBaseV2(input: {\n\treadonly namespace?: string;\n\treadonly modelId: string;\n\treadonly tokenizerId?: string;\n\treadonly policyVersion: string;\n\treadonly query?: string;\n\treadonly queryIntentHash?: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket?: string;\n\treadonly redactionPolicyHash?: string;\n\treadonly safetyProfileHash?: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly cacheInvalidationSnapshot?: ContextCacheInvalidationSnapshot;\n}): ContextBudgetCacheKeyBaseV2 {\n\tconst queryIntentHash = input.queryIntentHash ?? computeContextBudgetQueryIntentHashV2(input.query);\n\treturn {\n\t\tnamespace: input.namespace ?? DEFAULT_CACHE_NAMESPACE,\n\t\tmodelId: input.modelId,\n\t\ttokenizerId: input.tokenizerId ?? DEFAULT_TOKENIZER_ID,\n\t\tpolicyVersion: input.policyVersion,\n\t\tqueryIntentHash,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? queryIntentHash,\n\t\tbudgetBucket: input.budgetBucket ?? \"unknown\",\n\t\tredactionPolicyHash: input.redactionPolicyHash ?? DEFAULT_REDACTION_POLICY_HASH,\n\t\tsafetyProfileHash: input.safetyProfileHash ?? DEFAULT_SAFETY_PROFILE_HASH,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tinvalidationSnapshotHash:\n\t\t\tinput.cacheInvalidationSnapshot === undefined\n\t\t\t\t? \"none\"\n\t\t\t\t: sha256Hex(serializeContextCacheSnapshot(input.cacheInvalidationSnapshot)),\n\t};\n}\n\nexport function computeContextBudgetQueryIntentHashV2(query: string | undefined): string {\n\tconst normalized = (query ?? \"\")\n\t\t.toLowerCase()\n\t\t.split(/[\\s,.;:!?()[\\]{}\"'`]+/u)\n\t\t.map((term) => term.trim())\n\t\t.filter((term) => term.length > 0)\n\t\t.sort()\n\t\t.join(\" \");\n\treturn sha256Hex(normalized.length > 0 ? normalized : \"none\");\n}\n\nexport function buildContextBudgetRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2(input);\n}\n\n/**\n * Exact representation key. Content-addressed: identical content + rendering\n * policy yields one key across queries, budgets, sessions, forks, and worktrees.\n */\nexport function buildContextBudgetExactRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn `context-representation-exact:${sha256Canonical({\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationFingerprint: input.representationFingerprint ?? \"none\",\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function buildContextBudgetMaterializedRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly compressorId?: string;\n\t\treadonly targetTokenBucket: number;\n\t},\n): string {\n\treturn `context-representation-materialized:${sha256Canonical({\n\t\tbudgetBucket: input.budgetBucket,\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? input.queryIntentHash,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttargetTokenBucket: input.targetTokenBucket,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function computeContextBudgetRepresentationFingerprintV2(\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n): string {\n\treturn sha256Canonical({\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\testimatedTokens: candidate.estimatedTokens,\n\t\tfidelity: candidate.fidelity,\n\t\tkind: candidate.kind,\n\t\tsourceRef: fingerprintSourceRef(candidate.sourceRef),\n\t\tsummaryHash: candidate.summaryHash ?? null,\n\t\ttextHash: sha256Hex(candidate.text),\n\t});\n}\n\nexport function buildContextBudgetRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationFingerprint: computeContextBudgetRepresentationFingerprintV2(candidate),\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t});\n}\n\nexport function buildContextBudgetMaterializedRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetMaterializedRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t\ttargetTokenBucket: computeContextBudgetTargetTokenBucketV2(candidate.estimatedTokens),\n\t});\n}\n\nexport function computeContextBudgetTargetTokenBucketV2(tokens: number): number {\n\tif (!Number.isFinite(tokens) || tokens <= 0) {\n\t\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE;\n\t}\n\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE * Math.ceil(tokens / MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE);\n}\n\nexport function computeContextBudgetOutputTextHashV2(text: string): string {\n\treturn sha256Hex(text);\n}\n\nexport function fingerprintSourceRef(sourceRef: ContextRepresentationCandidateV2[\"sourceRef\"]): unknown {\n\tif (!sourceRef) return null;\n\treturn {\n\t\tcontentHash: sourceRef.contentHash,\n\t\trange: sourceRef.range\n\t\t\t? {\n\t\t\t\t\tendLine: sourceRef.range.endLine,\n\t\t\t\t\tstartLine: sourceRef.range.startLine,\n\t\t\t\t}\n\t\t\t: null,\n\t\tretrievable: sourceRef.retrievable,\n\t\tsymbol: sourceRef.symbol ?? null,\n\t\turi: sourceRef.uri,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-v2-cache-keys.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-keys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContextRepresentationCandidateV2, ContextRepresentationKindV2 } from \"./context-budget-headroom.ts\";\nimport { sha256Canonical, sha256Hex } from \"./context-budget-v2-cache-hash.ts\";\nimport {\n\ttype ContextCacheInvalidationSnapshot,\n\tserializeContextCacheSnapshot,\n} from \"./context-budget-v2-cache-invalidation.ts\";\nimport { CONTEXT_BUDGET_SELECTION_POLICY_V2 } from \"./context-budget-v2-types.ts\";\n\nconst DEFAULT_CACHE_NAMESPACE = \"context-budget-v2\";\nconst DEFAULT_TOKENIZER_ID = \"heuristic-v1\";\nconst DEFAULT_REDACTION_POLICY_HASH = \"none\";\nconst DEFAULT_SAFETY_PROFILE_HASH = \"default\";\nconst NO_COMPRESSOR_ID = \"none\";\nexport const DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2 = \"context-budget-v2-default-compressor\";\nexport const DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2 = \"context-budget-v2-default-quality\";\nconst MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE = 100;\n\ntype ContextBudgetRepresentationFingerprintInputV2 = Pick<\n\tContextRepresentationCandidateV2,\n\t\"compressorId\" | \"estimatedTokens\" | \"fidelity\" | \"kind\" | \"sourceRef\" | \"summaryHash\" | \"text\"\n>;\n\n/**\n * Cache-key inputs shared by the plan cache and the representation cache.\n *\n * Scope contract (deliberate asymmetry — see the two builders below):\n * - **Plan keys are session-scoped.** A plan is a selection over the whole\n * transcript and runtime state, so {@link invalidationSnapshotHash} (fork id,\n * worktree fingerprint, steering/repair/settings counters) is part of the key.\n * - **Representation keys are content-addressed.** A rendered representation is\n * fully identified by its source content plus the rendering policy, so the\n * invalidation snapshot is deliberately *excluded*. Folding a per-session\n * `forkId` into representation keys made every new session miss every entry\n * (structurally unreachable reuse, not a cold cache), while adding no safety:\n * `validateContextBudgetRepresentationCacheEntryV2` independently re-checks\n * source hash, fingerprint, model, tokenizer, policy version, and\n * TTL on every read, and worktree/model/redaction/safety changes already move\n * the key through `sourceHash` / `modelId` / `redactionPolicyHash` /\n * `safetyProfileHash`.\n */\nexport interface ContextBudgetCacheKeyBaseV2 {\n\treadonly namespace: string;\n\treadonly modelId: string;\n\treadonly tokenizerId: string;\n\treadonly policyVersion: string;\n\t/** Selection-policy token; separate from `policyVersion`, which is public. */\n\treadonly selectionPolicyVersion: string;\n\treadonly queryIntentHash: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket: string;\n\treadonly redactionPolicyHash: string;\n\treadonly safetyProfileHash: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly invalidationSnapshotHash?: string;\n}\n\nexport function createContextBudgetCacheKeyBaseV2(input: {\n\treadonly namespace?: string;\n\treadonly modelId: string;\n\treadonly tokenizerId?: string;\n\treadonly policyVersion: string;\n\treadonly selectionPolicyVersion?: string;\n\treadonly query?: string;\n\treadonly queryIntentHash?: string;\n\treadonly queryIntentCluster?: string;\n\treadonly budgetBucket?: string;\n\treadonly redactionPolicyHash?: string;\n\treadonly safetyProfileHash?: string;\n\treadonly compressorPolicyHash?: string;\n\treadonly qualityPolicyHash?: string;\n\treadonly cacheInvalidationSnapshot?: ContextCacheInvalidationSnapshot;\n}): ContextBudgetCacheKeyBaseV2 {\n\tconst queryIntentHash = input.queryIntentHash ?? computeContextBudgetQueryIntentHashV2(input.query);\n\treturn {\n\t\tnamespace: input.namespace ?? DEFAULT_CACHE_NAMESPACE,\n\t\tmodelId: input.modelId,\n\t\ttokenizerId: input.tokenizerId ?? DEFAULT_TOKENIZER_ID,\n\t\tpolicyVersion: input.policyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion ?? CONTEXT_BUDGET_SELECTION_POLICY_V2,\n\t\tqueryIntentHash,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? queryIntentHash,\n\t\tbudgetBucket: input.budgetBucket ?? \"unknown\",\n\t\tredactionPolicyHash: input.redactionPolicyHash ?? DEFAULT_REDACTION_POLICY_HASH,\n\t\tsafetyProfileHash: input.safetyProfileHash ?? DEFAULT_SAFETY_PROFILE_HASH,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tinvalidationSnapshotHash:\n\t\t\tinput.cacheInvalidationSnapshot === undefined\n\t\t\t\t? \"none\"\n\t\t\t\t: sha256Hex(serializeContextCacheSnapshot(input.cacheInvalidationSnapshot)),\n\t};\n}\n\nexport function computeContextBudgetQueryIntentHashV2(query: string | undefined): string {\n\tconst normalized = (query ?? \"\")\n\t\t.toLowerCase()\n\t\t.split(/[\\s,.;:!?()[\\]{}\"'`]+/u)\n\t\t.map((term) => term.trim())\n\t\t.filter((term) => term.length > 0)\n\t\t.sort()\n\t\t.join(\" \");\n\treturn sha256Hex(normalized.length > 0 ? normalized : \"none\");\n}\n\nexport function buildContextBudgetRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2(input);\n}\n\n/**\n * Exact representation key. Content-addressed: identical content + rendering\n * policy yields one key across queries, budgets, sessions, forks, and worktrees.\n */\nexport function buildContextBudgetExactRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly representationFingerprint?: string;\n\t\treadonly compressorId?: string;\n\t},\n): string {\n\treturn `context-representation-exact:${sha256Canonical({\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationFingerprint: input.representationFingerprint ?? \"none\",\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function buildContextBudgetMaterializedRepresentationCacheKeyV2(\n\tinput: ContextBudgetCacheKeyBaseV2 & {\n\t\treadonly representationKind: ContextRepresentationKindV2;\n\t\treadonly sourceHash: string;\n\t\treadonly compressorId?: string;\n\t\treadonly targetTokenBucket: number;\n\t},\n): string {\n\treturn `context-representation-materialized:${sha256Canonical({\n\t\tbudgetBucket: input.budgetBucket,\n\t\tcompressorId: input.compressorId ?? NO_COMPRESSOR_ID,\n\t\tcompressorPolicyHash: input.compressorPolicyHash ?? DEFAULT_MATERIALIZED_COMPRESSOR_POLICY_HASH_V2,\n\t\tmodelId: input.modelId,\n\t\tnamespace: input.namespace,\n\t\tpolicyVersion: input.policyVersion,\n\t\tqualityPolicyHash: input.qualityPolicyHash ?? DEFAULT_MATERIALIZED_QUALITY_POLICY_HASH_V2,\n\t\tqueryIntentCluster: input.queryIntentCluster ?? input.queryIntentHash,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\trepresentationKind: input.representationKind,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\tsourceHash: input.sourceHash,\n\t\ttargetTokenBucket: input.targetTokenBucket,\n\t\ttokenizerId: input.tokenizerId,\n\t})}`;\n}\n\nexport function computeContextBudgetRepresentationFingerprintV2(\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n): string {\n\treturn sha256Canonical({\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\testimatedTokens: candidate.estimatedTokens,\n\t\tfidelity: candidate.fidelity,\n\t\tkind: candidate.kind,\n\t\tsourceRef: fingerprintSourceRef(candidate.sourceRef),\n\t\tsummaryHash: candidate.summaryHash ?? null,\n\t\ttextHash: sha256Hex(candidate.text),\n\t});\n}\n\nexport function buildContextBudgetRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetExactRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationFingerprint: computeContextBudgetRepresentationFingerprintV2(candidate),\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t});\n}\n\nexport function buildContextBudgetMaterializedRepresentationCandidateKeyV2(\n\tkeyBase: ContextBudgetCacheKeyBaseV2,\n\tcandidate: ContextBudgetRepresentationFingerprintInputV2,\n\tsourceHash: string,\n): string {\n\treturn buildContextBudgetMaterializedRepresentationCacheKeyV2({\n\t\t...keyBase,\n\t\tcompressorId: candidate.compressorId ?? NO_COMPRESSOR_ID,\n\t\trepresentationKind: candidate.kind,\n\t\tsourceHash,\n\t\ttargetTokenBucket: computeContextBudgetTargetTokenBucketV2(candidate.estimatedTokens),\n\t});\n}\n\nexport function computeContextBudgetTargetTokenBucketV2(tokens: number): number {\n\tif (!Number.isFinite(tokens) || tokens <= 0) {\n\t\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE;\n\t}\n\treturn MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE * Math.ceil(tokens / MATERIALIZED_TARGET_TOKEN_BUCKET_SIZE);\n}\n\nexport function computeContextBudgetOutputTextHashV2(text: string): string {\n\treturn sha256Hex(text);\n}\n\nexport function fingerprintSourceRef(sourceRef: ContextRepresentationCandidateV2[\"sourceRef\"]): unknown {\n\tif (!sourceRef) return null;\n\treturn {\n\t\tcontentHash: sourceRef.contentHash,\n\t\trange: sourceRef.range\n\t\t\t? {\n\t\t\t\t\tendLine: sourceRef.range.endLine,\n\t\t\t\t\tstartLine: sourceRef.range.startLine,\n\t\t\t\t}\n\t\t\t: null,\n\t\tretrievable: sourceRef.retrievable,\n\t\tsymbol: sourceRef.symbol ?? null,\n\t\turi: sourceRef.uri,\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-v2-plan-cache-keys.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-plan-cache-keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5E,OAAO,EACN,KAAK,2BAA2B,EAGhC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAkCvE,wBAAgB,gCAAgC,CAAC,KAAK,EAAE;IACvD,QAAQ,CAAC,OAAO,EAAE,2BAA2B,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;CAChD,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"context-budget-v2-plan-cache-keys.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-plan-cache-keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5E,OAAO,EACN,KAAK,2BAA2B,EAGhC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAkCvE,wBAAgB,gCAAgC,CAAC,KAAK,EAAE;IACvD,QAAQ,CAAC,OAAO,EAAE,2BAA2B,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;CAChD,GAAG,MAAM,CAiDT","sourcesContent":["import type { HeadroomQualityPolicyV2 } from \"./context-budget-headroom.ts\";\nimport { sha256Canonical } from \"./context-budget-v2-cache-hash.ts\";\nimport {\n\ttype ContextBudgetCacheKeyBaseV2,\n\tcomputeContextBudgetRepresentationFingerprintV2,\n\tfingerprintSourceRef,\n} from \"./context-budget-v2-cache-keys.ts\";\nimport type { PlannedItemV2 } from \"./context-budget-v2-scoring.ts\";\nimport type { TierBudgetPolicyV2 } from \"./context-budget-v2-types.ts\";\n\n/**\n * Cache-key-only quantization for signals that legitimately drift every turn.\n *\n * `ageTurns`, `baseScore`, `effectiveScore`, `redundancyPenalty`, and an explicit `recency`\n * override are recomputed from scratch on every planning pass (recency decays continuously via\n * `deriveRecency()`'s `exp(-ageTurns / halfLife)`), so hashing their raw values means the plan\n * cache key changes on *every single turn* even when the resulting selection would be identical\n * -- the cache can structurally never hit past the first call in a session. Bucketing/rounding\n * them here does not touch actual selection logic (compareOptionalForSelection, tier allocation,\n * etc. still read the full-precision fields off `planned` directly); it only changes what counts\n * as \"the same plan\" for cache-lookup purposes. `ageTurns` buckets are log2-spaced so a bucket\n * boundary corresponds to roughly one additional recency half-life having elapsed -- the point\n * at which the decayed score has plausibly moved enough to change ranking/selection -- rather\n * than an arbitrary fixed window.\n */\nfunction bucketAgeTurnsForCacheKeyV2(ageTurns: number | null | undefined): number | null {\n\tif (ageTurns === null || ageTurns === undefined || !Number.isFinite(ageTurns)) return null;\n\tif (ageTurns <= 0) return 0;\n\treturn Math.floor(Math.log2(ageTurns + 1));\n}\n\nfunction roundScoreForCacheKeyV2(score: number | null | undefined): number | null {\n\tif (score === null || score === undefined || Number.isNaN(score)) return null;\n\tif (!Number.isFinite(score)) return score; // preserve +/-Infinity (hard-pinned items)\n\treturn Math.round(score);\n}\n\nfunction roundUnitIntervalForCacheKeyV2(value: number | null | undefined): number | null {\n\tif (value === null || value === undefined || !Number.isFinite(value)) return null;\n\treturn Math.round(value * 10) / 10;\n}\n\nexport function buildContextBudgetPlanCacheKeyV2(input: {\n\treadonly keyBase: ContextBudgetCacheKeyBaseV2;\n\treadonly promptHash?: string;\n\treadonly maxTokens: number;\n\treadonly responseReserveTokens: number;\n\treadonly safetyMarginTokens: number;\n\treadonly availableTokens: number;\n\treadonly planned: readonly PlannedItemV2[];\n\treadonly tierPolicy: Readonly<Record<string, TierBudgetPolicyV2>>;\n\treadonly qualityPolicy: HeadroomQualityPolicyV2;\n}): string {\n\treturn `context-plan:${sha256Canonical({\n\t\tavailableTokens: input.availableTokens,\n\t\tbudgetBucket: input.keyBase.budgetBucket,\n\t\t// Only the PLAN key carries this: representation entries are content-addressed\n\t\t// by source hash and kind, so a selection-order change cannot invalidate them.\n\t\tselectionPolicyVersion: input.keyBase.selectionPolicyVersion,\n\t\titems: input.planned\n\t\t\t.map((planned) => ({\n\t\t\t\tageTurns: bucketAgeTurnsForCacheKeyV2(planned.item.ageTurns),\n\t\t\t\tbaseScore: roundScoreForCacheKeyV2(planned.baseScore),\n\t\t\t\teffectiveScore: roundScoreForCacheKeyV2(planned.effectiveScore),\n\t\t\t\tevidenceKind: planned.item.evidenceKind ?? null,\n\t\t\t\tevidenceValue: planned.item.evidenceValue ?? null,\n\t\t\t\tid: planned.item.id,\n\t\t\t\tpinReason: planned.item.pinReason ?? null,\n\t\t\t\tpriority: planned.item.priority,\n\t\t\t\trecency: roundUnitIntervalForCacheKeyV2(planned.item.recency),\n\t\t\t\tredundancyKey: planned.item.redundancyKey ?? null,\n\t\t\t\tredundancyPenalty: roundScoreForCacheKeyV2(planned.redundancyPenalty),\n\t\t\t\trelevance: planned.item.relevance ?? null,\n\t\t\t\trepresentations: (planned.item.representations ?? [])\n\t\t\t\t\t.map((candidate) => ({\n\t\t\t\t\t\tfingerprint: computeContextBudgetRepresentationFingerprintV2(candidate),\n\t\t\t\t\t\tkind: candidate.kind,\n\t\t\t\t\t}))\n\t\t\t\t\t.sort((a, b) => a.kind.localeCompare(b.kind) || a.fingerprint.localeCompare(b.fingerprint)),\n\t\t\t\trequired: planned.item.required === true,\n\t\t\t\tsourceRef: fingerprintSourceRef(planned.item.sourceRef),\n\t\t\t\tsourceHash: planned.contentHash,\n\t\t\t\ttier: planned.item.tier,\n\t\t\t\ttokenEstimate: planned.item.tokenEstimate ?? null,\n\t\t\t}))\n\t\t\t.sort((a, b) => a.id.localeCompare(b.id)),\n\t\tmaxTokens: input.maxTokens,\n\t\tinvalidationSnapshotHash: input.keyBase.invalidationSnapshotHash ?? \"none\",\n\t\tmodelId: input.keyBase.modelId,\n\t\tnamespace: input.keyBase.namespace,\n\t\tpolicyVersion: input.keyBase.policyVersion,\n\t\tpromptHash: input.promptHash ?? null,\n\t\tqueryIntentHash: input.keyBase.queryIntentHash,\n\t\tredactionPolicyHash: input.keyBase.redactionPolicyHash,\n\t\tresponseReserveTokens: input.responseReserveTokens,\n\t\tsafetyMarginTokens: input.safetyMarginTokens,\n\t\tsafetyProfileHash: input.keyBase.safetyProfileHash,\n\t\ttierPolicy: input.tierPolicy,\n\t\tqualityPolicy: input.qualityPolicy,\n\t\ttokenizerId: input.keyBase.tokenizerId,\n\t})}`;\n}\n"]}
|
|
@@ -38,6 +38,9 @@ export function buildContextBudgetPlanCacheKeyV2(input) {
|
|
|
38
38
|
return `context-plan:${sha256Canonical({
|
|
39
39
|
availableTokens: input.availableTokens,
|
|
40
40
|
budgetBucket: input.keyBase.budgetBucket,
|
|
41
|
+
// Only the PLAN key carries this: representation entries are content-addressed
|
|
42
|
+
// by source hash and kind, so a selection-order change cannot invalidate them.
|
|
43
|
+
selectionPolicyVersion: input.keyBase.selectionPolicyVersion,
|
|
41
44
|
items: input.planned
|
|
42
45
|
.map((planned) => ({
|
|
43
46
|
ageTurns: bucketAgeTurnsForCacheKeyV2(planned.item.ageTurns),
|