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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +318 -2
  3. package/index.js +1836 -0
  4. package/package.json +70 -4
  5. package/skills/codebase-design/SKILL.md +114 -0
  6. package/skills/design-contract/SKILL.md +27 -0
  7. package/skills/design-critique/SKILL.md +19 -0
  8. package/skills/design-system/SKILL.md +17 -0
  9. package/skills/design-system/references/image-analysis.md +14 -0
  10. package/skills/design-system/references/tokens.md +32 -0
  11. package/skills/design-system/references/ui-rules.md +14 -0
  12. package/skills/diagnosing-bugs/SKILL.md +138 -0
  13. package/skills/frontend-design/SKILL.md +36 -0
  14. package/skills/handoff/SKILL.md +16 -0
  15. package/skills/improve-codebase-architecture/SKILL.md +71 -0
  16. package/skills/linter-negative/SKILL.md +74 -0
  17. package/skills/no-slop-ui/SKILL.md +26 -0
  18. package/skills/prototype/SKILL.md +26 -0
  19. package/skills/registry.json +44087 -0
  20. package/skills/tastemaker/SKILL.md +8 -0
  21. package/skills/tastemaker/references/tastemaker.md +14 -0
  22. package/skills/tdd/SKILL.md +38 -0
  23. package/skills/visual-review/SKILL.md +103 -0
  24. package/skills/visual-review/references/review-gates.md +42 -0
  25. package/skills/visual-review/references/surface-archetypes.md +37 -0
  26. package/skills/wizard/SKILL.md +44 -0
  27. package/skills/writing-for-agents/SKILL.md +81 -0
  28. package/src/cognition/cognition-store.js +251 -0
  29. package/src/cognition/cognitive-state.js +126 -0
  30. package/src/cognition/hypothesis-manager.js +113 -0
  31. package/src/cognition/observation-engine.js +112 -0
  32. package/src/cognition/observation-provenance.js +268 -0
  33. package/src/context/active-context-boundary.js +68 -0
  34. package/src/context/assembly.js +494 -0
  35. package/src/context/assessment-rules.js +55 -0
  36. package/src/context/capsule-staleness.js +267 -0
  37. package/src/context/capsules.js +2 -0
  38. package/src/context/compare-array.js +98 -0
  39. package/src/context/compare-object.js +89 -0
  40. package/src/context/compare-values.js +42 -0
  41. package/src/context/context-assembly-contract.js +71 -0
  42. package/src/context/context-budget-manager.js +232 -0
  43. package/src/context/context-builder.js +49 -0
  44. package/src/context/context-cache.js +55 -0
  45. package/src/context/context-capture.js +243 -0
  46. package/src/context/context-classification.js +199 -0
  47. package/src/context/context-compaction.js +179 -0
  48. package/src/context/context-decision-audit.js +267 -0
  49. package/src/context/context-demotion.js +173 -0
  50. package/src/context/context-dictionary.js +106 -0
  51. package/src/context/context-drift-detection.js +225 -0
  52. package/src/context/context-evaluation.js +374 -0
  53. package/src/context/context-event-ingress.js +100 -0
  54. package/src/context/context-event-normalization.js +224 -0
  55. package/src/context/context-feedback-loop.js +216 -0
  56. package/src/context/context-freshness.js +21 -0
  57. package/src/context/context-graph-builder.js +71 -0
  58. package/src/context/context-graph.js +451 -0
  59. package/src/context/context-history-relevance.js +321 -0
  60. package/src/context/context-interception.js +145 -0
  61. package/src/context/context-invalidation-events.js +154 -0
  62. package/src/context/context-lifecycle.js +170 -0
  63. package/src/context/context-loop-prevention.js +347 -0
  64. package/src/context/context-manager.js +340 -0
  65. package/src/context/context-memory-layers.js +265 -0
  66. package/src/context/context-minimality.js +166 -0
  67. package/src/context/context-normalization.js +130 -0
  68. package/src/context/context-optimization-metrics.js +434 -0
  69. package/src/context/context-output-policy.js +135 -0
  70. package/src/context/context-pack-authority.js +157 -0
  71. package/src/context/context-promotion.js +172 -0
  72. package/src/context/context-query-contract.js +101 -0
  73. package/src/context/context-ranking.js +164 -0
  74. package/src/context/context-rate-distortion.js +261 -0
  75. package/src/context/context-recovery.js +209 -0
  76. package/src/context/context-retrieval-integration.js +60 -0
  77. package/src/context/context-retrieval.js +93 -0
  78. package/src/context/context-router.js +457 -0
  79. package/src/context/context-routing-evaluation.js +331 -0
  80. package/src/context/context-routing-observability.js +296 -0
  81. package/src/context/context-routing-shadow.js +237 -0
  82. package/src/context/context-scope.js +181 -0
  83. package/src/context/context-snapshot.js +226 -0
  84. package/src/context/context-source-registry.js +95 -0
  85. package/src/context/context-sufficiency-gate.js +120 -0
  86. package/src/context/context-sufficiency-oracle.js +167 -0
  87. package/src/context/context-telemetry.js +176 -0
  88. package/src/context/context.js +633 -0
  89. package/src/context/dependency-closure.js +246 -0
  90. package/src/context/page-fault-tracker.js +275 -0
  91. package/src/context/prompt-engine.js +74 -0
  92. package/src/context/runtime-context-graph.js +260 -0
  93. package/src/context/selection.js +2 -0
  94. package/src/context/session.js +2 -0
  95. package/src/context/sufficiency-contract.js +300 -0
  96. package/src/context/tokenizer.js +17 -0
  97. package/src/context-benchmark-router.mjs +125 -0
  98. package/src/evidence/evidence-engine.js +161 -0
  99. package/src/evidence/evidence-factory.js +3 -0
  100. package/src/evidence/evidence-freshness.js +463 -0
  101. package/src/evidence/evidence-gap.js +76 -0
  102. package/src/evidence/evidence-lineage.js +525 -0
  103. package/src/evidence/evidence.js +136 -0
  104. package/src/evidence/freshness-validation.js +187 -0
  105. package/src/evidence/lineage.js +4 -0
  106. package/src/execution/action-evaluation.js +179 -0
  107. package/src/execution/circuit-breaker.js +70 -0
  108. package/src/execution/claim-interception.js +63 -0
  109. package/src/execution/execution-engine.js +258 -0
  110. package/src/execution/execution-intent.js +69 -0
  111. package/src/execution/execution-state.js +89 -0
  112. package/src/execution/formatting.js +60 -0
  113. package/src/execution/l1-sanitize.js +16 -0
  114. package/src/execution/l2-resource-guards.js +19 -0
  115. package/src/execution/l3-invariant-checker.js +5 -0
  116. package/src/execution/l4-containment.js +6 -0
  117. package/src/execution/runtime-guards.js +106 -0
  118. package/src/execution/strategy-identity.js +162 -0
  119. package/src/execution/tool-interceptor.js +7 -0
  120. package/src/execution/tool-result-classifier.js +63 -0
  121. package/src/integration/canary-deploy.js +143 -0
  122. package/src/integration/collector.js +59 -0
  123. package/src/integration/error-recovery.js +6 -0
  124. package/src/integration/event-bus.js +66 -0
  125. package/src/integration/health-checker.js +57 -0
  126. package/src/integration/logger.js +39 -0
  127. package/src/integration/message-handler.js +611 -0
  128. package/src/integration/opencode-db.js +153 -0
  129. package/src/integration/orchestration.js +419 -0
  130. package/src/integration/part-provenance.js +65 -0
  131. package/src/integration/rate-limiter.js +55 -0
  132. package/src/integration/release-gate.js +336 -0
  133. package/src/integration/rollback-manager.js +164 -0
  134. package/src/integration/router-adapter.js +269 -0
  135. package/src/integration/telemetry.js +45 -0
  136. package/src/integration/update-checker.js +10 -0
  137. package/src/persistence/event-sourcing.js +75 -0
  138. package/src/persistence/memory.js +501 -0
  139. package/src/persistence/migration-runner.js +16 -0
  140. package/src/persistence/persistence-manager.js +39 -0
  141. package/src/persistence/transaction-log.js +157 -0
  142. package/src/policy/action-capabilities.js +116 -0
  143. package/src/policy/action-policy.js +4 -0
  144. package/src/policy/assessment-engine.js +104 -0
  145. package/src/policy/assumption-tracking.js +66 -0
  146. package/src/policy/budget-enforcer.js +7 -0
  147. package/src/policy/config-validator.js +102 -0
  148. package/src/policy/execution-assessment.js +142 -0
  149. package/src/policy/failure-classification.js +5 -0
  150. package/src/policy/feature-flags.js +65 -0
  151. package/src/policy/governance-enforcement.js +118 -0
  152. package/src/policy/locale.js +15 -0
  153. package/src/policy/multi-signer.js +151 -0
  154. package/src/policy/policy-state-machine.js +97 -0
  155. package/src/policy/ponytail-constraint.js +38 -0
  156. package/src/policy/project-inspector.js +93 -0
  157. package/src/policy/query-builder.js +14 -0
  158. package/src/policy/request-classifier.js +79 -0
  159. package/src/policy/risk-engine.js +266 -0
  160. package/src/policy/scope-enforcement.js +140 -0
  161. package/src/policy/secret-scanner.js +68 -0
  162. package/src/policy/skill-routing.js +104 -0
  163. package/src/policy/strategy-capabilities.js +211 -0
  164. package/src/policy/strategy.js +6 -0
  165. package/src/policy/uncertainty.js +117 -0
  166. package/src/shared/container.js +44 -0
  167. package/src/shared/decision-types.js +24 -0
  168. package/src/shared/simple-test.js +160 -0
  169. package/src/shared/wam-events.js +52 -0
  170. package/src/skills/engine.js +1705 -0
  171. package/src/skills/skill-registry.js +19 -0
  172. package/src/skills/skill-routing.js +340 -0
  173. package/src/state/lifecycle-manager.js +113 -0
  174. package/src/state/operational-state.js +198 -0
  175. package/src/state/projections.js +47 -0
  176. package/src/state/replay-engine.js +5 -0
  177. package/src/state/requirement-ownership.js +20 -0
  178. package/src/state/requirement-state.js +152 -0
  179. package/src/state/run-event-identity.js +172 -0
  180. package/src/state/state-engine.js +19 -0
  181. package/src/state/state-machine.js +119 -0
  182. package/src/state/state-schema.js +318 -0
  183. package/src/state/state-store.js +335 -0
  184. package/src/state/state-transition.js +84 -0
  185. package/src/state/task-dependencies.js +287 -0
  186. package/src/state/task-execution.js +217 -0
  187. package/src/state/task-lifecycle.js +59 -0
  188. package/src/state/task-runs.js +361 -0
  189. package/src/state/task-state-file.js +86 -0
  190. package/src/state/task-state.js +41 -0
  191. package/src/state/task-status.js +54 -0
  192. package/src/state/task-store.js +48 -0
  193. package/src/state/wam-state.js +148 -0
  194. package/src/verification/completion-gate-simple.js +8 -0
  195. package/src/verification/completion-gate.js +183 -0
  196. package/src/verification/contract.js +9 -0
  197. package/src/verification/contradiction-resolution.js +255 -0
  198. package/src/verification/diagnostic-engine.js +659 -0
  199. package/src/verification/false-completion-prevention.js +100 -0
  200. package/src/verification/hook-validator.js +6 -0
  201. package/src/verification/requirements.js +6 -0
  202. package/src/verification/verification-context.js +162 -0
  203. package/src/verification/verification-lifecycle.js +66 -0
  204. package/src/verification/verification-model.js +89 -0
  205. package/src/verification/verification-persistence.js +129 -0
  206. package/src/verification/verification-policy.js +127 -0
  207. package/src/verification/verification.js +415 -0
