@deepseek-ai/dsh-api-session-controller 0.1.3-alpha.2 → 0.1.5-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 +12 -2
- package/README.zh.md +12 -2
- package/lib/client.js +246 -30
- package/lib/index.js +107 -29
- package/lib/typert.host.js +71 -55
- package/lib/typert.remote-client.js +34 -34
- package/lib/types/agent.js +6 -8
- package/lib/types/client/session-wire-event.d.ts +11 -0
- package/lib/types/client/session-wire-event.js +43 -0
- package/lib/types/client/transport.js +6 -0
- package/lib/types/commands.d.ts +1 -1
- package/lib/types/commands.js +14 -4
- package/lib/types/control.d.ts +0 -1
- package/lib/types/control.js +19 -22
- package/lib/types/index.js +2 -0
- package/lib/types/media-references.d.ts +16 -0
- package/lib/types/media-references.js +77 -0
- package/lib/types/types.d.ts +15 -7
- package/package.json +71 -64
|
@@ -99,6 +99,10 @@ const _deepseek_ai_dsh_api_session_controller_session_control_result$schema = z.
|
|
|
99
99
|
'projections': z.record(z.intersection(z.string(), z.unknown()), z.object({
|
|
100
100
|
'asOfSeq': z.number().readonly(),
|
|
101
101
|
'values': z.intersection(z.object({
|
|
102
|
+
'inbox': z.object({
|
|
103
|
+
'next-turn': z.array(z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema4)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema4))])).readonly(),
|
|
104
|
+
'next-step': z.array(z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema4)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema4))])).readonly(),
|
|
105
|
+
}).optional(),
|
|
102
106
|
'agentPreset': z.union([z.literal(null), z.string()]).optional(),
|
|
103
107
|
'title': z.union([z.literal(null), z.string()]).optional(),
|
|
104
108
|
'todos': z.union([z.literal(null), z.array(z.object({
|
|
@@ -227,12 +231,8 @@ const _deepseek_ai_dsh_api_session_controller_session_follow_result$schema = z.u
|
|
|
227
231
|
'time': z.number().readonly(),
|
|
228
232
|
'data': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))]).readonly(),
|
|
229
233
|
'ignorable': z.literal(true).readonly().optional(),
|
|
230
|
-
'sourceEventSeqs': z.
|
|
231
|
-
'surfaceOp': z.union([z.literal(
|
|
232
|
-
'op': z.literal("replace").readonly(),
|
|
233
|
-
'start': z.number().readonly(),
|
|
234
|
-
'end': z.number().readonly(),
|
|
235
|
-
})]).readonly().optional(),
|
|
234
|
+
'sourceEventSeqs': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))]).readonly().optional(),
|
|
235
|
+
'surfaceOp': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))]).readonly().optional(),
|
|
236
236
|
}).readonly(),
|
|
237
237
|
}), z.object({
|
|
238
238
|
'type': z.literal("snapshot").readonly(),
|
|
@@ -256,18 +256,18 @@ const _deepseek_ai_dsh_api_session_controller_session_follow_result$schema = z.u
|
|
|
256
256
|
'time': z.number().readonly(),
|
|
257
257
|
'data': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))]).readonly(),
|
|
258
258
|
'ignorable': z.literal(true).readonly().optional(),
|
|
259
|
-
'sourceEventSeqs': z.
|
|
260
|
-
'surfaceOp': z.union([z.literal(
|
|
261
|
-
'op': z.literal("replace").readonly(),
|
|
262
|
-
'start': z.number().readonly(),
|
|
263
|
-
'end': z.number().readonly(),
|
|
264
|
-
})]).readonly().optional(),
|
|
259
|
+
'sourceEventSeqs': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))]).readonly().optional(),
|
|
260
|
+
'surfaceOp': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))]).readonly().optional(),
|
|
265
261
|
}).readonly(),
|
|
266
262
|
})).readonly(),
|
|
267
263
|
'hasMore': z.boolean().readonly(),
|
|
268
264
|
'projections': z.object({
|
|
269
265
|
'asOfSeq': z.number().readonly(),
|
|
270
266
|
'values': z.intersection(z.object({
|
|
267
|
+
'inbox': z.object({
|
|
268
|
+
'next-turn': z.array(z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))])).readonly(),
|
|
269
|
+
'next-step': z.array(z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema3)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema3))])).readonly(),
|
|
270
|
+
}).optional(),
|
|
271
271
|
'agentPreset': z.union([z.literal(null), z.string()]).optional(),
|
|
272
272
|
'title': z.union([z.literal(null), z.string()]).optional(),
|
|
273
273
|
'todos': z.union([z.literal(null), z.array(z.object({
|
|
@@ -395,6 +395,10 @@ const _deepseek_ai_dsh_api_session_controller_session_list_result$schema = z.obj
|
|
|
395
395
|
'projections': z.object({
|
|
396
396
|
'asOfSeq': z.number().readonly(),
|
|
397
397
|
'values': z.intersection(z.object({
|
|
398
|
+
'inbox': z.object({
|
|
399
|
+
'next-turn': z.array(z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema))])).readonly(),
|
|
400
|
+
'next-step': z.array(z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema))])).readonly(),
|
|
401
|
+
}).optional(),
|
|
398
402
|
'agentPreset': z.union([z.literal(null), z.string()]).optional(),
|
|
399
403
|
'title': z.union([z.literal(null), z.string()]).optional(),
|
|
400
404
|
'todos': z.union([z.literal(null), z.array(z.object({
|
|
@@ -520,12 +524,8 @@ const _deepseek_ai_dsh_api_session_controller_session_page_result$schema = z.obj
|
|
|
520
524
|
'time': z.number().readonly(),
|
|
521
525
|
'data': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema2)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema2))]).readonly(),
|
|
522
526
|
'ignorable': z.literal(true).readonly().optional(),
|
|
523
|
-
'sourceEventSeqs': z.
|
|
524
|
-
'surfaceOp': z.union([z.literal(
|
|
525
|
-
'op': z.literal("replace").readonly(),
|
|
526
|
-
'start': z.number().readonly(),
|
|
527
|
-
'end': z.number().readonly(),
|
|
528
|
-
})]).readonly().optional(),
|
|
527
|
+
'sourceEventSeqs': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema2)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema2))]).readonly().optional(),
|
|
528
|
+
'surfaceOp': z.union([z.literal(null), z.string(), z.number(), z.literal(false), z.literal(true), z.array(z.lazy(() => JsonValueRemoteCodec$schema2)), z.record(z.string(), z.lazy(() => JsonValueRemoteCodec$schema2))]).readonly().optional(),
|
|
529
529
|
}).readonly(),
|
|
530
530
|
})).readonly(),
|
|
531
531
|
'hasMore': z.boolean().readonly(),
|
|
@@ -713,7 +713,7 @@ export const TYPERT_REMOTE = {
|
|
|
713
713
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionAttachmentValue',
|
|
714
714
|
schema: _deepseek_ai_dsh_api_session_controller_session_attachment_result$schema,
|
|
715
715
|
},
|
|
716
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
716
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":342,"column":3},
|
|
717
717
|
},
|
|
718
718
|
{
|
|
719
719
|
id: '@deepseek-ai/dsh-api-session-controller#session/cancel',
|
|
@@ -738,7 +738,7 @@ export const TYPERT_REMOTE = {
|
|
|
738
738
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionCancelValue',
|
|
739
739
|
schema: _deepseek_ai_dsh_api_session_controller_session_cancel_result$schema,
|
|
740
740
|
},
|
|
741
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
741
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":362,"column":3},
|
|
742
742
|
},
|
|
743
743
|
{
|
|
744
744
|
id: '@deepseek-ai/dsh-api-session-controller#session/canOpenWorkspacePath',
|
|
@@ -753,7 +753,7 @@ export const TYPERT_REMOTE = {
|
|
|
753
753
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller#session/canOpenWorkspacePath:result',
|
|
754
754
|
schema: _deepseek_ai_dsh_api_session_controller_session_canOpenWorkspacePath_result$schema,
|
|
755
755
|
},
|
|
756
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
756
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":267,"column":3},
|
|
757
757
|
},
|
|
758
758
|
{
|
|
759
759
|
id: '@deepseek-ai/dsh-api-session-controller#session/control',
|
|
@@ -770,7 +770,7 @@ export const TYPERT_REMOTE = {
|
|
|
770
770
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionControlFrame',
|
|
771
771
|
schema: _deepseek_ai_dsh_api_session_controller_session_control_result$schema,
|
|
772
772
|
},
|
|
773
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
773
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":395,"column":3},
|
|
774
774
|
},
|
|
775
775
|
{
|
|
776
776
|
id: '@deepseek-ai/dsh-api-session-controller#session/create',
|
|
@@ -795,7 +795,7 @@ export const TYPERT_REMOTE = {
|
|
|
795
795
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionCreateValue',
|
|
796
796
|
schema: _deepseek_ai_dsh_api_session_controller_session_create_result$schema,
|
|
797
797
|
},
|
|
798
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
798
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":239,"column":3},
|
|
799
799
|
},
|
|
800
800
|
{
|
|
801
801
|
id: '@deepseek-ai/dsh-api-session-controller#session/follow',
|
|
@@ -822,7 +822,7 @@ export const TYPERT_REMOTE = {
|
|
|
822
822
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionFollowFrame',
|
|
823
823
|
schema: _deepseek_ai_dsh_api_session_controller_session_follow_result$schema,
|
|
824
824
|
},
|
|
825
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
825
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":385,"column":3},
|
|
826
826
|
},
|
|
827
827
|
{
|
|
828
828
|
id: '@deepseek-ai/dsh-api-session-controller#session/fork',
|
|
@@ -847,7 +847,7 @@ export const TYPERT_REMOTE = {
|
|
|
847
847
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionForkValue',
|
|
848
848
|
schema: _deepseek_ai_dsh_api_session_controller_session_fork_result$schema,
|
|
849
849
|
},
|
|
850
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
850
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":320,"column":3},
|
|
851
851
|
},
|
|
852
852
|
{
|
|
853
853
|
id: '@deepseek-ai/dsh-api-session-controller#session/list',
|
|
@@ -873,7 +873,7 @@ export const TYPERT_REMOTE = {
|
|
|
873
873
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionListValue',
|
|
874
874
|
schema: _deepseek_ai_dsh_api_session_controller_session_list_result$schema,
|
|
875
875
|
},
|
|
876
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
876
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":218,"column":9},
|
|
877
877
|
},
|
|
878
878
|
{
|
|
879
879
|
id: '@deepseek-ai/dsh-api-session-controller#session/modelCatalog',
|
|
@@ -888,7 +888,7 @@ export const TYPERT_REMOTE = {
|
|
|
888
888
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#ModelCatalog',
|
|
889
889
|
schema: _deepseek_ai_dsh_api_session_controller_session_modelCatalog_result$schema,
|
|
890
890
|
},
|
|
891
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
891
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":258,"column":3},
|
|
892
892
|
},
|
|
893
893
|
{
|
|
894
894
|
id: '@deepseek-ai/dsh-api-session-controller#session/openWorkspacePath',
|
|
@@ -914,7 +914,7 @@ export const TYPERT_REMOTE = {
|
|
|
914
914
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionOpenWorkspacePathValue',
|
|
915
915
|
schema: _deepseek_ai_dsh_api_session_controller_session_openWorkspacePath_result$schema,
|
|
916
916
|
},
|
|
917
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
917
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":279,"column":9},
|
|
918
918
|
},
|
|
919
919
|
{
|
|
920
920
|
id: '@deepseek-ai/dsh-api-session-controller#session/page',
|
|
@@ -940,7 +940,7 @@ export const TYPERT_REMOTE = {
|
|
|
940
940
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionPage',
|
|
941
941
|
schema: _deepseek_ai_dsh_api_session_controller_session_page_result$schema,
|
|
942
942
|
},
|
|
943
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
943
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":373,"column":3},
|
|
944
944
|
},
|
|
945
945
|
{
|
|
946
946
|
id: '@deepseek-ai/dsh-api-session-controller#session/prompt',
|
|
@@ -966,7 +966,7 @@ export const TYPERT_REMOTE = {
|
|
|
966
966
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionPromptValue',
|
|
967
967
|
schema: _deepseek_ai_dsh_api_session_controller_session_prompt_result$schema,
|
|
968
968
|
},
|
|
969
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
969
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":331,"column":3},
|
|
970
970
|
},
|
|
971
971
|
{
|
|
972
972
|
id: '@deepseek-ai/dsh-api-session-controller#session/rename',
|
|
@@ -991,7 +991,7 @@ export const TYPERT_REMOTE = {
|
|
|
991
991
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionRenameValue',
|
|
992
992
|
schema: _deepseek_ai_dsh_api_session_controller_session_rename_result$schema,
|
|
993
993
|
},
|
|
994
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
994
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":310,"column":3},
|
|
995
995
|
},
|
|
996
996
|
{
|
|
997
997
|
id: '@deepseek-ai/dsh-api-session-controller#session/search',
|
|
@@ -1017,7 +1017,7 @@ export const TYPERT_REMOTE = {
|
|
|
1017
1017
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionSearchValue',
|
|
1018
1018
|
schema: _deepseek_ai_dsh_api_session_controller_session_search_result$schema,
|
|
1019
1019
|
},
|
|
1020
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
1020
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":229,"column":3},
|
|
1021
1021
|
},
|
|
1022
1022
|
{
|
|
1023
1023
|
id: '@deepseek-ai/dsh-api-session-controller#session/selectModel',
|
|
@@ -1042,7 +1042,7 @@ export const TYPERT_REMOTE = {
|
|
|
1042
1042
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionSelectModelValue',
|
|
1043
1043
|
schema: _deepseek_ai_dsh_api_session_controller_session_selectModel_result$schema,
|
|
1044
1044
|
},
|
|
1045
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
1045
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":249,"column":3},
|
|
1046
1046
|
},
|
|
1047
1047
|
{
|
|
1048
1048
|
id: '@deepseek-ai/dsh-api-session-controller#session/updateQueue',
|
|
@@ -1067,7 +1067,7 @@ export const TYPERT_REMOTE = {
|
|
|
1067
1067
|
typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionUpdateQueueValue',
|
|
1068
1068
|
schema: _deepseek_ai_dsh_api_session_controller_session_updateQueue_result$schema,
|
|
1069
1069
|
},
|
|
1070
|
-
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":
|
|
1070
|
+
sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":352,"column":3},
|
|
1071
1071
|
},
|
|
1072
1072
|
{
|
|
1073
1073
|
id: '@deepseek-ai/dsh-api-session-controller#skills/list',
|
package/lib/types/agent.js
CHANGED
|
@@ -380,13 +380,14 @@ export class ApiSessionAgentController {
|
|
|
380
380
|
*/
|
|
381
381
|
async composeAgent(presetId) {
|
|
382
382
|
const presets = this.ctx.get('agentPresets');
|
|
383
|
-
if (presets === undefined)
|
|
384
|
-
return { setup: (
|
|
383
|
+
if (presets === undefined) {
|
|
384
|
+
return { setup: (_agentCtx, agent) => { this.installSelection(agent); } };
|
|
385
|
+
}
|
|
385
386
|
const resolvedId = (await presets.resolve(presetId)).id;
|
|
386
387
|
return {
|
|
387
388
|
agentPreset: resolvedId,
|
|
388
|
-
setup: async (agentCtx) => {
|
|
389
|
-
this.installSelection(
|
|
389
|
+
setup: async (agentCtx, agent) => {
|
|
390
|
+
this.installSelection(agent);
|
|
390
391
|
await presets.mount(agentCtx, resolvedId);
|
|
391
392
|
},
|
|
392
393
|
};
|
|
@@ -506,10 +507,7 @@ export class ApiSessionAgentController {
|
|
|
506
507
|
const { provider, model } = this.ctx.agentDefaultModel.currentSelection();
|
|
507
508
|
return { provider, model };
|
|
508
509
|
}
|
|
509
|
-
installSelection(
|
|
510
|
-
const agent = agentCtx.agent;
|
|
511
|
-
if (agent === undefined)
|
|
512
|
-
throw new Error('api-session: Agent setup has no scoped Agent');
|
|
510
|
+
installSelection(agent) {
|
|
513
511
|
this.selectionFor(agent);
|
|
514
512
|
}
|
|
515
513
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Event-local acceptance for raw Session journal responses; payloads remain owner-defined JSON. */
|
|
2
|
+
import type { SessionWireEvent } from '../types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Reject non-current event envelopes without stripping or normalizing wire fields.
|
|
5
|
+
* Range membership and source existence require the durable log and remain Host-owned.
|
|
6
|
+
* @param value - one event received in a follow frame or history page.
|
|
7
|
+
* @returns nothing after narrowing the accepted event envelope.
|
|
8
|
+
* @throws when the envelope or current event-local metadata is invalid.
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertSessionWireEvent(value: unknown): asserts value is SessionWireEvent;
|
|
11
|
+
//# sourceMappingURL=session-wire-event.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Event-local acceptance for raw Session journal responses; payloads remain owner-defined JSON. */
|
|
2
|
+
import { validateSessionEventData, validateSurfaceMetadata } from '@deepseek-ai/dsh-session/surface';
|
|
3
|
+
/**
|
|
4
|
+
* Reject non-current event envelopes without stripping or normalizing wire fields.
|
|
5
|
+
* Range membership and source existence require the durable log and remain Host-owned.
|
|
6
|
+
* @param value - one event received in a follow frame or history page.
|
|
7
|
+
* @returns nothing after narrowing the accepted event envelope.
|
|
8
|
+
* @throws when the envelope or current event-local metadata is invalid.
|
|
9
|
+
*/
|
|
10
|
+
export function assertSessionWireEvent(value) {
|
|
11
|
+
const subject = 'session wire event';
|
|
12
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
13
|
+
throw new Error(`${subject} must be an object`);
|
|
14
|
+
}
|
|
15
|
+
const event = value;
|
|
16
|
+
for (const key of Object.keys(event)) {
|
|
17
|
+
switch (key) {
|
|
18
|
+
case 'type':
|
|
19
|
+
case 'seq':
|
|
20
|
+
case 'time':
|
|
21
|
+
case 'data':
|
|
22
|
+
case 'ignorable':
|
|
23
|
+
case 'surfaceOp':
|
|
24
|
+
case 'sourceEventSeqs':
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
throw new Error(`${subject} has unexpected field ${key}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const seq = event['seq'];
|
|
31
|
+
if (typeof event['type'] !== 'string'
|
|
32
|
+
|| typeof seq !== 'number' || !Number.isSafeInteger(seq) || seq < 0 || Object.is(seq, -0)
|
|
33
|
+
|| typeof event['time'] !== 'number' || !Number.isSafeInteger(event['time'])
|
|
34
|
+
|| !Object.hasOwn(event, 'data') || event['data'] === undefined
|
|
35
|
+
|| (Object.hasOwn(event, 'ignorable') && event['ignorable'] !== true)) {
|
|
36
|
+
throw new Error(`${subject} has an invalid envelope`);
|
|
37
|
+
}
|
|
38
|
+
// Event names and payloads are merge-extensible; only event-local owner rules run here.
|
|
39
|
+
const current = event;
|
|
40
|
+
validateSurfaceMetadata(current);
|
|
41
|
+
validateSessionEventData(current, subject);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=session-wire-event.js.map
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
|
|
3
3
|
import { RemoteJournalStream, RemoteSnapshotStream, RemoteStreamCarrierError, } from '@deepseek-ai/dsh-api-gateway/client';
|
|
4
4
|
import { historyEntries, historyRecordFirstSeq, historyRecordLastSeq, } from "./sessions/history-records.js";
|
|
5
|
+
import { assertSessionWireEvent } from "./session-wire-event.js";
|
|
5
6
|
export { SESSION_SEARCH_RESULT_LIMIT, SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS, } from "../types.js";
|
|
6
7
|
function toSessionJournalChange(change) {
|
|
7
8
|
switch (change.type) {
|
|
@@ -78,6 +79,8 @@ export class SessionEventStream extends RemoteJournalStream {
|
|
|
78
79
|
...(request.maxMessages === undefined ? {} : { maxMessages: request.maxMessages }),
|
|
79
80
|
}, signal)) {
|
|
80
81
|
if (frame.type === 'snapshot') {
|
|
82
|
+
for (const record of frame.records)
|
|
83
|
+
assertSessionWireEvent(record.event);
|
|
81
84
|
if (frame.assistantStream === undefined) {
|
|
82
85
|
throw new RemoteError('gateway/internal', 'session assistant stream omitted its opted-in opening baseline', {});
|
|
83
86
|
}
|
|
@@ -103,6 +106,7 @@ export class SessionEventStream extends RemoteJournalStream {
|
|
|
103
106
|
yield { type: 'notification', notification: frame.frame };
|
|
104
107
|
continue;
|
|
105
108
|
}
|
|
109
|
+
assertSessionWireEvent(frame.event);
|
|
106
110
|
yield { type: 'entry', entry: frame };
|
|
107
111
|
}
|
|
108
112
|
}
|
|
@@ -111,6 +115,8 @@ export class SessionEventStream extends RemoteJournalStream {
|
|
|
111
115
|
const result = await this.remote.session.page({ address: this.address, throughSeq, ...request }, signal);
|
|
112
116
|
if (!result.ok)
|
|
113
117
|
throw result.error;
|
|
118
|
+
for (const record of result.value.records)
|
|
119
|
+
assertSessionWireEvent(record.event);
|
|
114
120
|
return result.value;
|
|
115
121
|
}
|
|
116
122
|
/** @inheritdoc */
|
package/lib/types/commands.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export declare class SessionCommandController {
|
|
|
38
38
|
*/
|
|
39
39
|
fork(request: SessionForkRequest): Promise<SessionForkValue>;
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* Reject empty content, then admit one prompt after Agent and attachment validation.
|
|
42
42
|
* @param request - Session identity, prompt content, source metadata, and delivery mode.
|
|
43
43
|
* @returns acknowledgement that the Agent accepted the prompt.
|
|
44
44
|
*/
|
package/lib/types/commands.js
CHANGED
|
@@ -62,6 +62,9 @@ import { canonicalClientTimeZone } from '@deepseek-ai/dsh-util-time';
|
|
|
62
62
|
import { assertNever } from '@deepseek-ai/dsh-util-values';
|
|
63
63
|
import { RemoteError, remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol';
|
|
64
64
|
import { ApiSessionCwdConflict, ApiSessionNotFound, ApiSessionPresetConflict, ApiSessionSubagentOwnership, apiSessionSubagentOwnershipError, hasApiSessionSubagentOwner, inspectApiSession, } from "./agent.js";
|
|
65
|
+
function hasPromptContent(content) {
|
|
66
|
+
return content.some(part => part.type !== 'text' || part.text.trim().length > 0);
|
|
67
|
+
}
|
|
65
68
|
/** Implements Session business commands delegated by the Session Controller Remote service. */
|
|
66
69
|
export class SessionCommandController {
|
|
67
70
|
ctx;
|
|
@@ -271,11 +274,14 @@ export class SessionCommandController {
|
|
|
271
274
|
}
|
|
272
275
|
}
|
|
273
276
|
/**
|
|
274
|
-
*
|
|
277
|
+
* Reject empty content, then admit one prompt after Agent and attachment validation.
|
|
275
278
|
* @param request - Session identity, prompt content, source metadata, and delivery mode.
|
|
276
279
|
* @returns acknowledgement that the Agent accepted the prompt.
|
|
277
280
|
*/
|
|
278
281
|
async prompt(request) {
|
|
282
|
+
if (!hasPromptContent(request.content)) {
|
|
283
|
+
throw new RemoteError('gateway/bad-request', 'prompt content must include non-whitespace text or an attachment', {});
|
|
284
|
+
}
|
|
279
285
|
const clientTimeZone = request.clientTimeZone === undefined
|
|
280
286
|
? undefined
|
|
281
287
|
: canonicalClientTimeZone(request.clientTimeZone);
|
|
@@ -379,9 +385,13 @@ export class SessionCommandController {
|
|
|
379
385
|
* @returns acknowledgement that the queue mutation was applied.
|
|
380
386
|
*/
|
|
381
387
|
updateQueue(request) {
|
|
382
|
-
if (request.action.kind === 'edit'
|
|
383
|
-
|
|
384
|
-
|
|
388
|
+
if (request.action.kind === 'edit') {
|
|
389
|
+
if (request.action.content.some(block => block.type !== 'text')) {
|
|
390
|
+
throw new RemoteError('session/attachment-invalid', 'queue edits accept text content only', { reason: 'QUEUE_EDIT_NON_TEXT' });
|
|
391
|
+
}
|
|
392
|
+
if (!hasPromptContent(request.action.content)) {
|
|
393
|
+
throw new RemoteError('gateway/bad-request', 'queue edit content must include non-whitespace text', {});
|
|
394
|
+
}
|
|
385
395
|
}
|
|
386
396
|
const agent = this.ctx.agents.get(request.sessionId);
|
|
387
397
|
if (agent === undefined) {
|
package/lib/types/control.d.ts
CHANGED
|
@@ -15,7 +15,6 @@ export declare class SessionControlController {
|
|
|
15
15
|
control(signal: AbortSignal): AsyncIterable<SessionControlFrame>;
|
|
16
16
|
private baseline;
|
|
17
17
|
private projectionBaseline;
|
|
18
|
-
private onSessionEvent;
|
|
19
18
|
private onJobsChanged;
|
|
20
19
|
private jobsFor;
|
|
21
20
|
private broadcast;
|
package/lib/types/control.js
CHANGED
|
@@ -7,7 +7,6 @@ export class SessionControlController {
|
|
|
7
7
|
/** @param ctx - Host context carrying live Agent, projection, and jobs services. */
|
|
8
8
|
constructor(ctx) {
|
|
9
9
|
this.ctx = ctx;
|
|
10
|
-
ctx.on('session/event', (session, event) => { this.onSessionEvent(session, event); });
|
|
11
10
|
ctx.sessionProjections.onChanged((session, key, value, seq) => {
|
|
12
11
|
this.broadcast({
|
|
13
12
|
type: 'projection',
|
|
@@ -16,6 +15,16 @@ export class SessionControlController {
|
|
|
16
15
|
value: value,
|
|
17
16
|
seq,
|
|
18
17
|
});
|
|
18
|
+
if (key !== 'inbox')
|
|
19
|
+
return;
|
|
20
|
+
const agent = this.ctx.agents.get(session.id);
|
|
21
|
+
if (agent?.session !== session)
|
|
22
|
+
return;
|
|
23
|
+
this.broadcast({
|
|
24
|
+
type: 'queue',
|
|
25
|
+
sessionId: session.id,
|
|
26
|
+
items: queueItemsFromInbox(value),
|
|
27
|
+
});
|
|
19
28
|
});
|
|
20
29
|
ctx.inject(['jobs'], (jobsCtx) => {
|
|
21
30
|
jobsCtx.jobs.onJobsChanged((owner) => { this.onJobsChanged(owner); });
|
|
@@ -76,18 +85,6 @@ export class SessionControlController {
|
|
|
76
85
|
}
|
|
77
86
|
return blocks;
|
|
78
87
|
}
|
|
79
|
-
onSessionEvent(session, event) {
|
|
80
|
-
if (event.type !== 'agent/inbox/spliced')
|
|
81
|
-
return;
|
|
82
|
-
const agent = this.ctx.agents.get(session.id);
|
|
83
|
-
if (agent?.session !== session)
|
|
84
|
-
return;
|
|
85
|
-
this.broadcast({
|
|
86
|
-
type: 'queue',
|
|
87
|
-
sessionId: session.id,
|
|
88
|
-
items: queueItems(agent, event.data),
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
88
|
onJobsChanged(owner) {
|
|
92
89
|
if (owner !== undefined) {
|
|
93
90
|
this.broadcast({ type: 'jobs', sessionId: owner.id, jobs: this.jobsFor(owner) });
|
|
@@ -151,21 +148,21 @@ class ControlQueue {
|
|
|
151
148
|
}
|
|
152
149
|
}
|
|
153
150
|
}
|
|
154
|
-
function queueItems(agent
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
151
|
+
function queueItems(agent) {
|
|
152
|
+
return queueItemsFromInbox({
|
|
153
|
+
'next-turn': agent.inbox.nextTurn,
|
|
154
|
+
'next-step': agent.inbox.nextStep,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function queueItemsFromInbox(inbox) {
|
|
161
158
|
return [
|
|
162
|
-
...
|
|
159
|
+
...inbox['next-turn'].map(message => ({
|
|
163
160
|
id: message.id,
|
|
164
161
|
placement: 'queued',
|
|
165
162
|
...promptRpcId(message),
|
|
166
163
|
message: { id: message.id, content: message.content },
|
|
167
164
|
})),
|
|
168
|
-
...
|
|
165
|
+
...inbox['next-step'].map(message => ({
|
|
169
166
|
id: message.id,
|
|
170
167
|
placement: message.source.kind === 'user' ? 'steering' : 'context',
|
|
171
168
|
...promptRpcId(message),
|
package/lib/types/index.js
CHANGED
|
@@ -98,6 +98,7 @@ import { ApiSessionList } from "./list.js";
|
|
|
98
98
|
import { buildModelCatalog } from "./catalog.js";
|
|
99
99
|
import { installModelSelectionProjection } from "./model-selection-projection.js";
|
|
100
100
|
import { SessionSkillCatalog } from "./skill-catalog.js";
|
|
101
|
+
import { SessionMediaReferences } from "./media-references.js";
|
|
101
102
|
export { ApiSessionNotFound } from "./agent.js";
|
|
102
103
|
export { SessionFileReferences } from "./file-references.js";
|
|
103
104
|
export { SessionSkillCatalog } from "./skill-catalog.js";
|
|
@@ -209,6 +210,7 @@ let SessionController = (() => {
|
|
|
209
210
|
this.canOpenPath = internals.canOpenPath
|
|
210
211
|
?? (() => config.nativeOpen ?? (internals.openPath !== undefined || canOpenNativePath()));
|
|
211
212
|
ctx.plugin(SessionFileReferences);
|
|
213
|
+
ctx.plugin(SessionMediaReferences);
|
|
212
214
|
ctx.plugin(SessionSkillCatalog);
|
|
213
215
|
ctx.on('session/created', (session) => {
|
|
214
216
|
ctx.emit('api-session/added', this.listState.summaryFor(session));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authenticated GET/HEAD /api/file reads bounded file responses through
|
|
3
|
+
* the composed filesystem provider. Paths and MIME types do not restrict access;
|
|
4
|
+
* the connection service authenticates requests before this handler.
|
|
5
|
+
* @module @deepseek-ai/dsh-api-session-controller/media-references
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
/**
|
|
9
|
+
* File-display contribution. The connection service supplies authentication;
|
|
10
|
+
* `ctx.fs` supplies the execution world's paths, reads, and access policy.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SessionMediaReferences: {
|
|
13
|
+
inject: string[];
|
|
14
|
+
apply(ctx: Context): void;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=media-references.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authenticated GET/HEAD /api/file reads bounded file responses through
|
|
3
|
+
* the composed filesystem provider. Paths and MIME types do not restrict access;
|
|
4
|
+
* the connection service authenticates requests before this handler.
|
|
5
|
+
* @module @deepseek-ai/dsh-api-session-controller/media-references
|
|
6
|
+
*/
|
|
7
|
+
import { isAbsolute } from 'node:path';
|
|
8
|
+
import { FsError } from '@deepseek-ai/dsh-fs';
|
|
9
|
+
import mime from 'mime-types';
|
|
10
|
+
const BASE_HEADERS = {
|
|
11
|
+
'Cache-Control': 'private, no-store',
|
|
12
|
+
'X-Content-Type-Options': 'nosniff',
|
|
13
|
+
// HTML and SVG files may be opened directly on the authenticated API origin.
|
|
14
|
+
'Content-Security-Policy': "sandbox; default-src 'none'",
|
|
15
|
+
};
|
|
16
|
+
async function serveFile(request, fs, maxBytes) {
|
|
17
|
+
const fail = (status, text) => new Response(request.method === 'HEAD' ? null : text, { status, headers: BASE_HEADERS });
|
|
18
|
+
const path = new URL(request.url).searchParams.get('path');
|
|
19
|
+
if (path === null || path.length === 0)
|
|
20
|
+
return fail(400, 'missing path');
|
|
21
|
+
if (path.includes('\0') || !isAbsolute(path))
|
|
22
|
+
return fail(400, 'absolute path required');
|
|
23
|
+
try {
|
|
24
|
+
const target = await fs.resolve(path, { signal: request.signal });
|
|
25
|
+
const mediaType = mime.lookup(target.displayPath) || 'application/octet-stream';
|
|
26
|
+
const headers = {
|
|
27
|
+
...BASE_HEADERS,
|
|
28
|
+
'Content-Type': mediaType,
|
|
29
|
+
};
|
|
30
|
+
if (request.method === 'HEAD') {
|
|
31
|
+
const info = await fs.stat(target, request.signal);
|
|
32
|
+
if (info === undefined)
|
|
33
|
+
return fail(404, 'not found');
|
|
34
|
+
if (info.type !== 'file')
|
|
35
|
+
return fail(403, 'not a regular file');
|
|
36
|
+
if (info.size !== undefined) {
|
|
37
|
+
if (info.size > maxBytes)
|
|
38
|
+
return fail(413, 'file exceeds byte limit');
|
|
39
|
+
headers['Content-Length'] = String(info.size);
|
|
40
|
+
}
|
|
41
|
+
return new Response(null, { headers });
|
|
42
|
+
}
|
|
43
|
+
const bytes = await fs.readBytes(target, request.signal, maxBytes);
|
|
44
|
+
headers['Content-Length'] = String(bytes.byteLength);
|
|
45
|
+
return new Response(bytes.slice(), { headers });
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (!(error instanceof FsError))
|
|
49
|
+
throw error;
|
|
50
|
+
const statuses = {
|
|
51
|
+
FS_NOT_FOUND: 404,
|
|
52
|
+
FS_NOT_REGULAR_FILE: 403,
|
|
53
|
+
FS_PERMISSION_DENIED: 403,
|
|
54
|
+
FS_SANDBOX_DENIED: 403,
|
|
55
|
+
FS_TOO_LARGE: 413,
|
|
56
|
+
FS_ABORTED: 499,
|
|
57
|
+
};
|
|
58
|
+
return fail(statuses[error.code] ?? 500, error.code);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* File-display contribution. The connection service supplies authentication;
|
|
63
|
+
* `ctx.fs` supplies the execution world's paths, reads, and access policy.
|
|
64
|
+
*/
|
|
65
|
+
export const SessionMediaReferences = {
|
|
66
|
+
inject: ['connection', 'fs', 'attachments'],
|
|
67
|
+
apply(ctx) {
|
|
68
|
+
const maxBytes = ctx.attachments.imageLimits.maxImageBytes;
|
|
69
|
+
ctx.effect(() => ctx.connection.fetch.register({
|
|
70
|
+
path: '/api/file',
|
|
71
|
+
methods: ['GET', 'HEAD'],
|
|
72
|
+
requestBody: 'buffered',
|
|
73
|
+
fetch: request => serveFile(request, ctx.fs, maxBytes),
|
|
74
|
+
}), 'session-controller: /api/file');
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=media-references.js.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -134,6 +134,7 @@ export interface ModelCatalog {
|
|
|
134
134
|
/** One client-requested mutation of a still-pending queue item. */
|
|
135
135
|
export type QueueAction = {
|
|
136
136
|
readonly kind: 'edit';
|
|
137
|
+
/** Non-empty text-only replacement content. */
|
|
137
138
|
readonly content: readonly ContentBlock[];
|
|
138
139
|
} | {
|
|
139
140
|
readonly kind: 'remove';
|
|
@@ -293,6 +294,7 @@ export interface SessionPromptRequest {
|
|
|
293
294
|
readonly requestId: SessionRequestId;
|
|
294
295
|
readonly sessionId: SessionId;
|
|
295
296
|
readonly mode: 'queue' | 'steer';
|
|
297
|
+
/** At least one non-whitespace text part or attachment. */
|
|
296
298
|
readonly content: readonly PromptContentPart[];
|
|
297
299
|
readonly clientTimeZone?: string;
|
|
298
300
|
}
|
|
@@ -377,23 +379,29 @@ export interface SessionWireHeader {
|
|
|
377
379
|
readonly delegationDepth?: number;
|
|
378
380
|
readonly agentPreset?: string;
|
|
379
381
|
}
|
|
380
|
-
/** Browser wire
|
|
382
|
+
/** Browser wire surface operation; replacement endpoints are earlier event seqs in surface order. */
|
|
381
383
|
export type SessionWireSurfaceOp = 'append' | {
|
|
382
384
|
readonly op: 'replace';
|
|
383
|
-
readonly
|
|
384
|
-
readonly
|
|
385
|
+
readonly startSeq: number;
|
|
386
|
+
readonly endSeq: number;
|
|
385
387
|
};
|
|
386
|
-
/** One history-page record
|
|
388
|
+
/** One history-page record with compact Assistant streams embedded inside events. */
|
|
387
389
|
export type SessionHistoryRecord = SessionEventEntry;
|
|
388
|
-
/**
|
|
390
|
+
/**
|
|
391
|
+
* Exact Session event envelope accepted by the Client journal adapter.
|
|
392
|
+
* Surface events require surfaceOp; only non-Assistant surface events may cite earlier sources.
|
|
393
|
+
* Durable readers own recognition of merge-extensible event names.
|
|
394
|
+
*/
|
|
389
395
|
export interface SessionWireEvent {
|
|
390
396
|
readonly type: string;
|
|
391
397
|
readonly seq: number;
|
|
392
398
|
readonly time: number;
|
|
393
399
|
readonly data: JsonValue;
|
|
394
400
|
readonly ignorable?: true;
|
|
395
|
-
|
|
396
|
-
readonly
|
|
401
|
+
/** Earlier sources on current surface events; opaque JSON on unknown ignorable events. */
|
|
402
|
+
readonly sourceEventSeqs?: JsonValue;
|
|
403
|
+
/** Canonical placement on current surface events; opaque JSON on unknown ignorable events. */
|
|
404
|
+
readonly surfaceOp?: JsonValue;
|
|
397
405
|
}
|
|
398
406
|
/** One message-aligned backwards-history request. */
|
|
399
407
|
export interface SessionPageRequest {
|