@avantf/dsh-mission 0.3.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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +249 -0
  3. package/cordis.patch.yml +13 -0
  4. package/lib/client.js +225 -0
  5. package/lib/dsh-build.json +16 -0
  6. package/lib/envinit-bootstrap.js +334 -0
  7. package/lib/index.js +7021 -0
  8. package/lib/interface-version.json +4 -0
  9. package/lib/mission-core/capacity.d.ts +100 -0
  10. package/lib/mission-core/continuation.d.ts +106 -0
  11. package/lib/mission-core/dispatch.d.ts +165 -0
  12. package/lib/mission-core/engine.d.ts +331 -0
  13. package/lib/mission-core/index.d.ts +25 -0
  14. package/lib/mission-core/liveness.d.ts +99 -0
  15. package/lib/mission-core/prompt.d.ts +112 -0
  16. package/lib/mission-core/resources.d.ts +57 -0
  17. package/lib/mission-core/timing.d.ts +37 -0
  18. package/lib/mission-core/tree.d.ts +447 -0
  19. package/lib/mission-core/trouble.d.ts +40 -0
  20. package/lib/mission-core/types.d.ts +409 -0
  21. package/lib/mission-core/wellformed.d.ts +98 -0
  22. package/lib/types/claims.d.ts +3 -0
  23. package/lib/types/claims.d.ts.map +1 -0
  24. package/lib/types/client/MissionTreeView.d.ts +247 -0
  25. package/lib/types/client/MissionTreeView.d.ts.map +1 -0
  26. package/lib/types/client/api.d.ts +184 -0
  27. package/lib/types/client/api.d.ts.map +1 -0
  28. package/lib/types/client/contract.d.ts +209 -0
  29. package/lib/types/client/contract.d.ts.map +1 -0
  30. package/lib/types/client/index.d.ts +48 -0
  31. package/lib/types/client/index.d.ts.map +1 -0
  32. package/lib/types/client/seat.d.ts +26 -0
  33. package/lib/types/client/seat.d.ts.map +1 -0
  34. package/lib/types/client/styles.d.ts +6 -0
  35. package/lib/types/client/styles.d.ts.map +1 -0
  36. package/lib/types/coldResume.d.ts +123 -0
  37. package/lib/types/coldResume.d.ts.map +1 -0
  38. package/lib/types/domain.d.ts +93 -0
  39. package/lib/types/domain.d.ts.map +1 -0
  40. package/lib/types/envinit.d.ts +126 -0
  41. package/lib/types/envinit.d.ts.map +1 -0
  42. package/lib/types/executorSession.d.ts +129 -0
  43. package/lib/types/executorSession.d.ts.map +1 -0
  44. package/lib/types/faces.d.ts +30 -0
  45. package/lib/types/faces.d.ts.map +1 -0
  46. package/lib/types/host.d.ts +752 -0
  47. package/lib/types/host.d.ts.map +1 -0
  48. package/lib/types/index.d.ts +48 -0
  49. package/lib/types/index.d.ts.map +1 -0
  50. package/lib/types/interface_gate.d.ts +51 -0
  51. package/lib/types/interface_gate.d.ts.map +1 -0
  52. package/lib/types/log.d.ts +18 -0
  53. package/lib/types/log.d.ts.map +1 -0
  54. package/lib/types/projectionCache.d.ts +22 -0
  55. package/lib/types/projectionCache.d.ts.map +1 -0
  56. package/lib/types/prompt.d.ts +106 -0
  57. package/lib/types/prompt.d.ts.map +1 -0
  58. package/lib/types/source.d.ts +22 -0
  59. package/lib/types/source.d.ts.map +1 -0
  60. package/lib/types/store.d.ts +20 -0
  61. package/lib/types/store.d.ts.map +1 -0
  62. package/lib/types/timeFormat.d.ts +56 -0
  63. package/lib/types/timeFormat.d.ts.map +1 -0
  64. package/lib/types/tools.d.ts +34 -0
  65. package/lib/types/tools.d.ts.map +1 -0
  66. package/lib/types/wellformed.d.ts +43 -0
  67. package/lib/types/wellformed.d.ts.map +1 -0
  68. package/lib/types/wire.d.ts +178 -0
  69. package/lib/types/wire.d.ts.map +1 -0
  70. package/lib/types/workerEvents.d.ts +36 -0
  71. package/lib/types/workerEvents.d.ts.map +1 -0
  72. package/lib/types/workerSessions.d.ts +256 -0
  73. package/lib/types/workerSessions.d.ts.map +1 -0
  74. package/package.json +145 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * The "任务" view: one session's mission trees, driven by the `useSnapshot` hook so the
