@tea-agent/loop-agent 0.40.0-next.9 → 0.40.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 +1 -1
- package/CHANGELOG.md +21 -21
- package/README.md +2 -2
- package/dist/application/dag/generate-task-dag.js +6 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +9 -5
- package/dist/cli/program.js +38 -3
- package/dist/commands/client-recovery.js +8 -36
- 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/pi-sdk-executor.js +33 -5
- package/dist/executors/shell-executor.js +94 -20
- package/dist/governance/checks.js +1 -0
- package/dist/shared/operator/capabilities.js +9 -1
- 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/worker/console/chat/assistant-content.js +7 -0
- package/dist/worker/console/chat/chat-event-store.js +82 -11
- 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/routes.js +495 -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 +27 -18
- 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 +42 -5
- package/dist/worker/console/dag-execution-receipt.js +20 -2
- package/dist/worker/console/operator-actions.js +20 -5
- package/dist/worker/console/operator-selection.js +6 -2
- 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 +246 -6
- package/dist/worker/console/server.js +173 -42
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bs-CDAXM.js → abnfDiagram-N423BO3Z-Dy71QJp3.js} +1 -1
- package/dist/worker/console/static/assets/{arc-CAkA3We3.js → arc-M25fNMTE.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DEmI_zqr.js → architectureDiagram-T3A2C74G-CUWlVl1a.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-uDHANebE.js → blockDiagram-VBNYF7ZC-Dd0Whpx2.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Bv97b4_B.js → c4Diagram-5PPSVZJV-BdfeCkI5.js} +1 -1
- package/dist/worker/console/static/assets/channel-Ba0AKiEZ.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-shmQKsFz.js → chunk-2GRJ4B5K-D_1sUkyD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-BAraNvcN.js → chunk-2Q5K7J3B-Cxnxf36E.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BirhxUop.js → chunk-5RXB4S5H-D9RG3G_x.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C9LgUJpR.js → chunk-5VM5RSS4-Zz9jgjLX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DrU-s-t7.js → chunk-6Q2QTUOP-BCggDRCH.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Tx-H_FA2.js → chunk-GF5L2VYU-CFg-5iAS.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BjLtNFrR.js → chunk-JWPE2WC7-DRQTE8x_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-UNLVwSsy.js → chunk-KBJHAD2P-C_Dw7uZ8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CBEUX8XD.js → chunk-RYQCIY6F-BjIiApsk.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-WTL7IZOq.js → chunk-XXDRQBXY-B5XQD4KG.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BU3IjVEe.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BU3IjVEe.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CkHG6W3s.js → cose-bilkent-JH36ORCC-huHqH6WQ.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BRzFqTaL.js → cynefin-VYW2F7L2-BSi6yZqP.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-2_CFp_i7.js → cynefinDiagram-MW4NZA55-Bs_Jp31y.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BJtsmdkE.js → dagre-VZM6K2ZE-CYxavikT.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DNEFFo6w.js → diagram-7IWD3JNH-B6TErl71.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B_IWV7eo.js → diagram-B4RE2ZJO-CvH4J2ii.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DvvRx2dz.js → diagram-LBJQPF4R-v1NBAqXS.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-CsibsWLg.js → diagram-Q27KOJAE-CUNHVAn2.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-les_djBQ.js → diagram-UB23O5K3-Dzz1gUu-.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BEm2n7qJ.js → ebnfDiagram-BXEA7PRR-CCF8_LjK.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BI7vHHVm.js → erDiagram-JOGREHBK-DObdPVLa.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CUFCERrB.js → flowDiagram-UKHOOZJN-DlN8Pxrj.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-wxyLiGWt.js → ganttDiagram-PKOTCBZU-BJKIPTGX.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bt897K8Z.js → gitGraphDiagram-DS77QQ5N-XcSSnObi.js} +1 -1
- package/dist/worker/console/static/assets/index-CIYzS16i.css +1 -0
- package/dist/worker/console/static/assets/index-CS3QOTfl.js +451 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DYNx_IPv.js → infoDiagram-6WML65LV-BKrRKMww.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CzpLNEu6.js → ishikawaDiagram-WSZJBQD7-CN5dk-yb.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-97f9owNU.js → journeyDiagram-NVQOT4AX-B4P4KZ-Y.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BxM9hyBn.js → kanban-definition-27J2QSJJ-BRlteP32.js} +1 -1
- package/dist/worker/console/static/assets/{linear-ByuxcHvp.js → linear-CG8D9fuL.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DwPGvpCv.js → mermaid.core-BuB6P2rX.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DCoqG9RT.js → mindmap-definition-FAOFIHXS-DYXGl_DR.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BLSBmBMx.js → pegDiagram-VL7TDLO6-BM48yEVG.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B0t_0oJW.js → pieDiagram-7S7Q4E2Y-mT8NL-Ow.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js → quadrantDiagram-CIZ2JOQS-C4pZ6DH_.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-B3jRdATC.js → railroadDiagram-AXF67PYL-BKAcr70m.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-cTtXEN.js → requirementDiagram-LRYGKXZP-CRxcKpoO.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B25igJRE.js → sankeyDiagram-W5VNT64P-Ds4LxQDI.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-jW_lM1_D.js → sequenceDiagram-SI44F4Z6-CPLwPf_p.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CrzTgsHE.js → sizeCapture-X5ZJPWSS-CL65uij0.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-B01MvlqE.js → stateDiagram-OKZ733FA-CO0Mgd1W.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BUewAVYS.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-YcF12FiU.js → swimlanes-SLNWSIFB-Cxv3TSNw.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bhe67Wjl.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ClnRcg2s.js → timeline-definition-Z64GVDOM-Dw85f9L2.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-B6gr_z-q.js → vennDiagram-T6HMQDX7-B4QLCqCy.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CvSgguGH.js → wardleyDiagram-T6FBY63Y-B_yKCWwn.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-23OtSZCb.js → xychartDiagram-ELKLHX3M-nniBEGv2.js} +1 -1
- package/dist/worker/console/static/index.html +2 -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/useWorkspaces.js +150 -34
- package/dist/worker/console/static-src/shell/workspace-route.js +3 -1
- package/dist/worker/console/workspace-context.js +28 -3
- package/dist/worker/console/workspace-registry.js +333 -60
- 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/styles.css +87 -25
- package/dist/worker/observe/static/views/dag-inspector.js +42 -1
- package/dist/worker/observe/static/views/dags.js +7 -5
- package/dist/worker/observe/static/views/session-timeline.js +135 -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 +5 -1
- 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/init-hybrid.js +5 -4
- package/docs/architecture/runtime-boundaries.md +2 -1
- package/docs/templates/backend-test-dag.json +6 -5
- package/docs/templates/init-managed-agents.md +1 -1
- package/package.json +3 -2
- package/skills/loop-agent/references/command-reference.md +15 -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
|
@@ -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();
|
|
@@ -33,6 +33,9 @@ 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
40
|
import { OperationStore } from "./operation-store.js";
|
|
38
41
|
import { probePiReadiness, } from "./pi-readiness.js";
|
|
@@ -107,7 +110,16 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
107
110
|
fetchImpl: deps.fetchImpl,
|
|
108
111
|
getConsoleOrigin: deps.getConsoleOrigin,
|
|
109
112
|
};
|
|
110
|
-
const chatStore = new ChatSessionStore(appData
|
|
113
|
+
const chatStore = new ChatSessionStore(appData, {
|
|
114
|
+
onRecordChange: (sessionId, session) => {
|
|
115
|
+
if (session) {
|
|
116
|
+
deps.chatSessionCatalog?.upsert(session, record.workspaceId);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
deps.chatSessionCatalog?.remove(sessionId);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
});
|
|
111
123
|
const chatEventEpoch = randomBytes(12).toString("hex");
|
|
112
124
|
const chatEvents = createChatEventStore({
|
|
113
125
|
appData,
|
|
@@ -116,6 +128,7 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
116
128
|
const chatTurnExecutions = createChatTurnExecutionRegistry();
|
|
117
129
|
const questionRegistry = new UserQuestionRegistry(path.join(appData.chats, "user-questions"));
|
|
118
130
|
const todoRegistry = new TodoListRegistry();
|
|
131
|
+
const goalRegistry = new GoalRegistry(chatEvents);
|
|
119
132
|
questionRegistry.subscribe((change) => {
|
|
120
133
|
const question = change.question;
|
|
121
134
|
chatEvents.append(question.sessionId, question.turnId ?? `${question.sessionId}:question`, {
|
|
@@ -144,21 +157,27 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
144
157
|
actionContext,
|
|
145
158
|
questionRegistry,
|
|
146
159
|
todoRegistry,
|
|
160
|
+
goalRegistry,
|
|
147
161
|
});
|
|
148
|
-
|
|
162
|
+
let chatRouteDeps;
|
|
163
|
+
const goalRoundDriver = new GoalRoundDriver((sessionId) => driveGoalRound(chatRouteDeps, sessionId));
|
|
164
|
+
chatRouteDeps = {
|
|
149
165
|
runtime: chatRuntime,
|
|
150
166
|
store: chatStore,
|
|
151
167
|
events: chatEvents,
|
|
152
168
|
turnExecutions: chatTurnExecutions,
|
|
153
169
|
questionRegistry,
|
|
154
170
|
todoRegistry,
|
|
171
|
+
goalRegistry,
|
|
172
|
+
goalRoundDriver,
|
|
155
173
|
operationLinker: chatOperationLinker,
|
|
156
174
|
appData,
|
|
157
175
|
bootToken: deps.bootToken,
|
|
158
176
|
getConsoleOrigin: deps.getConsoleOrigin,
|
|
159
177
|
allowNonLoopbackAccess: deps.allowNonLoopbackAccess,
|
|
178
|
+
attachSession: (sessionId) => registry.attachSession(record.workspaceId, sessionId),
|
|
179
|
+
detachSession: (sessionId) => registry.detachSession(record.workspaceId, sessionId),
|
|
160
180
|
};
|
|
161
|
-
const { recoverDeferredTurns } = await import("./chat/routes.js");
|
|
162
181
|
const nightAuxTicker = new NightAuxTicker({ repoRoot, client });
|
|
163
182
|
const observeRouteContext = createObserveRouteContext(repoRoot, {
|
|
164
183
|
staticDir: deps.inspectStaticDir,
|
|
@@ -191,6 +210,12 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
191
210
|
if (disposed)
|
|
192
211
|
return;
|
|
193
212
|
disposed = true;
|
|
213
|
+
try {
|
|
214
|
+
goalRoundDriver.dispose();
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// best-effort
|
|
218
|
+
}
|
|
194
219
|
try {
|
|
195
220
|
chatOperationLinker.dispose();
|
|
196
221
|
}
|