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,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Pack Source of Truth — clarifies authority between components.
|
|
3
|
+
*
|
|
4
|
+
* Defines clear responsibility boundaries:
|
|
5
|
+
*
|
|
6
|
+
* Context Graph = relationships (what connects to what)
|
|
7
|
+
* Context Router = WHAT context is needed (graph-based resolution)
|
|
8
|
+
* Context Assembly = HOW to pack it (N0-N3 levels, budget, admission)
|
|
9
|
+
* Context Selection = capsule retrieval (legacy, fallback only)
|
|
10
|
+
*
|
|
11
|
+
* Authority:
|
|
12
|
+
* Router decides WHAT
|
|
13
|
+
* Assembly decides HOW
|
|
14
|
+
* Selection retrieves (no decisions)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {"graph" | "router" | "assembly" | "selection"} Component
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} AuthorityDecision
|
|
23
|
+
* @property {Component} component
|
|
24
|
+
* @property {string} decision
|
|
25
|
+
* @property {string} reason
|
|
26
|
+
* @property {number} timestamp
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Component responsibilities (canonical).
|
|
31
|
+
*/
|
|
32
|
+
export const RESPONSIBILITIES = {
|
|
33
|
+
graph: {
|
|
34
|
+
authority: "relationships",
|
|
35
|
+
description: "Stores and queries relationships between context nodes",
|
|
36
|
+
decisions: ["edge types", "node storage", "graph traversal primitives"],
|
|
37
|
+
},
|
|
38
|
+
router: {
|
|
39
|
+
authority: "what",
|
|
40
|
+
description: "Determines minimum sufficient context for a task",
|
|
41
|
+
decisions: [
|
|
42
|
+
"which nodes are required",
|
|
43
|
+
"dependency resolution",
|
|
44
|
+
"contradiction detection",
|
|
45
|
+
"sufficiency assessment",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
assembly: {
|
|
49
|
+
authority: "how",
|
|
50
|
+
description: "Packs context into N0-N3 levels within budget",
|
|
51
|
+
decisions: [
|
|
52
|
+
"level allocation (N0-N3)",
|
|
53
|
+
"budget partitioning",
|
|
54
|
+
"admission classes",
|
|
55
|
+
"final pack contents",
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
selection: {
|
|
59
|
+
authority: "retrieval",
|
|
60
|
+
description: "Retrieves capsules from storage (legacy, no decisions)",
|
|
61
|
+
decisions: ["capsule lookup", "lifecycle filtering"],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Validate that a decision is within component's authority.
|
|
67
|
+
*
|
|
68
|
+
* @param {Component} component
|
|
69
|
+
* @param {string} decision
|
|
70
|
+
* @returns {{ valid: boolean, reason: string }}
|
|
71
|
+
*/
|
|
72
|
+
export function validateAuthority(component, decision) {
|
|
73
|
+
const resp = RESPONSIBILITIES[component];
|
|
74
|
+
if (!resp) {
|
|
75
|
+
return { valid: false, reason: `Unknown component: ${component}` };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const isAuthorized = resp.decisions.some(
|
|
79
|
+
(d) => decision.toLowerCase().includes(d.toLowerCase())
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (isAuthorized) {
|
|
83
|
+
return { valid: true, reason: `${component} has authority over ${decision}` };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
reason: `${component} does NOT have authority over ${decision}. Authorized: ${resp.decisions.join(", ")}`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create an authority decision record.
|
|
94
|
+
*
|
|
95
|
+
* @param {Component} component
|
|
96
|
+
* @param {string} decision
|
|
97
|
+
* @param {string} reason
|
|
98
|
+
* @returns {AuthorityDecision}
|
|
99
|
+
*/
|
|
100
|
+
export function recordDecision(component, decision, reason) {
|
|
101
|
+
return {
|
|
102
|
+
component,
|
|
103
|
+
decision,
|
|
104
|
+
reason,
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolve authority conflict between two components.
|
|
111
|
+
*
|
|
112
|
+
* @param {Component} a
|
|
113
|
+
* @param {Component} b
|
|
114
|
+
* @param {string} decisionType
|
|
115
|
+
* @returns {{ winner: Component, reason: string }}
|
|
116
|
+
*/
|
|
117
|
+
export function resolveAuthorityConflict(a, b, decisionType) {
|
|
118
|
+
// Authority hierarchy: assembly > router > selection > graph
|
|
119
|
+
const hierarchy = ["assembly", "router", "selection", "graph"];
|
|
120
|
+
|
|
121
|
+
const idxA = hierarchy.indexOf(a);
|
|
122
|
+
const idxB = hierarchy.indexOf(b);
|
|
123
|
+
|
|
124
|
+
if (idxA === -1) return { winner: b, reason: `${a} not in hierarchy` };
|
|
125
|
+
if (idxB === -1) return { winner: a, reason: `${b} not in hierarchy` };
|
|
126
|
+
|
|
127
|
+
if (idxA < idxB) {
|
|
128
|
+
return { winner: a, reason: `${a} has higher authority than ${b} for ${decisionType}` };
|
|
129
|
+
}
|
|
130
|
+
return { winner: b, reason: `${b} has higher authority than ${a} for ${decisionType}` };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generate authority report for a context pack assembly.
|
|
135
|
+
*
|
|
136
|
+
* @param {Object} context - Assembly context
|
|
137
|
+
* @returns {string} Formatted report
|
|
138
|
+
*/
|
|
139
|
+
export function generateAuthorityReport(context) {
|
|
140
|
+
const lines = ["[wam authority]"];
|
|
141
|
+
|
|
142
|
+
lines.push(" Graph: stores relationships");
|
|
143
|
+
lines.push(" Router: determines WHAT context is needed");
|
|
144
|
+
lines.push(" Assembly: determines HOW to pack it");
|
|
145
|
+
lines.push(" Selection: retrieves capsules (no decisions)");
|
|
146
|
+
|
|
147
|
+
if (context?.routerDecision) {
|
|
148
|
+
lines.push(` Router decision: ${context.routerDecision}`);
|
|
149
|
+
}
|
|
150
|
+
if (context?.assemblyDecision) {
|
|
151
|
+
lines.push(` Assembly decision: ${context.assemblyDecision}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return lines.join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default RESPONSIBILITIES;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Promotion — move items to higher-priority memory layers.
|
|
3
|
+
*
|
|
4
|
+
* Promotion examples: N3→N2, N2→N1, N1→N0, N3→N0, ARCHIVE→N3, etc.
|
|
5
|
+
*
|
|
6
|
+
* Side effects handled by this module:
|
|
7
|
+
* - Lifecycle transitions when necessary (COMPRESSED → ACTIVE)
|
|
8
|
+
* - Metadata updates (memoryLayer, updatedAt, promotion 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} PromotionEvent
|
|
28
|
+
* @property {"promote"} 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} PromotionResult
|
|
39
|
+
* @property {Object} item — the promoted item (immutable copy with updates)
|
|
40
|
+
* @property {PromotionEvent} event
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Promote a context item to a higher-priority memory layer.
|
|
45
|
+
*
|
|
46
|
+
* Handles necessary lifecycle transitions (e.g. COMPRESSED → ACTIVE)
|
|
47
|
+
* before the layer change, updates metadata, and logs the event.
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} item - The item to promote (must have memoryLayer or inferable category)
|
|
50
|
+
* @param {string} targetLayer - Target memory layer (N0–N3)
|
|
51
|
+
* @returns {PromotionResult}
|
|
52
|
+
*/
|
|
53
|
+
export function promoteItem(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
|
+
if (!WORKING_LAYERS.includes(targetLayer)) {
|
|
61
|
+
throw new TypeError(
|
|
62
|
+
`cannot promote to ${targetLayer}: use demoteItem for archive moves`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const currentLayer = resolveCurrentLayer(item);
|
|
67
|
+
const currentPriority = LAYER_CONFIG[currentLayer].priority;
|
|
68
|
+
const targetPriority = LAYER_CONFIG[targetLayer].priority;
|
|
69
|
+
|
|
70
|
+
if (targetPriority >= currentPriority) {
|
|
71
|
+
throw new TypeError(
|
|
72
|
+
`not a promotion: ${currentLayer} (priority ${currentPriority}) → ${targetLayer} (priority ${targetPriority})`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
|
|
78
|
+
// --- Lifecycle transition if necessary ---
|
|
79
|
+
let lifecycle = item.lifecycle || LIFECYCLE_STATES.CREATED;
|
|
80
|
+
let lifecycleChanged = false;
|
|
81
|
+
let fromLifecycle = lifecycle;
|
|
82
|
+
|
|
83
|
+
if (lifecycle === LIFECYCLE_STATES.COMPRESSED) {
|
|
84
|
+
if (isValidTransition(lifecycle, LIFECYCLE_STATES.ACTIVE)) {
|
|
85
|
+
const transitioned = lifecycleTransition(item, LIFECYCLE_STATES.ACTIVE, {
|
|
86
|
+
reason: "promotion-from-compressed",
|
|
87
|
+
timestamp: now,
|
|
88
|
+
});
|
|
89
|
+
lifecycle = transitioned.lifecycle;
|
|
90
|
+
lifecycleChanged = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let toLifecycle = lifecycle;
|
|
95
|
+
|
|
96
|
+
// --- Build new item (immutable) ---
|
|
97
|
+
const promotedItem = {
|
|
98
|
+
...item,
|
|
99
|
+
memoryLayer: targetLayer,
|
|
100
|
+
lifecycle,
|
|
101
|
+
updatedAt: now,
|
|
102
|
+
metadata: {
|
|
103
|
+
...(item.metadata || {}),
|
|
104
|
+
promotedFrom: currentLayer,
|
|
105
|
+
promotedAt: now,
|
|
106
|
+
},
|
|
107
|
+
lifecycleHistory: [
|
|
108
|
+
...(Array.isArray(item.lifecycleHistory) ? item.lifecycleHistory : []),
|
|
109
|
+
{
|
|
110
|
+
type: "promote",
|
|
111
|
+
from: currentLayer,
|
|
112
|
+
to: targetLayer,
|
|
113
|
+
...(lifecycleChanged
|
|
114
|
+
? { lifecycle: { from: fromLifecycle, to: toLifecycle } }
|
|
115
|
+
: {}),
|
|
116
|
+
timestamp: now,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const event = {
|
|
122
|
+
type: "promote",
|
|
123
|
+
fromLayer: currentLayer,
|
|
124
|
+
toLayer: targetLayer,
|
|
125
|
+
lifecycleChanged,
|
|
126
|
+
...(lifecycleChanged ? { fromLifecycle, toLifecycle } : {}),
|
|
127
|
+
timestamp: now,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return { item: Object.freeze(promotedItem), event };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Resolve the current memory layer of an item.
|
|
135
|
+
* Falls back from item.memoryLayer → category-based default → N3.
|
|
136
|
+
*
|
|
137
|
+
* @param {Object} item
|
|
138
|
+
* @returns {string}
|
|
139
|
+
*/
|
|
140
|
+
function resolveCurrentLayer(item) {
|
|
141
|
+
if (isMemoryLayer(item.memoryLayer)) {
|
|
142
|
+
return item.memoryLayer;
|
|
143
|
+
}
|
|
144
|
+
const category = item.type || item.category;
|
|
145
|
+
if (category && typeof category === "string") {
|
|
146
|
+
return inferLayerFromCategory(category);
|
|
147
|
+
}
|
|
148
|
+
return "N3";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Infer a memory layer from a category string using the same
|
|
153
|
+
* rules as context-memory-layers.js CATEGORY_DEFAULT_LAYER.
|
|
154
|
+
*
|
|
155
|
+
* @param {string} category
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
function inferLayerFromCategory(category) {
|
|
159
|
+
const defaults = {
|
|
160
|
+
REQUIREMENT: "N0",
|
|
161
|
+
DECISION: "N0",
|
|
162
|
+
FACT: "N1",
|
|
163
|
+
OBSERVATION: "N2",
|
|
164
|
+
EVIDENCE: "N2",
|
|
165
|
+
RESULT: "N2",
|
|
166
|
+
ERROR: "N2",
|
|
167
|
+
CLAIM: "N3",
|
|
168
|
+
ASSUMPTION: "N3",
|
|
169
|
+
ACTION: "N3",
|
|
170
|
+
};
|
|
171
|
+
return defaults[category] || "N3";
|
|
172
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { LIFECYCLE_STATES } from "./context-lifecycle.js";
|
|
2
|
+
import { CONTEXT_EVENT_TYPES } from "./context-event-ingress.js";
|
|
3
|
+
|
|
4
|
+
export const CONTEXT_PURPOSE = Object.freeze({
|
|
5
|
+
PLANNING: "PLANNING",
|
|
6
|
+
IMPLEMENTATION: "IMPLEMENTATION",
|
|
7
|
+
INVESTIGATION: "INVESTIGATION",
|
|
8
|
+
DEBUGGING: "DEBUGGING",
|
|
9
|
+
VALIDATION: "VALIDATION",
|
|
10
|
+
REVIEW: "REVIEW",
|
|
11
|
+
RESUME: "RESUME",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const PURPOSE_LAYER_REQUIREMENT = Object.freeze({
|
|
15
|
+
[CONTEXT_PURPOSE.PLANNING]: ["R0", "R1", "R2"],
|
|
16
|
+
[CONTEXT_PURPOSE.IMPLEMENTATION]: ["R0", "R1"],
|
|
17
|
+
[CONTEXT_PURPOSE.INVESTIGATION]: ["H0", "H1"],
|
|
18
|
+
[CONTEXT_PURPOSE.DEBUGGING]: ["OBS", "EVID"],
|
|
19
|
+
[CONTEXT_PURPOSE.VALIDATION]: ["VER"],
|
|
20
|
+
[CONTEXT_PURPOSE.REVIEW]: ["ALL"],
|
|
21
|
+
[CONTEXT_PURPOSE.RESUME]: ["ALL"],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export function buildContextQuery({
|
|
25
|
+
taskId,
|
|
26
|
+
subtaskId = null,
|
|
27
|
+
requirementId = null,
|
|
28
|
+
hypothesisId = null,
|
|
29
|
+
experimentId = null,
|
|
30
|
+
executionId = null,
|
|
31
|
+
taskType = null,
|
|
32
|
+
phase = null,
|
|
33
|
+
activeFiles = [],
|
|
34
|
+
activeTools = [],
|
|
35
|
+
knownFailures = [],
|
|
36
|
+
unresolvedQuestions = [],
|
|
37
|
+
budget = null,
|
|
38
|
+
purpose = CONTEXT_PURPOSE.PLANNING,
|
|
39
|
+
} = {}) {
|
|
40
|
+
if (!taskId) throw new TypeError("contextQuery requires taskId");
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
taskId,
|
|
44
|
+
subtaskId,
|
|
45
|
+
requirementId,
|
|
46
|
+
hypothesisId,
|
|
47
|
+
experimentId,
|
|
48
|
+
executionId,
|
|
49
|
+
taskType,
|
|
50
|
+
phase,
|
|
51
|
+
activeFiles,
|
|
52
|
+
activeTools,
|
|
53
|
+
knownFailures,
|
|
54
|
+
unresolvedQuestions,
|
|
55
|
+
budget,
|
|
56
|
+
purpose,
|
|
57
|
+
_layerRequirement: PURPOSE_LAYER_REQUIREMENT[purpose] || PURPOSE_LAYER_REQUIREMENT[CONTEXT_PURPOSE.PLANNING],
|
|
58
|
+
_timestamp: Date.now(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function matchesQuery(item, query) {
|
|
63
|
+
if (query.requirementId && item.requirementId !== query.requirementId) return false;
|
|
64
|
+
if (query.hypothesisId && item.hypothesisId !== query.hypothesisId) return false;
|
|
65
|
+
if (query.experimentId && item.experimentId !== query.experimentId) return false;
|
|
66
|
+
if (query.executionId && item.executionId !== query.executionId) return false;
|
|
67
|
+
if (query.taskType && item.taskType && item.taskType !== query.taskType) return false;
|
|
68
|
+
if (query.phase && item.phase && item.phase !== query.phase) return false;
|
|
69
|
+
if (query.activeFiles && query.activeFiles.length > 0 && item.scope === "FILE") {
|
|
70
|
+
if (!query.activeFiles.some((f) => (item.content?.path || "").includes(f))) return false;
|
|
71
|
+
}
|
|
72
|
+
if (query.activeTools && query.activeTools.length > 0 && item.scope === "TOOL") {
|
|
73
|
+
if (!query.activeTools.some((t) => (item.content?.tool || "").includes(t))) return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function scoreRelevance(item, query) {
|
|
79
|
+
let score = 0;
|
|
80
|
+
if (item.mandatoryIncluded === true) score += 10;
|
|
81
|
+
if (item.lifecycle === LIFECYCLE_STATES.ACTIVE) score += 5;
|
|
82
|
+
if (item.lifecycle === LIFECYCLE_STATES.CREATED) score += 3;
|
|
83
|
+
if (item.importance === "HIGH") score += 5;
|
|
84
|
+
if (item.importance === "MEDIUM") score += 3;
|
|
85
|
+
if (item.unresolvedCriticalUnknowns?.length > 0) score += 8;
|
|
86
|
+
if (query.requirementId && item.requirementId === query.requirementId) score += 10;
|
|
87
|
+
if (query.hypothesisId && item.hypothesisId === query.hypothesisId) score += 10;
|
|
88
|
+
if (query.experimentId && item.experimentId === query.experimentId) score += 10;
|
|
89
|
+
if (query.executionId && item.executionId === query.executionId) score += 10;
|
|
90
|
+
const recency = (query._timestamp || Date.now()) - item.timestamp;
|
|
91
|
+
if (recency < 60000) score += 5;
|
|
92
|
+
else if (recency < 300000) score += 3;
|
|
93
|
+
return score;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function retrieveRelevantContext(items, query) {
|
|
97
|
+
const filtered = items.filter((item) => matchesQuery(item, query));
|
|
98
|
+
const scored = filtered.map((item) => ({ item, score: scoreRelevance(item, query) }));
|
|
99
|
+
scored.sort((a, b) => b.score - a.score);
|
|
100
|
+
return scored.map((s) => s.item);
|
|
101
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Ranking — Relevance Scoring & Ranking.
|
|
3
|
+
*
|
|
4
|
+
* Change 70: Implements multi-signal ranking for context retrieval.
|
|
5
|
+
* Scores combine term matching, recency, importance, lifecycle state,
|
|
6
|
+
* and provenance quality.
|
|
7
|
+
*
|
|
8
|
+
* Ranking formula (weighted sum):
|
|
9
|
+
* score = w1 * termMatch + w2 * recency + w3 * importance + w4 * lifecycleBoost
|
|
10
|
+
*
|
|
11
|
+
* Invariant: All scores normalized to [0, 1] range. Higher = more relevant.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const WEIGHTS = Object.freeze({
|
|
15
|
+
termMatch: 0.40,
|
|
16
|
+
recency: 0.15,
|
|
17
|
+
importance: 0.25,
|
|
18
|
+
lifecycle: 0.10,
|
|
19
|
+
provenance: 0.10,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const LIFECYCLE_BOOST = Object.freeze({
|
|
23
|
+
ACTIVE: 1.0,
|
|
24
|
+
CREATED: 0.8,
|
|
25
|
+
COMPRESSED: 0.4,
|
|
26
|
+
STALE: 0.2,
|
|
27
|
+
INVALIDATED: 0.0,
|
|
28
|
+
ARCHIVED: 0.1,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const PROVENANCE_BOOST = Object.freeze({
|
|
32
|
+
user_decided: 1.0,
|
|
33
|
+
observed: 0.7,
|
|
34
|
+
inferred: 0.3,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Tokenizes text into a set of lowercase terms.
|
|
39
|
+
* @param {string} text
|
|
40
|
+
* @returns {Set<string>}
|
|
41
|
+
*/
|
|
42
|
+
function tokenize(text = "") {
|
|
43
|
+
return new Set(
|
|
44
|
+
(text || "")
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.replace(/[^a-z0-9áéíóúñü\s]/g, " ")
|
|
47
|
+
.split(/\s+/)
|
|
48
|
+
.filter((w) => w.length > 1)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Computes term overlap ratio between query and item text.
|
|
54
|
+
* @param {string} query
|
|
55
|
+
* @param {string} itemText
|
|
56
|
+
* @returns {number} 0..1
|
|
57
|
+
*/
|
|
58
|
+
function computeTermMatch(query, itemText) {
|
|
59
|
+
const queryTokens = tokenize(query);
|
|
60
|
+
const itemTokens = tokenize(itemText);
|
|
61
|
+
if (!queryTokens.size || !itemTokens.size) return 0;
|
|
62
|
+
|
|
63
|
+
let hits = 0;
|
|
64
|
+
for (const t of queryTokens) {
|
|
65
|
+
if (itemTokens.has(t)) hits++;
|
|
66
|
+
}
|
|
67
|
+
return hits / queryTokens.size;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Computes recency score based on item timestamp.
|
|
72
|
+
* More recent = higher score. Decays over 30 days.
|
|
73
|
+
* @param {number} [timestamp]
|
|
74
|
+
* @param {number} [now]
|
|
75
|
+
* @returns {number} 0..1
|
|
76
|
+
*/
|
|
77
|
+
function computeRecency(timestamp, now = Date.now()) {
|
|
78
|
+
if (!timestamp) return 0.5;
|
|
79
|
+
const ageMs = now - timestamp;
|
|
80
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
81
|
+
// Exponential decay: 1.0 at 0 days, ~0.1 at 30 days
|
|
82
|
+
return Math.exp(-ageDays / 30);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Computes importance score from item metadata.
|
|
87
|
+
* @param {number} [importance]
|
|
88
|
+
* @returns {number} 0..1
|
|
89
|
+
*/
|
|
90
|
+
function computeImportance(importance) {
|
|
91
|
+
if (typeof importance !== "number") return 0.5;
|
|
92
|
+
return Math.min(1, Math.max(0, importance / 10));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Computes lifecycle boost factor.
|
|
97
|
+
* @param {string} lifecycle
|
|
98
|
+
* @returns {number}
|
|
99
|
+
*/
|
|
100
|
+
function computeLifecycleBoost(lifecycle) {
|
|
101
|
+
return LIFECYCLE_BOOST[lifecycle] ?? 0.3;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Computes provenance quality factor.
|
|
106
|
+
* @param {string} provenance
|
|
107
|
+
* @returns {number}
|
|
108
|
+
*/
|
|
109
|
+
function computeProvenanceBoost(provenance) {
|
|
110
|
+
return PROVENANCE_BOOST[provenance] ?? 0.3;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Ranks context items by multi-signal relevance scoring.
|
|
115
|
+
*
|
|
116
|
+
* @param {Array} items - Context items to rank.
|
|
117
|
+
* @param {string} query - Search query for term matching.
|
|
118
|
+
* @param {Object} [options]
|
|
119
|
+
* @param {string[]} [options.requiredTerms] - Terms that must appear (binary filter).
|
|
120
|
+
* @param {Object} [options.weights] - Override default weights.
|
|
121
|
+
* @returns {Array} Items sorted by relevanceScore descending, with score attached.
|
|
122
|
+
*/
|
|
123
|
+
export function rankContextItems(items, query, options = {}) {
|
|
124
|
+
const { requiredTerms = [], weights = {} } = options;
|
|
125
|
+
const w = { ...WEIGHTS, ...weights };
|
|
126
|
+
|
|
127
|
+
// Validate weights sum ~1.0 (informational only)
|
|
128
|
+
const weightSum = Object.values(w).reduce((a, b) => a + b, 0);
|
|
129
|
+
|
|
130
|
+
return items
|
|
131
|
+
.map((item) => {
|
|
132
|
+
const text = item.content || item.label || item.text || item.summary || `${item.id}`;
|
|
133
|
+
const termScore = computeTermMatch(query, text);
|
|
134
|
+
const recencyScore = computeRecency(item.timestamp || item.updatedAt);
|
|
135
|
+
const importanceScore = computeImportance(item.importance);
|
|
136
|
+
const lifecycleScore = computeLifecycleBoost(item.lifecycle);
|
|
137
|
+
const provenanceScore = computeProvenanceBoost(item.provenance);
|
|
138
|
+
|
|
139
|
+
// Required terms binary filter (not a score component)
|
|
140
|
+
if (requiredTerms.length > 0) {
|
|
141
|
+
const itemTokens = tokenize(text);
|
|
142
|
+
const allRequiredPresent = requiredTerms.every((t) => itemTokens.has(t.toLowerCase()));
|
|
143
|
+
if (!allRequiredPresent) {
|
|
144
|
+
return { ...item, relevanceScore: 0, relevanceReason: "required_terms_missing" };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const compositeScore =
|
|
149
|
+
w.termMatch * termScore +
|
|
150
|
+
w.recency * recencyScore +
|
|
151
|
+
w.importance * importanceScore +
|
|
152
|
+
w.lifecycle * lifecycleScore +
|
|
153
|
+
w.provenance * provenanceScore;
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
...item,
|
|
157
|
+
relevanceScore: Math.round(compositeScore * 1000) / 1000,
|
|
158
|
+
relevanceReason: `term:${termScore.toFixed(2)} recency:${recencyScore.toFixed(2)} importance:${importanceScore.toFixed(2)}`,
|
|
159
|
+
};
|
|
160
|
+
})
|
|
161
|
+
.sort((a, b) => (b.relevanceScore ?? 0) - (a.relevanceScore ?? 0));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export { WEIGHTS, LIFECYCLE_BOOST, PROVENANCE_BOOST };
|