@cassiomc1/forgeloop 1.12.0 → 1.14.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/.github/copilot-instructions.md +1 -1
- package/AGENTS.md +1 -1
- package/AGENT_COMPATIBILITY.md +8 -0
- package/CLAUDE.md +1 -1
- package/CONTRIBUTING.md +90 -0
- package/DOCS_INDEX.md +46 -12
- package/ENG/c-development-eng.md +112 -0
- package/ENG/cpp-development-eng.md +109 -0
- package/ENG/dotnet-aspnetcore-development-eng.md +401 -0
- package/ENG/go-development-eng.md +103 -0
- package/ENG/java-development-eng.md +125 -0
- package/ENG/nodejs-backend-development-eng.md +605 -0
- package/ENG/php-development-eng.md +104 -0
- package/ENG/rust-development-eng.md +422 -0
- package/ENG/sec-code-eng.md +7 -7
- package/ENG/sql-development-eng.md +108 -0
- package/ENG/swift-development-eng.md +111 -0
- package/ENG/typescript-development-eng.md +108 -0
- package/EXECUTION_STATE.md +12 -0
- package/GUIDE_ROUTER.md +418 -9
- package/LOOP_ENGINEERING.md +28 -2
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/QUALITY_SCORECARD.md +1 -0
- package/README.md +78 -52
- package/TERMINOLOGY.md +2 -0
- package/THIRD_PARTY_NOTICES.md +19 -7
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +22 -4
- package/completions/forgeloop.bash +40 -4
- package/completions/forgeloop.fish +130 -1
- package/docs/ADVISORY_CONTEXT.md +25 -0
- package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
- package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +81 -3
- package/docs/AGENT_SKILL.md +66 -0
- package/docs/ARTIFACT_REFERENCE.md +123 -0
- package/docs/AUDIT_UX.md +46 -0
- package/docs/BROWSER_VERIFICATION.md +136 -0
- package/docs/CLI_REFERENCE.md +392 -10
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +34 -12
- package/docs/GETTING_STARTED.md +59 -0
- package/docs/JEV_BENCHMARKS.md +31 -0
- package/docs/MODEL_ROUTING.md +37 -0
- package/docs/OPENSRC_ADAPTER.md +241 -0
- package/docs/PACKAGE_CONTENTS.md +60 -19
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +32 -0
- package/docs/RELEASE_CHECKLIST.md +66 -5
- package/docs/SECURITY_REVIEW.md +71 -0
- package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
- package/docs/TEST_INTELLIGENCE.md +29 -0
- package/docs/TEST_PRUNING.md +14 -0
- package/docs/TROUBLESHOOTING.md +298 -3
- package/docs/UNIVERSAL_INTEGRATION.md +31 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
- package/docs/diagrams/README.md +13 -9
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
- package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
- package/docs/documentation-manifest.json +1397 -0
- package/docs/protocol-requirements.json +101 -0
- package/package.json +46 -4
- package/schemas/config.schema.json +14 -0
- package/schemas/context-plan.schema.json +18 -0
- package/schemas/routing-input.schema.json +1 -1
- package/schemas/semantic-decision.schema.json +46 -0
- package/schemas/test-utility.schema.json +44 -0
- package/scripts/CI_VALIDATORS.md +84 -11
- package/scripts/benchmark-jev.mjs +5 -0
- package/scripts/benchmark-test-intelligence.mjs +4 -0
- package/scripts/generate-agent-protocol-summary.mjs +40 -1
- package/scripts/generate-forgeloop-skill.mjs +133 -0
- package/scripts/jev-smoke.mjs +19 -0
- package/skills/forgeloop/README.md +9 -0
- package/skills/forgeloop/SKILL.md +77 -0
- package/skills/forgeloop/references/lifecycle.md +9 -0
- package/skills/forgeloop/references/recovery.md +7 -0
- package/skills/forgeloop/references/verification.md +7 -0
- package/src/adapters/agent-browser/assertions.js +47 -0
- package/src/adapters/agent-browser/commands.js +54 -0
- package/src/adapters/agent-browser/index.js +3 -0
- package/src/adapters/agent-browser/locator.js +40 -0
- package/src/adapters/agent-browser/process.js +215 -0
- package/src/adapters/agent-browser/provider.js +313 -0
- package/src/adapters/emulated-services/constants.js +24 -0
- package/src/adapters/emulated-services/index.js +7 -0
- package/src/adapters/emulated-services/process.js +162 -0
- package/src/adapters/emulated-services/provider.js +282 -0
- package/src/adapters/opensrc/normalize.js +90 -0
- package/src/adapters/opensrc/process.js +248 -0
- package/src/adapters/opensrc/provider.js +338 -0
- package/src/adapters/opensrc/search.js +264 -0
- package/src/adapters/typesafe/client.js +28 -0
- package/src/adapters/typesafe/engine.js +63 -0
- package/src/adapters/typesafe/normalize.js +41 -0
- package/src/cli.js +108 -0
- package/src/commands/checkpoint-revalidate.js +176 -0
- package/src/commands/context-plan.js +38 -0
- package/src/commands/contract-create.js +264 -0
- package/src/commands/contract-revise.js +236 -0
- package/src/commands/decision-show.js +14 -0
- package/src/commands/decision-status.js +22 -0
- package/src/commands/discover.js +41 -0
- package/src/commands/doctor.js +15 -0
- package/src/commands/gate-record.js +205 -0
- package/src/commands/gate-revalidate.js +137 -0
- package/src/commands/model-route.js +32 -0
- package/src/commands/next.js +19 -7
- package/src/commands/route.js +146 -18
- package/src/commands/semantic-plan.js +17 -0
- package/src/commands/task-abandon.js +224 -0
- package/src/commands/task-create.js +84 -25
- package/src/commands/task-list.js +22 -2
- package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
- package/src/commands/task-repair-contract-bootstrap.js +263 -0
- package/src/commands/test-inventory.js +5 -0
- package/src/commands/test-prune-plan.js +5 -0
- package/src/commands/test-prune-probe.js +5 -0
- package/src/commands/test-utility.js +5 -0
- package/src/commands/validate-protocol.js +10 -1
- package/src/config/guides.json +44 -0
- package/src/core/artifact-registry.js +24 -0
- package/src/core/audit-ux.js +514 -0
- package/src/core/browser-verification/constants.js +149 -0
- package/src/core/browser-verification/normalize.js +254 -0
- package/src/core/browser-verification/provider.js +519 -0
- package/src/core/browser-verification/service.js +115 -0
- package/src/core/build-script.js +151 -0
- package/src/core/c-cpp-project.js +143 -0
- package/src/core/checkpoint-revalidation.js +319 -0
- package/src/core/cli-command-definitions.js +249 -1
- package/src/core/command-executors.js +115 -3
- package/src/core/command-input.js +212 -102
- package/src/core/completion-artifacts.js +14 -5
- package/src/core/completion.js +4 -6
- package/src/core/config.js +3 -0
- package/src/core/context-compiler/budget.js +9 -0
- package/src/core/context-compiler/candidates.js +39 -0
- package/src/core/context-compiler/compiler.js +63 -0
- package/src/core/context-compiler/fingerprint.js +11 -0
- package/src/core/context-compiler/policy.js +13 -0
- package/src/core/context-compiler/result.js +23 -0
- package/src/core/contract-bootstrap-recovery.js +655 -0
- package/src/core/contract-presets.js +82 -0
- package/src/core/contract-revision.js +210 -0
- package/src/core/decision/artifact.js +69 -0
- package/src/core/decision/benchmarks.js +103 -0
- package/src/core/decision/cache.js +27 -0
- package/src/core/decision/constants.js +58 -0
- package/src/core/decision/cutover.js +34 -0
- package/src/core/decision/engine.js +22 -0
- package/src/core/decision/errors.js +68 -0
- package/src/core/decision/events.js +101 -0
- package/src/core/decision/freshness.js +19 -0
- package/src/core/decision/normalizers/index.js +115 -0
- package/src/core/decision/policy.js +18 -0
- package/src/core/decision/projection.js +16 -0
- package/src/core/decision/question-registry.js +201 -0
- package/src/core/decision/request.js +26 -0
- package/src/core/decision/resolver.js +130 -0
- package/src/core/decision/result.js +58 -0
- package/src/core/decision/service.js +156 -0
- package/src/core/decision/state-builder.js +65 -0
- package/src/core/decision/task-bindings.js +30 -0
- package/src/core/decision/test-provider.js +32 -0
- package/src/core/decision/thresholds.js +15 -0
- package/src/core/error-codes.js +281 -3
- package/src/core/events.js +226 -57
- package/src/core/evidence-readiness.js +9 -0
- package/src/core/execution-prerequisites.js +14 -0
- package/src/core/execution-profile.js +63 -38
- package/src/core/filesystem.js +1 -10
- package/src/core/gate-provenance.js +124 -0
- package/src/core/go-project.js +206 -0
- package/src/core/integration-invocation-policy.js +27 -4
- package/src/core/integration-resources.js +86 -61
- package/src/core/java-project.js +403 -0
- package/src/core/model-router/constants.js +10 -0
- package/src/core/model-router/policy.js +103 -0
- package/src/core/model-router/router.js +37 -0
- package/src/core/multi-language-project.js +117 -0
- package/src/core/next-action-model.js +58 -0
- package/src/core/next-action-phases.js +130 -42
- package/src/core/next-action-refresh.js +43 -9
- package/src/core/next-action-review-phase.js +7 -2
- package/src/core/next-action.js +35 -7
- package/src/core/next-explanation.js +63 -0
- package/src/core/phase.js +128 -10
- package/src/core/php-project.js +85 -0
- package/src/core/preflight-consistency.js +23 -9
- package/src/core/preflight-loaders.js +37 -5
- package/src/core/project-detection.js +1760 -52
- package/src/core/protocol-info.js +65 -0
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +132 -53
- package/src/core/recovery-history.js +1 -0
- package/src/core/resumability.js +154 -44
- package/src/core/route-artifact.js +15 -1
- package/src/core/router.js +223 -4
- package/src/core/runtime-context.js +118 -61
- package/src/core/rust-project.js +400 -0
- package/src/core/schema-validation.js +3 -0
- package/src/core/security-review/constants.js +64 -0
- package/src/core/security-review/normalize.js +245 -0
- package/src/core/security-review/provider.js +204 -0
- package/src/core/security-review/service.js +134 -0
- package/src/core/semantic-planning/constants.js +19 -0
- package/src/core/semantic-planning/projection.js +94 -0
- package/src/core/semantic-planning/service.js +15 -0
- package/src/core/sources.js +37 -0
- package/src/core/sql-project.js +141 -0
- package/src/core/swift-project.js +200 -0
- package/src/core/task-claim-state.js +201 -1
- package/src/core/task-conflict-inspection.js +31 -5
- package/src/core/task-paths.js +13 -0
- package/src/core/task-recovery.js +1 -0
- package/src/core/templates.js +3 -0
- package/src/core/test-intelligence/benchmarks.js +68 -0
- package/src/core/test-intelligence/inventory.js +73 -0
- package/src/core/test-intelligence/prune.js +90 -0
- package/src/core/test-intelligence/semantic-state.js +15 -0
- package/src/core/test-intelligence/service.js +40 -0
- package/src/core/test-intelligence/utility.js +50 -0
- package/src/core/trace.js +11 -7
- package/src/core/transaction.js +1 -0
- package/src/core/typescript-project.js +349 -0
- package/src/core/xml-structure.js +123 -0
- package/src/integration.d.ts +492 -0
- package/src/integration.js +54 -0
- package/src/providers/README.md +47 -0
- package/src/providers/capabilities.js +46 -0
- package/src/providers/errors.js +15 -0
- package/src/providers/index.js +29 -0
- package/src/providers/json-snapshot.js +105 -0
- package/src/providers/registry.js +152 -0
|
@@ -14,6 +14,195 @@ function validateSearchCommandInput(command, options, help) {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function validatePolicyInput(command, options) {
|
|
18
|
+
if (command === "policy" && !options.policy) throw inputError("policy requires a name");
|
|
19
|
+
if (command !== "policy" && options.policy) throw inputError(`Policy name is not valid for ${command}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function validateContractBootstrapRepairMigrationInput(command, options) {
|
|
23
|
+
if (command !== "task-migrate-contract-bootstrap-repair") return;
|
|
24
|
+
if (!options.taskId) throw inputError("task-migrate-contract-bootstrap-repair requires --task");
|
|
25
|
+
if (!options.acknowledgeMigration) throw inputError("task-migrate-contract-bootstrap-repair requires --acknowledge-migration");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function validateTaskCreationInput(command, options, help = false) {
|
|
29
|
+
if (help) return;
|
|
30
|
+
if (command === "bundle" && !options.taskId) throw inputError("bundle requires --task");
|
|
31
|
+
if (command === "task-create" && !options.taskId) throw inputError("task-create requires --task");
|
|
32
|
+
if (!["task-create", "contract-create", "contract-revise"].includes(command) && (options.preset || options.preview)) {
|
|
33
|
+
throw inputError(`preset/preview options are not valid for ${command}`);
|
|
34
|
+
}
|
|
35
|
+
if (command === "task-create" && options.preview && !options.preset && !options.contractFile) {
|
|
36
|
+
throw inputError("task-create --preview requires --preset or --contract-file");
|
|
37
|
+
}
|
|
38
|
+
if (command === "task-create" && options.preset && options.contractFile) {
|
|
39
|
+
throw inputError("task-create accepts either --preset or --contract-file, not both");
|
|
40
|
+
}
|
|
41
|
+
if (command === "contract-create" && !options.taskId) throw inputError("contract-create requires --task");
|
|
42
|
+
if (command === "contract-create" && !options.preset && !options.contractFile) throw inputError("contract-create requires --preset or --contract-file");
|
|
43
|
+
if (command === "contract-create" && options.preset && options.contractFile) throw inputError("contract-create accepts either --preset or --contract-file, not both");
|
|
44
|
+
if (command === "contract-revise" && !options.taskId) throw inputError("contract-revise requires --task");
|
|
45
|
+
if (command === "contract-revise" && !options.preset && !options.contractFile) throw inputError("contract-revise requires --preset or --contract-file");
|
|
46
|
+
if (command === "contract-revise" && options.preset && options.contractFile) throw inputError("contract-revise accepts either --preset or --contract-file, not both");
|
|
47
|
+
if (command === "task-repair-contract-bootstrap" && !options.taskId) throw inputError("task-repair-contract-bootstrap requires --task");
|
|
48
|
+
if (command === "task-repair-contract-bootstrap" && !options.acknowledgeRepair) throw inputError("task-repair-contract-bootstrap requires --acknowledge-repair");
|
|
49
|
+
validateContractBootstrapRepairMigrationInput(command, options);
|
|
50
|
+
const hasTaskListFilter = Boolean(
|
|
51
|
+
options.phase
|
|
52
|
+
|| options.active
|
|
53
|
+
|| (options.limit !== undefined && options.limit !== null)
|
|
54
|
+
|| (options.offset !== undefined && options.offset !== null && options.offset !== 0),
|
|
55
|
+
);
|
|
56
|
+
if (command !== "task-list" && hasTaskListFilter) {
|
|
57
|
+
throw inputError(`task-list filters are not valid for ${command}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validateGateRecordInput(command, options, help) {
|
|
62
|
+
if (command !== "gate-record" || help) return;
|
|
63
|
+
if (!options.taskId) throw inputError("gate-record requires --task");
|
|
64
|
+
if (!options.gate) throw inputError("gate-record requires --gate");
|
|
65
|
+
if (!["satisfied", "unverified", "blocked"].includes(options.gateStatus)) {
|
|
66
|
+
throw inputError("gate-record --status must be satisfied, unverified, or blocked");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateExecutionProfileInput(command, options) {
|
|
71
|
+
if (options.executionProfile !== null && options.executionProfile !== undefined) {
|
|
72
|
+
if (command !== "route") throw inputError(`executionProfile is not valid for ${command}`);
|
|
73
|
+
if (!EXECUTION_PROFILE_REQUESTS.includes(options.executionProfile)) {
|
|
74
|
+
throw inputError(`route --execution-profile must be one of ${EXECUTION_PROFILE_REQUESTS.join(", ")}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validateUsageInput(command, options, help) {
|
|
80
|
+
if (command === "usage-record" && !help) {
|
|
81
|
+
if (!options.taskId) throw inputError("usage-record requires --task");
|
|
82
|
+
if ((options.usageSource ?? "ACTOR_REPORTED") !== "ACTOR_REPORTED") {
|
|
83
|
+
throw inputError("usage-record accepts only --source ACTOR_REPORTED");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (command === "efficiency" && !help && !options.taskId) {
|
|
87
|
+
throw inputError("efficiency requires --task");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function validateQualityInput(command, options, help) {
|
|
92
|
+
if (["quality-baseline", "quality-verify", "quality-status"].includes(command) && !help && !options.taskId) {
|
|
93
|
+
throw inputError(`${command} requires --task`);
|
|
94
|
+
}
|
|
95
|
+
if (["quality-baseline", "quality-verify"].includes(command) && !help
|
|
96
|
+
&& options.timeoutMs !== null && options.timeoutMs !== undefined
|
|
97
|
+
&& (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 0 || options.timeoutMs > 300000)) {
|
|
98
|
+
throw inputError(`${command} --timeout-ms must be between 0 and 300000`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function validateProfileAndUsageInput(command, options, help) {
|
|
103
|
+
validateExecutionProfileInput(command, options);
|
|
104
|
+
validateUsageInput(command, options, help);
|
|
105
|
+
validateQualityInput(command, options, help);
|
|
106
|
+
if (command !== "quality-baseline" && options.replace === true) throw inputError("--replace is only valid for quality-baseline");
|
|
107
|
+
if (command !== "usage-record" && options.usageSource !== undefined && options.usageSource !== "ACTOR_REPORTED") {
|
|
108
|
+
throw inputError(`usageSource is not valid for ${command}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function validateOutputInput(command, options) {
|
|
113
|
+
if (options.compact === true && !["next", "task-show"].includes(command)) {
|
|
114
|
+
throw inputError(`compact output is not valid for ${command}`);
|
|
115
|
+
}
|
|
116
|
+
if (command !== "next" && options.explain === true) throw inputError(`explain output is not valid for ${command}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const TASK_SELECTOR_COMMANDS = new Set([
|
|
120
|
+
"context-plan",
|
|
121
|
+
"model-route",
|
|
122
|
+
"semantic-plan",
|
|
123
|
+
"test-utility",
|
|
124
|
+
"checkpoint-revalidate",
|
|
125
|
+
"workspace-bind",
|
|
126
|
+
"workspace-status",
|
|
127
|
+
"handoff-create",
|
|
128
|
+
"handoff-list",
|
|
129
|
+
"handoff-show",
|
|
130
|
+
"handoff-accept",
|
|
131
|
+
"responsibility-set",
|
|
132
|
+
"responsibility-status",
|
|
133
|
+
"verify-scope",
|
|
134
|
+
"attestation-create",
|
|
135
|
+
"attestation-status",
|
|
136
|
+
"attestation-verify",
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
function validateRequiredTaskSelector(command, options, help) {
|
|
140
|
+
if (TASK_SELECTOR_COMMANDS.has(command) && !help && !options.taskId) {
|
|
141
|
+
throw inputError(`${command} requires --task`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validateHandoffShowInput(command, options, help) {
|
|
146
|
+
if (command === "handoff-show" && !help && !options.handoffId) throw inputError("handoff-show requires --id");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function validateHandoffAcceptInput(command, options, help) {
|
|
150
|
+
if (command !== "handoff-accept" || help) return;
|
|
151
|
+
if (!options.handoffId) throw inputError("handoff-accept requires --handoff");
|
|
152
|
+
if (!options.consumerId) throw inputError("handoff-accept requires --consumer-id");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function validateResponsibilityInput(command, options, help) {
|
|
156
|
+
if (command === "responsibility-set" && !help && !options.responsibilityLabel) {
|
|
157
|
+
throw inputError("responsibility-set requires --label");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function validateVerificationScopeInput(command, options, help) {
|
|
162
|
+
if (command !== "verify-scope" || help) return;
|
|
163
|
+
const mode = String(options.verificationScopeMode ?? "AUTO").toUpperCase();
|
|
164
|
+
if (!["AUTO", "CHANGED", "CLAIMED", "FULL"].includes(mode)) throw inputError("verify-scope --mode must be AUTO, CHANGED, CLAIMED, or FULL");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function validateAttestationRangeInput(command, options, help) {
|
|
168
|
+
if (command !== "attestation-verify-range" || help) return;
|
|
169
|
+
if (!options.baseRevision) throw inputError("attestation-verify-range requires --base");
|
|
170
|
+
if (!options.headRevision) throw inputError("attestation-verify-range requires --head");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function validateTaskSelectorsInput(command, options, help) {
|
|
174
|
+
validateRequiredTaskSelector(command, options, help);
|
|
175
|
+
validateHandoffShowInput(command, options, help);
|
|
176
|
+
validateHandoffAcceptInput(command, options, help);
|
|
177
|
+
validateResponsibilityInput(command, options, help);
|
|
178
|
+
validateVerificationScopeInput(command, options, help);
|
|
179
|
+
validateAttestationRangeInput(command, options, help);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function validateRecordCheckInput(options) {
|
|
183
|
+
if (!options.checkId) throw inputError("record-check requires --id");
|
|
184
|
+
if (!options.checkRequirement) throw inputError("record-check requires --requirement");
|
|
185
|
+
if (!options.checkStatus) throw inputError("record-check requires --status");
|
|
186
|
+
if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
|
|
187
|
+
if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function validateCommandCheckInput(options, command) {
|
|
191
|
+
if (!options.checkId) throw inputError(`${command} requires --id`);
|
|
192
|
+
if (!options.checkRequirement) throw inputError(`${command} requires --requirement`);
|
|
193
|
+
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
194
|
+
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
195
|
+
throw inputError(`${command} accepts only --id, --requirement, --details,${command === "run-check" ? " --timeout-ms, --scope-ref," : ""} and -- <argv>`);
|
|
196
|
+
}
|
|
197
|
+
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError(`${command} requires -- followed by an exact command argv`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function validateCheckInput(command, options, help) {
|
|
201
|
+
if (help) return;
|
|
202
|
+
if (command === "record-check") validateRecordCheckInput(options);
|
|
203
|
+
if (command === "run-check" || command === "reconcile-closure") validateCommandCheckInput(options, command);
|
|
204
|
+
}
|
|
205
|
+
|
|
17
206
|
/**
|
|
18
207
|
* Transport-neutral option defaults shared by the CLI parser and the
|
|
19
208
|
* programmatic command runtime so every executor observes the same
|
|
@@ -25,6 +214,7 @@ export function defaultCommandInputValues() {
|
|
|
25
214
|
dryRun: false,
|
|
26
215
|
json: false,
|
|
27
216
|
compact: false,
|
|
217
|
+
explain: false,
|
|
28
218
|
strict: false,
|
|
29
219
|
fix: false,
|
|
30
220
|
adopt: [],
|
|
@@ -44,6 +234,8 @@ export function defaultCommandInputValues() {
|
|
|
44
234
|
assetPath: null,
|
|
45
235
|
binaryPath: null,
|
|
46
236
|
force: false,
|
|
237
|
+
acknowledgeRepair: false,
|
|
238
|
+
acknowledgeMigration: false,
|
|
47
239
|
workType: null,
|
|
48
240
|
surfaces: [],
|
|
49
241
|
risks: [],
|
|
@@ -64,6 +256,19 @@ export function defaultCommandInputValues() {
|
|
|
64
256
|
to: null,
|
|
65
257
|
file: null,
|
|
66
258
|
contractFile: null,
|
|
259
|
+
preset: null,
|
|
260
|
+
gate: null,
|
|
261
|
+
gateStatus: null,
|
|
262
|
+
gateArtifacts: [],
|
|
263
|
+
gateDecisions: [],
|
|
264
|
+
gateUnknowns: [],
|
|
265
|
+
gateAssumptions: [],
|
|
266
|
+
gateEvidenceFile: null,
|
|
267
|
+
preview: false,
|
|
268
|
+
phase: null,
|
|
269
|
+
active: false,
|
|
270
|
+
limit: null,
|
|
271
|
+
offset: 0,
|
|
67
272
|
routeFile: null,
|
|
68
273
|
stateFile: null,
|
|
69
274
|
receiptFile: null,
|
|
@@ -128,110 +333,15 @@ export function validateForgeLoopCommandInput({ command, input, help = false } =
|
|
|
128
333
|
const options = input ?? {};
|
|
129
334
|
if (!command) return;
|
|
130
335
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (command !== "policy" && options.policy) {
|
|
135
|
-
throw inputError(`Policy name is not valid for ${command}`);
|
|
136
|
-
}
|
|
137
|
-
if (command === "bundle" && !options.taskId) {
|
|
138
|
-
throw inputError("bundle requires --task");
|
|
139
|
-
}
|
|
140
|
-
if (command === "task-create" && !options.taskId) {
|
|
141
|
-
throw inputError("task-create requires --task");
|
|
142
|
-
}
|
|
336
|
+
validatePolicyInput(command, options);
|
|
337
|
+
validateTaskCreationInput(command, options, help);
|
|
338
|
+
validateGateRecordInput(command, options, help);
|
|
143
339
|
validateSearchCommandInput(command, options, help);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
throw inputError(`route --execution-profile must be one of ${EXECUTION_PROFILE_REQUESTS.join(", ")}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (command === "usage-record" && !help) {
|
|
151
|
-
if (!options.taskId) throw inputError("usage-record requires --task");
|
|
152
|
-
if ((options.usageSource ?? "ACTOR_REPORTED") !== "ACTOR_REPORTED") {
|
|
153
|
-
throw inputError("usage-record accepts only --source ACTOR_REPORTED");
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (command === "efficiency" && !help && !options.taskId) {
|
|
157
|
-
throw inputError("efficiency requires --task");
|
|
158
|
-
}
|
|
159
|
-
if (["quality-baseline", "quality-verify", "quality-status"].includes(command) && !help && !options.taskId) {
|
|
160
|
-
throw inputError(`${command} requires --task`);
|
|
161
|
-
}
|
|
162
|
-
if (["quality-baseline", "quality-verify"].includes(command) && !help
|
|
163
|
-
&& options.timeoutMs !== null && options.timeoutMs !== undefined
|
|
164
|
-
&& (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 0 || options.timeoutMs > 300000)) {
|
|
165
|
-
throw inputError(`${command} --timeout-ms must be between 0 and 300000`);
|
|
166
|
-
}
|
|
167
|
-
if (command !== "quality-baseline" && options.replace === true) {
|
|
168
|
-
throw inputError(`--replace is only valid for quality-baseline`);
|
|
169
|
-
}
|
|
170
|
-
if (command !== "usage-record" && options.usageSource !== undefined && options.usageSource !== "ACTOR_REPORTED") {
|
|
171
|
-
throw inputError(`usageSource is not valid for ${command}`);
|
|
172
|
-
}
|
|
173
|
-
if (options.compact === true && !["next", "task-show"].includes(command)) {
|
|
174
|
-
throw inputError(`compact output is not valid for ${command}`);
|
|
175
|
-
}
|
|
176
|
-
if (["workspace-bind", "workspace-status", "handoff-create", "handoff-list", "handoff-show", "handoff-accept", "responsibility-set", "responsibility-status", "verify-scope", "attestation-create", "attestation-status", "attestation-verify"].includes(command)
|
|
177
|
-
&& !options.taskId) {
|
|
178
|
-
throw inputError(`${command} requires --task`);
|
|
179
|
-
}
|
|
180
|
-
if (command === "handoff-show" && !help && !options.handoffId) {
|
|
181
|
-
throw inputError("handoff-show requires --id");
|
|
182
|
-
}
|
|
183
|
-
if (command === "handoff-accept" && !help) {
|
|
184
|
-
if (!options.handoffId) {
|
|
185
|
-
throw inputError("handoff-accept requires --handoff");
|
|
186
|
-
}
|
|
187
|
-
if (!options.consumerId) {
|
|
188
|
-
throw inputError("handoff-accept requires --consumer-id");
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (command === "responsibility-set" && !help && !options.responsibilityLabel) {
|
|
192
|
-
throw inputError("responsibility-set requires --label");
|
|
193
|
-
}
|
|
194
|
-
if (command === "verify-scope" && !help) {
|
|
195
|
-
const mode = String(options.verificationScopeMode ?? "AUTO").toUpperCase();
|
|
196
|
-
if (!["AUTO", "CHANGED", "CLAIMED", "FULL"].includes(mode)) {
|
|
197
|
-
throw inputError("verify-scope --mode must be AUTO, CHANGED, CLAIMED, or FULL");
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (command === "attestation-verify-range" && !help) {
|
|
201
|
-
if (!options.baseRevision) throw inputError("attestation-verify-range requires --base");
|
|
202
|
-
if (!options.headRevision) throw inputError("attestation-verify-range requires --head");
|
|
203
|
-
}
|
|
340
|
+
validateProfileAndUsageInput(command, options, help);
|
|
341
|
+
validateOutputInput(command, options);
|
|
342
|
+
validateTaskSelectorsInput(command, options, help);
|
|
204
343
|
|
|
205
344
|
validateContinuityOptions(command, options);
|
|
206
345
|
|
|
207
|
-
|
|
208
|
-
if (!options.checkId) throw inputError("record-check requires --id");
|
|
209
|
-
if (!options.checkRequirement) throw inputError("record-check requires --requirement");
|
|
210
|
-
if (!options.checkStatus) throw inputError("record-check requires --status");
|
|
211
|
-
if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
|
|
212
|
-
if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
|
|
213
|
-
}
|
|
214
|
-
if (command === "run-check" && !help) {
|
|
215
|
-
if (!options.checkId) throw inputError("run-check requires --id");
|
|
216
|
-
if (!options.checkRequirement) throw inputError("run-check requires --requirement");
|
|
217
|
-
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
218
|
-
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
219
|
-
throw inputError("run-check accepts only --id, --requirement, --details, --timeout-ms, --scope-ref, and -- <argv>");
|
|
220
|
-
}
|
|
221
|
-
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) {
|
|
222
|
-
throw inputError("run-check requires -- followed by an exact command argv");
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (command === "reconcile-closure" && !help) {
|
|
226
|
-
if (!options.taskId) throw inputError("reconcile-closure requires --task");
|
|
227
|
-
if (!options.checkId) throw inputError("reconcile-closure requires --id");
|
|
228
|
-
if (!options.checkRequirement) throw inputError("reconcile-closure requires --requirement");
|
|
229
|
-
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
230
|
-
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
231
|
-
throw inputError("reconcile-closure accepts only --id, --requirement, --details, and -- <argv>");
|
|
232
|
-
}
|
|
233
|
-
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) {
|
|
234
|
-
throw inputError("reconcile-closure requires -- followed by an exact command argv");
|
|
235
|
-
}
|
|
236
|
-
}
|
|
346
|
+
validateCheckInput(command, options, help);
|
|
237
347
|
}
|
|
@@ -18,7 +18,7 @@ import { readPersistedRoute } from "./route-artifact.js";
|
|
|
18
18
|
import { createReceipt, validateReceipt } from "./receipt.js";
|
|
19
19
|
import { readWorkState, mutateWorkState } from "./work-state.js";
|
|
20
20
|
import { assertExecutionPrerequisites, hasExecutionStarted } from "./execution-prerequisites.js";
|
|
21
|
-
import { normalizeRequirements } from "./evidence-readiness.js";
|
|
21
|
+
import { normalizeRequirements, terminalRequirementsForContract } from "./evidence-readiness.js";
|
|
22
22
|
import { classifyCommandResolution, validateVerificationAuthority } from "./verification-capability.js";
|
|
23
23
|
import { readExecutionArtifact, validateExecutionBinding } from "./execution.js";
|
|
24
24
|
import { taskArtifactPath, taskExecutionPath } from "./task-paths.js";
|
|
@@ -478,10 +478,15 @@ export async function assertRecordCheckPrerequisites({
|
|
|
478
478
|
const requested = normalizeRequirements(requiredEvidence).find((item) => (
|
|
479
479
|
item.id === requirement || item.text === requirement
|
|
480
480
|
));
|
|
481
|
-
|
|
481
|
+
const terminalRequested = terminalRequirementsForContract(contract.value, {
|
|
482
|
+
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
483
|
+
}).find((item) => (
|
|
484
|
+
item.id === requirement || item.text === requirement
|
|
485
|
+
));
|
|
486
|
+
if ((requested?.terminalOwned || terminalRequested) && status === "passed" && evidenceKind === "OBSERVED") {
|
|
482
487
|
throw artifactError(
|
|
483
488
|
"E_FUTURE_LIFECYCLE_EVIDENCE",
|
|
484
|
-
`Terminal-owned requirement cannot be recorded before its authoritative result: ${requested.text}`,
|
|
489
|
+
`Terminal-owned requirement cannot be recorded before its authoritative result: ${(requested ?? terminalRequested).text}`,
|
|
485
490
|
[stateRel, eventsRel],
|
|
486
491
|
);
|
|
487
492
|
}
|
|
@@ -827,8 +832,12 @@ export async function recordTerminalResult({
|
|
|
827
832
|
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
828
833
|
});
|
|
829
834
|
|
|
830
|
-
const
|
|
831
|
-
|
|
835
|
+
const terminalRequirements = terminalRequirementsForContract(contract.value, {
|
|
836
|
+
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
837
|
+
});
|
|
838
|
+
const requested = terminalRequirements.find((item) => (
|
|
839
|
+
item.id === requirement || item.text === requirement
|
|
840
|
+
)) ?? normalizeRequirements(requiredEvidence).find((item) => (
|
|
832
841
|
item.id === requirement || item.text === requirement
|
|
833
842
|
));
|
|
834
843
|
|
package/src/core/completion.js
CHANGED
|
@@ -10,7 +10,7 @@ import { completionRelationshipErrors } from "./completion-relationships.js";
|
|
|
10
10
|
import { assertSafePath, ensureWithin, fileExists } from "./filesystem.js";
|
|
11
11
|
import { evaluateStartExecutionPrerequisites, hasExecutionStarted } from "./execution-prerequisites.js";
|
|
12
12
|
import { isRecoverableCompletionEvidenceCode } from "./completion-recovery.js";
|
|
13
|
-
import { evaluateTerminalRequirements } from "./evidence-readiness.js";
|
|
13
|
+
import { evaluateTerminalRequirements, terminalRequirementsForContract } from "./evidence-readiness.js";
|
|
14
14
|
import { PROJECT_ARTIFACT_PATHS, taskArtifactPath, taskCodeManifestPath } from "./task-paths.js";
|
|
15
15
|
import { detectPolicyCapability, evaluateTargetPolicy } from "./policy-engine.js";
|
|
16
16
|
import { listActions } from "./actions.js";
|
|
@@ -500,12 +500,10 @@ export async function evaluateCompletion({
|
|
|
500
500
|
const publication = receiptValue ? publicationStatus(receiptValue) : "not-published";
|
|
501
501
|
|
|
502
502
|
if (contract) {
|
|
503
|
-
const allContractReqs = [
|
|
504
|
-
...(contract.value.verification ?? []),
|
|
505
|
-
...(contract.value.successCriteria ?? []),
|
|
506
|
-
];
|
|
507
503
|
const terminalEval = evaluateTerminalRequirements({
|
|
508
|
-
requirements:
|
|
504
|
+
requirements: terminalRequirementsForContract(contract.value, {
|
|
505
|
+
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
506
|
+
}),
|
|
509
507
|
receipt: receiptValue,
|
|
510
508
|
});
|
|
511
509
|
for (const termErr of terminalEval.errors) {
|
package/src/core/config.js
CHANGED
|
@@ -4,6 +4,7 @@ import { E_ATTESTATION_CONFIGURATION_INVALID } from "./error-codes.js";
|
|
|
4
4
|
import { normalizeVerificationConfiguration } from "./verification-scope-capability.js";
|
|
5
5
|
import { EXECUTION_PROFILE_REQUESTS } from "./execution-profile.js";
|
|
6
6
|
import { normalizeStructuralQualityConfig } from "./structural-quality/policy.js";
|
|
7
|
+
import { normalizeDecisionPolicy } from "./decision/policy.js";
|
|
7
8
|
|
|
8
9
|
export const CONFIG_SCHEMA_VERSION = 1;
|
|
9
10
|
export const COMPLIANCE_MODES = Object.freeze(["advisory", "standard", "strict"]);
|
|
@@ -82,6 +83,7 @@ export function createConfig(input = {}) {
|
|
|
82
83
|
executionProfile = input.executionProfile;
|
|
83
84
|
}
|
|
84
85
|
const structuralQuality = normalizeStructuralQualityConfig(input.structuralQuality);
|
|
86
|
+
const decisionEngine = normalizeDecisionPolicy(input.decisionEngine);
|
|
85
87
|
return {
|
|
86
88
|
schemaVersion: CONFIG_SCHEMA_VERSION,
|
|
87
89
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -93,6 +95,7 @@ export function createConfig(input = {}) {
|
|
|
93
95
|
...(attestation ? { attestation } : {}),
|
|
94
96
|
...(executionProfile ? { executionProfile } : {}),
|
|
95
97
|
...(structuralQuality !== undefined ? { structuralQuality } : {}),
|
|
98
|
+
decisionEngine,
|
|
96
99
|
};
|
|
97
100
|
}
|
|
98
101
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const CONTEXT_BUDGETS = Object.freeze({
|
|
2
|
+
light: Object.freeze({ maxItems: 12, maxChars: 24_000 }),
|
|
3
|
+
balanced: Object.freeze({ maxItems: 32, maxChars: 64_000 }),
|
|
4
|
+
full: Object.freeze({ maxItems: 96, maxChars: 160_000 }),
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export function contextBudget(profile = "balanced") {
|
|
8
|
+
return CONTEXT_BUDGETS[profile] ?? CONTEXT_BUDGETS.balanced;
|
|
9
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { canonicalFingerprint } from "../artifacts.js";
|
|
2
|
+
import { buildDecisionState } from "../decision/state-builder.js";
|
|
3
|
+
|
|
4
|
+
const INJECTION_PATTERN = /(ignore|disregard|override)\s+(?:all|previous|forge|system|developer)|exfiltrat|reveal\s+(?:the|all)\s+(?:secret|token|key)|execute\s+(?:this|the)\s+command/i;
|
|
5
|
+
|
|
6
|
+
export function normalizeContextCandidate(input = {}) {
|
|
7
|
+
const safe = buildDecisionState({
|
|
8
|
+
id: input.id ?? null,
|
|
9
|
+
sourceRef: input.sourceRef ?? null,
|
|
10
|
+
kind: input.kind ?? "UNKNOWN",
|
|
11
|
+
summary: input.summary ?? "",
|
|
12
|
+
required: input.required === true,
|
|
13
|
+
mandatory: input.mandatory === true,
|
|
14
|
+
risk: input.risk ?? "NORMAL",
|
|
15
|
+
trust: input.trust ?? "UNTRUSTED",
|
|
16
|
+
});
|
|
17
|
+
const summary = typeof safe.summary === "string" ? safe.summary : "";
|
|
18
|
+
const id = typeof safe.id === "string" && safe.id ? safe.id : `candidate-${canonicalFingerprint(safe).slice(0, 16)}`;
|
|
19
|
+
return {
|
|
20
|
+
...safe,
|
|
21
|
+
id,
|
|
22
|
+
summary,
|
|
23
|
+
chars: summary.length,
|
|
24
|
+
promptInjection: INJECTION_PATTERN.test(summary),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function normalizeContextCandidates(candidates = []) {
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
const normalized = [];
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
const value = normalizeContextCandidate(candidate);
|
|
33
|
+
const identity = `${value.id}:${value.sourceRef ?? ""}`;
|
|
34
|
+
if (seen.has(identity)) continue;
|
|
35
|
+
seen.add(identity);
|
|
36
|
+
normalized.push(value);
|
|
37
|
+
}
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { contextBudget } from "./budget.js";
|
|
2
|
+
import { normalizeContextCandidates } from "./candidates.js";
|
|
3
|
+
import { contextPlanFingerprint } from "./fingerprint.js";
|
|
4
|
+
import { canonicalFingerprint } from "../artifacts.js";
|
|
5
|
+
import { assertContextCompilerPolicy, candidatePriority } from "./policy.js";
|
|
6
|
+
import { contextPlanResult } from "./result.js";
|
|
7
|
+
|
|
8
|
+
function orderCandidates(candidates, rankedIds = []) {
|
|
9
|
+
const ranks = new Map(rankedIds.map((id, index) => [id, rankedIds.length - index]));
|
|
10
|
+
return [...candidates].sort((a, b) => (b.mandatory - a.mandatory)
|
|
11
|
+
|| (b.required - a.required)
|
|
12
|
+
|| ((ranks.get(b.id) ?? 0) - (ranks.get(a.id) ?? 0))
|
|
13
|
+
|| (candidatePriority(b) - candidatePriority(a))
|
|
14
|
+
|| a.id.localeCompare(b.id));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function compileContext({ candidates = [], profile = "balanced", mandatoryIds = [], semanticRanker = null } = {}) {
|
|
18
|
+
const policy = assertContextCompilerPolicy({ profile, mandatoryIds });
|
|
19
|
+
const normalized = normalizeContextCandidates(candidates);
|
|
20
|
+
const candidateSetFingerprint = canonicalFingerprint(normalized.map((candidate) => ({
|
|
21
|
+
id: candidate.id,
|
|
22
|
+
sourceRef: candidate.sourceRef,
|
|
23
|
+
summary: candidate.summary,
|
|
24
|
+
required: candidate.required,
|
|
25
|
+
mandatory: candidate.mandatory,
|
|
26
|
+
})));
|
|
27
|
+
const mandatory = new Set(policy.mandatoryIds);
|
|
28
|
+
const marked = normalized.map((candidate) => ({ ...candidate, mandatory: candidate.mandatory || mandatory.has(candidate.id) }));
|
|
29
|
+
const semantic = typeof semanticRanker === "function" ? semanticRanker(marked.filter((item) => !item.promptInjection).map((item) => ({ id: item.id, summary: item.summary }))) : [];
|
|
30
|
+
const rankedIds = Array.isArray(semantic) ? semantic : (semantic?.rankedIds ?? []);
|
|
31
|
+
const excludedIds = new Set(Array.isArray(semantic) ? [] : (semantic?.excludedIds ?? []));
|
|
32
|
+
const ordered = orderCandidates(marked, Array.isArray(rankedIds) ? rankedIds : []);
|
|
33
|
+
const budget = contextBudget(profile);
|
|
34
|
+
const selected = [];
|
|
35
|
+
const omitted = [];
|
|
36
|
+
let chars = 0;
|
|
37
|
+
for (const candidate of ordered) {
|
|
38
|
+
const mustKeep = candidate.mandatory || candidate.required;
|
|
39
|
+
if (excludedIds.has(candidate.id) && !mustKeep) {
|
|
40
|
+
omitted.push(candidate);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const fits = selected.length < budget.maxItems && chars + candidate.chars <= budget.maxChars;
|
|
44
|
+
if (fits || mustKeep) {
|
|
45
|
+
selected.push(candidate);
|
|
46
|
+
chars += candidate.chars;
|
|
47
|
+
} else omitted.push(candidate);
|
|
48
|
+
}
|
|
49
|
+
const promptInjectionIds = marked.filter((item) => item.promptInjection).map((item) => item.id).sort();
|
|
50
|
+
const result = contextPlanResult({
|
|
51
|
+
profile,
|
|
52
|
+
candidates: marked,
|
|
53
|
+
selected,
|
|
54
|
+
omitted,
|
|
55
|
+
mandatoryIds: [...new Set([...policy.mandatoryIds, ...marked.filter((item) => item.mandatory).map((item) => item.id)])].sort(),
|
|
56
|
+
promptInjectionIds,
|
|
57
|
+
fingerprint: null,
|
|
58
|
+
budget,
|
|
59
|
+
candidateSetFingerprint,
|
|
60
|
+
});
|
|
61
|
+
result.decisionFingerprint = contextPlanFingerprint(result);
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { canonicalFingerprint } from "../artifacts.js";
|
|
2
|
+
|
|
3
|
+
export function contextPlanFingerprint(plan) {
|
|
4
|
+
return canonicalFingerprint({
|
|
5
|
+
profile: plan.profile,
|
|
6
|
+
selectedIds: plan.selected.map((item) => item.id),
|
|
7
|
+
omittedIds: plan.omitted.map((item) => item.id),
|
|
8
|
+
mandatoryIds: plan.mandatoryIds,
|
|
9
|
+
promptInjectionIds: plan.promptInjectionIds,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { decisionError, DECISION_ERROR_CODES } from "../decision/errors.js";
|
|
2
|
+
|
|
3
|
+
const RISK_ORDER = Object.freeze({ CRITICAL: 4, HIGH: 3, NORMAL: 2, LOW: 1 });
|
|
4
|
+
|
|
5
|
+
export function assertContextCompilerPolicy({ profile = "balanced", mandatoryIds = [] } = {}) {
|
|
6
|
+
if (!["light", "balanced", "full"].includes(profile)) throw decisionError(DECISION_ERROR_CODES.POLICY_INVALID, "Context compiler profile is invalid.");
|
|
7
|
+
if (!Array.isArray(mandatoryIds) || mandatoryIds.some((id) => typeof id !== "string" || !id)) throw decisionError(DECISION_ERROR_CODES.POLICY_INVALID, "Context compiler mandatory IDs are invalid.");
|
|
8
|
+
return { profile, mandatoryIds: [...new Set(mandatoryIds)] };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function candidatePriority(candidate) {
|
|
12
|
+
return (candidate.mandatory || candidate.required ? 100 : 0) + (candidate.promptInjection ? -50 : 0) + (RISK_ORDER[candidate.risk] ?? 0);
|
|
13
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function contextPlanResult({ profile, candidates, selected, omitted, mandatoryIds, promptInjectionIds, fingerprint, budget, candidateSetFingerprint }) {
|
|
2
|
+
return {
|
|
3
|
+
schemaVersion: 1,
|
|
4
|
+
protocolVersion: 1,
|
|
5
|
+
profile,
|
|
6
|
+
budget,
|
|
7
|
+
candidateItems: candidates.length,
|
|
8
|
+
selectedItems: selected.length,
|
|
9
|
+
candidateChars: candidates.reduce((total, item) => total + item.chars, 0),
|
|
10
|
+
selectedChars: selected.reduce((total, item) => total + item.chars, 0),
|
|
11
|
+
selected,
|
|
12
|
+
omitted,
|
|
13
|
+
mandatoryIds,
|
|
14
|
+
promptInjectionIds,
|
|
15
|
+
candidateSetFingerprint,
|
|
16
|
+
tokenUsage: { inputTokens: null, outputTokens: null, reportedBy: "UNKNOWN" },
|
|
17
|
+
decisionFingerprint: fingerprint,
|
|
18
|
+
authority: "SEMANTIC_DECISION",
|
|
19
|
+
evidenceAuthority: "NONE",
|
|
20
|
+
lifecycleAuthority: false,
|
|
21
|
+
completionAuthority: false,
|
|
22
|
+
};
|
|
23
|
+
}
|