@hoilab/ada-cli 0.84.21 → 0.84.22
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/CHANGELOG.md +20 -0
- package/README.es.md +163 -0
- package/README.md +99 -652
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/home-screen.d.ts.map +1 -1
- package/dist/cli/home-screen.js +2 -2
- package/dist/cli/home-screen.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +9 -3
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +57 -7
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +449 -73
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -3
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +41 -32
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/engine-personality.d.ts +12 -0
- package/dist/core/engine-personality.d.ts.map +1 -0
- package/dist/core/engine-personality.js +53 -0
- package/dist/core/engine-personality.js.map +1 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -5
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +23 -4
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/memory-engine/audit-export.d.ts +54 -0
- package/dist/core/memory-engine/audit-export.d.ts.map +1 -0
- package/dist/core/memory-engine/audit-export.js +85 -0
- package/dist/core/memory-engine/audit-export.js.map +1 -0
- package/dist/core/memory-engine/audit.d.ts.map +1 -1
- package/dist/core/memory-engine/audit.js +7 -8
- package/dist/core/memory-engine/audit.js.map +1 -1
- package/dist/core/memory-engine/engine.d.ts +2 -3
- package/dist/core/memory-engine/engine.d.ts.map +1 -1
- package/dist/core/memory-engine/engine.js +148 -50
- package/dist/core/memory-engine/engine.js.map +1 -1
- package/dist/core/memory-engine/inverted-index.d.ts.map +1 -1
- package/dist/core/memory-engine/inverted-index.js +67 -10
- package/dist/core/memory-engine/inverted-index.js.map +1 -1
- package/dist/core/memory-engine/keychain.d.ts.map +1 -1
- package/dist/core/memory-engine/keychain.js +1 -6
- package/dist/core/memory-engine/keychain.js.map +1 -1
- package/dist/core/memory-engine/observation-store.d.ts +1 -0
- package/dist/core/memory-engine/observation-store.d.ts.map +1 -1
- package/dist/core/memory-engine/observation-store.js +37 -8
- package/dist/core/memory-engine/observation-store.js.map +1 -1
- package/dist/core/memory-engine/scanner.d.ts.map +1 -1
- package/dist/core/memory-engine/scanner.js +25 -5
- package/dist/core/memory-engine/scanner.js.map +1 -1
- package/dist/core/memory-engine/security.d.ts.map +1 -1
- package/dist/core/memory-engine/security.js +131 -23
- package/dist/core/memory-engine/security.js.map +1 -1
- package/dist/core/memory-engine/session-indexer.d.ts.map +1 -1
- package/dist/core/memory-engine/session-indexer.js +8 -2
- package/dist/core/memory-engine/session-indexer.js.map +1 -1
- package/dist/core/memory-engine/tools.d.ts +2 -2
- package/dist/core/memory-engine/tools.d.ts.map +1 -1
- package/dist/core/memory-engine/tools.js +14 -7
- package/dist/core/memory-engine/tools.js.map +1 -1
- package/dist/core/memory-engine/vault.d.ts.map +1 -1
- package/dist/core/memory-engine/vault.js +2 -2
- package/dist/core/memory-engine/vault.js.map +1 -1
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +2 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +12 -6
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -6
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/prompt-optimizer.d.ts +53 -0
- package/dist/core/prompt-optimizer.d.ts.map +1 -0
- package/dist/core/prompt-optimizer.js +138 -0
- package/dist/core/prompt-optimizer.js.map +1 -0
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +1 -1
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +50 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +233 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +8 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +11 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +52 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +204 -10
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -4
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +2 -2
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/project-memory-tool.d.ts.map +1 -1
- package/dist/core/tools/project-memory-tool.js +4 -1
- package/dist/core/tools/project-memory-tool.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/workflows/agent-runner.d.ts +86 -0
- package/dist/core/workflows/agent-runner.d.ts.map +1 -0
- package/dist/core/workflows/agent-runner.js +225 -0
- package/dist/core/workflows/agent-runner.js.map +1 -0
- package/dist/core/workflows/builtins.d.ts +41 -0
- package/dist/core/workflows/builtins.d.ts.map +1 -0
- package/dist/core/workflows/builtins.js +282 -0
- package/dist/core/workflows/builtins.js.map +1 -0
- package/dist/core/workflows/goal-engine/engine.d.ts +134 -0
- package/dist/core/workflows/goal-engine/engine.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/engine.js +462 -0
- package/dist/core/workflows/goal-engine/engine.js.map +1 -0
- package/dist/core/workflows/goal-engine/errors.d.ts +7 -0
- package/dist/core/workflows/goal-engine/errors.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/errors.js +9 -0
- package/dist/core/workflows/goal-engine/errors.js.map +1 -0
- package/dist/core/workflows/goal-engine/index.d.ts +13 -0
- package/dist/core/workflows/goal-engine/index.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/index.js +8 -0
- package/dist/core/workflows/goal-engine/index.js.map +1 -0
- package/dist/core/workflows/goal-engine/persistence.d.ts +31 -0
- package/dist/core/workflows/goal-engine/persistence.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/persistence.js +145 -0
- package/dist/core/workflows/goal-engine/persistence.js.map +1 -0
- package/dist/core/workflows/goal-engine/prompts.d.ts +37 -0
- package/dist/core/workflows/goal-engine/prompts.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/prompts.js +131 -0
- package/dist/core/workflows/goal-engine/prompts.js.map +1 -0
- package/dist/core/workflows/goal-engine/state.d.ts +41 -0
- package/dist/core/workflows/goal-engine/state.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/state.js +397 -0
- package/dist/core/workflows/goal-engine/state.js.map +1 -0
- package/dist/core/workflows/goal-engine/tools.d.ts +31 -0
- package/dist/core/workflows/goal-engine/tools.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/tools.js +211 -0
- package/dist/core/workflows/goal-engine/tools.js.map +1 -0
- package/dist/core/workflows/goal-engine/types.d.ts +286 -0
- package/dist/core/workflows/goal-engine/types.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/types.js +17 -0
- package/dist/core/workflows/goal-engine/types.js.map +1 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.d.ts +23 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.js +72 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.js.map +1 -0
- package/dist/core/workflows/graph-sync.d.ts +67 -0
- package/dist/core/workflows/graph-sync.d.ts.map +1 -0
- package/dist/core/workflows/graph-sync.js +188 -0
- package/dist/core/workflows/graph-sync.js.map +1 -0
- package/dist/core/workflows/graph.d.ts +143 -0
- package/dist/core/workflows/graph.d.ts.map +1 -0
- package/dist/core/workflows/graph.js +319 -0
- package/dist/core/workflows/graph.js.map +1 -0
- package/dist/core/workflows/index.d.ts +18 -0
- package/dist/core/workflows/index.d.ts.map +1 -0
- package/dist/core/workflows/index.js +14 -0
- package/dist/core/workflows/index.js.map +1 -0
- package/dist/core/workflows/journal.d.ts +68 -0
- package/dist/core/workflows/journal.d.ts.map +1 -0
- package/dist/core/workflows/journal.js +83 -0
- package/dist/core/workflows/journal.js.map +1 -0
- package/dist/core/workflows/manager.d.ts +112 -0
- package/dist/core/workflows/manager.d.ts.map +1 -0
- package/dist/core/workflows/manager.js +327 -0
- package/dist/core/workflows/manager.js.map +1 -0
- package/dist/core/workflows/resolve-script.d.ts +35 -0
- package/dist/core/workflows/resolve-script.d.ts.map +1 -0
- package/dist/core/workflows/resolve-script.js +66 -0
- package/dist/core/workflows/resolve-script.js.map +1 -0
- package/dist/core/workflows/runtime.d.ts +49 -0
- package/dist/core/workflows/runtime.d.ts.map +1 -0
- package/dist/core/workflows/runtime.js +301 -0
- package/dist/core/workflows/runtime.js.map +1 -0
- package/dist/core/workflows/shared-store.d.ts +31 -0
- package/dist/core/workflows/shared-store.d.ts.map +1 -0
- package/dist/core/workflows/shared-store.js +80 -0
- package/dist/core/workflows/shared-store.js.map +1 -0
- package/dist/core/workflows/subagent-host.d.ts +50 -0
- package/dist/core/workflows/subagent-host.d.ts.map +1 -0
- package/dist/core/workflows/subagent-host.js +225 -0
- package/dist/core/workflows/subagent-host.js.map +1 -0
- package/dist/core/workflows/tool-workflow.d.ts +22 -0
- package/dist/core/workflows/tool-workflow.d.ts.map +1 -0
- package/dist/core/workflows/tool-workflow.js +120 -0
- package/dist/core/workflows/tool-workflow.js.map +1 -0
- package/dist/core/workflows/types.d.ts +142 -0
- package/dist/core/workflows/types.d.ts.map +1 -0
- package/dist/core/workflows/types.js +26 -0
- package/dist/core/workflows/types.js.map +1 -0
- package/dist/core/workflows/worktree.d.ts +42 -0
- package/dist/core/workflows/worktree.d.ts.map +1 -0
- package/dist/core/workflows/worktree.js +107 -0
- package/dist/core/workflows/worktree.js.map +1 -0
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -11
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +20 -15
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/home-screen.d.ts.map +1 -1
- package/dist/modes/interactive/components/home-screen.js +2 -2
- package/dist/modes/interactive/components/home-screen.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +2 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +16 -6
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/prompt-optimization.d.ts +16 -0
- package/dist/modes/interactive/components/prompt-optimization.d.ts.map +1 -0
- package/dist/modes/interactive/components/prompt-optimization.js +46 -0
- package/dist/modes/interactive/components/prompt-optimization.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +19 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +247 -46
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +8 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +29 -3
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +24 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +677 -34
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +4 -3
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +16 -13
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +20 -6
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +173 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +29 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +1 -1
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/skills/ada-engine/SKILL.md +153 -0
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/docs/compaction.md +15 -0
- package/docs/custom-provider.md +5 -2
- package/docs/extensions.md +11 -2
- package/docs/json.md +7 -2
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/rpc.md +9 -3
- package/docs/settings.md +2 -2
- package/docs/skills.md +4 -3
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/git-checkpoint.ts +2 -2
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/subagent/index.ts +6 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +2 -12
- package/package.json +3 -3
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ada Intelligence Engine — Grafo de trabajo (Fase 7 / §5).
|
|
3
|
+
*
|
|
4
|
+
* El grafo es memoria ESTRUCTURADA: nodos en 3 capas (product/design/code)
|
|
5
|
+
* y aristas con predicados CERRADOS. Live en `~/.ada/agent/workflows/graph/`
|
|
6
|
+
* (misma raíz que observaciones) con persistencia JSON + índice invertido
|
|
7
|
+
* (patrón memoria; sin SQLite nativo).
|
|
8
|
+
*
|
|
9
|
+
* Reglas no negociables:
|
|
10
|
+
* - Ids reutilizables TYPE:slug (≤72 chars); search antes de inventar.
|
|
11
|
+
* - Predicados fuera de la lista legal → REJECTED con warning.
|
|
12
|
+
* - addEdges es atómico: si un endpoint no existe, rechaza TODO el lote.
|
|
13
|
+
* - retract exige --reason (journal).
|
|
14
|
+
* - Confidence tiers EXTRACTED=1.0 / INFERRED=0.7-0.9 / AMBIGUOUS<0.7 / MANUAL=1.0.
|
|
15
|
+
* - context(id, minConfidence) filtra < 0.6 por defecto.
|
|
16
|
+
* - Gates edge-driven: ada gaps (spine incompleto) / ada gate (criterios con evidencia).
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from "node:crypto";
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { tokenize } from "../memory-engine/inverted-index.js";
|
|
22
|
+
export const NODE_LAYER = {
|
|
23
|
+
INTENT: "product",
|
|
24
|
+
REQUIREMENT: "product",
|
|
25
|
+
CONSTRAINT: "product",
|
|
26
|
+
DECISION: "product",
|
|
27
|
+
RISK: "product",
|
|
28
|
+
TASK: "product",
|
|
29
|
+
QA_FINDING: "product",
|
|
30
|
+
ARTIFACT: "product",
|
|
31
|
+
LEARNING: "product",
|
|
32
|
+
METRIC: "product",
|
|
33
|
+
PERSONA: "product",
|
|
34
|
+
DESIGN_TOKEN: "design",
|
|
35
|
+
UI_PATTERN: "design",
|
|
36
|
+
SCREEN: "design",
|
|
37
|
+
FLOW: "design",
|
|
38
|
+
A11Y_RULE: "design",
|
|
39
|
+
FILE: "code",
|
|
40
|
+
SYMBOL: "code",
|
|
41
|
+
ROUTE: "code",
|
|
42
|
+
TEST: "code",
|
|
43
|
+
DEPENDENCY: "code",
|
|
44
|
+
};
|
|
45
|
+
/** Predicados cerrados (spine + resto) §5. */
|
|
46
|
+
export const SPINE_EDGES = ["motivates", "planned_as", "touches", "implemented_by", "verified_by"];
|
|
47
|
+
export const LEGAL_EDGES = new Set([
|
|
48
|
+
...SPINE_EDGES,
|
|
49
|
+
"constrains",
|
|
50
|
+
"mitigated_by",
|
|
51
|
+
"found",
|
|
52
|
+
"affects",
|
|
53
|
+
"regresses",
|
|
54
|
+
"uses_token",
|
|
55
|
+
"applied_in",
|
|
56
|
+
"declared_in",
|
|
57
|
+
"renders",
|
|
58
|
+
"calls",
|
|
59
|
+
"references",
|
|
60
|
+
"imports",
|
|
61
|
+
"depends_on",
|
|
62
|
+
"defines",
|
|
63
|
+
"served_by",
|
|
64
|
+
"supersedes",
|
|
65
|
+
"rejected_in_favour_of",
|
|
66
|
+
"derives_from",
|
|
67
|
+
"measured_by",
|
|
68
|
+
"recalls",
|
|
69
|
+
]);
|
|
70
|
+
export class FileGraphStore {
|
|
71
|
+
file;
|
|
72
|
+
constructor(dir) {
|
|
73
|
+
mkdirSync(dir, { recursive: true });
|
|
74
|
+
this.file = join(dir, "graph.json");
|
|
75
|
+
}
|
|
76
|
+
async save(data) {
|
|
77
|
+
const tmp = `${this.file}.tmp`;
|
|
78
|
+
writeFileSync(tmp, JSON.stringify(data, null, 2), "utf8");
|
|
79
|
+
renameSync(tmp, this.file);
|
|
80
|
+
}
|
|
81
|
+
async load() {
|
|
82
|
+
if (!existsSync(this.file))
|
|
83
|
+
return null;
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(readFileSync(this.file, "utf8"));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Variante en memoria para tests */
|
|
93
|
+
export class MemoryGraphStore {
|
|
94
|
+
data = null;
|
|
95
|
+
async save(d) {
|
|
96
|
+
this.data = structuredClone(d);
|
|
97
|
+
}
|
|
98
|
+
async load() {
|
|
99
|
+
return this.data ? structuredClone(this.data) : null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const GRAPH_VERSION = 1;
|
|
103
|
+
export class Graph {
|
|
104
|
+
store;
|
|
105
|
+
minConfidence;
|
|
106
|
+
onEvent;
|
|
107
|
+
data;
|
|
108
|
+
constructor(options) {
|
|
109
|
+
this.store = options.store;
|
|
110
|
+
this.minConfidence = options.minConfidence ?? 0.6;
|
|
111
|
+
this.onEvent = options.onEvent;
|
|
112
|
+
this.data = { version: GRAPH_VERSION, nodes: {}, edges: [] };
|
|
113
|
+
}
|
|
114
|
+
async init() {
|
|
115
|
+
const loaded = await this.store.load();
|
|
116
|
+
if (loaded?.nodes) {
|
|
117
|
+
this.data = loaded;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.data = { version: GRAPH_VERSION, nodes: {}, edges: [] };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ─── Nodos ───
|
|
124
|
+
addNode(input) {
|
|
125
|
+
const id = `${input.type}:${input.slug}`;
|
|
126
|
+
if (id.length > 72) {
|
|
127
|
+
return { ok: false, error: `Id excede 72 chars: ${id.length}` };
|
|
128
|
+
}
|
|
129
|
+
const confidence = this.clampConfidence(input.confidence);
|
|
130
|
+
const now = new Date().toISOString();
|
|
131
|
+
const existing = this.data.nodes[id];
|
|
132
|
+
const node = {
|
|
133
|
+
id,
|
|
134
|
+
type: input.type,
|
|
135
|
+
description: input.description,
|
|
136
|
+
status: input.status ?? (input.type === "DECISION" ? "open" : undefined),
|
|
137
|
+
confidence,
|
|
138
|
+
...(input.origin ? { origin: "inferred" } : {}),
|
|
139
|
+
...(input.file ? { file: input.file } : {}),
|
|
140
|
+
createdAt: existing?.createdAt ?? now,
|
|
141
|
+
updatedAt: now,
|
|
142
|
+
};
|
|
143
|
+
this.data.nodes[id] = node;
|
|
144
|
+
void this.persist();
|
|
145
|
+
return { ok: true, data: node };
|
|
146
|
+
}
|
|
147
|
+
// ─── Aristas (atómico + predicados cerrados) ───
|
|
148
|
+
addEdges(edges) {
|
|
149
|
+
const warnings = [];
|
|
150
|
+
const rejected = [];
|
|
151
|
+
// 1. Validar predicados (lista cerrada).
|
|
152
|
+
const legal = edges.every((e) => LEGAL_EDGES.has(e.predicate));
|
|
153
|
+
if (!legal) {
|
|
154
|
+
for (const e of edges) {
|
|
155
|
+
if (!LEGAL_EDGES.has(e.predicate)) {
|
|
156
|
+
rejected.push(e.predicate);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
ok: false,
|
|
161
|
+
error: `Predicado(s) fuera de la lista legal: ${rejected.join(", ")}`,
|
|
162
|
+
rejected,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// 2. Validar que todos los endpoints existen (rechazo atómico).
|
|
166
|
+
const missing = edges.filter((e) => !this.data.nodes[e.subject] || !this.data.nodes[e.object]);
|
|
167
|
+
if (missing.length > 0) {
|
|
168
|
+
const missingIds = missing.map((e) => [!this.data.nodes[e.subject] && e.subject, !this.data.nodes[e.object] && e.object]
|
|
169
|
+
.filter(Boolean)
|
|
170
|
+
.join(","));
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
error: `Nodos de paso no existen: ${missingIds.join(", ")}`,
|
|
174
|
+
warnings,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// 3. Aplicar.
|
|
178
|
+
const now = new Date().toISOString();
|
|
179
|
+
const added = [];
|
|
180
|
+
for (const e of edges) {
|
|
181
|
+
const id = `${e.subject}--${e.predicate}-->${e.object}`;
|
|
182
|
+
// Rechazar duplicados exactos.
|
|
183
|
+
if (this.data.edges.some((x) => x.id === id))
|
|
184
|
+
continue;
|
|
185
|
+
const edge = {
|
|
186
|
+
id,
|
|
187
|
+
subject: e.subject,
|
|
188
|
+
predicate: e.predicate,
|
|
189
|
+
object: e.object,
|
|
190
|
+
confidence: this.clampConfidence(e.confidence),
|
|
191
|
+
...(e.reason ? { reason: e.reason } : {}),
|
|
192
|
+
createdAt: now,
|
|
193
|
+
};
|
|
194
|
+
this.data.edges.push(edge);
|
|
195
|
+
added.push(edge);
|
|
196
|
+
this.emit("graph_update", { type: "edge", edge });
|
|
197
|
+
}
|
|
198
|
+
void this.persist();
|
|
199
|
+
return { ok: true, data: { added } };
|
|
200
|
+
}
|
|
201
|
+
// ─── Retract (exige reason) ───
|
|
202
|
+
retract(edgeId, reason) {
|
|
203
|
+
if (!reason || reason.trim().length === 0) {
|
|
204
|
+
return { ok: false, error: "retract exige --reason (journal)" };
|
|
205
|
+
}
|
|
206
|
+
const idx = this.data.edges.findIndex((e) => e.id === edgeId);
|
|
207
|
+
if (idx === -1)
|
|
208
|
+
return { ok: false, error: `Arista no encontrada: ${edgeId}` };
|
|
209
|
+
const [removed] = this.data.edges.splice(idx, 1);
|
|
210
|
+
removed.reason = reason;
|
|
211
|
+
removed.rejected = true;
|
|
212
|
+
void this.persist();
|
|
213
|
+
return { ok: true, data: { removed } };
|
|
214
|
+
}
|
|
215
|
+
// ─── Búsqueda ───
|
|
216
|
+
search(term) {
|
|
217
|
+
const tokens = new Set(tokenize(term.toLowerCase()));
|
|
218
|
+
const scores = [];
|
|
219
|
+
for (const node of Object.values(this.data.nodes)) {
|
|
220
|
+
const text = `${node.description} ${node.id}`.toLowerCase();
|
|
221
|
+
let score = 0;
|
|
222
|
+
for (const t of tokens) {
|
|
223
|
+
if (text.includes(t))
|
|
224
|
+
score++;
|
|
225
|
+
}
|
|
226
|
+
if (score > 0)
|
|
227
|
+
scores.push({ node, score });
|
|
228
|
+
}
|
|
229
|
+
return scores.sort((a, b) => b.score - a.score);
|
|
230
|
+
}
|
|
231
|
+
context(id, minConfidence) {
|
|
232
|
+
const threshold = minConfidence ?? this.minConfidence;
|
|
233
|
+
const node = this.data.nodes[id];
|
|
234
|
+
if (!node)
|
|
235
|
+
return { ok: false, error: `Nodo no encontrado: ${id}` };
|
|
236
|
+
if (node.confidence < threshold) {
|
|
237
|
+
return { ok: false, error: `Nodo ${id} tiene confianza ${node.confidence} < ${threshold} (filtrado)` };
|
|
238
|
+
}
|
|
239
|
+
const neighbors = this.data.edges.filter((e) => (e.subject === id || e.object === id) && e.confidence >= threshold);
|
|
240
|
+
return { ok: true, data: { node, neighbors } };
|
|
241
|
+
}
|
|
242
|
+
// ─── Gates edge-driven ───
|
|
243
|
+
/**
|
|
244
|
+
* gaps(): devuelve el spine incompleto.
|
|
245
|
+
* - TODO INTENT sin motivar un REQUIREMENT
|
|
246
|
+
* - TODO REQUIREMENT sin planned_as → TASK
|
|
247
|
+
* - TODO TASK sin touches → FILE
|
|
248
|
+
* - TODO REQUIREMENT sin verified_by → TEST ni implemented_by → SYMBOL
|
|
249
|
+
*/
|
|
250
|
+
gaps() {
|
|
251
|
+
const gaps = [];
|
|
252
|
+
const edgeFor = (from, predicate) => this.data.edges.some((e) => e.subject === from && e.predicate === predicate);
|
|
253
|
+
for (const n of Object.values(this.data.nodes)) {
|
|
254
|
+
if (n.type === "INTENT" && !edgeFor(n.id, "motivates"))
|
|
255
|
+
gaps.push({ id: n.id, description: n.description, missing: "REQUIREMENT motivado (motivates)" });
|
|
256
|
+
if (n.type === "REQUIREMENT" && !edgeFor(n.id, "planned_as"))
|
|
257
|
+
gaps.push({ id: n.id, description: n.description, missing: "TASK planificada (planned_as)" });
|
|
258
|
+
if (n.type === "TASK" && !edgeFor(n.id, "touches"))
|
|
259
|
+
gaps.push({ id: n.id, description: n.description, missing: "FILE tocado (touches)" });
|
|
260
|
+
if (n.type === "REQUIREMENT" && !edgeFor(n.id, "verified_by") && !edgeFor(n.id, "implemented_by"))
|
|
261
|
+
gaps.push({ id: n.id, description: n.description, missing: "TEST verified_by o SYMBOL implemented_by" });
|
|
262
|
+
}
|
|
263
|
+
return gaps;
|
|
264
|
+
}
|
|
265
|
+
/** gate(criteria): devuelve true si TODOS los requisitos tienen verified_by REAL. */
|
|
266
|
+
gate() {
|
|
267
|
+
const requirements = Object.values(this.data.nodes).filter((n) => n.type === "REQUIREMENT");
|
|
268
|
+
if (requirements.length === 0)
|
|
269
|
+
return { ok: true, data: { passed: true, failures: [] } };
|
|
270
|
+
const failures = [];
|
|
271
|
+
// Una DECISION open bloquea el gate (§5 regla 4: gap que bloquea).
|
|
272
|
+
for (const n of Object.values(this.data.nodes)) {
|
|
273
|
+
if (n.type === "DECISION" && n.status === "open") {
|
|
274
|
+
failures.push(`${n.id}: decisión abierta bloquea el gate`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
for (const req of requirements) {
|
|
278
|
+
const verified = this.data.edges.some((e) => e.subject === req.id && e.predicate === "verified_by");
|
|
279
|
+
if (!verified)
|
|
280
|
+
failures.push(`${req.id}: sin verified_by`);
|
|
281
|
+
}
|
|
282
|
+
return { ok: true, data: { passed: failures.length === 0, failures } };
|
|
283
|
+
}
|
|
284
|
+
nodeCount() {
|
|
285
|
+
return Object.keys(this.data.nodes).length;
|
|
286
|
+
}
|
|
287
|
+
edgeCount() {
|
|
288
|
+
return this.data.edges.length;
|
|
289
|
+
}
|
|
290
|
+
nodes() {
|
|
291
|
+
return Object.values(this.data.nodes);
|
|
292
|
+
}
|
|
293
|
+
edges() {
|
|
294
|
+
return [...this.data.edges];
|
|
295
|
+
}
|
|
296
|
+
// ─── helpers ───
|
|
297
|
+
clampConfidence(c) {
|
|
298
|
+
if (c === undefined)
|
|
299
|
+
return 1.0;
|
|
300
|
+
const n = Math.max(0, Math.min(1, c));
|
|
301
|
+
return n;
|
|
302
|
+
}
|
|
303
|
+
async persist() {
|
|
304
|
+
try {
|
|
305
|
+
await this.store.save(this.data);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// best-effort: el disco es cache, no fuente de verdad estricta en memoria
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
emit(type, payload) {
|
|
312
|
+
this.onEvent?.(type, payload);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// ─── Hash de id auxiliar (para slugs estables) ───
|
|
316
|
+
export function slugHash(input, len = 6) {
|
|
317
|
+
return createHash("sha256").update(input).digest("hex").slice(0, len);
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../../src/core/workflows/graph.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ada Intelligence Engine — Grafo de trabajo (Fase 7 / §5).\n *\n * El grafo es memoria ESTRUCTURADA: nodos en 3 capas (product/design/code)\n * y aristas con predicados CERRADOS. Live en `~/.ada/agent/workflows/graph/`\n * (misma raíz que observaciones) con persistencia JSON + índice invertido\n * (patrón memoria; sin SQLite nativo).\n *\n * Reglas no negociables:\n * - Ids reutilizables TYPE:slug (≤72 chars); search antes de inventar.\n * - Predicados fuera de la lista legal → REJECTED con warning.\n * - addEdges es atómico: si un endpoint no existe, rechaza TODO el lote.\n * - retract exige --reason (journal).\n * - Confidence tiers EXTRACTED=1.0 / INFERRED=0.7-0.9 / AMBIGUOUS<0.7 / MANUAL=1.0.\n * - context(id, minConfidence) filtra < 0.6 por defecto.\n * - Gates edge-driven: ada gaps (spine incompleto) / ada gate (criterios con evidencia).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tokenize } from \"../memory-engine/inverted-index.ts\";\n\n// ─── Tipos (3 capas) ───\n\nexport type GraphLayer = \"product\" | \"design\" | \"code\";\n\nexport type GraphNodeType =\n\t// product\n\t| \"INTENT\"\n\t| \"REQUIREMENT\"\n\t| \"CONSTRAINT\"\n\t| \"DECISION\"\n\t| \"RISK\"\n\t| \"TASK\"\n\t| \"QA_FINDING\"\n\t| \"ARTIFACT\"\n\t| \"LEARNING\"\n\t| \"METRIC\"\n\t| \"PERSONA\"\n\t// design\n\t| \"DESIGN_TOKEN\"\n\t| \"UI_PATTERN\"\n\t| \"SCREEN\"\n\t| \"FLOW\"\n\t| \"A11Y_RULE\"\n\t// code\n\t| \"FILE\"\n\t| \"SYMBOL\"\n\t| \"ROUTE\"\n\t| \"TEST\"\n\t| \"DEPENDENCY\";\n\nexport const NODE_LAYER: Record<GraphNodeType, GraphLayer> = {\n\tINTENT: \"product\",\n\tREQUIREMENT: \"product\",\n\tCONSTRAINT: \"product\",\n\tDECISION: \"product\",\n\tRISK: \"product\",\n\tTASK: \"product\",\n\tQA_FINDING: \"product\",\n\tARTIFACT: \"product\",\n\tLEARNING: \"product\",\n\tMETRIC: \"product\",\n\tPERSONA: \"product\",\n\tDESIGN_TOKEN: \"design\",\n\tUI_PATTERN: \"design\",\n\tSCREEN: \"design\",\n\tFLOW: \"design\",\n\tA11Y_RULE: \"design\",\n\tFILE: \"code\",\n\tSYMBOL: \"code\",\n\tROUTE: \"code\",\n\tTEST: \"code\",\n\tDEPENDENCY: \"code\",\n};\n\n/** Predicados cerrados (spine + resto) §5. */\nexport const SPINE_EDGES = [\"motivates\", \"planned_as\", \"touches\", \"implemented_by\", \"verified_by\"] as const;\n\nexport const LEGAL_EDGES = new Set<string>([\n\t...SPINE_EDGES,\n\t\"constrains\",\n\t\"mitigated_by\",\n\t\"found\",\n\t\"affects\",\n\t\"regresses\",\n\t\"uses_token\",\n\t\"applied_in\",\n\t\"declared_in\",\n\t\"renders\",\n\t\"calls\",\n\t\"references\",\n\t\"imports\",\n\t\"depends_on\",\n\t\"defines\",\n\t\"served_by\",\n\t\"supersedes\",\n\t\"rejected_in_favour_of\",\n\t\"derives_from\",\n\t\"measured_by\",\n\t\"recalls\",\n]);\n\nexport type Confidence = 1.0 | 0.9 | 0.8 | 0.7 | 0.5 | 0.4 | 0.3;\n\nexport interface GraphNode {\n\tid: string; // TYPE:slug (≤72)\n\ttype: GraphNodeType;\n\tdescription: string; // una frase\n\tstatus?: \"open\" | \"resolved\" | \"rejected\";\n\tconfidence: Confidence;\n\torigin?: \"inferred\";\n\tfile?: string; // source/artefacto\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface GraphEdge {\n\tid: string;\n\tsubject: string; // from\n\tpredicate: string; // legal cerrado\n\tobject: string; // to\n\tconfidence: Confidence;\n\treason?: string;\n\trejected?: boolean; // DECISION status rejected\n\tcreatedAt: string;\n}\n\nexport interface GraphEdgeInput {\n\tsubject: string;\n\tpredicate: string;\n\tobject: string;\n\tconfidence?: Confidence;\n\treason?: string;\n}\n\nexport interface GraphData {\n\tversion: number;\n\tnodes: Record<string, GraphNode>;\n\tedges: GraphEdge[];\n}\n\nexport interface GraphResult<T> {\n\tok: boolean;\n\tdata?: T;\n\terror?: string;\n\trejected?: string[]; // predicados desconocidos\n\twarnings?: string[];\n}\n\n// ─── Persistencia (inyectable para tests) ───\n\nexport interface GraphStore {\n\tsave(data: GraphData): Promise<void>;\n\tload(): Promise<GraphData | null>;\n}\n\nexport class FileGraphStore implements GraphStore {\n\tprivate file: string;\n\tconstructor(dir: string) {\n\t\tmkdirSync(dir, { recursive: true });\n\t\tthis.file = join(dir, \"graph.json\");\n\t}\n\tasync save(data: GraphData): Promise<void> {\n\t\tconst tmp = `${this.file}.tmp`;\n\t\twriteFileSync(tmp, JSON.stringify(data, null, 2), \"utf8\");\n\t\trenameSync(tmp, this.file);\n\t}\n\tasync load(): Promise<GraphData | null> {\n\t\tif (!existsSync(this.file)) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(readFileSync(this.file, \"utf8\")) as GraphData;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n\n/** Variante en memoria para tests */\nexport class MemoryGraphStore implements GraphStore {\n\tdata: GraphData | null = null;\n\tasync save(d: GraphData): Promise<void> {\n\t\tthis.data = structuredClone(d);\n\t}\n\tasync load(): Promise<GraphData | null> {\n\t\treturn this.data ? structuredClone(this.data) : null;\n\t}\n}\n\n// ─── Grafo ───\n\nexport interface GraphOptions {\n\tstore: GraphStore;\n\t/** Confianza mínima para context() (default 0.6). */\n\tminConfidence?: number;\n\tonEvent?: (type: \"graph_update\" | \"graph_edge_rejected\", payload: Record<string, unknown>) => void;\n}\n\nconst GRAPH_VERSION = 1;\n\nexport class Graph {\n\tprivate store: GraphStore;\n\tprivate minConfidence: number;\n\tprivate onEvent?: GraphOptions[\"onEvent\"];\n\tprivate data: GraphData;\n\n\tconstructor(options: GraphOptions) {\n\t\tthis.store = options.store;\n\t\tthis.minConfidence = options.minConfidence ?? 0.6;\n\t\tthis.onEvent = options.onEvent;\n\t\tthis.data = { version: GRAPH_VERSION, nodes: {}, edges: [] };\n\t}\n\n\tasync init(): Promise<void> {\n\t\tconst loaded = await this.store.load();\n\t\tif (loaded?.nodes) {\n\t\t\tthis.data = loaded;\n\t\t} else {\n\t\t\tthis.data = { version: GRAPH_VERSION, nodes: {}, edges: [] };\n\t\t}\n\t}\n\n\t// ─── Nodos ───\n\n\taddNode(input: {\n\t\ttype: GraphNodeType;\n\t\tslug: string;\n\t\tdescription: string;\n\t\tconfidence?: Confidence;\n\t\tstatus?: GraphNode[\"status\"];\n\t\torigin?: \"inferred\";\n\t\tfile?: string;\n\t}): GraphResult<GraphNode> {\n\t\tconst id = `${input.type}:${input.slug}`;\n\t\tif (id.length > 72) {\n\t\t\treturn { ok: false, error: `Id excede 72 chars: ${id.length}` };\n\t\t}\n\t\tconst confidence = this.clampConfidence(input.confidence);\n\t\tconst now = new Date().toISOString();\n\t\tconst existing = this.data.nodes[id];\n\t\tconst node: GraphNode = {\n\t\t\tid,\n\t\t\ttype: input.type,\n\t\t\tdescription: input.description,\n\t\t\tstatus: input.status ?? (input.type === \"DECISION\" ? \"open\" : undefined),\n\t\t\tconfidence,\n\t\t\t...(input.origin ? { origin: \"inferred\" } : {}),\n\t\t\t...(input.file ? { file: input.file } : {}),\n\t\t\tcreatedAt: existing?.createdAt ?? now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\tthis.data.nodes[id] = node;\n\t\tvoid this.persist();\n\t\treturn { ok: true, data: node };\n\t}\n\n\t// ─── Aristas (atómico + predicados cerrados) ───\n\n\taddEdges(edges: GraphEdgeInput[]): GraphResult<{ added: GraphEdge[] }> {\n\t\tconst warnings: string[] = [];\n\t\tconst rejected: string[] = [];\n\n\t\t// 1. Validar predicados (lista cerrada).\n\t\tconst legal = edges.every((e) => LEGAL_EDGES.has(e.predicate));\n\t\tif (!legal) {\n\t\t\tfor (const e of edges) {\n\t\t\t\tif (!LEGAL_EDGES.has(e.predicate)) {\n\t\t\t\t\trejected.push(e.predicate);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `Predicado(s) fuera de la lista legal: ${rejected.join(\", \")}`,\n\t\t\t\trejected,\n\t\t\t};\n\t\t}\n\n\t\t// 2. Validar que todos los endpoints existen (rechazo atómico).\n\t\tconst missing = edges.filter((e) => !this.data.nodes[e.subject] || !this.data.nodes[e.object]);\n\t\tif (missing.length > 0) {\n\t\t\tconst missingIds = missing.map((e) =>\n\t\t\t\t[!this.data.nodes[e.subject] && e.subject, !this.data.nodes[e.object] && e.object]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(\",\"),\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `Nodos de paso no existen: ${missingIds.join(\", \")}`,\n\t\t\t\twarnings,\n\t\t\t};\n\t\t}\n\n\t\t// 3. Aplicar.\n\t\tconst now = new Date().toISOString();\n\t\tconst added: GraphEdge[] = [];\n\t\tfor (const e of edges) {\n\t\t\tconst id = `${e.subject}--${e.predicate}-->${e.object}`;\n\t\t\t// Rechazar duplicados exactos.\n\t\t\tif (this.data.edges.some((x) => x.id === id)) continue;\n\t\t\tconst edge: GraphEdge = {\n\t\t\t\tid,\n\t\t\t\tsubject: e.subject,\n\t\t\t\tpredicate: e.predicate,\n\t\t\t\tobject: e.object,\n\t\t\t\tconfidence: this.clampConfidence(e.confidence),\n\t\t\t\t...(e.reason ? { reason: e.reason } : {}),\n\t\t\t\tcreatedAt: now,\n\t\t\t};\n\t\t\tthis.data.edges.push(edge);\n\t\t\tadded.push(edge);\n\t\t\tthis.emit(\"graph_update\", { type: \"edge\", edge });\n\t\t}\n\t\tvoid this.persist();\n\t\treturn { ok: true, data: { added } };\n\t}\n\n\t// ─── Retract (exige reason) ───\n\n\tretract(edgeId: string, reason: string): GraphResult<{ removed: GraphEdge }> {\n\t\tif (!reason || reason.trim().length === 0) {\n\t\t\treturn { ok: false, error: \"retract exige --reason (journal)\" };\n\t\t}\n\t\tconst idx = this.data.edges.findIndex((e) => e.id === edgeId);\n\t\tif (idx === -1) return { ok: false, error: `Arista no encontrada: ${edgeId}` };\n\t\tconst [removed] = this.data.edges.splice(idx, 1);\n\t\tremoved.reason = reason;\n\t\tremoved.rejected = true;\n\t\tvoid this.persist();\n\t\treturn { ok: true, data: { removed } };\n\t}\n\n\t// ─── Búsqueda ───\n\n\tsearch(term: string): Array<{ node: GraphNode; score: number }> {\n\t\tconst tokens = new Set(tokenize(term.toLowerCase()));\n\t\tconst scores: Array<{ node: GraphNode; score: number }> = [];\n\t\tfor (const node of Object.values(this.data.nodes)) {\n\t\t\tconst text = `${node.description} ${node.id}`.toLowerCase();\n\t\t\tlet score = 0;\n\t\t\tfor (const t of tokens) {\n\t\t\t\tif (text.includes(t)) score++;\n\t\t\t}\n\t\t\tif (score > 0) scores.push({ node, score });\n\t\t}\n\t\treturn scores.sort((a, b) => b.score - a.score);\n\t}\n\n\tcontext(id: string, minConfidence?: number): GraphResult<{ node?: GraphNode; neighbors: GraphEdge[] }> {\n\t\tconst threshold = minConfidence ?? this.minConfidence;\n\t\tconst node = this.data.nodes[id];\n\t\tif (!node) return { ok: false, error: `Nodo no encontrado: ${id}` };\n\t\tif (node.confidence < threshold) {\n\t\t\treturn { ok: false, error: `Nodo ${id} tiene confianza ${node.confidence} < ${threshold} (filtrado)` };\n\t\t}\n\t\tconst neighbors = this.data.edges.filter(\n\t\t\t(e) => (e.subject === id || e.object === id) && e.confidence >= threshold,\n\t\t);\n\t\treturn { ok: true, data: { node, neighbors } };\n\t}\n\n\t// ─── Gates edge-driven ───\n\n\t/**\n\t * gaps(): devuelve el spine incompleto.\n\t * - TODO INTENT sin motivar un REQUIREMENT\n\t * - TODO REQUIREMENT sin planned_as → TASK\n\t * - TODO TASK sin touches → FILE\n\t * - TODO REQUIREMENT sin verified_by → TEST ni implemented_by → SYMBOL\n\t */\n\tgaps(): Array<{ id: string; description: string; missing: string }> {\n\t\tconst gaps: Array<{ id: string; description: string; missing: string }> = [];\n\t\tconst edgeFor = (from: string, predicate: string) =>\n\t\t\tthis.data.edges.some((e) => e.subject === from && e.predicate === predicate);\n\n\t\tfor (const n of Object.values(this.data.nodes)) {\n\t\t\tif (n.type === \"INTENT\" && !edgeFor(n.id, \"motivates\"))\n\t\t\t\tgaps.push({ id: n.id, description: n.description, missing: \"REQUIREMENT motivado (motivates)\" });\n\t\t\tif (n.type === \"REQUIREMENT\" && !edgeFor(n.id, \"planned_as\"))\n\t\t\t\tgaps.push({ id: n.id, description: n.description, missing: \"TASK planificada (planned_as)\" });\n\t\t\tif (n.type === \"TASK\" && !edgeFor(n.id, \"touches\"))\n\t\t\t\tgaps.push({ id: n.id, description: n.description, missing: \"FILE tocado (touches)\" });\n\t\t\tif (n.type === \"REQUIREMENT\" && !edgeFor(n.id, \"verified_by\") && !edgeFor(n.id, \"implemented_by\"))\n\t\t\t\tgaps.push({ id: n.id, description: n.description, missing: \"TEST verified_by o SYMBOL implemented_by\" });\n\t\t}\n\t\treturn gaps;\n\t}\n\n\t/** gate(criteria): devuelve true si TODOS los requisitos tienen verified_by REAL. */\n\tgate(): GraphResult<{ passed: boolean; failures: string[] }> {\n\t\tconst requirements = Object.values(this.data.nodes).filter((n) => n.type === \"REQUIREMENT\");\n\t\tif (requirements.length === 0) return { ok: true, data: { passed: true, failures: [] } };\n\t\tconst failures: string[] = [];\n\n\t\t// Una DECISION open bloquea el gate (§5 regla 4: gap que bloquea).\n\t\tfor (const n of Object.values(this.data.nodes)) {\n\t\t\tif (n.type === \"DECISION\" && n.status === \"open\") {\n\t\t\t\tfailures.push(`${n.id}: decisión abierta bloquea el gate`);\n\t\t\t}\n\t\t}\n\n\t\tfor (const req of requirements) {\n\t\t\tconst verified = this.data.edges.some((e) => e.subject === req.id && e.predicate === \"verified_by\");\n\t\t\tif (!verified) failures.push(`${req.id}: sin verified_by`);\n\t\t}\n\t\treturn { ok: true, data: { passed: failures.length === 0, failures } };\n\t}\n\n\tnodeCount(): number {\n\t\treturn Object.keys(this.data.nodes).length;\n\t}\n\tedgeCount(): number {\n\t\treturn this.data.edges.length;\n\t}\n\tnodes(): GraphNode[] {\n\t\treturn Object.values(this.data.nodes);\n\t}\n\tedges(): GraphEdge[] {\n\t\treturn [...this.data.edges];\n\t}\n\n\t// ─── helpers ───\n\n\tprivate clampConfidence(c?: Confidence): Confidence {\n\t\tif (c === undefined) return 1.0;\n\t\tconst n = Math.max(0, Math.min(1, c));\n\t\treturn n as Confidence;\n\t}\n\n\tprivate async persist(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.store.save(this.data);\n\t\t} catch {\n\t\t\t// best-effort: el disco es cache, no fuente de verdad estricta en memoria\n\t\t}\n\t}\n\n\tprivate emit(type: \"graph_update\" | \"graph_edge_rejected\", payload: Record<string, unknown>): void {\n\t\tthis.onEvent?.(type, payload);\n\t}\n}\n\n// ─── Hash de id auxiliar (para slugs estables) ───\n\nexport function slugHash(input: string, len = 6): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\").slice(0, len);\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { AgentRunner, agentIdentityHash, extractJson, messageText } from "./agent-runner.ts";
|
|
2
|
+
export { generateCodebaseAuditScript, generateCodeReviewScript, generateDeepResearchScript, generateMultiPerspectiveScript, generateProductBuildScript, } from "./builtins.ts";
|
|
3
|
+
export * from "./goal-engine/index.ts";
|
|
4
|
+
export type { GraphData, GraphEdge, GraphNode, GraphOptions, GraphResult, GraphStore } from "./graph.ts";
|
|
5
|
+
export { FileGraphStore, Graph, LEGAL_EDGES, MemoryGraphStore, NODE_LAYER, SPINE_EDGES, slugHash, } from "./graph.ts";
|
|
6
|
+
export { JOURNAL_VERSION, type PersistedRunState, RunJournal } from "./journal.ts";
|
|
7
|
+
export { type WorkflowEventMap, WorkflowManager, type WorkflowManagerOptions } from "./manager.ts";
|
|
8
|
+
export type { ResolveWorkflowResult } from "./resolve-script.ts";
|
|
9
|
+
export { listUserWorkflowScripts, resolveBuiltinWorkflowScript, resolveWorkflowScriptByName, } from "./resolve-script.ts";
|
|
10
|
+
export { createLimiter, parseWorkflowScript, runWorkflow } from "./runtime.ts";
|
|
11
|
+
export { SharedStore } from "./shared-store.ts";
|
|
12
|
+
export { type CreateSubagentSessionOptions, createSubagentSession } from "./subagent-host.ts";
|
|
13
|
+
export type { WorkflowToolContext } from "./tool-workflow.ts";
|
|
14
|
+
export { createWorkflowTool } from "./tool-workflow.ts";
|
|
15
|
+
export type { AgentRunOptions, AgentRunResult, AgentUsage, WorkflowApi, WorkflowErrorCode, WorkflowLimits, WorkflowMeta, WorkflowPhase, WorkflowRunResult, } from "./types.ts";
|
|
16
|
+
export { DEFAULT_WORKFLOW_LIMITS, WorkflowError } from "./types.ts";
|
|
17
|
+
export { createWorktree, removeWorktree, type Worktree } from "./worktree.ts";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflows/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EACN,2BAA2B,EAC3B,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,0BAA0B,GAC1B,MAAM,eAAe,CAAC;AACvB,cAAc,wBAAwB,CAAC;AACvC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACzG,OAAO,EACN,cAAc,EACd,KAAK,EACL,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,KAAK,gBAAgB,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACnG,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EACN,uBAAuB,EACvB,4BAA4B,EAC5B,2BAA2B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,KAAK,4BAA4B,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC9F,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,YAAY,EACX,eAAe,EACf,cAAc,EACd,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,iBAAiB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC","sourcesContent":["export { AgentRunner, agentIdentityHash, extractJson, messageText } from \"./agent-runner.ts\";\nexport {\n\tgenerateCodebaseAuditScript,\n\tgenerateCodeReviewScript,\n\tgenerateDeepResearchScript,\n\tgenerateMultiPerspectiveScript,\n\tgenerateProductBuildScript,\n} from \"./builtins.ts\";\nexport * from \"./goal-engine/index.ts\";\nexport type { GraphData, GraphEdge, GraphNode, GraphOptions, GraphResult, GraphStore } from \"./graph.ts\";\nexport {\n\tFileGraphStore,\n\tGraph,\n\tLEGAL_EDGES,\n\tMemoryGraphStore,\n\tNODE_LAYER,\n\tSPINE_EDGES,\n\tslugHash,\n} from \"./graph.ts\";\nexport { JOURNAL_VERSION, type PersistedRunState, RunJournal } from \"./journal.ts\";\nexport { type WorkflowEventMap, WorkflowManager, type WorkflowManagerOptions } from \"./manager.ts\";\nexport type { ResolveWorkflowResult } from \"./resolve-script.ts\";\nexport {\n\tlistUserWorkflowScripts,\n\tresolveBuiltinWorkflowScript,\n\tresolveWorkflowScriptByName,\n} from \"./resolve-script.ts\";\nexport { createLimiter, parseWorkflowScript, runWorkflow } from \"./runtime.ts\";\nexport { SharedStore } from \"./shared-store.ts\";\nexport { type CreateSubagentSessionOptions, createSubagentSession } from \"./subagent-host.ts\";\nexport type { WorkflowToolContext } from \"./tool-workflow.ts\";\nexport { createWorkflowTool } from \"./tool-workflow.ts\";\nexport type {\n\tAgentRunOptions,\n\tAgentRunResult,\n\tAgentUsage,\n\tWorkflowApi,\n\tWorkflowErrorCode,\n\tWorkflowLimits,\n\tWorkflowMeta,\n\tWorkflowPhase,\n\tWorkflowRunResult,\n} from \"./types.ts\";\nexport { DEFAULT_WORKFLOW_LIMITS, WorkflowError } from \"./types.ts\";\nexport { createWorktree, removeWorktree, type Worktree } from \"./worktree.ts\";\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { AgentRunner, agentIdentityHash, extractJson, messageText } from "./agent-runner.js";
|
|
2
|
+
export { generateCodebaseAuditScript, generateCodeReviewScript, generateDeepResearchScript, generateMultiPerspectiveScript, generateProductBuildScript, } from "./builtins.js";
|
|
3
|
+
export * from "./goal-engine/index.js";
|
|
4
|
+
export { FileGraphStore, Graph, LEGAL_EDGES, MemoryGraphStore, NODE_LAYER, SPINE_EDGES, slugHash, } from "./graph.js";
|
|
5
|
+
export { JOURNAL_VERSION, RunJournal } from "./journal.js";
|
|
6
|
+
export { WorkflowManager } from "./manager.js";
|
|
7
|
+
export { listUserWorkflowScripts, resolveBuiltinWorkflowScript, resolveWorkflowScriptByName, } from "./resolve-script.js";
|
|
8
|
+
export { createLimiter, parseWorkflowScript, runWorkflow } from "./runtime.js";
|
|
9
|
+
export { SharedStore } from "./shared-store.js";
|
|
10
|
+
export { createSubagentSession } from "./subagent-host.js";
|
|
11
|
+
export { createWorkflowTool } from "./tool-workflow.js";
|
|
12
|
+
export { DEFAULT_WORKFLOW_LIMITS, WorkflowError } from "./types.js";
|
|
13
|
+
export { createWorktree, removeWorktree } from "./worktree.js";
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/workflows/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EACN,2BAA2B,EAC3B,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,0BAA0B,GAC1B,MAAM,eAAe,CAAC;AACvB,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,cAAc,EACd,KAAK,EACL,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAA0B,UAAU,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAyB,eAAe,EAA+B,MAAM,cAAc,CAAC;AAEnG,OAAO,EACN,uBAAuB,EACvB,4BAA4B,EAC5B,2BAA2B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAqC,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAYxD,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAiB,MAAM,eAAe,CAAC","sourcesContent":["export { AgentRunner, agentIdentityHash, extractJson, messageText } from \"./agent-runner.ts\";\nexport {\n\tgenerateCodebaseAuditScript,\n\tgenerateCodeReviewScript,\n\tgenerateDeepResearchScript,\n\tgenerateMultiPerspectiveScript,\n\tgenerateProductBuildScript,\n} from \"./builtins.ts\";\nexport * from \"./goal-engine/index.ts\";\nexport type { GraphData, GraphEdge, GraphNode, GraphOptions, GraphResult, GraphStore } from \"./graph.ts\";\nexport {\n\tFileGraphStore,\n\tGraph,\n\tLEGAL_EDGES,\n\tMemoryGraphStore,\n\tNODE_LAYER,\n\tSPINE_EDGES,\n\tslugHash,\n} from \"./graph.ts\";\nexport { JOURNAL_VERSION, type PersistedRunState, RunJournal } from \"./journal.ts\";\nexport { type WorkflowEventMap, WorkflowManager, type WorkflowManagerOptions } from \"./manager.ts\";\nexport type { ResolveWorkflowResult } from \"./resolve-script.ts\";\nexport {\n\tlistUserWorkflowScripts,\n\tresolveBuiltinWorkflowScript,\n\tresolveWorkflowScriptByName,\n} from \"./resolve-script.ts\";\nexport { createLimiter, parseWorkflowScript, runWorkflow } from \"./runtime.ts\";\nexport { SharedStore } from \"./shared-store.ts\";\nexport { type CreateSubagentSessionOptions, createSubagentSession } from \"./subagent-host.ts\";\nexport type { WorkflowToolContext } from \"./tool-workflow.ts\";\nexport { createWorkflowTool } from \"./tool-workflow.ts\";\nexport type {\n\tAgentRunOptions,\n\tAgentRunResult,\n\tAgentUsage,\n\tWorkflowApi,\n\tWorkflowErrorCode,\n\tWorkflowLimits,\n\tWorkflowMeta,\n\tWorkflowPhase,\n\tWorkflowRunResult,\n} from \"./types.ts\";\nexport { DEFAULT_WORKFLOW_LIMITS, WorkflowError } from \"./types.ts\";\nexport { createWorktree, removeWorktree, type Worktree } from \"./worktree.ts\";\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunJournal — persistencia del estado de un run de workflow.
|
|
3
|
+
*
|
|
4
|
+
* El disco es la fuente de verdad (adversarial R8.5): el snapshot se escribe
|
|
5
|
+
* tras cada evento relevante; el resume lee el archivo y re-aplica el journal
|
|
6
|
+
* (resultados cacheados + deltas del store en orden callSeq).
|
|
7
|
+
*
|
|
8
|
+
* Formato versionado (adversarial R9.1): `version` desde el día 1, migraciones
|
|
9
|
+
* incrementales con test por migración.
|
|
10
|
+
*/
|
|
11
|
+
import type { JournalEntry, WorkflowMeta } from "./types.ts";
|
|
12
|
+
export declare const JOURNAL_VERSION = 1;
|
|
13
|
+
export interface PersistedRunState {
|
|
14
|
+
version: number;
|
|
15
|
+
/** Sesión que lanzó el run (para filtrar por conversación en el panel). */
|
|
16
|
+
sessionId?: string;
|
|
17
|
+
/** Nombre para mostrar en el panel (p.ej. el título de la sub-tarea del goal). */
|
|
18
|
+
displayName?: string;
|
|
19
|
+
runId: string;
|
|
20
|
+
name: string;
|
|
21
|
+
script: string;
|
|
22
|
+
args: Record<string, unknown>;
|
|
23
|
+
meta: WorkflowMeta;
|
|
24
|
+
status: "pending" | "running" | "paused" | "completed" | "failed" | "aborted";
|
|
25
|
+
phases: string[];
|
|
26
|
+
currentPhase: string | null;
|
|
27
|
+
/** Progreso en vivo por agente (lo lee el panel del desktop). */
|
|
28
|
+
agents?: Array<{
|
|
29
|
+
id: string;
|
|
30
|
+
label: string;
|
|
31
|
+
phase?: string;
|
|
32
|
+
status: "running" | "done" | "failed" | "blocked";
|
|
33
|
+
blockedReason?: string;
|
|
34
|
+
}>;
|
|
35
|
+
journal: JournalEntry[];
|
|
36
|
+
agentCount: number;
|
|
37
|
+
tokenUsage: {
|
|
38
|
+
input: number;
|
|
39
|
+
output: number;
|
|
40
|
+
total: number;
|
|
41
|
+
cost: number;
|
|
42
|
+
cacheRead: number;
|
|
43
|
+
cacheWrite: number;
|
|
44
|
+
};
|
|
45
|
+
startedAt: string;
|
|
46
|
+
updatedAt: string;
|
|
47
|
+
resultPath?: string;
|
|
48
|
+
error?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface RunJournalOptions {
|
|
51
|
+
/** Directorio raíz de persistencia (~/.ada/agent/workflows/runs). */
|
|
52
|
+
runsDir: string;
|
|
53
|
+
}
|
|
54
|
+
export declare class RunJournal {
|
|
55
|
+
private runsDir;
|
|
56
|
+
constructor(options: RunJournalOptions);
|
|
57
|
+
pathFor(runId: string): string;
|
|
58
|
+
save(state: PersistedRunState): Promise<void>;
|
|
59
|
+
load(runId: string): Promise<PersistedRunState | null>;
|
|
60
|
+
delete(runId: string): Promise<void>;
|
|
61
|
+
list(): Promise<Array<{
|
|
62
|
+
runId: string;
|
|
63
|
+
name: string;
|
|
64
|
+
status: PersistedRunState["status"];
|
|
65
|
+
updatedAt: string;
|
|
66
|
+
}>>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../../src/core/workflows/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE7D,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC9E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iEAAiE;IACjE,MAAM,CAAC,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;QAClD,aAAa,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC,CAAC;IACH,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAClH,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IACjC,sEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,UAAU;IACtB,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,OAAO,EAAE,iBAAiB,EAErC;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7B;IAEK,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOlD;IAEK,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAW3D;IAEK,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzC;IAEK,IAAI,IAAI,OAAO,CACpB,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAC9F,CAqBA;CACD","sourcesContent":["/**\n * RunJournal — persistencia del estado de un run de workflow.\n *\n * El disco es la fuente de verdad (adversarial R8.5): el snapshot se escribe\n * tras cada evento relevante; el resume lee el archivo y re-aplica el journal\n * (resultados cacheados + deltas del store en orden callSeq).\n *\n * Formato versionado (adversarial R9.1): `version` desde el día 1, migraciones\n * incrementales con test por migración.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { JournalEntry, WorkflowMeta } from \"./types.ts\";\n\nexport const JOURNAL_VERSION = 1;\n\nexport interface PersistedRunState {\n\tversion: number;\n\t/** Sesión que lanzó el run (para filtrar por conversación en el panel). */\n\tsessionId?: string;\n\t/** Nombre para mostrar en el panel (p.ej. el título de la sub-tarea del goal). */\n\tdisplayName?: string;\n\trunId: string;\n\tname: string;\n\tscript: string;\n\targs: Record<string, unknown>;\n\tmeta: WorkflowMeta;\n\tstatus: \"pending\" | \"running\" | \"paused\" | \"completed\" | \"failed\" | \"aborted\";\n\tphases: string[];\n\tcurrentPhase: string | null;\n\t/** Progreso en vivo por agente (lo lee el panel del desktop). */\n\tagents?: Array<{\n\t\tid: string;\n\t\tlabel: string;\n\t\tphase?: string;\n\t\tstatus: \"running\" | \"done\" | \"failed\" | \"blocked\";\n\t\tblockedReason?: string;\n\t}>;\n\tjournal: JournalEntry[];\n\tagentCount: number;\n\ttokenUsage: { input: number; output: number; total: number; cost: number; cacheRead: number; cacheWrite: number };\n\tstartedAt: string;\n\tupdatedAt: string;\n\tresultPath?: string;\n\terror?: string;\n}\n\nexport interface RunJournalOptions {\n\t/** Directorio raíz de persistencia (~/.ada/agent/workflows/runs). */\n\trunsDir: string;\n}\n\nexport class RunJournal {\n\tprivate runsDir: string;\n\n\tconstructor(options: RunJournalOptions) {\n\t\tthis.runsDir = options.runsDir;\n\t}\n\n\tpathFor(runId: string): string {\n\t\treturn join(this.runsDir, `${runId}.json`);\n\t}\n\n\tasync save(state: PersistedRunState): Promise<void> {\n\t\tconst payload = { ...state, version: JOURNAL_VERSION, updatedAt: new Date().toISOString() };\n\t\tawait mkdir(dirname(this.pathFor(state.runId)), { recursive: true });\n\t\t// tmp ÚNICO por save: saves concurrentes (live progress) no pisan el tmp ajeno.\n\t\tconst tmp = `${this.pathFor(state.runId)}.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`;\n\t\tawait writeFile(tmp, JSON.stringify(payload, null, 1), \"utf-8\");\n\t\tawait import(\"node:fs/promises\").then(({ rename }) => rename(tmp, this.pathFor(state.runId)));\n\t}\n\n\tasync load(runId: string): Promise<PersistedRunState | null> {\n\t\ttry {\n\t\t\tconst raw = await readFile(this.pathFor(runId), \"utf-8\");\n\t\t\tconst parsed = JSON.parse(raw) as PersistedRunState;\n\t\t\tif (typeof parsed.version !== \"number\" || parsed.version > JOURNAL_VERSION) {\n\t\t\t\treturn null; // versión futura — no tocar\n\t\t\t}\n\t\t\treturn migrate(parsed);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync delete(runId: string): Promise<void> {\n\t\ttry {\n\t\t\tconst { rm } = await import(\"node:fs/promises\");\n\t\t\tawait rm(this.pathFor(runId), { force: true });\n\t\t} catch {\n\t\t\t// best-effort\n\t\t}\n\t}\n\n\tasync list(): Promise<\n\t\tArray<{ runId: string; name: string; status: PersistedRunState[\"status\"]; updatedAt: string }>\n\t> {\n\t\ttry {\n\t\t\tconst { readdir } = await import(\"node:fs/promises\");\n\t\t\tconst files = await readdir(this.runsDir).catch(() => [] as string[]);\n\t\t\tconst out: Array<{ runId: string; name: string; status: PersistedRunState[\"status\"]; updatedAt: string }> = [];\n\t\t\tfor (const file of files) {\n\t\t\t\tif (!file.endsWith(\".json\") || file.endsWith(\".tmp\")) continue;\n\t\t\t\tconst state = await this.load(file.replace(/\\.json$/, \"\"));\n\t\t\t\tif (state) {\n\t\t\t\t\tout.push({\n\t\t\t\t\t\trunId: state.runId,\n\t\t\t\t\t\tname: state.name,\n\t\t\t\t\t\tstatus: state.status,\n\t\t\t\t\t\tupdatedAt: state.updatedAt,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n}\n\n/** Migraciones incrementales (v1 → v2 …). */\nfunction migrate(state: PersistedRunState & { version: number }): PersistedRunState {\n\tconst current = state as PersistedRunState;\n\t// v1: sin migraciones aún. Las siguientes versiones añaden `switch (current.version)`.\n\treturn current;\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunJournal — persistencia del estado de un run de workflow.
|
|
3
|
+
*
|
|
4
|
+
* El disco es la fuente de verdad (adversarial R8.5): el snapshot se escribe
|
|
5
|
+
* tras cada evento relevante; el resume lee el archivo y re-aplica el journal
|
|
6
|
+
* (resultados cacheados + deltas del store en orden callSeq).
|
|
7
|
+
*
|
|
8
|
+
* Formato versionado (adversarial R9.1): `version` desde el día 1, migraciones
|
|
9
|
+
* incrementales con test por migración.
|
|
10
|
+
*/
|
|
11
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
export const JOURNAL_VERSION = 1;
|
|
14
|
+
export class RunJournal {
|
|
15
|
+
runsDir;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.runsDir = options.runsDir;
|
|
18
|
+
}
|
|
19
|
+
pathFor(runId) {
|
|
20
|
+
return join(this.runsDir, `${runId}.json`);
|
|
21
|
+
}
|
|
22
|
+
async save(state) {
|
|
23
|
+
const payload = { ...state, version: JOURNAL_VERSION, updatedAt: new Date().toISOString() };
|
|
24
|
+
await mkdir(dirname(this.pathFor(state.runId)), { recursive: true });
|
|
25
|
+
// tmp ÚNICO por save: saves concurrentes (live progress) no pisan el tmp ajeno.
|
|
26
|
+
const tmp = `${this.pathFor(state.runId)}.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`;
|
|
27
|
+
await writeFile(tmp, JSON.stringify(payload, null, 1), "utf-8");
|
|
28
|
+
await import("node:fs/promises").then(({ rename }) => rename(tmp, this.pathFor(state.runId)));
|
|
29
|
+
}
|
|
30
|
+
async load(runId) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = await readFile(this.pathFor(runId), "utf-8");
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
if (typeof parsed.version !== "number" || parsed.version > JOURNAL_VERSION) {
|
|
35
|
+
return null; // versión futura — no tocar
|
|
36
|
+
}
|
|
37
|
+
return migrate(parsed);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async delete(runId) {
|
|
44
|
+
try {
|
|
45
|
+
const { rm } = await import("node:fs/promises");
|
|
46
|
+
await rm(this.pathFor(runId), { force: true });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// best-effort
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async list() {
|
|
53
|
+
try {
|
|
54
|
+
const { readdir } = await import("node:fs/promises");
|
|
55
|
+
const files = await readdir(this.runsDir).catch(() => []);
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const file of files) {
|
|
58
|
+
if (!file.endsWith(".json") || file.endsWith(".tmp"))
|
|
59
|
+
continue;
|
|
60
|
+
const state = await this.load(file.replace(/\.json$/, ""));
|
|
61
|
+
if (state) {
|
|
62
|
+
out.push({
|
|
63
|
+
runId: state.runId,
|
|
64
|
+
name: state.name,
|
|
65
|
+
status: state.status,
|
|
66
|
+
updatedAt: state.updatedAt,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Migraciones incrementales (v1 → v2 …). */
|
|
78
|
+
function migrate(state) {
|
|
79
|
+
const current = state;
|
|
80
|
+
// v1: sin migraciones aún. Las siguientes versiones añaden `switch (current.version)`.
|
|
81
|
+
return current;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../../src/core/workflows/journal.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RunJournal — persistencia del estado de un run de workflow.\n *\n * El disco es la fuente de verdad (adversarial R8.5): el snapshot se escribe\n * tras cada evento relevante; el resume lee el archivo y re-aplica el journal\n * (resultados cacheados + deltas del store en orden callSeq).\n *\n * Formato versionado (adversarial R9.1): `version` desde el día 1, migraciones\n * incrementales con test por migración.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { JournalEntry, WorkflowMeta } from \"./types.ts\";\n\nexport const JOURNAL_VERSION = 1;\n\nexport interface PersistedRunState {\n\tversion: number;\n\t/** Sesión que lanzó el run (para filtrar por conversación en el panel). */\n\tsessionId?: string;\n\t/** Nombre para mostrar en el panel (p.ej. el título de la sub-tarea del goal). */\n\tdisplayName?: string;\n\trunId: string;\n\tname: string;\n\tscript: string;\n\targs: Record<string, unknown>;\n\tmeta: WorkflowMeta;\n\tstatus: \"pending\" | \"running\" | \"paused\" | \"completed\" | \"failed\" | \"aborted\";\n\tphases: string[];\n\tcurrentPhase: string | null;\n\t/** Progreso en vivo por agente (lo lee el panel del desktop). */\n\tagents?: Array<{\n\t\tid: string;\n\t\tlabel: string;\n\t\tphase?: string;\n\t\tstatus: \"running\" | \"done\" | \"failed\" | \"blocked\";\n\t\tblockedReason?: string;\n\t}>;\n\tjournal: JournalEntry[];\n\tagentCount: number;\n\ttokenUsage: { input: number; output: number; total: number; cost: number; cacheRead: number; cacheWrite: number };\n\tstartedAt: string;\n\tupdatedAt: string;\n\tresultPath?: string;\n\terror?: string;\n}\n\nexport interface RunJournalOptions {\n\t/** Directorio raíz de persistencia (~/.ada/agent/workflows/runs). */\n\trunsDir: string;\n}\n\nexport class RunJournal {\n\tprivate runsDir: string;\n\n\tconstructor(options: RunJournalOptions) {\n\t\tthis.runsDir = options.runsDir;\n\t}\n\n\tpathFor(runId: string): string {\n\t\treturn join(this.runsDir, `${runId}.json`);\n\t}\n\n\tasync save(state: PersistedRunState): Promise<void> {\n\t\tconst payload = { ...state, version: JOURNAL_VERSION, updatedAt: new Date().toISOString() };\n\t\tawait mkdir(dirname(this.pathFor(state.runId)), { recursive: true });\n\t\t// tmp ÚNICO por save: saves concurrentes (live progress) no pisan el tmp ajeno.\n\t\tconst tmp = `${this.pathFor(state.runId)}.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`;\n\t\tawait writeFile(tmp, JSON.stringify(payload, null, 1), \"utf-8\");\n\t\tawait import(\"node:fs/promises\").then(({ rename }) => rename(tmp, this.pathFor(state.runId)));\n\t}\n\n\tasync load(runId: string): Promise<PersistedRunState | null> {\n\t\ttry {\n\t\t\tconst raw = await readFile(this.pathFor(runId), \"utf-8\");\n\t\t\tconst parsed = JSON.parse(raw) as PersistedRunState;\n\t\t\tif (typeof parsed.version !== \"number\" || parsed.version > JOURNAL_VERSION) {\n\t\t\t\treturn null; // versión futura — no tocar\n\t\t\t}\n\t\t\treturn migrate(parsed);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync delete(runId: string): Promise<void> {\n\t\ttry {\n\t\t\tconst { rm } = await import(\"node:fs/promises\");\n\t\t\tawait rm(this.pathFor(runId), { force: true });\n\t\t} catch {\n\t\t\t// best-effort\n\t\t}\n\t}\n\n\tasync list(): Promise<\n\t\tArray<{ runId: string; name: string; status: PersistedRunState[\"status\"]; updatedAt: string }>\n\t> {\n\t\ttry {\n\t\t\tconst { readdir } = await import(\"node:fs/promises\");\n\t\t\tconst files = await readdir(this.runsDir).catch(() => [] as string[]);\n\t\t\tconst out: Array<{ runId: string; name: string; status: PersistedRunState[\"status\"]; updatedAt: string }> = [];\n\t\t\tfor (const file of files) {\n\t\t\t\tif (!file.endsWith(\".json\") || file.endsWith(\".tmp\")) continue;\n\t\t\t\tconst state = await this.load(file.replace(/\\.json$/, \"\"));\n\t\t\t\tif (state) {\n\t\t\t\t\tout.push({\n\t\t\t\t\t\trunId: state.runId,\n\t\t\t\t\t\tname: state.name,\n\t\t\t\t\t\tstatus: state.status,\n\t\t\t\t\t\tupdatedAt: state.updatedAt,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n}\n\n/** Migraciones incrementales (v1 → v2 …). */\nfunction migrate(state: PersistedRunState & { version: number }): PersistedRunState {\n\tconst current = state as PersistedRunState;\n\t// v1: sin migraciones aún. Las siguientes versiones añaden `switch (current.version)`.\n\treturn current;\n}\n"]}
|