@exaudeus/workrail 3.14.0 → 3.16.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.
Files changed (156) hide show
  1. package/dist/application/services/validation-engine.js +4 -9
  2. package/dist/application/services/workflow-compiler.js +4 -6
  3. package/dist/application/services/workflow-service.d.ts +2 -0
  4. package/dist/application/services/workflow-service.js +3 -0
  5. package/dist/console/assets/index-BE5PAgPO.js +28 -0
  6. package/dist/console/assets/index-BZNM03t1.css +1 -0
  7. package/dist/console/index.html +2 -2
  8. package/dist/engine/engine-factory.js +2 -2
  9. package/dist/engine/types.d.ts +1 -1
  10. package/dist/env-flags.d.ts +1 -0
  11. package/dist/env-flags.js +4 -0
  12. package/dist/infrastructure/session/HttpServer.d.ts +3 -3
  13. package/dist/infrastructure/session/HttpServer.js +68 -74
  14. package/dist/infrastructure/storage/caching-workflow-storage.d.ts +2 -0
  15. package/dist/infrastructure/storage/caching-workflow-storage.js +15 -6
  16. package/dist/infrastructure/storage/file-workflow-storage.js +3 -4
  17. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +9 -8
  18. package/dist/manifest.json +283 -219
  19. package/dist/mcp/assert-output.d.ts +37 -0
  20. package/dist/mcp/assert-output.js +52 -0
  21. package/dist/mcp/boundary-coercion.d.ts +1 -0
  22. package/dist/mcp/boundary-coercion.js +44 -0
  23. package/dist/mcp/dev-mode.d.ts +1 -0
  24. package/dist/mcp/dev-mode.js +4 -0
  25. package/dist/mcp/handler-factory.js +12 -9
  26. package/dist/mcp/handlers/session.js +8 -9
  27. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +5 -0
  28. package/dist/mcp/handlers/shared/request-workflow-reader.js +47 -2
  29. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.d.ts +1 -1
  30. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.js +4 -5
  31. package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +2 -0
  32. package/dist/mcp/handlers/v2-advance-core/event-builders.js +6 -6
  33. package/dist/mcp/handlers/v2-advance-core/index.d.ts +2 -0
  34. package/dist/mcp/handlers/v2-advance-core/index.js +5 -4
  35. package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +2 -0
  36. package/dist/mcp/handlers/v2-advance-core/input-validation.js +32 -9
  37. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
  38. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +2 -2
  39. package/dist/mcp/handlers/v2-advance-core/outcome-success.d.ts +2 -0
  40. package/dist/mcp/handlers/v2-advance-core/outcome-success.js +1 -1
  41. package/dist/mcp/handlers/v2-checkpoint.d.ts +1 -1
  42. package/dist/mcp/handlers/v2-checkpoint.js +5 -6
  43. package/dist/mcp/handlers/v2-execution/advance.d.ts +4 -2
  44. package/dist/mcp/handlers/v2-execution/advance.js +5 -7
  45. package/dist/mcp/handlers/v2-execution/continue-advance.js +56 -26
  46. package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -1
  47. package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +9 -9
  48. package/dist/mcp/handlers/v2-execution/replay.d.ts +6 -4
  49. package/dist/mcp/handlers/v2-execution/replay.js +47 -30
  50. package/dist/mcp/handlers/v2-execution/start.d.ts +3 -3
  51. package/dist/mcp/handlers/v2-execution/start.js +31 -12
  52. package/dist/mcp/handlers/v2-execution/workflow-object-cache.d.ts +5 -0
  53. package/dist/mcp/handlers/v2-execution/workflow-object-cache.js +19 -0
  54. package/dist/mcp/handlers/v2-execution-helpers.d.ts +1 -0
  55. package/dist/mcp/handlers/v2-execution-helpers.js +23 -7
  56. package/dist/mcp/handlers/v2-resume.d.ts +1 -1
  57. package/dist/mcp/handlers/v2-resume.js +3 -4
  58. package/dist/mcp/handlers/v2-state-conversion.js +5 -1
  59. package/dist/mcp/handlers/v2-workflow.d.ts +100 -0
  60. package/dist/mcp/handlers/v2-workflow.js +155 -31
  61. package/dist/mcp/handlers/workflow.d.ts +2 -5
  62. package/dist/mcp/handlers/workflow.js +15 -12
  63. package/dist/mcp/output-schemas.d.ts +123 -29
  64. package/dist/mcp/output-schemas.js +36 -18
  65. package/dist/mcp/server.js +70 -5
  66. package/dist/mcp/tool-call-timing.d.ts +24 -0
  67. package/dist/mcp/tool-call-timing.js +85 -0
  68. package/dist/mcp/tool-descriptions.js +17 -9
  69. package/dist/mcp/transports/http-entry.js +3 -2
  70. package/dist/mcp/transports/http-listener.d.ts +1 -0
  71. package/dist/mcp/transports/http-listener.js +25 -0
  72. package/dist/mcp/transports/shutdown-hooks.d.ts +4 -1
  73. package/dist/mcp/transports/shutdown-hooks.js +3 -2
  74. package/dist/mcp/transports/stdio-entry.js +6 -28
  75. package/dist/mcp/v2/tools.d.ts +6 -0
  76. package/dist/mcp/v2/tools.js +2 -0
  77. package/dist/mcp/v2-response-formatter.js +2 -4
  78. package/dist/mcp/validation/schema-introspection.d.ts +1 -0
  79. package/dist/mcp/validation/schema-introspection.js +15 -5
  80. package/dist/mcp/validation/suggestion-generator.js +2 -2
  81. package/dist/mcp/workflow-protocol-contracts.js +5 -1
  82. package/dist/runtime/adapters/node-process-signals.d.ts +1 -0
  83. package/dist/runtime/adapters/node-process-signals.js +5 -0
  84. package/dist/runtime/adapters/noop-process-signals.d.ts +1 -0
  85. package/dist/runtime/adapters/noop-process-signals.js +2 -0
  86. package/dist/runtime/ports/process-signals.d.ts +1 -0
  87. package/dist/types/workflow-definition.d.ts +3 -2
  88. package/dist/types/workflow.d.ts +3 -0
  89. package/dist/types/workflow.js +35 -26
  90. package/dist/v2/durable-core/domain/context-template-resolver.js +2 -2
  91. package/dist/v2/durable-core/domain/function-definition-expander.js +2 -17
  92. package/dist/v2/durable-core/domain/prompt-renderer.d.ts +1 -0
  93. package/dist/v2/durable-core/domain/prompt-renderer.js +23 -18
  94. package/dist/v2/durable-core/domain/recap-recovery.js +23 -16
  95. package/dist/v2/durable-core/domain/retrieval-contract.js +13 -7
  96. package/dist/v2/durable-core/session-index.d.ts +22 -0
  97. package/dist/v2/durable-core/session-index.js +58 -0
  98. package/dist/v2/durable-core/sorted-event-log.d.ts +6 -0
  99. package/dist/v2/durable-core/sorted-event-log.js +15 -0
  100. package/dist/v2/infra/local/fs/index.js +8 -8
  101. package/dist/v2/infra/local/session-store/index.d.ts +1 -1
  102. package/dist/v2/infra/local/session-store/index.js +71 -61
  103. package/dist/v2/infra/local/session-summary-provider/index.js +9 -4
  104. package/dist/v2/infra/local/snapshot-store/index.js +2 -1
  105. package/dist/v2/infra/local/workspace-anchor/index.js +4 -1
  106. package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
  107. package/dist/v2/projections/assessment-consequences.d.ts +2 -1
  108. package/dist/v2/projections/assessment-consequences.js +0 -5
  109. package/dist/v2/projections/assessments.d.ts +2 -1
  110. package/dist/v2/projections/assessments.js +2 -4
  111. package/dist/v2/projections/gaps.d.ts +2 -1
  112. package/dist/v2/projections/gaps.js +0 -5
  113. package/dist/v2/projections/preferences.d.ts +2 -1
  114. package/dist/v2/projections/preferences.js +0 -5
  115. package/dist/v2/projections/run-context.d.ts +2 -2
  116. package/dist/v2/projections/run-context.js +0 -5
  117. package/dist/v2/projections/run-dag.js +7 -1
  118. package/dist/v2/projections/run-execution-trace.d.ts +8 -0
  119. package/dist/v2/projections/run-execution-trace.js +124 -0
  120. package/dist/v2/projections/run-status-signals.d.ts +2 -2
  121. package/dist/v2/usecases/console-routes.d.ts +3 -1
  122. package/dist/v2/usecases/console-routes.js +149 -3
  123. package/dist/v2/usecases/console-service.d.ts +2 -0
  124. package/dist/v2/usecases/console-service.js +87 -26
  125. package/dist/v2/usecases/console-types.d.ts +65 -0
  126. package/dist/v2/usecases/worktree-service.js +87 -8
  127. package/package.json +7 -6
  128. package/spec/authoring-spec.json +82 -1
  129. package/spec/workflow-tags.json +132 -0
  130. package/spec/workflow.schema.json +21 -11
  131. package/workflows/adaptive-ticket-creation.json +33 -8
  132. package/workflows/architecture-scalability-audit.json +50 -9
  133. package/workflows/bug-investigation.agentic.v2.json +43 -14
  134. package/workflows/coding-task-workflow-agentic.json +57 -38
  135. package/workflows/coding-task-workflow-agentic.lean.v2.json +129 -34
  136. package/workflows/coding-task-workflow-agentic.v2.json +97 -30
  137. package/workflows/cross-platform-code-conversion.v2.json +175 -48
  138. package/workflows/document-creation-workflow.json +49 -12
  139. package/workflows/documentation-update-workflow.json +9 -2
  140. package/workflows/intelligent-test-case-generation.json +9 -2
  141. package/workflows/learner-centered-course-workflow.json +273 -266
  142. package/workflows/mr-review-workflow.agentic.v2.json +88 -14
  143. package/workflows/personal-learning-materials-creation-branched.json +181 -174
  144. package/workflows/presentation-creation.json +167 -160
  145. package/workflows/production-readiness-audit.json +61 -15
  146. package/workflows/relocation-workflow-us.json +21 -5
  147. package/workflows/routines/tension-driven-design.json +1 -1
  148. package/workflows/scoped-documentation-workflow.json +9 -2
  149. package/workflows/test-artifact-loop-control.json +1 -2
  150. package/workflows/ui-ux-design-workflow.json +334 -0
  151. package/workflows/workflow-diagnose-environment.json +7 -1
  152. package/workflows/workflow-for-workflows.json +514 -484
  153. package/workflows/workflow-for-workflows.v2.json +55 -11
  154. package/workflows/wr.discovery.json +118 -29
  155. package/dist/console/assets/index-DW78t31j.css +0 -1
  156. package/dist/console/assets/index-EsSXrC_a.js +0 -28
