opencode-wait-a-minute 0.0.0-stage → 1.1.0-rc.10
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/LICENSE +21 -0
- package/README.md +318 -2
- package/index.js +1836 -0
- package/package.json +70 -4
- package/skills/codebase-design/SKILL.md +114 -0
- package/skills/design-contract/SKILL.md +27 -0
- package/skills/design-critique/SKILL.md +19 -0
- package/skills/design-system/SKILL.md +17 -0
- package/skills/design-system/references/image-analysis.md +14 -0
- package/skills/design-system/references/tokens.md +32 -0
- package/skills/design-system/references/ui-rules.md +14 -0
- package/skills/diagnosing-bugs/SKILL.md +138 -0
- package/skills/frontend-design/SKILL.md +36 -0
- package/skills/handoff/SKILL.md +16 -0
- package/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/skills/linter-negative/SKILL.md +74 -0
- package/skills/no-slop-ui/SKILL.md +26 -0
- package/skills/prototype/SKILL.md +26 -0
- package/skills/registry.json +44087 -0
- package/skills/tastemaker/SKILL.md +8 -0
- package/skills/tastemaker/references/tastemaker.md +14 -0
- package/skills/tdd/SKILL.md +38 -0
- package/skills/visual-review/SKILL.md +103 -0
- package/skills/visual-review/references/review-gates.md +42 -0
- package/skills/visual-review/references/surface-archetypes.md +37 -0
- package/skills/wizard/SKILL.md +44 -0
- package/skills/writing-for-agents/SKILL.md +81 -0
- package/src/cognition/cognition-store.js +251 -0
- package/src/cognition/cognitive-state.js +126 -0
- package/src/cognition/hypothesis-manager.js +113 -0
- package/src/cognition/observation-engine.js +112 -0
- package/src/cognition/observation-provenance.js +268 -0
- package/src/context/active-context-boundary.js +68 -0
- package/src/context/assembly.js +494 -0
- package/src/context/assessment-rules.js +55 -0
- package/src/context/capsule-staleness.js +267 -0
- package/src/context/capsules.js +2 -0
- package/src/context/compare-array.js +98 -0
- package/src/context/compare-object.js +89 -0
- package/src/context/compare-values.js +42 -0
- package/src/context/context-assembly-contract.js +71 -0
- package/src/context/context-budget-manager.js +232 -0
- package/src/context/context-builder.js +49 -0
- package/src/context/context-cache.js +55 -0
- package/src/context/context-capture.js +243 -0
- package/src/context/context-classification.js +199 -0
- package/src/context/context-compaction.js +179 -0
- package/src/context/context-decision-audit.js +267 -0
- package/src/context/context-demotion.js +173 -0
- package/src/context/context-dictionary.js +106 -0
- package/src/context/context-drift-detection.js +225 -0
- package/src/context/context-evaluation.js +374 -0
- package/src/context/context-event-ingress.js +100 -0
- package/src/context/context-event-normalization.js +224 -0
- package/src/context/context-feedback-loop.js +216 -0
- package/src/context/context-freshness.js +21 -0
- package/src/context/context-graph-builder.js +71 -0
- package/src/context/context-graph.js +451 -0
- package/src/context/context-history-relevance.js +321 -0
- package/src/context/context-interception.js +145 -0
- package/src/context/context-invalidation-events.js +154 -0
- package/src/context/context-lifecycle.js +170 -0
- package/src/context/context-loop-prevention.js +347 -0
- package/src/context/context-manager.js +340 -0
- package/src/context/context-memory-layers.js +265 -0
- package/src/context/context-minimality.js +166 -0
- package/src/context/context-normalization.js +130 -0
- package/src/context/context-optimization-metrics.js +434 -0
- package/src/context/context-output-policy.js +135 -0
- package/src/context/context-pack-authority.js +157 -0
- package/src/context/context-promotion.js +172 -0
- package/src/context/context-query-contract.js +101 -0
- package/src/context/context-ranking.js +164 -0
- package/src/context/context-rate-distortion.js +261 -0
- package/src/context/context-recovery.js +209 -0
- package/src/context/context-retrieval-integration.js +60 -0
- package/src/context/context-retrieval.js +93 -0
- package/src/context/context-router.js +457 -0
- package/src/context/context-routing-evaluation.js +331 -0
- package/src/context/context-routing-observability.js +296 -0
- package/src/context/context-routing-shadow.js +237 -0
- package/src/context/context-scope.js +181 -0
- package/src/context/context-snapshot.js +226 -0
- package/src/context/context-source-registry.js +95 -0
- package/src/context/context-sufficiency-gate.js +120 -0
- package/src/context/context-sufficiency-oracle.js +167 -0
- package/src/context/context-telemetry.js +176 -0
- package/src/context/context.js +633 -0
- package/src/context/dependency-closure.js +246 -0
- package/src/context/page-fault-tracker.js +275 -0
- package/src/context/prompt-engine.js +74 -0
- package/src/context/runtime-context-graph.js +260 -0
- package/src/context/selection.js +2 -0
- package/src/context/session.js +2 -0
- package/src/context/sufficiency-contract.js +300 -0
- package/src/context/tokenizer.js +17 -0
- package/src/context-benchmark-router.mjs +125 -0
- package/src/evidence/evidence-engine.js +161 -0
- package/src/evidence/evidence-factory.js +3 -0
- package/src/evidence/evidence-freshness.js +463 -0
- package/src/evidence/evidence-gap.js +76 -0
- package/src/evidence/evidence-lineage.js +525 -0
- package/src/evidence/evidence.js +136 -0
- package/src/evidence/freshness-validation.js +187 -0
- package/src/evidence/lineage.js +4 -0
- package/src/execution/action-evaluation.js +179 -0
- package/src/execution/circuit-breaker.js +70 -0
- package/src/execution/claim-interception.js +63 -0
- package/src/execution/execution-engine.js +258 -0
- package/src/execution/execution-intent.js +69 -0
- package/src/execution/execution-state.js +89 -0
- package/src/execution/formatting.js +60 -0
- package/src/execution/l1-sanitize.js +16 -0
- package/src/execution/l2-resource-guards.js +19 -0
- package/src/execution/l3-invariant-checker.js +5 -0
- package/src/execution/l4-containment.js +6 -0
- package/src/execution/runtime-guards.js +106 -0
- package/src/execution/strategy-identity.js +162 -0
- package/src/execution/tool-interceptor.js +7 -0
- package/src/execution/tool-result-classifier.js +63 -0
- package/src/integration/canary-deploy.js +143 -0
- package/src/integration/collector.js +59 -0
- package/src/integration/error-recovery.js +6 -0
- package/src/integration/event-bus.js +66 -0
- package/src/integration/health-checker.js +57 -0
- package/src/integration/logger.js +39 -0
- package/src/integration/message-handler.js +611 -0
- package/src/integration/opencode-db.js +153 -0
- package/src/integration/orchestration.js +419 -0
- package/src/integration/part-provenance.js +65 -0
- package/src/integration/rate-limiter.js +55 -0
- package/src/integration/release-gate.js +336 -0
- package/src/integration/rollback-manager.js +164 -0
- package/src/integration/router-adapter.js +269 -0
- package/src/integration/telemetry.js +45 -0
- package/src/integration/update-checker.js +10 -0
- package/src/persistence/event-sourcing.js +75 -0
- package/src/persistence/memory.js +501 -0
- package/src/persistence/migration-runner.js +16 -0
- package/src/persistence/persistence-manager.js +39 -0
- package/src/persistence/transaction-log.js +157 -0
- package/src/policy/action-capabilities.js +116 -0
- package/src/policy/action-policy.js +4 -0
- package/src/policy/assessment-engine.js +104 -0
- package/src/policy/assumption-tracking.js +66 -0
- package/src/policy/budget-enforcer.js +7 -0
- package/src/policy/config-validator.js +102 -0
- package/src/policy/execution-assessment.js +142 -0
- package/src/policy/failure-classification.js +5 -0
- package/src/policy/feature-flags.js +65 -0
- package/src/policy/governance-enforcement.js +118 -0
- package/src/policy/locale.js +15 -0
- package/src/policy/multi-signer.js +151 -0
- package/src/policy/policy-state-machine.js +97 -0
- package/src/policy/ponytail-constraint.js +38 -0
- package/src/policy/project-inspector.js +93 -0
- package/src/policy/query-builder.js +14 -0
- package/src/policy/request-classifier.js +79 -0
- package/src/policy/risk-engine.js +266 -0
- package/src/policy/scope-enforcement.js +140 -0
- package/src/policy/secret-scanner.js +68 -0
- package/src/policy/skill-routing.js +104 -0
- package/src/policy/strategy-capabilities.js +211 -0
- package/src/policy/strategy.js +6 -0
- package/src/policy/uncertainty.js +117 -0
- package/src/shared/container.js +44 -0
- package/src/shared/decision-types.js +24 -0
- package/src/shared/simple-test.js +160 -0
- package/src/shared/wam-events.js +52 -0
- package/src/skills/engine.js +1705 -0
- package/src/skills/skill-registry.js +19 -0
- package/src/skills/skill-routing.js +340 -0
- package/src/state/lifecycle-manager.js +113 -0
- package/src/state/operational-state.js +198 -0
- package/src/state/projections.js +47 -0
- package/src/state/replay-engine.js +5 -0
- package/src/state/requirement-ownership.js +20 -0
- package/src/state/requirement-state.js +152 -0
- package/src/state/run-event-identity.js +172 -0
- package/src/state/state-engine.js +19 -0
- package/src/state/state-machine.js +119 -0
- package/src/state/state-schema.js +318 -0
- package/src/state/state-store.js +335 -0
- package/src/state/state-transition.js +84 -0
- package/src/state/task-dependencies.js +287 -0
- package/src/state/task-execution.js +217 -0
- package/src/state/task-lifecycle.js +59 -0
- package/src/state/task-runs.js +361 -0
- package/src/state/task-state-file.js +86 -0
- package/src/state/task-state.js +41 -0
- package/src/state/task-status.js +54 -0
- package/src/state/task-store.js +48 -0
- package/src/state/wam-state.js +148 -0
- package/src/verification/completion-gate-simple.js +8 -0
- package/src/verification/completion-gate.js +183 -0
- package/src/verification/contract.js +9 -0
- package/src/verification/contradiction-resolution.js +255 -0
- package/src/verification/diagnostic-engine.js +659 -0
- package/src/verification/false-completion-prevention.js +100 -0
- package/src/verification/hook-validator.js +6 -0
- package/src/verification/requirements.js +6 -0
- package/src/verification/verification-context.js +162 -0
- package/src/verification/verification-lifecycle.js +66 -0
- package/src/verification/verification-model.js +89 -0
- package/src/verification/verification-persistence.js +129 -0
- package/src/verification/verification-policy.js +127 -0
- package/src/verification/verification.js +415 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Rate-Distortion (C05 — context-rate-distortion).
|
|
3
|
+
*
|
|
4
|
+
* Combines Context Reduction Rate (CRR = tokensSaved / tokensFull) with
|
|
5
|
+
* Oracle Sufficiency verification (verifySufficiency) to compute the true
|
|
6
|
+
* rate-distortion curve:
|
|
7
|
+
*
|
|
8
|
+
* rate(b) = 1 - tokens(b) / tokensFull (compression ratio)
|
|
9
|
+
* distortion(b) = 1 - sufficient(b) (sufficiency loss)
|
|
10
|
+
*
|
|
11
|
+
* Where:
|
|
12
|
+
* tokensFull = sum of token counts of all candidates
|
|
13
|
+
* tokens(b) = sum of token counts of the selected set at budget b
|
|
14
|
+
* sufficient(b) = verifySufficiency(task, selected(b))
|
|
15
|
+
*
|
|
16
|
+
* The curve answers: "how aggressively can we compress while still being
|
|
17
|
+
* oracle-sufficient?"
|
|
18
|
+
*
|
|
19
|
+
* - RDReport.curve - [{rate, distortion, sufficient, budget, selected}]
|
|
20
|
+
* - RDReport.operatingPoint - first budget where sufficient===true (or null)
|
|
21
|
+
* - RDReport.maxRate - largest rate at which distortion===0
|
|
22
|
+
* - RDReport.crr - rate at the operating point (or max rate)
|
|
23
|
+
*
|
|
24
|
+
* Independence: uses oracle ground truth; does not consult scoring weights.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
computeRequiredClosure,
|
|
29
|
+
verifySufficiency,
|
|
30
|
+
} from "./context-sufficiency-oracle.js";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {Object} ContextItem
|
|
34
|
+
* @property {string} id
|
|
35
|
+
* @property {number} tokens
|
|
36
|
+
* @property {number} [priority] - higher = kept earlier under budget; ties broken by id
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {Object} RDPoint
|
|
41
|
+
* @property {number} budget
|
|
42
|
+
* @property {number} rate - in [0, 1]
|
|
43
|
+
* @property {number} distortion - in [0, 1] (0 = sufficient)
|
|
44
|
+
* @property {boolean} sufficient
|
|
45
|
+
* @property {string[]} selected
|
|
46
|
+
* @property {number} tokensUsed
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {Object} RDReport
|
|
51
|
+
* @property {string} taskId
|
|
52
|
+
* @property {number} fullTokens
|
|
53
|
+
* @property {RDPoint[]} curve
|
|
54
|
+
* @property {RDPoint|null} operatingPoint - first point with sufficient===true
|
|
55
|
+
* @property {number} crr - rate at operating point (or maxRate)
|
|
56
|
+
* @property {number} maxRate - largest rate where distortion===0
|
|
57
|
+
* @property {Object} stats
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Greedy fill: pick items in priority-descending order until budget exhausted.
|
|
62
|
+
*
|
|
63
|
+
* @param {ContextItem[]} candidates
|
|
64
|
+
* @param {number} budget
|
|
65
|
+
* @returns {string[]} selected ids
|
|
66
|
+
*/
|
|
67
|
+
function selectByBudget(candidates, budget) {
|
|
68
|
+
if (!Array.isArray(candidates) || budget <= 0) return [];
|
|
69
|
+
const sorted = candidates
|
|
70
|
+
.filter((c) => c && typeof c.id === "string" && typeof c.tokens === "number")
|
|
71
|
+
.slice()
|
|
72
|
+
.sort((a, b) => {
|
|
73
|
+
const pa = typeof a.priority === "number" ? a.priority : 0;
|
|
74
|
+
const pb = typeof b.priority === "number" ? b.priority : 0;
|
|
75
|
+
if (pb !== pa) return pb - pa;
|
|
76
|
+
return a.id.localeCompare(b.id);
|
|
77
|
+
});
|
|
78
|
+
const out = [];
|
|
79
|
+
let used = 0;
|
|
80
|
+
for (const c of sorted) {
|
|
81
|
+
if (used + c.tokens <= budget) {
|
|
82
|
+
out.push(c.id);
|
|
83
|
+
used += c.tokens;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Compute the rate-distortion curve.
|
|
91
|
+
*
|
|
92
|
+
* @param {Object} input
|
|
93
|
+
* @param {string} input.taskId
|
|
94
|
+
* @param {ContextItem[]} input.candidates
|
|
95
|
+
* @param {Object} input.oracleGraph
|
|
96
|
+
* @param {number[]} [input.budgets] - explicit budgets to sweep. If omitted,
|
|
97
|
+
* sweep is derived from candidates (each unique prefix budget).
|
|
98
|
+
* @returns {RDReport}
|
|
99
|
+
*/
|
|
100
|
+
export function computeRateDistortion(input) {
|
|
101
|
+
if (!input || typeof input !== "object") {
|
|
102
|
+
throw new TypeError("computeRateDistortion: input required");
|
|
103
|
+
}
|
|
104
|
+
const { taskId, candidates, oracleGraph } = input;
|
|
105
|
+
if (typeof taskId !== "string" || taskId.length === 0) {
|
|
106
|
+
throw new TypeError("computeRateDistortion: taskId required");
|
|
107
|
+
}
|
|
108
|
+
if (!Array.isArray(candidates)) {
|
|
109
|
+
throw new TypeError("computeRateDistortion: candidates must be an array");
|
|
110
|
+
}
|
|
111
|
+
if (!oracleGraph || typeof oracleGraph.getNode !== "function") {
|
|
112
|
+
throw new TypeError("computeRateDistortion: oracleGraph required");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const requiredClosure = computeRequiredClosure(taskId, oracleGraph);
|
|
116
|
+
const requiredSet = new Set(requiredClosure);
|
|
117
|
+
|
|
118
|
+
const fullTokens = candidates.reduce(
|
|
119
|
+
(acc, c) => acc + (typeof c.tokens === "number" ? c.tokens : 0),
|
|
120
|
+
0,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
let budgets = input.budgets;
|
|
124
|
+
if (!Array.isArray(budgets) || budgets.length === 0) {
|
|
125
|
+
// derive: 0, plus cumulative token sums in priority order
|
|
126
|
+
const sorted = candidates
|
|
127
|
+
.filter((c) => c && typeof c.tokens === "number")
|
|
128
|
+
.slice()
|
|
129
|
+
.sort((a, b) => {
|
|
130
|
+
const pa = typeof a.priority === "number" ? a.priority : 0;
|
|
131
|
+
const pb = typeof b.priority === "number" ? b.priority : 0;
|
|
132
|
+
if (pb !== pa) return pb - pa;
|
|
133
|
+
return a.id.localeCompare(b.id);
|
|
134
|
+
});
|
|
135
|
+
const derived = [0];
|
|
136
|
+
let acc = 0;
|
|
137
|
+
for (const c of sorted) {
|
|
138
|
+
acc += c.tokens;
|
|
139
|
+
if (acc !== derived[derived.length - 1]) derived.push(acc);
|
|
140
|
+
}
|
|
141
|
+
if (derived[derived.length - 1] !== fullTokens) derived.push(fullTokens);
|
|
142
|
+
budgets = derived;
|
|
143
|
+
}
|
|
144
|
+
budgets = [...new Set(budgets.filter((b) => typeof b === "number" && b >= 0))].sort((a, b) => a - b);
|
|
145
|
+
|
|
146
|
+
/** @type {RDPoint[]} */
|
|
147
|
+
const curve = [];
|
|
148
|
+
let operatingPoint = null;
|
|
149
|
+
let maxRate = 0;
|
|
150
|
+
|
|
151
|
+
for (const budget of budgets) {
|
|
152
|
+
const selected = selectByBudget(candidates, budget);
|
|
153
|
+
const verdict = verifySufficiency(taskId, oracleGraph, selected);
|
|
154
|
+
const tokensUsed = selected.reduce((acc, id) => {
|
|
155
|
+
const c = candidates.find((x) => x.id === id);
|
|
156
|
+
return acc + (c && typeof c.tokens === "number" ? c.tokens : 0);
|
|
157
|
+
}, 0);
|
|
158
|
+
const rate = fullTokens === 0 ? 1 : 1 - tokensUsed / fullTokens;
|
|
159
|
+
const distortion = verdict.sufficient ? 0 : 1;
|
|
160
|
+
const point = {
|
|
161
|
+
budget,
|
|
162
|
+
rate: clamp01(rate),
|
|
163
|
+
distortion: clamp01(distortion),
|
|
164
|
+
sufficient: verdict.sufficient,
|
|
165
|
+
selected,
|
|
166
|
+
tokensUsed,
|
|
167
|
+
};
|
|
168
|
+
curve.push(point);
|
|
169
|
+
if (operatingPoint === null && verdict.sufficient) {
|
|
170
|
+
operatingPoint = point;
|
|
171
|
+
}
|
|
172
|
+
if (verdict.sufficient && point.rate > maxRate) {
|
|
173
|
+
maxRate = point.rate;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Track which required nodes were ever included across any point
|
|
178
|
+
const everIncludedRequired = new Set();
|
|
179
|
+
for (const p of curve) {
|
|
180
|
+
for (const id of p.selected) if (requiredSet.has(id)) everIncludedRequired.add(id);
|
|
181
|
+
}
|
|
182
|
+
const unreachableRequired = requiredClosure.filter((id) => !everIncludedRequired.has(id));
|
|
183
|
+
|
|
184
|
+
const crr = operatingPoint ? operatingPoint.rate : (curve.length ? curve[curve.length - 1].rate : 0);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
taskId,
|
|
188
|
+
fullTokens,
|
|
189
|
+
curve,
|
|
190
|
+
operatingPoint,
|
|
191
|
+
crr: clamp01(crr),
|
|
192
|
+
maxRate: clamp01(maxRate),
|
|
193
|
+
stats: {
|
|
194
|
+
pointCount: curve.length,
|
|
195
|
+
requiredCount: requiredClosure.length,
|
|
196
|
+
unreachableRequiredCount: unreachableRequired.length,
|
|
197
|
+
unreachableRequired,
|
|
198
|
+
sufficientPoints: curve.filter((p) => p.sufficient).length,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Convenience: Context Reduction Rate at a specific budget.
|
|
205
|
+
*
|
|
206
|
+
* @param {RDReport} report
|
|
207
|
+
* @param {number} budget
|
|
208
|
+
* @returns {number} CRR in [0, 1]; 0 if budget not in curve.
|
|
209
|
+
*/
|
|
210
|
+
export function crrAt(report, budget) {
|
|
211
|
+
if (!report || !Array.isArray(report.curve)) return 0;
|
|
212
|
+
const point = report.curve.find((p) => p.budget === budget);
|
|
213
|
+
if (!point) return 0;
|
|
214
|
+
return point.rate;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Convenience: distortion at a specific budget.
|
|
219
|
+
*
|
|
220
|
+
* @param {RDReport} report
|
|
221
|
+
* @param {number} budget
|
|
222
|
+
* @returns {number} distortion in [0, 1]; 1 if budget not in curve.
|
|
223
|
+
*/
|
|
224
|
+
export function distortionAt(report, budget) {
|
|
225
|
+
if (!report || !Array.isArray(report.curve)) return 1;
|
|
226
|
+
const point = report.curve.find((p) => p.budget === budget);
|
|
227
|
+
if (!point) return 1;
|
|
228
|
+
return point.distortion;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Aggregate rate-distortion across multiple tasks into a mean curve.
|
|
233
|
+
*
|
|
234
|
+
* @param {RDReport[]} reports
|
|
235
|
+
* @returns {{meanCrr: number, meanMaxRate: number, pointCount: number}}
|
|
236
|
+
*/
|
|
237
|
+
export function aggregateRateDistortion(reports) {
|
|
238
|
+
if (!Array.isArray(reports) || reports.length === 0) {
|
|
239
|
+
return { meanCrr: 0, meanMaxRate: 0, pointCount: 0 };
|
|
240
|
+
}
|
|
241
|
+
let crrSum = 0;
|
|
242
|
+
let maxSum = 0;
|
|
243
|
+
let pts = 0;
|
|
244
|
+
for (const r of reports) {
|
|
245
|
+
if (typeof r.crr === "number" && !Number.isNaN(r.crr)) crrSum += r.crr;
|
|
246
|
+
if (typeof r.maxRate === "number" && !Number.isNaN(r.maxRate)) maxSum += r.maxRate;
|
|
247
|
+
pts += r && Array.isArray(r.curve) ? r.curve.length : 0;
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
meanCrr: crrSum / reports.length,
|
|
251
|
+
meanMaxRate: maxSum / reports.length,
|
|
252
|
+
pointCount: pts,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function clamp01(n) {
|
|
257
|
+
if (typeof n !== "number" || Number.isNaN(n)) return 0;
|
|
258
|
+
if (n < 0) return 0;
|
|
259
|
+
if (n > 1) return 1;
|
|
260
|
+
return n;
|
|
261
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Recovery — Reconstruct minimum corrective context when agent drifts.
|
|
3
|
+
*
|
|
4
|
+
* Change 78: reconstructMinimumCorrectiveContext()
|
|
5
|
+
*
|
|
6
|
+
* When an agent action has drifted from the goal, this module
|
|
7
|
+
* rebuilds the minimal context needed to get back on track.
|
|
8
|
+
*
|
|
9
|
+
* The recovery context includes:
|
|
10
|
+
* - The original goal/objective
|
|
11
|
+
* - What has already been accomplished (verified items)
|
|
12
|
+
* - The most recent relevant observations
|
|
13
|
+
* - Constraints that must be respected
|
|
14
|
+
* - Open questions that need resolution
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} RecoveryContext
|
|
19
|
+
* @property {string} goal - The original goal
|
|
20
|
+
* @property {Object[]} accomplished - Verified/accomplished items
|
|
21
|
+
* @property {Object[]} recentObservations - Most recent relevant observations
|
|
22
|
+
* @property {Object[]} constraints - Active constraints
|
|
23
|
+
* @property {Object[]} openQuestions - Questions needing resolution
|
|
24
|
+
* @property {string} driftReason - Why recovery was needed
|
|
25
|
+
* @property {number} timestamp - When recovery context was built
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Object} DriftReport
|
|
30
|
+
* @property {boolean} drifted - Whether drift was detected
|
|
31
|
+
* @property {string} reason - Description of the drift
|
|
32
|
+
* @property {number} confidence - 0-1 confidence of drift detection
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Simple keyword check: does the content relate to the goal?
|
|
37
|
+
* @param {string} content
|
|
38
|
+
* @param {string} goal
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
function relatesToGoal(content, goal) {
|
|
42
|
+
if (!content || !goal) return false;
|
|
43
|
+
if (content === goal) return true;
|
|
44
|
+
const contentLower = String(content).toLowerCase();
|
|
45
|
+
const goalWords = String(goal).toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
46
|
+
if (goalWords.length === 0) return true;
|
|
47
|
+
const matches = goalWords.filter((w) => contentLower.includes(w));
|
|
48
|
+
return matches.length >= Math.ceil(goalWords.length / 2);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Change 78 — reconstructMinimumCorrectiveContext
|
|
53
|
+
*
|
|
54
|
+
* Builds a minimal context from available data when agent has drifted.
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} params
|
|
57
|
+
* @param {string} params.goal - The original goal/objective
|
|
58
|
+
* @param {Object[]} [params.accomplished=[]] - Already completed/verified items
|
|
59
|
+
* @param {Object[]} [params.observations=[]] - Recent observations
|
|
60
|
+
* @param {Object[]} [params.constraints=[]] - Active constraints
|
|
61
|
+
* @param {Object[]} [params.openQuestions=[]] - Unresolved questions
|
|
62
|
+
* @param {Object[]} [params.recentActions=[]] - Recent actions taken
|
|
63
|
+
* @param {string} params.driftReason - Why recovery is needed
|
|
64
|
+
* @param {number} [params.maxObservations=5] - Max observations to include
|
|
65
|
+
* @param {number} [params.maxAccomplished=10] - Max accomplished items
|
|
66
|
+
* @returns {RecoveryContext}
|
|
67
|
+
*/
|
|
68
|
+
export function reconstructMinimumCorrectiveContext({
|
|
69
|
+
goal,
|
|
70
|
+
accomplished = [],
|
|
71
|
+
observations = [],
|
|
72
|
+
constraints = [],
|
|
73
|
+
openQuestions = [],
|
|
74
|
+
recentActions = [],
|
|
75
|
+
driftReason,
|
|
76
|
+
maxObservations = 5,
|
|
77
|
+
maxAccomplished = 10,
|
|
78
|
+
}) {
|
|
79
|
+
if (!goal || typeof goal !== "string") {
|
|
80
|
+
throw new TypeError("goal must be a non-empty string");
|
|
81
|
+
}
|
|
82
|
+
if (!driftReason || typeof driftReason !== "string") {
|
|
83
|
+
throw new TypeError("driftReason must be a non-empty string");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Accomplished: verified items, most recent first
|
|
87
|
+
const sortedAccomplished = [...accomplished]
|
|
88
|
+
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
|
|
89
|
+
.slice(0, maxAccomplished);
|
|
90
|
+
|
|
91
|
+
// Observations: filter for goal relevance, most recent first
|
|
92
|
+
const relevantObservations = observations
|
|
93
|
+
.filter((obs) => relatesToGoal(obs.content || "", goal))
|
|
94
|
+
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
|
|
95
|
+
.slice(0, maxObservations);
|
|
96
|
+
|
|
97
|
+
// If not enough goal-relevant observations, include recent actions as context
|
|
98
|
+
if (relevantObservations.length < 2 && recentActions.length > 0) {
|
|
99
|
+
const actionContext = recentActions
|
|
100
|
+
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
|
|
101
|
+
.slice(0, Math.max(1, maxObservations - relevantObservations.length));
|
|
102
|
+
// Blend: alternate between observations and actions
|
|
103
|
+
const blended = [];
|
|
104
|
+
const maxLen = Math.max(relevantObservations.length, actionContext.length);
|
|
105
|
+
for (let i = 0; i < maxLen; i++) {
|
|
106
|
+
if (relevantObservations[i]) blended.push(relevantObservations[i]);
|
|
107
|
+
if (actionContext[i]) blended.push(actionContext[i]);
|
|
108
|
+
}
|
|
109
|
+
return buildRecovery({
|
|
110
|
+
goal,
|
|
111
|
+
accomplished: sortedAccomplished,
|
|
112
|
+
observations: blended.slice(0, maxObservations),
|
|
113
|
+
constraints,
|
|
114
|
+
openQuestions,
|
|
115
|
+
driftReason,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return buildRecovery({
|
|
120
|
+
goal,
|
|
121
|
+
accomplished: sortedAccomplished,
|
|
122
|
+
observations: relevantObservations,
|
|
123
|
+
constraints,
|
|
124
|
+
openQuestions,
|
|
125
|
+
driftReason,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {Object} parts
|
|
131
|
+
* @returns {RecoveryContext}
|
|
132
|
+
*/
|
|
133
|
+
function buildRecovery({ goal, accomplished, observations, constraints, openQuestions, driftReason }) {
|
|
134
|
+
return {
|
|
135
|
+
goal,
|
|
136
|
+
accomplished: accomplished || [],
|
|
137
|
+
recentObservations: observations || [],
|
|
138
|
+
constraints: constraints || [],
|
|
139
|
+
openQuestions: openQuestions || [],
|
|
140
|
+
driftReason,
|
|
141
|
+
timestamp: Date.now(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Quick check: does the agent action represent a drift from the goal?
|
|
147
|
+
*
|
|
148
|
+
* @param {Object} agentAction - The action taken by the agent
|
|
149
|
+
* @param {string} goal - The original goal
|
|
150
|
+
* @returns {DriftReport}
|
|
151
|
+
*/
|
|
152
|
+
export function detectActionDrift(agentAction, goal) {
|
|
153
|
+
if (!agentAction || typeof agentAction !== "object") {
|
|
154
|
+
return { drifted: false, reason: "No action provided", confidence: 0 };
|
|
155
|
+
}
|
|
156
|
+
if (!goal || typeof goal !== "string") {
|
|
157
|
+
return { drifted: false, reason: "No goal provided", confidence: 0 };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const actionContent = String(agentAction.content || agentAction.description || "").toLowerCase();
|
|
161
|
+
const actionType = String(agentAction.type || "").toLowerCase();
|
|
162
|
+
|
|
163
|
+
// Direct match: action type or content explicitly references goal
|
|
164
|
+
const directMatch = actionContent.includes(goal.toLowerCase()) ||
|
|
165
|
+
goal.toLowerCase().includes(actionContent) ||
|
|
166
|
+
actionContent === goal.toLowerCase();
|
|
167
|
+
|
|
168
|
+
if (directMatch) {
|
|
169
|
+
return { drifted: false, reason: "Action directly related to goal", confidence: 0.95 };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Check for verb/topic overlap
|
|
173
|
+
const goalWords = goal.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
174
|
+
const contentWords = actionContent.split(/\s+/).filter((w) => w.length > 2);
|
|
175
|
+
const overlap = goalWords.filter((w) => contentWords.includes(w)).length;
|
|
176
|
+
const overlapRatio = goalWords.length > 0 ? overlap / goalWords.length : 0;
|
|
177
|
+
|
|
178
|
+
if (overlapRatio >= 0.3) {
|
|
179
|
+
return { drifted: false, reason: "Action has topic overlap with goal", confidence: 0.7 };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Known drift indicators
|
|
183
|
+
const driftIndicators = [
|
|
184
|
+
"exploring", "wandering", "asking clarification", "need more info",
|
|
185
|
+
"unclear", "confused", "redundant", "waiting", "idle",
|
|
186
|
+
];
|
|
187
|
+
const isDrift = driftIndicators.some((ind) => actionContent.includes(ind));
|
|
188
|
+
|
|
189
|
+
if (isDrift) {
|
|
190
|
+
return {
|
|
191
|
+
drifted: true,
|
|
192
|
+
reason: `Action contains drift indicator: "${actionContent.substring(0, 60)}"`,
|
|
193
|
+
confidence: 0.75,
|
|
194
|
+
signals: [actionContent],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Low overlap, no drift indicators = likely drift but low confidence
|
|
199
|
+
if (overlapRatio < 0.1 && actionContent.length > 0) {
|
|
200
|
+
return {
|
|
201
|
+
drifted: true,
|
|
202
|
+
reason: `Low topic overlap (${Math.round(overlapRatio * 100)}%) between action and goal`,
|
|
203
|
+
confidence: 0.5,
|
|
204
|
+
signals: [`low_overlap:${Math.round(overlapRatio * 100)}%`],
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { drifted: false, reason: "Action appears goal-aligned", confidence: 0.8 };
|
|
209
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { retrieveContext } from "./context-retrieval.js";
|
|
2
|
+
import { retrieveActiveContext, isContextSufficient } from "./active-context-boundary.js";
|
|
3
|
+
import { buildContextQuery, matchesQuery, scoreRelevance, retrieveRelevantContext, CONTEXT_PURPOSE } from "./context-query-contract.js";
|
|
4
|
+
import { LIFECYCLE_STATES } from "./context-lifecycle.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* High-level retrieval that integrates ContextManager with query contract and active boundary.
|
|
8
|
+
* @param {Object} manager - ContextManager instance (must have registry.list())
|
|
9
|
+
* @param {Object} query - Output from buildContextQuery() or compatible object
|
|
10
|
+
* @param {Object} options
|
|
11
|
+
* @param {number} [options.maxResults=50]
|
|
12
|
+
* @param {boolean} [options.includeHistory=false] - include INVALIDATED/ARCHIVED
|
|
13
|
+
* @returns {{relevantItems: ManagedItem[], sufficiencyCheck: Object, metadata: Object}}
|
|
14
|
+
*/
|
|
15
|
+
export function integrateRetrieval(manager, query, options = {}) {
|
|
16
|
+
const { maxResults = 50, includeHistory = false } = options;
|
|
17
|
+
const startTime = Date.now();
|
|
18
|
+
|
|
19
|
+
if (!manager || typeof manager.registry?.list !== "function") {
|
|
20
|
+
throw new TypeError("integrateRetrieval requires a manager with registry.list()");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const allItems = manager.registry.list();
|
|
24
|
+
|
|
25
|
+
let relevantItems = retrieveRelevantContext(allItems, query);
|
|
26
|
+
|
|
27
|
+
if (!includeHistory) {
|
|
28
|
+
relevantItems = retrieveActiveContext(relevantItems, query.purpose);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (maxResults > 0 && relevantItems.length > maxResults) {
|
|
32
|
+
relevantItems = relevantItems.slice(0, maxResults);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const sufficiencyCheck = isContextSufficient(relevantItems, query.purpose);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
relevantItems,
|
|
39
|
+
sufficiencyCheck,
|
|
40
|
+
metadata: {
|
|
41
|
+
totalItems: allItems.length,
|
|
42
|
+
retrievedCount: relevantItems.length,
|
|
43
|
+
purpose: query.purpose,
|
|
44
|
+
elapsedMs: Date.now() - startTime,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Convenience: build query and retrieve in one call.
|
|
51
|
+
* @param {Object} manager - ContextManager instance
|
|
52
|
+
* @param {Object} params - buildContextQuery parameters
|
|
53
|
+
* @param {Object} options - integrateRetrieval options
|
|
54
|
+
*/
|
|
55
|
+
export function retrieveForContext(manager, params, options = {}) {
|
|
56
|
+
const query = buildContextQuery(params);
|
|
57
|
+
return integrateRetrieval(manager, query, options);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { CONTEXT_PURPOSE, buildContextQuery, matchesQuery, scoreRelevance, retrieveRelevantContext, retrieveActiveContext, isContextSufficient };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Retrieval — retrieveContext(query, constraints).
|
|
3
|
+
*
|
|
4
|
+
* Change 69: Retrieves context items from a ContextManager/registry
|
|
5
|
+
* based on requirement relevance. Uses ranking from context-ranking.js
|
|
6
|
+
* and respects constraint filters (layer, lifecycle, admission).
|
|
7
|
+
*
|
|
8
|
+
* Invariant: Returns results ordered by relevance score (descending).
|
|
9
|
+
* Items that don't satisfy mandatory constraints are excluded.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { rankContextItems } from "./context-ranking.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} RetrievalConstraints
|
|
16
|
+
* @property {string[]} [layers] - Filter by memory layers (N0..N3, ARCHIVE).
|
|
17
|
+
* @property {string[]} [lifecycle] - Filter by lifecycle states.
|
|
18
|
+
* @property {string[]} [admission] - Filter by admission class.
|
|
19
|
+
* @property {number} [maxResults] - Max items to return.
|
|
20
|
+
* @property {number} [minScore] - Minimum relevance score threshold.
|
|
21
|
+
* @property {string[]} [requiredTerms] - Terms that must appear.
|
|
22
|
+
* @property {string[]} [excludedIds] - Item IDs to exclude.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {Object} RetrievalResult
|
|
27
|
+
* @property {Array} items - Ranked context items.
|
|
28
|
+
* @property {number} total - Total items before limiting.
|
|
29
|
+
* @property {number} returned - Number of items returned.
|
|
30
|
+
* @property {number} elapsedMs - Query execution time.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves and ranks context items based on query relevance and constraints.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} query - Search query / requirement text.
|
|
37
|
+
* @param {RetrievalConstraints} [constraints={}] - Filter and limit options.
|
|
38
|
+
* @returns {RetrievalResult}
|
|
39
|
+
*/
|
|
40
|
+
export function retrieveContext(query, constraints = {}) {
|
|
41
|
+
const startTime = Date.now();
|
|
42
|
+
const {
|
|
43
|
+
layers,
|
|
44
|
+
lifecycle,
|
|
45
|
+
admission,
|
|
46
|
+
maxResults = 50,
|
|
47
|
+
minScore = 0,
|
|
48
|
+
requiredTerms = [],
|
|
49
|
+
excludedIds = [],
|
|
50
|
+
} = constraints;
|
|
51
|
+
|
|
52
|
+
// Admission class filter (MANDATORY/CONDITIONAL/OPTIONAL)
|
|
53
|
+
const admissionFilter = (item) => {
|
|
54
|
+
if (!admission || !admission.length) return true;
|
|
55
|
+
const itemAdmission = item.admission || item.admissionClass || "OPTIONAL";
|
|
56
|
+
return admission.includes(itemAdmission);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Retrieve from ContextManager if provided, or use registry directly.
|
|
60
|
+
// Note: `manager` is used for API compatibility (not a standard property).
|
|
61
|
+
const registry = constraints.registry || constraints.manager;
|
|
62
|
+
let items = [];
|
|
63
|
+
|
|
64
|
+
if (registry) {
|
|
65
|
+
const allItems = registry.list ? registry.list() : Object.values(registry);
|
|
66
|
+
items = allItems.filter((item) => {
|
|
67
|
+
if (layers && !layers.includes(item.memoryLayer)) return false;
|
|
68
|
+
if (lifecycle && !lifecycle.includes(item.lifecycle)) return false;
|
|
69
|
+
if (excludedIds.includes(item.id)) return false;
|
|
70
|
+
if (!admissionFilter(item)) return false;
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Rank by relevance to query
|
|
76
|
+
const ranked = rankContextItems(items, query, { requiredTerms });
|
|
77
|
+
|
|
78
|
+
// Apply min score filter
|
|
79
|
+
const filtered = ranked.filter((item) => {
|
|
80
|
+
const score = item.relevanceScore ?? 0;
|
|
81
|
+
return score >= minScore;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const total = filtered.length;
|
|
85
|
+
const result = filtered.slice(0, maxResults);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
items: result,
|
|
89
|
+
total,
|
|
90
|
+
returned: result.length,
|
|
91
|
+
elapsedMs: Date.now() - startTime,
|
|
92
|
+
};
|
|
93
|
+
}
|