dsh-pause-session 0.2.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.
@@ -28,6 +28,7 @@ export declare const zh: {
28
28
  'all.unclean.aria': string;
29
29
  'all.safe': string;
30
30
  'all.checking.aria': string;
31
+ 'all.unavailable.aria': string;
31
32
  'all.caveat.jobs': string;
32
33
  'all.caveat.unresumable': string;
33
34
  'all.caveat.stalled': string;
@@ -47,6 +47,12 @@ export interface RemoteStatusSource {
47
47
  }
48
48
  /**
49
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.
50
56
  * @param remote - the generated Remote namespace.
51
57
  * @param lifetime - client lifetime; aborting it releases the stream.
52
58
  * @returns the cached source and its change subscription.
@@ -1,58 +1,34 @@
1
1
  /**
2
- * Rebuild the scoped Agent composition a parked Session ran under.
2
+ * What a parked Session needs to come back as itself.
3
3
  *
4
- * Activation is not only a persisted Session: the Agent preset supplies the
5
- * scoped tools, prompt sections, skills, and — for a subagent child — the
6
- * delegation context, persona, and tool restriction that were in force before
7
- * the park. A resume that loads the log without that setup starts a different
8
- * Agent on the same history.
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 preset a Session runs is the `agentPreset` projection, never the
11
- * creation-time header alone: a blank Session may select another preset before
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 { Agent, AgentOptions, AgentSetup } from '@deepseek-ai/dsh-agent';
27
- import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session';
28
- /** The setup and route options a resumed Agent receives from its stored composition. */
29
- export interface RestoredComposition {
30
- /** The durable child route, when the descriptor recorded one. */
31
- readonly agentOptions?: AgentOptions;
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
- * A stored subagent route that no resume may reactivate.
37
- *
38
- * A one-shot child is terminal by contract; activating it as a root Agent
39
- * would turn a finished delegation into an ordinary conversation that happens
40
- * to share its Session id.
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 stored composition and build the resume options.
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
- * The returned `setup` belongs to the unpublished Agent scope, so all
79
- * registrations exist before `agent/created`. For a root Session the setup
80
- * mounts the Session's projected preset, or the profile's default when the
81
- * Session never selected one. For a child under a live parent it applies the
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 the Agent options and setup to pass to `agents.resume`.
90
- * @throws {UnresumableSessionError} when the stored child route is one-shot.
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 restoreComposition(ctx: Context, sessionId: SessionId, parent: Agent | undefined, signal?: AbortSignal): Promise<RestoredComposition>;
68
+ export declare function readParkedComposition(ctx: Context, sessionId: SessionId, signal?: AbortSignal): Promise<ParkedComposition>;
@@ -51,7 +51,7 @@ export type Config = z.infer<typeof ConfigShape>;
51
51
  export declare const Config: z.ZodPreprocess<z.ZodObject<{
52
52
  silentWakeRoutes: z.ZodDefault<z.ZodArray<z.ZodString>>;
53
53
  maxListedLogEvents: z.ZodDefault<z.ZodNumber>;
54
- }, z.core.$strip>>;
54
+ }, z.core.$strip>, unknown>;
55
55
  /**
56
56
  * Mount the durable pause registry, the pause gate, the activity fold, their
57
57
  * session control, and the clean resume path.
@@ -1,13 +1,17 @@
1
1
  /**
2
- * The order a tree release walks: parent before child, shallowest first.
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.
3
9
  * @module dsh-pause-session/release-order
4
10
  */
5
11
  import type { Context } from '@deepseek-ai/cordis';
6
12
  import type { SessionId } from '@deepseek-ai/dsh-session';
7
13
  /** How a batch release is sequenced. */
8
14
  export interface ReleaseOrder {
9
- /** The tree edge each child was found under. */
10
- readonly parents: ReadonlyMap<SessionId, SessionId>;
11
15
  /** One list per depth, shallowest first. */
12
16
  readonly levels: readonly (readonly SessionId[])[];
13
17
  }
@@ -16,6 +20,6 @@ export interface ReleaseOrder {
16
20
  * @param ctx - host context carrying the optional subagent catalog.
17
21
  * @param sessionIds - every recorded session, in registry order.
18
22
  * @param signal - cancellation for each catalog read.
19
- * @returns the parent of each child, and one list per depth level.
23
+ * @returns one list per depth level, shallowest first.
20
24
  */
21
25
  export declare function releaseOrder(ctx: Context, sessionIds: readonly SessionId[], signal?: AbortSignal): Promise<ReleaseOrder>;
@@ -1,6 +1,15 @@
1
1
  /**
2
- * Reactivate one parked session: restore its composition, give it the route its
3
- * log recorded, deliver the wake it owes, and only then drop the gate and record.
2
+ * Reactivate one parked Session and deliver the wake it owes.
3
+ *
4
+ * The two Session kinds come back through different owners. An ordinary
5
+ * Session is loaded by this plugin and given the route its own log recorded.
6
+ * A subagent child belongs to the subagent runtime: delivering the wake through
7
+ * `ctx.subagents.prompt` is what cold-resumes it as a managed activation, under
8
+ * its live tree root, with its persisted composition and tree membership
9
+ * restored by the runtime itself.
10
+ *
11
+ * Both paths keep the same release order: the durable record goes first, then
12
+ * the gate, so a record that cannot be dropped leaves the Session parked.
4
13
  * @module dsh-pause-session/resume-activation
5
14
  */
6
15
  import type { Context } from '@deepseek-ai/cordis';
@@ -11,17 +20,17 @@ import type { ParkRegistry } from './registry.js';
11
20
  import type { ResumeAttempt, ResumeOptions, WakeRequest } from './resume-text.js';
12
21
  /** The activation entry points the resume path composes. */
13
22
  export interface Activator {
14
- activate(sessionId: SessionId, request: WakeRequest, rearm: boolean, parentId?: SessionId, signal?: AbortSignal): Promise<ResumeAttempt>;
23
+ activate(sessionId: SessionId, request: WakeRequest, rearm: boolean, signal?: AbortSignal): Promise<ResumeAttempt>;
15
24
  ensureRoute(agent: Agent): void;
16
25
  wake(agent: Agent, request: WakeRequest): void;
17
26
  rearmGoal(agent: Agent): void;
18
27
  }
19
28
  /**
20
29
  * Build the activation closure.
21
- * @param ctx - host context carrying the agent store and goal service.
30
+ * @param ctx - host context carrying the agent store, subagent runtime, and goal service.
22
31
  * @param gate - the pause gate released after activation.
23
32
  * @param registry - the durable list the release forgets from.
24
- * @param options - the route knowledge the wake content depends on.
33
+ * @param options - the route knowledge the root wake content depends on.
25
34
  * @returns the activation methods the resume path uses.
26
35
  */
27
36
  export declare function createActivator(ctx: Context, gate: PauseGate, registry: ParkRegistry, options: ResumeOptions): Activator;
@@ -71,11 +71,10 @@ export interface ResumePath {
71
71
  * Resume one session the registry remembers.
72
72
  * @param sessionId - the session to resume.
73
73
  * @param request - which wake this resume delivers; see {@link WakeRequest}.
74
- * @param parentId - the direct parent a batch release already brought back, when there is one.
75
74
  * @param signal - cancellation for the cold load and setup.
76
75
  * @returns what happened, and the copy for whoever asked.
77
76
  */
78
- resumeSession(sessionId: SessionId, request: WakeRequest, parentId?: SessionId, signal?: AbortSignal): Promise<ResumeAttempt>;
77
+ resumeSession(sessionId: SessionId, request: WakeRequest, signal?: AbortSignal): Promise<ResumeAttempt>;
79
78
  /**
80
79
  * Resume one session the decision list officially owns — a record that is
81
80
  * known NOT to be clean, which the clean path above must refuse and this entry
@@ -86,11 +85,10 @@ export interface ResumePath {
86
85
  * or only answers it (`'review'`). A review activates nothing and writes
87
86
  * nothing: opening a session to read it is the user's own action, not this
88
87
  * plugin's.
89
- * @param parentId - the direct parent a batch release already brought back, when there is one.
90
88
  * @param signal - cancellation for the cold load and setup.
91
89
  * @returns what happened, and the copy for whoever asked.
92
90
  */
93
- decideUnclean(sessionId: SessionId, request: WakeRequest, finalize: 'release' | 'review', parentId?: SessionId, signal?: AbortSignal): Promise<ResumeAttempt>;
91
+ decideUnclean(sessionId: SessionId, request: WakeRequest, finalize: 'release' | 'review', signal?: AbortSignal): Promise<ResumeAttempt>;
94
92
  /**
95
93
  * Resume every session the registry remembers, clean ones only. One session's
96
94
  * failure never stops the others.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-pause-session",
3
- "version": "0.2.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",
@@ -86,7 +86,7 @@
86
86
  "@deepseek-ai/dsh-typert-protocol": "*"
87
87
  },
88
88
  "dependencies": {
89
- "zod": "4.4.3"
89
+ "zod": "4.6.6"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@deepseek-ai/cordis": "link:../../deepseek-harness/vendor/cordis",