@@ -0,0 +1 @@
1
+ .react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--leading-snug:1.375;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-1\/2{top:50%}.top-3{top:calc(var(--spacing) * 3)}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.bottom-3{bottom:calc(var(--spacing) * 3)}.z-20{z-index:20}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-1{margin-inline:calc(var(--spacing) * 1)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-\[3px\]{margin-top:3px}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-1{height:calc(var(--spacing) * 1)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-\[506px\]{height:506px}.h-full{height:100%}.h-px{height:1px}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-screen{min-height:100vh}.w-0\.5{width:calc(var(--spacing) * .5)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-1\/2{width:50%}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing) * 3)}.w-4\/6{width:66.6667%}.w-5\/6{width:83.3333%}.w-6{width:calc(var(--spacing) * 6)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-64{width:calc(var(--spacing) * 64)}.w-\[560px\]{width:560px}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.max-w-\[360px\]{max-width:360px}.max-w-\[calc\(92vw-12px\)\]{max-width:calc(92vw - 12px)}.max-w-lg{max-width:var(--container-lg)}.max-w-prose{max-width:65ch}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[44px\]{min-width:44px}.min-w-\[120px\]{min-width:120px}.min-w-\[200px\]{min-width:200px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-8{gap:calc(var(--spacing) * 8)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-px>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(1px * var(--tw-space-y-reverse));margin-block-end:calc(1px * calc(1 - var(--tw-space-y-reverse)))}.gap-x-8{column-gap:calc(var(--spacing) * 8)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}.self-center{align-self:center}.self-stretch{align-self:stretch}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-\[rgba\(255\,255\,255\,0\.06\)\]{border-color:#ffffff0f}.border-\[var\(--blocked\)\]{border-color:var(--blocked)}.border-\[var\(--border\)\]{border-color:var(--border)}.border-blue-500\/20{border-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/20{border-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.border-orange-500\/20{border-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/20{border-color:color-mix(in oklab,var(--color-orange-500) 20%,transparent)}}.bg-\[rgba\(0\,0\,0\,0\.18\)\]{background-color:#0000002e}.bg-\[rgba\(10\,10\,10\,0\.38\)\]{background-color:#0a0a0a61}.bg-\[rgba\(255\,255\,255\,0\.02\)\]{background-color:#ffffff05}.bg-\[var\(--accent\)\]{background-color:var(--accent)}.bg-\[var\(--bg-card\)\]{background-color:var(--bg-card)}.bg-\[var\(--bg-primary\)\]{background-color:var(--bg-primary)}.bg-\[var\(--bg-secondary\)\]{background-color:var(--bg-secondary)}.bg-\[var\(--bg-tertiary\)\]{background-color:var(--bg-tertiary)}.bg-\[var\(--border\)\]{background-color:var(--border)}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500) 10%,transparent)}}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/10{background-color:color-mix(in oklab,var(--color-green-500) 10%,transparent)}}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/10{background-color:color-mix(in oklab,var(--color-orange-500) 10%,transparent)}}.bg-transparent{background-color:#0000}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.py-20{padding-block:calc(var(--spacing) * 20)}.py-32{padding-block:calc(var(--spacing) * 32)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.tracking-\[0\.18em\]{--tw-tracking:.18em;letter-spacing:.18em}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-words{overflow-wrap:break-word}.text-ellipsis{text-overflow:ellipsis}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[var\(--accent\)\]{color:var(--accent)}.text-\[var\(--error\)\]{color:var(--error)}.text-\[var\(--text-muted\)\]{color:var(--text-muted)}.text-\[var\(--text-primary\)\]{color:var(--text-primary)}.text-\[var\(--text-secondary\)\]{color:var(--text-secondary)}.text-\[var\(--warning\)\]{color:var(--warning)}.text-blue-400{color:var(--color-blue-400)}.text-green-400{color:var(--color-green-400)}.text-orange-400{color:var(--color-orange-400)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.placeholder-\[var\(--text-muted\)\]::placeholder{color:var(--text-muted)}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-\[var\(--accent\)\]{--tw-ring-color:var(--accent)}.ring-blue-500\/40{--tw-ring-color:#3080ff66}@supports (color:color-mix(in lab,red,red)){.ring-blue-500\/40{--tw-ring-color:color-mix(in oklab, var(--color-blue-500) 40%, transparent)}}.ring-orange-500\/40{--tw-ring-color:#fe6e0066}@supports (color:color-mix(in lab,red,red)){.ring-orange-500\/40{--tw-ring-color:color-mix(in oklab, var(--color-orange-500) 40%, transparent)}}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.drop-shadow{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color,#0000000f));--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}@media(hover:hover){.group-hover\:text-\[var\(--accent\)\]:is(:where(.group):hover *){color:var(--accent)}.group-hover\:text-\[var\(--text-primary\)\]:is(:where(.group):hover *){color:var(--text-primary)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:border-\[var\(--accent\)\]:hover{border-color:var(--accent)}.hover\:border-\[var\(--accent-strong\)\]:hover{border-color:var(--accent-strong)}.hover\:border-\[var\(--blocked\)\]:hover{border-color:var(--blocked)}.hover\:bg-\[var\(--bg-card\)\]:hover{background-color:var(--bg-card)}.hover\:bg-\[var\(--bg-tertiary\)\]:hover{background-color:var(--bg-tertiary)}.hover\:bg-blue-500\/20:hover{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.hover\:bg-blue-500\/20:hover{background-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.hover\:bg-orange-500\/20:hover{background-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.hover\:bg-orange-500\/20:hover{background-color:color-mix(in oklab,var(--color-orange-500) 20%,transparent)}}.hover\:text-\[var\(--accent-hover\)\]:hover{color:var(--accent-hover)}.hover\:text-\[var\(--text-primary\)\]:hover{color:var(--text-primary)}.hover\:text-\[var\(--text-secondary\)\]:hover{color:var(--text-secondary)}}.focus\:border-\[var\(--accent\)\]:focus{border-color:var(--accent)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-\[var\(--accent\)\]:focus-visible{--tw-ring-color:var(--accent)}.focus-visible\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:block{display:block}}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:80rem){.xl\:grid-cols-\[minmax\(0\,1\.15fr\)_minmax\(0\,0\.85fr\)\]{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr)}}}:root{--bg-primary:#0a0a0a;--bg-secondary:#141414;--bg-card:#1a1a1a;--border:#2a2a2a;--text-primary:#fafafa;--text-secondary:#a0a0a0;--text-muted:#666;--accent:#3b82f6;--accent-hover:#2563eb;--accent-strong:#00f0ff;--success:#22c55e;--warning:#eab308;--error:#ef4444;--blocked:#f97316}body{background:var(--bg-primary);color:var(--text-primary);margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.workrail-minimap-node{opacity:1}*{box-sizing:border-box}.console-blueprint-grid{background-image:linear-gradient(#41577624 1px,#0000 1px),linear-gradient(90deg,#41577624 1px,#0000 1px);background-size:24px 24px}.lineage-scroll-surface{background:var(--bg-primary)}.lineage-scroll-surface .react-flow,.lineage-scroll-surface .react-flow__renderer,.lineage-scroll-surface .react-flow__viewport,.lineage-scroll-surface .react-flow__pane{background:0 0!important}.workrail-current-lineage-node{isolation:isolate;animation:3.2s ease-in-out infinite workrail-current-node-pulse;position:relative;box-shadow:inset 0 0 0 1px #00f0ff38,inset 0 0 22px #00f0ff12}.workrail-current-lineage-node:after{content:"";pointer-events:none;border:1px solid #00f0ff47;animation:3.2s ease-in-out infinite workrail-current-node-outline;position:absolute;inset:-1px}.workrail-current-lineage-edge .react-flow__edge-path{stroke-dasharray:7 9;animation:1.35s linear infinite workrail-current-edge-flow}.workrail-selected-lineage-node{isolation:isolate;position:relative;box-shadow:inset 0 0 0 1px #f4b34161,0 0 0 1px #f4b34147,0 0 18px #f4b3411a}@keyframes workrail-current-node-pulse{0%,to{filter:brightness();transform:translateZ(0)}50%{filter:brightness(1.1);transform:translateZ(0)}}@keyframes workrail-current-node-outline{0%,to{opacity:.34;box-shadow:0 0 #00f0ff00,inset 0 0 #00f0ff00}50%{opacity:.82;box-shadow:0 0 20px #00f0ff2e,inset 0 0 18px #00f0ff14}}@keyframes workrail-current-edge-flow{to{stroke-dashoffset:-32px}}.markdown-view{color:var(--text-secondary);word-break:break-word;font-size:.8125rem;line-height:1.6}.markdown-view h1,.markdown-view h2,.markdown-view h3,.markdown-view h4,.markdown-view h5,.markdown-view h6{color:var(--text-primary);margin:1em 0 .4em;font-weight:600;line-height:1.3}.markdown-view h1{font-size:1.1em}.markdown-view h2{font-size:1em}.markdown-view h3{font-size:.95em}.markdown-view h4,.markdown-view h5,.markdown-view h6{font-size:.9em}.markdown-view>:first-child{margin-top:0}.markdown-view p{margin:.5em 0}.markdown-view strong{color:var(--text-primary);font-weight:600}.markdown-view em{font-style:italic}.markdown-view a{color:var(--accent);text-underline-offset:2px;text-decoration:underline}.markdown-view ul{margin:.4em 0;padding-left:1.4em;list-style-type:disc}.markdown-view ol{margin:.4em 0;padding-left:1.4em;list-style-type:decimal}.markdown-view li{color:var(--text-secondary);margin:.15em 0}.markdown-view li::marker{color:var(--text-muted)}.markdown-view li>ul,.markdown-view li>ol{margin:.1em 0}.markdown-view li>ul{list-style-type:circle}.markdown-view code{background:var(--bg-primary);color:var(--text-primary);border-radius:3px;padding:.15em .35em;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.88em}.markdown-view pre{background:var(--bg-primary);border-radius:6px;margin:.5em 0;padding:.75em;overflow-x:auto}.markdown-view pre code{background:0 0;padding:0;font-size:.85em;line-height:1.5}.markdown-view blockquote{border-left:3px solid var(--border);color:var(--text-muted);margin:.5em 0;padding:.25em .75em}.markdown-view hr{border:none;border-top:1px solid var(--border);margin:.75em 0}.markdown-view table{border-collapse:collapse;width:100%;margin:.5em 0;font-size:.9em}.markdown-view th,.markdown-view td{border:1px solid var(--border);text-align:left;padding:.35em .6em}.markdown-view th{background:var(--bg-primary);color:var(--text-primary);font-weight:600}.markdown-view img{border-radius:4px;max-width:100%}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>WorkRail Console</title>
7
- <script type="module" crossorigin src="/console/assets/index-EsSXrC_a.js"></script>
8
- <link rel="stylesheet" crossorigin href="/console/assets/index-DW78t31j.css">
7
+ <script type="module" crossorigin src="/console/assets/index-BE5PAgPO.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/console/assets/index-BZNM03t1.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -241,9 +241,9 @@ async function createWorkRailEngine(config = {}) {
241
241
  const v2Ctx = ctx;
242
242
  engineActive = true;
243
243
  const engine = {
244
- async startWorkflow(workflowId) {
244
+ async startWorkflow(workflowId, goal) {
245
245
  const workspacePath = process.cwd();
246
- const result = await (0, start_js_1.executeStartWorkflow)({ workflowId, workspacePath }, v2Ctx);
246
+ const result = await (0, start_js_1.executeStartWorkflow)({ workflowId, workspacePath, goal }, v2Ctx);
247
247
  if (result.isErr()) {
248
248
  return (0, types_js_1.engineErr)(mapStartError(result.error));
249
249
  }
@@ -118,7 +118,7 @@ export interface EngineConfig {
118
118
  readonly dataDir?: string;
119
119
  }
120
120
  export interface WorkRailEngine {
121
- readonly startWorkflow: (workflowId: string) => Promise<EngineResult<StepResponse>>;
121
+ readonly startWorkflow: (workflowId: string, goal: string) => Promise<EngineResult<StepResponse>>;
122
122
  readonly continueWorkflow: (stateToken: StateToken, ackToken: AckToken | null, output?: {
123
123
  readonly notesMarkdown?: string;
124
124
  readonly artifacts?: readonly unknown[];
@@ -0,0 +1 @@
1
+ export declare const CLEAN_RESPONSE_FORMAT: boolean;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CLEAN_RESPONSE_FORMAT = void 0;
4
+ exports.CLEAN_RESPONSE_FORMAT = process.env.WORKRAIL_CLEAN_RESPONSE_FORMAT === 'true';
@@ -49,11 +49,11 @@ export declare class HttpServer {
49
49
  private printBanner;
50
50
  openDashboard(sessionId?: string): Promise<string>;
51
51
  stop(): Promise<void>;
52
- mountRoutes(installer: (app: Application) => void): void;
52
+ mountRoutes(installer: (app: Application) => (() => void) | void): void;
53
+ private readonly _routeDisposers;
53
54
  finalize(): void;
54
55
  getBaseUrl(): string;
55
56
  getPort(): number;
56
- private quickCleanup;
57
- private getWorkrailPorts;
58
57
  fullCleanup(): Promise<number>;
58
+ private getWorkrailPorts;
59
59
  }
@@ -29,6 +29,8 @@ const DashboardLockRelease_js_1 = require("./DashboardLockRelease.js");
29
29
  const cors_1 = __importDefault(require("cors"));
30
30
  const open_1 = __importDefault(require("open"));
31
31
  const child_process_1 = require("child_process");
32
+ const _pkg = require(path_1.default.join(__dirname, '../../../package.json'));
33
+ const CURRENT_VERSION = _pkg.version;
32
34
  let HttpServer = class HttpServer {
33
35
  constructor(sessionManager, processLifecyclePolicy, processSignals, shutdownEvents, dashboardMode, browserBehavior) {
34
36
  this.sessionManager = sessionManager;
@@ -41,6 +43,7 @@ let HttpServer = class HttpServer {
41
43
  this.baseUrl = '';
42
44
  this.isPrimary = false;
43
45
  this.config = {};
46
+ this._routeDisposers = [];
44
47
  this.port = 3456;
45
48
  this.lockFile = path_1.default.join(os_1.default.homedir(), '.workrail', 'dashboard.lock');
46
49
  this.heartbeat = new DashboardHeartbeat_js_1.DashboardHeartbeat(this.lockFile, () => this.isPrimary);
@@ -314,7 +317,7 @@ let HttpServer = class HttpServer {
314
317
  });
315
318
  }
316
319
  });
317
- this.app.get('/api/health', (req, res) => {
320
+ this.app.get('/api/health', (_req, res) => {
318
321
  res.json({
319
322
  success: true,
320
323
  status: 'healthy',
@@ -322,12 +325,12 @@ let HttpServer = class HttpServer {
322
325
  timestamp: new Date().toISOString(),
323
326
  isPrimary: this.isPrimary,
324
327
  pid: process.pid,
325
- port: this.port
328
+ port: this.port,
329
+ version: CURRENT_VERSION
326
330
  });
327
331
  });
328
332
  }
329
333
  async start() {
330
- await this.quickCleanup();
331
334
  const mode = this.config.dashboardMode ?? this.dashboardMode;
332
335
  if (mode.kind === 'legacy') {
333
336
  console.error('[Dashboard] Unified dashboard disabled, using legacy mode');
@@ -361,7 +364,8 @@ let HttpServer = class HttpServer {
361
364
  startedAt: new Date().toISOString(),
362
365
  lastHeartbeat: new Date().toISOString(),
363
366
  projectId: this.sessionManager.getProjectId(),
364
- projectPath: this.sessionManager.getProjectPath()
367
+ projectPath: this.sessionManager.getProjectPath(),
368
+ version: CURRENT_VERSION
365
369
  };
366
370
  await promises_1.default.writeFile(this.lockFile, JSON.stringify(lockData, null, 2), { flag: 'wx' });
367
371
  console.error('[Dashboard] Primary elected');
@@ -385,6 +389,9 @@ let HttpServer = class HttpServer {
385
389
  if (!lockData.pid || !lockData.port || !lockData.startedAt) {
386
390
  return { reclaim: true, reason: 'invalid lock structure' };
387
391
  }
392
+ if (lockData.version !== CURRENT_VERSION) {
393
+ return { reclaim: true, reason: `version mismatch (lock=${lockData.version}, current=${CURRENT_VERSION})` };
394
+ }
388
395
  const lastHeartbeat = new Date(lockData.lastHeartbeat || lockData.startedAt);
389
396
  const ageMinutes = (Date.now() - lastHeartbeat.getTime()) / 60000;
390
397
  if (ageMinutes > 2) {
@@ -421,6 +428,20 @@ let HttpServer = class HttpServer {
421
428
  }
422
429
  else {
423
430
  console.error(`[Dashboard] Lock reclaim needed: ${reason}`);
431
+ try {
432
+ process.kill(lockData.pid, 0);
433
+ const isWorkRailOnPort = await this.checkHealth(lockData.port);
434
+ if (isWorkRailOnPort) {
435
+ console.error(`[Dashboard] Sending SIGTERM to old process (PID ${lockData.pid})`);
436
+ process.kill(lockData.pid, 'SIGTERM');
437
+ await new Promise(resolve => setTimeout(resolve, 2000));
438
+ }
439
+ else {
440
+ console.error(`[Dashboard] PID ${lockData.pid} alive but not WorkRail on port ${lockData.port} -- reclaiming without SIGTERM`);
441
+ }
442
+ }
443
+ catch {
444
+ }
424
445
  }
425
446
  const tempPath = `${this.lockFile}.${process.pid}.${Date.now()}`;
426
447
  const newLockData = {
@@ -429,7 +450,8 @@ let HttpServer = class HttpServer {
429
450
  startedAt: new Date().toISOString(),
430
451
  lastHeartbeat: new Date().toISOString(),
431
452
  projectId: this.sessionManager.getProjectId(),
432
- projectPath: this.sessionManager.getProjectPath()
453
+ projectPath: this.sessionManager.getProjectPath(),
454
+ version: CURRENT_VERSION
433
455
  };
434
456
  try {
435
457
  await promises_1.default.writeFile(tempPath, JSON.stringify(newLockData, null, 2));
@@ -609,6 +631,13 @@ let HttpServer = class HttpServer {
609
631
  async stop() {
610
632
  this.heartbeat.stop();
611
633
  this.sessionManager.unwatchAll();
634
+ for (const dispose of this._routeDisposers) {
635
+ try {
636
+ dispose();
637
+ }
638
+ catch { }
639
+ }
640
+ this._routeDisposers.length = 0;
612
641
  await new Promise((resolve) => {
613
642
  if (!this.server)
614
643
  return resolve();
@@ -628,7 +657,10 @@ let HttpServer = class HttpServer {
628
657
  }
629
658
  }
630
659
  mountRoutes(installer) {
631
- installer(this.app);
660
+ const disposer = installer(this.app);
661
+ if (disposer != null) {
662
+ this._routeDisposers.push(disposer);
663
+ }
632
664
  }
633
665
  finalize() {
634
666
  this.app.use((req, res) => {
@@ -645,42 +677,50 @@ let HttpServer = class HttpServer {
645
677
  getPort() {
646
678
  return this.port;
647
679
  }
648
- async quickCleanup() {
680
+ async fullCleanup() {
649
681
  try {
650
682
  const busyPorts = await this.getWorkrailPorts();
651
683
  if (busyPorts.length === 0) {
652
- return;
684
+ console.error('[Cleanup] No workrail processes found');
685
+ return 0;
653
686
  }
654
- console.error(`[Cleanup] Found ${busyPorts.length} workrail process(es), checking health...`);
687
+ console.error(`[Cleanup] Found ${busyPorts.length} workrail process(es), removing all...`);
655
688
  let cleanedCount = 0;
656
689
  for (const { port, pid } of busyPorts) {
657
- if (pid === process.pid)
690
+ if (pid === process.pid) {
691
+ console.error(`[Cleanup] Skipping current process ${pid}`);
658
692
  continue;
659
- const isHealthy = await this.checkHealth(port);
660
- if (!isHealthy) {
661
- console.error(`[Cleanup] Removing unresponsive process ${pid} on port ${port}`);
693
+ }
694
+ console.error(`[Cleanup] Killing process ${pid} on port ${port}`);
695
+ try {
696
+ process.kill(pid, 'SIGTERM');
697
+ await new Promise(r => setTimeout(r, 1000));
662
698
  try {
663
- process.kill(pid, 'SIGTERM');
664
- await new Promise(r => setTimeout(r, 1000));
665
- try {
666
- process.kill(pid, 0);
667
- process.kill(pid, 'SIGKILL');
668
- }
669
- catch {
670
- }
671
- cleanedCount++;
699
+ process.kill(pid, 0);
700
+ process.kill(pid, 'SIGKILL');
701
+ console.error(`[Cleanup] Force killed process ${pid}`);
672
702
  }
673
- catch (error) {
703
+ catch {
704
+ console.error(`[Cleanup] Process ${pid} terminated gracefully`);
674
705
  }
706
+ cleanedCount++;
707
+ }
708
+ catch (error) {
709
+ console.error(`[Cleanup] Failed to kill process ${pid}:`, error);
675
710
  }
676
711
  }
677
- if (cleanedCount > 0) {
678
- console.error(`[Cleanup] Cleaned up ${cleanedCount} orphaned process(es)`);
679
- await new Promise(r => setTimeout(r, 1000));
712
+ console.error(`[Cleanup] Cleaned up ${cleanedCount} process(es)`);
713
+ try {
714
+ await promises_1.default.unlink(this.lockFile);
715
+ console.error('[Cleanup] Removed lock file');
680
716
  }
717
+ catch {
718
+ }
719
+ return cleanedCount;
681
720
  }
682
721
  catch (error) {
683
- console.error('[Cleanup] Failed, continuing anyway:', error);
722
+ console.error('[Cleanup] Full cleanup failed:', error);
723
+ throw error;
684
724
  }
685
725
  }
686
726
  async getWorkrailPorts() {
@@ -719,56 +759,10 @@ let HttpServer = class HttpServer {
719
759
  }
720
760
  return [];
721
761
  }
722
- catch (error) {
762
+ catch {
723
763
  return [];
724
764
  }
725
765
  }
726
- async fullCleanup() {
727
- try {
728
- const busyPorts = await this.getWorkrailPorts();
729
- if (busyPorts.length === 0) {
730
- console.error('[Cleanup] No workrail processes found');
731
- return 0;
732
- }
733
- console.error(`[Cleanup] Found ${busyPorts.length} workrail process(es), removing all...`);
734
- let cleanedCount = 0;
735
- for (const { port, pid } of busyPorts) {
736
- if (pid === process.pid) {
737
- console.error(`[Cleanup] Skipping current process ${pid}`);
738
- continue;
739
- }
740
- console.error(`[Cleanup] Killing process ${pid} on port ${port}`);
741
- try {
742
- process.kill(pid, 'SIGTERM');
743
- await new Promise(r => setTimeout(r, 1000));
744
- try {
745
- process.kill(pid, 0);
746
- process.kill(pid, 'SIGKILL');
747
- console.error(`[Cleanup] Force killed process ${pid}`);
748
- }
749
- catch {
750
- console.error(`[Cleanup] Process ${pid} terminated gracefully`);
751
- }
752
- cleanedCount++;
753
- }
754
- catch (error) {
755
- console.error(`[Cleanup] Failed to kill process ${pid}:`, error);
756
- }
757
- }
758
- console.error(`[Cleanup] Cleaned up ${cleanedCount} process(es)`);
759
- try {
760
- await promises_1.default.unlink(this.lockFile);
761
- console.error('[Cleanup] Removed lock file');
762
- }
763
- catch {
764
- }
765
- return cleanedCount;
766
- }
767
- catch (error) {
768
- console.error('[Cleanup] Full cleanup failed:', error);
769
- throw error;
770
- }
771
- }
772
766
  };
773
767
  exports.HttpServer = HttpServer;
774
768
  exports.HttpServer = HttpServer = __decorate([
@@ -6,6 +6,7 @@ export declare class CachingWorkflowStorage implements IWorkflowStorage {
6
6
  readonly kind: "single";
7
7
  private workflowCache;
8
8
  private summaryCache;
9
+ private workflowCacheIndex;
9
10
  private stats;
10
11
  constructor(inner: IWorkflowStorage, ttlMs: number);
11
12
  get source(): WorkflowSource;
@@ -26,6 +27,7 @@ export declare class CachingCompositeWorkflowStorage implements ICompositeWorkfl
26
27
  readonly kind: "composite";
27
28
  private workflowCache;
28
29
  private summaryCache;
30
+ private workflowCacheIndex;
29
31
  private stats;
30
32
  constructor(inner: ICompositeWorkflowStorage, ttlMs: number);
31
33
  private isFresh;
@@ -14,6 +14,7 @@ class CachingWorkflowStorage {
14
14
  this.kind = 'single';
15
15
  this.workflowCache = null;
16
16
  this.summaryCache = null;
17
+ this.workflowCacheIndex = null;
17
18
  this.stats = { hits: 0, misses: 0 };
18
19
  }
19
20
  get source() {
@@ -25,6 +26,7 @@ class CachingWorkflowStorage {
25
26
  clearCache() {
26
27
  this.workflowCache = null;
27
28
  this.summaryCache = null;
29
+ this.workflowCacheIndex = null;
28
30
  }
29
31
  isFresh(cache) {
30
32
  return cache !== null && Date.now() - cache.timestamp < this.ttlMs;
@@ -37,16 +39,18 @@ class CachingWorkflowStorage {
37
39
  this.stats.misses += 1;
38
40
  const workflows = await this.inner.loadAllWorkflows();
39
41
  this.workflowCache = { value: workflows, timestamp: Date.now() };
42
+ this.workflowCacheIndex = new Map(workflows.map((w) => [w.definition.id, w]));
40
43
  this.summaryCache = null;
41
44
  return deepClone(workflows);
42
45
  }
43
46
  async getWorkflowById(id) {
44
- if (this.isFresh(this.workflowCache)) {
45
- const wf = this.workflowCache.value.find((w) => w.definition.id === id);
46
- if (wf) {
47
+ if (this.isFresh(this.workflowCache) && this.workflowCacheIndex !== null) {
48
+ const wf = this.workflowCacheIndex.get(id);
49
+ if (wf !== undefined) {
47
50
  this.stats.hits += 1;
48
51
  return deepClone(wf);
49
52
  }
53
+ return null;
50
54
  }
51
55
  this.stats.misses += 1;
52
56
  const workflow = await this.inner.getWorkflowById(id);
@@ -77,6 +81,7 @@ class CachingCompositeWorkflowStorage {
77
81
  this.kind = 'composite';
78
82
  this.workflowCache = null;
79
83
  this.summaryCache = null;
84
+ this.workflowCacheIndex = null;
80
85
  this.stats = { hits: 0, misses: 0 };
81
86
  }
82
87
  isFresh(cache) {
@@ -96,16 +101,18 @@ class CachingCompositeWorkflowStorage {
96
101
  this.stats.misses += 1;
97
102
  const workflows = await this.inner.loadAllWorkflows();
98
103
  this.workflowCache = { value: workflows, timestamp: Date.now() };
104
+ this.workflowCacheIndex = new Map(workflows.map((w) => [w.definition.id, w]));
99
105
  this.summaryCache = null;
100
106
  return deepClone(workflows);
101
107
  }
102
108
  async getWorkflowById(id) {
103
- if (this.isFresh(this.workflowCache)) {
104
- const wf = this.workflowCache.value.find((w) => w.definition.id === id);
105
- if (wf) {
109
+ if (this.isFresh(this.workflowCache) && this.workflowCacheIndex !== null) {
110
+ const wf = this.workflowCacheIndex.get(id);
111
+ if (wf !== undefined) {
106
112
  this.stats.hits += 1;
107
113
  return deepClone(wf);
108
114
  }
115
+ return null;
109
116
  }
110
117
  this.stats.misses += 1;
111
118
  const workflow = await this.inner.getWorkflowById(id);
@@ -126,6 +133,7 @@ class CachingCompositeWorkflowStorage {
126
133
  await this.inner.save(definition);
127
134
  this.workflowCache = null;
128
135
  this.summaryCache = null;
136
+ this.workflowCacheIndex = null;
129
137
  }
130
138
  }
131
139
  getCacheStats() {
@@ -134,6 +142,7 @@ class CachingCompositeWorkflowStorage {
134
142
  clearCache() {
135
143
  this.workflowCache = null;
136
144
  this.summaryCache = null;
145
+ this.workflowCacheIndex = null;
137
146
  }
138
147
  }
139
148
  exports.CachingCompositeWorkflowStorage = CachingCompositeWorkflowStorage;
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.FileWorkflowStorage = void 0;
7
7
  exports.createDefaultFileWorkflowStorage = createDefaultFileWorkflowStorage;
8
8
  const promises_1 = __importDefault(require("fs/promises"));
9
- const fs_1 = require("fs");
10
9
  const path_1 = __importDefault(require("path"));
11
10
  const workflow_1 = require("../../types/workflow");
12
11
  const error_handler_1 = require("../../core/error-handler");
@@ -63,7 +62,7 @@ class FileWorkflowStorage {
63
62
  }
64
63
  const filePathRaw = path_1.default.resolve(this.baseDirReal, file);
65
64
  assertWithinBase(filePathRaw, this.baseDirReal);
66
- const stats = (0, fs_1.statSync)(filePathRaw);
65
+ const stats = await promises_1.default.stat(filePathRaw);
67
66
  if (stats.size > this.maxFileSize)
68
67
  continue;
69
68
  const raw = await promises_1.default.readFile(filePathRaw, 'utf-8');
@@ -94,7 +93,7 @@ class FileWorkflowStorage {
94
93
  const selection = (0, workflow_resolution_1.selectVariant)(candidates, flags);
95
94
  const selected = files.find(f => f.file === selection.selectedIdentifier) ?? files[0];
96
95
  const filePath = path_1.default.resolve(this.baseDirReal, selected.file);
97
- const stats = (0, fs_1.statSync)(filePath);
96
+ const stats = await promises_1.default.stat(filePath);
98
97
  index.set(id, {
99
98
  id: selected.definition.id,
100
99
  filename: selected.file,
@@ -117,7 +116,7 @@ class FileWorkflowStorage {
117
116
  const filePath = path_1.default.resolve(this.baseDirReal, filename);
118
117
  assertWithinBase(filePath, this.baseDirReal);
119
118
  try {
120
- const stats = (0, fs_1.statSync)(filePath);
119
+ const stats = await promises_1.default.stat(filePath);
121
120
  if (stats.size > this.maxFileSize) {
122
121
  throw new error_handler_1.SecurityError('Workflow file exceeds size limit', 'file-size');
123
122
  }
@@ -10,6 +10,13 @@ const ajv_1 = __importDefault(require("ajv"));
10
10
  const workflow_1 = require("../../types/workflow");
11
11
  const error_handler_1 = require("../../core/error-handler");
12
12
  const workflow_id_policy_1 = require("../../domain/workflow-id-policy");
13
+ function buildModuleValidator() {
14
+ const schemaPath = path_1.default.resolve(__dirname, '../../../spec/workflow.schema.json');
15
+ const schema = JSON.parse(fs_1.default.readFileSync(schemaPath, 'utf-8'));
16
+ const ajv = new ajv_1.default({ allErrors: true, strict: false });
17
+ return ajv.compile(schema);
18
+ }
19
+ const MODULE_WORKFLOW_VALIDATOR = buildModuleValidator();
13
20
  const VALIDATION_ERROR_PREFIX = '[ValidationError]';
14
21
  function reportValidationFailure(workflowId, sourceKind, error) {
15
22
  console.error(`${VALIDATION_ERROR_PREFIX} ${sourceKind}/${workflowId}: ${error}`);
@@ -18,10 +25,7 @@ class SchemaValidatingWorkflowStorage {
18
25
  constructor(inner) {
19
26
  this.inner = inner;
20
27
  this.kind = 'single';
21
- const schemaPath = path_1.default.resolve(__dirname, '../../../spec/workflow.schema.json');
22
- const schema = JSON.parse(fs_1.default.readFileSync(schemaPath, 'utf-8'));
23
- const ajv = new ajv_1.default({ allErrors: true, strict: false });
24
- this.validator = ajv.compile(schema);
28
+ this.validator = MODULE_WORKFLOW_VALIDATOR;
25
29
  }
26
30
  get source() {
27
31
  return this.inner.source;
@@ -81,10 +85,7 @@ class SchemaValidatingCompositeWorkflowStorage {
81
85
  constructor(inner) {
82
86
  this.inner = inner;
83
87
  this.kind = 'composite';
84
- const schemaPath = path_1.default.resolve(__dirname, '../../../spec/workflow.schema.json');
85
- const schema = JSON.parse(fs_1.default.readFileSync(schemaPath, 'utf-8'));
86
- const ajv = new ajv_1.default({ allErrors: true, strict: false });
87
- this.validator = ajv.compile(schema);
88
+ this.validator = MODULE_WORKFLOW_VALIDATOR;
88
89
  }
89
90
  validateDefinition(definition, sourceKind) {
90
91
  const isValid = this.validator(definition);