dsh-pause-session 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +51 -20
  2. package/DEVELOPMENT.md +25 -2
  3. package/README.md +41 -31
  4. package/README.zh.md +31 -21
  5. package/REHEARSAL.md +6 -6
  6. package/lib/client.js +1125 -1375
  7. package/lib/client.js.map +1 -1
  8. package/lib/index.js +1107 -1081
  9. package/lib/index.js.map +1 -1
  10. package/lib/typert.host.d.ts +3 -0
  11. package/lib/typert.host.js +339 -0
  12. package/lib/typert.remote-client.d.ts +38 -0
  13. package/lib/typert.remote-client.d.ts.map +1 -0
  14. package/lib/typert.remote-client.js +244 -0
  15. package/lib/types/client/control.d.ts +25 -158
  16. package/lib/types/client/global.d.ts +39 -239
  17. package/lib/types/client/index.d.ts +6 -5
  18. package/lib/types/client/locales.d.ts +3 -4
  19. package/lib/types/client/pill.d.ts +5 -5
  20. package/lib/types/client/remote.d.ts +77 -0
  21. package/lib/types/client/view.d.ts +19 -5
  22. package/lib/types/coldlog.d.ts +1 -1
  23. package/lib/types/composition.d.ts +35 -61
  24. package/lib/types/control.d.ts +41 -60
  25. package/lib/types/global.d.ts +6 -4
  26. package/lib/types/index.d.ts +2 -2
  27. package/lib/types/park-state.d.ts +51 -0
  28. package/lib/types/registry.d.ts +1 -1
  29. package/lib/types/release-order.d.ts +25 -0
  30. package/lib/types/remote-adapter.d.ts +22 -0
  31. package/lib/types/remote.d.ts +158 -0
  32. package/lib/types/resume-activation.d.ts +36 -0
  33. package/lib/types/resume-text.d.ts +67 -0
  34. package/lib/types/resume.d.ts +7 -104
  35. package/lib/types/shared.d.ts +20 -123
  36. package/lib/types/snapshot.d.ts +29 -0
  37. package/lib/types/triage.d.ts +8 -9
  38. package/package.json +38 -19
  39. package/lib/types/client/burst.d.ts +0 -253
@@ -11,8 +11,8 @@
11
11
  * stored log through a read handle that claims no ownership and writes nothing,
12
12
  * so asking what happened cannot itself change what happened.
13
13
  *
14
- * WHAT ONE READ COSTS. The list is read again on every command-registry change
15
- * while the panel is open, and what it publishes is about the end of a log that
14
+ * WHAT ONE READ COSTS. The list is read when the panel opens and when a native
15
+ * action changes the park, and what it publishes is about the end of a log that
16
16
  * only grows, so `./coldlog.ts` bounds each row's read to that end. A row whose
17
17
  * facts the bound cannot hold reports itself unreadable rather than publishing
18
18
  * a fragment.
@@ -35,10 +35,10 @@
35
35
  * reported; a withheld promise costs a user nothing, a broken one spends their
36
36
  * money.
37
37
  *
38
- * WHO MAY DECIDE. A decision names its target as command input, and the command
39
- * channel carries no origin of its own, so this list acts only on rows its most
40
- * recent read published to the calling session: the caller's own read is what
41
- * makes a target actionable. Anything else is refused, not looked up.
38
+ * WHO MAY DECIDE. A decision carries the calling session and its target as wire
39
+ * input, so this list acts only on rows its most recent read published to the
40
+ * calling session: the caller's own read is what makes a target actionable.
41
+ * Anything else is refused, not looked up.
42
42
  *
43
43
  * TWO OF THE FIVE VERBS PUT THE SESSION BACK ON THE CLEAN PATH. Continue and
44
44
  * continue-with-the-note go through `ResumePath.decideUnclean`, which activates
@@ -58,12 +58,11 @@
58
58
  */
59
59
  import type { Context } from '@deepseek-ai/cordis';
60
60
  import type { SessionId } from '@deepseek-ai/dsh-session';
