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.
- package/CHANGELOG.md +51 -20
- package/DEVELOPMENT.md +25 -2
- package/README.md +41 -31
- package/README.zh.md +31 -21
- package/REHEARSAL.md +6 -6
- package/lib/client.js +1125 -1375
- package/lib/client.js.map +1 -1
- package/lib/index.js +1107 -1081
- package/lib/index.js.map +1 -1
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +339 -0
- package/lib/typert.remote-client.d.ts +38 -0
- package/lib/typert.remote-client.d.ts.map +1 -0
- package/lib/typert.remote-client.js +244 -0
- package/lib/types/client/control.d.ts +25 -158
- package/lib/types/client/global.d.ts +39 -239
- package/lib/types/client/index.d.ts +6 -5
- package/lib/types/client/locales.d.ts +3 -4
- package/lib/types/client/pill.d.ts +5 -5
- package/lib/types/client/remote.d.ts +77 -0
- package/lib/types/client/view.d.ts +19 -5
- package/lib/types/coldlog.d.ts +1 -1
- package/lib/types/composition.d.ts +35 -61
- package/lib/types/control.d.ts +41 -60
- package/lib/types/global.d.ts +6 -4
- package/lib/types/index.d.ts +2 -2
- package/lib/types/park-state.d.ts +51 -0
- package/lib/types/registry.d.ts +1 -1
- package/lib/types/release-order.d.ts +25 -0
- package/lib/types/remote-adapter.d.ts +22 -0
- package/lib/types/remote.d.ts +158 -0
- package/lib/types/resume-activation.d.ts +36 -0
- package/lib/types/resume-text.d.ts +67 -0
- package/lib/types/resume.d.ts +7 -104
- package/lib/types/shared.d.ts +20 -123
- package/lib/types/snapshot.d.ts +29 -0
- package/lib/types/triage.d.ts +8 -9
- package/package.json +38 -19
- package/lib/types/client/burst.d.ts +0 -253
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side bridge from the generated `pauseSession` Remote namespace to the
|
|
3
|
+
* control state machines.
|
|
4
|
+
*
|
|
5
|
+
* The native namespace is the only transport: the stream supplies snapshots and
|
|
6
|
+
* the action methods drive every button. No slash command is involved.
|
|
7
|
+
* @module dsh-pause-session/client/remote
|
|
8
|
+
*/
|
|
9
|
+
import type { PauseActionResultWire, PauseDecisionRequest, PauseSnapshotWire, TriageRowWire } from '../remote.js';
|
|
10
|
+
import type { GlobalStatus, ParkStatus } from '../shared.js';
|
|
11
|
+
/** The generated namespace shape this bridge needs. */
|
|
12
|
+
export interface RemotePauseNamespace {
|
|
13
|
+
observe(signal?: AbortSignal): AsyncIterable<PauseSnapshotWire>;
|
|
14
|
+
act(request: {
|
|
15
|
+
readonly action: 'pause' | 'resume' | 'pause-all' | 'resume-all' | 'cancel-all' | 'goal';
|
|
16
|
+
readonly sessionId: string;
|
|
17
|
+
}, signal?: AbortSignal): Promise<{
|
|
18
|
+
readonly ok: boolean;
|
|
19
|
+
readonly value?: PauseActionResultWire;
|
|
20
|
+
readonly error?: {
|
|
21
|
+
readonly code: string;
|
|
22
|
+
};
|
|
23
|
+
}>;
|
|
24
|
+
triage(request: {
|
|
25
|
+
readonly caller: string;
|
|
26
|
+
}, signal?: AbortSignal): Promise<{
|
|
27
|
+
readonly ok: boolean;
|
|
28
|
+
readonly value?: readonly TriageRowWire[];
|
|
29
|
+
}>;
|
|
30
|
+
decide(request: PauseDecisionRequest, signal?: AbortSignal): Promise<{
|
|
31
|
+
readonly ok: boolean;
|
|
32
|
+
readonly value?: PauseActionResultWire;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
/** The status shape shared by the controls, with branded ids erased at this boundary. */
|
|
36
|
+
export interface PauseSnapshotLike {
|
|
37
|
+
readonly global: GlobalStatus;
|
|
38
|
+
readonly sessions: Readonly<Record<string, ParkStatus>>;
|
|
39
|
+
}
|
|
40
|
+
/** A remote-backed status source with a change subscription. */
|
|
41
|
+
export interface RemoteStatusSource {
|
|
42
|
+
/** Read the latest host snapshot; undefined before the first answer or after failure. */
|
|
43
|
+
read(signal?: AbortSignal): Promise<PauseSnapshotLike | undefined>;
|
|
44
|
+
onChange(listener: () => void): () => void;
|
|
45
|
+
/** Receive each pushed snapshot directly; `undefined` means the stream failed. */
|
|
46
|
+
onSnapshot(listener: (snapshot: PauseSnapshotLike | undefined) => void): () => void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Open the native status stream and expose its latest snapshot to both controls.
|
|
50
|
+
*
|
|
51
|
+
* The stream owns no retry of its own: a carrier can drop it on a mux reconnect,
|
|
52
|
+
* a host reload, or a network blip, and the controls must not stay unavailable
|
|
53
|
+
* until the page is refreshed. This source reconnects with a bounded backoff and
|
|
54
|
+
* republishes the new snapshot; while it is disconnected the last value is
|
|
55
|
+
* discarded, so readers see "unavailable" rather than a stale claim.
|
|
56
|
+
* @param remote - the generated Remote namespace.
|
|
57
|
+
* @param lifetime - client lifetime; aborting it releases the stream.
|
|
58
|
+
* @returns the cached source and its change subscription.
|
|
59
|
+
*/
|
|
60
|
+
export declare function openRemoteSource(remote: RemotePauseNamespace, lifetime: AbortSignal): RemoteStatusSource;
|
|
61
|
+
/** Direct browser transport over the generated namespace. */
|
|
62
|
+
export interface PauseTransport {
|
|
63
|
+
readonly source: RemoteStatusSource;
|
|
64
|
+
act(request: {
|
|
65
|
+
readonly action: 'pause' | 'resume' | 'pause-all' | 'resume-all' | 'cancel-all' | 'goal';
|
|
66
|
+
readonly sessionId: string;
|
|
67
|
+
}, signal?: AbortSignal): Promise<PauseActionResultWire>;
|
|
68
|
+
triage(caller: string, signal?: AbortSignal): Promise<readonly TriageRowWire[] | undefined>;
|
|
69
|
+
decide(caller: string, target: string, decision: 'continue' | 'note' | 'review' | 'leave' | 'goal', signal?: AbortSignal): Promise<PauseActionResultWire>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build the direct transport over one mounted Remote namespace.
|
|
73
|
+
* @param remote - the generated `pauseSession` namespace.
|
|
74
|
+
* @param lifetime - client lifetime.
|
|
75
|
+
* @returns the source and action methods the controls use.
|
|
76
|
+
*/
|
|
77
|
+
export declare function remoteTransport(remote: RemotePauseNamespace, lifetime: AbortSignal): PauseTransport;
|
|
@@ -51,7 +51,7 @@ export declare function showsPauseControl(subagent: {
|
|
|
51
51
|
* @param reading - the host-reported reading, or `undefined` before the first answer.
|
|
52
52
|
* @returns the view for that reading.
|
|
53
53
|
*/
|
|
54
|
-
export declare function pauseView(reading: PauseReading | undefined): PauseView;
|
|
54
|
+
export declare function pauseView(reading: PauseReading | undefined, running?: boolean): PauseView;
|
|
55
55
|
/** One named fact from the whole-app report, with the copy key that introduces it. */
|
|
56
56
|
export interface GlobalCaveat {
|
|
57
57
|
/** What this list of names is. */
|
|
@@ -60,7 +60,7 @@ export interface GlobalCaveat {
|
|
|
60
60
|
readonly names: readonly string[];
|
|
61
61
|
}
|
|
62
62
|
/** Which verb a whole-app press runs. */
|
|
63
|
-
export type GlobalAction = 'pause-all' | 'resume-all' | 'none';
|
|
63
|
+
export type GlobalAction = 'pause-all' | 'resume-all' | 'cancel-all' | 'none';
|
|
64
64
|
/** One decision as the list renders it. */
|
|
65
65
|
export interface TriageActionView {
|
|
66
66
|
/** The decision this button takes. */
|
|
@@ -72,6 +72,14 @@ export interface TriageActionView {
|
|
|
72
72
|
}
|
|
73
73
|
/** The four decisions, in the order a user reads them: least to most cautious. */
|
|
74
74
|
export declare const TRIAGE_ACTION_VIEWS: readonly TriageActionView[];
|
|
75
|
+
/**
|
|
76
|
+
* The decisions one row may offer. Continue and note both deliver the owed
|
|
77
|
+
* request, so they are absent when the log is unreadable or the host says it
|
|
78
|
+
* cannot wake the entry; the remaining decisions still give the user a way out.
|
|
79
|
+
* @param row - the rendered row.
|
|
80
|
+
* @returns the actions that can be taken on it.
|
|
81
|
+
*/
|
|
82
|
+
export declare function triageActionsFor(row: TriageRowView): readonly TriageActionView[];
|
|
75
83
|
/**
|
|
76
84
|
* The fifth listed verb, rendered apart from the four decisions: it is a
|
|
77
85
|
* switch on what the NEXT resume may spend, not a way to decide this row.
|
|
@@ -96,9 +104,11 @@ export interface TriageRowView {
|
|
|
96
104
|
}
|
|
97
105
|
/** What the decision-list panel renders. */
|
|
98
106
|
export interface TriageView {
|
|
99
|
-
/** Whether the
|
|
107
|
+
/** Whether the host answered the list read at all. */
|
|
100
108
|
readonly visible: boolean;
|
|
101
|
-
/** Whether
|
|
109
|
+
/** Whether the last list read failed rather than returning rows. */
|
|
110
|
+
readonly unavailable: boolean;
|
|
111
|
+
/** The rows the host reported. */
|
|
102
112
|
readonly rows: readonly TriageRowView[];
|
|
103
113
|
}
|
|
104
114
|
/**
|
|
@@ -128,10 +138,14 @@ export interface GlobalPauseView {
|
|
|
128
138
|
readonly unclean: number;
|
|
129
139
|
/** Named facts the user must not miss; rendered on the tooltip. */
|
|
130
140
|
readonly caveats: readonly GlobalCaveat[];
|
|
141
|
+
/** Draining sessions and what each is running, when the host still has work. */
|
|
142
|
+
readonly running?: string;
|
|
131
143
|
}
|
|
132
144
|
/**
|
|
133
145
|
* Map one whole-app reading to its view.
|
|
134
146
|
* @param reading - the host-reported reading, or `undefined` before the first answer.
|
|
135
147
|
* @returns the view for that reading.
|
|
136
148
|
*/
|
|
137
|
-
export declare function globalPauseView(reading: GlobalReading | undefined
|
|
149
|
+
export declare function globalPauseView(reading: GlobalReading | undefined, sessions?: Readonly<Record<string, {
|
|
150
|
+
readonly runningTools: readonly string[];
|
|
151
|
+
}>>): GlobalPauseView;
|
package/lib/types/coldlog.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* list only has to name where the session stopped, which tool call the log
|
|
8
8
|
* never heard back from, and whether an answer is still owed — all of them
|
|
9
9
|
* facts about the log's END. Reading from seq 0 to the end for every row, on
|
|
10
|
-
* every
|
|
10
|
+
* every time the panel opens, would make a long
|
|
11
11
|
* session's cost grow with a log nobody is asking about.
|
|
12
12
|
*
|
|
13
13
|
* WHAT THE BOUND BUYS. A stored log is append-only, so the tail is stable: the
|
|
@@ -1,58 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* What a parked Session needs to come back as itself.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* scoped tools, prompt sections, skills,
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
4
|
+
* A root Session is loaded directly: the Agent preset it ran supplies the
|
|
5
|
+
* scoped tools, prompt sections, and skills, so the resume mounts that preset
|
|
6
|
+
* before the Agent is published. A subagent child is not loaded here at all —
|
|
7
|
+
* the subagent runtime owns its activation, composition, and tree membership,
|
|
8
|
+
* and cold-resumes it when this plugin delivers the wake through
|
|
9
|
+
* `ctx.subagents.prompt`.
|
|
9
10
|
*
|
|
10
|
-
* The
|
|
11
|
-
*
|
|
12
|
-
* its first turn, and the projection records the selection that actually ran.
|
|
13
|
-
* A child with no independent selection joins its live parent's preset
|
|
14
|
-
* generation; one that selected independently mounts the projected preset.
|
|
15
|
-
* The durable child descriptor supplies the persona, tool filter, and route
|
|
16
|
-
* that are reapplied only on cold resume.
|
|
17
|
-
*
|
|
18
|
-
* Both services are optional. A profile without `agentPresets` has no scoped
|
|
19
|
-
* preset composition to restore, and this module leaves the resume unchanged.
|
|
20
|
-
* A profile that mounts one but cannot read the Session projection cannot
|
|
21
|
-
* prove what composition the Session ran, so it fails the resume instead of
|
|
22
|
-
* silently starting a different Agent.
|
|
11
|
+
* The two cases are told apart by one read of the Session's own log, which is
|
|
12
|
+
* the same read the press-time resumability check already pays for.
|
|
23
13
|
* @module dsh-pause-session/composition
|
|
24
14
|
*/
|
|
25
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
26
|
-
import type {
|
|
27
|
-
import type {
|
|
28
|
-
/**
|
|
29
|
-
export
|
|
30
|
-
/**
|
|
31
|
-
readonly
|
|
16
|
+
import type { AgentSetup } from '@deepseek-ai/dsh-agent';
|
|
17
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
18
|
+
/** How one parked Session comes back. */
|
|
19
|
+
export type ParkedComposition = {
|
|
20
|
+
/** An ordinary Session the plugin loads itself. */
|
|
21
|
+
readonly kind: 'root';
|
|
32
22
|
/** The scoped Agent world to mount before the Agent is published. */
|
|
33
23
|
readonly setup?: AgentSetup;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export declare class UnresumableSessionError extends Error {
|
|
43
|
-
constructor(sessionId: SessionId);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Whether the child's own log selected a preset before its descriptor.
|
|
47
|
-
*
|
|
48
|
-
* The creation-time selection precedes the first `subagent/descriptor`; a
|
|
49
|
-
* selection after the descriptor belongs to the child's own later history and
|
|
50
|
-
* does not decide which composition cold resume joins. This mirrors the
|
|
51
|
-
* subagent runtime's own cold-resume rule.
|
|
52
|
-
* @param events - the child's own log suffix, without the fork-inherited prefix.
|
|
53
|
-
* @returns whether the child carries an independent preset selection.
|
|
54
|
-
*/
|
|
55
|
-
export declare function hasIndependentPresetSelection(events: readonly SessionEvent[]): boolean;
|
|
24
|
+
} | {
|
|
25
|
+
/**
|
|
26
|
+
* A subagent child. The subagent runtime restores its composition from the
|
|
27
|
+
* persisted descriptor and preset projection, so the plugin only delivers
|
|
28
|
+
* the wake.
|
|
29
|
+
*/
|
|
30
|
+
readonly kind: 'child';
|
|
31
|
+
};
|
|
56
32
|
/**
|
|
57
33
|
* Whether a local Session may be parked under a promise of a later resume.
|
|
58
34
|
*
|
|
@@ -73,22 +49,20 @@ export declare function hasIndependentPresetSelection(events: readonly SessionEv
|
|
|
73
49
|
*/
|
|
74
50
|
export declare function isResumableSession(ctx: Context, sessionId: SessionId, signal?: AbortSignal): Promise<boolean>;
|
|
75
51
|
/**
|
|
76
|
-
* Read one parked Session's
|
|
52
|
+
* Read one parked Session's own log and decide how it comes back.
|
|
53
|
+
*
|
|
54
|
+
* A child returns as `child` with no setup: the subagent runtime restores the
|
|
55
|
+
* composition from the descriptor and the preset projection, and mounting a
|
|
56
|
+
* second one here would start a different Agent on the same history.
|
|
77
57
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* child composition: an independent preset mounts separately, while an
|
|
83
|
-
* inherited composition joins the parent's current generation; the historical
|
|
84
|
-
* descriptor's persona and tool filter are reapplied in both cases.
|
|
58
|
+
* A root resolves the preset it ran — the projection, never the creation-time
|
|
59
|
+
* header alone, because a blank Session may select another preset before its
|
|
60
|
+
* first turn — and hands back the setup that mounts it. A profile with no
|
|
61
|
+
* preset roster has no scoped composition to restore and resumes unchanged.
|
|
85
62
|
* @param ctx - host context carrying the optional preset and Session-query services.
|
|
86
63
|
* @param sessionId - the parked Session being resumed.
|
|
87
|
-
* @param parent - the live direct parent, when a child is resumed under it.
|
|
88
64
|
* @param signal - cancellation for the observation read.
|
|
89
|
-
* @returns
|
|
90
|
-
* @throws
|
|
91
|
-
* @throws when a mounted preset roster cannot read the Session projection, or
|
|
92
|
-
* a recorded preset cannot be restored.
|
|
65
|
+
* @returns how this Session comes back.
|
|
66
|
+
* @throws when a mounted preset roster cannot read the Session projection.
|
|
93
67
|
*/
|
|
94
|
-
export declare function
|
|
68
|
+
export declare function readParkedComposition(ctx: Context, sessionId: SessionId, signal?: AbortSignal): Promise<ParkedComposition>;
|
package/lib/types/control.d.ts
CHANGED
|
@@ -2,55 +2,11 @@
|
|
|
2
2
|
* The session control the browser half drives: the pause verb, the park state
|
|
3
3
|
* machine behind it, and the live state channel that accompanies both.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* publish through is the command registry itself — `commands/change` is a
|
|
11
|
-
* forwarded event, and `commands.list(sessionId)` is a Client-callable read.
|
|
12
|
-
*
|
|
13
|
-
* That same registry is how a phase change leaves this process: a transition
|
|
14
|
-
* re-registers the status verb, which emits the change a client needs to read
|
|
15
|
-
* the new phase. It is the only way a third-party plugin can announce anything,
|
|
16
|
-
* and it is what makes the flush that resolves `parking` into `safe` observable
|
|
17
|
-
* at all — that step changes no registration of its own.
|
|
18
|
-
*
|
|
19
|
-
* So the resume definition IS the paused marker: it is registered on a paused
|
|
20
|
-
* session's own agent context and removed on resume, and the browser half reads
|
|
21
|
-
* "paused" from that registration rather than inventing it. A descriptor cannot
|
|
22
|
-
* change after registration, so the park state rides a companion status
|
|
23
|
-
* definition (registered beside the marker) whose answer is derived per call
|
|
24
|
-
* rather than frozen into a descriptor — see `./shared.ts`. That one read
|
|
25
|
-
* carries the record's goal opt-out too, so the session-header control can show
|
|
26
|
-
* and switch it while the session is parked and clean — the state in which a
|
|
27
|
-
* silent morning resume would otherwise spend the user's rounds unasked. The
|
|
28
|
-
* switch is addressed to the session it changes, so it needs no listed row.
|
|
29
|
-
*
|
|
30
|
-
* The header control is the user's button for ONE session's work, and that work
|
|
31
|
-
* includes the subagents under it: the press registers the session and its
|
|
32
|
-
* descendant tree, the release takes the same tree back, and the status the
|
|
33
|
-
* header shows is the worst phase among the members the press registered. The
|
|
34
|
-
* whole-app control covers every running tree and answers for all of them; this
|
|
35
|
-
* one answers for the tree it owns.
|
|
36
|
-
*
|
|
37
|
-
* A mark is not a park. A session is parked only once nothing is left to drain:
|
|
38
|
-
* no call in flight AND either its next model request is held at the gate or no
|
|
39
|
-
* turn is open at all (there will be no next request to hold). Only then is the
|
|
40
|
-
* session flushed, at the session level — `SessionHandle.flush()` does not
|
|
41
|
-
* drain the routed live buffer, so it would record a park that a crash erases.
|
|
42
|
-
* The "safe to close" phase appears only after that flush resolves.
|
|
43
|
-
*
|
|
44
|
-
* Every phase a pause reaches is written to the durable registry before it is
|
|
45
|
-
* published — including the move into `parking`, which the status command may
|
|
46
|
-
* therefore never serve ahead of the record. A restart re-holds exactly the
|
|
47
|
-
* sessions the last process left parked, and a phase that could not be recorded
|
|
48
|
-
* is never shown as cleaner than the record says.
|
|
49
|
-
*
|
|
50
|
-
* A park verb changes state, so it acts only on a person's command: a command
|
|
51
|
-
* dispatched from inside a model turn is refused. The listed verbs add the
|
|
52
|
-
* one binding their target needs — the row must be one the calling session was
|
|
53
|
-
* shown — and that rule lives with the list (`./triage.ts`).
|
|
5
|
+
* The host exposes this control through the native Typert Remote namespace:
|
|
6
|
+
* `observe` streams snapshots and `act` / `triage` / `decide` drive the UI. No
|
|
7
|
+
* slash command is registered. Phase changes wake the Remote stream through a
|
|
8
|
+
* local listener set; the durable registry remains the source of truth for what
|
|
9
|
+
* the snapshot reports.
|
|
54
10
|
* @module dsh-pause-session/control
|
|
55
11
|
*/
|
|
56
12
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -58,14 +14,10 @@ import { SessionId } from '@deepseek-ai/dsh-session';
|
|
|
58
14
|
import type { PauseGate } from './gate.js';
|
|
59
15
|
import type { ParkWatch } from './park.js';
|
|
60
16
|
import type { ParkRegistry } from './registry.js';
|
|
61
|
-
import type { ResumeAttempt, ResumeOptions } from './resume.js';
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* own reading of the one signal that separates a person's command from a
|
|
66
|
-
* model's.
|
|
67
|
-
*/
|
|
68
|
-
export declare const UNATTENDED_TEXT: string;
|
|
17
|
+
import type { ReleaseTreeOutcome, ResumeAttempt, ResumeOptions } from './resume.js';
|
|
18
|
+
import type { TriageAction } from './shared.js';
|
|
19
|
+
import type { PauseSnapshot, TriageList } from './shared.js';
|
|
20
|
+
import type { PauseAnswer } from './shared.js';
|
|
69
21
|
/** What one tree-wide press registered, and what it could not. */
|
|
70
22
|
export interface PressTreeOutcome {
|
|
71
23
|
/** How many sessions this press covered: the session plus its descendants. */
|
|
@@ -76,8 +28,12 @@ export interface PressTreeOutcome {
|
|
|
76
28
|
readonly rootRegistered: boolean;
|
|
77
29
|
/** The members whose record could not be written; they are not paused. */
|
|
78
30
|
readonly unrecorded: readonly SessionId[];
|
|
79
|
-
/** Whether
|
|
31
|
+
/** Whether any branch was unread, so the subagent tree was not fully covered. */
|
|
80
32
|
readonly unread: boolean;
|
|
33
|
+
/** Tree members on a one-shot or unreadable route; they were not registered. */
|
|
34
|
+
readonly unresumable: readonly SessionId[];
|
|
35
|
+
/** Branch ids the tree walk could not read. */
|
|
36
|
+
readonly unreadBranches: readonly SessionId[];
|
|
81
37
|
}
|
|
82
38
|
/** What mounting the session control hands back to the plugin entry. */
|
|
83
39
|
export interface SessionControl {
|
|
@@ -87,8 +43,33 @@ export interface SessionControl {
|
|
|
87
43
|
* after mount.
|
|
88
44
|
*/
|
|
89
45
|
adopt(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Read the whole app and every live paused session as one JSON-safe snapshot.
|
|
48
|
+
* The native snapshot serves this; it never enters a Session log.
|
|
49
|
+
* @param signal - cancellation for the tree reads this snapshot performs.
|
|
50
|
+
* @returns the whole-app verdict and the paused sessions this process holds.
|
|
51
|
+
*/
|
|
52
|
+
readSnapshot(signal?: AbortSignal): Promise<PauseSnapshot>;
|
|
53
|
+
/** Wake a listener after any visible status change. */
|
|
54
|
+
subscribe(listener: () => void): () => void;
|
|
55
|
+
/** Press one Session tree. */
|
|
56
|
+
pressTree(sessionId: SessionId, signal?: AbortSignal): Promise<PressTreeOutcome>;
|
|
57
|
+
/** Release one Session's registered tree. */
|
|
58
|
+
releaseTree(sessionId: SessionId, signal?: AbortSignal): Promise<ReleaseTreeOutcome>;
|
|
59
|
+
/** Press every running Session tree. */
|
|
60
|
+
pauseAll(signal?: AbortSignal): Promise<PauseAnswer>;
|
|
61
|
+
/** Resume every cleanly paused Session. */
|
|
62
|
+
resumeAll(signal?: AbortSignal): Promise<PauseAnswer>;
|
|
63
|
+
/** Release every registered tree, cancelling a press that is still draining. */
|
|
64
|
+
cancelAll(signal?: AbortSignal): Promise<PauseAnswer>;
|
|
65
|
+
/** Flip one Session's goal re-arm opt-out. */
|
|
66
|
+
toggleGoal(sessionId: SessionId, signal?: AbortSignal): Promise<PauseAnswer>;
|
|
67
|
+
/** Read the morning list for one calling Session. */
|
|
68
|
+
readTriage(caller: SessionId, signal?: AbortSignal): Promise<TriageList>;
|
|
69
|
+
/** Take one listed decision for one calling Session. */
|
|
70
|
+
decideTriage(caller: SessionId, target: SessionId, action: TriageAction, signal?: AbortSignal): Promise<PauseAnswer>;
|
|
90
71
|
}
|
|
91
|
-
/** How resuming all parked sessions answers the
|
|
72
|
+
/** How resuming all parked sessions answers the caller. */
|
|
92
73
|
export declare function summarizeResume(attempts: readonly ResumeAttempt[]): string;
|
|
93
74
|
/**
|
|
94
75
|
* Everything the session control needs from the composition: the clean resume
|
|
@@ -103,7 +84,7 @@ export interface SessionControlOptions extends ResumeOptions {
|
|
|
103
84
|
* Contribute the session control: the pause verb, the per-session status read,
|
|
104
85
|
* the global resume verb, and — for every paused session that has a live agent
|
|
105
86
|
* — the resume marker.
|
|
106
|
-
* @param ctx - host context carrying the
|
|
87
|
+
* @param ctx - host context carrying the agent store, the
|
|
107
88
|
* session store, and the projection registry.
|
|
108
89
|
* @param gate - the pause gate this control drives and reflects.
|
|
109
90
|
* @param watch - the per-session activity fold the park predicate reads.
|
package/lib/types/global.d.ts
CHANGED
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
* @module dsh-pause-session/global
|
|
24
24
|
*/
|
|
25
25
|
import type { Context } from '@deepseek-ai/cordis';
|
|
26
|
-
import type { CommandResult } from '@deepseek-ai/dsh-commands';
|
|
27
26
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
28
27
|
import type { ParkWatch } from './park.js';
|
|
29
28
|
import type { ParkRegistry } from './registry.js';
|
|
30
|
-
import type { ParkPhase } from './shared.js';
|
|
29
|
+
import type { GlobalStatus, ParkPhase } from './shared.js';
|
|
30
|
+
import type { TreeWalk } from './tree.js';
|
|
31
|
+
import type { PauseAnswer } from './shared.js';
|
|
31
32
|
/** One session's live park state, as the per-session control sees it. */
|
|
32
33
|
export interface LivePark {
|
|
33
34
|
readonly phase: ParkPhase;
|
|
@@ -74,12 +75,13 @@ export interface GlobalPause {
|
|
|
74
75
|
* @param signal - cancellation for the tree reads this action performs.
|
|
75
76
|
* @returns the aggregate as of this action's end.
|
|
76
77
|
*/
|
|
77
|
-
pressAll(signal?: AbortSignal): Promise<
|
|
78
|
+
pressAll(signal?: AbortSignal): Promise<PauseAnswer>;
|
|
78
79
|
/**
|
|
80
|
+
* The aggregate as structured data for the native Remote stream.
|
|
79
81
|
* @param signal - cancellation for the tree reads this read performs.
|
|
80
82
|
* @returns the aggregate as of this read.
|
|
81
83
|
*/
|
|
82
|
-
|
|
84
|
+
readState(signal?: AbortSignal, walks?: ReadonlyMap<SessionId, TreeWalk>): Promise<GlobalStatus>;
|
|
83
85
|
/**
|
|
84
86
|
* Report one session a press could not register, so the aggregate names it.
|
|
85
87
|
* The whole-app action records that fact for its own targets; a press of a
|
package/lib/types/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { z } from 'zod';
|
|
16
16
|
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
+
export { PauseRemote } from './remote.js';
|
|
17
18
|
/** Plugin name; the Loader row and the bundle patch both use it. */
|
|
18
19
|
export declare const name = "dsh-pause-session";
|
|
19
20
|
/**
|
|
@@ -50,7 +51,7 @@ export type Config = z.infer<typeof ConfigShape>;
|
|
|
50
51
|
export declare const Config: z.ZodPreprocess<z.ZodObject<{
|
|
51
52
|
silentWakeRoutes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
52
53
|
maxListedLogEvents: z.ZodDefault<z.ZodNumber>;
|
|
53
|
-
}, z.core.$strip
|
|
54
|
+
}, z.core.$strip>, unknown>;
|
|
54
55
|
/**
|
|
55
56
|
* Mount the durable pause registry, the pause gate, the activity fold, their
|
|
56
57
|
* session control, and the clean resume path.
|
|
@@ -61,4 +62,3 @@ export declare const Config: z.ZodPreprocess<z.ZodObject<{
|
|
|
61
62
|
* @param config - validated route knowledge for the resume path's wake content.
|
|
62
63
|
*/
|
|
63
64
|
export declare function apply(ctx: Context, config?: Config): Promise<void>;
|
|
64
|
-
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One session's park episode: the durable phase writes, the activity walk that
|
|
3
|
+
* settles them, and the press/release lifecycle. The control composes this with
|
|
4
|
+
* the aggregate, the resume path, and the decision list.
|
|
5
|
+
* @module dsh-pause-session/park-state
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
9
|
+
import type { PauseGate } from './gate.js';
|
|
10
|
+
import type { LivePark, PressResult } from './global.js';
|
|
11
|
+
import type { ParkWatch } from './park.js';
|
|
12
|
+
import type { ParkRegistry } from './registry.js';
|
|
13
|
+
import type { ParkPhase, PauseAnswer } from './shared.js';
|
|
14
|
+
/** One session's park in progress. */
|
|
15
|
+
export interface ParkMark {
|
|
16
|
+
/** The phase the durable registry confirms for this session. */
|
|
17
|
+
phase: ParkPhase;
|
|
18
|
+
/** Generation of the attempt that owns this mark. */
|
|
19
|
+
generation: number;
|
|
20
|
+
/** Whether this mark is a live press or a record adopted at mount. */
|
|
21
|
+
origin: 'press' | 'registry';
|
|
22
|
+
/** Whether a settle attempt is already walking this mark. */
|
|
23
|
+
advancing: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** The park lifecycle the session control composes. */
|
|
26
|
+
export interface ParkState {
|
|
27
|
+
readonly marks: Map<SessionId, ParkMark>;
|
|
28
|
+
readonly unrecorded: Set<SessionId>;
|
|
29
|
+
readonly unheld: Set<SessionId>;
|
|
30
|
+
subscribe(listener: () => void): () => void;
|
|
31
|
+
notify(): void;
|
|
32
|
+
press(sessionId: SessionId, signal?: AbortSignal, knownResumable?: boolean): Promise<PressResult>;
|
|
33
|
+
/** Adopt one durable record as a mark that never advances in this process. */
|
|
34
|
+
adopt(sessionId: SessionId, phase: ParkPhase): void;
|
|
35
|
+
look(sessionId: SessionId): void;
|
|
36
|
+
drop(sessionId: SessionId): void;
|
|
37
|
+
sync(sessionId: SessionId): void;
|
|
38
|
+
release(sessionId: SessionId, text: string): Promise<PauseAnswer>;
|
|
39
|
+
markUnrecorded(sessionId: SessionId, unrecorded: boolean): void;
|
|
40
|
+
live(): Map<SessionId, LivePark>;
|
|
41
|
+
cleanup(): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Mount the park episode state machine over the gate, registry, and activity fold.
|
|
45
|
+
* @param ctx - host context carrying the session store and logger.
|
|
46
|
+
* @param gate - the pause gate this state drives.
|
|
47
|
+
* @param registry - the durable pause list every phase write goes to.
|
|
48
|
+
* @param watch - the activity fold the park predicate reads.
|
|
49
|
+
* @returns the park lifecycle the control composes.
|
|
50
|
+
*/
|
|
51
|
+
export declare function installParkState(ctx: Context, gate: PauseGate, registry: ParkRegistry, watch: ParkWatch): ParkState;
|
package/lib/types/registry.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ export interface ParkRegistry {
|
|
|
72
72
|
* The same list as {@link list}, as a lazily kept read-only view.
|
|
73
73
|
*
|
|
74
74
|
* The whole-app aggregate and the decision list read this once per status
|
|
75
|
-
* read, and a status read can happen on every
|
|
75
|
+
* read, and a status read can happen on every native-stream notification; the
|
|
76
76
|
* view is rebuilt only when the stored entries actually changed, so a read
|
|
77
77
|
* that changes nothing does not build a second copy of the list to look at.
|
|
78
78
|
* A caller must not mutate it.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The order a batch release walks: parent before child, shallowest first.
|
|
3
|
+
*
|
|
4
|
+
* A single tree release already has the pre-order from its own walk, so this
|
|
5
|
+
* module serves the batch path, where the recorded sessions may span several
|
|
6
|
+
* trees. `listDescendants` stamps each row with its distance from the listing
|
|
7
|
+
* root, so one catalog read per recorded root places every member. A session
|
|
8
|
+
* the walk never reached is its own root and releases with the first level.
|
|
9
|
+
* @module dsh-pause-session/release-order
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
13
|
+
/** How a batch release is sequenced. */
|
|
14
|
+
export interface ReleaseOrder {
|
|
15
|
+
/** One list per depth, shallowest first. */
|
|
16
|
+
readonly levels: readonly (readonly SessionId[])[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sequence a batch release parent before child.
|
|
20
|
+
* @param ctx - host context carrying the optional subagent catalog.
|
|
21
|
+
* @param sessionIds - every recorded session, in registry order.
|
|
22
|
+
* @param signal - cancellation for each catalog read.
|
|
23
|
+
* @returns one list per depth level, shallowest first.
|
|
24
|
+
*/
|
|
25
|
+
export declare function releaseOrder(ctx: Context, sessionIds: readonly SessionId[], signal?: AbortSignal): Promise<ReleaseOrder>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire adapter between the pause engine and the generated Remote namespace.
|
|
3
|
+
*
|
|
4
|
+
* The engine speaks in branded Session ids and user-facing answer copy; the Remote
|
|
5
|
+
* contract is plain JSON. Mapping lives here so neither side depends on the
|
|
6
|
+
* other's types.
|
|
7
|
+
* @module dsh-pause-session/remote-adapter
|
|
8
|
+
*/
|
|
9
|
+
import type { SessionControl } from './control.js';
|
|
10
|
+
import type { PauseRemoteApi, PauseSnapshotWire } from './remote.js';
|
|
11
|
+
import type { GlobalStatus, ParkStatus } from './shared.js';
|
|
12
|
+
/** Map the engine's snapshot to the wire shape. */
|
|
13
|
+
export declare function snapshotOf(snapshot: {
|
|
14
|
+
global: GlobalStatus;
|
|
15
|
+
sessions: Readonly<Record<string, ParkStatus>>;
|
|
16
|
+
}): PauseSnapshotWire;
|
|
17
|
+
/**
|
|
18
|
+
* Build the Remote callbacks over the mounted pause control.
|
|
19
|
+
* @param control - the engine's host control surface.
|
|
20
|
+
* @returns the wire API.
|
|
21
|
+
*/
|
|
22
|
+
export declare function remoteApiOf(control: SessionControl): PauseRemoteApi;
|