@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,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowManager — ejecución de workflows en background con estado durable.
|
|
3
|
+
*
|
|
4
|
+
* - execute(script, args): valida, persiste el estado (RunJournal), corre el
|
|
5
|
+
* runtime, emite eventos (started/phase/agent/update/finished).
|
|
6
|
+
* - pause/resume/abort: aborta con señal; el estado queda para reanudar
|
|
7
|
+
* (resume con el mismo runId reutiliza el journal).
|
|
8
|
+
* - El disco es la fuente de verdad (snapshot en cada evento).
|
|
9
|
+
*/
|
|
10
|
+
import { type PersistedRunState, RunJournal } from "./journal.ts";
|
|
11
|
+
import { type WorkflowRuntimeOptions } from "./runtime.ts";
|
|
12
|
+
export interface WorkflowEventMap {
|
|
13
|
+
workflow_run_started: {
|
|
14
|
+
runId: string;
|
|
15
|
+
name: string;
|
|
16
|
+
phases: string[];
|
|
17
|
+
};
|
|
18
|
+
workflow_phase: {
|
|
19
|
+
runId: string;
|
|
20
|
+
phase: string;
|
|
21
|
+
};
|
|
22
|
+
workflow_agent: {
|
|
23
|
+
runId: string;
|
|
24
|
+
agent: {
|
|
25
|
+
id: string;
|
|
26
|
+
label: string;
|
|
27
|
+
phase?: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
workflow_run_update: {
|
|
31
|
+
runId: string;
|
|
32
|
+
status: PersistedRunState["status"];
|
|
33
|
+
agentsDone: number;
|
|
34
|
+
agentsTotal: number;
|
|
35
|
+
tokens: number;
|
|
36
|
+
cost: number;
|
|
37
|
+
};
|
|
38
|
+
workflow_run_finished: {
|
|
39
|
+
runId: string;
|
|
40
|
+
status: PersistedRunState["status"];
|
|
41
|
+
resultPath?: string;
|
|
42
|
+
agentCount: number;
|
|
43
|
+
tokens: number;
|
|
44
|
+
cost: number;
|
|
45
|
+
error?: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export interface WorkflowManagerOptions {
|
|
49
|
+
runsDir: string;
|
|
50
|
+
createSession: WorkflowRuntimeOptions["createSession"];
|
|
51
|
+
limits?: WorkflowRuntimeOptions["limits"];
|
|
52
|
+
onEvent?: <K extends keyof WorkflowEventMap>(type: K, payload: WorkflowEventMap[K]) => void;
|
|
53
|
+
confirm?: WorkflowRuntimeOptions["confirm"];
|
|
54
|
+
}
|
|
55
|
+
export interface ExecuteWorkflowOptions {
|
|
56
|
+
args?: Record<string, unknown>;
|
|
57
|
+
runId?: string;
|
|
58
|
+
/** Sesión que lanza el run (filtro por conversación en el panel). */
|
|
59
|
+
sessionId?: string;
|
|
60
|
+
/** Nombre para mostrar en el panel (título de sub-tarea del goal, etc.). */
|
|
61
|
+
displayName?: string;
|
|
62
|
+
/** Esperar a que termine (default: true). */
|
|
63
|
+
await?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export declare class WorkflowManager {
|
|
66
|
+
private journal;
|
|
67
|
+
private runsDir;
|
|
68
|
+
private createSession;
|
|
69
|
+
private limits;
|
|
70
|
+
private onEvent?;
|
|
71
|
+
private confirm?;
|
|
72
|
+
private abortControllers;
|
|
73
|
+
constructor(options: WorkflowManagerOptions);
|
|
74
|
+
get runJournal(): RunJournal;
|
|
75
|
+
list(): Promise<Array<{
|
|
76
|
+
runId: string;
|
|
77
|
+
name: string;
|
|
78
|
+
status: PersistedRunState["status"];
|
|
79
|
+
updatedAt: string;
|
|
80
|
+
}>>;
|
|
81
|
+
state(runId: string): Promise<PersistedRunState | null>;
|
|
82
|
+
/**
|
|
83
|
+
* Desatasca los runs que murieron con su proceso.
|
|
84
|
+
*
|
|
85
|
+
* `execute` siempre persiste `status: "running"` y nadie escaneaba `runsDir`
|
|
86
|
+
* al arrancar, así que un Ctrl-C durante una sub-tarea dejaba el run en
|
|
87
|
+
* `running` para siempre. Y `GoalEngine.tick` se niega a avanzar mientras
|
|
88
|
+
* haya una task en `running` → el goal quedaba atascado sin salida.
|
|
89
|
+
*
|
|
90
|
+
* Marca esos runs como `aborted` y emite `workflow_run_finished(ok:false)`,
|
|
91
|
+
* con lo que `onWorkflowFinished` libera la sub-tarea por el camino que ya
|
|
92
|
+
* existe (y que ahora además auto-continúa).
|
|
93
|
+
*
|
|
94
|
+
* Deliberadamente NO reanuda el run desde el journal: eso exige confiar en el
|
|
95
|
+
* hash de identidad y es otro item. Aquí solo hay que liberar el goal.
|
|
96
|
+
*
|
|
97
|
+
* ponytail: la propiedad del run se decide por antigüedad de `updatedAt`
|
|
98
|
+
* porque no hay leases de sesión. Si algún día dos procesos comparten
|
|
99
|
+
* `runsDir`, esto necesita un lease de verdad.
|
|
100
|
+
*/
|
|
101
|
+
reconcile(options?: {
|
|
102
|
+
staleAfterMs?: number;
|
|
103
|
+
}): Promise<string[]>;
|
|
104
|
+
/** Ejecuta un workflow. Si await=true (default), resuelve al terminar. */
|
|
105
|
+
execute(script: string, options?: ExecuteWorkflowOptions): Promise<{
|
|
106
|
+
runId: string;
|
|
107
|
+
result?: unknown;
|
|
108
|
+
}>;
|
|
109
|
+
abort(runId: string): boolean;
|
|
110
|
+
private agentDirFromRunsDir;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/workflows/manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WorkflowManager — ejecución de workflows en background con estado durable.\n *\n * - execute(script, args): valida, persiste el estado (RunJournal), corre el\n * runtime, emite eventos (started/phase/agent/update/finished).\n * - pause/resume/abort: aborta con señal; el estado queda para reanudar\n * (resume con el mismo runId reutiliza el journal).\n * - El disco es la fuente de verdad (snapshot en cada evento).\n */\n\nimport { type PersistedRunState, RunJournal } from \"./journal.ts\";\nimport { runWorkflow, type WorkflowRuntimeOptions } from \"./runtime.ts\";\nimport { WorkflowError } from \"./types.ts\";\n\nexport interface WorkflowEventMap {\n\tworkflow_run_started: { runId: string; name: string; phases: string[] };\n\tworkflow_phase: { runId: string; phase: string };\n\tworkflow_agent: { runId: string; agent: { id: string; label: string; phase?: string } };\n\tworkflow_run_update: {\n\t\trunId: string;\n\t\tstatus: PersistedRunState[\"status\"];\n\t\tagentsDone: number;\n\t\tagentsTotal: number;\n\t\ttokens: number;\n\t\tcost: number;\n\t};\n\tworkflow_run_finished: {\n\t\trunId: string;\n\t\tstatus: PersistedRunState[\"status\"];\n\t\tresultPath?: string;\n\t\tagentCount: number;\n\t\ttokens: number;\n\t\tcost: number;\n\t\terror?: string;\n\t};\n}\n\nexport interface WorkflowManagerOptions {\n\trunsDir: string;\n\tcreateSession: WorkflowRuntimeOptions[\"createSession\"];\n\tlimits?: WorkflowRuntimeOptions[\"limits\"];\n\tonEvent?: <K extends keyof WorkflowEventMap>(type: K, payload: WorkflowEventMap[K]) => void;\n\tconfirm?: WorkflowRuntimeOptions[\"confirm\"];\n}\n\nexport interface ExecuteWorkflowOptions {\n\targs?: Record<string, unknown>;\n\trunId?: string;\n\t/** Sesión que lanza el run (filtro por conversación en el panel). */\n\tsessionId?: string;\n\t/** Nombre para mostrar en el panel (título de sub-tarea del goal, etc.). */\n\tdisplayName?: string;\n\t/** Esperar a que termine (default: true). */\n\tawait?: boolean;\n}\n\nexport class WorkflowManager {\n\tprivate journal: RunJournal;\n\tprivate runsDir: string;\n\tprivate createSession: WorkflowRuntimeOptions[\"createSession\"];\n\tprivate limits: WorkflowRuntimeOptions[\"limits\"];\n\tprivate onEvent?: WorkflowManagerOptions[\"onEvent\"];\n\tprivate confirm?: WorkflowRuntimeOptions[\"confirm\"];\n\tprivate abortControllers = new Map<string, AbortController>();\n\n\tconstructor(options: WorkflowManagerOptions) {\n\t\tthis.journal = new RunJournal({ runsDir: options.runsDir });\n\t\tthis.runsDir = options.runsDir;\n\t\tthis.createSession = options.createSession;\n\t\tthis.limits = options.limits;\n\t\tthis.onEvent = options.onEvent;\n\t\tthis.confirm = options.confirm;\n\t}\n\n\tget runJournal(): RunJournal {\n\t\treturn this.journal;\n\t}\n\n\tasync list(): Promise<\n\t\tArray<{ runId: string; name: string; status: PersistedRunState[\"status\"]; updatedAt: string }>\n\t> {\n\t\treturn this.journal.list();\n\t}\n\n\tasync state(runId: string): Promise<PersistedRunState | null> {\n\t\treturn this.journal.load(runId);\n\t}\n\n\t/**\n\t * Desatasca los runs que murieron con su proceso.\n\t *\n\t * `execute` siempre persiste `status: \"running\"` y nadie escaneaba `runsDir`\n\t * al arrancar, así que un Ctrl-C durante una sub-tarea dejaba el run en\n\t * `running` para siempre. Y `GoalEngine.tick` se niega a avanzar mientras\n\t * haya una task en `running` → el goal quedaba atascado sin salida.\n\t *\n\t * Marca esos runs como `aborted` y emite `workflow_run_finished(ok:false)`,\n\t * con lo que `onWorkflowFinished` libera la sub-tarea por el camino que ya\n\t * existe (y que ahora además auto-continúa).\n\t *\n\t * Deliberadamente NO reanuda el run desde el journal: eso exige confiar en el\n\t * hash de identidad y es otro item. Aquí solo hay que liberar el goal.\n\t *\n\t * ponytail: la propiedad del run se decide por antigüedad de `updatedAt`\n\t * porque no hay leases de sesión. Si algún día dos procesos comparten\n\t * `runsDir`, esto necesita un lease de verdad.\n\t */\n\tasync reconcile(options: { staleAfterMs?: number } = {}): Promise<string[]> {\n\t\tconst staleAfterMs = options.staleAfterMs ?? 5 * 60 * 1000;\n\t\tconst now = Date.now();\n\t\tconst reclaimed: string[] = [];\n\t\tlet runs: Awaited<ReturnType<RunJournal[\"list\"]>>;\n\t\ttry {\n\t\t\truns = await this.journal.list();\n\t\t} catch {\n\t\t\treturn reclaimed; // sin runsDir todavía\n\t\t}\n\t\tconst error = \"interrumpido por reinicio del proceso\";\n\t\tfor (const entry of runs) {\n\t\t\tif (entry.status !== \"running\" && entry.status !== \"pending\") continue;\n\t\t\t// Un run vivo de ESTE proceso tiene su AbortController registrado.\n\t\t\tif (this.abortControllers.has(entry.runId)) continue;\n\t\t\tconst age = now - Date.parse(entry.updatedAt);\n\t\t\tif (Number.isNaN(age) || age < staleAfterMs) continue;\n\t\t\tconst state = await this.journal.load(entry.runId);\n\t\t\tif (!state) continue;\n\t\t\tconst aborted: PersistedRunState = {\n\t\t\t\t...state,\n\t\t\t\tstatus: \"aborted\",\n\t\t\t\terror,\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t};\n\t\t\tawait this.journal.save(aborted);\n\t\t\tthis.onEvent?.(\"workflow_run_finished\", {\n\t\t\t\trunId: entry.runId,\n\t\t\t\tstatus: \"aborted\",\n\t\t\t\tagentCount: state.agentCount,\n\t\t\t\ttokens: state.tokenUsage.total,\n\t\t\t\tcost: state.tokenUsage.cost,\n\t\t\t\terror,\n\t\t\t});\n\t\t\treclaimed.push(entry.runId);\n\t\t}\n\t\treturn reclaimed;\n\t}\n\n\t/** Ejecuta un workflow. Si await=true (default), resuelve al terminar. */\n\tasync execute(script: string, options: ExecuteWorkflowOptions = {}): Promise<{ runId: string; result?: unknown }> {\n\t\tconst { meta, body: _body } = await import(\"./runtime.ts\").then((m) => m.parseWorkflowScript(script));\n\t\tconst runId = options.runId ?? `wf-${Date.now().toString(36)}`;\n\t\tconst controller = new AbortController();\n\t\tthis.abortControllers.set(runId, controller);\n\n\t\tconst initialState: PersistedRunState = {\n\t\t\tversion: 1,\n\t\t\tphases: [],\n\t\t\trunId,\n\t\t\tname: meta.name,\n\t\t\tscript,\n\t\t\targs: options.args ?? {},\n\t\t\tmeta,\n\t\t\tstatus: \"running\",\n\t\t\tcurrentPhase: null,\n\t\t\tsessionId: options.sessionId,\n\t\t\tdisplayName: options.displayName,\n\t\t\tjournal: [],\n\t\t\tagentCount: 0,\n\t\t\ttokenUsage: { input: 0, output: 0, total: 0, cost: 0, cacheRead: 0, cacheWrite: 0 },\n\t\t\tstartedAt: new Date().toISOString(),\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t};\n\t\tawait this.journal.save(initialState);\n\t\tthis.onEvent?.(\"workflow_run_started\", {\n\t\t\trunId,\n\t\t\tname: meta.name,\n\t\t\tphases: (meta.phases ?? []).map((p) => p.title),\n\t\t});\n\n\t\tlet agentsDone = 0;\n\t\tlet agentsTotal = 0;\n\t\t// Agente en curso (para marcar blocked cuando espera confirm).\n\t\tlet currentAgent = { id: \"\", label: \"\" };\n\t\t// Estado vivo: se persiste en cada evento para que el panel del\n\t\t// desktop vea fases y agentes en tiempo real (polling de /api/engine).\n\t\tconst live: PersistedRunState = { ...initialState };\n\t\tconst liveAgents: NonNullable<PersistedRunState[\"agents\"]> = [];\n\t\t// Marcar/desmarcar blocked del agente en curso (P4).\n\t\tconst setAgentBlocked = (blocked: boolean, reason = \"\") => {\n\t\t\tif (!currentAgent.id) return;\n\t\t\tconst rec = liveAgents.find((a) => a.id === currentAgent.id);\n\t\t\tif (rec) {\n\t\t\t\trec.status = blocked ? \"blocked\" : \"running\";\n\t\t\t\trec.blockedReason = blocked ? reason : undefined;\n\t\t\t}\n\t\t\tvoid saveLive({ agents: [...liveAgents] });\n\t\t};\n\t\t// Saves serializados (cadena de promesas): preserva el orden y evita\n\t\t// que un snapshot viejo pise uno nuevo.\n\t\tlet saveChain: Promise<void> = Promise.resolve();\n\t\tconst saveLive = (patch: Partial<PersistedRunState>) => {\n\t\t\tObject.assign(live, patch, { updatedAt: new Date().toISOString() });\n\t\t\tsaveChain = saveChain\n\t\t\t\t.then(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.journal.save(live);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// best-effort: nunca rompe el run por un save\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t\treturn saveChain;\n\t\t};\n\n\t\tconst runPromise = runWorkflow(script, {\n\t\t\tcwd: process.cwd(),\n\t\t\tagentDir: this.agentDirFromRunsDir(),\n\t\t\tcreateSession: this.createSession,\n\t\t\tlimits: this.limits,\n\t\t\tsignal: controller.signal,\n\t\t\tconfirm: async (prompt, checkpointOptions) => {\n\t\t\t\t// P4: mientras hay un checkpoint pendiente de input humano,\n\t\t\t\t// el agente en curso está BLOCKED (no procesando).\n\t\t\t\tconst wasBlocked = liveAgents.some((a) => a.status === \"blocked\");\n\t\t\t\tif (this.confirm) {\n\t\t\t\t\tsetAgentBlocked(true, String(prompt).slice(0, 120));\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn await this.confirm(prompt, checkpointOptions);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (wasBlocked) setAgentBlocked(true);\n\t\t\t\t\t\telse setAgentBlocked(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Sin confirm del host: headless → abort (checkpoint no resuelto).\n\t\t\t\tif ((checkpointOptions as { headless?: string } | undefined)?.headless === \"abort\") {\n\t\t\t\t\tthrow new WorkflowError(\n\t\t\t\t\t\t\"WORKFLOW_ABORTED\",\n\t\t\t\t\t\t`checkpoint \"${prompt}\" needs human input but none is available`,\n\t\t\t\t\t\t{ recoverable: false },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn (checkpointOptions as { default?: unknown } | undefined)?.default ?? true;\n\t\t\t},\n\t\t\targs: options.args ?? {},\n\t\t\trunId,\n\t\t\tresumeJournal: new Map(),\n\t\t\tonPhase: (phase) => {\n\t\t\t\tlive.currentPhase = phase;\n\t\t\t\tif (!live.phases.includes(phase)) live.phases.push(phase);\n\t\t\t\tvoid saveLive({});\n\t\t\t\tthis.onEvent?.(\"workflow_phase\", { runId, phase });\n\t\t\t},\n\t\t\tonAgentStart: (_index, label, phase) => {\n\t\t\t\tagentsTotal++;\n\t\t\t\tcurrentAgent = { id: `${runId}:${_index}`, label };\n\t\t\t\tliveAgents.push({ id: `${runId}:${_index}`, label, phase, status: \"running\" });\n\t\t\t\tvoid saveLive({ agents: [...liveAgents] });\n\t\t\t\tthis.onEvent?.(\"workflow_agent\", { runId, agent: { id: `${runId}:${_index}`, label, phase } });\n\t\t\t},\n\t\t\tonAgentEnd: (_index, _label, _result) => {\n\t\t\t\tagentsDone++;\n\t\t\t\tcurrentAgent = { id: \"\", label: \"\" };\n\t\t\t\tconst rec = liveAgents.find((a) => a.id === `${runId}:${_index}`);\n\t\t\t\tif (rec) rec.status = \"done\";\n\t\t\t\tvoid saveLive({ agents: [...liveAgents] });\n\t\t\t\tthis.onEvent?.(\"workflow_run_update\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\tagentsDone,\n\t\t\t\t\tagentsTotal,\n\t\t\t\t\ttokens: 0,\n\t\t\t\t\tcost: 0,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonTokenUsage: (usage) => {\n\t\t\t\tconst u = usage as { total?: number; cost?: number };\n\t\t\t\tvoid saveLive({\n\t\t\t\t\ttokenUsage: {\n\t\t\t\t\t\t...(live.tokenUsage ?? { input: 0, output: 0, total: 0, cost: 0, cacheRead: 0, cacheWrite: 0 }),\n\t\t\t\t\t\ttotal: u.total ?? live.tokenUsage.total,\n\t\t\t\t\t\tcost: u.cost ?? live.tokenUsage.cost,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tthis.onEvent?.(\"workflow_run_update\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\tagentsDone,\n\t\t\t\t\tagentsTotal,\n\t\t\t\t\ttokens: u.total ?? 0,\n\t\t\t\t\tcost: u.cost ?? 0,\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\n\t\tconst settle = async () => {\n\t\t\ttry {\n\t\t\t\tconst result = await runPromise;\n\t\t\t\t// Drenar saves en vivo ANTES del estado final: un saveLive tardío\n\t\t\t\t// no debe pisar el estado completed/failed.\n\t\t\t\tawait saveChain.catch(() => {});\n\t\t\t\tconst finalState: PersistedRunState = {\n\t\t\t\t\t...initialState,\n\t\t\t\t\tstatus: \"completed\",\n\t\t\t\t\tphases: result.phases,\n\t\t\t\t\tcurrentPhase: null,\n\t\t\t\t\tagents: liveAgents.map((a) => ({ ...a, status: \"done\" as const })),\n\t\t\t\t\tagentCount: result.agentCount,\n\t\t\t\t\ttokenUsage: {\n\t\t\t\t\t\tinput: result.tokenUsage.input,\n\t\t\t\t\t\toutput: result.tokenUsage.output,\n\t\t\t\t\t\ttotal: result.tokenUsage.total,\n\t\t\t\t\t\tcost: result.tokenUsage.cost,\n\t\t\t\t\t\tcacheRead: result.tokenUsage.cacheRead ?? 0,\n\t\t\t\t\t\tcacheWrite: result.tokenUsage.cacheWrite ?? 0,\n\t\t\t\t\t},\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t};\n\t\t\t\tawait this.journal.save(finalState);\n\t\t\t\tthis.onEvent?.(\"workflow_run_finished\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus: \"completed\",\n\t\t\t\t\tagentCount: result.agentCount,\n\t\t\t\t\ttokens: result.tokenUsage.total,\n\t\t\t\t\tcost: result.tokenUsage.cost,\n\t\t\t\t});\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tconst status: PersistedRunState[\"status\"] =\n\t\t\t\t\t(error as WorkflowError)?.code === \"WORKFLOW_ABORTED\" ? \"aborted\" : \"failed\";\n\t\t\t\tconst finalState = {\n\t\t\t\t\t...initialState,\n\t\t\t\t\tstatus,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t};\n\t\t\t\tawait this.journal.save(finalState);\n\t\t\t\tthis.onEvent?.(\"workflow_run_finished\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus,\n\t\t\t\t\tagentCount: agentsDone,\n\t\t\t\t\ttokens: 0,\n\t\t\t\t\tcost: 0,\n\t\t\t\t\terror: finalState.error,\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tthis.abortControllers.delete(runId);\n\t\t\t}\n\t\t};\n\n\t\tif (options.await === false) {\n\t\t\t// El error ya se persiste en el estado; capturar evita unhandled.\n\t\t\tvoid settle().catch(() => {});\n\t\t\treturn { runId };\n\t\t}\n\t\tconst result = await settle();\n\t\treturn { runId, result: (result as { result?: unknown }).result };\n\t}\n\n\tabort(runId: string): boolean {\n\t\tconst controller = this.abortControllers.get(runId);\n\t\tif (!controller) return false;\n\t\tcontroller.abort();\n\t\treturn true;\n\t}\n\n\tprivate agentDirFromRunsDir(): string {\n\t\t// El agentDir es el padre de workflows/runs → workflows → agent.\n\t\t// OJO: preservar el / inicial (absoluto) — split+filter(Boolean) lo\n\t\t// elimina y producía una ruta RELATIVA → auth.json irresoluble.\n\t\tconst isAbsolute = this.runsDir.startsWith(\"/\");\n\t\tconst parts = this.runsDir.split(\"/\").filter(Boolean);\n\t\tconst parent = parts.slice(0, Math.max(0, parts.length - 2)).join(\"/\");\n\t\treturn isAbsolute ? `/${parent}` : parent;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowManager — ejecución de workflows en background con estado durable.
|
|
3
|
+
*
|
|
4
|
+
* - execute(script, args): valida, persiste el estado (RunJournal), corre el
|
|
5
|
+
* runtime, emite eventos (started/phase/agent/update/finished).
|
|
6
|
+
* - pause/resume/abort: aborta con señal; el estado queda para reanudar
|
|
7
|
+
* (resume con el mismo runId reutiliza el journal).
|
|
8
|
+
* - El disco es la fuente de verdad (snapshot en cada evento).
|
|
9
|
+
*/
|
|
10
|
+
import { RunJournal } from "./journal.js";
|
|
11
|
+
import { runWorkflow } from "./runtime.js";
|
|
12
|
+
import { WorkflowError } from "./types.js";
|
|
13
|
+
export class WorkflowManager {
|
|
14
|
+
journal;
|
|
15
|
+
runsDir;
|
|
16
|
+
createSession;
|
|
17
|
+
limits;
|
|
18
|
+
onEvent;
|
|
19
|
+
confirm;
|
|
20
|
+
abortControllers = new Map();
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.journal = new RunJournal({ runsDir: options.runsDir });
|
|
23
|
+
this.runsDir = options.runsDir;
|
|
24
|
+
this.createSession = options.createSession;
|
|
25
|
+
this.limits = options.limits;
|
|
26
|
+
this.onEvent = options.onEvent;
|
|
27
|
+
this.confirm = options.confirm;
|
|
28
|
+
}
|
|
29
|
+
get runJournal() {
|
|
30
|
+
return this.journal;
|
|
31
|
+
}
|
|
32
|
+
async list() {
|
|
33
|
+
return this.journal.list();
|
|
34
|
+
}
|
|
35
|
+
async state(runId) {
|
|
36
|
+
return this.journal.load(runId);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Desatasca los runs que murieron con su proceso.
|
|
40
|
+
*
|
|
41
|
+
* `execute` siempre persiste `status: "running"` y nadie escaneaba `runsDir`
|
|
42
|
+
* al arrancar, así que un Ctrl-C durante una sub-tarea dejaba el run en
|
|
43
|
+
* `running` para siempre. Y `GoalEngine.tick` se niega a avanzar mientras
|
|
44
|
+
* haya una task en `running` → el goal quedaba atascado sin salida.
|
|
45
|
+
*
|
|
46
|
+
* Marca esos runs como `aborted` y emite `workflow_run_finished(ok:false)`,
|
|
47
|
+
* con lo que `onWorkflowFinished` libera la sub-tarea por el camino que ya
|
|
48
|
+
* existe (y que ahora además auto-continúa).
|
|
49
|
+
*
|
|
50
|
+
* Deliberadamente NO reanuda el run desde el journal: eso exige confiar en el
|
|
51
|
+
* hash de identidad y es otro item. Aquí solo hay que liberar el goal.
|
|
52
|
+
*
|
|
53
|
+
* ponytail: la propiedad del run se decide por antigüedad de `updatedAt`
|
|
54
|
+
* porque no hay leases de sesión. Si algún día dos procesos comparten
|
|
55
|
+
* `runsDir`, esto necesita un lease de verdad.
|
|
56
|
+
*/
|
|
57
|
+
async reconcile(options = {}) {
|
|
58
|
+
const staleAfterMs = options.staleAfterMs ?? 5 * 60 * 1000;
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
const reclaimed = [];
|
|
61
|
+
let runs;
|
|
62
|
+
try {
|
|
63
|
+
runs = await this.journal.list();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return reclaimed; // sin runsDir todavía
|
|
67
|
+
}
|
|
68
|
+
const error = "interrumpido por reinicio del proceso";
|
|
69
|
+
for (const entry of runs) {
|
|
70
|
+
if (entry.status !== "running" && entry.status !== "pending")
|
|
71
|
+
continue;
|
|
72
|
+
// Un run vivo de ESTE proceso tiene su AbortController registrado.
|
|
73
|
+
if (this.abortControllers.has(entry.runId))
|
|
74
|
+
continue;
|
|
75
|
+
const age = now - Date.parse(entry.updatedAt);
|
|
76
|
+
if (Number.isNaN(age) || age < staleAfterMs)
|
|
77
|
+
continue;
|
|
78
|
+
const state = await this.journal.load(entry.runId);
|
|
79
|
+
if (!state)
|
|
80
|
+
continue;
|
|
81
|
+
const aborted = {
|
|
82
|
+
...state,
|
|
83
|
+
status: "aborted",
|
|
84
|
+
error,
|
|
85
|
+
updatedAt: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
await this.journal.save(aborted);
|
|
88
|
+
this.onEvent?.("workflow_run_finished", {
|
|
89
|
+
runId: entry.runId,
|
|
90
|
+
status: "aborted",
|
|
91
|
+
agentCount: state.agentCount,
|
|
92
|
+
tokens: state.tokenUsage.total,
|
|
93
|
+
cost: state.tokenUsage.cost,
|
|
94
|
+
error,
|
|
95
|
+
});
|
|
96
|
+
reclaimed.push(entry.runId);
|
|
97
|
+
}
|
|
98
|
+
return reclaimed;
|
|
99
|
+
}
|
|
100
|
+
/** Ejecuta un workflow. Si await=true (default), resuelve al terminar. */
|
|
101
|
+
async execute(script, options = {}) {
|
|
102
|
+
const { meta, body: _body } = await import("./runtime.js").then((m) => m.parseWorkflowScript(script));
|
|
103
|
+
const runId = options.runId ?? `wf-${Date.now().toString(36)}`;
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
this.abortControllers.set(runId, controller);
|
|
106
|
+
const initialState = {
|
|
107
|
+
version: 1,
|
|
108
|
+
phases: [],
|
|
109
|
+
runId,
|
|
110
|
+
name: meta.name,
|
|
111
|
+
script,
|
|
112
|
+
args: options.args ?? {},
|
|
113
|
+
meta,
|
|
114
|
+
status: "running",
|
|
115
|
+
currentPhase: null,
|
|
116
|
+
sessionId: options.sessionId,
|
|
117
|
+
displayName: options.displayName,
|
|
118
|
+
journal: [],
|
|
119
|
+
agentCount: 0,
|
|
120
|
+
tokenUsage: { input: 0, output: 0, total: 0, cost: 0, cacheRead: 0, cacheWrite: 0 },
|
|
121
|
+
startedAt: new Date().toISOString(),
|
|
122
|
+
updatedAt: new Date().toISOString(),
|
|
123
|
+
};
|
|
124
|
+
await this.journal.save(initialState);
|
|
125
|
+
this.onEvent?.("workflow_run_started", {
|
|
126
|
+
runId,
|
|
127
|
+
name: meta.name,
|
|
128
|
+
phases: (meta.phases ?? []).map((p) => p.title),
|
|
129
|
+
});
|
|
130
|
+
let agentsDone = 0;
|
|
131
|
+
let agentsTotal = 0;
|
|
132
|
+
// Agente en curso (para marcar blocked cuando espera confirm).
|
|
133
|
+
let currentAgent = { id: "", label: "" };
|
|
134
|
+
// Estado vivo: se persiste en cada evento para que el panel del
|
|
135
|
+
// desktop vea fases y agentes en tiempo real (polling de /api/engine).
|
|
136
|
+
const live = { ...initialState };
|
|
137
|
+
const liveAgents = [];
|
|
138
|
+
// Marcar/desmarcar blocked del agente en curso (P4).
|
|
139
|
+
const setAgentBlocked = (blocked, reason = "") => {
|
|
140
|
+
if (!currentAgent.id)
|
|
141
|
+
return;
|
|
142
|
+
const rec = liveAgents.find((a) => a.id === currentAgent.id);
|
|
143
|
+
if (rec) {
|
|
144
|
+
rec.status = blocked ? "blocked" : "running";
|
|
145
|
+
rec.blockedReason = blocked ? reason : undefined;
|
|
146
|
+
}
|
|
147
|
+
void saveLive({ agents: [...liveAgents] });
|
|
148
|
+
};
|
|
149
|
+
// Saves serializados (cadena de promesas): preserva el orden y evita
|
|
150
|
+
// que un snapshot viejo pise uno nuevo.
|
|
151
|
+
let saveChain = Promise.resolve();
|
|
152
|
+
const saveLive = (patch) => {
|
|
153
|
+
Object.assign(live, patch, { updatedAt: new Date().toISOString() });
|
|
154
|
+
saveChain = saveChain
|
|
155
|
+
.then(async () => {
|
|
156
|
+
try {
|
|
157
|
+
await this.journal.save(live);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// best-effort: nunca rompe el run por un save
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
.catch(() => { });
|
|
164
|
+
return saveChain;
|
|
165
|
+
};
|
|
166
|
+
const runPromise = runWorkflow(script, {
|
|
167
|
+
cwd: process.cwd(),
|
|
168
|
+
agentDir: this.agentDirFromRunsDir(),
|
|
169
|
+
createSession: this.createSession,
|
|
170
|
+
limits: this.limits,
|
|
171
|
+
signal: controller.signal,
|
|
172
|
+
confirm: async (prompt, checkpointOptions) => {
|
|
173
|
+
// P4: mientras hay un checkpoint pendiente de input humano,
|
|
174
|
+
// el agente en curso está BLOCKED (no procesando).
|
|
175
|
+
const wasBlocked = liveAgents.some((a) => a.status === "blocked");
|
|
176
|
+
if (this.confirm) {
|
|
177
|
+
setAgentBlocked(true, String(prompt).slice(0, 120));
|
|
178
|
+
try {
|
|
179
|
+
return await this.confirm(prompt, checkpointOptions);
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
if (wasBlocked)
|
|
183
|
+
setAgentBlocked(true);
|
|
184
|
+
else
|
|
185
|
+
setAgentBlocked(false);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Sin confirm del host: headless → abort (checkpoint no resuelto).
|
|
189
|
+
if (checkpointOptions?.headless === "abort") {
|
|
190
|
+
throw new WorkflowError("WORKFLOW_ABORTED", `checkpoint "${prompt}" needs human input but none is available`, { recoverable: false });
|
|
191
|
+
}
|
|
192
|
+
return checkpointOptions?.default ?? true;
|
|
193
|
+
},
|
|
194
|
+
args: options.args ?? {},
|
|
195
|
+
runId,
|
|
196
|
+
resumeJournal: new Map(),
|
|
197
|
+
onPhase: (phase) => {
|
|
198
|
+
live.currentPhase = phase;
|
|
199
|
+
if (!live.phases.includes(phase))
|
|
200
|
+
live.phases.push(phase);
|
|
201
|
+
void saveLive({});
|
|
202
|
+
this.onEvent?.("workflow_phase", { runId, phase });
|
|
203
|
+
},
|
|
204
|
+
onAgentStart: (_index, label, phase) => {
|
|
205
|
+
agentsTotal++;
|
|
206
|
+
currentAgent = { id: `${runId}:${_index}`, label };
|
|
207
|
+
liveAgents.push({ id: `${runId}:${_index}`, label, phase, status: "running" });
|
|
208
|
+
void saveLive({ agents: [...liveAgents] });
|
|
209
|
+
this.onEvent?.("workflow_agent", { runId, agent: { id: `${runId}:${_index}`, label, phase } });
|
|
210
|
+
},
|
|
211
|
+
onAgentEnd: (_index, _label, _result) => {
|
|
212
|
+
agentsDone++;
|
|
213
|
+
currentAgent = { id: "", label: "" };
|
|
214
|
+
const rec = liveAgents.find((a) => a.id === `${runId}:${_index}`);
|
|
215
|
+
if (rec)
|
|
216
|
+
rec.status = "done";
|
|
217
|
+
void saveLive({ agents: [...liveAgents] });
|
|
218
|
+
this.onEvent?.("workflow_run_update", {
|
|
219
|
+
runId,
|
|
220
|
+
status: "running",
|
|
221
|
+
agentsDone,
|
|
222
|
+
agentsTotal,
|
|
223
|
+
tokens: 0,
|
|
224
|
+
cost: 0,
|
|
225
|
+
});
|
|
226
|
+
},
|
|
227
|
+
onTokenUsage: (usage) => {
|
|
228
|
+
const u = usage;
|
|
229
|
+
void saveLive({
|
|
230
|
+
tokenUsage: {
|
|
231
|
+
...(live.tokenUsage ?? { input: 0, output: 0, total: 0, cost: 0, cacheRead: 0, cacheWrite: 0 }),
|
|
232
|
+
total: u.total ?? live.tokenUsage.total,
|
|
233
|
+
cost: u.cost ?? live.tokenUsage.cost,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
this.onEvent?.("workflow_run_update", {
|
|
237
|
+
runId,
|
|
238
|
+
status: "running",
|
|
239
|
+
agentsDone,
|
|
240
|
+
agentsTotal,
|
|
241
|
+
tokens: u.total ?? 0,
|
|
242
|
+
cost: u.cost ?? 0,
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
const settle = async () => {
|
|
247
|
+
try {
|
|
248
|
+
const result = await runPromise;
|
|
249
|
+
// Drenar saves en vivo ANTES del estado final: un saveLive tardío
|
|
250
|
+
// no debe pisar el estado completed/failed.
|
|
251
|
+
await saveChain.catch(() => { });
|
|
252
|
+
const finalState = {
|
|
253
|
+
...initialState,
|
|
254
|
+
status: "completed",
|
|
255
|
+
phases: result.phases,
|
|
256
|
+
currentPhase: null,
|
|
257
|
+
agents: liveAgents.map((a) => ({ ...a, status: "done" })),
|
|
258
|
+
agentCount: result.agentCount,
|
|
259
|
+
tokenUsage: {
|
|
260
|
+
input: result.tokenUsage.input,
|
|
261
|
+
output: result.tokenUsage.output,
|
|
262
|
+
total: result.tokenUsage.total,
|
|
263
|
+
cost: result.tokenUsage.cost,
|
|
264
|
+
cacheRead: result.tokenUsage.cacheRead ?? 0,
|
|
265
|
+
cacheWrite: result.tokenUsage.cacheWrite ?? 0,
|
|
266
|
+
},
|
|
267
|
+
updatedAt: new Date().toISOString(),
|
|
268
|
+
};
|
|
269
|
+
await this.journal.save(finalState);
|
|
270
|
+
this.onEvent?.("workflow_run_finished", {
|
|
271
|
+
runId,
|
|
272
|
+
status: "completed",
|
|
273
|
+
agentCount: result.agentCount,
|
|
274
|
+
tokens: result.tokenUsage.total,
|
|
275
|
+
cost: result.tokenUsage.cost,
|
|
276
|
+
});
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
const status = error?.code === "WORKFLOW_ABORTED" ? "aborted" : "failed";
|
|
281
|
+
const finalState = {
|
|
282
|
+
...initialState,
|
|
283
|
+
status,
|
|
284
|
+
error: error instanceof Error ? error.message : String(error),
|
|
285
|
+
updatedAt: new Date().toISOString(),
|
|
286
|
+
};
|
|
287
|
+
await this.journal.save(finalState);
|
|
288
|
+
this.onEvent?.("workflow_run_finished", {
|
|
289
|
+
runId,
|
|
290
|
+
status,
|
|
291
|
+
agentCount: agentsDone,
|
|
292
|
+
tokens: 0,
|
|
293
|
+
cost: 0,
|
|
294
|
+
error: finalState.error,
|
|
295
|
+
});
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
this.abortControllers.delete(runId);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
if (options.await === false) {
|
|
303
|
+
// El error ya se persiste en el estado; capturar evita unhandled.
|
|
304
|
+
void settle().catch(() => { });
|
|
305
|
+
return { runId };
|
|
306
|
+
}
|
|
307
|
+
const result = await settle();
|
|
308
|
+
return { runId, result: result.result };
|
|
309
|
+
}
|
|
310
|
+
abort(runId) {
|
|
311
|
+
const controller = this.abortControllers.get(runId);
|
|
312
|
+
if (!controller)
|
|
313
|
+
return false;
|
|
314
|
+
controller.abort();
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
agentDirFromRunsDir() {
|
|
318
|
+
// El agentDir es el padre de workflows/runs → workflows → agent.
|
|
319
|
+
// OJO: preservar el / inicial (absoluto) — split+filter(Boolean) lo
|
|
320
|
+
// elimina y producía una ruta RELATIVA → auth.json irresoluble.
|
|
321
|
+
const isAbsolute = this.runsDir.startsWith("/");
|
|
322
|
+
const parts = this.runsDir.split("/").filter(Boolean);
|
|
323
|
+
const parent = parts.slice(0, Math.max(0, parts.length - 2)).join("/");
|
|
324
|
+
return isAbsolute ? `/${parent}` : parent;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/workflows/manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WorkflowManager — ejecución de workflows en background con estado durable.\n *\n * - execute(script, args): valida, persiste el estado (RunJournal), corre el\n * runtime, emite eventos (started/phase/agent/update/finished).\n * - pause/resume/abort: aborta con señal; el estado queda para reanudar\n * (resume con el mismo runId reutiliza el journal).\n * - El disco es la fuente de verdad (snapshot en cada evento).\n */\n\nimport { type PersistedRunState, RunJournal } from \"./journal.ts\";\nimport { runWorkflow, type WorkflowRuntimeOptions } from \"./runtime.ts\";\nimport { WorkflowError } from \"./types.ts\";\n\nexport interface WorkflowEventMap {\n\tworkflow_run_started: { runId: string; name: string; phases: string[] };\n\tworkflow_phase: { runId: string; phase: string };\n\tworkflow_agent: { runId: string; agent: { id: string; label: string; phase?: string } };\n\tworkflow_run_update: {\n\t\trunId: string;\n\t\tstatus: PersistedRunState[\"status\"];\n\t\tagentsDone: number;\n\t\tagentsTotal: number;\n\t\ttokens: number;\n\t\tcost: number;\n\t};\n\tworkflow_run_finished: {\n\t\trunId: string;\n\t\tstatus: PersistedRunState[\"status\"];\n\t\tresultPath?: string;\n\t\tagentCount: number;\n\t\ttokens: number;\n\t\tcost: number;\n\t\terror?: string;\n\t};\n}\n\nexport interface WorkflowManagerOptions {\n\trunsDir: string;\n\tcreateSession: WorkflowRuntimeOptions[\"createSession\"];\n\tlimits?: WorkflowRuntimeOptions[\"limits\"];\n\tonEvent?: <K extends keyof WorkflowEventMap>(type: K, payload: WorkflowEventMap[K]) => void;\n\tconfirm?: WorkflowRuntimeOptions[\"confirm\"];\n}\n\nexport interface ExecuteWorkflowOptions {\n\targs?: Record<string, unknown>;\n\trunId?: string;\n\t/** Sesión que lanza el run (filtro por conversación en el panel). */\n\tsessionId?: string;\n\t/** Nombre para mostrar en el panel (título de sub-tarea del goal, etc.). */\n\tdisplayName?: string;\n\t/** Esperar a que termine (default: true). */\n\tawait?: boolean;\n}\n\nexport class WorkflowManager {\n\tprivate journal: RunJournal;\n\tprivate runsDir: string;\n\tprivate createSession: WorkflowRuntimeOptions[\"createSession\"];\n\tprivate limits: WorkflowRuntimeOptions[\"limits\"];\n\tprivate onEvent?: WorkflowManagerOptions[\"onEvent\"];\n\tprivate confirm?: WorkflowRuntimeOptions[\"confirm\"];\n\tprivate abortControllers = new Map<string, AbortController>();\n\n\tconstructor(options: WorkflowManagerOptions) {\n\t\tthis.journal = new RunJournal({ runsDir: options.runsDir });\n\t\tthis.runsDir = options.runsDir;\n\t\tthis.createSession = options.createSession;\n\t\tthis.limits = options.limits;\n\t\tthis.onEvent = options.onEvent;\n\t\tthis.confirm = options.confirm;\n\t}\n\n\tget runJournal(): RunJournal {\n\t\treturn this.journal;\n\t}\n\n\tasync list(): Promise<\n\t\tArray<{ runId: string; name: string; status: PersistedRunState[\"status\"]; updatedAt: string }>\n\t> {\n\t\treturn this.journal.list();\n\t}\n\n\tasync state(runId: string): Promise<PersistedRunState | null> {\n\t\treturn this.journal.load(runId);\n\t}\n\n\t/**\n\t * Desatasca los runs que murieron con su proceso.\n\t *\n\t * `execute` siempre persiste `status: \"running\"` y nadie escaneaba `runsDir`\n\t * al arrancar, así que un Ctrl-C durante una sub-tarea dejaba el run en\n\t * `running` para siempre. Y `GoalEngine.tick` se niega a avanzar mientras\n\t * haya una task en `running` → el goal quedaba atascado sin salida.\n\t *\n\t * Marca esos runs como `aborted` y emite `workflow_run_finished(ok:false)`,\n\t * con lo que `onWorkflowFinished` libera la sub-tarea por el camino que ya\n\t * existe (y que ahora además auto-continúa).\n\t *\n\t * Deliberadamente NO reanuda el run desde el journal: eso exige confiar en el\n\t * hash de identidad y es otro item. Aquí solo hay que liberar el goal.\n\t *\n\t * ponytail: la propiedad del run se decide por antigüedad de `updatedAt`\n\t * porque no hay leases de sesión. Si algún día dos procesos comparten\n\t * `runsDir`, esto necesita un lease de verdad.\n\t */\n\tasync reconcile(options: { staleAfterMs?: number } = {}): Promise<string[]> {\n\t\tconst staleAfterMs = options.staleAfterMs ?? 5 * 60 * 1000;\n\t\tconst now = Date.now();\n\t\tconst reclaimed: string[] = [];\n\t\tlet runs: Awaited<ReturnType<RunJournal[\"list\"]>>;\n\t\ttry {\n\t\t\truns = await this.journal.list();\n\t\t} catch {\n\t\t\treturn reclaimed; // sin runsDir todavía\n\t\t}\n\t\tconst error = \"interrumpido por reinicio del proceso\";\n\t\tfor (const entry of runs) {\n\t\t\tif (entry.status !== \"running\" && entry.status !== \"pending\") continue;\n\t\t\t// Un run vivo de ESTE proceso tiene su AbortController registrado.\n\t\t\tif (this.abortControllers.has(entry.runId)) continue;\n\t\t\tconst age = now - Date.parse(entry.updatedAt);\n\t\t\tif (Number.isNaN(age) || age < staleAfterMs) continue;\n\t\t\tconst state = await this.journal.load(entry.runId);\n\t\t\tif (!state) continue;\n\t\t\tconst aborted: PersistedRunState = {\n\t\t\t\t...state,\n\t\t\t\tstatus: \"aborted\",\n\t\t\t\terror,\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t};\n\t\t\tawait this.journal.save(aborted);\n\t\t\tthis.onEvent?.(\"workflow_run_finished\", {\n\t\t\t\trunId: entry.runId,\n\t\t\t\tstatus: \"aborted\",\n\t\t\t\tagentCount: state.agentCount,\n\t\t\t\ttokens: state.tokenUsage.total,\n\t\t\t\tcost: state.tokenUsage.cost,\n\t\t\t\terror,\n\t\t\t});\n\t\t\treclaimed.push(entry.runId);\n\t\t}\n\t\treturn reclaimed;\n\t}\n\n\t/** Ejecuta un workflow. Si await=true (default), resuelve al terminar. */\n\tasync execute(script: string, options: ExecuteWorkflowOptions = {}): Promise<{ runId: string; result?: unknown }> {\n\t\tconst { meta, body: _body } = await import(\"./runtime.ts\").then((m) => m.parseWorkflowScript(script));\n\t\tconst runId = options.runId ?? `wf-${Date.now().toString(36)}`;\n\t\tconst controller = new AbortController();\n\t\tthis.abortControllers.set(runId, controller);\n\n\t\tconst initialState: PersistedRunState = {\n\t\t\tversion: 1,\n\t\t\tphases: [],\n\t\t\trunId,\n\t\t\tname: meta.name,\n\t\t\tscript,\n\t\t\targs: options.args ?? {},\n\t\t\tmeta,\n\t\t\tstatus: \"running\",\n\t\t\tcurrentPhase: null,\n\t\t\tsessionId: options.sessionId,\n\t\t\tdisplayName: options.displayName,\n\t\t\tjournal: [],\n\t\t\tagentCount: 0,\n\t\t\ttokenUsage: { input: 0, output: 0, total: 0, cost: 0, cacheRead: 0, cacheWrite: 0 },\n\t\t\tstartedAt: new Date().toISOString(),\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t};\n\t\tawait this.journal.save(initialState);\n\t\tthis.onEvent?.(\"workflow_run_started\", {\n\t\t\trunId,\n\t\t\tname: meta.name,\n\t\t\tphases: (meta.phases ?? []).map((p) => p.title),\n\t\t});\n\n\t\tlet agentsDone = 0;\n\t\tlet agentsTotal = 0;\n\t\t// Agente en curso (para marcar blocked cuando espera confirm).\n\t\tlet currentAgent = { id: \"\", label: \"\" };\n\t\t// Estado vivo: se persiste en cada evento para que el panel del\n\t\t// desktop vea fases y agentes en tiempo real (polling de /api/engine).\n\t\tconst live: PersistedRunState = { ...initialState };\n\t\tconst liveAgents: NonNullable<PersistedRunState[\"agents\"]> = [];\n\t\t// Marcar/desmarcar blocked del agente en curso (P4).\n\t\tconst setAgentBlocked = (blocked: boolean, reason = \"\") => {\n\t\t\tif (!currentAgent.id) return;\n\t\t\tconst rec = liveAgents.find((a) => a.id === currentAgent.id);\n\t\t\tif (rec) {\n\t\t\t\trec.status = blocked ? \"blocked\" : \"running\";\n\t\t\t\trec.blockedReason = blocked ? reason : undefined;\n\t\t\t}\n\t\t\tvoid saveLive({ agents: [...liveAgents] });\n\t\t};\n\t\t// Saves serializados (cadena de promesas): preserva el orden y evita\n\t\t// que un snapshot viejo pise uno nuevo.\n\t\tlet saveChain: Promise<void> = Promise.resolve();\n\t\tconst saveLive = (patch: Partial<PersistedRunState>) => {\n\t\t\tObject.assign(live, patch, { updatedAt: new Date().toISOString() });\n\t\t\tsaveChain = saveChain\n\t\t\t\t.then(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.journal.save(live);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// best-effort: nunca rompe el run por un save\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t\treturn saveChain;\n\t\t};\n\n\t\tconst runPromise = runWorkflow(script, {\n\t\t\tcwd: process.cwd(),\n\t\t\tagentDir: this.agentDirFromRunsDir(),\n\t\t\tcreateSession: this.createSession,\n\t\t\tlimits: this.limits,\n\t\t\tsignal: controller.signal,\n\t\t\tconfirm: async (prompt, checkpointOptions) => {\n\t\t\t\t// P4: mientras hay un checkpoint pendiente de input humano,\n\t\t\t\t// el agente en curso está BLOCKED (no procesando).\n\t\t\t\tconst wasBlocked = liveAgents.some((a) => a.status === \"blocked\");\n\t\t\t\tif (this.confirm) {\n\t\t\t\t\tsetAgentBlocked(true, String(prompt).slice(0, 120));\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn await this.confirm(prompt, checkpointOptions);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (wasBlocked) setAgentBlocked(true);\n\t\t\t\t\t\telse setAgentBlocked(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Sin confirm del host: headless → abort (checkpoint no resuelto).\n\t\t\t\tif ((checkpointOptions as { headless?: string } | undefined)?.headless === \"abort\") {\n\t\t\t\t\tthrow new WorkflowError(\n\t\t\t\t\t\t\"WORKFLOW_ABORTED\",\n\t\t\t\t\t\t`checkpoint \"${prompt}\" needs human input but none is available`,\n\t\t\t\t\t\t{ recoverable: false },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn (checkpointOptions as { default?: unknown } | undefined)?.default ?? true;\n\t\t\t},\n\t\t\targs: options.args ?? {},\n\t\t\trunId,\n\t\t\tresumeJournal: new Map(),\n\t\t\tonPhase: (phase) => {\n\t\t\t\tlive.currentPhase = phase;\n\t\t\t\tif (!live.phases.includes(phase)) live.phases.push(phase);\n\t\t\t\tvoid saveLive({});\n\t\t\t\tthis.onEvent?.(\"workflow_phase\", { runId, phase });\n\t\t\t},\n\t\t\tonAgentStart: (_index, label, phase) => {\n\t\t\t\tagentsTotal++;\n\t\t\t\tcurrentAgent = { id: `${runId}:${_index}`, label };\n\t\t\t\tliveAgents.push({ id: `${runId}:${_index}`, label, phase, status: \"running\" });\n\t\t\t\tvoid saveLive({ agents: [...liveAgents] });\n\t\t\t\tthis.onEvent?.(\"workflow_agent\", { runId, agent: { id: `${runId}:${_index}`, label, phase } });\n\t\t\t},\n\t\t\tonAgentEnd: (_index, _label, _result) => {\n\t\t\t\tagentsDone++;\n\t\t\t\tcurrentAgent = { id: \"\", label: \"\" };\n\t\t\t\tconst rec = liveAgents.find((a) => a.id === `${runId}:${_index}`);\n\t\t\t\tif (rec) rec.status = \"done\";\n\t\t\t\tvoid saveLive({ agents: [...liveAgents] });\n\t\t\t\tthis.onEvent?.(\"workflow_run_update\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\tagentsDone,\n\t\t\t\t\tagentsTotal,\n\t\t\t\t\ttokens: 0,\n\t\t\t\t\tcost: 0,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonTokenUsage: (usage) => {\n\t\t\t\tconst u = usage as { total?: number; cost?: number };\n\t\t\t\tvoid saveLive({\n\t\t\t\t\ttokenUsage: {\n\t\t\t\t\t\t...(live.tokenUsage ?? { input: 0, output: 0, total: 0, cost: 0, cacheRead: 0, cacheWrite: 0 }),\n\t\t\t\t\t\ttotal: u.total ?? live.tokenUsage.total,\n\t\t\t\t\t\tcost: u.cost ?? live.tokenUsage.cost,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tthis.onEvent?.(\"workflow_run_update\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\tagentsDone,\n\t\t\t\t\tagentsTotal,\n\t\t\t\t\ttokens: u.total ?? 0,\n\t\t\t\t\tcost: u.cost ?? 0,\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\n\t\tconst settle = async () => {\n\t\t\ttry {\n\t\t\t\tconst result = await runPromise;\n\t\t\t\t// Drenar saves en vivo ANTES del estado final: un saveLive tardío\n\t\t\t\t// no debe pisar el estado completed/failed.\n\t\t\t\tawait saveChain.catch(() => {});\n\t\t\t\tconst finalState: PersistedRunState = {\n\t\t\t\t\t...initialState,\n\t\t\t\t\tstatus: \"completed\",\n\t\t\t\t\tphases: result.phases,\n\t\t\t\t\tcurrentPhase: null,\n\t\t\t\t\tagents: liveAgents.map((a) => ({ ...a, status: \"done\" as const })),\n\t\t\t\t\tagentCount: result.agentCount,\n\t\t\t\t\ttokenUsage: {\n\t\t\t\t\t\tinput: result.tokenUsage.input,\n\t\t\t\t\t\toutput: result.tokenUsage.output,\n\t\t\t\t\t\ttotal: result.tokenUsage.total,\n\t\t\t\t\t\tcost: result.tokenUsage.cost,\n\t\t\t\t\t\tcacheRead: result.tokenUsage.cacheRead ?? 0,\n\t\t\t\t\t\tcacheWrite: result.tokenUsage.cacheWrite ?? 0,\n\t\t\t\t\t},\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t};\n\t\t\t\tawait this.journal.save(finalState);\n\t\t\t\tthis.onEvent?.(\"workflow_run_finished\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus: \"completed\",\n\t\t\t\t\tagentCount: result.agentCount,\n\t\t\t\t\ttokens: result.tokenUsage.total,\n\t\t\t\t\tcost: result.tokenUsage.cost,\n\t\t\t\t});\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tconst status: PersistedRunState[\"status\"] =\n\t\t\t\t\t(error as WorkflowError)?.code === \"WORKFLOW_ABORTED\" ? \"aborted\" : \"failed\";\n\t\t\t\tconst finalState = {\n\t\t\t\t\t...initialState,\n\t\t\t\t\tstatus,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t};\n\t\t\t\tawait this.journal.save(finalState);\n\t\t\t\tthis.onEvent?.(\"workflow_run_finished\", {\n\t\t\t\t\trunId,\n\t\t\t\t\tstatus,\n\t\t\t\t\tagentCount: agentsDone,\n\t\t\t\t\ttokens: 0,\n\t\t\t\t\tcost: 0,\n\t\t\t\t\terror: finalState.error,\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tthis.abortControllers.delete(runId);\n\t\t\t}\n\t\t};\n\n\t\tif (options.await === false) {\n\t\t\t// El error ya se persiste en el estado; capturar evita unhandled.\n\t\t\tvoid settle().catch(() => {});\n\t\t\treturn { runId };\n\t\t}\n\t\tconst result = await settle();\n\t\treturn { runId, result: (result as { result?: unknown }).result };\n\t}\n\n\tabort(runId: string): boolean {\n\t\tconst controller = this.abortControllers.get(runId);\n\t\tif (!controller) return false;\n\t\tcontroller.abort();\n\t\treturn true;\n\t}\n\n\tprivate agentDirFromRunsDir(): string {\n\t\t// El agentDir es el padre de workflows/runs → workflows → agent.\n\t\t// OJO: preservar el / inicial (absoluto) — split+filter(Boolean) lo\n\t\t// elimina y producía una ruta RELATIVA → auth.json irresoluble.\n\t\tconst isAbsolute = this.runsDir.startsWith(\"/\");\n\t\tconst parts = this.runsDir.split(\"/\").filter(Boolean);\n\t\tconst parent = parts.slice(0, Math.max(0, parts.length - 2)).join(\"/\");\n\t\treturn isAbsolute ? `/${parent}` : parent;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolución de scripts de workflow POR NOMBRE (§16.1).
|
|
3
|
+
*
|
|
4
|
+
* Orden de resolución:
|
|
5
|
+
* 1. Builtin versionado con el SDK (deep_research, code_review, …).
|
|
6
|
+
* 2. Script guardado por el usuario: <agentDir>/workflows/<name>.js
|
|
7
|
+
* (cualquier JS con la API del runtime: agent/parallel/pipeline/phase/
|
|
8
|
+
* store/checkpoint/args/cwd/runId/log — scripts DINÁMICOS completos).
|
|
9
|
+
*
|
|
10
|
+
* Un solo punto de verdad para CLI (/workflow), RPC (desktop), la tool
|
|
11
|
+
* `workflow` del agente y el SDK.
|
|
12
|
+
*/
|
|
13
|
+
export interface ResolveWorkflowResult {
|
|
14
|
+
/** Fuente del script: "builtin" | "user-script". */
|
|
15
|
+
source: "builtin" | "user-script";
|
|
16
|
+
/** Nombre canónico (el del archivo o el builtin). */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Contenido del script listo para runWorkflow. */
|
|
19
|
+
script: string;
|
|
20
|
+
/** Ruta del archivo si es user-script. */
|
|
21
|
+
filePath?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Resuelve un builtin por nombre; null si no lo es. */
|
|
24
|
+
export declare function resolveBuiltinWorkflowScript(name: string): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Resuelve un workflow por nombre: builtin primero, luego script guardado en
|
|
27
|
+
* <agentDir>/workflows/<name>.js (extensiones .js/.mjs/.cjs aceptadas).
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveWorkflowScriptByName(agentDir: string, name: string): ResolveWorkflowResult | null;
|
|
30
|
+
/** Lista scripts del usuario guardados en <agentDir>/workflows/*.js. */
|
|
31
|
+
export declare function listUserWorkflowScripts(agentDir: string): Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
filePath: string;
|
|
34
|
+
}>;
|
|
35
|
+
//# sourceMappingURL=resolve-script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-script.d.ts","sourceRoot":"","sources":["../../../src/core/workflows/resolve-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,MAAM,WAAW,qBAAqB;IACrC,oDAAoD;IACpD,MAAM,EAAE,SAAS,GAAG,aAAa,CAAC;IAClC,sDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wDAAwD;AACxD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAexE;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAexG;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CASnG","sourcesContent":["/**\n * Resolución de scripts de workflow POR NOMBRE (§16.1).\n *\n * Orden de resolución:\n * 1. Builtin versionado con el SDK (deep_research, code_review, …).\n * 2. Script guardado por el usuario: <agentDir>/workflows/<name>.js\n * (cualquier JS con la API del runtime: agent/parallel/pipeline/phase/\n * store/checkpoint/args/cwd/runId/log — scripts DINÁMICOS completos).\n *\n * Un solo punto de verdad para CLI (/workflow), RPC (desktop), la tool\n * `workflow` del agente y el SDK.\n */\n\nimport { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tgenerateCodebaseAuditScript,\n\tgenerateCodeReviewScript,\n\tgenerateDeepResearchScript,\n\tgenerateMultiPerspectiveScript,\n\tgenerateProductBuildScript,\n} from \"./builtins.ts\";\n\nexport interface ResolveWorkflowResult {\n\t/** Fuente del script: \"builtin\" | \"user-script\". */\n\tsource: \"builtin\" | \"user-script\";\n\t/** Nombre canónico (el del archivo o el builtin). */\n\tname: string;\n\t/** Contenido del script listo para runWorkflow. */\n\tscript: string;\n\t/** Ruta del archivo si es user-script. */\n\tfilePath?: string;\n}\n\n/** Resuelve un builtin por nombre; null si no lo es. */\nexport function resolveBuiltinWorkflowScript(name: string): string | null {\n\tswitch (name.replace(/-/g, \"_\").toLowerCase()) {\n\t\tcase \"deep_research\":\n\t\t\treturn generateDeepResearchScript({});\n\t\tcase \"code_review\":\n\t\t\treturn generateCodeReviewScript({});\n\t\tcase \"product_build\":\n\t\t\treturn generateProductBuildScript();\n\t\tcase \"codebase_audit\":\n\t\t\treturn generateCodebaseAuditScript({});\n\t\tcase \"multi_perspective\":\n\t\t\treturn generateMultiPerspectiveScript({});\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\n/**\n * Resuelve un workflow por nombre: builtin primero, luego script guardado en\n * <agentDir>/workflows/<name>.js (extensiones .js/.mjs/.cjs aceptadas).\n */\nexport function resolveWorkflowScriptByName(agentDir: string, name: string): ResolveWorkflowResult | null {\n\tconst builtin = resolveBuiltinWorkflowScript(name);\n\tif (builtin) return { source: \"builtin\", name: name.replace(/-/g, \"_\").toLowerCase(), script: builtin };\n\n\tconst base = name.replace(/\\.(js|mjs|cjs)$/i, \"\");\n\tfor (const ext of [\"js\", \"mjs\", \"cjs\"]) {\n\t\tconst filePath = join(agentDir, \"workflows\", `${base}.${ext}`);\n\t\ttry {\n\t\t\tconst script = readFileSync(filePath, \"utf-8\");\n\t\t\treturn { source: \"user-script\", name: base, script, filePath };\n\t\t} catch {\n\t\t\t// siguiente extensión\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Lista scripts del usuario guardados en <agentDir>/workflows/*.js. */\nexport function listUserWorkflowScripts(agentDir: string): Array<{ name: string; filePath: string }> {\n\ttry {\n\t\tconst dir = join(agentDir, \"workflows\");\n\t\treturn readdirSync(dir)\n\t\t\t.filter((f) => /\\.(js|mjs|cjs)$/.test(f) && !f.endsWith(\".json\"))\n\t\t\t.map((f) => ({ name: f.replace(/\\.(js|mjs|cjs)$/i, \"\"), filePath: join(dir, f) }));\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolución de scripts de workflow POR NOMBRE (§16.1).
|
|
3
|
+
*
|
|
4
|
+
* Orden de resolución:
|
|
5
|
+
* 1. Builtin versionado con el SDK (deep_research, code_review, …).
|
|
6
|
+
* 2. Script guardado por el usuario: <agentDir>/workflows/<name>.js
|
|
7
|
+
* (cualquier JS con la API del runtime: agent/parallel/pipeline/phase/
|
|
8
|
+
* store/checkpoint/args/cwd/runId/log — scripts DINÁMICOS completos).
|
|
9
|
+
*
|
|
10
|
+
* Un solo punto de verdad para CLI (/workflow), RPC (desktop), la tool
|
|
11
|
+
* `workflow` del agente y el SDK.
|
|
12
|
+
*/
|
|
13
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { generateCodebaseAuditScript, generateCodeReviewScript, generateDeepResearchScript, generateMultiPerspectiveScript, generateProductBuildScript, } from "./builtins.js";
|
|
16
|
+
/** Resuelve un builtin por nombre; null si no lo es. */
|
|
17
|
+
export function resolveBuiltinWorkflowScript(name) {
|
|
18
|
+
switch (name.replace(/-/g, "_").toLowerCase()) {
|
|
19
|
+
case "deep_research":
|
|
20
|
+
return generateDeepResearchScript({});
|
|
21
|
+
case "code_review":
|
|
22
|
+
return generateCodeReviewScript({});
|
|
23
|
+
case "product_build":
|
|
24
|
+
return generateProductBuildScript();
|
|
25
|
+
case "codebase_audit":
|
|
26
|
+
return generateCodebaseAuditScript({});
|
|
27
|
+
case "multi_perspective":
|
|
28
|
+
return generateMultiPerspectiveScript({});
|
|
29
|
+
default:
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resuelve un workflow por nombre: builtin primero, luego script guardado en
|
|
35
|
+
* <agentDir>/workflows/<name>.js (extensiones .js/.mjs/.cjs aceptadas).
|
|
36
|
+
*/
|
|
37
|
+
export function resolveWorkflowScriptByName(agentDir, name) {
|
|
38
|
+
const builtin = resolveBuiltinWorkflowScript(name);
|
|
39
|
+
if (builtin)
|
|
40
|
+
return { source: "builtin", name: name.replace(/-/g, "_").toLowerCase(), script: builtin };
|
|
41
|
+
const base = name.replace(/\.(js|mjs|cjs)$/i, "");
|
|
42
|
+
for (const ext of ["js", "mjs", "cjs"]) {
|
|
43
|
+
const filePath = join(agentDir, "workflows", `${base}.${ext}`);
|
|
44
|
+
try {
|
|
45
|
+
const script = readFileSync(filePath, "utf-8");
|
|
46
|
+
return { source: "user-script", name: base, script, filePath };
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// siguiente extensión
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
/** Lista scripts del usuario guardados en <agentDir>/workflows/*.js. */
|
|
55
|
+
export function listUserWorkflowScripts(agentDir) {
|
|
56
|
+
try {
|
|
57
|
+
const dir = join(agentDir, "workflows");
|
|
58
|
+
return readdirSync(dir)
|
|
59
|
+
.filter((f) => /\.(js|mjs|cjs)$/.test(f) && !f.endsWith(".json"))
|
|
60
|
+
.map((f) => ({ name: f.replace(/\.(js|mjs|cjs)$/i, ""), filePath: join(dir, f) }));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=resolve-script.js.map
|