@deepseek-ai/dsh-session 0.1.5-rc.2 → 0.1.6-alpha.2
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 +8 -6
- package/README.zh.md +14 -12
- package/lib/index.js +130 -42
- package/lib/types/index.d.ts +34 -13
- package/lib/types/index.js +56 -20
- package/lib/types/invariant.js +1 -0
- package/lib/types/known-event-types.d.ts +2 -0
- package/lib/types/known-event-types.js +6 -0
- package/lib/types/surface.d.ts +53 -12
- package/lib/types/surface.js +80 -23
- package/lib/types/types.d.ts +8 -2
- package/package.json +10 -10
package/lib/types/surface.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @module @deepseek-ai/dsh-session/surface
|
|
9
9
|
*/
|
|
10
10
|
import { SessionLogOffset, SessionSeq } from "./types.js";
|
|
11
|
-
import { KNOWN_SESSION_EVENT_TYPES } from "./known-event-types.js";
|
|
11
|
+
import { KNOWN_SESSION_EVENT_TYPES, MESSAGE_PROJECTION_EVENT_TYPES } from "./known-event-types.js";
|
|
12
12
|
/** Runtime counterpart of the message-producing event union. */
|
|
13
13
|
const SURFACE_EVENT_TYPES = new Set([
|
|
14
14
|
'system/message',
|
|
@@ -62,17 +62,19 @@ export function isReplacementSurfaceEvent(event) {
|
|
|
62
62
|
/**
|
|
63
63
|
* Project a single event into the LLM message it derives to, or null when it
|
|
64
64
|
* produces none — a non-surface event (attempt, boundary, log-only record) or an
|
|
65
|
-
* empty-content assistant/message (which exists only to host usage).
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* nested in the event wrapper and shared by delivery, durable history, and
|
|
71
|
-
* model requests.
|
|
65
|
+
* empty-content assistant/message (which exists only to host usage). A caller
|
|
66
|
+
* reconstructing model input supplies the same prefix's `projectedMessages`
|
|
67
|
+
* from {@link foldSurface}; without that map this function reads original
|
|
68
|
+
* event content. Session instance methods apply the live projection. Messages
|
|
69
|
+
* are immutable and unchanged content retains its durable identity.
|
|
72
70
|
* @param event - the event to project.
|
|
71
|
+
* @param projectedMessages - message projections from the same log prefix's surface fold.
|
|
73
72
|
* @returns the derived message, or null when the event produces none.
|
|
74
73
|
*/
|
|
75
|
-
export function deriveEventMessage(event) {
|
|
74
|
+
export function deriveEventMessage(event, projectedMessages) {
|
|
75
|
+
const projected = projectedMessages?.get(event.seq);
|
|
76
|
+
if (projected !== undefined)
|
|
77
|
+
return projected;
|
|
76
78
|
// Intentionally non-exhaustive: only message-producing events derive
|
|
77
79
|
// history; turn/step boundaries, failed attempts, and errors are trace/replay
|
|
78
80
|
// data.
|
|
@@ -152,7 +154,7 @@ export function validateSessionEventData(event, subject) {
|
|
|
152
154
|
}
|
|
153
155
|
/** Create an empty surface fold state. */
|
|
154
156
|
function createFoldState() {
|
|
155
|
-
return { nodes: [], replaceGeneration: 0 };
|
|
157
|
+
return { nodes: [], replaceGeneration: 0, contentGeneration: 0, projectedMessages: new Map(), projections: new Set() };
|
|
156
158
|
}
|
|
157
159
|
/** Whether a runtime value is a non-negative safe event sequence. */
|
|
158
160
|
function isEventSeq(value) {
|
|
@@ -202,7 +204,7 @@ function surfaceOpOf(event) {
|
|
|
202
204
|
return op;
|
|
203
205
|
}
|
|
204
206
|
/** Validate cited source-event seqs against prior log entries and the replacement range. */
|
|
205
|
-
function
|
|
207
|
+
function assertSourceEventReferences(event, shadowedSeqs) {
|
|
206
208
|
const raw = event.sourceEventSeqs;
|
|
207
209
|
if (event.type === 'assistant/message' && raw !== undefined) {
|
|
208
210
|
throw new Error('assistant/message embeds its source stream and cannot carry sourceEventSeqs');
|
|
@@ -250,7 +252,7 @@ export function validateSurfaceMetadata(event) {
|
|
|
250
252
|
throw new Error(`surface replace at seq ${event.seq}: startSeq and endSeq must reference earlier events`);
|
|
251
253
|
}
|
|
252
254
|
if (op !== undefined)
|
|
253
|
-
|
|
255
|
+
assertSourceEventReferences(event, []);
|
|
254
256
|
return op;
|
|
255
257
|
}
|
|
256
258
|
/** Locate one replacement range without mutating the current fold state. */
|
|
@@ -339,18 +341,27 @@ function assertSystemHeadRewrite(event, state, startIdx, shadowedSeqs, events, b
|
|
|
339
341
|
}
|
|
340
342
|
}
|
|
341
343
|
/** Validate one event at its replay boundary and prepare its atomic fold transition. */
|
|
342
|
-
function planSurfaceEvent(state, event, expectedSeq, events, baseSeq) {
|
|
344
|
+
function planSurfaceEvent(state, event, expectedSeq, events, baseSeq, projections) {
|
|
343
345
|
if (event.seq !== expectedSeq) {
|
|
344
346
|
throw new Error(`session event seq ${event.seq} is not contiguous; expected ${expectedSeq}`);
|
|
345
347
|
}
|
|
346
348
|
const surfaceOp = validateSurfaceMetadata(event);
|
|
349
|
+
const projection = projections.find(item => item.type === event.type);
|
|
350
|
+
if (projection !== undefined) {
|
|
351
|
+
return { kind: 'project', projection, messages: projection.project(event, {
|
|
352
|
+
nodes: state.nodes, events, baseSeq, messages: state.projectedMessages,
|
|
353
|
+
}) };
|
|
354
|
+
}
|
|
355
|
+
if (MESSAGE_PROJECTION_EVENT_TYPES.has(event.type)) {
|
|
356
|
+
throw new Error(`session event "${event.type}" requires a message projection; load its owning plugin or supply its projection definition`);
|
|
357
|
+
}
|
|
347
358
|
if (surfaceOp === undefined)
|
|
348
359
|
return;
|
|
349
360
|
if (surfaceOp === 'append') {
|
|
350
361
|
return { kind: 'append', seq: event.seq };
|
|
351
362
|
}
|
|
352
363
|
const range = replacementRange(state, surfaceOp);
|
|
353
|
-
|
|
364
|
+
assertSourceEventReferences(event, range.shadowedSeqs);
|
|
354
365
|
assertToolResultRewrite(event, range.shadowedSeqs, events, baseSeq);
|
|
355
366
|
assertSystemHeadRewrite(event, state, range.startIdx, range.shadowedSeqs, events, baseSeq);
|
|
356
367
|
return {
|
|
@@ -362,8 +373,8 @@ function planSurfaceEvent(state, event, expectedSeq, events, baseSeq) {
|
|
|
362
373
|
};
|
|
363
374
|
}
|
|
364
375
|
/** Apply one event and return replacement metadata only when one occurred. */
|
|
365
|
-
function applySurfaceEvent(state, event, expectedSeq, events, baseSeq) {
|
|
366
|
-
const plan = planSurfaceEvent(state, event, expectedSeq, events, baseSeq);
|
|
376
|
+
function applySurfaceEvent(state, event, expectedSeq, events, baseSeq, projections) {
|
|
377
|
+
const plan = planSurfaceEvent(state, event, expectedSeq, events, baseSeq, projections);
|
|
367
378
|
return applySurfacePlan(state, plan);
|
|
368
379
|
}
|
|
369
380
|
/** Commit one previously validated surface transition. */
|
|
@@ -374,6 +385,13 @@ function applySurfacePlan(state, plan) {
|
|
|
374
385
|
else if (plan?.kind === 'replace') {
|
|
375
386
|
state.nodes.splice(plan.startIdx, plan.endIdx - plan.startIdx + 1, plan.seq);
|
|
376
387
|
state.replaceGeneration += 1;
|
|
388
|
+
state.contentGeneration += 1;
|
|
389
|
+
}
|
|
390
|
+
else if (plan?.kind === 'project') {
|
|
391
|
+
for (const [seq, message] of plan.messages)
|
|
392
|
+
state.projectedMessages.set(seq, message);
|
|
393
|
+
state.projections.add(plan.projection);
|
|
394
|
+
state.contentGeneration += 1;
|
|
377
395
|
}
|
|
378
396
|
if (plan?.kind !== 'replace')
|
|
379
397
|
return;
|
|
@@ -387,23 +405,25 @@ function applySurfacePlan(state, plan) {
|
|
|
387
405
|
/**
|
|
388
406
|
* Replay a complete session log through the canonical surface fold.
|
|
389
407
|
* @param events - session events in contiguous seq order.
|
|
408
|
+
* @param projections - pure interpreters for plugin-owned message changes; required definitions must be supplied.
|
|
390
409
|
* @returns detached current sequences and replacement history.
|
|
391
|
-
* @throws when an event violates surface metadata, source
|
|
410
|
+
* @throws when an interpreter is missing or an event violates its projection, surface metadata, source attribution, or replacement rules.
|
|
392
411
|
*/
|
|
393
|
-
export function foldSurface(events) {
|
|
412
|
+
export function foldSurface(events, projections = []) {
|
|
394
413
|
const state = createFoldState();
|
|
395
414
|
const replacements = [];
|
|
396
415
|
for (const [index, event] of events.entries()) {
|
|
397
|
-
const replacement = applySurfaceEvent(state, event, SessionSeq(index), events, SessionLogOffset(0));
|
|
416
|
+
const replacement = applySurfaceEvent(state, event, SessionSeq(index), events, SessionLogOffset(0), projections);
|
|
398
417
|
if (replacement !== undefined)
|
|
399
418
|
replacements.push(replacement);
|
|
400
419
|
}
|
|
401
|
-
return { nodes: [...state.nodes], replacements };
|
|
420
|
+
return { nodes: [...state.nodes], replacements, projectedMessages: new Map(state.projectedMessages) };
|
|
402
421
|
}
|
|
403
422
|
/** Incremental ordered surface view and append-boundary validator. */
|
|
404
423
|
export class SurfaceManager {
|
|
405
424
|
log;
|
|
406
425
|
baseSeq;
|
|
426
|
+
projections;
|
|
407
427
|
/** Shared transition state; replacement history is not retained. */
|
|
408
428
|
_state = createFoldState();
|
|
409
429
|
/** Last processed absolute seq. */
|
|
@@ -413,10 +433,12 @@ export class SurfaceManager {
|
|
|
413
433
|
/**
|
|
414
434
|
* @param log - Contiguous complete log or loaded event window.
|
|
415
435
|
* @param baseSeq - Absolute sequence of the window's first event.
|
|
436
|
+
* @param projections - live borrowed definitions; removing a used definition invalidates further reads.
|
|
416
437
|
*/
|
|
417
|
-
constructor(log, baseSeq = SessionLogOffset(0)) {
|
|
438
|
+
constructor(log, baseSeq = SessionLogOffset(0), projections = []) {
|
|
418
439
|
this.log = log;
|
|
419
440
|
this.baseSeq = baseSeq;
|
|
441
|
+
this.projections = projections;
|
|
420
442
|
this._lastProcessedSeq = baseSeq === 0 ? -1 : SessionSeq(baseSeq - 1);
|
|
421
443
|
}
|
|
422
444
|
/**
|
|
@@ -424,23 +446,44 @@ export class SurfaceManager {
|
|
|
424
446
|
* @param event - candidate event that has not entered the log yet.
|
|
425
447
|
*/
|
|
426
448
|
validateNext(event) {
|
|
449
|
+
this._assertProjections();
|
|
427
450
|
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1)
|
|
428
451
|
this._processDelta();
|
|
429
452
|
const expectedSeq = SessionSeq(this.baseSeq + this.log.length);
|
|
430
453
|
this._pendingPlan = {
|
|
431
454
|
event,
|
|
432
455
|
expectedSeq,
|
|
433
|
-
plan: planSurfaceEvent(this._state, event, expectedSeq, this.log, this.baseSeq),
|
|
456
|
+
plan: planSurfaceEvent(this._state, event, expectedSeq, this.log, this.baseSeq, this.projections),
|
|
434
457
|
};
|
|
435
458
|
}
|
|
436
459
|
/** Monotonic count of folded positional replacements. */
|
|
437
460
|
get replaceGeneration() {
|
|
461
|
+
this._assertProjections();
|
|
438
462
|
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1)
|
|
439
463
|
this._processDelta();
|
|
440
464
|
return this._state.replaceGeneration;
|
|
441
465
|
}
|
|
466
|
+
/** Monotonic count of committed changes to existing model-visible content. */
|
|
467
|
+
get contentGeneration() {
|
|
468
|
+
this._assertProjections();
|
|
469
|
+
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1)
|
|
470
|
+
this._processDelta();
|
|
471
|
+
return this._state.contentGeneration;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Project one message with every committed message projection applied.
|
|
475
|
+
* @param event - message-producing or log-only event.
|
|
476
|
+
* @returns its immutable projected message, or null when it produces none.
|
|
477
|
+
*/
|
|
478
|
+
deriveEventMessage(event) {
|
|
479
|
+
this._assertProjections();
|
|
480
|
+
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1)
|
|
481
|
+
this._processDelta();
|
|
482
|
+
return deriveEventMessage(event, this._state.projectedMessages);
|
|
483
|
+
}
|
|
442
484
|
/** Surface event sequences in model-visible order. */
|
|
443
485
|
get nodes() {
|
|
486
|
+
this._assertProjections();
|
|
444
487
|
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1)
|
|
445
488
|
this._processDelta();
|
|
446
489
|
return this._state.nodes;
|
|
@@ -457,12 +500,26 @@ export class SurfaceManager {
|
|
|
457
500
|
applySurfacePlan(this._state, pending.plan);
|
|
458
501
|
}
|
|
459
502
|
else {
|
|
460
|
-
applySurfaceEvent(this._state, event, SessionSeq(seq), this.log, this.baseSeq);
|
|
503
|
+
applySurfaceEvent(this._state, event, SessionSeq(seq), this.log, this.baseSeq, this.projections);
|
|
461
504
|
}
|
|
462
505
|
if (pending !== undefined && pending.expectedSeq <= seq)
|
|
463
506
|
this._pendingPlan = undefined;
|
|
464
507
|
this._lastProcessedSeq = SessionSeq(seq);
|
|
465
508
|
}
|
|
466
509
|
}
|
|
510
|
+
/** Cached messages cannot outlive the definitions that interpreted their log. */
|
|
511
|
+
_assertProjections() {
|
|
512
|
+
const candidate = this._pendingPlan;
|
|
513
|
+
const pending = candidate !== undefined && this.log[candidate.expectedSeq - this.baseSeq] === candidate.event
|
|
514
|
+
? candidate.plan : undefined;
|
|
515
|
+
const required = pending?.kind === 'project'
|
|
516
|
+
? [...this._state.projections, pending.projection]
|
|
517
|
+
: this._state.projections;
|
|
518
|
+
for (const projection of required) {
|
|
519
|
+
if (!this.projections.includes(projection)) {
|
|
520
|
+
throw new Error(`session message projection "${projection.type}" was removed or replaced; restore the session with its owning plugin`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
467
524
|
}
|
|
468
525
|
//# sourceMappingURL=surface.js.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -339,7 +339,9 @@ export interface SessionEventMap {
|
|
|
339
339
|
};
|
|
340
340
|
/**
|
|
341
341
|
* A completed tool call's model-facing result, optional internal failure
|
|
342
|
-
* identity, and optional tool-private `meta`
|
|
342
|
+
* identity and user-facing reason, and optional tool-private `meta`
|
|
343
|
+
* presentation payload. The reason remains outside the model-facing message.
|
|
344
|
+
* `meta` is
|
|
343
345
|
* opaque to the core (the producing tool owns its shape and reads it back in
|
|
344
346
|
* `presentResult`) but MUST be JSON-serializable: `Session.append`
|
|
345
347
|
* runtime-validates all event data with `isJsonValue`, so a non-serializable
|
|
@@ -352,10 +354,14 @@ export interface SessionEventMap {
|
|
|
352
354
|
turn: number;
|
|
353
355
|
step: number;
|
|
354
356
|
message: ToolResultMessage;
|
|
355
|
-
/**
|
|
357
|
+
/**
|
|
358
|
+
* Optional failure identity and raw user-facing reason, outside model content;
|
|
359
|
+
* allowed only when the tool-result block has `isError: true`.
|
|
360
|
+
*/
|
|
356
361
|
error?: {
|
|
357
362
|
name: string;
|
|
358
363
|
code: string;
|
|
364
|
+
reason?: string;
|
|
359
365
|
};
|
|
360
366
|
meta?: JsonValue;
|
|
361
367
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-session",
|
|
3
3
|
"description": "Event-sourced session store for the DeepSeek Harness",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -41,19 +41,19 @@
|
|
|
41
41
|
],
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@deepseek-ai/dsh-scope": "^0.1.
|
|
44
|
+
"@deepseek-ai/dsh-scope": "^0.1.6-alpha.2",
|
|
45
45
|
"@deepseek-ai/cordis": "^4.0.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
49
|
-
"@deepseek-ai/dsh-scope": "^0.1.
|
|
50
|
-
"@deepseek-ai/dsh-typert-
|
|
51
|
-
"@deepseek-ai/
|
|
52
|
-
"@deepseek-ai/
|
|
48
|
+
"@deepseek-ai/dsh-invariants": "^0.1.6-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-scope": "^0.1.6-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-typert-registry": "^0.1.6-alpha.2",
|
|
52
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
55
|
+
"@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-util-values": "^0.1.6-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-brand": "^0.1.6-alpha.2"
|
|
58
58
|
}
|
|
59
59
|
}
|