@tea-agent/loop-agent 0.40.0-next.9 → 0.41.0-beta.1
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 +1 -1
- package/CHANGELOG.md +130 -3
- package/README.md +2 -2
- package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
- package/dist/adapters/context-transfer/pi-session.js +61 -0
- package/dist/application/dag/generate-task-dag.js +6 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +21 -5
- package/dist/cli/program.js +61 -4
- package/dist/commands/client-recovery.js +8 -36
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/commands/init-model-catalog.js +22 -0
- package/dist/commands/task-advance.js +23 -1
- package/dist/commands/task-source-prepare.js +5 -0
- package/dist/commands/task-status.js +1 -0
- package/dist/executors/dag-pi-executor.js +39 -12
- package/dist/executors/pi-sdk-executor.js +36 -5
- package/dist/executors/shell-executor.js +64 -17
- package/dist/governance/checks.js +1 -0
- package/dist/{worker → infrastructure}/console/app-data.js +4 -0
- package/dist/infrastructure/console/artifact-revision-store.js +430 -0
- package/dist/infrastructure/console/context-export-store.js +160 -0
- package/dist/infrastructure/console/dir-lock.js +132 -0
- package/dist/{worker → infrastructure}/console/operation-store.js +28 -0
- package/dist/infrastructure/harness/artifact-store.js +10 -1
- package/dist/infrastructure/harness/atomic-write.js +12 -2
- package/dist/shared/context-transfer/artifact-revision.js +172 -0
- package/dist/shared/context-transfer.js +418 -0
- package/dist/shared/operator/capabilities.js +23 -1
- package/dist/shared/operator/safe-run-summary.js +1 -0
- package/dist/shared/path-safety.js +93 -0
- package/dist/shared/pi-context-pressure/checkpoint.js +116 -0
- package/dist/shared/pi-context-pressure/compaction-policy.js +151 -0
- package/dist/shared/pi-context-pressure/env.js +58 -0
- package/dist/shared/pi-context-pressure/extension.js +100 -0
- package/dist/shared/pi-context-pressure/index.js +7 -0
- package/dist/shared/pi-context-pressure/overflow.js +252 -0
- package/dist/shared/pi-context-pressure/sift-bridge.js +386 -0
- package/dist/shared/pi-context-pressure/telemetry.js +51 -0
- package/dist/shared/preview.js +28 -4
- package/dist/task/config-types.js +2 -1
- package/dist/task/task-demand-routing.js +3 -0
- package/dist/worker/console/chat/assistant-content.js +7 -0
- package/dist/worker/console/chat/chat-event-store.js +84 -13
- package/dist/worker/console/chat/goal-round-driver.js +72 -0
- package/dist/worker/console/chat/goals.js +300 -0
- package/dist/worker/console/chat/pi-runtime.js +285 -8
- package/dist/worker/console/chat/provider-error.js +2 -1
- package/dist/worker/console/chat/repo-browser.js +94 -12
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +487 -30
- package/dist/worker/console/chat/session-catalog.js +48 -0
- package/dist/worker/console/chat/session-stats.js +96 -0
- package/dist/worker/console/chat/session-store.js +28 -19
- package/dist/worker/console/chat/shortcuts.js +7 -0
- package/dist/worker/console/chat/sift-bridge.js +1 -0
- package/dist/worker/console/chat/turn-process.js +32 -3
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +21 -3
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +3 -3
- package/dist/worker/console/interview/assessment.js +1 -1
- package/dist/worker/console/interview/session.js +1 -1
- package/dist/worker/console/operation-runner.js +45 -4
- package/dist/worker/console/operation-sse.js +1 -1
- package/dist/worker/console/operation-wait.js +1 -1
- package/dist/worker/console/operator-actions.js +124 -8
- package/dist/worker/console/operator-selection.js +6 -2
- package/dist/worker/console/operator-surface-health.js +1 -1
- package/dist/worker/console/operator-user-error.js +169 -0
- package/dist/worker/console/pi-plugins.catalog.json +73658 -0
- package/dist/worker/console/pi-plugins.js +179 -0
- package/dist/worker/console/routes.js +864 -13
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +177 -44
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bs-CDAXM.js → abnfDiagram-N423BO3Z-DSVcg1YO.js} +1 -1
- package/dist/worker/console/static/assets/{arc-CAkA3We3.js → arc-BtXhIqui.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DEmI_zqr.js → architectureDiagram-T3A2C74G-B-nh-ilJ.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-uDHANebE.js → blockDiagram-VBNYF7ZC-BIUSVdZI.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Bv97b4_B.js → c4Diagram-5PPSVZJV-Cmw3L4qM.js} +1 -1
- package/dist/worker/console/static/assets/channel-BhZFNefx.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-shmQKsFz.js → chunk-2GRJ4B5K-CGpMMjGm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-BAraNvcN.js → chunk-2Q5K7J3B-BNBkEpEJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BirhxUop.js → chunk-5RXB4S5H-ubln3eaU.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C9LgUJpR.js → chunk-5VM5RSS4-_XcBTt1i.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DrU-s-t7.js → chunk-6Q2QTUOP-Ln30Pgrh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Tx-H_FA2.js → chunk-GF5L2VYU-ESH3lhSx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BjLtNFrR.js → chunk-JWPE2WC7-DAJt-qXB.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-UNLVwSsy.js → chunk-KBJHAD2P-Cx10LIcH.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CBEUX8XD.js → chunk-RYQCIY6F-CENeOxoJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-WTL7IZOq.js → chunk-XXDRQBXY-DXqK-D38.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Sbv-NJni.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Sbv-NJni.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CkHG6W3s.js → cose-bilkent-JH36ORCC-CE4dmvGY.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BRzFqTaL.js → cynefin-VYW2F7L2-CRacN1zK.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-2_CFp_i7.js → cynefinDiagram-MW4NZA55-Cneukjrt.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BJtsmdkE.js → dagre-VZM6K2ZE-DPFboRoL.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DNEFFo6w.js → diagram-7IWD3JNH-DIAQgcOP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B_IWV7eo.js → diagram-B4RE2ZJO-VrYMKxuy.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DvvRx2dz.js → diagram-LBJQPF4R-CLHvBghn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-CsibsWLg.js → diagram-Q27KOJAE-DVCWBif8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-les_djBQ.js → diagram-UB23O5K3-BTMmbBzJ.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BEm2n7qJ.js → ebnfDiagram-BXEA7PRR-BUZsKJ56.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BI7vHHVm.js → erDiagram-JOGREHBK-BsBZ5NXC.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CUFCERrB.js → flowDiagram-UKHOOZJN-D7fH_kDa.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-wxyLiGWt.js → ganttDiagram-PKOTCBZU-Bza5OohY.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bt897K8Z.js → gitGraphDiagram-DS77QQ5N-DpkoGR_Y.js} +1 -1
- package/dist/worker/console/static/assets/index-Ius3vxhf.css +1 -0
- package/dist/worker/console/static/assets/index-yASzcbBr.js +451 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DYNx_IPv.js → infoDiagram-6WML65LV-BYm3g6LT.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CzpLNEu6.js → ishikawaDiagram-WSZJBQD7-C2RS_H4v.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-97f9owNU.js → journeyDiagram-NVQOT4AX-B4slKpCo.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BxM9hyBn.js → kanban-definition-27J2QSJJ-B7orRNpI.js} +1 -1
- package/dist/worker/console/static/assets/{linear-ByuxcHvp.js → linear-D1BeBxLQ.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DwPGvpCv.js → mermaid.core-0TtMP8k4.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DCoqG9RT.js → mindmap-definition-FAOFIHXS-XL5EbCtT.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BLSBmBMx.js → pegDiagram-VL7TDLO6-CwF7uas5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B0t_0oJW.js → pieDiagram-7S7Q4E2Y-A_Jo-72f.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js → quadrantDiagram-CIZ2JOQS-CxOa4gKL.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-B3jRdATC.js → railroadDiagram-AXF67PYL-DoAoswIH.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-cTtXEN.js → requirementDiagram-LRYGKXZP-C1Lc1MlD.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B25igJRE.js → sankeyDiagram-W5VNT64P-DFXOkq0U.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-jW_lM1_D.js → sequenceDiagram-SI44F4Z6-B0ufUCuW.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CrzTgsHE.js → sizeCapture-X5ZJPWSS-iPbW55g3.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-B01MvlqE.js → stateDiagram-OKZ733FA-CHiXzEKP.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BtMgzo_H.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-YcF12FiU.js → swimlanes-SLNWSIFB-LyFs7TpX.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cb-OVd9Y.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ClnRcg2s.js → timeline-definition-Z64GVDOM-tNkJnb6z.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-B6gr_z-q.js → vennDiagram-T6HMQDX7-DFm7Oo8o.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CvSgguGH.js → wardleyDiagram-T6FBY63Y-tpblTp9i.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-23OtSZCb.js → xychartDiagram-ELKLHX3M-BCDLWR_D.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +95 -25
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +35 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +3 -1
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +26 -19
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +0 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +14 -1
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +34 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
- package/dist/worker/console/static-src/operator-chat/workspace-reorder.js +18 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
- package/dist/worker/console/static-src/shell/useWorkspaces.js +150 -34
- package/dist/worker/console/static-src/shell/workspace-route.js +3 -1
- package/dist/worker/console/workspace-context.js +56 -5
- package/dist/worker/console/workspace-registry.js +335 -62
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +51 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/inspect-bootstrap.d.ts +8 -0
- package/dist/worker/observe/static/inspect-bootstrap.js +57 -0
- package/dist/worker/observe/static/inspect-workspace.js +54 -10
- package/dist/worker/observe/static/operator-chrome.css +23 -4
- package/dist/worker/observe/static/operator-chrome.js +7 -2
- package/dist/worker/observe/static/state.js +3 -1
- package/dist/worker/observe/static/styles.css +1108 -135
- package/dist/worker/observe/static/views/dag-inspector.js +1257 -59
- package/dist/worker/observe/static/views/dags.js +7 -5
- package/dist/worker/observe/static/views/session-timeline.js +210 -10
- package/dist/worker/outcomes/adapters.js +19 -0
- package/dist/worker/outcomes/types.js +1 -0
- package/dist/worker/pool/attempt-lease.js +97 -66
- package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
- package/dist/worker/pool/reconcile.js +46 -1
- package/dist/worker/pool/run-store.js +2 -0
- package/dist/worker/pool/state-projection.js +2 -0
- package/dist/worker/task-spec/workflow-routing.js +8 -3
- package/dist/workflows/dag/artifact-bindings.js +149 -0
- package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +162 -8
- package/dist/workflows/dag/backend-test-pytest-collection.js +70 -2
- package/dist/workflows/dag/backend-test-result-contract.js +4 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
- package/dist/workflows/dag/backend-test-writer-completeness.js +11 -0
- package/dist/workflows/dag/budget-enforcement.js +10 -0
- package/dist/workflows/dag/context-receipt.js +305 -0
- package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
- package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
- package/dist/workflows/dag/context-transfer/renderers.js +93 -0
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +151 -4
- package/dist/workflows/dag/node-execution.js +112 -27
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +29 -4
- package/dist/workflows/dag/rerun-plan.js +555 -1
- package/dist/workflows/dag/rerun-run.js +601 -15
- package/dist/workflows/dag/runner.js +20 -1
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +183 -7
- package/docs/architecture/runtime-boundaries.md +8 -7
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/templates/agent-dag.schema.json +32 -0
- package/docs/templates/backend-test-dag.json +6 -5
- package/docs/templates/init-managed-agents.md +1 -1
- package/docs/templates/product-line/task.yaml +1 -1
- package/package.json +5 -2
- package/skills/loop-agent/references/command-reference.md +21 -1
- package/dist/worker/console/static/assets/channel-amxUpk7o.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +0 -1
- package/dist/worker/console/static/assets/index-BHhUOTri.css +0 -1
- package/dist/worker/console/static/assets/index-DqJbO3-p.js +0 -407
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +0 -8
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -4,7 +4,7 @@ import { describeMutationGateFailure, isMutationGateFailureCode, } from "../oper
|
|
|
4
4
|
/**
|
|
5
5
|
* Multi-workspace registry state (2026-08-23, AC-001/AC-003).
|
|
6
6
|
*
|
|
7
|
-
* Consumes the process-level GET/POST/DELETE /api/operator/v1/workspaces
|
|
7
|
+
* Consumes the process-level GET/POST/PATCH/DELETE /api/operator/v1/workspaces
|
|
8
8
|
* surface. POST/DELETE pass the Console mutation-gate confirmation token.
|
|
9
9
|
* The selected workspaceId is remembered in localStorage and validated
|
|
10
10
|
* against the visible list. A stale local preference falls back to the first
|
|
@@ -64,6 +64,11 @@ export function createWorkspacesController(params = {}) {
|
|
|
64
64
|
const storage = params.storage;
|
|
65
65
|
const token = params.confirmationTokenFn ?? confirmationToken;
|
|
66
66
|
const listeners = new Set();
|
|
67
|
+
let appliedRevision = 0;
|
|
68
|
+
let refreshQueued = false;
|
|
69
|
+
let refreshPromise = null;
|
|
70
|
+
let eventStream;
|
|
71
|
+
let disposed = false;
|
|
67
72
|
let state = {
|
|
68
73
|
workspaces: [],
|
|
69
74
|
loading: true,
|
|
@@ -79,44 +84,90 @@ export function createWorkspacesController(params = {}) {
|
|
|
79
84
|
state = { ...state, ...patch };
|
|
80
85
|
emit();
|
|
81
86
|
};
|
|
82
|
-
const refresh =
|
|
87
|
+
const refresh = () => {
|
|
88
|
+
refreshQueued = true;
|
|
89
|
+
if (refreshPromise)
|
|
90
|
+
return refreshPromise;
|
|
91
|
+
refreshPromise = (async () => {
|
|
92
|
+
while (refreshQueued && !disposed) {
|
|
93
|
+
refreshQueued = false;
|
|
94
|
+
try {
|
|
95
|
+
const res = await fetchImpl(`${origin}/api/operator/v1/workspaces`, {
|
|
96
|
+
credentials: "include",
|
|
97
|
+
});
|
|
98
|
+
if (!res.ok)
|
|
99
|
+
continue;
|
|
100
|
+
const body = (await res.json());
|
|
101
|
+
const revision = Number.isSafeInteger(body.revision)
|
|
102
|
+
? Math.max(0, body.revision ?? 0)
|
|
103
|
+
: undefined;
|
|
104
|
+
// Legacy/injected responses without revision remain usable. A
|
|
105
|
+
// revisioned response can never roll the projection backwards.
|
|
106
|
+
if (revision !== undefined && revision < appliedRevision)
|
|
107
|
+
continue;
|
|
108
|
+
if (revision !== undefined)
|
|
109
|
+
appliedRevision = revision;
|
|
110
|
+
const list = body.workspaces ?? [];
|
|
111
|
+
const pick = pickDefaultWorkspaceId(list, state.workspaceId);
|
|
112
|
+
if (pick.status === "unknown-workspace") {
|
|
113
|
+
setState({
|
|
114
|
+
workspaces: list,
|
|
115
|
+
unknownWorkspaceId: pick.workspaceId,
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const nextId = pick.workspaceId;
|
|
120
|
+
const previousId = state.workspaceId;
|
|
121
|
+
setState({
|
|
122
|
+
workspaces: list,
|
|
123
|
+
unknownWorkspaceId: null,
|
|
124
|
+
...(nextId !== previousId ? { workspaceId: nextId } : {}),
|
|
125
|
+
});
|
|
126
|
+
if (nextId !== previousId)
|
|
127
|
+
rememberWorkspaceId(nextId, storage);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
/* Registry unreachable: keep last known list. */
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
if (state.loading)
|
|
134
|
+
setState({ loading: false });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})().finally(() => {
|
|
138
|
+
refreshPromise = null;
|
|
139
|
+
// An event can arrive between the loop condition and finally.
|
|
140
|
+
if (refreshQueued && !disposed)
|
|
141
|
+
void refresh();
|
|
142
|
+
});
|
|
143
|
+
return refreshPromise;
|
|
144
|
+
};
|
|
145
|
+
const consumeRegistryEvent = (event) => {
|
|
83
146
|
try {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (!res.ok)
|
|
88
|
-
return;
|
|
89
|
-
const body = (await res.json());
|
|
90
|
-
const list = body.workspaces ?? [];
|
|
91
|
-
// A stale local preference falls back to the first server-visible
|
|
92
|
-
// Workspace. Explicit route ids are validated separately and remain
|
|
93
|
-
// fail-closed.
|
|
94
|
-
const pick = pickDefaultWorkspaceId(list, state.workspaceId);
|
|
95
|
-
if (pick.status === "unknown-workspace") {
|
|
96
|
-
setState({
|
|
97
|
-
workspaces: list,
|
|
98
|
-
unknownWorkspaceId: pick.workspaceId,
|
|
99
|
-
});
|
|
100
|
-
return;
|
|
147
|
+
const revision = Number(JSON.parse(event.data).revision);
|
|
148
|
+
if (Number.isSafeInteger(revision) && revision > appliedRevision) {
|
|
149
|
+
void refresh();
|
|
101
150
|
}
|
|
102
|
-
const nextId = pick.workspaceId;
|
|
103
|
-
const previousId = state.workspaceId;
|
|
104
|
-
setState({
|
|
105
|
-
workspaces: list,
|
|
106
|
-
unknownWorkspaceId: null,
|
|
107
|
-
...(nextId !== previousId ? { workspaceId: nextId } : {}),
|
|
108
|
-
});
|
|
109
|
-
if (nextId !== previousId)
|
|
110
|
-
rememberWorkspaceId(nextId, storage);
|
|
111
151
|
}
|
|
112
152
|
catch {
|
|
113
|
-
/*
|
|
114
|
-
}
|
|
115
|
-
finally {
|
|
116
|
-
if (state.loading)
|
|
117
|
-
setState({ loading: false });
|
|
153
|
+
/* Malformed frames are ignored; reconnect baseline remains authoritative. */
|
|
118
154
|
}
|
|
119
155
|
};
|
|
156
|
+
const startEventStream = () => {
|
|
157
|
+
if (eventStream || disposed)
|
|
158
|
+
return;
|
|
159
|
+
const factory = params.eventSourceFactory ??
|
|
160
|
+
(typeof EventSource !== "undefined"
|
|
161
|
+
? (url) => new EventSource(url, {
|
|
162
|
+
withCredentials: true,
|
|
163
|
+
})
|
|
164
|
+
: undefined);
|
|
165
|
+
if (!factory)
|
|
166
|
+
return;
|
|
167
|
+
eventStream = factory(`${origin}/api/operator/v1/workspaces/events`);
|
|
168
|
+
eventStream.addEventListener("workspace_registry_ready", consumeRegistryEvent);
|
|
169
|
+
eventStream.addEventListener("workspace_registry_changed", consumeRegistryEvent);
|
|
170
|
+
};
|
|
120
171
|
const addWorkspace = async (directoryPath, name) => {
|
|
121
172
|
setState({ error: null });
|
|
122
173
|
try {
|
|
@@ -214,12 +265,66 @@ export function createWorkspacesController(params = {}) {
|
|
|
214
265
|
return { ok: false, error: display };
|
|
215
266
|
}
|
|
216
267
|
};
|
|
268
|
+
const reorderWorkspace = async (workspaceId, beforeWorkspaceId) => {
|
|
269
|
+
const previous = state.workspaces;
|
|
270
|
+
const source = previous.find((workspace) => workspace.workspaceId === workspaceId);
|
|
271
|
+
const anchor = beforeWorkspaceId
|
|
272
|
+
? previous.find((workspace) => workspace.workspaceId === beforeWorkspaceId)
|
|
273
|
+
: undefined;
|
|
274
|
+
if (!source || (beforeWorkspaceId && !anchor)) {
|
|
275
|
+
return { ok: false, error: "工作区顺序已变化,请稍后重试。" };
|
|
276
|
+
}
|
|
277
|
+
const optimistic = previous.filter((workspace) => workspace.workspaceId !== workspaceId);
|
|
278
|
+
const insertAt = beforeWorkspaceId
|
|
279
|
+
? optimistic.findIndex((workspace) => workspace.workspaceId === beforeWorkspaceId)
|
|
280
|
+
: optimistic.length;
|
|
281
|
+
optimistic.splice(insertAt, 0, source);
|
|
282
|
+
if (optimistic.every((workspace, index) => workspace.workspaceId === previous[index]?.workspaceId)) {
|
|
283
|
+
return { ok: true };
|
|
284
|
+
}
|
|
285
|
+
setState({ workspaces: optimistic, error: null });
|
|
286
|
+
try {
|
|
287
|
+
const res = await fetchImpl(`${origin}/api/operator/v1/workspaces/${encodeURIComponent(workspaceId)}`, {
|
|
288
|
+
method: "PATCH",
|
|
289
|
+
credentials: "include",
|
|
290
|
+
headers: {
|
|
291
|
+
"Content-Type": "application/json",
|
|
292
|
+
"x-loop-console-confirmation": token(),
|
|
293
|
+
},
|
|
294
|
+
body: JSON.stringify({ beforeWorkspaceId }),
|
|
295
|
+
});
|
|
296
|
+
const body = (await res.json().catch(() => ({})));
|
|
297
|
+
if (!res.ok) {
|
|
298
|
+
const message = isMutationGateFailureCode(body.error?.code)
|
|
299
|
+
? describeMutationGateFailure(body.error?.code ?? "")
|
|
300
|
+
: (body.error?.message ?? `调整 Workspace 顺序失败(HTTP ${res.status})`);
|
|
301
|
+
setState({ workspaces: previous, error: message });
|
|
302
|
+
void refresh();
|
|
303
|
+
return { ok: false, error: message };
|
|
304
|
+
}
|
|
305
|
+
return { ok: true };
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
const message = `调整 Workspace 顺序失败:${error instanceof Error ? error.message : String(error)}`;
|
|
309
|
+
setState({ workspaces: previous, error: message });
|
|
310
|
+
void refresh();
|
|
311
|
+
return { ok: false, error: message };
|
|
312
|
+
}
|
|
313
|
+
};
|
|
217
314
|
const selectWorkspace = (next) => {
|
|
218
315
|
if (next === state.workspaceId && !state.unknownWorkspaceId)
|
|
219
316
|
return;
|
|
220
317
|
setState({ workspaceId: next, unknownWorkspaceId: null });
|
|
221
318
|
rememberWorkspaceId(next, storage);
|
|
222
319
|
};
|
|
320
|
+
const dispose = () => {
|
|
321
|
+
if (disposed)
|
|
322
|
+
return;
|
|
323
|
+
disposed = true;
|
|
324
|
+
eventStream?.close();
|
|
325
|
+
eventStream = undefined;
|
|
326
|
+
listeners.clear();
|
|
327
|
+
};
|
|
223
328
|
void refresh();
|
|
224
329
|
return {
|
|
225
330
|
get workspaces() {
|
|
@@ -241,10 +346,19 @@ export function createWorkspacesController(params = {}) {
|
|
|
241
346
|
pickWorkspaceDirectory,
|
|
242
347
|
addWorkspace,
|
|
243
348
|
removeWorkspace,
|
|
349
|
+
reorderWorkspace,
|
|
244
350
|
selectWorkspace,
|
|
351
|
+
dispose,
|
|
245
352
|
subscribe(listener) {
|
|
246
353
|
listeners.add(listener);
|
|
247
|
-
|
|
354
|
+
startEventStream();
|
|
355
|
+
return () => {
|
|
356
|
+
listeners.delete(listener);
|
|
357
|
+
if (listeners.size === 0) {
|
|
358
|
+
eventStream?.close();
|
|
359
|
+
eventStream = undefined;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
248
362
|
},
|
|
249
363
|
};
|
|
250
364
|
}
|
|
@@ -294,6 +408,8 @@ export function useWorkspaces(params) {
|
|
|
294
408
|
pickWorkspaceDirectory: controller.pickWorkspaceDirectory,
|
|
295
409
|
addWorkspace: controller.addWorkspace,
|
|
296
410
|
removeWorkspace: controller.removeWorkspace,
|
|
411
|
+
reorderWorkspace: controller.reorderWorkspace,
|
|
297
412
|
selectWorkspace: controller.selectWorkspace,
|
|
413
|
+
dispose: controller.dispose,
|
|
298
414
|
}), [state, controller]);
|
|
299
415
|
}
|
|
@@ -12,6 +12,7 @@ export const OPERATE_TAB_IDS = [
|
|
|
12
12
|
"night",
|
|
13
13
|
"models",
|
|
14
14
|
"settings",
|
|
15
|
+
"pi-plugins",
|
|
15
16
|
];
|
|
16
17
|
export const LAST_OPERATE_TAB_STORAGE_KEY = "loop-console.operator-shell.last-operate-tab.v1";
|
|
17
18
|
export const LAST_INSPECT_HASH_STORAGE_KEY = "loop-console.operator-shell.last-inspect-hash.v1";
|
|
@@ -44,7 +45,8 @@ function isOperateTab(value) {
|
|
|
44
45
|
value === "recovery" ||
|
|
45
46
|
value === "night" ||
|
|
46
47
|
value === "models" ||
|
|
47
|
-
value === "settings"
|
|
48
|
+
value === "settings" ||
|
|
49
|
+
value === "pi-plugins");
|
|
48
50
|
}
|
|
49
51
|
function readParam(query, key) {
|
|
50
52
|
const value = query.get(key)?.trim();
|
|
@@ -21,7 +21,7 @@ import { randomBytes } from "node:crypto";
|
|
|
21
21
|
import { existsSync } from "node:fs";
|
|
22
22
|
import path from "node:path";
|
|
23
23
|
import { LoopAgentClient } from "../loop-agent/loop-agent-client.js";
|
|
24
|
-
import { openConsoleAppData } from "
|
|
24
|
+
import { openConsoleAppData, } from "../../infrastructure/console/app-data.js";
|
|
25
25
|
import { DagConfirmationStore } from "./dag-confirmation.js";
|
|
26
26
|
import { DagExecutionReceiptStore } from "./dag-execution-receipt.js";
|
|
27
27
|
import { DraftStore } from "./draft-store.js";
|
|
@@ -33,8 +33,14 @@ import { createChatTurnExecutionRegistry } from "./chat/turn-execution-registry.
|
|
|
33
33
|
import { ConsolePiRuntime } from "./chat/pi-runtime.js";
|
|
34
34
|
import { UserQuestionRegistry } from "./chat/user-questions.js";
|
|
35
35
|
import { TodoListRegistry } from "./chat/todos.js";
|
|
36
|
+
import { GoalRegistry } from "./chat/goals.js";
|
|
37
|
+
import { GoalRoundDriver } from "./chat/goal-round-driver.js";
|
|
38
|
+
import { driveGoalRound, recoverDeferredTurns } from "./chat/routes.js";
|
|
36
39
|
import { createOperationEventStore, } from "./operation-sse.js";
|
|
37
|
-
import { OperationStore } from "
|
|
40
|
+
import { OperationStore } from "../../infrastructure/console/operation-store.js";
|
|
41
|
+
import { reconcileContextExports } from "../../infrastructure/console/context-export-store.js";
|
|
42
|
+
import { reconcileArtifactRevisions } from "../../infrastructure/console/artifact-revision-store.js";
|
|
43
|
+
import { recordContextTransferEvent } from "./context-transfer-diagnostics.js";
|
|
38
44
|
import { probePiReadiness, } from "./pi-readiness.js";
|
|
39
45
|
import { resolveSiblingLoopAgentBin } from "./sibling-controller.js";
|
|
40
46
|
import { NightAuxTicker } from "./night-aux-ticker.js";
|
|
@@ -77,6 +83,29 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
77
83
|
const assessments = new AssessmentStore(appData);
|
|
78
84
|
if (!deps.skipReconcile) {
|
|
79
85
|
await operations.reconcileOnBoot();
|
|
86
|
+
const [expiredExports, expiredRevisions] = await Promise.all([
|
|
87
|
+
reconcileContextExports({ appData }),
|
|
88
|
+
reconcileArtifactRevisions({ appData }),
|
|
89
|
+
]);
|
|
90
|
+
// Boot-time lifecycle evidence for what reconcile reclaimed. Only
|
|
91
|
+
// 32-hex ids are real export/revision identities; uploads/ and lock/
|
|
92
|
+
// entries are blob housekeeping and get no event.
|
|
93
|
+
await Promise.all([
|
|
94
|
+
...expiredExports.removed
|
|
95
|
+
.filter((exportId) => /^[a-f0-9]{32}$/.test(exportId))
|
|
96
|
+
.map((exportId) => recordContextTransferEvent({
|
|
97
|
+
appData,
|
|
98
|
+
event: "context_export.expired",
|
|
99
|
+
fields: { exportId },
|
|
100
|
+
}).catch(() => undefined)),
|
|
101
|
+
...expiredRevisions
|
|
102
|
+
.filter((revisionId) => /^[a-f0-9]{32}$/.test(revisionId))
|
|
103
|
+
.map((revisionId) => recordContextTransferEvent({
|
|
104
|
+
appData,
|
|
105
|
+
event: "artifact_revision.expired",
|
|
106
|
+
fields: { revisionId },
|
|
107
|
+
}).catch(() => undefined)),
|
|
108
|
+
]);
|
|
80
109
|
}
|
|
81
110
|
const getReadiness = () => readinessCache;
|
|
82
111
|
const refreshReadiness = async () => {
|
|
@@ -107,7 +136,16 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
107
136
|
fetchImpl: deps.fetchImpl,
|
|
108
137
|
getConsoleOrigin: deps.getConsoleOrigin,
|
|
109
138
|
};
|
|
110
|
-
const chatStore = new ChatSessionStore(appData
|
|
139
|
+
const chatStore = new ChatSessionStore(appData, {
|
|
140
|
+
onRecordChange: (sessionId, session) => {
|
|
141
|
+
if (session) {
|
|
142
|
+
deps.chatSessionCatalog?.upsert(session, record.workspaceId);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
deps.chatSessionCatalog?.remove(sessionId);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
});
|
|
111
149
|
const chatEventEpoch = randomBytes(12).toString("hex");
|
|
112
150
|
const chatEvents = createChatEventStore({
|
|
113
151
|
appData,
|
|
@@ -116,6 +154,7 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
116
154
|
const chatTurnExecutions = createChatTurnExecutionRegistry();
|
|
117
155
|
const questionRegistry = new UserQuestionRegistry(path.join(appData.chats, "user-questions"));
|
|
118
156
|
const todoRegistry = new TodoListRegistry();
|
|
157
|
+
const goalRegistry = new GoalRegistry(chatEvents);
|
|
119
158
|
questionRegistry.subscribe((change) => {
|
|
120
159
|
const question = change.question;
|
|
121
160
|
chatEvents.append(question.sessionId, question.turnId ?? `${question.sessionId}:question`, {
|
|
@@ -144,21 +183,27 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
144
183
|
actionContext,
|
|
145
184
|
questionRegistry,
|
|
146
185
|
todoRegistry,
|
|
186
|
+
goalRegistry,
|
|
147
187
|
});
|
|
148
|
-
|
|
188
|
+
let chatRouteDeps;
|
|
189
|
+
const goalRoundDriver = new GoalRoundDriver((sessionId) => driveGoalRound(chatRouteDeps, sessionId));
|
|
190
|
+
chatRouteDeps = {
|
|
149
191
|
runtime: chatRuntime,
|
|
150
192
|
store: chatStore,
|
|
151
193
|
events: chatEvents,
|
|
152
194
|
turnExecutions: chatTurnExecutions,
|
|
153
195
|
questionRegistry,
|
|
154
196
|
todoRegistry,
|
|
197
|
+
goalRegistry,
|
|
198
|
+
goalRoundDriver,
|
|
155
199
|
operationLinker: chatOperationLinker,
|
|
156
200
|
appData,
|
|
157
201
|
bootToken: deps.bootToken,
|
|
158
202
|
getConsoleOrigin: deps.getConsoleOrigin,
|
|
159
203
|
allowNonLoopbackAccess: deps.allowNonLoopbackAccess,
|
|
204
|
+
attachSession: (sessionId) => registry.attachSession(record.workspaceId, sessionId),
|
|
205
|
+
detachSession: (sessionId) => registry.detachSession(record.workspaceId, sessionId),
|
|
160
206
|
};
|
|
161
|
-
const { recoverDeferredTurns } = await import("./chat/routes.js");
|
|
162
207
|
const nightAuxTicker = new NightAuxTicker({ repoRoot, client });
|
|
163
208
|
const observeRouteContext = createObserveRouteContext(repoRoot, {
|
|
164
209
|
staticDir: deps.inspectStaticDir,
|
|
@@ -191,6 +236,12 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
191
236
|
if (disposed)
|
|
192
237
|
return;
|
|
193
238
|
disposed = true;
|
|
239
|
+
try {
|
|
240
|
+
goalRoundDriver.dispose();
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// best-effort
|
|
244
|
+
}
|
|
194
245
|
try {
|
|
195
246
|
chatOperationLinker.dispose();
|
|
196
247
|
}
|