@deepseek-ai/dsh-api-session-controller 0.1.7-alpha.1 → 0.1.7-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 +6 -4
- package/README.zh.md +6 -4
- package/lib/client.js +74 -29
- package/lib/index.js +19 -6
- package/lib/typert.host.js +11 -3
- package/lib/typert.remote-client.js +9 -1
- package/lib/types/client/contract/session.d.ts +9 -4
- package/lib/types/client/contract/snapshot.d.ts +1 -1
- package/lib/types/client/sessions/projection-store.d.ts +6 -0
- package/lib/types/client/sessions/projection-store.js +9 -0
- package/lib/types/client/sessions/session.d.ts +7 -5
- package/lib/types/client/sessions/session.js +75 -38
- package/lib/types/client/transport.js +5 -2
- package/lib/types/history.js +24 -9
- package/lib/types/types.d.ts +8 -2
- package/package.json +71 -71
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/api/session-controller/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: fa2a251e94cfa2855790d7d71457ac4bdf2ff752
|
|
6
|
+
README.zh.md: d1a73ed5843fddadfc2f401fee1a86eece4e5272
|
package/README.md
CHANGED
|
@@ -43,12 +43,12 @@ Projection reads retain loading and failure state independently of their values.
|
|
|
43
43
|
|
|
44
44
|
Accepted prompts and observed running keep their Client display conversion across list refresh, reconnect, and Session-object replacement. The Manager retains these observations by Session id; they do not establish durable history. Drafts, projection stores, and sidebar display rules retain their existing behavior. The [blank rollback decision](../../../.agents/notes/implemented/bug-fix/2026-09-15-client-session-blank-reconciliation.md) defines retention and late-response handling.
|
|
45
45
|
|
|
46
|
-
The Client adapter exposes `SessionEventStream`, a Gateway `RemoteJournalStream` bound to one ordinary or direct-subagent address. It opens follow before the initial page, publishes only contiguous `replace`, `prepend`, `append`, and `settle-assistant` changes, and repairs reconnect or sequence gaps through a tail page. Backwards paging has two verbs: `loadOlder()` pulls one
|
|
46
|
+
The Client adapter exposes `SessionEventStream`, a Gateway `RemoteJournalStream` bound to one ordinary or direct-subagent address. It opens follow before the initial page, publishes only contiguous `replace`, `prepend`, `append`, and `settle-assistant` changes, and repairs reconnect or sequence gaps through a tail page. Backwards paging has two verbs: `loadOlder()` pulls one Turn-aligned page, and `loadThrough(seq)` — the turn-jump loader — loops Turn-aligned pages until the window covers the target seq, lowering a shared target on repeated calls, stopping on a page that makes no progress, and reporting busy through the same `loadingOlder` snapshot bit. The Web adapter explicitly opts into cursorless Assistant frames: each opening carries the active attempt's `startedAfterSeq`, `nextIndex`, and compact stream, and every stream member becomes a Client-only `assistant/live-chunk` entry ordered between durable cursors. The Host captures a follower-local arrival ordinal with that baseline and suppresses buffered frames at or before the cut; a replacement Agent may restart frame revision at one. A durable `assistant/message` or `assistant/attempt` arriving after an active opening stays staged only when its seq follows `startedAfterSeq` and its Turn and Step match; the matching end type, seq, and index publishes one named settlement delta that retires the attempt's transient rows and adds the durable entry while earlier same-step retries remain visible. Revision, dense-index, or settlement gaps for a known attempt reopen follow, while a controller that missed the start ignores unknown-attempt frames and publishes their durable settlement normally. An abandoned end publishes a settlement delta without a durable entry so its transient rows retire immediately. A durable gap-repair page has no Assistant baseline, so its held notification reopens follow once for a paired page and baseline. Every history record covers exactly its event seq. A business, persistence, or unresolved continuity failure terminates the stream, while only physical carrier loss selects automatic resumption. `SessionControlStream` is a Gateway `RemoteSnapshotStream`; every generation opens with a complete process-local baseline, so reconnect replaces projection state instead of treating transient values as durable events. At each ready Host generation, a synchronous Client subscription clears retained projection values and watermarks before refreshing queries and restarting the control stream, including Sessions absent from the control baseline. The first control stream waits for generation readiness, so its opening values cannot precede invalidation. Outstanding list responses from the previous generation cannot republish those values. Within a generation, a delayed control baseline cannot overwrite or clear newer sequenced values, whether they arrived through a live Session's list block, a history page, or a frame; a cached list block, viewed from the projection cache for a cold Session, yields to the connected Session's baseline whatever watermark it carries. The durable `inbox` projection carries both pending lists through the same cold-read and reconnect path as other projections. Client Agent contexts provide the identity used by the independent [`fileUpload`](../../client/file-upload/README.md) service; Session objects expose lifecycle, prompt, queue, and history operations rather than file transfer.
|
|
47
47
|
|
|
48
|
-
The Session object also carries local submission echoes: `session.beginSubmission` inserts one into `SessionSnapshot.pendingSubmissions` synchronously, before the caller serializes and prompts, so a conversation UI can show the message on the submit click's own frame. The echo stores ordered image previews and durable file references. Session derives its `transcript`, `queued`, or `steering` placement from the current running state and requested delivery mode, then retains that placement
|
|
48
|
+
The Session object also carries local submission echoes: `session.beginSubmission` inserts one into `SessionSnapshot.pendingSubmissions` synchronously, before the caller serializes and prompts, so a conversation UI can show the message on the submit click's own frame. The echo stores ordered image previews and durable file references. Session derives its `transcript`, `queued`, or `steering` placement from the current running state and requested delivery mode, then retains that placement until the display handoff. The prompt's `requestId` is the correlation identity: the Host echoes it as the durable user source's `rpcId`, including pending messages in the `inbox` projection. Queued echoes retire one animation frame after queue acceptance; Chat echoes follow the admission rule below. Identified failures and abandonment retire unadmitted echoes immediately. Disposal preserves observed admission and fails other unsettled submissions. Each retirement fires `onRetire` exactly once; an observed retirement includes the ordered durable attachment references so the composer can release successful cards while preserving failed drafts. Echoes are Client memory only; reload and reconnect rebuild the conversation from durable events alone.
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
During uninterrupted following, steering echoes stay
|
|
51
|
+
During uninterrupted following, transcript and steering echoes stay in Chat through Inbox acceptance and claim until their durable messages arrive. After a local Chat message is admitted, its identity remains until the Inbox watermark reaches its next-turn or next-step claim seq: Chat already renders the durable Node, while Chat and QueueDock exclude matching stale Inbox rows. Other queued messages remain visible. A replacement follow baseline withdraws receipt-confirmed echoes as observed, using their accepted attachment references; unconfirmed submissions remain pending. Host data then supplies the pending or admitted rows. Reconnecting between claim and admission can briefly omit that bubble; withdrawal confirms acceptance, not execution or failure.
|
|
52
52
|
|
|
53
53
|
The user-invocable `skills/list` metadata includes the winning provider’s optional instruction-file `path`. The composer can preview that file without loading every skill body or activating a cold Agent.
|
|
54
54
|
|
|
@@ -56,7 +56,9 @@ Fork copies the exact inclusive event prefix selected by `atSeq`, including a cu
|
|
|
56
56
|
|
|
57
57
|
A resume blocked by an existing write handle returns `session/writer-held` with the Session id; other resume failures retain `gateway/internal`.
|
|
58
58
|
|
|
59
|
-
`loadThrough(seq)` retains older pages privately until its shared target is covered or loading ends, then publishes the successful pages as one ordered prepend. Live events remain visible while history loads. A later page failure retains the successful prefix; replacing the history window discards buffered pages from the replaced window. Ordinary `loadOlder()`
|
|
59
|
+
`loadThrough(seq)` retains older pages privately until its shared target is covered or loading ends, then publishes the successful pages as one ordered prepend. Live events remain visible while history loads. A later page failure retains the successful prefix; replacing the history window discards buffered pages from the replaced window. Ordinary `loadOlder()` publishes one Host-selected page directly.
|
|
60
|
+
|
|
61
|
+
The Client's initial `follow`, reconnect openings, and `loadOlder()` request at least 50 append-origin `user/message` and `assistant/message` events and cross at least two `turn/start` events, including the partial Turn at the loaded window's beginning. The Host stops at the first Turn start satisfying both minima, or at 500 counted messages or history exhaustion, whichever comes first. Steering does not add a Turn boundary. Intervening events accompany the page without counting toward the message budget. The optional `turnWindow` on page and follow requests supplies these minima under `maxMessages`. `loadThrough()` uses the same rules with a 200-message minimum per page; the 500-message cap does not limit the whole jump. Requests without `turnWindow` retain message-aligned paging.
|
|
60
62
|
|
|
61
63
|
Queue edits replace pending content with non-empty text only.
|
|
62
64
|
|
package/README.zh.md
CHANGED
|
@@ -43,12 +43,12 @@ Client 列表刷新保留未变化的行对象,并在顺序和值均相同时
|
|
|
43
43
|
|
|
44
44
|
已受理的提示词与已观察到的运行,会使客户端展示转换跨列表刷新、重连及 Session 对象替换保留。Manager 按 Session id 保留这些观察;它们不证明持久历史已经产生。草稿、投影存储和侧边栏展示规则保留原有行为。[blank 回退修复决策](../../../.agents/notes/implemented/bug-fix/2026-09-15-client-session-blank-reconciliation.zh.md) 定义保留期限及迟到响应处理。
|
|
45
45
|
|
|
46
|
-
Client 适配器提供 `SessionEventStream`,即绑定到一个普通 Session 或 direct subagent address 的 Gateway `RemoteJournalStream`。它在读取首个 page 前打开 follow,只发布连续的 `replace`、`prepend`、`append` 与 `settle-assistant` 变更,并通过 tail page 修复重连或 seq 缺口。向后分页有两个动词:`loadOlder()`
|
|
46
|
+
Client 适配器提供 `SessionEventStream`,即绑定到一个普通 Session 或 direct subagent address 的 Gateway `RemoteJournalStream`。它在读取首个 page 前打开 follow,只发布连续的 `replace`、`prepend`、`append` 与 `settle-assistant` 变更,并通过 tail page 修复重连或 seq 缺口。向后分页有两个动词:`loadOlder()` 拉一页按轮次开头对齐的历史,而 `loadThrough(seq)`——轮次跳转加载器——按轮次开头对齐页面循环拉取直到窗口覆盖目标 seq,重复调用会下调共享目标,遇到无进展的页即停止,忙碌状态复用同一个 `loadingOlder` 快照位。Web 适配器显式选择接收无 cursor 的 Assistant frame:每个 opening 携带活跃 attempt 的 `startedAfterSeq`、`nextIndex` 与紧凑 stream,每个 stream member 都成为排在持久 cursor 之间的 Client-only `assistant/live-chunk` 条目。Host 会随该 baseline 捕获 follower 本地到达序号,并抑制该 cut 及之前的 buffered frame;replacement Agent 可以从 revision 一重新开始。活跃 opening 之后到达的持久 `assistant/message` 或 `assistant/attempt` 只有在其 seq 晚于 `startedAfterSeq` 且轮次与步骤匹配时才会保持暂存;匹配的 end type、seq 与 index 会发布一个具名 settlement delta,删除该 attempt 的瞬态 row、加入持久条目,并保留同一步骤中更早的 retry。已知 attempt 的 revision、密集 index 或 settlement 缺口会重新打开 follow;若 controller 错过 start,则忽略 unknown-attempt frame,并正常发布其持久 settlement。Abandoned end 会发布不含持久条目的 settlement delta,使瞬态 row 立即退出。持久缺口修复 page 不携带 Assistant baseline,因此 held notification 会重新打开 follow 一次,以取得配对的 page 与 baseline。每条历史 record 只覆盖自身的事件 seq。业务、persistence 或无法恢复的连续性错误会终止 stream,只有物理载体断开才触发自动恢复。`SessionControlStream` 是 Gateway `RemoteSnapshotStream`;每代都以完整的进程本地 baseline 开始,因此重连会替换 projection 状态,而不会把瞬态值当作 durable event。每次 Host generation 就绪时,同步的 Client 订阅会先清除保留的投影值及其水位,再刷新查询并重新打开 control stream,其中也包括 control baseline 中没有列出的 Session。首次 control stream 会等待 generation 就绪,确保其 opening 值不会先于旧状态清理到达。上一代尚未完成的 list 响应无法重新发布这些值。同一 generation 内,延迟到达的 control baseline 不能覆盖或清除较新的 sequenced 值,无论它们来自活 Session 的 list block、history page 还是 frame;冷 Session 从 projection cache 看出来的 cached list block 则不论水位都让位于建连 Session 的 baseline。持久 `inbox` 投影通过与其他投影相同的冷读取和重连路径传输两份待处理列表。Client Agent 上下文提供独立 [`fileUpload`](../../client/file-upload/README.zh.md) 服务使用的身份;Session 对象提供生命周期、prompt、queue 与历史操作,不提供文件传输。
|
|
47
47
|
|
|
48
|
-
Session 对象还承载本地提交回显:`session.beginSubmission` 在调用方序列化与提示词之前,同步把一条回显写入 `SessionSnapshot.pendingSubmissions`,会话 UI 因此能在点击提交的当帧显示消息。回显按顺序存放图片预览与持久文件引用。Session 根据当前运行状态与请求的投递模式推导其 `transcript`、`queued` 或 `steering`
|
|
48
|
+
Session 对象还承载本地提交回显:`session.beginSubmission` 在调用方序列化与提示词之前,同步把一条回显写入 `SessionSnapshot.pendingSubmissions`,会话 UI 因此能在点击提交的当帧显示消息。回显按顺序存放图片预览与持久文件引用。Session 根据当前运行状态与请求的投递模式推导其 `transcript`、`queued` 或 `steering` 位置,并保留该位置直到展示接管。提示词的 `requestId` 是关联标识:Host 把它回显为 durable user source 的 `rpcId`,`inbox` 投影中的待处理消息也保留同一 source。排队回显在队列接受后延迟一个动画帧退休;Chat 回显遵循下述入档规则。尚未入档的回显在带标识的提示词失败或被放弃时立即退休。销毁时保留已观察到的入档结果,其余未结算提交按 failed 退休。每次退休恰好触发一次 `onRetire`;observed 退休还会携带有序的持久附件引用,让 composer 释放成功卡片并保留失败草稿。回显只存在于 Client 内存;刷新与重连只从持久事件重建会话。
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
正常在线时,steering 回显在 Inbox
|
|
51
|
+
正常在线时,transcript 与 steering 回显在 Inbox 接受与领取期间留在 Chat,直到持久消息到达。本地 Chat 消息入档后,其身份保留到 Inbox 水位覆盖 next-turn 或 next-step 领取序号:Chat 已显示真实 Node,而 Chat 与 QueueDock 排除匹配的旧 Inbox 行。其他排队消息照常显示。新的 follow 基线会将已有接收回执的回显按 observed 撤去,使用已接受的附件引用完成结算;尚未确认接收的提交继续保留。待处理或已入档的消息随后由 Host 数据呈现。在领取与入档之间重连时,气泡可能短暂消失;撤去回显只确认接收,不代表执行或失败。
|
|
52
52
|
|
|
53
53
|
面向用户调用的 `skills/list` 元数据包含胜出提供方可选的指令文件 `path`。输入框可据此预览文件,无需加载每个 skill 的正文或激活冷态 Agent。
|
|
54
54
|
|
|
@@ -56,7 +56,9 @@ Fork 复制 `atSeq` 所选的精确事件前缀,包含切点事件,允许在
|
|
|
56
56
|
|
|
57
57
|
恢复会话时若已有写句柄占用,返回 `session/writer-held`,并携带会话 id;其他恢复失败仍返回 `gateway/internal`。
|
|
58
58
|
|
|
59
|
-
`loadThrough(seq)` 在共享目标被覆盖或加载结束前私下保留较早页面,随后把成功取得的页面按顺序作为一次前插发布。历史加载期间实时事件仍然可见。后续页面失败时保留已成功取得的部分;历史窗口被替换时丢弃被替换窗口的暂存页面。普通 `loadOlder()`
|
|
59
|
+
`loadThrough(seq)` 在共享目标被覆盖或加载结束前私下保留较早页面,随后把成功取得的页面按顺序作为一次前插发布。历史加载期间实时事件仍然可见。后续页面失败时保留已成功取得的部分;历史窗口被替换时丢弃被替换窗口的暂存页面。普通 `loadOlder()` 直接发布 Host 选取的一页结果。
|
|
60
|
+
|
|
61
|
+
Client 的首次 `follow`、重连首屏与 `loadOlder()` 至少请求 50 条以 append 方式追加的 `user/message` 和 `assistant/message` 事件,并向前跨过至少两个 `turn/start`,其中包含已加载窗口开头尚未补齐的轮次。Host 在首次同时满足两个下限的轮次开头停止;若先达到 500 条计数消息或历史已耗尽,则立即返回。Steering 不增加轮次分界。区间内的其他事件随页返回,但不计入消息预算。分页和 follow 请求可选的 `turnWindow` 在 `maxMessages` 上限内指定这两个下限。`loadThrough()` 复用相同规则,仅将每页消息数下限设为 200;500 条上限不限制整次跳转。不带 `turnWindow` 的请求保留按消息对齐的分页方式。
|
|
60
62
|
|
|
61
63
|
队列编辑仅允许用非空文本替换待处理内容。
|
|
62
64
|
|
package/lib/client.js
CHANGED
|
@@ -428,7 +428,7 @@ window.__ModuleLoader__.load({
|
|
|
428
428
|
for await (const frame of this.remote.session.follow({
|
|
429
429
|
address: this.address,
|
|
430
430
|
assistantStream: true,
|
|
431
|
-
...
|
|
431
|
+
...this.repairRequest(request)
|
|
432
432
|
}, signal)) {
|
|
433
433
|
if (frame.type === "snapshot") {
|
|
434
434
|
for (const record of frame.records) assertSessionWireEvent(record.event);
|
|
@@ -476,7 +476,10 @@ window.__ModuleLoader__.load({
|
|
|
476
476
|
}
|
|
477
477
|
/** @inheritdoc */
|
|
478
478
|
repairRequest(request) {
|
|
479
|
-
return
|
|
479
|
+
return {
|
|
480
|
+
...request.maxMessages === void 0 ? {} : { maxMessages: request.maxMessages },
|
|
481
|
+
...request.turnWindow === void 0 ? {} : { turnWindow: request.turnWindow }
|
|
482
|
+
};
|
|
480
483
|
}
|
|
481
484
|
};
|
|
482
485
|
//#endregion
|
|
@@ -949,6 +952,15 @@ window.__ModuleLoader__.load({
|
|
|
949
952
|
return this.rows.get(key)?.value;
|
|
950
953
|
}
|
|
951
954
|
/**
|
|
955
|
+
* Read the accepted Host watermark without subscribing or copying a value.
|
|
956
|
+
* @param key - projection key.
|
|
957
|
+
* @returns the current sequence, or undefined for absent and cached values.
|
|
958
|
+
*/
|
|
959
|
+
seqOf(key) {
|
|
960
|
+
const row = this.rows.get(key);
|
|
961
|
+
return row?.kind === "sequenced" ? row.seq : void 0;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
952
964
|
* Read every current projection value as one reference-stable snapshot.
|
|
953
965
|
* @returns The same frozen value map until a row changes.
|
|
954
966
|
*/
|
|
@@ -1516,6 +1528,20 @@ window.__ModuleLoader__.load({
|
|
|
1516
1528
|
asOfSeq: value.asOfSeq === -1 ? -1 : SessionSeq(value.asOfSeq)
|
|
1517
1529
|
};
|
|
1518
1530
|
}
|
|
1531
|
+
const HISTORY_PAGE_OPTIONS = {
|
|
1532
|
+
maxMessages: 500,
|
|
1533
|
+
turnWindow: {
|
|
1534
|
+
minMessages: 50,
|
|
1535
|
+
minTurns: 2
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
const JUMP_PAGE_OPTIONS = {
|
|
1539
|
+
...HISTORY_PAGE_OPTIONS,
|
|
1540
|
+
turnWindow: {
|
|
1541
|
+
...HISTORY_PAGE_OPTIONS.turnWindow,
|
|
1542
|
+
minMessages: 200
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1519
1545
|
/**
|
|
1520
1546
|
* Owns a session's event window, lifecycle state, and observable
|
|
1521
1547
|
* snapshot. React bindings remain outside this data layer. Features see only
|
|
@@ -1816,7 +1842,7 @@ window.__ModuleLoader__.load({
|
|
|
1816
1842
|
this.openPromise = promise;
|
|
1817
1843
|
return promise;
|
|
1818
1844
|
}
|
|
1819
|
-
/**
|
|
1845
|
+
/** Prepend one Turn-aligned page: at least 50 messages and two Turn starts, capped at 500 messages. */
|
|
1820
1846
|
async loadOlder() {
|
|
1821
1847
|
if (this.openState !== "open" || !this.hasMore || this.loadingOlder) return;
|
|
1822
1848
|
const events = this.events;
|
|
@@ -1826,7 +1852,7 @@ window.__ModuleLoader__.load({
|
|
|
1826
1852
|
try {
|
|
1827
1853
|
await events.prepend({
|
|
1828
1854
|
beforeSeq: this.baseSeq,
|
|
1829
|
-
|
|
1855
|
+
...HISTORY_PAGE_OPTIONS
|
|
1830
1856
|
});
|
|
1831
1857
|
} catch (error) {
|
|
1832
1858
|
if (!(0, _deepseek_ai_dsh_api_gateway_client.isRemoteFailure)(error)) console.error("[session-controller] loadOlder failed:", error);
|
|
@@ -1862,7 +1888,7 @@ window.__ModuleLoader__.load({
|
|
|
1862
1888
|
const before = pending.beforeSeq;
|
|
1863
1889
|
await events.prepend({
|
|
1864
1890
|
beforeSeq: before,
|
|
1865
|
-
|
|
1891
|
+
...JUMP_PAGE_OPTIONS
|
|
1866
1892
|
});
|
|
1867
1893
|
if (pending.beforeSeq >= before) return;
|
|
1868
1894
|
}
|
|
@@ -1981,7 +2007,8 @@ window.__ModuleLoader__.load({
|
|
|
1981
2007
|
*/
|
|
1982
2008
|
async dispose() {
|
|
1983
2009
|
this.stopObservingInbox();
|
|
1984
|
-
for (const requestId of [...this.submissionSettlements
|
|
2010
|
+
for (const [requestId, settlement] of [...this.submissionSettlements]) if (settlement.admitted !== void 0) this.scheduleObservedRetirement(requestId, settlement.admitted);
|
|
2011
|
+
else this.retireFailedSubmission(requestId);
|
|
1985
2012
|
this.openGeneration++;
|
|
1986
2013
|
const events = this.events;
|
|
1987
2014
|
this.events = void 0;
|
|
@@ -2003,7 +2030,7 @@ window.__ModuleLoader__.load({
|
|
|
2003
2030
|
});
|
|
2004
2031
|
this.events = events;
|
|
2005
2032
|
try {
|
|
2006
|
-
await events.open(
|
|
2033
|
+
await events.open(HISTORY_PAGE_OPTIONS);
|
|
2007
2034
|
if (generation !== this.openGeneration || this.events !== events) return;
|
|
2008
2035
|
this.openState = "open";
|
|
2009
2036
|
} catch (error) {
|
|
@@ -2050,7 +2077,7 @@ window.__ModuleLoader__.load({
|
|
|
2050
2077
|
for (const entry of visible) this.observeSubmissionEvent(entry.event);
|
|
2051
2078
|
if (projections !== void 0) {
|
|
2052
2079
|
const inbox = projections.values.inbox;
|
|
2053
|
-
|
|
2080
|
+
for (const target of ["next-turn", "next-step"]) this.observeSubmissionInsertions(target, inbox?.[target] ?? [], 0, projections.asOfSeq);
|
|
2054
2081
|
}
|
|
2055
2082
|
this.notifier.markDirty();
|
|
2056
2083
|
}
|
|
@@ -2106,36 +2133,35 @@ window.__ModuleLoader__.load({
|
|
|
2106
2133
|
if (this.submissionSettlements.size === 0) return;
|
|
2107
2134
|
if (event.type === "agent/inbox/spliced") {
|
|
2108
2135
|
const { target, start, removedCount = 0, inserted, outcome } = event.data;
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
};
|
|
2120
|
-
}
|
|
2121
|
-
this.observeSteeringInsertions(inserted, start, event.seq);
|
|
2136
|
+
for (const [requestId, settlement] of this.submissionSettlements) {
|
|
2137
|
+
const receipt = settlement.receipt;
|
|
2138
|
+
if (receipt?.target !== target || receipt.index === null || receipt.seq >= event.seq) continue;
|
|
2139
|
+
const removed = receipt.index >= start && receipt.index < start + removedCount;
|
|
2140
|
+
if (removed && outcome === "canceled") this.retireFailedSubmission(requestId);
|
|
2141
|
+
else settlement.receipt = {
|
|
2142
|
+
...receipt,
|
|
2143
|
+
seq: event.seq,
|
|
2144
|
+
index: removed ? null : receipt.index < start ? receipt.index : receipt.index + inserted.length - removedCount
|
|
2145
|
+
};
|
|
2122
2146
|
}
|
|
2147
|
+
this.observeSubmissionInsertions(target, inserted, start, event.seq);
|
|
2123
2148
|
for (const message of inserted) this.observeSubmissionMessage(message, false);
|
|
2124
2149
|
return;
|
|
2125
2150
|
}
|
|
2126
2151
|
if (event.type === "request/context" || event.type === "turn/end") {
|
|
2127
|
-
for (const [requestId, settlement] of this.submissionSettlements) if (settlement.receipt?.index === null && settlement.receipt.seq < event.seq) this.retireFailedSubmission(requestId);
|
|
2152
|
+
for (const [requestId, settlement] of this.submissionSettlements) if (settlement.admitted === void 0 && settlement.receipt?.index === null && settlement.receipt.seq < event.seq) this.retireFailedSubmission(requestId);
|
|
2128
2153
|
return;
|
|
2129
2154
|
}
|
|
2130
2155
|
if (event.type === "user/message") this.observeSubmissionMessage(event.data, true);
|
|
2131
2156
|
}
|
|
2132
|
-
|
|
2157
|
+
observeSubmissionInsertions(target, messages, start, seq) {
|
|
2133
2158
|
for (const [index, message] of messages.entries()) {
|
|
2134
2159
|
const source = message.source;
|
|
2135
2160
|
if (source.kind !== "user" || !("rpcId" in source)) continue;
|
|
2136
2161
|
const settlement = this.submissionSettlements.get(source.rpcId);
|
|
2137
|
-
if (settlement
|
|
2162
|
+
if (settlement === void 0 || settlement.placement === "queued" || settlement.retiring || (settlement.receipt?.seq ?? -1) > seq) continue;
|
|
2138
2163
|
settlement.receipt = {
|
|
2164
|
+
target,
|
|
2139
2165
|
seq,
|
|
2140
2166
|
index: start + index,
|
|
2141
2167
|
attachments: attachmentRefsIn(message.content)
|
|
@@ -2145,15 +2171,34 @@ window.__ModuleLoader__.load({
|
|
|
2145
2171
|
observeSubmissionMessage(message, admitted) {
|
|
2146
2172
|
const source = message.source;
|
|
2147
2173
|
if (source.kind !== "user" || !("rpcId" in source)) return;
|
|
2148
|
-
|
|
2149
|
-
|
|
2174
|
+
const settlement = this.submissionSettlements.get(source.rpcId);
|
|
2175
|
+
if (settlement === void 0 || settlement.retiring) return;
|
|
2176
|
+
if (!admitted) {
|
|
2177
|
+
if (settlement.placement === "queued") this.scheduleObservedRetirement(source.rpcId, attachmentRefsIn(message.content));
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
settlement.admitted = attachmentRefsIn(message.content);
|
|
2181
|
+
this.retireAdmittedSubmission(source.rpcId);
|
|
2182
|
+
}
|
|
2183
|
+
/** Retire admitted Chat identities only after stale Inbox rows can no longer reappear. */
|
|
2184
|
+
retireAdmittedSubmission(requestId) {
|
|
2185
|
+
const settlement = this.submissionSettlements.get(requestId);
|
|
2186
|
+
if (settlement?.admitted === void 0) return;
|
|
2187
|
+
const receipt = settlement.receipt;
|
|
2188
|
+
if (receipt?.index === null && (this.projections.seqOf("inbox") ?? -1) < receipt.seq) return;
|
|
2189
|
+
this.scheduleObservedRetirement(requestId, settlement.admitted);
|
|
2150
2190
|
}
|
|
2151
|
-
/**
|
|
2191
|
+
/** Inbox acceptance retires queued echoes; its watermark completes admitted Chat handoffs. */
|
|
2152
2192
|
observeSubmissionInbox() {
|
|
2153
2193
|
if (this.submissionSettlements.size === 0) return;
|
|
2154
2194
|
const inbox = this.projections.get("inbox");
|
|
2155
2195
|
if (inbox === void 0) return;
|
|
2156
|
-
|
|
2196
|
+
const seq = this.projections.seqOf("inbox");
|
|
2197
|
+
for (const target of ["next-turn", "next-step"]) {
|
|
2198
|
+
if (seq !== void 0) this.observeSubmissionInsertions(target, inbox[target], 0, seq);
|
|
2199
|
+
for (const message of inbox[target]) this.observeSubmissionMessage(message, false);
|
|
2200
|
+
}
|
|
2201
|
+
for (const requestId of this.submissionSettlements.keys()) this.retireAdmittedSubmission(requestId);
|
|
2157
2202
|
}
|
|
2158
2203
|
/**
|
|
2159
2204
|
* Latch one observed settlement and remove the echo an animation frame
|
|
@@ -2175,7 +2220,7 @@ window.__ModuleLoader__.load({
|
|
|
2175
2220
|
/** Remove one unsettled echo immediately (prompt rejection, abort, or disposal). */
|
|
2176
2221
|
retireFailedSubmission(requestId) {
|
|
2177
2222
|
const settlement = this.submissionSettlements.get(requestId);
|
|
2178
|
-
if (settlement === void 0 || settlement.retiring) return;
|
|
2223
|
+
if (settlement === void 0 || settlement.retiring || settlement.admitted !== void 0) return;
|
|
2179
2224
|
settlement.retiring = true;
|
|
2180
2225
|
this.finishSubmission(requestId, { reason: "failed" });
|
|
2181
2226
|
}
|
package/lib/index.js
CHANGED
|
@@ -1329,7 +1329,7 @@ var SessionHistoryController = class {
|
|
|
1329
1329
|
if (throughSeq > sourceCursor) throw new RemoteError("gateway/bad-request", `session page through seq ${String(throughSeq)} is past cursor ${String(sourceCursor)}`, {});
|
|
1330
1330
|
/* v8 ignore next -- Session and persistence validation guarantee a dense zero-based event prefix. */
|
|
1331
1331
|
if (throughSeq >= 0 && sourceLog[throughSeq]?.seq !== throughSeq) throw new RemoteError("gateway/internal", `session log does not contain through seq ${String(throughSeq)}`, {});
|
|
1332
|
-
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq);
|
|
1332
|
+
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq, request.turnWindow);
|
|
1333
1333
|
return {
|
|
1334
1334
|
records: pageRecords(page.events),
|
|
1335
1335
|
hasMore: page.hasMore
|
|
@@ -1348,7 +1348,7 @@ var SessionHistoryController = class {
|
|
|
1348
1348
|
* @returns a complete opening snapshot followed by gap-free durable events and opted-in assistant frames.
|
|
1349
1349
|
*/
|
|
1350
1350
|
async *follow(request, signal) {
|
|
1351
|
-
|
|
1351
|
+
validateHistoryWindow(request);
|
|
1352
1352
|
const { address } = request;
|
|
1353
1353
|
const target = addressId(address);
|
|
1354
1354
|
const buffered = new Deque();
|
|
@@ -1408,7 +1408,7 @@ var SessionHistoryController = class {
|
|
|
1408
1408
|
signal.throwIfAborted();
|
|
1409
1409
|
const cursor = source.cursor;
|
|
1410
1410
|
snapshotCursor = cursor;
|
|
1411
|
-
const page = paginate(events, void 0, request.maxMessages ?? DEFAULT_MAX_MESSAGES);
|
|
1411
|
+
const page = paginate(events, void 0, request.maxMessages ?? DEFAULT_MAX_MESSAGES, cursor, request.turnWindow);
|
|
1412
1412
|
const assistantStream = request.assistantStream === true ? this.assistantStreams.get(target)?.snapshot() ?? { revision: 0 } : void 0;
|
|
1413
1413
|
const assistantStreamOrdinalCut = assistantStreamOrdinal;
|
|
1414
1414
|
yield {
|
|
@@ -1515,10 +1515,15 @@ function projectionBlock(snapshot) {
|
|
|
1515
1515
|
function validatePageRequest(request) {
|
|
1516
1516
|
if (!Number.isSafeInteger(request.throughSeq) || request.throughSeq < -1 || Object.is(request.throughSeq, -0)) throw new RemoteError("gateway/bad-request", "throughSeq must be an integer greater than or equal to -1", {});
|
|
1517
1517
|
if (request.beforeSeq !== void 0 && (!Number.isSafeInteger(request.beforeSeq) || request.beforeSeq < 0 || Object.is(request.beforeSeq, -0))) throw new RemoteError("gateway/bad-request", "beforeSeq must be a non-negative safe integer", {});
|
|
1518
|
-
|
|
1518
|
+
validateHistoryWindow(request);
|
|
1519
1519
|
}
|
|
1520
|
-
function
|
|
1520
|
+
function validateHistoryWindow(request) {
|
|
1521
1521
|
if (request.maxMessages !== void 0 && (!Number.isSafeInteger(request.maxMessages) || request.maxMessages <= 0)) throw new RemoteError("gateway/bad-request", "maxMessages must be a positive safe integer", {});
|
|
1522
|
+
const window = request.turnWindow;
|
|
1523
|
+
if (window !== void 0) {
|
|
1524
|
+
if (!Number.isSafeInteger(window.minMessages) || window.minMessages <= 0 || window.minMessages > (request.maxMessages ?? DEFAULT_MAX_MESSAGES)) throw new RemoteError("gateway/bad-request", "turnWindow.minMessages must be a positive safe integer no greater than maxMessages", {});
|
|
1525
|
+
if (!Number.isSafeInteger(window.minTurns) || window.minTurns <= 0) throw new RemoteError("gateway/bad-request", "turnWindow.minTurns must be a positive safe integer", {});
|
|
1526
|
+
}
|
|
1522
1527
|
}
|
|
1523
1528
|
function addressId(address) {
|
|
1524
1529
|
return address.kind === "session" ? address.sessionId : address.childSessionId;
|
|
@@ -1549,12 +1554,20 @@ function rejectNotFound(address) {
|
|
|
1549
1554
|
childSessionId: address.childSessionId
|
|
1550
1555
|
});
|
|
1551
1556
|
}
|
|
1552
|
-
function paginate(events, beforeSeq, maxMessages, throughSeq
|
|
1557
|
+
function paginate(events, beforeSeq, maxMessages, throughSeq, turnWindow) {
|
|
1553
1558
|
const end = SessionLogOffset(Math.min(throughSeq + 1, beforeSeq ?? throughSeq + 1));
|
|
1554
1559
|
let count = 0;
|
|
1560
|
+
let turns = 0;
|
|
1555
1561
|
let cut = SessionLogOffset(0);
|
|
1556
1562
|
for (let index = end - 1; index >= 0; index--) {
|
|
1557
1563
|
const event = events[index];
|
|
1564
|
+
if (turnWindow !== void 0 && event.type === "turn/start") {
|
|
1565
|
+
turns++;
|
|
1566
|
+
if (count >= turnWindow.minMessages && turns >= turnWindow.minTurns) {
|
|
1567
|
+
cut = SessionLogOffset(index);
|
|
1568
|
+
break;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1558
1571
|
if (!MESSAGE_TYPES$1.has(event.type) || !isAppendSurfaceEvent(event)) continue;
|
|
1559
1572
|
count++;
|
|
1560
1573
|
const sources = event.sourceEventSeqs;
|
package/lib/typert.host.js
CHANGED
|
@@ -181,8 +181,12 @@ const _deepseek_ai_dsh_api_session_controller_session_follow_parameter_0$schema
|
|
|
181
181
|
'childSessionId': z.intersection(z.string(), z.unknown()).readonly(),
|
|
182
182
|
'mode': z.union([z.literal("one-shot"), z.literal("continuable"), z.literal("unknown")]).readonly(),
|
|
183
183
|
})]).readonly(),
|
|
184
|
-
'maxMessages': z.number().readonly().optional(),
|
|
185
184
|
'assistantStream': z.literal(true).readonly().optional(),
|
|
185
|
+
'maxMessages': z.number().readonly().optional(),
|
|
186
|
+
'turnWindow': z.object({
|
|
187
|
+
'minMessages': z.number().readonly(),
|
|
188
|
+
'minTurns': z.number().readonly(),
|
|
189
|
+
}).readonly().optional(),
|
|
186
190
|
}))
|
|
187
191
|
let _deepseek_ai_dsh_api_session_controller_session_follow_result$schema$value
|
|
188
192
|
const _deepseek_ai_dsh_api_session_controller_session_follow_result$schema = () => (_deepseek_ai_dsh_api_session_controller_session_follow_result$schema$value ??= z.union([z.object({
|
|
@@ -534,6 +538,10 @@ const _deepseek_ai_dsh_api_session_controller_session_page_parameter_0$schema =
|
|
|
534
538
|
'throughSeq': z.number().readonly(),
|
|
535
539
|
'beforeSeq': z.number().readonly().optional(),
|
|
536
540
|
'maxMessages': z.number().readonly().optional(),
|
|
541
|
+
'turnWindow': z.object({
|
|
542
|
+
'minMessages': z.number().readonly(),
|
|
543
|
+
'minTurns': z.number().readonly(),
|
|
544
|
+
}).readonly().optional(),
|
|
537
545
|
}))
|
|
538
546
|
let _deepseek_ai_dsh_api_session_controller_session_page_result$schema$value
|
|
539
547
|
const _deepseek_ai_dsh_api_session_controller_session_page_result$schema = () => (_deepseek_ai_dsh_api_session_controller_session_page_result$schema$value ??= z.object({
|
|
@@ -1914,7 +1922,7 @@ export const TYPERT = {
|
|
|
1914
1922
|
},
|
|
1915
1923
|
{
|
|
1916
1924
|
"name": "SessionFollowRequest",
|
|
1917
|
-
"declaration": "export interface SessionFollowRequest {\n readonly address: SessionAddress;\n readonly
|
|
1925
|
+
"declaration": "export interface SessionFollowRequest extends Pick<SessionPageRequest, 'maxMessages' | 'turnWindow'> {\n readonly address: SessionAddress;\n readonly assistantStream?: true;\n}"
|
|
1918
1926
|
},
|
|
1919
1927
|
{
|
|
1920
1928
|
"name": "SessionForkRequest",
|
|
@@ -1970,7 +1978,7 @@ export const TYPERT = {
|
|
|
1970
1978
|
},
|
|
1971
1979
|
{
|
|
1972
1980
|
"name": "SessionPageRequest",
|
|
1973
|
-
"declaration": "export interface SessionPageRequest {\n readonly address: SessionAddress;\n readonly throughSeq: number;\n readonly beforeSeq?: number;\n readonly maxMessages?: number;\n}"
|
|
1981
|
+
"declaration": "export interface SessionPageRequest {\n readonly address: SessionAddress;\n readonly throughSeq: number;\n readonly beforeSeq?: number;\n readonly maxMessages?: number;\n readonly turnWindow?: { readonly minMessages: number; readonly minTurns: number; };\n}"
|
|
1974
1982
|
},
|
|
1975
1983
|
{
|
|
1976
1984
|
"name": "SessionProjectionBaseline",
|
|
@@ -181,8 +181,12 @@ const _deepseek_ai_dsh_api_session_controller_session_follow_parameter_0$schema
|
|
|
181
181
|
'childSessionId': z.intersection(z.string(), z.unknown()).readonly(),
|
|
182
182
|
'mode': z.union([z.literal("one-shot"), z.literal("continuable"), z.literal("unknown")]).readonly(),
|
|
183
183
|
})]).readonly(),
|
|
184
|
-
'maxMessages': z.number().readonly().optional(),
|
|
185
184
|
'assistantStream': z.literal(true).readonly().optional(),
|
|
185
|
+
'maxMessages': z.number().readonly().optional(),
|
|
186
|
+
'turnWindow': z.object({
|
|
187
|
+
'minMessages': z.number().readonly(),
|
|
188
|
+
'minTurns': z.number().readonly(),
|
|
189
|
+
}).readonly().optional(),
|
|
186
190
|
}))
|
|
187
191
|
let _deepseek_ai_dsh_api_session_controller_session_follow_result$schema$value
|
|
188
192
|
const _deepseek_ai_dsh_api_session_controller_session_follow_result$schema = () => (_deepseek_ai_dsh_api_session_controller_session_follow_result$schema$value ??= z.union([z.object({
|
|
@@ -534,6 +538,10 @@ const _deepseek_ai_dsh_api_session_controller_session_page_parameter_0$schema =
|
|
|
534
538
|
'throughSeq': z.number().readonly(),
|
|
535
539
|
'beforeSeq': z.number().readonly().optional(),
|
|
536
540
|
'maxMessages': z.number().readonly().optional(),
|
|
541
|
+
'turnWindow': z.object({
|
|
542
|
+
'minMessages': z.number().readonly(),
|
|
543
|
+
'minTurns': z.number().readonly(),
|
|
544
|
+
}).readonly().optional(),
|
|
537
545
|
}))
|
|
538
546
|
let _deepseek_ai_dsh_api_session_controller_session_page_result$schema$value
|
|
539
547
|
const _deepseek_ai_dsh_api_session_controller_session_page_result$schema = () => (_deepseek_ai_dsh_api_session_controller_session_page_result$schema$value ??= z.object({
|
|
@@ -63,9 +63,10 @@ export interface ISession {
|
|
|
63
63
|
/**
|
|
64
64
|
* Register one local submission echo in `snapshot.pendingSubmissions`,
|
|
65
65
|
* synchronously, before the caller serializes and sends the prompt. The
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
66
|
+
* Chat echoes persist until durable admission; transcript identities also
|
|
67
|
+
* wait for the Inbox claim watermark so stale queue rows stay suppressed.
|
|
68
|
+
* Queued echoes retire on queue acceptance. Identified failures retire
|
|
69
|
+
* submissions that have not already reached durable admission.
|
|
69
70
|
* @param input - echo content and the optional settlement callback.
|
|
70
71
|
* @returns the minted identity for {@link prompt} plus the pre-prompt abandon path.
|
|
71
72
|
*/
|
|
@@ -118,7 +119,9 @@ export interface ISession {
|
|
|
118
119
|
seq: SessionSeq;
|
|
119
120
|
}>>;
|
|
120
121
|
/**
|
|
121
|
-
* Extend
|
|
122
|
+
* Extend history by at least 50 messages and two Turn starts, including a
|
|
123
|
+
* partial Turn at the window's beginning. Stop at 500 messages or history
|
|
124
|
+
* exhaustion even when those minima cannot be met. Publish one prepend.
|
|
122
125
|
* @returns completion; failures land in snapshot.openState/loadingOlder.
|
|
123
126
|
*/
|
|
124
127
|
loadOlder(): Promise<void>;
|
|
@@ -127,6 +130,8 @@ export interface ISession {
|
|
|
127
130
|
* turn-jump loader. Repeated calls while a jump is paging lower its shared
|
|
128
131
|
* target and return the in-flight completion; `snapshot.loadingOlder` is
|
|
129
132
|
* the busy signal for the whole jump.
|
|
133
|
+
* Each page uses the same Turn alignment as loadOlder, with a 200-message
|
|
134
|
+
* minimum. The 500-message limit applies per page, not to the whole jump.
|
|
130
135
|
* Older pages publish together at completion, including successful pages
|
|
131
136
|
* before a later failure; live events remain independently visible.
|
|
132
137
|
* @param seq - durable event seq the window must reach (a turn's `turn/start` seq).
|
|
@@ -38,7 +38,7 @@ export type PendingSubmissionPlacement = 'transcript' | 'queued' | 'steering';
|
|
|
38
38
|
export interface PendingSubmission {
|
|
39
39
|
/** The prompt RPC identity; the durable `user/message` source echoes it as `rpcId`. */
|
|
40
40
|
readonly requestId: SessionRequestId;
|
|
41
|
-
/**
|
|
41
|
+
/** Surface fixed at submission time; Inbox acceptance does not move a Chat echo into the dock. */
|
|
42
42
|
readonly placement: PendingSubmissionPlacement;
|
|
43
43
|
/** Client wall-clock ms when the submission began. */
|
|
44
44
|
readonly time: number;
|
|
@@ -75,6 +75,12 @@ export declare class ProjectionValueStore {
|
|
|
75
75
|
* @returns the value, or undefined while the key is absent.
|
|
76
76
|
*/
|
|
77
77
|
get(key: string): unknown;
|
|
78
|
+
/**
|
|
79
|
+
* Read the accepted Host watermark without subscribing or copying a value.
|
|
80
|
+
* @param key - projection key.
|
|
81
|
+
* @returns the current sequence, or undefined for absent and cached values.
|
|
82
|
+
*/
|
|
83
|
+
seqOf(key: string): SessionSeqCursor | undefined;
|
|
78
84
|
/**
|
|
79
85
|
* Read every current projection value as one reference-stable snapshot.
|
|
80
86
|
* @returns The same frozen value map until a row changes.
|
|
@@ -38,6 +38,15 @@ export class ProjectionValueStore {
|
|
|
38
38
|
get(key) {
|
|
39
39
|
return this.rows.get(key)?.value;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Read the accepted Host watermark without subscribing or copying a value.
|
|
43
|
+
* @param key - projection key.
|
|
44
|
+
* @returns the current sequence, or undefined for absent and cached values.
|
|
45
|
+
*/
|
|
46
|
+
seqOf(key) {
|
|
47
|
+
const row = this.rows.get(key);
|
|
48
|
+
return row?.kind === 'sequenced' ? row.seq : undefined;
|
|
49
|
+
}
|
|
41
50
|
/**
|
|
42
51
|
* Read every current projection value as one reference-stable snapshot.
|
|
43
52
|
* @returns The same frozen value map until a row changes.
|
|
@@ -10,9 +10,9 @@ import { MutableSessionEventSource } from '../contract/events.ts';
|
|
|
10
10
|
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
|
|
11
11
|
import type { SessionRemotes } from './remotes.ts';
|
|
12
12
|
import { ProjectionValueStore } from './projection-store.ts';
|
|
13
|
-
/**
|
|
13
|
+
/** Minimum message count for ordinary history windows. */
|
|
14
14
|
export declare const PAGE_MESSAGES = 50;
|
|
15
|
-
/**
|
|
15
|
+
/** Minimum messages per page while a turn jump loops backwards. */
|
|
16
16
|
export declare const JUMP_PAGE_MESSAGES = 200;
|
|
17
17
|
/** Manager-owned observers of a Session object's local state edges. */
|
|
18
18
|
export interface SessionOptions {
|
|
@@ -192,7 +192,7 @@ export declare class Session implements SessionFace {
|
|
|
192
192
|
}>>;
|
|
193
193
|
/** First open: pull the tail page (idempotent — in-flight/already-open returns the existing promise). */
|
|
194
194
|
open(): Promise<void>;
|
|
195
|
-
/**
|
|
195
|
+
/** Prepend one Turn-aligned page: at least 50 messages and two Turn starts, capped at 500 messages. */
|
|
196
196
|
loadOlder(): Promise<void>;
|
|
197
197
|
/** Jump loader: page backwards until the window covers seq (see ISession.loadThrough). */
|
|
198
198
|
loadThrough(seq: SessionSeq): Promise<void>;
|
|
@@ -262,9 +262,11 @@ export declare class Session implements SessionFace {
|
|
|
262
262
|
private appendLive;
|
|
263
263
|
/** Observe durable acceptance even when insertion and claim share one projection notification. */
|
|
264
264
|
private observeSubmissionEvent;
|
|
265
|
-
private
|
|
265
|
+
private observeSubmissionInsertions;
|
|
266
266
|
private observeSubmissionMessage;
|
|
267
|
-
/** Retire
|
|
267
|
+
/** Retire admitted Chat identities only after stale Inbox rows can no longer reappear. */
|
|
268
|
+
private retireAdmittedSubmission;
|
|
269
|
+
/** Inbox acceptance retires queued echoes; its watermark completes admitted Chat handoffs. */
|
|
268
270
|
private observeSubmissionInbox;
|
|
269
271
|
/**
|
|
270
272
|
* Latch one observed settlement and remove the echo an animation frame
|
|
@@ -15,10 +15,15 @@ function projectionsBaseline(value) {
|
|
|
15
15
|
asOfSeq: value.asOfSeq === -1 ? -1 : SessionSeq(value.asOfSeq),
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
18
|
+
/** Minimum message count for ordinary history windows. */
|
|
19
19
|
export const PAGE_MESSAGES = 50;
|
|
20
|
-
|
|
20
|
+
const HISTORY_PAGE_OPTIONS = { maxMessages: 500, turnWindow: { minMessages: PAGE_MESSAGES, minTurns: 2 } };
|
|
21
|
+
/** Minimum messages per page while a turn jump loops backwards. */
|
|
21
22
|
export const JUMP_PAGE_MESSAGES = 200;
|
|
23
|
+
const JUMP_PAGE_OPTIONS = {
|
|
24
|
+
...HISTORY_PAGE_OPTIONS,
|
|
25
|
+
turnWindow: { ...HISTORY_PAGE_OPTIONS.turnWindow, minMessages: JUMP_PAGE_MESSAGES },
|
|
26
|
+
};
|
|
22
27
|
/**
|
|
23
28
|
* Owns a session's event window, lifecycle state, and observable
|
|
24
29
|
* snapshot. React bindings remain outside this data layer. Features see only
|
|
@@ -304,7 +309,7 @@ export class Session {
|
|
|
304
309
|
this.openPromise = promise;
|
|
305
310
|
return promise;
|
|
306
311
|
}
|
|
307
|
-
/**
|
|
312
|
+
/** Prepend one Turn-aligned page: at least 50 messages and two Turn starts, capped at 500 messages. */
|
|
308
313
|
async loadOlder() {
|
|
309
314
|
if (this.openState !== 'open' || !this.hasMore || this.loadingOlder)
|
|
310
315
|
return;
|
|
@@ -314,7 +319,10 @@ export class Session {
|
|
|
314
319
|
this.loadingOlder = true;
|
|
315
320
|
this.notifier.markDirty();
|
|
316
321
|
try {
|
|
317
|
-
await events.prepend({
|
|
322
|
+
await events.prepend({
|
|
323
|
+
beforeSeq: this.baseSeq,
|
|
324
|
+
...HISTORY_PAGE_OPTIONS,
|
|
325
|
+
});
|
|
318
326
|
}
|
|
319
327
|
catch (error) {
|
|
320
328
|
if (!isRemoteFailure(error)) {
|
|
@@ -363,7 +371,7 @@ export class Session {
|
|
|
363
371
|
if (generation !== this.openGeneration)
|
|
364
372
|
return;
|
|
365
373
|
const before = pending.beforeSeq;
|
|
366
|
-
await events.prepend({ beforeSeq: before,
|
|
374
|
+
await events.prepend({ beforeSeq: before, ...JUMP_PAGE_OPTIONS });
|
|
367
375
|
// No-progress guard: an empty or dropped page that still claims more
|
|
368
376
|
// history must end the loop, not spin it.
|
|
369
377
|
if (pending.beforeSeq >= before)
|
|
@@ -504,10 +512,12 @@ export class Session {
|
|
|
504
512
|
async dispose() {
|
|
505
513
|
this.stopObservingInbox();
|
|
506
514
|
// Unsettled echoes retire as failed so their owners can restore or
|
|
507
|
-
// release browser resources; echoes
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
515
|
+
// release browser resources; admitted echoes keep their observed outcome.
|
|
516
|
+
for (const [requestId, settlement] of [...this.submissionSettlements]) {
|
|
517
|
+
if (settlement.admitted !== undefined)
|
|
518
|
+
this.scheduleObservedRetirement(requestId, settlement.admitted);
|
|
519
|
+
else
|
|
520
|
+
this.retireFailedSubmission(requestId);
|
|
511
521
|
}
|
|
512
522
|
this.openGeneration++;
|
|
513
523
|
const events = this.events;
|
|
@@ -532,7 +542,7 @@ export class Session {
|
|
|
532
542
|
});
|
|
533
543
|
this.events = events;
|
|
534
544
|
try {
|
|
535
|
-
await events.open(
|
|
545
|
+
await events.open(HISTORY_PAGE_OPTIONS);
|
|
536
546
|
if (generation !== this.openGeneration || this.events !== events)
|
|
537
547
|
return;
|
|
538
548
|
this.openState = 'open';
|
|
@@ -585,7 +595,7 @@ export class Session {
|
|
|
585
595
|
if (projections !== undefined)
|
|
586
596
|
this.projections.seed(projections);
|
|
587
597
|
this.eventSource.replace(visible, hasMore);
|
|
588
|
-
// A new follow baseline replaces optimistic
|
|
598
|
+
// A new follow baseline replaces confirmed optimistic inputs with Host-owned rows.
|
|
589
599
|
// Receipt-backed inputs are accepted, not failed, even if their history is outside this window.
|
|
590
600
|
if (projections !== undefined) {
|
|
591
601
|
for (const [requestId, { receipt }] of this.submissionSettlements) {
|
|
@@ -598,7 +608,9 @@ export class Session {
|
|
|
598
608
|
this.observeSubmissionEvent(entry.event);
|
|
599
609
|
if (projections !== undefined) {
|
|
600
610
|
const inbox = projections.values.inbox;
|
|
601
|
-
|
|
611
|
+
for (const target of ['next-turn', 'next-step']) {
|
|
612
|
+
this.observeSubmissionInsertions(target, inbox?.[target] ?? [], 0, projections.asOfSeq);
|
|
613
|
+
}
|
|
602
614
|
}
|
|
603
615
|
this.notifier.markDirty();
|
|
604
616
|
}
|
|
@@ -662,30 +674,29 @@ export class Session {
|
|
|
662
674
|
return;
|
|
663
675
|
if (event.type === 'agent/inbox/spliced') {
|
|
664
676
|
const { target, start, removedCount = 0, inserted, outcome } = event.data;
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
this.observeSteeringInsertions(inserted, start, event.seq);
|
|
677
|
+
for (const [requestId, settlement] of this.submissionSettlements) {
|
|
678
|
+
const receipt = settlement.receipt;
|
|
679
|
+
if (receipt?.target !== target || receipt.index === null || receipt.seq >= event.seq)
|
|
680
|
+
continue;
|
|
681
|
+
const removed = receipt.index >= start && receipt.index < start + removedCount;
|
|
682
|
+
if (removed && outcome === 'canceled')
|
|
683
|
+
this.retireFailedSubmission(requestId);
|
|
684
|
+
else
|
|
685
|
+
settlement.receipt = {
|
|
686
|
+
...receipt,
|
|
687
|
+
seq: event.seq,
|
|
688
|
+
index: removed ? null : receipt.index < start ? receipt.index : receipt.index + inserted.length - removedCount,
|
|
689
|
+
};
|
|
681
690
|
}
|
|
691
|
+
this.observeSubmissionInsertions(target, inserted, start, event.seq);
|
|
682
692
|
for (const message of inserted)
|
|
683
693
|
this.observeSubmissionMessage(message, false);
|
|
684
694
|
return;
|
|
685
695
|
}
|
|
686
696
|
if (event.type === 'request/context' || event.type === 'turn/end') {
|
|
687
697
|
for (const [requestId, settlement] of this.submissionSettlements) {
|
|
688
|
-
if (settlement.
|
|
698
|
+
if (settlement.admitted === undefined && settlement.receipt?.index === null
|
|
699
|
+
&& settlement.receipt.seq < event.seq)
|
|
689
700
|
this.retireFailedSubmission(requestId);
|
|
690
701
|
}
|
|
691
702
|
return;
|
|
@@ -693,34 +704,60 @@ export class Session {
|
|
|
693
704
|
if (event.type === 'user/message')
|
|
694
705
|
this.observeSubmissionMessage(event.data, true);
|
|
695
706
|
}
|
|
696
|
-
|
|
707
|
+
observeSubmissionInsertions(target, messages, start, seq) {
|
|
697
708
|
for (const [index, message] of messages.entries()) {
|
|
698
709
|
const source = message.source;
|
|
699
710
|
if (source.kind !== 'user' || !('rpcId' in source))
|
|
700
711
|
continue;
|
|
701
712
|
const settlement = this.submissionSettlements.get(source.rpcId);
|
|
702
|
-
if (settlement
|
|
713
|
+
if (settlement === undefined || settlement.placement === 'queued'
|
|
714
|
+
|| settlement.retiring || (settlement.receipt?.seq ?? -1) > seq)
|
|
703
715
|
continue;
|
|
704
|
-
settlement.receipt = { seq, index: start + index, attachments: attachmentRefsIn(message.content) };
|
|
716
|
+
settlement.receipt = { target, seq, index: start + index, attachments: attachmentRefsIn(message.content) };
|
|
705
717
|
}
|
|
706
718
|
}
|
|
707
719
|
observeSubmissionMessage(message, admitted) {
|
|
708
720
|
const source = message.source;
|
|
709
721
|
if (source.kind !== 'user' || !('rpcId' in source))
|
|
710
722
|
return;
|
|
711
|
-
|
|
723
|
+
const settlement = this.submissionSettlements.get(source.rpcId);
|
|
724
|
+
if (settlement === undefined || settlement.retiring)
|
|
725
|
+
return;
|
|
726
|
+
if (!admitted) {
|
|
727
|
+
if (settlement.placement === 'queued')
|
|
728
|
+
this.scheduleObservedRetirement(source.rpcId, attachmentRefsIn(message.content));
|
|
712
729
|
return;
|
|
713
|
-
|
|
730
|
+
}
|
|
731
|
+
settlement.admitted = attachmentRefsIn(message.content);
|
|
732
|
+
this.retireAdmittedSubmission(source.rpcId);
|
|
733
|
+
}
|
|
734
|
+
/** Retire admitted Chat identities only after stale Inbox rows can no longer reappear. */
|
|
735
|
+
retireAdmittedSubmission(requestId) {
|
|
736
|
+
const settlement = this.submissionSettlements.get(requestId);
|
|
737
|
+
if (settlement?.admitted === undefined)
|
|
738
|
+
return;
|
|
739
|
+
const receipt = settlement.receipt;
|
|
740
|
+
if (receipt?.index === null
|
|
741
|
+
&& (this.projections.seqOf('inbox') ?? -1) < receipt.seq)
|
|
742
|
+
return;
|
|
743
|
+
this.scheduleObservedRetirement(requestId, settlement.admitted);
|
|
714
744
|
}
|
|
715
|
-
/**
|
|
745
|
+
/** Inbox acceptance retires queued echoes; its watermark completes admitted Chat handoffs. */
|
|
716
746
|
observeSubmissionInbox() {
|
|
717
747
|
if (this.submissionSettlements.size === 0)
|
|
718
748
|
return;
|
|
719
749
|
const inbox = this.projections.get('inbox');
|
|
720
750
|
if (inbox === undefined)
|
|
721
751
|
return;
|
|
722
|
-
|
|
723
|
-
|
|
752
|
+
const seq = this.projections.seqOf('inbox');
|
|
753
|
+
for (const target of ['next-turn', 'next-step']) {
|
|
754
|
+
if (seq !== undefined)
|
|
755
|
+
this.observeSubmissionInsertions(target, inbox[target], 0, seq);
|
|
756
|
+
for (const message of inbox[target])
|
|
757
|
+
this.observeSubmissionMessage(message, false);
|
|
758
|
+
}
|
|
759
|
+
for (const requestId of this.submissionSettlements.keys())
|
|
760
|
+
this.retireAdmittedSubmission(requestId);
|
|
724
761
|
}
|
|
725
762
|
/**
|
|
726
763
|
* Latch one observed settlement and remove the echo an animation frame
|
|
@@ -738,7 +775,7 @@ export class Session {
|
|
|
738
775
|
/** Remove one unsettled echo immediately (prompt rejection, abort, or disposal). */
|
|
739
776
|
retireFailedSubmission(requestId) {
|
|
740
777
|
const settlement = this.submissionSettlements.get(requestId);
|
|
741
|
-
if (settlement === undefined || settlement.retiring)
|
|
778
|
+
if (settlement === undefined || settlement.retiring || settlement.admitted !== undefined)
|
|
742
779
|
return;
|
|
743
780
|
settlement.retiring = true;
|
|
744
781
|
this.finishSubmission(requestId, { reason: 'failed' });
|
|
@@ -76,7 +76,7 @@ export class SessionEventStream extends RemoteJournalStream {
|
|
|
76
76
|
for await (const frame of this.remote.session.follow({
|
|
77
77
|
address: this.address,
|
|
78
78
|
assistantStream: true,
|
|
79
|
-
...(request
|
|
79
|
+
...this.repairRequest(request),
|
|
80
80
|
}, signal)) {
|
|
81
81
|
if (frame.type === 'snapshot') {
|
|
82
82
|
for (const record of frame.records)
|
|
@@ -121,7 +121,10 @@ export class SessionEventStream extends RemoteJournalStream {
|
|
|
121
121
|
}
|
|
122
122
|
/** @inheritdoc */
|
|
123
123
|
repairRequest(request) {
|
|
124
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
...(request.maxMessages === undefined ? {} : { maxMessages: request.maxMessages }),
|
|
126
|
+
...(request.turnWindow === undefined ? {} : { turnWindow: request.turnWindow }),
|
|
127
|
+
};
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
//# sourceMappingURL=transport.js.map
|
package/lib/types/history.js
CHANGED
|
@@ -115,7 +115,7 @@ export class SessionHistoryController {
|
|
|
115
115
|
if (throughSeq >= 0 && sourceLog[throughSeq]?.seq !== throughSeq) {
|
|
116
116
|
throw new RemoteError('gateway/internal', `session log does not contain through seq ${String(throughSeq)}`, {});
|
|
117
117
|
}
|
|
118
|
-
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq);
|
|
118
|
+
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq, request.turnWindow);
|
|
119
119
|
const records = pageRecords(page.events);
|
|
120
120
|
return {
|
|
121
121
|
records,
|
|
@@ -137,7 +137,7 @@ export class SessionHistoryController {
|
|
|
137
137
|
* @returns a complete opening snapshot followed by gap-free durable events and opted-in assistant frames.
|
|
138
138
|
*/
|
|
139
139
|
async *follow(request, signal) {
|
|
140
|
-
|
|
140
|
+
validateHistoryWindow(request);
|
|
141
141
|
const { address } = request;
|
|
142
142
|
const target = addressId(address);
|
|
143
143
|
const buffered = new Deque();
|
|
@@ -198,7 +198,7 @@ export class SessionHistoryController {
|
|
|
198
198
|
signal.throwIfAborted();
|
|
199
199
|
const cursor = source.cursor;
|
|
200
200
|
snapshotCursor = cursor;
|
|
201
|
-
const page = paginate(events, undefined, request.maxMessages ?? DEFAULT_MAX_MESSAGES);
|
|
201
|
+
const page = paginate(events, undefined, request.maxMessages ?? DEFAULT_MAX_MESSAGES, cursor, request.turnWindow);
|
|
202
202
|
const assistantStream = request.assistantStream === true
|
|
203
203
|
? this.assistantStreams.get(target)?.snapshot() ?? { revision: 0 }
|
|
204
204
|
: undefined;
|
|
@@ -327,16 +327,23 @@ function validatePageRequest(request) {
|
|
|
327
327
|
|| Object.is(request.beforeSeq, -0))) {
|
|
328
328
|
throw new RemoteError('gateway/bad-request', 'beforeSeq must be a non-negative safe integer', {});
|
|
329
329
|
}
|
|
330
|
-
|
|
331
|
-
&& (!Number.isSafeInteger(request.maxMessages) || request.maxMessages <= 0)) {
|
|
332
|
-
throw new RemoteError('gateway/bad-request', 'maxMessages must be a positive safe integer', {});
|
|
333
|
-
}
|
|
330
|
+
validateHistoryWindow(request);
|
|
334
331
|
}
|
|
335
|
-
function
|
|
332
|
+
function validateHistoryWindow(request) {
|
|
336
333
|
if (request.maxMessages !== undefined
|
|
337
334
|
&& (!Number.isSafeInteger(request.maxMessages) || request.maxMessages <= 0)) {
|
|
338
335
|
throw new RemoteError('gateway/bad-request', 'maxMessages must be a positive safe integer', {});
|
|
339
336
|
}
|
|
337
|
+
const window = request.turnWindow;
|
|
338
|
+
if (window !== undefined) {
|
|
339
|
+
if (!Number.isSafeInteger(window.minMessages) || window.minMessages <= 0
|
|
340
|
+
|| window.minMessages > (request.maxMessages ?? DEFAULT_MAX_MESSAGES)) {
|
|
341
|
+
throw new RemoteError('gateway/bad-request', 'turnWindow.minMessages must be a positive safe integer no greater than maxMessages', {});
|
|
342
|
+
}
|
|
343
|
+
if (!Number.isSafeInteger(window.minTurns) || window.minTurns <= 0) {
|
|
344
|
+
throw new RemoteError('gateway/bad-request', 'turnWindow.minTurns must be a positive safe integer', {});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
340
347
|
}
|
|
341
348
|
function addressId(address) {
|
|
342
349
|
return address.kind === 'session' ? address.sessionId : address.childSessionId;
|
|
@@ -385,12 +392,20 @@ function rejectNotFound(address) {
|
|
|
385
392
|
childSessionId: address.childSessionId,
|
|
386
393
|
});
|
|
387
394
|
}
|
|
388
|
-
function paginate(events, beforeSeq, maxMessages, throughSeq
|
|
395
|
+
function paginate(events, beforeSeq, maxMessages, throughSeq, turnWindow) {
|
|
389
396
|
const end = SessionLogOffset(Math.min(throughSeq + 1, beforeSeq ?? throughSeq + 1));
|
|
390
397
|
let count = 0;
|
|
398
|
+
let turns = 0;
|
|
391
399
|
let cut = SessionLogOffset(0);
|
|
392
400
|
for (let index = end - 1; index >= 0; index--) {
|
|
393
401
|
const event = events[index];
|
|
402
|
+
if (turnWindow !== undefined && event.type === 'turn/start') {
|
|
403
|
+
turns++;
|
|
404
|
+
if (count >= turnWindow.minMessages && turns >= turnWindow.minTurns) {
|
|
405
|
+
cut = SessionLogOffset(index);
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
394
409
|
if (!MESSAGE_TYPES.has(event.type) || !isAppendSurfaceEvent(event))
|
|
395
410
|
continue;
|
|
396
411
|
count++;
|
package/lib/types/types.d.ts
CHANGED
|
@@ -443,11 +443,17 @@ export interface SessionPageRequest {
|
|
|
443
443
|
readonly throughSeq: number;
|
|
444
444
|
readonly beforeSeq?: number;
|
|
445
445
|
readonly maxMessages?: number;
|
|
446
|
+
/** Stop at a Turn start after both minima, unless maxMessages or history exhaustion wins. */
|
|
447
|
+
readonly turnWindow?: {
|
|
448
|
+
/** Minimum append-origin user/assistant messages; must not exceed maxMessages. */
|
|
449
|
+
readonly minMessages: number;
|
|
450
|
+
/** Minimum Turn starts crossed, including the partial Turn at beforeSeq. */
|
|
451
|
+
readonly minTurns: number;
|
|
452
|
+
};
|
|
446
453
|
}
|
|
447
454
|
/** One live event request for a durable Session address. */
|
|
448
|
-
export interface SessionFollowRequest {
|
|
455
|
+
export interface SessionFollowRequest extends Pick<SessionPageRequest, 'maxMessages' | 'turnWindow'> {
|
|
449
456
|
readonly address: SessionAddress;
|
|
450
|
-
readonly maxMessages?: number;
|
|
451
457
|
/** Include process-local assistant presentation frames for the Web client. */
|
|
452
458
|
readonly assistantStream?: true;
|
|
453
459
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-api-session-controller",
|
|
3
3
|
"description": "Session Remote commands, cold reads, and live control transport",
|
|
4
|
-
"version": "0.1.7-alpha.
|
|
4
|
+
"version": "0.1.7-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -67,39 +67,39 @@
|
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"mime-types": "^3.0.2",
|
|
69
69
|
"zod": "^4.4.3",
|
|
70
|
-
"@deepseek-ai/
|
|
71
|
-
"@deepseek-ai/
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
70
|
+
"@deepseek-ai/schemastery": "~3.18.4",
|
|
71
|
+
"@deepseek-ai/dsh-brand": "0.1.7-alpha.2",
|
|
72
|
+
"@deepseek-ai/dsh-deque": "0.1.7-alpha.2"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"@deepseek-ai/
|
|
76
|
-
"@deepseek-ai/
|
|
77
|
-
"@deepseek-ai/dsh-agent-default-model": "
|
|
78
|
-
"@deepseek-ai/dsh-agent-preset-registry": "
|
|
79
|
-
"@deepseek-ai/dsh-api-gateway": "
|
|
80
|
-
"@deepseek-ai/dsh-
|
|
81
|
-
"@deepseek-ai/dsh-client-
|
|
82
|
-
"@deepseek-ai/dsh-
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-
|
|
85
|
-
"@deepseek-ai/dsh-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-
|
|
89
|
-
"@deepseek-ai/dsh-session-projection
|
|
90
|
-
"@deepseek-ai/dsh-session-
|
|
91
|
-
"@deepseek-ai/dsh-session-query": "
|
|
92
|
-
"@deepseek-ai/dsh-
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/dsh-
|
|
95
|
-
"@deepseek-ai/dsh-
|
|
96
|
-
"@deepseek-ai/dsh-
|
|
97
|
-
"@deepseek-ai/dsh-
|
|
98
|
-
"@deepseek-ai/dsh-
|
|
99
|
-
"@deepseek-ai/dsh-
|
|
100
|
-
"@deepseek-ai/dsh-util-workspace-path": "
|
|
101
|
-
"@deepseek-ai/dsh-
|
|
102
|
-
"@deepseek-ai/dsh-
|
|
75
|
+
"@deepseek-ai/dsh-agent": "0.1.7-alpha.2",
|
|
76
|
+
"@deepseek-ai/cordis": "~4.0.4",
|
|
77
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.7-alpha.2",
|
|
78
|
+
"@deepseek-ai/dsh-agent-preset-registry": "0.1.7-alpha.2",
|
|
79
|
+
"@deepseek-ai/dsh-api-gateway": "0.1.7-alpha.2",
|
|
80
|
+
"@deepseek-ai/dsh-attachment": "0.1.7-alpha.2",
|
|
81
|
+
"@deepseek-ai/dsh-client-connection": "0.1.7-alpha.2",
|
|
82
|
+
"@deepseek-ai/dsh-client-file-upload": "0.1.7-alpha.2",
|
|
83
|
+
"@deepseek-ai/dsh-llm": "0.1.7-alpha.2",
|
|
84
|
+
"@deepseek-ai/dsh-native-command": "0.1.7-alpha.2",
|
|
85
|
+
"@deepseek-ai/dsh-scope": "0.1.7-alpha.2",
|
|
86
|
+
"@deepseek-ai/dsh-session": "0.1.7-alpha.2",
|
|
87
|
+
"@deepseek-ai/dsh-file-reference": "0.1.7-alpha.2",
|
|
88
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.7-alpha.2",
|
|
89
|
+
"@deepseek-ai/dsh-session-projection": "0.1.7-alpha.2",
|
|
90
|
+
"@deepseek-ai/dsh-session-projection-cache": "0.1.7-alpha.2",
|
|
91
|
+
"@deepseek-ai/dsh-session-query": "0.1.7-alpha.2",
|
|
92
|
+
"@deepseek-ai/dsh-session-title": "0.1.7-alpha.2",
|
|
93
|
+
"@deepseek-ai/dsh-fs": "0.1.7-alpha.2",
|
|
94
|
+
"@deepseek-ai/dsh-commands": "0.1.7-alpha.2",
|
|
95
|
+
"@deepseek-ai/dsh-skill": "0.1.7-alpha.2",
|
|
96
|
+
"@deepseek-ai/dsh-subagent": "0.1.7-alpha.2",
|
|
97
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.7-alpha.2",
|
|
98
|
+
"@deepseek-ai/dsh-typert-registry": "0.1.7-alpha.2",
|
|
99
|
+
"@deepseek-ai/dsh-util-time": "0.1.7-alpha.2",
|
|
100
|
+
"@deepseek-ai/dsh-util-workspace-path": "0.1.7-alpha.2",
|
|
101
|
+
"@deepseek-ai/dsh-workspace": "0.1.7-alpha.2",
|
|
102
|
+
"@deepseek-ai/dsh-util-values": "0.1.7-alpha.2"
|
|
103
103
|
},
|
|
104
104
|
"peerDependenciesMeta": {
|
|
105
105
|
"@deepseek-ai/dsh-session-persistence": {
|
|
@@ -111,45 +111,45 @@
|
|
|
111
111
|
},
|
|
112
112
|
"devDependencies": {
|
|
113
113
|
"@types/mime-types": "^3.0.1",
|
|
114
|
-
"@deepseek-ai/
|
|
115
|
-
"@deepseek-ai/dsh-agent
|
|
116
|
-
"@deepseek-ai/dsh-agent-
|
|
117
|
-
"@deepseek-ai/dsh-
|
|
118
|
-
"@deepseek-ai/dsh-
|
|
119
|
-
"@deepseek-ai/dsh-
|
|
120
|
-
"@deepseek-ai/dsh-
|
|
121
|
-
"@deepseek-ai/dsh-client-
|
|
122
|
-
"@deepseek-ai/dsh-
|
|
123
|
-
"@deepseek-ai/dsh-
|
|
124
|
-
"@deepseek-ai/dsh-file-reference": "
|
|
125
|
-
"@deepseek-ai/dsh-
|
|
126
|
-
"@deepseek-ai/dsh-fs
|
|
127
|
-
"@deepseek-ai/dsh-
|
|
128
|
-
"@deepseek-ai/dsh-
|
|
129
|
-
"@deepseek-ai/
|
|
130
|
-
"@deepseek-ai/dsh-
|
|
131
|
-
"@deepseek-ai/dsh-
|
|
132
|
-
"@deepseek-ai/dsh-
|
|
133
|
-
"@deepseek-ai/dsh-
|
|
134
|
-
"@deepseek-ai/dsh-session-
|
|
135
|
-
"@deepseek-ai/dsh-
|
|
136
|
-
"@deepseek-ai/dsh-session-projection": "
|
|
137
|
-
"@deepseek-ai/dsh-session-
|
|
138
|
-
"@deepseek-ai/dsh-session-title": "
|
|
139
|
-
"@deepseek-ai/dsh-
|
|
140
|
-
"@deepseek-ai/dsh-
|
|
141
|
-
"@deepseek-ai/dsh-
|
|
142
|
-
"@deepseek-ai/dsh-
|
|
143
|
-
"@deepseek-ai/dsh-
|
|
144
|
-
"@deepseek-ai/dsh-
|
|
145
|
-
"@deepseek-ai/dsh-
|
|
146
|
-
"@deepseek-ai/dsh-
|
|
147
|
-
"@deepseek-ai/dsh-
|
|
148
|
-
"@deepseek-ai/dsh-
|
|
149
|
-
"@deepseek-ai/dsh-util-
|
|
150
|
-
"@deepseek-ai/dsh-
|
|
151
|
-
"@deepseek-ai/dsh-
|
|
152
|
-
"@deepseek-ai/dsh-util-
|
|
114
|
+
"@deepseek-ai/cordis": "~4.0.4",
|
|
115
|
+
"@deepseek-ai/dsh-agent": "0.1.7-alpha.2",
|
|
116
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.7-alpha.2",
|
|
117
|
+
"@deepseek-ai/dsh-agent-loop": "0.1.7-alpha.2",
|
|
118
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.1.7-alpha.2",
|
|
119
|
+
"@deepseek-ai/dsh-agent-preset-registry": "0.1.7-alpha.2",
|
|
120
|
+
"@deepseek-ai/dsh-api-gateway": "0.1.7-alpha.2",
|
|
121
|
+
"@deepseek-ai/dsh-client-connection": "0.1.7-alpha.2",
|
|
122
|
+
"@deepseek-ai/dsh-attachment": "0.1.7-alpha.2",
|
|
123
|
+
"@deepseek-ai/dsh-client-test-runtime": "0.1.7-alpha.2",
|
|
124
|
+
"@deepseek-ai/dsh-file-reference": "0.1.7-alpha.2",
|
|
125
|
+
"@deepseek-ai/dsh-commands": "0.1.7-alpha.2",
|
|
126
|
+
"@deepseek-ai/dsh-fs": "0.1.7-alpha.2",
|
|
127
|
+
"@deepseek-ai/dsh-fs-local": "0.1.7-alpha.2",
|
|
128
|
+
"@deepseek-ai/dsh-permission-presets": "0.1.7-alpha.2",
|
|
129
|
+
"@deepseek-ai/dsh-llm": "0.1.7-alpha.2",
|
|
130
|
+
"@deepseek-ai/dsh-remote-mock": "0.1.7-alpha.2",
|
|
131
|
+
"@deepseek-ai/dsh-native-command": "0.1.7-alpha.2",
|
|
132
|
+
"@deepseek-ai/dsh-session": "0.1.7-alpha.2",
|
|
133
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.7-alpha.2",
|
|
134
|
+
"@deepseek-ai/dsh-session-projection": "0.1.7-alpha.2",
|
|
135
|
+
"@deepseek-ai/dsh-scope": "0.1.7-alpha.2",
|
|
136
|
+
"@deepseek-ai/dsh-session-projection-cache": "0.1.7-alpha.2",
|
|
137
|
+
"@deepseek-ai/dsh-session-query": "0.1.7-alpha.2",
|
|
138
|
+
"@deepseek-ai/dsh-session-title": "0.1.7-alpha.2",
|
|
139
|
+
"@deepseek-ai/dsh-client-store": "0.1.7-alpha.2",
|
|
140
|
+
"@deepseek-ai/dsh-client-file-upload": "0.1.7-alpha.2",
|
|
141
|
+
"@deepseek-ai/dsh-skill": "0.1.7-alpha.2",
|
|
142
|
+
"@deepseek-ai/dsh-storage-domain": "0.1.7-alpha.2",
|
|
143
|
+
"@deepseek-ai/dsh-storage-json": "0.1.7-alpha.2",
|
|
144
|
+
"@deepseek-ai/dsh-storage": "0.1.7-alpha.2",
|
|
145
|
+
"@deepseek-ai/dsh-subagent": "0.1.7-alpha.2",
|
|
146
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.7-alpha.2",
|
|
147
|
+
"@deepseek-ai/dsh-typert-registry": "0.1.7-alpha.2",
|
|
148
|
+
"@deepseek-ai/dsh-util-crypto": "0.1.7-alpha.2",
|
|
149
|
+
"@deepseek-ai/dsh-util-time": "0.1.7-alpha.2",
|
|
150
|
+
"@deepseek-ai/dsh-util-values": "0.1.7-alpha.2",
|
|
151
|
+
"@deepseek-ai/dsh-workspace": "0.1.7-alpha.2",
|
|
152
|
+
"@deepseek-ai/dsh-util-workspace-path": "0.1.7-alpha.2"
|
|
153
153
|
},
|
|
154
154
|
"scripts": {
|
|
155
155
|
"bundle": "tsdown",
|