dsh-loop-engine 1.0.0-rc10

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.
Files changed (63) hide show
  1. package/README.md +121 -0
  2. package/README.zh.md +38 -0
  3. package/cordis.patch.yml +3 -0
  4. package/lib/client.js +37506 -0
  5. package/lib/index.js +6412 -0
  6. package/lib/invariant.js +108 -0
  7. package/lib/types/client/LoopEngineBadge.d.ts +34 -0
  8. package/lib/types/client/LoopEngineComposerSelect.d.ts +40 -0
  9. package/lib/types/client/LoopEngineSection.d.ts +34 -0
  10. package/lib/types/client/index.d.ts +29 -0
  11. package/lib/types/client/locales.d.ts +46 -0
  12. package/lib/types/client/store.d.ts +58 -0
  13. package/lib/types/commands.d.ts +69 -0
  14. package/lib/types/driver-core/context-files.d.ts +62 -0
  15. package/lib/types/driver-core/ownership.d.ts +40 -0
  16. package/lib/types/driver-core/permission-knobs.d.ts +26 -0
  17. package/lib/types/driver-core/prompt.d.ts +23 -0
  18. package/lib/types/driver-core/skill-inject.d.ts +59 -0
  19. package/lib/types/engine-claude/agent.d.ts +104 -0
  20. package/lib/types/engine-claude/loop.d.ts +111 -0
  21. package/lib/types/engine-claude/mapping.d.ts +83 -0
  22. package/lib/types/engine-claude/permission.d.ts +41 -0
  23. package/lib/types/engine-claude/process.d.ts +59 -0
  24. package/lib/types/engine-claude/sdk.d.ts +57 -0
  25. package/lib/types/engine-claude/types.d.ts +18 -0
  26. package/lib/types/engine-codex/agent.d.ts +111 -0
  27. package/lib/types/engine-codex/appserver/client.d.ts +49 -0
  28. package/lib/types/engine-codex/appserver/mapping.d.ts +67 -0
  29. package/lib/types/engine-codex/appserver/thread.d.ts +66 -0
  30. package/lib/types/engine-codex/appserver/types.d.ts +215 -0
  31. package/lib/types/engine-codex/loop.d.ts +114 -0
  32. package/lib/types/engine-codex/permission.d.ts +32 -0
  33. package/lib/types/engine-codex/skills.d.ts +29 -0
  34. package/lib/types/engine-codex/types.d.ts +19 -0
  35. package/lib/types/engine-kimi/acp/client.d.ts +76 -0
  36. package/lib/types/engine-kimi/acp/mapping.d.ts +44 -0
  37. package/lib/types/engine-kimi/acp/types.d.ts +95 -0
  38. package/lib/types/engine-kimi/agent.d.ts +123 -0
  39. package/lib/types/engine-kimi/commands.d.ts +40 -0
  40. package/lib/types/engine-kimi/loop.d.ts +108 -0
  41. package/lib/types/engine-kimi/mapping.d.ts +71 -0
  42. package/lib/types/engine-kimi/permission.d.ts +28 -0
  43. package/lib/types/engine-kimi/process.d.ts +61 -0
  44. package/lib/types/engine-kimi/skills.d.ts +57 -0
  45. package/lib/types/engine-kimi/types.d.ts +23 -0
  46. package/lib/types/engine-pi/agent.d.ts +135 -0
  47. package/lib/types/engine-pi/loop.d.ts +123 -0
  48. package/lib/types/engine-pi/permission.d.ts +43 -0
  49. package/lib/types/engine-pi/probe.d.ts +23 -0
  50. package/lib/types/engine-pi/rpc/client.d.ts +105 -0
  51. package/lib/types/engine-pi/rpc/mapping.d.ts +37 -0
  52. package/lib/types/engine-pi/rpc/types.d.ts +235 -0
  53. package/lib/types/engine-pi/skills.d.ts +55 -0
  54. package/lib/types/engine-pi/types.d.ts +27 -0
  55. package/lib/types/index.d.ts +114 -0
  56. package/lib/types/invariant.d.ts +23 -0
  57. package/lib/types/namespace.d.ts +9 -0
  58. package/lib/types/patch-manager.d.ts +59 -0
  59. package/lib/types/preset.d.ts +73 -0
  60. package/lib/types/provider-route.d.ts +49 -0
  61. package/lib/types/settings.d.ts +31 -0
  62. package/lib/types/skills.d.ts +93 -0
  63. package/package.json +103 -0
