@deepseek-ai/dsh-client-ui-trajectory 0.2.0-rc.2 → 0.2.1-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 +8 -8
- package/README.md +6 -6
- package/README.zh.md +6 -6
- package/lib/client.js +710 -43
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/trajectory-contract.d.ts +7 -3
- package/lib/types/client/trajectory-record.d.ts +1 -1
- package/package.json +22 -21
package/README.i18n.yaml
CHANGED
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
en: 4bd17d05b8385675
|
|
19
19
|
zh: 5174e27f754f2a03
|
|
20
20
|
/deepseek-ai-dsh-client-ui-trajectory/use-this-package/inspecting-records:
|
|
21
|
-
en:
|
|
22
|
-
zh:
|
|
21
|
+
en: e4ba2d9cab2fb258
|
|
22
|
+
zh: 7db041270420b2ba
|
|
23
23
|
/deepseek-ai-dsh-client-ui-trajectory/use-this-package/the-timing-overview:
|
|
24
|
-
en:
|
|
25
|
-
zh:
|
|
24
|
+
en: 0f4ee2189324ac06
|
|
25
|
+
zh: 12e76fed22c09bc1
|
|
26
26
|
/deepseek-ai-dsh-client-ui-trajectory/understand-the-implementation:
|
|
27
|
-
en:
|
|
28
|
-
zh:
|
|
27
|
+
en: 74cc8fc87241da34
|
|
28
|
+
zh: cd72ff9c0fdd2575
|
|
29
29
|
/deepseek-ai-dsh-client-ui-trajectory/understand-the-implementation/virtual-rows:
|
|
30
30
|
en: c85e342bc7eee4e6
|
|
31
31
|
zh: 9feab66ef1755b94
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
en: d7da620f56eb3c65
|
|
46
46
|
zh: fb030b68322b94cd
|
|
47
47
|
/deepseek-ai-dsh-client-ui-trajectory/known-limitations-and-deferred-work/dev-note:
|
|
48
|
-
en:
|
|
49
|
-
zh:
|
|
48
|
+
en: e67f633dc6fe7773
|
|
49
|
+
zh: 3bb0c20c93f16f98
|
package/README.md
CHANGED
|
@@ -29,17 +29,19 @@ Open the Trajectory tab in the conversation's view ring to inspect agent activit
|
|
|
29
29
|
|
|
30
30
|
### Inspecting records
|
|
31
31
|
|
|
32
|
-
Calls are identified by their recorded tool name. For `run_code`, the row shows the program description and the inspector opens numbered, highlighted source. The Code tab provides wrapping, a `{}` toggle for the original JSON arguments, and exact source copying, including trailing newlines. Copying the original arguments retains their recorded JSON whitespace. Each newly opened code view takes the last wrapping choice; changing it leaves other open views as they are. Output preserves the recorded text, using a tree for complete JSON objects or arrays. Highlighting uses only an unambiguous TypeScript or Python hint in the recorded tool schema; missing or conflicting hints leave plain source.
|
|
32
|
+
Calls are identified by their recorded tool name. For `run_code`, the row shows the program description and the inspector opens numbered, highlighted source. The Code tab provides wrapping, a `{}` toggle for the original JSON arguments, and exact source copying, including trailing newlines. Copying the original arguments retains their recorded JSON whitespace. Each newly opened code view takes the last wrapping choice; changing it leaves other open views as they are. Output preserves the recorded text, using a tree for complete JSON objects or arrays. Highlighting uses only an unambiguous TypeScript or Python hint in the recorded tool schema; missing or conflicting hints leave plain source.
|
|
33
33
|
|
|
34
34
|
Selection, timeline navigation, folding, and search cover the React-visible window. Request numbers and cumulative usage cover the complete resident snapshot. Selecting a record opens a local inspector for token usage, duration, Input, Output, Timing, and durable images. Image URLs use the Conversation-owned per-session cache, so Chat and Trajectory share one authorized read per attachment. A user record shows both nonzero image and ordinary-file counts beside its text, including attachment-only records. A standalone compaction request appears chronologically in its own `Between turns` section, while a numbered compaction remains inside its owning turn.
|
|
35
35
|
|
|
36
36
|
Summary and Preview share one ordered attachment list after the message text, preserving repeated references. Each row shows a contained image thumbnail or file-type icon, the recorded filename (a localized numbered label for unnamed images), and recorded size, type, and image dimensions where available. Zero-byte files retain their size, and truncated filenames expose the full name in a tooltip. Images open the existing lightbox. Raw keeps content-block order and unrendered text, with images and files in initially collapsed disclosures containing their complete recorded fields.
|
|
37
37
|
|
|
38
|
+
Requests remain pending while awaiting output, streaming, or retrying. A request without assistant output has no result link; interrupted output remains inspectable and both the request and assistant record show failure, including when the Step start is outside the loaded history. Retry counts and the last attempt error remain available after recovery; they do not change the current request status. A completed model response does not imply that its tools or turn have finished.
|
|
39
|
+
|
|
38
40
|
Thinking in the inspector uses compact Markdown at the inspector's fixed 13px size and 20px line height, independent of the content-size setting. Headings add bold weight without increasing size or line height. Assistant output keeps its regular Markdown typography.
|
|
39
41
|
|
|
40
42
|
### The timing overview
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
Streaming replies retain their known start time and TTFT. After settlement, token timestamps provide generation duration; throughput also requires recorded usage. Historical replies retain the same recorded timing. TTFT measures from the Step start to its first token, including output from an earlier retry attempt; an unloaded Step start or a stream without tokens leaves the corresponding metric unavailable.
|
|
43
45
|
|
|
44
46
|
A fixed Overview above the ledger projects real record start/duration timing from left to right; Assistant spans divide recorded TTFT from decoding, and a 500 ms hover reveals exact clock and duration details. Dragging an interval focuses the ledger on every record active at any point in that inclusive range; wheel gestures zoom the time domain; a right-button click clears the selected interval, and a right-button drag pans an already zoomed viewport. The initial view and streaming updates stay at the tail; scrolling upward suspends following so new records do not interrupt inspection of earlier rows.
|
|
45
47
|
|
|
@@ -53,9 +55,9 @@ A fixed Overview above the ledger projects real record start/duration timing fro
|
|
|
53
55
|
|
|
54
56
|
The view is a pure projection: Trajectory-owned Definitions assemble business records from the shared Session window — including durable cancellation-finalized prefixes, chunk-only interruption fallbacks, and interrupted Tool records — so Trajectory neither reads nor changes the Chat conversation snapshot. Its steering classifier retains only next-step Inbox IDs through persistent splice state and shares each current claimed batch across later Contexts.
|
|
55
57
|
|
|
56
|
-
Native and nested PTC Tool results retain their raw structured error details. Failed records show the error code in the ledger and the error name and code in the inspector.
|
|
58
|
+
Native and nested PTC Tool results retain their raw structured error details and presentation metadata. Failed records show the error code in the ledger and the error name and code in the inspector.
|
|
57
59
|
|
|
58
|
-
Tool records begin at durable tool/call events and
|
|
60
|
+
Tool records begin at durable tool/call events and retain complete argument text with a lazy `args` view, reused by paired results. Chat's transient preparing stage does not create Trajectory tool rows or alter historical tool timing.
|
|
59
61
|
|
|
60
62
|
A complete appended prompt without a loaded request header appears as a standalone system row; only its known text is available, with no inferred request options or tool catalog. Prepending its request history replaces that standalone presentation without duplicating the prompt. In-history system prompt changes compare against the most recent request state, including earlier prompt updates without a new request header. Each request retains the prompt and change that applied at its own position. Surface replacements, including compaction, restore the last nonempty surviving system prompt even without a new system event; an unloaded prompt remains unavailable until its page arrives.
|
|
61
63
|
|
|
@@ -112,5 +114,3 @@ These limits define what the view can show while work is in flight; they are cur
|
|
|
112
114
|
None.
|
|
113
115
|
|
|
114
116
|
</details>
|
|
115
|
-
|
|
116
|
-
**Runtime invariant:** No companion is published. It is a pure-consumer plugin: it emits no Cordis events and owns no mutable cross-plugin state; its view-slot registration is a plain effect whose disposal the slot ledger's own specs and this package's behavior specs observe directly.
|
package/README.zh.md
CHANGED
|
@@ -29,17 +29,19 @@ Trajectory 标签页让你以按轮次组织的事件记录表和交互式时间
|
|
|
29
29
|
|
|
30
30
|
### 检查记录
|
|
31
31
|
|
|
32
|
-
调用按已记录的工具名识别。对于 `run_code`,记录行展示程序说明,检查器显示带行号和语法高亮的源码。“代码”页提供换行、查看原始 JSON 参数的 `{}` 切换按钮,以及保留末尾换行的源码原样复制。复制原始参数时保留记录中 JSON 的空白。每个新打开的代码视图采用上次选择的换行状态;切换时,其他已打开视图保持原状。输出保留记录中的文本,完整 JSON 对象或数组使用树形展示。高亮只采用已记录工具 Schema 中明确且无冲突的 TypeScript 或 Python
|
|
32
|
+
调用按已记录的工具名识别。对于 `run_code`,记录行展示程序说明,检查器显示带行号和语法高亮的源码。“代码”页提供换行、查看原始 JSON 参数的 `{}` 切换按钮,以及保留末尾换行的源码原样复制。复制原始参数时保留记录中 JSON 的空白。每个新打开的代码视图采用上次选择的换行状态;切换时,其他已打开视图保持原状。输出保留记录中的文本,完整 JSON 对象或数组使用树形展示。高亮只采用已记录工具 Schema 中明确且无冲突的 TypeScript 或 Python 提示;提示缺失或冲突时显示纯文本源码。
|
|
33
33
|
|
|
34
34
|
选择、时间线导航、折叠与搜索只覆盖 React 可见窗口。请求编号与累计用量覆盖完整的驻留快照。选择记录会打开局部检查器,查看 token 用量、耗时、输入、输出、计时与持久保留的图片。图片 URL 使用 Conversation 拥有的逐会话缓存,因此 Chat 与 Trajectory 对每个附件共享一次已授权读取。用户记录会在文本旁同时显示非零的图片与普通文件数量,仅含附件的记录也显示这些数量。独立运行的压缩请求会按时间顺序显示在自己的 `Between turns` 区段中,而带编号的压缩仍位于其所属轮次内。
|
|
35
35
|
|
|
36
36
|
概述与预览共用正文之后的有序附件列表,保留重复引用。每行展示完整缩放的图片缩略图或文件类型图标、已记录的文件名(未命名图片使用本地化的编号名称),以及可用的已记录大小、类型和图片尺寸。零字节文件保留大小显示,截断的文件名通过提示显示完整名称。图片打开现有灯箱。原始内容保留内容块顺序和未渲染文本,图片与文件使用默认折叠的展开项,包含完整的已记录字段。
|
|
37
37
|
|
|
38
|
+
请求在等待输出、流式生成或重试期间保持等待状态。尚无助手输出的请求不提供结果链接;中断的输出仍可检查,请求和助手记录都显示失败,即使步骤起点位于已加载历史之外。恢复后仍可查看重试次数与上次尝试错误;这些历史信息不改变请求当前的状态。模型回复完成并不表示其工具或轮次已经结束。
|
|
39
|
+
|
|
38
40
|
检查器中的思考内容使用紧凑 Markdown,沿用检查器固定的 13px 字号和 20px 行高,不受内容字号设置影响。标题只加粗,不放大字号或行高。助手输出保留常规 Markdown 排版。
|
|
39
41
|
|
|
40
42
|
### 时间概览
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
流式回复保留已知的开始时间与 TTFT。回复结束后,token 时间戳用于计算生成耗时;吞吐量还需要已记录的用量。历史回复保留相同的已记录计时。TTFT 从步骤开始计量到首个 token,包括更早重试尝试中的输出;步骤起点尚未加载或流中没有 token 时,对应指标保持不可用。
|
|
43
45
|
|
|
44
46
|
固定在记录表上方的 Overview 区域从左到右投影记录的真实开始时间与耗时;助手时间条区分记录到的 TTFT 与解码时间,悬停 500 ms 后可查看精确时刻与耗时详情。拖选区间会把记录表聚焦到该闭区间内任何时刻处于活动的记录;滚轮手势用于缩放时间域;右键单击会清除所选区间,在已放大的视口上按住右键拖动则会平移。初始视图与流式更新都停留在尾部;向上滚动会暂停跟随,因此新记录不会打断对旧记录的检查。
|
|
45
47
|
|
|
@@ -53,9 +55,9 @@ Trajectory 标签页让你以按轮次组织的事件记录表和交互式时间
|
|
|
53
55
|
|
|
54
56
|
视图是纯投影:Trajectory 自有的定义从共享会话窗口组装业务记录——包括因取消而定稿并持久保留的前缀、仅有分片时采用的中断回退记录,以及被中断的工具记录——因此 Trajectory 既不读取也不改变 Chat 会话快照。其 steering(中途引导)分类器通过持续保留的拼接状态只保留下一步的 Inbox ID,并让后续上下文共享当前已认领的每个批次。
|
|
55
57
|
|
|
56
|
-
原生与嵌套 PTC
|
|
58
|
+
原生与嵌套 PTC 工具结果保留原始结构化错误详情和展示元数据。失败记录在记录表中显示错误代码,在检查器中显示错误名称和代码。
|
|
57
59
|
|
|
58
|
-
工具记录从持久 tool/call
|
|
60
|
+
工具记录从持久 tool/call 事件开始,保留完整参数文本和由配对结果复用的懒计算 `args` 视图。Chat 的瞬态 preparing 阶段不创建 Trajectory 工具行,也不改变历史工具计时。
|
|
59
61
|
|
|
60
62
|
完整的追加提示词在请求头未加载时显示为独立系统行;仅提供已知文本,不推断请求选项或工具目录。补入其请求历史后,该独立展示被替代而不重复提示词。历史中的系统提示词变更与最近的请求状态比较,包括没有新请求头的先前提示词更新。每个请求保留其所在位置生效的提示词与变更。包括压缩在内的 surface 替换会恢复最后一个非空的存活系统提示词,即使没有新的系统事件;未加载的提示词在对应分页到达前仍不可用。
|
|
61
63
|
|
|
@@ -112,5 +114,3 @@ Trajectory 要求会话壳把 composer 作为浮层置于全高记录表上方
|
|
|
112
114
|
无。
|
|
113
115
|
|
|
114
116
|
</details>
|
|
115
|
-
|
|
116
|
-
**运行时不变式:** 不发布伴生入口。这是纯消费插件,不发出 Cordis 事件,也不持有跨插件可变状态;其 view-slot 注册是普通 effect,slot ledger 自身的规格测试与本包的行为规格测试会直接观察其释放。
|
package/lib/client.js
CHANGED
|
@@ -211,9 +211,9 @@ window.__ModuleLoader__.load({
|
|
|
211
211
|
"details.toolCalls": "工具调用",
|
|
212
212
|
"details.subtoolCalls": "子工具调用",
|
|
213
213
|
"details.error": "错误",
|
|
214
|
+
"details.lastAttemptError": "上次尝试错误",
|
|
214
215
|
"details.failure.auth": "API 密钥无效",
|
|
215
216
|
"details.retry": "重试",
|
|
216
|
-
"details.scheduled": "已计划",
|
|
217
217
|
"details.retryDelay": "重试延迟",
|
|
218
218
|
"details.result": "结果",
|
|
219
219
|
"details.compacted": "已压缩",
|
|
@@ -406,9 +406,9 @@ window.__ModuleLoader__.load({
|
|
|
406
406
|
"details.toolCalls": "Tool calls",
|
|
407
407
|
"details.subtoolCalls": "Subtool calls",
|
|
408
408
|
"details.error": "Error",
|
|
409
|
+
"details.lastAttemptError": "Last attempt error",
|
|
409
410
|
"details.failure.auth": "API key is invalid",
|
|
410
411
|
"details.retry": "Retry",
|
|
411
|
-
"details.scheduled": "Scheduled",
|
|
412
412
|
"details.retryDelay": "Retry delay",
|
|
413
413
|
"details.result": "Result",
|
|
414
414
|
"details.compacted": "Compacted",
|
|
@@ -445,6 +445,648 @@ window.__ModuleLoader__.load({
|
|
|
445
445
|
"layout.compactionInterrupted": "Compaction was interrupted before completion."
|
|
446
446
|
};
|
|
447
447
|
//#endregion
|
|
448
|
+
//#region ../../util/values/src/partial-json.ts
|
|
449
|
+
/**
|
|
450
|
+
* Lazily scanned view of one JSON object's top-level fields, built from text
|
|
451
|
+
* that may still be streaming or from an already parsed object. Nothing is
|
|
452
|
+
* scanned until a reader asks; the view remembers every question it answered
|
|
453
|
+
* and reports changed answers when the owner refreshes for publication.
|
|
454
|
+
* Used for model tool-call arguments: a row reads the fields it
|
|
455
|
+
* cares about at whatever granularity it displays, at every stage of the call.
|
|
456
|
+
* @module @deepseek-ai/dsh-util-values/src/partial-json
|
|
457
|
+
*/
|
|
458
|
+
const SIMPLE_ESCAPES = {
|
|
459
|
+
"\"": "\"",
|
|
460
|
+
"\\": "\\",
|
|
461
|
+
"/": "/",
|
|
462
|
+
b: "\b",
|
|
463
|
+
f: "\f",
|
|
464
|
+
n: "\n",
|
|
465
|
+
r: "\r",
|
|
466
|
+
t: " "
|
|
467
|
+
};
|
|
468
|
+
const CONTENT_ESCAPE = /[\\\u0000-\u001f]/u;
|
|
469
|
+
function isWhitespace(c) {
|
|
470
|
+
return c === " " || c === "\n" || c === "\r" || c === " ";
|
|
471
|
+
}
|
|
472
|
+
function isHex(c) {
|
|
473
|
+
return c >= "0" && c <= "9" || c >= "a" && c <= "f" || c >= "A" && c <= "F";
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* The view. A streaming instance grows through {@link PartialArguments.append};
|
|
477
|
+
* {@link PartialArguments.fromText} and {@link PartialArguments.fromObject} build
|
|
478
|
+
* sealed instances over a finished call. Every reader is total: an absent or
|
|
479
|
+
* differently typed field answers `undefined` (or `false`), never throws.
|
|
480
|
+
*/
|
|
481
|
+
var PartialArguments = class PartialArguments {
|
|
482
|
+
/** The view of a call with no arguments available. */
|
|
483
|
+
static EMPTY = PartialArguments.fromObject({});
|
|
484
|
+
/**
|
|
485
|
+
* View finished argument text without scanning it until a reader asks.
|
|
486
|
+
* @param text - the complete argument JSON text.
|
|
487
|
+
* @returns a sealed view.
|
|
488
|
+
*/
|
|
489
|
+
static fromText(text) {
|
|
490
|
+
const view = new PartialArguments();
|
|
491
|
+
view.append(text);
|
|
492
|
+
view.sealed = true;
|
|
493
|
+
return view;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* View an already parsed argument payload, such as a PTC dispatch object.
|
|
497
|
+
* @param value - the parsed argument value.
|
|
498
|
+
* @returns a sealed view; a non-object payload has no fields.
|
|
499
|
+
*/
|
|
500
|
+
static fromObject(value) {
|
|
501
|
+
const view = new PartialArguments();
|
|
502
|
+
view.object = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
503
|
+
view.sealed = true;
|
|
504
|
+
return view;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* The source: text so far or a parsed object, plus whether it can still grow.
|
|
508
|
+
* These are the only enumerable fields, so two views over the same source
|
|
509
|
+
* compare equal structurally however far each has been read.
|
|
510
|
+
*/
|
|
511
|
+
chunks = [];
|
|
512
|
+
object;
|
|
513
|
+
sealed = false;
|
|
514
|
+
#ends = [];
|
|
515
|
+
#size = 0;
|
|
516
|
+
#consumed = 0;
|
|
517
|
+
#mode = "root";
|
|
518
|
+
#escape = false;
|
|
519
|
+
#keyStart = 0;
|
|
520
|
+
#keyEscaped = false;
|
|
521
|
+
#key = "";
|
|
522
|
+
#current = null;
|
|
523
|
+
#nestedEnds = [];
|
|
524
|
+
#nestedInString = false;
|
|
525
|
+
#invalidAt;
|
|
526
|
+
#invalidValue = false;
|
|
527
|
+
#entries = /* @__PURE__ */ new Map();
|
|
528
|
+
#order = [];
|
|
529
|
+
#reads = /* @__PURE__ */ new Map();
|
|
530
|
+
/** Whether this view rejects further appends; does not scan text or register reads. */
|
|
531
|
+
get isSealed() {
|
|
532
|
+
return this.sealed;
|
|
533
|
+
}
|
|
534
|
+
/** Whether indexing or a content read found invalid JSON; unread value contents are not validated. */
|
|
535
|
+
get invalid() {
|
|
536
|
+
this.scan();
|
|
537
|
+
return this.#mode === "invalid" || this.#invalidValue;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Retain streamed argument text without scanning or comparing observed answers.
|
|
541
|
+
* @param fragment - the text following every fragment appended before.
|
|
542
|
+
*/
|
|
543
|
+
append(fragment) {
|
|
544
|
+
if (this.sealed) throw new Error("PartialArguments: cannot append to a sealed view");
|
|
545
|
+
if (fragment.length === 0) return;
|
|
546
|
+
this.chunks.push(fragment);
|
|
547
|
+
this.#size += fragment.length;
|
|
548
|
+
this.#ends.push(this.#size);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Reconcile a streamed prefix with authoritative complete text without joining the fragments.
|
|
552
|
+
* @param text - the final argument text, which replaces missing or conflicting deltas.
|
|
553
|
+
* @returns this view sealed with its caches retained when every character matches; otherwise a new sealed view.
|
|
554
|
+
*/
|
|
555
|
+
settle(text) {
|
|
556
|
+
if (this.object !== void 0 || text.length !== this.#size) return PartialArguments.fromText(text);
|
|
557
|
+
let offset = 0;
|
|
558
|
+
for (const chunk of this.chunks) {
|
|
559
|
+
if (!text.startsWith(chunk, offset)) return PartialArguments.fromText(text);
|
|
560
|
+
offset += chunk.length;
|
|
561
|
+
}
|
|
562
|
+
this.chunks = text.length === 0 ? [] : [text];
|
|
563
|
+
this.#ends = text.length === 0 ? [] : [text.length];
|
|
564
|
+
this.sealed = true;
|
|
565
|
+
return this;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Compare observed answers and advance their publication baseline. Unread views remain unscanned.
|
|
569
|
+
* @returns whether any observed answer changed since its first read or the preceding refresh.
|
|
570
|
+
*/
|
|
571
|
+
refresh() {
|
|
572
|
+
if (this.#reads.size === 0) return false;
|
|
573
|
+
this.scan();
|
|
574
|
+
let changed = false;
|
|
575
|
+
let completions = false;
|
|
576
|
+
for (const read of this.#reads.values()) {
|
|
577
|
+
if (read.completion) {
|
|
578
|
+
completions = true;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
changed = this.refreshRead(read) || changed;
|
|
582
|
+
}
|
|
583
|
+
if (completions) {
|
|
584
|
+
for (const read of this.#reads.values()) if (read.completion) changed = this.refreshRead(read) || changed;
|
|
585
|
+
}
|
|
586
|
+
if (this.sealed) this.#reads.clear();
|
|
587
|
+
return changed;
|
|
588
|
+
}
|
|
589
|
+
refreshRead(read) {
|
|
590
|
+
const now = read.answer();
|
|
591
|
+
if (Object.is(now, read.last)) return false;
|
|
592
|
+
read.last = now;
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Check whether no further fields can arrive.
|
|
597
|
+
* @returns whether the outer object closed, indexing failed, or the view is sealed; unread values are not validated.
|
|
598
|
+
*/
|
|
599
|
+
closed() {
|
|
600
|
+
return this.remember("closed", "", () => this.closedNow());
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* List discovered fields in first-appearance order.
|
|
604
|
+
* @returns top-level keys seen so far, in first-appearance order.
|
|
605
|
+
*/
|
|
606
|
+
keys() {
|
|
607
|
+
return this.remember("keys", "", () => this.keysNow(), (keys) => keys.length);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Check whether a top-level field has appeared.
|
|
611
|
+
* @param key - argument name.
|
|
612
|
+
* @returns whether the field has appeared (a string opened or another value began).
|
|
613
|
+
*/
|
|
614
|
+
has(key) {
|
|
615
|
+
return this.remember("has", key, () => this.hasNow(key));
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Check whether a field's closing delimiter has arrived, without validating its contents.
|
|
619
|
+
* @param key - argument name.
|
|
620
|
+
* @returns whether its delimiter arrived and no content reader has reported an error for this value.
|
|
621
|
+
*/
|
|
622
|
+
complete(key) {
|
|
623
|
+
return this.remember("complete", key, () => this.completeNow(key));
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Read string length without materializing its text.
|
|
627
|
+
* @param key - argument name.
|
|
628
|
+
* @param options - change granularity for a streaming string.
|
|
629
|
+
* @returns decoded UTF-16 length of the string field so far; undefined when absent or not a string.
|
|
630
|
+
*/
|
|
631
|
+
stringLength(key, options) {
|
|
632
|
+
const step = Math.max(1, Math.floor(options?.step ?? 1));
|
|
633
|
+
const offset = options?.offset ?? 0;
|
|
634
|
+
return this.remember(`length:${step}:${offset}`, key, () => this.lengthNow(key), (length) => length === void 0 ? void 0 : Math.ceil((length + offset) / step));
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Check a string against a decoded UTF-16 length limit without materializing it.
|
|
638
|
+
* @param key - argument name.
|
|
639
|
+
* @param maxLength - decoded UTF-16 limit, floored to at least zero.
|
|
640
|
+
* @returns whether the string is longer than the limit; false when absent or not a string.
|
|
641
|
+
*/
|
|
642
|
+
stringExceeds(key, maxLength) {
|
|
643
|
+
const limit = Math.max(0, Math.floor(maxLength));
|
|
644
|
+
return this.remember(`exceeds:${limit}`, key, () => (this.lengthNow(key, limit + 1) ?? 0) > limit);
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Read a decoded string, including a streaming prefix.
|
|
648
|
+
* @param key - argument name.
|
|
649
|
+
* @returns the string field's decoded text so far; undefined when absent or not a string.
|
|
650
|
+
*/
|
|
651
|
+
text(key) {
|
|
652
|
+
return this.remember("text", key, () => this.textNow(key));
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Read at most the first decoded UTF-16 units of a string.
|
|
656
|
+
* @param key - argument name.
|
|
657
|
+
* @param maxLength - maximum decoded UTF-16 length, floored to at least one.
|
|
658
|
+
* @returns the bounded string prefix; undefined when absent or not a string.
|
|
659
|
+
*/
|
|
660
|
+
textPrefix(key, maxLength) {
|
|
661
|
+
const limit = Math.max(1, Math.floor(maxLength));
|
|
662
|
+
return this.remember(`prefix:${limit}`, key, () => this.textPrefixNow(key, limit));
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Read a completed non-string argument.
|
|
666
|
+
* @param key - argument name.
|
|
667
|
+
* @returns the parsed non-string value once it closed; undefined while open, absent, or a string.
|
|
668
|
+
*/
|
|
669
|
+
value(key) {
|
|
670
|
+
return this.remember("value", key, () => this.valueNow(key));
|
|
671
|
+
}
|
|
672
|
+
/** Answer a question and, on a streaming view, remember it for change detection. */
|
|
673
|
+
remember(kind, key, read, comparison) {
|
|
674
|
+
this.scan();
|
|
675
|
+
const result = read();
|
|
676
|
+
if (!this.sealed) {
|
|
677
|
+
const id = `${kind}/${key}`;
|
|
678
|
+
if (!this.#reads.has(id)) this.#reads.set(id, {
|
|
679
|
+
completion: kind === "complete",
|
|
680
|
+
answer: comparison === void 0 ? read : () => comparison(read()),
|
|
681
|
+
last: comparison === void 0 ? result : comparison(result)
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
return result;
|
|
685
|
+
}
|
|
686
|
+
closedNow() {
|
|
687
|
+
return this.sealed || this.#mode === "closed" || this.#mode === "invalid";
|
|
688
|
+
}
|
|
689
|
+
keysNow() {
|
|
690
|
+
return this.object === void 0 ? this.#order : Object.keys(this.object);
|
|
691
|
+
}
|
|
692
|
+
hasNow(key) {
|
|
693
|
+
return this.object === void 0 ? this.#entries.has(key) : Object.hasOwn(this.object, key);
|
|
694
|
+
}
|
|
695
|
+
completeNow(key) {
|
|
696
|
+
if (this.object !== void 0) return Object.hasOwn(this.object, key);
|
|
697
|
+
const entry = this.#entries.get(key);
|
|
698
|
+
return entry !== void 0 && entry.end >= 0 && (entry.kind === "string" ? entry.invalidAt === void 0 : !entry.invalid);
|
|
699
|
+
}
|
|
700
|
+
lengthNow(key, limit = Number.POSITIVE_INFINITY) {
|
|
701
|
+
if (this.object !== void 0) {
|
|
702
|
+
const field = Object.hasOwn(this.object, key) ? this.object[key] : void 0;
|
|
703
|
+
return typeof field === "string" ? field.length : void 0;
|
|
704
|
+
}
|
|
705
|
+
const entry = this.#entries.get(key);
|
|
706
|
+
if (entry?.kind !== "string") return void 0;
|
|
707
|
+
if (entry.text !== void 0 && entry.text.at === entry.end) return entry.text.length;
|
|
708
|
+
const read = entry.length ??= {
|
|
709
|
+
at: entry.start,
|
|
710
|
+
length: 0,
|
|
711
|
+
text: ""
|
|
712
|
+
};
|
|
713
|
+
this.readString(entry, read, limit, false);
|
|
714
|
+
return read.length;
|
|
715
|
+
}
|
|
716
|
+
textNow(key) {
|
|
717
|
+
if (this.object !== void 0) {
|
|
718
|
+
const field = Object.hasOwn(this.object, key) ? this.object[key] : void 0;
|
|
719
|
+
return typeof field === "string" ? field : void 0;
|
|
720
|
+
}
|
|
721
|
+
const entry = this.#entries.get(key);
|
|
722
|
+
if (entry?.kind !== "string") return void 0;
|
|
723
|
+
if (entry.text === void 0 && entry.end >= 0 && entry.needsDecoding && entry.invalidAt === void 0) {
|
|
724
|
+
let text;
|
|
725
|
+
try {
|
|
726
|
+
text = JSON.parse(`"${this.slice(entry.start, entry.end)}"`);
|
|
727
|
+
} catch (_error) {}
|
|
728
|
+
if (text !== void 0) entry.text = {
|
|
729
|
+
at: entry.end,
|
|
730
|
+
length: text.length,
|
|
731
|
+
text
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
const read = entry.text ??= {
|
|
735
|
+
at: entry.start,
|
|
736
|
+
length: 0,
|
|
737
|
+
text: ""
|
|
738
|
+
};
|
|
739
|
+
this.readString(entry, read, Number.POSITIVE_INFINITY, true);
|
|
740
|
+
return read.text;
|
|
741
|
+
}
|
|
742
|
+
textPrefixNow(key, maxLength) {
|
|
743
|
+
if (this.object !== void 0) {
|
|
744
|
+
const field = Object.hasOwn(this.object, key) ? this.object[key] : void 0;
|
|
745
|
+
return typeof field === "string" ? field.slice(0, maxLength) : void 0;
|
|
746
|
+
}
|
|
747
|
+
const entry = this.#entries.get(key);
|
|
748
|
+
if (entry?.kind !== "string") return void 0;
|
|
749
|
+
const prefixes = entry.prefixes ??= /* @__PURE__ */ new Map();
|
|
750
|
+
let read = prefixes.get(maxLength);
|
|
751
|
+
if (read === void 0) {
|
|
752
|
+
read = {
|
|
753
|
+
at: entry.start,
|
|
754
|
+
length: 0,
|
|
755
|
+
text: ""
|
|
756
|
+
};
|
|
757
|
+
prefixes.set(maxLength, read);
|
|
758
|
+
}
|
|
759
|
+
this.readString(entry, read, maxLength, true);
|
|
760
|
+
return read.text;
|
|
761
|
+
}
|
|
762
|
+
valueNow(key) {
|
|
763
|
+
if (this.object !== void 0) {
|
|
764
|
+
if (!Object.hasOwn(this.object, key)) return void 0;
|
|
765
|
+
const field = this.object[key];
|
|
766
|
+
return typeof field === "string" ? void 0 : field;
|
|
767
|
+
}
|
|
768
|
+
const entry = this.#entries.get(key);
|
|
769
|
+
if (entry?.kind !== "value" || entry.end < 0 || entry.invalid) return void 0;
|
|
770
|
+
if (entry.parsed === void 0) try {
|
|
771
|
+
entry.parsed = JSON.parse(this.slice(entry.start, entry.end));
|
|
772
|
+
} catch (_error) {
|
|
773
|
+
entry.invalid = true;
|
|
774
|
+
this.#invalidValue = true;
|
|
775
|
+
}
|
|
776
|
+
return entry.parsed;
|
|
777
|
+
}
|
|
778
|
+
chunkAt(at) {
|
|
779
|
+
let low = 0;
|
|
780
|
+
let high = this.#ends.length;
|
|
781
|
+
while (low < high) {
|
|
782
|
+
const mid = low + high >>> 1;
|
|
783
|
+
if (this.#ends[mid] <= at) low = mid + 1;
|
|
784
|
+
else high = mid;
|
|
785
|
+
}
|
|
786
|
+
return low;
|
|
787
|
+
}
|
|
788
|
+
/** Materialize only a requested range, never the cumulative source. */
|
|
789
|
+
slice(start, end) {
|
|
790
|
+
if (start >= end) return "";
|
|
791
|
+
const first = this.chunkAt(start);
|
|
792
|
+
const last = this.chunkAt(end - 1);
|
|
793
|
+
const base = first === 0 ? 0 : this.#ends[first - 1];
|
|
794
|
+
if (first === last) return this.chunks[first].slice(start - base, end - base);
|
|
795
|
+
const parts = [this.chunks[first].slice(start - base)];
|
|
796
|
+
for (let i = first + 1; i < last; i++) parts.push(this.chunks[i]);
|
|
797
|
+
parts.push(this.chunks[last].slice(0, end - this.#ends[last - 1]));
|
|
798
|
+
return parts.join("");
|
|
799
|
+
}
|
|
800
|
+
readString(entry, read, limit, materialize) {
|
|
801
|
+
const end = Math.min(entry.end < 0 ? this.#consumed : entry.end, entry.invalidAt ?? Number.POSITIVE_INFINITY, this.#invalidAt ?? Number.POSITIVE_INFINITY);
|
|
802
|
+
if (!entry.needsDecoding) {
|
|
803
|
+
const length = Math.min(end - read.at, limit - read.length);
|
|
804
|
+
if (length <= 0) return;
|
|
805
|
+
if (materialize) read.text += this.slice(read.at, read.at + length);
|
|
806
|
+
read.at += length;
|
|
807
|
+
read.length += length;
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
let chunkIndex = this.chunkAt(read.at);
|
|
811
|
+
while (read.at < end && read.length < limit) {
|
|
812
|
+
const base = chunkIndex === 0 ? 0 : this.#ends[chunkIndex - 1];
|
|
813
|
+
const chunk = this.chunks[chunkIndex];
|
|
814
|
+
const remaining = chunk.slice(read.at - base, Math.min(chunk.length, end - base));
|
|
815
|
+
const boundary = remaining.search(CONTENT_ESCAPE);
|
|
816
|
+
const length = Math.min(boundary < 0 ? remaining.length : boundary, limit - read.length);
|
|
817
|
+
if (length > 0) {
|
|
818
|
+
if (materialize) read.text += remaining.slice(0, length);
|
|
819
|
+
read.at += length;
|
|
820
|
+
read.length += length;
|
|
821
|
+
if (read.at === base + chunk.length) chunkIndex++;
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
const type = remaining.length > 1 ? remaining[1] : read.at + 1 < end ? this.chunks[chunkIndex + 1][0] : void 0;
|
|
825
|
+
let decoded;
|
|
826
|
+
let width = 2;
|
|
827
|
+
if (remaining[0] === "\\" && type === void 0 && entry.end < 0) return;
|
|
828
|
+
if (remaining[0] === "\\" && type === "u") {
|
|
829
|
+
const hex = this.slice(read.at + 2, Math.min(end, read.at + 6));
|
|
830
|
+
let valid = true;
|
|
831
|
+
for (let i = 0; i < hex.length; i++) if (!isHex(hex[i])) valid = false;
|
|
832
|
+
if (valid) {
|
|
833
|
+
if (hex.length < 4 && entry.end < 0) return;
|
|
834
|
+
if (hex.length === 4) decoded = String.fromCharCode(Number.parseInt(hex, 16));
|
|
835
|
+
}
|
|
836
|
+
width = 6;
|
|
837
|
+
} else if (remaining[0] === "\\" && type !== void 0) decoded = SIMPLE_ESCAPES[type];
|
|
838
|
+
if (decoded === void 0) {
|
|
839
|
+
entry.invalidAt = read.at;
|
|
840
|
+
this.#invalidValue = true;
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (materialize) read.text += decoded;
|
|
844
|
+
read.length++;
|
|
845
|
+
read.at += width;
|
|
846
|
+
while (chunkIndex < this.chunks.length && read.at >= this.#ends[chunkIndex]) chunkIndex++;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/** Locate new field ranges without decoding or parsing their contents. */
|
|
850
|
+
scan() {
|
|
851
|
+
if (this.object !== void 0 || this.#consumed === this.#size) return;
|
|
852
|
+
for (let i = this.chunkAt(this.#consumed); i < this.chunks.length && this.#invalidAt === void 0; i++) {
|
|
853
|
+
const pending = this.chunks[i];
|
|
854
|
+
const base = i === 0 ? 0 : this.#ends[i - 1];
|
|
855
|
+
for (let index = this.#consumed - base; index < pending.length && this.#mode !== "invalid"; index++) {
|
|
856
|
+
if (this.#mode === "string" || this.#mode === "nested" && this.#nestedInString) {
|
|
857
|
+
const end = this.stringBoundary(pending, index);
|
|
858
|
+
this.#consumed += end - index;
|
|
859
|
+
index = end;
|
|
860
|
+
if (index === pending.length) break;
|
|
861
|
+
}
|
|
862
|
+
this.step(pending[index], this.#consumed);
|
|
863
|
+
this.#consumed++;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
/** Only raw quotes and their preceding backslash runs can terminate a string. */
|
|
868
|
+
stringBoundary(fragment, start) {
|
|
869
|
+
let at = start;
|
|
870
|
+
while (true) {
|
|
871
|
+
const quote = fragment.indexOf("\"", at);
|
|
872
|
+
const end = quote < 0 ? fragment.length : quote;
|
|
873
|
+
if (this.#mode === "string") {
|
|
874
|
+
const entry = this.#current;
|
|
875
|
+
if (!entry.needsDecoding && CONTENT_ESCAPE.test(fragment.slice(at, end))) entry.needsDecoding = true;
|
|
876
|
+
}
|
|
877
|
+
let slashStart = end;
|
|
878
|
+
while (slashStart > at && fragment[slashStart - 1] === "\\") slashStart--;
|
|
879
|
+
const escaped = (end - slashStart) % 2 === 1 !== (slashStart === at && this.#escape);
|
|
880
|
+
this.#escape = quote < 0 && escaped;
|
|
881
|
+
if (quote < 0 || !escaped) return end;
|
|
882
|
+
at = quote + 1;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
step(c, at) {
|
|
886
|
+
switch (this.#mode) {
|
|
887
|
+
case "root":
|
|
888
|
+
if (isWhitespace(c)) return;
|
|
889
|
+
if (c === "{") {
|
|
890
|
+
this.#mode = "key-or-end";
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
this.fail();
|
|
894
|
+
return;
|
|
895
|
+
case "key-or-end":
|
|
896
|
+
if (isWhitespace(c)) return;
|
|
897
|
+
if (c === "}") {
|
|
898
|
+
this.#mode = "closed";
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
if (c === "\"") {
|
|
902
|
+
this.beginKey(at);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
this.fail();
|
|
906
|
+
return;
|
|
907
|
+
case "key-only":
|
|
908
|
+
if (isWhitespace(c)) return;
|
|
909
|
+
if (c === "\"") {
|
|
910
|
+
this.beginKey(at);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
this.fail();
|
|
914
|
+
return;
|
|
915
|
+
case "key":
|
|
916
|
+
this.stepKey(c, at);
|
|
917
|
+
return;
|
|
918
|
+
case "colon":
|
|
919
|
+
if (isWhitespace(c)) return;
|
|
920
|
+
if (c === ":") {
|
|
921
|
+
this.#mode = "value";
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
this.fail();
|
|
925
|
+
return;
|
|
926
|
+
case "value":
|
|
927
|
+
this.beginValue(c, at);
|
|
928
|
+
return;
|
|
929
|
+
case "string": {
|
|
930
|
+
const entry = this.#current;
|
|
931
|
+
entry.end = at;
|
|
932
|
+
this.#current = null;
|
|
933
|
+
this.#mode = "comma-or-end";
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
case "scalar":
|
|
937
|
+
this.stepScalar(c, at);
|
|
938
|
+
return;
|
|
939
|
+
case "nested":
|
|
940
|
+
this.stepNested(c, at);
|
|
941
|
+
return;
|
|
942
|
+
case "comma-or-end":
|
|
943
|
+
if (isWhitespace(c)) return;
|
|
944
|
+
if (c === ",") {
|
|
945
|
+
this.#mode = "key-only";
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (c === "}") {
|
|
949
|
+
this.#mode = "closed";
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
this.fail();
|
|
953
|
+
return;
|
|
954
|
+
case "closed":
|
|
955
|
+
if (isWhitespace(c)) return;
|
|
956
|
+
this.fail();
|
|
957
|
+
return;
|
|
958
|
+
/* v8 ignore next 2 -- scan() stops stepping once the view is invalid. */
|
|
959
|
+
case "invalid": return;
|
|
960
|
+
/* v8 ignore next 2 -- Every scanner mode has a handler above. */
|
|
961
|
+
default: assertNever(this.#mode);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
fail() {
|
|
965
|
+
this.#invalidAt = this.#consumed;
|
|
966
|
+
this.#mode = "invalid";
|
|
967
|
+
this.#current = null;
|
|
968
|
+
}
|
|
969
|
+
beginKey(at) {
|
|
970
|
+
this.#mode = "key";
|
|
971
|
+
this.#keyStart = at + 1;
|
|
972
|
+
this.#keyEscaped = false;
|
|
973
|
+
this.#escape = false;
|
|
974
|
+
}
|
|
975
|
+
stepKey(c, at) {
|
|
976
|
+
if (c < " ") {
|
|
977
|
+
this.fail();
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
if (this.#escape) {
|
|
981
|
+
this.#escape = false;
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
if (c === "\\") {
|
|
985
|
+
this.#escape = true;
|
|
986
|
+
this.#keyEscaped = true;
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (c !== "\"") return;
|
|
990
|
+
const raw = this.slice(this.#keyStart, at);
|
|
991
|
+
if (this.#keyEscaped) try {
|
|
992
|
+
this.#key = JSON.parse(`"${raw}"`);
|
|
993
|
+
} catch (_error) {
|
|
994
|
+
this.fail();
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
else this.#key = raw;
|
|
998
|
+
this.#mode = "colon";
|
|
999
|
+
}
|
|
1000
|
+
open(entry) {
|
|
1001
|
+
if (!this.#entries.has(this.#key)) this.#order.push(this.#key);
|
|
1002
|
+
this.#entries.set(this.#key, entry);
|
|
1003
|
+
this.#current = entry;
|
|
1004
|
+
}
|
|
1005
|
+
beginValue(c, at) {
|
|
1006
|
+
if (isWhitespace(c)) return;
|
|
1007
|
+
if (c === "\"") {
|
|
1008
|
+
this.open({
|
|
1009
|
+
kind: "string",
|
|
1010
|
+
start: at + 1,
|
|
1011
|
+
end: -1,
|
|
1012
|
+
needsDecoding: false,
|
|
1013
|
+
invalidAt: void 0,
|
|
1014
|
+
length: void 0,
|
|
1015
|
+
text: void 0,
|
|
1016
|
+
prefixes: void 0
|
|
1017
|
+
});
|
|
1018
|
+
this.#escape = false;
|
|
1019
|
+
this.#mode = "string";
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (c === "}" || c === "," || c === ":" || c === "]") {
|
|
1023
|
+
this.fail();
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
this.open({
|
|
1027
|
+
kind: "value",
|
|
1028
|
+
start: at,
|
|
1029
|
+
end: -1,
|
|
1030
|
+
parsed: void 0,
|
|
1031
|
+
invalid: false
|
|
1032
|
+
});
|
|
1033
|
+
if (c === "{" || c === "[") {
|
|
1034
|
+
this.#mode = "nested";
|
|
1035
|
+
this.#nestedEnds = [c === "{" ? "}" : "]"];
|
|
1036
|
+
this.#nestedInString = false;
|
|
1037
|
+
this.#escape = false;
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
this.#mode = "scalar";
|
|
1041
|
+
}
|
|
1042
|
+
stepScalar(c, at) {
|
|
1043
|
+
if (c !== "," && c !== "}" && !isWhitespace(c)) return;
|
|
1044
|
+
this.closeValue(at);
|
|
1045
|
+
this.#mode = c === "," ? "key-only" : c === "}" ? "closed" : "comma-or-end";
|
|
1046
|
+
}
|
|
1047
|
+
stepNested(c, at) {
|
|
1048
|
+
if (this.#nestedInString) {
|
|
1049
|
+
this.#nestedInString = false;
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
if (c === "\"") {
|
|
1053
|
+
this.#nestedInString = true;
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
if (c === "{" || c === "[") {
|
|
1057
|
+
this.#nestedEnds.push(c === "{" ? "}" : "]");
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
if (c === "}" || c === "]") {
|
|
1061
|
+
if (this.#nestedEnds.pop() !== c) {
|
|
1062
|
+
this.fail();
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
if (this.#nestedEnds.length === 0) {
|
|
1066
|
+
this.closeValue(at + 1);
|
|
1067
|
+
this.#mode = "comma-or-end";
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
closeValue(end) {
|
|
1072
|
+
const entry = this.#current;
|
|
1073
|
+
entry.end = end;
|
|
1074
|
+
this.#current = null;
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
//#endregion
|
|
1078
|
+
//#region ../../util/values/src/index.ts
|
|
1079
|
+
/**
|
|
1080
|
+
* Mark an unreachable closed-union branch.
|
|
1081
|
+
* @param value - impossible value; an unhandled typed variant fails at the call site.
|
|
1082
|
+
* @param context - optional switch-site label included in the failure message.
|
|
1083
|
+
* @returns never; a runtime value that escaped its type always throws.
|
|
1084
|
+
*/
|
|
1085
|
+
function assertNever(value, context) {
|
|
1086
|
+
const rendered = JSON.stringify(value) ?? String(value);
|
|
1087
|
+
throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
|
|
1088
|
+
}
|
|
1089
|
+
//#endregion
|
|
448
1090
|
//#region ../../llm/llm/src/assistant-stream.ts
|
|
449
1091
|
/**
|
|
450
1092
|
* Whether one chunk carries the model's first output token for latency measurement.
|
|
@@ -692,7 +1334,6 @@ window.__ModuleLoader__.load({
|
|
|
692
1334
|
startSeq,
|
|
693
1335
|
startTime,
|
|
694
1336
|
started,
|
|
695
|
-
sawChunk: false,
|
|
696
1337
|
blocks: [],
|
|
697
1338
|
visibleBlocks: 0,
|
|
698
1339
|
firstVisibleSeq: void 0,
|
|
@@ -735,7 +1376,6 @@ window.__ModuleLoader__.load({
|
|
|
735
1376
|
function updateChunk(state, chunk, seq, time) {
|
|
736
1377
|
if (chunk.type === "usage") return {
|
|
737
1378
|
...state,
|
|
738
|
-
sawChunk: true,
|
|
739
1379
|
usage: addUsage$1(state.usage, chunk.usage)
|
|
740
1380
|
};
|
|
741
1381
|
const blocks = [...state.blocks];
|
|
@@ -790,15 +1430,11 @@ window.__ModuleLoader__.load({
|
|
|
790
1430
|
previousVisible = blockIsVisible(blocks[chunk.index]);
|
|
791
1431
|
blocks[chunk.index] = toAssistantBlock(chunk.block);
|
|
792
1432
|
break;
|
|
793
|
-
default: return
|
|
794
|
-
...state,
|
|
795
|
-
sawChunk: true
|
|
796
|
-
};
|
|
1433
|
+
default: return state;
|
|
797
1434
|
}
|
|
798
1435
|
const visibleBlocks = state.visibleBlocks - Number(previousVisible) + Number(blockIsVisible(blocks[changedIndex]));
|
|
799
1436
|
return {
|
|
800
1437
|
...state,
|
|
801
|
-
sawChunk: true,
|
|
802
1438
|
blocks,
|
|
803
1439
|
visibleBlocks,
|
|
804
1440
|
...visibleBlocks > 0 && state.firstVisibleSeq === void 0 ? {
|
|
@@ -819,7 +1455,6 @@ window.__ModuleLoader__.load({
|
|
|
819
1455
|
const blocks = toAssistantBlocks(event.data.message.content);
|
|
820
1456
|
return {
|
|
821
1457
|
...settleTiming(state, event),
|
|
822
|
-
sawChunk: false,
|
|
823
1458
|
blocks,
|
|
824
1459
|
visibleBlocks: countVisibleBlocks(blocks),
|
|
825
1460
|
final: match,
|
|
@@ -893,7 +1528,7 @@ window.__ModuleLoader__.load({
|
|
|
893
1528
|
}
|
|
894
1529
|
function assistantRequest(state, node, boundary) {
|
|
895
1530
|
if (!state.started) return void 0;
|
|
896
|
-
const status = node !== void 0
|
|
1531
|
+
const status = node !== void 0 ? node.interrupted === true ? "error" : "complete" : boundary !== void 0 ? "error" : "running";
|
|
897
1532
|
return {
|
|
898
1533
|
purpose: "assistant",
|
|
899
1534
|
startSeq: state.startSeq,
|
|
@@ -970,10 +1605,14 @@ window.__ModuleLoader__.load({
|
|
|
970
1605
|
if (state === void 0) return null;
|
|
971
1606
|
const node = finalNode(state, context);
|
|
972
1607
|
const boundary = closedBoundary(context);
|
|
973
|
-
const partial = node === void 0 && boundary === void 0 && state.
|
|
1608
|
+
const partial = node === void 0 && boundary === void 0 && state.blocks.length > 0 ? {
|
|
974
1609
|
turn: state.turn,
|
|
975
1610
|
step: state.step,
|
|
976
|
-
blocks: compactBlocks(state.blocks)
|
|
1611
|
+
blocks: compactBlocks(state.blocks),
|
|
1612
|
+
timing: {
|
|
1613
|
+
stepStartTime: state.started ? state.startTime : null,
|
|
1614
|
+
firstTokenTime: state.firstTokenTime ?? null
|
|
1615
|
+
}
|
|
977
1616
|
} : null;
|
|
978
1617
|
const request = assistantRequest(state, node, boundary);
|
|
979
1618
|
if (node === void 0 && partial === null && request === void 0) return null;
|
|
@@ -1654,6 +2293,7 @@ window.__ModuleLoader__.load({
|
|
|
1654
2293
|
callId: String(match.event.data.callId),
|
|
1655
2294
|
name: match.event.data.name,
|
|
1656
2295
|
argsRaw: match.event.data.arguments,
|
|
2296
|
+
args: PartialArguments.fromText(match.event.data.arguments),
|
|
1657
2297
|
turn: match.event.data.turn,
|
|
1658
2298
|
step: match.event.data.step,
|
|
1659
2299
|
time: match.event.time,
|
|
@@ -1668,6 +2308,8 @@ window.__ModuleLoader__.load({
|
|
|
1668
2308
|
seq: match.event.seq,
|
|
1669
2309
|
time: match.event.time,
|
|
1670
2310
|
callId: String(message.source.callId),
|
|
2311
|
+
name: previous?.name ?? "",
|
|
2312
|
+
args: previous?.args ?? PartialArguments.EMPTY,
|
|
1671
2313
|
call: previous === void 0 ? null : {
|
|
1672
2314
|
name: previous.name,
|
|
1673
2315
|
argsRaw: previous.argsRaw
|
|
@@ -1693,6 +2335,7 @@ window.__ModuleLoader__.load({
|
|
|
1693
2335
|
parentCallId: data.parentCallId,
|
|
1694
2336
|
name: data.name,
|
|
1695
2337
|
argsRaw: JSON.stringify(data.arguments),
|
|
2338
|
+
args: PartialArguments.fromObject(data.arguments),
|
|
1696
2339
|
turn: locationTurn(match),
|
|
1697
2340
|
step: locationStep(match),
|
|
1698
2341
|
time: match.event.time,
|
|
@@ -1706,12 +2349,15 @@ window.__ModuleLoader__.load({
|
|
|
1706
2349
|
time: match.event.time,
|
|
1707
2350
|
callId: data.subCallId,
|
|
1708
2351
|
parentCallId: data.parentCallId,
|
|
2352
|
+
name: data.name,
|
|
2353
|
+
args: previous?.args ?? PartialArguments.fromObject(data.arguments),
|
|
1709
2354
|
call: {
|
|
1710
2355
|
name: data.name,
|
|
1711
2356
|
argsRaw: JSON.stringify(data.arguments)
|
|
1712
2357
|
},
|
|
1713
2358
|
callTime: previous === void 0 || "kind" in previous ? null : previous.time,
|
|
1714
2359
|
content: data.content ?? [],
|
|
2360
|
+
meta: data.meta,
|
|
1715
2361
|
isError: data.isError === true,
|
|
1716
2362
|
...data.error === void 0 ? {} : { error: data.error },
|
|
1717
2363
|
subCalls: []
|
|
@@ -1800,6 +2446,8 @@ window.__ModuleLoader__.load({
|
|
|
1800
2446
|
time: interruptedAt.time,
|
|
1801
2447
|
callId: block.callId,
|
|
1802
2448
|
...block.parentCallId === void 0 ? {} : { parentCallId: block.parentCallId },
|
|
2449
|
+
name: block.name,
|
|
2450
|
+
args: block.args,
|
|
1803
2451
|
call: {
|
|
1804
2452
|
name: block.name,
|
|
1805
2453
|
argsRaw: block.argsRaw
|
|
@@ -1890,7 +2538,7 @@ window.__ModuleLoader__.load({
|
|
|
1890
2538
|
ctx.uiConversation.events.register(trajectoryToolDefinition);
|
|
1891
2539
|
}
|
|
1892
2540
|
//#endregion
|
|
1893
|
-
//#region ../../../node_modules/.pnpm/@tanstack+virtual-core@3.17.
|
|
2541
|
+
//#region ../../../node_modules/.pnpm/@tanstack+virtual-core@3.17.8/node_modules/@tanstack/virtual-core/dist/esm/lazy-measurements.js
|
|
1894
2542
|
function createLazyMeasurementsView(count, flat, getItemKey) {
|
|
1895
2543
|
const cache = new Array(count);
|
|
1896
2544
|
return new Proxy(cache, { get(target, prop, receiver) {
|
|
@@ -1920,7 +2568,7 @@ window.__ModuleLoader__.load({
|
|
|
1920
2568
|
} });
|
|
1921
2569
|
}
|
|
1922
2570
|
//#endregion
|
|
1923
|
-
//#region ../../../node_modules/.pnpm/@tanstack+virtual-core@3.17.
|
|
2571
|
+
//#region ../../../node_modules/.pnpm/@tanstack+virtual-core@3.17.8/node_modules/@tanstack/virtual-core/dist/esm/utils.js
|
|
1924
2572
|
function memo$1(getDeps, fn, opts) {
|
|
1925
2573
|
let deps = opts.initialDeps ?? [];
|
|
1926
2574
|
let result;
|
|
@@ -1946,13 +2594,15 @@ window.__ModuleLoader__.load({
|
|
|
1946
2594
|
const approxEqual = (a, b) => Math.abs(a - b) < 1.01;
|
|
1947
2595
|
const debounce = (targetWindow, fn, ms) => {
|
|
1948
2596
|
let timeoutId;
|
|
1949
|
-
return function(...args) {
|
|
2597
|
+
return Object.assign(function(...args) {
|
|
1950
2598
|
targetWindow.clearTimeout(timeoutId);
|
|
1951
2599
|
timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
|
|
1952
|
-
}
|
|
2600
|
+
}, { cancel: () => {
|
|
2601
|
+
targetWindow.clearTimeout(timeoutId);
|
|
2602
|
+
} });
|
|
1953
2603
|
};
|
|
1954
2604
|
//#endregion
|
|
1955
|
-
//#region ../../../node_modules/.pnpm/@tanstack+virtual-core@3.17.
|
|
2605
|
+
//#region ../../../node_modules/.pnpm/@tanstack+virtual-core@3.17.8/node_modules/@tanstack/virtual-core/dist/esm/index.js
|
|
1956
2606
|
let _isIOSResult;
|
|
1957
2607
|
const isIOSWebKit = () => {
|
|
1958
2608
|
if (_isIOSResult !== void 0) return _isIOSResult;
|
|
@@ -2034,6 +2684,7 @@ window.__ModuleLoader__.load({
|
|
|
2034
2684
|
return () => {
|
|
2035
2685
|
element.removeEventListener("scroll", handler);
|
|
2036
2686
|
if (registerScrollendEvent) element.removeEventListener("scrollend", endHandler);
|
|
2687
|
+
fallback?.cancel();
|
|
2037
2688
|
};
|
|
2038
2689
|
};
|
|
2039
2690
|
const observeElementOffset = (instance, cb) => observeOffset(instance, cb, (el) => {
|
|
@@ -2115,6 +2766,7 @@ window.__ModuleLoader__.load({
|
|
|
2115
2766
|
}
|
|
2116
2767
|
return;
|
|
2117
2768
|
}
|
|
2769
|
+
if (!this.isIndexInRange(index)) return;
|
|
2118
2770
|
if (this.shouldMeasureDuringScroll(index)) this.resizeItem(index, this.options.measureElement(node, entry, this));
|
|
2119
2771
|
};
|
|
2120
2772
|
this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
@@ -2258,6 +2910,8 @@ window.__ModuleLoader__.load({
|
|
|
2258
2910
|
this.rafId = null;
|
|
2259
2911
|
}
|
|
2260
2912
|
this.scrollState = null;
|
|
2913
|
+
this.isScrolling = false;
|
|
2914
|
+
this.scrollDirection = null;
|
|
2261
2915
|
this._iosDeferredAdjustment = 0;
|
|
2262
2916
|
this._iosTouching = false;
|
|
2263
2917
|
this._iosJustTouchEnded = false;
|
|
@@ -2410,6 +3064,7 @@ window.__ModuleLoader__.load({
|
|
|
2410
3064
|
gap
|
|
2411
3065
|
};
|
|
2412
3066
|
}, { key: false });
|
|
3067
|
+
this.isIndexInRange = (index) => index >= 0 && index < this.options.count;
|
|
2413
3068
|
this.getMeasurements = memo$1(() => [this.getMeasurementOptions(), this.itemSizeCacheVersion], ({ count, paddingStart, scrollMargin, getItemKey, enabled, lanes, laneAssignmentMode, gap }, _itemSizeCacheVersion) => {
|
|
2414
3069
|
const itemSizeCache = this.itemSizeCache;
|
|
2415
3070
|
if (!enabled) {
|
|
@@ -2608,6 +3263,7 @@ window.__ModuleLoader__.load({
|
|
|
2608
3263
|
return;
|
|
2609
3264
|
}
|
|
2610
3265
|
const index = this.indexFromElement(node);
|
|
3266
|
+
if (!this.isIndexInRange(index)) return;
|
|
2611
3267
|
const key = this.options.getItemKey(index);
|
|
2612
3268
|
const prevNode = this.elementsCache.get(key);
|
|
2613
3269
|
if (prevNode !== node) {
|
|
@@ -2619,7 +3275,7 @@ window.__ModuleLoader__.load({
|
|
|
2619
3275
|
};
|
|
2620
3276
|
this.resizeItem = (index, size) => {
|
|
2621
3277
|
var _a, _b;
|
|
2622
|
-
if (
|
|
3278
|
+
if (!this.isIndexInRange(index)) return;
|
|
2623
3279
|
let cachedSize;
|
|
2624
3280
|
let itemStart;
|
|
2625
3281
|
let key;
|
|
@@ -2966,7 +3622,7 @@ window.__ModuleLoader__.load({
|
|
|
2966
3622
|
};
|
|
2967
3623
|
}
|
|
2968
3624
|
//#endregion
|
|
2969
|
-
//#region ../../../node_modules/.pnpm/@tanstack+react-virtual@3.14.
|
|
3625
|
+
//#region ../../../node_modules/.pnpm/@tanstack+react-virtual@3.14.10_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@tanstack/react-virtual/dist/esm/index.js
|
|
2970
3626
|
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? react.useLayoutEffect : react.useEffect;
|
|
2971
3627
|
function useVirtualizerBase({ useFlushSync = true, directDomUpdates = false, directDomUpdatesMode = "transform", ...options }) {
|
|
2972
3628
|
const rerender = react.useReducer((x) => x + 1, 0)[1];
|
|
@@ -4406,6 +5062,7 @@ window.__ModuleLoader__.load({
|
|
|
4406
5062
|
function stateOf(record) {
|
|
4407
5063
|
if (record.cell.isError) return "error";
|
|
4408
5064
|
if (record.cell.kind === "compacted" && record.cell.timeSeconds === null) return "running";
|
|
5065
|
+
if (record.cell.assistantMetrics?.completedTime === null) return "running";
|
|
4409
5066
|
if ((record.cell.kind === "tool" || record.cell.kind === "subtool") && record.cell.outputDetail === void 0) return "running";
|
|
4410
5067
|
return "complete";
|
|
4411
5068
|
}
|
|
@@ -5566,7 +6223,7 @@ window.__ModuleLoader__.load({
|
|
|
5566
6223
|
const selectedState = selected === void 0 ? void 0 : stateOf(selected);
|
|
5567
6224
|
const selectedRequestInfo = selectedRequest === null ? void 0 : sessionRequestNumbers?.find((request) => requestIdentity(request) === selectedRequest.identity);
|
|
5568
6225
|
const selectedRequestRecords = (0, react.useMemo)(() => selectedRequestInfo === void 0 ? [] : allRecords.filter((record) => record.turn === selectedRequestInfo.turn && record.group === selectedRequestInfo.group), [allRecords, selectedRequestInfo]).map(currentRecord);
|
|
5569
|
-
const selectedRequestAssistant = selectedRequestRecords.find((record) => record.cell.kind === "message");
|
|
6226
|
+
const selectedRequestAssistant = selectedRequestRecords.find((record) => record.cell.kind === "message" && record.cell.requestOnly !== true);
|
|
5570
6227
|
const selectedRequestAnchor = selectedRequestAssistant ?? selectedRequestRecords[0];
|
|
5571
6228
|
const selectedRequestNumber = selectedRequestInfo?.number;
|
|
5572
6229
|
const selectedRequestState = selectedRequestInfo === void 0 ? void 0 : selectedRequestInfo.status ?? (selectedRequestAssistant?.cell.assistantMetrics?.completedTime === null ? "running" : selectedRequestAssistant === void 0 && selectedRequestRecords.some((record) => stateOf(record) === "running") ? "running" : "complete");
|
|
@@ -6212,18 +6869,14 @@ window.__ModuleLoader__.load({
|
|
|
6212
6869
|
(selectedRequestInfo.model ?? selectedRequestInfo.requestConfig?.model) !== void 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.model") }), (0, react_jsx_runtime.jsx)("dd", { children: selectedRequestInfo.model ?? selectedRequestInfo.requestConfig?.model })] }),
|
|
6213
6870
|
(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.toolCalls") }), (0, react_jsx_runtime.jsx)("dd", { children: selectedRequestToolCalls })] }),
|
|
6214
6871
|
selectedRequestSubtoolCalls > 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.subtoolCalls") }), (0, react_jsx_runtime.jsx)("dd", { children: selectedRequestSubtoolCalls })] }),
|
|
6215
|
-
selectedRequestInfo.error !== void 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.error") }), (0, react_jsx_runtime.jsx)("dd", {
|
|
6216
|
-
className: TrajectoryTable_module_css_default.error,
|
|
6872
|
+
selectedRequestInfo.error !== void 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t(selectedRequestState !== "error" && selectedRequestInfo.retry !== void 0 ? "details.lastAttemptError" : "details.error") }), (0, react_jsx_runtime.jsx)("dd", {
|
|
6873
|
+
className: selectedRequestState === "error" ? TrajectoryTable_module_css_default.error : void 0,
|
|
6217
6874
|
children: requestErrorMessage(selectedRequestInfo, t)
|
|
6218
6875
|
})] }),
|
|
6219
|
-
selectedRequestInfo.retry !== void 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.retry") }), (0, react_jsx_runtime.
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
retry: selectedRequestInfo.retry,
|
|
6224
|
-
maximum: selectedRequestInfo.maxRetries
|
|
6225
|
-
})
|
|
6226
|
-
] })] }),
|
|
6876
|
+
selectedRequestInfo.retry !== void 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.retry") }), (0, react_jsx_runtime.jsx)("dd", { children: selectedRequestInfo.maxRetries === void 0 ? selectedRequestInfo.retry : t("request.retryProgress", {
|
|
6877
|
+
retry: selectedRequestInfo.retry,
|
|
6878
|
+
maximum: selectedRequestInfo.maxRetries
|
|
6879
|
+
}) })] }),
|
|
6227
6880
|
selectedRequestInfo.retryDelayMs !== void 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.retryDelay") }), (0, react_jsx_runtime.jsx)("dd", { children: formatDurationMs(selectedRequestInfo.retryDelayMs, t) })] }),
|
|
6228
6881
|
selectedRequestResult !== void 0 && (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("details.result") }), (0, react_jsx_runtime.jsx)("dd", {
|
|
6229
6882
|
className: TrajectoryTable_module_css_default.overviewParentLinks,
|
|
@@ -7672,7 +8325,10 @@ window.__ModuleLoader__.load({
|
|
|
7672
8325
|
turn: partial.turn,
|
|
7673
8326
|
step: partial.step,
|
|
7674
8327
|
blocks: partial.blocks
|
|
7675
|
-
}, index + 1, prevAbsTime, resultByCall, callStartById, callById, t, {
|
|
8328
|
+
}, index + 1, prevAbsTime, resultByCall, callStartById, callById, t, {
|
|
8329
|
+
streaming: true,
|
|
8330
|
+
timing: partial.timing
|
|
8331
|
+
}), t);
|
|
7676
8332
|
if (partial.step > 0) pushStep(partial.turn, partial.step, laidList);
|
|
7677
8333
|
else for (const laid of laidList) pushMessage(partial.turn, laid);
|
|
7678
8334
|
const last = laidList[laidList.length - 1];
|
|
@@ -7835,7 +8491,8 @@ window.__ModuleLoader__.load({
|
|
|
7835
8491
|
let index = startIndex - 1;
|
|
7836
8492
|
const usage = node.usage;
|
|
7837
8493
|
const streaming = opts?.streaming === true;
|
|
7838
|
-
const
|
|
8494
|
+
const timing = opts?.timing ?? node.timing;
|
|
8495
|
+
const recordedStart = finiteTime(timing?.stepStartTime);
|
|
7839
8496
|
const messageDuration = streaming ? null : durationSeconds(node.time, recordedStart ?? prevAbsTime);
|
|
7840
8497
|
const nodeAbs = streaming ? null : finiteTime(node.time);
|
|
7841
8498
|
const messageText = node.blocks.filter((block) => block.kind === "text" && (!streaming || block.text !== "")).map((block) => block.kind === "text" ? block.text : "").join("\n\n");
|
|
@@ -7850,14 +8507,15 @@ window.__ModuleLoader__.load({
|
|
|
7850
8507
|
...messageText !== "" ? { outputDetail: messageText } : {},
|
|
7851
8508
|
...thinkingText !== "" ? { thinkingDetail: thinkingText } : {},
|
|
7852
8509
|
sourceBlocks: node.blocks.map((block) => assistantSourceBlock(block)),
|
|
8510
|
+
...node.interrupted === true ? { isError: true } : {},
|
|
7853
8511
|
timeSeconds: messageDuration,
|
|
7854
8512
|
startedAt: recordedStart
|
|
7855
8513
|
};
|
|
7856
8514
|
attachUsage(message, usage);
|
|
7857
8515
|
message.assistantMetrics = {
|
|
7858
|
-
timingRecorded:
|
|
7859
|
-
stepStartTime:
|
|
7860
|
-
firstTokenTime:
|
|
8516
|
+
timingRecorded: timing !== void 0,
|
|
8517
|
+
stepStartTime: timing?.stepStartTime ?? null,
|
|
8518
|
+
firstTokenTime: timing?.firstTokenTime ?? null,
|
|
7861
8519
|
completedTime: streaming ? null : finiteTime(node.time),
|
|
7862
8520
|
usageProvided: usage !== void 0,
|
|
7863
8521
|
outputTokens: Number.isFinite(usage?.outputTokens) ? usage?.outputTokens ?? null : null
|
|
@@ -8410,18 +9068,21 @@ window.__ModuleLoader__.load({
|
|
|
8410
9068
|
const provider = request?.providerMetadata?.provider ?? node?.providerMetadata?.provider;
|
|
8411
9069
|
const model = request?.providerMetadata?.model ?? node?.providerMetadata?.model;
|
|
8412
9070
|
const requestConfig = request?.requestConfig ?? node?.requestConfig;
|
|
9071
|
+
const startedAt = request?.startedAt;
|
|
9072
|
+
const completedAt = request === void 0 ? node?.time : request.completedAt;
|
|
9073
|
+
const resultSeq = request?.resultSeq ?? node?.seq;
|
|
8413
9074
|
numbered.push({
|
|
8414
9075
|
seq: entry.seq,
|
|
8415
9076
|
turn,
|
|
8416
9077
|
step,
|
|
8417
9078
|
group: t("group.step", { step }),
|
|
8418
9079
|
number: index + 1,
|
|
8419
|
-
|
|
8420
|
-
...
|
|
8421
|
-
...
|
|
9080
|
+
status: request?.status ?? (node?.interrupted === true ? "error" : "complete"),
|
|
9081
|
+
...startedAt === void 0 ? {} : { startedAt },
|
|
9082
|
+
...completedAt === void 0 ? {} : { completedAt },
|
|
8422
9083
|
...request?.error === void 0 ? {} : { error: request.error },
|
|
8423
9084
|
...request?.errorCode === void 0 ? {} : { errorCode: request.errorCode },
|
|
8424
|
-
...
|
|
9085
|
+
...resultSeq === void 0 ? {} : { resultSeq },
|
|
8425
9086
|
...request?.retry === void 0 ? {} : { retry: request.retry },
|
|
8426
9087
|
...request?.maxRetries === void 0 ? {} : { maxRetries: request.maxRetries },
|
|
8427
9088
|
...request?.retryDelayMs === void 0 ? {} : { retryDelayMs: request.retryDelayMs },
|
|
@@ -8446,7 +9107,7 @@ window.__ModuleLoader__.load({
|
|
|
8446
9107
|
completedAt: request.completedAt,
|
|
8447
9108
|
...request.error === void 0 ? {} : { error: request.error },
|
|
8448
9109
|
...request.errorCode === void 0 ? {} : { errorCode: request.errorCode },
|
|
8449
|
-
resultSeq: request.startSeq,
|
|
9110
|
+
...request.status === "running" ? {} : { resultSeq: request.startSeq },
|
|
8450
9111
|
...request.providerMetadata?.provider === void 0 ? {} : { provider: request.providerMetadata.provider },
|
|
8451
9112
|
...request.providerMetadata?.model === void 0 ? {} : { model: request.providerMetadata.model },
|
|
8452
9113
|
...request.requestConfig === void 0 ? {} : { requestConfig: request.requestConfig },
|
|
@@ -8491,14 +9152,20 @@ window.__ModuleLoader__.load({
|
|
|
8491
9152
|
callSchemas,
|
|
8492
9153
|
t
|
|
8493
9154
|
]);
|
|
9155
|
+
const timelinePartialSignature = partialStructureSignature(partial);
|
|
9156
|
+
const partialStartTime = partial?.timing?.stepStartTime;
|
|
9157
|
+
const partialFirstTokenTime = partial?.timing?.firstTokenTime;
|
|
8494
9158
|
const timelinePartial = (0, react.useMemo)(() => partial === null ? null : {
|
|
8495
9159
|
turn: partial.turn,
|
|
8496
9160
|
step: partial.step,
|
|
8497
|
-
blocks: partial.blocks.map((block) => timelineBlock(block))
|
|
9161
|
+
blocks: partial.blocks.map((block) => timelineBlock(block)),
|
|
9162
|
+
...partial.timing === void 0 ? {} : { timing: partial.timing }
|
|
8498
9163
|
}, [
|
|
8499
9164
|
partialStep,
|
|
8500
9165
|
partialTurn,
|
|
8501
|
-
|
|
9166
|
+
timelinePartialSignature,
|
|
9167
|
+
partialStartTime,
|
|
9168
|
+
partialFirstTokenTime
|
|
8502
9169
|
]);
|
|
8503
9170
|
const timelineTurns = (0, react.useMemo)(() => appendTrajectoryPartialLayout(finalized.turns, timelinePartial, finalized.lastIndex, t), [
|
|
8504
9171
|
finalized,
|
|
@@ -158,9 +158,9 @@ export declare const zh: {
|
|
|
158
158
|
readonly 'details.toolCalls': "工具调用";
|
|
159
159
|
readonly 'details.subtoolCalls': "子工具调用";
|
|
160
160
|
readonly 'details.error': "错误";
|
|
161
|
+
readonly 'details.lastAttemptError': "上次尝试错误";
|
|
161
162
|
readonly 'details.failure.auth': "API 密钥无效";
|
|
162
163
|
readonly 'details.retry': "重试";
|
|
163
|
-
readonly 'details.scheduled': "已计划";
|
|
164
164
|
readonly 'details.retryDelay': "重试延迟";
|
|
165
165
|
readonly 'details.result': "结果";
|
|
166
166
|
readonly 'details.compacted': "已压缩";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessageNode, ConversationLocation, ConversationNode, ConversationPromptSnapshot, ConversationViewNode, MessageImagesOwnerProps, PartialAssistant, RequestPromptChange, RequestView, RunningToolCall, SystemPromptNode, ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
1
|
+
import type { AssistantMessageNode, AssistantTiming, ConversationLocation, ConversationNode, ConversationPromptSnapshot, ConversationViewNode, MessageImagesOwnerProps, PartialAssistant, RequestPromptChange, RequestView, RunningToolCall, SystemPromptNode, ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
2
2
|
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
3
|
/** Request-header facts retained by the Trajectory target. */
|
|
4
4
|
export interface TrajectoryRequestHeaderState {
|
|
@@ -8,6 +8,10 @@ export interface TrajectoryRequestHeaderState {
|
|
|
8
8
|
readonly change?: RequestPromptChange;
|
|
9
9
|
readonly location: ConversationLocation;
|
|
10
10
|
}
|
|
11
|
+
/** In-flight assistant output with the loaded Step start and first token timestamps. */
|
|
12
|
+
export interface TrajectoryPartialAssistant extends PartialAssistant {
|
|
13
|
+
readonly timing?: Pick<AssistantTiming, 'stepStartTime' | 'firstTokenTime'>;
|
|
14
|
+
}
|
|
11
15
|
/** One independently assembled contribution to the legacy Trajectory ledger. */
|
|
12
16
|
export type TrajectoryContribution = {
|
|
13
17
|
readonly kind: 'system-prompt';
|
|
@@ -18,7 +22,7 @@ export type TrajectoryContribution = {
|
|
|
18
22
|
} | {
|
|
19
23
|
readonly kind: 'assistant';
|
|
20
24
|
readonly node?: AssistantMessageNode;
|
|
21
|
-
readonly partial:
|
|
25
|
+
readonly partial: TrajectoryPartialAssistant | null;
|
|
22
26
|
readonly request?: Extract<RequestView, {
|
|
23
27
|
purpose: 'assistant';
|
|
24
28
|
}>;
|
|
@@ -59,7 +63,7 @@ export interface TrajectorySnapshot {
|
|
|
59
63
|
readonly eventLocations: ReadonlyMap<number, ConversationLocation>;
|
|
60
64
|
readonly requests: readonly RequestView[];
|
|
61
65
|
readonly callSchemas: ReadonlyMap<string, ConversationPromptSnapshot['tools'][number]>;
|
|
62
|
-
readonly partial:
|
|
66
|
+
readonly partial: TrajectoryPartialAssistant | null;
|
|
63
67
|
readonly runningCalls: readonly RunningToolCall[];
|
|
64
68
|
}
|
|
65
69
|
/** Selector hook over the current Conversation binding's Trajectory target. */
|
|
@@ -72,7 +72,7 @@ export interface TrajectoryCellProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
72
72
|
callId?: string;
|
|
73
73
|
/** Recorded tool name; independent of the display summary. */
|
|
74
74
|
toolName?: string;
|
|
75
|
-
/**
|
|
75
|
+
/** Failed operation or interrupted assistant output. */
|
|
76
76
|
isError?: boolean;
|
|
77
77
|
/** Own duration in seconds, or `null` when no duration is known. */
|
|
78
78
|
timeSeconds: number | null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-trajectory",
|
|
3
3
|
"description": "Trajectory event ledger with an interactive timing overview: pure-consumer plugin registering into the conversation ViewMap (no service)",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -39,32 +39,33 @@
|
|
|
39
39
|
},
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@deepseek-ai/cordis": "~4.0.
|
|
42
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
|
|
46
|
+
"@deepseek-ai/dsh-client-locale": "0.2.1-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-client-test-runtime": "0.2.1-alpha.2",
|
|
48
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-compaction": "0.2.1-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.2",
|
|
45
54
|
"@types/react": "~18.3.1",
|
|
46
55
|
"@types/react-dom": "~18.3.0",
|
|
56
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
47
57
|
"react": "^18.2.0",
|
|
48
58
|
"react-dom": "^18.2.0",
|
|
49
|
-
"@
|
|
50
|
-
"
|
|
51
|
-
"@deepseek-ai/dsh-
|
|
52
|
-
"@deepseek-ai/dsh-
|
|
53
|
-
"@deepseek-ai/dsh-client-ui-
|
|
54
|
-
"@deepseek-ai/dsh-client-ui-
|
|
55
|
-
"@deepseek-ai/dsh-
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@
|
|
58
|
-
"
|
|
59
|
-
"@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
|
|
60
|
-
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
|
|
63
|
-
"@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
|
|
64
|
-
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
65
|
-
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
66
|
-
"@deepseek-ai/dsh-client-ui-chat": "0.2.0-rc.2",
|
|
67
|
-
"@deepseek-ai/dsh-client-test-runtime": "0.2.0-rc.2"
|
|
59
|
+
"@deepseek-ai/dsh-client-store": "0.2.1-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-chat": "0.2.1-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.2",
|
|
62
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.2",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.2",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.2",
|
|
65
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
|
|
66
|
+
"@deepseek-ai/dsh-attachment": "0.2.1-alpha.2",
|
|
67
|
+
"@tanstack/react-virtual": "^3.14.10",
|
|
68
|
+
"diff": "^9.0.0"
|
|
68
69
|
},
|
|
69
70
|
"files": [
|
|
70
71
|
"lib/index.js",
|