3
+ * component knows nothing about the transport; one mission's detail is read on demand and
4
+ * shown in a modal panel (left: section nav, right: the section, scrolled), the same shape
5
+ * as the shell's 设置 dialog. There is no toolbar (the engine pushes changes), deletion is a
6
+ * TREE-level gesture in the tree header (removing a node from a live tree leaves one that
7
+ * cannot converge), and a row's CHILDREN are open by default while its mission is in play and
8
+ * folded once it has finished — that folding is the tree, not the detail.
9
+ * @module @avantf/dsh-mission/client/MissionTreeView
10
+ */
11
+ import { type ReactNode } from 'react';
12
+ import type { ExecutorSessionLookup, MissionNodeDetail, MissionViewProps, WorkerSessionTarget } from './contract.js';
13
+ /**
14
+ * The one dialog's data, as this view tracks it (not part of the contract): which node it is
15
+ * for, and how far that read has got. One at a time, because the dialog is modal — the
16
+ * per-node cache the inline panel kept existed only to survive rows scrolling past.
17
+ */
18
+ interface DialogState {
19
+ readonly nodeId: string;
20
+ readonly status: 'loading' | 'ready' | 'error';
21
+ readonly detail?: MissionNodeDetail;
22
+ readonly error?: string;
23
+ }
24
+ /** How one on-demand full-result read is going, or `undefined` before it has been asked for. */
25
+ export interface FullResultState {
26
+ readonly status: 'loading' | 'ready' | 'error';
27
+ readonly text?: string;
28
+ readonly error?: string;
29
+ }
30
+ /**
31
+ * The result pane for ONE read state, rendered as a pure function so every rule that matters —
32
+ * expanded replaces the head, a failed read keeps the locator, no reader means no button — is
33
+ * testable without a DOM to click in.
34
+ */
35
+ export declare function ResultPane({ text, pointer, full, canLoad, onToggle }: {
36
+ text: string | null;
37
+ pointer: string | null;
38
+ full: FullResultState | undefined;
39
+ /** Whether a full read is even possible (a `result` face on the host). */
40
+ canLoad: boolean;
41
+ onToggle: () => void;
42
+ }): ReactNode;
43
+ /** One section of the detail dialog: its nav cell and the pane the cell shows. */
44
+ export interface DetailTab {
45
+ readonly id: string;
46
+ readonly label: string;
47
+ /** How many entries the section holds; rendered after the label when present. */
48
+ readonly count?: number;
49
+ readonly body: ReactNode;
50
+ }
51
+ /**
52
+ * The detail's sections, derived from ONE detail read.
53
+ *
54
+ * Exported and pure so the mapping is testable without a DOM: the spec renders to static
55
+ * markup and cannot click a nav cell, so "what does the 纠偏 section hold" has to be
56
+ * askable of a function rather than of a rendered, clicked dialog.
57
+ *
58
+ * A section with nothing to show is ABSENT rather than empty: a 「纠偏」 tab reading
59
+ * 「(无)」 on a mission that was never steered is a section that exists only to say it does
60
+ * not. 内容 / 结果 always exist — every mission has a body to achieve, and "no result yet" is
61
+ * itself a fact about one.
62
+ *
63
+ * 标题 is NOT a section: it heads the dialog (see `MissionDetailDialog`), because it answers
64
+ * "which mission am I looking at" — a question asked before any section is chosen, and one a
65
+ * reader must be able to answer while reading every one of them. What used to share that tab
66
+ * (id / dispatch count / depth) rides the same header as its meta line.
67
+ *
68
+ * Order is the order a reader asks the questions: what it must achieve (内容) → why it exists
69
+ * (上下文) → how the executor reasoned about splitting it (拆解信息) → how the direction
70
+ * changed (纠偏) → what came out (结果) → what the parts reported (子任务).
71
+ */
72
+ export declare function detailTabs(detail: MissionNodeDetail, options?: {
73
+ readonly loadResult?: (nodeId: string) => Promise<string>;
74
+ }): readonly DetailTab[];
75
+ /**
76
+ * Take everything that is NOT on the path from `overlay` up to `<body>` out of the tab order, and
77
+ * return the undo.
78
+ *
79
+ * `inert` is the platform's own "not tabbable, not clickable, hidden from assistive tech" flag, and
80
+ * the shell's overlays use it for the same reason. Only elements that were not ALREADY inert are
81
+ * touched, so an overlay opened over another one restores exactly what it changed. The walk goes all
82
+ * the way up, not just to the panel's own root: the composer and the sidebars are behind the mask
83
+ * too, and a dialog that lets Tab reach them is modal only in appearance.
84
+ */
85
+ export declare function inertBackground(overlay: HTMLElement): () => void;
86
+ /**
87
+ * The address of one worker session, exactly as the host's `uiWorkspace.openSession` takes it: the
88
+ * durable parent/child address of a *continuable* subagent — what the main UI sends when a child is
89
+ * picked from the "N 个子智能" dropdown. The PARENT is the owner session this panel belongs to.
90
+ *
91
+ * Exported, with `workerSessionOpen`/`createWorkerSessionClick`/`NodeIdEntry`, because this suite
92
+ * has no DOM: a spec cannot press a rendered button, so it asks these functions (the very ones the
93
+ * entry is wired to) what a click sends. That keeps "点击发出的 target 形状" pinned without adding a
94
+ * browser environment.
95
+ */
96
+ export declare function workerSessionTarget(parentSessionId: string, workerSessionId: string): WorkerSessionTarget;
97
+ /**
98
+ * What a node-id entry says, in BOTH cases — W18: the id is an entry whether or not the record
99
+ * already names an executor. The wording names the DESTINATION (the session that executed this
100
+ * mission) and its state, because the thing rendered is the MISSION's id and a reader must not
101
+ * mistake the click for "open the task".
102
+ *
103
+ * Without a handle the label stays honest rather than promising a session that may not exist: the
104
+ * click TRIES, and a miss is explained by {@link workerFailureText} instead of being a dead link.
105
+ * That is also why the two forms are not the same sentence — a reader can tell whether a lookup is
106
+ * about to happen.
107
+ */
108
+ export declare function nodeIdLinkLabel(workerSessionId: string | null | undefined, workerLive: boolean | undefined): string;
109
+ /** Why one click could not open a session. Four kinds, four sentences (see {@link workerFailureText}). */
110
+ export type WorkerSessionFailureReason = 'never-dispatched' | 'not-found' | 'unsupported' | 'open';
111
+ /**
112
+ * The sentence a failed click leaves behind, per reason. Exported and pure so each of the four is
113
+ * pinned by a test (this suite has no DOM) and so no caller has to invent wording:
114
+ *
115
+ * - 「从未派发过」 is a FACT about the record, not a failure to find something;
116
+ * - 「找不到」 is the case the spec names: a node that WAS dispatched, whose session is gone;
117
+ * - 「无法打开」 is the host's own limitation — no `uiWorkspace` to navigate with, no session service
118
+ * to look one up in, or an older host whose Remote face predates the lookup — and the detail says
119
+ * which one, since the three have different remedies;
120
+ * - 「打开失败」 is the one case where a session WAS named and navigation refused it.
121
+ */
122
+ export declare function workerFailureText(reason: WorkerSessionFailureReason, detail?: string): string;
123
+ /** What a busy button shows instead of the id — a separate export because there is no DOM here to
124
+ * re-render, so the affordance itself is what a test can pin. */
125
+ export declare function workerSessionBusyText(busy: boolean, nodeId: string): string;
126
+ /** What one click did, as data: a test pins the address that was opened, and the panel renders this
127
+ * failure instead of the click having "done nothing". */
128
+ export type WorkerSessionOpenOutcome = {
129
+ readonly opened: true;
130
+ readonly workerSessionId: string;
131
+ } | {
132
+ readonly opened: false;
133
+ readonly reason: WorkerSessionFailureReason;
134
+ readonly message: string;
135
+ };
136
+ /**
137
+ * The panel's one click handler, W18: the node id is ALWAYS an entry, and the session is resolved
138
+ * lazily when the record does not already name one.
139
+ *
140
+ * Order: ① a handle on the record opens immediately (zero I/O); ② otherwise `resolveSession` asks the
141
+ * host, and ONLY a `resolved` answer is opened; ③ a miss becomes a sentence about WHY — never a throw,
142
+ * never a blank panel. `onLookupStart` fires before the first await, which is what lets the button
143
+ * say 查找中… instead of looking like it ignored the click.
144
+ *
145
+ * `open` may throw or answer with a rejected promise (the session really was cleaned up): both become
146
+ * the `open` failure. The panel must never blank — or lose its place — because a link went stale,
147
+ * which is the one failure a link invited by us can cause.
148
+ */
149
+ export declare function workerSessionOpen(input: {
150
+ readonly nodeId: string;
151
+ readonly parentSessionId: string;
152
+ /** The handle already on the record, when there is one. */
153
+ readonly workerSessionId?: string | null;
154
+ readonly open?: (target: WorkerSessionTarget) => unknown;
155
+ readonly resolveSession?: (nodeId: string) => Promise<ExecutorSessionLookup>;
156
+ readonly onLookupStart?: () => void;
157
+ }): Promise<WorkerSessionOpenOutcome>;
158
+ /**
159
+ * The click handler the entry is wired to, as a function of the entry's own state setters.
160
+ *
161
+ * Split out for the same reason `workerSessionTarget` is: this suite has no DOM, so "what a click
162
+ * does" has to be askable of a function rather than of a rendered, clicked button. It is also where
163
+ * the 查找中… bookkeeping lives — `onLookupStart` fires only when a lookup is really needed, so a
164
+ * click that already has a handle does not flash a wait state it never had.
165
+ */
166
+ export declare function createWorkerSessionClick(input: {
167
+ readonly nodeId: string;
168
+ readonly parentSessionId: string;
169
+ readonly workerSessionId?: string | null;
170
+ readonly open?: (target: WorkerSessionTarget) => unknown;
171
+ readonly resolveSession?: (nodeId: string) => Promise<ExecutorSessionLookup>;
172
+ /** Whether a click is already in flight; a second one is ignored rather than queued. */
173
+ readonly busy: boolean;
174
+ readonly setBusy: (busy: boolean) => void;
175
+ readonly setFailed: (failed: boolean) => void;
176
+ readonly onFailure: (message: string) => void;
177
+ }): () => Promise<void>;
178
+ /**
179
+ * One node id as an ENTRY. W18: the id is clickable on EVERY node — an existing handle opens its
180
+ * session directly, and a historical record without one is looked up at CLICK time (the lookup reads
181
+ * session logs, so it must never happen while rendering). Both places that show a node id (the tree
182
+ * header's root id and the detail dialog's heading) render this, so the same thing never becomes two
183
+ * links — W8 put a second, separate link on the worker session id, and that link is what this
184
+ * replaces.
185
+ *
186
+ * Two degradation paths, both deliberate:
187
+ * ① the host cannot complete the jump — no `uiWorkspace` service, no session service, or an older
188
+ * host whose Remote face predates the lookup → the click SAYS which
189
+ * ({@link workerFailureText}); the id stays an entry rather than a dead-looking one, so the
190
+ * explanation is one click away instead of a tooltip a reader has to go looking for;
191
+ * ② `open` throws or rejects (the session really was cleaned up) → `onFailure` turns it into the
192
+ * caller's message; the panel never blanks and never loses its place.
193
+ */
194
+ export declare function NodeIdEntry({ nodeId, workerSessionId, workerLive, sessionId, open, onFailure, resolveSession, className, }: {
195
+ /** The MISSION's id — what is rendered; the session is only the destination. */
196
+ nodeId: string;
197
+ workerSessionId: string | null | undefined;
198
+ workerLive?: boolean;
199
+ /** The owner session the worker hangs under — the parent half of the address. */
200
+ sessionId: string;
201
+ open?: (target: WorkerSessionTarget) => void;
202
+ onFailure: (message: string) => void;
203
+ /** The click-time lookup; absent on a host whose Remote face predates it (see `MissionViewProps`). */
204
+ resolveSession?: (nodeId: string) => Promise<ExecutorSessionLookup>;
205
+ /** The caller's own monospace slot class, applied to both the link and the plain-text form. */
206
+ className: string;
207
+ }): ReactNode;
208
+ /**
209
+ * The inline message a failed open leaves behind. Split out as a pure component, like `ResultPane`,
210
+ * so its markup is testable without a DOM to click in; the dialog owns the state that shows it.
211
+ */
212
+ export declare function WorkerSessionHint({ message }: {
213
+ message: string;
214
+ }): ReactNode;
215
+ /**
216
+ * One mission's detail as a MODAL panel: a section rail on the left, the chosen section in a
217
+ * scrollable column on the right — the shell's 设置 dialog, sized for reading (1040×880, capped
218
+ * by the viewport) rather than the settings' 800×800.
219
+ *
220
+ * The heading is the mission's own TITLE, not a 「标题」 section: "which mission is this" is asked
221
+ * before a section is chosen and must stay answerable while reading every one of them.
222
+ *
223
+ * Why a dialog instead of an expanding row: the detail exists to be READ and it runs long —
224
+ * a description, every correction, every child's conclusion. Inline, it pushed the tree down
225
+ * by however much the text happened to be, so comparing two missions meant scrolling one of
226
+ * them out of sight. A fixed panel with its own scroll keeps the tree where it was.
227
+ */
228
+ export declare function MissionDetailDialog({ nodeId, state, onClose, loadResult, sessionId, openWorkerSession, resolveWorkerSession }: {
229
+ nodeId: string;
230
+ /** `undefined` until this node's first read lands; a loading state once it has been asked for. */
231
+ state: DialogState | undefined;
232
+ onClose: () => void;
233
+ /** Read a spilled result back in full. Absent in a host that has no `result` face, and then the
234
+ * pane shows the locator without offering a read it cannot perform. */
235
+ loadResult?: (nodeId: string) => Promise<string>;
236
+ /** The owner session the shown mission's worker hangs under — the parent half of the address. */
237
+ sessionId: string;
238
+ /** Open the executor's session; absent on a host with no `uiWorkspace` service, and then the id
239
+ * is rendered as plain text instead of a dead link (see `MissionViewProps.openWorkerSession`). */
240
+ openWorkerSession?: (target: WorkerSessionTarget) => void;
241
+ /** The click-time lookup for a record with no handle (see `MissionViewProps.resolveWorkerSession`). */
242
+ resolveWorkerSession?: (nodeId: string) => Promise<ExecutorSessionLookup>;
243
+ }): ReactNode;
244
+ /** Render the session's mission trees. */
245
+ export declare function MissionTreeView({ useSnapshot, onDeleteTree, onCleanFinished, loadDetail, loadResult, sessionId, openWorkerSession, resolveWorkerSession, }: MissionViewProps): ReactNode;
246
+ export {};
247
+ //# sourceMappingURL=MissionTreeView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MissionTreeView.d.ts","sourceRoot":"","sources":["../../../src/client/MissionTreeView.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,184 @@
1
+ import type { ExecutorSessionLookup, MissionNodeDetail, MissionSnapshot } from './contract.js';
2
+ /** Safety-net re-read interval; slow on purpose, since the session itself is the primary trigger. */
3
+ export declare const POLL_INTERVAL_MS = 5000;
4
+ /**
5
+ * Coalescing window: one turn's burst of session revisions becomes a single read.
6
+ */
7
+ export declare const REFRESH_COALESCE_MS = 400;
8
+ /**
9
+ * How often the panel re-reads even while the engine's change stream is up. The stream is the update
10
+ * channel, but a stream that is silently dead (open, delivering nothing) is not detectable from this
11
+ * side, and the panel must not be able to go stale forever — this bounds the staleness. It is not a
12
+ * "poll": at 30 s it is a backstop, and a healthy stream still gives sub-second freshness.
13
+ */
14
+ export declare const STREAM_KEEPALIVE_MS = 30000;
15
+ /**
16
+ * The cheap session-side value that changes when the mission tree may have. It deliberately does NOT
17
+ * include the chat node count: the mission tree is not a function of how many messages the
18
+ * conversation holds, and folding that in made every chat activity re-read and re-render the whole
19
+ * tree (the stream is what carries actual mission changes; `queued`/`running` are the session-side
20
+ * facts that matter).
21
+ */
22
+ export declare function sessionRevision(input: {
23
+ readonly queued: number;
24
+ readonly running: boolean;
25
+ }): string;
26
+ /** The Remote namespace surface this plugin consumes. */
27
+ export interface MissionRemote {
28
+ snapshot: (args: {
29
+ sessionId: string;
30
+ }) => Promise<unknown>;
31
+ detail: (args: {
32
+ sessionId: string;
33
+ nodeId: string;
34
+ }) => Promise<unknown>;
35
+ delete: (args: {
36
+ sessionId: string;
37
+ rootId: string;
38
+ }) => Promise<unknown>;
39
+ /** Delete EVERY closed tree this session owns (the panel's "清理已完成"). Optional for the same
40
+ * reason as `result`: an older host has no such method, and the panel refuses to send the call
41
+ * rather than reading the gateway's 404 as "the missions are gone". */
42
+ cleanFinished?: (args: {
43
+ sessionId: string;
44
+ }) => Promise<unknown>;
45
+ /** The FULL text behind a spilled result. Optional for the same reason `watch` is: an older host
46
+ * simply does not have it, and the pane falls back to showing the locator. */
47
+ result?: (args: {
48
+ sessionId: string;
49
+ nodeId: string;
50
+ }) => Promise<unknown>;
51
+ /** The click-time executor lookup (W18). Optional for the same reason as `result`: an older host
52
+ * has no such method, and the panel says "the two halves are out of step" instead of guessing. */
53
+ resolveExecutorSession?: (args: {
54
+ sessionId: string;
55
+ nodeId: string;
56
+ }) => Promise<unknown>;
57
+ /** The engine's change stream, called with the transport's cancellation signal.
58
+ * Optional: an older host may not expose it, so the caller keeps its timer fallback. */
59
+ watch?: (args: {
60
+ sessionId: string;
61
+ }, signal: AbortSignal) => AsyncIterable<unknown>;
62
+ }
63
+ /** A timer as the client platform publishes it (`interval` returns its disposer). */
64
+ export interface IntervalTimer {
65
+ interval: (callback: () => void, delay: number) => () => void;
66
+ }
67
+ /**
68
+ * The human half of a failure. `Error#message` is NOT an own enumerable property, so a plain
69
+ * `JSON.stringify(error)` drops exactly the sentence a reader needs and leaves `{"code":…}` — the
70
+ * `RemoteError` this boundary carries is an `Error` subclass with `code`/`details` as the own ones.
71
+ * So: the message first, the JSON only when there is no message to show.
72
+ */
73
+ export declare function errorText(error: unknown): string;
74
+ /**
75
+ * Collapse a burst of triggers into ONE call after `delayMs` of quiet.
76
+ *
77
+ * The mission tree has three refresh triggers (the session revision, the engine's change stream, and the
78
+ * stream's reopen) and the engine pushes a frame per state change, so without this a burst of changes
79
+ * costs one snapshot RPC per frame. `cancel` releases a pending call — the disposer of the effect that
80
+ * owns the coalescer, so leaving the tab cannot fire a read into an unmounted panel.
81
+ */
82
+ export declare function coalesce(run: () => void, delayMs: number): {
83
+ request: () => void;
84
+ cancel: () => void;
85
+ };
86
+ /** Peel the transport envelope (`{ ok, value }` / `{ ok, error }`, or a bare value) so a
87
+ * wiring mistake surfaces as a message rather than an empty view. */
88
+ export declare function unwrap(response: unknown): {
89
+ value?: unknown;
90
+ error?: string;
91
+ };
92
+ /**
93
+ * The version-skew note for one `snapshot` payload, or `undefined` when the two halves agree.
94
+ *
95
+ * WHY A NOTE AND NOT A THROW. The host half is loaded ONCE when `dsh web` starts; this bundle is
96
+ * re-read on every page load. So a rebuilt client routinely talks to an older host, and the ONE thing
97
+ * the marker must never do is blank the panel: `snapshot` is the entry point every other read hangs
98
+ * off, and an unrecognised revision usually still carries the fields the panel renders. So an absent
99
+ * marker ("host predates it") and an unknown one ("host is newer") both come back as a note beside the
100
+ * tree, never as a failed read.
101
+ *
102
+ * `wire` is read off the RAW payload rather than a validated field: the schema accepts any number, and
103
+ * a payload whose `wire` is missing must not be reported as a shape failure.
104
+ */
105
+ export declare function snapshotSkew(value: unknown): string | undefined;
106
+ /** Read one session's trees; throws with the reason when the read cannot be trusted. */
107
+ export declare function fetchSnapshot(remote: MissionRemote, sessionId: string): Promise<MissionSnapshot>;
108
+ /**
109
+ * Delete one whole finished mission tree by root — the unit is the TREE; a refusal travels in
110
+ * the result envelope and becomes an exception here.
111
+ */
112
+ export declare function deleteWork(remote: MissionRemote, sessionId: string, rootId: string): Promise<readonly string[]>;
113
+ /**
114
+ * Delete EVERY closed tree this session owns — the batch entry behind the panel's "清理已完成".
115
+ *
116
+ * The first half is the version-skew gate, exactly as {@link fetchExecutorSession}'s: against a host
117
+ * whose `wire` predates the method, sending the call can only produce a gateway 404 that reads like
118
+ * "the missions are gone". The call is not sent, and the sentence names the remedy (restart dsh).
119
+ *
120
+ * Returns the two id lists the host reported — the roots removed and the roots kept because they were
121
+ * never retired — so the caller can render "N removed / M skipped" without parsing prose.
122
+ */
123
+ export declare function cleanFinishedWork(remote: MissionRemote, sessionId: string, hostWire?: number): Promise<{
124
+ deleted: readonly string[];
125
+ skipped: readonly string[];
126
+ }>;
127
+ export declare function asDetail(value: unknown): Partial<MissionNodeDetail> & {
128
+ error?: string;
129
+ };
130
+ /** Read one mission's full detail on demand — results run to 2 KB each, and the snapshot re-reads on change. */
131
+ export declare function fetchDetail(remote: MissionRemote, sessionId: string, nodeId: string): Promise<MissionNodeDetail>;
132
+ /**
133
+ * Name a transport failure that is really a VERSION SKEW.
134
+ *
135
+ * The two halves of this plugin do not update together: the browser bundle is re-read on every page
136
+ * load, the host half is loaded once when `dsh web` starts. So a rebuilt plugin routinely talks to an
137
+ * older host, and the gateway answers a method that host never published with an HTTP 404 — which
138
+ * reads exactly like "the mission or the file is gone", the two things it does NOT mean. The sentence
139
+ * therefore names the missing REGISTRATION (an older host, or one that was never restarted) rather
140
+ * than only repeating the transport text.
141
+ */
142
+ export declare function transportHint(cause: unknown): string;
143
+ /**
144
+ * Read the FULL text behind a spilled result. On demand only: the pane offering it is answering a
145
+ * question ("show me all of it"), and the host reads a file to answer — so nothing prefetches it.
146
+ * A host that cannot resolve the locator answers with a reason instead, which the pane shows beside
147
+ * the locator rather than as a failed read.
148
+ */
149
+ export declare function fetchFullResult(remote: MissionRemote, sessionId: string, nodeId: string): Promise<string>;
150
+ /**
151
+ * Ask the host to find the session that ran one node (W18). CALLED ONLY FROM A CLICK: the server
152
+ * side lists the session corpus and reads a few logs, which is exactly the cost the panel must not
153
+ * pay while rendering.
154
+ *
155
+ * Four failure shapes, kept apart on purpose:
156
+ * - the host reports a `wire` older than the method (an older host, or one never restarted) → the call
157
+ * is NOT SENT at all, and the sentence names the remedy (restart dsh);
158
+ * - the host has no such method despite a current `wire` → the same version-skew sentence;
159
+ * - the call itself failed (transport) → `transportHint`'s reading of it;
160
+ * - the host ANSWERED "never dispatched" / "not found" / "cannot look up" → returned as an answer,
161
+ * because those are outcomes a reader must be told about, not failures of the panel.
162
+ *
163
+ * `hostWire` is the revision the host reported on its last `snapshot` (carried on the snapshot the
164
+ * panel renders from). Checked FIRST and before the remote is even touched: against a host that never
165
+ * registered the method, sending the call can only produce a gateway 404 that reads like a deleted
166
+ * mission (see `transportHint`).
167
+ */
168
+ export declare function fetchExecutorSession(remote: MissionRemote, sessionId: string, nodeId: string, hostWire?: number): Promise<ExecutorSessionLookup>;
169
+ /**
170
+ * Follow the engine's change stream, calling `onChange` per frame; the frame carries a
171
+ * revision and nothing else. The CALLER decides what a normal end means.
172
+ */ export declare function watchChanges(remote: MissionRemote, sessionId: string, signal: AbortSignal, onChange: () => void): Promise<void>;
173
+ export declare const STREAM_REOPEN_MS = 1000;
174
+ /**
175
+ * Start re-reading on a timer — a poll, not a subscription, because the tree lives in a
176
+ * host-side KV domain with no event feed. Two rungs because the platform `timer` service is
177
+ * NOT reliably mounted: the service is preferred, the browser's own timers are the fallback.
178
+ */
179
+ export declare function startPolling(refresh: () => void, intervalMs: number, service: Partial<IntervalTimer> | undefined, host?: HostTimers): () => void;
180
+ export interface HostTimers {
181
+ setInterval?: (callback: () => void, delay: number) => unknown;
182
+ clearInterval?: (handle: unknown) => void;
183
+ }
184
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/client/api.ts"],"names":[],"mappings":"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"}