@deepseek-ai/dsh-client-connection 0.1.3-alpha.2 → 0.1.5-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 +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +486 -53
- package/lib/index.js +27 -24
- package/lib/types/client/fixture.d.ts +2 -0
- package/lib/types/index.d.ts +3 -3
- package/package.json +13 -13
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/client/connection/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 865c894403d2177e15085d0616289cf8950b555d
|
|
6
|
+
README.zh.md: 3d299a4be7b3490298554982eed6d99fbba9718c
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ The package carries browser-to-Host Remote calls, exact Fetch responses, and con
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
-
The browser uses HTTP POST for Remote unary calls. API Gateway owns the `/api/remote.mux` WebSocket and its logical streams;
|
|
28
|
+
The browser uses HTTP POST for Remote unary calls. API Gateway owns the `/api/remote.mux` WebSocket and its logical streams; shell-owned compositions provide equivalent Remote streams through `connection.rpc.open` without opening a WebSocket. The Host half always provides the carrier-neutral RPC and exact `GET`/`HEAD`/`POST` route registries. When a Web carrier is present it also owns the sole `/api` route, Fetch bridge, browser authentication, and Host/Origin checks; a shell-owned carrier dispatches the shared Fetch handler directly. Each exact route declares buffered or streaming request-body handling before the bridge reads any bytes. Typert Gateway claims generated Remote endpoints, feature packages register non-JSON responses such as Session-log downloads and raw file uploads, and unclaimed requests return 404. Loopback hostname classification remains package-internal to the browser-facing Client state. Browser raw-body transfer is provided by [`dsh-client-file-upload`](../file-upload/README.md).
|
|
29
29
|
|
|
30
30
|
-----
|
|
31
31
|
|
package/README.zh.md
CHANGED
|
@@ -25,7 +25,7 @@ kind: "package-reference"
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
-
浏览器通过 HTTP POST 执行 Remote
|
|
28
|
+
浏览器通过 HTTP POST 执行 Remote 一元调用;API Gateway 自己拥有 `/api/remote.mux` WebSocket 及其逻辑流。由 shell 持有的组合通过 `connection.rpc.open` 提供等价的 Remote 流,不打开 WebSocket。Host half 始终提供与载体无关的 RPC 注册表和精确 `GET`/`HEAD`/`POST` 路由注册表。存在 Web 载体时,它还持有唯一 `/api` route、Fetch bridge、浏览器认证与 Host/Origin 校验;由 shell 持有的载体则直接分派共享 Fetch handler。每条精确路由会在 bridge 读取任何字节前声明缓冲或流式请求体处理方式。Typert Gateway 认领生成的 Remote endpoint,功能包注册 Session 日志下载、原始文件上传等非 JSON 响应,未认领的请求返回 404。Loopback hostname 判定只供浏览器侧当前页面状态使用,留在包内。浏览器原始请求体传输由 [`dsh-client-file-upload`](../file-upload/README.zh.md) 提供。
|
|
29
29
|
|
|
30
30
|
-----
|
|
31
31
|
|
package/lib/client.js
CHANGED
|
@@ -1354,6 +1354,26 @@ window.__ModuleLoader__.load({
|
|
|
1354
1354
|
});
|
|
1355
1355
|
}
|
|
1356
1356
|
/**
|
|
1357
|
+
* Create and freeze one identified system-role message holding a rendered
|
|
1358
|
+
* system prompt.
|
|
1359
|
+
* @param text - the complete rendered prompt; `''` records "no system prompt".
|
|
1360
|
+
* @param plugin - the plugin that assembled the prompt.
|
|
1361
|
+
* @returns an immutable system message with a fresh stable identity.
|
|
1362
|
+
*/
|
|
1363
|
+
function createSystemMessage(text, plugin) {
|
|
1364
|
+
return createMessage({
|
|
1365
|
+
role: "system",
|
|
1366
|
+
content: text.length === 0 ? [] : [{
|
|
1367
|
+
type: "text",
|
|
1368
|
+
text
|
|
1369
|
+
}],
|
|
1370
|
+
source: {
|
|
1371
|
+
kind: "plugin",
|
|
1372
|
+
plugin
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1357
1377
|
* Create and freeze one identified tool-result message.
|
|
1358
1378
|
* @param input - call identity, raw result blocks, and outcome.
|
|
1359
1379
|
* @returns an immutable user-role tool-result message.
|
|
@@ -1671,6 +1691,86 @@ window.__ModuleLoader__.load({
|
|
|
1671
1691
|
return brandNumber(value);
|
|
1672
1692
|
}
|
|
1673
1693
|
//#endregion
|
|
1694
|
+
//#region ../../core/session/lib/types/known-event-types.js
|
|
1695
|
+
/**
|
|
1696
|
+
* GENERATED by `scripts/gen-persistence-catalog.ts` — do not edit by hand; run
|
|
1697
|
+
* `pnpm run gen-persistence-catalog` to regenerate (verified fresh by
|
|
1698
|
+
* `pnpm run verify-persistence-catalog`, part of `doc-sync`).
|
|
1699
|
+
* @module @deepseek-ai/dsh-session/known-event-types
|
|
1700
|
+
*/
|
|
1701
|
+
/**
|
|
1702
|
+
* Every `SessionEventMap` member declared in this repository — the event
|
|
1703
|
+
* vocabulary this build understands. The persistence read path refuses to
|
|
1704
|
+
* interpret a log containing a type outside this set unless the event
|
|
1705
|
+
* carries the envelope's `ignorable` marker (see `SessionEvent.ignorable`
|
|
1706
|
+
* in `./types.ts`): such a log was likely written by a newer harness, and
|
|
1707
|
+
* silently skipping a required event would reconstruct a wrong session.
|
|
1708
|
+
* Downstream (out-of-repo) plugin events are outside this list by
|
|
1709
|
+
* construction. The persisted `SessionEvent.ignorable` marker is the
|
|
1710
|
+
* compatibility mechanism; event-name registration was rejected because
|
|
1711
|
+
* it does not classify omission safety and would make reads
|
|
1712
|
+
* composition-dependent. The rationale is in
|
|
1713
|
+
* `.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.md`.
|
|
1714
|
+
*/
|
|
1715
|
+
const KNOWN_SESSION_EVENT_TYPES = new Set([
|
|
1716
|
+
"agent-preset/selected",
|
|
1717
|
+
"agent/inbox/spliced",
|
|
1718
|
+
"approval/asked",
|
|
1719
|
+
"approval/decided",
|
|
1720
|
+
"approval/policy",
|
|
1721
|
+
"assistant/attempt",
|
|
1722
|
+
"assistant/message",
|
|
1723
|
+
"command/done",
|
|
1724
|
+
"command/run",
|
|
1725
|
+
"compaction/end",
|
|
1726
|
+
"compaction/prune",
|
|
1727
|
+
"compaction/start",
|
|
1728
|
+
"compaction/summary",
|
|
1729
|
+
"deliverables/presented",
|
|
1730
|
+
"feedback/message-delete",
|
|
1731
|
+
"feedback/message-put",
|
|
1732
|
+
"feedback/record",
|
|
1733
|
+
"goal/change",
|
|
1734
|
+
"hook/invoked",
|
|
1735
|
+
"hook/result",
|
|
1736
|
+
"llm/retry",
|
|
1737
|
+
"llm/retry-started",
|
|
1738
|
+
"model/selection",
|
|
1739
|
+
"permission/preset",
|
|
1740
|
+
"plan/mode",
|
|
1741
|
+
"request/context",
|
|
1742
|
+
"request/header",
|
|
1743
|
+
"sandbox/mode",
|
|
1744
|
+
"schedule/change",
|
|
1745
|
+
"session-log-deepseek/delivery-accepted",
|
|
1746
|
+
"session/end-seed",
|
|
1747
|
+
"session/title",
|
|
1748
|
+
"session/title-llm-request",
|
|
1749
|
+
"step/end",
|
|
1750
|
+
"step/start",
|
|
1751
|
+
"subagent/catalog",
|
|
1752
|
+
"subagent/descriptor",
|
|
1753
|
+
"subagent/model-selection-policy",
|
|
1754
|
+
"system/message",
|
|
1755
|
+
"team/member",
|
|
1756
|
+
"team/message/delivered",
|
|
1757
|
+
"team/message/queued",
|
|
1758
|
+
"team/task",
|
|
1759
|
+
"todo/write",
|
|
1760
|
+
"tool-workflow/agent-end",
|
|
1761
|
+
"tool-workflow/agent-start",
|
|
1762
|
+
"tool-workflow/run-end",
|
|
1763
|
+
"tool-workflow/run-start",
|
|
1764
|
+
"tool/call",
|
|
1765
|
+
"tool/ptc-dispatch",
|
|
1766
|
+
"tool/ptc-dispatch-start",
|
|
1767
|
+
"tool/result",
|
|
1768
|
+
"turn/end",
|
|
1769
|
+
"turn/start",
|
|
1770
|
+
"user/message",
|
|
1771
|
+
"web/deepseek-search-llm-request"
|
|
1772
|
+
]);
|
|
1773
|
+
//#endregion
|
|
1674
1774
|
//#region ../../core/session/lib/types/surface.js
|
|
1675
1775
|
/**
|
|
1676
1776
|
* Surface layer on top of the session event log: an ordered view of events
|
|
@@ -1683,6 +1783,7 @@ window.__ModuleLoader__.load({
|
|
|
1683
1783
|
*/
|
|
1684
1784
|
/** Runtime counterpart of the message-producing event union. */
|
|
1685
1785
|
const SURFACE_EVENT_TYPES = new Set([
|
|
1786
|
+
"system/message",
|
|
1686
1787
|
"user/message",
|
|
1687
1788
|
"assistant/message",
|
|
1688
1789
|
"tool/result"
|
|
@@ -1690,7 +1791,7 @@ window.__ModuleLoader__.load({
|
|
|
1690
1791
|
/**
|
|
1691
1792
|
* Whether an event type can join the model-visible surface.
|
|
1692
1793
|
* @param type - event type to test.
|
|
1693
|
-
* @returns true for one of the
|
|
1794
|
+
* @returns true for one of the four message-producing event types.
|
|
1694
1795
|
*/
|
|
1695
1796
|
function isSurfaceEligibleType(type) {
|
|
1696
1797
|
return SURFACE_EVENT_TYPES.has(type);
|
|
@@ -1711,6 +1812,7 @@ window.__ModuleLoader__.load({
|
|
|
1711
1812
|
function deriveEventMessage(event) {
|
|
1712
1813
|
switch (event.type) {
|
|
1713
1814
|
case "user/message": return event.data;
|
|
1815
|
+
case "system/message":
|
|
1714
1816
|
case "assistant/message":
|
|
1715
1817
|
if (event.data.message.content.length === 0) return null;
|
|
1716
1818
|
return event.data.message;
|
|
@@ -1732,12 +1834,13 @@ window.__ModuleLoader__.load({
|
|
|
1732
1834
|
/** Whether a runtime value is the exact positional-replacement shape. */
|
|
1733
1835
|
function isReplaceOp(value) {
|
|
1734
1836
|
const op = value;
|
|
1735
|
-
return Object.keys(op).length === 3 && Object.hasOwn(op, "op") && Object.hasOwn(op, "
|
|
1837
|
+
return Object.keys(op).length === 3 && Object.hasOwn(op, "op") && Object.hasOwn(op, "startSeq") && Object.hasOwn(op, "endSeq") && op["op"] === "replace" && isEventSeq(op["startSeq"]) && isEventSeq(op["endSeq"]);
|
|
1736
1838
|
}
|
|
1737
1839
|
/** Validate event-local surface eligibility and return its operation. */
|
|
1738
1840
|
function surfaceOpOf(event) {
|
|
1739
1841
|
const raw = event;
|
|
1740
1842
|
if (!isSurfaceEligibleType(event.type)) {
|
|
1843
|
+
if (!KNOWN_SESSION_EVENT_TYPES.has(event.type) && event.ignorable === true) return;
|
|
1741
1844
|
if (raw.surfaceOp !== void 0) throw new Error(`session event "${event.type}" is not surface-eligible and cannot carry surfaceOp`);
|
|
1742
1845
|
if (raw.sourceEventSeqs !== void 0) throw new Error(`session event "${event.type}" is not surface-eligible and cannot carry sourceEventSeqs`);
|
|
1743
1846
|
return;
|
|
@@ -1769,13 +1872,26 @@ window.__ModuleLoader__.load({
|
|
|
1769
1872
|
const missing = shadowedSeqs.filter((seq) => !sources.has(seq));
|
|
1770
1873
|
if (missing.length > 0) throw new Error(`surface replace: sourceEventSeqs must include every shadowed surface node; missing ${missing.join(", ")}`);
|
|
1771
1874
|
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Validate one event's surface metadata without checking membership in a log or surface.
|
|
1877
|
+
* @param event - event whose marker and source sequence values are inspected.
|
|
1878
|
+
* Unknown ignorable records retain opaque metadata and never change the surface.
|
|
1879
|
+
* @returns the validated operation, or undefined for a log-only or unknown ignorable event.
|
|
1880
|
+
* @throws when metadata violates event-local eligibility, marker, or source-sequence rules.
|
|
1881
|
+
*/
|
|
1882
|
+
function validateSurfaceMetadata(event) {
|
|
1883
|
+
const op = surfaceOpOf(event);
|
|
1884
|
+
if (op !== void 0 && op !== "append" && (op.startSeq >= event.seq || op.endSeq >= event.seq)) throw new Error(`surface replace at seq ${event.seq}: startSeq and endSeq must reference earlier events`);
|
|
1885
|
+
if (op !== void 0) assertProvenance(event, []);
|
|
1886
|
+
return op;
|
|
1887
|
+
}
|
|
1772
1888
|
/** Locate one replacement range without mutating the current fold state. */
|
|
1773
1889
|
function replacementRange(state, op) {
|
|
1774
|
-
const startIdx = state.nodes.indexOf(op.
|
|
1775
|
-
if (startIdx === -1) throw new Error(`surface replace: start seq ${op.
|
|
1776
|
-
const endIdx = state.nodes.indexOf(op.
|
|
1777
|
-
if (endIdx === -1) throw new Error(`surface replace: end seq ${op.
|
|
1778
|
-
if (startIdx > endIdx) throw new Error(`surface replace: start seq ${op.
|
|
1890
|
+
const startIdx = state.nodes.indexOf(op.startSeq);
|
|
1891
|
+
if (startIdx === -1) throw new Error(`surface replace: start seq ${op.startSeq} not found in surface`);
|
|
1892
|
+
const endIdx = state.nodes.indexOf(op.endSeq);
|
|
1893
|
+
if (endIdx === -1) throw new Error(`surface replace: end seq ${op.endSeq} not found in surface`);
|
|
1894
|
+
if (startIdx > endIdx) throw new Error(`surface replace: start seq ${op.startSeq} (index ${startIdx}) is after end seq ${op.endSeq} (index ${endIdx})`);
|
|
1779
1895
|
return {
|
|
1780
1896
|
startIdx,
|
|
1781
1897
|
endIdx,
|
|
@@ -1827,26 +1943,35 @@ window.__ModuleLoader__.load({
|
|
|
1827
1943
|
if (!isDeepEqualJson(originalRest, replacementRest)) throw new Error("tool/result surface replacement may change only content");
|
|
1828
1944
|
}
|
|
1829
1945
|
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Protect the system prompt at surface node 0. A replacement covering node 0
|
|
1948
|
+
* while that node is a `system/message` must itself be a `system/message` over
|
|
1949
|
+
* exactly that node; later system nodes carry no protection and a compaction
|
|
1950
|
+
* range may shadow them.
|
|
1951
|
+
*/
|
|
1952
|
+
function assertSystemHeadRewrite(event, state, startIdx, shadowedSeqs, events, baseSeq) {
|
|
1953
|
+
if (startIdx !== 0) return;
|
|
1954
|
+
if (events[state.nodes[0] - baseSeq]?.type !== "system/message") return;
|
|
1955
|
+
if (event.type !== "system/message" || shadowedSeqs.length !== 1) throw new Error("surface replace: node 0 holds the system prompt and may be rewritten only by a system/message over exactly that node");
|
|
1956
|
+
}
|
|
1830
1957
|
/** Validate one event at its replay boundary and prepare its atomic fold transition. */
|
|
1831
1958
|
function planSurfaceEvent(state, event, expectedSeq, events, baseSeq) {
|
|
1832
1959
|
if (event.seq !== expectedSeq) throw new Error(`session event seq ${event.seq} is not contiguous; expected ${expectedSeq}`);
|
|
1833
|
-
const surfaceOp =
|
|
1960
|
+
const surfaceOp = validateSurfaceMetadata(event);
|
|
1834
1961
|
if (surfaceOp === void 0) return;
|
|
1835
|
-
if (surfaceOp === "append") {
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
seq: event.seq
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1962
|
+
if (surfaceOp === "append") return {
|
|
1963
|
+
kind: "append",
|
|
1964
|
+
seq: event.seq
|
|
1965
|
+
};
|
|
1842
1966
|
const range = replacementRange(state, surfaceOp);
|
|
1843
1967
|
assertProvenance(event, range.shadowedSeqs);
|
|
1844
1968
|
assertToolResultRewrite(event, range.shadowedSeqs, events, baseSeq);
|
|
1969
|
+
assertSystemHeadRewrite(event, state, range.startIdx, range.shadowedSeqs, events, baseSeq);
|
|
1845
1970
|
return {
|
|
1846
1971
|
kind: "replace",
|
|
1847
1972
|
seq: event.seq,
|
|
1848
|
-
start: surfaceOp.
|
|
1849
|
-
end: surfaceOp.
|
|
1973
|
+
start: surfaceOp.startSeq,
|
|
1974
|
+
end: surfaceOp.endSeq,
|
|
1850
1975
|
...range
|
|
1851
1976
|
};
|
|
1852
1977
|
}
|
|
@@ -2263,6 +2388,8 @@ window.__ModuleLoader__.load({
|
|
|
2263
2388
|
});
|
|
2264
2389
|
return stream;
|
|
2265
2390
|
}
|
|
2391
|
+
/** Rendered system prompt of the fx-alpha history: surface node 0. */
|
|
2392
|
+
const FIXTURE_SYSTEM_PROMPT = "你是 DeepSeek Harness 的 fixture 助手。用简洁的中文回答,并在需要时调用工具。";
|
|
2266
2393
|
/** fx-alpha history script: 75 turns (~150+ messages -> 4 pages at PAGE_MESSAGES=50),
|
|
2267
2394
|
* mixing reasoning blocks / tool call+result / context. */
|
|
2268
2395
|
function buildAlphaLog() {
|
|
@@ -2300,6 +2427,15 @@ window.__ModuleLoader__.load({
|
|
|
2300
2427
|
type: "turn/start",
|
|
2301
2428
|
data: { turn }
|
|
2302
2429
|
});
|
|
2430
|
+
if (turn === 0) push({
|
|
2431
|
+
type: "system/message",
|
|
2432
|
+
surfaceOp: "append",
|
|
2433
|
+
data: {
|
|
2434
|
+
turn,
|
|
2435
|
+
step: 0,
|
|
2436
|
+
message: createSystemMessage(FIXTURE_SYSTEM_PROMPT, "@deepseek-ai/dsh-system-prompt")
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2303
2439
|
const userSeq = push({
|
|
2304
2440
|
type: "user/message",
|
|
2305
2441
|
surfaceOp: "append",
|
|
@@ -2572,21 +2708,21 @@ window.__ModuleLoader__.load({
|
|
|
2572
2708
|
});
|
|
2573
2709
|
const dispatchPair = (n, name, dispatchArgs, resultText, isError = false) => {
|
|
2574
2710
|
push({
|
|
2575
|
-
type: "tool/
|
|
2711
|
+
type: "tool/ptc-dispatch-start",
|
|
2576
2712
|
data: {
|
|
2577
2713
|
rootCallId: callId,
|
|
2578
2714
|
parentCallId: callId,
|
|
2579
|
-
subCallId: `${callId}:
|
|
2715
|
+
subCallId: `${callId}:ptc:${n}`,
|
|
2580
2716
|
name,
|
|
2581
2717
|
arguments: dispatchArgs
|
|
2582
2718
|
}
|
|
2583
2719
|
});
|
|
2584
2720
|
push({
|
|
2585
|
-
type: "tool/
|
|
2721
|
+
type: "tool/ptc-dispatch",
|
|
2586
2722
|
data: {
|
|
2587
2723
|
rootCallId: callId,
|
|
2588
2724
|
parentCallId: callId,
|
|
2589
|
-
subCallId: `${callId}:
|
|
2725
|
+
subCallId: `${callId}:ptc:${n}`,
|
|
2590
2726
|
name,
|
|
2591
2727
|
arguments: dispatchArgs,
|
|
2592
2728
|
isError,
|
|
@@ -2981,19 +3117,30 @@ window.__ModuleLoader__.load({
|
|
|
2981
3117
|
return tokens + densityPrice(JSON.stringify(block)) + BLOCK_OVERHEAD;
|
|
2982
3118
|
}, 0);
|
|
2983
3119
|
}
|
|
2984
|
-
/**
|
|
3120
|
+
/**
|
|
3121
|
+
* Fixture parallel of token-meter's heuristic context-composition projection.
|
|
3122
|
+
* The system prompt is the system-role surface node; it prices as text plus
|
|
3123
|
+
* role framing with no block overhead and stays out of the message figure.
|
|
3124
|
+
*/
|
|
2985
3125
|
function contextBreakdownOf(log) {
|
|
2986
3126
|
const headerEvent = log.findLast((event) => event.type === "request/header");
|
|
2987
3127
|
const header = headerEvent === void 0 ? void 0 : headerEvent.data.header;
|
|
3128
|
+
let systemTokens = 0;
|
|
2988
3129
|
let messageTokens = 0;
|
|
2989
3130
|
for (const seq of foldSurface(log).nodes) {
|
|
2990
3131
|
const event = log[seq];
|
|
2991
3132
|
if (event === void 0) continue;
|
|
2992
3133
|
const message = deriveEventMessage(event);
|
|
2993
|
-
if (message
|
|
3134
|
+
if (message === null) continue;
|
|
3135
|
+
if (message.role === "system") {
|
|
3136
|
+
const characters = message.content.reduce((total, block) => total + (block.type === "text" ? block.text.length : JSON.stringify(block).length), 0);
|
|
3137
|
+
systemTokens = Math.ceil(characters / CHARS_PER_TOKEN) + ROLE_OVERHEAD;
|
|
3138
|
+
continue;
|
|
3139
|
+
}
|
|
3140
|
+
messageTokens += estimateFixtureContent(message.content) + ROLE_OVERHEAD;
|
|
2994
3141
|
}
|
|
2995
3142
|
return {
|
|
2996
|
-
systemTokens
|
|
3143
|
+
systemTokens,
|
|
2997
3144
|
toolsTokens: header?.tools === void 0 || header.tools.length === 0 ? 0 : Math.ceil(JSON.stringify(header.tools).length / CHARS_PER_TOKEN) + BLOCK_OVERHEAD,
|
|
2998
3145
|
messageTokens
|
|
2999
3146
|
};
|
|
@@ -3108,7 +3255,7 @@ window.__ModuleLoader__.load({
|
|
|
3108
3255
|
value: contextPressureOf(log),
|
|
3109
3256
|
seq: event.seq
|
|
3110
3257
|
});
|
|
3111
|
-
if (type === "request/header" || type === "user/message" || type === "assistant/message" || type === "tool/result") frames.push({
|
|
3258
|
+
if (type === "request/header" || type === "system/message" || type === "user/message" || type === "assistant/message" || type === "tool/result") frames.push({
|
|
3112
3259
|
type: "projection",
|
|
3113
3260
|
sessionId: id,
|
|
3114
3261
|
key: "contextBreakdown",
|
|
@@ -3400,6 +3547,7 @@ window.__ModuleLoader__.load({
|
|
|
3400
3547
|
}
|
|
3401
3548
|
];
|
|
3402
3549
|
const logs = new Map([[sid("fx-alpha"), buildAlphaLog()]]);
|
|
3550
|
+
const goalActivations = /* @__PURE__ */ new Map();
|
|
3403
3551
|
const modelSelections = new Map(sessions.map((session) => [session.sessionId, {
|
|
3404
3552
|
provider: "deepseek-official",
|
|
3405
3553
|
model: "deepseek-v4-flash"
|
|
@@ -3818,6 +3966,26 @@ window.__ModuleLoader__.load({
|
|
|
3818
3966
|
});
|
|
3819
3967
|
return backscanGoal(log);
|
|
3820
3968
|
};
|
|
3969
|
+
/** Update process-local goal activation and publish the forwarded edge. */
|
|
3970
|
+
const setGoalActivation = (id, activation) => {
|
|
3971
|
+
const current = backscanGoal(logOf(id));
|
|
3972
|
+
if (current === null) {
|
|
3973
|
+
if (!goalActivations.delete(id)) return;
|
|
3974
|
+
emitRemote("goal/activation-changed", [{ sessionId: id }]);
|
|
3975
|
+
return;
|
|
3976
|
+
}
|
|
3977
|
+
const previous = goalActivations.get(id);
|
|
3978
|
+
goalActivations.set(id, activation);
|
|
3979
|
+
if (previous === activation) return;
|
|
3980
|
+
emitRemote("goal/activation-changed", [{
|
|
3981
|
+
sessionId: id,
|
|
3982
|
+
goal: {
|
|
3983
|
+
id: current.goal.id,
|
|
3984
|
+
revision: current.goal.revision,
|
|
3985
|
+
activation
|
|
3986
|
+
}
|
|
3987
|
+
}]);
|
|
3988
|
+
};
|
|
3821
3989
|
const goalFailure = (message) => ({
|
|
3822
3990
|
ok: false,
|
|
3823
3991
|
error: {
|
|
@@ -3991,21 +4159,25 @@ window.__ModuleLoader__.load({
|
|
|
3991
4159
|
let text;
|
|
3992
4160
|
if (objective === "") text = current === null ? "No goal is set. Usage: /goal <objective>" : `Current goal: ${current.goal.objective}`;
|
|
3993
4161
|
else if (current !== null && current.goal.phase !== "complete") text = `A goal already exists (${current.goal.objective}). Clear it first.`;
|
|
3994
|
-
else
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4162
|
+
else {
|
|
4163
|
+
const created = appendGoalChange(id, {
|
|
4164
|
+
kind: "goal/change",
|
|
4165
|
+
version: 1,
|
|
4166
|
+
operation: "create",
|
|
4167
|
+
goal: {
|
|
4168
|
+
id: `fx-goal-${logOf(id).length}`,
|
|
4169
|
+
revision: 1,
|
|
4170
|
+
objective,
|
|
4171
|
+
phase: "active",
|
|
4172
|
+
maxGoalRounds: 256
|
|
4173
|
+
},
|
|
4174
|
+
roundsStarted: 0,
|
|
4175
|
+
createdAt: Date.now(),
|
|
4176
|
+
updatedAt: Date.now()
|
|
4177
|
+
});
|
|
4178
|
+
setGoalActivation(id, "armed");
|
|
4179
|
+
text = `Goal created: ${created.goal.objective}`;
|
|
4180
|
+
}
|
|
4009
4181
|
const result = {
|
|
4010
4182
|
kind: "success",
|
|
4011
4183
|
text
|
|
@@ -4073,12 +4245,12 @@ window.__ModuleLoader__.load({
|
|
|
4073
4245
|
};
|
|
4074
4246
|
}
|
|
4075
4247
|
};
|
|
4076
|
-
const goalView = (projection) => ({
|
|
4248
|
+
const goalView = (id, projection) => ({
|
|
4077
4249
|
...projection.goal,
|
|
4078
4250
|
roundsStarted: projection.roundsStarted,
|
|
4079
4251
|
createdAt: projection.createdAt,
|
|
4080
4252
|
updatedAt: projection.updatedAt,
|
|
4081
|
-
activation: projection.goal.phase === "active" ? "armed" : "disarmed"
|
|
4253
|
+
activation: goalActivations.get(id) ?? (projection.goal.phase === "active" ? "armed" : "disarmed")
|
|
4082
4254
|
});
|
|
4083
4255
|
/** Canonical fixture implementation of the generated Goal Remote contract. */
|
|
4084
4256
|
/** Canonical fixture implementation of the generated reference-discovery Remote contracts. */
|
|
@@ -4126,6 +4298,212 @@ window.__ModuleLoader__.load({
|
|
|
4126
4298
|
}
|
|
4127
4299
|
};
|
|
4128
4300
|
/**
|
|
4301
|
+
* Workspace text reads under `?fixture`.
|
|
4302
|
+
*
|
|
4303
|
+
* The sample content is deliberately more than one shape: the panel's states
|
|
4304
|
+
* (text, oversized, unreadable) are only demonstrable if the fixture can
|
|
4305
|
+
* produce each of them, and a preview that can only ever succeed hides its
|
|
4306
|
+
* own failure rendering.
|
|
4307
|
+
*/
|
|
4308
|
+
const WORKSPACE_FILES_ROOT = "/tmp/fixture";
|
|
4309
|
+
const workspaceFileTree = new Map([
|
|
4310
|
+
["", [
|
|
4311
|
+
{
|
|
4312
|
+
name: ".gitignore",
|
|
4313
|
+
type: "file",
|
|
4314
|
+
size: 24
|
|
4315
|
+
},
|
|
4316
|
+
{
|
|
4317
|
+
name: "dev.sock",
|
|
4318
|
+
type: "other"
|
|
4319
|
+
},
|
|
4320
|
+
{
|
|
4321
|
+
name: "notes",
|
|
4322
|
+
type: "directory"
|
|
4323
|
+
},
|
|
4324
|
+
{
|
|
4325
|
+
name: "package.json",
|
|
4326
|
+
type: "file",
|
|
4327
|
+
size: 512
|
|
4328
|
+
},
|
|
4329
|
+
{
|
|
4330
|
+
name: "README.md",
|
|
4331
|
+
type: "file",
|
|
4332
|
+
size: 640
|
|
4333
|
+
},
|
|
4334
|
+
{
|
|
4335
|
+
name: "src",
|
|
4336
|
+
type: "directory"
|
|
4337
|
+
}
|
|
4338
|
+
]],
|
|
4339
|
+
["notes", [{
|
|
4340
|
+
name: "demo.txt",
|
|
4341
|
+
type: "file",
|
|
4342
|
+
size: 14
|
|
4343
|
+
}, {
|
|
4344
|
+
name: "new-demo.txt",
|
|
4345
|
+
type: "file",
|
|
4346
|
+
size: 14
|
|
4347
|
+
}]],
|
|
4348
|
+
["src", [
|
|
4349
|
+
{
|
|
4350
|
+
name: "config.ts",
|
|
4351
|
+
type: "file",
|
|
4352
|
+
size: 211
|
|
4353
|
+
},
|
|
4354
|
+
{
|
|
4355
|
+
name: "index.ts",
|
|
4356
|
+
type: "file",
|
|
4357
|
+
size: 88
|
|
4358
|
+
},
|
|
4359
|
+
{
|
|
4360
|
+
name: "lib",
|
|
4361
|
+
type: "directory"
|
|
4362
|
+
}
|
|
4363
|
+
]],
|
|
4364
|
+
["src/lib", Array.from({ length: 24 }, (_, index) => ({
|
|
4365
|
+
name: `module-${String(index + 1).padStart(2, "0")}.ts`,
|
|
4366
|
+
type: "file",
|
|
4367
|
+
size: 96 + index
|
|
4368
|
+
}))]
|
|
4369
|
+
]);
|
|
4370
|
+
/** Resolve a `list` argument to its workspace-relative path, or undefined when it leaves the root. */
|
|
4371
|
+
const workspaceFilePath = (path) => {
|
|
4372
|
+
const segments = [];
|
|
4373
|
+
for (const segment of (path.startsWith("/") ? path : `${WORKSPACE_FILES_ROOT}/${path}`).split("/")) {
|
|
4374
|
+
if (segment === "" || segment === ".") continue;
|
|
4375
|
+
if (segment === "..") {
|
|
4376
|
+
segments.pop();
|
|
4377
|
+
continue;
|
|
4378
|
+
}
|
|
4379
|
+
segments.push(segment);
|
|
4380
|
+
}
|
|
4381
|
+
const absolute = `/${segments.join("/")}`;
|
|
4382
|
+
if (absolute !== WORKSPACE_FILES_ROOT && !absolute.startsWith(`${WORKSPACE_FILES_ROOT}/`)) return void 0;
|
|
4383
|
+
return absolute.slice(13);
|
|
4384
|
+
};
|
|
4385
|
+
const WORKSPACE_FILE_PAGE_LINES = 5e3;
|
|
4386
|
+
/**
|
|
4387
|
+
* Sample text for any readable path: a heading plus two lines of copy. The
|
|
4388
|
+
* replayed conversation's `demo` files, and any `huge` path, run past two
|
|
4389
|
+
* default pages so paging can be exercised without a real workspace.
|
|
4390
|
+
*/
|
|
4391
|
+
const workspaceFileLines = (path) => {
|
|
4392
|
+
const head = [
|
|
4393
|
+
`# ${path.slice(path.lastIndexOf("/") + 1)}`,
|
|
4394
|
+
"",
|
|
4395
|
+
"fixture 模式下的示例文本,用于验收侧栏的文本预览。",
|
|
4396
|
+
"真实构建从工作区读取同名文件。"
|
|
4397
|
+
];
|
|
4398
|
+
return path.includes("demo") || path.includes("huge") ? [...head, ...Array.from({ length: 12e3 }, (_, index) => `第 ${index + 5} 行:用于验收分页与滚动的长文本样本。`)] : head;
|
|
4399
|
+
};
|
|
4400
|
+
const workspaceFileRemotes = {
|
|
4401
|
+
list(path) {
|
|
4402
|
+
if (path.length === 0) return {
|
|
4403
|
+
ok: false,
|
|
4404
|
+
error: {
|
|
4405
|
+
code: "gateway/bad-request",
|
|
4406
|
+
message: "path is required",
|
|
4407
|
+
details: {}
|
|
4408
|
+
}
|
|
4409
|
+
};
|
|
4410
|
+
const relative = workspaceFilePath(path);
|
|
4411
|
+
if (relative === void 0) return {
|
|
4412
|
+
ok: false,
|
|
4413
|
+
error: {
|
|
4414
|
+
code: "workspace-file/outside-workspace",
|
|
4415
|
+
message: `${path} is outside the workspace`,
|
|
4416
|
+
details: { path }
|
|
4417
|
+
}
|
|
4418
|
+
};
|
|
4419
|
+
const entries = workspaceFileTree.get(relative);
|
|
4420
|
+
if (entries === void 0) {
|
|
4421
|
+
const cut = relative.lastIndexOf("/");
|
|
4422
|
+
const name = relative.slice(cut + 1);
|
|
4423
|
+
const sibling = workspaceFileTree.get(cut === -1 ? "" : relative.slice(0, cut))?.find((entry) => entry.name === name);
|
|
4424
|
+
if (sibling === void 0) return {
|
|
4425
|
+
ok: false,
|
|
4426
|
+
error: {
|
|
4427
|
+
code: "workspace-file/not-found",
|
|
4428
|
+
message: `no entry at ${path}`,
|
|
4429
|
+
details: { path }
|
|
4430
|
+
}
|
|
4431
|
+
};
|
|
4432
|
+
return {
|
|
4433
|
+
ok: false,
|
|
4434
|
+
error: {
|
|
4435
|
+
code: "workspace-file/not-directory",
|
|
4436
|
+
message: `${path} is a ${sibling.type}`,
|
|
4437
|
+
details: {
|
|
4438
|
+
path,
|
|
4439
|
+
kind: sibling.type === "file" ? "file" : "other"
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
};
|
|
4443
|
+
}
|
|
4444
|
+
return {
|
|
4445
|
+
ok: true,
|
|
4446
|
+
value: {
|
|
4447
|
+
path: relative,
|
|
4448
|
+
entries,
|
|
4449
|
+
truncated: relative === "src/lib"
|
|
4450
|
+
}
|
|
4451
|
+
};
|
|
4452
|
+
},
|
|
4453
|
+
read(path, range) {
|
|
4454
|
+
const located = workspaceFileRemotes.stat(path);
|
|
4455
|
+
if (!located.ok) return located;
|
|
4456
|
+
const offset = range.offset ?? 1;
|
|
4457
|
+
const limit = range.limit ?? WORKSPACE_FILE_PAGE_LINES;
|
|
4458
|
+
if (!Number.isInteger(offset) || offset < 1 || !Number.isInteger(limit) || limit < 1 || limit > WORKSPACE_FILE_PAGE_LINES) return {
|
|
4459
|
+
ok: false,
|
|
4460
|
+
error: {
|
|
4461
|
+
code: "gateway/bad-request",
|
|
4462
|
+
message: "offset and limit must be positive integers within the page cap",
|
|
4463
|
+
details: {}
|
|
4464
|
+
}
|
|
4465
|
+
};
|
|
4466
|
+
const lines = workspaceFileLines(path);
|
|
4467
|
+
const page = lines.slice(offset - 1, offset - 1 + limit);
|
|
4468
|
+
return {
|
|
4469
|
+
ok: true,
|
|
4470
|
+
value: {
|
|
4471
|
+
...located.value,
|
|
4472
|
+
offset,
|
|
4473
|
+
text: page.join("\n"),
|
|
4474
|
+
lines: page.length,
|
|
4475
|
+
eof: offset - 1 + limit >= lines.length
|
|
4476
|
+
}
|
|
4477
|
+
};
|
|
4478
|
+
},
|
|
4479
|
+
stat(path) {
|
|
4480
|
+
if (path.length === 0) return {
|
|
4481
|
+
ok: false,
|
|
4482
|
+
error: {
|
|
4483
|
+
code: "gateway/bad-request",
|
|
4484
|
+
message: "path is required",
|
|
4485
|
+
details: {}
|
|
4486
|
+
}
|
|
4487
|
+
};
|
|
4488
|
+
if (path.endsWith(".png") || path.endsWith(".bin")) return {
|
|
4489
|
+
ok: false,
|
|
4490
|
+
error: {
|
|
4491
|
+
code: "workspace-file/not-text",
|
|
4492
|
+
message: `${path} is not UTF-8 text`,
|
|
4493
|
+
details: { path }
|
|
4494
|
+
}
|
|
4495
|
+
};
|
|
4496
|
+
return {
|
|
4497
|
+
ok: true,
|
|
4498
|
+
value: {
|
|
4499
|
+
absolutePath: path.startsWith("/") ? path : `/${path}`,
|
|
4500
|
+
version: "fx-v1",
|
|
4501
|
+
bytes: new TextEncoder().encode(workspaceFileLines(path).join("\n")).byteLength
|
|
4502
|
+
}
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
};
|
|
4506
|
+
/**
|
|
4129
4507
|
* Canonical fixture implementation of the generated Directory Picker Remote
|
|
4130
4508
|
* contract. The pick is deterministic — the keyless lanes drive the full
|
|
4131
4509
|
* pick-then-adopt path without an OS chooser — over the same design-mock
|
|
@@ -4192,6 +4570,15 @@ window.__ModuleLoader__.load({
|
|
|
4192
4570
|
}
|
|
4193
4571
|
};
|
|
4194
4572
|
const goalRemotes = {
|
|
4573
|
+
get(id) {
|
|
4574
|
+
const missing = requireGoalSession(id);
|
|
4575
|
+
if (missing !== void 0) return missing;
|
|
4576
|
+
const current = backscanGoal(logOf(id));
|
|
4577
|
+
return {
|
|
4578
|
+
ok: true,
|
|
4579
|
+
value: current === null ? void 0 : goalView(id, current)
|
|
4580
|
+
};
|
|
4581
|
+
},
|
|
4195
4582
|
create(id, request) {
|
|
4196
4583
|
const missing = requireGoalSession(id);
|
|
4197
4584
|
if (missing !== void 0) return missing;
|
|
@@ -4213,6 +4600,7 @@ window.__ModuleLoader__.load({
|
|
|
4213
4600
|
createdAt: now,
|
|
4214
4601
|
updatedAt: now
|
|
4215
4602
|
});
|
|
4603
|
+
setGoalActivation(id, "armed");
|
|
4216
4604
|
return {
|
|
4217
4605
|
ok: true,
|
|
4218
4606
|
value: { ref: {
|
|
@@ -4265,6 +4653,7 @@ window.__ModuleLoader__.load({
|
|
|
4265
4653
|
cleared: tombstone,
|
|
4266
4654
|
clearedAt: Date.now()
|
|
4267
4655
|
});
|
|
4656
|
+
setGoalActivation(id, "disarmed");
|
|
4268
4657
|
return {
|
|
4269
4658
|
ok: true,
|
|
4270
4659
|
value: tombstone
|
|
@@ -4289,17 +4678,21 @@ window.__ModuleLoader__.load({
|
|
|
4289
4678
|
const current = resolved.value;
|
|
4290
4679
|
const goal = next(current);
|
|
4291
4680
|
if (goal === void 0) return goalFailure(`invalid goal transition from "${current.goal.phase}"`);
|
|
4681
|
+
const currentActivation = goalActivations.get(id) ?? (current.goal.phase === "active" ? "armed" : "disarmed");
|
|
4682
|
+
const activation = goal.phase === "active" ? current.goal.phase === "active" ? currentActivation : "armed" : "disarmed";
|
|
4683
|
+
const projection = appendGoalChange(id, {
|
|
4684
|
+
kind: "goal/change",
|
|
4685
|
+
version: 1,
|
|
4686
|
+
operation: goal.phase === current.goal.phase ? "edit" : goal.phase === "paused" ? "pause" : goal.phase === "active" ? "resume" : "complete",
|
|
4687
|
+
goal,
|
|
4688
|
+
roundsStarted: current.roundsStarted,
|
|
4689
|
+
createdAt: current.createdAt,
|
|
4690
|
+
updatedAt: Date.now()
|
|
4691
|
+
});
|
|
4692
|
+
setGoalActivation(id, activation);
|
|
4292
4693
|
return {
|
|
4293
4694
|
ok: true,
|
|
4294
|
-
value: goalView(
|
|
4295
|
-
kind: "goal/change",
|
|
4296
|
-
version: 1,
|
|
4297
|
-
operation: goal.phase === current.goal.phase ? "edit" : goal.phase === "paused" ? "pause" : goal.phase === "active" ? "resume" : "complete",
|
|
4298
|
-
goal,
|
|
4299
|
-
roundsStarted: current.roundsStarted,
|
|
4300
|
-
createdAt: current.createdAt,
|
|
4301
|
-
updatedAt: Date.now()
|
|
4302
|
-
}))
|
|
4695
|
+
value: goalView(id, projection)
|
|
4303
4696
|
};
|
|
4304
4697
|
}
|
|
4305
4698
|
/** Canonical fixture implementation of the generated AgentPresets Remote contract. */
|
|
@@ -4441,6 +4834,16 @@ window.__ModuleLoader__.load({
|
|
|
4441
4834
|
}
|
|
4442
4835
|
});
|
|
4443
4836
|
},
|
|
4837
|
+
/** Disarm the single active goal without a durable phase change. */
|
|
4838
|
+
disarmOnlyGoal() {
|
|
4839
|
+
const active = [...logs.entries()].filter(([, log]) => {
|
|
4840
|
+
return backscanGoal(log)?.goal.phase === "active";
|
|
4841
|
+
});
|
|
4842
|
+
if (active.length !== 1) throw new Error(`fixture: expected one active goal, found ${String(active.length)}`);
|
|
4843
|
+
const [session] = active;
|
|
4844
|
+
if (session === void 0) return;
|
|
4845
|
+
setGoalActivation(session[0], "disarmed");
|
|
4846
|
+
},
|
|
4444
4847
|
/** Start an externally paced reasoning stream for the opt-in browser stress lane. */
|
|
4445
4848
|
startReasoningChunkStorm(id, chunkCount, chunksPerInterval, intervalMs) {
|
|
4446
4849
|
if (!Number.isSafeInteger(chunkCount) || chunkCount < 1) throw new Error("fixture: reasoning chunk count must be a positive safe integer");
|
|
@@ -5241,6 +5644,30 @@ window.__ModuleLoader__.load({
|
|
|
5241
5644
|
workspaceConns.delete(conn);
|
|
5242
5645
|
}
|
|
5243
5646
|
}
|
|
5647
|
+
async function* openWorkspaceFileChanges(signal) {
|
|
5648
|
+
signal.throwIfAborted();
|
|
5649
|
+
const conn = new FxInbox();
|
|
5650
|
+
const breakNow = () => {
|
|
5651
|
+
conn.breakNow();
|
|
5652
|
+
};
|
|
5653
|
+
streamBreakers.add(breakNow);
|
|
5654
|
+
const announce = options.fileChanges ? setTimeout(() => {
|
|
5655
|
+
conn.push({
|
|
5656
|
+
kind: "change",
|
|
5657
|
+
change: {
|
|
5658
|
+
absolutePath: `${WORKSPACE_FILES_ROOT}/notes/demo.txt`,
|
|
5659
|
+
version: "fx-demo-v2"
|
|
5660
|
+
}
|
|
5661
|
+
});
|
|
5662
|
+
}, 1e3) : void 0;
|
|
5663
|
+
try {
|
|
5664
|
+
yield { kind: "ready" };
|
|
5665
|
+
yield* conn.drain(signal);
|
|
5666
|
+
} finally {
|
|
5667
|
+
if (announce !== void 0) clearTimeout(announce);
|
|
5668
|
+
streamBreakers.delete(breakNow);
|
|
5669
|
+
}
|
|
5670
|
+
}
|
|
5244
5671
|
async function* openRemoteEvents(signal) {
|
|
5245
5672
|
signal.throwIfAborted();
|
|
5246
5673
|
const clientId = randomUuid();
|
|
@@ -5291,7 +5718,7 @@ window.__ModuleLoader__.load({
|
|
|
5291
5718
|
yield {
|
|
5292
5719
|
type: "snapshot",
|
|
5293
5720
|
header: {
|
|
5294
|
-
version:
|
|
5721
|
+
version: 3,
|
|
5295
5722
|
id: sessionId,
|
|
5296
5723
|
createdAt: summary.updatedAt,
|
|
5297
5724
|
...summary.cwd === void 0 ? {} : { cwd: summary.cwd },
|
|
@@ -5525,6 +5952,7 @@ window.__ModuleLoader__.load({
|
|
|
5525
5952
|
case "directoryPicker/pick": return Promise.resolve(directoryPickerRemotes.pick());
|
|
5526
5953
|
case "directoryPicker/list": return Promise.resolve(directoryPickerRemotes.list(args.path));
|
|
5527
5954
|
case "directoryPicker/createDirectory": return Promise.resolve(directoryPickerRemotes.createDirectory(args.path ?? "", args.name ?? ""));
|
|
5955
|
+
case "goals/get": return Promise.resolve(goalRemotes.get(sessionId));
|
|
5528
5956
|
case "goals/create": return Promise.resolve(goalRemotes.create(sessionId, {
|
|
5529
5957
|
objective: request?.objective,
|
|
5530
5958
|
...request?.maxGoalRounds === void 0 ? {} : { maxGoalRounds: request.maxGoalRounds }
|
|
@@ -5579,6 +6007,9 @@ window.__ModuleLoader__.load({
|
|
|
5579
6007
|
}] });
|
|
5580
6008
|
}
|
|
5581
6009
|
case "session/openWorkspacePath": return sessionOk({ opened: true });
|
|
6010
|
+
case "workspaceFiles/read": return Promise.resolve(workspaceFileRemotes.read(args.path ?? "", args.range ?? {}));
|
|
6011
|
+
case "workspaceFiles/stat": return Promise.resolve(workspaceFileRemotes.stat(args.path ?? ""));
|
|
6012
|
+
case "workspaceFiles/list": return Promise.resolve(workspaceFileRemotes.list(args.path ?? ""));
|
|
5582
6013
|
case "session/canOpenWorkspacePath": return Promise.resolve({
|
|
5583
6014
|
ok: true,
|
|
5584
6015
|
value: true
|
|
@@ -5696,6 +6127,7 @@ window.__ModuleLoader__.load({
|
|
|
5696
6127
|
case "session/control": return openControl(signal);
|
|
5697
6128
|
case "session/follow": return openFollow(args.request, signal);
|
|
5698
6129
|
case "workspace/follow": return openWorkspace(signal);
|
|
6130
|
+
case "workspaceFiles/changes": return openWorkspaceFileChanges(signal);
|
|
5699
6131
|
default: throw new Error(`fixture connection stream endpoint ${JSON.stringify(endpoint)} is unavailable`);
|
|
5700
6132
|
}
|
|
5701
6133
|
}
|
|
@@ -5717,7 +6149,8 @@ window.__ModuleLoader__.load({
|
|
|
5717
6149
|
rejectPrompt: query.get("fixturePrompt") === "reject",
|
|
5718
6150
|
failWorkspaceAttach: query.get("fixtureAttach") === "fail",
|
|
5719
6151
|
dropSessionCreateResponse: query.get("fixtureSessionCreate") === "drop-response",
|
|
5720
|
-
createFrameOrder: query.get("fixtureFrames") === "workspace-first" ? "workspace-first" : "session-first"
|
|
6152
|
+
createFrameOrder: query.get("fixtureFrames") === "workspace-first" ? "workspace-first" : "session-first",
|
|
6153
|
+
fileChanges: query.get("fixtureFileChanges") === "demo"
|
|
5721
6154
|
};
|
|
5722
6155
|
}
|
|
5723
6156
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -733,7 +733,7 @@ function assertImageBodyCapacity(ctx, maxRequestBodyBytes) {
|
|
|
733
733
|
if (maxRequestBodyBytes < requiredImageBodyBytes) throw new Error(`client-connection maxRequestBodyBytes (${String(maxRequestBodyBytes)}) must be at least ${String(requiredImageBodyBytes)} for the configured aggregate image limit`);
|
|
734
734
|
}
|
|
735
735
|
/** Services required before providing Connection. */
|
|
736
|
-
const inject = ["
|
|
736
|
+
const inject = ["credentials"];
|
|
737
737
|
const Config = z.object({
|
|
738
738
|
recovery: ConnectionRecoveryConfigSchema.default({}),
|
|
739
739
|
trustedHosts: z.array(String).default([]),
|
|
@@ -741,9 +741,9 @@ const Config = z.object({
|
|
|
741
741
|
maxRequestBodyBytes: z.natural().min(1).default(DEFAULT_MAX_REQUEST_BODY_BYTES)
|
|
742
742
|
});
|
|
743
743
|
/**
|
|
744
|
-
*
|
|
745
|
-
* the
|
|
746
|
-
*
|
|
744
|
+
* Provides carrier-neutral RPC and Fetch registries. When `webServer` is
|
|
745
|
+
* present, the plugin also mounts the `/api` browser transport with Host/Origin
|
|
746
|
+
* checks and persistent browser authentication.
|
|
747
747
|
* @param ctx - Host plugin context.
|
|
748
748
|
* @param config - resolved plugin config (schema defaults applied).
|
|
749
749
|
*/
|
|
@@ -755,28 +755,31 @@ async function apply(ctx, config) {
|
|
|
755
755
|
for (const entry of trustedHosts) assertTrustedAuthority(entry);
|
|
756
756
|
assertImageBodyCapacity(ctx, maxRequestBodyBytes);
|
|
757
757
|
const connection = new HostConnectionService(ctx, trustedHosts, await BrowserAuth.create(ctx.root, ctx.credentials, cookieMaxAgeDays));
|
|
758
|
-
ctx.
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
758
|
+
ctx.inject(["webServer"], (webCtx) => {
|
|
759
|
+
assertImageBodyCapacity(webCtx, maxRequestBodyBytes);
|
|
760
|
+
webCtx.on("webserver/index-inject", (table) => {
|
|
761
|
+
table.push({
|
|
762
|
+
kind: "global",
|
|
763
|
+
name: "__DSH_CONNECTION_RECOVERY__",
|
|
764
|
+
value: recovery
|
|
765
|
+
});
|
|
763
766
|
});
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
767
|
+
const fetchHandler = connection.createSharedFetchHandler(API_PATH);
|
|
768
|
+
const route = {
|
|
769
|
+
kind: "prefix",
|
|
770
|
+
path: API_PATH,
|
|
771
|
+
handler: async (req, res) => {
|
|
772
|
+
const rejection = connection.requestRejection(req);
|
|
773
|
+
if (rejection !== void 0) {
|
|
774
|
+
res.writeHead(rejection);
|
|
775
|
+
res.end(rejection === 401 ? "unauthorized" : "forbidden");
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
await bridge(req, res, fetchHandler, maxRequestBodyBytes);
|
|
775
779
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
};
|
|
779
|
-
ctx.effect(() => ctx.webServer.register(route), "client-connection: /api route");
|
|
780
|
+
};
|
|
781
|
+
webCtx.effect(() => webCtx.webServer.register(route), "client-connection: /api route");
|
|
782
|
+
});
|
|
780
783
|
ctx.inject(["attachments"], (attachmentCtx) => {
|
|
781
784
|
assertImageBodyCapacity(attachmentCtx, maxRequestBodyBytes);
|
|
782
785
|
});
|
|
@@ -42,6 +42,8 @@ export interface FixtureOptions {
|
|
|
42
42
|
dropSessionCreateResponse?: boolean;
|
|
43
43
|
/** Order of the two successful create frames. */
|
|
44
44
|
createFrameOrder?: 'session-first' | 'workspace-first';
|
|
45
|
+
/** Announce one Agent write to `notes/demo.txt` shortly after a `workspaceFiles/changes` stream opens. */
|
|
46
|
+
fileChanges?: boolean;
|
|
45
47
|
}
|
|
46
48
|
/** Fixture RPC face over one in-memory state graph. */
|
|
47
49
|
export interface FixtureWorld {
|
package/lib/types/index.d.ts
CHANGED
|
@@ -31,9 +31,9 @@ export interface ConnectionConfig {
|
|
|
31
31
|
}
|
|
32
32
|
export declare const Config: z<ConnectionConfig>;
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
35
|
-
* the
|
|
36
|
-
*
|
|
34
|
+
* Provides carrier-neutral RPC and Fetch registries. When `webServer` is
|
|
35
|
+
* present, the plugin also mounts the `/api` browser transport with Host/Origin
|
|
36
|
+
* checks and persistent browser authentication.
|
|
37
37
|
* @param ctx - Host plugin context.
|
|
38
38
|
* @param config - resolved plugin config (schema defaults applied).
|
|
39
39
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-connection",
|
|
3
3
|
"description": "Authenticated RPC transport, generation lifecycle, and browser fixture",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"zod": "^4.4.3",
|
|
38
|
-
"@deepseek-ai/dsh-credentials": "^0.1.
|
|
38
|
+
"@deepseek-ai/dsh-credentials": "^0.1.5-alpha.2",
|
|
39
39
|
"@deepseek-ai/schemastery": "^3.18.2"
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
51
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.1.
|
|
52
|
-
"@deepseek-ai/dsh-attachment": "^0.1.
|
|
53
|
-
"@deepseek-ai/dsh-brand": "^0.1.
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/dsh-host-
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
59
|
-
"@deepseek-ai/dsh-
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
51
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.5-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-attachment": "^0.1.5-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-brand": "^0.1.5-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-host-directory-picker": "^0.1.5-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.5-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-commands": "^0.1.5-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-tool-todo": "^0.1.5-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-util-values": "^0.1.5-alpha.2"
|
|
62
62
|
}
|
|
63
63
|
}
|