@tea-agent/loop-agent 0.37.0 → 0.37.1-beta.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/AGENTS.md +2 -6
- package/CHANGELOG.md +71 -144
- package/README.md +1 -1
- package/bin/loop-agent.js +1 -37
- package/dist/application/task-lifecycle/advance.js +0 -1
- package/dist/application/task-lifecycle/observe.js +0 -15
- package/dist/application/task-lifecycle/plan-transitions.js +0 -15
- package/dist/cli/command-definitions.js +0 -7
- package/dist/cli/program.js +3 -8
- package/dist/commands/init-upgrade.js +19 -351
- package/dist/commands/init.js +67 -14
- package/dist/commands/run-dag-progress.js +0 -14
- package/dist/commands/task-advance.js +3 -33
- package/dist/executors/dag-pi-executor.js +30 -47
- package/dist/executors/pi-sdk-executor.js +0 -34
- package/dist/executors/shell-executor.js +95 -6
- package/dist/shared/operator/capabilities.js +13 -200
- package/dist/shared/package-metadata.js +0 -42
- package/dist/task/contract/project.js +0 -4
- package/dist/task/source-prepare/completeness.js +0 -53
- package/dist/task/source-prepare/parse-intent.js +12 -82
- package/dist/task/source-prepare/prepare.js +5 -102
- package/dist/task/source-prepare/semantic-intake.js +23 -144
- package/dist/worker/console/chat/artifact-card.js +1 -8
- package/dist/worker/console/chat/chat-event-store.js +0 -152
- package/dist/worker/console/chat/human-gate-card.js +1 -9
- package/dist/worker/console/chat/operation-card.js +2 -71
- package/dist/worker/console/chat/pi-runtime.js +62 -111
- package/dist/worker/console/chat/routes.js +10 -43
- package/dist/worker/console/chat/session-store.js +1 -9
- package/dist/worker/console/chat/shortcuts.js +7 -9
- package/dist/worker/console/chat/turn-process.js +23 -204
- package/dist/worker/console/chat/workspace-landing.js +1 -2
- package/dist/worker/console/operation-runner.js +6 -155
- package/dist/worker/console/operator-actions.js +14 -391
- package/dist/worker/console/operator-user-error.js +0 -4
- package/dist/worker/console/recovery-cta.js +3 -50
- package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
- package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -13
- package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
- package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
- package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
- package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
- package/dist/worker/loop-agent/loop-agent-client.js +3 -17
- package/dist/worker/observability/read-model.js +0 -20
- package/dist/worker/observe/spec-evidence.js +8 -3
- package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
- package/dist/worker/observe/static/operator-chrome.js +3 -10
- package/dist/worker/observe/static/views/dag-inspector.js +71 -6
- package/dist/worker/observe/static/views/dag.js +0 -12
- package/dist/worker/preflight.js +1 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
- package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
- package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
- package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
- package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
- package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
- package/dist/workflows/dag/frontend-repair.js +18 -219
- package/dist/workflows/dag/frontend-verification-trace.js +32 -47
- package/dist/workflows/dag/init-hybrid.js +82 -165
- package/dist/workflows/dag/node-execution.js +0 -89
- package/dist/workflows/dag/recovery-recommendation.js +0 -58
- package/dist/workflows/dag/runner.js +15 -333
- package/dist/workflows/dag/scheduler.js +3 -257
- package/dist/workflows/dag/types.js +9 -130
- package/docs/README.md +3 -3
- package/docs/architecture/evolution.md +67 -102
- package/docs/templates/backend-test-dag.json +50 -32
- package/docs/templates/init-managed-agents.md +2 -5
- package/harness.json +2 -2
- package/package.json +3 -9
- package/skills/loop-agent/SKILL.md +0 -1
- package/skills/loop-agent/references/command-reference.md +0 -4
- package/dist/build-stamp.json +0 -6
- package/dist/commands/dag-request-interrupt.js +0 -20
- package/dist/worker/console/chat/assistant-content.js +0 -121
- package/dist/worker/console/chat/semantic-activity.js +0 -477
- package/dist/worker/console/operation-run-facts.js +0 -190
- package/dist/worker/console/operation-wait.js +0 -355
- package/dist/worker/console/recovery-error-copy.js +0 -198
- package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
- package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
- package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
- package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
- package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
- package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
- package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
- package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
- package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
- package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
- package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
- package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
- package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
- package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
- package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
- package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
- package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
- package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
- package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
- package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
- package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
- package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
- package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
- package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
- package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
- package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
- package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
- package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
- package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
- package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
- package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
- package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
- package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
- package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
- package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
- package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
- package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
- package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
- package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
- package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
- package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
- package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
- package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
- package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
- package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
- package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
- package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
- package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
- package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
- package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
- package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
- package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
- package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
- package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
- package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
- package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
- package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
- package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
- package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
- package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
- package/dist/worker/observability/interrupt-eligibility.js +0 -264
- package/dist/workflows/dag/contract-output-registry.js +0 -14
- package/dist/workflows/dag/contract-validator-registrations.js +0 -8
- package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
- package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
- package/dist/workflows/dag/frontend-recovery-run.js +0 -539
- package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
- package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
- package/dist/workflows/dag/interrupt-request.js +0 -559
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { useLayoutEffect, useState } from "react";
|
|
2
|
-
export const ACTIVITY_RAIL_WIDTH = 420;
|
|
3
|
-
export const ACTIVITY_RAIL_GAP = 12;
|
|
4
|
-
/**
|
|
5
|
-
* Resolve Activity Rail placement from the actual free space to the right of
|
|
6
|
-
* ChatMain. Activity presence is deliberately not an input: session data may
|
|
7
|
-
* show/hide the Rail, but it must never change the conversation geometry.
|
|
8
|
-
*/
|
|
9
|
-
export function resolveActivityRailPresentation(input) {
|
|
10
|
-
if (input.layoutMode === "narrow")
|
|
11
|
-
return "sheet";
|
|
12
|
-
const railWidth = input.railWidth ?? ACTIVITY_RAIL_WIDTH;
|
|
13
|
-
const gap = input.gap ?? ACTIVITY_RAIL_GAP;
|
|
14
|
-
const available = input.viewportWidth - input.mainRight;
|
|
15
|
-
return available >= railWidth + gap ? "sidecar" : "drawer";
|
|
16
|
-
}
|
|
17
|
-
const INITIAL = {
|
|
18
|
-
presentation: "drawer",
|
|
19
|
-
sidecarRect: null,
|
|
20
|
-
};
|
|
21
|
-
/** Measure ChatMain without feeding Activity state back into the page layout. */
|
|
22
|
-
export function useActivityRailPresentation(mainRef, layoutMode, refreshKey) {
|
|
23
|
-
const [snapshot, setSnapshot] = useState(INITIAL);
|
|
24
|
-
useLayoutEffect(() => {
|
|
25
|
-
const main = mainRef.current;
|
|
26
|
-
if (!main)
|
|
27
|
-
return;
|
|
28
|
-
const verticalAnchor = main.closest(".panel--chat") ?? main;
|
|
29
|
-
let frame = 0;
|
|
30
|
-
const measure = () => {
|
|
31
|
-
window.cancelAnimationFrame(frame);
|
|
32
|
-
frame = window.requestAnimationFrame(() => {
|
|
33
|
-
const rect = main.getBoundingClientRect();
|
|
34
|
-
const verticalRect = verticalAnchor.getBoundingClientRect();
|
|
35
|
-
const presentation = resolveActivityRailPresentation({
|
|
36
|
-
layoutMode,
|
|
37
|
-
viewportWidth: window.innerWidth,
|
|
38
|
-
mainRight: rect.right,
|
|
39
|
-
});
|
|
40
|
-
const sidecarRect = presentation === "sidecar"
|
|
41
|
-
? {
|
|
42
|
-
top: verticalRect.top,
|
|
43
|
-
left: rect.right + ACTIVITY_RAIL_GAP,
|
|
44
|
-
height: verticalRect.height,
|
|
45
|
-
width: ACTIVITY_RAIL_WIDTH,
|
|
46
|
-
}
|
|
47
|
-
: null;
|
|
48
|
-
setSnapshot((current) => {
|
|
49
|
-
if (current.presentation === presentation &&
|
|
50
|
-
current.sidecarRect?.top === sidecarRect?.top &&
|
|
51
|
-
current.sidecarRect?.left === sidecarRect?.left &&
|
|
52
|
-
current.sidecarRect?.height === sidecarRect?.height &&
|
|
53
|
-
current.sidecarRect?.width === sidecarRect?.width) {
|
|
54
|
-
return current;
|
|
55
|
-
}
|
|
56
|
-
return { presentation, sidecarRect };
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
measure();
|
|
61
|
-
const observer = new ResizeObserver(measure);
|
|
62
|
-
observer.observe(main);
|
|
63
|
-
if (verticalAnchor !== main)
|
|
64
|
-
observer.observe(verticalAnchor);
|
|
65
|
-
window.addEventListener("resize", measure);
|
|
66
|
-
return () => {
|
|
67
|
-
window.cancelAnimationFrame(frame);
|
|
68
|
-
observer.disconnect();
|
|
69
|
-
window.removeEventListener("resize", measure);
|
|
70
|
-
};
|
|
71
|
-
}, [layoutMode, mainRef, refreshKey]);
|
|
72
|
-
return snapshot;
|
|
73
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export function operationActivityRefs(operation) {
|
|
2
|
-
return [
|
|
3
|
-
operation.activityRef ?? `operation:${operation.operationId}`,
|
|
4
|
-
`operation:${operation.operationId}`,
|
|
5
|
-
...(operation.toolCallId ? [`tool:${operation.toolCallId}`] : []),
|
|
6
|
-
];
|
|
7
|
-
}
|
|
8
|
-
export function humanGateActivityRefs(card) {
|
|
9
|
-
return [card.activityRef ?? `gate:${card.cardId}`];
|
|
10
|
-
}
|
|
11
|
-
export function artifactActivityRefs(card) {
|
|
12
|
-
return [card.activityRef ?? `artifact:${card.artifactId}`];
|
|
13
|
-
}
|
|
14
|
-
export function buildActivityTargetRefs(input) {
|
|
15
|
-
return new Set([
|
|
16
|
-
...input.operations.flatMap(operationActivityRefs),
|
|
17
|
-
...input.gates.flatMap(humanGateActivityRefs),
|
|
18
|
-
...input.artifacts.flatMap(artifactActivityRefs),
|
|
19
|
-
]);
|
|
20
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export const SHORTCUT_PALETTE_AVAILABLE_HEIGHT_VAR = "--oc-shortcut-palette-available-height";
|
|
2
|
-
export const SHORTCUT_PALETTE_TOP_INSET = 8;
|
|
3
|
-
export const SHORTCUT_PALETTE_ANCHOR_GAP = 8;
|
|
4
|
-
/**
|
|
5
|
-
* Resolve the real vertical room above the composer anchor. The palette is
|
|
6
|
-
* clipped by `.oc-main`, while a shifted VisualViewport (for example when the
|
|
7
|
-
* browser UI or virtual keyboard changes) can impose a lower visible top edge.
|
|
8
|
-
*/
|
|
9
|
-
export function resolveShortcutPaletteAvailableHeight(geometry) {
|
|
10
|
-
const viewportTop = geometry.viewportTop ?? 0;
|
|
11
|
-
const topInset = geometry.topInset ?? SHORTCUT_PALETTE_TOP_INSET;
|
|
12
|
-
const anchorGap = geometry.anchorGap ?? SHORTCUT_PALETTE_ANCHOR_GAP;
|
|
13
|
-
const values = [
|
|
14
|
-
geometry.boundaryTop,
|
|
15
|
-
geometry.anchorTop,
|
|
16
|
-
viewportTop,
|
|
17
|
-
topInset,
|
|
18
|
-
anchorGap,
|
|
19
|
-
];
|
|
20
|
-
if (values.some((value) => !Number.isFinite(value)))
|
|
21
|
-
return null;
|
|
22
|
-
const visibleTop = Math.max(geometry.boundaryTop, viewportTop);
|
|
23
|
-
return Math.max(0, Math.floor(geometry.anchorTop - visibleTop - topInset - anchorGap));
|
|
24
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slash palette display + navigation model (UI-12 card grid).
|
|
3
|
-
*
|
|
4
|
-
* Display order (SLASH_GROUP_DISPLAY_ORDER) is intentionally decoupled from
|
|
5
|
-
* the conflict/priority order in shortcuts.ts (SOURCE_PRIORITY): merge and
|
|
6
|
-
* dedupe keep SOURCE_PRIORITY semantics (skill 3 < prompt 4), while rendering
|
|
7
|
-
* follows the display order (prompt group before skill group). buildPaletteModel
|
|
8
|
-
* models that divergence explicitly — every card carries its flat-list
|
|
9
|
-
* globalIndex so keyboard/AT state round-trips from the rendered position back
|
|
10
|
-
* to filteredShortcuts[globalIndex]. shortcuts.ts itself stays untouched.
|
|
11
|
-
*
|
|
12
|
-
* All functions here are pure/DOM-free so the geometric and row-aware
|
|
13
|
-
* navigation can be unit-tested in node with fixture rects.
|
|
14
|
-
*/
|
|
15
|
-
import { SLASH_SOURCE_LABEL, } from "../../chat/shortcuts.js";
|
|
16
|
-
/** Group render order (independent from SOURCE_PRIORITY conflict order). */
|
|
17
|
-
export const SLASH_GROUP_DISPLAY_ORDER = [
|
|
18
|
-
"operator",
|
|
19
|
-
"web",
|
|
20
|
-
"extension",
|
|
21
|
-
"prompt",
|
|
22
|
-
"skill",
|
|
23
|
-
];
|
|
24
|
-
/** Group filtered candidates by display order, skipping empty groups. */
|
|
25
|
-
export function buildPaletteModel(entries) {
|
|
26
|
-
const groups = [];
|
|
27
|
-
for (const source of SLASH_GROUP_DISPLAY_ORDER) {
|
|
28
|
-
const items = [];
|
|
29
|
-
for (let i = 0; i < entries.length; i += 1) {
|
|
30
|
-
if (entries[i].source === source) {
|
|
31
|
-
items.push({ entry: entries[i], globalIndex: i });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
if (items.length === 0)
|
|
35
|
-
continue;
|
|
36
|
-
groups.push({ source, label: SLASH_SOURCE_LABEL[source], items });
|
|
37
|
-
}
|
|
38
|
-
return groups;
|
|
39
|
-
}
|
|
40
|
-
/** Keep the active option index inside the current candidate range. */
|
|
41
|
-
export function clampShortcutIndex(index, length) {
|
|
42
|
-
if (!Number.isFinite(length) || length <= 0)
|
|
43
|
-
return 0;
|
|
44
|
-
if (!Number.isFinite(index) || index < 0)
|
|
45
|
-
return 0;
|
|
46
|
-
if (index >= length)
|
|
47
|
-
return length - 1;
|
|
48
|
-
return index;
|
|
49
|
-
}
|
|
50
|
-
/** CSS grid columns per workspace layout mode (single source: layoutMode). */
|
|
51
|
-
export function paletteColumnCount(mode) {
|
|
52
|
-
if (mode === "comfortable")
|
|
53
|
-
return 3;
|
|
54
|
-
if (mode === "compact")
|
|
55
|
-
return 2;
|
|
56
|
-
return 1;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Group- and row-aware movement on the displayed card grid.
|
|
60
|
-
*
|
|
61
|
-
* Left/Right move to the adjacent card in the current row and stay inside the
|
|
62
|
-
* current group (row start/end stick; group headers are never entered; narrow
|
|
63
|
-
* 1-column layout is a no-op). Up/Down move within the same column to the
|
|
64
|
-
* adjacent row, may cross group boundaries, and stick at grid edges — never
|
|
65
|
-
* wrapping around and never leaving [0, entries.length - 1].
|
|
66
|
-
*/
|
|
67
|
-
export function movePaletteIndex(currentIndex, model, columns, direction) {
|
|
68
|
-
const groupPos = model.findIndex((group) => group.items.some((card) => card.globalIndex === currentIndex));
|
|
69
|
-
if (groupPos < 0)
|
|
70
|
-
return currentIndex;
|
|
71
|
-
const group = model[groupPos];
|
|
72
|
-
const offset = group.items.findIndex((card) => card.globalIndex === currentIndex);
|
|
73
|
-
if (offset < 0)
|
|
74
|
-
return currentIndex;
|
|
75
|
-
const row = Math.floor(offset / columns);
|
|
76
|
-
const col = offset % columns;
|
|
77
|
-
if (direction === "left" || direction === "right") {
|
|
78
|
-
if (columns <= 1)
|
|
79
|
-
return currentIndex;
|
|
80
|
-
const delta = direction === "left" ? -1 : 1;
|
|
81
|
-
const targetOffset = offset + delta;
|
|
82
|
-
if (targetOffset < 0 || targetOffset >= group.items.length) {
|
|
83
|
-
return currentIndex;
|
|
84
|
-
}
|
|
85
|
-
if (Math.floor(targetOffset / columns) !== row)
|
|
86
|
-
return currentIndex;
|
|
87
|
-
return group.items[targetOffset].globalIndex;
|
|
88
|
-
}
|
|
89
|
-
const delta = direction === "up" ? -1 : 1;
|
|
90
|
-
const targetOffset = offset + delta * columns;
|
|
91
|
-
if (targetOffset >= 0 && targetOffset < group.items.length) {
|
|
92
|
-
return group.items[targetOffset].globalIndex;
|
|
93
|
-
}
|
|
94
|
-
// Cross-group: walk to the previous/next non-empty group and target the
|
|
95
|
-
// same column on its last (up) / first (down) row; stick when that column
|
|
96
|
-
// does not exist in the partial edge row.
|
|
97
|
-
for (let cursor = groupPos + delta; cursor >= 0 && cursor < model.length; cursor += delta) {
|
|
98
|
-
const candidate = model[cursor];
|
|
99
|
-
if (candidate.items.length === 0)
|
|
100
|
-
continue;
|
|
101
|
-
const lastRowStart = Math.floor((candidate.items.length - 1) / columns) * columns;
|
|
102
|
-
const target = direction === "up" ? lastRowStart + col : col;
|
|
103
|
-
if (target >= candidate.items.length)
|
|
104
|
-
return currentIndex;
|
|
105
|
-
return candidate.items[target].globalIndex;
|
|
106
|
-
}
|
|
107
|
-
return currentIndex;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Geometry-first Up/Down targeting: from the active card's measured rect,
|
|
111
|
-
* pick the adjacent row's candidate whose horizontal center is nearest the
|
|
112
|
-
* active card's center (group boundaries may be crossed). Returns null when
|
|
113
|
-
* no candidate exists above/below (or the layout was not measured), so the
|
|
114
|
-
* caller falls back to movePaletteIndex. globalIndex comes from
|
|
115
|
-
* data-shortcut-index — never from the DOM list position.
|
|
116
|
-
*/
|
|
117
|
-
export function pickAdjacentRowTarget(activeRect, cells, direction) {
|
|
118
|
-
const activeCenterX = (activeRect.left + activeRect.right) / 2;
|
|
119
|
-
let best = null;
|
|
120
|
-
for (const cell of cells) {
|
|
121
|
-
if (cell.globalIndex === activeRect.globalIndex)
|
|
122
|
-
continue;
|
|
123
|
-
const gap = direction === "up"
|
|
124
|
-
? activeRect.top - cell.bottom
|
|
125
|
-
: cell.top - activeRect.bottom;
|
|
126
|
-
// Same-row cells overlap vertically (gap < 0) and must be ignored.
|
|
127
|
-
if (gap < 0)
|
|
128
|
-
continue;
|
|
129
|
-
const centerX = (cell.left + cell.right) / 2;
|
|
130
|
-
const dist = Math.abs(centerX - activeCenterX);
|
|
131
|
-
if (best === null ||
|
|
132
|
-
gap < best.gap ||
|
|
133
|
-
(gap === best.gap && dist < best.dist) ||
|
|
134
|
-
(gap === best.gap &&
|
|
135
|
-
dist === best.dist &&
|
|
136
|
-
cell.globalIndex < best.index)) {
|
|
137
|
-
best = { gap, dist, index: cell.globalIndex };
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return best?.index ?? null;
|
|
141
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
export const ACTIVITY_RAIL_ENTER_MS = 160;
|
|
3
|
-
export const ACTIVITY_RAIL_LEAVE_MS = 120;
|
|
4
|
-
export const ACTIVITY_RAIL_SWITCH_MS = 120;
|
|
5
|
-
export function activityRailSnapshotTotal(snapshot) {
|
|
6
|
-
if (!snapshot)
|
|
7
|
-
return 0;
|
|
8
|
-
return (snapshot.operationCards.length +
|
|
9
|
-
snapshot.humanGateCards.length +
|
|
10
|
-
snapshot.artifactCards.length);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Keep the Rail shell independent from ChatMain while sessions change. Old
|
|
14
|
-
* content is non-interactive during the short switching/leaving phase, then is
|
|
15
|
-
* replaced or removed without ever changing conversation geometry.
|
|
16
|
-
*/
|
|
17
|
-
export function useActivityRailTransition(current) {
|
|
18
|
-
const currentTotal = activityRailSnapshotTotal(current);
|
|
19
|
-
const [state, setState] = useState(() => currentTotal > 0
|
|
20
|
-
? { phase: "visible", snapshot: current }
|
|
21
|
-
: { phase: "hidden", snapshot: null });
|
|
22
|
-
const stateRef = useRef(state);
|
|
23
|
-
stateRef.current = state;
|
|
24
|
-
const currentKey = useMemo(() => `${current?.sessionId ?? "none"}:${currentTotal}`, [current?.sessionId, currentTotal]);
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
const timers = [];
|
|
27
|
-
const displayed = stateRef.current.snapshot;
|
|
28
|
-
if (!current || currentTotal === 0) {
|
|
29
|
-
if (!displayed) {
|
|
30
|
-
setState({ phase: "hidden", snapshot: null });
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
setState({ phase: "leaving", snapshot: displayed });
|
|
34
|
-
timers.push(window.setTimeout(() => {
|
|
35
|
-
setState({ phase: "hidden", snapshot: null });
|
|
36
|
-
}, ACTIVITY_RAIL_LEAVE_MS));
|
|
37
|
-
}
|
|
38
|
-
else if (!displayed) {
|
|
39
|
-
setState({ phase: "entering", snapshot: current });
|
|
40
|
-
timers.push(window.setTimeout(() => {
|
|
41
|
-
setState({ phase: "visible", snapshot: current });
|
|
42
|
-
}, ACTIVITY_RAIL_ENTER_MS));
|
|
43
|
-
}
|
|
44
|
-
else if (displayed.sessionId !== current.sessionId) {
|
|
45
|
-
setState({ phase: "switching", snapshot: current });
|
|
46
|
-
timers.push(window.setTimeout(() => {
|
|
47
|
-
setState({ phase: "visible", snapshot: current });
|
|
48
|
-
}, ACTIVITY_RAIL_SWITCH_MS));
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
setState({ phase: "visible", snapshot: current });
|
|
52
|
-
}
|
|
53
|
-
return () => {
|
|
54
|
-
for (const timer of timers)
|
|
55
|
-
window.clearTimeout(timer);
|
|
56
|
-
};
|
|
57
|
-
}, [current, currentKey, currentTotal]);
|
|
58
|
-
return state;
|
|
59
|
-
}
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import { hostname as localHostname } from "node:os";
|
|
2
|
-
import { DagInterruptError, INTERRUPT_ERROR_MESSAGES, projectedInterruptStatus, readInterruptRequest, runnerIdentitiesMatch, runnerIdentityFromState, } from "../../workflows/dag/interrupt-request.js";
|
|
3
|
-
import { assessDagRunLiveness, locateDagRun, readDagRunState, } from "../../workflows/dag/lifecycle.js";
|
|
4
|
-
function operationControllerFingerprint(operation) {
|
|
5
|
-
const identity = operation.controllerIdentity;
|
|
6
|
-
if (!identity)
|
|
7
|
-
return undefined;
|
|
8
|
-
if ("fingerprint" in identity && typeof identity.fingerprint === "string") {
|
|
9
|
-
const value = identity.fingerprint.trim();
|
|
10
|
-
return value || undefined;
|
|
11
|
-
}
|
|
12
|
-
if ("packageFingerprint" in identity &&
|
|
13
|
-
identity.packageFingerprint &&
|
|
14
|
-
typeof identity.packageFingerprint === "object" &&
|
|
15
|
-
"value" in identity.packageFingerprint &&
|
|
16
|
-
typeof identity.packageFingerprint.value === "string") {
|
|
17
|
-
const value = identity.packageFingerprint.value.trim();
|
|
18
|
-
return value || undefined;
|
|
19
|
-
}
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
function ineligible(partial) {
|
|
23
|
-
const code = partial.reasonCodes[0] ?? "RUN_NOT_ACTIVE";
|
|
24
|
-
return {
|
|
25
|
-
...partial,
|
|
26
|
-
eligible: false,
|
|
27
|
-
message: partial.message ?? INTERRUPT_ERROR_MESSAGES[code],
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function bindLiveOperations(input) {
|
|
31
|
-
return input.operations.filter((operation) => {
|
|
32
|
-
if (operation.state !== "running" && operation.state !== "starting") {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
if (operation.pid !== input.runner.pid)
|
|
36
|
-
return false;
|
|
37
|
-
if (operation.dagRunId && operation.dagRunId !== input.runId)
|
|
38
|
-
return false;
|
|
39
|
-
const fingerprint = operationControllerFingerprint(operation);
|
|
40
|
-
if (!fingerprint || fingerprint !== input.runner.controllerFingerprint) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
return true;
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
export async function evaluateConsoleDagInterruptEligibility(input) {
|
|
47
|
-
const runId = input.runId.trim();
|
|
48
|
-
if (!runId) {
|
|
49
|
-
return ineligible({
|
|
50
|
-
runId: "",
|
|
51
|
-
ownerKind: "unknown",
|
|
52
|
-
reasonCodes: ["INVALID_INPUT"],
|
|
53
|
-
interruptStatus: "none",
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
const located = await locateDagRun(input.repoRoot, runId);
|
|
57
|
-
if (!located) {
|
|
58
|
-
return ineligible({
|
|
59
|
-
runId,
|
|
60
|
-
ownerKind: "unknown",
|
|
61
|
-
reasonCodes: ["RUN_NOT_ACTIVE"],
|
|
62
|
-
interruptStatus: "none",
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
let state;
|
|
66
|
-
try {
|
|
67
|
-
state = await readDagRunState(located.runDir);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return ineligible({
|
|
71
|
-
runId,
|
|
72
|
-
lifecycle: located.lifecycle,
|
|
73
|
-
ownerKind: "unknown",
|
|
74
|
-
reasonCodes: ["RUN_NOT_ACTIVE"],
|
|
75
|
-
interruptStatus: "none",
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
let interrupt;
|
|
79
|
-
try {
|
|
80
|
-
interrupt = await readInterruptRequest(located.runDir);
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
interrupt = undefined;
|
|
84
|
-
}
|
|
85
|
-
const interruptStatus = projectedInterruptStatus(interrupt);
|
|
86
|
-
if (state.workerAssociation) {
|
|
87
|
-
return ineligible({
|
|
88
|
-
runId,
|
|
89
|
-
lifecycle: located.lifecycle,
|
|
90
|
-
dagStatus: state.status,
|
|
91
|
-
ownerKind: "worker-managed",
|
|
92
|
-
reasonCodes: ["RUN_NOT_CONSOLE_OWNED"],
|
|
93
|
-
interruptStatus,
|
|
94
|
-
interrupt,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
if (located.lifecycle !== "active" || state.status !== "running") {
|
|
98
|
-
const terminal = located.lifecycle === "completed" ||
|
|
99
|
-
located.lifecycle === "paused" ||
|
|
100
|
-
state.status === "paused" ||
|
|
101
|
-
state.status === "finished" ||
|
|
102
|
-
state.status === "failed" ||
|
|
103
|
-
state.status === "partial_failed" ||
|
|
104
|
-
state.status === "superseded" ||
|
|
105
|
-
state.status === "abandoned";
|
|
106
|
-
return ineligible({
|
|
107
|
-
runId,
|
|
108
|
-
lifecycle: located.lifecycle,
|
|
109
|
-
dagStatus: state.status,
|
|
110
|
-
ownerKind: "external-local",
|
|
111
|
-
reasonCodes: [terminal ? "RUN_ALREADY_TERMINAL" : "RUN_NOT_ACTIVE"],
|
|
112
|
-
interruptStatus,
|
|
113
|
-
interrupt,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
const runner = runnerIdentityFromState(state);
|
|
117
|
-
if (!runner) {
|
|
118
|
-
return ineligible({
|
|
119
|
-
runId,
|
|
120
|
-
lifecycle: located.lifecycle,
|
|
121
|
-
dagStatus: state.status,
|
|
122
|
-
ownerKind: "unknown",
|
|
123
|
-
reasonCodes: ["RUNNER_IDENTITY_MISMATCH"],
|
|
124
|
-
interruptStatus,
|
|
125
|
-
interrupt,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
if (runner.hostname !== (input.hostname ?? localHostname())) {
|
|
129
|
-
return ineligible({
|
|
130
|
-
runId,
|
|
131
|
-
lifecycle: located.lifecycle,
|
|
132
|
-
dagStatus: state.status,
|
|
133
|
-
ownerKind: "external-local",
|
|
134
|
-
reasonCodes: ["RUN_NOT_CONSOLE_OWNED"],
|
|
135
|
-
interruptStatus,
|
|
136
|
-
interrupt,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
const liveness = assessDagRunLiveness({
|
|
140
|
-
state,
|
|
141
|
-
hostname: input.hostname ?? localHostname(),
|
|
142
|
-
});
|
|
143
|
-
if (liveness.status === "orphaned" || liveness.status === "unknown-host") {
|
|
144
|
-
return ineligible({
|
|
145
|
-
runId,
|
|
146
|
-
lifecycle: located.lifecycle,
|
|
147
|
-
dagStatus: state.status,
|
|
148
|
-
ownerKind: "unknown",
|
|
149
|
-
reasonCodes: ["RUN_NOT_ACTIVE"],
|
|
150
|
-
interruptStatus,
|
|
151
|
-
interrupt,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
const liveOps = await input.operations.list();
|
|
155
|
-
const matches = bindLiveOperations({
|
|
156
|
-
operations: liveOps,
|
|
157
|
-
runId,
|
|
158
|
-
runner,
|
|
159
|
-
});
|
|
160
|
-
const targetFromInterrupt = interrupt?.targetOperationId
|
|
161
|
-
? (liveOps.find((op) => op.operationId === interrupt.targetOperationId) ??
|
|
162
|
-
(await input.operations.get(interrupt.targetOperationId)))
|
|
163
|
-
: undefined;
|
|
164
|
-
const targetOperationState = (matches.length === 1 ? matches[0]?.state : undefined) ??
|
|
165
|
-
targetFromInterrupt?.state;
|
|
166
|
-
const expected = {
|
|
167
|
-
pid: runner.pid,
|
|
168
|
-
hostname: runner.hostname,
|
|
169
|
-
startedAt: runner.startedAt,
|
|
170
|
-
controllerFingerprint: runner.controllerFingerprint,
|
|
171
|
-
};
|
|
172
|
-
if (interrupt &&
|
|
173
|
-
(interrupt.status === "requested" || interrupt.status === "acknowledged")) {
|
|
174
|
-
return ineligible({
|
|
175
|
-
runId,
|
|
176
|
-
lifecycle: located.lifecycle,
|
|
177
|
-
dagStatus: state.status,
|
|
178
|
-
failureCategory: state.failureCategory,
|
|
179
|
-
ownerKind: "console-operation",
|
|
180
|
-
targetOperationId: interrupt.targetOperationId,
|
|
181
|
-
targetOperationState,
|
|
182
|
-
expectedRunnerIdentity: runner,
|
|
183
|
-
reasonCodes: ["INTERRUPT_ALREADY_PENDING"],
|
|
184
|
-
interruptStatus,
|
|
185
|
-
interrupt,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
if (interrupt?.status === "needs-reconcile") {
|
|
189
|
-
return ineligible({
|
|
190
|
-
runId,
|
|
191
|
-
lifecycle: located.lifecycle,
|
|
192
|
-
dagStatus: state.status,
|
|
193
|
-
failureCategory: state.failureCategory,
|
|
194
|
-
ownerKind: "console-operation",
|
|
195
|
-
targetOperationId: interrupt.targetOperationId,
|
|
196
|
-
targetOperationState,
|
|
197
|
-
expectedRunnerIdentity: runner,
|
|
198
|
-
reasonCodes: ["INTERRUPT_NEEDS_RECONCILE"],
|
|
199
|
-
interruptStatus,
|
|
200
|
-
interrupt,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
if (matches.length === 0) {
|
|
204
|
-
return ineligible({
|
|
205
|
-
runId,
|
|
206
|
-
lifecycle: located.lifecycle,
|
|
207
|
-
dagStatus: state.status,
|
|
208
|
-
failureCategory: state.failureCategory,
|
|
209
|
-
ownerKind: "external-local",
|
|
210
|
-
reasonCodes: ["RUN_NOT_CONSOLE_OWNED"],
|
|
211
|
-
interruptStatus,
|
|
212
|
-
interrupt,
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
if (matches.length > 1) {
|
|
216
|
-
return ineligible({
|
|
217
|
-
runId,
|
|
218
|
-
lifecycle: located.lifecycle,
|
|
219
|
-
dagStatus: state.status,
|
|
220
|
-
failureCategory: state.failureCategory,
|
|
221
|
-
ownerKind: "unknown",
|
|
222
|
-
reasonCodes: ["RUNNER_IDENTITY_MISMATCH"],
|
|
223
|
-
message: "找到多个可能持有该运行的 Console operation,无法无歧义绑定。请用 Doctor 查看,不要猜测。",
|
|
224
|
-
interruptStatus,
|
|
225
|
-
interrupt,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
const target = matches[0];
|
|
229
|
-
if (target.pid !== expected.pid ||
|
|
230
|
-
!runnerIdentitiesMatch(expected, runner)) {
|
|
231
|
-
return ineligible({
|
|
232
|
-
runId,
|
|
233
|
-
lifecycle: located.lifecycle,
|
|
234
|
-
dagStatus: state.status,
|
|
235
|
-
failureCategory: state.failureCategory,
|
|
236
|
-
ownerKind: "unknown",
|
|
237
|
-
reasonCodes: ["RUNNER_IDENTITY_MISMATCH"],
|
|
238
|
-
interruptStatus,
|
|
239
|
-
interrupt,
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
return {
|
|
243
|
-
eligible: true,
|
|
244
|
-
runId,
|
|
245
|
-
lifecycle: located.lifecycle,
|
|
246
|
-
dagStatus: state.status,
|
|
247
|
-
failureCategory: state.failureCategory,
|
|
248
|
-
ownerKind: "console-operation",
|
|
249
|
-
targetOperationId: target.operationId,
|
|
250
|
-
targetOperationState: target.state,
|
|
251
|
-
expectedRunnerIdentity: expected,
|
|
252
|
-
reasonCodes: [],
|
|
253
|
-
message: "可以由当前 Console 协作中止此运行。",
|
|
254
|
-
interruptStatus,
|
|
255
|
-
interrupt,
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
export function interruptEligibilityError(eligibility) {
|
|
259
|
-
const code = eligibility.reasonCodes[0] ?? "RUN_NOT_ACTIVE";
|
|
260
|
-
return new DagInterruptError(code, eligibility.message);
|
|
261
|
-
}
|
|
262
|
-
// Console-side re-exports: keep the console layer free of direct workflows/dag
|
|
263
|
-
// kernel imports (see test/worker/console/security-and-tools.test.ts).
|
|
264
|
-
export { INTERRUPT_ERROR_MESSAGES, isInterruptReasonCode, normalizeInterruptReasonDetail, } from "../../workflows/dag/interrupt-request.js";
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { structuredContractOutputSchemaIds, } from "./types.js";
|
|
2
|
-
const validators = new Map();
|
|
3
|
-
export function registerStructuredContractValidator(schemaId, validator) {
|
|
4
|
-
if (validators.has(schemaId)) {
|
|
5
|
-
throw new Error(`structured contract validator already registered for ${schemaId}`);
|
|
6
|
-
}
|
|
7
|
-
validators.set(schemaId, validator);
|
|
8
|
-
}
|
|
9
|
-
export function getStructuredContractValidator(schemaId) {
|
|
10
|
-
if (structuredContractOutputSchemaIds.includes(schemaId)) {
|
|
11
|
-
return validators.get(schemaId);
|
|
12
|
-
}
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Side-effect module: registers every structured contract validator against
|
|
3
|
-
* its schemaId in the contract output registry. Imported once by the node
|
|
4
|
-
* executor; keeps per-contract imports out of the executor.
|
|
5
|
-
*/
|
|
6
|
-
import { validateFrontendContractNodeOutput } from "./frontend-implementation-contract.js";
|
|
7
|
-
import { registerStructuredContractValidator } from "./contract-output-registry.js";
|
|
8
|
-
registerStructuredContractValidator("frontend-implementation-contract-v1", validateFrontendContractNodeOutput);
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { computeResetClosure } from "./rerun-plan.js";
|
|
2
|
-
/**
|
|
3
|
-
* Frontend-only candidate-continuation recovery planning (AC-5).
|
|
4
|
-
*
|
|
5
|
-
* Distinct from the generic `dag rerun` reset closure: the failure source is
|
|
6
|
-
* derived from the prewrite-result's `selectedPlanNodeId` rather than an
|
|
7
|
-
* operator-selected node, and the closure starts at the specific frontend
|
|
8
|
-
* candidate producer that failed.
|
|
9
|
-
*/
|
|
10
|
-
/** Idempotency namespace for requestId-keyed recovery intents (D2). */
|
|
11
|
-
export const FRONTEND_RECOVERY_INTENT_REL_DIR = ".runtime/frontend-recovery";
|
|
12
|
-
/** Import manifest path inside the child run dir; hashed into the activation marker. */
|
|
13
|
-
export const FRONTEND_RECOVERY_IMPORT_MANIFEST_REL_PATH = ".runtime/import-manifest.json";
|
|
14
|
-
/** Staging directory prefix under `active/` for in-flight child materialization. */
|
|
15
|
-
export const FRONTEND_RECOVERY_STAGING_PREFIX = ".frontend-recovery-staging-";
|
|
16
|
-
export function deriveFrontendRecoveryFailureSource(result) {
|
|
17
|
-
if (result.selectedPlanNodeId === "frontend-plan-revision-pi") {
|
|
18
|
-
return "frontend-plan-revision-pi";
|
|
19
|
-
}
|
|
20
|
-
if (result.selectedPlanNodeId === "frontend-plan-pi") {
|
|
21
|
-
return "frontend-plan-pi";
|
|
22
|
-
}
|
|
23
|
-
return "frontend-prewrite-gate-shell";
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Compute the frontend candidate-continuation reset partition:
|
|
27
|
-
* - `frontend-plan-pi` failure → reset plan + design-review + prewrite (+ descendants);
|
|
28
|
-
* - `frontend-plan-revision-pi` failure → reset revision + final-review + prewrite (+ descendants);
|
|
29
|
-
* - prewrite-only materialization → reset prewrite (+ descendants), reuse verified plan/review.
|
|
30
|
-
*
|
|
31
|
-
* Everything upstream of the failure source is imported as verified parent facts.
|
|
32
|
-
*/
|
|
33
|
-
export function computeFrontendRecoveryPlan(input) {
|
|
34
|
-
const failureSource = deriveFrontendRecoveryFailureSource(input.result);
|
|
35
|
-
return computeFrontendRecoveryPlanForSource({
|
|
36
|
-
spec: input.spec,
|
|
37
|
-
parentRunId: input.parentRunId,
|
|
38
|
-
requestId: input.requestId,
|
|
39
|
-
failureSource,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Phase 5: writer transient partial-write recovery plan. The writer itself
|
|
44
|
-
* failed after some bounded writes, so the child re-runs `frontend-implement-pi`
|
|
45
|
-
* (and its descendants) while importing the verified contract/scout/plan/prewrite
|
|
46
|
-
* facts. Everything upstream of the writer is imported; the writer + descendants
|
|
47
|
-
* are reset.
|
|
48
|
-
*/
|
|
49
|
-
export function computeFrontendWriterRecoveryPlan(input) {
|
|
50
|
-
return computeFrontendRecoveryPlanForSource({
|
|
51
|
-
spec: input.spec,
|
|
52
|
-
parentRunId: input.parentRunId,
|
|
53
|
-
requestId: input.requestId,
|
|
54
|
-
failureSource: "frontend-implement-pi",
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
function computeFrontendRecoveryPlanForSource(input) {
|
|
58
|
-
const resetNodeIds = computeResetClosure(input.spec, input.failureSource);
|
|
59
|
-
const resetSet = new Set(resetNodeIds);
|
|
60
|
-
const importedNodeIds = input.spec.tasks
|
|
61
|
-
.map((task) => task.id)
|
|
62
|
-
.filter((nodeId) => !resetSet.has(nodeId))
|
|
63
|
-
.sort();
|
|
64
|
-
return {
|
|
65
|
-
schemaVersion: 1,
|
|
66
|
-
parentRunId: input.parentRunId,
|
|
67
|
-
requestId: input.requestId,
|
|
68
|
-
failureSource: input.failureSource,
|
|
69
|
-
resetNodeIds,
|
|
70
|
-
importedNodeIds,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
export { computeFrontendRecoveryPlanForSource };
|