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.
@@ -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
- * (`[class$="_turnStatus"]`), which takes an attribute selector on an ancestor,
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 carries exactly one class whose `[hash]_turnStatus` suffix is
52
- * stable across rebuilds (the hash in front is not), so
53
- * `[class$="_turnStatus"]` matches it and not the sibling clock;
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`), and release the
59
- * declaration when this surface goes away (`blurTurnStatusSession`, which
60
- * withdraws it only while it is still this session's).
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
@@ -30,8 +30,12 @@ export interface SkillsService {
30
30
  export interface AgentPresetsService {
31
31
  /** The effective default preset id. */
32
32
  readonly defaultId: string;
33
- /** Read one preset's composition text. */
34
- read(id: string): Promise<string>;
33
+ /** 0.1.5 line: read one preset's composition text directly. */
34
+ read?(id: string): Promise<string>;
35
+ /** 0.1.7 line: read one preset's document; its `content` is the composition text. */
36
+ readDocument?(id: string): Promise<{
37
+ readonly content: string;
38
+ }>;
35
39
  }
36
40
  /** The host settings service's mutation seam, as this plugin uses it. */
37
41
  export interface SettingsMutator {
@@ -91,9 +91,9 @@ export interface SwapAgentOptions {
91
91
  *
92
92
  * Creation and resume follow the registry factory contract and the shared
93
93
  * publication transaction: prepare, run setup, then publish through both
94
- * registries, announce, and emit `agent/session-start`. {@link swap} follows the
95
- * same transaction onto a session another agent entered, which is the only
96
- * difference between taking a session over and owning it from birth.
94
+ * registries and await the serial `agent/created` announcement. {@link swap}
95
+ * follows the same transaction onto a session another agent entered, which is
96
+ * the only difference between taking a session over and owning it from birth.
97
97
  */
98
98
  export declare abstract class HostedEngineRuntime<TConfig, TAgent extends HostedAgent> implements AgentFactory {
99
99
  /** Validated configuration owned by this engine instance. */
@@ -32,6 +32,11 @@
32
32
  * and never a guess. The warning names the provider and the model, never a
33
33
  * credential.
34
34
  *
35
+ * What counts as "recognized" is deliberately narrow: a non-empty `baseURL` and
36
+ * a resolvable credential. The wire protocol is handed over when the deployment
37
+ * declares one, but its absence does NOT refuse the handover — see
38
+ * {@link resolveModelHandover} and {@link SHIPPED_ROUTE_APIS}.
39
+ *
35
40
  * @module dsh-loop-engine/driver-core/model-handover
36
41
  */
37
42
  import type { Context } from '@deepseek-ai/cordis';
@@ -54,8 +59,13 @@ export interface DshModelHandover {
54
59
  readonly model: string;
55
60
  /** Endpoint base as dsh configured it. */
56
61
  readonly baseURL: string;
57
- /** dsh's wire protocol name for this endpoint. */
58
- readonly api: string;
62
+ /**
63
+ * dsh's wire protocol name for this endpoint, when the deployment declares
64
+ * one (or {@link SHIPPED_ROUTE_APIS} knows it for a shipped route). `undefined`
65
+ * is a normal answer, not a failure: engines that need a protocol degrade the
66
+ * way each one documents, and engines that do not (`claude-code`) ignore it.
67
+ */
68
+ readonly api: string | undefined;
59
69
  /** The resolved credential value; never logged, never evented. */
60
70
  readonly apiKey: string;
61
71
  }
@@ -102,8 +112,16 @@ export interface CredentialsService {
102
112
  * `undefined` is the answer in exactly two situations: the selection names no
103
113
  * real dsh model ({@link SessionModelOverride} is already `undefined` for the
104
114
  * hosted seat), or a real model whose endpoint/credential dsh does not disclose
105
- * to this read. The second case warns once — it is a deployment-shape gap the
106
- * operator can see and fix, not a silent downgrade.
115
+ * to this read — no `baseURL`, or no credential. Both warn once: they are
116
+ * deployment-shape gaps the operator can see and fix, not a silent downgrade.
117
+ *
118
+ * The wire protocol is NOT such a gate. A profile that names no `api` still
119
+ * hands over its endpoint (with `api: undefined`), because refusing would let
120
+ * the engine send dsh's model id to the engine's OWN endpoint — a failure that
121
+ * reads like an auth or model problem while the real one is "wrong endpoint".
122
+ * Shipped routes whose adapter owns the wire are named in
123
+ * {@link SHIPPED_ROUTE_APIS} so they hand over a real protocol; anything still
124
+ * unknown degrades per engine, which is the engine's call to make.
107
125
  *
108
126
  * Called fresh on every step by each driver, so a model or provider picked
109
127
  * mid-conversation reaches the engine's next request rather than being frozen
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Give a session started on a hosted engine the protected system head the
3
+ * harness's own loop would have logged.
4
+ *
5
+ * The harness loop logs the rendered system prompt as a `system/message` on a
6
+ * session's first step (`packages/core/agent-loop/src/agent.ts`:
7
+ * `systemPrompt.project` → `session.append('system/message', …)`). A hosted
8
+ * engine's external CLI owns its own system prompt, so these drivers log no dsh
9
+ * system message at all: a session that STARTS on a hosted engine has no surface
10
+ * head, and if it is later served by the in-process loop the harness logs its
11
+ * system message MID-log. Session format v4 requires the system message — when
12
+ * one exists — to be the FIRST surface event, so such a mixed session is refused
13
+ * on the v3→v4 migration (`packages/session/session-format-v3-to-v4/src/
14
+ * relationships.ts` `foldSurface`: "system/message requires a protected first
15
+ * surface head").
16
+ *
17
+ * Appending an EMPTY system message as the session's first surface event closes
18
+ * that hole: the head is protected from the start (a later in-process system
19
+ * message REPLACES it rather than arriving misplaced), and empty content is
20
+ * dropped from the derived history (`deriveEventMessage` yields `null`), so no
21
+ * system prompt is injected into what the engine is handed — the drivers build
22
+ * their prompt from `serializeHistory`, which ignores the `system` role too.
23
+ *
24
+ * Conservative and idempotent: it appends ONLY while the session has no surface
25
+ * event of its own, so a session the harness loop created (or one already
26
+ * carrying history) is left exactly as it is. A mid-surface system head would be
27
+ * refused by the SAME v4 rule and is never produced here.
28
+ *
29
+ * @module dsh-loop-engine/driver-core/system-head
30
+ */
31
+ import type { Session } from '@deepseek-ai/dsh-session';
32
+ /**
33
+ * Append an empty `system/message` head when the session has no surface event
34
+ * yet, so a hosted-engine session carries the protected head format v4 expects.
35
+ *
36
+ * A no-op once any surface event exists: the head can only ever be the FIRST
37
+ * surface event, and a later one would be refused on migration.
38
+ * @param session - the session a hosted driver is about to run a step on.
39
+ * @param turn - the open turn's number.
40
+ * @param step - the open step's number.
41
+ */
42
+ export declare function appendSystemHeadIfMissing(session: Session, turn: number, step: number): void;
43
+ //# sourceMappingURL=system-head.d.ts.map
@@ -3,17 +3,28 @@
3
3
  * overrides and environment that point it at dsh's model.
4
4
  *
5
5
  * Codex resolves a custom endpoint through `model_providers.<id>` in its own
6
- * `config.toml` (`base_url`, `wire_api`, and an `env_key` naming the variable
7
- * the credential is read from), and `codex app-server` accepts those as `-c
8
- * key=value` overrides — so the handover needs no edit of `~/.codex`. The
6
+ * `config.toml` (`name`, `base_url`, `wire_api`, and an `env_key` naming the
7
+ * variable the credential is read from), and `codex app-server` accepts those as
8
+ * `-c key=value` overrides — so the handover needs no edit of `~/.codex`. The
9
9
  * credential travels in the child's environment under {@link CODEX_DSH_API_KEY_ENV},
10
10
  * which the `env_key` names.
11
11
  *
12
- * Codex speaks only OpenAI's wires (`responses` or `chat`). A dsh protocol with
13
- * no codex equivalent (Anthropic Messages) is NOT guessed: `wire_api` is
14
- * omitted, so codex uses its own default wire against dsh's base URL and fails
15
- * loud on the request — the honest outcome, since the plugin cannot make codex
16
- * speak a protocol it does not implement.
12
+ * `name` is not optional in practice even though the CLI's own docs describe it
13
+ * as display metadata: codex 0.149.1 (the version this plugin pins) refuses to
14
+ * load a provider whose `name` is empty — "provider name must not be empty in
15
+ * `model_providers`" — and that config error kills `app-server` on startup, which
16
+ * the driver can only report as "process exited unexpectedly". Omitting it looked
17
+ * correct and failed before any request was ever sent, so every override this
18
+ * module builds carries it (verified against the pinned binary).
19
+ *
20
+ * Codex speaks only OpenAI's `responses` wire as of the version this plugin pins
21
+ * (0.149.1 dropped `chat`). A dsh protocol with no codex equivalent (Anthropic
22
+ * Messages, and now OpenAI Chat Completions) is NOT guessed: `wire_api` is
23
+ * omitted, so codex falls back to its own default wire and fails loud on the
24
+ * request — the honest outcome, since the plugin cannot make codex speak a
25
+ * protocol it does not implement. That default is `responses` as of 0.149.1: the
26
+ * request lands on `<baseURL>/responses`, which an Anthropic Messages or Chat
27
+ * Completions endpoint does not serve.
17
28
  *
18
29
  * @module dsh-loop-engine/engine-codex/model-handover
19
30
  */
@@ -36,7 +47,8 @@ export interface CodexModelConfig {
36
47
  * The provider id ({@link CODEX_DSH_PROVIDER}) is a plugin-owned name: it exists
37
48
  * only in this child's command line, so it cannot collide with a provider the
38
49
  * user configured, and the model itself is still handed over as the thread's own
39
- * `model` param.
50
+ * `model` param. The provider's `name` repeats that id because codex rejects an
51
+ * empty one at config load, before any request is made.
40
52
  * @param handover - the resolved dsh endpoint for the session's selection.
41
53
  * @returns the argv entries and environment to spawn the app-server with.
42
54
  */
@@ -17,6 +17,10 @@
17
17
  * user's own `~/.pi` skills/auth/theme. That is the trade the deployment makes
18
18
  * by selecting a real dsh model on pi.
19
19
  *
20
+ * pi's provider schema requires a wire protocol (`api`), so this is the one
21
+ * engine that cannot express a handover without one; the driver drops such a
22
+ * handover before it reaches here instead of writing a file pi rejects.
23
+ *
20
24
  * @module dsh-loop-engine/engine-pi/model-handover
21
25
  */
22
26
  import type { DshModelHandover } from '../driver-core/model-handover.ts';
@@ -29,7 +29,7 @@
29
29
  * with no record) — it is no longer the thing that decides a running session's
30
30
  * engine.
31
31
  *
32
- * The `agent-loop-engine` settings section carries the DEFAULT engine for new
32
+ * The plugin's own live `engine` Config field carries the DEFAULT engine for new
33
33
  * sessions — which preset the roster's default points at — not a process-wide
34
34
  * switch: existing sessions keep the engine they run, and nothing is torn down or
35
35
  * reloaded by it. (A PER-SESSION switch is a different thing: it is made from the
@@ -38,10 +38,11 @@
38
38
  *
39
39
  * @module dsh-loop-engine
40
40
  */
41
- import type { Context } from '@deepseek-ai/cordis';
41
+ import type { Context, Volatile } from '@deepseek-ai/cordis';
42
42
  import z from '@deepseek-ai/schemastery';
43
43
  import { type Config as ClaudeCodeConfig } from './engine-claude/loop.ts';
44
44
  import type { CodexApprovalPolicy, CodexSandboxMode } from './engine-codex/types.ts';
45
+ import { type LoopEngineId } from './settings.ts';
45
46
  export declare const name = "loop-engine";
46
47
  /**
47
48
  * Services the plugin's own fiber requires. The plugin declares none of its
@@ -69,7 +70,20 @@ export interface Config extends ClaudeCodeConfig {
69
70
  piThinking?: string;
70
71
  /** Kimi CLI executable; `'kimi'` resolves through PATH when not pinned to an absolute path. */
71
72
  kimiBin?: string;
73
+ /**
74
+ * Default engine NEW sessions are created on. A live, profile-backed field:
75
+ * the settings shell edits it and the running plugin reads it through this
76
+ * volatile reference (`config.engine.get()`).
77
+ */
78
+ engine: Volatile<LoopEngineId>;
79
+ /** Whether the chat composer shows the engine picker; a live, profile-backed field. */
80
+ showInComposer: Volatile<boolean>;
72
81
  }
82
+ /** Rejected input of the composition entry: live fields arrive as plain values and resolve to references. */
83
+ type ConfigInput = Omit<Config, 'engine' | 'showInComposer'> & {
84
+ engine?: LoopEngineId;
85
+ showInComposer?: boolean;
86
+ };
73
87
  /**
74
88
  * Schema of the loop engine composition entry.
75
89
  *
@@ -80,8 +94,15 @@ export interface Config extends ClaudeCodeConfig {
80
94
  * omitted deployment tunables fall back to the session). The composition entry
81
95
  * is an engine-agnostic superset: every hosted engine's knobs live here at
82
96
  * once, because any session may select any engine.
97
+ *
98
+ * On the 0.1.7 line `engine` and `showInComposer` are the entry's live fields:
99
+ * `.volatile()` makes them profile-backed settings the settings shell projects
100
+ * as form fields, and the runtime reads each one through its reference
101
+ * (`config.engine.get()`). On the 0.1.5 line there are no live Config fields —
102
+ * the selection is a settings section (`LOOP_ENGINE_SETTINGS_SCHEMA`), whose
103
+ * provider owns the storage — so the entry is the engine knobs alone.
83
104
  */
84
- export declare const Config: z<Config>;
105
+ export declare const Config: z<ConfigInput, Config>;
85
106
  /** Resolve the managed patch file from configuration, defaulting to the web profile. */
86
107
  export declare function resolvePatchPath(config: Config): string;
87
108
  /** Atomically replace the patch file (same-directory temp + rename). */
@@ -112,4 +133,5 @@ export declare function syncManagedBlock(path: string): Promise<boolean>;
112
133
  * @param config - composition entry for the managed patch file and engine knobs.
113
134
  */
114
135
  export declare function apply(ctx: Context, config: Config): void;
136
+ export {};
115
137
  //# sourceMappingURL=index.d.ts.map
@@ -1,9 +1,25 @@
1
1
  /**
2
- * Loop engine namespace literal: the one string both halves agree on, in a
3
- * module with no runtime imports so the browser bundle can import it without
2
+ * Loop engine namespace literals: the strings both halves agree on, in a
3
+ * module with no runtime imports so the browser bundle can import them without
4
4
  * dragging `dsh-settings` (a host-side service) into the client artifact.
5
5
  * @module dsh-loop-engine/namespace
6
6
  */
7
- /** Settings namespace carrying the deployment's selected agent loop engine. */
8
- export declare const LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "agent-loop-engine";
7
+ /**
8
+ * Settings namespace carrying the deployment's selected agent loop engine on
9
+ * the 0.1.7 line.
10
+ *
11
+ * Profile-backed settings are addressed by the composing entry id, so this
12
+ * literal is the plugin's own profile entry id: the shipped `cordis.patch.yml`
13
+ * inserts the entry as `loop-engine`, and the browser half passes the same
14
+ * string to `ctx.configForms.get(...)`. A deployment that renames the entry id
15
+ * must change this literal with it.
16
+ */
17
+ export declare const LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "loop-engine";
18
+ /**
19
+ * Settings namespace carrying the same selection on the 0.1.5 line, where a
20
+ * settings section is addressed by the namespace a provider registers rather
21
+ * than by a composing entry id. This is the literal the pre-rewrite plugin
22
+ * shipped; it is stable across the whole 0.1.5 line.
23
+ */
24
+ export declare const LEGACY_LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "agent-loop-engine";
9
25
  //# sourceMappingURL=namespace.d.ts.map
@@ -68,10 +68,14 @@ export declare const STRIPPED_ROWS: readonly ["skill-filesystem", "tool-skill",
68
68
  * @returns the stripped composition.
69
69
  */
70
70
  export declare function stripPresetRows(text: string, ids?: readonly string[]): string;
71
- /** Minimal read seam over the host's preset roster (`AgentPresets.read`). */
71
+ /** Minimal read seam over the host's preset roster, per harness generation. */
72
72
  export interface PresetCompositionSource {
73
- /** Read one preset's composition text; throws when the id is unknown. */
74
- read(id: string): Promise<string>;
73
+ /** 0.1.5 line: read one preset's composition text; throws when the id is unknown. */
74
+ read?(id: string): Promise<string>;
75
+ /** 0.1.7 line: read one preset's document; `content` is the composition text. */
76
+ readDocument?(id: string): Promise<{
77
+ readonly content: string;
78
+ }>;
75
79
  }
76
80
  /**
77
81
  * Regenerate every hosted engine's preset under the dsh home's user preset root
@@ -1,21 +1,30 @@
1
1
  /**
2
- * Shared loop-engine identity, namespace, and schema.
2
+ * Shared loop-engine identity, namespaces, and schemas.
3
3
  *
4
- * Both the namespace literal and the engine/preset-id mapping live in
4
+ * Both the namespace literals and the engine/preset-id mapping live in
5
5
  * zero-import modules (`./namespace.ts`, `./agent-preset-ids.ts`) so both halves
6
6
  * agree on them: the node half brands the literal as a `SettingsNamespace` and
7
- * builds this schema, while the browser half imports the same literals without
8
- * pulling host-side packages (`dsh-settings`, `schemastery`, `node:fs`) into the
9
- * client bundle (cross-plugin value imports go through cordis services, and
10
- * `settings-scope.ts` follows the same discipline). This module re-exports them
11
- * so node-side importers keep their paths.
7
+ * builds these schemas, while the browser half imports the same literals
8
+ * without pulling host-side packages (`dsh-settings`, `schemastery`, `node:fs`)
9
+ * into the client bundle (cross-plugin value imports go through cordis
10
+ * services). This module re-exports them so node-side importers keep their
11
+ * paths.
12
+ *
13
+ * The two live schema families exist because one build serves two harness
14
+ * generations: the 0.1.5 line addresses a settings SECTION registered by a
15
+ * provider under a namespace string ({@link LOOP_ENGINE_SETTINGS_SCHEMA} via
16
+ * {@link loopEngineSettingsNamespace}), while the 0.1.7 line makes the plugin's
17
+ * two live fields profile-backed Config entries
18
+ * ({@link LOOP_ENGINE_ENGINE_SCHEMA} / {@link LOOP_ENGINE_SHOW_IN_COMPOSER_SCHEMA}).
19
+ * Which family a running plugin builds is decided by `./compat.ts`
20
+ * (`LEGACY_HARNESS`) in `./index.ts`.
12
21
  *
13
22
  * @module dsh-loop-engine/settings
14
23
  */
15
24
  import z from '@deepseek-ai/schemastery';
16
25
  import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
17
26
  import type { LoopEngineId } from './agent-preset-ids.ts';
18
- export { LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL } from './namespace.ts';
27
+ export { LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL, LEGACY_LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL } from './namespace.ts';
19
28
  export { HOSTED_ENGINE_IDS, HOSTED_PRESET_PREFIX, LOOP_ENGINE_IDS, SOURCE_PRESET_ID, engineOfPreset, enginePresetId, } from './agent-preset-ids.ts';
20
29
  export type { HostedEngineId, LoopEngineId } from './agent-preset-ids.ts';
21
30
  /** Stored and composed loop engine selection. */
@@ -26,10 +35,21 @@ export interface LoopEngineSettings {
26
35
  showInComposer: boolean;
27
36
  }
28
37
  /**
29
- * Schema of the loop engine settings section. The engine union derives from
30
- * {@link LOOP_ENGINE_IDS}, so adding an engine there also admits it here.
38
+ * Schema of the loop engine settings section, as the 0.1.5 line's settings host
39
+ * consumes it. The engine union derives from {@link LOOP_ENGINE_IDS}, so adding
40
+ * an engine there also admits it here.
31
41
  */
32
42
  export declare const LOOP_ENGINE_SETTINGS_SCHEMA: z<LoopEngineSettings>;
33
- /** The shared literal branded as a settings namespace on the node side. */
43
+ /** The 0.1.5-line namespace branded as a settings namespace on the node side. */
34
44
  export declare function loopEngineSettingsNamespace(): SettingsNamespace;
45
+ /**
46
+ * Live Config field schema for the default engine (0.1.7 line). `.volatile()`
47
+ * declares it profile-backed: the settings shell projects it as a form field,
48
+ * and a committed edit reaches the running plugin through the live Config
49
+ * reference. The engine union derives from {@link LOOP_ENGINE_IDS}, so adding
50
+ * an engine there admits it here too.
51
+ */
52
+ export declare const LOOP_ENGINE_ENGINE_SCHEMA: z<"in-process" | "claude-code" | "codex" | "pi" | "kimi", "in-process" | "claude-code" | "codex" | "pi" | "kimi", "volatile-defined">;
53
+ /** Live Config field schema for the composer picker toggle (0.1.7 line). */
54
+ export declare const LOOP_ENGINE_SHOW_IN_COMPOSER_SCHEMA: z<boolean, boolean, "volatile-defined">;
35
55
  //# sourceMappingURL=settings.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-loop-engine",
3
3
  "description": "Web-switchable agent loop engine selection for the DeepSeek Harness - out-of-tree plugin (Claude Code / Codex / Pi / Kimi Code drivers) maintained by @kuun993, zero main-repo changes",
4
- "version": "0.1.5-rc4",
4
+ "version": "0.1.7-rc1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -63,39 +63,39 @@
63
63
  "dependencies": {
64
64
  "@anthropic-ai/claude-agent-sdk": "0.3.220",
65
65
  "@anthropic-ai/sdk": "0.93.0",
66
- "@deepseek-ai/schemastery": "3.18.1",
66
+ "@deepseek-ai/schemastery": "3.18.4",
67
67
  "@earendil-works/pi-coding-agent": "0.84.3",
68
68
  "@openai/codex": "0.149.1"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "@deepseek-ai/cordis": "^4.0.1",
72
- "@deepseek-ai/dsh-agent": "0.1.5-rc.2",
73
- "@deepseek-ai/dsh-agent-loop": "0.1.5-rc.2",
74
- "@deepseek-ai/dsh-invariants": "0.1.5-rc.2",
75
- "@deepseek-ai/dsh-llm": "0.1.5-rc.2",
76
- "@deepseek-ai/dsh-scope": "0.1.5-rc.2",
77
- "@deepseek-ai/dsh-session": "0.1.5-rc.2",
78
- "@deepseek-ai/dsh-session-persistence": "0.1.5-rc.2",
79
- "@deepseek-ai/dsh-settings": "0.1.5-rc.2",
80
- "@deepseek-ai/dsh-subprocess": "0.1.5-rc.2",
81
- "@deepseek-ai/dsh-timeout": "0.1.5-rc.2",
82
- "@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.2"
72
+ "@deepseek-ai/dsh-agent": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
73
+ "@deepseek-ai/dsh-agent-loop": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
74
+ "@deepseek-ai/dsh-invariants": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
75
+ "@deepseek-ai/dsh-llm": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
76
+ "@deepseek-ai/dsh-scope": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
77
+ "@deepseek-ai/dsh-session": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
78
+ "@deepseek-ai/dsh-session-persistence": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
79
+ "@deepseek-ai/dsh-settings": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
80
+ "@deepseek-ai/dsh-subprocess": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
81
+ "@deepseek-ai/dsh-timeout": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0",
82
+ "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0"
83
83
  },
84
84
  "devDependencies": {
85
- "@deepseek-ai/dsh-agent-loop": "0.1.5-rc.2",
86
- "@deepseek-ai/dsh-attachment": "0.1.5-rc.2",
87
- "@deepseek-ai/dsh-client-locale": "0.1.5-rc.2",
88
- "@deepseek-ai/dsh-client-store": "0.1.5-rc.2",
89
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2",
90
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
91
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2",
92
- "@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2",
93
- "@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
94
- "@deepseek-ai/dsh-home-paths": "0.1.5-rc.2",
95
- "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.5-rc.2",
96
- "@deepseek-ai/dsh-subprocess-local": "0.1.5-rc.2",
97
- "@deepseek-ai/dsh-system-prompt": "0.1.5-rc.2",
98
- "@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.2",
85
+ "@deepseek-ai/dsh-agent-loop": "0.1.7-rc.1",
86
+ "@deepseek-ai/dsh-attachment": "0.1.7-rc.1",
87
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
88
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
89
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.1",
90
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
91
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
92
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
93
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
94
+ "@deepseek-ai/dsh-home-paths": "0.1.7-rc.1",
95
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-rc.1",
96
+ "@deepseek-ai/dsh-subprocess-local": "0.1.7-rc.1",
97
+ "@deepseek-ai/dsh-system-prompt": "0.1.7-rc.1",
98
+ "@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.1",
99
99
  "@types/node": "^22.20.0",
100
100
  "@types/react": "^19.0.0",
101
101
  "@vitest/coverage-v8": "^4.1.8",