@deepseek-ai/dsh-api-session-controller 0.1.6-alpha.1 → 0.1.7-alpha.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 +42 -7
- package/README.zh.md +42 -7
- package/lib/client.js +1037 -992
- package/lib/index.js +314 -183
- package/lib/typert.host.js +539 -331
- package/lib/typert.remote-client.d.ts +10 -5
- package/lib/typert.remote-client.js +374 -264
- package/lib/types/agent.d.ts +1 -1
- package/lib/types/agent.js +8 -1
- package/lib/types/archived-session-gate.d.ts +30 -0
- package/lib/types/archived-session-gate.js +52 -0
- package/lib/types/client/contract/session.d.ts +2 -0
- package/lib/types/client/contract/sessions.d.ts +65 -35
- package/lib/types/client/contract/snapshot.d.ts +0 -14
- package/lib/types/client/index.d.ts +12 -3
- 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 +5 -5
- package/lib/types/client/sessions/lineage.js +3 -4
- package/lib/types/client/sessions/manager.d.ts +91 -105
- package/lib/types/client/sessions/manager.js +307 -434
- package/lib/types/client/sessions/projection-store.d.ts +34 -21
- package/lib/types/client/sessions/projection-store.js +48 -24
- package/lib/types/client/sessions/remotes.d.ts +1 -2
- package/lib/types/client/sessions/service.d.ts +53 -150
- package/lib/types/client/sessions/service.js +267 -264
- package/lib/types/client/sessions/session.d.ts +24 -35
- package/lib/types/client/sessions/session.js +131 -73
- package/lib/types/commands.d.ts +6 -4
- package/lib/types/commands.js +82 -40
- package/lib/types/control.d.ts +2 -4
- package/lib/types/control.js +2 -84
- package/lib/types/history.js +1 -1
- package/lib/types/index.d.ts +34 -7
- package/lib/types/index.js +118 -14
- package/lib/types/list.js +24 -15
- package/lib/types/skill-catalog.js +60 -50
- package/lib/types/types.d.ts +40 -40
- package/lib/types/types.js +0 -1
- package/package.json +71 -76
- 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
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The controller's admission gate for archived Sessions: an archived
|
|
3
|
+
* Session, or a subagent descendant of one, must not run a model step until
|
|
4
|
+
* it is restored. The work a Session still runs is reported and stopped by
|
|
5
|
+
* its owners — the Agent registry (`turn`), the job registry seam (`job`),
|
|
6
|
+
* the Subagent runtime (`subagent`), and the Schedule plugin (`schedule`) —
|
|
7
|
+
* through the Workspace registry's archive-admission events.
|
|
8
|
+
*/
|
|
9
|
+
import type { Context, Plugin } from '@deepseek-ai/cordis';
|
|
10
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
11
|
+
/**
|
|
12
|
+
* The gate as a plugin for `ctx.plugin(...)`: it loads once the Agent
|
|
13
|
+
* registry, Session store, and Workspace registry are available and unwinds
|
|
14
|
+
* with the plugin's fiber. A late waking delivery to an archived Session — a
|
|
15
|
+
* subagent settlement, a queued follow-up — proposes a step the gate rejects,
|
|
16
|
+
* which the loop ends as `blocked` without a request; unarchiving lifts the
|
|
17
|
+
* gate for the whole lineage.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ArchivedSessionGate: Plugin.Object<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the Agent's Session, or a Session above it in its subagent lineage,
|
|
22
|
+
* is archived. Lineage follows the durable header fields through
|
|
23
|
+
* subagent-origin Sessions only: a fork of an archived Session is an
|
|
24
|
+
* independent conversation.
|
|
25
|
+
* @param ctx - Host context.
|
|
26
|
+
* @param agent - the Agent proposing a step.
|
|
27
|
+
* @returns whether an archived Session owns the step.
|
|
28
|
+
*/
|
|
29
|
+
export declare function underArchivedSession(ctx: Context, agent: Agent): boolean;
|
|
30
|
+
//# sourceMappingURL=archived-session-gate.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The controller's admission gate for archived Sessions: an archived
|
|
3
|
+
* Session, or a subagent descendant of one, must not run a model step until
|
|
4
|
+
* it is restored. The work a Session still runs is reported and stopped by
|
|
5
|
+
* its owners — the Agent registry (`turn`), the job registry seam (`job`),
|
|
6
|
+
* the Subagent runtime (`subagent`), and the Schedule plugin (`schedule`) —
|
|
7
|
+
* through the Workspace registry's archive-admission events.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* The gate as a plugin for `ctx.plugin(...)`: it loads once the Agent
|
|
11
|
+
* registry, Session store, and Workspace registry are available and unwinds
|
|
12
|
+
* with the plugin's fiber. A late waking delivery to an archived Session — a
|
|
13
|
+
* subagent settlement, a queued follow-up — proposes a step the gate rejects,
|
|
14
|
+
* which the loop ends as `blocked` without a request; unarchiving lifts the
|
|
15
|
+
* gate for the whole lineage.
|
|
16
|
+
*/
|
|
17
|
+
export const ArchivedSessionGate = {
|
|
18
|
+
name: 'archived-session-gate',
|
|
19
|
+
inject: ['agents', 'sessions', 'workspaceRegistry'],
|
|
20
|
+
apply(ctx) {
|
|
21
|
+
ctx.on('agent/pre-step', (payload, next) => underArchivedSession(ctx, payload.agent)
|
|
22
|
+
? Promise.resolve({ kind: 'reject' })
|
|
23
|
+
: next());
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Whether the Agent's Session, or a Session above it in its subagent lineage,
|
|
28
|
+
* is archived. Lineage follows the durable header fields through
|
|
29
|
+
* subagent-origin Sessions only: a fork of an archived Session is an
|
|
30
|
+
* independent conversation.
|
|
31
|
+
* @param ctx - Host context.
|
|
32
|
+
* @param agent - the Agent proposing a step.
|
|
33
|
+
* @returns whether an archived Session owns the step.
|
|
34
|
+
*/
|
|
35
|
+
export function underArchivedSession(ctx, agent) {
|
|
36
|
+
const archived = ctx.workspaceRegistry.archivedSessionIds;
|
|
37
|
+
let header = agent.session.header;
|
|
38
|
+
const visited = new Set();
|
|
39
|
+
while (!visited.has(header.id)) {
|
|
40
|
+
if (archived.includes(header.id))
|
|
41
|
+
return true;
|
|
42
|
+
visited.add(header.id);
|
|
43
|
+
if (header.origin !== 'subagent' || header.parentSession === undefined)
|
|
44
|
+
return false;
|
|
45
|
+
const parent = ctx.sessions.get(header.parentSession);
|
|
46
|
+
if (parent === undefined)
|
|
47
|
+
return archived.includes(header.parentSession);
|
|
48
|
+
header = parent.header;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=archived-session-gate.js.map
|
|
@@ -127,6 +127,8 @@ export interface ISession {
|
|
|
127
127
|
* turn-jump loader. Repeated calls while a jump is paging lower its shared
|
|
128
128
|
* target and return the in-flight completion; `snapshot.loadingOlder` is
|
|
129
129
|
* the busy signal for the whole jump.
|
|
130
|
+
* Older pages publish together at completion, including successful pages
|
|
131
|
+
* before a later failure; live events remain independently visible.
|
|
130
132
|
* @param seq - durable event seq the window must reach (a turn's `turn/start` seq).
|
|
131
133
|
* @returns completion once covered, exhausted, superseded, or failed soft.
|
|
132
134
|
*/
|
|
@@ -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,43 +75,25 @@ 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.
|
|
51
|
-
* @returns
|
|
88
|
+
* @returns a retained or loaded-catalog address, without retaining a new selection or scope.
|
|
52
89
|
*/
|
|
53
90
|
subagentAddress(id: SessionId): SubagentAddress | undefined;
|
|
54
91
|
/**
|
|
55
|
-
*
|
|
56
|
-
* @param
|
|
57
|
-
* @param open - current menu state.
|
|
58
|
-
*/
|
|
59
|
-
setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void;
|
|
60
|
-
/**
|
|
61
|
-
* Refresh one direct-child catalog.
|
|
62
|
-
* @param parentSessionId - catalog owner.
|
|
92
|
+
* Load all Session projections once per connection; retry an unsuccessful initial read.
|
|
93
|
+
* @param sessionId - Session to inspect without opening its conversation.
|
|
63
94
|
* @returns completion of the current or newly started refresh.
|
|
64
95
|
*/
|
|
65
|
-
|
|
66
|
-
/** Clear the current selection into the no-session view state. */
|
|
67
|
-
clear(): void;
|
|
96
|
+
refreshProjections(sessionId: SessionId): Promise<void>;
|
|
68
97
|
/**
|
|
69
98
|
* Refresh the Host-authoritative Session list.
|
|
70
99
|
* @returns completion of the current or newly started Session-list refresh.
|
|
@@ -82,12 +111,13 @@ export interface ISessions {
|
|
|
82
111
|
hasMore: boolean;
|
|
83
112
|
}>>;
|
|
84
113
|
/**
|
|
85
|
-
* Fork a session from
|
|
86
|
-
* the child is
|
|
87
|
-
* @param opts - source session id, the optional
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* and whether to increment an
|
|
114
|
+
* Fork a session from an exact inclusive prefix of the source; on
|
|
115
|
+
* resolution the child is catalogued and can be explicitly retained.
|
|
116
|
+
* @param opts - source session id, the optional exact inclusive boundary
|
|
117
|
+
* seq (a real event seq the caller already knows; a cut inside an open
|
|
118
|
+
* turn is balanced Host-side with synthetic closers, and omission selects
|
|
119
|
+
* the latest completed-turn prefix), and whether to increment an
|
|
120
|
+
* inherited durable title before resolving.
|
|
91
121
|
* @returns the child session id.
|
|
92
122
|
* @throws when the fork fails, or when a requested child-title rename fails after creation.
|
|
93
123
|
*/
|
|
@@ -97,9 +127,9 @@ export interface ISessions {
|
|
|
97
127
|
increaseTitle?: boolean;
|
|
98
128
|
}): Promise<SessionId>;
|
|
99
129
|
/**
|
|
100
|
-
*
|
|
130
|
+
* Borrow an already-retained Agent-scoped Context without extending its lifetime.
|
|
101
131
|
* @param id - session id.
|
|
102
|
-
* @returns scoped
|
|
132
|
+
* @returns the live scoped Context, or undefined without a retained generation.
|
|
103
133
|
*/
|
|
104
134
|
scope(id: SessionId): AgentContext | undefined;
|
|
105
135
|
/**
|
|
@@ -112,13 +142,13 @@ export interface ISessions {
|
|
|
112
142
|
/**
|
|
113
143
|
* Resolve the session face behind an Agent-scoped context.
|
|
114
144
|
* @param ctx - an Agent-scoped context.
|
|
115
|
-
* @returns the
|
|
145
|
+
* @returns the matching live Session, or undefined for an untagged, foreign, or ended generation.
|
|
116
146
|
*/
|
|
117
147
|
sessionOf(ctx: Context): SessionFace | undefined;
|
|
118
148
|
/**
|
|
119
|
-
*
|
|
149
|
+
* Borrow an already-retained Session binding without extending its lifetime.
|
|
120
150
|
* @param id - session id.
|
|
121
|
-
* @returns binding, or undefined
|
|
151
|
+
* @returns the live binding, or undefined without a retained generation.
|
|
122
152
|
*/
|
|
123
153
|
binding(id: SessionId): SessionBinding | undefined;
|
|
124
154
|
}
|
|
@@ -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;
|
|
@@ -6,14 +6,23 @@ export { createScope, scopeOf } from './scope.ts';
|
|
|
6
6
|
export type { AgentContext, AgentScopeHandle } from './scope.ts';
|
|
7
7
|
export { SessionCreateError, SessionForkError } from './sessions/service.ts';
|
|
8
8
|
export type { SessionBinding, SessionListState, SessionSummary } from './sessions/service.ts';
|
|
9
|
-
export type { SessionListPhase, SessionListSnapshot, SessionSearchResultItem,
|
|
9
|
+
export type { SessionListPhase, SessionListSnapshot, SessionSearchResultItem, SessionProjectionSnapshot, } from './sessions/manager.ts';
|
|
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
|
|
@@ -13,7 +13,10 @@ export interface SessionListEntry {
|
|
|
13
13
|
title?: string;
|
|
14
14
|
updatedAt: number;
|
|
15
15
|
running: boolean;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* New Session presentation and reuse eligibility, reconciled with
|
|
18
|
+
* `sessionListMetadata`; lists hide blank sessions (filtering stays with the consumer).
|
|
19
|
+
*/
|
|
17
20
|
blank: boolean;
|
|
18
21
|
parentSessionId?: SessionId;
|
|
19
22
|
/** Coarse durable origin for navigation filtering; not a continuation capability. */
|
|
@@ -21,8 +24,6 @@ export interface SessionListEntry {
|
|
|
21
24
|
cwd?: string;
|
|
22
25
|
/** Current host-computed projection values for list consumers. */
|
|
23
26
|
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
27
|
/** Lineage indent depth: root = 0; the UI just multiplies by the indent width. */
|
|
27
28
|
depth: number;
|
|
28
29
|
}
|
|
@@ -31,8 +32,7 @@ export interface SessionListEntry {
|
|
|
31
32
|
* follows the established input order; this projection never re-sorts a
|
|
32
33
|
* hydrated list from mutable timestamps.
|
|
33
34
|
* @param summaries - the host's session.list items.
|
|
34
|
-
* @param completed - sessions with a pending completion reminder (manager-owned live fact; absent = false).
|
|
35
35
|
* @returns display rows in render order.
|
|
36
36
|
*/
|
|
37
|
-
export declare function flattenLineage(summaries: readonly TitledSessionSummary[]
|
|
37
|
+
export declare function flattenLineage(summaries: readonly TitledSessionSummary[]): SessionListEntry[];
|
|
38
38
|
//# 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);
|
|
@@ -33,9 +32,9 @@ export function flattenLineage(summaries, completed) {
|
|
|
33
32
|
return;
|
|
34
33
|
}
|
|
35
34
|
visited.add(s.sessionId);
|
|
35
|
+
const { agentAvailable: _agentAvailable, ...row } = s;
|
|
36
36
|
out.push({
|
|
37
|
-
...
|
|
38
|
-
completed: completed?.has(s.sessionId) ?? false,
|
|
37
|
+
...row,
|
|
39
38
|
depth,
|
|
40
39
|
});
|
|
41
40
|
const kids = children.get(s.sessionId);
|