@mstar-harness/dsh 3.7.3 → 3.8.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/README.i18n.yaml +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/bundle/README.md +8 -1
- package/dist/client/index.d.ts +12 -6
- package/dist/client/panel/PanelView.d.ts +38 -16
- package/dist/client/panel/engine-status-client.d.ts +187 -0
- package/dist/client/panel/graph/project-graph.d.ts +4 -4
- package/dist/client/panel/guards.d.ts +52 -3
- package/dist/client/panel/locale.d.ts +1 -1
- package/dist/client/panel/panel-meta.d.ts +2 -2
- package/dist/client/panel/sidebar.d.ts +2 -2
- package/dist/client/panel/state-section.d.ts +2 -2
- package/dist/client/panel/use-mstar-engine-status.d.ts +104 -32
- package/dist/client.js +333 -50
- package/dist/engine-status-endpoint.d.ts +154 -0
- package/dist/engine-status-store.d.ts +190 -0
- package/dist/engine-status-wire.d.ts +29 -0
- package/dist/gates/_shared.d.ts +9 -0
- package/dist/gates/catalog.d.ts +7 -7
- package/dist/gates/system-prompt.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +528 -94
- package/dist/types.d.ts +33 -19
- package/harness-skills/mstar-artifacts/references/status-and-residuals.md +1 -1
- package/harness-skills/mstar-host/references/dsh.md +13 -4
- package/package.json +4 -2
|
@@ -1,42 +1,114 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Data hook for the workflow panel (spec §5):
|
|
3
|
-
* session's conversation log through the session standard kit's chat target
|
|
4
|
-
* (`useChat`, a uSES selector hook over the `chat` conversation view target)
|
|
5
|
-
* and scans the log for the latest `mstar-engine-status` catalog row.
|
|
2
|
+
* Data hook for the workflow panel (spec §5): the panel's ONE data path.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the
|
|
11
|
-
* selection — no manual reload, no polling.
|
|
4
|
+
* The persisted catalog row is an ANCHOR, not a data source: since the source
|
|
5
|
+
* reduction the row carries exactly `{ kind: 'plugin', plugin:
|
|
6
|
+
* 'mstar-engine-status', form: 'catalog' }`, and the structured payload lives
|
|
7
|
+
* in the host's per-session snapshot store. So the hook
|
|
12
8
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
9
|
+
* 1. finds the LATEST anchor row in the active session's conversation log
|
|
10
|
+
* (`kind === 'context'` + `form === 'catalog'` + the first-party `plugin`
|
|
11
|
+
* arm with this plugin's identity) and takes its message time as the
|
|
12
|
+
* refresh key — a re-emission appends a new row, the snapshot bump re-runs
|
|
13
|
+
* this selection, and the newest anchor asks for the newest snapshot;
|
|
14
|
+
* 2. reads the session's workspace directory from the session standard kit's
|
|
15
|
+
* session feed (the host cross-checks the asserted `cwd` against the
|
|
16
|
+
* session AND the stored snapshot, so it must be the real one);
|
|
17
|
+
* 3. asks {@link MstarEngineStatusClient} for that session's snapshot over the
|
|
18
|
+
* host's shared `/api` typert gateway, and renders the validated result.
|
|
19
|
+
*
|
|
20
|
+
* Every degraded path is an EXPLICIT state (spec §5): `waiting` (no anchor row
|
|
21
|
+
* — the plugin never ran on this session), `loading` (anchor seen, no answer
|
|
22
|
+
* for it yet), `unavailable` (a reason: transport failure, an unknown session
|
|
23
|
+
* directory, no connection, a malformed answer, or the host's own reason).
|
|
24
|
+
* None of them renders an empty plans list, an empty event log or a zeroed
|
|
25
|
+
* counter as if it were data. A VALID snapshot that says "empty" still renders
|
|
26
|
+
* as data — the distinction between "no snapshot" and "a snapshot that says
|
|
27
|
+
* empty" survives.
|
|
28
|
+
*
|
|
29
|
+
* Freshness is the served snapshot's own `at`, never "live".
|
|
30
|
+
*
|
|
31
|
+
* The hook never throws: a throwing seat or a missing client degrades to an
|
|
32
|
+
* explicit state instead of bubbling a crash (spec §5 degradation path).
|
|
20
33
|
*/
|
|
34
|
+
import type { ConversationNode, ContextMessageNode } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
21
35
|
import type { ChatSnapshot } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
22
36
|
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
|
|
23
|
-
import type {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
import type { SessionListState } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
38
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
39
|
+
import type { MstarEngineStatusPayload } from '../../types.ts';
|
|
40
|
+
import type { MstarEngineStatusClient } from './engine-status-client.ts';
|
|
41
|
+
/** Selector hook over the Host session list (the `useSessions` standard seat). */
|
|
42
|
+
export type UseSessions = SnapshotSelectorHook<SessionListState>;
|
|
43
|
+
/** The panel's render state — a closed set: no shape can be both data and empty. */
|
|
44
|
+
export type MstarEngineStatusView =
|
|
45
|
+
/** No anchor row: this session never ran the plugin (today's empty state). */
|
|
46
|
+
{
|
|
47
|
+
readonly state: 'waiting';
|
|
48
|
+
readonly anchorTime: null;
|
|
49
|
+
readonly payload: null;
|
|
50
|
+
readonly at: null;
|
|
51
|
+
readonly turn: null;
|
|
52
|
+
readonly reason: null;
|
|
53
|
+
}
|
|
54
|
+
/** Anchor row seen, no answer for it yet — pending, never rendered as data. */
|
|
55
|
+
| {
|
|
56
|
+
readonly state: 'loading';
|
|
57
|
+
readonly anchorTime: number;
|
|
58
|
+
readonly payload: null;
|
|
59
|
+
readonly at: null;
|
|
60
|
+
readonly turn: null;
|
|
61
|
+
readonly reason: null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The validated snapshot: the payload plus the snapshot's OWN emission
|
|
65
|
+
* identity (`at` timestamp and the agent turn it was written for). Both are
|
|
66
|
+
* the host's records of the stored entry, so the panel can name which
|
|
67
|
+
* emission it is showing rather than implying it is the newest one.
|
|
68
|
+
*/
|
|
69
|
+
| {
|
|
70
|
+
readonly state: 'ok';
|
|
71
|
+
readonly anchorTime: number;
|
|
72
|
+
readonly payload: MstarEngineStatusPayload;
|
|
73
|
+
readonly at: string;
|
|
74
|
+
readonly turn: number;
|
|
75
|
+
readonly reason: null;
|
|
76
|
+
}
|
|
77
|
+
/** An explicit degraded answer — always WITH a reason, never silently empty. */
|
|
78
|
+
| {
|
|
79
|
+
readonly state: 'unavailable';
|
|
80
|
+
readonly anchorTime: number;
|
|
81
|
+
readonly payload: null;
|
|
82
|
+
readonly at: null;
|
|
83
|
+
readonly turn: null;
|
|
84
|
+
readonly reason: string;
|
|
85
|
+
};
|
|
86
|
+
/** The seats the hook needs: the session standard kit + the plugin's client. */
|
|
87
|
+
export interface MstarEngineStatusSeats {
|
|
88
|
+
/** Selector hook over the chat target snapshot (the `conversation.view` kit). */
|
|
89
|
+
useChat: SnapshotSelectorHook<ChatSnapshot>;
|
|
90
|
+
/**
|
|
91
|
+
* Selector hook over the Host session list (the global standard seat). The
|
|
92
|
+
* view ring always hands it to a `conversation.view` entry; it stays
|
|
93
|
+
* optional here because a program without the ui-session adapter does not
|
|
94
|
+
* see the declaration merge (the panel then reports `session-cwd-unknown`).
|
|
95
|
+
*/
|
|
96
|
+
useSessions: UseSessions | undefined;
|
|
97
|
+
/** Current session identity (the strict-session slot's own prop). */
|
|
98
|
+
sessionId: SessionId | undefined;
|
|
99
|
+
/** The plugin's engine-status client (absent in a composition without one). */
|
|
100
|
+
engineStatus: MstarEngineStatusClient | undefined;
|
|
29
101
|
}
|
|
102
|
+
/** Latest `mstar-engine-status` anchor row in snapshot order, or null. */
|
|
103
|
+
export declare function latestEngineStatusRow(nodes: readonly ConversationNode[]): ContextMessageNode | null;
|
|
104
|
+
/** Anchor row message time, or null when the log carries no anchor row. */
|
|
105
|
+
export declare function selectAnchorTime(snapshot: ChatSnapshot): number | null;
|
|
106
|
+
/** One session's workspace directory from the session list, or null when unknown. */
|
|
107
|
+
export declare function selectSessionCwd(sessionId: SessionId | undefined): (state: SessionListState) => string | null;
|
|
30
108
|
/**
|
|
31
|
-
*
|
|
32
|
-
* hook (spec §5). The session standard kit's `useChat` is passed in (the
|
|
33
|
-
* view ring hands it to every `conversation.view` entry); the hook rides it as
|
|
34
|
-
* a selector over the chat target snapshot, so a snapshot bump (new catalog
|
|
35
|
-
* row) re-runs the selection and refreshes the panel.
|
|
109
|
+
* The panel's data hook (spec §5).
|
|
36
110
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* signal instead of bubbling a crash — the strict-session slot normally
|
|
40
|
-
* guarantees a session, the guard is belt-and-suspenders.
|
|
111
|
+
* @param seats - the session standard kit + the plugin's engine-status client.
|
|
112
|
+
* @returns the explicit render state (never a throw, never a half-parsed view).
|
|
41
113
|
*/
|
|
42
|
-
export declare function useMstarEngineStatus(
|
|
114
|
+
export declare function useMstarEngineStatus(seats: MstarEngineStatusSeats): MstarEngineStatusView;
|
package/dist/client.js
CHANGED
|
@@ -72,6 +72,222 @@ __export(exports_client, {
|
|
|
72
72
|
});
|
|
73
73
|
module.exports = __toCommonJS(exports_client);
|
|
74
74
|
|
|
75
|
+
// src/engine-status-wire.ts
|
|
76
|
+
var ENGINE_STATUS_CHANNEL = "/api";
|
|
77
|
+
var ENGINE_STATUS_ENDPOINT = "mstar/engineStatus";
|
|
78
|
+
|
|
79
|
+
// src/client/panel/guards.ts
|
|
80
|
+
function str(value) {
|
|
81
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
82
|
+
}
|
|
83
|
+
function bool(value) {
|
|
84
|
+
return typeof value === "boolean" ? value : null;
|
|
85
|
+
}
|
|
86
|
+
function count(value) {
|
|
87
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
88
|
+
}
|
|
89
|
+
function record(value) {
|
|
90
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
91
|
+
}
|
|
92
|
+
function unavailable(reason) {
|
|
93
|
+
return { status: "unavailable", reason };
|
|
94
|
+
}
|
|
95
|
+
function parseEngineStatusResult(raw, sessionId, cwd) {
|
|
96
|
+
const envelope = record(raw);
|
|
97
|
+
if (envelope === null)
|
|
98
|
+
return unavailable("malformed-response");
|
|
99
|
+
if (envelope.ok !== true) {
|
|
100
|
+
const error = record(envelope.error);
|
|
101
|
+
const code = str(error?.code);
|
|
102
|
+
return unavailable(code === null ? "transport-error" : `transport-error:${code}`);
|
|
103
|
+
}
|
|
104
|
+
const value = record(envelope.value);
|
|
105
|
+
if (value === null)
|
|
106
|
+
return unavailable("malformed-response");
|
|
107
|
+
if (value.status === "unavailable")
|
|
108
|
+
return unavailable(str(value.reason) ?? "unavailable");
|
|
109
|
+
if (value.status !== "ok")
|
|
110
|
+
return unavailable("malformed-response");
|
|
111
|
+
if (value.sessionId !== sessionId)
|
|
112
|
+
return unavailable("session-mismatch");
|
|
113
|
+
if (cwd !== undefined && value.cwd !== cwd)
|
|
114
|
+
return unavailable("cwd-mismatch");
|
|
115
|
+
const at = str(value.at);
|
|
116
|
+
if (at === null)
|
|
117
|
+
return unavailable("malformed-snapshot");
|
|
118
|
+
const turn = count(value.turn);
|
|
119
|
+
if (turn === null)
|
|
120
|
+
return unavailable("malformed-snapshot");
|
|
121
|
+
const payload = record(value.payload);
|
|
122
|
+
if (payload === null)
|
|
123
|
+
return unavailable("malformed-payload");
|
|
124
|
+
return { status: "ok", payload, at, turn };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/client/panel/engine-status-client.ts
|
|
128
|
+
var ENGINE_STATUS_REQUEST_TIMEOUT_MS = 5000;
|
|
129
|
+
var ENGINE_STATUS_REFRESH_INTERVAL_MS = 30000;
|
|
130
|
+
var EMPTY = { entries: new Map };
|
|
131
|
+
|
|
132
|
+
class MstarEngineStatusClient {
|
|
133
|
+
connection;
|
|
134
|
+
snapshot = EMPTY;
|
|
135
|
+
listeners = new Set;
|
|
136
|
+
inFlight = new Map;
|
|
137
|
+
abort = new AbortController;
|
|
138
|
+
refreshing = new Set;
|
|
139
|
+
timeoutMs;
|
|
140
|
+
refreshIntervalMs;
|
|
141
|
+
refreshTimer = null;
|
|
142
|
+
generationDisposer = null;
|
|
143
|
+
generation = 0;
|
|
144
|
+
disposed = false;
|
|
145
|
+
constructor(connection, options = {}) {
|
|
146
|
+
this.connection = connection;
|
|
147
|
+
this.timeoutMs = options.timeoutMs ?? ENGINE_STATUS_REQUEST_TIMEOUT_MS;
|
|
148
|
+
this.refreshIntervalMs = options.refreshIntervalMs ?? ENGINE_STATUS_REFRESH_INTERVAL_MS;
|
|
149
|
+
try {
|
|
150
|
+
this.generationDisposer = connection?.generation?.subscribe(() => {
|
|
151
|
+
this.invalidate();
|
|
152
|
+
}) ?? null;
|
|
153
|
+
} catch {
|
|
154
|
+
this.generationDisposer = null;
|
|
155
|
+
}
|
|
156
|
+
this.startRefreshTimer();
|
|
157
|
+
}
|
|
158
|
+
getSnapshot = () => this.snapshot;
|
|
159
|
+
subscribe = (listener) => {
|
|
160
|
+
this.listeners.add(listener);
|
|
161
|
+
return () => {
|
|
162
|
+
this.listeners.delete(listener);
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
ensure(sessionId, cwd, anchorTime) {
|
|
166
|
+
if (this.disposed)
|
|
167
|
+
return;
|
|
168
|
+
if (this.snapshot.entries.get(sessionId)?.anchorTime === anchorTime)
|
|
169
|
+
return;
|
|
170
|
+
const key = `${sessionId}\x00${anchorTime}`;
|
|
171
|
+
if (this.inFlight.has(key))
|
|
172
|
+
return;
|
|
173
|
+
const request = this.fetch(sessionId, cwd, anchorTime).finally(() => {
|
|
174
|
+
this.inFlight.delete(key);
|
|
175
|
+
});
|
|
176
|
+
this.inFlight.set(key, request);
|
|
177
|
+
}
|
|
178
|
+
invalidate() {
|
|
179
|
+
if (this.disposed)
|
|
180
|
+
return;
|
|
181
|
+
this.generation += 1;
|
|
182
|
+
this.inFlight.clear();
|
|
183
|
+
if (this.snapshot.entries.size === 0)
|
|
184
|
+
return;
|
|
185
|
+
this.snapshot = EMPTY;
|
|
186
|
+
this.notify();
|
|
187
|
+
}
|
|
188
|
+
dispose() {
|
|
189
|
+
this.disposed = true;
|
|
190
|
+
this.generationDisposer?.();
|
|
191
|
+
this.generationDisposer = null;
|
|
192
|
+
if (this.refreshTimer !== null) {
|
|
193
|
+
clearInterval(this.refreshTimer);
|
|
194
|
+
this.refreshTimer = null;
|
|
195
|
+
}
|
|
196
|
+
this.abort.abort();
|
|
197
|
+
this.listeners.clear();
|
|
198
|
+
}
|
|
199
|
+
startRefreshTimer() {
|
|
200
|
+
if (this.disposed || this.refreshIntervalMs <= 0)
|
|
201
|
+
return;
|
|
202
|
+
if (typeof setInterval !== "function")
|
|
203
|
+
return;
|
|
204
|
+
this.refreshTimer = setInterval(() => {
|
|
205
|
+
this.refresh();
|
|
206
|
+
}, this.refreshIntervalMs);
|
|
207
|
+
const timer = this.refreshTimer;
|
|
208
|
+
timer.unref?.();
|
|
209
|
+
}
|
|
210
|
+
refresh() {
|
|
211
|
+
if (this.disposed)
|
|
212
|
+
return;
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
for (const [sessionId, entry] of [...this.snapshot.entries]) {
|
|
215
|
+
if (this.disposed)
|
|
216
|
+
return;
|
|
217
|
+
if (now - entry.fetchedAt < this.refreshIntervalMs)
|
|
218
|
+
continue;
|
|
219
|
+
if (this.refreshing.has(sessionId))
|
|
220
|
+
continue;
|
|
221
|
+
if (this.snapshot.entries.get(sessionId)?.anchorTime !== entry.anchorTime)
|
|
222
|
+
continue;
|
|
223
|
+
this.refreshing.add(sessionId);
|
|
224
|
+
this.fetch(sessionId, entry.cwd, entry.anchorTime).finally(() => {
|
|
225
|
+
this.refreshing.delete(sessionId);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async fetch(sessionId, cwd, anchorTime) {
|
|
230
|
+
const generation = this.generation;
|
|
231
|
+
let answer;
|
|
232
|
+
if (this.connection === null || this.connection === undefined || typeof this.connection.rpc?.call !== "function") {
|
|
233
|
+
answer = { status: "unavailable", reason: "no-connection" };
|
|
234
|
+
} else {
|
|
235
|
+
try {
|
|
236
|
+
const raw = await this.withDeadline((signal) => this.connection.rpc.call(ENGINE_STATUS_CHANNEL, ENGINE_STATUS_ENDPOINT, { args: { sessionId, cwd } }, signal));
|
|
237
|
+
answer = parseEngineStatusResult(raw, sessionId, cwd);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
answer = { status: "unavailable", reason: `transport-error:${error?.message ?? "unknown"}` };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
this.write(generation, sessionId, cwd, anchorTime, answer);
|
|
243
|
+
}
|
|
244
|
+
async withDeadline(call) {
|
|
245
|
+
if (this.timeoutMs <= 0 || typeof setTimeout !== "function")
|
|
246
|
+
return await call(this.abort.signal);
|
|
247
|
+
const controller = new AbortController;
|
|
248
|
+
const relay = () => {
|
|
249
|
+
controller.abort();
|
|
250
|
+
};
|
|
251
|
+
this.abort.signal.addEventListener("abort", relay);
|
|
252
|
+
let timer = null;
|
|
253
|
+
const deadline = new Promise((_resolve, reject) => {
|
|
254
|
+
timer = setTimeout(() => {
|
|
255
|
+
controller.abort();
|
|
256
|
+
reject(new Error(`timeout:${this.timeoutMs}ms`));
|
|
257
|
+
}, this.timeoutMs);
|
|
258
|
+
});
|
|
259
|
+
try {
|
|
260
|
+
return await Promise.race([call(controller.signal), deadline]);
|
|
261
|
+
} finally {
|
|
262
|
+
if (timer !== null)
|
|
263
|
+
clearTimeout(timer);
|
|
264
|
+
this.abort.signal.removeEventListener("abort", relay);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
write(generation, sessionId, cwd, anchorTime, fetch) {
|
|
268
|
+
if (this.disposed)
|
|
269
|
+
return;
|
|
270
|
+
if (generation !== this.generation)
|
|
271
|
+
return;
|
|
272
|
+
if ((this.snapshot.entries.get(sessionId)?.anchorTime ?? anchorTime) > anchorTime)
|
|
273
|
+
return;
|
|
274
|
+
this.writeEntry(sessionId, { anchorTime, cwd, fetchedAt: Date.now(), fetch });
|
|
275
|
+
}
|
|
276
|
+
writeEntry(sessionId, entry) {
|
|
277
|
+
const entries = new Map(this.snapshot.entries);
|
|
278
|
+
entries.set(sessionId, entry);
|
|
279
|
+
this.snapshot = { entries };
|
|
280
|
+
this.notify();
|
|
281
|
+
}
|
|
282
|
+
notify() {
|
|
283
|
+
for (const listener of [...this.listeners]) {
|
|
284
|
+
try {
|
|
285
|
+
listener();
|
|
286
|
+
} catch {}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
75
291
|
// src/client/panel/locale.ts
|
|
76
292
|
var NS = "mstar-panel";
|
|
77
293
|
var zh = {
|
|
@@ -111,6 +327,8 @@ var zh = {
|
|
|
111
327
|
"event-log.yes": "是",
|
|
112
328
|
"event-log.no": "否",
|
|
113
329
|
"empty.waiting": "等待首条 engine-status catalog…",
|
|
330
|
+
"empty.loading": "正在读取该会话的 engine-status 快照…",
|
|
331
|
+
"empty.unavailable": "engine-status 快照不可用({reason})",
|
|
114
332
|
"empty.no-harness": "未检测到 Morning Star harness",
|
|
115
333
|
"empty.no-harness-hint": "当前工作区未发现 .mstar/ harness 目录,详细面板保持未激活;检测到 harness 后自动呈现",
|
|
116
334
|
"watermark.version": "mstar {version}",
|
|
@@ -192,8 +410,8 @@ var zh = {
|
|
|
192
410
|
"state.policy.worktree": "worktree",
|
|
193
411
|
"state.policy.control-worktree": "control worktree",
|
|
194
412
|
"state.knowledge.docs": "{count} 篇文档",
|
|
195
|
-
"freshness.last-updated": "
|
|
196
|
-
"freshness.refresh-note": "
|
|
413
|
+
"freshness.last-updated": "快照 {time} · 第 {turn} 轮",
|
|
414
|
+
"freshness.refresh-note": "该会话已存储的快照——不是实时值"
|
|
197
415
|
};
|
|
198
416
|
var en = {
|
|
199
417
|
"view.mstar-workflow": "MStar Workflow",
|
|
@@ -232,6 +450,8 @@ var en = {
|
|
|
232
450
|
"event-log.yes": "yes",
|
|
233
451
|
"event-log.no": "no",
|
|
234
452
|
"empty.waiting": "Waiting for the first engine-status catalog…",
|
|
453
|
+
"empty.loading": "Reading this session’s engine-status snapshot…",
|
|
454
|
+
"empty.unavailable": "Engine-status snapshot unavailable ({reason})",
|
|
235
455
|
"empty.no-harness": "No Morning Star harness detected",
|
|
236
456
|
"empty.no-harness-hint": "No .mstar/ harness directory found in this workspace — the detail panel stays inactive and activates automatically once a harness is detected",
|
|
237
457
|
"watermark.version": "mstar {version}",
|
|
@@ -313,12 +533,12 @@ var en = {
|
|
|
313
533
|
"state.policy.worktree": "worktree",
|
|
314
534
|
"state.policy.control-worktree": "control worktree",
|
|
315
535
|
"state.knowledge.docs": "{count} docs",
|
|
316
|
-
"freshness.last-updated": "
|
|
317
|
-
"freshness.refresh-note": "
|
|
536
|
+
"freshness.last-updated": "snapshot {time} · turn {turn}",
|
|
537
|
+
"freshness.refresh-note": "the stored snapshot for this session — never a live value"
|
|
318
538
|
};
|
|
319
539
|
|
|
320
540
|
// src/client/panel/PanelView.tsx
|
|
321
|
-
var
|
|
541
|
+
var import_react4 = require("react");
|
|
322
542
|
|
|
323
543
|
// src/client/panel/panel.module.css
|
|
324
544
|
var css = `
|
|
@@ -1053,17 +1273,6 @@ if (typeof document !== "undefined" && document.querySelector("style[data-plugin
|
|
|
1053
1273
|
}
|
|
1054
1274
|
var panel_module_default = { root: "20fd0e45_root", emptyRoot: "f83e09fa_emptyRoot", noHarnessCard: "58245a20_noHarnessCard", noHarnessIcon: "ffb65da7_noHarnessIcon", noHarnessTitle: "81e1817c_noHarnessTitle", noHarnessHint: "a2289da7_noHarnessHint", sidebar: "c87acfc7_sidebar", iterationHeadSplit: "77aea5a6_iterationHeadSplit", iterationBranches: "c3b8dcb0_iterationBranches", main: "ea90e208_main", tabNav: "4bb38bc9_tabNav", tab: "98f72e4c_tab", tabActive: "59e199de_tabActive", content: "90bec3c2_content", iterationPage: "3b1c4147_iterationPage", iterationHead: "8a629a8e_iterationHead", iterationSummary: "2d1bee34_iterationSummary", iterationSummaryId: "17162a99_iterationSummaryId", iterationSummaryVerdict: "d3d74135_iterationSummaryVerdict", iterationSummaryStatus: "d9a886be_iterationSummaryStatus", iterationSummaryStatusMuted: "13bf2f41_iterationSummaryStatusMuted", iterationSummaryHint: "ede5708d_iterationSummaryHint", iterationHeadBody: "64d1da62_iterationHeadBody", iterationStepsRow: "c52f7b85_iterationStepsRow", iterationStepItem: "81013a9d_iterationStepItem", iterationStepBadge: "7befde45_iterationStepBadge", iterationStepPhase: "d6cd23a5_iterationStepPhase", iterationStepChip: "de293024_iterationStepChip", iterationVerdictSeat: "afea5850_iterationVerdictSeat", iterationVerdict: "ebe6bbcd_iterationVerdict", iterationBranchesTitle: "e6921da6_iterationBranchesTitle", iterationBranchList: "8a2cd65c_iterationBranchList", iterationBranchRow: "997b0230_iterationBranchRow", iterationBranchLabel: "6cafc032_iterationBranchLabel", iterationBranchValue: "cdcd06ed_iterationBranchValue", iterationTasks: "87f7efbe_iterationTasks", sidebarScroll: "a0b80516_sidebarScroll", meta: "81fe4f58_meta", metaLine: "0fc275e2_metaLine", section: "fcdd0ccc_section", sectionTitle: "aded23ea_sectionTitle", subTitle: "b7190159_subTitle", defList: "26763614_defList", defTerm: "670df156_defTerm", defValue: "3f1b3a05_defValue", planList: "a129f16e_planList", residualList: "113e0aa2_residualList", leaseList: "33dfcc85_leaseList", violationList: "999efdc0_violationList", planId: "744bf63f_planId", leasePlan: "560663a6_leasePlan", leaseHolder: "a68a6d03_leaseHolder", leaseWorktree: "db027b44_leaseWorktree", findingSeverity: "1797fd51_findingSeverity", findingTitle: "c80b7bb0_findingTitle", findingPlan: "616667ad_findingPlan", violationCode: "e42c0bfd_violationCode", knowledgeCategories: "e2098247_knowledgeCategories", planStatus: "a3021d7c_planStatus", truncated: "1849d129_truncated", knowledge: "3eb5feeb_knowledge", direction: "df6dc76a_direction", selection: "4364e615_selection", selectionId: "0ae21b10_selectionId", selectionMeta: "5e43d3a8_selectionMeta", selectionWarning: "da64723f_selectionWarning", selectionCode: "ac988a64_selectionCode", empty: "18a7beee_empty", freshness: "c75c59b0_freshness" };
|
|
1055
1275
|
|
|
1056
|
-
// src/client/panel/guards.ts
|
|
1057
|
-
function str(value) {
|
|
1058
|
-
return typeof value === "string" && value.length > 0 ? value : null;
|
|
1059
|
-
}
|
|
1060
|
-
function bool(value) {
|
|
1061
|
-
return typeof value === "boolean" ? value : null;
|
|
1062
|
-
}
|
|
1063
|
-
function count(value) {
|
|
1064
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
1276
|
// src/client/panel/plan-sort.ts
|
|
1068
1277
|
var PLAN_CAP = 5;
|
|
1069
1278
|
var FINDINGS_CAP = 10;
|
|
@@ -1679,7 +1888,7 @@ function pairingKeyOf(row) {
|
|
|
1679
1888
|
return [row.agent ?? "", row.role ?? "", row.planId ?? "", row.taskId ?? ""].join("\x00");
|
|
1680
1889
|
}
|
|
1681
1890
|
function pairSettleStatus(rows) {
|
|
1682
|
-
const
|
|
1891
|
+
const record2 = new Map;
|
|
1683
1892
|
const lastDispatch = new Map;
|
|
1684
1893
|
for (let i = rows.length - 1;i >= 0; i--) {
|
|
1685
1894
|
const entry = rows[i];
|
|
@@ -1689,10 +1898,10 @@ function pairSettleStatus(rows) {
|
|
|
1689
1898
|
} else if (entry.paired === true && entry.agent !== null) {
|
|
1690
1899
|
const paired = lastDispatch.get(pairingKeyOf(entry));
|
|
1691
1900
|
if (paired !== undefined)
|
|
1692
|
-
|
|
1901
|
+
record2.set(paired, entry.status ?? "ok");
|
|
1693
1902
|
}
|
|
1694
1903
|
}
|
|
1695
|
-
return
|
|
1904
|
+
return record2;
|
|
1696
1905
|
}
|
|
1697
1906
|
function pairSettleIndexes(rows) {
|
|
1698
1907
|
return new Set(pairSettleStatus(rows).keys());
|
|
@@ -3424,11 +3633,11 @@ function EntityCard({ entity, t, box }) {
|
|
|
3424
3633
|
const running = entity.status === "running";
|
|
3425
3634
|
const done = entity.status === "settled" && entity.emphasis !== "off";
|
|
3426
3635
|
const title = entity.idle ? entity.name : entity.role !== "" ? entity.role : entity.name;
|
|
3427
|
-
const
|
|
3636
|
+
const record2 = [];
|
|
3428
3637
|
if (entity.agent !== null)
|
|
3429
|
-
|
|
3638
|
+
record2.push(entity.agent);
|
|
3430
3639
|
if (entity.task !== null)
|
|
3431
|
-
|
|
3640
|
+
record2.push(entity.task);
|
|
3432
3641
|
return /* @__PURE__ */ jsx_runtime7.jsxs("li", {
|
|
3433
3642
|
className: entity.idle ? `${agent_canvas_module_default.agentCard} ${agent_canvas_module_default.agentCardIdle}` : running ? `${agent_canvas_module_default.agentCard} ${agent_canvas_module_default.agentCardRunning}` : done ? `${agent_canvas_module_default.agentCard} ${agent_canvas_module_default.agentCardDone}` : agent_canvas_module_default.agentCard,
|
|
3434
3643
|
style: { left: box.x, top: box.y, width: box.w, height: box.h },
|
|
@@ -3490,10 +3699,10 @@ function EntityCard({ entity, t, box }) {
|
|
|
3490
3699
|
"data-agent-role": entity.role,
|
|
3491
3700
|
children: entity.role
|
|
3492
3701
|
}),
|
|
3493
|
-
|
|
3702
|
+
record2.length > 0 && /* @__PURE__ */ jsx_runtime7.jsx("span", {
|
|
3494
3703
|
className: agent_canvas_module_default.agentRecord,
|
|
3495
3704
|
"data-agent-record": true,
|
|
3496
|
-
children:
|
|
3705
|
+
children: record2.join(" · ")
|
|
3497
3706
|
})
|
|
3498
3707
|
]
|
|
3499
3708
|
});
|
|
@@ -4668,44 +4877,87 @@ function IterationTaskPage({ view, t }) {
|
|
|
4668
4877
|
}
|
|
4669
4878
|
|
|
4670
4879
|
// src/client/panel/use-mstar-engine-status.ts
|
|
4671
|
-
var
|
|
4880
|
+
var import_react3 = require("react");
|
|
4881
|
+
var EMPTY_SNAPSHOT = { entries: new Map };
|
|
4882
|
+
var NO_SUBSCRIBE = () => () => {};
|
|
4883
|
+
var readEmptySnapshot = () => EMPTY_SNAPSHOT;
|
|
4672
4884
|
function latestEngineStatusRow(nodes) {
|
|
4673
4885
|
for (let i = nodes.length - 1;i >= 0; i--) {
|
|
4674
4886
|
const node = nodes[i];
|
|
4675
4887
|
if (node.kind !== "context" || node.form !== "catalog")
|
|
4676
4888
|
continue;
|
|
4677
4889
|
const source = node.source;
|
|
4678
|
-
if (source?.kind === "mstar-engine-status")
|
|
4890
|
+
if (source?.kind === "plugin" && source.plugin === "mstar-engine-status")
|
|
4679
4891
|
return node;
|
|
4680
4892
|
}
|
|
4681
4893
|
return null;
|
|
4682
4894
|
}
|
|
4683
|
-
function
|
|
4684
|
-
return a.source === b.source && a.lastUpdated === b.lastUpdated;
|
|
4685
|
-
}
|
|
4686
|
-
function selectEngineStatus(snapshot) {
|
|
4895
|
+
function selectAnchorTime(snapshot) {
|
|
4687
4896
|
try {
|
|
4688
|
-
const row = latestEngineStatusRow(snapshot
|
|
4689
|
-
|
|
4690
|
-
return EMPTY;
|
|
4691
|
-
return { source: row.source, lastUpdated: row.time };
|
|
4897
|
+
const row = latestEngineStatusRow(snapshot?.legacy?.nodes ?? []);
|
|
4898
|
+
return row === null ? null : row.time;
|
|
4692
4899
|
} catch {
|
|
4693
|
-
return
|
|
4900
|
+
return null;
|
|
4694
4901
|
}
|
|
4695
4902
|
}
|
|
4696
|
-
function
|
|
4903
|
+
function selectSessionCwd(sessionId) {
|
|
4904
|
+
return (state) => {
|
|
4905
|
+
try {
|
|
4906
|
+
if (sessionId === undefined)
|
|
4907
|
+
return null;
|
|
4908
|
+
const cwd = state?.byId?.[sessionId]?.cwd;
|
|
4909
|
+
return typeof cwd === "string" && cwd !== "" ? cwd : null;
|
|
4910
|
+
} catch {
|
|
4911
|
+
return null;
|
|
4912
|
+
}
|
|
4913
|
+
};
|
|
4914
|
+
}
|
|
4915
|
+
var WAITING = { state: "waiting", anchorTime: null, payload: null, at: null, turn: null, reason: null };
|
|
4916
|
+
function unavailable2(anchorTime, reason) {
|
|
4917
|
+
return { state: "unavailable", anchorTime, payload: null, at: null, turn: null, reason };
|
|
4918
|
+
}
|
|
4919
|
+
function useMstarEngineStatus(seats) {
|
|
4920
|
+
const { useChat, useSessions, sessionId, engineStatus } = seats;
|
|
4921
|
+
let anchorTime = null;
|
|
4922
|
+
let cwd = null;
|
|
4923
|
+
try {
|
|
4924
|
+
anchorTime = useChat(selectAnchorTime);
|
|
4925
|
+
} catch {
|
|
4926
|
+
anchorTime = null;
|
|
4927
|
+
}
|
|
4697
4928
|
try {
|
|
4698
|
-
|
|
4699
|
-
return view ?? EMPTY;
|
|
4929
|
+
cwd = useSessions === undefined ? null : useSessions(selectSessionCwd(sessionId));
|
|
4700
4930
|
} catch {
|
|
4701
|
-
|
|
4931
|
+
cwd = null;
|
|
4932
|
+
}
|
|
4933
|
+
const snapshot = import_react3.useSyncExternalStore(engineStatus?.subscribe ?? NO_SUBSCRIBE, engineStatus?.getSnapshot ?? readEmptySnapshot, engineStatus?.getSnapshot ?? readEmptySnapshot);
|
|
4934
|
+
if (anchorTime === null)
|
|
4935
|
+
return WAITING;
|
|
4936
|
+
if (engineStatus !== undefined && sessionId !== undefined && cwd !== null) {
|
|
4937
|
+
try {
|
|
4938
|
+
engineStatus.ensure(String(sessionId), cwd, anchorTime);
|
|
4939
|
+
} catch {}
|
|
4940
|
+
}
|
|
4941
|
+
const entry = sessionId === undefined ? undefined : snapshot.entries.get(String(sessionId));
|
|
4942
|
+
if (entry === undefined) {
|
|
4943
|
+
if (engineStatus === undefined)
|
|
4944
|
+
return unavailable2(anchorTime, "no-connection");
|
|
4945
|
+
if (sessionId === undefined)
|
|
4946
|
+
return unavailable2(anchorTime, "session-unknown");
|
|
4947
|
+
if (cwd === null)
|
|
4948
|
+
return unavailable2(anchorTime, "session-cwd-unknown");
|
|
4949
|
+
return { state: "loading", anchorTime, payload: null, at: null, turn: null, reason: null };
|
|
4702
4950
|
}
|
|
4951
|
+
if (entry.fetch.status === "unavailable")
|
|
4952
|
+
return unavailable2(anchorTime, entry.fetch.reason);
|
|
4953
|
+
return { state: "ok", anchorTime, payload: entry.fetch.payload, at: entry.fetch.at, turn: entry.fetch.turn, reason: null };
|
|
4703
4954
|
}
|
|
4704
4955
|
|
|
4705
4956
|
// src/client/panel/PanelView.tsx
|
|
4706
4957
|
var jsx_runtime12 = require("react/jsx-runtime");
|
|
4707
|
-
function
|
|
4708
|
-
|
|
4958
|
+
function formatSnapshotTime(at) {
|
|
4959
|
+
const ms = Date.parse(at);
|
|
4960
|
+
return Number.isNaN(ms) ? null : new Date(ms).toLocaleTimeString("en-GB");
|
|
4709
4961
|
}
|
|
4710
4962
|
function PanelContent({ tab, source, t }) {
|
|
4711
4963
|
if (tab === "agents") {
|
|
@@ -4726,10 +4978,10 @@ function PanelContent({ tab, source, t }) {
|
|
|
4726
4978
|
t
|
|
4727
4979
|
});
|
|
4728
4980
|
}
|
|
4729
|
-
function PanelView({ t, useChat }) {
|
|
4730
|
-
const {
|
|
4731
|
-
const [tab, setTab] =
|
|
4732
|
-
if (
|
|
4981
|
+
function PanelView({ t, useChat, useSessions, sessionId, engineStatus }) {
|
|
4982
|
+
const view = useMstarEngineStatus({ useChat, useSessions, sessionId, engineStatus });
|
|
4983
|
+
const [tab, setTab] = import_react4.useState("tasks");
|
|
4984
|
+
if (view.state === "waiting") {
|
|
4733
4985
|
return /* @__PURE__ */ jsx_runtime12.jsx("div", {
|
|
4734
4986
|
className: panel_module_default.emptyRoot,
|
|
4735
4987
|
"data-mstar-panel": "waiting",
|
|
@@ -4741,14 +4993,43 @@ function PanelView({ t, useChat }) {
|
|
|
4741
4993
|
})
|
|
4742
4994
|
});
|
|
4743
4995
|
}
|
|
4996
|
+
if (view.state === "loading") {
|
|
4997
|
+
return /* @__PURE__ */ jsx_runtime12.jsx("div", {
|
|
4998
|
+
className: panel_module_default.emptyRoot,
|
|
4999
|
+
"data-mstar-panel": "loading",
|
|
5000
|
+
"data-conversation-composer-overlay": "",
|
|
5001
|
+
children: /* @__PURE__ */ jsx_runtime12.jsx("p", {
|
|
5002
|
+
className: panel_module_default.empty,
|
|
5003
|
+
"data-mstar-empty": "loading",
|
|
5004
|
+
children: t("empty.loading")
|
|
5005
|
+
})
|
|
5006
|
+
});
|
|
5007
|
+
}
|
|
5008
|
+
if (view.state === "unavailable") {
|
|
5009
|
+
return /* @__PURE__ */ jsx_runtime12.jsx("div", {
|
|
5010
|
+
className: panel_module_default.emptyRoot,
|
|
5011
|
+
"data-mstar-panel": "unavailable",
|
|
5012
|
+
"data-conversation-composer-overlay": "",
|
|
5013
|
+
children: /* @__PURE__ */ jsx_runtime12.jsx("p", {
|
|
5014
|
+
className: panel_module_default.empty,
|
|
5015
|
+
"data-mstar-empty": "unavailable",
|
|
5016
|
+
"data-mstar-unavailable-reason": view.reason,
|
|
5017
|
+
children: t("empty.unavailable", { reason: view.reason })
|
|
5018
|
+
})
|
|
5019
|
+
});
|
|
5020
|
+
}
|
|
5021
|
+
const source = view.payload;
|
|
5022
|
+
const snapshotTime = formatSnapshotTime(view.at);
|
|
4744
5023
|
const noHarness = source.harnessDir === null && source.state === null && source.iteration == null;
|
|
4745
5024
|
const freshness = /* @__PURE__ */ jsx_runtime12.jsxs("footer", {
|
|
4746
5025
|
className: panel_module_default.freshness,
|
|
4747
5026
|
"data-mstar-freshness": true,
|
|
4748
5027
|
children: [
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
5028
|
+
snapshotTime === null ? null : /* @__PURE__ */ jsx_runtime12.jsx("span", {
|
|
5029
|
+
"data-mstar-freshness-at": view.at,
|
|
5030
|
+
"data-mstar-freshness-turn": String(view.turn),
|
|
5031
|
+
children: t("freshness.last-updated", { time: snapshotTime, turn: String(view.turn) })
|
|
5032
|
+
}),
|
|
4752
5033
|
/* @__PURE__ */ jsx_runtime12.jsx("span", {
|
|
4753
5034
|
children: t("freshness.refresh-note")
|
|
4754
5035
|
})
|
|
@@ -4844,8 +5125,10 @@ function PanelView({ t, useChat }) {
|
|
|
4844
5125
|
}
|
|
4845
5126
|
|
|
4846
5127
|
// src/client/index.ts
|
|
4847
|
-
var inject = ["slots", "sessions", "locale"];
|
|
5128
|
+
var inject = ["slots", "sessions", "locale", "connection"];
|
|
4848
5129
|
function apply(ctx) {
|
|
5130
|
+
const engineStatus = new MstarEngineStatusClient(ctx.connection);
|
|
5131
|
+
ctx.effect(() => () => engineStatus.dispose(), "ui-mstar-panel: engine-status client");
|
|
4849
5132
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "ui-mstar-panel: dictionaries");
|
|
4850
5133
|
ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
4851
5134
|
name: "conversation.view",
|
|
@@ -4853,7 +5136,7 @@ function apply(ctx) {
|
|
|
4853
5136
|
order: 20,
|
|
4854
5137
|
label: () => ctx.locale.bind(NS)("view.mstar-workflow"),
|
|
4855
5138
|
locale: NS
|
|
4856
|
-
}, PanelView));
|
|
5139
|
+
}, (props) => PanelView({ ...props, engineStatus })));
|
|
4857
5140
|
}
|
|
4858
5141
|
|
|
4859
5142
|
return module.exports; } });
|