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.
- 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,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
|
+
}
|