@crazx/dsh-api-session-controller 0.1.6-alpha.1.zw.2 → 0.1.6-alpha.2.zw.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/README.i18n.yaml +2 -2
- package/README.md +15 -3
- package/README.zh.md +15 -3
- package/lib/client.js +428 -471
- package/lib/index.js +16 -47
- package/lib/typert.host.js +179 -161
- package/lib/typert.remote-client.js +168 -150
- package/lib/types/agent.d.ts +1 -1
- package/lib/types/agent.js +8 -1
- package/lib/types/client/contract/sessions.d.ts +55 -20
- package/lib/types/client/contract/snapshot.d.ts +0 -14
- package/lib/types/client/index.d.ts +11 -2
- package/lib/types/client/index.js +16 -5
- package/lib/types/client/scope.d.ts +13 -3
- package/lib/types/client/scope.js +15 -22
- package/lib/types/client/sessions/lineage.d.ts +1 -4
- package/lib/types/client/sessions/lineage.js +1 -3
- package/lib/types/client/sessions/manager.d.ts +28 -49
- package/lib/types/client/sessions/manager.js +75 -174
- package/lib/types/client/sessions/projection-store.d.ts +5 -11
- package/lib/types/client/sessions/projection-store.js +6 -14
- package/lib/types/client/sessions/service.d.ts +34 -119
- package/lib/types/client/sessions/service.js +259 -244
- package/lib/types/client/sessions/session.d.ts +7 -20
- package/lib/types/client/sessions/session.js +29 -33
- package/lib/types/commands.d.ts +2 -2
- package/lib/types/commands.js +10 -4
- package/lib/types/control.d.ts +1 -1
- package/lib/types/control.js +1 -41
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.js +1 -1
- package/lib/types/types.d.ts +3 -17
- package/package.json +70 -70
- package/lib/types/client/sessions/queue-mirror.d.ts +0 -26
- package/lib/types/client/sessions/queue-mirror.js +0 -61
package/lib/types/agent.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare class ApiSessionPresetConflict extends Error {
|
|
|
29
29
|
constructor(sessionId: SessionId, requestedPreset: string, existingPreset: string | undefined);
|
|
30
30
|
}
|
|
31
31
|
/** Failures produced while resolving one ordinary Session identity to its live Agent. */
|
|
32
|
-
export type ApiSessionAgentError = RemoteError<'session/not-found' | 'session/agent-busy' | 'gateway/internal'>;
|
|
32
|
+
export type ApiSessionAgentError = RemoteError<'session/not-found' | 'session/agent-busy' | 'session/writer-held' | 'gateway/internal'>;
|
|
33
33
|
/** Result of resolving one ordinary Session identity to its live Agent. */
|
|
34
34
|
export type ApiSessionAgentResult = {
|
|
35
35
|
readonly agent: Agent;
|
package/lib/types/agent.js
CHANGED
|
@@ -219,7 +219,11 @@ export class ApiSessionAgentController {
|
|
|
219
219
|
this.resumes.set(sessionId, resume);
|
|
220
220
|
}
|
|
221
221
|
try {
|
|
222
|
-
|
|
222
|
+
const agent = await resume;
|
|
223
|
+
// A shared resume can publish an identity that subagent routing adopts
|
|
224
|
+
// before every waiter observes it; apply the live ownership policy again.
|
|
225
|
+
const published = this.liveAgent(sessionId);
|
|
226
|
+
return published ?? { agent };
|
|
223
227
|
}
|
|
224
228
|
catch (error) {
|
|
225
229
|
if (error instanceof ApiSessionNotFound) {
|
|
@@ -235,6 +239,9 @@ export class ApiSessionAgentController {
|
|
|
235
239
|
if (racedSession !== undefined && hasApiSessionSubagentOwner(this.ctx, racedSession, undefined)) {
|
|
236
240
|
return { error: apiSessionSubagentOwnershipError(sessionId) };
|
|
237
241
|
}
|
|
242
|
+
if (error instanceof Error && error.name === 'SessionAlreadyOwnedError') {
|
|
243
|
+
return { error: new RemoteError('session/writer-held', error.message, { sessionId }) };
|
|
244
|
+
}
|
|
238
245
|
return {
|
|
239
246
|
error: new RemoteError('gateway/internal', `resume failed for session "${sessionId}": ${String(error)}`, {}),
|
|
240
247
|
};
|
|
@@ -14,11 +14,58 @@ import type { SessionSearchResultItem } from '../sessions/manager.ts';
|
|
|
14
14
|
import type { SessionBinding, SessionListState } from '../sessions/service.ts';
|
|
15
15
|
import type { SessionFace } from './session.ts';
|
|
16
16
|
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
|
|
17
|
+
import type { SessionReferenceSource } from '../index.ts';
|
|
17
18
|
export type { AgentContext } from '../scope.ts';
|
|
19
|
+
/** Known Session identity or durable direct-parent subagent address; an address owns no lifetime. */
|
|
20
|
+
export type SessionTarget = SessionId | SubagentAddress;
|
|
21
|
+
/** One independent use of an exact Client generation, without Host Agent ownership. */
|
|
22
|
+
export interface SessionReference extends Disposable {
|
|
23
|
+
readonly sessionId: SessionId;
|
|
24
|
+
/** Shared binding; access fails after reference release or generation disposal. */
|
|
25
|
+
readonly binding: SessionBinding;
|
|
26
|
+
/** This reference's cancellable wait for the shared initial `Session.open()` attempt to settle. */
|
|
27
|
+
readonly ready: Promise<SessionBinding>;
|
|
28
|
+
/** Release once; the final reference starts local scope and history teardown. */
|
|
29
|
+
release(): void;
|
|
30
|
+
}
|
|
31
|
+
/** Consumer identity and optional cancellation of one acquisition waiter. */
|
|
32
|
+
export interface SessionRetainOptions {
|
|
33
|
+
readonly source: SessionReferenceSource;
|
|
34
|
+
readonly signal?: AbortSignal | undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Local ownership counts, independent of catalog membership and never persisted. */
|
|
37
|
+
export interface SessionRetainInfo {
|
|
38
|
+
readonly referenceCount: number;
|
|
39
|
+
/** Positive source counts only; a source without references is absent. */
|
|
40
|
+
readonly retainedBy: Readonly<Partial<Record<SessionReferenceSource, number>>>;
|
|
41
|
+
}
|
|
18
42
|
/** The sessions-service face injected as `ctx.sessions`. */
|
|
19
43
|
export interface ISessions {
|
|
20
|
-
/**
|
|
44
|
+
/** Host catalog and local reference-source counts; navigation belongs to view owners. */
|
|
21
45
|
readonly list: ObservableSnapshot<SessionListState>;
|
|
46
|
+
/**
|
|
47
|
+
* Retain an exact Client generation and start its shared initial history opening.
|
|
48
|
+
* @param target - known identity or durable direct-parent address.
|
|
49
|
+
* @param options - required consumer source and optional independent waiter cancellation.
|
|
50
|
+
* @returns an owned reference immediately; await `reference.ready` when the initial open attempt must settle first.
|
|
51
|
+
*/
|
|
52
|
+
retain(target: SessionTarget, options: SessionRetainOptions): SessionReference;
|
|
53
|
+
/**
|
|
54
|
+
* Hold one reference through callback settlement, including synchronous and asynchronous failures.
|
|
55
|
+
* @param target - Session to acquire.
|
|
56
|
+
* @param options - source and acquisition cancellation.
|
|
57
|
+
* @param operation - callback using the reference only until its returned value or Promise settles.
|
|
58
|
+
* @returns the callback result after release; acquisition and callback failures propagate unchanged.
|
|
59
|
+
*/
|
|
60
|
+
using<T>(target: SessionTarget, options: SessionRetainOptions, operation: (reference: SessionReference) => T | Promise<T>): Promise<T>;
|
|
61
|
+
/**
|
|
62
|
+
* Observe local reference counts without retaining, creating a scope, or opening history.
|
|
63
|
+
* The returned source keeps stable identity across same-id generations and remains allocated
|
|
64
|
+
* until the Client root is disposed, even after its final subscriber leaves.
|
|
65
|
+
* @param id - explicit Session identity; Host existence is not implied.
|
|
66
|
+
* @returns a stable read-only source across same-id generations, with zero counts when none is live.
|
|
67
|
+
*/
|
|
68
|
+
retainInfo(id: SessionId): ObservableSnapshot<SessionRetainInfo>;
|
|
22
69
|
/**
|
|
23
70
|
* The `session.search` result bound the wire schema fixes, exposed to
|
|
24
71
|
* presentation as injected data. Not per-connection state: every transport
|
|
@@ -28,23 +75,13 @@ export interface ISessions {
|
|
|
28
75
|
/**
|
|
29
76
|
* Create or adopt a Session on the Host.
|
|
30
77
|
* @param opts - target workspace, directory, and optional preallocated identity.
|
|
31
|
-
* @returns the
|
|
78
|
+
* @returns the catalogued identity; retain it before borrowing its binding.
|
|
32
79
|
*/
|
|
33
80
|
create(opts?: {
|
|
34
81
|
workspaceId?: WorkspaceId;
|
|
35
82
|
cwd?: string;
|
|
36
83
|
sessionId?: SessionId;
|
|
37
84
|
}): Promise<SessionId>;
|
|
38
|
-
/**
|
|
39
|
-
* Select a session as current.
|
|
40
|
-
* @param id - session id (must exist in the list; unknown ids fail loud).
|
|
41
|
-
*/
|
|
42
|
-
open(id: SessionId): void;
|
|
43
|
-
/**
|
|
44
|
-
* Open a healthy catalog child through its exact direct-parent address.
|
|
45
|
-
* @param address - catalog-derived parent and child ids.
|
|
46
|
-
*/
|
|
47
|
-
openSubagent(address: SubagentAddress): void;
|
|
48
85
|
/**
|
|
49
86
|
* Resolve an already discovered direct-parent address without opening it.
|
|
50
87
|
* @param id - possible addressed child id.
|
|
@@ -63,8 +100,6 @@ export interface ISessions {
|
|
|
63
100
|
* @returns completion of the current or newly started refresh.
|
|
64
101
|
*/
|
|
65
102
|
refreshSubagents(parentSessionId: SessionId): Promise<void>;
|
|
66
|
-
/** Clear the current selection into the no-session view state. */
|
|
67
|
-
clear(): void;
|
|
68
103
|
/**
|
|
69
104
|
* Refresh the Host-authoritative Session list.
|
|
70
105
|
* @returns completion of the current or newly started Session-list refresh.
|
|
@@ -83,7 +118,7 @@ export interface ISessions {
|
|
|
83
118
|
}>>;
|
|
84
119
|
/**
|
|
85
120
|
* Fork a session from a completed-turn prefix of the source; on resolution
|
|
86
|
-
* the child is in the
|
|
121
|
+
* the child is in the catalog and may be explicitly retained.
|
|
87
122
|
* @param opts - source session id, the optional event seq anchoring the
|
|
88
123
|
* cut (the boundary is the first turn/end at or after it; an in-log
|
|
89
124
|
* anchor in an open turn is unavailable rather than clipped backward),
|
|
@@ -97,9 +132,9 @@ export interface ISessions {
|
|
|
97
132
|
increaseTitle?: boolean;
|
|
98
133
|
}): Promise<SessionId>;
|
|
99
134
|
/**
|
|
100
|
-
*
|
|
135
|
+
* Borrow an already-retained Agent-scoped Context without extending its lifetime.
|
|
101
136
|
* @param id - session id.
|
|
102
|
-
* @returns scoped
|
|
137
|
+
* @returns the live scoped Context, or undefined without a retained generation.
|
|
103
138
|
*/
|
|
104
139
|
scope(id: SessionId): AgentContext | undefined;
|
|
105
140
|
/**
|
|
@@ -112,13 +147,13 @@ export interface ISessions {
|
|
|
112
147
|
/**
|
|
113
148
|
* Resolve the session face behind an Agent-scoped context.
|
|
114
149
|
* @param ctx - an Agent-scoped context.
|
|
115
|
-
* @returns the
|
|
150
|
+
* @returns the matching live Session, or undefined for an untagged, foreign, or ended generation.
|
|
116
151
|
*/
|
|
117
152
|
sessionOf(ctx: Context): SessionFace | undefined;
|
|
118
153
|
/**
|
|
119
|
-
*
|
|
154
|
+
* Borrow an already-retained Session binding without extending its lifetime.
|
|
120
155
|
* @param id - session id.
|
|
121
|
-
* @returns binding, or undefined
|
|
156
|
+
* @returns the live binding, or undefined without a retained generation.
|
|
122
157
|
*/
|
|
123
158
|
binding(id: SessionId): SessionBinding | undefined;
|
|
124
159
|
}
|
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
/** Session-owned observable state excluding Conversation target data. */
|
|
2
|
-
import type { ContentBlock } from '@deepseek-ai/dsh-llm/types';
|
|
3
2
|
import type { FileAttachmentRef } from '@deepseek-ai/dsh-attachment';
|
|
4
|
-
import type { MessageId } from '@deepseek-ai/dsh-llm/brand';
|
|
5
3
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
6
4
|
import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client';
|
|
7
5
|
import type { RemoteFailure } from '@deepseek-ai/dsh-typert-protocol';
|
|
8
6
|
import type { SessionRequestId } from '../../types.ts';
|
|
9
|
-
/** One transient inbox occurrence from the authoritative queue snapshot. */
|
|
10
|
-
export interface QueuedMessage {
|
|
11
|
-
readonly id: MessageId;
|
|
12
|
-
readonly messageId: MessageId;
|
|
13
|
-
readonly placement: 'queued' | 'steering' | 'context';
|
|
14
|
-
/** Prompt-RPC identity of a browser-submitted occurrence; correlates the local submission echo. */
|
|
15
|
-
readonly rpcId?: SessionRequestId;
|
|
16
|
-
readonly content: readonly ContentBlock[];
|
|
17
|
-
readonly preview: string;
|
|
18
|
-
readonly text: string | null;
|
|
19
|
-
}
|
|
20
7
|
/** One image displayed by a local submission echo before durable admission. */
|
|
21
8
|
export interface PendingSubmissionImage {
|
|
22
9
|
/** Browser-owned preview URL; its lifecycle belongs to the submitter, never this snapshot. */
|
|
@@ -70,7 +57,6 @@ export interface PromptError {
|
|
|
70
57
|
/** Immutable Session lifecycle and control snapshot. */
|
|
71
58
|
export interface SessionSnapshot {
|
|
72
59
|
readonly sessionId: SessionId;
|
|
73
|
-
readonly queue: readonly QueuedMessage[];
|
|
74
60
|
/** Local prompt-submission echoes not yet observed as durable events or queue occurrences. */
|
|
75
61
|
readonly pendingSubmissions: readonly PendingSubmission[];
|
|
76
62
|
readonly running: boolean;
|
|
@@ -10,10 +10,19 @@ export type { SessionListPhase, SessionListSnapshot, SessionSearchResultItem, Su
|
|
|
10
10
|
export type { Session } from './sessions/session.ts';
|
|
11
11
|
export type { ProjectionsBaseline, ProjectionValueStore, SessionProjectionMap, UseProjection, } from './sessions/projection-store.ts';
|
|
12
12
|
export type { BeginSubmissionInput, ISession, PendingSubmissionRetirement, ProjectionsFace, SessionFace, SubmissionHandle, } from './contract/session.ts';
|
|
13
|
-
export type { ISessions } from './contract/sessions.ts';
|
|
13
|
+
export type { ISessions, SessionReference, SessionRetainInfo, SessionRetainOptions, SessionTarget, } from './contract/sessions.ts';
|
|
14
14
|
export { MutableSessionEventSource } from './contract/events.ts';
|
|
15
15
|
export type { AssistantLiveChunkEvent, SessionAssistantSettlementEntry, SessionEventChange, SessionEventLike, SessionEventLikeEntry, SessionEventSource, SessionEventWindow, SessionLiveEventEntry, SessionTransientEventEntry, } from './contract/events.ts';
|
|
16
|
-
export type { OpenState, PendingSubmission, PendingSubmissionAttachment, PendingSubmissionFileAttachment, PendingSubmissionImage, PendingSubmissionImageAttachment, PendingSubmissionPlacement, PromptError,
|
|
16
|
+
export type { OpenState, PendingSubmission, PendingSubmissionAttachment, PendingSubmissionFileAttachment, PendingSubmissionImage, PendingSubmissionImageAttachment, PendingSubmissionPlacement, PromptError, SessionSnapshot, } from './contract/snapshot.ts';
|
|
17
|
+
/** Consumer-owned reference labels; extend this map through the package's canonical /client entry. */
|
|
18
|
+
export interface SessionReferenceSourceMap {
|
|
19
|
+
/** Temporary Client Controller work, including fork-title preparation. */
|
|
20
|
+
controllerOperation: unknown;
|
|
21
|
+
/** A Client Gateway invocation's synchronous Context ownership. */
|
|
22
|
+
gateway: unknown;
|
|
23
|
+
}
|
|
24
|
+
/** Declaration-merge-extensible labels carried by independent Client references. */
|
|
25
|
+
export type SessionReferenceSource = Extract<keyof SessionReferenceSourceMap, string>;
|
|
17
26
|
declare module '@deepseek-ai/cordis' {
|
|
18
27
|
interface Context {
|
|
19
28
|
/** Client Session object layer and Agent scope owner. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Client Session object layer, Agent scopes, and Remote lifecycle wiring. */
|
|
2
|
+
import { typertOwnedValue } from '@deepseek-ai/dsh-typert-protocol';
|
|
2
3
|
import { createSessionControlStream } from "./transport.js";
|
|
3
4
|
import { ClientSessions } from "./sessions/service.js";
|
|
4
5
|
export { createSessionControlStream, SessionEventStream, SESSION_SEARCH_RESULT_LIMIT, SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS, } from "./transport.js";
|
|
@@ -21,6 +22,7 @@ export const inject = [
|
|
|
21
22
|
*/
|
|
22
23
|
export function apply(ctx) {
|
|
23
24
|
const remotes = ctx.remote;
|
|
25
|
+
const connection = ctx.get('connection');
|
|
24
26
|
const sessions = new ClientSessions(ctx, remotes);
|
|
25
27
|
ctx.remote.$on('api-session/added', (summary) => { sessions.handleSessionAdded(summary); });
|
|
26
28
|
ctx.remote.$on('api-session/removed', (sessionId) => { sessions.handleSessionRemoved(sessionId); });
|
|
@@ -37,13 +39,22 @@ export function apply(ctx) {
|
|
|
37
39
|
accept: (frame) => { sessions.handleControlFrame(frame); },
|
|
38
40
|
failed: (error) => { console.error('[session-controller] control stream failed:', error); },
|
|
39
41
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const connected = () => {
|
|
43
|
+
if (connection.generation.getSnapshot() === undefined)
|
|
44
|
+
return;
|
|
45
|
+
// A ready control baseline may arrive before Cordis delivers connection/reset.
|
|
43
46
|
sessions.handleConnected();
|
|
47
|
+
control.restart();
|
|
48
|
+
control.start();
|
|
49
|
+
};
|
|
50
|
+
ctx.effect(() => connection.generation.subscribe(connected), 'session-controller.client.generation');
|
|
51
|
+
connected();
|
|
44
52
|
ctx.typert.contexts.registerClient('agent', {
|
|
45
|
-
identity: candidate => sessions.
|
|
46
|
-
resolve: sessionId =>
|
|
53
|
+
identity: candidate => sessions.sessionOf(candidate)?.sessionId,
|
|
54
|
+
resolve: (sessionId) => {
|
|
55
|
+
const reference = sessions.retainAgentScope(sessionId);
|
|
56
|
+
return typertOwnedValue(reference.binding.ctx, () => { reference.release(); });
|
|
57
|
+
},
|
|
47
58
|
});
|
|
48
59
|
ctx.effect(() => async () => { await control.dispose(); }, 'session-controller.client.control');
|
|
49
60
|
}
|
|
@@ -6,11 +6,14 @@ import type { TypertRemoteScopeApi } from '@deepseek-ai/dsh-typert-protocol';
|
|
|
6
6
|
export type AgentContext = Omit<Context, 'remote'> & {
|
|
7
7
|
readonly remote: ClientRemote & TypertRemoteScopeApi<'agent'>;
|
|
8
8
|
};
|
|
9
|
+
interface ScopeIdentity {
|
|
10
|
+
readonly sessionId: SessionId;
|
|
11
|
+
}
|
|
9
12
|
/** A minted Agent scope and its disposal boundary. */
|
|
10
13
|
export interface AgentScopeHandle {
|
|
11
14
|
/**
|
|
12
15
|
* Tagged context: scope-owned registrations and scoped dispatch both go
|
|
13
|
-
* through it (passing it as the dispatch subject routes to this
|
|
16
|
+
* through it (passing it as the dispatch subject routes to this generation's
|
|
14
17
|
* tagged listeners plus every untagged one).
|
|
15
18
|
*/
|
|
16
19
|
ctx: AgentContext;
|
|
@@ -20,10 +23,10 @@ export interface AgentScopeHandle {
|
|
|
20
23
|
/**
|
|
21
24
|
* Mint an Agent scope under `ctx`: a no-op plugin fiber whose context
|
|
22
25
|
* carries the agent tag and the dispatch filter — untagged listeners are
|
|
23
|
-
* admitted globally, tagged listeners only for
|
|
26
|
+
* admitted globally, tagged listeners only for the same Client generation.
|
|
24
27
|
* Registrations through the returned ctx dispose with the fiber.
|
|
25
28
|
* @param ctx - client root context the scope fiber mounts under.
|
|
26
|
-
* @param key -
|
|
29
|
+
* @param key - durable Session identity carried by this generation.
|
|
27
30
|
* @returns the tagged context and its backing fiber.
|
|
28
31
|
*/
|
|
29
32
|
export declare function createScope(ctx: Context, key: SessionId): AgentScopeHandle;
|
|
@@ -33,4 +36,11 @@ export declare function createScope(ctx: Context, key: SessionId): AgentScopeHan
|
|
|
33
36
|
* @returns its agent identity (the session id), or undefined for root contexts.
|
|
34
37
|
*/
|
|
35
38
|
export declare function scopeOf(ctx: Context): SessionId | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Read the exact generation identity inherited by a Client Context.
|
|
41
|
+
* @param ctx - scoped or root Client Context.
|
|
42
|
+
* @returns the generation identity, or undefined for an unscoped Context.
|
|
43
|
+
*/
|
|
44
|
+
export declare function scopeIdentityOf(ctx: Context): ScopeIdentity | undefined;
|
|
45
|
+
export {};
|
|
36
46
|
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client Agent-scope primitive: mint a Cordis context tagged with the owning
|
|
3
|
-
* Agent's identity. The mechanism mirrors the host `dsh-scope` architecture
|
|
4
|
-
* (no-op plugin fiber + context tag + `Context.filter` routing predicate);
|
|
5
|
-
* the shape deliberately diverges: the filter lives on the actx itself
|
|
6
|
-
* instead of a separate carrier object, so scoped dispatch is plain cordis —
|
|
7
|
-
* `actx.bail(actx, event, payload)` / `actx.emit(actx, ...)` — with no
|
|
8
|
-
* wrapper. The host needs a detached carrier because its dispatch subject is
|
|
9
|
-
* the business Agent object; client scope events carry only ids, so the
|
|
10
|
-
* actx is the natural subject. The second divergence stands: the scope key
|
|
11
|
-
* is the branded `SessionId` (value compared), not an object identity — the
|
|
12
|
-
* agent and its session share one id (1:1, same axis; no separate AgentId
|
|
13
|
-
* brand), and a client scope's identity IS that wire id. Third divergence,
|
|
14
|
-
* deliberate: the client scopes the Agent IDENTITY, not a live Agent object
|
|
15
|
-
* — a cold session's host Agent is already disposed while its client actx
|
|
16
|
-
* stays alive for history viewing.
|
|
17
|
-
*/
|
|
1
|
+
/** Client scope generations route local events independently of Host Agent residency. */
|
|
18
2
|
import { Context as CordisContext } from '@deepseek-ai/cordis';
|
|
19
3
|
/** Context tag written by {@link createScope}. */
|
|
20
4
|
const kScope = Symbol('dsh.client.scope');
|
|
@@ -23,19 +7,20 @@ function agentScope() { }
|
|
|
23
7
|
/**
|
|
24
8
|
* Mint an Agent scope under `ctx`: a no-op plugin fiber whose context
|
|
25
9
|
* carries the agent tag and the dispatch filter — untagged listeners are
|
|
26
|
-
* admitted globally, tagged listeners only for
|
|
10
|
+
* admitted globally, tagged listeners only for the same Client generation.
|
|
27
11
|
* Registrations through the returned ctx dispose with the fiber.
|
|
28
12
|
* @param ctx - client root context the scope fiber mounts under.
|
|
29
|
-
* @param key -
|
|
13
|
+
* @param key - durable Session identity carried by this generation.
|
|
30
14
|
* @returns the tagged context and its backing fiber.
|
|
31
15
|
*/
|
|
32
16
|
export function createScope(ctx, key) {
|
|
33
17
|
const fiber = ctx.plugin(agentScope);
|
|
18
|
+
const identity = { sessionId: key };
|
|
34
19
|
const scoped = fiber.ctx.extend({
|
|
35
|
-
[kScope]:
|
|
20
|
+
[kScope]: identity,
|
|
36
21
|
[CordisContext.filter](listenerCtx) {
|
|
37
|
-
const tag =
|
|
38
|
-
return tag === undefined || tag ===
|
|
22
|
+
const tag = scopeIdentityOf(listenerCtx);
|
|
23
|
+
return tag === undefined || tag === identity;
|
|
39
24
|
},
|
|
40
25
|
});
|
|
41
26
|
return {
|
|
@@ -49,6 +34,14 @@ export function createScope(ctx, key) {
|
|
|
49
34
|
* @returns its agent identity (the session id), or undefined for root contexts.
|
|
50
35
|
*/
|
|
51
36
|
export function scopeOf(ctx) {
|
|
37
|
+
return scopeIdentityOf(ctx)?.sessionId;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read the exact generation identity inherited by a Client Context.
|
|
41
|
+
* @param ctx - scoped or root Client Context.
|
|
42
|
+
* @returns the generation identity, or undefined for an unscoped Context.
|
|
43
|
+
*/
|
|
44
|
+
export function scopeIdentityOf(ctx) {
|
|
52
45
|
return ctx[kScope];
|
|
53
46
|
}
|
|
54
47
|
//# sourceMappingURL=scope.js.map
|
|
@@ -21,8 +21,6 @@ export interface SessionListEntry {
|
|
|
21
21
|
cwd?: string;
|
|
22
22
|
/** Current host-computed projection values for list consumers. */
|
|
23
23
|
projectionValues?: Readonly<Partial<SessionProjectionMap>>;
|
|
24
|
-
/** Finished running while not selected and not yet opened — the sidebar's green "done" reminder (clears on select or the next run). */
|
|
25
|
-
completed: boolean;
|
|
26
24
|
/** Lineage indent depth: root = 0; the UI just multiplies by the indent width. */
|
|
27
25
|
depth: number;
|
|
28
26
|
}
|
|
@@ -31,8 +29,7 @@ export interface SessionListEntry {
|
|
|
31
29
|
* follows the established input order; this projection never re-sorts a
|
|
32
30
|
* hydrated list from mutable timestamps.
|
|
33
31
|
* @param summaries - the host's session.list items.
|
|
34
|
-
* @param completed - sessions with a pending completion reminder (manager-owned live fact; absent = false).
|
|
35
32
|
* @returns display rows in render order.
|
|
36
33
|
*/
|
|
37
|
-
export declare function flattenLineage(summaries: readonly TitledSessionSummary[]
|
|
34
|
+
export declare function flattenLineage(summaries: readonly TitledSessionSummary[]): SessionListEntry[];
|
|
38
35
|
//# sourceMappingURL=lineage.d.ts.map
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* follows the established input order; this projection never re-sorts a
|
|
7
7
|
* hydrated list from mutable timestamps.
|
|
8
8
|
* @param summaries - the host's session.list items.
|
|
9
|
-
* @param completed - sessions with a pending completion reminder (manager-owned live fact; absent = false).
|
|
10
9
|
* @returns display rows in render order.
|
|
11
10
|
*/
|
|
12
|
-
export function flattenLineage(summaries
|
|
11
|
+
export function flattenLineage(summaries) {
|
|
13
12
|
const byId = new Map();
|
|
14
13
|
for (const s of summaries)
|
|
15
14
|
byId.set(s.sessionId, s);
|
|
@@ -35,7 +34,6 @@ export function flattenLineage(summaries, completed) {
|
|
|
35
34
|
visited.add(s.sessionId);
|
|
36
35
|
out.push({
|
|
37
36
|
...s,
|
|
38
|
-
completed: completed?.has(s.sessionId) ?? false,
|
|
39
37
|
depth,
|
|
40
38
|
});
|
|
41
39
|
const kids = children.get(s.sessionId);
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
/** Host catalog, durable projection caches, and explicitly retained Client instances. */
|
|
1
2
|
import type { SubagentAddress, SubagentCatalog } from '@deepseek-ai/dsh-subagent/client';
|
|
2
3
|
import { SessionSeq, type SessionId } from '@deepseek-ai/dsh-session/types';
|
|
4
|
+
import type { SessionProjectionMap } from '@deepseek-ai/dsh-session-projection/types';
|
|
3
5
|
import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types';
|
|
4
6
|
import type { SessionControlFrame, SessionSummary, SessionJob as JobView } from '../../types.ts';
|
|
5
7
|
import type { RemoteFailure, RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
|
|
6
8
|
import type { SessionListEntry } from './lineage.ts';
|
|
7
9
|
import { Session } from './session.ts';
|
|
8
10
|
import type { SessionRemotes } from './remotes.ts';
|
|
11
|
+
import type { SessionTarget } from '../contract/sessions.ts';
|
|
9
12
|
/**
|
|
10
13
|
* List arrival lifecycle, orthogonal to the pull-activity `state` axis:
|
|
11
14
|
* `pending` (no successful pull yet — an empty items array means "nothing
|
|
@@ -23,8 +26,6 @@ export interface SessionSearchResultItem {
|
|
|
23
26
|
/** Immutable session-list snapshot for useSessionList. */
|
|
24
27
|
export interface SessionListSnapshot {
|
|
25
28
|
items: readonly SessionListEntry[];
|
|
26
|
-
/** Selected Session id (validated against items; masked to undefined while its session is off the list). */
|
|
27
|
-
current: SessionId | undefined;
|
|
28
29
|
state: 'idle' | 'loading' | 'error';
|
|
29
30
|
/** Arrival lifecycle (see {@link SessionListPhase}); `state` stays the pull-activity axis. */
|
|
30
31
|
phase: SessionListPhase;
|
|
@@ -32,7 +33,6 @@ export interface SessionListSnapshot {
|
|
|
32
33
|
subagentsByParent: Readonly<Record<SessionId, SubagentCatalogSnapshot>>;
|
|
33
34
|
/** Background jobs per session; an absent key is an empty set. */
|
|
34
35
|
jobsBySession: Readonly<Record<SessionId, readonly JobView[]>>;
|
|
35
|
-
currentAddress: SubagentAddress | undefined;
|
|
36
36
|
}
|
|
37
37
|
/** One parent-addressed durable catalog projected through the sessions snapshot. */
|
|
38
38
|
export type SubagentCatalogSnapshot = Omit<SubagentCatalog, 'parentAvailable'> & {
|
|
@@ -47,16 +47,6 @@ export declare class SessionManager {
|
|
|
47
47
|
private readonly sessions;
|
|
48
48
|
/** In-flight Session disposals remain here after instances leave `sessions`, so manager disposal can await quiescence. */
|
|
49
49
|
private readonly sessionDisposals;
|
|
50
|
-
/** Latest transient queues, retained independently of Session object materialization. */
|
|
51
|
-
private readonly queues;
|
|
52
|
-
/**
|
|
53
|
-
* Sessions that finished running while not selected — the sidebar's green
|
|
54
|
-
* "done" reminder (manager-owned, survives connection generations; cleared
|
|
55
|
-
* on select and session-removed, re-armed by the next completion).
|
|
56
|
-
*/
|
|
57
|
-
private readonly completedNotifications;
|
|
58
|
-
/** Last-observed running bits per session; the true→false edge here arms {@link completedNotifications}. */
|
|
59
|
-
private readonly prevRunning;
|
|
60
50
|
/** Per-session projection value stores, retained independently of instance arrival (the
|
|
61
51
|
* title-snapshot precedent, generalized): push frames land here whether or not the Session
|
|
62
52
|
* is instantiated (list rows read the 'title' key), and an instantiated Session adopts the
|
|
@@ -68,7 +58,7 @@ export declare class SessionManager {
|
|
|
68
58
|
private listPhase;
|
|
69
59
|
private listError;
|
|
70
60
|
private listInflight;
|
|
71
|
-
/**
|
|
61
|
+
/** Active list request's mutation log; its identity also fences completion after reconnect. */
|
|
72
62
|
private listMutations;
|
|
73
63
|
private readonly addresses;
|
|
74
64
|
private readonly catalogs;
|
|
@@ -83,7 +73,6 @@ export declare class SessionManager {
|
|
|
83
73
|
* one representation.
|
|
84
74
|
*/
|
|
85
75
|
private readonly jobsBySession;
|
|
86
|
-
private selected;
|
|
87
76
|
private listSnapshotCache;
|
|
88
77
|
/** Entry-identity cache (reference stability): list rebuilds reuse the previous entry
|
|
89
78
|
* object when every field matches — wire refreshes mint all-new summary objects, so identity
|
|
@@ -91,23 +80,14 @@ export declare class SessionManager {
|
|
|
91
80
|
private entryCache;
|
|
92
81
|
private itemsCache;
|
|
93
82
|
private readonly notifier;
|
|
83
|
+
/** @param remote - generated Remote namespaces used by catalog and history readers. */
|
|
84
|
+
constructor(remote: SessionRemotes);
|
|
94
85
|
/**
|
|
95
|
-
*
|
|
96
|
-
* @param
|
|
97
|
-
|
|
98
|
-
constructor(remote: SessionRemotes, restoredSelection?: SessionId, restoredAddress?: SubagentAddress);
|
|
99
|
-
/**
|
|
100
|
-
* Select a listed Session or a retained catalog-addressed child.
|
|
101
|
-
* @param sessionId - listed or catalog-addressed Session id.
|
|
102
|
-
*/
|
|
103
|
-
select(sessionId: SessionId): void;
|
|
104
|
-
/**
|
|
105
|
-
* Select a healthy child through its durable direct-parent address.
|
|
106
|
-
* @param address - catalog-derived parent and child ids.
|
|
86
|
+
* Resolve an acquisition target without materializing a Session.
|
|
87
|
+
* @param target - known identity or durable direct-parent address.
|
|
88
|
+
* @returns the resolved identity with its explicit or catalog-derived history route installed.
|
|
107
89
|
*/
|
|
108
|
-
|
|
109
|
-
/** Clear the selection (the layout falls to the no-session view state). */
|
|
110
|
-
clearSelection(): void;
|
|
90
|
+
resolveTarget(target: SessionTarget): SessionId;
|
|
111
91
|
/**
|
|
112
92
|
* Return the durable catalog address retained for one child.
|
|
113
93
|
* @param sessionId - possible addressed child id.
|
|
@@ -121,22 +101,22 @@ export declare class SessionManager {
|
|
|
121
101
|
*/
|
|
122
102
|
navigationAddress(sessionId: SessionId): SubagentAddress | undefined;
|
|
123
103
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* @
|
|
104
|
+
* Withdraw an exact Client instance before running its teardown callbacks.
|
|
105
|
+
* @param sessionId - identity to withdraw.
|
|
106
|
+
* @param expected - instance being released; a replacement is left untouched.
|
|
107
|
+
* @returns completion of the detached instance's stream teardown.
|
|
128
108
|
*/
|
|
129
|
-
drop(sessionId: SessionId): Promise<void>;
|
|
109
|
+
drop(sessionId: SessionId, expected: Session): Promise<void>;
|
|
130
110
|
/**
|
|
131
|
-
* Stop
|
|
132
|
-
* @returns
|
|
111
|
+
* Stop catalog requests and dispose every resident Session.
|
|
112
|
+
* @returns once catalog requests and every Session stream have stopped.
|
|
133
113
|
*/
|
|
134
114
|
dispose(): Promise<void>;
|
|
135
115
|
private startSessionDisposal;
|
|
136
116
|
private drainSessionDisposals;
|
|
137
117
|
/**
|
|
138
118
|
* Lazy build: return the existing instance or construct one (no auto-open —
|
|
139
|
-
*
|
|
119
|
+
* the reference allocator opens history after binding the scope).
|
|
140
120
|
* @param sessionId - the session to get.
|
|
141
121
|
* @returns the resident instance.
|
|
142
122
|
*/
|
|
@@ -155,7 +135,7 @@ export declare class SessionManager {
|
|
|
155
135
|
* @param open - current menu state.
|
|
156
136
|
*/
|
|
157
137
|
setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void;
|
|
158
|
-
/** Full refresh via session.list (single-flight
|
|
138
|
+
/** Full refresh via session.list (single-flight within one Host generation). */
|
|
159
139
|
refreshList(): Promise<void>;
|
|
160
140
|
/**
|
|
161
141
|
* Search visible session message content without adding transient query
|
|
@@ -217,6 +197,12 @@ export declare class SessionManager {
|
|
|
217
197
|
* @returns the cached reference (stable until the next flush).
|
|
218
198
|
*/
|
|
219
199
|
getListSnapshot(): SessionListSnapshot;
|
|
200
|
+
/**
|
|
201
|
+
* Read cached projection values for a Session that may exist only in a loaded subagent catalog.
|
|
202
|
+
* @param sessionId - Session whose control or history baseline supplied projections.
|
|
203
|
+
* @returns current values, or undefined before any projection store exists.
|
|
204
|
+
*/
|
|
205
|
+
projectionValues(sessionId: SessionId): Readonly<Partial<SessionProjectionMap>> | undefined;
|
|
220
206
|
/**
|
|
221
207
|
* Apply a complete control baseline or one later replacement frame.
|
|
222
208
|
* @param frame - baseline or live control replacement from Session Controller.
|
|
@@ -253,10 +239,12 @@ export declare class SessionManager {
|
|
|
253
239
|
handleSessionError(sessionId: SessionId, message: string): void;
|
|
254
240
|
/**
|
|
255
241
|
* Repair one re-established Host-event generation with queryable baselines.
|
|
242
|
+
* Discard old projection cuts before new queries, including cold Sessions
|
|
243
|
+
* absent from the process-local control baseline.
|
|
256
244
|
* Opened Session follow streams resume independently through API Gateway.
|
|
257
245
|
*/
|
|
258
246
|
handleConnected(): void;
|
|
259
|
-
/** Debounce membership refetches
|
|
247
|
+
/** Debounce membership refetches for an explicitly consumed catalog. */
|
|
260
248
|
private scheduleCatalogRefresh;
|
|
261
249
|
/** Apply one Agent-driver transition to loaded and in-flight catalogs. */
|
|
262
250
|
private updateCatalogActivity;
|
|
@@ -266,15 +254,6 @@ export declare class SessionManager {
|
|
|
266
254
|
private applyCatalogParentExpandable;
|
|
267
255
|
/** Fold request-local row mutations into one catalog result before publication. */
|
|
268
256
|
private withCatalogMutations;
|
|
269
|
-
/**
|
|
270
|
-
* Reconcile completion reminders against the latest summaries, eagerly after
|
|
271
|
-
* every mutation and pull (a snapshot-build-time pass would collapse
|
|
272
|
-
* consecutive status frames into one observation). A running→idle edge of a
|
|
273
|
-
* non-selected session arms its reminder; running disarms it; removal drops
|
|
274
|
-
* it. First observation only records the running bit — sessions already
|
|
275
|
-
* idle at load get no reminder.
|
|
276
|
-
*/
|
|
277
|
-
private syncCompletedNotifications;
|
|
278
257
|
private buildListSnapshot;
|
|
279
258
|
}
|
|
280
259
|
//# sourceMappingURL=manager.d.ts.map
|