mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
refactor(surface): use nodeBySeq map for lookup in _replace, drop dead params
This commit is contained in:
@@ -66,7 +66,51 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
|
||||
`SessionEventType = keyof SessionEventMap`. Because `SessionEventMap` is merge-extensible, switches over `SessionEvent` must NOT use `assertNever` — a plugin-added variant is a valid unknown value; handle the known cases and fall through `default`.
|
||||
|
||||
The five message-producing types (`SurfaceEventType` — `user/message`, `assistant/message`, `tool/result`, `context/message`, `steering/message`) additionally carry two optional surface fields: `surfaceOp` (how the event enters the derived surface linked list — `'append'` or a `{ op: 'replace', start, end }` shadow) and `sourceEventSeqs` (provenance). See the [session surface RFC](../rfc/implemented/architecture/2026-06-18-session-surface.md).
|
||||
## Surface types
|
||||
|
||||
The five message-producing types (`SurfaceEventType` — `user/message`, `assistant/message`, `tool/result`, `context/message`, `steering/message`) carry surface metadata declaring how they join the derived surface linked list. See the [session surface RFC](../rfc/implemented/architecture/2026-06-18-session-surface.md).
|
||||
|
||||
### `SurfaceEventType` — the message-producing subset of event types
|
||||
|
||||
```ts type-equiv
|
||||
export type SurfaceEventType =
|
||||
| 'user/message'
|
||||
| 'assistant/message'
|
||||
| 'tool/result'
|
||||
| 'context/message'
|
||||
| 'steering/message'
|
||||
```
|
||||
|
||||
### `SurfaceOp` — how an event entered the surface
|
||||
|
||||
```ts type-equiv
|
||||
export type SurfaceOp =
|
||||
| 'append'
|
||||
| { op: 'replace'; start: number; end: number }
|
||||
```
|
||||
|
||||
`'append'` is the normal tail-append path. `replace` shadows surface nodes from `start` through `end` inclusive (both must be valid surface node seqs; `start === end` replaces a single node) and inserts the new node in their place.
|
||||
|
||||
### `SurfaceIntent` — the parameter to `session.append()`
|
||||
|
||||
```ts type-equiv
|
||||
export interface SurfaceIntent {
|
||||
surfaceOp: SurfaceOp
|
||||
sourceEventSeqs?: number[]
|
||||
}
|
||||
```
|
||||
|
||||
Required for `SurfaceEventType` events — every message-producing event must declare how it joins the surface, the sole source of derived history. Non-surface types reject it at compile time.
|
||||
|
||||
### `SurfaceNode` — a node in the surface linked list
|
||||
|
||||
```ts type-equiv
|
||||
export interface SurfaceNode {
|
||||
seq: number
|
||||
prev: number | null
|
||||
next: number | null
|
||||
}
|
||||
```
|
||||
|
||||
## Derived history: `deriveMessages()`
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export interface SurfaceNode {
|
||||
export class SurfaceManager {
|
||||
/** Surface nodes in linked-list order (head to tail). Empty until first access. */
|
||||
private _nodes: SurfaceNode[] = []
|
||||
/** Map from event seq → node for O(1) lookup during replacements. */
|
||||
/** Map from event seq → node. */
|
||||
private _nodeBySeq = new Map<number, SurfaceNode>()
|
||||
/** The last processed seq. -1 forces a full rebuild on first access. */
|
||||
private _lastProcessedSeq = -1
|
||||
@@ -100,7 +100,7 @@ export class SurfaceManager {
|
||||
this._nodes.push(node)
|
||||
this._nodeBySeq.set(event.seq, node)
|
||||
} else {
|
||||
this._replace(this._nodes, this._nodeBySeq, event.seq, event.surfaceOp)
|
||||
this._replace(event.seq, event.surfaceOp)
|
||||
}
|
||||
}
|
||||
this._lastProcessedSeq = this.log.length - 1
|
||||
@@ -108,31 +108,31 @@ export class SurfaceManager {
|
||||
|
||||
/** Apply a replace operation to the in-progress surface. */
|
||||
private _replace(
|
||||
nodes: SurfaceNode[],
|
||||
nodeBySeq: Map<number, SurfaceNode>,
|
||||
newSeq: number,
|
||||
op: Extract<SurfaceOp, { op: 'replace' }>,
|
||||
): void {
|
||||
const startIdx = nodes.findIndex(n => n.seq === op.start)
|
||||
if (startIdx === -1) {
|
||||
const startNode = this._nodeBySeq.get(op.start)
|
||||
if (!startNode) {
|
||||
throw new Error(`surface replace: start seq ${op.start} not found in surface`)
|
||||
}
|
||||
const endIdx = nodes.findIndex(n => n.seq === op.end)
|
||||
if (endIdx === -1) {
|
||||
const endNode = this._nodeBySeq.get(op.end)
|
||||
if (!endNode) {
|
||||
throw new Error(`surface replace: end seq ${op.end} not found in surface`)
|
||||
}
|
||||
const startIdx = this._nodes.indexOf(startNode)
|
||||
const endIdx = this._nodes.indexOf(endNode)
|
||||
if (startIdx > endIdx) {
|
||||
throw new Error(`surface replace: start seq ${op.start} (index ${startIdx}) is after end seq ${op.end} (index ${endIdx})`)
|
||||
}
|
||||
|
||||
// Remove shadowed nodes from `[startIdx, endIdx]` inclusive.
|
||||
const count = endIdx - startIdx + 1
|
||||
const removed = nodes.splice(startIdx, count)
|
||||
for (const r of removed) nodeBySeq.delete(r.seq)
|
||||
const removed = this._nodes.splice(startIdx, count)
|
||||
for (const r of removed) this._nodeBySeq.delete(r.seq)
|
||||
|
||||
// Insert the new node where the removed range was.
|
||||
const prevNode = startIdx > 0 ? nodes[startIdx - 1] : undefined
|
||||
const nextNode = startIdx < nodes.length ? nodes[startIdx] : undefined
|
||||
const prevNode = startIdx > 0 ? this._nodes[startIdx - 1] : undefined
|
||||
const nextNode = startIdx < this._nodes.length ? this._nodes[startIdx] : undefined
|
||||
|
||||
const newNode: SurfaceNode = {
|
||||
seq: newSeq,
|
||||
@@ -141,7 +141,7 @@ export class SurfaceManager {
|
||||
}
|
||||
if (prevNode) prevNode.next = newSeq
|
||||
if (nextNode) nextNode.prev = newSeq
|
||||
nodes.splice(startIdx, 0, newNode)
|
||||
nodeBySeq.set(newSeq, newNode)
|
||||
this._nodes.splice(startIdx, 0, newNode)
|
||||
this._nodeBySeq.set(newSeq, newNode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "SessionEvent", "source": "packages/core/session/src/types.ts" },
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "TurnTriggerMap", "source": "packages/core/session/src/types.ts" },
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "TurnEndReasonMap", "source": "packages/core/session/src/types.ts" },
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "SurfaceEventType", "source": "packages/core/session/src/types.ts" },
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "SurfaceOp", "source": "packages/core/session/src/types.ts" },
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "SurfaceIntent", "source": "packages/core/session/src/types.ts" },
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "SurfaceNode", "source": "packages/core/session/src/surface.ts" },
|
||||
|
||||
{ "doc": "docs/core-data-structures/persistence.md", "symbol": "SessionHeader", "source": "packages/core/session/src/types.ts" },
|
||||
{ "doc": "docs/core-data-structures/persistence.md", "symbol": "CreateSessionOptions", "source": "packages/core/session/src/types.ts" },
|
||||
|
||||
Reference in New Issue
Block a user