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,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous execution loop — hypothesis → classify → experiment → observe → replan.
|
|
3
|
+
* Non-destructive: never hard-deletes. Failed approaches are archived/rejected.
|
|
4
|
+
*
|
|
5
|
+
* Decoupled into:
|
|
6
|
+
* - observation-engine.js — raw observation recording
|
|
7
|
+
* - evidence-engine.js — evidence creation + lineage linking
|
|
8
|
+
* - state-machine.js — hypothesis / experiment status transitions
|
|
9
|
+
*
|
|
10
|
+
* This module orchestrates the three engines and remains the entry point
|
|
11
|
+
* for startExperiment / noteFailure / noteSuccess / handleContradiction.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
createHypothesis,
|
|
16
|
+
createExperiment,
|
|
17
|
+
completeExperiment,
|
|
18
|
+
failExperiment,
|
|
19
|
+
updateHypothesisStatus,
|
|
20
|
+
hasRepetitiveFailure,
|
|
21
|
+
archiveHypothesis,
|
|
22
|
+
HYPOTHESIS_STATUS,
|
|
23
|
+
} from "../cognition/cognition-store.js";
|
|
24
|
+
import { guardAction } from "./runtime-guards.js";
|
|
25
|
+
import { rejectHypothesis } from "../cognition/cognitive-state.js";
|
|
26
|
+
import { assessObservation, AssessmentResult } from "../policy/assessment-engine.js";
|
|
27
|
+
import { noteContradiction } from "../cognition/hypothesis-manager.js";
|
|
28
|
+
import { getTaskState, persistTaskState } from "../skills/engine.js";
|
|
29
|
+
|
|
30
|
+
// Re-exported as public API surface of the execution entry point.
|
|
31
|
+
export { assessObservation };
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
recordRawObservation,
|
|
35
|
+
resolveExperimentContext,
|
|
36
|
+
} from "../cognition/observation-engine.js";
|
|
37
|
+
import {
|
|
38
|
+
produceExecutionEvidence,
|
|
39
|
+
linkEvidenceIfBound,
|
|
40
|
+
} from "../evidence/evidence-engine.js";
|
|
41
|
+
import {
|
|
42
|
+
deriveHypothesisStatus,
|
|
43
|
+
shouldReplan,
|
|
44
|
+
shouldNoteContradiction,
|
|
45
|
+
} from "../state/state-machine.js";
|
|
46
|
+
|
|
47
|
+
function describe(tool, args = {}) {
|
|
48
|
+
const { hypothesisId: _h, ...rest } = args;
|
|
49
|
+
return `${tool}:${JSON.stringify(rest)}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the requirement an execution outcome belongs to. Prefers the
|
|
54
|
+
* explicit id supplied by the runtime bridge; falls back to the task's
|
|
55
|
+
* active requirement (first not done/verified) so a success observation
|
|
56
|
+
* still propagates to the lifecycle state it satisfies.
|
|
57
|
+
*/
|
|
58
|
+
function resolveRequirementId(taskRoot, taskId, requirementId) {
|
|
59
|
+
if (requirementId) return requirementId;
|
|
60
|
+
const state = getTaskState(taskId, taskRoot);
|
|
61
|
+
const requirements = Array.isArray(state?.requirements) ? state.requirements : [];
|
|
62
|
+
const active =
|
|
63
|
+
requirements.find((r) => r.status !== "done" && r.status !== "verified") ||
|
|
64
|
+
requirements[0] ||
|
|
65
|
+
null;
|
|
66
|
+
return active?.id || null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Propagate a SUPPORTED execution outcome to the requirement lifecycle:
|
|
71
|
+
* mark it verified and attach the producing evidence id. This is the seam
|
|
72
|
+
* where the cognition store (evidence) meets the task state (requirement
|
|
73
|
+
* status); without it a successful tool leaves the completion gate blocked.
|
|
74
|
+
*/
|
|
75
|
+
function markRequirementVerified(taskRoot, taskId, requirementId, evidenceId) {
|
|
76
|
+
if (!requirementId) return false;
|
|
77
|
+
const state = getTaskState(taskId, taskRoot);
|
|
78
|
+
if (!state || !Array.isArray(state.requirements)) return false;
|
|
79
|
+
const requirement = state.requirements.find((r) => r.id === requirementId);
|
|
80
|
+
if (!requirement) return false;
|
|
81
|
+
requirement.status = "verified";
|
|
82
|
+
if (evidenceId) {
|
|
83
|
+
if (!Array.isArray(requirement.evidence)) requirement.evidence = [];
|
|
84
|
+
if (!requirement.evidence.includes(evidenceId)) requirement.evidence.push(evidenceId);
|
|
85
|
+
}
|
|
86
|
+
requirement.verifiedAt = requirement.verifiedAt || Date.now();
|
|
87
|
+
persistTaskState(taskId, state, taskRoot);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Pure orchestrator: turn an observation + assessment into the next
|
|
93
|
+
* hypothesis status and the persisted observation record. Delegates
|
|
94
|
+
* persistence to observation-engine and status derivation to state-machine.
|
|
95
|
+
*/
|
|
96
|
+
function processObservation(taskRoot, taskId, args) {
|
|
97
|
+
// Resolve experiment context first so the assessment has the right input shape
|
|
98
|
+
// (it needs experiment.expectedObservation to compare against args.actual).
|
|
99
|
+
const experiment = resolveExperimentContext(taskRoot, taskId, args);
|
|
100
|
+
const assessment = assessObservation(experiment ?? { id: null }, {
|
|
101
|
+
actual: args.actual,
|
|
102
|
+
unexpected: args.unexpected,
|
|
103
|
+
provenance: args.provenance,
|
|
104
|
+
experimentId: args.experimentId,
|
|
105
|
+
});
|
|
106
|
+
// Now persist with the *assessment* result, not the raw tool result.
|
|
107
|
+
const { observation } = recordRawObservation(taskRoot, taskId, {
|
|
108
|
+
...args,
|
|
109
|
+
result: assessment.result,
|
|
110
|
+
});
|
|
111
|
+
const { status, severity } = deriveHypothesisStatus(assessment);
|
|
112
|
+
updateHypothesisStatus(taskRoot, taskId, args.hypothesisId, status);
|
|
113
|
+
|
|
114
|
+
return { assessment, hypothesisStatus: status, severity, observation };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function startExperiment(taskRoot, taskId, { statement, tool, args = {}, confidence = 0.5, expectedObservation }) {
|
|
118
|
+
const hypothesis = createHypothesis(taskRoot, taskId, { statement, confidence });
|
|
119
|
+
const input = { ...args, hypothesisId: hypothesis.id };
|
|
120
|
+
const guard = await guardAction(tool, input, taskRoot, taskId);
|
|
121
|
+
if (!guard.allowed) {
|
|
122
|
+
archiveHypothesis(taskRoot, taskId, hypothesis.id, guard.reason || "blocked by policy");
|
|
123
|
+
return { ok: false, hypothesis, guard };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const actionDescription = describe(tool, args);
|
|
127
|
+
if (hasRepetitiveFailure(taskRoot, taskId, { hypothesisId: hypothesis.id, actionDescription })) {
|
|
128
|
+
archiveHypothesis(taskRoot, taskId, hypothesis.id, "repetitive failure");
|
|
129
|
+
return { ok: false, hypothesis, guard: { allowed: false, reason: "repetitive failure" } };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const experiment = createExperiment(taskRoot, taskId, {
|
|
133
|
+
hypothesisId: hypothesis.id,
|
|
134
|
+
tool,
|
|
135
|
+
args,
|
|
136
|
+
actionDescription,
|
|
137
|
+
expectedObservation,
|
|
138
|
+
});
|
|
139
|
+
updateHypothesisStatus(taskRoot, taskId, hypothesis.id, HYPOTHESIS_STATUS.TESTING);
|
|
140
|
+
return { ok: true, hypothesis, experiment, guard };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function noteFailure(taskRoot, taskId, { hypothesisId, experimentId, reason, actual, unexpected, provenance }) {
|
|
144
|
+
if (experimentId) failExperiment(taskRoot, taskId, experimentId, reason);
|
|
145
|
+
const { assessment, hypothesisStatus, severity } = processObservation(taskRoot, taskId, {
|
|
146
|
+
hypothesisId,
|
|
147
|
+
experimentId,
|
|
148
|
+
reason,
|
|
149
|
+
actual,
|
|
150
|
+
unexpected,
|
|
151
|
+
provenance,
|
|
152
|
+
outcome: "failure",
|
|
153
|
+
});
|
|
154
|
+
// Explicit failure: if the assessment didn't already drive the hypothesis to
|
|
155
|
+
// a terminal state (SUPPORTED), archive it so callers observing the cognition
|
|
156
|
+
// store see REJECTED/ARCHIVED instead of stale TESTING. This preserves
|
|
157
|
+
// noteSuccess semantics (no forced archive on success outcomes).
|
|
158
|
+
// Preserve terminal decisions: REJECTED is already the outcome of a
|
|
159
|
+
// high-severity contradiction (state-machine derive). Only soft-archive
|
|
160
|
+
// non-terminal states (e.g. TESTING) so callers never observe stale
|
|
161
|
+
// TESTING. Archiving REJECTED on top of itself erased the distinction
|
|
162
|
+
// between "contradicted" and "retired".
|
|
163
|
+
if (
|
|
164
|
+
hypothesisStatus !== HYPOTHESIS_STATUS.SUPPORTED &&
|
|
165
|
+
hypothesisStatus !== HYPOTHESIS_STATUS.REJECTED
|
|
166
|
+
) {
|
|
167
|
+
archiveHypothesis(taskRoot, taskId, hypothesisId, reason || "failure");
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
assessment,
|
|
171
|
+
hypothesisStatus,
|
|
172
|
+
severity,
|
|
173
|
+
replan: shouldReplan({ assessment, hypothesisStatus }),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function noteSuccess(taskRoot, taskId, { hypothesisId, experimentId, result, actual, unexpected, provenance, requirementId }) {
|
|
178
|
+
// 0. Mark the experiment as COMPLETED (mirrors noteFailure → failExperiment).
|
|
179
|
+
if (experimentId) completeExperiment(taskRoot, taskId, experimentId, { result });
|
|
180
|
+
|
|
181
|
+
// Bind the outcome to a requirement even when the caller omits it, so the
|
|
182
|
+
// evidence lineage and the task lifecycle stay connected.
|
|
183
|
+
const effectiveRequirementId = resolveRequirementId(taskRoot, taskId, requirementId);
|
|
184
|
+
|
|
185
|
+
// 1. Create evidence first so we can carry its id into the observation facts.
|
|
186
|
+
const { evidence } = produceExecutionEvidence(taskId, taskRoot, {
|
|
187
|
+
requirementId: effectiveRequirementId,
|
|
188
|
+
result,
|
|
189
|
+
hypothesisId,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// 2. Persist observation with the evidence id as the primary fact.
|
|
193
|
+
const { assessment, hypothesisStatus, observation } = processObservation(taskRoot, taskId, {
|
|
194
|
+
hypothesisId,
|
|
195
|
+
experimentId,
|
|
196
|
+
result,
|
|
197
|
+
actual,
|
|
198
|
+
unexpected,
|
|
199
|
+
provenance,
|
|
200
|
+
outcome: "success",
|
|
201
|
+
facts: [evidence.id],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// 3. Bind evidence to the requirement lineage when both sides are known.
|
|
205
|
+
linkEvidenceIfBound({
|
|
206
|
+
evidenceId: evidence.id,
|
|
207
|
+
requirementId: effectiveRequirementId,
|
|
208
|
+
hypothesisId,
|
|
209
|
+
experimentId,
|
|
210
|
+
observationId: observation?.id,
|
|
211
|
+
taskId,
|
|
212
|
+
taskRoot,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// A supported outcome verifies the requirement it satisfies: this closes the
|
|
216
|
+
// seam between cognition evidence and task-state verification.
|
|
217
|
+
if (assessment.result === AssessmentResult.SUPPORTED) {
|
|
218
|
+
markRequirementVerified(taskRoot, taskId, effectiveRequirementId, evidence?.id);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (assessment.result === AssessmentResult.CONTRADICTED) {
|
|
222
|
+
if (shouldNoteContradiction({ assessment, hypothesisStatus })) {
|
|
223
|
+
noteContradiction(taskId, hypothesisId, observation, assessment, taskRoot);
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
assessment,
|
|
227
|
+
hypothesisStatus,
|
|
228
|
+
severity: null,
|
|
229
|
+
replan: shouldReplan({ assessment, hypothesisStatus }),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return { assessment, hypothesisStatus };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function handleContradiction(taskRoot, taskId, { experiment, observation, assessment }) {
|
|
236
|
+
const { hypothesisId, id: experimentId } = experiment;
|
|
237
|
+
updateHypothesisStatus(taskRoot, taskId, hypothesisId, "contradicted");
|
|
238
|
+
rejectHypothesis(taskRoot, hypothesisId, "contradicted by observation");
|
|
239
|
+
const contradiction = noteContradiction(taskId, hypothesisId, observation, assessment, taskRoot);
|
|
240
|
+
recordRawObservation(taskRoot, taskId, {
|
|
241
|
+
experimentId,
|
|
242
|
+
hypothesisId,
|
|
243
|
+
result: assessment.result,
|
|
244
|
+
facts: [contradiction.detail],
|
|
245
|
+
provenance: { type: "execution-assessment" },
|
|
246
|
+
});
|
|
247
|
+
const newHypothesis = createHypothesis(taskRoot, taskId, {
|
|
248
|
+
statement: `[REPLAN] New hypothesis after contradicting ${hypothesisId}: reassess strategy`,
|
|
249
|
+
confidence: 0.3,
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
status: "CONTRADICTED",
|
|
253
|
+
previousHypothesisId: hypothesisId,
|
|
254
|
+
previousExperimentId: experimentId,
|
|
255
|
+
assessment,
|
|
256
|
+
newHypothesis,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export const EXECUTION_INTENT = Object.freeze({
|
|
2
|
+
DIRECT_REQUIREMENT_ACTION: "DIRECT_REQUIREMENT_ACTION",
|
|
3
|
+
SUPPORTING_ACTION: "SUPPORTING_ACTION",
|
|
4
|
+
INVESTIGATION: "INVESTIGATION",
|
|
5
|
+
VALIDATION: "VALIDATION",
|
|
6
|
+
RECOVERY: "RECOVERY",
|
|
7
|
+
UNKNOWN: "UNKNOWN",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const INVESTIGATION_TOOLS = new Set(["read", "read_file", "list_directory", "list_files", "get_file", "glob", "grep", "search", "webfetch", "fetch", "task_search", "tool_search"]);
|
|
11
|
+
const RECOVERY_TOOLS = new Set(["revert", "rollback", "restore"]);
|
|
12
|
+
const VALIDATION_TOOLS = new Set(["test", "spec", "vitest", "jest", "pytest", "mocha"]);
|
|
13
|
+
const MODIFICATION_TOOLS = new Set(["write", "edit", "apply_patch", "patch", "todo_write", "todowrite"]);
|
|
14
|
+
|
|
15
|
+
export function classifyExecutionIntent(tool, args = {}, statement = "") {
|
|
16
|
+
const operation = String(tool || "").toLowerCase();
|
|
17
|
+
const stmt = String(statement || "").toLowerCase();
|
|
18
|
+
const cmd = String(args?.command || "").toLowerCase();
|
|
19
|
+
const first = (cmd.split(/\s+/)[0] || "");
|
|
20
|
+
|
|
21
|
+
if (RECOVERY_TOOLS.has(operation)) return EXECUTION_INTENT.RECOVERY;
|
|
22
|
+
|
|
23
|
+
if (VALIDATION_TOOLS.has(operation) || /\b(test|spec|verify|check|assert)\b/.test(operation) || /\btest\b/.test(cmd)) {
|
|
24
|
+
return EXECUTION_INTENT.VALIDATION;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (INVESTIGATION_TOOLS.has(operation)) return EXECUTION_INTENT.INVESTIGATION;
|
|
28
|
+
if (/^(ls|find|cat|grep|search|inspect|inspect_file|view|show)/.test(cmd.split(/\s+/)[0] || "")) return EXECUTION_INTENT.INVESTIGATION;
|
|
29
|
+
|
|
30
|
+
if (MODIFICATION_TOOLS.has(operation)) {
|
|
31
|
+
return EXECUTION_INTENT.SUPPORTING_ACTION;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (/^(npm|pnpm|yarn|brew|apt|pip)\s+(install|add|setup|i)\b/.test(cmd) || /^(install|setup|setup_)/.test(cmd.split(/\s+/)[0] || "")) {
|
|
35
|
+
return EXECUTION_INTENT.SUPPORTING_ACTION;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (/^(npm|pnpm|yarn|node|deno|ruby|python|bash|sh|make|cargo|go|docker|kubectl|npx)/.test(first)) {
|
|
39
|
+
if (/^npx\s+/.test(cmd) || /\binstall\b/.test(cmd) || /\bsetup\b/.test(cmd)) {
|
|
40
|
+
return EXECUTION_INTENT.SUPPORTING_ACTION;
|
|
41
|
+
}
|
|
42
|
+
if (/\btest\b/.test(cmd)) {
|
|
43
|
+
return EXECUTION_INTENT.VALIDATION;
|
|
44
|
+
}
|
|
45
|
+
if (/\b(run|exec|start|build)\b/.test(cmd)) {
|
|
46
|
+
return EXECUTION_INTENT.DIRECT_REQUIREMENT_ACTION;
|
|
47
|
+
}
|
|
48
|
+
return EXECUTION_INTENT.SUPPORTING_ACTION;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (stmt && /\brequirement\b/.test(stmt) && !/(install|setup|test|spec)/.test(cmd)) {
|
|
52
|
+
if (operation && MODIFICATION_TOOLS.has(operation)) {
|
|
53
|
+
return EXECUTION_INTENT.DIRECT_REQUIREMENT_ACTION;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return EXECUTION_INTENT.UNKNOWN;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isDirectRequirementAction(intent) {
|
|
61
|
+
return intent === EXECUTION_INTENT.DIRECT_REQUIREMENT_ACTION;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isSupportingAction(intent) {
|
|
65
|
+
return intent === EXECUTION_INTENT.SUPPORTING_ACTION ||
|
|
66
|
+
intent === EXECUTION_INTENT.INVESTIGATION ||
|
|
67
|
+
intent === EXECUTION_INTENT.VALIDATION ||
|
|
68
|
+
intent === EXECUTION_INTENT.RECOVERY;
|
|
69
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formal execution state — single source of truth for task lifecycle.
|
|
3
|
+
* Implements spec: formal-execution-state.
|
|
4
|
+
*
|
|
5
|
+
* States:
|
|
6
|
+
* INITIALIZING | INVESTIGATING | EXECUTING | VERIFYING | COMPLETED
|
|
7
|
+
* BLOCKED | WAITING_AUTHORIZATION | FAILED
|
|
8
|
+
*
|
|
9
|
+
* Transition table is strict: only documented transitions are allowed.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const VALID_STATES = new Set([
|
|
13
|
+
"INITIALIZING",
|
|
14
|
+
"INVESTIGATING",
|
|
15
|
+
"EXECUTING",
|
|
16
|
+
"VERIFYING",
|
|
17
|
+
"COMPLETED",
|
|
18
|
+
"BLOCKED",
|
|
19
|
+
"WAITING_AUTHORIZATION",
|
|
20
|
+
"FAILED",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const ALLOWED_TRANSITIONS = {
|
|
24
|
+
INITIALIZING: new Set(["INVESTIGATING", "BLOCKED"]),
|
|
25
|
+
INVESTIGATING: new Set(["EXECUTING", "WAITING_AUTHORIZATION", "BLOCKED"]),
|
|
26
|
+
EXECUTING: new Set(["VERIFYING", "BLOCKED", "FAILED"]),
|
|
27
|
+
VERIFYING: new Set(["COMPLETED", "EXECUTING", "FAILED"]),
|
|
28
|
+
WAITING_AUTHORIZATION: new Set(["EXECUTING", "BLOCKED"]),
|
|
29
|
+
BLOCKED: new Set(["INVESTIGATING", "EXECUTING"]),
|
|
30
|
+
FAILED: new Set(["INVESTIGATING"]),
|
|
31
|
+
COMPLETED: new Set(),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const LEGACY_PHASE_TO_STATE = {
|
|
35
|
+
ASKING: "WAITING_AUTHORIZATION",
|
|
36
|
+
PROPOSED: "INVESTIGATING",
|
|
37
|
+
IMPLEMENTING: "EXECUTING",
|
|
38
|
+
VERIFYING: "VERIFYING",
|
|
39
|
+
DONE: "COMPLETED",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function isValidState(s) {
|
|
43
|
+
return typeof s === "string" && VALID_STATES.has(s);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function migrateLegacyPhase(phase) {
|
|
47
|
+
return LEGACY_PHASE_TO_STATE[phase] || "INITIALIZING";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function validateState(s) {
|
|
51
|
+
if (!isValidState(s)) {
|
|
52
|
+
throw new Error(`Invalid execution state: ${JSON.stringify(s)}. Must be one of: ${[...VALID_STATES].join(", ")}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
import { hasOutstandingWork } from "../verification/verification-lifecycle.js";
|
|
57
|
+
|
|
58
|
+
export function transition(from, to, taskState = null) {
|
|
59
|
+
validateState(from);
|
|
60
|
+
validateState(to);
|
|
61
|
+
// Guard: block transition to COMPLETED if there is outstanding work
|
|
62
|
+
if (to === "COMPLETED" && hasOutstandingWork(taskState)) {
|
|
63
|
+
throw new Error("Cannot transition to COMPLETED while there is outstanding work (unverified requirements or pending backlog)");
|
|
64
|
+
}
|
|
65
|
+
const allowed = ALLOWED_TRANSITIONS[from];
|
|
66
|
+
if (!allowed.has(to)) {
|
|
67
|
+
throw new Error(`Invalid transition: ${from} → ${to}. Allowed from ${from}: ${[...allowed].join(", ") || "(none, terminal)"}`);
|
|
68
|
+
}
|
|
69
|
+
return to;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getStatusReport(taskState, { experiments = [], failures = [], pendingAuth = null, verification = null, completionEvidence = [] } = {}) {
|
|
73
|
+
if (!taskState) return "Sin estado de tarea";
|
|
74
|
+
const lines = [
|
|
75
|
+
`state: ${taskState.state || migrateLegacyPhase(taskState.phase)}`,
|
|
76
|
+
`strategy: ${taskState.approvedStrategy?.strategy || "—"}`,
|
|
77
|
+
`current hypothesis: ${taskState.currentHypothesis || "—"}`,
|
|
78
|
+
`experiments: ${experiments.length}`,
|
|
79
|
+
`failures: ${failures.length}`,
|
|
80
|
+
`pending authorization: ${pendingAuth ? pendingAuth.id || pendingAuth.question || "yes" : "none"}`,
|
|
81
|
+
`verification status: ${verification?.status || "pending"}`,
|
|
82
|
+
`completion evidence: ${completionEvidence.length} item(s)`,
|
|
83
|
+
];
|
|
84
|
+
// Add each piece of completion evidence with req id and evidence
|
|
85
|
+
for (const item of completionEvidence) {
|
|
86
|
+
lines.push(`${item.req}: ${item.evidence}`);
|
|
87
|
+
}
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting — funciones puras de formateo y presentación.
|
|
3
|
+
*
|
|
4
|
+
* Contiene:
|
|
5
|
+
* - truncate: utilidad de truncamiento de texto (compartida)
|
|
6
|
+
* - delegationLines: formatea lines de delegación para el inyector
|
|
7
|
+
* - getStatusReport: formatea reporte de estado (re-exportado)
|
|
8
|
+
*
|
|
9
|
+
* Todas son PURAS: toman datos como entrada, retornan strings/arrays.
|
|
10
|
+
* Sin side effects (no persisten, no leen, no escriben).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { getStatusReport } from "./execution-state.js";
|
|
14
|
+
|
|
15
|
+
export { getStatusReport } from "./execution-state.js";
|
|
16
|
+
|
|
17
|
+
// -- Shared utility --------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Trunca texto a máximo `max` caracteres con "..." al final.
|
|
21
|
+
*/
|
|
22
|
+
export function truncate(text, max = 80) {
|
|
23
|
+
if (!text || text.length <= max) return text || "";
|
|
24
|
+
return text.slice(0, max - 3) + "...";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// -- Delegation lines -------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
const MAX_VISIBLE_REQS = 8;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Formatea las líneas de delegación visible para el inyector de sistema.
|
|
33
|
+
* Solo se muestra when contract está APPROVED con requisitos pendientes.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} state - Estado de la tarea
|
|
36
|
+
* @param {Function} [domainHintFn] - Función opcional para clasificar dominio (ej: domainHint de index.js)
|
|
37
|
+
* @returns {string[]} Líneas de delegación
|
|
38
|
+
*/
|
|
39
|
+
export function delegationLines(state, domainHintFn = null) {
|
|
40
|
+
const reqs = state?.contract?.requirements || [];
|
|
41
|
+
if (!reqs.length) return [];
|
|
42
|
+
if (state?.contract?.status !== "APPROVED") return [];
|
|
43
|
+
|
|
44
|
+
const pending = reqs.filter((r) => r.status !== "done" && r.status !== "verified");
|
|
45
|
+
if (!pending.length) return [];
|
|
46
|
+
|
|
47
|
+
const lines = [
|
|
48
|
+
"⏳ Delegación: estos requerimientos están aprobados para ejecución paralela:",
|
|
49
|
+
];
|
|
50
|
+
const visible = pending.slice(0, MAX_VISIBLE_REQS);
|
|
51
|
+
const overflow = pending.length - MAX_VISIBLE_REQS;
|
|
52
|
+
for (const r of visible) {
|
|
53
|
+
const hint = domainHintFn ? domainHintFn(r.title) : "";
|
|
54
|
+
lines.push(` ${r.id} → Task(parallel) "${truncate(r.title, 120)}"${hint ? ` [contexto: ${hint}]` : ""}`);
|
|
55
|
+
}
|
|
56
|
+
if (overflow > 0) {
|
|
57
|
+
lines.push(` ...(+${overflow} requerimientos adicionales consolidados)`);
|
|
58
|
+
}
|
|
59
|
+
return lines;
|
|
60
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export function sanitizeShellArg(arg) {
|
|
4
|
+
// Simple shell escaping
|
|
5
|
+
return "'" + arg.replace(/'/g, "'\\''") + "'";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function sanitizePath(unsafePath) {
|
|
9
|
+
// Resolve and verify base to prevent traversal
|
|
10
|
+
const resolvedPath = path.resolve(unsafePath);
|
|
11
|
+
return resolvedPath;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function stripControlChars(str) {
|
|
15
|
+
return str.replace(/[\x00-\x1F\x7F]/g, "");
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class ResourceGuard {
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this.maxDuration = options.maxDuration || 5000;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async runWithTimeout(task) {
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
const timeout = setTimeout(() => controller.abort(), this.maxDuration);
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
return await Promise.race([
|
|
12
|
+
task(controller.signal),
|
|
13
|
+
new Promise((_, reject) => controller.signal.addEventListener('abort', () => reject(new Error("AbortError"))))
|
|
14
|
+
]);
|
|
15
|
+
} finally {
|
|
16
|
+
clearTimeout(timeout);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { findRepeatedExperiment, hasRepetitiveFailure } from "../cognition/cognition-store.js";
|
|
2
|
+
import { evaluateAction } from "../policy/risk-engine.js";
|
|
3
|
+
import { getCapability, CAPABILITY_LEVELS } from "../policy/action-capabilities.js";
|
|
4
|
+
|
|
5
|
+
const HARD_DELETE_RE =
|
|
6
|
+
/\b(rmdir|unlink|shred|truncate)\b|\brm\s+(-[a-zA-Z]*f|--force)|\bgit\s+reset\s+--hard\b|\b(DROP|TRUNCATE)\s+(TABLE|DATABASE)\b/i;
|
|
7
|
+
|
|
8
|
+
const HARD_DELETE_TOOLS = new Set(["rm", "rmdir"]);
|
|
9
|
+
|
|
10
|
+
function isHardDelete(tool, args = {}) {
|
|
11
|
+
const t = String(tool || "").toLowerCase();
|
|
12
|
+
if (HARD_DELETE_TOOLS.has(t)) return true;
|
|
13
|
+
const cmd = String(args.command || args.cmd || "");
|
|
14
|
+
return HARD_DELETE_RE.test(cmd);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function actionDescription(tool, args = {}) {
|
|
18
|
+
const { hypothesisId: _h, ...rest } = args;
|
|
19
|
+
return `${tool}:${JSON.stringify(rest)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Autonomy Runtime Guards
|
|
24
|
+
*
|
|
25
|
+
* Intercepta ejecución para evaluar:
|
|
26
|
+
* 1. Riesgo de la acción (Risk Envelope)
|
|
27
|
+
* 2. Stagnation/Repetition (cognition-store)
|
|
28
|
+
* 3. Hard-delete ban (U1: soft-archive only)
|
|
29
|
+
* 4. Scope enforcement
|
|
30
|
+
*/
|
|
31
|
+
export async function guardAction(tool, args, taskRoot, taskId) {
|
|
32
|
+
// 0. Fail-closed (C01): herramientas no catalogadas se bloquean aquí,
|
|
33
|
+
// ANTES de cualquier otra evaluación. Esto crea una segunda línea
|
|
34
|
+
// de defensa independiente del risk-engine: aunque la evaluación
|
|
35
|
+
// contextual cambie, una herramienta desconocida nunca llega a
|
|
36
|
+
// ejecutarse.
|
|
37
|
+
const capability = getCapability(tool);
|
|
38
|
+
if (capability === CAPABILITY_LEVELS.UNKNOWN) {
|
|
39
|
+
return {
|
|
40
|
+
allowed: false,
|
|
41
|
+
level: "BLOCKED",
|
|
42
|
+
reason: `fail-closed: herramienta no catalogada (${tool})`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isHardDelete(tool, args)) {
|
|
47
|
+
return {
|
|
48
|
+
allowed: false,
|
|
49
|
+
level: "BLOCKED",
|
|
50
|
+
reason: "hard delete forbidden (U1: soft-archive only)",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let risk;
|
|
55
|
+
try {
|
|
56
|
+
risk = evaluateAction(tool, args, taskRoot);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
const name = err?.name || err?.constructor?.name;
|
|
59
|
+
if (name === "WamPolicyBlock") {
|
|
60
|
+
return { allowed: false, level: "BLOCKED", reason: err.message };
|
|
61
|
+
}
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (risk.level === "BLOCKED") {
|
|
66
|
+
return {
|
|
67
|
+
allowed: false,
|
|
68
|
+
level: "BLOCKED",
|
|
69
|
+
reason: risk.reason || "destructive or irreversible action blocked",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const desc = actionDescription(tool, args);
|
|
74
|
+
const probe = {
|
|
75
|
+
hypothesisId: args?.hypothesisId || "unknown",
|
|
76
|
+
actionDescription: desc,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (hasRepetitiveFailure(taskRoot, taskId, probe)) {
|
|
80
|
+
return {
|
|
81
|
+
allowed: false,
|
|
82
|
+
level: risk.level,
|
|
83
|
+
reason: "repetitive failure: same hypothesis+action already failed. Replan with a new hypothesis.",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const repeated = findRepeatedExperiment(taskRoot, taskId, probe);
|
|
88
|
+
|
|
89
|
+
if (risk.level === "GUARDED" && !args?.hypothesisId) {
|
|
90
|
+
return {
|
|
91
|
+
allowed: true,
|
|
92
|
+
level: risk.level,
|
|
93
|
+
warning: "GUARDED action without hypothesisId — attach hypothesis before mutating.",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (repeated) {
|
|
98
|
+
return {
|
|
99
|
+
allowed: true,
|
|
100
|
+
level: risk.level,
|
|
101
|
+
warning: `Repetición detectada: ya se intentó ${repeated.status}. Considera replanificar.`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { allowed: true, level: risk.level };
|
|
106
|
+
}
|