opencode-wait-a-minute 0.0.0-stage → 1.1.0-rc.8

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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +318 -2
  3. package/index.js +1836 -0
  4. package/package.json +70 -4
  5. package/skills/codebase-design/SKILL.md +114 -0
  6. package/skills/design-contract/SKILL.md +27 -0
  7. package/skills/design-critique/SKILL.md +19 -0
  8. package/skills/design-system/SKILL.md +17 -0
  9. package/skills/design-system/references/image-analysis.md +14 -0
  10. package/skills/design-system/references/tokens.md +32 -0
  11. package/skills/design-system/references/ui-rules.md +14 -0
  12. package/skills/diagnosing-bugs/SKILL.md +138 -0
  13. package/skills/frontend-design/SKILL.md +36 -0
  14. package/skills/handoff/SKILL.md +16 -0
  15. package/skills/improve-codebase-architecture/SKILL.md +71 -0
  16. package/skills/linter-negative/SKILL.md +74 -0
  17. package/skills/no-slop-ui/SKILL.md +26 -0
  18. package/skills/prototype/SKILL.md +26 -0
  19. package/skills/registry.json +44087 -0
  20. package/skills/tastemaker/SKILL.md +8 -0
  21. package/skills/tastemaker/references/tastemaker.md +14 -0
  22. package/skills/tdd/SKILL.md +38 -0
  23. package/skills/visual-review/SKILL.md +103 -0
  24. package/skills/visual-review/references/review-gates.md +42 -0
  25. package/skills/visual-review/references/surface-archetypes.md +37 -0
  26. package/skills/wizard/SKILL.md +44 -0
  27. package/skills/writing-for-agents/SKILL.md +81 -0
  28. package/src/cognition/cognition-store.js +251 -0
  29. package/src/cognition/cognitive-state.js +126 -0
  30. package/src/cognition/hypothesis-manager.js +113 -0
  31. package/src/cognition/observation-engine.js +112 -0
  32. package/src/cognition/observation-provenance.js +268 -0
  33. package/src/context/active-context-boundary.js +68 -0
  34. package/src/context/assembly.js +494 -0
  35. package/src/context/assessment-rules.js +55 -0
  36. package/src/context/capsule-staleness.js +267 -0
  37. package/src/context/capsules.js +2 -0
  38. package/src/context/compare-array.js +98 -0
  39. package/src/context/compare-object.js +89 -0
  40. package/src/context/compare-values.js +42 -0
  41. package/src/context/context-assembly-contract.js +71 -0
  42. package/src/context/context-budget-manager.js +232 -0
  43. package/src/context/context-builder.js +49 -0
  44. package/src/context/context-cache.js +55 -0
  45. package/src/context/context-capture.js +243 -0
  46. package/src/context/context-classification.js +199 -0
  47. package/src/context/context-compaction.js +179 -0
  48. package/src/context/context-decision-audit.js +267 -0
  49. package/src/context/context-demotion.js +173 -0
  50. package/src/context/context-dictionary.js +106 -0
  51. package/src/context/context-drift-detection.js +225 -0
  52. package/src/context/context-evaluation.js +374 -0
  53. package/src/context/context-event-ingress.js +100 -0
  54. package/src/context/context-event-normalization.js +224 -0
  55. package/src/context/context-feedback-loop.js +216 -0
  56. package/src/context/context-freshness.js +21 -0
  57. package/src/context/context-graph-builder.js +71 -0
  58. package/src/context/context-graph.js +451 -0
  59. package/src/context/context-history-relevance.js +321 -0
  60. package/src/context/context-interception.js +145 -0
  61. package/src/context/context-invalidation-events.js +154 -0
  62. package/src/context/context-lifecycle.js +170 -0
  63. package/src/context/context-loop-prevention.js +347 -0
  64. package/src/context/context-manager.js +340 -0
  65. package/src/context/context-memory-layers.js +265 -0
  66. package/src/context/context-minimality.js +166 -0
  67. package/src/context/context-normalization.js +130 -0
  68. package/src/context/context-optimization-metrics.js +434 -0
  69. package/src/context/context-output-policy.js +135 -0
  70. package/src/context/context-pack-authority.js +157 -0
  71. package/src/context/context-promotion.js +172 -0
  72. package/src/context/context-query-contract.js +101 -0
  73. package/src/context/context-ranking.js +164 -0
  74. package/src/context/context-rate-distortion.js +261 -0
  75. package/src/context/context-recovery.js +209 -0
  76. package/src/context/context-retrieval-integration.js +60 -0
  77. package/src/context/context-retrieval.js +93 -0
  78. package/src/context/context-router.js +457 -0
  79. package/src/context/context-routing-evaluation.js +331 -0
  80. package/src/context/context-routing-observability.js +296 -0
  81. package/src/context/context-routing-shadow.js +237 -0
  82. package/src/context/context-scope.js +181 -0
  83. package/src/context/context-snapshot.js +226 -0
  84. package/src/context/context-source-registry.js +95 -0
  85. package/src/context/context-sufficiency-gate.js +120 -0
  86. package/src/context/context-sufficiency-oracle.js +167 -0
  87. package/src/context/context-telemetry.js +176 -0
  88. package/src/context/context.js +633 -0
  89. package/src/context/dependency-closure.js +246 -0
  90. package/src/context/page-fault-tracker.js +275 -0
  91. package/src/context/prompt-engine.js +74 -0
  92. package/src/context/runtime-context-graph.js +260 -0
  93. package/src/context/selection.js +2 -0
  94. package/src/context/session.js +2 -0
  95. package/src/context/sufficiency-contract.js +300 -0
  96. package/src/context/tokenizer.js +17 -0
  97. package/src/context-benchmark-router.mjs +125 -0
  98. package/src/evidence/evidence-engine.js +161 -0
  99. package/src/evidence/evidence-factory.js +3 -0
  100. package/src/evidence/evidence-freshness.js +463 -0
  101. package/src/evidence/evidence-gap.js +76 -0
  102. package/src/evidence/evidence-lineage.js +525 -0
  103. package/src/evidence/evidence.js +136 -0
  104. package/src/evidence/freshness-validation.js +187 -0
  105. package/src/evidence/lineage.js +4 -0
  106. package/src/execution/action-evaluation.js +179 -0
  107. package/src/execution/circuit-breaker.js +70 -0
  108. package/src/execution/claim-interception.js +63 -0
  109. package/src/execution/execution-engine.js +258 -0
  110. package/src/execution/execution-intent.js +69 -0
  111. package/src/execution/execution-state.js +89 -0
  112. package/src/execution/formatting.js +60 -0
  113. package/src/execution/l1-sanitize.js +16 -0
  114. package/src/execution/l2-resource-guards.js +19 -0
  115. package/src/execution/l3-invariant-checker.js +5 -0
  116. package/src/execution/l4-containment.js +6 -0
  117. package/src/execution/runtime-guards.js +106 -0
  118. package/src/execution/strategy-identity.js +162 -0
  119. package/src/execution/tool-interceptor.js +7 -0
  120. package/src/execution/tool-result-classifier.js +63 -0
  121. package/src/integration/canary-deploy.js +143 -0
  122. package/src/integration/collector.js +59 -0
  123. package/src/integration/error-recovery.js +6 -0
  124. package/src/integration/event-bus.js +66 -0
  125. package/src/integration/health-checker.js +57 -0
  126. package/src/integration/logger.js +39 -0
  127. package/src/integration/message-handler.js +611 -0
  128. package/src/integration/opencode-db.js +153 -0
  129. package/src/integration/orchestration.js +419 -0
  130. package/src/integration/part-provenance.js +65 -0
  131. package/src/integration/rate-limiter.js +55 -0
  132. package/src/integration/release-gate.js +336 -0
  133. package/src/integration/rollback-manager.js +164 -0
  134. package/src/integration/router-adapter.js +269 -0
  135. package/src/integration/telemetry.js +45 -0
  136. package/src/integration/update-checker.js +10 -0
  137. package/src/persistence/event-sourcing.js +75 -0
  138. package/src/persistence/memory.js +501 -0
  139. package/src/persistence/migration-runner.js +16 -0
  140. package/src/persistence/persistence-manager.js +39 -0
  141. package/src/persistence/transaction-log.js +157 -0
  142. package/src/policy/action-capabilities.js +116 -0
  143. package/src/policy/action-policy.js +4 -0
  144. package/src/policy/assessment-engine.js +104 -0
  145. package/src/policy/assumption-tracking.js +66 -0
  146. package/src/policy/budget-enforcer.js +7 -0
  147. package/src/policy/config-validator.js +102 -0
  148. package/src/policy/execution-assessment.js +142 -0
  149. package/src/policy/failure-classification.js +5 -0
  150. package/src/policy/feature-flags.js +65 -0
  151. package/src/policy/governance-enforcement.js +118 -0
  152. package/src/policy/locale.js +15 -0
  153. package/src/policy/multi-signer.js +151 -0
  154. package/src/policy/policy-state-machine.js +97 -0
  155. package/src/policy/ponytail-constraint.js +38 -0
  156. package/src/policy/project-inspector.js +93 -0
  157. package/src/policy/query-builder.js +14 -0
  158. package/src/policy/request-classifier.js +79 -0
  159. package/src/policy/risk-engine.js +266 -0
  160. package/src/policy/scope-enforcement.js +140 -0
  161. package/src/policy/secret-scanner.js +68 -0
  162. package/src/policy/skill-routing.js +104 -0
  163. package/src/policy/strategy-capabilities.js +211 -0
  164. package/src/policy/strategy.js +6 -0
  165. package/src/policy/uncertainty.js +117 -0
  166. package/src/shared/container.js +44 -0
  167. package/src/shared/decision-types.js +24 -0
  168. package/src/shared/simple-test.js +160 -0
  169. package/src/shared/wam-events.js +52 -0
  170. package/src/skills/engine.js +1705 -0
  171. package/src/skills/skill-registry.js +19 -0
  172. package/src/skills/skill-routing.js +340 -0
  173. package/src/state/lifecycle-manager.js +113 -0
  174. package/src/state/operational-state.js +198 -0
  175. package/src/state/projections.js +47 -0
  176. package/src/state/replay-engine.js +5 -0
  177. package/src/state/requirement-ownership.js +20 -0
  178. package/src/state/requirement-state.js +152 -0
  179. package/src/state/run-event-identity.js +172 -0
  180. package/src/state/state-engine.js +19 -0
  181. package/src/state/state-machine.js +119 -0
  182. package/src/state/state-schema.js +318 -0
  183. package/src/state/state-store.js +335 -0
  184. package/src/state/state-transition.js +84 -0
  185. package/src/state/task-dependencies.js +287 -0
  186. package/src/state/task-execution.js +217 -0
  187. package/src/state/task-lifecycle.js +59 -0
  188. package/src/state/task-runs.js +361 -0
  189. package/src/state/task-state-file.js +86 -0
  190. package/src/state/task-state.js +41 -0
  191. package/src/state/task-status.js +54 -0
  192. package/src/state/task-store.js +48 -0
  193. package/src/state/wam-state.js +148 -0
  194. package/src/verification/completion-gate-simple.js +8 -0
  195. package/src/verification/completion-gate.js +183 -0
  196. package/src/verification/contract.js +9 -0
  197. package/src/verification/contradiction-resolution.js +255 -0
  198. package/src/verification/diagnostic-engine.js +659 -0
  199. package/src/verification/false-completion-prevention.js +100 -0
  200. package/src/verification/hook-validator.js +6 -0
  201. package/src/verification/requirements.js +6 -0
  202. package/src/verification/verification-context.js +162 -0
  203. package/src/verification/verification-lifecycle.js +66 -0
  204. package/src/verification/verification-model.js +89 -0
  205. package/src/verification/verification-persistence.js +129 -0
  206. package/src/verification/verification-policy.js +127 -0
  207. 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
+ }