open-multi-agent-kit 0.99.0 → 1.0.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 +24 -0
- package/README.md +3 -3
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +1 -0
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/register-bundled-coding-agent.d.ts +10 -0
- package/dist/bun/register-bundled-coding-agent.d.ts.map +1 -0
- package/dist/bun/register-bundled-coding-agent.js +12 -0
- package/dist/bun/register-bundled-coding-agent.js.map +1 -0
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +1 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/neo-cli.d.ts +9 -0
- package/dist/commands/neo-cli.d.ts.map +1 -0
- package/dist/commands/neo-cli.js +61 -0
- package/dist/commands/neo-cli.js.map +1 -0
- package/dist/core/agent-session.d.ts +29 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +107 -30
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bundled-skills.d.ts +4 -0
- package/dist/core/bundled-skills.d.ts.map +1 -0
- package/dist/core/bundled-skills.js +32 -0
- package/dist/core/bundled-skills.js.map +1 -0
- package/dist/core/cli-diagnostics.d.ts +6 -0
- package/dist/core/cli-diagnostics.d.ts.map +1 -0
- package/dist/core/cli-diagnostics.js +20 -0
- package/dist/core/cli-diagnostics.js.map +1 -0
- package/dist/core/context-budget-headroom.d.ts +12 -0
- package/dist/core/context-budget-headroom.d.ts.map +1 -1
- package/dist/core/context-budget-headroom.js +35 -0
- package/dist/core/context-budget-headroom.js.map +1 -1
- package/dist/core/context-budget-v2-input-validation.d.ts +4 -0
- package/dist/core/context-budget-v2-input-validation.d.ts.map +1 -0
- package/dist/core/context-budget-v2-input-validation.js +79 -0
- package/dist/core/context-budget-v2-input-validation.js.map +1 -0
- package/dist/core/context-budget-v2-observability.d.ts +15 -0
- package/dist/core/context-budget-v2-observability.d.ts.map +1 -0
- package/dist/core/context-budget-v2-observability.js +35 -0
- package/dist/core/context-budget-v2-observability.js.map +1 -0
- package/dist/core/context-budget-v2-planned-items.d.ts +5 -0
- package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -0
- package/dist/core/context-budget-v2-planned-items.js +40 -0
- package/dist/core/context-budget-v2-planned-items.js.map +1 -0
- package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
- package/dist/core/context-budget-v2-planner.js +57 -38
- package/dist/core/context-budget-v2-planner.js.map +1 -1
- package/dist/core/context-budget-v2-selection.d.ts +19 -3
- package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
- package/dist/core/context-budget-v2-selection.js +35 -57
- package/dist/core/context-budget-v2-selection.js.map +1 -1
- package/dist/core/context-budget-v2-tiers.d.ts.map +1 -1
- package/dist/core/context-budget-v2-tiers.js +9 -42
- package/dist/core/context-budget-v2-tiers.js.map +1 -1
- package/dist/core/context-budget-v2-types.d.ts +8 -1
- package/dist/core/context-budget-v2-types.d.ts.map +1 -1
- package/dist/core/context-budget-v2-types.js.map +1 -1
- package/dist/core/extensions/bundled-virtual-modules.d.ts +15 -0
- package/dist/core/extensions/bundled-virtual-modules.d.ts.map +1 -0
- package/dist/core/extensions/bundled-virtual-modules.js +63 -0
- package/dist/core/extensions/bundled-virtual-modules.js.map +1 -0
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +2 -42
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +6 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +10 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/loadout-runtime.d.ts +2 -8
- package/dist/core/loadout-runtime.d.ts.map +1 -1
- package/dist/core/loadout-runtime.js.map +1 -1
- package/dist/core/mcp/client.d.ts +27 -6
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +80 -20
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/manager.d.ts +10 -1
- package/dist/core/mcp/manager.d.ts.map +1 -1
- package/dist/core/mcp/manager.js +68 -9
- package/dist/core/mcp/manager.js.map +1 -1
- package/dist/core/mcp/protocol.d.ts +9 -3
- package/dist/core/mcp/protocol.d.ts.map +1 -1
- package/dist/core/mcp/protocol.js +61 -15
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/stdio-transport.d.ts +12 -1
- package/dist/core/mcp/stdio-transport.d.ts.map +1 -1
- package/dist/core/mcp/stdio-transport.js +30 -2
- package/dist/core/mcp/stdio-transport.js.map +1 -1
- package/dist/core/mcp-descriptor-injection.d.ts +26 -0
- package/dist/core/mcp-descriptor-injection.d.ts.map +1 -0
- package/dist/core/mcp-descriptor-injection.js +25 -0
- package/dist/core/mcp-descriptor-injection.js.map +1 -0
- package/dist/core/mcp-public-presets.d.ts +1 -3
- package/dist/core/mcp-public-presets.d.ts.map +1 -1
- package/dist/core/mcp-public-presets.js +3 -15
- package/dist/core/mcp-public-presets.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +24 -3
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/neo/catalog.d.ts +20 -0
- package/dist/core/neo/catalog.d.ts.map +1 -0
- package/dist/core/neo/catalog.js +50 -0
- package/dist/core/neo/catalog.js.map +1 -0
- package/dist/core/neo/setup.d.ts +3 -0
- package/dist/core/neo/setup.d.ts.map +1 -0
- package/dist/core/neo/setup.js +47 -0
- package/dist/core/neo/setup.js.map +1 -0
- package/dist/core/provider-default-models.d.ts +1 -0
- package/dist/core/provider-default-models.d.ts.map +1 -1
- package/dist/core/provider-default-models.js +1 -0
- package/dist/core/provider-default-models.js.map +1 -1
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +1 -0
- package/dist/core/provider-display-names.js.map +1 -1
- package/dist/core/provider-error-classification.d.ts +57 -0
- package/dist/core/provider-error-classification.d.ts.map +1 -0
- package/dist/core/provider-error-classification.js +103 -0
- package/dist/core/provider-error-classification.js.map +1 -0
- package/dist/core/provider-resilience.d.ts +10 -0
- package/dist/core/provider-resilience.d.ts.map +1 -1
- package/dist/core/provider-resilience.js +36 -3
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/provider-usage-commandcode.d.ts +9 -0
- package/dist/core/provider-usage-commandcode.d.ts.map +1 -0
- package/dist/core/provider-usage-commandcode.js +198 -0
- package/dist/core/provider-usage-commandcode.js.map +1 -0
- package/dist/core/provider-usage-devin.d.ts +5 -3
- package/dist/core/provider-usage-devin.d.ts.map +1 -1
- package/dist/core/provider-usage-devin.js +15 -5
- package/dist/core/provider-usage-devin.js.map +1 -1
- package/dist/core/provider-usage-types.d.ts +1 -1
- package/dist/core/provider-usage-types.d.ts.map +1 -1
- package/dist/core/provider-usage-types.js.map +1 -1
- package/dist/core/provider-usage.d.ts.map +1 -1
- package/dist/core/provider-usage.js +6 -0
- package/dist/core/provider-usage.js.map +1 -1
- package/dist/core/resource-admission.d.ts +36 -0
- package/dist/core/resource-admission.d.ts.map +1 -1
- package/dist/core/resource-admission.js +59 -0
- package/dist/core/resource-admission.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +2 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/run-usage-ledger.d.ts +47 -0
- package/dist/core/run-usage-ledger.d.ts.map +1 -0
- package/dist/core/run-usage-ledger.js +162 -0
- package/dist/core/run-usage-ledger.js.map +1 -0
- package/dist/core/run-usage-operation.d.ts +8 -0
- package/dist/core/run-usage-operation.d.ts.map +1 -0
- package/dist/core/run-usage-operation.js +12 -0
- package/dist/core/run-usage-operation.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -6
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-failure-cause.d.ts.map +1 -1
- package/dist/core/session-failure-cause.js +14 -5
- package/dist/core/session-failure-cause.js.map +1 -1
- package/dist/core/session-prompt-lifecycle.d.ts +6 -0
- package/dist/core/session-prompt-lifecycle.d.ts.map +1 -1
- package/dist/core/session-prompt-lifecycle.js +47 -2
- package/dist/core/session-prompt-lifecycle.js.map +1 -1
- package/dist/core/session-termination.d.ts.map +1 -1
- package/dist/core/session-termination.js +4 -2
- package/dist/core/session-termination.js.map +1 -1
- package/dist/core/subagent-lane-authority.d.ts +28 -0
- package/dist/core/subagent-lane-authority.d.ts.map +1 -0
- package/dist/core/subagent-lane-authority.js +119 -0
- package/dist/core/subagent-lane-authority.js.map +1 -0
- package/dist/core/subagent-lane-contract.d.ts +114 -0
- package/dist/core/subagent-lane-contract.d.ts.map +1 -0
- package/dist/core/subagent-lane-contract.js +18 -0
- package/dist/core/subagent-lane-contract.js.map +1 -0
- package/dist/core/subagent-lane-launcher.d.ts +3 -6
- package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
- package/dist/core/subagent-lane-launcher.js +53 -12
- package/dist/core/subagent-lane-launcher.js.map +1 -1
- package/dist/core/subagent-orchestration.d.ts +4 -17
- package/dist/core/subagent-orchestration.d.ts.map +1 -1
- package/dist/core/subagent-orchestration.js.map +1 -1
- package/dist/core/verified-run/broker.d.ts.map +1 -1
- package/dist/core/verified-run/broker.js +4 -1
- package/dist/core/verified-run/broker.js.map +1 -1
- package/dist/core/workload-permit-pool.d.ts +1 -1
- package/dist/core/workload-permit-pool.d.ts.map +1 -1
- package/dist/core/workload-permit-pool.js +3 -0
- package/dist/core/workload-permit-pool.js.map +1 -1
- package/dist/guardrails/strict-evidence-approval-adapter.d.ts +34 -0
- package/dist/guardrails/strict-evidence-approval-adapter.d.ts.map +1 -0
- package/dist/guardrails/strict-evidence-approval-adapter.js +70 -0
- package/dist/guardrails/strict-evidence-approval-adapter.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +11 -18
- package/dist/main.js.map +1 -1
- package/dist/modes/acp/acp-agent.d.ts +24 -0
- package/dist/modes/acp/acp-agent.d.ts.map +1 -0
- package/dist/modes/acp/acp-agent.js +133 -0
- package/dist/modes/acp/acp-agent.js.map +1 -0
- package/dist/modes/acp/acp-mode.d.ts +4 -0
- package/dist/modes/acp/acp-mode.d.ts.map +1 -0
- package/dist/modes/acp/acp-mode.js +34 -0
- package/dist/modes/acp/acp-mode.js.map +1 -0
- package/dist/modes/acp/acp-session.d.ts +4 -0
- package/dist/modes/acp/acp-session.d.ts.map +1 -0
- package/dist/modes/acp/acp-session.js +76 -0
- package/dist/modes/acp/acp-session.js.map +1 -0
- package/dist/modes/acp/acp-transport.d.ts +5 -0
- package/dist/modes/acp/acp-transport.d.ts.map +1 -0
- package/dist/modes/acp/acp-transport.js +90 -0
- package/dist/modes/acp/acp-transport.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/audit-revalidation-2026-09-17.md +77 -0
- package/docs/devin-harness.md +33 -4
- package/docs/ecraf-normalization.md +83 -0
- package/docs/model-catalog-refresh.md +51 -1
- package/docs/models.md +1 -1
- package/docs/neo.md +134 -0
- package/docs/providers.md +72 -6
- package/docs/run-usage-ledger.md +56 -0
- package/docs/runtime-algorithms.md +44 -0
- package/docs/skills.md +1 -1
- package/docs/usage.md +2 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/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/subagent/adaptive-agent-runtime.ts +14 -1
- package/examples/extensions/subagent/graph-result.ts +48 -0
- package/examples/extensions/subagent/index.ts +246 -111
- package/examples/extensions/subagent/managed-process-tree.ts +42 -0
- package/examples/extensions/subagent/managed-process.test.ts +24 -0
- package/examples/extensions/subagent/managed-process.ts +93 -112
- package/examples/extensions/subagent/subagent-runtime-types.ts +17 -1
- package/examples/extensions/subagent/subagent-stream.ts +161 -0
- package/examples/extensions/terminal-browser/README.md +54 -0
- package/examples/extensions/terminal-browser/bridge-protocol.ts +29 -0
- package/examples/extensions/terminal-browser/bridge.ts +219 -0
- package/examples/extensions/terminal-browser/browser-surface.ts +288 -0
- package/examples/extensions/terminal-browser/index.ts +215 -0
- package/examples/extensions/terminal-browser/placeholders.ts +49 -0
- 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 +8 -7
- package/resources/neo/skills/omk-browser/SKILL.md +32 -0
- package/resources/neo/skills/omk-code-review/SKILL.md +24 -0
- package/resources/neo/skills/omk-computeruse/SKILL.md +34 -0
- package/resources/neo/skills/omk-mcp-setup/SKILL.md +48 -0
- package/resources/neo/skills/omk-research/SKILL.md +24 -0
- package/resources/neo/skills/omk-site/SKILL.md +28 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type LoadSkillsOptions, type LoadSkillsResult } from "./skills.ts";
|
|
2
|
+
/** User/project/explicit skills keep ownership of names. Bundled instructions grant no tool authority. */
|
|
3
|
+
export declare function loadSkillsWithBundled(noSkills: boolean, options: LoadSkillsOptions): LoadSkillsResult;
|
|
4
|
+
//# sourceMappingURL=bundled-skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-skills.d.ts","sourceRoot":"","sources":["../../src/core/bundled-skills.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAiC,MAAM,aAAa,CAAC;AAI3G,0GAA0G;AAC1G,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAuBrG","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getPackageDir } from \"../config.ts\";\nimport { type LoadSkillsOptions, type LoadSkillsResult, loadSkills, loadSkillsFromDir } from \"./skills.ts\";\n\nconst DISABLED = new Set([\"0\", \"false\", \"off\", \"disable\", \"disabled\"]);\n\n/** User/project/explicit skills keep ownership of names. Bundled instructions grant no tool authority. */\nexport function loadSkillsWithBundled(noSkills: boolean, options: LoadSkillsOptions): LoadSkillsResult {\n\tconst selected = loadSkills(options);\n\tif (noSkills || DISABLED.has((process.env.OMK_BUNDLED_SKILLS ?? \"\").trim().toLowerCase())) return selected;\n\tconst dir = join(getPackageDir(), \"resources\", \"neo\", \"skills\");\n\tif (!existsSync(dir)) {\n\t\treturn {\n\t\t\t...selected,\n\t\t\tdiagnostics: [\n\t\t\t\t...selected.diagnostics,\n\t\t\t\t{\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tpath: dir,\n\t\t\t\t\tmessage: \"Bundled Neo skills are missing from this installation; reinstall a complete package.\",\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\tconst bundled = loadSkillsFromDir({ dir, source: \"builtin\" });\n\tconst occupied = new Set(selected.skills.map((skill) => skill.name));\n\treturn {\n\t\tskills: [...selected.skills, ...bundled.skills.filter((skill) => !occupied.has(skill.name))],\n\t\tdiagnostics: [...selected.diagnostics, ...bundled.diagnostics],\n\t};\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getPackageDir } from "../config.js";
|
|
4
|
+
import { loadSkills, loadSkillsFromDir } from "./skills.js";
|
|
5
|
+
const DISABLED = new Set(["0", "false", "off", "disable", "disabled"]);
|
|
6
|
+
/** User/project/explicit skills keep ownership of names. Bundled instructions grant no tool authority. */
|
|
7
|
+
export function loadSkillsWithBundled(noSkills, options) {
|
|
8
|
+
const selected = loadSkills(options);
|
|
9
|
+
if (noSkills || DISABLED.has((process.env.OMK_BUNDLED_SKILLS ?? "").trim().toLowerCase()))
|
|
10
|
+
return selected;
|
|
11
|
+
const dir = join(getPackageDir(), "resources", "neo", "skills");
|
|
12
|
+
if (!existsSync(dir)) {
|
|
13
|
+
return {
|
|
14
|
+
...selected,
|
|
15
|
+
diagnostics: [
|
|
16
|
+
...selected.diagnostics,
|
|
17
|
+
{
|
|
18
|
+
type: "warning",
|
|
19
|
+
path: dir,
|
|
20
|
+
message: "Bundled Neo skills are missing from this installation; reinstall a complete package.",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const bundled = loadSkillsFromDir({ dir, source: "builtin" });
|
|
26
|
+
const occupied = new Set(selected.skills.map((skill) => skill.name));
|
|
27
|
+
return {
|
|
28
|
+
skills: [...selected.skills, ...bundled.skills.filter((skill) => !occupied.has(skill.name))],
|
|
29
|
+
diagnostics: [...selected.diagnostics, ...bundled.diagnostics],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=bundled-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-skills.js","sourceRoot":"","sources":["../../src/core/bundled-skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAiD,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE3G,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;AAEvE,0GAA0G;AAC1G,MAAM,UAAU,qBAAqB,CAAC,QAAiB,EAAE,OAA0B,EAAoB;IACtG,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3G,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAChE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO;YACN,GAAG,QAAQ;YACX,WAAW,EAAE;gBACZ,GAAG,QAAQ,CAAC,WAAW;gBACvB;oBACC,IAAI,EAAE,SAAS;oBACf,IAAI,EAAE,GAAG;oBACT,OAAO,EAAE,sFAAsF;iBAC/F;aACD;SACD,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,iBAAiB,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,OAAO;QACN,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5F,WAAW,EAAE,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;KAC9D,CAAC;AAAA,CACF","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getPackageDir } from \"../config.ts\";\nimport { type LoadSkillsOptions, type LoadSkillsResult, loadSkills, loadSkillsFromDir } from \"./skills.ts\";\n\nconst DISABLED = new Set([\"0\", \"false\", \"off\", \"disable\", \"disabled\"]);\n\n/** User/project/explicit skills keep ownership of names. Bundled instructions grant no tool authority. */\nexport function loadSkillsWithBundled(noSkills: boolean, options: LoadSkillsOptions): LoadSkillsResult {\n\tconst selected = loadSkills(options);\n\tif (noSkills || DISABLED.has((process.env.OMK_BUNDLED_SKILLS ?? \"\").trim().toLowerCase())) return selected;\n\tconst dir = join(getPackageDir(), \"resources\", \"neo\", \"skills\");\n\tif (!existsSync(dir)) {\n\t\treturn {\n\t\t\t...selected,\n\t\t\tdiagnostics: [\n\t\t\t\t...selected.diagnostics,\n\t\t\t\t{\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tpath: dir,\n\t\t\t\t\tmessage: \"Bundled Neo skills are missing from this installation; reinstall a complete package.\",\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\tconst bundled = loadSkillsFromDir({ dir, source: \"builtin\" });\n\tconst occupied = new Set(selected.skills.map((skill) => skill.name));\n\treturn {\n\t\tskills: [...selected.skills, ...bundled.skills.filter((skill) => !occupied.has(skill.name))],\n\t\tdiagnostics: [...selected.diagnostics, ...bundled.diagnostics],\n\t};\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AgentSessionRuntimeDiagnostic } from "./agent-session-runtime.ts";
|
|
2
|
+
import type { SettingsManager } from "./settings-manager.ts";
|
|
3
|
+
/** CLI diagnostics presentation, split out of main.ts to hold its module-size ratchet. */
|
|
4
|
+
export declare function collectSettingsDiagnostics(settingsManager: SettingsManager, context: string): AgentSessionRuntimeDiagnostic[];
|
|
5
|
+
export declare function reportDiagnostics(diagnostics: readonly AgentSessionRuntimeDiagnostic[]): void;
|
|
6
|
+
//# sourceMappingURL=cli-diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-diagnostics.d.ts","sourceRoot":"","sources":["../../src/core/cli-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,0FAA0F;AAC1F,wBAAgB,0BAA0B,CACzC,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,MAAM,GACb,6BAA6B,EAAE,CAKjC;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,6BAA6B,EAAE,GAAG,IAAI,CAU7F","sourcesContent":["import chalk from \"chalk\";\nimport type { AgentSessionRuntimeDiagnostic } from \"./agent-session-runtime.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** CLI diagnostics presentation, split out of main.ts to hold its module-size ratchet. */\nexport function collectSettingsDiagnostics(\n\tsettingsManager: SettingsManager,\n\tcontext: string,\n): AgentSessionRuntimeDiagnostic[] {\n\treturn settingsManager.drainErrors().map(({ scope, error }) => ({\n\t\ttype: \"warning\",\n\t\tmessage: `(${context}, ${scope} settings) ${error.message}`,\n\t}));\n}\n\nexport function reportDiagnostics(diagnostics: readonly AgentSessionRuntimeDiagnostic[]): void {\n\tfor (const diagnostic of diagnostics) {\n\t\tconst isError = diagnostic.type === \"error\";\n\t\tconst isWarning = diagnostic.type === \"warning\";\n\t\tconst color = isError ? chalk.red : isWarning ? chalk.yellow : chalk.dim;\n\t\tconst prefix = isError ? \"Error: \" : \"Warning: \";\n\t\t// Diagnostic presentation is intentional stderr, not application logging.\n\t\t// pi-lens-ignore: no-console-except-error\n\t\tconsole.error(color(`${prefix}${diagnostic.message}`));\n\t}\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
/** CLI diagnostics presentation, split out of main.ts to hold its module-size ratchet. */
|
|
3
|
+
export function collectSettingsDiagnostics(settingsManager, context) {
|
|
4
|
+
return settingsManager.drainErrors().map(({ scope, error }) => ({
|
|
5
|
+
type: "warning",
|
|
6
|
+
message: `(${context}, ${scope} settings) ${error.message}`,
|
|
7
|
+
}));
|
|
8
|
+
}
|
|
9
|
+
export function reportDiagnostics(diagnostics) {
|
|
10
|
+
for (const diagnostic of diagnostics) {
|
|
11
|
+
const isError = diagnostic.type === "error";
|
|
12
|
+
const isWarning = diagnostic.type === "warning";
|
|
13
|
+
const color = isError ? chalk.red : isWarning ? chalk.yellow : chalk.dim;
|
|
14
|
+
const prefix = isError ? "Error: " : "Warning: ";
|
|
15
|
+
// Diagnostic presentation is intentional stderr, not application logging.
|
|
16
|
+
// pi-lens-ignore: no-console-except-error
|
|
17
|
+
console.error(color(`${prefix}${diagnostic.message}`));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=cli-diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-diagnostics.js","sourceRoot":"","sources":["../../src/core/cli-diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,0FAA0F;AAC1F,MAAM,UAAU,0BAA0B,CACzC,eAAgC,EAChC,OAAe,EACmB;IAClC,OAAO,eAAe,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI,OAAO,KAAK,KAAK,cAAc,KAAK,CAAC,OAAO,EAAE;KAC3D,CAAC,CAAC,CAAC;AAAA,CACJ;AAED,MAAM,UAAU,iBAAiB,CAAC,WAAqD,EAAQ;IAC9F,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,KAAK,OAAO,CAAC;QAC5C,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,KAAK,SAAS,CAAC;QAChD,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;QACzE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;QACjD,0EAA0E;QAC1E,0CAA0C;QAC1C,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;AAAA,CACD","sourcesContent":["import chalk from \"chalk\";\nimport type { AgentSessionRuntimeDiagnostic } from \"./agent-session-runtime.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** CLI diagnostics presentation, split out of main.ts to hold its module-size ratchet. */\nexport function collectSettingsDiagnostics(\n\tsettingsManager: SettingsManager,\n\tcontext: string,\n): AgentSessionRuntimeDiagnostic[] {\n\treturn settingsManager.drainErrors().map(({ scope, error }) => ({\n\t\ttype: \"warning\",\n\t\tmessage: `(${context}, ${scope} settings) ${error.message}`,\n\t}));\n}\n\nexport function reportDiagnostics(diagnostics: readonly AgentSessionRuntimeDiagnostic[]): void {\n\tfor (const diagnostic of diagnostics) {\n\t\tconst isError = diagnostic.type === \"error\";\n\t\tconst isWarning = diagnostic.type === \"warning\";\n\t\tconst color = isError ? chalk.red : isWarning ? chalk.yellow : chalk.dim;\n\t\tconst prefix = isError ? \"Error: \" : \"Warning: \";\n\t\t// Diagnostic presentation is intentional stderr, not application logging.\n\t\t// pi-lens-ignore: no-console-except-error\n\t\tconsole.error(color(`${prefix}${diagnostic.message}`));\n\t}\n}\n"]}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { type ContextBudgetItemV2, type ContextRepresentationCandidateV2, type HeadroomQualityPolicyV2, type RepresentationBudgetContext } from "./context-budget-headroom-types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Budget-independent admissibility for one representation candidate — the
|
|
4
|
+
* same gate {@link chooseHeadroomRepresentation} applies via its per-kind
|
|
5
|
+
* rejections. Ranking and selection must share this set, otherwise a cheap
|
|
6
|
+
* representation the chooser could never pick distorts the density
|
|
7
|
+
* denominator (an item ranked on a 1-token pointer it can only use as a
|
|
8
|
+
* 80-token full text is admitted ahead of genuinely denser items).
|
|
9
|
+
*
|
|
10
|
+
* Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT
|
|
11
|
+
* here: it is a ranking-time property, not an admissibility property.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isRepresentationEligible(candidate: ContextRepresentationCandidateV2, item: ContextBudgetItemV2, policy?: HeadroomQualityPolicyV2): boolean;
|
|
2
14
|
export declare function chooseHeadroomRepresentation(item: ContextBudgetItemV2, budget: RepresentationBudgetContext, policy?: HeadroomQualityPolicyV2): ContextRepresentationCandidateV2;
|
|
3
15
|
export { deriveRepresentationCandidates } from "./context-budget-headroom-candidates.ts";
|
|
4
16
|
export { type ContextBudgetItemV2, type ContextBudgetPriorityV2, type ContextBudgetTierV2, type ContextEvidenceKindV2, type ContextRepresentationCacheMetadataV2, type ContextRepresentationCandidateV2, type ContextRepresentationFidelityV2, type ContextRepresentationKindV2, type ContextSourceRefV2, DEFAULT_HEADROOM_QUALITY_POLICY, estimatePointerTokens, fnv1aHex, fullTextTokens, getHeadroomRuntimeStatus, type HeadroomQualityPolicyV2, type HeadroomRuntimeStatusV2, heuristicTokenCount, type RepresentationBudgetContext, } from "./context-budget-headroom-types.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-headroom.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,gCAAgC,EAGrC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,MAAM,oCAAoC,CAAC;AAqB5C,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,2BAA2B,EACnC,MAAM,GAAE,uBAAyD,GAC/D,gCAAgC,
|
|
1
|
+
{"version":3,"file":"context-budget-headroom.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,gCAAgC,EAGrC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,MAAM,oCAAoC,CAAC;AAqB5C;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACvC,SAAS,EAAE,gCAAgC,EAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,GAAE,uBAAyD,GAC/D,OAAO,CAkBT;AAED,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,2BAA2B,EACnC,MAAM,GAAE,uBAAyD,GAC/D,gCAAgC,CA2ClC;AAgGD,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EACN,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oCAAoC,EACzC,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,+BAA+B,EAC/B,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,wBAAwB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,mBAAmB,EACnB,KAAK,2BAA2B,GAChC,MAAM,oCAAoC,CAAC","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\n/**\n * Budget-independent admissibility for one representation candidate — the\n * same gate {@link chooseHeadroomRepresentation} applies via its per-kind\n * rejections. Ranking and selection must share this set, otherwise a cheap\n * representation the chooser could never pick distorts the density\n * denominator (an item ranked on a 1-token pointer it can only use as a\n * 80-token full text is admitted ahead of genuinely denser items).\n *\n * Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT\n * here: it is a ranking-time property, not an admissibility property.\n */\nexport function isRepresentationEligible(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): boolean {\n\tif (candidate.kind === \"omit\") return policy.allowOmit;\n\t// A non-full representation that does not actually save tokens is never a\n\t// real choice; counting it as admissible only flatters the cost model.\n\tif (candidate.kind !== \"full\" && candidate.estimatedTokens >= fullTextTokens(item)) return false;\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tswitch (candidate.kind) {\n\t\tcase \"full\":\n\t\t\treturn true;\n\t\tcase \"pointer\":\n\t\t\treturn policy.preferPointerForRetrievable && retrievable;\n\t\tcase \"headroom-compressed\":\n\t\t\treturn retrievable;\n\t\tcase \"summary\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (!isRepresentationEligible(candidate, item, policy)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
|
|
@@ -16,6 +16,38 @@ const CACHE_HIT_BONUS = 8;
|
|
|
16
16
|
const TIGHT_CACHE_HIT_BONUS = 20;
|
|
17
17
|
const MATERIALIZED_CACHE_HIT_BONUS = 8;
|
|
18
18
|
const TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;
|
|
19
|
+
/**
|
|
20
|
+
* Budget-independent admissibility for one representation candidate — the
|
|
21
|
+
* same gate {@link chooseHeadroomRepresentation} applies via its per-kind
|
|
22
|
+
* rejections. Ranking and selection must share this set, otherwise a cheap
|
|
23
|
+
* representation the chooser could never pick distorts the density
|
|
24
|
+
* denominator (an item ranked on a 1-token pointer it can only use as a
|
|
25
|
+
* 80-token full text is admitted ahead of genuinely denser items).
|
|
26
|
+
*
|
|
27
|
+
* Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT
|
|
28
|
+
* here: it is a ranking-time property, not an admissibility property.
|
|
29
|
+
*/
|
|
30
|
+
export function isRepresentationEligible(candidate, item, policy = DEFAULT_HEADROOM_QUALITY_POLICY) {
|
|
31
|
+
if (candidate.kind === "omit")
|
|
32
|
+
return policy.allowOmit;
|
|
33
|
+
// A non-full representation that does not actually save tokens is never a
|
|
34
|
+
// real choice; counting it as admissible only flatters the cost model.
|
|
35
|
+
if (candidate.kind !== "full" && candidate.estimatedTokens >= fullTextTokens(item))
|
|
36
|
+
return false;
|
|
37
|
+
const retrievable = item.sourceRef?.retrievable === true;
|
|
38
|
+
switch (candidate.kind) {
|
|
39
|
+
case "full":
|
|
40
|
+
return true;
|
|
41
|
+
case "pointer":
|
|
42
|
+
return policy.preferPointerForRetrievable && retrievable;
|
|
43
|
+
case "headroom-compressed":
|
|
44
|
+
return retrievable;
|
|
45
|
+
case "summary":
|
|
46
|
+
return true;
|
|
47
|
+
default:
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
19
51
|
export function chooseHeadroomRepresentation(item, budget, policy = DEFAULT_HEADROOM_QUALITY_POLICY) {
|
|
20
52
|
const candidates = item.representations ?? deriveRepresentationCandidates(item, policy);
|
|
21
53
|
const full = fullTextTokens(item);
|
|
@@ -35,6 +67,9 @@ export function chooseHeadroomRepresentation(item, budget, policy = DEFAULT_HEAD
|
|
|
35
67
|
let best;
|
|
36
68
|
let bestScore = Number.NEGATIVE_INFINITY;
|
|
37
69
|
for (const candidate of candidates) {
|
|
70
|
+
if (!isRepresentationEligible(candidate, item, policy)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
38
73
|
if (isMoreExpensiveThanFull(candidate, full)) {
|
|
39
74
|
continue;
|
|
40
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-headroom.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EAIN,+BAA+B,EAC/B,cAAc,GAGd,MAAM,oCAAoC,CAAC;AAE5C,MAAM,eAAe,GAA4C;IAChE,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,EAAE;IACV,GAAG,EAAE,EAAE;CACP,CAAC;AAEF,MAAM,cAAc,GAAiE;IACpF,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,KAAK,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AACvC,MAAM,kCAAkC,GAAG,EAAE,CAAC;AAE9C,MAAM,UAAU,4BAA4B,CAC3C,IAAyB,EACzB,MAAmC,EACnC,MAAM,GAA4B,+BAA+B,EAC9B;IACnC,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,IAAI,8BAA8B,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,iBAAiB,CAAC;IACnE,MAAM,WAAW,GAAG,MAAM,CAAC,qBAAqB,GAAG,IAAI,CAAC;IACxD,MAAM,KAAK,GAAG,QAAQ,IAAI,WAAW,CAAC;IACtC,8EAA8E;IAC9E,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACzB,MAAM,CAAC,qBAAqB,EAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,cAAc,CAAC,CAC7D,CAAC;IAEF,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/C,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;QAChF,IAAI,aAAa,EAAE,CAAC;YACnB,OAAO,aAAa,CAAC;QACtB,CAAC;IACF,CAAC;IAED,IAAI,IAAkD,CAAC;IACvD,IAAI,SAAS,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,uBAAuB,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;YAC9C,SAAS;QACV,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,eAAe,GAAG,SAAS,EAAE,CAAC;YACxE,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,wBAAwB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACvE,IACC,KAAK,GAAG,SAAS;YACjB,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,0BAA0B,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAC7F,CAAC;YACF,IAAI,GAAG,SAAS,CAAC;YACjB,SAAS,GAAG,KAAK,CAAC;QACnB,CAAC;IACF,CAAC;IACD,OAAO,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;AAAA,CAChG;AAED,SAAS,aAAa,CAAC,IAAyB,EAAoC;IACnF,OAAO;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,EAAE;QACR,eAAe,EAAE,CAAC;QAClB,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;KACzB,CAAC;AAAA,CACF;AAED,SAAS,uBAAuB,CAAC,SAA2C,EAAE,UAAkB,EAAW;IAC1G,OAAO,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,eAAe,IAAI,UAAU,CAAC;AAAA,CACzG;AAED,SAAS,wBAAwB,CAChC,SAA2C,EAC3C,IAAyB,EACzB,MAA+B,EAC/B,KAAc,EACL;IACT,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,SAAS,CAAC,eAAe,CAAC;IACvC,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,KAAK,IAAI,CAAC;IACzD,MAAM,UAAU,GACf,SAAS,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;QAC5B,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,KAAK,cAAc;YAC3C,CAAC,CAAC,KAAK;gBACN,CAAC,CAAC,kCAAkC;gBACpC,CAAC,CAAC,4BAA4B;YAC/B,CAAC,CAAC,KAAK;gBACN,CAAC,CAAC,qBAAqB;gBACvB,CAAC,CAAC,eAAe;QACnB,CAAC,CAAC,CAAC,CAAC;IAEN,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,MAAM,EAAE,CAAC;YACb,IAAI,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;YAC3D,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC9E,KAAK,IAAI,EAAE,CAAC;YACb,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,SAAS,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,2BAA2B,IAAI,CAAC,WAAW,EAAE,CAAC;gBACzD,OAAO,MAAM,CAAC,iBAAiB,CAAC;YACjC,CAAC;YACD,IAAI,KAAK,GAAG,EAAE,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,CAAC;YACpD,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,SAAS,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,IAAI,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,CAAC;YACvE,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,qBAAqB,EAAE,CAAC;YAC5B,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC,iBAAiB,CAAC;YACjC,CAAC;YACD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7E,IAAI,KAAK,GAAG,KAAK,GAAG,EAAE,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,CAAC;YAC5D,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACvB,OAAO,MAAM,CAAC,iBAAiB,CAAC;YACjC,CAAC;YACD,IAAI,KAAK,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,WAAW,EAAE,CAAC;gBACrD,OAAO,EAAE,CAAC;YACX,CAAC;YACD,OAAO,MAAM,CAAC,iBAAiB,CAAC;QACjC,CAAC;IACF,CAAC;AAAA,CACD;AAED,SAAS,QAAQ,CAAC,IAAyB,EAAU;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAAA,CAC9C;AAED,SAAS,aAAa,CAAC,IAAyB,EAAW;IAC1D,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;AAAA,CACtF;AAED,SAAS,0BAA0B,CAAC,CAAmC,EAAE,CAAmC,EAAU;IACrH,IAAI,CAAC,CAAC,eAAe,KAAK,CAAC,CAAC,eAAe,EAAE,CAAC;QAC7C,OAAO,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC;IAC9C,CAAC;IACD,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAAA,CACpC;AAED,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EAUN,+BAA+B,EAC/B,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,wBAAwB,EAGxB,mBAAmB,GAEnB,MAAM,oCAAoC,CAAC","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-headroom.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EAIN,+BAA+B,EAC/B,cAAc,GAGd,MAAM,oCAAoC,CAAC;AAE5C,MAAM,eAAe,GAA4C;IAChE,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,EAAE;IACV,GAAG,EAAE,EAAE;CACP,CAAC;AAEF,MAAM,cAAc,GAAiE;IACpF,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,KAAK,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AACvC,MAAM,kCAAkC,GAAG,EAAE,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CACvC,SAA2C,EAC3C,IAAyB,EACzB,MAAM,GAA4B,+BAA+B,EACvD;IACV,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC,SAAS,CAAC;IACvD,0EAA0E;IAC1E,uEAAuE;IACvE,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,eAAe,IAAI,cAAc,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjG,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,KAAK,IAAI,CAAC;IACzD,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,MAAM;YACV,OAAO,IAAI,CAAC;QACb,KAAK,SAAS;YACb,OAAO,MAAM,CAAC,2BAA2B,IAAI,WAAW,CAAC;QAC1D,KAAK,qBAAqB;YACzB,OAAO,WAAW,CAAC;QACpB,KAAK,SAAS;YACb,OAAO,IAAI,CAAC;QACb;YACC,OAAO,KAAK,CAAC;IACf,CAAC;AAAA,CACD;AAED,MAAM,UAAU,4BAA4B,CAC3C,IAAyB,EACzB,MAAmC,EACnC,MAAM,GAA4B,+BAA+B,EAC9B;IACnC,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,IAAI,8BAA8B,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,iBAAiB,CAAC;IACnE,MAAM,WAAW,GAAG,MAAM,CAAC,qBAAqB,GAAG,IAAI,CAAC;IACxD,MAAM,KAAK,GAAG,QAAQ,IAAI,WAAW,CAAC;IACtC,8EAA8E;IAC9E,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACzB,MAAM,CAAC,qBAAqB,EAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,cAAc,CAAC,CAC7D,CAAC;IAEF,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/C,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;QAChF,IAAI,aAAa,EAAE,CAAC;YACnB,OAAO,aAAa,CAAC;QACtB,CAAC;IACF,CAAC;IAED,IAAI,IAAkD,CAAC;IACvD,IAAI,SAAS,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,CAAC,wBAAwB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YACxD,SAAS;QACV,CAAC;QACD,IAAI,uBAAuB,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;YAC9C,SAAS;QACV,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,eAAe,GAAG,SAAS,EAAE,CAAC;YACxE,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,wBAAwB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACvE,IACC,KAAK,GAAG,SAAS;YACjB,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,0BAA0B,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAC7F,CAAC;YACF,IAAI,GAAG,SAAS,CAAC;YACjB,SAAS,GAAG,KAAK,CAAC;QACnB,CAAC;IACF,CAAC;IACD,OAAO,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;AAAA,CAChG;AAED,SAAS,aAAa,CAAC,IAAyB,EAAoC;IACnF,OAAO;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,EAAE;QACR,eAAe,EAAE,CAAC;QAClB,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;KACzB,CAAC;AAAA,CACF;AAED,SAAS,uBAAuB,CAAC,SAA2C,EAAE,UAAkB,EAAW;IAC1G,OAAO,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,eAAe,IAAI,UAAU,CAAC;AAAA,CACzG;AAED,SAAS,wBAAwB,CAChC,SAA2C,EAC3C,IAAyB,EACzB,MAA+B,EAC/B,KAAc,EACL;IACT,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,SAAS,CAAC,eAAe,CAAC;IACvC,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,KAAK,IAAI,CAAC;IACzD,MAAM,UAAU,GACf,SAAS,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;QAC5B,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,KAAK,cAAc;YAC3C,CAAC,CAAC,KAAK;gBACN,CAAC,CAAC,kCAAkC;gBACpC,CAAC,CAAC,4BAA4B;YAC/B,CAAC,CAAC,KAAK;gBACN,CAAC,CAAC,qBAAqB;gBACvB,CAAC,CAAC,eAAe;QACnB,CAAC,CAAC,CAAC,CAAC;IAEN,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,MAAM,EAAE,CAAC;YACb,IAAI,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;YAC3D,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC9E,KAAK,IAAI,EAAE,CAAC;YACb,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,SAAS,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,2BAA2B,IAAI,CAAC,WAAW,EAAE,CAAC;gBACzD,OAAO,MAAM,CAAC,iBAAiB,CAAC;YACjC,CAAC;YACD,IAAI,KAAK,GAAG,EAAE,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,CAAC;YACpD,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,SAAS,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,IAAI,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,CAAC;YACvE,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,qBAAqB,EAAE,CAAC;YAC5B,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC,iBAAiB,CAAC;YACjC,CAAC;YACD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7E,IAAI,KAAK,GAAG,KAAK,GAAG,EAAE,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,CAAC;YAC5D,IAAI,KAAK;gBAAE,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACvB,OAAO,MAAM,CAAC,iBAAiB,CAAC;YACjC,CAAC;YACD,IAAI,KAAK,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,WAAW,EAAE,CAAC;gBACrD,OAAO,EAAE,CAAC;YACX,CAAC;YACD,OAAO,MAAM,CAAC,iBAAiB,CAAC;QACjC,CAAC;IACF,CAAC;AAAA,CACD;AAED,SAAS,QAAQ,CAAC,IAAyB,EAAU;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAAA,CAC9C;AAED,SAAS,aAAa,CAAC,IAAyB,EAAW;IAC1D,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;AAAA,CACtF;AAED,SAAS,0BAA0B,CAAC,CAAmC,EAAE,CAAmC,EAAU;IACrH,IAAI,CAAC,CAAC,eAAe,KAAK,CAAC,CAAC,eAAe,EAAE,CAAC;QAC7C,OAAO,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC;IAC9C,CAAC;IACD,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAAA,CACpC;AAED,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EAUN,+BAA+B,EAC/B,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,wBAAwB,EAGxB,mBAAmB,GAEnB,MAAM,oCAAoC,CAAC","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\n/**\n * Budget-independent admissibility for one representation candidate — the\n * same gate {@link chooseHeadroomRepresentation} applies via its per-kind\n * rejections. Ranking and selection must share this set, otherwise a cheap\n * representation the chooser could never pick distorts the density\n * denominator (an item ranked on a 1-token pointer it can only use as a\n * 80-token full text is admitted ahead of genuinely denser items).\n *\n * Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT\n * here: it is a ranking-time property, not an admissibility property.\n */\nexport function isRepresentationEligible(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): boolean {\n\tif (candidate.kind === \"omit\") return policy.allowOmit;\n\t// A non-full representation that does not actually save tokens is never a\n\t// real choice; counting it as admissible only flatters the cost model.\n\tif (candidate.kind !== \"full\" && candidate.estimatedTokens >= fullTextTokens(item)) return false;\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tswitch (candidate.kind) {\n\t\tcase \"full\":\n\t\t\treturn true;\n\t\tcase \"pointer\":\n\t\t\treturn policy.preferPointerForRetrievable && retrievable;\n\t\tcase \"headroom-compressed\":\n\t\t\treturn retrievable;\n\t\tcase \"summary\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (!isRepresentationEligible(candidate, item, policy)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ContextBudgetItemV2 } from "./context-budget-headroom.ts";
|
|
2
|
+
import { type QualityDiagnosticV2 } from "./context-budget-v2-types.ts";
|
|
3
|
+
export declare function validateBudgetItems(items: readonly ContextBudgetItemV2[], diagnostics: QualityDiagnosticV2[]): ContextBudgetItemV2[];
|
|
4
|
+
//# sourceMappingURL=context-budget-v2-input-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget-v2-input-validation.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-input-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAatF,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,WAAW,EAAE,mBAAmB,EAAE,GAChC,mBAAmB,EAAE,CAoEvB","sourcesContent":["import type { ContextBudgetItemV2 } from \"./context-budget-headroom.ts\";\nimport { ALL_TIERS_V2, type QualityDiagnosticV2 } from \"./context-budget-v2-types.ts\";\n\n/**\n * Input boundary for the planner (audit §13): selection is keyed by `item.id`,\n * so a duplicate id would silently overwrite an earlier selection while its\n * tokens still counted twice in demand; a NaN or negative numeric field\n * poisons every budget sum it touches; an unregistered tier lands in no\n * allocation and corrupts `tierUsed` with NaN. Identity violations drop the\n * item with a diagnostic; invalid numeric fields are dropped and recomputed\n * rather than silently corrected.\n */\nconst KNOWN_TIERS_V2 = new Set<string>(ALL_TIERS_V2);\n\nexport function validateBudgetItems(\n\titems: readonly ContextBudgetItemV2[],\n\tdiagnostics: QualityDiagnosticV2[],\n): ContextBudgetItemV2[] {\n\tconst seen = new Set<string>();\n\tconst out: ContextBudgetItemV2[] = [];\n\tfor (const [position, item] of items.entries()) {\n\t\tif (typeof item.id !== \"string\" || item.id.length === 0) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\tdetail: `items[${position}] has a missing or non-string id and was dropped`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (seen.has(item.id)) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `duplicate item id \"${item.id}\" dropped — a later entry would overwrite the earlier selection`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tseen.add(item.id);\n\t\tif (!KNOWN_TIERS_V2.has(item.tier)) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `item \"${item.id}\" has unregistered tier \"${String(item.tier)}\" and was dropped`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tlet sanitized = item;\n\t\tif (item.tokenEstimate !== undefined && (!Number.isFinite(item.tokenEstimate) || item.tokenEstimate < 0)) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `item \"${item.id}\" tokenEstimate ${item.tokenEstimate} is not a finite non-negative number; recomputed from text`,\n\t\t\t});\n\t\t\tsanitized = { ...sanitized, tokenEstimate: undefined };\n\t\t}\n\t\tif (item.representations !== undefined) {\n\t\t\tconst reps = item.representations.filter((rep) => {\n\t\t\t\tif (!Number.isFinite(rep.estimatedTokens) || rep.estimatedTokens < 0) {\n\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\treason: \"invalid_input\",\n\t\t\t\t\t\titemId: item.id,\n\t\t\t\t\t\tdetail: `item \"${item.id}\" ${rep.kind} representation cost ${rep.estimatedTokens} is not a finite non-negative number; dropped`,\n\t\t\t\t\t});\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\tif (reps.length !== item.representations.length) {\n\t\t\t\tsanitized = { ...sanitized, representations: reps };\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\titem.priority !== \"hard\" &&\n\t\t\titem.priority !== \"high\" &&\n\t\t\titem.priority !== \"medium\" &&\n\t\t\titem.priority !== \"low\"\n\t\t) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `item \"${item.id}\" has unknown priority \"${String(item.priority)}\"; scored without a priority weight`,\n\t\t\t});\n\t\t}\n\t\tout.push(sanitized);\n\t}\n\treturn out;\n}\n"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ALL_TIERS_V2 } from "./context-budget-v2-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Input boundary for the planner (audit §13): selection is keyed by `item.id`,
|
|
4
|
+
* so a duplicate id would silently overwrite an earlier selection while its
|
|
5
|
+
* tokens still counted twice in demand; a NaN or negative numeric field
|
|
6
|
+
* poisons every budget sum it touches; an unregistered tier lands in no
|
|
7
|
+
* allocation and corrupts `tierUsed` with NaN. Identity violations drop the
|
|
8
|
+
* item with a diagnostic; invalid numeric fields are dropped and recomputed
|
|
9
|
+
* rather than silently corrected.
|
|
10
|
+
*/
|
|
11
|
+
const KNOWN_TIERS_V2 = new Set(ALL_TIERS_V2);
|
|
12
|
+
export function validateBudgetItems(items, diagnostics) {
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const [position, item] of items.entries()) {
|
|
16
|
+
if (typeof item.id !== "string" || item.id.length === 0) {
|
|
17
|
+
diagnostics.push({
|
|
18
|
+
reason: "invalid_input",
|
|
19
|
+
detail: `items[${position}] has a missing or non-string id and was dropped`,
|
|
20
|
+
});
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (seen.has(item.id)) {
|
|
24
|
+
diagnostics.push({
|
|
25
|
+
reason: "invalid_input",
|
|
26
|
+
itemId: item.id,
|
|
27
|
+
detail: `duplicate item id "${item.id}" dropped — a later entry would overwrite the earlier selection`,
|
|
28
|
+
});
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
seen.add(item.id);
|
|
32
|
+
if (!KNOWN_TIERS_V2.has(item.tier)) {
|
|
33
|
+
diagnostics.push({
|
|
34
|
+
reason: "invalid_input",
|
|
35
|
+
itemId: item.id,
|
|
36
|
+
detail: `item "${item.id}" has unregistered tier "${String(item.tier)}" and was dropped`,
|
|
37
|
+
});
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
let sanitized = item;
|
|
41
|
+
if (item.tokenEstimate !== undefined && (!Number.isFinite(item.tokenEstimate) || item.tokenEstimate < 0)) {
|
|
42
|
+
diagnostics.push({
|
|
43
|
+
reason: "invalid_input",
|
|
44
|
+
itemId: item.id,
|
|
45
|
+
detail: `item "${item.id}" tokenEstimate ${item.tokenEstimate} is not a finite non-negative number; recomputed from text`,
|
|
46
|
+
});
|
|
47
|
+
sanitized = { ...sanitized, tokenEstimate: undefined };
|
|
48
|
+
}
|
|
49
|
+
if (item.representations !== undefined) {
|
|
50
|
+
const reps = item.representations.filter((rep) => {
|
|
51
|
+
if (!Number.isFinite(rep.estimatedTokens) || rep.estimatedTokens < 0) {
|
|
52
|
+
diagnostics.push({
|
|
53
|
+
reason: "invalid_input",
|
|
54
|
+
itemId: item.id,
|
|
55
|
+
detail: `item "${item.id}" ${rep.kind} representation cost ${rep.estimatedTokens} is not a finite non-negative number; dropped`,
|
|
56
|
+
});
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
});
|
|
61
|
+
if (reps.length !== item.representations.length) {
|
|
62
|
+
sanitized = { ...sanitized, representations: reps };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (item.priority !== "hard" &&
|
|
66
|
+
item.priority !== "high" &&
|
|
67
|
+
item.priority !== "medium" &&
|
|
68
|
+
item.priority !== "low") {
|
|
69
|
+
diagnostics.push({
|
|
70
|
+
reason: "invalid_input",
|
|
71
|
+
itemId: item.id,
|
|
72
|
+
detail: `item "${item.id}" has unknown priority "${String(item.priority)}"; scored without a priority weight`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
out.push(sanitized);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=context-budget-v2-input-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget-v2-input-validation.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-input-validation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContextBudgetItemV2 } from \"./context-budget-headroom.ts\";\nimport { ALL_TIERS_V2, type QualityDiagnosticV2 } from \"./context-budget-v2-types.ts\";\n\n/**\n * Input boundary for the planner (audit §13): selection is keyed by `item.id`,\n * so a duplicate id would silently overwrite an earlier selection while its\n * tokens still counted twice in demand; a NaN or negative numeric field\n * poisons every budget sum it touches; an unregistered tier lands in no\n * allocation and corrupts `tierUsed` with NaN. Identity violations drop the\n * item with a diagnostic; invalid numeric fields are dropped and recomputed\n * rather than silently corrected.\n */\nconst KNOWN_TIERS_V2 = new Set<string>(ALL_TIERS_V2);\n\nexport function validateBudgetItems(\n\titems: readonly ContextBudgetItemV2[],\n\tdiagnostics: QualityDiagnosticV2[],\n): ContextBudgetItemV2[] {\n\tconst seen = new Set<string>();\n\tconst out: ContextBudgetItemV2[] = [];\n\tfor (const [position, item] of items.entries()) {\n\t\tif (typeof item.id !== \"string\" || item.id.length === 0) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\tdetail: `items[${position}] has a missing or non-string id and was dropped`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (seen.has(item.id)) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `duplicate item id \"${item.id}\" dropped — a later entry would overwrite the earlier selection`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tseen.add(item.id);\n\t\tif (!KNOWN_TIERS_V2.has(item.tier)) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `item \"${item.id}\" has unregistered tier \"${String(item.tier)}\" and was dropped`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tlet sanitized = item;\n\t\tif (item.tokenEstimate !== undefined && (!Number.isFinite(item.tokenEstimate) || item.tokenEstimate < 0)) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `item \"${item.id}\" tokenEstimate ${item.tokenEstimate} is not a finite non-negative number; recomputed from text`,\n\t\t\t});\n\t\t\tsanitized = { ...sanitized, tokenEstimate: undefined };\n\t\t}\n\t\tif (item.representations !== undefined) {\n\t\t\tconst reps = item.representations.filter((rep) => {\n\t\t\t\tif (!Number.isFinite(rep.estimatedTokens) || rep.estimatedTokens < 0) {\n\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\treason: \"invalid_input\",\n\t\t\t\t\t\titemId: item.id,\n\t\t\t\t\t\tdetail: `item \"${item.id}\" ${rep.kind} representation cost ${rep.estimatedTokens} is not a finite non-negative number; dropped`,\n\t\t\t\t\t});\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\tif (reps.length !== item.representations.length) {\n\t\t\t\tsanitized = { ...sanitized, representations: reps };\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\titem.priority !== \"hard\" &&\n\t\t\titem.priority !== \"high\" &&\n\t\t\titem.priority !== \"medium\" &&\n\t\t\titem.priority !== \"low\"\n\t\t) {\n\t\t\tdiagnostics.push({\n\t\t\t\treason: \"invalid_input\",\n\t\t\t\titemId: item.id,\n\t\t\t\tdetail: `item \"${item.id}\" has unknown priority \"${String(item.priority)}\"; scored without a priority weight`,\n\t\t\t});\n\t\t}\n\t\tout.push(sanitized);\n\t}\n\treturn out;\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ContextSourceRefV2 } from "./context-budget-headroom.ts";
|
|
2
|
+
import type { PromptContextBudgetObservabilityV2, QualityDiagnosticV2, SelectedRepresentationV2 } from "./context-budget-v2-types.ts";
|
|
3
|
+
export declare function buildObservability(input: {
|
|
4
|
+
readonly available: number;
|
|
5
|
+
readonly diagnostics: readonly QualityDiagnosticV2[];
|
|
6
|
+
readonly omittedItemIds: readonly string[];
|
|
7
|
+
readonly omittedTokens: number;
|
|
8
|
+
readonly planHash: string;
|
|
9
|
+
readonly rawTokens: number;
|
|
10
|
+
readonly retrievalFallbacks: readonly ContextSourceRefV2[];
|
|
11
|
+
readonly selection: ReadonlyMap<string, SelectedRepresentationV2>;
|
|
12
|
+
readonly usedTokens: number;
|
|
13
|
+
readonly cacheTelemetry: PromptContextBudgetObservabilityV2["cache"];
|
|
14
|
+
}): PromptContextBudgetObservabilityV2;
|
|
15
|
+
//# sourceMappingURL=context-budget-v2-observability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget-v2-observability.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-observability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EACX,kCAAkC,EAClC,mBAAmB,EACnB,wBAAwB,EAExB,MAAM,8BAA8B,CAAC;AAUtC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACrD,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC3D,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,kCAAkC,CAAC,OAAO,CAAC,CAAC;CACrE,GAAG,kCAAkC,CAuBrC","sourcesContent":["import type { ContextSourceRefV2 } from \"./context-budget-headroom.ts\";\nimport type {\n\tPromptContextBudgetObservabilityV2,\n\tQualityDiagnosticV2,\n\tSelectedRepresentationV2,\n\tTokenOptimizerRuntimeStatus,\n} from \"./context-budget-v2-types.ts\";\n\nconst TOKEN_OPTIMIZER_RUNTIME_STATUS: TokenOptimizerRuntimeStatus = {\n\toptimizerId: \"legacy-token-optimizer\",\n\tstatus: \"quarantined_compatibility\",\n\tactive: false,\n\tactiveContextBudgetOptimizer: \"context-budget-v2\",\n\tcompatibilityOnly: true,\n};\n\nexport function buildObservability(input: {\n\treadonly available: number;\n\treadonly diagnostics: readonly QualityDiagnosticV2[];\n\treadonly omittedItemIds: readonly string[];\n\treadonly omittedTokens: number;\n\treadonly planHash: string;\n\treadonly rawTokens: number;\n\treadonly retrievalFallbacks: readonly ContextSourceRefV2[];\n\treadonly selection: ReadonlyMap<string, SelectedRepresentationV2>;\n\treadonly usedTokens: number;\n\treadonly cacheTelemetry: PromptContextBudgetObservabilityV2[\"cache\"];\n}): PromptContextBudgetObservabilityV2 {\n\tconst selected = [...input.selection.values()];\n\treturn {\n\t\tcounts: {\n\t\t\tselected: selected.length,\n\t\t\tomitted: input.omittedItemIds.length,\n\t\t\tpointer: selected.filter((representation) => representation.kind === \"pointer\").length,\n\t\t\tcompressed: selected.filter((representation) => representation.kind === \"headroom-compressed\").length,\n\t\t\tfull: selected.filter((representation) => representation.kind === \"full\").length,\n\t\t\tretrievalFallback: countUniqueRetrievalFallbacks(input.retrievalFallbacks),\n\t\t},\n\t\tdiagnosticReasons: [...new Set(input.diagnostics.map((diagnostic) => diagnostic.reason))].sort(),\n\t\ttokens: {\n\t\t\tavailable: input.available,\n\t\t\tused: input.usedTokens,\n\t\t\traw: input.rawTokens,\n\t\t\tomitted: input.omittedTokens,\n\t\t\ttokenSavings: Math.max(0, input.rawTokens - input.usedTokens),\n\t\t},\n\t\tplanHash: input.planHash,\n\t\tcache: input.cacheTelemetry,\n\t\ttokenOptimizer: TOKEN_OPTIMIZER_RUNTIME_STATUS,\n\t};\n}\n\nfunction countUniqueRetrievalFallbacks(retrievalFallbacks: readonly ContextSourceRefV2[]): number {\n\treturn new Set(\n\t\tretrievalFallbacks.map((ref) =>\n\t\t\t[ref.uri, ref.contentHash, ref.symbol ?? \"\", ref.range?.startLine ?? \"\", ref.range?.endLine ?? \"\"].join(\"\\0\"),\n\t\t),\n\t).size;\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const TOKEN_OPTIMIZER_RUNTIME_STATUS = {
|
|
2
|
+
optimizerId: "legacy-token-optimizer",
|
|
3
|
+
status: "quarantined_compatibility",
|
|
4
|
+
active: false,
|
|
5
|
+
activeContextBudgetOptimizer: "context-budget-v2",
|
|
6
|
+
compatibilityOnly: true,
|
|
7
|
+
};
|
|
8
|
+
export function buildObservability(input) {
|
|
9
|
+
const selected = [...input.selection.values()];
|
|
10
|
+
return {
|
|
11
|
+
counts: {
|
|
12
|
+
selected: selected.length,
|
|
13
|
+
omitted: input.omittedItemIds.length,
|
|
14
|
+
pointer: selected.filter((representation) => representation.kind === "pointer").length,
|
|
15
|
+
compressed: selected.filter((representation) => representation.kind === "headroom-compressed").length,
|
|
16
|
+
full: selected.filter((representation) => representation.kind === "full").length,
|
|
17
|
+
retrievalFallback: countUniqueRetrievalFallbacks(input.retrievalFallbacks),
|
|
18
|
+
},
|
|
19
|
+
diagnosticReasons: [...new Set(input.diagnostics.map((diagnostic) => diagnostic.reason))].sort(),
|
|
20
|
+
tokens: {
|
|
21
|
+
available: input.available,
|
|
22
|
+
used: input.usedTokens,
|
|
23
|
+
raw: input.rawTokens,
|
|
24
|
+
omitted: input.omittedTokens,
|
|
25
|
+
tokenSavings: Math.max(0, input.rawTokens - input.usedTokens),
|
|
26
|
+
},
|
|
27
|
+
planHash: input.planHash,
|
|
28
|
+
cache: input.cacheTelemetry,
|
|
29
|
+
tokenOptimizer: TOKEN_OPTIMIZER_RUNTIME_STATUS,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function countUniqueRetrievalFallbacks(retrievalFallbacks) {
|
|
33
|
+
return new Set(retrievalFallbacks.map((ref) => [ref.uri, ref.contentHash, ref.symbol ?? "", ref.range?.startLine ?? "", ref.range?.endLine ?? ""].join("\0"))).size;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=context-budget-v2-observability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget-v2-observability.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-observability.ts"],"names":[],"mappings":"AAQA,MAAM,8BAA8B,GAAgC;IACnE,WAAW,EAAE,wBAAwB;IACrC,MAAM,EAAE,2BAA2B;IACnC,MAAM,EAAE,KAAK;IACb,4BAA4B,EAAE,mBAAmB;IACjD,iBAAiB,EAAE,IAAI;CACvB,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,KAWlC,EAAsC;IACtC,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/C,OAAO;QACN,MAAM,EAAE;YACP,QAAQ,EAAE,QAAQ,CAAC,MAAM;YACzB,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM;YACpC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,MAAM;YACtF,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,KAAK,qBAAqB,CAAC,CAAC,MAAM;YACrG,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,MAAM;YAChF,iBAAiB,EAAE,6BAA6B,CAAC,KAAK,CAAC,kBAAkB,CAAC;SAC1E;QACD,iBAAiB,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QAChG,MAAM,EAAE;YACP,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,IAAI,EAAE,KAAK,CAAC,UAAU;YACtB,GAAG,EAAE,KAAK,CAAC,SAAS;YACpB,OAAO,EAAE,KAAK,CAAC,aAAa;YAC5B,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;SAC7D;QACD,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,cAAc;QAC3B,cAAc,EAAE,8BAA8B;KAC9C,CAAC;AAAA,CACF;AAED,SAAS,6BAA6B,CAAC,kBAAiD,EAAU;IACjG,OAAO,IAAI,GAAG,CACb,kBAAkB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAC9B,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC7G,CACD,CAAC,IAAI,CAAC;AAAA,CACP","sourcesContent":["import type { ContextSourceRefV2 } from \"./context-budget-headroom.ts\";\nimport type {\n\tPromptContextBudgetObservabilityV2,\n\tQualityDiagnosticV2,\n\tSelectedRepresentationV2,\n\tTokenOptimizerRuntimeStatus,\n} from \"./context-budget-v2-types.ts\";\n\nconst TOKEN_OPTIMIZER_RUNTIME_STATUS: TokenOptimizerRuntimeStatus = {\n\toptimizerId: \"legacy-token-optimizer\",\n\tstatus: \"quarantined_compatibility\",\n\tactive: false,\n\tactiveContextBudgetOptimizer: \"context-budget-v2\",\n\tcompatibilityOnly: true,\n};\n\nexport function buildObservability(input: {\n\treadonly available: number;\n\treadonly diagnostics: readonly QualityDiagnosticV2[];\n\treadonly omittedItemIds: readonly string[];\n\treadonly omittedTokens: number;\n\treadonly planHash: string;\n\treadonly rawTokens: number;\n\treadonly retrievalFallbacks: readonly ContextSourceRefV2[];\n\treadonly selection: ReadonlyMap<string, SelectedRepresentationV2>;\n\treadonly usedTokens: number;\n\treadonly cacheTelemetry: PromptContextBudgetObservabilityV2[\"cache\"];\n}): PromptContextBudgetObservabilityV2 {\n\tconst selected = [...input.selection.values()];\n\treturn {\n\t\tcounts: {\n\t\t\tselected: selected.length,\n\t\t\tomitted: input.omittedItemIds.length,\n\t\t\tpointer: selected.filter((representation) => representation.kind === \"pointer\").length,\n\t\t\tcompressed: selected.filter((representation) => representation.kind === \"headroom-compressed\").length,\n\t\t\tfull: selected.filter((representation) => representation.kind === \"full\").length,\n\t\t\tretrievalFallback: countUniqueRetrievalFallbacks(input.retrievalFallbacks),\n\t\t},\n\t\tdiagnosticReasons: [...new Set(input.diagnostics.map((diagnostic) => diagnostic.reason))].sort(),\n\t\ttokens: {\n\t\t\tavailable: input.available,\n\t\t\tused: input.usedTokens,\n\t\t\traw: input.rawTokens,\n\t\t\tomitted: input.omittedTokens,\n\t\t\ttokenSavings: Math.max(0, input.rawTokens - input.usedTokens),\n\t\t},\n\t\tplanHash: input.planHash,\n\t\tcache: input.cacheTelemetry,\n\t\ttokenOptimizer: TOKEN_OPTIMIZER_RUNTIME_STATUS,\n\t};\n}\n\nfunction countUniqueRetrievalFallbacks(retrievalFallbacks: readonly ContextSourceRefV2[]): number {\n\treturn new Set(\n\t\tretrievalFallbacks.map((ref) =>\n\t\t\t[ref.uri, ref.contentHash, ref.symbol ?? \"\", ref.range?.startLine ?? \"\", ref.range?.endLine ?? \"\"].join(\"\\0\"),\n\t\t),\n\t).size;\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ContextBudgetItemV2, deriveRepresentationCandidates } from "./context-budget-headroom.ts";
|
|
2
|
+
import { type TokenCounterAdapter } from "./context-budget-token-counter.ts";
|
|
3
|
+
import { type PlannedItemV2 } from "./context-budget-v2-scoring.ts";
|
|
4
|
+
export declare function createPlannedItems(items: readonly ContextBudgetItemV2[], tokenCounter: TokenCounterAdapter | undefined, modelId: string, qualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1]): PlannedItemV2[];
|
|
5
|
+
//# sourceMappingURL=context-budget-v2-planned-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget-v2-planned-items.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-planned-items.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,mBAAmB,EAExB,8BAA8B,EAI9B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAA8B,KAAK,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAEzG,OAAO,EAAE,KAAK,aAAa,EAA4B,MAAM,gCAAgC,CAAC;AAwB9F,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,YAAY,EAAE,mBAAmB,GAAG,SAAS,EAC7C,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,8BAA8B,CAAC,CAAC,CAAC,CAAC,GAClE,aAAa,EAAE,CAsBjB","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\tderiveRepresentationCandidates,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\tisRepresentationEligible,\n} from \"./context-budget-headroom.ts\";\nimport { createFallbackTokenCounter, type TokenCounterAdapter } from \"./context-budget-token-counter.ts\";\nimport { contentHashOf } from \"./context-budget-v2-plan-hash.ts\";\nimport { type PlannedItemV2, scoreContextBudgetItemV2 } from \"./context-budget-v2-scoring.ts\";\n\n// Cheapest representation the chooser could actually pick: only candidates\n// passing the selection policy gate count, so a pointer the chooser would\n// reject cannot make an item look cheaper to admit than it is.\nfunction minAdmissibleTokens(\n\tcandidates: readonly ContextRepresentationCandidateV2[],\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 | undefined,\n\tfullTokens: number,\n): number {\n\tlet min = Number.POSITIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (\n\t\t\tcandidate.kind !== \"omit\" &&\n\t\t\tisRepresentationEligible(candidate, item, policy) &&\n\t\t\tcandidate.estimatedTokens < min\n\t\t) {\n\t\t\tmin = candidate.estimatedTokens;\n\t\t}\n\t}\n\treturn Math.max(1, Number.isFinite(min) ? min : fullTokens);\n}\n\nexport function createPlannedItems(\n\titems: readonly ContextBudgetItemV2[],\n\ttokenCounter: TokenCounterAdapter | undefined,\n\tmodelId: string,\n\tqualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1],\n): PlannedItemV2[] {\n\tconst counter = tokenCounter ?? createFallbackTokenCounter();\n\treturn items.map((item) => {\n\t\tconst overrideTokenEstimate = item.tokenEstimate ?? counter.countText(item.text, modelId).tokens;\n\t\tconst itemWithTokens =\n\t\t\titem.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;\n\t\tconst fullTokens = fullTextTokens(itemWithTokens);\n\t\tconst isHard = item.priority === \"hard\" || item.required === true;\n\t\tconst baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);\n\t\tconst candidates =\n\t\t\titemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy);\n\t\treturn {\n\t\t\titem: itemWithTokens,\n\t\t\tfullTokens,\n\t\t\tadmissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),\n\t\t\tcontentHash: contentHashOf(item.text),\n\t\t\tbaseScore,\n\t\t\tredundancyPenalty: 0,\n\t\t\teffectiveScore: baseScore,\n\t\t\tisHard,\n\t\t};\n\t});\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { deriveRepresentationCandidates, fullTextTokens, isRepresentationEligible, } from "./context-budget-headroom.js";
|
|
2
|
+
import { createFallbackTokenCounter } from "./context-budget-token-counter.js";
|
|
3
|
+
import { contentHashOf } from "./context-budget-v2-plan-hash.js";
|
|
4
|
+
import { scoreContextBudgetItemV2 } from "./context-budget-v2-scoring.js";
|
|
5
|
+
// Cheapest representation the chooser could actually pick: only candidates
|
|
6
|
+
// passing the selection policy gate count, so a pointer the chooser would
|
|
7
|
+
// reject cannot make an item look cheaper to admit than it is.
|
|
8
|
+
function minAdmissibleTokens(candidates, item, policy, fullTokens) {
|
|
9
|
+
let min = Number.POSITIVE_INFINITY;
|
|
10
|
+
for (const candidate of candidates) {
|
|
11
|
+
if (candidate.kind !== "omit" &&
|
|
12
|
+
isRepresentationEligible(candidate, item, policy) &&
|
|
13
|
+
candidate.estimatedTokens < min) {
|
|
14
|
+
min = candidate.estimatedTokens;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return Math.max(1, Number.isFinite(min) ? min : fullTokens);
|
|
18
|
+
}
|
|
19
|
+
export function createPlannedItems(items, tokenCounter, modelId, qualityPolicy) {
|
|
20
|
+
const counter = tokenCounter ?? createFallbackTokenCounter();
|
|
21
|
+
return items.map((item) => {
|
|
22
|
+
const overrideTokenEstimate = item.tokenEstimate ?? counter.countText(item.text, modelId).tokens;
|
|
23
|
+
const itemWithTokens = item.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;
|
|
24
|
+
const fullTokens = fullTextTokens(itemWithTokens);
|
|
25
|
+
const isHard = item.priority === "hard" || item.required === true;
|
|
26
|
+
const baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);
|
|
27
|
+
const candidates = itemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy);
|
|
28
|
+
return {
|
|
29
|
+
item: itemWithTokens,
|
|
30
|
+
fullTokens,
|
|
31
|
+
admissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),
|
|
32
|
+
contentHash: contentHashOf(item.text),
|
|
33
|
+
baseScore,
|
|
34
|
+
redundancyPenalty: 0,
|
|
35
|
+
effectiveScore: baseScore,
|
|
36
|
+
isHard,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=context-budget-v2-planned-items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget-v2-planned-items.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-planned-items.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,8BAA8B,EAC9B,cAAc,EAEd,wBAAwB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,0BAA0B,EAA4B,MAAM,mCAAmC,CAAC;AACzG,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAsB,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE9F,2EAA2E;AAC3E,0EAA0E;AAC1E,+DAA+D;AAC/D,SAAS,mBAAmB,CAC3B,UAAuD,EACvD,IAAyB,EACzB,MAA2C,EAC3C,UAAkB,EACT;IACT,IAAI,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACnC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IACC,SAAS,CAAC,IAAI,KAAK,MAAM;YACzB,wBAAwB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC;YACjD,SAAS,CAAC,eAAe,GAAG,GAAG,EAC9B,CAAC;YACF,GAAG,GAAG,SAAS,CAAC,eAAe,CAAC;QACjC,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AAAA,CAC5D;AAED,MAAM,UAAU,kBAAkB,CACjC,KAAqC,EACrC,YAA6C,EAC7C,OAAe,EACf,aAAoE,EAClD;IAClB,MAAM,OAAO,GAAG,YAAY,IAAI,0BAA0B,EAAE,CAAC;IAC7D,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1B,MAAM,qBAAqB,GAAG,IAAI,CAAC,aAAa,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC;QACjG,MAAM,cAAc,GACnB,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7F,MAAM,UAAU,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;QAClE,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,wBAAwB,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAC3G,MAAM,UAAU,GACf,cAAc,CAAC,eAAe,IAAI,8BAA8B,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;QACjG,OAAO;YACN,IAAI,EAAE,cAAc;YACpB,UAAU;YACV,gBAAgB,EAAE,mBAAmB,CAAC,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,CAAC;YAC5F,WAAW,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YACrC,SAAS;YACT,iBAAiB,EAAE,CAAC;YACpB,cAAc,EAAE,SAAS;YACzB,MAAM;SACN,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACH","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\tderiveRepresentationCandidates,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\tisRepresentationEligible,\n} from \"./context-budget-headroom.ts\";\nimport { createFallbackTokenCounter, type TokenCounterAdapter } from \"./context-budget-token-counter.ts\";\nimport { contentHashOf } from \"./context-budget-v2-plan-hash.ts\";\nimport { type PlannedItemV2, scoreContextBudgetItemV2 } from \"./context-budget-v2-scoring.ts\";\n\n// Cheapest representation the chooser could actually pick: only candidates\n// passing the selection policy gate count, so a pointer the chooser would\n// reject cannot make an item look cheaper to admit than it is.\nfunction minAdmissibleTokens(\n\tcandidates: readonly ContextRepresentationCandidateV2[],\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 | undefined,\n\tfullTokens: number,\n): number {\n\tlet min = Number.POSITIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (\n\t\t\tcandidate.kind !== \"omit\" &&\n\t\t\tisRepresentationEligible(candidate, item, policy) &&\n\t\t\tcandidate.estimatedTokens < min\n\t\t) {\n\t\t\tmin = candidate.estimatedTokens;\n\t\t}\n\t}\n\treturn Math.max(1, Number.isFinite(min) ? min : fullTokens);\n}\n\nexport function createPlannedItems(\n\titems: readonly ContextBudgetItemV2[],\n\ttokenCounter: TokenCounterAdapter | undefined,\n\tmodelId: string,\n\tqualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1],\n): PlannedItemV2[] {\n\tconst counter = tokenCounter ?? createFallbackTokenCounter();\n\treturn items.map((item) => {\n\t\tconst overrideTokenEstimate = item.tokenEstimate ?? counter.countText(item.text, modelId).tokens;\n\t\tconst itemWithTokens =\n\t\t\titem.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;\n\t\tconst fullTokens = fullTextTokens(itemWithTokens);\n\t\tconst isHard = item.priority === \"hard\" || item.required === true;\n\t\tconst baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);\n\t\tconst candidates =\n\t\t\titemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy);\n\t\treturn {\n\t\t\titem: itemWithTokens,\n\t\t\tfullTokens,\n\t\t\tadmissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),\n\t\t\tcontentHash: contentHashOf(item.text),\n\t\t\tbaseScore,\n\t\t\tredundancyPenalty: 0,\n\t\t\teffectiveScore: baseScore,\n\t\t\tisHard,\n\t\t};\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-v2-planner.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-planner.ts"],"names":[],"mappings":"AA2BA,OAAO,EAGN,KAAK,0BAA0B,EAE/B,KAAK,yBAAyB,EAK9B,MAAM,8BAA8B,CAAC;AAUtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,0BAA0B,GAAG,yBAAyB,CA8KtG","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n} from \"./context-budget-headroom.ts\";\nimport {\n\tbuildContextBudgetPlanCacheKeyV2,\n\tcreateContextBudgetCacheKeyBaseV2,\n\tcreateContextBudgetSelectionCacheV2,\n\tfinalizeContextBudgetCacheTelemetryV2,\n\treadValidPlanCacheV2,\n\twritePlanCacheV2,\n} from \"./context-budget-v2-cache.ts\";\nimport { computePlanHash } from \"./context-budget-v2-plan-hash.ts\";\nimport { compareOptionalForSelection } from \"./context-budget-v2-scoring.ts\";\nimport {\n\tapplyPlannerRedundancyPenalties,\n\tbuildTierAllocations,\n\tcreatePlannedItems,\n\tcreateTierUsage,\n\temitCoverageDiagnostics,\n\temitOmissionDiagnostics,\n\tselectHardItem,\n\tselectOptionalItem,\n} from \"./context-budget-v2-selection.ts\";\nimport { allocateTiers, computeTierDemand } from \"./context-budget-v2-tiers.ts\";\nimport {\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\tDEFAULT_TIER_POLICY_V2,\n\ttype PromptContextBudgetInputV2,\n\ttype PromptContextBudgetObservabilityV2,\n\ttype PromptContextBudgetPlanV2,\n\ttype QualityDiagnosticV2,\n\ttype SelectedRepresentationV2,\n\ttype TierBudgetPolicyV2,\n\ttype TokenOptimizerRuntimeStatus,\n} from \"./context-budget-v2-types.ts\";\n\nconst TOKEN_OPTIMIZER_RUNTIME_STATUS: TokenOptimizerRuntimeStatus = {\n\toptimizerId: \"legacy-token-optimizer\",\n\tstatus: \"quarantined_compatibility\",\n\tactive: false,\n\tactiveContextBudgetOptimizer: \"context-budget-v2\",\n\tcompatibilityOnly: true,\n};\n\nexport function planPromptContextBudgetV2(input: PromptContextBudgetInputV2): PromptContextBudgetPlanV2 {\n\tconst modelId = input.modelId ?? \"unknown\";\n\tconst qualityPolicy = input.qualityPolicy ?? DEFAULT_HEADROOM_QUALITY_POLICY;\n\tconst policyVersion = input.policyVersion ?? CONTEXT_BUDGET_POLICY_VERSION_V2;\n\tconst diagnostics: QualityDiagnosticV2[] = [];\n\n\tconst maxTokens = normalizeBudgetTokens(\"maxTokens\", input.maxTokens, diagnostics);\n\tconst responseReserve = normalizeBudgetTokens(\n\t\t\"responseReserveTokens\",\n\t\tinput.responseReserveTokens ?? 0,\n\t\tdiagnostics,\n\t);\n\tconst safetyMargin = normalizeBudgetTokens(\n\t\t\"safetyMarginTokens\",\n\t\tinput.safetyMarginTokens ?? Math.ceil(maxTokens * 0.05),\n\t\tdiagnostics,\n\t);\n\tconst available = Math.max(0, maxTokens - responseReserve - safetyMargin);\n\tconst canUsePlanCache = !diagnostics.some((diagnostic) => diagnostic.reason === \"invalid_budget\");\n\n\tconst tierPolicy: Readonly<Record<ContextBudgetTierV2, TierBudgetPolicyV2>> = {\n\t\t...DEFAULT_TIER_POLICY_V2,\n\t\t...(input.tierPolicy ?? {}),\n\t};\n\tconst basePlanned = createPlannedItems(input.items, input.tokenCounter, modelId, qualityPolicy);\n\tapplyPlannerRedundancyPenalties(basePlanned);\n\tconst rawTokens = basePlanned.reduce((sum, planned) => sum + planned.fullTokens, 0);\n\n\tconst demand = computeTierDemand(basePlanned);\n\tconst allocation = allocateTiers(available, tierPolicy, demand);\n\tconst cacheKeyBase = createContextBudgetCacheKeyBaseV2({\n\t\tbudgetBucket: input.cacheBudgetBucket ?? String(available),\n\t\tcacheInvalidationSnapshot: input.cacheInvalidationSnapshot ?? input.cacheProvider?.getInvalidationSnapshot?.(),\n\t\tmodelId,\n\t\tnamespace: input.cacheNamespace,\n\t\tpolicyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion,\n\t\tquery: input.query,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\ttokenizerId: input.tokenizerId,\n\t});\n\tconst planCacheKey = buildContextBudgetPlanCacheKeyV2({\n\t\tavailableTokens: available,\n\t\tkeyBase: cacheKeyBase,\n\t\tmaxTokens,\n\t\tplanned: basePlanned,\n\t\tpromptHash: input.promptHash,\n\t\tqualityPolicy,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\ttierPolicy,\n\t});\n\tconst cache = createContextBudgetSelectionCacheV2({\n\t\tkeyBase: cacheKeyBase,\n\t\tnowEpochMs: input.cacheNowEpochMs,\n\t\tplanCacheKey,\n\t\tprovider: input.cacheProvider,\n\t\tttlMs: input.cacheTtlMs,\n\t});\n\tconst cachedPlan = canUsePlanCache\n\t\t? readValidPlanCacheV2({\n\t\t\t\tavailableTokens: available,\n\t\t\t\tcache,\n\t\t\t\tkey: planCacheKey,\n\t\t\t\tplanned: basePlanned,\n\t\t\t})\n\t\t: undefined;\n\tif (cachedPlan) {\n\t\treturn cachedPlan;\n\t}\n\tconst selection = new Map<string, SelectedRepresentationV2>();\n\tconst tierUsed = createTierUsage();\n\tconst omitted: ContextBudgetItemV2[] = [];\n\tconst retrievalFallbacks: ContextSourceRefV2[] = [];\n\tlet usedTokens = 0;\n\n\tfor (const planned of basePlanned) {\n\t\tif (!planned.isHard) continue;\n\t\tusedTokens += selectHardItem(planned, selection, tierUsed);\n\t}\n\tif (usedTokens > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"hard_pin_over_capacity\",\n\t\t\tdetail: `hard-pinned context uses ${usedTokens} tokens against ${available} available tokens`,\n\t\t});\n\t}\n\n\tconst optionalPlanned = basePlanned.filter((planned) => !planned.isHard).sort(compareOptionalForSelection);\n\tfor (const planned of optionalPlanned) {\n\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\tallocation,\n\t\t\tavailable,\n\t\t\tcache,\n\t\t\tdiagnostics,\n\t\t\tomitted,\n\t\t\tqualityPolicy,\n\t\t\tretrievalFallbacks,\n\t\t\tselection,\n\t\t\ttierUsed,\n\t\t\tusedTokens,\n\t\t});\n\t}\n\n\tconst omittedHighPriority = omitted.some((item) => item.priority === \"high\" || item.required === true);\n\temitOmissionDiagnostics(omitted, diagnostics, retrievalFallbacks);\n\temitCoverageDiagnostics(input.query, selection, diagnostics);\n\n\tconst tierAllocations = buildTierAllocations(allocation, demand, tierUsed);\n\tconst omittedTokens = omitted.reduce(\n\t\t(sum, item) => sum + (basePlanned.find((planned) => planned.item.id === item.id)?.fullTokens ?? 0),\n\t\t0,\n\t);\n\tconst omittedItemIds = omitted.map((item) => item.id);\n\tconst planHash = computePlanHash({\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanned: basePlanned,\n\t\tselection,\n\t\tallocations: tierAllocations,\n\t\tomittedItemIds,\n\t});\n\tconst observability = buildObservability({\n\t\tavailable,\n\t\tdiagnostics,\n\t\tomittedItemIds,\n\t\tomittedTokens,\n\t\tplanHash,\n\t\trawTokens,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\tusedTokens,\n\t\tcacheTelemetry: finalizeContextBudgetCacheTelemetryV2(cache.telemetry, {\n\t\t\tomittedTokens,\n\t\t\trawTokens,\n\t\t\tusedTokens,\n\t\t}),\n\t});\n\n\tconst plan: PromptContextBudgetPlanV2 = {\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanHash,\n\t\tmaxTokens,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\tavailableTokens: available,\n\t\tusedTokens,\n\t\tomittedTokens,\n\t\trawTokens,\n\t\ttokenSavingsRatio: rawTokens > 0 ? Math.max(0, 1 - usedTokens / rawTokens) : 0,\n\t\temergency: usedTokens > available || diagnostics.some((diagnostic) => diagnostic.reason === \"invalid_budget\"),\n\t\tomittedHighPriority,\n\t\ttierAllocations,\n\t\tselectedRepresentations: [...selection.values()].sort((a, b) => a.itemId.localeCompare(b.itemId)),\n\t\tincludedItemIds: basePlanned\n\t\t\t.map((planned) => planned.item.id)\n\t\t\t.filter((id) => selection.has(id))\n\t\t\t.sort(),\n\t\tomittedItemIds,\n\t\tdiagnostics,\n\t\tretrievalFallbacks,\n\t\tobservability,\n\t};\n\tif (canUsePlanCache) {\n\t\twritePlanCacheV2({\n\t\t\tcache,\n\t\t\tkey: planCacheKey,\n\t\t\tplan,\n\t\t\tplanned: basePlanned,\n\t\t});\n\t}\n\treturn plan;\n}\n\nfunction normalizeBudgetTokens(field: string, value: number, diagnostics: QualityDiagnosticV2[]): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tdiagnostics.push({\n\t\t\treason: \"invalid_budget\",\n\t\t\tdetail: `${field} must be a non-negative finite number, got ${value}`,\n\t\t});\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n\nfunction buildObservability(input: {\n\treadonly available: number;\n\treadonly diagnostics: readonly QualityDiagnosticV2[];\n\treadonly omittedItemIds: readonly string[];\n\treadonly omittedTokens: number;\n\treadonly planHash: string;\n\treadonly rawTokens: number;\n\treadonly retrievalFallbacks: readonly ContextSourceRefV2[];\n\treadonly selection: ReadonlyMap<string, SelectedRepresentationV2>;\n\treadonly usedTokens: number;\n\treadonly cacheTelemetry: PromptContextBudgetObservabilityV2[\"cache\"];\n}): PromptContextBudgetObservabilityV2 {\n\tconst selected = [...input.selection.values()];\n\treturn {\n\t\tcounts: {\n\t\t\tselected: selected.length,\n\t\t\tomitted: input.omittedItemIds.length,\n\t\t\tpointer: selected.filter((representation) => representation.kind === \"pointer\").length,\n\t\t\tcompressed: selected.filter((representation) => representation.kind === \"headroom-compressed\").length,\n\t\t\tfull: selected.filter((representation) => representation.kind === \"full\").length,\n\t\t\tretrievalFallback: countUniqueRetrievalFallbacks(input.retrievalFallbacks),\n\t\t},\n\t\tdiagnosticReasons: [...new Set(input.diagnostics.map((diagnostic) => diagnostic.reason))].sort(),\n\t\ttokens: {\n\t\t\tavailable: input.available,\n\t\t\tused: input.usedTokens,\n\t\t\traw: input.rawTokens,\n\t\t\tomitted: input.omittedTokens,\n\t\t\ttokenSavings: Math.max(0, input.rawTokens - input.usedTokens),\n\t\t},\n\t\tplanHash: input.planHash,\n\t\tcache: input.cacheTelemetry,\n\t\ttokenOptimizer: TOKEN_OPTIMIZER_RUNTIME_STATUS,\n\t};\n}\n\nfunction countUniqueRetrievalFallbacks(retrievalFallbacks: readonly ContextSourceRefV2[]): number {\n\treturn new Set(\n\t\tretrievalFallbacks.map((ref) =>\n\t\t\t[ref.uri, ref.contentHash, ref.symbol ?? \"\", ref.range?.startLine ?? \"\", ref.range?.endLine ?? \"\"].join(\"\\0\"),\n\t\t),\n\t).size;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-v2-planner.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-planner.ts"],"names":[],"mappings":"AA8BA,OAAO,EAIN,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAI9B,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,0BAA0B,GAAG,yBAAyB,CAmOtG","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n} from \"./context-budget-headroom.ts\";\nimport {\n\tbuildContextBudgetPlanCacheKeyV2,\n\tcreateContextBudgetCacheKeyBaseV2,\n\tcreateContextBudgetSelectionCacheV2,\n\tfinalizeContextBudgetCacheTelemetryV2,\n\treadValidPlanCacheV2,\n\twritePlanCacheV2,\n} from \"./context-budget-v2-cache.ts\";\nimport { validateBudgetItems } from \"./context-budget-v2-input-validation.ts\";\nimport { buildObservability } from \"./context-budget-v2-observability.ts\";\nimport { computePlanHash } from \"./context-budget-v2-plan-hash.ts\";\nimport { createPlannedItems } from \"./context-budget-v2-planned-items.ts\";\nimport { compareOptionalForSelection } from \"./context-budget-v2-scoring.ts\";\nimport {\n\tapplyPlannerRedundancyPenalties,\n\tbuildTierAllocations,\n\tcreateTierUsage,\n\temitCoverageDiagnostics,\n\temitOmissionDiagnostics,\n\tselectHardItem,\n\tselectOptionalItem,\n} from \"./context-budget-v2-selection.ts\";\nimport { allocateTiers, computeTierDemand } from \"./context-budget-v2-tiers.ts\";\nimport {\n\tALL_TIERS_V2,\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\tDEFAULT_TIER_POLICY_V2,\n\ttype PromptContextBudgetInputV2,\n\ttype PromptContextBudgetPlanV2,\n\ttype QualityDiagnosticV2,\n\ttype SelectedRepresentationV2,\n\ttype TierBudgetPolicyV2,\n} from \"./context-budget-v2-types.ts\";\n\nexport function planPromptContextBudgetV2(input: PromptContextBudgetInputV2): PromptContextBudgetPlanV2 {\n\tconst modelId = input.modelId ?? \"unknown\";\n\tconst qualityPolicy = input.qualityPolicy ?? DEFAULT_HEADROOM_QUALITY_POLICY;\n\tconst policyVersion = input.policyVersion ?? CONTEXT_BUDGET_POLICY_VERSION_V2;\n\tconst diagnostics: QualityDiagnosticV2[] = [];\n\n\tconst maxTokens = normalizeBudgetTokens(\"maxTokens\", input.maxTokens, diagnostics);\n\tconst responseReserve = normalizeBudgetTokens(\n\t\t\"responseReserveTokens\",\n\t\tinput.responseReserveTokens ?? 0,\n\t\tdiagnostics,\n\t);\n\tconst safetyMargin = normalizeBudgetTokens(\n\t\t\"safetyMarginTokens\",\n\t\tinput.safetyMarginTokens ?? Math.ceil(maxTokens * 0.05),\n\t\tdiagnostics,\n\t);\n\tconst available = Math.max(0, maxTokens - responseReserve - safetyMargin);\n\n\tconst tierPolicy: Readonly<Record<ContextBudgetTierV2, TierBudgetPolicyV2>> = {\n\t\t...DEFAULT_TIER_POLICY_V2,\n\t\t...(input.tierPolicy ?? {}),\n\t};\n\tconst basePlanned = createPlannedItems(\n\t\tvalidateBudgetItems(input.items, diagnostics),\n\t\tinput.tokenCounter,\n\t\tmodelId,\n\t\tqualityPolicy,\n\t);\n\t// Sanitized items can share a key with valid input, but their diagnostics cannot.\n\tconst canUsePlanCache = diagnostics.length === 0;\n\tapplyPlannerRedundancyPenalties(basePlanned);\n\tconst rawTokens = basePlanned.reduce((sum, planned) => sum + planned.fullTokens, 0);\n\n\tconst demand = computeTierDemand(basePlanned);\n\tconst allocation = allocateTiers(available, tierPolicy, demand);\n\tconst cacheKeyBase = createContextBudgetCacheKeyBaseV2({\n\t\tbudgetBucket: input.cacheBudgetBucket ?? String(available),\n\t\tcacheInvalidationSnapshot: input.cacheInvalidationSnapshot ?? input.cacheProvider?.getInvalidationSnapshot?.(),\n\t\tmodelId,\n\t\tnamespace: input.cacheNamespace,\n\t\tpolicyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion,\n\t\tquery: input.query,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\ttokenizerId: input.tokenizerId,\n\t});\n\tconst planCacheKey = buildContextBudgetPlanCacheKeyV2({\n\t\tavailableTokens: available,\n\t\tkeyBase: cacheKeyBase,\n\t\tmaxTokens,\n\t\tplanned: basePlanned,\n\t\tpromptHash: input.promptHash,\n\t\tqualityPolicy,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\ttierPolicy,\n\t});\n\tconst cache = createContextBudgetSelectionCacheV2({\n\t\tkeyBase: cacheKeyBase,\n\t\tnowEpochMs: input.cacheNowEpochMs,\n\t\tplanCacheKey,\n\t\tprovider: input.cacheProvider,\n\t\tttlMs: input.cacheTtlMs,\n\t});\n\tconst cachedPlan = canUsePlanCache\n\t\t? readValidPlanCacheV2({\n\t\t\t\tavailableTokens: available,\n\t\t\t\tcache,\n\t\t\t\tkey: planCacheKey,\n\t\t\t\tplanned: basePlanned,\n\t\t\t})\n\t\t: undefined;\n\tif (cachedPlan) {\n\t\treturn cachedPlan;\n\t}\n\tconst selection = new Map<string, SelectedRepresentationV2>();\n\tconst tierUsed = createTierUsage();\n\tconst omitted: ContextBudgetItemV2[] = [];\n\tconst retrievalFallbacks: ContextSourceRefV2[] = [];\n\tlet usedTokens = 0;\n\n\tfor (const planned of basePlanned) {\n\t\tif (!planned.isHard) continue;\n\t\tusedTokens += selectHardItem(planned, selection, tierUsed);\n\t}\n\tif (usedTokens > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"hard_pin_over_capacity\",\n\t\t\tdetail: `hard-pinned context uses ${usedTokens} tokens against ${available} available tokens`,\n\t\t});\n\t}\n\n\tconst optionalPlanned = basePlanned.filter((planned) => !planned.isHard).sort(compareOptionalForSelection);\n\n\t// Floor-reservation pass: each tier spends its guaranteed claim (the\n\t// `allocated` field — hard demand, else demand capped at the floor) on its\n\t// own optional items before anything competes globally. A tier's floor is\n\t// therefore a real reservation, not a lower bound on its ceiling. Items\n\t// that cannot fit inside the remaining floor stay unselected for the\n\t// global pass; when the summed floors exceed the budget the reservation is\n\t// best-effort in tier order and the plan reports it explicitly.\n\t// One cache-resolution per item, shared by both passes so an item that is\n\t// deferred out of the floor and reconsidered globally does not double\n\t// count provider reads or redo materialization work.\n\tconst resolvedCandidates = new Map<string, readonly ContextRepresentationCandidateV2[]>();\n\tlet totalFloor = 0;\n\tfor (const tier of ALL_TIERS_V2) {\n\t\ttotalFloor += allocation.get(tier)?.floor ?? 0;\n\t}\n\tif (totalFloor > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"tier_floor_over_budget\",\n\t\t\tdetail: `tier floor guarantees total ${totalFloor} tokens against ${available} available; floors degrade to tier-order best effort`,\n\t\t});\n\t}\n\tfor (const tier of ALL_TIERS_V2) {\n\t\tconst floor = allocation.get(tier)?.floor ?? 0;\n\t\tif (floor <= 0 || tierUsed[tier] >= floor) continue;\n\t\tfor (const planned of optionalPlanned) {\n\t\t\tif (planned.item.tier !== tier || selection.has(planned.item.id)) continue;\n\t\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\t\tallocation,\n\t\t\t\tavailable,\n\t\t\t\tcache,\n\t\t\t\tdiagnostics,\n\t\t\t\tomitted,\n\t\t\t\tqualityPolicy,\n\t\t\t\tretrievalFallbacks,\n\t\t\t\tselection,\n\t\t\t\ttierUsed,\n\t\t\t\tusedTokens,\n\t\t\t\tfloorReservation: { tier, floorTokens: floor },\n\t\t\t\tresolvedCandidates,\n\t\t\t});\n\t\t\tif (tierUsed[tier] >= floor || usedTokens >= available) break;\n\t\t}\n\t}\n\n\tfor (const planned of optionalPlanned) {\n\t\tif (selection.has(planned.item.id)) continue;\n\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\tallocation,\n\t\t\tavailable,\n\t\t\tcache,\n\t\t\tdiagnostics,\n\t\t\tomitted,\n\t\t\tqualityPolicy,\n\t\t\tresolvedCandidates,\n\t\t\tretrievalFallbacks,\n\t\t\tselection,\n\t\t\ttierUsed,\n\t\t\tusedTokens,\n\t\t});\n\t}\n\n\tconst omittedHighPriority = omitted.some((item) => item.priority === \"high\" || item.required === true);\n\temitOmissionDiagnostics(omitted, diagnostics, retrievalFallbacks);\n\temitCoverageDiagnostics(input.query, selection, diagnostics);\n\n\tconst tierAllocations = buildTierAllocations(allocation, demand, tierUsed);\n\tconst omittedTokens = omitted.reduce(\n\t\t(sum, item) => sum + (basePlanned.find((planned) => planned.item.id === item.id)?.fullTokens ?? 0),\n\t\t0,\n\t);\n\tconst omittedItemIds = omitted.map((item) => item.id);\n\tconst planHash = computePlanHash({\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanned: basePlanned,\n\t\tselection,\n\t\tallocations: tierAllocations,\n\t\tomittedItemIds,\n\t});\n\tconst observability = buildObservability({\n\t\tavailable,\n\t\tdiagnostics,\n\t\tomittedItemIds,\n\t\tomittedTokens,\n\t\tplanHash,\n\t\trawTokens,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\tusedTokens,\n\t\tcacheTelemetry: finalizeContextBudgetCacheTelemetryV2(cache.telemetry, {\n\t\t\tomittedTokens,\n\t\t\trawTokens,\n\t\t\tusedTokens,\n\t\t}),\n\t});\n\n\tconst plan: PromptContextBudgetPlanV2 = {\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanHash,\n\t\tmaxTokens,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\tavailableTokens: available,\n\t\tusedTokens,\n\t\tomittedTokens,\n\t\trawTokens,\n\t\ttokenSavingsRatio: rawTokens > 0 ? Math.max(0, 1 - usedTokens / rawTokens) : 0,\n\t\temergency: usedTokens > available || diagnostics.some((diagnostic) => diagnostic.reason === \"invalid_budget\"),\n\t\tomittedHighPriority,\n\t\ttierAllocations,\n\t\tselectedRepresentations: [...selection.values()].sort((a, b) => a.itemId.localeCompare(b.itemId)),\n\t\tincludedItemIds: basePlanned\n\t\t\t.map((planned) => planned.item.id)\n\t\t\t.filter((id) => selection.has(id))\n\t\t\t.sort(),\n\t\tomittedItemIds,\n\t\tdiagnostics,\n\t\tretrievalFallbacks,\n\t\tobservability,\n\t};\n\tif (canUsePlanCache) {\n\t\twritePlanCacheV2({\n\t\t\tcache,\n\t\t\tkey: planCacheKey,\n\t\t\tplan,\n\t\t\tplanned: basePlanned,\n\t\t});\n\t}\n\treturn plan;\n}\n\nfunction normalizeBudgetTokens(field: string, value: number, diagnostics: QualityDiagnosticV2[]): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tdiagnostics.push({\n\t\t\treason: \"invalid_budget\",\n\t\t\tdetail: `${field} must be a non-negative finite number, got ${value}`,\n\t\t});\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
|