dsh-loop-engine 0.1.5-rc4 → 0.1.7-rc1
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.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +174 -76
- package/lib/index.js +136 -57
- package/lib/invariant.js +8 -0
- package/lib/types/client/index.d.ts +16 -6
- package/lib/types/client/session-engine.d.ts +40 -6
- package/lib/types/client/store.d.ts +53 -18
- package/lib/types/client/turn-status.d.ts +27 -6
- package/lib/types/client/use-session-engine.d.ts +12 -4
- package/lib/types/compat.d.ts +25 -0
- package/lib/types/driver-core/host-servers.d.ts +6 -2
- package/lib/types/driver-core/hosted-engine-runtime.d.ts +3 -3
- package/lib/types/driver-core/model-handover.d.ts +22 -4
- package/lib/types/driver-core/system-head.d.ts +43 -0
- package/lib/types/engine-codex/model-handover.d.ts +21 -9
- package/lib/types/engine-pi/model-handover.d.ts +4 -0
- package/lib/types/index.d.ts +25 -3
- package/lib/types/namespace.d.ts +20 -4
- package/lib/types/preset.d.ts +7 -3
- package/lib/types/settings.d.ts +31 -11
- package/package.json +27 -27
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ A **published** dsh needs no extra setup. Booting the harness from its **source
|
|
|
22
22
|
|
|
23
23
|
## Version compatibility
|
|
24
24
|
|
|
25
|
-
dsh-loop-engine is versioned **in lockstep with the harness it targets**: `<harness version>-rcN`. `0.1.5-rc3`
|
|
25
|
+
dsh-loop-engine is versioned **in lockstep with the harness it targets**: `<harness version>-rcN`. From `0.1.7-rc1` on, **one release serves two harness generations**: the 0.1.5 line (`>=0.1.5-rc.1 <0.1.6-0`, all sharing the old settings API) and `0.1.7-rc.1` (`>=0.1.7-rc.1 <0.1.8-0`). It detects the running generation at load and takes the matching code path, so a single published package installs onto either. Every harness package it consumes declares that union range in `peerDependencies`; a harness outside it fails loudly at boot or session resume. `0.1.5-rc3` … `0.1.5-rc5` target harness `0.1.5-rc.2`; `0.1.5-rc1`/`0.1.5-rc2` target `0.1.5-rc.1`; `1.0.0-rc8` … `1.0.0-rc15` target `0.1.2-rc.1`; `1.0.0-rc7` and earlier target `0.1.1-rc.2`.
|
|
26
26
|
|
|
27
27
|
### Requirements
|
|
28
28
|
|
package/README.zh.md
CHANGED
|
@@ -22,7 +22,7 @@ dsh plugin --profile web add dsh-loop-engine
|
|
|
22
22
|
|
|
23
23
|
## 版本兼容
|
|
24
24
|
|
|
25
|
-
`dsh-loop-engine` 与它针对的 harness **同版本对齐**:`<harness version>-rcN
|
|
25
|
+
`dsh-loop-engine` 与它针对的 harness **同版本对齐**:`<harness version>-rcN`。自 `0.1.7-rc1` 起,**一个发布版本同时服务两代 harness**:0.1.5 线(`>=0.1.5-rc.1 <0.1.6-0`,三段 `rc` 共用同一套旧 settings API)与 `0.1.7-rc.1`(`>=0.1.7-rc.1 <0.1.8-0`)。它在加载期探测当前是哪一代、走对应分支,所以同一份已发布产物在两代上都能安装运行。它消费的每个 harness 包都在 `peerDependencies` 里声明这个并集范围;超出范围会在启动或会话恢复时响亮地失败。`0.1.5-rc3` … `0.1.5-rc5` 针对 harness `0.1.5-rc.2`;`0.1.5-rc1`/`0.1.5-rc2` 针对 `0.1.5-rc.1`;`1.0.0-rc8` … `1.0.0-rc15` 针对 `0.1.2-rc.1`;`1.0.0-rc7` 及更早针对 `0.1.1-rc.2`。
|
|
26
26
|
|
|
27
27
|
### 环境要求
|
|
28
28
|
|
package/lib/client.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
var module = { exports: {} }; var exports = module.exports; window.__ModuleLoader__.load({ id: "dsh-loop-engine", factory: (require) => {
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
9
|
var __export = (target, all) => {
|
|
8
10
|
for (var name in all)
|
|
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/client/index.ts
|
|
@@ -28,6 +38,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
28
38
|
|
|
29
39
|
// src/client/LoopEngineSection.tsx
|
|
30
40
|
var import_react = require("react");
|
|
41
|
+
var primitives = __toESM(require("@deepseek-ai/dsh-client-ui-primitives"), 1);
|
|
31
42
|
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
32
43
|
|
|
33
44
|
// src/agent-preset-ids.ts
|
|
@@ -60,6 +71,7 @@ function hostedEngineOf(session) {
|
|
|
60
71
|
|
|
61
72
|
// src/client/LoopEngineSection.tsx
|
|
62
73
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
74
|
+
var IconChevronDown = primitives.IconChevronDownOutlineRegular ?? primitives.IconChevronDownOutline14;
|
|
63
75
|
var ENGINE_OPTIONS = [
|
|
64
76
|
{ value: "in-process", key: "engineInProcess" },
|
|
65
77
|
{ value: "claude-code", key: "engineClaudeCode" },
|
|
@@ -208,7 +220,7 @@ function LoopEngineSection(props) {
|
|
|
208
220
|
},
|
|
209
221
|
children: [
|
|
210
222
|
label,
|
|
211
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
223
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconChevronDown, { size: 14 })
|
|
212
224
|
]
|
|
213
225
|
}
|
|
214
226
|
)
|
|
@@ -253,9 +265,18 @@ var import_react2 = require("react");
|
|
|
253
265
|
|
|
254
266
|
// src/client/turn-status.ts
|
|
255
267
|
var ENGINE_ATTR = "data-loop-engine";
|
|
268
|
+
var RUNNING_ATTR = "data-loop-engine-running";
|
|
256
269
|
var PLUGIN_ID = "dsh-loop-engine";
|
|
257
|
-
var
|
|
258
|
-
|
|
270
|
+
var ROW_SELECTORS = [
|
|
271
|
+
/** 0.1.5 line: the turn-status row, whose class name ends `_turnStatus`. */
|
|
272
|
+
'[class$="_turnStatus"]',
|
|
273
|
+
/** 0.1.7 line: the turn-process button (`data-turn-process`); its status text is the label span. */
|
|
274
|
+
'button[data-turn-process] [class$="_label"]'
|
|
275
|
+
];
|
|
276
|
+
var sheetFor = (ROW) => {
|
|
277
|
+
const GATE = `html[${ENGINE_ATTR}][${RUNNING_ATTR}]`;
|
|
278
|
+
return `
|
|
279
|
+
${GATE} ${ROW}::before {
|
|
259
280
|
margin-right: 6px;
|
|
260
281
|
background: none;
|
|
261
282
|
-webkit-background-clip: border-box;
|
|
@@ -263,18 +284,34 @@ var STYLESHEET = `
|
|
|
263
284
|
}
|
|
264
285
|
|
|
265
286
|
/*
|
|
266
|
-
* The
|
|
287
|
+
* The engine-colored sweep \u2014 and, on the 0.1.7 line, the sweep itself.
|
|
267
288
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
289
|
+
* The 0.1.5 row paints its own gradient (a \`linear-gradient\` over
|
|
290
|
+
* --dsw-static-deepseek-*, clipped to the text) and only needs its animation
|
|
291
|
+
* re-asserted; this rule declares that same gradient so the 0.1.7 row, whose
|
|
292
|
+
* turn-process button is plain tertiary text, gets the sweep back. Either way
|
|
293
|
+
* the per-engine override below recolors it by swapping the two custom
|
|
294
|
+
* properties. ui-chat disables the animation under
|
|
295
|
+
* \`prefers-reduced-motion: reduce\`, and a media query carries no specificity \u2014
|
|
296
|
+
* so this attribute-gated rule outranks it. That is deliberate here:
|
|
297
|
+
* deployment images ship with Windows' client-area animation off
|
|
298
|
+
* (SPI_GETCLIENTAREAANIMATION false), and with the guard in force EVERY
|
|
299
|
+
* indicator on this row is frozen \u2014 the sweep and the glyph alike. Scoped to
|
|
300
|
+
* hosted engines, so in-process sessions keep the stock reduced-motion
|
|
301
|
+
* behaviour; delete this rule and restore the guard at the foot of the sheet to
|
|
302
|
+
* hand the decision back to the OS.
|
|
276
303
|
*/
|
|
277
|
-
|
|
304
|
+
${GATE} ${ROW} {
|
|
305
|
+
background: linear-gradient(90deg,
|
|
306
|
+
var(--dsw-static-deepseek-500) 0%,
|
|
307
|
+
var(--dsw-static-deepseek-500) 40%,
|
|
308
|
+
var(--dsw-static-deepseek-200) 50%,
|
|
309
|
+
var(--dsw-static-deepseek-500) 60%,
|
|
310
|
+
var(--dsw-static-deepseek-500) 100%);
|
|
311
|
+
color: #0000;
|
|
312
|
+
-webkit-text-fill-color: transparent;
|
|
313
|
+
-webkit-background-clip: text;
|
|
314
|
+
background-clip: text;
|
|
278
315
|
background-position: 100% 0;
|
|
279
316
|
background-size: 250% 100%;
|
|
280
317
|
animation: le-shimmer 1.8s linear infinite;
|
|
@@ -284,22 +321,22 @@ html[${ENGINE_ATTR}] [class$="_turnStatus"] {
|
|
|
284
321
|
to { background-position: 0 0; }
|
|
285
322
|
}
|
|
286
323
|
|
|
287
|
-
html[${ENGINE_ATTR}="claude-code"]
|
|
324
|
+
html[${ENGINE_ATTR}="claude-code"][${RUNNING_ATTR}] ${ROW} {
|
|
288
325
|
--dsw-static-deepseek-500: #d97757;
|
|
289
326
|
--dsw-static-deepseek-200: #f5bda6;
|
|
290
327
|
}
|
|
291
|
-
html[${ENGINE_ATTR}="claude-code"]
|
|
328
|
+
html[${ENGINE_ATTR}="claude-code"][${RUNNING_ATTR}] ${ROW}::before {
|
|
292
329
|
content: "\u273B";
|
|
293
330
|
color: #d97757;
|
|
294
331
|
-webkit-text-fill-color: #d97757;
|
|
295
332
|
animation: le-bloom 1.6s ease-in-out infinite;
|
|
296
333
|
}
|
|
297
334
|
|
|
298
|
-
html[${ENGINE_ATTR}="codex"]
|
|
335
|
+
html[${ENGINE_ATTR}="codex"][${RUNNING_ATTR}] ${ROW} {
|
|
299
336
|
--dsw-static-deepseek-500: #a9b1c0;
|
|
300
337
|
--dsw-static-deepseek-200: #e6eaf2;
|
|
301
338
|
}
|
|
302
|
-
html[${ENGINE_ATTR}="codex"]
|
|
339
|
+
html[${ENGINE_ATTR}="codex"][${RUNNING_ATTR}] ${ROW}::before {
|
|
303
340
|
content: "\u2022";
|
|
304
341
|
color: #a9b1c0;
|
|
305
342
|
-webkit-text-fill-color: #a9b1c0;
|
|
@@ -307,11 +344,11 @@ html[${ENGINE_ATTR}="codex"] [class$="_turnStatus"]::before {
|
|
|
307
344
|
animation: le-pulse 1.4s ease-in-out infinite;
|
|
308
345
|
}
|
|
309
346
|
|
|
310
|
-
html[${ENGINE_ATTR}="pi"]
|
|
347
|
+
html[${ENGINE_ATTR}="pi"][${RUNNING_ATTR}] ${ROW} {
|
|
311
348
|
--dsw-static-deepseek-500: #8e4ec6;
|
|
312
349
|
--dsw-static-deepseek-200: #d6bff0;
|
|
313
350
|
}
|
|
314
|
-
html[${ENGINE_ATTR}="pi"]
|
|
351
|
+
html[${ENGINE_ATTR}="pi"][${RUNNING_ATTR}] ${ROW}::before {
|
|
315
352
|
content: "\u280B";
|
|
316
353
|
color: #8e4ec6;
|
|
317
354
|
-webkit-text-fill-color: #8e4ec6;
|
|
@@ -319,11 +356,11 @@ html[${ENGINE_ATTR}="pi"] [class$="_turnStatus"]::before {
|
|
|
319
356
|
animation: le-braille 1s linear infinite;
|
|
320
357
|
}
|
|
321
358
|
|
|
322
|
-
html[${ENGINE_ATTR}="kimi"]
|
|
359
|
+
html[${ENGINE_ATTR}="kimi"][${RUNNING_ATTR}] ${ROW} {
|
|
323
360
|
--dsw-static-deepseek-500: #e5484d;
|
|
324
361
|
--dsw-static-deepseek-200: #f5b2b4;
|
|
325
362
|
}
|
|
326
|
-
html[${ENGINE_ATTR}="kimi"]
|
|
363
|
+
html[${ENGINE_ATTR}="kimi"][${RUNNING_ATTR}] ${ROW}::before {
|
|
327
364
|
content: "\u{1F317}";
|
|
328
365
|
color: #e5484d;
|
|
329
366
|
-webkit-text-fill-color: #e5484d;
|
|
@@ -380,10 +417,13 @@ html[${ENGINE_ATTR}="kimi"] [class$="_turnStatus"]::before {
|
|
|
380
417
|
}
|
|
381
418
|
|
|
382
419
|
`;
|
|
420
|
+
};
|
|
421
|
+
var STYLESHEET = ROW_SELECTORS.map(sheetFor).join("\n");
|
|
383
422
|
var focusedSessionId;
|
|
384
|
-
function reflectTurnStatusEngine(sessionId, engine) {
|
|
423
|
+
function reflectTurnStatusEngine(sessionId, engine, running) {
|
|
385
424
|
if (sessionId === void 0 || sessionId !== focusedSessionId) return;
|
|
386
425
|
writeTurnStatusEngine(engine);
|
|
426
|
+
if (running !== void 0) writeTurnStatusRunning(running);
|
|
387
427
|
}
|
|
388
428
|
function focusTurnStatusSession(sessionId) {
|
|
389
429
|
focusedSessionId = sessionId;
|
|
@@ -400,6 +440,12 @@ function writeTurnStatusEngine(engine) {
|
|
|
400
440
|
}
|
|
401
441
|
root.dataset.loopEngine = engine;
|
|
402
442
|
}
|
|
443
|
+
function writeTurnStatusRunning(running) {
|
|
444
|
+
if (typeof document === "undefined") return;
|
|
445
|
+
const root = document.documentElement;
|
|
446
|
+
if (running) root.dataset.loopEngineRunning = "";
|
|
447
|
+
else delete root.dataset.loopEngineRunning;
|
|
448
|
+
}
|
|
403
449
|
function installTurnStatusStyles(ctx) {
|
|
404
450
|
if (typeof document === "undefined") return;
|
|
405
451
|
ctx.effect(() => {
|
|
@@ -411,12 +457,13 @@ function installTurnStatusStyles(ctx) {
|
|
|
411
457
|
return () => {
|
|
412
458
|
tag.remove();
|
|
413
459
|
delete document.documentElement.dataset.loopEngine;
|
|
460
|
+
delete document.documentElement.dataset.loopEngineRunning;
|
|
414
461
|
};
|
|
415
462
|
}, "loop-engine: per-engine turn status styles");
|
|
416
463
|
}
|
|
417
464
|
|
|
418
465
|
// src/client/use-session-engine.ts
|
|
419
|
-
function useEngineOfSession(cache, sessionId) {
|
|
466
|
+
function useEngineOfSession(cache, sessionId, running) {
|
|
420
467
|
const [, bump] = (0, import_react2.useState)(0);
|
|
421
468
|
const report = sessionId === void 0 ? void 0 : cache.read(sessionId);
|
|
422
469
|
(0, import_react2.useEffect)(() => {
|
|
@@ -429,11 +476,11 @@ function useEngineOfSession(cache, sessionId) {
|
|
|
429
476
|
(0, import_react2.useEffect)(() => {
|
|
430
477
|
if (sessionId === void 0) return;
|
|
431
478
|
focusTurnStatusSession(sessionId);
|
|
432
|
-
reflectTurnStatusEngine(sessionId, engine);
|
|
479
|
+
reflectTurnStatusEngine(sessionId, engine, running);
|
|
433
480
|
return () => {
|
|
434
481
|
blurTurnStatusSession(sessionId);
|
|
435
482
|
};
|
|
436
|
-
}, [sessionId, engine]);
|
|
483
|
+
}, [sessionId, engine, running]);
|
|
437
484
|
return report;
|
|
438
485
|
}
|
|
439
486
|
|
|
@@ -574,8 +621,9 @@ var pill = {
|
|
|
574
621
|
whiteSpace: "nowrap"
|
|
575
622
|
};
|
|
576
623
|
function LoopEngineBadge(props) {
|
|
577
|
-
const { sessionId, sessionEngines, t } = props;
|
|
578
|
-
const
|
|
624
|
+
const { sessionId, sessionEngines, useSession, t } = props;
|
|
625
|
+
const running = useSession?.((snapshot) => snapshot.running);
|
|
626
|
+
const report = useEngineOfSession(sessionEngines, sessionId, running);
|
|
579
627
|
if (sessionId === void 0 || report === void 0) return null;
|
|
580
628
|
const engine = report.engine;
|
|
581
629
|
if (engine.kind === "unset") return null;
|
|
@@ -593,6 +641,7 @@ function LoopEngineBadge(props) {
|
|
|
593
641
|
|
|
594
642
|
// src/client/LoopEngineComposerSelect.tsx
|
|
595
643
|
var import_react3 = require("react");
|
|
644
|
+
var primitives2 = __toESM(require("@deepseek-ai/dsh-client-ui-primitives"), 1);
|
|
596
645
|
var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
597
646
|
|
|
598
647
|
// src/client/reload.ts
|
|
@@ -747,13 +796,15 @@ function descriptor(method, request, result) {
|
|
|
747
796
|
codec: {
|
|
748
797
|
mode: "strict",
|
|
749
798
|
typeSymbol: `${LOOP_ENGINE_PACKAGE}#${LOOP_ENGINE_REMOTE_NAMESPACE}/${method}:request`,
|
|
750
|
-
schema: request
|
|
799
|
+
schema: request,
|
|
800
|
+
create: () => request
|
|
751
801
|
}
|
|
752
802
|
}],
|
|
753
803
|
result: {
|
|
754
804
|
mode: "strict",
|
|
755
805
|
typeSymbol: `${LOOP_ENGINE_PACKAGE}#${LOOP_ENGINE_REMOTE_NAMESPACE}/${method}:result`,
|
|
756
|
-
schema: result
|
|
806
|
+
schema: result,
|
|
807
|
+
create: () => result
|
|
757
808
|
}
|
|
758
809
|
};
|
|
759
810
|
}
|
|
@@ -999,6 +1050,7 @@ function sessionEngineSwitcher(ctx, onSwitched, page = browserPage()) {
|
|
|
999
1050
|
|
|
1000
1051
|
// src/client/LoopEngineComposerSelect.tsx
|
|
1001
1052
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1053
|
+
var IconChevronDown2 = primitives2.IconChevronDownOutlineRegular ?? primitives2.IconChevronDownOutline14;
|
|
1002
1054
|
var ENGINE_OPTIONS2 = [
|
|
1003
1055
|
{ value: "in-process", key: "engineInProcess" },
|
|
1004
1056
|
{ value: "claude-code", key: "engineClaudeCode" },
|
|
@@ -1091,9 +1143,10 @@ var noticeDetail = {
|
|
|
1091
1143
|
wordBreak: "break-word"
|
|
1092
1144
|
};
|
|
1093
1145
|
function LoopEngineComposerSelect(props) {
|
|
1094
|
-
const { controller, useSnapshot, sessionId, sessionEngines, switchEngine, t } = props;
|
|
1146
|
+
const { controller, useSnapshot, sessionId, sessionEngines, switchEngine, useSession, t } = props;
|
|
1095
1147
|
const { status, engine: defaultEngine, showInComposer, writable } = useSnapshot((snapshot) => snapshot);
|
|
1096
|
-
const
|
|
1148
|
+
const running = useSession?.((snapshot) => snapshot.running);
|
|
1149
|
+
const resolved = useEngineOfSession(sessionEngines, sessionId, running);
|
|
1097
1150
|
const { label, engine, selectedId, pending } = sessionId === void 0 ? defaultFace(defaultEngine) : resolved === void 0 ? READING : triggerFace(resolved);
|
|
1098
1151
|
const switchReady = sessionId === void 0 || engineSwitchReady(resolved);
|
|
1099
1152
|
const [open, setOpen] = (0, import_react3.useState)(false);
|
|
@@ -1206,7 +1259,7 @@ function LoopEngineComposerSelect(props) {
|
|
|
1206
1259
|
labelText,
|
|
1207
1260
|
pendingText
|
|
1208
1261
|
] }),
|
|
1209
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: open ? chevronOpen : chevron, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: open ? chevronOpen : chevron, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconChevronDown2, { size: 14 }) })
|
|
1210
1263
|
]
|
|
1211
1264
|
}
|
|
1212
1265
|
)
|
|
@@ -1255,12 +1308,12 @@ function decodeLoopEngine(section) {
|
|
|
1255
1308
|
}
|
|
1256
1309
|
var LoopEngineStore = class {
|
|
1257
1310
|
/**
|
|
1258
|
-
* @param
|
|
1311
|
+
* @param transport - the settings channel the store follows.
|
|
1259
1312
|
*/
|
|
1260
|
-
constructor(
|
|
1261
|
-
this.
|
|
1313
|
+
constructor(transport) {
|
|
1314
|
+
this.transport = transport;
|
|
1262
1315
|
}
|
|
1263
|
-
|
|
1316
|
+
transport;
|
|
1264
1317
|
/** uSES-safe state source shared by the registered settings section. */
|
|
1265
1318
|
store = (0, import_dsh_client_store.createSnapshotStore)({
|
|
1266
1319
|
status: "loading",
|
|
@@ -1271,16 +1324,17 @@ var LoopEngineStore = class {
|
|
|
1271
1324
|
});
|
|
1272
1325
|
following;
|
|
1273
1326
|
saving = false;
|
|
1274
|
-
/** Begin following the
|
|
1327
|
+
/** Begin following the transport and publish its current answer. */
|
|
1275
1328
|
load() {
|
|
1276
|
-
this.following ??= this.
|
|
1329
|
+
this.following ??= this.transport.subscribe(() => {
|
|
1277
1330
|
this.derive();
|
|
1278
1331
|
});
|
|
1279
1332
|
this.derive();
|
|
1280
1333
|
}
|
|
1281
1334
|
/**
|
|
1282
|
-
* Persist the selected engine. Success is
|
|
1283
|
-
*
|
|
1335
|
+
* Persist the selected engine. Success is the transport's accepted answer
|
|
1336
|
+
* (when it reports one) checked against the snapshot the write left behind,
|
|
1337
|
+
* so a refused write reports error after its recovery.
|
|
1284
1338
|
* @param engine - the engine to select for future Agent turns.
|
|
1285
1339
|
* @returns whether the write landed.
|
|
1286
1340
|
*/
|
|
@@ -1290,14 +1344,14 @@ var LoopEngineStore = class {
|
|
|
1290
1344
|
state.status = "saving";
|
|
1291
1345
|
state.error = null;
|
|
1292
1346
|
});
|
|
1347
|
+
let accepted = true;
|
|
1293
1348
|
try {
|
|
1294
|
-
await this.
|
|
1349
|
+
accepted = await this.transport.set("engine", engine);
|
|
1295
1350
|
} finally {
|
|
1296
1351
|
this.saving = false;
|
|
1297
1352
|
}
|
|
1298
1353
|
this.derive();
|
|
1299
|
-
const
|
|
1300
|
-
const landed = settled === engine;
|
|
1354
|
+
const landed = accepted !== false && this.store.getSnapshot().engine === engine;
|
|
1301
1355
|
if (!landed) {
|
|
1302
1356
|
this.store.update((state) => {
|
|
1303
1357
|
state.status = "unavailable";
|
|
@@ -1307,10 +1361,9 @@ var LoopEngineStore = class {
|
|
|
1307
1361
|
return landed;
|
|
1308
1362
|
}
|
|
1309
1363
|
/**
|
|
1310
|
-
* Persist whether the composer shows the engine picker.
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1313
|
-
* the page — the toggle only changes composer visibility.
|
|
1364
|
+
* Persist whether the composer shows the engine picker. Unlike
|
|
1365
|
+
* {@link setEngine}, landing does not reload the page — the toggle only
|
|
1366
|
+
* changes composer visibility.
|
|
1314
1367
|
* @param show - whether the chat page composer reveals the engine picker.
|
|
1315
1368
|
* @returns whether the write landed.
|
|
1316
1369
|
*/
|
|
@@ -1320,14 +1373,14 @@ var LoopEngineStore = class {
|
|
|
1320
1373
|
state.status = "saving";
|
|
1321
1374
|
state.error = null;
|
|
1322
1375
|
});
|
|
1376
|
+
let accepted = true;
|
|
1323
1377
|
try {
|
|
1324
|
-
await this.
|
|
1378
|
+
accepted = await this.transport.set("showInComposer", show);
|
|
1325
1379
|
} finally {
|
|
1326
1380
|
this.saving = false;
|
|
1327
1381
|
}
|
|
1328
1382
|
this.derive();
|
|
1329
|
-
const
|
|
1330
|
-
const landed = settled === show;
|
|
1383
|
+
const landed = accepted !== false && this.store.getSnapshot().showInComposer === show;
|
|
1331
1384
|
if (!landed) {
|
|
1332
1385
|
this.store.update((state) => {
|
|
1333
1386
|
state.status = "unavailable";
|
|
@@ -1336,15 +1389,15 @@ var LoopEngineStore = class {
|
|
|
1336
1389
|
}
|
|
1337
1390
|
return landed;
|
|
1338
1391
|
}
|
|
1339
|
-
/** Stop following the
|
|
1392
|
+
/** Stop following the transport. */
|
|
1340
1393
|
dispose() {
|
|
1341
1394
|
this.following?.();
|
|
1342
1395
|
this.following = void 0;
|
|
1343
1396
|
}
|
|
1344
1397
|
derive() {
|
|
1345
1398
|
if (this.saving) return;
|
|
1346
|
-
const
|
|
1347
|
-
switch (
|
|
1399
|
+
const snapshot = this.transport.getSnapshot();
|
|
1400
|
+
switch (snapshot.status) {
|
|
1348
1401
|
case "loading":
|
|
1349
1402
|
this.store.update((state) => {
|
|
1350
1403
|
state.status = "loading";
|
|
@@ -1360,64 +1413,109 @@ var LoopEngineStore = class {
|
|
|
1360
1413
|
});
|
|
1361
1414
|
return;
|
|
1362
1415
|
case "ready": {
|
|
1363
|
-
const engine =
|
|
1364
|
-
const showInComposer =
|
|
1416
|
+
const engine = snapshot.value?.engine ?? "in-process";
|
|
1417
|
+
const showInComposer = snapshot.value?.showInComposer ?? true;
|
|
1365
1418
|
this.store.update((state) => {
|
|
1366
1419
|
state.status = "ready";
|
|
1367
1420
|
state.engine = engine;
|
|
1368
1421
|
state.showInComposer = showInComposer;
|
|
1369
|
-
state.writable =
|
|
1422
|
+
state.writable = snapshot.writable;
|
|
1370
1423
|
state.error = null;
|
|
1371
1424
|
});
|
|
1372
1425
|
return;
|
|
1373
1426
|
}
|
|
1374
1427
|
default: {
|
|
1375
|
-
const exhaustive =
|
|
1376
|
-
throw new Error(`unexpected loop engine
|
|
1428
|
+
const exhaustive = snapshot.status;
|
|
1429
|
+
throw new Error(`unexpected loop engine form status: ${String(exhaustive)}`);
|
|
1377
1430
|
}
|
|
1378
1431
|
}
|
|
1379
1432
|
}
|
|
1380
1433
|
};
|
|
1381
1434
|
|
|
1382
1435
|
// src/namespace.ts
|
|
1383
|
-
var LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "
|
|
1436
|
+
var LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "loop-engine";
|
|
1437
|
+
var LEGACY_LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "agent-loop-engine";
|
|
1384
1438
|
|
|
1385
1439
|
// src/client/index.ts
|
|
1386
1440
|
var NS = "settings.loop-engine";
|
|
1387
|
-
var inject = ["slots", "locale"
|
|
1441
|
+
var inject = ["slots", "locale"];
|
|
1388
1442
|
function apply(ctx) {
|
|
1389
1443
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "loop-engine: copy dictionaries");
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1444
|
+
installTurnStatusStyles(ctx);
|
|
1445
|
+
const sessionEngines = createSessionEngineCache(ctx);
|
|
1446
|
+
installReloadReturn(ctx);
|
|
1447
|
+
const t = ctx.locale.bind(NS);
|
|
1448
|
+
let source;
|
|
1449
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1450
|
+
const transport = {
|
|
1451
|
+
getSnapshot: () => source?.getSnapshot() ?? { status: "loading", value: void 0, writable: false },
|
|
1452
|
+
subscribe: (listener) => {
|
|
1453
|
+
listeners.add(listener);
|
|
1454
|
+
return () => {
|
|
1455
|
+
listeners.delete(listener);
|
|
1456
|
+
};
|
|
1457
|
+
},
|
|
1458
|
+
set: (field, value) => source === void 0 ? Promise.resolve(false) : source.set(field, value)
|
|
1459
|
+
};
|
|
1460
|
+
const adopt = (next) => {
|
|
1461
|
+
source = next;
|
|
1462
|
+
const forget = next.subscribe(() => {
|
|
1463
|
+
for (const listener of [...listeners]) listener();
|
|
1464
|
+
});
|
|
1465
|
+
for (const listener of [...listeners]) listener();
|
|
1466
|
+
return () => {
|
|
1467
|
+
forget();
|
|
1468
|
+
if (source === next) source = void 0;
|
|
1469
|
+
};
|
|
1470
|
+
};
|
|
1471
|
+
const controller = new LoopEngineStore(transport);
|
|
1395
1472
|
ctx.effect(() => {
|
|
1396
1473
|
controller.load();
|
|
1397
1474
|
return () => {
|
|
1398
1475
|
controller.dispose();
|
|
1399
1476
|
};
|
|
1400
1477
|
}, "loop-engine: store lifecycle");
|
|
1401
|
-
installTurnStatusStyles(ctx);
|
|
1402
|
-
const sessionEngines = createSessionEngineCache(ctx);
|
|
1403
|
-
installReloadReturn(ctx);
|
|
1404
|
-
const t = ctx.locale.bind(NS);
|
|
1405
1478
|
const injected = () => ({
|
|
1406
1479
|
controller,
|
|
1407
1480
|
hooks: { snapshot: controller.store },
|
|
1408
1481
|
t
|
|
1409
1482
|
});
|
|
1410
|
-
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1483
|
+
const registerPage = () => ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1411
1484
|
name: "settings.section",
|
|
1412
1485
|
id: "loop-engine",
|
|
1413
1486
|
order: 30,
|
|
1414
1487
|
label: () => t("nav"),
|
|
1415
1488
|
inject: injected
|
|
1416
1489
|
}, LoopEngineSection));
|
|
1417
|
-
ctx.inject(["
|
|
1490
|
+
ctx.inject(["configForms"], (scope) => {
|
|
1491
|
+
const form = scope.configForms.get(LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL);
|
|
1492
|
+
scope.effect(() => adopt({
|
|
1493
|
+
getSnapshot: () => form.getSnapshot(),
|
|
1494
|
+
subscribe: (listener) => form.subscribe(listener),
|
|
1495
|
+
set: (field, value) => form.set(field, value)
|
|
1496
|
+
}), "loop-engine: settings transport (config forms)");
|
|
1497
|
+
scope.effect(
|
|
1498
|
+
() => scope.configForms.whileServed([LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL], registerPage),
|
|
1499
|
+
"loop-engine: settings page"
|
|
1500
|
+
);
|
|
1501
|
+
});
|
|
1502
|
+
ctx.inject(["settingsScope"], (scope) => {
|
|
1503
|
+
const scopes = scope.settingsScope;
|
|
1504
|
+
const bound = scopes.bind({
|
|
1505
|
+
namespace: LEGACY_LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL,
|
|
1506
|
+
decode: decodeLoopEngine
|
|
1507
|
+
});
|
|
1508
|
+
scope.effect(() => adopt({
|
|
1509
|
+
getSnapshot: () => bound.getSnapshot(),
|
|
1510
|
+
subscribe: (listener) => bound.subscribe(listener),
|
|
1511
|
+
set: (field, value) => bound.set(field, value)
|
|
1512
|
+
}), "loop-engine: settings transport (settings scope)");
|
|
1513
|
+
scope.effect(registerPage, "loop-engine: settings page");
|
|
1514
|
+
});
|
|
1515
|
+
ctx.inject(["slots", "conversation"], (scope) => {
|
|
1418
1516
|
const badgeInjected = () => ({ sessionEngines, t });
|
|
1419
|
-
|
|
1420
|
-
return
|
|
1517
|
+
scope.effect(() => {
|
|
1518
|
+
return scope.slots.register({
|
|
1421
1519
|
name: "conversation.session.header.actions",
|
|
1422
1520
|
id: "loop-engine",
|
|
1423
1521
|
// Static session context precedes interactive actions (agent-preset's
|
|
@@ -1431,7 +1529,7 @@ function apply(ctx) {
|
|
|
1431
1529
|
const switchEngine = sessionEngineSwitcher(ctx, (sessionId) => {
|
|
1432
1530
|
sessionEngines.invalidate(sessionId);
|
|
1433
1531
|
});
|
|
1434
|
-
ctx.inject(["slots", "conversation"], (
|
|
1532
|
+
ctx.inject(["slots", "conversation"], (scope) => {
|
|
1435
1533
|
const composerInjected = () => ({
|
|
1436
1534
|
controller,
|
|
1437
1535
|
hooks: { snapshot: controller.store },
|
|
@@ -1439,8 +1537,8 @@ function apply(ctx) {
|
|
|
1439
1537
|
switchEngine,
|
|
1440
1538
|
t
|
|
1441
1539
|
});
|
|
1442
|
-
|
|
1443
|
-
return
|
|
1540
|
+
scope.effect(() => {
|
|
1541
|
+
return scope.slots.register({
|
|
1444
1542
|
name: "conversation.input.right",
|
|
1445
1543
|
id: "loop-engine",
|
|
1446
1544
|
order: 0,
|