@cassiomc1/forgeloop 1.13.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/AGENT_COMPATIBILITY.md +8 -0
- package/DOCS_INDEX.md +35 -3
- package/ENG/nodejs-backend-development-eng.md +2 -2
- package/ENG/sec-code-eng.md +7 -7
- package/EXECUTION_STATE.md +12 -0
- package/LOOP_ENGINEERING.md +28 -2
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/README.md +40 -25
- package/TERMINOLOGY.md +2 -0
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +19 -1
- package/completions/forgeloop.bash +37 -1
- package/completions/forgeloop.fish +123 -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 +27 -2
- 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 +366 -6
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +32 -11
- 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 +35 -8
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +9 -0
- package/docs/RELEASE_CHECKLIST.md +38 -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 +198 -1
- 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 +750 -5
- package/docs/protocol-requirements.json +24 -0
- package/package.json +30 -3
- package/schemas/config.schema.json +14 -0
- package/schemas/context-plan.schema.json +18 -0
- package/schemas/semantic-decision.schema.json +46 -0
- package/schemas/test-utility.schema.json +44 -0
- package/scripts/CI_VALIDATORS.md +6 -6
- package/scripts/benchmark-jev.mjs +5 -0
- package/scripts/benchmark-test-intelligence.mjs +4 -0
- package/scripts/generate-agent-protocol-summary.mjs +4 -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/route.js +146 -18
- package/src/commands/semantic-plan.js +17 -0
- package/src/commands/task-abandon.js +224 -0
- 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/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/checkpoint-revalidation.js +319 -0
- package/src/core/cli-command-definitions.js +241 -0
- package/src/core/command-executors.js +110 -0
- package/src/core/command-input.js +115 -43
- 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-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 +278 -0
- 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/gate-provenance.js +124 -0
- package/src/core/integration-invocation-policy.js +27 -4
- package/src/core/integration-resources.js +86 -61
- 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/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/phase.js +128 -10
- package/src/core/preflight-consistency.js +23 -9
- package/src/core/preflight-loaders.js +37 -5
- package/src/core/protocol-info.js +65 -0
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +128 -52
- 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 +67 -1
- package/src/core/runtime-context.js +118 -61
- 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/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/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
|
@@ -2,6 +2,7 @@ import { runDoctor } from "../commands/doctor.js";
|
|
|
2
2
|
import { runStatus } from "../commands/status.js";
|
|
3
3
|
import { runValidateState } from "../commands/validate-state.js";
|
|
4
4
|
import { runClearState } from "../commands/clear-state.js";
|
|
5
|
+
import { runCheckpointRevalidate } from "../commands/checkpoint-revalidate.js";
|
|
5
6
|
import { inspectTarget } from "../commands/inspect.js";
|
|
6
7
|
import { runInit } from "../commands/init.js";
|
|
7
8
|
import { runRoute } from "../commands/route.js";
|
|
@@ -57,6 +58,11 @@ import { runRecordContinuity } from "../commands/record-continuity.js";
|
|
|
57
58
|
import { runReconcileContinuity } from "../commands/reconcile-continuity.js";
|
|
58
59
|
import { runClearContinuity } from "../commands/clear-continuity.js";
|
|
59
60
|
import { runTaskCreate } from "../commands/task-create.js";
|
|
61
|
+
import { runDiscover } from "../commands/discover.js";
|
|
62
|
+
import { runContractCreate } from "../commands/contract-create.js";
|
|
63
|
+
import { runContractRevise } from "../commands/contract-revise.js";
|
|
64
|
+
import { runGateRecord } from "../commands/gate-record.js";
|
|
65
|
+
import { runGateRevalidate } from "../commands/gate-revalidate.js";
|
|
60
66
|
import { runTaskList } from "../commands/task-list.js";
|
|
61
67
|
import { runTaskShow } from "../commands/task-show.js";
|
|
62
68
|
import { runTaskScope } from "../commands/task-scope.js";
|
|
@@ -64,10 +70,22 @@ import { runTaskMigrate } from "../commands/task-migrate.js";
|
|
|
64
70
|
import { runMigrateProtocol } from "../commands/migrate-protocol.js";
|
|
65
71
|
import { runTaskUnlock } from "../commands/task-unlock.js";
|
|
66
72
|
import { runTaskRecover } from "../commands/task-recover.js";
|
|
73
|
+
import { runTaskAbandon } from "../commands/task-abandon.js";
|
|
67
74
|
import { runTaskResume } from "../commands/task-resume.js";
|
|
68
75
|
import { runTaskRepairLegacyRecovery } from "../commands/task-repair-legacy-recovery.js";
|
|
76
|
+
import { runTaskRepairContractBootstrap } from "../commands/task-repair-contract-bootstrap.js";
|
|
77
|
+
import { runTaskMigrateContractBootstrapRepair } from "../commands/task-migrate-contract-bootstrap-repair.js";
|
|
69
78
|
import { runTaskLockStatus } from "../commands/task-lock-status.js";
|
|
70
79
|
import { runProtocolInfo } from "../commands/protocol-info.js";
|
|
80
|
+
import { runDecisionStatus } from "../commands/decision-status.js";
|
|
81
|
+
import { runDecisionShow } from "../commands/decision-show.js";
|
|
82
|
+
import { runContextPlan } from "../commands/context-plan.js";
|
|
83
|
+
import { runModelRoute } from "../commands/model-route.js";
|
|
84
|
+
import { runSemanticPlan } from "../commands/semantic-plan.js";
|
|
85
|
+
import { runTestInventory } from "../commands/test-inventory.js";
|
|
86
|
+
import { runTestUtility } from "../commands/test-utility.js";
|
|
87
|
+
import { runTestPrunePlan } from "../commands/test-prune-plan.js";
|
|
88
|
+
import { runTestPruneProbe } from "../commands/test-prune-probe.js";
|
|
71
89
|
import { runWorkspaceBind } from "../commands/workspace-bind.js";
|
|
72
90
|
import { runWorkspaceStatus } from "../commands/workspace-status.js";
|
|
73
91
|
import { runHandoffCreate } from "../commands/handoff-create.js";
|
|
@@ -103,10 +121,81 @@ import {
|
|
|
103
121
|
* directly. No ownership, recovery, or lifecycle logic may live here.
|
|
104
122
|
*/
|
|
105
123
|
export const COMMAND_EXECUTORS = {
|
|
124
|
+
discover: async ({ target, packageRoot, options }) => ({
|
|
125
|
+
result: await runDiscover({ target, packageRoot, taskId: options.taskId }),
|
|
126
|
+
exitCode: 0,
|
|
127
|
+
}),
|
|
128
|
+
"contract-create": async ({ target, packageRoot, options }) => ({
|
|
129
|
+
result: await runContractCreate({ target, packageRoot, taskId: options.taskId, preset: options.preset, contractFile: options.contractFile }),
|
|
130
|
+
exitCode: 0,
|
|
131
|
+
}),
|
|
132
|
+
"contract-revise": async ({ target, packageRoot, options }) => ({
|
|
133
|
+
result: await runContractRevise({ target, packageRoot, taskId: options.taskId, preset: options.preset, contractFile: options.contractFile }),
|
|
134
|
+
exitCode: 0,
|
|
135
|
+
}),
|
|
136
|
+
"gate-record": async ({ target, packageRoot, options }) => ({
|
|
137
|
+
result: await runGateRecord({ target, packageRoot, taskId: options.taskId, gate: options.gate, status: options.gateStatus, artifacts: options.gateArtifacts, decisions: options.gateDecisions, unknowns: options.gateUnknowns, assumptions: options.gateAssumptions, evidenceFile: options.gateEvidenceFile }),
|
|
138
|
+
exitCode: 0,
|
|
139
|
+
}),
|
|
140
|
+
"gate-revalidate": async ({ target, packageRoot, options }) => ({
|
|
141
|
+
result: await runGateRevalidate({ target, packageRoot, taskId: options.taskId, gate: options.gate, acknowledgeStale: options.acknowledgeStale }),
|
|
142
|
+
exitCode: 0,
|
|
143
|
+
}),
|
|
106
144
|
"protocol-info": async ({ packageVersion }) => ({
|
|
107
145
|
result: await runProtocolInfo({ packageVersion }),
|
|
108
146
|
exitCode: 0,
|
|
109
147
|
}),
|
|
148
|
+
"decision-status": async ({ target, packageRoot, options }) => ({
|
|
149
|
+
result: await runDecisionStatus({ target, packageRoot, health: options.health }),
|
|
150
|
+
exitCode: 0,
|
|
151
|
+
}),
|
|
152
|
+
"decision-show": async ({ target, packageRoot, options }) => ({
|
|
153
|
+
result: await runDecisionShow({ target, packageRoot, taskId: options.taskId, decisionId: options.decisionId }),
|
|
154
|
+
exitCode: 0,
|
|
155
|
+
}),
|
|
156
|
+
"context-plan": async ({ target, packageRoot, options }) => ({
|
|
157
|
+
result: await runContextPlan({ target, packageRoot, taskId: options.taskId, decisionId: options.decisionId, profile: options.profile ?? "balanced" }),
|
|
158
|
+
exitCode: 0,
|
|
159
|
+
}),
|
|
160
|
+
"model-route": async ({ target, packageRoot, options }) => ({
|
|
161
|
+
result: await runModelRoute({
|
|
162
|
+
target, packageRoot, taskId: options.taskId, decisionId: options.decisionId,
|
|
163
|
+
workType: options.workType,
|
|
164
|
+
surfaces: options.surfaces,
|
|
165
|
+
risks: options.risks,
|
|
166
|
+
platforms: options.platforms,
|
|
167
|
+
behaviorChange: options.behaviorChange,
|
|
168
|
+
executableChange: options.executableChange,
|
|
169
|
+
generationRequired: options.generationRequired,
|
|
170
|
+
architectureChange: options.architectureChange,
|
|
171
|
+
ambiguity: options.ambiguity,
|
|
172
|
+
}),
|
|
173
|
+
exitCode: 0,
|
|
174
|
+
}),
|
|
175
|
+
"semantic-plan": async ({ target, packageRoot, options }) => ({
|
|
176
|
+
result: await runSemanticPlan({
|
|
177
|
+
target, packageRoot, taskId: options.taskId, decisionId: options.decisionId,
|
|
178
|
+
kind: options.semanticPlanKind,
|
|
179
|
+
input: options.semanticPlanInput ?? {},
|
|
180
|
+
}),
|
|
181
|
+
exitCode: 0,
|
|
182
|
+
}),
|
|
183
|
+
"test-inventory": async ({ target }) => ({
|
|
184
|
+
result: await runTestInventory({ target }),
|
|
185
|
+
exitCode: 0,
|
|
186
|
+
}),
|
|
187
|
+
"test-utility": async ({ target, packageRoot, options }) => ({
|
|
188
|
+
result: await runTestUtility({ target, packageRoot, taskId: options.taskId }),
|
|
189
|
+
exitCode: 0,
|
|
190
|
+
}),
|
|
191
|
+
"test-prune-plan": async ({ target, packageRoot, options }) => ({
|
|
192
|
+
result: await runTestPrunePlan({ target, packageRoot, taskId: options.taskId }),
|
|
193
|
+
exitCode: 0,
|
|
194
|
+
}),
|
|
195
|
+
"test-prune-probe": async ({ target, packageRoot, options }) => ({
|
|
196
|
+
result: await runTestPruneProbe({ target, packageRoot, taskId: options.taskId, testId: options.testId }),
|
|
197
|
+
exitCode: 0,
|
|
198
|
+
}),
|
|
110
199
|
init: async ({ target, packageRoot, packageVersion, options }) => ({
|
|
111
200
|
result: await runInit({ target, dryRun: options.dryRun, packageRoot, packageVersion, repositoryIndex: true }),
|
|
112
201
|
exitCode: 0,
|
|
@@ -593,6 +682,10 @@ export const COMMAND_EXECUTORS = {
|
|
|
593
682
|
result: await runClearState({ target, taskId: options.taskId }),
|
|
594
683
|
exitCode: 0,
|
|
595
684
|
}),
|
|
685
|
+
"checkpoint-revalidate": async ({ target, packageRoot, options }) => ({
|
|
686
|
+
result: await runCheckpointRevalidate({ target, packageRoot, taskId: options.taskId }),
|
|
687
|
+
exitCode: 0,
|
|
688
|
+
}),
|
|
596
689
|
"task-create": async ({ target, packageRoot, options }) => ({
|
|
597
690
|
result: await runTaskCreate({
|
|
598
691
|
target,
|
|
@@ -648,10 +741,27 @@ export const COMMAND_EXECUTORS = {
|
|
|
648
741
|
}),
|
|
649
742
|
exitCode: 0,
|
|
650
743
|
}),
|
|
744
|
+
"task-abandon": async ({ target, packageRoot, options }) => ({
|
|
745
|
+
result: await runTaskAbandon({
|
|
746
|
+
target,
|
|
747
|
+
packageRoot,
|
|
748
|
+
taskId: options.taskId,
|
|
749
|
+
acknowledgeAbandonment: options.acknowledgeAbandonment,
|
|
750
|
+
}),
|
|
751
|
+
exitCode: 0,
|
|
752
|
+
}),
|
|
651
753
|
"task-resume": async ({ target, packageRoot, options }) => ({
|
|
652
754
|
result: await runTaskResume({ target, packageRoot, taskId: options.taskId, claims: options.claims }),
|
|
653
755
|
exitCode: 0,
|
|
654
756
|
}),
|
|
757
|
+
"task-repair-contract-bootstrap": async ({ target, packageRoot, options }) => ({
|
|
758
|
+
result: await runTaskRepairContractBootstrap({ target, packageRoot, taskId: options.taskId, acknowledgeRepair: options.acknowledgeRepair }),
|
|
759
|
+
exitCode: 0,
|
|
760
|
+
}),
|
|
761
|
+
"task-migrate-contract-bootstrap-repair": async ({ target, packageRoot, options }) => ({
|
|
762
|
+
result: await runTaskMigrateContractBootstrapRepair({ target, packageRoot, taskId: options.taskId, acknowledgeMigration: options.acknowledgeMigration }),
|
|
763
|
+
exitCode: 0,
|
|
764
|
+
}),
|
|
655
765
|
"task-repair-legacy-recovery": async ({ target, packageRoot, options }) => ({
|
|
656
766
|
result: await runTaskRepairLegacyRecovery({
|
|
657
767
|
target,
|
|
@@ -19,10 +19,17 @@ function validatePolicyInput(command, options) {
|
|
|
19
19
|
if (command !== "policy" && options.policy) throw inputError(`Policy name is not valid for ${command}`);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function
|
|
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;
|
|
23
30
|
if (command === "bundle" && !options.taskId) throw inputError("bundle requires --task");
|
|
24
31
|
if (command === "task-create" && !options.taskId) throw inputError("task-create requires --task");
|
|
25
|
-
if (
|
|
32
|
+
if (!["task-create", "contract-create", "contract-revise"].includes(command) && (options.preset || options.preview)) {
|
|
26
33
|
throw inputError(`preset/preview options are not valid for ${command}`);
|
|
27
34
|
}
|
|
28
35
|
if (command === "task-create" && options.preview && !options.preset && !options.contractFile) {
|
|
@@ -31,6 +38,15 @@ function validateTaskCreationInput(command, options) {
|
|
|
31
38
|
if (command === "task-create" && options.preset && options.contractFile) {
|
|
32
39
|
throw inputError("task-create accepts either --preset or --contract-file, not both");
|
|
33
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);
|
|
34
50
|
const hasTaskListFilter = Boolean(
|
|
35
51
|
options.phase
|
|
36
52
|
|| options.active
|
|
@@ -42,6 +58,15 @@ function validateTaskCreationInput(command, options) {
|
|
|
42
58
|
}
|
|
43
59
|
}
|
|
44
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
|
+
|
|
45
70
|
function validateExecutionProfileInput(command, options) {
|
|
46
71
|
if (options.executionProfile !== null && options.executionProfile !== undefined) {
|
|
47
72
|
if (command !== "route") throw inputError(`executionProfile is not valid for ${command}`);
|
|
@@ -91,54 +116,91 @@ function validateOutputInput(command, options) {
|
|
|
91
116
|
if (command !== "next" && options.explain === true) throw inputError(`explain output is not valid for ${command}`);
|
|
92
117
|
}
|
|
93
118
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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`);
|
|
101
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) {
|
|
102
156
|
if (command === "responsibility-set" && !help && !options.responsibilityLabel) {
|
|
103
157
|
throw inputError("responsibility-set requires --label");
|
|
104
158
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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>`);
|
|
112
196
|
}
|
|
197
|
+
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError(`${command} requires -- followed by an exact command argv`);
|
|
113
198
|
}
|
|
114
199
|
|
|
115
200
|
function validateCheckInput(command, options, help) {
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!options.checkStatus) throw inputError("record-check requires --status");
|
|
120
|
-
if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
|
|
121
|
-
if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
|
|
122
|
-
}
|
|
123
|
-
if (command === "run-check" && !help) {
|
|
124
|
-
if (!options.checkId) throw inputError("run-check requires --id");
|
|
125
|
-
if (!options.checkRequirement) throw inputError("run-check requires --requirement");
|
|
126
|
-
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
127
|
-
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
128
|
-
throw inputError("run-check accepts only --id, --requirement, --details, --timeout-ms, --scope-ref, and -- <argv>");
|
|
129
|
-
}
|
|
130
|
-
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError("run-check requires -- followed by an exact command argv");
|
|
131
|
-
}
|
|
132
|
-
if (command === "reconcile-closure" && !help) {
|
|
133
|
-
if (!options.taskId) throw inputError("reconcile-closure requires --task");
|
|
134
|
-
if (!options.checkId) throw inputError("reconcile-closure requires --id");
|
|
135
|
-
if (!options.checkRequirement) throw inputError("reconcile-closure requires --requirement");
|
|
136
|
-
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
137
|
-
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
138
|
-
throw inputError("reconcile-closure accepts only --id, --requirement, --details, and -- <argv>");
|
|
139
|
-
}
|
|
140
|
-
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError("reconcile-closure requires -- followed by an exact command argv");
|
|
141
|
-
}
|
|
201
|
+
if (help) return;
|
|
202
|
+
if (command === "record-check") validateRecordCheckInput(options);
|
|
203
|
+
if (command === "run-check" || command === "reconcile-closure") validateCommandCheckInput(options, command);
|
|
142
204
|
}
|
|
143
205
|
|
|
144
206
|
/**
|
|
@@ -172,6 +234,8 @@ export function defaultCommandInputValues() {
|
|
|
172
234
|
assetPath: null,
|
|
173
235
|
binaryPath: null,
|
|
174
236
|
force: false,
|
|
237
|
+
acknowledgeRepair: false,
|
|
238
|
+
acknowledgeMigration: false,
|
|
175
239
|
workType: null,
|
|
176
240
|
surfaces: [],
|
|
177
241
|
risks: [],
|
|
@@ -193,6 +257,13 @@ export function defaultCommandInputValues() {
|
|
|
193
257
|
file: null,
|
|
194
258
|
contractFile: null,
|
|
195
259
|
preset: null,
|
|
260
|
+
gate: null,
|
|
261
|
+
gateStatus: null,
|
|
262
|
+
gateArtifacts: [],
|
|
263
|
+
gateDecisions: [],
|
|
264
|
+
gateUnknowns: [],
|
|
265
|
+
gateAssumptions: [],
|
|
266
|
+
gateEvidenceFile: null,
|
|
196
267
|
preview: false,
|
|
197
268
|
phase: null,
|
|
198
269
|
active: false,
|
|
@@ -263,7 +334,8 @@ export function validateForgeLoopCommandInput({ command, input, help = false } =
|
|
|
263
334
|
if (!command) return;
|
|
264
335
|
|
|
265
336
|
validatePolicyInput(command, options);
|
|
266
|
-
validateTaskCreationInput(command, options);
|
|
337
|
+
validateTaskCreationInput(command, options, help);
|
|
338
|
+
validateGateRecordInput(command, options, help);
|
|
267
339
|
validateSearchCommandInput(command, options, help);
|
|
268
340
|
validateProfileAndUsageInput(command, options, help);
|
|
269
341
|
validateOutputInput(command, options);
|
|
@@ -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
|
+
}
|