@@ -0,0 +1,5 @@
1
+ export function classifyFailure(observation) {
2
+ if (!observation) return { category: "UNKNOWN", confidence: 0 };
3
+ if (observation.error?.includes("timeout")) return { category: "TIMEOUT", confidence: 0.9 };
4
+ return { category: "GENERIC", confidence: 0.5 };
5
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Feature Flags — in-memory key/value toggles with defaults.
3
+ *
4
+ * Supports: get/set/has/toggle, with optional per-user overrides.
5
+ * Production hardening (M5-55).
6
+ */
7
+
8
+ /**
9
+ * @typedef {Object} FeatureFlags
10
+ * @property {(key: string, fallback?: boolean) => boolean} isEnabled
11
+ * @property {(key: string, value: boolean) => void} set
12
+ * @property {(key: string) => boolean} has
13
+ * @property {(key: string) => void} toggle
14
+ * @property {() => string[]} list
15
+ * @property {(ctx: {userId?: string}) => FeatureFlags} forContext
16
+ */
17
+
18
+ /**
19
+ * @param {Record<string, boolean>} initial
20
+ * @returns {FeatureFlags}
21
+ */
22
+ export function createFeatureFlags(initial = {}) {
23
+ /** @type {Map<string, boolean>} */
24
+ const store = new Map(Object.entries(initial));
25
+
26
+ function isEnabled(key, fallback = false) {
27
+ return store.has(key) ? store.get(key) : fallback;
28
+ }
29
+
30
+ function set(key, value) {
31
+ store.set(key, Boolean(value));
32
+ }
33
+
34
+ function has(key) {
35
+ return store.has(key);
36
+ }
37
+
38
+ function toggle(key) {
39
+ const next = !isEnabled(key);
40
+ store.set(key, next);
41
+ return next;
42
+ }
43
+
44
+ function list() {
45
+ return [...store.keys()];
46
+ }
47
+
48
+ function forContext(ctx) {
49
+ const userId = ctx?.userId;
50
+ return {
51
+ isEnabled: (key, fallback = false) => {
52
+ if (userId && store.has(`${key}:${userId}`)) {
53
+ return store.get(`${key}:${userId}`);
54
+ }
55
+ return isEnabled(key, fallback);
56
+ },
57
+ set: (key, value) => {
58
+ if (userId) store.set(`${key}:${userId}`, Boolean(value));
59
+ else set(key, value);
60
+ },
61
+ };
62
+ }
63
+
64
+ return { isEnabled, set, has, toggle, list, forContext };
65
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Governance Enforcement — Centralized policy check for tool execution.
3
+ *
4
+ * Returns a decision object, or throws WamPolicyBlock when governance is
5
+ * violated. Invariants:
6
+ * - Only CONTRACT_GATED_TOOLS are ever blocked; `bash`/`task` (investigation
7
+ * and delegation surfaces) stay usable so tests can run and workers can be
8
+ * dispatched.
9
+ * - A session with no tracked task/contract fails OPEN: there is nothing to
10
+ * approve, and default-deny here deadlocked every write/edit.
11
+ * - Delegated workers (sessions with a parentID) are exempt.
12
+ * - An explicit, audited override disables enforcement (WAM_GOVERNANCE=off).
13
+ */
14
+
15
+ import { WamPolicyBlock } from "./risk-engine.js";
16
+ import { logger } from "../integration/logger.js";
17
+
18
+ /** Tools gated by contract approval. Deliberately narrower than the risk
19
+ * engine's MUTATING_TOOLS (which also covers bash/task). */
20
+ export const CONTRACT_GATED_TOOLS = new Set([
21
+ "write", "edit", "apply_patch", "patch", "todo_write", "todowrite",
22
+ ]);
23
+
24
+ /** Phases from which no further mutation gate applies. */
25
+ export const TERMINAL_PHASES = new Set(["COMPLETE", "DONE"]);
26
+
27
+ const TRIVIAL_MAX_FILES = Number.parseInt(process.env.WAM_TRIVIAL_MAX_FILES || "2", 10);
28
+
29
+ /** Paths that always require an approved contract regardless of file count. */
30
+ const PROTECTED_SEGMENT_RE = /(^|[\\/])(\.github|migrations?|deploy|\.env)([\\/]|$)/i;
31
+ const PROTECTED_BASENAME_RE = /(^|[\\/])(ci\.ya?ml|package\.json|package-lock\.json|tsconfig[^\\/]*\.json)$/i;
32
+
33
+ export function isGovernanceDisabled() {
34
+ const v = String(process.env.WAM_GOVERNANCE || "").toLowerCase();
35
+ return v === "off" || v === "0" || process.env.WAM_BYPASS === "1";
36
+ }
37
+
38
+ export function isTerminalPhase(phase) {
39
+ return TERMINAL_PHASES.has(phase);
40
+ }
41
+
42
+ export function isProtectedPath(file) {
43
+ const f = String(file || "");
44
+ return PROTECTED_SEGMENT_RE.test(f) || PROTECTED_BASENAME_RE.test(f);
45
+ }
46
+
47
+ export function isTrivialChange(declaredFiles) {
48
+ if (!Array.isArray(declaredFiles) || declaredFiles.length === 0) return false;
49
+ if (declaredFiles.length > TRIVIAL_MAX_FILES) return false;
50
+ return declaredFiles.every((f) => !isProtectedPath(f));
51
+ }
52
+
53
+ function logDecision(event) {
54
+ try {
55
+ logger.info("governance", JSON.stringify(event));
56
+ } catch {
57
+ /* logging must never break enforcement */
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @param {string} tool
63
+ * @param {object|null} state - task state ({ phase, contract, requirements }).
64
+ * @param {object} [ctx]
65
+ * @param {boolean} [ctx.isSubagent] - true when the caller is a delegated worker.
66
+ * @param {string[]} [ctx.declaredFiles] - files the current task is scoped to.
67
+ * @returns {{allowed: boolean, reason: string}}
68
+ * @throws {WamPolicyBlock}
69
+ */
70
+ export function enforceGovernance(tool, state, ctx = {}) {
71
+ if (!CONTRACT_GATED_TOOLS.has(tool)) return { allowed: true, reason: "tool-not-gated" };
72
+
73
+ if (isGovernanceDisabled()) {
74
+ logDecision({ decision: "allow", reason: "override", tool, phase: state?.phase });
75
+ return { allowed: true, reason: "override" };
76
+ }
77
+
78
+ if (ctx.isSubagent) {
79
+ logDecision({ decision: "allow", reason: "subagent", tool, phase: state?.phase });
80
+ return { allowed: true, reason: "subagent" };
81
+ }
82
+
83
+ // No tracked task/contract: nothing to approve → fail open (never deadlock
84
+ // ad-hoc/untracked sessions before a contract can exist).
85
+ if (!state) {
86
+ logDecision({ decision: "allow", reason: "untracked", tool });
87
+ return { allowed: true, reason: "untracked" };
88
+ }
89
+
90
+ const contractStatus = state?.contract?.status;
91
+ const phase = state?.phase;
92
+
93
+ if (contractStatus === "APPROVED") return { allowed: true, reason: "contract-approved" };
94
+ if (isTerminalPhase(phase)) return { allowed: true, reason: "terminal-phase" };
95
+ // Let the Clarification Gate handle ASKING (answer the unknown, don't approve).
96
+ if (phase === "ASKING") return { allowed: true, reason: "defer-asking" };
97
+ if (isTrivialChange(ctx.declaredFiles)) {
98
+ logDecision({ decision: "allow", reason: "trivial", tool, files: ctx.declaredFiles });
99
+ return { allowed: true, reason: "trivial" };
100
+ }
101
+
102
+ const pending = (state.requirements || state.contract?.requirements || [])
103
+ .filter((r) => r.status !== "done" && r.status !== "verified").length;
104
+
105
+ const directive =
106
+ `[wait-a-minute] GOVERNANCE BLOCK (${tool}): contrato no aprobado ` +
107
+ `(fase ${phase || "?"}). ${pending} requisito(s) pendiente(s). ` +
108
+ `Aprobá el contrato (/wam contract approve) o pasá el token de aprobación en el Task. ` +
109
+ `Si sos subagente, ESCALATE al orquestador — no explores buscando un camino alternativo.`;
110
+
111
+ logDecision({ decision: "deny", tool, phase, contractStatus, files: ctx.declaredFiles || [] });
112
+ throw new WamPolicyBlock(directive, {
113
+ tool,
114
+ reason: "contract-not-approved",
115
+ level: "BLOCKED",
116
+ source: "governance-enforcement",
117
+ });
118
+ }
@@ -0,0 +1,15 @@
1
+ export function createLocale(defaultLang, dict = {}) {
2
+ let currentLang = defaultLang;
3
+ let translations = dict;
4
+
5
+ return {
6
+ t: (key, vars = {}) => {
7
+ let str = translations[currentLang]?.[key] || key;
8
+ for (const [k, v] of Object.entries(vars)) {
9
+ str = str.replace(`{${k}}`, v);
10
+ }
11
+ return str;
12
+ },
13
+ setLang: (lang) => { currentLang = lang; }
14
+ };
15
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Multi-Signer Approval
3
+ *
4
+ * N-of-M approval quorum for strict release mode. Each signer may approve
5
+ * at most once per change; a signer cannot both propose and approve.
6
+ *
7
+ * Config shape (`config`):
8
+ * {
9
+ * signers: string[], // M: the eligible signer pool
10
+ * required: number, // N: approvals needed (default = signers.length)
11
+ * mode: "strict" | "majority" | "any",// policy hint; quorum = required
12
+ * }
13
+ *
14
+ * API:
15
+ * createMultiSigner(config) -> {
16
+ * approve(changeId, signerId) -> { ok, changeId, signerId, tally, reason }
17
+ * status(changeId) -> { tally, required, total, satisfied, approvals }
18
+ * reset(changeId?) -> void
19
+ * }
20
+ */
21
+
22
+ const VALID_MODES = new Set(["strict", "majority", "any"]);
23
+
24
+ function normalizeConfig(config = {}) {
25
+ const signers = Array.isArray(config.signers)
26
+ ? config.signers.filter((s) => typeof s === "string" && s.length > 0)
27
+ : [];
28
+ if (signers.length === 0) {
29
+ throw new Error("multi-signer config requires non-empty signers array");
30
+ }
31
+ const explicit = Number.isFinite(config.required) ? config.required : null;
32
+ let required;
33
+ if (explicit !== null) {
34
+ if (explicit < 1 || explicit > signers.length) {
35
+ throw new Error(
36
+ `multi-signer required (${explicit}) must be in 1..${signers.length}`
37
+ );
38
+ }
39
+ required = explicit;
40
+ } else {
41
+ const mode = VALID_MODES.has(config.mode) ? config.mode : "strict";
42
+ if (mode === "strict") required = signers.length;
43
+ else if (mode === "majority") required = Math.floor(signers.length / 2) + 1;
44
+ else required = 1;
45
+ }
46
+ return { signers, required, mode: config.mode ?? "strict" };
47
+ }
48
+
49
+ export function createMultiSigner(config = {}) {
50
+ const { signers, required, mode } = normalizeConfig(config);
51
+ const signerSet = new Set(signers);
52
+ const tallies = new Map();
53
+
54
+ function getTally(changeId) {
55
+ let tally = tallies.get(changeId);
56
+ if (!tally) {
57
+ tally = {
58
+ changeId,
59
+ approvals: [],
60
+ rejected: [],
61
+ };
62
+ tallies.set(changeId, tally);
63
+ }
64
+ return tally;
65
+ }
66
+
67
+ function approve(changeId, signerId) {
68
+ if (typeof changeId !== "string" || !changeId) {
69
+ return {
70
+ ok: false,
71
+ changeId,
72
+ signerId,
73
+ reason: "changeId required",
74
+ tally: null,
75
+ satisfied: false,
76
+ };
77
+ }
78
+ if (typeof signerId !== "string" || !signerId) {
79
+ return {
80
+ ok: false,
81
+ changeId,
82
+ signerId,
83
+ reason: "signerId required",
84
+ tally: null,
85
+ satisfied: false,
86
+ };
87
+ }
88
+ if (!signerSet.has(signerId)) {
89
+ return {
90
+ ok: false,
91
+ changeId,
92
+ signerId,
93
+ reason: `signer not eligible: ${signerId}`,
94
+ tally: status(changeId),
95
+ satisfied: false,
96
+ };
97
+ }
98
+ const tally = getTally(changeId);
99
+ if (tally.approvals.includes(signerId)) {
100
+ return {
101
+ ok: false,
102
+ changeId,
103
+ signerId,
104
+ reason: "duplicate approval",
105
+ tally: status(changeId),
106
+ satisfied: false,
107
+ };
108
+ }
109
+ tally.approvals.push(signerId);
110
+ const tallyView = status(changeId);
111
+ return {
112
+ ok: true,
113
+ changeId,
114
+ signerId,
115
+ reason: tallyView.satisfied ? "quorum reached" : "approval recorded",
116
+ tally: tallyView,
117
+ satisfied: tallyView.satisfied,
118
+ };
119
+ }
120
+
121
+ function status(changeId) {
122
+ const tally = tallies.get(changeId) || {
123
+ changeId,
124
+ approvals: [],
125
+ rejected: [],
126
+ };
127
+ return {
128
+ changeId,
129
+ required,
130
+ total: signers.length,
131
+ signed: tally.approvals.length,
132
+ approvals: [...tally.approvals],
133
+ satisfied: tally.approvals.length >= required,
134
+ };
135
+ }
136
+
137
+ function reset(changeId) {
138
+ if (changeId === undefined) {
139
+ tallies.clear();
140
+ return;
141
+ }
142
+ tallies.delete(changeId);
143
+ }
144
+
145
+ return {
146
+ approve,
147
+ status,
148
+ reset,
149
+ config: { signers: [...signers], required, mode },
150
+ };
151
+ }
@@ -0,0 +1,97 @@
1
+ // Change 37 — Policy-state-machine
2
+ // Policy state machine with states, transitions, decisions, and preconditions.
3
+
4
+ export const POLICY_STATES = {
5
+ SCOPE: "Scope",
6
+ INVESTIGATE: "Investigate",
7
+ ACTION: "Action",
8
+ DEBUG: "Debug",
9
+ OBSERVE: "Observe",
10
+ VERIFY: "Verify",
11
+ REVIEW: "Review",
12
+ COMPLETION: "Completion"
13
+ };
14
+
15
+ export const POLICY_TRANSITIONS = {
16
+ [POLICY_STATES.SCOPE]: [POLICY_STATES.INVESTIGATE],
17
+ [POLICY_STATES.INVESTIGATE]: [POLICY_STATES.ACTION, POLICY_STATES.DEBUG],
18
+ [POLICY_STATES.ACTION]: [POLICY_STATES.OBSERVE, POLICY_STATES.VERIFY],
19
+ [POLICY_STATES.DEBUG]: [POLICY_STATES.OBSERVE, POLICY_STATES.VERIFY],
20
+ [POLICY_STATES.OBSERVE]: [POLICY_STATES.VERIFY, POLICY_STATES.REVIEW],
21
+ [POLICY_STATES.VERIFY]: [POLICY_STATES.REVIEW, POLICY_STATES.COMPLETION],
22
+ [POLICY_STATES.REVIEW]: [POLICY_STATES.COMPLETION, POLICY_STATES.INVESTIGATE],
23
+ [POLICY_STATES.COMPLETION]: []
24
+ };
25
+
26
+ // Change 36 — Policy-coordination (validatePolicyFlow, getNextPolicy)
27
+ export const POLICY_CHAIN = [
28
+ POLICY_STATES.SCOPE,
29
+ POLICY_STATES.INVESTIGATE,
30
+ POLICY_STATES.ACTION,
31
+ POLICY_STATES.DEBUG,
32
+ POLICY_STATES.OBSERVE,
33
+ POLICY_STATES.VERIFY,
34
+ POLICY_STATES.REVIEW,
35
+ POLICY_STATES.COMPLETION
36
+ ];
37
+
38
+ export function validatePolicyFlow(currentPolicy, nextPolicy) {
39
+ if (currentPolicy === nextPolicy) return { valid: false, reason: "same-policy" };
40
+ const allowed = POLICY_TRANSITIONS[currentPolicy] || [];
41
+ if (!allowed.includes(nextPolicy)) return { valid: false, reason: "invalid-transition" };
42
+ return { valid: true };
43
+ }
44
+
45
+ export function getNextPolicy(currentPolicy) {
46
+ const idx = POLICY_CHAIN.indexOf(currentPolicy);
47
+ if (idx === -1 || idx >= POLICY_CHAIN.length - 1) return null;
48
+ return POLICY_CHAIN[idx + 1];
49
+ }
50
+
51
+ export function createPolicyDecision(policy, action, reason, requirementsAffected = [], expectedObservation = null) {
52
+ return {
53
+ policy,
54
+ action,
55
+ reason,
56
+ requirementsAffected,
57
+ expectedObservation,
58
+ timestamp: Date.now()
59
+ };
60
+ }
61
+
62
+ export function isValidPolicyTransition(fromPolicy, toPolicy) {
63
+ const allowed = POLICY_TRANSITIONS[fromPolicy];
64
+ return allowed ? allowed.includes(toPolicy) : false;
65
+ }
66
+
67
+ // Change 38 — Policy-preconditions
68
+ export const POLICY_PRECONDITIONS = {
69
+ [POLICY_STATES.INVESTIGATE]: { contextSufficient: true },
70
+ [POLICY_STATES.ACTION]: { requirementKnown: true },
71
+ [POLICY_STATES.DEBUG]: { requirementKnown: true },
72
+ [POLICY_STATES.VERIFY]: { evidenceAvailable: true },
73
+ [POLICY_STATES.REVIEW]: { allVerified: false } // can review even if not all verified
74
+ };
75
+
76
+ export function checkPolicyPreconditions(policy, taskState) {
77
+ const required = POLICY_PRECONDITIONS[policy];
78
+ if (!required) return { satisfied: true, failures: [] };
79
+
80
+ const failures = [];
81
+
82
+ if (required.contextSufficient === true && !taskState.contextSufficient) {
83
+ failures.push("context not sufficient");
84
+ }
85
+ if (required.requirementKnown === true && !taskState.currentRequirement) {
86
+ failures.push("no current requirement");
87
+ }
88
+ if (required.evidenceAvailable === true && (!taskState.evidence || taskState.evidence.length === 0)) {
89
+ failures.push("no evidence available");
90
+ }
91
+ if (required.allVerified === true) {
92
+ const unverified = (taskState.requirements || []).filter(r => r.status !== "VERIFIED");
93
+ if (unverified.length > 0) failures.push("requirements not verified");
94
+ }
95
+
96
+ return { satisfied: failures.length === 0, failures };
97
+ }
@@ -0,0 +1,38 @@
1
+ // Change 39 — Ponytail-cross-cutting
2
+ // Cross-cutting constraints that modify policy behavior (ponytail, simplify, safety, security).
3
+
4
+ export const CONSTRAINT_TYPES = {
5
+ PONYTAIL: "ponytail",
6
+ SIMPLIFY: "simplify",
7
+ SAFETY: "safety",
8
+ SECURITY: "security"
9
+ };
10
+
11
+ export function applyConstraint(policy, constraints) {
12
+ const activeConstraints = constraints.filter(c => c.active);
13
+
14
+ for (const constraint of activeConstraints) {
15
+ if (constraint.type === CONSTRAINT_TYPES.PONYTAIL) {
16
+ // Ponytail: keep actions minimal and focused
17
+ if (policy.actions && policy.actions.length > 3) {
18
+ policy.actions = policy.actions.slice(0, 3);
19
+ policy.constrained = true;
20
+ policy.constraintReason = "ponytail";
21
+ }
22
+ }
23
+
24
+ if (constraint.type === CONSTRAINT_TYPES.SIMPLIFY) {
25
+ // Simplify: prefer simple solutions over complex
26
+ if (policy.complexity && policy.complexity > 5) {
27
+ policy.simplified = true;
28
+ policy.constraintReason = "simplify";
29
+ }
30
+ }
31
+ }
32
+
33
+ return policy;
34
+ }
35
+
36
+ export function isConstraintActive(constraints, type) {
37
+ return constraints.some(c => c.type === type && c.active);
38
+ }
@@ -0,0 +1,93 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Verifica si un archivo existe
6
+ */
7
+ function fileExists(filePath) {
8
+ try {
9
+ fs.accessSync(filePath);
10
+ return true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Obtiene el contenido de AGENTS.md si existe
18
+ */
19
+ export function getAgentsMd(projectPath) {
20
+ const agentsPath = path.join(projectPath, "AGENTS.md");
21
+ if (!fileExists(agentsPath)) return "";
22
+ return fs.readFileSync(agentsPath, "utf-8");
23
+ }
24
+
25
+ /**
26
+ * Obtiene el package.json si existe
27
+ */
28
+ export function getPackageJson(projectPath) {
29
+ const pkgPath = path.join(projectPath, "package.json");
30
+ if (!fileExists(pkgPath)) return null;
31
+ try {
32
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Obtiene dependencias relevantes del package.json
40
+ */
41
+ export function getDependencies(pkgJson) {
42
+ if (!pkgJson) return {};
43
+ return {
44
+ ...(pkgJson.dependencies || {}),
45
+ ...(pkgJson.devDependencies || {}),
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Detecta el stack tecnológico del proyecto
51
+ */
52
+ export function detectStack(projectPath) {
53
+ if (!fileExists(path.join(projectPath, "package.json"))) {
54
+ return { stack: "unknown", languages: [] };
55
+ }
56
+
57
+ const pkg = getPackageJson(projectPath);
58
+ if (!pkg) return { stack: "unknown", languages: [] };
59
+
60
+ const deps = getDependencies(pkg);
61
+ const languages = [];
62
+
63
+ // Node/TypeScript/JavaScript
64
+ if (deps.typescript || deps.ts) languages.push("typescript");
65
+ if (deps.vue || deps["vue-template-compiler"]) languages.push("vue");
66
+ if (deps.react || deps["react-dom"]) languages.push("react");
67
+ if (deps.svelte) languages.push("svelte");
68
+
69
+ // Python
70
+ if (deps.flask || deps.django) {
71
+ languages.push("python");
72
+ }
73
+
74
+ // Go
75
+ if (deps.go || deps["go.mod"]) languages.push("go");
76
+
77
+ // Rust
78
+ if (deps.rust || deps["Cargo.toml"]) languages.push("rust");
79
+
80
+ // Java
81
+ if (deps.java || deps["javax"]) languages.push("java");
82
+
83
+ // PHP
84
+ if (deps.laravel || deps.symfony || deps.woocommerce) languages.push("php");
85
+
86
+ // Ruby
87
+ if (deps.ruby || deps["rake"]) languages.push("ruby");
88
+
89
+ // Determine primary stack
90
+ const primary = languages.length > 0 ? languages[0] : "other";
91
+
92
+ return { stack: primary, languages };
93
+ }
@@ -0,0 +1,14 @@
1
+ export function buildAuditQuery(filters) {
2
+ const query = {};
3
+ if (filters.entity) query.entity = filters.entity;
4
+ if (filters.actor) query.actor = filters.actor;
5
+ if (filters.action) query.action = filters.action;
6
+ if (filters.outcome) query.outcome = filters.outcome;
7
+ if (filters.timeRange) {
8
+ query.timeRange = {
9
+ start: filters.timeRange.start,
10
+ end: filters.timeRange.end,
11
+ };
12
+ }
13
+ return query;
14
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Pre-flight request classification logic.
3
+ */
4
+ export function classifyRequest(prompt) {
5
+ const lower = String(prompt ?? "").toLowerCase();
6
+
7
+ // Trivial patterns - bypass wait-a-minute
8
+ const trivialPatterns = [
9
+ /^\s*rename\s+/i,
10
+ /^\s*renombra\s+/i,
11
+ /^\s*cambia\s+\w+/i,
12
+ /^\s*change\s+\w+/i,
13
+ /^\s*what(is|are)\s+/i,
14
+ /^\s*qué\s+es\s+/i,
15
+ /^\s*que\s+es\s+/i,
16
+ /^\s*explain\s+/i,
17
+ /^\s*explica\s+/i,
18
+ /^\s*how\s+to\s+/i,
19
+ /^\s*cómo\s+/i,
20
+ /^\s*como\s+hago\s+/i,
21
+ /^\s*list\s+/i,
22
+ /^\s*lista\s+/i,
23
+ /^\s*listar\s+/i,
24
+ /^\s*show\s+/i,
25
+ /^\s*muestra\s+/i,
26
+ /^\s*get\s+\w+/i,
27
+ ];
28
+
29
+ for (const pattern of trivialPatterns) {
30
+ if (pattern.test(prompt)) {
31
+ return { type: "trivial", ambiguity: "low", confidence: 95 };
32
+ }
33
+ }
34
+
35
+ // Architecture/security/high-risk patterns -> STRICT
36
+ const strictPatterns = [
37
+ /(migra|migrate)/i,
38
+ /(seguridad|security)/i,
39
+ /(arquitectura|architecture)/i,
40
+ /(provee(?:r|ndase)|provide)/i,
41
+ /(alto impacto|high impact)/i,
42
+ /(producción|production)/i,
43
+ /(destructivo|destructive)/i,
44
+ ];
45
+
46
+ let strictMatch = null;
47
+ for (const pattern of strictPatterns) {
48
+ if (pattern.test(prompt)) {
49
+ strictMatch = pattern;
50
+ break;
51
+ }
52
+ }
53
+
54
+ if (strictMatch) {
55
+ return {
56
+ type: "architectural",
57
+ ambiguity: "medium",
58
+ confidence: 60,
59
+ mode: "STRICT",
60
+ };
61
+ }
62
+
63
+ // Research/exploration patterns
64
+ const researchPatterns = [
65
+ /(buscar|research)/i,
66
+ /(comparar|compare)/i,
67
+ /(opciones|options)/i,
68
+ /(alternativas|alternatives)/i,
69
+ ];
70
+
71
+ for (const pattern of researchPatterns) {
72
+ if (pattern.test(prompt)) {
73
+ return { type: "research", ambiguity: "medium", confidence: 70 };
74
+ }
75
+ }
76
+
77
+ // Default: normal ambiguity
78
+ return { type: "normal", ambiguity: "medium", confidence: 50 };
79
+ }