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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Budget Manager — N0-N3/Archive Budget Tracking & Enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Change 72: Tracks token budgets per memory layer, enforces limits,
|
|
5
|
+
* reports violations, and suggests eviction priorities.
|
|
6
|
+
*
|
|
7
|
+
* Budget model:
|
|
8
|
+
* - Each layer (N0, N1, N2, N3, ARCHIVE) has a max token budget.
|
|
9
|
+
* - Items are tracked with their estimated token cost.
|
|
10
|
+
* - Budget violations are reported with remediation suggestions.
|
|
11
|
+
* - N0 is always protected (minimum guaranteed budget).
|
|
12
|
+
*
|
|
13
|
+
* Invariant: Total budget across layers >= reserved + flex from assembly.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Default tier budgets in tokens (approximate). */
|
|
17
|
+
const DEFAULT_BUDGETS = Object.freeze({
|
|
18
|
+
N0: 200, // Identity/goal — tiny but mandatory
|
|
19
|
+
N1: 800, // Domain/project context
|
|
20
|
+
N2: 500, // Current task state/evidence
|
|
21
|
+
N3: 1500, // Session/capsule retrieval layer
|
|
22
|
+
ARCHIVE: 4000, // Historical/compressed
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/** Minimum guaranteed budgets (cannot be reduced). */
|
|
26
|
+
const MINIMUM_BUDGETS = Object.freeze({
|
|
27
|
+
N0: 100,
|
|
28
|
+
N1: 100,
|
|
29
|
+
N2: 100,
|
|
30
|
+
N3: 200,
|
|
31
|
+
ARCHIVE: 500,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const LAYER_ORDER = ["N0", "N1", "N2", "N3", "ARCHIVE"];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ContextBudgetManager tracks and enforces token budgets per memory layer.
|
|
38
|
+
*/
|
|
39
|
+
export class ContextBudgetManager {
|
|
40
|
+
/**
|
|
41
|
+
* @param {Object} [options]
|
|
42
|
+
* @param {Object} [options.budgets] - Custom max budgets per layer.
|
|
43
|
+
* @param {Object} [options.minimums] - Custom minimum budgets per layer.
|
|
44
|
+
*/
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
this._budgets = { ...DEFAULT_BUDGETS, ...(options.budgets || {}) };
|
|
47
|
+
this._minimums = { ...MINIMUM_BUDGETS, ...(options.minimums || {}) };
|
|
48
|
+
/** @type {Map<string, {tokens: number, itemId: string, admission: string}>} */
|
|
49
|
+
this._items = new Map();
|
|
50
|
+
/** @type {Array<{layer: string, action: string, detail: string, timestamp: number}>} */
|
|
51
|
+
this._auditLog = [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------
|
|
55
|
+
// Budget configuration
|
|
56
|
+
// ---------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
/** Get max budget for a layer. */
|
|
59
|
+
getBudget(layer) {
|
|
60
|
+
return this._budgets[layer] ?? 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Get minimum guaranteed budget for a layer. */
|
|
64
|
+
getMinimum(layer) {
|
|
65
|
+
return this._minimums[layer] ?? 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Get all budgets. */
|
|
69
|
+
getAllBudgets() {
|
|
70
|
+
return { ...this._budgets };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Set budget for a layer. */
|
|
74
|
+
setBudget(layer, tokens) {
|
|
75
|
+
if (tokens < this.getMinimum(layer)) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Budget for ${layer} (${tokens}) below minimum (${this.getMinimum(layer)})`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
this._budgets[layer] = tokens;
|
|
81
|
+
this._log(layer, "budget_updated", `Budget set to ${tokens}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------
|
|
85
|
+
// Item tracking
|
|
86
|
+
// ---------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
/** Register an item with its token cost in a layer. */
|
|
89
|
+
addItem(layer, itemId, tokens, admission = "OPTIONAL") {
|
|
90
|
+
if (!LAYER_ORDER.includes(layer)) {
|
|
91
|
+
throw new Error(`Unknown layer: ${layer}`);
|
|
92
|
+
}
|
|
93
|
+
this._items.set(itemId, { layer, tokens, itemId, admission });
|
|
94
|
+
this._log(layer, "item_added", `${itemId}: ${tokens} tok (${admission})`);
|
|
95
|
+
return this.getLayerUsage(layer);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Remove an item from tracking. */
|
|
99
|
+
removeItem(itemId) {
|
|
100
|
+
const item = this._items.get(itemId);
|
|
101
|
+
if (item) {
|
|
102
|
+
this._items.delete(itemId);
|
|
103
|
+
this._log(item.layer, "item_removed", itemId);
|
|
104
|
+
}
|
|
105
|
+
return item;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Get all tracked items for a layer. */
|
|
109
|
+
getItems(layer) {
|
|
110
|
+
return Array.from(this._items.values()).filter((i) => i.layer === layer);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Get all tracked items. */
|
|
114
|
+
getAllItems() {
|
|
115
|
+
return Array.from(this._items.values());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------
|
|
119
|
+
// Usage & enforcement
|
|
120
|
+
// ---------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
/** Get current token usage for a layer. */
|
|
123
|
+
getLayerUsage(layer) {
|
|
124
|
+
let used = 0;
|
|
125
|
+
for (const item of this._items.values()) {
|
|
126
|
+
if (item.layer === layer) used += item.tokens;
|
|
127
|
+
}
|
|
128
|
+
const budget = this.getBudget(layer);
|
|
129
|
+
return {
|
|
130
|
+
layer,
|
|
131
|
+
used,
|
|
132
|
+
budget,
|
|
133
|
+
remaining: Math.max(0, budget - used),
|
|
134
|
+
utilization: budget > 0 ? used / budget : 0,
|
|
135
|
+
overBudget: used > budget,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Get usage for all layers. */
|
|
140
|
+
getAllUsage() {
|
|
141
|
+
return LAYER_ORDER.map((layer) => this.getLayerUsage(layer));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Get total tokens across all layers. */
|
|
145
|
+
getTotalUsage() {
|
|
146
|
+
let total = 0;
|
|
147
|
+
for (const item of this._items.values()) total += item.tokens;
|
|
148
|
+
return total;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Check for budget violations and return report. */
|
|
152
|
+
checkViolations() {
|
|
153
|
+
const violations = [];
|
|
154
|
+
for (const layer of LAYER_ORDER) {
|
|
155
|
+
const usage = this.getLayerUsage(layer);
|
|
156
|
+
if (usage.overBudget) {
|
|
157
|
+
const excess = usage.used - usage.budget;
|
|
158
|
+
const minimumProtected = usage.used > this.getMinimum(layer)
|
|
159
|
+
? this.getMinimum(layer)
|
|
160
|
+
: usage.used;
|
|
161
|
+
violations.push({
|
|
162
|
+
layer,
|
|
163
|
+
severity: this.getMinimum(layer) > 0 && usage.used <= this.getMinimum(layer)
|
|
164
|
+
? "SOFT"
|
|
165
|
+
: "HARD",
|
|
166
|
+
overBy: excess,
|
|
167
|
+
used: usage.used,
|
|
168
|
+
budget: usage.budget,
|
|
169
|
+
minimum: this.getMinimum(layer),
|
|
170
|
+
suggestion: this._suggestEviction(layer, excess),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
violations,
|
|
176
|
+
clean: violations.length === 0,
|
|
177
|
+
totalOverBudget: violations.reduce((sum, v) => sum + v.overBy, 0),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Suggest items to evict from a layer to meet budget. */
|
|
182
|
+
_suggestEviction(layer, excessTokens) {
|
|
183
|
+
const items = this.getItems(layer).sort((a, b) => a.tokens - b.tokens);
|
|
184
|
+
const suggestions = [];
|
|
185
|
+
let freed = 0;
|
|
186
|
+
for (const item of items) {
|
|
187
|
+
if (freed >= excessTokens) break;
|
|
188
|
+
suggestions.push({ itemId: item.itemId, tokens: item.tokens, admission: item.admission });
|
|
189
|
+
freed += item.tokens;
|
|
190
|
+
}
|
|
191
|
+
return suggestions;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ---------------------------------------------------------------
|
|
195
|
+
// Audit & reporting
|
|
196
|
+
// ---------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
/** Get audit log entries. */
|
|
199
|
+
getAuditLog() {
|
|
200
|
+
return [...this._auditLog];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Generate a budget report summary. */
|
|
204
|
+
report() {
|
|
205
|
+
const allUsage = this.getAllUsage();
|
|
206
|
+
const check = this.checkViolations();
|
|
207
|
+
return {
|
|
208
|
+
layer: allUsage,
|
|
209
|
+
layers: allUsage,
|
|
210
|
+
totalUsed: this.getTotalUsage(),
|
|
211
|
+
totalBudget: Object.values(this._budgets).reduce((a, b) => a + b, 0),
|
|
212
|
+
violations: check.violations,
|
|
213
|
+
violationReport: check,
|
|
214
|
+
timestamp: Date.now(),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
_log(layer, action, detail) {
|
|
219
|
+
this._auditLog.push({
|
|
220
|
+
layer,
|
|
221
|
+
action,
|
|
222
|
+
detail,
|
|
223
|
+
timestamp: Date.now(),
|
|
224
|
+
});
|
|
225
|
+
// Keep audit log manageable
|
|
226
|
+
if (this._auditLog.length > 10000) {
|
|
227
|
+
this._auditLog = this._auditLog.slice(-5000);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export { DEFAULT_BUDGETS, MINIMUM_BUDGETS, LAYER_ORDER };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Builder.
|
|
3
|
+
* Assembles a context artifact from various sources with freshness, promotion, and budget rules.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isStale, updateLastAccess } from "./context-freshness.js";
|
|
7
|
+
|
|
8
|
+
export function build({ context = [], memory = [], decision = [], query = {}, options = {} }) {
|
|
9
|
+
const { maxItems = 10, ttl = 60000 } = options;
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
|
|
12
|
+
// Combine and deduplicate
|
|
13
|
+
const allItems = [...context, ...memory, ...decision];
|
|
14
|
+
const uniqueItems = Array.from(new Map(allItems.map(item => [item.id, item])).values());
|
|
15
|
+
|
|
16
|
+
// Apply freshness / staleness / ttl
|
|
17
|
+
const freshItems = uniqueItems
|
|
18
|
+
.filter(item => !isStale(item, ttl, now))
|
|
19
|
+
.map(item => updateLastAccess(item, now));
|
|
20
|
+
|
|
21
|
+
// Promotion rules (e.g., items mentioned in query get promoted)
|
|
22
|
+
const promotedItems = freshItems.map(item => ({
|
|
23
|
+
...item,
|
|
24
|
+
isPromoted: query.keywords?.some(kw => item.content?.includes(kw)) || false,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
// Budget enforcement
|
|
28
|
+
// Sort by promoted first, then lastAccess
|
|
29
|
+
const sortedItems = promotedItems.sort((a, b) => {
|
|
30
|
+
if (a.isPromoted !== b.isPromoted) return a.isPromoted ? -1 : 1;
|
|
31
|
+
return b.lastAccess - a.lastAccess;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const finalItems = sortedItems.slice(0, maxItems);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
structure: "artifact",
|
|
38
|
+
items: finalItems,
|
|
39
|
+
provenance: {
|
|
40
|
+
source: "context-builder",
|
|
41
|
+
timestamp: now,
|
|
42
|
+
method: "build",
|
|
43
|
+
},
|
|
44
|
+
metadata: {
|
|
45
|
+
totalInput: allItems.length,
|
|
46
|
+
finalCount: finalItems.length,
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export function createContextCache({ ttlMs = 60_000, maxEntries = 100 } = {}) {
|
|
2
|
+
const store = new Map(); // key -> { value, expiresAt }
|
|
3
|
+
|
|
4
|
+
function get(key) {
|
|
5
|
+
const entry = store.get(key);
|
|
6
|
+
if (!entry) return undefined;
|
|
7
|
+
if (Date.now() > entry.expiresAt) {
|
|
8
|
+
store.delete(key);
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return entry.value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function set(key, value, customTtlMs) {
|
|
15
|
+
const ttl = customTtlMs ?? ttlMs;
|
|
16
|
+
if (store.size >= maxEntries && !store.has(key)) {
|
|
17
|
+
// Evict oldest insertion (Map preserves insertion order).
|
|
18
|
+
const oldestKey = store.keys().next().value;
|
|
19
|
+
if (oldestKey !== undefined) store.delete(oldestKey);
|
|
20
|
+
}
|
|
21
|
+
store.set(key, { value, expiresAt: Date.now() + ttl });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function has(key) {
|
|
25
|
+
return get(key) !== undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function deleteKey(key) {
|
|
29
|
+
return store.delete(key);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function clear() {
|
|
33
|
+
store.clear();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function size() {
|
|
37
|
+
// Lazy prune expired entries before counting.
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
for (const [k, v] of store) {
|
|
40
|
+
if (now > v.expiresAt) store.delete(k);
|
|
41
|
+
}
|
|
42
|
+
return store.size;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function keys() {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const [k, v] of store) {
|
|
49
|
+
if (now <= v.expiresAt) out.push(k);
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { get, set, has, delete: deleteKey, clear, size, keys };
|
|
55
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Capture — mechanisms for capturing context from diverse sources.
|
|
3
|
+
*
|
|
4
|
+
* Captures: tool calls, agent messages, file modifications, git changes,
|
|
5
|
+
* requirement changes, and verification results.
|
|
6
|
+
*
|
|
7
|
+
* Each capture produces a raw record with: id, type, content, source,
|
|
8
|
+
* timestamp, scope, metadata.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import crypto from "node:crypto";
|
|
12
|
+
import { classifyContextItem } from "./context-classification.js";
|
|
13
|
+
import { scopeContextItem } from "./context-scope.js";
|
|
14
|
+
|
|
15
|
+
let _idCounter = 0;
|
|
16
|
+
|
|
17
|
+
function genId(prefix) {
|
|
18
|
+
_idCounter += 1;
|
|
19
|
+
return `${prefix}_${Date.now().toString(36)}${_idCounter.toString(36).padStart(4, "0")}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function now() {
|
|
23
|
+
return Date.now();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Attach classification and scope metadata to a capture record.
|
|
28
|
+
* @param {Object} record
|
|
29
|
+
*/
|
|
30
|
+
function attachContextMeta(record) {
|
|
31
|
+
if (!record) return;
|
|
32
|
+
record.classification = classifyContextItem(record);
|
|
33
|
+
record.scopeAssignment = scopeContextItem(record);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildRecord(type, content, source, scope, metadata = {}) {
|
|
37
|
+
return {
|
|
38
|
+
id: genId(`${type}_ctx`),
|
|
39
|
+
type,
|
|
40
|
+
content,
|
|
41
|
+
source,
|
|
42
|
+
timestamp: now(),
|
|
43
|
+
scope: scope || "session",
|
|
44
|
+
metadata,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// -- 1. Tool calls ---------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Capture a tool call invocation.
|
|
52
|
+
* @param {string} toolName - Name of the tool called
|
|
53
|
+
* @param {Object} params - Arguments passed to the tool
|
|
54
|
+
* @param {Object} [opts] - Optional scope/metadata
|
|
55
|
+
* @returns {Object} Capture record
|
|
56
|
+
*/
|
|
57
|
+
export function captureToolCall(toolName, params, opts = {}) {
|
|
58
|
+
const record = buildRecord(
|
|
59
|
+
"tool_call",
|
|
60
|
+
{ toolName, params },
|
|
61
|
+
"tool",
|
|
62
|
+
opts.scope || "session",
|
|
63
|
+
{ toolName, paramsSummary: JSON.stringify(params).slice(0, 500), ...opts.metadata },
|
|
64
|
+
);
|
|
65
|
+
attachContextMeta(record);
|
|
66
|
+
return record;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Capture a tool call result.
|
|
71
|
+
* @param {string} toolName
|
|
72
|
+
* @param {*} result - The result returned
|
|
73
|
+
* @param {Object} [opts]
|
|
74
|
+
* @returns {Object} Capture record
|
|
75
|
+
*/
|
|
76
|
+
export function captureToolResult(toolName, result, opts = {}) {
|
|
77
|
+
const record = buildRecord(
|
|
78
|
+
"tool_result",
|
|
79
|
+
{ toolName, result },
|
|
80
|
+
"tool",
|
|
81
|
+
opts.scope || "session",
|
|
82
|
+
{ toolName, resultSize: typeof result === "string" ? result.length : 0, ...opts.metadata },
|
|
83
|
+
);
|
|
84
|
+
attachContextMeta(record);
|
|
85
|
+
return record;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// -- 2. Agent messages -----------------------------------------------------
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Capture an agent message (user or assistant).
|
|
92
|
+
* @param {"user"|"assistant"} role
|
|
93
|
+
* @param {string} content - Message text
|
|
94
|
+
* @param {Object} [opts]
|
|
95
|
+
* @returns {Object} Capture record
|
|
96
|
+
*/
|
|
97
|
+
export function captureAgentMessage(role, content, opts = {}) {
|
|
98
|
+
if (!["user", "assistant"].includes(role)) {
|
|
99
|
+
throw new TypeError(`Invalid role: ${role}. Must be "user" or "assistant"`);
|
|
100
|
+
}
|
|
101
|
+
const record = buildRecord(
|
|
102
|
+
"agent_message",
|
|
103
|
+
{ role, content },
|
|
104
|
+
"agent",
|
|
105
|
+
opts.scope || "session",
|
|
106
|
+
{ role, contentLength: content.length, ...opts.metadata },
|
|
107
|
+
);
|
|
108
|
+
attachContextMeta(record);
|
|
109
|
+
return record;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Capture a system prompt fragment.
|
|
114
|
+
* @param {string} content
|
|
115
|
+
* @param {Object} [opts]
|
|
116
|
+
* @returns {Object} Capture record
|
|
117
|
+
*/
|
|
118
|
+
export function captureSystemMessage(content, opts = {}) {
|
|
119
|
+
const record = buildRecord(
|
|
120
|
+
"agent_message",
|
|
121
|
+
{ role: "system", content },
|
|
122
|
+
"agent",
|
|
123
|
+
opts.scope || "session",
|
|
124
|
+
{ role: "system", contentLength: content.length, ...opts.metadata },
|
|
125
|
+
);
|
|
126
|
+
attachContextMeta(record);
|
|
127
|
+
return record;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// -- 3. File modifications -------------------------------------------------
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Capture a file modification event.
|
|
134
|
+
* @param {string} filePath - Path to the file
|
|
135
|
+
* @param {"create"|"modify"|"delete"} action
|
|
136
|
+
* @param {Object} [details] - Optional diff/summary
|
|
137
|
+
* @param {Object} [opts]
|
|
138
|
+
* @returns {Object} Capture record
|
|
139
|
+
*/
|
|
140
|
+
export function captureFileModification(filePath, action, details = {}, opts = {}) {
|
|
141
|
+
if (!["create", "modify", "delete"].includes(action)) {
|
|
142
|
+
throw new TypeError(`Invalid action: ${action}. Must be create/modify/delete`);
|
|
143
|
+
}
|
|
144
|
+
const record = buildRecord(
|
|
145
|
+
"file_modification",
|
|
146
|
+
{ filePath, action, details },
|
|
147
|
+
"filesystem",
|
|
148
|
+
opts.scope || "session",
|
|
149
|
+
{ filePath, action, ...opts.metadata },
|
|
150
|
+
);
|
|
151
|
+
attachContextMeta(record);
|
|
152
|
+
return record;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// -- 4. Git changes --------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Capture a git change (commit, branch, merge, etc.).
|
|
159
|
+
* @param {"commit"|"branch"|"merge"|"rebase"|"reset"} action
|
|
160
|
+
* @param {Object} details - { hash, message, branch, filesChanged, ... }
|
|
161
|
+
* @param {Object} [opts]
|
|
162
|
+
* @returns {Object} Capture record
|
|
163
|
+
*/
|
|
164
|
+
export function captureGitChange(action, details, opts = {}) {
|
|
165
|
+
if (!["commit", "branch", "merge", "rebase", "reset"].includes(action)) {
|
|
166
|
+
throw new TypeError(`Invalid git action: ${action}`);
|
|
167
|
+
}
|
|
168
|
+
return buildRecord(
|
|
169
|
+
"git_change",
|
|
170
|
+
{ action, details },
|
|
171
|
+
"git",
|
|
172
|
+
opts.scope || "session",
|
|
173
|
+
{ action, commitHash: details.hash || null, ...opts.metadata },
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// -- 5. Requirement changes ------------------------------------------------
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Capture a requirement change (created, updated, closed).
|
|
181
|
+
* @param {"created"|"updated"|"closed"|"reopened"} action
|
|
182
|
+
* @param {Object} requirement - { id, title, description, ... }
|
|
183
|
+
* @param {Object} [opts]
|
|
184
|
+
* @returns {Object} Capture record
|
|
185
|
+
*/
|
|
186
|
+
export function captureRequirementChange(action, requirement, opts = {}) {
|
|
187
|
+
if (!["created", "updated", "closed", "reopened"].includes(action)) {
|
|
188
|
+
throw new TypeError(`Invalid requirement action: ${action}`);
|
|
189
|
+
}
|
|
190
|
+
const record = buildRecord(
|
|
191
|
+
"requirement_change",
|
|
192
|
+
{ action, requirement },
|
|
193
|
+
"requirement",
|
|
194
|
+
opts.scope || "session",
|
|
195
|
+
{
|
|
196
|
+
action,
|
|
197
|
+
requirementId: requirement.id || null,
|
|
198
|
+
requirementTitle: requirement.title || null,
|
|
199
|
+
...opts.metadata,
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
attachContextMeta(record);
|
|
203
|
+
return record;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// -- 6. Verification results -----------------------------------------------
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Capture a verification result.
|
|
210
|
+
* @param {"passed"|"failed"|"skipped"|"blocked"} status
|
|
211
|
+
* @param {Object} details - { testName, assertion, output, ... }
|
|
212
|
+
* @param {Object} [opts]
|
|
213
|
+
* @returns {Object} Capture record
|
|
214
|
+
*/
|
|
215
|
+
export function captureVerificationResult(status, details, opts = {}) {
|
|
216
|
+
if (!["passed", "failed", "skipped", "blocked"].includes(status)) {
|
|
217
|
+
throw new TypeError(`Invalid verification status: ${status}`);
|
|
218
|
+
}
|
|
219
|
+
const record = buildRecord(
|
|
220
|
+
"verification_result",
|
|
221
|
+
{ status, details },
|
|
222
|
+
"verification",
|
|
223
|
+
opts.scope || "session",
|
|
224
|
+
{ status, testName: details.testName || null, ...opts.metadata },
|
|
225
|
+
);
|
|
226
|
+
attachContextMeta(record);
|
|
227
|
+
return record;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// -- Batch -----------------------------------------------------------------
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Capture multiple records at once.
|
|
234
|
+
* @param {Object[]} records - Array of partial records to capture
|
|
235
|
+
* @returns {Object[]}
|
|
236
|
+
*/
|
|
237
|
+
export function captureBatch(records) {
|
|
238
|
+
return records.map((r) => {
|
|
239
|
+
const record = buildRecord(r.type, r.content, r.source, r.scope, r.metadata || {});
|
|
240
|
+
attachContextMeta(record);
|
|
241
|
+
return record;
|
|
242
|
+
});
|
|
243
|
+
}
|