@exaudeus/workrail 3.39.0 → 3.41.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/dist/cli/commands/init.js +0 -3
- package/dist/cli-worktrain.js +58 -26
- package/dist/cli.js +0 -18
- package/dist/config/app-config.d.ts +0 -16
- package/dist/config/app-config.js +0 -14
- package/dist/config/config-file.js +0 -3
- package/dist/console-ui/assets/index-CQt4UhPB.js +28 -0
- package/dist/console-ui/assets/index-DGj8EsFR.css +1 -0
- package/dist/console-ui/index.html +2 -2
- package/dist/coordinators/pr-review.d.ts +23 -1
- package/dist/coordinators/pr-review.js +224 -5
- package/dist/daemon/daemon-events.d.ts +9 -1
- package/dist/daemon/soul-template.d.ts +2 -2
- package/dist/daemon/soul-template.js +11 -1
- package/dist/daemon/workflow-runner.d.ts +17 -3
- package/dist/daemon/workflow-runner.js +401 -28
- package/dist/di/container.js +1 -25
- package/dist/di/tokens.d.ts +0 -3
- package/dist/di/tokens.js +0 -3
- package/dist/engine/engine-factory.js +0 -1
- package/dist/infrastructure/console-defaults.d.ts +1 -0
- package/dist/infrastructure/console-defaults.js +4 -0
- package/dist/infrastructure/session/index.d.ts +0 -1
- package/dist/infrastructure/session/index.js +1 -3
- package/dist/manifest.json +124 -124
- package/dist/mcp/handlers/session.d.ts +1 -0
- package/dist/mcp/handlers/session.js +61 -13
- package/dist/mcp/output-schemas.d.ts +10 -10
- package/dist/mcp/server.js +1 -18
- package/dist/mcp/tools.d.ts +12 -12
- package/dist/mcp/transports/http-entry.js +0 -2
- package/dist/mcp/transports/stdio-entry.js +1 -2
- package/dist/mcp/types.d.ts +0 -2
- package/dist/trigger/daemon-console.d.ts +2 -0
- package/dist/trigger/daemon-console.js +1 -1
- package/dist/trigger/trigger-listener.d.ts +2 -0
- package/dist/trigger/trigger-listener.js +3 -1
- package/dist/trigger/trigger-router.d.ts +4 -3
- package/dist/trigger/trigger-router.js +13 -5
- package/dist/trigger/trigger-store.js +17 -4
- package/dist/types/workflow-source.d.ts +0 -1
- package/dist/types/workflow-source.js +3 -6
- package/dist/types/workflow.d.ts +1 -1
- package/dist/types/workflow.js +1 -2
- package/dist/v2/durable-core/domain/artifact-contract-validator.js +66 -0
- package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.d.ts +25 -0
- package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.js +31 -0
- package/dist/v2/durable-core/schemas/artifacts/index.d.ts +3 -1
- package/dist/v2/durable-core/schemas/artifacts/index.js +14 -1
- package/dist/v2/durable-core/schemas/artifacts/review-verdict.d.ts +41 -0
- package/dist/v2/durable-core/schemas/artifacts/review-verdict.js +30 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +236 -236
- package/dist/v2/durable-core/schemas/session/events.d.ts +50 -50
- package/dist/v2/durable-core/schemas/session/gaps.d.ts +2 -2
- package/dist/v2/durable-core/schemas/session/manifest.d.ts +4 -4
- package/dist/v2/durable-core/schemas/session/outputs.d.ts +8 -8
- package/dist/v2/usecases/console-routes.d.ts +2 -1
- package/dist/v2/usecases/console-routes.js +207 -5
- package/dist/v2/usecases/console-service.js +14 -0
- package/dist/v2/usecases/console-types.d.ts +1 -0
- package/docs/authoring.md +16 -16
- package/docs/design/coordinator-artifact-protocol-design-candidates.md +155 -0
- package/docs/design/coordinator-artifact-protocol-design-review.md +103 -0
- package/docs/design/coordinator-artifact-protocol-implementation-plan.md +259 -0
- package/docs/design/coordinator-message-queue-drain-plan.md +241 -0
- package/docs/design/coordinator-message-queue-drain-review.md +120 -0
- package/docs/design/coordinator-message-queue-drain.md +289 -0
- package/docs/design/shaping-workflow-external-research.md +119 -0
- package/docs/discovery/late-bound-goals-impl-plan.md +147 -0
- package/docs/discovery/late-bound-goals-review.md +82 -0
- package/docs/discovery/late-bound-goals.md +118 -0
- package/docs/discovery/steer-endpoint-design-candidates.md +288 -0
- package/docs/discovery/steer-endpoint-design-review-findings.md +104 -0
- package/docs/discovery/steer-endpoint-implementation-plan.md +284 -0
- package/docs/ideas/backlog.md +447 -97
- package/docs/ideas/design-candidates-console-session-tree-impl.md +64 -0
- package/docs/ideas/design-candidates-session-tree-view.md +196 -0
- package/docs/ideas/design-review-findings-console-session-tree-impl.md +75 -0
- package/docs/ideas/design-review-findings-session-tree-view.md +88 -0
- package/docs/ideas/implementation_plan_session_tree_view.md +238 -0
- package/package.json +2 -1
- package/spec/authoring-spec.json +16 -16
- package/spec/shape.schema.json +178 -0
- package/spec/workflow-tags.json +232 -47
- package/workflows/coding-task-workflow-agentic.json +491 -480
- package/workflows/mr-review-workflow.agentic.v2.json +5 -1
- package/workflows/wr.shaping.json +182 -0
- package/dist/console-ui/assets/index-3oXZ_A9m.js +0 -28
- package/dist/console-ui/assets/index-8dh0Psu-.css +0 -1
- package/dist/infrastructure/session/DashboardHeartbeat.d.ts +0 -8
- package/dist/infrastructure/session/DashboardHeartbeat.js +0 -39
- package/dist/infrastructure/session/DashboardLockRelease.d.ts +0 -2
- package/dist/infrastructure/session/DashboardLockRelease.js +0 -29
- package/dist/infrastructure/session/HttpServer.d.ts +0 -60
- package/dist/infrastructure/session/HttpServer.js +0 -912
- package/workflows/coding-task-workflow-agentic.lean.v2.json +0 -648
- package/workflows/coding-task-workflow-agentic.v2.json +0 -324
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.react-flow{--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:#ffffff80;--xy-minimap-background-color-default:#fff;--xy-minimap-mask-background-color-default:#f0f0f099;--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:#f0f0f040;--xy-node-boxshadow-hover-default:0 1px 4px 1px #00000014;--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:#0059dc14;--xy-selection-border-default:1px dotted #0059dccc;--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 #00000014;--xy-edge-label-background-color-default:#fff;--xy-edge-label-color-default:inherit;--xy-resize-background-color-default:#3367d9;direction:ltr}.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:#96969640;--xy-minimap-background-color-default:#141414;--xy-minimap-mask-background-color-default:#3c3c3c99;--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:#f0f0f040;--xy-node-boxshadow-hover-default:0 1px 4px 1px #ffffff14;--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:#c8c8dc14;--xy-selection-border-default:1px dotted #c8c8dccc;--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 #00000014;--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{width:100%;height:100%;position:absolute;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{pointer-events:none;position:absolute;overflow:visible}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:.5s linear infinite dashdraw}.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;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:.5s linear infinite dashdraw}svg.react-flow__connectionline{z-index:1001;position:absolute;overflow:visible}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{-webkit-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default;position:absolute}.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:0 0;pointer-events:none}.react-flow__nodesselection-rect{pointer-events:all;cursor:grab;position:absolute}.react-flow__handle{pointer-events:none;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%;width:6px;min-width:5px;height:6px;min-height:5px;position:absolute}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;bottom:0;left:50%;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{z-index:5;margin:15px;position:absolute}.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{background:var(--xy-attribution-background-color,var(--xy-attribution-background-color-default));margin:0;padding:2px 3px;font-size:10px}.react-flow__attribution a{color:#999;text-decoration:none}@keyframes dashdraw{0%{stroke-dashoffset:10px}}.react-flow__edgelabel-renderer{pointer-events:none;-webkit-user-select:none;user-select:none;width:100%;height:100%;position:absolute;top:0;left:0}.react-flow__viewport-portal{-webkit-user-select:none;user-select:none;width:100%;height:100%;position:absolute;top:0;left:0}.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{box-shadow:var(--xy-controls-box-shadow,var(--xy-controls-box-shadow-default));flex-direction:column;display:flex}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{background:var(--xy-controls-button-background-color,var(--xy-controls-button-background-color-default));border:none;border-bottom:1px solid var(--xy-controls-button-border-color-props,var(--xy-controls-button-border-color,var(--xy-controls-button-border-color-default)));width:26px;height:26px;color:var(--xy-controls-button-color-props,var(--xy-controls-button-color,var(--xy-controls-button-color-default)));cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;padding:4px;display:flex}.react-flow__controls-button svg{fill:currentColor;width:100%;max-width:12px;max-height:12px}.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{border-radius:var(--xy-node-border-radius,var(--xy-node-border-radius-default));width:150px;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));padding:10px;font-size:12px}.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{background-color:var(--xy-resize-background-color,var(--xy-resize-background-color-default));border:1px solid #fff;border-radius:1px;width:5px;height:5px;translate:-50% -50%}.react-flow__resize-control.handle.left{top:50%;left:0}.react-flow__resize-control.handle.right{top:50%;left:100%}.react-flow__resize-control.handle.top{top:0;left:50%}.react-flow__resize-control.handle.bottom{top:100%;left:50%}.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-style:solid;border-width:0}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;height:100%;top:0;transform:translate(-50%)}.react-flow__resize-control.line.left{border-left-width:1px;left:0}.react-flow__resize-control.line.right{border-right-width:1px;left:100%}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{width:100%;height:1px;left:0;transform:translateY(-50%)}.react-flow__resize-control.line.top{border-top-width:1px;top:0}.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-outline-style:solid;--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-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia: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:calc(1.5 / 1);--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);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--leading-snug:1.375;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--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;-webkit-text-decoration:inherit;-webkit-text-decoration: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{.branch-tree-line{margin-left:4px;padding-left:14px;position:relative}.branch-tree-line:before{content:"";pointer-events:none;clip-path:polygon(0 0,100% 0,100% calc(100% - 14px),0 100%);background:linear-gradient(#f4c4302e,#f4c4302e) 0 0/1px 100% no-repeat,linear-gradient(#f4c43073,#f4c43073) 5px 0/3px 100% no-repeat;width:10px;position:absolute;top:0;bottom:0;left:0}.tab-btn{position:relative;overflow:visible}.tab-corner{opacity:0;pointer-events:none;width:8px;height:8px;transition:opacity .15s,transform .15s;position:absolute;transform:scale(.5)}.tab-corner--tl{border-top:1px solid var(--accent);border-left:1px solid var(--accent);top:2px;left:2px}.tab-corner--tr{border-top:1px solid var(--accent);border-right:1px solid var(--accent);top:2px;right:2px}.tab-corner--bl{border-bottom:1px solid var(--accent);border-left:1px solid var(--accent);bottom:2px;left:2px}.tab-corner--br{border-bottom:1px solid var(--accent);border-right:1px solid var(--accent);bottom:2px;right:2px}.tab-btn--active .tab-corner{opacity:1;transform:scale(1)}}@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.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}.inset-0{inset:calc(var(--spacing) * 0)}.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)}.bottom-4{bottom:calc(var(--spacing) * 4)}.left-1\/2{left:50%}.z-50{z-index:50}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (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}.mt-auto{margin-top:auto}.mt-px{margin-top:1px}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.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-11{margin-left:calc(var(--spacing) * 11)}.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}.line-clamp-3{-webkit-line-clamp:3;-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-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-11{height:calc(var(--spacing) * 11)}.h-14{height:calc(var(--spacing) * 14)}.h-\[3px\]{height:3px}.h-\[100px\]{height:100px}.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-\[44px\]{min-height:44px}.min-h-\[160px\]{min-height:160px}.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-3{width:calc(var(--spacing) * 3)}.w-3\/4{width:75%}.w-4\/6{width:66.6667%}.w-5\/6{width:83.3333%}.w-6{width:calc(var(--spacing) * 6)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.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-\[80px\]{max-width:80px}.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-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}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);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}.resize-y{resize:vertical}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.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-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0{gap:calc(var(--spacing) * 0)}.gap-0\.5{gap:calc(var(--spacing) * .5)}.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-6{gap:calc(var(--spacing) * 6)}.gap-8{gap:calc(var(--spacing) * 8)}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}: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-start{align-self:flex-start}.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}.overscroll-contain{overscroll-behavior:contain}.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-none{border-radius:0}.border{border-style:var(--tw-border-style);border-width:1px}.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-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-\[rgba\(255\,255\,255\,0\.06\)\]{border-color:#ffffff0f}.border-\[var\(--accent\)\]{border-color:var(--accent)}.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)}}.border-transparent{border-color:#0000}.bg-\[rgba\(0\,0\,0\,0\.18\)\]{background-color:#0000002e}.bg-\[rgba\(0\,240\,255\,0\.03\)\]{background-color:#00f0ff08}.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-1{padding-top:calc(var(--spacing) * 1)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-\[calc\(theme\(spacing\.2\)\+theme\(spacing\.3\)\+4ch\)\]{padding-left:calc(1.25rem + 4ch)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.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-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.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-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.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\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.tracking-\[0\.12em\]{--tw-tracking:.12em;letter-spacing:.12em}.tracking-\[0\.15em\]{--tw-tracking:.15em;letter-spacing:.15em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.tracking-\[0\.20em\]{--tw-tracking:.2em;letter-spacing:.2em}.tracking-\[0\.22em\]{--tw-tracking:.22em;letter-spacing:.22em}.tracking-\[0\.25em\]{--tw-tracking:.25em;letter-spacing:.25em}.tracking-\[0\.30em\]{--tw-tracking:.3em;letter-spacing:.3em}.tracking-\[0\.35em\]{--tw-tracking:.35em;letter-spacing:.35em}.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-\[\#0f131f\]{color:#0f131f}.text-\[var\(--accent\)\]{color:var(--accent)}.text-\[var\(--error\)\]{color:var(--error)}.text-\[var\(--success\)\]{color:var(--success)}.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-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-40{opacity:.4}.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)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);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,)}.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,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.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}.duration-600{--tw-duration:.6s;transition-duration:.6s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}@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}}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:border-\[var\(--accent\)\]:hover{border-color:var(--accent)}.hover\:border-\[var\(--accent-strong\)\]:hover{border-color:var(--accent-strong)}.hover\:bg-\[var\(--bg-card\)\]:hover{background-color:var(--bg-card)}.hover\:bg-\[var\(--bg-secondary\)\]:hover{background-color:var(--bg-secondary)}.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{color:var(--accent)}.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)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media (prefers-reduced-motion:no-preference){.motion-safe\:animate-pulse{animation:var(--animate-pulse)}}@media (width>=40rem){.sm\:block{display:block}.sm\:flex{display:flex}}@media (width>=48rem){.md\:w-2\/5{width:40%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:flex-row{flex-direction:row}}@media (width>=64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (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:#0f131f;--bg-secondary:#171b28b3;--bg-card:#1b1f2c99;--bg-tertiary:#262937bf;--border:#3a3a2e;--text-primary:#e8dfc8;--text-secondary:#a89f8c;--text-muted:#5e5a4e;--accent:#f4c430;--accent-hover:#d4a80e;--accent-strong:#00afc0;--success:#00e1a9;--warning:#f97316;--error:#ff6b6b;--blocked:#f44}body{background-color:var(--bg-primary);color:var(--text-primary);background-image:radial-gradient(80% 50% at 50% -5%,#f4c4302e 0%,#0000 70%),radial-gradient(60% 50% at 100% 110%,#00dbe91f 0%,#0000 60%);background-attachment:fixed;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.glass-panel{-webkit-backdrop-filter:blur(16px);background:#1b1f2ca6!important}body:after{content:"";pointer-events:none;z-index:9999;background:repeating-linear-gradient(0deg,#00000005 0 1px,#0000 1px 4px);position:fixed;inset:0}.workrail-minimap-node{opacity:1}*{box-sizing:border-box}.console-blueprint-grid{background-image:linear-gradient(#84949514 1px,#0000 1px),linear-gradient(90deg,#84949514 1px,#0000 1px);background-size:20px 20px}.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 #f4c43059,inset 0 0 28px #f4c4301a,0 0 16px #f4c43026}.workrail-current-lineage-node:after{content:"";pointer-events:none;border:1px solid #f4c43059;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 #f4c43000,inset 0 0 #f4c43000}50%{opacity:1;box-shadow:0 0 32px #f4c4304d,inset 0 0 24px #f4c4301f}}@keyframes workrail-current-edge-flow{to{stroke-dashoffset:-32px}}.energy-glow{box-shadow:0 0 20px #f4c4304d,0 0 8px #f4c43033}.energy-live{animation:2.4s ease-in-out infinite energy-live-pulse;box-shadow:0 0 16px #00dbe94d,0 0 4px #00dbe933}@keyframes energy-live-pulse{0%,to{box-shadow:0 0 10px #00dbe933,0 0 3px #00dbe926}50%{box-shadow:0 0 22px #00dbe973,0 0 8px #00dbe94d}}.energy-card{transition:border-color .15s,box-shadow .15s}.energy-card:hover{border-color:#f4c43080;box-shadow:0 0 0 1px #f4c4301f,0 4px 24px #0006}.text-glow-amber{text-shadow:0 0 12px #f4c43080,0 0 4px #f4c4304d}.text-glow-cyan{text-shadow:0 0 12px #00dbe980,0 0 4px #00dbe94d}.badge-live{color:var(--accent-strong);animation:2.4s ease-in-out infinite badge-live-text-pulse}@keyframes badge-live-text-pulse{0%,to{text-shadow:0 0 6px #00dbe980}50%{text-shadow:0 0 16px #00dbe9e6,0 0 4px #00dbe999}}.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 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%}.markdown-view h1,.markdown-view h2,.markdown-view h3,.markdown-view h4{color:var(--accent);letter-spacing:.08em}.markdown-view strong{color:var(--accent);font-weight:700}.markdown-view code{color:var(--accent-strong);background:#00dbe914;border:1px solid #00dbe933;border-radius:0}.markdown-view pre{border:1px solid #00dbe933;border-left:2px solid var(--accent-strong);background:#0006;border-radius:0}.markdown-view pre code{color:var(--accent-strong);background:0 0;border:none}.markdown-view blockquote{border-left:2px solid var(--accent);color:var(--text-secondary);background:#f4c4300a;padding:.5em .75em}.markdown-view ul li:before{content:"// ";color:var(--accent);font-family:monospace;font-weight:700}.markdown-view ul{padding-left:1.2em;list-style:none}.markdown-view a{color:var(--accent-strong)}.corner-brackets{background-image:linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--accent), var(--accent));background-position:0 0,0 0,right 0 top 0,right 0 top 0,left 0 bottom 0,left 0 bottom 0,right 0 bottom 0,right 0 bottom 0;background-repeat:no-repeat;background-size:1px 12px,12px 1px,1px 12px,12px 1px,1px 12px,12px 1px,1px 12px,12px 1px;position:relative}@keyframes tab-activate-flicker{0%{opacity:1;filter:brightness()}10%{opacity:.2;filter:brightness(.5)}20%{opacity:1;filter:brightness(3)hue-rotate(180deg)}35%{opacity:.5;filter:brightness(1.5)}50%{opacity:1;filter:brightness(2.5)}70%{opacity:.8;filter:brightness(1.8)}85%{opacity:1;filter:brightness(1.3)}to{opacity:1;filter:brightness()}}.tab-activating{animation:.28s ease-out forwards tab-activate-flicker}@media (prefers-reduced-motion:reduce){.badge-live,.energy-live,.workrail-current-lineage-node,.workrail-current-lineage-node:after,.workrail-current-lineage-edge .react-flow__edge-path,.tab-activating{animation:none!important}.tab-corner{transition:none}}@keyframes modal-exit-to-top{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-14px)}}@keyframes modal-exit-to-bottom{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(14px)}}@keyframes modal-enter-from-below{0%{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}@keyframes modal-enter-from-above{0%{opacity:0;transform:translateY(-14px)}to{opacity:1;transform:translateY(0)}}@keyframes modal-exit-to-left{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(-20px)}}@keyframes modal-exit-to-right{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(20px)}}@keyframes modal-enter-from-right{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}@keyframes modal-enter-from-left{0%{opacity:0;transform:translate(-20px)}to{opacity:1;transform:translate(0)}}@keyframes modal-border-flash{0%{filter:brightness()}15%{filter:brightness(1.8)}50%{filter:brightness(1.3)}to{filter:brightness()}}@keyframes modal-crt-fade{0%{opacity:0}20%{opacity:.65}70%{opacity:.65}to{opacity:0}}.modal-content--exit-v-next{animation:80ms ease-in forwards modal-exit-to-top}.modal-content--exit-v-prev{animation:80ms ease-in forwards modal-exit-to-bottom}.modal-content--enter-v-next{animation:.16s ease-out forwards modal-enter-from-below}.modal-content--enter-v-prev{animation:.16s ease-out forwards modal-enter-from-above}.modal-content--exit-h-next{animation:80ms ease-in forwards modal-exit-to-left}.modal-content--exit-h-prev{animation:80ms ease-in forwards modal-exit-to-right}.modal-content--enter-h-next{animation:.16s ease-out forwards modal-enter-from-right}.modal-content--enter-h-prev{animation:.16s ease-out forwards modal-enter-from-left}.modal-border-flashing{animation:.2s ease-out forwards modal-border-flash}.modal-scanline{pointer-events:none;z-index:10;background:linear-gradient(to bottom, transparent calc(var(--glitch-y,38%) - 1px), #b4d2ff59 calc(var(--glitch-y,38%) - 1px), #b4d2ff59 calc(var(--glitch-y,38%) + var(--glitch-w,3px)), transparent calc(var(--glitch-y,38%) + var(--glitch-w,3px))), linear-gradient(to bottom, transparent calc(var(--glitch-y2,62%) - 1px), #f4c4304d calc(var(--glitch-y2,62%) - 1px), #f4c4304d calc(var(--glitch-y2,62%) + var(--glitch-w2,2px)), transparent calc(var(--glitch-y2,62%) + var(--glitch-w2,2px))), repeating-linear-gradient(to bottom, transparent 0px, transparent 2px, #0000008c 2px, #0000008c 3px), repeating-linear-gradient(to bottom, transparent 0px, transparent 5px, #00000038 5px, #00000042 7px), repeating-linear-gradient(to bottom, transparent 0px, transparent 3px, #0000001f 3px, #0000001f 5px);background-position-y:0, 0, var(--crt-offset,0px), calc(var(--crt-offset,0px) + 2px), calc(var(--crt-offset,0px) + 4px);opacity:0;animation:.22s linear forwards modal-crt-fade;position:absolute;inset:0}.modal-scanline--hidden{display:none}@media (prefers-reduced-motion:reduce){.modal-content--exit-v-next,.modal-content--exit-v-prev,.modal-content--enter-v-next,.modal-content--enter-v-prev,.modal-content--exit-h-next,.modal-content--exit-h-prev,.modal-content--enter-h-next,.modal-content--enter-h-prev{opacity:1!important;animation:none!important;transform:none!important}.modal-scanline{display:none}.modal-border-flashing{filter:none!important;animation:none!important}}@keyframes hero-card-glow-breathe{0%,to{filter:drop-shadow(0 0 12px #00afc033)}50%{filter:drop-shadow(0 0 28px #00afc08c)}}.hero-card-breathing{animation:3s ease-in-out infinite hero-card-glow-breathe}@media (prefers-reduced-motion:reduce){.hero-card-breathing{animation:none}}@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-outline-style{syntax:"*";inherits:false;initial-value:solid}@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-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{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-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/console/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/console/assets/index-CQt4UhPB.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/console/assets/index-DGj8EsFR.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -5,6 +5,7 @@ export interface ReviewFindings {
|
|
|
5
5
|
readonly severity: ReviewSeverity;
|
|
6
6
|
readonly findingSummaries: readonly string[];
|
|
7
7
|
readonly raw: string;
|
|
8
|
+
readonly source?: 'artifact' | 'keyword_scan';
|
|
8
9
|
}
|
|
9
10
|
export interface PrSummary {
|
|
10
11
|
readonly number: number;
|
|
@@ -37,15 +38,28 @@ export interface PrReviewOpts {
|
|
|
37
38
|
export interface CoordinatorDeps {
|
|
38
39
|
readonly spawnSession: (workflowId: string, goal: string, workspace: string) => Promise<Result<string, string>>;
|
|
39
40
|
readonly awaitSessions: (handles: readonly string[], timeoutMs: number) => Promise<AwaitResult>;
|
|
40
|
-
readonly getAgentResult: (sessionHandle: string) => Promise<
|
|
41
|
+
readonly getAgentResult: (sessionHandle: string) => Promise<{
|
|
42
|
+
recapMarkdown: string | null;
|
|
43
|
+
artifacts: readonly unknown[];
|
|
44
|
+
}>;
|
|
41
45
|
readonly listOpenPRs: (workspace: string) => Promise<PrSummary[]>;
|
|
42
46
|
readonly mergePR: (prNumber: number, workspace: string) => Promise<Result<void, string>>;
|
|
43
47
|
readonly writeFile: (path: string, content: string) => Promise<void>;
|
|
44
48
|
readonly stderr: (line: string) => void;
|
|
45
49
|
readonly now: () => number;
|
|
46
50
|
readonly port: number;
|
|
51
|
+
readonly readFile: (path: string) => Promise<string>;
|
|
52
|
+
readonly appendFile: (path: string, content: string) => Promise<void>;
|
|
53
|
+
readonly mkdir: (path: string, options: {
|
|
54
|
+
recursive: boolean;
|
|
55
|
+
}) => Promise<string | undefined>;
|
|
56
|
+
readonly homedir: () => string;
|
|
57
|
+
readonly joinPath: (...paths: string[]) => string;
|
|
58
|
+
readonly nowIso: () => string;
|
|
59
|
+
readonly generateId: () => string;
|
|
47
60
|
}
|
|
48
61
|
export declare function parseFindingsFromNotes(notes: string | null): Result<ReviewFindings, string>;
|
|
62
|
+
export declare function readVerdictArtifact(artifacts: readonly unknown[], sessionHandle?: string): ReviewFindings | null;
|
|
49
63
|
export declare function buildFixGoal(prNumber: number, findings: ReviewFindings): string;
|
|
50
64
|
export declare function formatElapsed(ms: number): string;
|
|
51
65
|
export declare function discoverConsolePort(deps: Pick<CoordinatorPortDiscoveryDeps, 'readFile' | 'homedir' | 'joinPath'>, portOverride?: number): Promise<number>;
|
|
@@ -54,4 +68,12 @@ export interface CoordinatorPortDiscoveryDeps {
|
|
|
54
68
|
readonly homedir: () => string;
|
|
55
69
|
readonly joinPath: (...paths: string[]) => string;
|
|
56
70
|
}
|
|
71
|
+
export interface DrainResult {
|
|
72
|
+
readonly stop: boolean;
|
|
73
|
+
readonly stopReason: string | null;
|
|
74
|
+
readonly skipPrNumbers: readonly number[];
|
|
75
|
+
readonly addPrNumbers: readonly number[];
|
|
76
|
+
readonly messagesProcessed: number;
|
|
77
|
+
}
|
|
78
|
+
export declare function drainMessageQueue(deps: Pick<CoordinatorDeps, 'readFile' | 'appendFile' | 'writeFile' | 'mkdir' | 'homedir' | 'joinPath' | 'nowIso' | 'generateId' | 'stderr'>): Promise<DrainResult>;
|
|
57
79
|
export declare function runPrReviewCoordinator(deps: CoordinatorDeps, opts: PrReviewOpts): Promise<CoordinatorResult>;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseFindingsFromNotes = parseFindingsFromNotes;
|
|
4
|
+
exports.readVerdictArtifact = readVerdictArtifact;
|
|
4
5
|
exports.buildFixGoal = buildFixGoal;
|
|
5
6
|
exports.formatElapsed = formatElapsed;
|
|
6
7
|
exports.discoverConsolePort = discoverConsolePort;
|
|
8
|
+
exports.drainMessageQueue = drainMessageQueue;
|
|
7
9
|
exports.runPrReviewCoordinator = runPrReviewCoordinator;
|
|
8
10
|
const result_js_1 = require("../runtime/result.js");
|
|
11
|
+
const review_verdict_js_1 = require("../v2/durable-core/schemas/artifacts/review-verdict.js");
|
|
9
12
|
const MAX_FIX_PASSES = 3;
|
|
10
13
|
const CHILD_SESSION_TIMEOUT_MS = 15 * 60 * 1000;
|
|
11
14
|
const COORDINATOR_MAX_MS = 90 * 60 * 1000;
|
|
@@ -48,6 +51,7 @@ function parseFindingsFromNotes(notes) {
|
|
|
48
51
|
severity: 'blocking',
|
|
49
52
|
findingSummaries: extractFindingSummaries(notes),
|
|
50
53
|
raw: notes,
|
|
54
|
+
source: 'keyword_scan',
|
|
51
55
|
});
|
|
52
56
|
}
|
|
53
57
|
const hasCleanKeyword = upperNotes.includes('APPROVE') ||
|
|
@@ -64,6 +68,7 @@ function parseFindingsFromNotes(notes) {
|
|
|
64
68
|
severity: 'clean',
|
|
65
69
|
findingSummaries: [],
|
|
66
70
|
raw: notes,
|
|
71
|
+
source: 'keyword_scan',
|
|
67
72
|
});
|
|
68
73
|
}
|
|
69
74
|
if (hasMinorKeyword) {
|
|
@@ -71,14 +76,39 @@ function parseFindingsFromNotes(notes) {
|
|
|
71
76
|
severity: 'minor',
|
|
72
77
|
findingSummaries: extractFindingSummaries(notes),
|
|
73
78
|
raw: notes,
|
|
79
|
+
source: 'keyword_scan',
|
|
74
80
|
});
|
|
75
81
|
}
|
|
76
82
|
return (0, result_js_1.ok)({
|
|
77
83
|
severity: 'unknown',
|
|
78
84
|
findingSummaries: [],
|
|
79
85
|
raw: notes,
|
|
86
|
+
source: 'keyword_scan',
|
|
80
87
|
});
|
|
81
88
|
}
|
|
89
|
+
function readVerdictArtifact(artifacts, sessionHandle) {
|
|
90
|
+
const handlePrefix = sessionHandle ? sessionHandle.slice(0, 16) : 'unknown';
|
|
91
|
+
for (const raw of artifacts) {
|
|
92
|
+
if (!(0, review_verdict_js_1.isReviewVerdictArtifact)(raw))
|
|
93
|
+
continue;
|
|
94
|
+
const result = review_verdict_js_1.ReviewVerdictArtifactV1Schema.safeParse(raw);
|
|
95
|
+
if (!result.success) {
|
|
96
|
+
const issues = result.error.issues
|
|
97
|
+
.map((i) => `${i.path.join('.')}: ${i.message}`)
|
|
98
|
+
.join('; ');
|
|
99
|
+
process.stderr.write(`[WARN coord:reason=artifact_parse_failed handle=${handlePrefix}] readVerdictArtifact: wr.review_verdict schema validation failed: ${issues}\n`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const v = result.data;
|
|
103
|
+
return {
|
|
104
|
+
severity: v.verdict,
|
|
105
|
+
findingSummaries: v.findings.map((f) => f.summary),
|
|
106
|
+
raw: JSON.stringify(v),
|
|
107
|
+
source: 'artifact',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
82
112
|
function extractFindingSummaries(notes) {
|
|
83
113
|
const summaries = [];
|
|
84
114
|
const lines = notes.split('\n');
|
|
@@ -132,6 +162,126 @@ async function discoverConsolePort(deps, portOverride) {
|
|
|
132
162
|
}
|
|
133
163
|
return DEFAULT_CONSOLE_PORT;
|
|
134
164
|
}
|
|
165
|
+
async function drainMessageQueue(deps) {
|
|
166
|
+
const workrailDir = deps.joinPath(deps.homedir(), '.workrail');
|
|
167
|
+
const queuePath = deps.joinPath(workrailDir, 'message-queue.jsonl');
|
|
168
|
+
const cursorPath = deps.joinPath(workrailDir, 'message-queue-cursor.json');
|
|
169
|
+
const outboxPath = deps.joinPath(workrailDir, 'outbox.jsonl');
|
|
170
|
+
let queueContent;
|
|
171
|
+
try {
|
|
172
|
+
queueContent = await deps.readFile(queuePath);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (isEnoentError(err)) {
|
|
176
|
+
return { stop: false, stopReason: null, skipPrNumbers: [], addPrNumbers: [], messagesProcessed: 0 };
|
|
177
|
+
}
|
|
178
|
+
deps.stderr(`[WARN coord:drain reason=read_failed] drainMessageQueue: could not read message queue: ${err instanceof Error ? err.message : String(err)}`);
|
|
179
|
+
return { stop: false, stopReason: null, skipPrNumbers: [], addPrNumbers: [], messagesProcessed: 0 };
|
|
180
|
+
}
|
|
181
|
+
const allLines = queueContent.split('\n').filter((line) => line.trim() !== '');
|
|
182
|
+
const parsedMessages = [];
|
|
183
|
+
for (const line of allLines) {
|
|
184
|
+
try {
|
|
185
|
+
const msg = JSON.parse(line);
|
|
186
|
+
parsedMessages.push(msg);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
deps.stderr(`[WARN coord:drain reason=malformed_line] drainMessageQueue: skipped malformed JSONL line`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const totalLines = parsedMessages.length;
|
|
193
|
+
let lastReadCount = 0;
|
|
194
|
+
try {
|
|
195
|
+
const cursorContent = await deps.readFile(cursorPath);
|
|
196
|
+
const cursor = JSON.parse(cursorContent);
|
|
197
|
+
if (typeof cursor.lastReadCount === 'number' && cursor.lastReadCount >= 0) {
|
|
198
|
+
lastReadCount = cursor.lastReadCount;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
lastReadCount = 0;
|
|
203
|
+
}
|
|
204
|
+
if (lastReadCount > totalLines) {
|
|
205
|
+
lastReadCount = 0;
|
|
206
|
+
}
|
|
207
|
+
const newMessages = parsedMessages.slice(lastReadCount);
|
|
208
|
+
let stop = false;
|
|
209
|
+
let stopReason = null;
|
|
210
|
+
const skipSet = new Set();
|
|
211
|
+
const addSet = new Set();
|
|
212
|
+
const outboxEntries = [];
|
|
213
|
+
const STOP_RE = /^\s*stop\b/i;
|
|
214
|
+
const SKIP_PR_RE = /\bskip[- ]pr[\s#]+([0-9]+)/i;
|
|
215
|
+
const ADD_PR_RE = /\badd[- ]pr[\s#]+([0-9]+)/i;
|
|
216
|
+
for (const msg of newMessages) {
|
|
217
|
+
const text = msg.message;
|
|
218
|
+
if (STOP_RE.test(text)) {
|
|
219
|
+
stop = true;
|
|
220
|
+
stopReason = text;
|
|
221
|
+
deps.stderr(`[INFO coord:drain kind=stop ts=${msg.timestamp}] drainMessageQueue: stop signal received -- message: "${text}"`);
|
|
222
|
+
outboxEntries.push(JSON.stringify({
|
|
223
|
+
id: deps.generateId(),
|
|
224
|
+
message: `WorkTrain coordinator stopped by queued message: "${text}" (queued at ${msg.timestamp})`,
|
|
225
|
+
timestamp: deps.nowIso(),
|
|
226
|
+
}) + '\n');
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const skipMatch = SKIP_PR_RE.exec(text);
|
|
230
|
+
if (skipMatch !== null) {
|
|
231
|
+
const prNum = parseInt(skipMatch[1], 10);
|
|
232
|
+
skipSet.add(prNum);
|
|
233
|
+
deps.stderr(`[INFO coord:drain kind=skip-pr prNumber=${prNum} ts=${msg.timestamp}] drainMessageQueue: skip-pr signal received -- message: "${text}"`);
|
|
234
|
+
outboxEntries.push(JSON.stringify({
|
|
235
|
+
id: deps.generateId(),
|
|
236
|
+
message: `WorkTrain coordinator skipping PR #${prNum} per queued message: "${text}" (queued at ${msg.timestamp})`,
|
|
237
|
+
timestamp: deps.nowIso(),
|
|
238
|
+
}) + '\n');
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const addMatch = ADD_PR_RE.exec(text);
|
|
242
|
+
if (addMatch !== null) {
|
|
243
|
+
const prNum = parseInt(addMatch[1], 10);
|
|
244
|
+
addSet.add(prNum);
|
|
245
|
+
deps.stderr(`[INFO coord:drain kind=add-pr prNumber=${prNum} ts=${msg.timestamp}] drainMessageQueue: add-pr signal received -- message: "${text}"`);
|
|
246
|
+
outboxEntries.push(JSON.stringify({
|
|
247
|
+
id: deps.generateId(),
|
|
248
|
+
message: `WorkTrain coordinator adding PR #${prNum} per queued message: "${text}" (queued at ${msg.timestamp})`,
|
|
249
|
+
timestamp: deps.nowIso(),
|
|
250
|
+
}) + '\n');
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (outboxEntries.length > 0) {
|
|
255
|
+
try {
|
|
256
|
+
await deps.mkdir(workrailDir, { recursive: true });
|
|
257
|
+
await deps.appendFile(outboxPath, outboxEntries.join(''));
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
deps.stderr(`[WARN coord:drain reason=outbox_write_failed] drainMessageQueue: could not write outbox notifications: ${err instanceof Error ? err.message : String(err)}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const newCursor = JSON.stringify({ lastReadCount: totalLines }, null, 2) + '\n';
|
|
264
|
+
try {
|
|
265
|
+
await deps.mkdir(workrailDir, { recursive: true });
|
|
266
|
+
await deps.writeFile(cursorPath, newCursor);
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
deps.stderr(`[WARN coord:drain reason=cursor_write_failed] drainMessageQueue: could not update cursor: ${err instanceof Error ? err.message : String(err)}`);
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
stop,
|
|
273
|
+
stopReason,
|
|
274
|
+
skipPrNumbers: [...skipSet],
|
|
275
|
+
addPrNumbers: [...addSet],
|
|
276
|
+
messagesProcessed: newMessages.length,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function isEnoentError(err) {
|
|
280
|
+
return (err !== null &&
|
|
281
|
+
typeof err === 'object' &&
|
|
282
|
+
'code' in err &&
|
|
283
|
+
err.code === 'ENOENT');
|
|
284
|
+
}
|
|
135
285
|
async function runPrReviewCoordinator(deps, opts) {
|
|
136
286
|
const coordinatorStartMs = deps.now();
|
|
137
287
|
const today = new Date(deps.now()).toISOString().slice(0, 10);
|
|
@@ -141,6 +291,30 @@ async function runPrReviewCoordinator(deps, opts) {
|
|
|
141
291
|
deps.stderr(line);
|
|
142
292
|
reportLines.push(line);
|
|
143
293
|
}
|
|
294
|
+
const drainResult = await drainMessageQueue(deps);
|
|
295
|
+
if (drainResult.messagesProcessed > 0) {
|
|
296
|
+
const skipStr = drainResult.skipPrNumbers.length > 0
|
|
297
|
+
? `, skip=[${drainResult.skipPrNumbers.join(',')}]`
|
|
298
|
+
: '';
|
|
299
|
+
const addStr = drainResult.addPrNumbers.length > 0
|
|
300
|
+
? `, add=[${drainResult.addPrNumbers.join(',')}]`
|
|
301
|
+
: '';
|
|
302
|
+
log(`[drain] processed ${drainResult.messagesProcessed} message(s)${skipStr}${addStr}${drainResult.stop ? ', STOP SIGNAL' : ''}`);
|
|
303
|
+
}
|
|
304
|
+
if (drainResult.stop) {
|
|
305
|
+
const stopMsg = drainResult.stopReason ?? 'stop signal in message queue';
|
|
306
|
+
log(` STOP: coordinator halted by queued message: "${stopMsg}"`);
|
|
307
|
+
const result = {
|
|
308
|
+
reviewed: 0,
|
|
309
|
+
approved: 0,
|
|
310
|
+
escalated: 0,
|
|
311
|
+
mergedPrs: [],
|
|
312
|
+
reportPath,
|
|
313
|
+
hasErrors: false,
|
|
314
|
+
};
|
|
315
|
+
await writeReport(deps, reportPath, reportLines, result);
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
144
318
|
log('[1/3] Gathering open PRs...');
|
|
145
319
|
const stageStart = deps.now();
|
|
146
320
|
let prs;
|
|
@@ -154,6 +328,25 @@ async function runPrReviewCoordinator(deps, opts) {
|
|
|
154
328
|
else {
|
|
155
329
|
prs = await deps.listOpenPRs(opts.workspace);
|
|
156
330
|
}
|
|
331
|
+
if (drainResult.addPrNumbers.length > 0) {
|
|
332
|
+
const existingNums = new Set(prs.map((p) => p.number));
|
|
333
|
+
for (const addNum of drainResult.addPrNumbers) {
|
|
334
|
+
if (!existingNums.has(addNum)) {
|
|
335
|
+
prs = [...prs, { number: addNum, title: `PR #${addNum}`, headRef: '' }];
|
|
336
|
+
existingNums.add(addNum);
|
|
337
|
+
log(` [drain] added PR #${addNum} from message queue`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (drainResult.skipPrNumbers.length > 0) {
|
|
342
|
+
const skipSet = new Set(drainResult.skipPrNumbers);
|
|
343
|
+
const prsBefore = prs.length;
|
|
344
|
+
prs = prs.filter((p) => !skipSet.has(p.number));
|
|
345
|
+
const skippedCount = prsBefore - prs.length;
|
|
346
|
+
if (skippedCount > 0) {
|
|
347
|
+
log(` [drain] skipped ${skippedCount} PR(s) from message queue: [${[...skipSet].join(',')}]`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
157
350
|
log(` done (${formatElapsed(deps.now() - stageStart)}) -- ${prs.length} PR(s) found`);
|
|
158
351
|
if (prs.length === 0) {
|
|
159
352
|
const result = {
|
|
@@ -220,10 +413,26 @@ async function runPrReviewCoordinator(deps, opts) {
|
|
|
220
413
|
const elapsedMs = sessionResult.durationMs;
|
|
221
414
|
const handle = sessionResult.handle;
|
|
222
415
|
let notes = null;
|
|
416
|
+
let artifacts = [];
|
|
223
417
|
if (sessionResult.outcome === 'success') {
|
|
224
|
-
|
|
418
|
+
const agentResult = await deps.getAgentResult(handle);
|
|
419
|
+
notes = agentResult.recapMarkdown;
|
|
420
|
+
artifacts = agentResult.artifacts;
|
|
225
421
|
}
|
|
226
|
-
const
|
|
422
|
+
const verdictFromArtifact = readVerdictArtifact(artifacts, handle);
|
|
423
|
+
const findingsResult = verdictFromArtifact !== null
|
|
424
|
+
? (() => {
|
|
425
|
+
deps.stderr(`[INFO coord:source=artifact handle=${handle.slice(0, 16)}] readVerdictArtifact succeeded`);
|
|
426
|
+
return (0, result_js_1.ok)(verdictFromArtifact);
|
|
427
|
+
})()
|
|
428
|
+
: (() => {
|
|
429
|
+
const keywordResult = parseFindingsFromNotes(notes);
|
|
430
|
+
if (keywordResult.kind === 'ok') {
|
|
431
|
+
const reason = artifacts.length > 0 ? 'no_valid_artifact' : 'no_artifacts';
|
|
432
|
+
deps.stderr(`[INFO coord:source=keyword_scan reason=${reason} artifactCount=${artifacts.length} handle=${handle.slice(0, 16)}]`);
|
|
433
|
+
}
|
|
434
|
+
return keywordResult;
|
|
435
|
+
})();
|
|
227
436
|
const severity = findingsResult.kind === 'ok'
|
|
228
437
|
? findingsResult.value.severity
|
|
229
438
|
: 'unknown';
|
|
@@ -440,8 +649,18 @@ async function runFixAgentLoop(deps, opts, pr, initialFindings, initialOutcome,
|
|
|
440
649
|
escalationReason: `re-review ${outcome} (pass ${passCount})`,
|
|
441
650
|
};
|
|
442
651
|
}
|
|
443
|
-
const
|
|
444
|
-
const
|
|
652
|
+
const reAgentResult = await deps.getAgentResult(reReviewHandle);
|
|
653
|
+
const reVerdictFromArtifact = readVerdictArtifact(reAgentResult.artifacts, reReviewHandle);
|
|
654
|
+
const reFindingsResult = reVerdictFromArtifact !== null
|
|
655
|
+
? (() => {
|
|
656
|
+
deps.stderr(`[INFO coord:source=artifact handle=${reReviewHandle.slice(0, 16)}] readVerdictArtifact succeeded (re-review pass ${passCount})`);
|
|
657
|
+
return (0, result_js_1.ok)(reVerdictFromArtifact);
|
|
658
|
+
})()
|
|
659
|
+
: (() => {
|
|
660
|
+
const reason = reAgentResult.artifacts.length > 0 ? 'no_valid_artifact' : 'no_artifacts';
|
|
661
|
+
deps.stderr(`[INFO coord:source=keyword_scan reason=${reason} artifactCount=${reAgentResult.artifacts.length} handle=${reReviewHandle.slice(0, 16)}]`);
|
|
662
|
+
return parseFindingsFromNotes(reAgentResult.recapMarkdown);
|
|
663
|
+
})();
|
|
445
664
|
const reSeverity = reFindingsResult.kind === 'ok'
|
|
446
665
|
? reFindingsResult.value.severity
|
|
447
666
|
: 'unknown';
|
|
@@ -477,7 +696,7 @@ async function runFixAgentLoop(deps, opts, pr, initialFindings, initialOutcome,
|
|
|
477
696
|
}
|
|
478
697
|
currentFindings = reFindingsResult.kind === 'ok'
|
|
479
698
|
? reFindingsResult.value
|
|
480
|
-
: { severity: 'minor', findingSummaries: [], raw:
|
|
699
|
+
: { severity: 'minor', findingSummaries: [], raw: reAgentResult.recapMarkdown ?? '' };
|
|
481
700
|
}
|
|
482
701
|
log(` PR #${pr.number} -> ${MAX_FIX_PASSES} fix passes exhausted, escalating`);
|
|
483
702
|
return {
|
|
@@ -101,6 +101,14 @@ export interface ToolCallFailedEvent {
|
|
|
101
101
|
readonly errorMessage: string;
|
|
102
102
|
readonly workrailSessionId?: string;
|
|
103
103
|
}
|
|
104
|
+
export interface SignalEmittedEvent {
|
|
105
|
+
readonly kind: 'signal_emitted';
|
|
106
|
+
readonly sessionId: string;
|
|
107
|
+
readonly signalKind: string;
|
|
108
|
+
readonly signalId: string;
|
|
109
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
110
|
+
readonly workrailSessionId?: string;
|
|
111
|
+
}
|
|
104
112
|
export interface AgentStuckEvent {
|
|
105
113
|
readonly kind: 'agent_stuck';
|
|
106
114
|
readonly sessionId: string;
|
|
@@ -110,7 +118,7 @@ export interface AgentStuckEvent {
|
|
|
110
118
|
readonly argsSummary?: string;
|
|
111
119
|
readonly workrailSessionId?: string;
|
|
112
120
|
}
|
|
113
|
-
export type DaemonEvent = DaemonStartedEvent | TriggerFiredEvent | SessionQueuedEvent | SessionStartedEvent | ToolCalledEvent | ToolErrorEvent | StepAdvancedEvent | SessionCompletedEvent | DeliveryAttemptedEvent | IssueReportedEvent | LlmTurnStartedEvent | LlmTurnCompletedEvent | ToolCallStartedEvent | ToolCallCompletedEvent | ToolCallFailedEvent | AgentStuckEvent;
|
|
121
|
+
export type DaemonEvent = DaemonStartedEvent | TriggerFiredEvent | SessionQueuedEvent | SessionStartedEvent | ToolCalledEvent | ToolErrorEvent | StepAdvancedEvent | SessionCompletedEvent | DeliveryAttemptedEvent | IssueReportedEvent | LlmTurnStartedEvent | LlmTurnCompletedEvent | ToolCallStartedEvent | ToolCallCompletedEvent | ToolCallFailedEvent | AgentStuckEvent | SignalEmittedEvent;
|
|
114
122
|
export declare class DaemonEventEmitter {
|
|
115
123
|
private readonly _dir;
|
|
116
124
|
constructor(dirOverride?: string);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const DAEMON_SOUL_DEFAULT = "- Write code that follows the patterns already established in the codebase\n- Never skip tests. Run existing tests before and after changes\n- Prefer small, focused changes over large rewrites\n- If a step asks you to write code, write actual code -- do not write pseudocode or placeholders\n- Commit your work when you complete a logical unit";
|
|
2
|
-
export declare const DAEMON_SOUL_TEMPLATE = "# WorkRail Daemon Soul\n#\n# This file is injected into every WorkRail Auto daemon session system prompt under\n# \"## Agent Rules and Philosophy\". Edit it to customize the agent's behavior for\n# your environment: coding conventions, commit style, tool preferences, etc.\n#\n# Changes take effect on the next daemon session -- no restart required.\n#\n# The defaults below reflect general best practices. Override them freely.\n\n- Write code that follows the patterns already established in the codebase\n- Never skip tests. Run existing tests before and after changes\n- Prefer small, focused changes over large rewrites\n- If a step asks you to write code, write actual code -- do not write pseudocode or placeholders\n- Commit your work when you complete a logical unit\n";
|
|
1
|
+
export declare const DAEMON_SOUL_DEFAULT = "- Write code that follows the patterns already established in the codebase\n- Never skip tests. Run existing tests before and after changes\n- Prefer small, focused changes over large rewrites\n- If a step asks you to write code, write actual code -- do not write pseudocode or placeholders\n- Commit your work when you complete a logical unit\n\n## File work\n- File search: Use Glob (NOT find or ls)\n- Content search: Use Grep (NOT grep or rg)\n- Read files: Use Read (NOT cat/head/tail)\n- Edit files: Use Edit (NOT sed/awk)\n- Write files: Use Write (NOT echo >/cat <<EOF)\n- Always Read a file before Edit. Edit requires the file to have been read in this session.\n- Use Edit for targeted in-place changes. Use Write only for new files or full rewrites.\n- Grep output_mode: use \"files_with_matches\" to find which files, then Read the relevant ones.";
|
|
2
|
+
export declare const DAEMON_SOUL_TEMPLATE = "# WorkRail Daemon Soul\n#\n# This file is injected into every WorkRail Auto daemon session system prompt under\n# \"## Agent Rules and Philosophy\". Edit it to customize the agent's behavior for\n# your environment: coding conventions, commit style, tool preferences, etc.\n#\n# Changes take effect on the next daemon session -- no restart required.\n#\n# The defaults below reflect general best practices. Override them freely.\n\n- Write code that follows the patterns already established in the codebase\n- Never skip tests. Run existing tests before and after changes\n- Prefer small, focused changes over large rewrites\n- If a step asks you to write code, write actual code -- do not write pseudocode or placeholders\n- Commit your work when you complete a logical unit\n\n## File work\n- File search: Use Glob (NOT find or ls)\n- Content search: Use Grep (NOT grep or rg)\n- Read files: Use Read (NOT cat/head/tail)\n- Edit files: Use Edit (NOT sed/awk)\n- Write files: Use Write (NOT echo >/cat <<EOF)\n- Always Read a file before Edit. Edit requires the file to have been read in this session.\n- Use Edit for targeted in-place changes. Use Write only for new files or full rewrites.\n- Grep output_mode: use \"files_with_matches\" to find which files, then Read the relevant ones.\n";
|
|
@@ -6,7 +6,17 @@ exports.DAEMON_SOUL_DEFAULT = `\
|
|
|
6
6
|
- Never skip tests. Run existing tests before and after changes
|
|
7
7
|
- Prefer small, focused changes over large rewrites
|
|
8
8
|
- If a step asks you to write code, write actual code -- do not write pseudocode or placeholders
|
|
9
|
-
- Commit your work when you complete a logical unit
|
|
9
|
+
- Commit your work when you complete a logical unit
|
|
10
|
+
|
|
11
|
+
## File work
|
|
12
|
+
- File search: Use Glob (NOT find or ls)
|
|
13
|
+
- Content search: Use Grep (NOT grep or rg)
|
|
14
|
+
- Read files: Use Read (NOT cat/head/tail)
|
|
15
|
+
- Edit files: Use Edit (NOT sed/awk)
|
|
16
|
+
- Write files: Use Write (NOT echo >/cat <<EOF)
|
|
17
|
+
- Always Read a file before Edit. Edit requires the file to have been read in this session.
|
|
18
|
+
- Use Edit for targeted in-place changes. Use Write only for new files or full rewrites.
|
|
19
|
+
- Grep output_mode: use "files_with_matches" to find which files, then Read the relevant ones.`;
|
|
10
20
|
exports.DAEMON_SOUL_TEMPLATE = `\
|
|
11
21
|
# WorkRail Daemon Soul
|
|
12
22
|
#
|
|
@@ -7,6 +7,11 @@ import type { V2StartWorkflowOutputSchema } from '../mcp/output-schemas.js';
|
|
|
7
7
|
import type { DaemonEventEmitter } from './daemon-events.js';
|
|
8
8
|
export declare const DAEMON_SESSIONS_DIR: string;
|
|
9
9
|
export { DAEMON_SOUL_DEFAULT, DAEMON_SOUL_TEMPLATE } from './soul-template.js';
|
|
10
|
+
export type ReadFileState = {
|
|
11
|
+
content: string;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
isPartialView: boolean;
|
|
14
|
+
};
|
|
10
15
|
export interface WorkflowTrigger {
|
|
11
16
|
readonly workflowId: string;
|
|
12
17
|
readonly goal: string;
|
|
@@ -29,6 +34,7 @@ export interface WorkflowRunSuccess {
|
|
|
29
34
|
readonly workflowId: string;
|
|
30
35
|
readonly stopReason: string;
|
|
31
36
|
readonly lastStepNotes?: string;
|
|
37
|
+
readonly lastStepArtifacts?: readonly unknown[];
|
|
32
38
|
}
|
|
33
39
|
export interface WorkflowRunError {
|
|
34
40
|
readonly _tag: 'error';
|
|
@@ -52,6 +58,7 @@ export interface WorkflowDeliveryFailed {
|
|
|
52
58
|
}
|
|
53
59
|
export type WorkflowRunResult = WorkflowRunSuccess | WorkflowRunError | WorkflowRunTimeout | WorkflowDeliveryFailed;
|
|
54
60
|
export type ChildWorkflowRunResult = WorkflowRunSuccess | WorkflowRunError | WorkflowRunTimeout;
|
|
61
|
+
export type SteerRegistry = Map<string, (text: string) => void>;
|
|
55
62
|
export interface OrphanedSession {
|
|
56
63
|
readonly sessionId: string;
|
|
57
64
|
readonly continueToken: string;
|
|
@@ -64,11 +71,18 @@ export declare function readDaemonSessionState(sessionId: string): Promise<{
|
|
|
64
71
|
} | null>;
|
|
65
72
|
export declare function readAllDaemonSessions(sessionsDir?: string): Promise<OrphanedSession[]>;
|
|
66
73
|
export declare function runStartupRecovery(sessionsDir?: string): Promise<void>;
|
|
67
|
-
export declare function makeContinueWorkflowTool(sessionId: string, ctx: V2ToolContext, onAdvance: (nextStepText: string, continueToken: string) => void, onComplete: (notes: string | undefined) => void, schemas: Record<string, any>, _executeContinueWorkflowFn?: typeof executeContinueWorkflow, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
68
|
-
export declare function makeCompleteStepTool(sessionId: string, ctx: V2ToolContext, getCurrentToken: () => string, onAdvance: (nextStepText: string, continueToken: string) => void, onComplete: (notes: string | undefined) => void, onTokenUpdate: (t: string) => void, schemas: Record<string, any>, _executeContinueWorkflowFn?: typeof executeContinueWorkflow, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
74
|
+
export declare function makeContinueWorkflowTool(sessionId: string, ctx: V2ToolContext, onAdvance: (nextStepText: string, continueToken: string) => void, onComplete: (notes: string | undefined, artifacts?: readonly unknown[]) => void, schemas: Record<string, any>, _executeContinueWorkflowFn?: typeof executeContinueWorkflow, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
75
|
+
export declare function makeCompleteStepTool(sessionId: string, ctx: V2ToolContext, getCurrentToken: () => string, onAdvance: (nextStepText: string, continueToken: string) => void, onComplete: (notes: string | undefined, artifacts?: readonly unknown[]) => void, onTokenUpdate: (t: string) => void, schemas: Record<string, any>, _executeContinueWorkflowFn?: typeof executeContinueWorkflow, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
69
76
|
export declare function makeBashTool(workspacePath: string, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
77
|
+
export declare function makeReadTool(readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
78
|
+
export declare function makeWriteTool(readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
79
|
+
export declare function makeGlobTool(workspacePath: string, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
80
|
+
export declare function makeGrepTool(workspacePath: string, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
81
|
+
export declare function makeEditTool(workspacePath: string, readFileState: Map<string, ReadFileState>, schemas: Record<string, any>, sessionId?: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null): AgentTool;
|
|
70
82
|
export declare function makeSpawnAgentTool(sessionId: string, ctx: V2ToolContext, apiKey: string, thisWorkrailSessionId: string, currentDepth: number, maxDepth: number, runWorkflowFn: typeof runWorkflow, schemas: Record<string, any>, emitter?: DaemonEventEmitter): AgentTool;
|
|
71
83
|
export declare function makeReportIssueTool(sessionId: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null, issuesDirOverride?: string, onIssueSummary?: (summary: string) => void): AgentTool;
|
|
84
|
+
export declare const DAEMON_SIGNALS_DIR: string;
|
|
85
|
+
export declare function makeSignalCoordinatorTool(sessionId: string, emitter?: DaemonEventEmitter, workrailSessionId?: string | null, signalsDirOverride?: string): AgentTool;
|
|
72
86
|
export declare function buildSessionRecap(notes: readonly string[]): string;
|
|
73
87
|
export declare function buildSystemPrompt(trigger: WorkflowTrigger, sessionState: string, soulContent: string, workspaceContext: string | null): string;
|
|
74
|
-
export declare function runWorkflow(trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, daemonRegistry?: DaemonRegistry, emitter?: DaemonEventEmitter): Promise<WorkflowRunResult>;
|
|
88
|
+
export declare function runWorkflow(trigger: WorkflowTrigger, ctx: V2ToolContext, apiKey: string, daemonRegistry?: DaemonRegistry, emitter?: DaemonEventEmitter, steerRegistry?: SteerRegistry): Promise<WorkflowRunResult>;
|