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.
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,173 @@
1
+ /**
2
+ * Context Demotion — move items to lower-priority memory layers.
3
+ *
4
+ * Demotion examples: N0→N1, N1→N2, N2→N3, N3→ARCHIVE, etc.
5
+ *
6
+ * Side effects handled by this module:
7
+ * - Lifecycle transitions when moving to ARCHIVE (ACTIVE → COMPRESSED)
8
+ * - Metadata updates (memoryLayer, updatedAt, demotion history)
9
+ * - Event logging via lifecycleHistory entry
10
+ *
11
+ * All operations are immutable: the input item is never mutated.
12
+ */
13
+
14
+ import {
15
+ WORKING_LAYERS,
16
+ MEMORY_LAYERS,
17
+ LAYER_CONFIG,
18
+ isMemoryLayer,
19
+ } from "./context-memory-layers.js";
20
+ import {
21
+ LIFECYCLE_STATES,
22
+ transition as lifecycleTransition,
23
+ canTransition as isValidTransition,
24
+ } from "./context-lifecycle.js";
25
+
26
+ /**
27
+ * @typedef {Object} DemotionEvent
28
+ * @property {"demote"} type
29
+ * @property {string} fromLayer
30
+ * @property {string} toLayer
31
+ * @property {boolean} lifecycleChanged
32
+ * @property {string} [fromLifecycle]
33
+ * @property {string} [toLifecycle]
34
+ * @property {number} timestamp
35
+ */
36
+
37
+ /**
38
+ * @typedef {Object} DemotionResult
39
+ * @property {Object} item — the demoted item (immutable copy with updates)
40
+ * @property {DemotionEvent} event
41
+ */
42
+
43
+ /**
44
+ * Demote a context item to a lower-priority memory layer.
45
+ *
46
+ * Handles necessary lifecycle transitions when moving to ARCHIVE
47
+ * (e.g. ACTIVE → COMPRESSED). Updates metadata and logs the event.
48
+ *
49
+ * @param {Object} item - The item to demote
50
+ * @param {string} targetLayer - Target memory layer (N0–N3 or ARCHIVE)
51
+ * @returns {DemotionResult}
52
+ */
53
+ export function demoteItem(item, targetLayer) {
54
+ if (!item || typeof item !== "object") {
55
+ throw new TypeError("item must be an object");
56
+ }
57
+ if (!isMemoryLayer(targetLayer)) {
58
+ throw new TypeError(`invalid target layer: ${targetLayer}`);
59
+ }
60
+
61
+ const currentLayer = resolveCurrentLayer(item);
62
+ const currentPriority = LAYER_CONFIG[currentLayer].priority;
63
+ const targetPriority = LAYER_CONFIG[targetLayer].priority;
64
+
65
+ if (targetPriority <= currentPriority) {
66
+ throw new TypeError(
67
+ `not a demotion: ${currentLayer} (priority ${currentPriority}) → ${targetLayer} (priority ${targetPriority})`,
68
+ );
69
+ }
70
+
71
+ const now = Date.now();
72
+
73
+ // --- Lifecycle transition if moving to ARCHIVE ---
74
+ let lifecycle = item.lifecycle || LIFECYCLE_STATES.CREATED;
75
+ let lifecycleChanged = false;
76
+ let fromLifecycle = lifecycle;
77
+
78
+ if (targetLayer === MEMORY_LAYERS.ARCHIVE) {
79
+ if (
80
+ !TERMINAL_STATES.includes(lifecycle) &&
81
+ isValidTransition(lifecycle, LIFECYCLE_STATES.COMPRESSED)
82
+ ) {
83
+ const transitioned = lifecycleTransition(item, LIFECYCLE_STATES.COMPRESSED, {
84
+ reason: "demotion-to-archive",
85
+ timestamp: now,
86
+ });
87
+ lifecycle = transitioned.lifecycle;
88
+ lifecycleChanged = true;
89
+ }
90
+ }
91
+
92
+ let toLifecycle = lifecycle;
93
+
94
+ // --- Build new item (immutable) ---
95
+ const demotedItem = {
96
+ ...item,
97
+ memoryLayer: targetLayer,
98
+ lifecycle,
99
+ updatedAt: now,
100
+ metadata: {
101
+ ...(item.metadata || {}),
102
+ demotedFrom: currentLayer,
103
+ demotedAt: now,
104
+ ...(targetLayer === MEMORY_LAYERS.ARCHIVE ? { archivedAt: now } : {}),
105
+ },
106
+ lifecycleHistory: [
107
+ ...(Array.isArray(item.lifecycleHistory) ? item.lifecycleHistory : []),
108
+ {
109
+ type: "demote",
110
+ from: currentLayer,
111
+ to: targetLayer,
112
+ ...(lifecycleChanged
113
+ ? { lifecycle: { from: fromLifecycle, to: toLifecycle } }
114
+ : {}),
115
+ timestamp: now,
116
+ },
117
+ ],
118
+ };
119
+
120
+ const event = {
121
+ type: "demote",
122
+ fromLayer: currentLayer,
123
+ toLayer: targetLayer,
124
+ lifecycleChanged,
125
+ ...(lifecycleChanged ? { fromLifecycle, toLifecycle } : {}),
126
+ timestamp: now,
127
+ };
128
+
129
+ return { item: Object.freeze(demotedItem), event };
130
+ }
131
+
132
+ /** Terminal lifecycle states — cannot be further transitioned. */
133
+ const TERMINAL_STATES = Object.freeze(["INVALIDATED", "ARCHIVED"]);
134
+
135
+ /**
136
+ * Resolve the current memory layer of an item.
137
+ * Falls back from item.memoryLayer → category-based default → N3.
138
+ *
139
+ * @param {Object} item
140
+ * @returns {string}
141
+ */
142
+ function resolveCurrentLayer(item) {
143
+ if (isMemoryLayer(item.memoryLayer)) {
144
+ return item.memoryLayer;
145
+ }
146
+ const category = item.type || item.category;
147
+ if (category && typeof category === "string") {
148
+ return inferLayerFromCategory(category);
149
+ }
150
+ return "N3";
151
+ }
152
+
153
+ /**
154
+ * Infer a memory layer from a category string.
155
+ *
156
+ * @param {string} category
157
+ * @returns {string}
158
+ */
159
+ function inferLayerFromCategory(category) {
160
+ const defaults = {
161
+ REQUIREMENT: "N0",
162
+ DECISION: "N0",
163
+ FACT: "N1",
164
+ OBSERVATION: "N2",
165
+ EVIDENCE: "N2",
166
+ RESULT: "N2",
167
+ ERROR: "N2",
168
+ CLAIM: "N3",
169
+ ASSUMPTION: "N3",
170
+ ACTION: "N3",
171
+ };
172
+ return defaults[category] || "N3";
173
+ }
@@ -0,0 +1,106 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
5
+ /**
6
+ * Calcula el hash SHA-256 del contenido de un archivo.
7
+ */
8
+ export function calculateContentHash(filePath) {
9
+ try {
10
+ const content = fs.readFileSync(filePath);
11
+ return crypto.createHash("sha256").update(content).digest("hex");
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Representación de la evidencia que sustenta una asociación semántica.
19
+ */
20
+ export class Evidence {
21
+ constructor({ type, value, source, timestamp = new Date().toISOString() }) {
22
+ this.type = type; // e.g., 'symbol', 'path', 'user_confirmation', 'import'
23
+ this.value = value;
24
+ this.source = source;
25
+ this.timestamp = timestamp;
26
+ }
27
+ }
28
+
29
+ export class StructuralIndexEntry {
30
+ constructor({ path, hash, symbols = [], exports = [], imports = [], module = null, repositoryId, indexerVersion = "1.0.0" }) {
31
+ this.path = path;
32
+ this.hash = hash;
33
+ this.symbols = symbols;
34
+ this.exports = exports;
35
+ this.imports = imports;
36
+ this.module = module;
37
+ this.repositoryId = repositoryId;
38
+ this.indexerVersion = indexerVersion;
39
+ this.updatedAt = new Date().toISOString();
40
+ }
41
+ }
42
+
43
+ export class SemanticDictionaryEntry {
44
+ constructor({ concept, targets = [], aliases = [], confidence = 0, evidence = [], status = "discovered" }) {
45
+ this.concept = concept;
46
+ this.targets = targets; // Array of paths or symbols
47
+ this.aliases = aliases;
48
+ this.confidence = confidence; // 0.0 - 1.0
49
+ this.evidence = evidence; // Array of Evidence objects
50
+ this.status = status; // discovered, candidate, confirmed, invalid
51
+ this.updatedAt = new Date().toISOString();
52
+ }
53
+ }
54
+
55
+ export class ContextDictionary {
56
+ constructor(root = process.cwd()) {
57
+ this.root = root;
58
+ this.filePath = path.join(root, ".wam", "context-dictionary.json");
59
+ this.structuralIndex = new Map(); // path -> StructuralIndexEntry
60
+ this.semanticDictionary = new Map(); // concept -> SemanticDictionaryEntry
61
+ this.load();
62
+ }
63
+
64
+ load() {
65
+ if (fs.existsSync(this.filePath)) {
66
+ const data = JSON.parse(fs.readFileSync(this.filePath, "utf-8"));
67
+ this.structuralIndex = new Map(Object.entries(data.structuralIndex || {}));
68
+ this.semanticDictionary = new Map(Object.entries(data.semanticDictionary || {}));
69
+ }
70
+ }
71
+
72
+ save() {
73
+ fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
74
+ fs.writeFileSync(this.filePath, JSON.stringify({
75
+ structuralIndex: Object.fromEntries(this.structuralIndex),
76
+ semanticDictionary: Object.fromEntries(this.semanticDictionary),
77
+ }, null, 2));
78
+ }
79
+
80
+ indexFile(filePath) {
81
+ const hash = calculateContentHash(filePath);
82
+ if (!hash) return;
83
+
84
+ const relPath = path.relative(this.root, filePath);
85
+ const entry = new StructuralIndexEntry({
86
+ path: relPath,
87
+ hash,
88
+ repositoryId: path.basename(this.root)
89
+ });
90
+
91
+ this.structuralIndex.set(relPath, entry);
92
+ this.save();
93
+ }
94
+
95
+ invalidateFile(filePath) {
96
+ const relPath = path.relative(this.root, filePath);
97
+ if (this.structuralIndex.has(relPath)) {
98
+ this.structuralIndex.delete(relPath);
99
+ this.save();
100
+ }
101
+ }
102
+
103
+ resolveConcept(concept) {
104
+ return this.semanticDictionary.get(concept) || null;
105
+ }
106
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Context Drift Detection — Detect when agent actions diverge from goals.
3
+ *
4
+ * Change 79: detectContextDrift(agentAction, goal)
5
+ *
6
+ * Compares an agent action against the stated goal using the
7
+ * current context as reference. Determines if the agent is
8
+ * drifting off-course.
9
+ *
10
+ * Detection methods:
11
+ * 1. Content similarity (keyword overlap)
12
+ * 2. Action type mismatch
13
+ * 3. Temporal patterns (repeated off-topic actions)
14
+ * 4. Goal achievement progress stagnation
15
+ */
16
+
17
+ /**
18
+ * @typedef {Object} DriftResult
19
+ * @property {boolean} drifted - Whether drift was detected
20
+ * @property {number} score - Drift score (0=aligned, 1=fully drifted)
21
+ * @property {string} reason - Human-readable explanation
22
+ * @property {string[]} signals - Specific signals that triggered detection
23
+ * @property {number} confidence - 0-1 confidence level
24
+ */
25
+
26
+ /**
27
+ * @typedef {Object} DriftConfig
28
+ * @property {number} [threshold=0.4] - Score above which drift is flagged
29
+ * @property {number} [keywordWeight=0.4] - Weight of keyword overlap
30
+ * @property {number} [typeWeight=0.3] - Weight of type alignment
31
+ * @property {number} [recencyWeight=0.3] - Weight of recency factor
32
+ */
33
+
34
+ const DEFAULT_CONFIG = {
35
+ threshold: 0.4,
36
+ keywordWeight: 0.4,
37
+ typeWeight: 0.3,
38
+ recencyWeight: 0.3,
39
+ };
40
+
41
+ /**
42
+ * Tokenize text into meaningful words (ignore stop words).
43
+ * @param {string} text
44
+ * @returns {string[]}
45
+ */
46
+ function tokenize(text) {
47
+ const STOP_WORDS = new Set([
48
+ "the", "a", "an", "is", "are", "was", "were", "be", "been", "being",
49
+ "have", "has", "had", "do", "does", "did", "will", "would", "could",
50
+ "should", "may", "might", "shall", "can", "need", "dare", "ought",
51
+ "used", "to", "of", "in", "for", "on", "with", "at", "by", "from",
52
+ "as", "into", "through", "during", "before", "after", "above", "below",
53
+ "between", "out", "off", "over", "under", "again", "further", "then",
54
+ "once", "here", "there", "when", "where", "why", "how", "all", "each",
55
+ "every", "both", "few", "more", "most", "other", "some", "such", "no",
56
+ "not", "only", "own", "same", "so", "than", "too", "very", "just",
57
+ "because", "but", "and", "or", "nor", "if", "while", "about", "up",
58
+ ]);
59
+ return String(text)
60
+ .toLowerCase()
61
+ .split(/[\s\W]+/)
62
+ .filter((w) => w.length > 2 && !STOP_WORDS.has(w));
63
+ }
64
+
65
+ /**
66
+ * Compute Jaccard-like similarity between two word sets.
67
+ * @param {string[]} a
68
+ * @param {string[]} b
69
+ * @returns {number} 0-1
70
+ */
71
+ function wordOverlap(a, b) {
72
+ if (a.length === 0 || b.length === 0) return 0;
73
+ const setA = new Set(a);
74
+ const setB = new Set(b);
75
+ let intersection = 0;
76
+ for (const word of setA) {
77
+ if (setB.has(word)) intersection++;
78
+ }
79
+ const union = new Set([...setA, ...setB]).size;
80
+ return union > 0 ? intersection / union : 0;
81
+ }
82
+
83
+ // Known action types that are generally safe (goal-aligned)
84
+ const SAFE_ACTION_TYPES = new Set([
85
+ "task", "requirement", "decision", "verification", "observation",
86
+ "evidence", "clarification", "plan", "implementation", "analysis",
87
+ ]);
88
+
89
+ // Known action types that may indicate drift
90
+ const DRIFT_ACTION_TYPES = new Set([
91
+ "exploration", "idle", "waiting", "redundant", "wander", "miscategorize",
92
+ ]);
93
+
94
+ /**
95
+ * Change 79 — detectContextDrift
96
+ *
97
+ * Determines whether an agent action represents drift from the stated goal,
98
+ * considering the current context.
99
+ *
100
+ * @param {Object} agentAction - The action taken by the agent
101
+ * @param {string} goal - The original goal/objective
102
+ * @param {Object} [context] - Current context (optional, improves accuracy)
103
+ * @param {DriftConfig} [config={}] - Detection configuration
104
+ * @returns {DriftResult}
105
+ */
106
+ export function detectContextDrift(agentAction, goal, context = null, config = {}) {
107
+ if (!agentAction || typeof agentAction !== "object") {
108
+ return {
109
+ drifted: false,
110
+ score: 0,
111
+ reason: "No action provided",
112
+ signals: [],
113
+ confidence: 0,
114
+ };
115
+ }
116
+ if (!goal || typeof goal !== "string") {
117
+ return {
118
+ drifted: false,
119
+ score: 0,
120
+ reason: "No goal provided",
121
+ signals: [],
122
+ confidence: 0,
123
+ };
124
+ }
125
+
126
+ const cfg = { ...DEFAULT_CONFIG, ...config };
127
+ const content = String(agentAction.content || agentAction.description || agentAction.name || "");
128
+ const actionType = String(agentAction.type || agentAction.category || "");
129
+ const signals = [];
130
+ let totalScore = 0;
131
+ let totalWeight = 0;
132
+
133
+ // Method 1: Keyword/semantic overlap
134
+ const goalWords = tokenize(goal);
135
+ const actionWords = tokenize(content);
136
+ const overlap = wordOverlap(goalWords, actionWords);
137
+ totalScore += cfg.keywordWeight * overlap;
138
+ totalWeight += cfg.keywordWeight;
139
+ if (overlap < 0.2 && content.length > 5) {
140
+ signals.push(`low_keyword_overlap:${Math.round(overlap * 100)}%`);
141
+ }
142
+
143
+ // Method 2: Action type alignment
144
+ let typeScore = 0.5; // neutral default
145
+ if (actionType) {
146
+ if (SAFE_ACTION_TYPES.has(actionType.toLowerCase())) {
147
+ typeScore = 0.9;
148
+ } else if (DRIFT_ACTION_TYPES.has(actionType.toLowerCase())) {
149
+ typeScore = 0.1;
150
+ signals.push(`drift_action_type:${actionType}`);
151
+ } else {
152
+ // Unknown type: check if it contains goal-related words
153
+ const typeWords = tokenize(actionType);
154
+ typeScore = 0.5 + wordOverlap(goalWords, typeWords) * 0.5;
155
+ }
156
+ } else {
157
+ signals.push("no_action_type");
158
+ typeScore = 0.4;
159
+ }
160
+ totalScore += cfg.typeWeight * typeScore;
161
+ totalWeight += cfg.typeWeight;
162
+
163
+ // Method 3: Recency (if context has timestamps)
164
+ if (context && context.timestamp) {
165
+ const age = Date.now() - context.timestamp;
166
+ const hoursOld = age / (1000 * 60 * 60);
167
+ const recencyScore = Math.max(0.1, 1 - hoursOld * 0.05);
168
+ totalScore += cfg.recencyWeight * recencyScore;
169
+ totalWeight += cfg.recencyWeight;
170
+ } else {
171
+ // No context timestamp = moderate confidence assumption
172
+ totalScore += cfg.recencyWeight * 0.5;
173
+ totalWeight += cfg.recencyWeight;
174
+ }
175
+
176
+ const finalScore = totalWeight > 0 ? totalScore / totalWeight : 0;
177
+ const drifted = finalScore < cfg.threshold;
178
+
179
+ // Confidence: higher when signals are consistent
180
+ const confidence = drifted
181
+ ? Math.min(1, 0.5 + signals.length * 0.15)
182
+ : Math.min(1, 0.6 + (1 - finalScore) * 0.4);
183
+
184
+ return {
185
+ drifted,
186
+ score: Math.round(finalScore * 1000) / 1000,
187
+ reason: drifted
188
+ ? `Drift detected (score: ${finalScore.toFixed(3)}): ${signals.join(", ") || "low similarity"}`
189
+ : `No drift (score: ${finalScore.toFixed(3)}): action aligned with goal`,
190
+ signals,
191
+ confidence: Math.round(confidence * 1000) / 1000,
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Track drift over multiple actions and detect patterns.
197
+ *
198
+ * @param {Object[]} actions - Array of agent actions
199
+ * @param {string} goal - The original goal
200
+ * @param {DriftConfig} [config] - Detection configuration
201
+ * @returns {Object}
202
+ */
203
+ export function trackDriftPattern(actions, goal, config = {}) {
204
+ if (!Array.isArray(actions)) {
205
+ throw new TypeError("actions must be an array");
206
+ }
207
+
208
+ const results = actions.map((action) => detectContextDrift(action, goal, null, config));
209
+ const driftCount = results.filter((r) => r.drifted).length;
210
+ const totalActions = results.length;
211
+ const driftRate = totalActions > 0 ? driftCount / totalActions : 0;
212
+
213
+ return {
214
+ totalActions,
215
+ driftCount,
216
+ driftRate: Math.round(driftRate * 1000) / 1000,
217
+ isPattern: driftRate >= 0.5 && totalActions >= 3,
218
+ results,
219
+ summary: {
220
+ consistentDrift: driftRate >= 0.7,
221
+ intermittentDrift: driftRate >= 0.3 && driftRate < 0.7,
222
+ mostlyAligned: driftRate < 0.3,
223
+ },
224
+ };
225
+ }