@@ -0,0 +1,108 @@
1
+ // src/settings.ts
2
+ import z from "@deepseek-ai/schemastery";
3
+ var LOOP_ENGINE_IDS = ["in-process", "claude-code", "codex", "pi", "kimi"];
4
+ var LOOP_ENGINE_SETTINGS_SCHEMA = z.object({
5
+ engine: z.union([z.const("in-process"), z.const("claude-code"), z.const("codex"), z.const("pi"), z.const("kimi")]).default("in-process"),
6
+ showInComposer: z.boolean().default(true)
7
+ });
8
+
9
+ // src/patch-manager.ts
10
+ var MANAGED_BLOCK_BEGIN = "# -- dsh-loop-engine managed block: ";
11
+ var MANAGED_BLOCK_END = "# -- /dsh-loop-engine managed block --";
12
+ var END_MARKER_LINE = `${MANAGED_BLOCK_END}
13
+ `;
14
+ function renderManagedBlock(engine) {
15
+ if (engine === "in-process") return "";
16
+ return [
17
+ `${MANAGED_BLOCK_BEGIN}${engine} --`,
18
+ "- id: agent-loop",
19
+ " disabled: true",
20
+ "- id: command-goal",
21
+ " disabled: true",
22
+ END_MARKER_LINE
23
+ ].join("\n");
24
+ }
25
+ var BEGIN_MARKER_RE = /^# -- dsh-loop-engine managed block: (\S+) --$/m;
26
+ function currentEngineOf(text) {
27
+ const engine = BEGIN_MARKER_RE.exec(text)?.[1];
28
+ return LOOP_ENGINE_IDS.includes(engine ?? "") ? engine : "in-process";
29
+ }
30
+ function managedSpan(text) {
31
+ const begin = text.indexOf(MANAGED_BLOCK_BEGIN);
32
+ if (begin === -1) return { head: text, tail: "", present: false, blankBefore: false };
33
+ const afterBegin = begin + MANAGED_BLOCK_BEGIN.length;
34
+ const endAt = text.indexOf(MANAGED_BLOCK_END, afterBegin);
35
+ const spanEnd = endAt === -1 ? text.length : endAt + END_MARKER_LINE.length;
36
+ const before = text.slice(0, begin);
37
+ const blankBefore = before.endsWith("\n\n");
38
+ return {
39
+ head: blankBefore ? before.slice(0, -1) : before,
40
+ tail: text.slice(spanEnd),
41
+ present: true,
42
+ blankBefore
43
+ };
44
+ }
45
+ function ensureTrailingNewline(text) {
46
+ return text.endsWith("\n") ? text : `${text}
47
+ `;
48
+ }
49
+ function hasRootEntry(text) {
50
+ return /^(?:- |\[)/m.test(text);
51
+ }
52
+ function dropSeedPlaceholder(text) {
53
+ return text.replace(/^\[\]\n/m, "");
54
+ }
55
+ function seedEmptyArray(text) {
56
+ const head = text.replace(/\n+$/, "");
57
+ return head === "" ? "[]\n" : `${head}
58
+ []
59
+ `;
60
+ }
61
+ function applyManagedBlock(text, engine) {
62
+ const block = renderManagedBlock(engine);
63
+ const span = managedSpan(text);
64
+ let result;
65
+ if (!span.present) {
66
+ if (block === "") {
67
+ result = text;
68
+ } else {
69
+ const base = ensureTrailingNewline(text);
70
+ result = `${base}
71
+ ${block}`;
72
+ }
73
+ } else if (block === "") {
74
+ result = span.tail.startsWith("\n") ? `${span.head}${span.tail.slice(1)}` : `${span.head}${span.tail}`;
75
+ } else {
76
+ result = `${span.head}${span.blankBefore ? "\n" : ""}${block}${span.tail}`;
77
+ }
78
+ if (block !== "") return dropSeedPlaceholder(result);
79
+ if (text.trim() === "") return result;
80
+ return hasRootEntry(result) ? result : seedEmptyArray(result);
81
+ }
82
+
83
+ // src/invariant.ts
84
+ var PACKAGE_NAME = "dsh-loop-engine";
85
+ var name = "loop-engine-invariant";
86
+ var inject = ["invariants"];
87
+ var install = (ctx, fail) => {
88
+ void ctx;
89
+ const seed = "";
90
+ const commentOnly = "# dsh profile patch layer\n";
91
+ for (const engine of LOOP_ENGINE_IDS) {
92
+ const applied = applyManagedBlock(seed, engine);
93
+ const reborn = applyManagedBlock(applied, currentEngineOf(applied));
94
+ if (reborn !== applied) fail(`bare round trip for ${engine} is not a fixed point`);
95
+ if (engine === "in-process" && applied !== seed) fail("in-process engine must leave a bare layer unchanged");
96
+ if (engine !== "in-process" && currentEngineOf(renderManagedBlock(engine)) !== engine) fail(`${engine} block must read back as the ${engine} engine`);
97
+ if (engine === "in-process" && applyManagedBlock(commentOnly, engine) === commentOnly) {
98
+ fail("in-process engine must re-seed a comment-only file to a loadable top-level array");
99
+ }
100
+ }
101
+ };
102
+ var apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
103
+ export {
104
+ apply,
105
+ inject,
106
+ name
107
+ };
108
+ //# sourceMappingURL=invariant.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Session header engine badge: a read-only chip naming the loop engine that
3
+ * drives this session. The engine is a deployment-level choice, so the chip
4
+ * reports the same value for every session — naming what sessions run is the
5
+ * honest affordance; the switch itself lives in the settings section.
6
+ *
7
+ * Styling is token-driven inline styles like the settings section (the
8
+ * client-module bundle is esbuild-built without a CSS loader).
9
+ * @module dsh-loop-engine/client/badge
10
+ */
11
+ import type { JSX } from 'react';
12
+ import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
13
+ import type { InjectFace } from '@deepseek-ai/dsh-client-ui-slots';
14
+ import type { LoopEngineState } from './store.ts';
15
+ import type { en } from './locales.ts';
16
+ /** Registration-side business face for the header badge. */
17
+ export interface LoopEngineBadgeInjected {
18
+ hooks: {
19
+ /** Engine snapshot bound by the renderer as useSnapshot. */
20
+ snapshot: SnapshotStore<LoopEngineState>;
21
+ };
22
+ /** Section copy bound to the engine dictionaries. */
23
+ t: (key: keyof typeof en) => string;
24
+ }
25
+ /** Props delivered by the slot outlet (the renderer erases the share boundary). */
26
+ export type LoopEngineBadgeProps = Partial<InjectFace<LoopEngineBadgeInjected>>;
27
+ /**
28
+ * Render the session header's loop-engine chip. Hides until the settings
29
+ * scope settles, so the header never flashes a provisional engine.
30
+ * @param props - composed slot props.
31
+ * @returns the chip, or null while the engine is unknown.
32
+ */
33
+ export declare function LoopEngineBadge(props: LoopEngineBadgeProps): JSX.Element | null;
34
+ //# sourceMappingURL=LoopEngineBadge.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Composer loop-engine picker: a compact dropdown registered at the
3
+ * `conversation.input.right` seat, so it sits immediately left of the model
4
+ * select in the composer's tool row. The engine is a deployment-level choice,
5
+ * so this surface shares the same settings-backed {@link LoopEngineStore} as
6
+ * the settings section and the header badge — a change in any one is what the
7
+ * others show next. Switching still asks for confirmation first (it interrupts
8
+ * sessions still running on the previous engine) and reloads the page once the
9
+ * commit lands, matching the settings section's semantics.
10
+ *
11
+ * Styling is token-driven inline styles like the badge and section (the
12
+ * client-module bundle is esbuild-built without a CSS loader).
13
+ * @module dsh-loop-engine/client/composer
14
+ */
15
+ import { type JSX } from 'react';
16
+ import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
17
+ import type { InjectFace } from '@deepseek-ai/dsh-client-ui-slots';
18
+ import type { LoopEngineStore, LoopEngineState } from './store.ts';
19
+ import type { en } from './locales.ts';
20
+ /** Injected dependencies of {@link LoopEngineComposerSelect} (slot `inject`). */
21
+ export interface LoopEngineComposerSelectInjected {
22
+ /** The selection store (loaded on mount, refreshed by scope pushes). */
23
+ controller: LoopEngineStore;
24
+ hooks: {
25
+ /** Engine snapshot bound by the UI renderer as useSnapshot. */
26
+ snapshot: SnapshotStore<LoopEngineState>;
27
+ };
28
+ /** Composer copy bound to the loop engine dictionaries. */
29
+ t: (key: keyof typeof en) => string;
30
+ }
31
+ /** Props delivered by the slot outlet (the renderer erases the share boundary). */
32
+ export type LoopEngineComposerSelectProps = Partial<InjectFace<LoopEngineComposerSelectInjected>>;
33
+ /**
34
+ * Render the composer's loop-engine dropdown. Hides until the settings scope
35
+ * settles, so the composer never flashes a provisional engine.
36
+ * @param props - composed slot props.
37
+ * @returns the picker, or null while the engine is unknown.
38
+ */
39
+ export declare function LoopEngineComposerSelect(props: LoopEngineComposerSelectProps): JSX.Element | null;
40
+ //# sourceMappingURL=LoopEngineComposerSelect.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Loop engine settings section component: one dropdown choosing the agent
3
+ * loop engine, backed by the duplicated settings scope through the inject face.
4
+ * Changing the engine asks for confirmation first, because the switch
5
+ * interrupts sessions still running on the previous engine.
6
+ *
7
+ * Styling is token-driven like the rest of the settings shell (`--dsw-*`
8
+ * aliases), with the picker rendered through the shared `Menu` primitive and
9
+ * the confirmation through `Modal`. The client-module bundle is esbuild-built
10
+ * without a CSS loader, so the section shell uses token-based inline styles
11
+ * instead of a CSS module.
12
+ * @module dsh-loop-engine/client
13
+ */
14
+ import { type JSX } from 'react';
15
+ import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
16
+ import type { InjectFace } from '@deepseek-ai/dsh-client-ui-slots';
17
+ import type { LoopEngineStore, LoopEngineState } from './store.ts';
18
+ import type { en } from './locales.ts';
19
+ /** Injected dependencies of {@link LoopEngineSection} (slot `inject`). */
20
+ export interface LoopEngineSectionInjected {
21
+ /** The selection store (loaded on mount, refreshed by scope pushes). */
22
+ controller: LoopEngineStore;
23
+ hooks: {
24
+ /** Section snapshot bound by the UI renderer as useSnapshot. */
25
+ snapshot: SnapshotStore<LoopEngineState>;
26
+ };
27
+ /** Section copy. */
28
+ t: (key: keyof typeof en) => string;
29
+ }
30
+ /** Props delivered by the slot outlet (the renderer erases the share boundary). */
31
+ export type LoopEngineSectionProps = Partial<InjectFace<LoopEngineSectionInjected>>;
32
+ /** Render the engine dropdown plus the interrupt notice and the switch confirmation. */
33
+ export declare function LoopEngineSection(props: LoopEngineSectionProps): JSX.Element;
34
+ //# sourceMappingURL=LoopEngineSection.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Loop engine settings plugin, browser half. Registers the "Loop engine"
3
+ * page under the settings section slot once the settings shell declares it,
4
+ * binding one store to the duplicated `agent-loop-engine` settings scope.
5
+ * Export discipline: packages/client/AGENTS.md.
6
+ * @module dsh-loop-engine/client
7
+ */
8
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
9
+ import { type LoopEngineKey } from './locales.ts';
10
+ export type { LoopEngineSectionInjected, LoopEngineSectionProps } from './LoopEngineSection.tsx';
11
+ export type { LoopEngineBadgeInjected, LoopEngineBadgeProps } from './LoopEngineBadge.tsx';
12
+ export type { LoopEngineComposerSelectInjected, LoopEngineComposerSelectProps } from './LoopEngineComposerSelect.tsx';
13
+ export type { LoopEngineState } from './store.ts';
14
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
15
+ interface LocaleNamespaceMap {
16
+ /** The Loop engine settings page copy. */
17
+ 'settings.loop-engine': LoopEngineKey;
18
+ }
19
+ }
20
+ /** Required services (cordis fiber inject). The target slot is declared by
21
+ * ui-settings' apply; registration depends on it through `slots.inject()`. */
22
+ export declare const inject: string[];
23
+ /**
24
+ * Register the Loop engine section once the `settings.section` declaration is
25
+ * on the ledger and bind its store to the duplicated settings scope.
26
+ * @param ctx - client root context.
27
+ */
28
+ export declare function apply(ctx: ClientContext): void;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Loop engine settings page copy (Chinese product copy; comments in English).
3
+ * @module dsh-loop-engine/client/locales
4
+ */
5
+ /** Copy keys of the loop engine settings page. */
6
+ export interface LoopEngineKey {
7
+ /** Settings navigation label. */
8
+ nav: string;
9
+ /** Panel description under the title. */
10
+ description: string;
11
+ /** Option label: the default in-process loop driver. */
12
+ engineInProcess: string;
13
+ /** Option label: the Claude Code CLI driver. */
14
+ engineClaudeCode: string;
15
+ /** Option label: the Codex CLI driver. */
16
+ engineCodex: string;
17
+ /** Option label: the Pi CLI driver. */
18
+ enginePi: string;
19
+ /** Option label: the Kimi Code CLI driver. */
20
+ engineKimi: string;
21
+ /** Settings toggle: show the engine picker in the chat page composer. */
22
+ showInComposerLabel: string;
23
+ /** Unavailable-state message. */
24
+ unavailable: string;
25
+ /** Notice shown when the selection would interrupt running agents. */
26
+ switchNotice: string;
27
+ /** Saving state label. */
28
+ saving: string;
29
+ /** Confirmation dialog title. */
30
+ confirmTitle: string;
31
+ /** Confirmation dialog body. */
32
+ confirmBody: string;
33
+ /** Confirmation action label. */
34
+ confirmAction: string;
35
+ /** Cancel action label. */
36
+ cancelAction: string;
37
+ /** Accessible close-button label of the confirmation dialog. */
38
+ closeLabel: string;
39
+ /** Notice shown while the Claude Code engine owns the slot: model selection is native. */
40
+ claudeModelNotice: string;
41
+ }
42
+ /** Simplified Chinese copy. */
43
+ export declare const zh: Record<keyof LoopEngineKey, string>;
44
+ /** English copy. */
45
+ export declare const en: Record<keyof LoopEngineKey, string>;
46
+ //# sourceMappingURL=locales.d.ts.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Loop engine selection store: the durable settings scope is the transport,
3
+ * and the store publishes a render-safe snapshot plus the write path.
4
+ * @module dsh-loop-engine/client/store
5
+ */
6
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
7
+ import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
8
+ import type { LoopEngineId } from '../settings.ts';
9
+ /** State rendered by the loop engine section. */
10
+ export interface LoopEngineState {
11
+ status: 'loading' | 'ready' | 'unavailable' | 'saving';
12
+ engine: LoopEngineId;
13
+ showInComposer: boolean;
14
+ writable: boolean;
15
+ error: string | null;
16
+ }
17
+ /** Narrow a wire section to the stored engine id and display toggle; an invalid one reads default. */
18
+ export declare function decodeLoopEngine(section: unknown): {
19
+ engine: LoopEngineId;
20
+ showInComposer: boolean;
21
+ } | undefined;
22
+ /** Coordinates the settings-backed loop engine selection. */
23
+ export declare class LoopEngineStore {
24
+ private readonly scope;
25
+ /** uSES-safe state source shared by the registered settings section. */
26
+ readonly store: SnapshotStore<LoopEngineState>;
27
+ private following;
28
+ private saving;
29
+ /**
30
+ * @param scope - the loop engine settings namespace scope.
31
+ */
32
+ constructor(scope: SettingsScope<{
33
+ engine: LoopEngineId;
34
+ showInComposer: boolean;
35
+ }>);
36
+ /** Begin following the bound scope and publish its current answer. */
37
+ load(): void;
38
+ /**
39
+ * Persist the selected engine. Success is judged against the snapshot the
40
+ * write left behind, so a refused write reports error after its recovery.
41
+ * @param engine - the engine to select for future Agent turns.
42
+ * @returns whether the write landed.
43
+ */
44
+ setEngine(engine: LoopEngineId): Promise<boolean>;
45
+ /**
46
+ * Persist whether the composer shows the engine picker. Success is judged
47
+ * against the snapshot the write left behind, so a refused write reports
48
+ * error after its recovery. Unlike {@link setEngine}, landing does not reload
49
+ * the page — the toggle only changes composer visibility.
50
+ * @param show - whether the chat page composer reveals the engine picker.
51
+ * @returns whether the write landed.
52
+ */
53
+ setShowInComposer(show: boolean): Promise<boolean>;
54
+ /** Stop following the scope. */
55
+ dispose(): void;
56
+ private derive;
57
+ }
58
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Claude Code slash-command bridge.
3
+ *
4
+ * The dsh `commands` runtime executes a registered command locally — the line
5
+ * is consumed and never reaches the model — so a command whose real processing
6
+ * lives inside the Claude Code CLI must forward the raw line back to the
7
+ * engine. The definitions here do exactly that: the handler delivers
8
+ * `/<name> [args]` to the receiving agent as a plain user message, and the CLI
9
+ * then expands it natively (built-ins and custom `.claude/commands/*.md`).
10
+ * Registering the built-ins keeps them visible in the dsh web slash menu;
11
+ * unregistered `/lines` pass through as user text, but the menu would hide the
12
+ * engine's command surface.
13
+ *
14
+ * User-level custom slash commands (`~/.claude/commands/*.md`) are discovered
15
+ * and registered the same way, so they appear in the menu AND reach the CLI.
16
+ * Project-level `.claude/commands/` files are left to the CLI entirely: they
17
+ * are cwd-dependent, and a global dsh registration would collide across
18
+ * projects.
19
+ *
20
+ * @module dsh-loop-engine/commands
21
+ */
22
+ import type { UserMessage } from '@deepseek-ai/dsh-session';
23
+ /** Minimal shape of a DSH command definition (avoiding a direct peer dep on @deepseek-ai/dsh-commands). */
24
+ export interface CommandDefinition {
25
+ readonly name: string;
26
+ readonly description: string;
27
+ readonly input?: {
28
+ readonly hint: string;
29
+ readonly images?: boolean;
30
+ };
31
+ readonly handler: (invocation: CommandInvocation) => CommandResult | Promise<CommandResult>;
32
+ }
33
+ /** Invocation delivered to one registered command handler. */
34
+ export interface CommandInvocation {
35
+ readonly commandId: string;
36
+ /** The receiving agent; forwarding handlers deliver the raw line back to it. */
37
+ readonly agent: {
38
+ readonly followup: (input: UserMessage) => void;
39
+ };
40
+ /** Exact text following the command name, including separator whitespace. */
41
+ readonly rawInput: string;
42
+ readonly signal: AbortSignal;
43
+ }
44
+ /** Settled result of one command handler. */
45
+ export interface CommandResult {
46
+ readonly kind: 'success' | 'error';
47
+ readonly text?: string;
48
+ }
49
+ /**
50
+ * Build the forwarding handler for one Claude Code slash command: it
51
+ * re-delivers the full `/<name> [args]` line to the receiving agent as a
52
+ * plain user message, where the CLI expands it. `rawInput` already carries the
53
+ * separator whitespace and any arguments.
54
+ * @param name - the command name without the leading slash.
55
+ * @returns the command handler.
56
+ */
57
+ export declare function forwardClaudeCodeCommand(name: string): (invocation: CommandInvocation) => CommandResult;
58
+ /** Claude Code's built-in slash commands. */
59
+ export declare const CLAUDE_CODE_COMMANDS: readonly CommandDefinition[];
60
+ /**
61
+ * Discover the user-level custom slash commands from `~/.claude/commands/*.md`
62
+ * and build forwarding definitions for them. The scan is synchronous so the
63
+ * mount path can register the commands before the engine-selection commit
64
+ * returns; files without a usable name or description, and names already taken
65
+ * by the built-ins, are skipped.
66
+ * @returns forwarding definitions, sorted by file name.
67
+ */
68
+ export declare function discoverUserSlashCommands(): CommandDefinition[];
69
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Context-file collection and body loading shared by the hosted engine
3
+ * drivers.
4
+ *
5
+ * Codex and Pi read per-directory instruction files (`AGENTS.md`; pi also
6
+ * accepts `CLAUDE.md` and prefers `AGENTS.override.md` inside any directory
7
+ * that has one) while walking from the session cwd up to the git root. The
8
+ * skill providers surface each collected set as one merged skill candidate so
9
+ * the dsh skill-injection seam (`/name` gestures) can carry it into the
10
+ * prompt; the body-loading helpers below feed both providers' list/get paths.
11
+ *
12
+ * @module dsh-loop-engine/driver-core/context-files
13
+ */
14
+ /** Per-directory context-file resolution policy for one engine. */
15
+ export interface ContextFilePolicy {
16
+ /** Per-directory override file that replaces the primary files when present. */
17
+ readonly override?: string;
18
+ /** Per-directory primary files, tried in order until one exists. */
19
+ readonly primary: readonly string[];
20
+ }
21
+ /**
22
+ * The directory chain from `cwd` up to the git root, nearest first. Without a
23
+ * repository the chain is just the resolved `cwd` itself, matching
24
+ * {@link findProjectRoot}'s fallback so the walk stays bounded.
25
+ * @param cwd - the session working directory.
26
+ * @returns the chain of directories to inspect.
27
+ */
28
+ export declare function projectAncestors(cwd: string): Promise<string[]>;
29
+ /**
30
+ * Collect every directory's context file per the policy, from the session cwd
31
+ * up to the git root.
32
+ * @param cwd - the session working directory.
33
+ * @param policy - per-directory resolution policy.
34
+ * @returns existing context files, nearest directory first.
35
+ */
36
+ export declare function collectProjectContextFiles(cwd: string, policy: ContextFilePolicy): Promise<string[]>;
37
+ /**
38
+ * Read one file, or `undefined` when it is unreadable.
39
+ * @param path - the file to read.
40
+ * @returns the file body, or `undefined` on any failure.
41
+ */
42
+ export declare function readOptionalFile(path: string): Promise<string | undefined>;
43
+ /**
44
+ * Whether any of the given sources carries non-whitespace content.
45
+ * @param paths - candidate file paths.
46
+ * @returns whether at least one readable source is non-empty.
47
+ */
48
+ export declare function anySourceNonEmpty(paths: readonly string[]): Promise<boolean>;
49
+ /**
50
+ * Whether one file exists and carries non-whitespace content.
51
+ * @param path - the file to inspect.
52
+ * @returns whether the file is readable and non-empty.
53
+ */
54
+ export declare function fileNonEmpty(path: string): Promise<boolean>;
55
+ /**
56
+ * Concatenate every non-empty readable source body in order, or `undefined`
57
+ * when none are readable.
58
+ * @param paths - candidate file paths, nearest directory first.
59
+ * @returns the joined bodies, or `undefined` when nothing could be read.
60
+ */
61
+ export declare function readSources(paths: readonly string[]): Promise<string | undefined>;
62
+ //# sourceMappingURL=context-files.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Shared factory ownership and abort-race machinery for the hosted engines.
3
+ * Both the Claude Code and Codex loop drivers run the same lifecycle: exactly
4
+ * one factory owns the AgentFactory slot, every live agent's teardown is
5
+ * tracked until it settles, and setup awaits are raced against a fused abort
6
+ * signal. These helpers are engine-free — they only touch the fiber state,
7
+ * the session id type, and an AbortController — so the two loop modules share
8
+ * them verbatim.
9
+ *
10
+ * @module dsh-loop-engine/driver-core/ownership
11
+ */
12
+ import type { Context } from '@deepseek-ai/cordis';
13
+ import type { SessionId } from '@deepseek-ai/dsh-session';
14
+ /** Fiber states that cannot own or serve a new lifecycle. */
15
+ export declare const INACTIVE_STATES: ReadonlySet<number>;
16
+ /** Factory-level ownership: live agent teardowns plus load-time tracking. */
17
+ export declare class FactoryOwnership {
18
+ private readonly fiber;
19
+ private accepting;
20
+ private readonly teardown;
21
+ private readonly inactive;
22
+ private readonly liveAgents;
23
+ private startupTasks;
24
+ constructor(fiber: Context['fiber']);
25
+ /** Aborts (reason: `agent loop is not active` error) when factory teardown begins. */
26
+ get signal(): AbortSignal;
27
+ isActive(): boolean;
28
+ /** Track one live agent's shared teardown until it has run. */
29
+ track(dispose: () => Promise<void>): () => void;
30
+ /** Join config startup work that begins before an agent exists. */
31
+ trackStartup(job: Promise<void>): void;
32
+ /** Join one public create/resume continuation; factory dispose awaits its settlement. */
33
+ trackWrapper(job: Promise<unknown>): void;
34
+ dispose(): Promise<void>;
35
+ }
36
+ /** Await `operation`, or throw the signal's reason as soon as it aborts. */
37
+ export declare function raceAbort<T>(operation: PromiseLike<T> | T, signal: AbortSignal, id: SessionId): Promise<T>;
38
+ /** Start an abortable operation and release a value that arrives after cancellation. */
39
+ export declare function raceAbortCall<T>(operation: () => PromiseLike<T> | T, signal: AbortSignal, id: SessionId, releaseAbandoned?: (value: T) => void): Promise<T>;
40
+ //# sourceMappingURL=ownership.d.ts.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Reading the dsh session's durable permission knobs from the session log.
3
+ * Both the Claude Code and Codex drivers fold the same `sandbox/mode` and
4
+ * `approval/policy` events (pinned at creation, re-recorded on every switch)
5
+ * into per-query permission decisions; the knob readers are engine-free.
6
+ *
7
+ * @module dsh-loop-engine/driver-core/permission-knobs
8
+ */
9
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
10
+ /**
11
+ * Minimal structural shape of one session log event. The base `SessionEvent`
12
+ * union in this compilation does not carry the sandbox/approval packages'
13
+ * augmentation keys, so the fold reads the wire shape directly.
14
+ */
15
+ export type PermissionEvent = Pick<SessionEvent, 'data'> & {
16
+ readonly type: string;
17
+ };
18
+ /** dsh sandbox modes, mirrored inline to avoid a peer dep on @deepseek-ai/dsh-sandbox-policy. */
19
+ export type DshSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
20
+ /** dsh approval policies, mirrored inline to avoid a peer dep on @deepseek-ai/dsh-user-approval. */
21
+ export type DshApprovalPolicy = 'ask' | 'never';
22
+ /** The session's sandbox-mode override: the last `sandbox/mode` event, if any. */
23
+ export declare function sessionSandboxMode(events: readonly PermissionEvent[]): DshSandboxMode | undefined;
24
+ /** The session's approval-policy override: the last `approval/policy` event, if any. */
25
+ export declare function sessionApprovalPolicy(events: readonly PermissionEvent[]): DshApprovalPolicy | undefined;
26
+ //# sourceMappingURL=permission-knobs.d.ts.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Serialization of the durable session history into the prompt text of one
3
+ * hosted-engine query. Both the Claude Code and Codex drivers build their
4
+ * per-step input from the durable session log: the transcript is the log's
5
+ * exact projection, so a later replay of the same log derives the identical
6
+ * prompt (Model-visible ⟺ logged bridge).
7
+ *
8
+ * @module dsh-loop-engine/driver-core/prompt
9
+ */
10
+ import type { Message } from '@deepseek-ai/dsh-llm';
11
+ /** Model-facing stand-in for an image block that the hosted engines cannot consume as bytes. */
12
+ export declare const OMITTED_IMAGE_TEXT = "[image omitted: the driver does not transcribe images; read the file when a path is available]";
13
+ /**
14
+ * Serialize a derived conversation history into the prompt text of one hosted
15
+ * query. The last message is the live user request that triggered the step;
16
+ * every earlier message is durable replay context. The output is a pure
17
+ * function of the log prefix.
18
+ * @param messages - derived history, oldest first, as returned by
19
+ * `Session.deriveMessages()` at step time.
20
+ * @returns the prompt text to pass to the engine.
21
+ */
22
+ export declare function serializeHistory(messages: readonly Message[]): string;
23
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Skill-injection helpers shared by the hosted engine drivers. Both the Claude
3
+ * Code and Codex agents replicate the dsh `/name` skill gesture scan and the
4
+ * XML `<skill_content>` rendering that the in-process engine's dsh-tool-skill
5
+ * handler would otherwise provide — their agent contexts do not descend from
6
+ * the agent-preset chain. These helpers are pure: they take user messages or a
7
+ * loaded skill and return the injected text, with no session or loop access.
8
+ *
9
+ * @module dsh-loop-engine/driver-core/skill-inject
10
+ */
11
+ import type { UserMessage } from '@deepseek-ai/dsh-session';
12
+ export declare function isSkillName(name: string): boolean;
13
+ /** Minimal shape of a loaded skill definition. */
14
+ export interface SkillDefinition {
15
+ readonly name: string;
16
+ readonly description: string;
17
+ readonly whenToUse?: string;
18
+ readonly invocation: {
19
+ readonly modelInvocable: boolean;
20
+ readonly userInvocable: boolean;
21
+ };
22
+ readonly source: string;
23
+ readonly provider: string;
24
+ readonly content: string;
25
+ readonly path?: string;
26
+ readonly resourceBase?: {
27
+ readonly kind: string;
28
+ readonly path: string;
29
+ };
30
+ }
31
+ /** Durable source for an injected user-explicit skill invocation (mirrors dsh-skill's). */
32
+ export interface SkillInvocationSource {
33
+ readonly kind: 'skill-invocation';
34
+ readonly name: string;
35
+ readonly form: 'instructions';
36
+ }
37
+ declare module '@deepseek-ai/dsh-llm' {
38
+ interface MessageSourceMap {
39
+ /** A user-explicit skill invocation injected by this driver. */
40
+ 'skill-invocation': SkillInvocationSource;
41
+ }
42
+ }
43
+ /** Minimal shape of the SkillRegistry service. */
44
+ export interface SkillsService {
45
+ get(name: string, options: {
46
+ cwd?: string;
47
+ signal?: AbortSignal;
48
+ scope?: unknown;
49
+ }): Promise<SkillDefinition | undefined>;
50
+ }
51
+ /** Escape text for inclusion in XML-like skill markup. */
52
+ export declare function escapeText(value: string): string;
53
+ /** Escape an XML-like attribute value. */
54
+ export declare function escapeAttr(value: string): string;
55
+ /** Render the `<skill_content>` block for a loaded skill. */
56
+ export declare function renderSkillContent(skill: SkillDefinition): string;
57
+ /** Collect `/name` gesture tokens from direct user messages, in first-seen order. */
58
+ export declare function invokedSkillNames(messages: readonly UserMessage[]): string[];
59
+ //# sourceMappingURL=skill-inject.d.ts.map