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.
@@ -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. */
@@ -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
@@ -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-rc5",
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",