dsh-loop-engine 0.1.5-rc5 → 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 +124 -48
- 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/system-head.d.ts +43 -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/lib/index.js
CHANGED
|
@@ -395,21 +395,30 @@ function renderAssistantBlocks(blocks) {
|
|
|
395
395
|
}
|
|
396
396
|
return sections.join("\n\n");
|
|
397
397
|
}
|
|
398
|
-
function
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
switch (child.type) {
|
|
398
|
+
function renderTextBlocks(blocks) {
|
|
399
|
+
return blocks.map((block) => {
|
|
400
|
+
switch (block.type) {
|
|
402
401
|
case "text":
|
|
403
|
-
return
|
|
402
|
+
return block.text;
|
|
404
403
|
case "image":
|
|
405
404
|
return OMITTED_IMAGE_TEXT;
|
|
406
405
|
default:
|
|
407
406
|
return "";
|
|
408
407
|
}
|
|
409
408
|
}).filter((section) => section !== "").join("\n\n");
|
|
410
|
-
|
|
409
|
+
}
|
|
410
|
+
function renderToolResult(blocks, isError) {
|
|
411
|
+
const body = renderTextBlocks(blocks);
|
|
412
|
+
const tag = isError === true ? "tool-result-error" : "tool-result";
|
|
411
413
|
return frame(tag, body || "(no content)");
|
|
412
414
|
}
|
|
415
|
+
function renderModernToolResult(message) {
|
|
416
|
+
return renderToolResult(message.content, message.isError);
|
|
417
|
+
}
|
|
418
|
+
function renderLegacyToolResult(message) {
|
|
419
|
+
const block = message.content[0];
|
|
420
|
+
return renderToolResult(block?.content ?? [], block?.isError);
|
|
421
|
+
}
|
|
413
422
|
var ENGINE_SLASH_LINE = /^\/[^\s/]+(?:[ \t]+.*)?$/;
|
|
414
423
|
function engineSlashPrompt(messages) {
|
|
415
424
|
const last = messages.at(-1);
|
|
@@ -433,24 +442,18 @@ function serializeHistory(messages) {
|
|
|
433
442
|
break;
|
|
434
443
|
}
|
|
435
444
|
case "user": {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
sections.push(renderToolResult(user));
|
|
445
|
+
if (message.source.kind === "tool") {
|
|
446
|
+
sections.push(renderLegacyToolResult(message));
|
|
439
447
|
} else {
|
|
440
|
-
const body =
|
|
441
|
-
switch (block.type) {
|
|
442
|
-
case "text":
|
|
443
|
-
return block.text;
|
|
444
|
-
case "image":
|
|
445
|
-
return OMITTED_IMAGE_TEXT;
|
|
446
|
-
default:
|
|
447
|
-
return "";
|
|
448
|
-
}
|
|
449
|
-
}).filter((section) => section !== "").join("\n\n");
|
|
448
|
+
const body = renderTextBlocks(message.content);
|
|
450
449
|
sections.push(frame("user", body || "(no content)"));
|
|
451
450
|
}
|
|
452
451
|
break;
|
|
453
452
|
}
|
|
453
|
+
case "tool": {
|
|
454
|
+
sections.push(renderModernToolResult(message));
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
454
457
|
default:
|
|
455
458
|
break;
|
|
456
459
|
}
|
|
@@ -624,6 +627,29 @@ var DriverInbox = class {
|
|
|
624
627
|
}
|
|
625
628
|
};
|
|
626
629
|
|
|
630
|
+
// src/driver-core/system-head.ts
|
|
631
|
+
import { createSystemMessage } from "@deepseek-ai/dsh-llm";
|
|
632
|
+
|
|
633
|
+
// src/compat.ts
|
|
634
|
+
import * as dshSettings from "@deepseek-ai/dsh-settings";
|
|
635
|
+
var LEGACY_HARNESS = "SettingsProvider" in dshSettings;
|
|
636
|
+
|
|
637
|
+
// src/driver-core/system-head.ts
|
|
638
|
+
var SURFACE_TYPES = /* @__PURE__ */ new Set([
|
|
639
|
+
"system/message",
|
|
640
|
+
"user/message",
|
|
641
|
+
"developer/message",
|
|
642
|
+
"assistant/message",
|
|
643
|
+
"tool/result"
|
|
644
|
+
]);
|
|
645
|
+
function appendSystemHeadIfMissing(session, turn, step) {
|
|
646
|
+
if (LEGACY_HARNESS) return;
|
|
647
|
+
for (const event of session.snapshotEvents()) {
|
|
648
|
+
if (SURFACE_TYPES.has(event.type) && event.surfaceOp !== void 0) return;
|
|
649
|
+
}
|
|
650
|
+
session.append("system/message", { turn, step, message: createSystemMessage("") }, { surfaceOp: "append" });
|
|
651
|
+
}
|
|
652
|
+
|
|
627
653
|
// src/provider-route.ts
|
|
628
654
|
import { LlmAdapter, LlmError } from "@deepseek-ai/dsh-llm";
|
|
629
655
|
function hostedRouteLabelOf(_engine) {
|
|
@@ -1508,6 +1534,7 @@ var ClaudeCodeAgent = class {
|
|
|
1508
1534
|
this.session.append("step/start", { turn, step });
|
|
1509
1535
|
phase.step = step;
|
|
1510
1536
|
try {
|
|
1537
|
+
appendSystemHeadIfMissing(this.session, turn, step);
|
|
1511
1538
|
for (const message of decision.messages) {
|
|
1512
1539
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
1513
1540
|
}
|
|
@@ -2037,16 +2064,21 @@ var HostedEngineRuntime = class {
|
|
|
2037
2064
|
return {
|
|
2038
2065
|
agent,
|
|
2039
2066
|
signal: abort.signal,
|
|
2040
|
-
publish: (source) => {
|
|
2067
|
+
publish: async (source) => {
|
|
2041
2068
|
assertLive();
|
|
2042
2069
|
const joining = lifetime.entered;
|
|
2043
2070
|
if (!joining) lifetime.bind(agent.ctx.sessions.enter(session));
|
|
2044
2071
|
detachAgent = loopCtx.agents.enter(agent, parentAgent);
|
|
2045
2072
|
if (!joining) agent.ctx.sessions.announce(session);
|
|
2046
2073
|
assertLive();
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2074
|
+
if (LEGACY_HARNESS) {
|
|
2075
|
+
;
|
|
2076
|
+
loopCtx.agents.announce(agent);
|
|
2077
|
+
assertLive();
|
|
2078
|
+
emitAgentEvent(loopCtx, agent, "agent/session-start", { source });
|
|
2079
|
+
} else {
|
|
2080
|
+
await loopCtx.agents.announce(agent, source, abort.signal);
|
|
2081
|
+
}
|
|
2050
2082
|
assertLive();
|
|
2051
2083
|
return { agent, dispose, retire, lifetime };
|
|
2052
2084
|
},
|
|
@@ -2913,6 +2945,7 @@ var CodexAgent = class {
|
|
|
2913
2945
|
this.session.append("step/end", { turn: phase.turn, step: phase.step });
|
|
2914
2946
|
phase.step += 1;
|
|
2915
2947
|
this.session.append("step/start", { turn: phase.turn, step: phase.step });
|
|
2948
|
+
appendSystemHeadIfMissing(this.session, phase.turn, phase.step);
|
|
2916
2949
|
this.stepSettledTools = 0;
|
|
2917
2950
|
}
|
|
2918
2951
|
/** Lazily created app-server client, reused across steps and released on scope teardown. */
|
|
@@ -3964,6 +3997,7 @@ var PiAgent = class {
|
|
|
3964
3997
|
this.session.append("step/end", { turn: phase.turn, step: phase.step });
|
|
3965
3998
|
phase.step += 1;
|
|
3966
3999
|
this.session.append("step/start", { turn: phase.turn, step: phase.step });
|
|
4000
|
+
appendSystemHeadIfMissing(this.session, phase.turn, phase.step);
|
|
3967
4001
|
this.stepSettledTools = 0;
|
|
3968
4002
|
}
|
|
3969
4003
|
/**
|
|
@@ -5299,6 +5333,7 @@ var KimiAgent = class {
|
|
|
5299
5333
|
this.session.append("step/start", { turn, step });
|
|
5300
5334
|
phase.step = step;
|
|
5301
5335
|
try {
|
|
5336
|
+
appendSystemHeadIfMissing(this.session, turn, step);
|
|
5302
5337
|
for (const message of decision.messages) {
|
|
5303
5338
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
5304
5339
|
}
|
|
@@ -5693,7 +5728,7 @@ var KimiLoop = class extends HostedEngineRuntime {
|
|
|
5693
5728
|
import z5 from "@deepseek-ai/schemastery";
|
|
5694
5729
|
|
|
5695
5730
|
// src/namespace.ts
|
|
5696
|
-
var
|
|
5731
|
+
var LEGACY_LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "agent-loop-engine";
|
|
5697
5732
|
|
|
5698
5733
|
// src/settings.ts
|
|
5699
5734
|
var LOOP_ENGINE_SETTINGS_SCHEMA = z5.object({
|
|
@@ -5701,8 +5736,16 @@ var LOOP_ENGINE_SETTINGS_SCHEMA = z5.object({
|
|
|
5701
5736
|
showInComposer: z5.boolean().default(true)
|
|
5702
5737
|
});
|
|
5703
5738
|
function loopEngineSettingsNamespace() {
|
|
5704
|
-
return
|
|
5739
|
+
return LEGACY_LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL;
|
|
5740
|
+
}
|
|
5741
|
+
function withVolatile(schema) {
|
|
5742
|
+
const volatile = schema.volatile;
|
|
5743
|
+
return volatile === void 0 ? schema : volatile.call(schema);
|
|
5705
5744
|
}
|
|
5745
|
+
var LOOP_ENGINE_ENGINE_SCHEMA = withVolatile(
|
|
5746
|
+
z5.union(LOOP_ENGINE_IDS.map((id) => z5.const(id))).default("in-process")
|
|
5747
|
+
);
|
|
5748
|
+
var LOOP_ENGINE_SHOW_IN_COMPOSER_SCHEMA = withVolatile(z5.boolean().default(true));
|
|
5706
5749
|
|
|
5707
5750
|
// src/patch-manager.ts
|
|
5708
5751
|
var MANAGED_BLOCK_BEGIN = "# -- dsh-loop-engine managed block --";
|
|
@@ -5841,8 +5884,13 @@ async function writeIfDifferent(path, text) {
|
|
|
5841
5884
|
await rename(tmp, path);
|
|
5842
5885
|
return true;
|
|
5843
5886
|
}
|
|
5887
|
+
async function readComposition(source, id) {
|
|
5888
|
+
if (source.readDocument !== void 0) return (await source.readDocument(id)).content;
|
|
5889
|
+
if (source.read !== void 0) return source.read(id);
|
|
5890
|
+
throw new Error("loop-engine: the preset roster exposes neither readDocument() nor read()");
|
|
5891
|
+
}
|
|
5844
5892
|
async function ensureEnginePresets(dshHome, source) {
|
|
5845
|
-
const composition = await source
|
|
5893
|
+
const composition = await readComposition(source, SOURCE_PRESET_ID);
|
|
5846
5894
|
const stripped = `${MANAGED_HEADER}
|
|
5847
5895
|
${stripPresetRows(composition)}`;
|
|
5848
5896
|
let changed = false;
|
|
@@ -5856,7 +5904,7 @@ ${stripPresetRows(composition)}`;
|
|
|
5856
5904
|
}
|
|
5857
5905
|
|
|
5858
5906
|
// src/router-loop.ts
|
|
5859
|
-
import AgentLoop from "@deepseek-ai/dsh-agent-loop";
|
|
5907
|
+
import AgentLoop, { DEFAULT_MAX_PARALLEL_TOOL_CALLS } from "@deepseek-ai/dsh-agent-loop";
|
|
5860
5908
|
|
|
5861
5909
|
// src/model-selection-reset.ts
|
|
5862
5910
|
var AGENT_DEFAULT_MODEL_NS = "agent-default-model";
|
|
@@ -6757,7 +6805,10 @@ var RouterLoop = class extends AgentLoop {
|
|
|
6757
6805
|
* @param warn - diagnostic sink for a skipped engine command.
|
|
6758
6806
|
*/
|
|
6759
6807
|
constructor(ctx, build, records, warn) {
|
|
6760
|
-
super(ctx, { agents: [] }
|
|
6808
|
+
super(ctx, LEGACY_HARNESS ? { agents: [] } : {
|
|
6809
|
+
agents: [],
|
|
6810
|
+
maxParallelToolCalls: { get: () => DEFAULT_MAX_PARALLEL_TOOL_CALLS }
|
|
6811
|
+
});
|
|
6761
6812
|
this.build = build;
|
|
6762
6813
|
this.records = records;
|
|
6763
6814
|
this.warn = warn;
|
|
@@ -7341,7 +7392,13 @@ var Config5 = z6.object({
|
|
|
7341
7392
|
approvalPolicy: z6.union(CODEX_APPROVAL_POLICIES.map((policy) => z6.const(policy))),
|
|
7342
7393
|
piProvider: z6.string(),
|
|
7343
7394
|
piThinking: z6.string(),
|
|
7344
|
-
kimiBin: z6.string()
|
|
7395
|
+
kimiBin: z6.string(),
|
|
7396
|
+
// The live fields exist only on the 0.1.7 line; the 0.1.5 line carries the
|
|
7397
|
+
// selection in a settings section instead. The empty arm never runs when the
|
|
7398
|
+
// coverage job is on 0.1.7; the 0.1.5 dep set is exercised by
|
|
7399
|
+
// vitest.config.compat015.ts, which takes it.
|
|
7400
|
+
/* v8 ignore next -- legacy arm, exercised by vitest.config.compat015.ts */
|
|
7401
|
+
...LEGACY_HARNESS ? {} : { engine: LOOP_ENGINE_ENGINE_SCHEMA, showInComposer: LOOP_ENGINE_SHOW_IN_COMPOSER_SCHEMA }
|
|
7345
7402
|
});
|
|
7346
7403
|
function resolvePatchPath(config) {
|
|
7347
7404
|
if (config.patchPath !== void 0 && config.patchPath !== "") return config.patchPath;
|
|
@@ -7541,9 +7598,7 @@ function apply(ctx, config) {
|
|
|
7541
7598
|
});
|
|
7542
7599
|
};
|
|
7543
7600
|
let savedPresetDefault;
|
|
7544
|
-
let steeredEngine;
|
|
7545
7601
|
const steerPresetDefault = (engine) => {
|
|
7546
|
-
steeredEngine = engine;
|
|
7547
7602
|
if (engine === "in-process") {
|
|
7548
7603
|
const saved = savedPresetDefault;
|
|
7549
7604
|
savedPresetDefault = void 0;
|
|
@@ -7562,7 +7617,6 @@ function apply(ctx, config) {
|
|
|
7562
7617
|
mutatePresetDefault({ op: "set", path: ["default"], value: target });
|
|
7563
7618
|
});
|
|
7564
7619
|
};
|
|
7565
|
-
const seedEngine = legacyEngine ?? "in-process";
|
|
7566
7620
|
const pluginWarn = (message) => {
|
|
7567
7621
|
ctx.logger.warn(message);
|
|
7568
7622
|
};
|
|
@@ -7623,25 +7677,47 @@ function apply(ctx, config) {
|
|
|
7623
7677
|
CLEAR_ROUTER_RETRY();
|
|
7624
7678
|
releaseRoutes();
|
|
7625
7679
|
}, "loop-engine: cleanup");
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7680
|
+
if (LEGACY_HARNESS) {
|
|
7681
|
+
let source;
|
|
7682
|
+
let steered;
|
|
7683
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
7684
|
+
const settings = settingsCtx.settings;
|
|
7685
|
+
settings.installSection(
|
|
7686
|
+
ctx,
|
|
7687
|
+
loopEngineSettingsNamespace(),
|
|
7688
|
+
LOOP_ENGINE_SETTINGS_SCHEMA,
|
|
7689
|
+
// The managed block's engine survives the upgrade as the section's seed;
|
|
7690
|
+
// a deployment with no block keeps the in-process default.
|
|
7691
|
+
{ engine: legacyEngine ?? "in-process", showInComposer: true },
|
|
7692
|
+
{
|
|
7693
|
+
setSource: (current) => {
|
|
7694
|
+
source = current;
|
|
7695
|
+
},
|
|
7696
|
+
onChange: () => {
|
|
7697
|
+
const next = source().engine;
|
|
7698
|
+
if (next === steered) return;
|
|
7699
|
+
steered = next;
|
|
7700
|
+
steerPresetDefault(next);
|
|
7701
|
+
}
|
|
7641
7702
|
}
|
|
7642
|
-
|
|
7703
|
+
);
|
|
7704
|
+
});
|
|
7705
|
+
return;
|
|
7706
|
+
}
|
|
7707
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
7708
|
+
settingsCtx.effect(
|
|
7709
|
+
() => settingsCtx.settings.configure({ auto: false }, ctx.fiber),
|
|
7710
|
+
"loop-engine: settings page policy"
|
|
7643
7711
|
);
|
|
7644
7712
|
});
|
|
7713
|
+
let observedEngine = config.engine.get();
|
|
7714
|
+
ctx.on("settings/document-updated", () => {
|
|
7715
|
+
const next = config.engine.get();
|
|
7716
|
+
if (next === observedEngine) return;
|
|
7717
|
+
observedEngine = next;
|
|
7718
|
+
steerPresetDefault(next);
|
|
7719
|
+
});
|
|
7720
|
+
steerPresetDefault(legacyEngine ?? observedEngine);
|
|
7645
7721
|
}
|
|
7646
7722
|
export {
|
|
7647
7723
|
Config5 as Config,
|
package/lib/invariant.js
CHANGED
|
@@ -12,6 +12,14 @@ var LOOP_ENGINE_SETTINGS_SCHEMA = z.object({
|
|
|
12
12
|
engine: z.union(LOOP_ENGINE_IDS.map((id) => z.const(id))).default("in-process"),
|
|
13
13
|
showInComposer: z.boolean().default(true)
|
|
14
14
|
});
|
|
15
|
+
function withVolatile(schema) {
|
|
16
|
+
const volatile = schema.volatile;
|
|
17
|
+
return volatile === void 0 ? schema : volatile.call(schema);
|
|
18
|
+
}
|
|
19
|
+
var LOOP_ENGINE_ENGINE_SCHEMA = withVolatile(
|
|
20
|
+
z.union(LOOP_ENGINE_IDS.map((id) => z.const(id))).default("in-process")
|
|
21
|
+
);
|
|
22
|
+
var LOOP_ENGINE_SHOW_IN_COMPOSER_SCHEMA = withVolatile(z.boolean().default(true));
|
|
15
23
|
|
|
16
24
|
// src/patch-manager.ts
|
|
17
25
|
var MANAGED_BLOCK_BEGIN = "# -- dsh-loop-engine managed block --";
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Loop engine settings plugin, browser half. Registers the "Loop engine"
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Loop engine settings plugin, browser half. Registers the "Loop engine" page
|
|
3
|
+
* under the settings section slot and binds one store to whichever settings
|
|
4
|
+
* client the running harness serves: the 0.1.7 `configForms` form for the
|
|
5
|
+
* plugin's own `loop-engine` profile entry, or the 0.1.5 `settingsScope` for its
|
|
6
|
+
* `agent-loop-engine` settings section. Two inject callbacks register the page,
|
|
7
|
+
* one per generation — whichever service exists fires, and the client bundle
|
|
8
|
+
* never imports a host package, so it cannot probe the generation like the node
|
|
9
|
+
* half does.
|
|
5
10
|
* Export discipline: packages/client/AGENTS.md.
|
|
6
11
|
* @module dsh-loop-engine/client
|
|
7
12
|
*/
|
|
@@ -17,12 +22,17 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
17
22
|
'settings.loop-engine': LoopEngineKey;
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
25
|
+
/**
|
|
26
|
+
* Required services (cordis fiber inject). Only the services BOTH generations
|
|
27
|
+
* provide are listed statically: `configForms` (0.1.7) and `settingsScope`
|
|
28
|
+
* (0.1.5) are mutually exclusive, so each is injected inside its own callback
|
|
29
|
+
* rather than named here. The target slot is declared by ui-settings' apply;
|
|
30
|
+
* registration depends on it through `slots.inject()`.
|
|
31
|
+
*/
|
|
22
32
|
export declare const inject: string[];
|
|
23
33
|
/**
|
|
24
34
|
* Register the Loop engine section once the `settings.section` declaration is
|
|
25
|
-
* on the ledger and bind its store to
|
|
35
|
+
* on the ledger and bind its store to whichever settings client is present.
|
|
26
36
|
* @param ctx - client root context.
|
|
27
37
|
*/
|
|
28
38
|
export declare function apply(ctx: ClientContext): void;
|
|
@@ -82,8 +82,9 @@ export type { SessionEngineReport } from '../agent-preset-ids.ts';
|
|
|
82
82
|
/**
|
|
83
83
|
* The standard seat members this half reads off a session-scope slot.
|
|
84
84
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
85
|
+
* The identity, plus the one live fact the turn-status row needs: whether the
|
|
86
|
+
* session is mid-turn. The engine itself is no longer read from a framework
|
|
87
|
+
* hook, it is asked of the host through this plugin's own Remote.
|
|
87
88
|
*/
|
|
88
89
|
export interface SessionSeat {
|
|
89
90
|
/**
|
|
@@ -92,6 +93,23 @@ export interface SessionSeat {
|
|
|
92
93
|
* (which only ever renders with a session) shows nothing.
|
|
93
94
|
*/
|
|
94
95
|
sessionId?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Selector hook over the current Session's snapshot (`SessionStandardProps` of
|
|
98
|
+
* every session-scoped seat). Optional for the same reason as the identity: a
|
|
99
|
+
* caller with no such seat — or a test rendering one directly — has none, and
|
|
100
|
+
* the turn-status gate is then left alone rather than guessed.
|
|
101
|
+
*/
|
|
102
|
+
useSession?: SnapshotSelector;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The selector-hook shape this half needs from `useSession`: a read of one field
|
|
106
|
+
* of the Session snapshot, with no equality function of its own.
|
|
107
|
+
*/
|
|
108
|
+
export type SnapshotSelector = <S>(select: (snapshot: SessionRunningState) => S) => S;
|
|
109
|
+
/** The one field of the Session snapshot the turn-status row reads. */
|
|
110
|
+
export interface SessionRunningState {
|
|
111
|
+
/** Whether a turn is in flight for this session. */
|
|
112
|
+
readonly running: boolean;
|
|
95
113
|
}
|
|
96
114
|
/** Cordis service key AND wire namespace of the plugin's own Remote. */
|
|
97
115
|
export declare const LOOP_ENGINE_REMOTE_NAMESPACE = "loopEngine";
|
|
@@ -239,14 +257,30 @@ export declare function engineSwitchReady(report: SessionEngineReport | undefine
|
|
|
239
257
|
* this page.
|
|
240
258
|
*/
|
|
241
259
|
export declare function switchNeedsReload(current: SessionEngine, target: LoopEngineId): boolean;
|
|
242
|
-
/**
|
|
260
|
+
/** One boundary schema of a wire field (a zod schema, as the Gateway holds it). */
|
|
261
|
+
interface RemoteSchema {
|
|
262
|
+
parse(value: unknown): unknown;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* A codec of one wire field (harness: `TypertCodec`, strict branch).
|
|
266
|
+
*
|
|
267
|
+
* The two harness generations read DIFFERENT members of this object and each
|
|
268
|
+
* validates only its own: the 0.1.5 line's registry and client parse through
|
|
269
|
+
* `schema`, while the 0.1.7 line's registry requires a `create()` factory and
|
|
270
|
+
* parses through `codec.create()` (`typert: … strict codec has no create()
|
|
271
|
+
* factory`). One build serves both, so the codec carries both fields.
|
|
272
|
+
*/
|
|
243
273
|
interface RemoteCodec {
|
|
244
274
|
readonly mode: 'strict';
|
|
245
275
|
/** Stable identity of the field's declared type, for diagnostics. */
|
|
246
276
|
readonly typeSymbol: string;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
277
|
+
/** The 0.1.5 line's read: the boundary schema itself. */
|
|
278
|
+
readonly schema: RemoteSchema;
|
|
279
|
+
/**
|
|
280
|
+
* The 0.1.7 line's read: a factory returning the boundary schema.
|
|
281
|
+
* @returns the same boundary schema.
|
|
282
|
+
*/
|
|
283
|
+
readonly create: () => RemoteSchema;
|
|
250
284
|
}
|
|
251
285
|
/** One ordered business parameter (harness: `InvocationParameterDescriptor`). */
|
|
252
286
|
interface RemoteParameter {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Loop engine selection store:
|
|
3
|
-
*
|
|
2
|
+
* Loop engine selection store: a settings transport is the channel, and the
|
|
3
|
+
* store publishes a render-safe snapshot plus the write path. The transport is
|
|
4
|
+
* abstracted so one store follows either generation's client service — the
|
|
5
|
+
* 0.1.7 `ConfigForm` for the plugin's own profile entry, or the 0.1.5
|
|
6
|
+
* `SettingsScope` for its `agent-loop-engine` settings section.
|
|
4
7
|
* @module dsh-loop-engine/client/store
|
|
5
8
|
*/
|
|
6
|
-
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
7
9
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
8
10
|
import type { LoopEngineId } from '../agent-preset-ids.ts';
|
|
11
|
+
import type { LoopEngineSettings } from '../settings.ts';
|
|
9
12
|
/** State rendered by the loop engine section. */
|
|
10
13
|
export interface LoopEngineState {
|
|
11
14
|
status: 'loading' | 'ready' | 'unavailable' | 'saving';
|
|
@@ -14,44 +17,76 @@ export interface LoopEngineState {
|
|
|
14
17
|
writable: boolean;
|
|
15
18
|
error: string | null;
|
|
16
19
|
}
|
|
17
|
-
/**
|
|
20
|
+
/** The render-relevant slice of a settings snapshot, shared by both generations' transports. */
|
|
21
|
+
export interface LoopEngineSettingsSnapshot {
|
|
22
|
+
status: 'loading' | 'ready' | 'unavailable';
|
|
23
|
+
value: LoopEngineSettings | undefined;
|
|
24
|
+
writable: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The settings channel the store follows. Satisfied by the 0.1.7 `ConfigForm`
|
|
28
|
+
* and by the 0.1.5 `SettingsScope` (through a thin adapter in `./index.ts`); the
|
|
29
|
+
* write's boolean is optional because the 0.1.5 scope reports acceptance only
|
|
30
|
+
* through the snapshot it leaves behind.
|
|
31
|
+
*/
|
|
32
|
+
export interface LoopEngineSettingsTransport {
|
|
33
|
+
/** @returns the current sync snapshot. */
|
|
34
|
+
getSnapshot(): LoopEngineSettingsSnapshot;
|
|
35
|
+
/**
|
|
36
|
+
* Observe snapshot replacements.
|
|
37
|
+
* @param listener - invoked after each snapshot change.
|
|
38
|
+
* @returns the disposer removing the listener.
|
|
39
|
+
*/
|
|
40
|
+
subscribe(listener: () => void): () => void;
|
|
41
|
+
/**
|
|
42
|
+
* Persist one field.
|
|
43
|
+
* @param field - the field to write.
|
|
44
|
+
* @param value - the value to write.
|
|
45
|
+
* @returns whether the write was accepted, when the transport reports it.
|
|
46
|
+
*/
|
|
47
|
+
set(field: 'engine' | 'showInComposer', value: unknown): Promise<boolean | void>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Narrow a wire section to the stored engine id and display toggle; an invalid
|
|
51
|
+
* one reads default. Used by the 0.1.5 `settingsScope.bind` decoder, whose scope
|
|
52
|
+
* receives raw wire sections.
|
|
53
|
+
* @param section - the wire section value.
|
|
54
|
+
* @returns the decoded selection, or `undefined` when the section is unusable.
|
|
55
|
+
*/
|
|
18
56
|
export declare function decodeLoopEngine(section: unknown): {
|
|
19
57
|
engine: LoopEngineId;
|
|
20
58
|
showInComposer: boolean;
|
|
21
59
|
} | undefined;
|
|
22
60
|
/** Coordinates the settings-backed loop engine selection. */
|
|
23
61
|
export declare class LoopEngineStore {
|
|
24
|
-
private readonly
|
|
62
|
+
private readonly transport;
|
|
25
63
|
/** uSES-safe state source shared by the registered settings section. */
|
|
26
64
|
readonly store: SnapshotStore<LoopEngineState>;
|
|
27
65
|
private following;
|
|
28
66
|
private saving;
|
|
29
67
|
/**
|
|
30
|
-
* @param
|
|
68
|
+
* @param transport - the settings channel the store follows.
|
|
31
69
|
*/
|
|
32
|
-
constructor(
|
|
33
|
-
|
|
34
|
-
showInComposer: boolean;
|
|
35
|
-
}>);
|
|
36
|
-
/** Begin following the bound scope and publish its current answer. */
|
|
70
|
+
constructor(transport: LoopEngineSettingsTransport);
|
|
71
|
+
/** Begin following the transport and publish its current answer. */
|
|
37
72
|
load(): void;
|
|
38
73
|
/**
|
|
39
|
-
* Persist the selected engine. Success is
|
|
40
|
-
*
|
|
74
|
+
* Persist the selected engine. Success is the transport's accepted answer
|
|
75
|
+
* (when it reports one) checked against the snapshot the write left behind,
|
|
76
|
+
* so a refused write reports error after its recovery.
|
|
41
77
|
* @param engine - the engine to select for future Agent turns.
|
|
42
78
|
* @returns whether the write landed.
|
|
43
79
|
*/
|
|
44
80
|
setEngine(engine: LoopEngineId): Promise<boolean>;
|
|
45
81
|
/**
|
|
46
|
-
* Persist whether the composer shows the engine picker.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* the page — the toggle only changes composer visibility.
|
|
82
|
+
* Persist whether the composer shows the engine picker. Unlike
|
|
83
|
+
* {@link setEngine}, landing does not reload the page — the toggle only
|
|
84
|
+
* changes composer visibility.
|
|
50
85
|
* @param show - whether the chat page composer reveals the engine picker.
|
|
51
86
|
* @returns whether the write landed.
|
|
52
87
|
*/
|
|
53
88
|
setShowInComposer(show: boolean): Promise<boolean>;
|
|
54
|
-
/** Stop following the
|
|
89
|
+
/** Stop following the transport. */
|
|
55
90
|
dispose(): void;
|
|
56
91
|
private derive;
|
|
57
92
|
}
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
* default, a session running pi included.
|
|
21
21
|
*
|
|
22
22
|
* The reflection's SUBJECT is still a document-level attribute rather than the
|
|
23
|
-
* row's own element: the sheet has to reach a class the harness hashes
|
|
24
|
-
*
|
|
23
|
+
* row's own element: the sheet has to reach a class the harness hashes (see
|
|
24
|
+
* {@link ROW_SELECTORS}), which takes an attribute selector on an ancestor,
|
|
25
25
|
* and the row offers no session-scoped hook for a plugin to hang it on. A
|
|
26
26
|
* document-level attribute has exactly ONE owner at a time, though — and on this
|
|
27
27
|
* page the WRITERS outnumber the reader: the chip and the composer of every
|
|
@@ -47,10 +47,24 @@
|
|
|
47
47
|
* paints by it, and a leftover attribute is inert on a page whose turn-status row
|
|
48
48
|
* is not rendered.
|
|
49
49
|
*
|
|
50
|
+
* The paint is also gated on the session being MID-TURN, through a second
|
|
51
|
+
* document-level attribute ({@link RUNNING_ATTR}, written from the same
|
|
52
|
+
* reflection). On the 0.1.5 line the row only exists while a turn is in flight,
|
|
53
|
+
* so the engine attribute alone was enough; on the 0.1.7 line that same button
|
|
54
|
+
* stays on screen after the turn ends, as the collapsed summary that reads
|
|
55
|
+
* "用时 4秒" — an engine-only gate leaves the glyph and the sweep animating on a
|
|
56
|
+
* finished turn, which is not what the live line means. The gate is a surface's
|
|
57
|
+
* own answer (`session.running`, which every session-scoped seat receives), so a
|
|
58
|
+
* surface that cannot answer passes nothing and leaves it alone rather than
|
|
59
|
+
* guessing.
|
|
60
|
+
*
|
|
50
61
|
* Three facts about the harness markup make that safe and specific:
|
|
51
|
-
* - the row
|
|
52
|
-
*
|
|
53
|
-
* `
|
|
62
|
+
* - the row has one stable handle per harness generation — an
|
|
63
|
+
* `[hash]_turnStatus` class suffix on the 0.1.5 line, and, on the 0.1.7
|
|
64
|
+
* line, the `data-turn-process` button whose text is a `<span>` whose hashed
|
|
65
|
+
* class ends `_label` — so each generation gets its own copy of the sheet
|
|
66
|
+
* ({@link ROW_SELECTORS}) and the one the running app renders matches while
|
|
67
|
+
* the other stays inert;
|
|
54
68
|
* - its gradient paints through the `--dsw-static-deepseek-*` custom
|
|
55
69
|
* properties, so recoloring is a variable override rather than a fight
|
|
56
70
|
* over `background` and `background-clip`;
|
|
@@ -93,6 +107,11 @@ import type { LoopEngineId } from '../agent-preset-ids.ts';
|
|
|
93
107
|
* clears the attribute, because the sibling surface of the same session is still
|
|
94
108
|
* on screen painting through it.
|
|
95
109
|
*
|
|
110
|
+
* The MID-TURN gate is written from here as well, but only by a surface that can
|
|
111
|
+
* answer it (`running` given): the sheet is gated on both, because the 0.1.7 row
|
|
112
|
+
* outlives its turn, and a surface with no answer must leave the gate as the
|
|
113
|
+
* surface that has one left it rather than guess.
|
|
114
|
+
*
|
|
96
115
|
* The NO-OP covers the late reflection as well — the one from the session the
|
|
97
116
|
* user has just left, whose own answer landing after the switch used to paint
|
|
98
117
|
* over the session that replaced it.
|
|
@@ -101,8 +120,10 @@ import type { LoopEngineId } from '../agent-preset-ids.ts';
|
|
|
101
120
|
* row): such a page has nothing to say about the row, so nothing is written and
|
|
102
121
|
* nothing is withdrawn — it leaves the row it found alone.
|
|
103
122
|
* @param engine - the engine that session runs, when it is known.
|
|
123
|
+
* @param running - whether that session is mid-turn, when the surface knows;
|
|
124
|
+
* omitted by a surface with no answer, which leaves the gate alone.
|
|
104
125
|
*/
|
|
105
|
-
export declare function reflectTurnStatusEngine(sessionId: string | undefined, engine: LoopEngineId | undefined): void;
|
|
126
|
+
export declare function reflectTurnStatusEngine(sessionId: string | undefined, engine: LoopEngineId | undefined, running?: boolean): void;
|
|
106
127
|
/**
|
|
107
128
|
* Declare which session's surfaces are on screen — the row's one subject.
|
|
108
129
|
*
|
|
@@ -55,12 +55,20 @@ import type { SessionEngineCache } from './session-engine.ts';
|
|
|
55
55
|
*
|
|
56
56
|
* One effect does the whole thing, so the three steps cannot come apart: declare
|
|
57
57
|
* this session as the row's subject (`focusTurnStatusSession`), reflect its
|
|
58
|
-
* engine by that declaration (`reflectTurnStatusEngine`),
|
|
59
|
-
* declaration when this surface goes away
|
|
60
|
-
* withdraws it only while it is still this
|
|
58
|
+
* engine and its mid-turn gate by that declaration (`reflectTurnStatusEngine`),
|
|
59
|
+
* and release the declaration when this surface goes away
|
|
60
|
+
* (`blurTurnStatusSession`, which withdraws it only while it is still this
|
|
61
|
+
* session's).
|
|
62
|
+
*
|
|
63
|
+
* `running` is the session's own live state (`session.running`, from the
|
|
64
|
+
* session-scoped standard kit) and it is part of the effect's key: the 0.1.7 row
|
|
65
|
+
* stays on screen after the turn ends, so the paint has to come off when the turn
|
|
66
|
+
* does — a surface that does not know the state passes nothing and leaves the
|
|
67
|
+
* gate alone.
|
|
61
68
|
* @param cache - the cache the plugin mounted.
|
|
62
69
|
* @param sessionId - the session to follow, or undefined off a session scope.
|
|
70
|
+
* @param running - whether that session is mid-turn, when the surface knows.
|
|
63
71
|
* @returns the engine report, or undefined while it is unknown.
|
|
64
72
|
*/
|
|
65
|
-
export declare function useEngineOfSession(cache: SessionEngineCache, sessionId: string | undefined): SessionEngineReport | undefined;
|
|
73
|
+
export declare function useEngineOfSession(cache: SessionEngineCache, sessionId: string | undefined, running?: boolean): SessionEngineReport | undefined;
|
|
66
74
|
//# sourceMappingURL=use-session-engine.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime harness-generation probe. One released build must serve the 0.1.5
|
|
3
|
+
* line (`0.1.5-rc.1` … `0.1.5-rc.3`, all sharing the OLD settings API) and the
|
|
4
|
+
* 0.1.7 line (`0.1.7-rc.1`), which rewrote the settings host and client and
|
|
5
|
+
* turned the agent-creation announcement async. The differences are not
|
|
6
|
+
* version strings but API SHAPES, so the switch is a structural probe of the
|
|
7
|
+
* imported module rather than a semver parse: `@deepseek-ai/dsh-settings`
|
|
8
|
+
* exported a named `SettingsProvider` class on the 0.1.5 line, and the 0.1.7
|
|
9
|
+
* rewrite deleted it in favour of `SettingsForms` plus per-entry volatile
|
|
10
|
+
* Config fields.
|
|
11
|
+
*
|
|
12
|
+
* The probe resolves to whichever `@deepseek-ai/dsh-settings` the running
|
|
13
|
+
* profile actually provides. It is host-side only: the browser bundle must not
|
|
14
|
+
* import host packages, so `src/client/*` never imports this module.
|
|
15
|
+
*
|
|
16
|
+
* @module dsh-loop-engine/compat
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Whether the running harness predates the profile-backed settings rewrite
|
|
20
|
+
* (the 0.1.5 line). `true` selects the legacy settings host/client, the
|
|
21
|
+
* synchronous agent-creation announcement, and the legacy tool-result message
|
|
22
|
+
* introspection.
|
|
23
|
+
*/
|
|
24
|
+
export declare const LEGACY_HARNESS: boolean;
|
|
25
|
+
//# sourceMappingURL=compat.d.ts.map
|