groundswell 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +26 -9
- package/dist/cache/cache-key.d.ts +86 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +204 -0
- package/dist/cache/cache-key.js.map +1 -0
- package/dist/cache/cache.d.ts +104 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +179 -0
- package/dist/cache/cache.js.map +1 -0
- package/{src/cache/index.ts → dist/cache/index.d.ts} +1 -1
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/core/agent.d.ts +203 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +833 -0
- package/dist/core/agent.js.map +1 -0
- package/{src/core/context.ts → dist/core/context.d.ts} +16 -67
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +80 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/event-tree.d.ts +72 -0
- package/dist/core/event-tree.d.ts.map +1 -0
- package/dist/core/event-tree.js +211 -0
- package/dist/core/event-tree.js.map +1 -0
- package/{src/core/factory.ts → dist/core/factory.d.ts} +6 -27
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +110 -0
- package/dist/core/factory.js.map +1 -0
- package/{src/core/index.ts → dist/core/index.d.ts} +2 -10
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +9 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +50 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +91 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mcp-handler.d.ts +127 -0
- package/dist/core/mcp-handler.d.ts.map +1 -0
- package/dist/core/mcp-handler.js +323 -0
- package/dist/core/mcp-handler.js.map +1 -0
- package/dist/core/prompt.d.ts +80 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +120 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/workflow-context.d.ts +61 -0
- package/dist/core/workflow-context.d.ts.map +1 -0
- package/dist/core/workflow-context.js +358 -0
- package/dist/core/workflow-context.js.map +1 -0
- package/dist/core/workflow.d.ts +543 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +986 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/debugger/event-replayer.d.ts +422 -0
- package/dist/debugger/event-replayer.d.ts.map +1 -0
- package/dist/debugger/event-replayer.js +639 -0
- package/dist/debugger/event-replayer.js.map +1 -0
- package/dist/debugger/index.d.ts +2 -0
- package/dist/debugger/index.d.ts.map +1 -0
- package/{src/debugger/index.ts → dist/debugger/index.js} +1 -0
- package/dist/debugger/index.js.map +1 -0
- package/dist/debugger/tree-debugger.d.ts +240 -0
- package/dist/debugger/tree-debugger.d.ts.map +1 -0
- package/dist/debugger/tree-debugger.js +620 -0
- package/dist/debugger/tree-debugger.js.map +1 -0
- package/dist/decorators/index.d.ts +4 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/{src/decorators/index.ts → dist/decorators/index.js} +1 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/observed-state.d.ts +32 -0
- package/dist/decorators/observed-state.d.ts.map +1 -0
- package/dist/decorators/observed-state.js +79 -0
- package/dist/decorators/observed-state.js.map +1 -0
- package/dist/decorators/step.d.ts +15 -0
- package/dist/decorators/step.d.ts.map +1 -0
- package/dist/decorators/step.js +192 -0
- package/dist/decorators/step.js.map +1 -0
- package/dist/decorators/task.d.ts +50 -0
- package/dist/decorators/task.d.ts.map +1 -0
- package/dist/decorators/task.js +118 -0
- package/dist/decorators/task.js.map +1 -0
- package/dist/examples/index.d.ts +3 -0
- package/dist/examples/index.d.ts.map +1 -0
- package/{src/examples/index.ts → dist/examples/index.js} +1 -0
- package/dist/examples/index.js.map +1 -0
- package/dist/examples/tdd-orchestrator.d.ts +15 -0
- package/dist/examples/tdd-orchestrator.d.ts.map +1 -0
- package/dist/examples/tdd-orchestrator.js +121 -0
- package/dist/examples/tdd-orchestrator.js.map +1 -0
- package/dist/examples/test-cycle-workflow.d.ts +14 -0
- package/dist/examples/test-cycle-workflow.d.ts.map +1 -0
- package/dist/examples/test-cycle-workflow.js +116 -0
- package/dist/examples/test-cycle-workflow.js.map +1 -0
- package/dist/harnesses/claude-code-harness.d.ts +391 -0
- package/dist/harnesses/claude-code-harness.d.ts.map +1 -0
- package/dist/harnesses/claude-code-harness.js +1076 -0
- package/dist/harnesses/claude-code-harness.js.map +1 -0
- package/dist/harnesses/harness-registry.d.ts +440 -0
- package/dist/harnesses/harness-registry.d.ts.map +1 -0
- package/dist/harnesses/harness-registry.js +543 -0
- package/dist/harnesses/harness-registry.js.map +1 -0
- package/dist/harnesses/index.d.ts +12 -0
- package/dist/harnesses/index.d.ts.map +1 -0
- package/dist/harnesses/index.js +11 -0
- package/dist/harnesses/index.js.map +1 -0
- package/dist/harnesses/pi-harness.d.ts +219 -0
- package/dist/harnesses/pi-harness.d.ts.map +1 -0
- package/dist/harnesses/pi-harness.js +676 -0
- package/dist/harnesses/pi-harness.js.map +1 -0
- package/dist/harnesses/pi-schema-converter.d.ts +24 -0
- package/dist/harnesses/pi-schema-converter.d.ts.map +1 -0
- package/dist/harnesses/pi-schema-converter.js +81 -0
- package/dist/harnesses/pi-schema-converter.js.map +1 -0
- package/dist/harnesses/register-defaults.d.ts +24 -0
- package/dist/harnesses/register-defaults.d.ts.map +1 -0
- package/dist/harnesses/register-defaults.js +40 -0
- package/dist/harnesses/register-defaults.js.map +1 -0
- package/dist/harnesses/session-store.d.ts +201 -0
- package/dist/harnesses/session-store.d.ts.map +1 -0
- package/dist/harnesses/session-store.js +254 -0
- package/dist/harnesses/session-store.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/reflection/index.d.ts +5 -0
- package/dist/reflection/index.d.ts.map +1 -0
- package/{src/reflection/index.ts → dist/reflection/index.js} +1 -1
- package/dist/reflection/index.js.map +1 -0
- package/dist/reflection/reflection.d.ts +84 -0
- package/dist/reflection/reflection.d.ts.map +1 -0
- package/dist/reflection/reflection.js +344 -0
- package/dist/reflection/reflection.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +11 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/introspection.d.ts +165 -0
- package/dist/tools/introspection.d.ts.map +1 -0
- package/dist/tools/introspection.js +324 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/types/agent.d.ts +1317 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +423 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/decorators.d.ts +40 -0
- package/dist/types/decorators.d.ts.map +1 -0
- package/dist/types/decorators.js +2 -0
- package/dist/types/decorators.js.map +1 -0
- package/dist/types/error-strategy.d.ts +13 -0
- package/dist/types/error-strategy.d.ts.map +1 -0
- package/dist/types/error-strategy.js +2 -0
- package/dist/types/error-strategy.js.map +1 -0
- package/dist/types/error.d.ts +20 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +2 -0
- package/dist/types/error.js.map +1 -0
- package/dist/types/events.d.ts +113 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/harnesses.d.ts +474 -0
- package/dist/types/harnesses.d.ts.map +1 -0
- package/dist/types/harnesses.js +2 -0
- package/dist/types/harnesses.js.map +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/logging.d.ts +24 -0
- package/dist/types/logging.d.ts.map +1 -0
- package/dist/types/logging.js +2 -0
- package/dist/types/logging.js.map +1 -0
- package/dist/types/observer.d.ts +18 -0
- package/dist/types/observer.d.ts.map +1 -0
- package/dist/types/observer.js +2 -0
- package/dist/types/observer.js.map +1 -0
- package/dist/types/prompt.d.ts +31 -0
- package/dist/types/prompt.d.ts.map +1 -0
- package/dist/types/prompt.js +6 -0
- package/dist/types/prompt.js.map +1 -0
- package/dist/types/providers.d.ts +691 -0
- package/dist/types/providers.d.ts.map +1 -0
- package/dist/types/providers.js +14 -0
- package/dist/types/providers.js.map +1 -0
- package/dist/types/reflection.d.ts +96 -0
- package/dist/types/reflection.d.ts.map +1 -0
- package/dist/types/reflection.js +24 -0
- package/dist/types/reflection.js.map +1 -0
- package/dist/types/restart.d.ts +132 -0
- package/dist/types/restart.d.ts.map +1 -0
- package/dist/types/restart.js +2 -0
- package/dist/types/restart.js.map +1 -0
- package/dist/types/sdk-primitives.d.ts +118 -0
- package/dist/types/sdk-primitives.d.ts.map +1 -0
- package/dist/types/sdk-primitives.js +6 -0
- package/dist/types/sdk-primitives.js.map +1 -0
- package/{src/types/snapshot.ts → dist/types/snapshot.d.ts} +5 -5
- package/dist/types/snapshot.d.ts.map +1 -0
- package/dist/types/snapshot.js +2 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/types/streaming.d.ts +194 -0
- package/dist/types/streaming.d.ts.map +1 -0
- package/dist/types/streaming.js +67 -0
- package/dist/types/streaming.js.map +1 -0
- package/dist/types/workflow-context.d.ts +275 -0
- package/dist/types/workflow-context.d.ts.map +1 -0
- package/dist/types/workflow-context.js +8 -0
- package/dist/types/workflow-context.js.map +1 -0
- package/dist/types/workflow.d.ts +30 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +2 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/utils/agent-validation.d.ts +88 -0
- package/dist/utils/agent-validation.d.ts.map +1 -0
- package/dist/utils/agent-validation.js +87 -0
- package/dist/utils/agent-validation.js.map +1 -0
- package/dist/utils/delay.d.ts +7 -0
- package/dist/utils/delay.d.ts.map +1 -0
- package/dist/utils/delay.js +9 -0
- package/dist/utils/delay.js.map +1 -0
- package/dist/utils/harness-config.d.ts +180 -0
- package/dist/utils/harness-config.d.ts.map +1 -0
- package/dist/utils/harness-config.js +311 -0
- package/dist/utils/harness-config.js.map +1 -0
- package/dist/utils/id.d.ts +6 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +12 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/model-spec.d.ts +110 -0
- package/dist/utils/model-spec.d.ts.map +1 -0
- package/dist/utils/model-spec.js +149 -0
- package/dist/utils/model-spec.js.map +1 -0
- package/dist/utils/observable.d.ts +54 -0
- package/dist/utils/observable.d.ts.map +1 -0
- package/dist/utils/observable.js +82 -0
- package/dist/utils/observable.js.map +1 -0
- package/dist/utils/provider-config.d.ts +10 -0
- package/dist/utils/provider-config.d.ts.map +1 -0
- package/dist/utils/provider-config.js +10 -0
- package/dist/utils/provider-config.js.map +1 -0
- package/dist/utils/restart-analysis.d.ts +202 -0
- package/dist/utils/restart-analysis.d.ts.map +1 -0
- package/dist/utils/restart-analysis.js +426 -0
- package/dist/utils/restart-analysis.js.map +1 -0
- package/dist/utils/session-serialization.d.ts +118 -0
- package/dist/utils/session-serialization.d.ts.map +1 -0
- package/dist/utils/session-serialization.js +217 -0
- package/dist/utils/session-serialization.js.map +1 -0
- package/dist/utils/workflow-error-utils.d.ts +22 -0
- package/dist/utils/workflow-error-utils.d.ts.map +1 -0
- package/dist/utils/workflow-error-utils.js +45 -0
- package/dist/utils/workflow-error-utils.js.map +1 -0
- package/package.json +34 -5
- package/.claude/commands/subtask-planning/prp-base-create.md +0 -120
- package/.claude/commands/subtask-planning/prp-base-execute.md +0 -65
- package/.claude/commands/task-breakdown.md +0 -94
- package/.claude/settings.local.json +0 -9
- package/.claude/system_prompts/task-breakdown.md +0 -101
- package/CHANGELOG.md +0 -188
- package/PRD.md +0 -543
- package/PRPs/001-hierarchical-workflow-engine.md +0 -2438
- package/PRPs/PRDs/002-agent-prompt.md +0 -390
- package/PRPs/PRDs/003-agent-prompt.md +0 -943
- package/PRPs/PRDs/004-agent-prompt.md +0 -1136
- package/PRPs/PRDs/tasks-001.json +0 -492
- package/PRPs/README.md +0 -83
- package/PRPs/templates/prp_base.md +0 -222
- package/docs/agent.md +0 -422
- package/docs/prompt.md +0 -419
- package/docs/workflow.md +0 -600
- package/examples/README.md +0 -258
- package/examples/examples/01-basic-workflow.ts +0 -100
- package/examples/examples/02-decorator-options.ts +0 -217
- package/examples/examples/03-parent-child.ts +0 -241
- package/examples/examples/04-observers-debugger.ts +0 -340
- package/examples/examples/05-error-handling.ts +0 -387
- package/examples/examples/06-concurrent-tasks.ts +0 -352
- package/examples/examples/07-agent-loops.ts +0 -432
- package/examples/examples/08-sdk-features.ts +0 -667
- package/examples/examples/09-reflection.ts +0 -573
- package/examples/examples/10-introspection.ts +0 -550
- package/examples/examples/11-reparenting-workflows.ts +0 -269
- package/examples/index.ts +0 -147
- package/examples/utils/helpers.ts +0 -57
- package/package-lock.json +0 -2398
- package/plan/001_d3bb02af4886/TEST_RESULTS.md +0 -259
- package/plan/001_d3bb02af4886/backlog.json +0 -867
- package/plan/001_d3bb02af4886/bug_fix_tasks.json +0 -484
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S1/PRP.md +0 -488
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S2/PRP.md +0 -581
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S3/PRP.md +0 -687
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S1/PRP.md +0 -492
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/PRP.md +0 -932
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/concurrent_error_testing_patterns.md +0 -1109
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/vitest_concurrent_testing.md +0 -802
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/workflow_engine_test_references.md +0 -603
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md +0 -564
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md +0 -518
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/PRP.md +0 -1252
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/PRP.md +0 -364
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/CODEBASE_INVENTORY.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/DECORATOR_DOCUMENTATION_PATTERNS.md +0 -205
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/PRD_LOCATION_ANALYSIS.md +0 -199
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/ULTRATHINK_PRP_PLAN.md +0 -134
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/PRP.md +0 -495
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/research/console_error_inventory.md +0 -435
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/PRP.md +0 -558
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/research/external_research.md +0 -788
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S2/PRP.md +0 -460
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S3/PRP.md +0 -454
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/PRP.md +0 -520
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/RECOMMENDATION.md +0 -417
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/external_workflow_engines_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/security_implications_analysis.md +0 -245
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S2/PRP.md +0 -792
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/PRP.md +0 -535
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/TEST_EXECUTION_REPORT.md +0 -190
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/PRP.md +0 -654
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/TEST_FIX_REPORT.md +0 -227
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/KEY_FINDINGS.md +0 -345
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/QUICK_REFERENCE.md +0 -193
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/test_maintenance_research.md +0 -1323
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/BREAKING_CHANGES_AUDIT.md +0 -1011
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/PRP.md +0 -927
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S2/PRP.md +0 -505
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/architecture/logger_child_signature_analysis.md +0 -401
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/child_implementation_research.md +0 -142
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/test_patterns_research.md +0 -112
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/vitest_patterns_research.md +0 -159
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/PRP.md +0 -549
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/VERIFICATION_REPORT.md +0 -368
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/edge_case_analysis.md +0 -172
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/usage_inventory.md +0 -175
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md +0 -696
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S4/PRP.md +0 -860
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md +0 -1066
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01-testing-aggregated-errors.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md +0 -789
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02-error-merge-strategy-testing-guide.md +0 -1098
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02_aggregate_error_patterns.md +0 -1037
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03-promise-allsettled-testing-patterns.md +0 -916
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03_error_merging_strategies.md +0 -1045
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/04_github_stackoverflow_examples.md +0 -890
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/05_comprehensive_summary.md +0 -822
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/INDEX.md +0 -668
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md +0 -706
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/README.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/RESEARCH_REPORT.md +0 -655
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S4/research/vitest_testing_patterns.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T3S2/PRP.md +0 -426
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/QUICK_REFERENCE.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/RESEARCH_SUMMARY.md +0 -316
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/vitest_observer_error_logging_best_practices.md +0 -754
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/PRP.md +0 -719
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/README.md +0 -215
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/analysis.md +0 -765
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S3/PRP.md +0 -718
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/DECISION.md +0 -149
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/PRP.md +0 -470
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/ULTRATHINK_PLAN.md +0 -332
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/codebase_workflow_name_analysis.md +0 -167
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/external_best_practices.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/validation_patterns.md +0 -273
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S1/workflow_engine_ancestry_api_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S3-PRP.md +0 -434
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S1/PRP.md +0 -717
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/PRP.md +0 -472
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/VALIDATION_REPORT.md +0 -125
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/research/ULTRATHINK_PRP_PLAN.md +0 -301
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/error-logging-best-practices.md +0 -1170
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/research_typescript_partial_and_overloads.md +0 -940
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-quick-reference.md +0 -151
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-research.md +0 -650
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md +0 -259
- package/plan/001_d3bb02af4886/bugfix/P1M1T1S1/PRP.md +0 -457
- package/plan/001_d3bb02af4886/bugfix/RESEARCH_SUMMARY.md +0 -346
- package/plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md +0 -311
- package/plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md +0 -1565
- package/plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md +0 -288
- package/plan/001_d3bb02af4886/bugfix/architecture/promise_all_analysis.md +0 -741
- package/plan/001_d3bb02af4886/docs/PRP/P1M1T1S4-functional-workflow-error-state-capture-test.md +0 -652
- package/plan/001_d3bb02af4886/docs/PRP/P1P2-PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/P3P4-PRP.md +0 -1388
- package/plan/001_d3bb02af4886/docs/PRP/P4P5-PRP.md +0 -1136
- package/plan/001_d3bb02af4886/docs/PRP/PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S1-PRP.md +0 -415
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S2-PRP.md +0 -378
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S4-PRP.md +0 -713
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M2T1S4-PRP.md +0 -370
- package/plan/001_d3bb02af4886/docs/PRP_P1M3T1S3.md +0 -499
- package/plan/001_d3bb02af4886/docs/TEST_RESULTS.md +0 -230
- package/plan/001_d3bb02af4886/docs/architecture/external_deps.md +0 -358
- package/plan/001_d3bb02af4886/docs/architecture/system_context.md +0 -242
- package/plan/001_d3bb02af4886/docs/bugfix/ANALYSIS_PRD_VS_IMPLEMENTATION.md +0 -1134
- package/plan/001_d3bb02af4886/docs/bugfix/GAP_ANALYSIS_SUMMARY.md +0 -179
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/PRP.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/validation-report.md +0 -214
- package/plan/001_d3bb02af4886/docs/bugfix/PRP_P1M4T2S3.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_PRP.md +0 -529
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_QUICK_REFERENCE.md +0 -142
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_README.md +0 -304
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_TEST_RESULTS.md +0 -558
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_VALIDATION_SUMMARY.md +0 -256
- package/plan/001_d3bb02af4886/docs/bugfix/system_context.md +0 -346
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/bug_analysis.md +0 -415
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/implementation_patterns.md +0 -489
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/system_context.md +0 -218
- package/plan/001_d3bb02af4886/docs/bugfix_INITIATION_SUMMARY.md +0 -380
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_PATTERNS.md +0 -1923
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_QUICK_REF.md +0 -319
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/codebase-context.md +0 -115
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/cycle-detection-algorithms.md +0 -134
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/test-patterns.md +0 -153
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/workflow-class.md +0 -132
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_BEST_PRACTICES.md +0 -716
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_QUICK_REF.md +0 -186
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/GROUNDSWELL_DECORATOR_EXAMPLES.md +0 -604
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/INDEX.md +0 -213
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/codebase_structure.md +0 -30
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/existing_test_pattern.md +0 -56
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/getRootObservers_implementation.md +0 -53
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/test_conventions.md +0 -49
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/PRP.md +0 -958
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/QUICK_REFERENCE.md +0 -339
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/README.md +0 -305
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/SUMMARY.md +0 -433
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md +0 -1574
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/test-pattern-examples.md +0 -1014
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_BEST_PRACTICES.md +0 -1929
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_CODE_PATTERNS.md +0 -857
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_INTEGRATION_GUIDE.md +0 -738
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_RESEARCH_INDEX.md +0 -424
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_INDEX.md +0 -291
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_RESEARCH_REPORT.md +0 -1342
- package/plan/001_d3bb02af4886/docs/research/P1P2/RESEARCH_SUMMARY.md +0 -342
- package/plan/001_d3bb02af4886/docs/research/P1P2/anthropic-sdk.md +0 -174
- package/plan/001_d3bb02af4886/docs/research/P1P2/async-local-storage.md +0 -200
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-code-patterns.md +0 -1205
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-decision-matrix.md +0 -421
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-implementation-guide.md +0 -1341
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-integration-guide.md +0 -834
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-patterns.md +0 -1468
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-quick-reference.md +0 -558
- package/plan/001_d3bb02af4886/docs/research/P1P2/zod-schema.md +0 -152
- package/plan/001_d3bb02af4886/docs/research/P3P4/caching-lru.md +0 -116
- package/plan/001_d3bb02af4886/docs/research/P3P4/introspection-tools.md +0 -177
- package/plan/001_d3bb02af4886/docs/research/P3P4/reflection-patterns.md +0 -117
- package/plan/001_d3bb02af4886/docs/research/P4P5/RESEARCH_SUMMARY.md +0 -151
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_QUICK_REF.md +0 -376
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_RESEARCH.md +0 -1507
- package/plan/001_d3bb02af4886/docs/research/bugfix_typescript_patterns.md +0 -949
- package/plan/001_d3bb02af4886/docs/research/error-testing-research.md +0 -619
- package/plan/001_d3bb02af4886/docs/research/error_handling_patterns.md +0 -723
- package/plan/001_d3bb02af4886/docs/research/general/INTROSPECTION_RESEARCH_SUMMARY.md +0 -378
- package/plan/001_d3bb02af4886/docs/research/general/README-INTROSPECTION.md +0 -352
- package/plan/001_d3bb02af4886/docs/research/general/agent-introspection-patterns.md +0 -1085
- package/plan/001_d3bb02af4886/docs/research/general/introspection-security-guide.md +0 -984
- package/plan/001_d3bb02af4886/docs/research/general/introspection-tool-examples.md +0 -875
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/PRP_TEMPLATE.md +0 -460
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/QUICK_REFERENCE.md +0 -324
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/README.md +0 -175
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/RESEARCH_REPORT.md +0 -499
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/SUMMARY.md +0 -163
- package/plan/001_d3bb02af4886/prd_snapshot.md +0 -543
- package/plan/bugfix/BUG_FIX_SUMMARY.md +0 -961
- package/scripts/generate-llms-full.ts +0 -206
- package/src/__tests__/adversarial/attachChild-performance.test.ts +0 -216
- package/src/__tests__/adversarial/circular-reference.test.ts +0 -101
- package/src/__tests__/adversarial/complex-circular-reference.test.ts +0 -139
- package/src/__tests__/adversarial/concurrent-task-failures.test.ts +0 -571
- package/src/__tests__/adversarial/deep-analysis.test.ts +0 -729
- package/src/__tests__/adversarial/deep-hierarchy-stress.test.ts +0 -213
- package/src/__tests__/adversarial/e2e-prd-validation.test.ts +0 -448
- package/src/__tests__/adversarial/edge-case.test.ts +0 -703
- package/src/__tests__/adversarial/error-merge-strategy.test.ts +0 -760
- package/src/__tests__/adversarial/incremental-performance.test.ts +0 -140
- package/src/__tests__/adversarial/node-map-update-benchmarks.test.ts +0 -457
- package/src/__tests__/adversarial/observer-propagation.test.ts +0 -487
- package/src/__tests__/adversarial/parent-validation.test.ts +0 -143
- package/src/__tests__/adversarial/prd-12-2-compliance.test.ts +0 -611
- package/src/__tests__/adversarial/prd-compliance.test.ts +0 -731
- package/src/__tests__/compatibility/backward-compatibility.test.ts +0 -1572
- package/src/__tests__/helpers/index.ts +0 -18
- package/src/__tests__/helpers/tree-verification.ts +0 -257
- package/src/__tests__/integration/agent-workflow.test.ts +0 -256
- package/src/__tests__/integration/bidirectional-consistency.test.ts +0 -847
- package/src/__tests__/integration/observer-logging.test.ts +0 -643
- package/src/__tests__/integration/tree-mirroring.test.ts +0 -151
- package/src/__tests__/integration/workflow-reparenting.test.ts +0 -303
- package/src/__tests__/unit/agent.test.ts +0 -169
- package/src/__tests__/unit/cache-key.test.ts +0 -182
- package/src/__tests__/unit/cache.test.ts +0 -172
- package/src/__tests__/unit/context.test.ts +0 -217
- package/src/__tests__/unit/decorators.test.ts +0 -100
- package/src/__tests__/unit/introspection-tools.test.ts +0 -277
- package/src/__tests__/unit/logger.test.ts +0 -293
- package/src/__tests__/unit/observable.test.ts +0 -321
- package/src/__tests__/unit/prompt.test.ts +0 -135
- package/src/__tests__/unit/reflection.test.ts +0 -210
- package/src/__tests__/unit/tree-debugger-incremental.test.ts +0 -170
- package/src/__tests__/unit/tree-debugger.test.ts +0 -85
- package/src/__tests__/unit/utils/workflow-error-utils.test.ts +0 -209
- package/src/__tests__/unit/workflow-detachChild.test.ts +0 -100
- package/src/__tests__/unit/workflow-emitEvent-childDetached.test.ts +0 -153
- package/src/__tests__/unit/workflow-isDescendantOf.test.ts +0 -180
- package/src/__tests__/unit/workflow.test.ts +0 -357
- package/src/cache/cache-key.ts +0 -244
- package/src/cache/cache.ts +0 -236
- package/src/core/agent.ts +0 -593
- package/src/core/event-tree.ts +0 -260
- package/src/core/logger.ts +0 -112
- package/src/core/mcp-handler.ts +0 -184
- package/src/core/prompt.ts +0 -150
- package/src/core/workflow-context.ts +0 -351
- package/src/core/workflow.ts +0 -540
- package/src/debugger/tree-debugger.ts +0 -255
- package/src/decorators/observed-state.ts +0 -95
- package/src/decorators/step.ts +0 -139
- package/src/decorators/task.ts +0 -159
- package/src/examples/tdd-orchestrator.ts +0 -65
- package/src/examples/test-cycle-workflow.ts +0 -64
- package/src/index.ts +0 -142
- package/src/reflection/reflection.ts +0 -407
- package/src/tools/index.ts +0 -36
- package/src/tools/introspection.ts +0 -464
- package/src/types/agent.ts +0 -90
- package/src/types/decorators.ts +0 -32
- package/src/types/error-strategy.ts +0 -13
- package/src/types/error.ts +0 -20
- package/src/types/events.ts +0 -75
- package/src/types/index.ts +0 -55
- package/src/types/logging.ts +0 -24
- package/src/types/observer.ts +0 -18
- package/src/types/prompt.ts +0 -40
- package/src/types/reflection.ts +0 -117
- package/src/types/sdk-primitives.ts +0 -128
- package/src/types/workflow-context.ts +0 -163
- package/src/types/workflow.ts +0 -37
- package/src/utils/id.ts +0 -11
- package/src/utils/index.ts +0 -4
- package/src/utils/observable.ts +0 -106
- package/src/utils/workflow-error-utils.ts +0 -56
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -16
|
@@ -0,0 +1,986 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { generateId } from '../utils/id.js';
|
|
3
|
+
import { validateAgentResponse } from '../utils/agent-validation.js';
|
|
4
|
+
import { analyzeErrorForRestart } from '../utils/restart-analysis.js';
|
|
5
|
+
import { mergeWorkflowErrors } from '../utils/workflow-error-utils.js';
|
|
6
|
+
import { WorkflowLogger } from './logger.js';
|
|
7
|
+
import { getObservedState } from '../decorators/observed-state.js';
|
|
8
|
+
import { createWorkflowContext } from './workflow-context.js';
|
|
9
|
+
/**
|
|
10
|
+
* Base class for all workflows
|
|
11
|
+
* Supports both class-based (subclass with run()) and functional (executor) patterns
|
|
12
|
+
*
|
|
13
|
+
* @example Class-based pattern:
|
|
14
|
+
* ```ts
|
|
15
|
+
* class MyWorkflow extends Workflow {
|
|
16
|
+
* async run() {
|
|
17
|
+
* this.setStatus('running');
|
|
18
|
+
* // workflow logic
|
|
19
|
+
* this.setStatus('completed');
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example Functional pattern:
|
|
25
|
+
* ```ts
|
|
26
|
+
* const workflow = new Workflow({ name: 'MyWorkflow' }, async (ctx) => {
|
|
27
|
+
* await ctx.step('step1', async () => {
|
|
28
|
+
* // step logic
|
|
29
|
+
* });
|
|
30
|
+
* });
|
|
31
|
+
* await workflow.run();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class Workflow {
|
|
35
|
+
/** Unique identifier for this workflow instance */
|
|
36
|
+
id;
|
|
37
|
+
/** Parent workflow (null for root workflows) */
|
|
38
|
+
parent = null;
|
|
39
|
+
/** Child workflows */
|
|
40
|
+
children = [];
|
|
41
|
+
/** Current execution status */
|
|
42
|
+
status = 'idle';
|
|
43
|
+
/** Logger instance for this workflow */
|
|
44
|
+
logger;
|
|
45
|
+
/** The node representation of this workflow */
|
|
46
|
+
node;
|
|
47
|
+
/** Observers (only populated on root workflow) */
|
|
48
|
+
observers = [];
|
|
49
|
+
/** Optional executor function for functional workflows */
|
|
50
|
+
executor;
|
|
51
|
+
/** Workflow configuration */
|
|
52
|
+
config;
|
|
53
|
+
/** Error collection state for workflow-level error merge */
|
|
54
|
+
collectedErrors = [];
|
|
55
|
+
/** Event history entries with insertion timestamps for replay functionality (ES2022 private field) */
|
|
56
|
+
#eventHistory = [];
|
|
57
|
+
/** Total operations count for error merge context */
|
|
58
|
+
totalOperations = 0;
|
|
59
|
+
/** Operation counter for error merge context */
|
|
60
|
+
operationCounter = 0;
|
|
61
|
+
/**
|
|
62
|
+
* Create a new workflow instance
|
|
63
|
+
*
|
|
64
|
+
* @overload Class-based pattern: constructor(name?: string, parent?: Workflow)
|
|
65
|
+
* @overload Functional pattern: constructor(config: WorkflowConfig, executor?: WorkflowExecutor)
|
|
66
|
+
* @param name For class-based pattern, human-readable name. Allowed characters: alphanumeric (a-z, A-Z, 0-9), spaces, hyphens (-), underscores (_). (default: class name).
|
|
67
|
+
* For functional pattern, config object with workflow settings.
|
|
68
|
+
* @param parentOrExecutor For class-based pattern, optional parent workflow.
|
|
69
|
+
* For functional pattern, executor function.
|
|
70
|
+
*
|
|
71
|
+
* @remarks Security validation rejects names containing control characters, HTML tags, JavaScript patterns, path traversal sequences (..), and file system special characters (/ \ : * ? " < > |). This prevents XSS attacks, injection attacks, and path traversal vulnerabilities.
|
|
72
|
+
*/
|
|
73
|
+
constructor(name, parentOrExecutor) {
|
|
74
|
+
this.id = generateId();
|
|
75
|
+
// Parse overloaded arguments
|
|
76
|
+
if (typeof name === 'object' && name !== null) {
|
|
77
|
+
// Functional pattern: constructor(config, executor)
|
|
78
|
+
this.config = name;
|
|
79
|
+
this.executor = parentOrExecutor;
|
|
80
|
+
this.parent = null;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// Class-based pattern: constructor(name, parent)
|
|
84
|
+
this.config = { name: name ?? this.constructor.name };
|
|
85
|
+
this.parent = parentOrExecutor ?? null;
|
|
86
|
+
}
|
|
87
|
+
// Validate workflow name (after config is normalized)
|
|
88
|
+
if (typeof this.config.name === 'string') {
|
|
89
|
+
const trimmedName = this.config.name.trim();
|
|
90
|
+
if (trimmedName.length === 0) {
|
|
91
|
+
throw new Error('Workflow name cannot be empty or whitespace only');
|
|
92
|
+
}
|
|
93
|
+
if (this.config.name.length > 100) {
|
|
94
|
+
throw new Error('Workflow name cannot exceed 100 characters');
|
|
95
|
+
}
|
|
96
|
+
// Shared message for all security validations below.
|
|
97
|
+
const invalidNameMessage = 'Invalid workflow name. Names may contain letters, numbers, spaces, hyphens, underscores, and emoji.';
|
|
98
|
+
// Security validation: control characters (ASCII 0x00-0x1F, 0x7F)
|
|
99
|
+
if (/[\x00-\x1F\x7F]/.test(trimmedName)) {
|
|
100
|
+
throw new Error(invalidNameMessage);
|
|
101
|
+
}
|
|
102
|
+
// Security validation: HTML/JavaScript injection patterns
|
|
103
|
+
if (/<[^>]*>/.test(trimmedName) || /javascript:/i.test(trimmedName)) {
|
|
104
|
+
throw new Error(invalidNameMessage);
|
|
105
|
+
}
|
|
106
|
+
// Security validation: path traversal patterns
|
|
107
|
+
if (/\.\./.test(trimmedName)) {
|
|
108
|
+
throw new Error(invalidNameMessage);
|
|
109
|
+
}
|
|
110
|
+
// Security validation: file system special characters
|
|
111
|
+
if (/[\/\\:*?"<>|]/.test(trimmedName)) {
|
|
112
|
+
throw new Error(invalidNameMessage);
|
|
113
|
+
}
|
|
114
|
+
// Security validation: allowed characters (allowlist - defense in depth)
|
|
115
|
+
// Unicode-aware: permits letters (\p{L}), numbers (\p{N}), and emoji
|
|
116
|
+
// (including pictographic bases, variation selectors U+FE0F, and ZWJ
|
|
117
|
+
// sequences U+200D) from any script, while still blocking exotic or
|
|
118
|
+
// symbolic characters not covered by the targeted checks above.
|
|
119
|
+
if (!/^[\p{L}\p{N}\p{Emoji_Presentation}\p{Extended_Pictographic}\u200D\uFE0F _-]+$/u.test(trimmedName)) {
|
|
120
|
+
throw new Error(invalidNameMessage);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Create the node representation
|
|
124
|
+
this.node = {
|
|
125
|
+
id: this.id,
|
|
126
|
+
name: this.config.name ?? this.constructor.name,
|
|
127
|
+
parent: this.parent?.node ?? null,
|
|
128
|
+
children: [],
|
|
129
|
+
status: 'idle',
|
|
130
|
+
logs: [],
|
|
131
|
+
events: [],
|
|
132
|
+
stateSnapshot: null,
|
|
133
|
+
};
|
|
134
|
+
// Create logger with root observers
|
|
135
|
+
this.logger = new WorkflowLogger(this.node, this.getRootObservers());
|
|
136
|
+
// Attach to parent if provided
|
|
137
|
+
if (this.parent) {
|
|
138
|
+
this.parent.attachChild(this);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get observers from the root workflow
|
|
143
|
+
* Traverses up the tree to find the root
|
|
144
|
+
* Uses cycle detection to prevent infinite loops from circular parent-child relationships
|
|
145
|
+
*/
|
|
146
|
+
getRootObservers() {
|
|
147
|
+
const visited = new Set();
|
|
148
|
+
let root = this;
|
|
149
|
+
let current = this;
|
|
150
|
+
while (current) {
|
|
151
|
+
if (visited.has(current)) {
|
|
152
|
+
throw new Error('Circular parent-child relationship detected');
|
|
153
|
+
}
|
|
154
|
+
visited.add(current);
|
|
155
|
+
root = current;
|
|
156
|
+
current = current.parent;
|
|
157
|
+
}
|
|
158
|
+
return root.observers;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if event history is enabled for this workflow
|
|
162
|
+
*
|
|
163
|
+
* @returns true if event history is enabled, false otherwise
|
|
164
|
+
*/
|
|
165
|
+
isEventHistoryEnabled() {
|
|
166
|
+
return this.config.eventHistory?.enabled === true;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get event history configuration with defaults applied
|
|
170
|
+
*
|
|
171
|
+
* @returns Configuration object with all required fields populated
|
|
172
|
+
*/
|
|
173
|
+
getEventHistoryConfig() {
|
|
174
|
+
return {
|
|
175
|
+
enabled: this.config.eventHistory?.enabled ?? false,
|
|
176
|
+
maxEvents: this.config.eventHistory?.maxEvents ?? 1000,
|
|
177
|
+
maxAgeMs: this.config.eventHistory?.maxAgeMs ?? 3600000,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Trim event history based on configuration
|
|
182
|
+
*
|
|
183
|
+
* Uses lazy trimming for performance:
|
|
184
|
+
* - Only trims when at least 1.5x over the maxEvents limit
|
|
185
|
+
* - Applies both count and age constraints
|
|
186
|
+
* - Uses slice() for efficiency (not shift())
|
|
187
|
+
*
|
|
188
|
+
* @remarks
|
|
189
|
+
* Lazy trimming reduces the number of trim operations by only trimming
|
|
190
|
+
* when the history is significantly over the limit (1.5x). This provides
|
|
191
|
+
* better performance for high-frequency event emission.
|
|
192
|
+
*/
|
|
193
|
+
trimEventHistory() {
|
|
194
|
+
const config = this.getEventHistoryConfig();
|
|
195
|
+
// Lazy trimming: only trim when significantly over limit
|
|
196
|
+
const trimThreshold = Math.floor(config.maxEvents * 1.5);
|
|
197
|
+
if (this.#eventHistory.length < trimThreshold) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
const ageCutoff = now - config.maxAgeMs;
|
|
202
|
+
// Age-based trimming: find first entry within age limit
|
|
203
|
+
let keepFromIndex = 0;
|
|
204
|
+
for (let i = 0; i < this.#eventHistory.length; i++) {
|
|
205
|
+
if (this.#eventHistory[i].insertedAt > ageCutoff) {
|
|
206
|
+
keepFromIndex = i;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Count-based trimming: remove excess events
|
|
211
|
+
const countBasedIndex = Math.max(0, this.#eventHistory.length - config.maxEvents);
|
|
212
|
+
// Use the more aggressive constraint
|
|
213
|
+
const finalIndex = Math.max(keepFromIndex, countBasedIndex);
|
|
214
|
+
if (finalIndex > 0) {
|
|
215
|
+
this.#eventHistory = this.#eventHistory.slice(finalIndex);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Check if this workflow is a descendant of the given ancestor workflow.
|
|
220
|
+
*
|
|
221
|
+
* Traverses the parent chain upward looking for the ancestor reference.
|
|
222
|
+
* Uses a visited Set to detect cycles during traversal. This method provides
|
|
223
|
+
* a convenient way to check workflow hierarchy relationships without manually
|
|
224
|
+
* traversing the parent chain.
|
|
225
|
+
*
|
|
226
|
+
* @remarks SECURITY WARNING: This method reveals workflow hierarchy information.
|
|
227
|
+
* If your application exposes workflows via an API, ensure you implement proper
|
|
228
|
+
* access control to prevent unauthorized topology discovery. Note that the parent
|
|
229
|
+
* and children properties are already public, so this method does not expose any
|
|
230
|
+
* new information beyond what is currently accessible.
|
|
231
|
+
*
|
|
232
|
+
* **Time Complexity**: O(d) where d is the depth of the hierarchy
|
|
233
|
+
* **Space Complexity**: O(d) for the visited Set in worst case (cycle detection)
|
|
234
|
+
*
|
|
235
|
+
* @example Check if a workflow belongs to a specific hierarchy
|
|
236
|
+
* ```typescript
|
|
237
|
+
* const root = new Workflow('root');
|
|
238
|
+
* const child = new Workflow('child', { parent: root });
|
|
239
|
+
*
|
|
240
|
+
* if (child.isDescendantOf(root)) {
|
|
241
|
+
* console.log('Child is in root hierarchy');
|
|
242
|
+
* }
|
|
243
|
+
* ```
|
|
244
|
+
*
|
|
245
|
+
* @example Validate before attaching to prevent circular references
|
|
246
|
+
* ```typescript
|
|
247
|
+
* if (!newChild.isDescendantOf(parent)) {
|
|
248
|
+
* parent.attachChild(newChild);
|
|
249
|
+
* } else {
|
|
250
|
+
* throw new Error('Would create circular reference');
|
|
251
|
+
* }
|
|
252
|
+
* ```
|
|
253
|
+
*
|
|
254
|
+
* @example Check for ancestor relationship in conditional logic
|
|
255
|
+
* ```typescript
|
|
256
|
+
* const isInProductionBranch = workflow.isDescendantOf(productionRoot);
|
|
257
|
+
* if (isInProductionBranch) {
|
|
258
|
+
* // Apply production-specific logic
|
|
259
|
+
* }
|
|
260
|
+
* ```
|
|
261
|
+
*
|
|
262
|
+
* @param ancestor - The potential ancestor workflow to check
|
|
263
|
+
* @returns true if ancestor is found in parent chain, false otherwise
|
|
264
|
+
* @throws {Error} If a cycle is detected during traversal (indicates corrupted tree structure)
|
|
265
|
+
*/
|
|
266
|
+
isDescendantOf(ancestor) {
|
|
267
|
+
const visited = new Set();
|
|
268
|
+
let current = this.parent;
|
|
269
|
+
while (current !== null) {
|
|
270
|
+
if (visited.has(current)) {
|
|
271
|
+
throw new Error('Circular parent-child relationship detected');
|
|
272
|
+
}
|
|
273
|
+
visited.add(current);
|
|
274
|
+
if (current === ancestor) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
current = current.parent;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Get the root workflow
|
|
283
|
+
* Uses cycle detection to prevent infinite loops from circular parent-child relationships
|
|
284
|
+
*/
|
|
285
|
+
getRoot() {
|
|
286
|
+
const visited = new Set();
|
|
287
|
+
let root = this;
|
|
288
|
+
let current = this;
|
|
289
|
+
while (current) {
|
|
290
|
+
if (visited.has(current)) {
|
|
291
|
+
throw new Error('Circular parent-child relationship detected');
|
|
292
|
+
}
|
|
293
|
+
visited.add(current);
|
|
294
|
+
root = current;
|
|
295
|
+
current = current.parent;
|
|
296
|
+
}
|
|
297
|
+
return root;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Add an observer to this workflow (must be root)
|
|
301
|
+
* @throws Error if called on non-root workflow
|
|
302
|
+
* @side effects Adds observer to internal observers array for root workflows.
|
|
303
|
+
* Observers will receive notifications for workflow events.
|
|
304
|
+
*/
|
|
305
|
+
addObserver(observer) {
|
|
306
|
+
if (this.parent) {
|
|
307
|
+
throw new Error('Observers can only be added to root workflows');
|
|
308
|
+
}
|
|
309
|
+
this.observers.push(observer);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Remove an observer from this workflow
|
|
313
|
+
*/
|
|
314
|
+
removeObserver(observer) {
|
|
315
|
+
const index = this.observers.indexOf(observer);
|
|
316
|
+
if (index !== -1) {
|
|
317
|
+
this.observers.splice(index, 1);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Attach a child workflow to this parent workflow.
|
|
322
|
+
*
|
|
323
|
+
* Validates that the child can be attached by checking:
|
|
324
|
+
* 1. Child is not already attached to this parent workflow
|
|
325
|
+
* 2. Child does not have a different parent (enforces single-parent invariant)
|
|
326
|
+
* 3. Child is not an ancestor of this parent (prevents circular references)
|
|
327
|
+
*
|
|
328
|
+
* **Structural Changes:**
|
|
329
|
+
* - Adds child to this.children array (workflow tree)
|
|
330
|
+
* - Adds child.node to this.node.children array (node tree)
|
|
331
|
+
* - Sets child.parent = this (workflow tree)
|
|
332
|
+
* - Sets child.node.parent = this.node (node tree)
|
|
333
|
+
* - Emits childAttached event to notify observers
|
|
334
|
+
* - Emits treeUpdated event to trigger tree debugger rebuild
|
|
335
|
+
*
|
|
336
|
+
* **Invariants Maintained:**
|
|
337
|
+
* - Single-parent rule: A workflow can only have one parent
|
|
338
|
+
* - 1:1 tree mirror: workflow tree and node tree stay synchronized
|
|
339
|
+
* - No cycles: A workflow cannot be its own ancestor
|
|
340
|
+
*
|
|
341
|
+
* **Cycle Detection:**
|
|
342
|
+
* - Uses isDescendantOf() helper with Set-based cycle detection
|
|
343
|
+
* - Throws immediately if circular reference would be created
|
|
344
|
+
*
|
|
345
|
+
* @param child - The child workflow to attach
|
|
346
|
+
* @throws {Error} If the child is already attached to this workflow
|
|
347
|
+
* @throws {Error} If the child already has a different parent (use detachChild() first for reparenting)
|
|
348
|
+
* @throws {Error} If the child is an ancestor of this parent (would create circular reference)
|
|
349
|
+
* @side effects Modifies workflow tree structure, emits childAttached event,
|
|
350
|
+
* and triggers treeUpdated event for debugger.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* const parent = new Workflow('Parent');
|
|
355
|
+
* const child = new Workflow('Child');
|
|
356
|
+
* parent.attachChild(child);
|
|
357
|
+
* // child.parent === parent
|
|
358
|
+
* // parent.children.includes(child) === true
|
|
359
|
+
* ```
|
|
360
|
+
*
|
|
361
|
+
* @example Reparenting workflow
|
|
362
|
+
* ```ts
|
|
363
|
+
* const parent1 = new Workflow('Parent1');
|
|
364
|
+
* const parent2 = new Workflow('Parent2');
|
|
365
|
+
* const child = new Workflow('Child', parent1); // Attached to parent1
|
|
366
|
+
*
|
|
367
|
+
* // Later, move child to parent2
|
|
368
|
+
* parent1.detachChild(child);
|
|
369
|
+
* parent2.attachChild(child);
|
|
370
|
+
* // child.parent === parent2
|
|
371
|
+
* ```
|
|
372
|
+
*
|
|
373
|
+
* @see detachChild - For detaching children (enables reparenting)
|
|
374
|
+
* @see isDescendantOf - Private helper for circular reference detection
|
|
375
|
+
*/
|
|
376
|
+
attachChild(child) {
|
|
377
|
+
if (this.children.includes(child)) {
|
|
378
|
+
throw new Error('Child already attached to this workflow');
|
|
379
|
+
}
|
|
380
|
+
// Check if child already has a different parent
|
|
381
|
+
if (child.parent !== null && child.parent !== this) {
|
|
382
|
+
const errorMessage = `Child '${child.node.name}' already has a parent '${child.parent.node.name}'. ` +
|
|
383
|
+
`A workflow can only have one parent. ` +
|
|
384
|
+
`Use detachChild() on '${child.parent.node.name}' first if you need to reparent.`;
|
|
385
|
+
console.error(errorMessage);
|
|
386
|
+
throw new Error(errorMessage);
|
|
387
|
+
}
|
|
388
|
+
// Check if child is an ancestor (would create circular reference)
|
|
389
|
+
if (this.isDescendantOf(child)) {
|
|
390
|
+
const errorMessage = `Cannot attach child '${child.node.name}' - it is an ancestor of '${this.node.name}'. ` +
|
|
391
|
+
`This would create a circular reference.`;
|
|
392
|
+
console.error(errorMessage);
|
|
393
|
+
throw new Error(errorMessage);
|
|
394
|
+
}
|
|
395
|
+
// Update child's parent if it's currently null
|
|
396
|
+
if (child.parent === null) {
|
|
397
|
+
child.parent = this;
|
|
398
|
+
child.node.parent = this.node; // Maintain 1:1 mirror between workflow tree and node tree
|
|
399
|
+
}
|
|
400
|
+
this.children.push(child);
|
|
401
|
+
this.node.children.push(child.node);
|
|
402
|
+
// Emit child attached event (triggers onTreeChanged via emitEvent)
|
|
403
|
+
this.emitEvent({
|
|
404
|
+
type: 'childAttached',
|
|
405
|
+
parentId: this.id,
|
|
406
|
+
child: child.node,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Detach a child workflow from this parent workflow.
|
|
411
|
+
*
|
|
412
|
+
* Removes the child from both the workflow tree (this.children) and
|
|
413
|
+
* the node tree (this.node.children), clears the child's parent reference,
|
|
414
|
+
* and emits a childDetached event to notify observers.
|
|
415
|
+
*
|
|
416
|
+
* Also emits treeUpdated event to trigger tree debugger rebuild.
|
|
417
|
+
*
|
|
418
|
+
* This enables reparenting workflows: oldParent.detachChild(child); newParent.attachChild(child);
|
|
419
|
+
*
|
|
420
|
+
* @param child - The child workflow to detach
|
|
421
|
+
* @throws {Error} If the child is not attached to this parent workflow
|
|
422
|
+
* @side effects Modifies workflow tree structure, emits childDetached event,
|
|
423
|
+
* and triggers treeUpdated event for debugger.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```ts
|
|
427
|
+
* const parent = new Workflow('Parent');
|
|
428
|
+
* const child = new Workflow('Child', parent);
|
|
429
|
+
*
|
|
430
|
+
* // Later, reparent to a different parent
|
|
431
|
+
* parent.detachChild(child);
|
|
432
|
+
* newParent.attachChild(child);
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
detachChild(child) {
|
|
436
|
+
// Validate child is actually attached
|
|
437
|
+
const index = this.children.indexOf(child);
|
|
438
|
+
if (index === -1) {
|
|
439
|
+
throw new Error(`Child '${child.node.name}' is not attached to workflow '${this.node.name}'`);
|
|
440
|
+
}
|
|
441
|
+
// Remove from workflow tree (this.children array)
|
|
442
|
+
this.children.splice(index, 1);
|
|
443
|
+
// Remove from node tree (this.node.children array)
|
|
444
|
+
const nodeIndex = this.node.children.indexOf(child.node);
|
|
445
|
+
if (nodeIndex !== -1) {
|
|
446
|
+
this.node.children.splice(nodeIndex, 1);
|
|
447
|
+
}
|
|
448
|
+
// Clear child's parent reference (both workflow tree and node tree)
|
|
449
|
+
child.parent = null;
|
|
450
|
+
child.node.parent = null; // Maintain 1:1 mirror between workflow tree and node tree
|
|
451
|
+
// Emit childDetached event (triggers onTreeChanged via emitEvent)
|
|
452
|
+
this.emitEvent({
|
|
453
|
+
type: 'childDetached',
|
|
454
|
+
parentId: this.id,
|
|
455
|
+
childId: child.id,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Emit an event to all root observers
|
|
460
|
+
* @side effects Pushes event to node.events array and notifies all registered observers.
|
|
461
|
+
* May trigger treeUpdated notifications for specific event types.
|
|
462
|
+
*/
|
|
463
|
+
emitEvent(event) {
|
|
464
|
+
// Store event in history FIRST (for replay functionality) - only if enabled
|
|
465
|
+
if (this.isEventHistoryEnabled()) {
|
|
466
|
+
this.#eventHistory.push({ event, insertedAt: Date.now() });
|
|
467
|
+
this.trimEventHistory();
|
|
468
|
+
}
|
|
469
|
+
this.node.events.push(event);
|
|
470
|
+
const observers = this.getRootObservers();
|
|
471
|
+
for (const obs of observers) {
|
|
472
|
+
try {
|
|
473
|
+
obs.onEvent(event);
|
|
474
|
+
// Also notify tree changed for tree update events
|
|
475
|
+
if (event.type === 'treeUpdated' || event.type === 'childAttached' || event.type === 'childDetached') {
|
|
476
|
+
obs.onTreeChanged(this.getRoot().node);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
catch (err) {
|
|
480
|
+
this.logger.error('Observer onEvent error', { error: err, eventType: event.type });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Replay historical events to an observer
|
|
486
|
+
*
|
|
487
|
+
* **Strategy:**
|
|
488
|
+
* 1. Start with event history array
|
|
489
|
+
* 2. Filter by timestamp if `since` is provided
|
|
490
|
+
* 3. Limit events if `limit` is provided
|
|
491
|
+
* 4. Call observer.onEvent() for each event
|
|
492
|
+
* 5. Handle observer errors gracefully
|
|
493
|
+
*
|
|
494
|
+
* **Performance:** O(n) where n = number of events in history
|
|
495
|
+
*
|
|
496
|
+
* **Timestamp Handling:**
|
|
497
|
+
* - Events with timestamps: stepRetry, stepRestarted, invalidResponse
|
|
498
|
+
* - Events without timestamps: Always included (considered timeless)
|
|
499
|
+
* - Filter applies only to events with timestamp field
|
|
500
|
+
*
|
|
501
|
+
* **Order of Operations:** Filter first, then limit (more efficient)
|
|
502
|
+
*
|
|
503
|
+
* **Use Case:**
|
|
504
|
+
* - Catch up new observers to current state
|
|
505
|
+
* - Debug by replaying events to diagnostic observers
|
|
506
|
+
* - Test scenarios by replaying historical events
|
|
507
|
+
*
|
|
508
|
+
* @param observer - The observer to replay events to
|
|
509
|
+
* @param options - Optional replay configuration
|
|
510
|
+
* @param options.since - Only replay events after this timestamp (ms since epoch)
|
|
511
|
+
* @param options.limit - Maximum number of events to replay
|
|
512
|
+
*
|
|
513
|
+
* @example Replay all events to new observer
|
|
514
|
+
* ```ts
|
|
515
|
+
* const observer = {
|
|
516
|
+
* onLog: () => {},
|
|
517
|
+
* onEvent: (e) => console.log(e.type),
|
|
518
|
+
* onStateUpdated: () => {},
|
|
519
|
+
* onTreeChanged: () => {},
|
|
520
|
+
* };
|
|
521
|
+
* workflow.replayEvents(observer);
|
|
522
|
+
* ```
|
|
523
|
+
*
|
|
524
|
+
* @example Replay last 10 events
|
|
525
|
+
* ```ts
|
|
526
|
+
* workflow.replayEvents(observer, { limit: 10 });
|
|
527
|
+
* ```
|
|
528
|
+
*
|
|
529
|
+
* @example Replay events from last 5 minutes
|
|
530
|
+
* ```ts
|
|
531
|
+
* const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
|
|
532
|
+
* workflow.replayEvents(observer, { since: fiveMinutesAgo });
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
replayEvents(observer, options) {
|
|
536
|
+
// Extract events from entries
|
|
537
|
+
let events = this.#eventHistory.map(entry => entry.event);
|
|
538
|
+
// Filter by timestamp if provided
|
|
539
|
+
if (options?.since !== undefined) {
|
|
540
|
+
events = events.filter(event => {
|
|
541
|
+
// Extract timestamp from events that have it
|
|
542
|
+
const timestamp = event.type === 'stepRetry' ? event.timestamp :
|
|
543
|
+
event.type === 'stepRestarted' ? event.timestamp :
|
|
544
|
+
event.type === 'invalidResponse' ? event.timestamp :
|
|
545
|
+
undefined;
|
|
546
|
+
// Include events without timestamp or events after since
|
|
547
|
+
return timestamp === undefined || timestamp >= options.since;
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
// Apply limit if provided
|
|
551
|
+
if (options?.limit !== undefined) {
|
|
552
|
+
events = events.slice(0, options.limit);
|
|
553
|
+
}
|
|
554
|
+
// Replay events to observer
|
|
555
|
+
for (const event of events) {
|
|
556
|
+
try {
|
|
557
|
+
observer.onEvent(event);
|
|
558
|
+
}
|
|
559
|
+
catch (err) {
|
|
560
|
+
this.logger.error('Observer replay error', { error: err, eventType: event.type });
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Clear the event history array
|
|
566
|
+
*
|
|
567
|
+
* **Strategy:**
|
|
568
|
+
* - Reassign #eventHistory to empty array
|
|
569
|
+
* - Frees memory by discarding all stored events
|
|
570
|
+
* - Events in node.events are preserved
|
|
571
|
+
*
|
|
572
|
+
* **Use Case:**
|
|
573
|
+
* - Free memory after workflow completes
|
|
574
|
+
* - Reset history between test runs
|
|
575
|
+
* - Prevent memory leaks in long-running workflows
|
|
576
|
+
*
|
|
577
|
+
* **Side Effects:**
|
|
578
|
+
* - Frees memory for discarded events
|
|
579
|
+
* - Future replayEvents() calls will return empty
|
|
580
|
+
* - Does NOT affect node.events array
|
|
581
|
+
*
|
|
582
|
+
* @example Clear history after workflow completes
|
|
583
|
+
* ```ts
|
|
584
|
+
* await workflow.run();
|
|
585
|
+
* workflow.clearEventHistory(); // Free memory
|
|
586
|
+
* ```
|
|
587
|
+
*/
|
|
588
|
+
clearEventHistory() {
|
|
589
|
+
this.#eventHistory = [];
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Capture and emit a state snapshot
|
|
593
|
+
* @side effects Updates node.stateSnapshot, notifies observers via onStateUpdated callback,
|
|
594
|
+
* emits snapshot event, and triggers treeUpdated event for debugger.
|
|
595
|
+
*/
|
|
596
|
+
snapshotState() {
|
|
597
|
+
const snapshot = getObservedState(this);
|
|
598
|
+
this.node.stateSnapshot = snapshot;
|
|
599
|
+
// Notify observers
|
|
600
|
+
const observers = this.getRootObservers();
|
|
601
|
+
for (const obs of observers) {
|
|
602
|
+
try {
|
|
603
|
+
obs.onStateUpdated(this.node);
|
|
604
|
+
}
|
|
605
|
+
catch (err) {
|
|
606
|
+
this.logger.error('Observer onStateUpdated error', { error: err, nodeId: this.node.id });
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
// Emit snapshot event
|
|
610
|
+
this.emitEvent({
|
|
611
|
+
type: 'stateSnapshot',
|
|
612
|
+
node: this.node,
|
|
613
|
+
});
|
|
614
|
+
// Emit treeUpdated event to trigger tree debugger rebuild
|
|
615
|
+
this.emitEvent({ type: 'treeUpdated', root: this.getRoot().node });
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Restart a specific step with state restoration and retry tracking
|
|
619
|
+
*
|
|
620
|
+
* This method enables manual step restart from parent workflows. It validates
|
|
621
|
+
* the step exists, checks retry limits, optionally restores state, re-executes
|
|
622
|
+
* the step method, and emits a stepRestarted event for observability.
|
|
623
|
+
*
|
|
624
|
+
* @param stepName - The name of the step method to restart
|
|
625
|
+
* @param options - Optional configuration for the restart attempt
|
|
626
|
+
* @returns The result of the step execution
|
|
627
|
+
* @throws {WorkflowError} When step is not found or max retries exceeded
|
|
628
|
+
*
|
|
629
|
+
* @example Restart a step with default retry tracking
|
|
630
|
+
* ```ts
|
|
631
|
+
* class MyWorkflow extends Workflow {
|
|
632
|
+
* @Step({ restartable: true })
|
|
633
|
+
* async myStep() { return 'result'; }
|
|
634
|
+
*
|
|
635
|
+
* async run() {
|
|
636
|
+
* const result = await this.restartStep('myStep');
|
|
637
|
+
* }
|
|
638
|
+
* }
|
|
639
|
+
* ```
|
|
640
|
+
*
|
|
641
|
+
* @example Restart with explicit retry count and state override
|
|
642
|
+
* ```ts
|
|
643
|
+
* await this.restartStep('failingStep', {
|
|
644
|
+
* retryCount: 1,
|
|
645
|
+
* maxRetries: 3,
|
|
646
|
+
* stateOverride: { counter: 5 }
|
|
647
|
+
* });
|
|
648
|
+
* ```
|
|
649
|
+
*/
|
|
650
|
+
async restartStep(stepName, options) {
|
|
651
|
+
// Calculate the retry count for this attempt
|
|
652
|
+
const retryCount = (options?.retryCount ?? 0) + 1;
|
|
653
|
+
const maxRetries = options?.maxRetries ?? 3;
|
|
654
|
+
// Check retry limit
|
|
655
|
+
if (retryCount > maxRetries) {
|
|
656
|
+
const error = {
|
|
657
|
+
message: `Max retries (${maxRetries}) exceeded for step '${stepName}'`,
|
|
658
|
+
original: new Error('Max retries exceeded'),
|
|
659
|
+
workflowId: this.id,
|
|
660
|
+
state: getObservedState(this),
|
|
661
|
+
logs: [...this.node.logs],
|
|
662
|
+
};
|
|
663
|
+
throw error;
|
|
664
|
+
}
|
|
665
|
+
// Verify the step method exists and is callable
|
|
666
|
+
const method = this[stepName];
|
|
667
|
+
if (typeof method !== 'function') {
|
|
668
|
+
const error = {
|
|
669
|
+
message: `Step '${stepName}' not found`,
|
|
670
|
+
original: new Error('Step not found'),
|
|
671
|
+
workflowId: this.id,
|
|
672
|
+
state: getObservedState(this),
|
|
673
|
+
logs: [...this.node.logs],
|
|
674
|
+
};
|
|
675
|
+
throw error;
|
|
676
|
+
}
|
|
677
|
+
// Restore state - use override if provided, otherwise capture current state
|
|
678
|
+
let restoredState;
|
|
679
|
+
if (options?.stateOverride !== undefined) {
|
|
680
|
+
restoredState = options.stateOverride;
|
|
681
|
+
// For state override, we'd ideally restore the state here
|
|
682
|
+
// Since no restoreState() method exists, stateOverride is primarily for the event payload
|
|
683
|
+
// and any future state restoration implementation
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
// Capture current state as the restored state
|
|
687
|
+
this.snapshotState();
|
|
688
|
+
restoredState = this.node.stateSnapshot ?? {};
|
|
689
|
+
}
|
|
690
|
+
// Execute the step method
|
|
691
|
+
const result = await method.call(this);
|
|
692
|
+
// Emit stepRestarted event
|
|
693
|
+
this.emitEvent({
|
|
694
|
+
type: 'stepRestarted',
|
|
695
|
+
node: this.node,
|
|
696
|
+
stepName,
|
|
697
|
+
retryCount,
|
|
698
|
+
restoredState,
|
|
699
|
+
timestamp: Date.now(),
|
|
700
|
+
});
|
|
701
|
+
return result;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Analyze a WorkflowError from a child workflow and determine restart action
|
|
705
|
+
*
|
|
706
|
+
* This method enables parent workflows to make intelligent decisions about child
|
|
707
|
+
* workflow failures by analyzing the error and step metadata to determine whether
|
|
708
|
+
* to retry the child, abort the parent, or rebuild the execution plan.
|
|
709
|
+
*
|
|
710
|
+
* **Analysis Flow:**
|
|
711
|
+
* 1. Check `error.original?.recoverable` - if false, return 'abort'
|
|
712
|
+
* 2. Extract stepName from error metadata (if available)
|
|
713
|
+
* 3. Retrieve step metadata from stepMetadata map (if exists)
|
|
714
|
+
* 4. Check if step is marked as restartable - if not, return 'abort'
|
|
715
|
+
* 5. Use `analyzeErrorForRestart` utility to check retry criteria
|
|
716
|
+
* 6. Return 'retry' if any criteria match, otherwise 'abort'
|
|
717
|
+
*
|
|
718
|
+
* **Integration with restartStep:**
|
|
719
|
+
* This method is designed to be used alongside `restartStep()`:
|
|
720
|
+
* - Call `analyzeError()` to get the decision
|
|
721
|
+
* - If 'retry', call `restartStep(stepName)` to execute
|
|
722
|
+
* - If 'abort', throw the error or return early
|
|
723
|
+
* - If 'rebuild', trigger plan rebuild logic
|
|
724
|
+
*
|
|
725
|
+
* @param error - The WorkflowError to analyze (typically from child workflow)
|
|
726
|
+
* @returns The recommended action: 'retry', 'abort', or 'rebuild'
|
|
727
|
+
*
|
|
728
|
+
* @example Parent workflow error handling
|
|
729
|
+
* ```ts
|
|
730
|
+
* class ParentWorkflow extends Workflow {
|
|
731
|
+
* @Step({ restartable: true, retryOn: [{ code: 'TIMEOUT' }] })
|
|
732
|
+
* async childWorkflow(): Promise<void> {
|
|
733
|
+
* // Child logic that may fail
|
|
734
|
+
* }
|
|
735
|
+
*
|
|
736
|
+
* async run(): Promise<void> {
|
|
737
|
+
* try {
|
|
738
|
+
* await this.childWorkflow();
|
|
739
|
+
* } catch (err) {
|
|
740
|
+
* const error = err as WorkflowError;
|
|
741
|
+
* const action = this.analyzeError(error);
|
|
742
|
+
*
|
|
743
|
+
* if (action === 'retry') {
|
|
744
|
+
* await this.restartStep('childWorkflow');
|
|
745
|
+
* } else if (action === 'abort') {
|
|
746
|
+
* throw error;
|
|
747
|
+
* } else if (action === 'rebuild') {
|
|
748
|
+
* // Trigger plan rebuild logic
|
|
749
|
+
* }
|
|
750
|
+
* }
|
|
751
|
+
* }
|
|
752
|
+
* }
|
|
753
|
+
* ```
|
|
754
|
+
*
|
|
755
|
+
* @example Analyze error from child workflow event
|
|
756
|
+
* ```ts
|
|
757
|
+
* class ParentWorkflow extends Workflow {
|
|
758
|
+
* private lastError: WorkflowError | null = null;
|
|
759
|
+
*
|
|
760
|
+
* async run(): Promise<void> {
|
|
761
|
+
* // Subscribe to error events
|
|
762
|
+
* this.on('error', (event) => {
|
|
763
|
+
* this.lastError = event.error;
|
|
764
|
+
* });
|
|
765
|
+
*
|
|
766
|
+
* // Later, analyze the error
|
|
767
|
+
* if (this.lastError) {
|
|
768
|
+
* const action = this.analyzeError(this.lastError);
|
|
769
|
+
* // Take action based on decision
|
|
770
|
+
* }
|
|
771
|
+
* }
|
|
772
|
+
* }
|
|
773
|
+
* ```
|
|
774
|
+
*
|
|
775
|
+
* @remarks
|
|
776
|
+
* **Known Limitation:**
|
|
777
|
+
* The `stepMetadata` map is not yet populated by the `@Step` decorator.
|
|
778
|
+
* This method will return 'abort' if stepMetadata is not available or the step
|
|
779
|
+
* is not found. This will be improved in a future update to the decorator.
|
|
780
|
+
*
|
|
781
|
+
* **Error Metadata:**
|
|
782
|
+
* The stepName is extracted from `error.state?.stepName`. Ensure child
|
|
783
|
+
* workflows populate this field when creating WorkflowError instances.
|
|
784
|
+
*
|
|
785
|
+
* @see {@link restartStep} - For executing a retry after analysis
|
|
786
|
+
* @see {@link analyzeErrorForRestart} - For the underlying utility function
|
|
787
|
+
*/
|
|
788
|
+
analyzeError(error) {
|
|
789
|
+
// STEP 1: Check recoverable flag
|
|
790
|
+
const original = error.original;
|
|
791
|
+
if (original && 'recoverable' in original && !original.recoverable) {
|
|
792
|
+
return 'abort';
|
|
793
|
+
}
|
|
794
|
+
// STEP 2: Extract stepName from error metadata
|
|
795
|
+
// GOTCHA: WorkflowError doesn't have stepName property
|
|
796
|
+
// Check if error.state or error.original has step information
|
|
797
|
+
const stepName = error.state?.stepName;
|
|
798
|
+
if (!stepName) {
|
|
799
|
+
return 'abort'; // Can't determine which step failed
|
|
800
|
+
}
|
|
801
|
+
// STEP 3: Get step metadata with graceful handling
|
|
802
|
+
// CRITICAL: stepMetadata may not exist yet (decorator doesn't store it)
|
|
803
|
+
if (!('stepMetadata' in this)) {
|
|
804
|
+
return 'abort'; // No metadata available
|
|
805
|
+
}
|
|
806
|
+
const stepMeta = this.stepMetadata.get(stepName);
|
|
807
|
+
if (!stepMeta) {
|
|
808
|
+
return 'abort'; // Step not found in metadata
|
|
809
|
+
}
|
|
810
|
+
// STEP 4: Check if step is restartable
|
|
811
|
+
if (!stepMeta.options?.restartable) {
|
|
812
|
+
return 'abort'; // Step not marked as restartable
|
|
813
|
+
}
|
|
814
|
+
// STEP 5: Use analyzeErrorForRestart for criterion matching
|
|
815
|
+
const analysis = analyzeErrorForRestart(error, stepMeta.options);
|
|
816
|
+
if (analysis.shouldRestart) {
|
|
817
|
+
return 'retry';
|
|
818
|
+
}
|
|
819
|
+
// STEP 6: Default to abort
|
|
820
|
+
return 'abort';
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Validate an agent response at the workflow level
|
|
824
|
+
*
|
|
825
|
+
* This method enables parent workflows to validate agent responses
|
|
826
|
+
* before processing them. It follows the same validation pattern as
|
|
827
|
+
* Agent.validateResponse() but emits events and creates WorkflowError
|
|
828
|
+
* for workflow-level error handling.
|
|
829
|
+
*
|
|
830
|
+
* @template T - The type of response data
|
|
831
|
+
* @param response - The AgentResponse to validate
|
|
832
|
+
* @param agentId - Identifier of the agent that produced the response
|
|
833
|
+
* @param dataSchema - Optional Zod schema for response data (defaults to z.unknown())
|
|
834
|
+
* @returns true if validation passes, false if validation fails
|
|
835
|
+
*
|
|
836
|
+
* @example Validate response from child workflow
|
|
837
|
+
* ```ts
|
|
838
|
+
* class ParentWorkflow extends Workflow {
|
|
839
|
+
* @Step()
|
|
840
|
+
* async processChildResult() {
|
|
841
|
+
* const response = await this.childWorkflow.run();
|
|
842
|
+
*
|
|
843
|
+
* if (!this.validateAgentResponse(response, this.childWorkflow.agent.id)) {
|
|
844
|
+
* // Handle validation failure
|
|
845
|
+
* const action = this.analyzeError(this.lastError);
|
|
846
|
+
* if (action === 'retry') {
|
|
847
|
+
* return await this.restartStep('processChildResult');
|
|
848
|
+
* }
|
|
849
|
+
* }
|
|
850
|
+
*
|
|
851
|
+
* // Process valid response
|
|
852
|
+
* return response.data;
|
|
853
|
+
* }
|
|
854
|
+
* }
|
|
855
|
+
* ```
|
|
856
|
+
*/
|
|
857
|
+
validateAgentResponse(response, agentId, dataSchema = z.unknown()) {
|
|
858
|
+
// Call shared utility for validation
|
|
859
|
+
const result = validateAgentResponse(response, dataSchema);
|
|
860
|
+
if (result.valid) {
|
|
861
|
+
// Response is valid
|
|
862
|
+
return true;
|
|
863
|
+
}
|
|
864
|
+
// Validation failed - emit event and create error
|
|
865
|
+
const zodError = result.errors; // Safe: errors exists when valid is false
|
|
866
|
+
// Emit invalidResponse event
|
|
867
|
+
this.emitEvent({
|
|
868
|
+
type: 'invalidResponse',
|
|
869
|
+
node: this.node,
|
|
870
|
+
response,
|
|
871
|
+
agentId,
|
|
872
|
+
errors: zodError,
|
|
873
|
+
timestamp: Date.now(),
|
|
874
|
+
});
|
|
875
|
+
// Create WorkflowError with INVALID_RESPONSE_FORMAT context
|
|
876
|
+
const validationError = {
|
|
877
|
+
message: `Agent response validation failed for agent '${agentId}'`,
|
|
878
|
+
original: zodError,
|
|
879
|
+
workflowId: this.id,
|
|
880
|
+
stack: zodError.stack,
|
|
881
|
+
state: getObservedState(this),
|
|
882
|
+
logs: [...this.node.logs],
|
|
883
|
+
};
|
|
884
|
+
// Store error for potential use by analyzeError/restartStep
|
|
885
|
+
// Note: Consider adding lastError property to Workflow class if not exists
|
|
886
|
+
// For now, emit event and return false
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Update workflow status and sync with node
|
|
891
|
+
*/
|
|
892
|
+
setStatus(status) {
|
|
893
|
+
this.status = status;
|
|
894
|
+
this.node.status = status;
|
|
895
|
+
this.emitEvent({ type: 'treeUpdated', root: this.getRoot().node });
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Get the node representation of this workflow
|
|
899
|
+
*/
|
|
900
|
+
getNode() {
|
|
901
|
+
return this.node;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Run the workflow
|
|
905
|
+
*
|
|
906
|
+
* For functional workflows (created with executor), runs the executor function.
|
|
907
|
+
* For class-based workflows (subclasses), this should be overridden.
|
|
908
|
+
*
|
|
909
|
+
* @returns Workflow result
|
|
910
|
+
*/
|
|
911
|
+
async run(..._args) {
|
|
912
|
+
if (this.executor) {
|
|
913
|
+
return this.runFunctional();
|
|
914
|
+
}
|
|
915
|
+
// Class-based workflows must override this method
|
|
916
|
+
throw new Error('Workflow.run() must be overridden in subclass or provide executor in constructor');
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Run a functional workflow with context
|
|
920
|
+
*/
|
|
921
|
+
async runFunctional() {
|
|
922
|
+
if (!this.executor) {
|
|
923
|
+
throw new Error('No executor provided');
|
|
924
|
+
}
|
|
925
|
+
const startTime = Date.now();
|
|
926
|
+
this.setStatus('running');
|
|
927
|
+
// Reset error collection state
|
|
928
|
+
this.collectedErrors = [];
|
|
929
|
+
this.operationCounter = 0;
|
|
930
|
+
// Create workflow context with error merge strategy
|
|
931
|
+
const ctx = createWorkflowContext(this, this.parent?.id, this.config.enableReflection ? { enabled: true } : undefined, this.config.autoValidateResponses ?? true, this.config.errorMergeStrategy);
|
|
932
|
+
try {
|
|
933
|
+
const result = await this.executor(ctx);
|
|
934
|
+
// Check if we should merge collected errors
|
|
935
|
+
if (this.collectedErrors.length > 0) {
|
|
936
|
+
if (this.config.errorMergeStrategy?.enabled) {
|
|
937
|
+
// Merge errors
|
|
938
|
+
const mergedError = this.config.errorMergeStrategy?.combine
|
|
939
|
+
? this.config.errorMergeStrategy.combine(this.collectedErrors)
|
|
940
|
+
: mergeWorkflowErrors(this.collectedErrors, this.config.name || this.id, this.id, this.operationCounter);
|
|
941
|
+
// Emit error event with merged error
|
|
942
|
+
this.emitEvent({
|
|
943
|
+
type: 'error',
|
|
944
|
+
node: this.node,
|
|
945
|
+
error: mergedError,
|
|
946
|
+
});
|
|
947
|
+
// Throw merged error
|
|
948
|
+
throw mergedError;
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
// Throw first error (backward compatibility)
|
|
952
|
+
throw this.collectedErrors[0];
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
this.setStatus('completed');
|
|
956
|
+
return {
|
|
957
|
+
data: result,
|
|
958
|
+
node: this.node,
|
|
959
|
+
duration: Date.now() - startTime,
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
catch (error) {
|
|
963
|
+
// Handle errors thrown directly (not collected)
|
|
964
|
+
if (!this.config.errorMergeStrategy?.enabled) {
|
|
965
|
+
this.setStatus('failed');
|
|
966
|
+
this.emitEvent({
|
|
967
|
+
type: 'error',
|
|
968
|
+
node: this.node,
|
|
969
|
+
error: {
|
|
970
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
971
|
+
original: error,
|
|
972
|
+
workflowId: this.id,
|
|
973
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
974
|
+
state: getObservedState(this),
|
|
975
|
+
logs: [...this.node.logs],
|
|
976
|
+
},
|
|
977
|
+
});
|
|
978
|
+
throw error;
|
|
979
|
+
}
|
|
980
|
+
// If in collection mode, error should have been collected already
|
|
981
|
+
// Re-throw if it somehow escaped collection
|
|
982
|
+
throw error;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
//# sourceMappingURL=workflow.js.map
|