61
- import type { CommandResult } from '@deepseek-ai/dsh-commands';
62
61
  import type { ParkRegistry } from './registry.js';
63
62
  import type { ResumePath } from './resume.js';
64
63
  import type { TriageAction, TriageList } from './shared.js';
64
+ import type { PauseAnswer } from './shared.js';
65
65
  /** Result copy for a decision the list itself could not carry out. */
66
- export declare const TRIAGE_FAILED_TEXT = "pause could not carry out that decision; the session is exactly as it was.";
67
66
  /** Result copy for leaving one listed session alone. */
68
67
  export declare const TRIAGE_LEFT_TEXT = "pause left this session paused; it is still on the list.";
69
68
  /** Result copy for a decision aimed at a session that is not on the list. */
@@ -104,7 +103,7 @@ export interface TriageListService {
104
103
  * @param signal - cancellation for the cold load and setup.
105
104
  * @returns the outcome for whoever asked.
106
105
  */
107
- decide(caller: SessionId, sessionId: SessionId, action: TriageAction, signal?: AbortSignal): Promise<CommandResult>;
106
+ decide(caller: SessionId, sessionId: SessionId, action: TriageAction, signal?: AbortSignal): Promise<PauseAnswer>;
108
107
  }
109
108
  /**
110
109
  * Mount the decision list over the durable registry and the clean resume path.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-pause-session",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "DSH plugin: pause every running session (and its subagents) at a safe point before you leave, then resume them the next morning as if nothing was interrupted.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -17,7 +17,19 @@
17
17
  "./cordis.patch.yml": "./cordis.patch.yml",
18
18
  "./package.json": "./package.json",
19
19
  "./locale/*.json": "./locale/*.json",
20
- "./icon": "./icon.svg"
20
+ "./icon": "./icon.svg",
21
+ "./typert": {
22
+ "types": "./lib/typert.host.d.ts",
23
+ "default": "./lib/typert.host.js"
24
+ },
25
+ "./remote": {
26
+ "types": "./lib/typert.remote-client.d.ts",
27
+ "default": "./lib/typert.remote-client.js"
28
+ },
29
+ "./types": {
30
+ "types": "./lib/types/remote.d.ts",
31
+ "default": "./lib/types/remote.js"
32
+ }
21
33
  },
22
34
  "files": [
23
35
  "lib/index.js",
@@ -34,16 +46,13 @@
34
46
  "cordis.patch.yml",
35
47
  "locale/*.json",
36
48
  "icon.svg",
37
- "docs/images/pause-controls.webp"
49
+ "docs/images/pause-controls.webp",
50
+ "lib/typert.host.js",
51
+ "lib/typert.host.d.ts",
52
+ "lib/typert.remote-client.js",
53
+ "lib/typert.remote-client.d.ts",
54
+ "lib/typert.remote-client.d.ts.map"
38
55
  ],
39
- "scripts": {
40
- "build:host": "tsc -p tsconfig.json",
41
- "build:client": "tsc -p tsconfig.client.json && tsdown",
42
- "build": "npm run build:host && npm run build:client && node scripts/trim-build-whitespace.mjs",
43
- "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
44
- "test": "vitest run",
45
- "verify": "npm run typecheck && npm run build && npm run test"
46
- },
47
56
  "dsh": {
48
57
  "bundle": {
49
58
  "patch": "./cordis.patch.yml"
@@ -66,7 +75,6 @@
66
75
  "@deepseek-ai/dsh-client-ui-conversation": "*",
67
76
  "@deepseek-ai/dsh-client-ui-renderer": "*",
68
77
  "@deepseek-ai/dsh-client-ui-session": "*",
69
- "@deepseek-ai/dsh-commands": "*",
70
78
  "@deepseek-ai/dsh-goal": "*",
71
79
  "@deepseek-ai/dsh-jobs": "*",
72
80
  "@deepseek-ai/dsh-llm": "*",
@@ -74,15 +82,16 @@
74
82
  "@deepseek-ai/dsh-subagent": "*",
75
83
  "@deepseek-ai/dsh-session": "*",
76
84
  "@deepseek-ai/dsh-session-query": "*",
77
- "@deepseek-ai/dsh-agent-preset-registry": "*"
85
+ "@deepseek-ai/dsh-agent-preset-registry": "*",
86
+ "@deepseek-ai/dsh-typert-protocol": "*"
78
87
  },
79
88
  "dependencies": {
80
- "zod": "4.4.3"
89
+ "zod": "4.6.6"
81
90
  },
82
91
  "devDependencies": {
83
92
  "@deepseek-ai/cordis": "link:../../deepseek-harness/vendor/cordis",
84
- "@deepseek-ai/cordis-plugin-include": "1.0.6",
85
- "@deepseek-ai/cordis-plugin-loader": "1.0.2",
93
+ "@deepseek-ai/cordis-plugin-include": "1.1.0",
94
+ "@deepseek-ai/cordis-plugin-loader": "1.1.0",
86
95
  "@deepseek-ai/dsh-agent": "link:../../deepseek-harness/packages/core/agent",
87
96
  "@deepseek-ai/dsh-agent-loop": "link:../../deepseek-harness/packages/core/agent-loop",
88
97
  "@deepseek-ai/dsh-agent-loop-testkit": "link:../../deepseek-harness/packages/test-support/agent-loop-testkit",
@@ -94,7 +103,6 @@
94
103
  "@deepseek-ai/dsh-client-ui-renderer": "link:../../deepseek-harness/packages/client/ui-renderer",
95
104
  "@deepseek-ai/dsh-client-ui-session": "link:../../deepseek-harness/packages/client/ui-session",
96
105
  "@deepseek-ai/dsh-client-ui-slots": "link:../../deepseek-harness/packages/client/ui-slots",
97
- "@deepseek-ai/dsh-commands": "link:../../deepseek-harness/packages/interaction/commands",
98
106
  "@deepseek-ai/dsh-goal": "link:../../deepseek-harness/packages/goal/goal",
99
107
  "@deepseek-ai/dsh-goal-round-driver": "link:../../deepseek-harness/packages/goal/goal-round-driver",
100
108
  "@deepseek-ai/dsh-jobs": "link:../../deepseek-harness/packages/jobs/jobs",
@@ -122,7 +130,9 @@
122
130
  "react": "^18.2.0",
123
131
  "tsdown": "0.22.2",
124
132
  "typescript": "^5.7.0",
125
- "vitest": "^3.0.0"
133
+ "vitest": "^3.0.0",
134
+ "@deepseek-ai/dsh-typert-protocol": "link:../../deepseek-harness/packages/typert/protocol",
135
+ "@deepseek-ai/dsh-typert-generator": "link:../../deepseek-harness/packages/typert/generator"
126
136
  },
127
137
  "license": "MIT",
128
138
  "engines": {
@@ -146,5 +156,14 @@
146
156
  ],
147
157
  "publishConfig": {
148
158
  "access": "public"
159
+ },
160
+ "scripts": {
161
+ "build:host": "tsc -p tsconfig.json",
162
+ "build:client": "tsc -p tsconfig.client.json && tsdown",
163
+ "build": "npm run generate:typert && npm run build:host && npm run build:client && node scripts/trim-build-whitespace.mjs",
164
+ "typecheck": "npm run generate:typert && tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
165
+ "test": "vitest run",
166
+ "verify": "npm run typecheck && npm run build && npm run test",
167
+ "generate:typert": "node scripts/generate-typert.mjs"
149
168
  }
150
- }
169
+ }
@@ -1,253 +0,0 @@
1
- /**
2
- * The re-read burst both browser controls share: a handful of reads after
3
- * something moved, and silence once it settles.
4
- *
5
- * What a control reads is published through the host's command channel, and
6
- * every read is two durable `command/run|done` records in the address session's
7
- * log plus an aggregate read on the host. Reading is therefore something the
8
- * host announces and the user asks for, never a heartbeat:
9
- *
10
- * - the host announces a transition by registering or unregistering the paused
11
- * session's marker commands, and — for a phase change, which no registration
12
- * of its own would show — by registering that session's status verb again.
13
- * Each of those emits `commands/change`, and that event, plus the user's own
14
- * press or decision, is what starts a burst;
15
- * - one transition announces itself several times: a park registers two or three
16
- * marker commands per session and a phase change re-registers one, and the host
17
- * emits one `commands/change` per step. The burst therefore waits out a short
18
- * `windowMs` before its first read and any trigger inside that window
19
- * postpones it, so the announcements of one transition start one burst rather
20
- * than one each;
21
- * - a change that arrives within `ACTION_ABSORB_MS` of the user's own action is
22
- * that action's own consequence, not news: the action already read the host,
23
- * so the answer is on screen, and the unregistrations it caused — one event
24
- * per marker command, measured seconds apart, which the window above cannot
25
- * merge — would only be read back. Such a change is absorbed, and each one
26
- * absorbed pushes the window out, because a storm that keeps arriving is
27
- * still the same storm. The window always closes: the first change after
28
- * `ACTION_ABSORB_MS` of quiet is news again, and a user action reads on its
29
- * own whatever the window says;
30
- * - an absorbed change is deferred rather than lost: a control whose action left
31
- * a phase still moving asks for one look at the moment the window closes
32
- * ({@link ReadBurst.recheckAfterEcho}), which is the read that finds a
33
- * transition the window hid — the park can reach its final phase inside that
34
- * window and never announce anything again. One look per action, and only
35
- * while the state it left is still moving;
36
- * - a burst is a fixed short list of waits, so at most that many reads answer
37
- * one transition, however long the page stays open;
38
- * - a read that finds the verdict settled ends the burst at once, and the last
39
- * reading stays on screen — "the park is settled" is not a state change, so
40
- * it must not be re-asked;
41
- * - a read that finds a phase still moving ends it just as fast. Every phase
42
- * transition is announced at its source, the flush that resolves `parking`
43
- * into `safe` included, and that announcement is the very event that starts
44
- * the next burst — so watching a moving phase from here spends records on a
45
- * transition that will speak for itself;
46
- * - what the burst still owes after a read is delivery: the read one
47
- * announcement bought, and one more when a change lands while a pass is in
48
- * flight, which that pass cannot have answered. The bound is a bound, not a
49
- * heartbeat: a phase that never settles is left on screen unread until
50
- * something else moves;
51
- * - the bound is a trade, taken knowingly: a reading left on screen when the
52
- * burst runs out is the host's own last answer, and the next trigger — a
53
- * forwarded change, a press, or the shell handing a control its session again
54
- * — reads the truth again. A reading nobody answered is never rendered as a
55
- * confident state. A longer watch is a longer list of waits, at a linear price
56
- * in the address session's log;
57
- * - while the page is hidden no read is armed at all, and the wait a burst
58
- * still owed is armed again when the page comes back: hidden time is neither
59
- * charged to the burst nor lost from it.
60
- *
61
- * A burst is not restarted by a trigger that lands while one is running: every
62
- * read it still owes happens after that trigger, so absorbing it loses nothing
63
- * and a storm of registry changes costs one burst rather than one each.
64
- * @module dsh-pause-session/client/burst
65
- */
66
- /** Whether the page is on screen, as the shell reports it. */
67
- export type PageVisibility = () => boolean;
68
- /**
69
- * One control's burst policy.
70
- *
71
- * `waitsMs` is the whole bound: a burst spends at most one read per wait, and
72
- * an empty list turns bursting off entirely — which is how a test or a
73
- * composition disables it without touching anything else.
74
- */
75
- export interface BurstPolicy {
76
- /** How long the burst collects triggers before its first wait starts. */
77
- readonly windowMs: number;
78
- /** Waits between the reads of one burst, in order; the first follows a trigger. */
79
- readonly waitsMs: readonly number[];
80
- /** Whether the page is on screen now. */
81
- readonly visible: PageVisibility;
82
- }
83
- /** The policy every control uses unless a caller says otherwise. */
84
- export declare const DEFAULT_BURST_POLICY: BurstPolicy;
85
- /**
86
- * How long after a client action a registry change is that action's own echo.
87
- *
88
- * A press, a release, a decision, a goal switch and an opened panel each read
89
- * the host as they run, so their answer is already on screen when the host
90
- * announces what they did — one `commands/change` per marker command, measured
91
- * 0.44 s to 2.04 s apart, a cadence the coalescing window cannot merge. Reading
92
- * those back is paying for an answer the user already has.
93
- *
94
- * The window is what makes `commands/change` mean "something moved that this
95
- * browser did not do". It is refreshed by every change it absorbs, so a storm
96
- * that keeps arriving stays absorbed rather than leaking one read per change,
97
- * and it ends for good `ACTION_ABSORB_MS` after the last change: a change after
98
- * that is news, and so is the next user action, which reads on its own.
99
- *
100
- * The margin over the measured 2.04 s gap is deliberate: a transition's own
101
- * storm has to fit inside the window for the whole storm to stay absorbed.
102
- */
103
- export declare const ACTION_ABSORB_MS = 2500;
104
- /**
105
- * The waits one burst uses when its first wait is `first`: four reads spread
106
- * over roughly ten times that. Long enough that a read a hidden page or an
107
- * in-flight pass deferred is still taken, short enough that a state which never
108
- * settles costs a handful of log records rather than a night of them.
109
- * @param first - milliseconds before the burst's first read, after its window; `<= 0` disables bursting.
110
- * @returns the waits, in order; empty when bursting is disabled.
111
- */
112
- export declare function burstWaits(first: number): readonly number[];
113
- /** Start and clear one timer, spelled so a test can drive the clock. */
114
- export interface BurstClock {
115
- /**
116
- * @param callback - the read to run.
117
- * @param ms - how long to wait.
118
- * @returns the scheduled timer.
119
- */
120
- set(callback: () => void, ms: number): ReturnType<typeof setTimeout>;
121
- /**
122
- * @param timer - the timer to clear.
123
- */
124
- clear(timer: ReturnType<typeof setTimeout>): void;
125
- }
126
- /**
127
- * One control's re-read burst. Every method is safe to call in any order and as
128
- * often as the caller likes: a trigger starts at most one burst, and one burst
129
- * never arms more than one read at a time.
130
- */
131
- export declare class ReadBurst {
132
- private readonly onRead;
133
- private readonly policy;
134
- private readonly clock;
135
- /** The armed wait, when one is counting down. */
136
- private timer;
137
- /** Whether the burst is waiting out its collection window rather than a wait between reads. */
138
- private window;
139
- /** Waits this burst has not spent yet, front first. */
140
- private left;
141
- /** Whether a burst is in progress: it may be collecting, waiting, reading, or reading again. */
142
- private running;
143
- /** The absorption window a client action opened, while one is open. */
144
- private absorbed;
145
- /** Whether a control asked for the one look owed when the echo window closes. */
146
- private recheck;
147
- /**
148
- * @param onRead - one more read. Called only for a read the burst owes and the page can show.
149
- * @param policy - the collection window, the waits, and the visibility source.
150
- * @param clock - the timer implementation.
151
- */
152
- constructor(onRead: () => void, policy: BurstPolicy, clock?: BurstClock);
153
- /** Whether this control bursts at all. */
154
- get enabled(): boolean;
155
- /** Whether one more read is already counting down. */
156
- get armed(): boolean;
157
- /** Whether a burst is in progress, with or without a read counting down. */
158
- get live(): boolean;
159
- /** Whether a change right now is a client action's own echo. */
160
- get absorbing(): boolean;
161
- /**
162
- * The user acted on this control and the action reads: a press, a release, a
163
- * decision, a goal switch, an opened panel. Its read is the answer, and the
164
- * registry changes it sets off announce themselves for a while afterwards, so
165
- * until the window closes a trigger is that echo rather than news.
166
- *
167
- * Called as the action is taken, before its read lands, because the echo can
168
- * arrive while the action is still running.
169
- */
170
- absorb(): void;
171
- /**
172
- * Ask for one read at the moment the echo window closes, because the action's
173
- * own read left a state that is still moving.
174
- *
175
- * The echo window swallows a transition announced inside it — the forwarded
176
- * event carries no payload, so nothing distinguishes a marker registration
177
- * from a phase change — and after the window no change is guaranteed to come:
178
- * the transition that was hidden may be the last one this park makes. The
179
- * look owed here is the read that finds it, one per action, and it is not a
180
- * watch: once the window has closed, every further change is news and buys its
181
- * own read. A control whose action left a settled state owes nothing and asks
182
- * for nothing.
183
- */
184
- recheckAfterEcho(): void;
185
- /**
186
- * Something moved — a registry change. The burst first waits out its
187
- * collection window, and every trigger that lands inside it restarts that
188
- * window: the two or three registrations of one park start one burst rather
189
- * than one each. A burst already past its window takes this as covered —
190
- * every read it still owes happens after the change.
191
- *
192
- * @returns whether the change is news rather than a client action's own echo.
193
- * The echo announces what the action's read already answered, so only a news
194
- * trigger is a change a control still owes a read for; a change that lands
195
- * while a burst is under way is covered by the read that burst already owes.
196
- * `false` also for a disabled burst, which reads nothing at all.
197
- */
198
- trigger(): boolean;
199
- /**
200
- * The control knows it still owes a read: a change that is news has arrived,
201
- * or a reading just revealed something only a read can answer. A call outside
202
- * any burst starts one. Absorption never applies here — this is not a change
203
- * some action caused, it is a read the control already knows it needs.
204
- */
205
- next(): void;
206
- /**
207
- * Nothing is moving: end the burst and forget it. The last reading stays
208
- * published, and the next trigger starts a fresh burst. Two things outlive
209
- * this: an action's absorption window, which is about what the action caused
210
- * rather than about the state, and the one look it may still owe — see
211
- * {@link recheckAfterEcho}.
212
- */
213
- stop(): void;
214
- /** Drop the armed wait. A burst that is still owed its read survives it; see {@link checkVisibility}. */
215
- cancel(): void;
216
- /**
217
- * Re-evaluate one page-visibility change. Going off screen drops the armed
218
- * wait — a hidden page is not being read — and coming back arms it again, so
219
- * the burst resumes where it stopped instead of being spent while nobody was
220
- * looking. A burst still collecting resumes its window, restarted from the
221
- * moment the page came back: a trigger that arrived while it was hidden still
222
- * postpones the read.
223
- */
224
- checkVisibility(): void;
225
- /** Spend the next wait of the burst, or end it when the bound is reached. */
226
- private owe;
227
- /**
228
- * (Re)start the collection window. While the page is hidden nothing is armed
229
- * — hidden time is not charged to the burst — and a trigger that lands then
230
- * is still collected, because coming back starts a whole window.
231
- */
232
- private armWindow;
233
- /** Put back whatever the burst still owed: its window, or the wait after a read. */
234
- private arm;
235
- /**
236
- * (Re)start the absorption window a client action opened. A hidden page keeps
237
- * absorbing: this window is about what the action caused, not about what the
238
- * page is doing. When the window closes, the one look an unsettled action owes
239
- * is taken; the window is restarted by every change it absorbs, so that look
240
- * waits for the echo to actually stop.
241
- */
242
- private armAbsorb;
243
- /** Close the absorption window: changes are news again. */
244
- private cancelAbsorb;
245
- /**
246
- * Arm one wait, spending it when it fires. The wait stays in `left` until
247
- * then, so a page hidden mid-wait takes the whole wait again: hidden time is
248
- * neither charged to the burst nor lost from it.
249
- */
250
- private armWait;
251
- /** Whether the page counts as on screen now, with an absent `document` read as on screen. */
252
- private visibleNow;
253
- }